Skip to content

Commit b6ff30a

Browse files
CLOUDP-166251: Added optional secret references for alert configurations (#1002)
1 parent e086710 commit b6ff30a

File tree

9 files changed

+305
-6
lines changed

9 files changed

+305
-6
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ e2e-openshift-upgrade:
107107

108108
.PHONY: manager
109109
manager: generate fmt vet ## Build manager binary
110-
@echo "Building operator with version $(VERSION); $(TARGET_OS) - $(TARGET_ARCH)}"
110+
@echo "Building operator with version $(VERSION); $(TARGET_OS) - $(TARGET_ARCH)"
111111
CGO_ENABLED=0 GOOS=$(TARGET_OS) GOARCH=$(TARGET_ARCH) go build -o bin/manager -ldflags="-X github.com/mongodb/mongodb-atlas-kubernetes/pkg/version.Version=$(VERSION)" cmd/manager/main.go
112112

113113
.PHONY: run

config/crd/bases/atlas.mongodb.com_atlasprojects.yaml

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,10 +111,38 @@ spec:
111111
invalid, Atlas sends an email to the project owner and
112112
eventually removes the token.
113113
type: string
114+
apiTokenRef:
115+
description: ResourceRefNamespaced is a reference to a
116+
Kubernetes Resource that allows to configure the namespace
117+
properties:
118+
name:
119+
description: Name is the name of the Kubernetes Resource
120+
type: string
121+
namespace:
122+
description: Namespace is the namespace of the Kubernetes
123+
Resource
124+
type: string
125+
required:
126+
- name
127+
type: object
114128
channelName:
115129
description: Slack channel name. Populated for the SLACK
116130
notifications type.
117131
type: string
132+
datadogAPIKeyRef:
133+
description: ResourceRefNamespaced is a reference to a
134+
Kubernetes Resource that allows to configure the namespace
135+
properties:
136+
name:
137+
description: Name is the name of the Kubernetes Resource
138+
type: string
139+
namespace:
140+
description: Namespace is the namespace of the Kubernetes
141+
Resource
142+
type: string
143+
required:
144+
- name
145+
type: object
118146
datadogApiKey:
119147
description: Datadog API Key. Found in the Datadog dashboard.
120148
Populated for the DATADOG notifications type.
@@ -144,6 +172,20 @@ spec:
144172
becomes invalid, Atlas sends an email to the project
145173
owner and eventually removes the token.
146174
type: string
175+
flowdockApiTokenRef:
176+
description: ResourceRefNamespaced is a reference to a
177+
Kubernetes Resource that allows to configure the namespace
178+
properties:
179+
name:
180+
description: Name is the name of the Kubernetes Resource
181+
type: string
182+
namespace:
183+
description: Namespace is the namespace of the Kubernetes
184+
Resource
185+
type: string
186+
required:
187+
- name
188+
type: object
147189
intervalMin:
148190
description: Number of minutes to wait between successive
149191
notifications for unacknowledged alerts that are not
@@ -159,6 +201,20 @@ spec:
159201
Atlas sends an email to the project owner and eventually
160202
removes the token.
161203
type: string
204+
opsGenieApiKeyRef:
205+
description: ResourceRefNamespaced is a reference to a
206+
Kubernetes Resource that allows to configure the namespace
207+
properties:
208+
name:
209+
description: Name is the name of the Kubernetes Resource
210+
type: string
211+
namespace:
212+
description: Namespace is the namespace of the Kubernetes
213+
Resource
214+
type: string
215+
required:
216+
- name
217+
type: object
162218
opsGenieRegion:
163219
description: Region that indicates which API URL to use.
164220
type: string
@@ -180,6 +236,20 @@ spec:
180236
invalid, Atlas sends an email to the project owner and
181237
eventually removes the key.
182238
type: string
239+
serviceKeyRef:
240+
description: ResourceRefNamespaced is a reference to a
241+
Kubernetes Resource that allows to configure the namespace
242+
properties:
243+
name:
244+
description: Name is the name of the Kubernetes Resource
245+
type: string
246+
namespace:
247+
description: Namespace is the namespace of the Kubernetes
248+
Resource
249+
type: string
250+
required:
251+
- name
252+
type: object
183253
smsEnabled:
184254
description: Flag indicating if text message notifications
185255
should be sent. Populated for ORG, GROUP, and USER notifications
@@ -212,6 +282,20 @@ spec:
212282
invalid, Atlas sends an email to the project owner and
213283
eventually removes the key.
214284
type: string
285+
victorOpsSecretRef:
286+
description: Secret for VictorOps should contain both
287+
APIKey and RoutingKey values
288+
properties:
289+
name:
290+
description: Name is the name of the Kubernetes Resource
291+
type: string
292+
namespace:
293+
description: Namespace is the namespace of the Kubernetes
294+
Resource
295+
type: string
296+
required:
297+
- name
298+
type: object
215299
type: object
216300
type: array
217301
threshold:

pkg/api/v1/alert_configurations.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"go.mongodb.org/atlas/mongodbatlas"
88
"go.uber.org/zap"
99

10+
"github.com/mongodb/mongodb-atlas-kubernetes/pkg/api/v1/common"
1011
"github.com/mongodb/mongodb-atlas-kubernetes/pkg/util"
1112
"github.com/mongodb/mongodb-atlas-kubernetes/pkg/util/compat"
1213
)
@@ -138,10 +139,14 @@ func (in *Threshold) ToAtlas() (*mongodbatlas.Threshold, error) {
138139
type Notification struct {
139140
// Slack API token or Bot token. Populated for the SLACK notifications type. If the token later becomes invalid, Atlas sends an email to the project owner and eventually removes the token.
140141
APIToken string `json:"apiToken,omitempty"`
142+
// +optional
143+
APITokenRef common.ResourceRefNamespaced `json:"apiTokenRef,omitempty"`
141144
// Slack channel name. Populated for the SLACK notifications type.
142145
ChannelName string `json:"channelName,omitempty"`
143146
// Datadog API Key. Found in the Datadog dashboard. Populated for the DATADOG notifications type.
144147
DatadogAPIKey string `json:"datadogApiKey,omitempty"`
148+
// +optional
149+
DatadogAPIKeyRef common.ResourceRefNamespaced `json:"datadogAPIKeyRef,omitempty"`
145150
// Region that indicates which API URL to use
146151
DatadogRegion string `json:"datadogRegion,omitempty"`
147152
// Number of minutes to wait after an alert condition is detected before sending out the first notification.
@@ -152,6 +157,8 @@ type Notification struct {
152157
EmailEnabled *bool `json:"emailEnabled,omitempty"`
153158
// The Flowdock personal API token. Populated for the FLOWDOCK notifications type. If the token later becomes invalid, Atlas sends an email to the project owner and eventually removes the token.
154159
FlowdockAPIToken string `json:"flowdockApiToken,omitempty"`
160+
// +optional
161+
FlowdockAPITokenRef common.ResourceRefNamespaced `json:"flowdockApiTokenRef,omitempty"`
155162
// Flowdock flow namse in lower-case letters.
156163
FlowName string `json:"flowName,omitempty"`
157164
// Number of minutes to wait between successive notifications for unacknowledged alerts that are not resolved.
@@ -160,12 +167,16 @@ type Notification struct {
160167
MobileNumber string `json:"mobileNumber,omitempty"`
161168
// Opsgenie API Key. Populated for the OPS_GENIE notifications type. If the key later becomes invalid, Atlas sends an email to the project owner and eventually removes the token.
162169
OpsGenieAPIKey string `json:"opsGenieApiKey,omitempty"`
170+
// +optional
171+
OpsGenieAPIKeyRef common.ResourceRefNamespaced `json:"opsGenieApiKeyRef,omitempty"`
163172
// Region that indicates which API URL to use.
164173
OpsGenieRegion string `json:"opsGenieRegion,omitempty"`
165174
// Flowdock organization name in lower-case letters. This is the name that appears after www.flowdock.com/app/ in the URL string. Populated for the FLOWDOCK notifications type.
166175
OrgName string `json:"orgName,omitempty"`
167176
// PagerDuty service key. Populated for the PAGER_DUTY notifications type. If the key later becomes invalid, Atlas sends an email to the project owner and eventually removes the key.
168177
ServiceKey string `json:"serviceKey,omitempty"`
178+
// +optinal
179+
ServiceKeyRef common.ResourceRefNamespaced `json:"serviceKeyRef,omitempty"`
169180
// Flag indicating if text message notifications should be sent. Populated for ORG, GROUP, and USER notifications types.
170181
SMSEnabled *bool `json:"smsEnabled,omitempty"`
171182
// Unique identifier of a team.
@@ -178,6 +189,9 @@ type Notification struct {
178189
Username string `json:"username,omitempty"`
179190
// VictorOps API key. Populated for the VICTOR_OPS notifications type. If the key later becomes invalid, Atlas sends an email to the project owner and eventually removes the key.
180191
VictorOpsAPIKey string `json:"victorOpsApiKey,omitempty"`
192+
// +optional
193+
// Secret for VictorOps should contain both APIKey and RoutingKey values
194+
VictorOpsSecretRef common.ResourceRefNamespaced `json:"victorOpsSecretRef,omitempty"`
181195
// VictorOps routing key. Populated for the VICTOR_OPS notifications type. If the key later becomes invalid, Atlas sends an email to the project owner and eventually removes the key.
182196
VictorOpsRoutingKey string `json:"victorOpsRoutingKey,omitempty"`
183197
// The following roles grant privileges within a project.

pkg/api/v1/status/condition.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ const (
4242
NetworkPeerReadyType ConditionType = "NetworkPeerReady"
4343
CloudProviderAccessReadyType ConditionType = "CloudProviderAccessReady"
4444
IntegrationReadyType ConditionType = "ThirdPartyIntegrationReady"
45-
AlertConfigurationReadyType ConditionType = "AlertConfigurationReadyType"
45+
AlertConfigurationReadyType ConditionType = "AlertConfigurationReady"
4646
EncryptionAtRestReadyType ConditionType = "EncryptionAtRestReady"
4747
AuditingReadyType ConditionType = "AuditingReady"
4848
ProjectSettingsReadyType ConditionType = "ProjectSettingsReady"

pkg/api/v1/zz_generated.deepcopy.go

Lines changed: 7 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/controller/atlasproject/alert_configurations.go

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,19 @@ import (
77

88
"go.mongodb.org/atlas/mongodbatlas"
99
"go.uber.org/zap"
10+
v1 "k8s.io/api/core/v1"
11+
"sigs.k8s.io/controller-runtime/pkg/client"
1012

1113
mdbv1 "github.com/mongodb/mongodb-atlas-kubernetes/pkg/api/v1"
14+
"github.com/mongodb/mongodb-atlas-kubernetes/pkg/api/v1/common"
1215
"github.com/mongodb/mongodb-atlas-kubernetes/pkg/api/v1/status"
1316
"github.com/mongodb/mongodb-atlas-kubernetes/pkg/controller/workflow"
1417
)
1518

16-
func ensureAlertConfigurations(service *workflow.Context, project *mdbv1.AtlasProject, groupID string) workflow.Result {
19+
func (r *AtlasProjectReconciler) ensureAlertConfigurations(service *workflow.Context, project *mdbv1.AtlasProject, groupID string) workflow.Result {
20+
service.Log.Debug("starting alert configurations processing")
21+
defer service.Log.Debug("finished alert configurations processing")
22+
1723
if project.Spec.AlertConfigurationSyncEnabled {
1824
specToSync := project.Spec.DeepCopy().AlertConfigurations
1925

@@ -23,6 +29,11 @@ func ensureAlertConfigurations(service *workflow.Context, project *mdbv1.AtlasPr
2329
service.UnsetCondition(alertConfigurationCondition)
2430
return workflow.OK()
2531
}
32+
err := readAlertConfigurationsSecretsData(r.Client, project.Namespace, specToSync)
33+
if err != nil {
34+
service.SetConditionFalseMsg(alertConfigurationCondition, err.Error())
35+
return workflow.Terminate(workflow.Internal, err.Error())
36+
}
2637
result := syncAlertConfigurations(ctx, service, groupID, specToSync)
2738
if !result.IsOk() {
2839
service.SetConditionFromResult(alertConfigurationCondition, result)
@@ -36,6 +47,76 @@ func ensureAlertConfigurations(service *workflow.Context, project *mdbv1.AtlasPr
3647
return workflow.OK()
3748
}
3849

50+
// This method reads secrets refs and fills the secret data for the related Notification
51+
func readAlertConfigurationsSecretsData(kubeClient client.Client, projectNs string, alertConfigs []mdbv1.AlertConfiguration) error {
52+
for i := 0; i < len(alertConfigs); i++ {
53+
ac := &alertConfigs[i]
54+
for j := 0; j < len(ac.Notifications); j++ {
55+
nf := &ac.Notifications[j]
56+
var err error
57+
switch {
58+
case nf.APITokenRef.Name != "":
59+
nf.APIToken, err = readNotificationSecret(kubeClient, nf.APITokenRef, projectNs, "APIToken")
60+
if err != nil {
61+
return err
62+
}
63+
case nf.DatadogAPIKeyRef.Name != "":
64+
nf.DatadogAPIKey, err = readNotificationSecret(kubeClient, nf.DatadogAPIKeyRef, projectNs, "DatadogAPIKey")
65+
if err != nil {
66+
return err
67+
}
68+
case nf.FlowdockAPITokenRef.Name != "":
69+
nf.FlowdockAPIToken, err = readNotificationSecret(kubeClient, nf.FlowdockAPITokenRef, projectNs, "FlowdockAPIToken")
70+
if err != nil {
71+
return err
72+
}
73+
case nf.OpsGenieAPIKeyRef.Name != "":
74+
nf.OpsGenieAPIKey, err = readNotificationSecret(kubeClient, nf.OpsGenieAPIKeyRef, projectNs, "OpsGenieAPIKey")
75+
if err != nil {
76+
return err
77+
}
78+
case nf.ServiceKeyRef.Name != "":
79+
nf.ServiceKey, err = readNotificationSecret(kubeClient, nf.ServiceKeyRef, projectNs, "ServiceKey")
80+
if err != nil {
81+
return err
82+
}
83+
case nf.VictorOpsSecretRef.Name != "":
84+
nf.VictorOpsAPIKey, err = readNotificationSecret(kubeClient, nf.VictorOpsSecretRef, projectNs, "VictorOpsAPIKey")
85+
if err != nil {
86+
return err
87+
}
88+
nf.VictorOpsRoutingKey, err = readNotificationSecret(kubeClient, nf.VictorOpsSecretRef, projectNs, "VictorOpsRoutingKey")
89+
if err != nil {
90+
return err
91+
}
92+
}
93+
}
94+
}
95+
return nil
96+
}
97+
98+
func readNotificationSecret(kubeClient client.Client, res common.ResourceRefNamespaced, parentNamespace string, fieldName string) (string, error) {
99+
secret := &v1.Secret{}
100+
var ns string
101+
if res.Namespace == "" {
102+
ns = parentNamespace
103+
} else {
104+
ns = res.Namespace
105+
}
106+
107+
if err := kubeClient.Get(context.Background(), client.ObjectKey{Name: res.Name, Namespace: ns}, secret); err != nil {
108+
return "", err
109+
}
110+
val, exists := secret.Data[fieldName]
111+
switch {
112+
case !exists:
113+
return "", fmt.Errorf("secret '%s/%s' doesn't contain '%s' parameter", ns, res.Name, fieldName)
114+
case len(val) == 0:
115+
return "", fmt.Errorf("secret '%s/%s' contain an empty value for '%s' parameter", ns, res.Name, fieldName)
116+
}
117+
return string(val), nil
118+
}
119+
39120
func syncAlertConfigurations(context context.Context, service *workflow.Context, groupID string, alertSpec []mdbv1.AlertConfiguration) workflow.Result {
40121
logger := service.Log
41122
existedAlertConfigs, _, err := service.Client.AlertConfigurations.List(context, groupID, nil)

pkg/controller/atlasproject/atlasproject_controller.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ func (r *AtlasProjectReconciler) ensureProjectResources(ctx *workflow.Context, p
246246
}
247247
results = append(results, result)
248248

249-
if result = ensureAlertConfigurations(ctx, project, projectID); result.IsOk() {
249+
if result = r.ensureAlertConfigurations(ctx, project, projectID); result.IsOk() {
250250
r.EventRecorder.Event(project, "Normal", string(status.AlertConfigurationReadyType), "")
251251
}
252252
results = append(results, result)

pkg/controller/workflow/context.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,15 @@ func (c *Context) SetConditionFalse(conditionType status.ConditionType) *Context
9696
return c
9797
}
9898

99+
func (c *Context) SetConditionFalseMsg(conditionType status.ConditionType, msg string) *Context {
100+
c.EnsureCondition(status.Condition{
101+
Type: conditionType,
102+
Status: corev1.ConditionFalse,
103+
Message: msg,
104+
})
105+
return c
106+
}
107+
99108
func (c *Context) SetConditionTrue(conditionType status.ConditionType) *Context {
100109
c.EnsureCondition(status.Condition{
101110
Type: conditionType,

0 commit comments

Comments
 (0)