Skip to content

Commit db9f21d

Browse files
author
Anton
authored
CLOUDP-80516: global connection secret (#96)
1 parent 0555f81 commit db9f21d

File tree

7 files changed

+108
-24
lines changed

7 files changed

+108
-24
lines changed

config/manager/manager.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,13 @@ spec:
4040
requests:
4141
cpu: 100m
4242
memory: 20Mi
43+
env:
44+
- name: OPERATOR_NAME
45+
valueFrom:
46+
fieldRef:
47+
fieldPath: metadata.name
48+
- name: OPERATOR_NAMESPACE
49+
valueFrom:
50+
fieldRef:
51+
fieldPath: metadata.namespace
4352
terminationGracePeriodSeconds: 10

main.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package main
1818

1919
import (
2020
"flag"
21+
"log"
2122
"os"
2223

2324
"github.com/go-logr/zapr"
@@ -27,6 +28,7 @@ import (
2728
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
2829
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
2930
ctrl "sigs.k8s.io/controller-runtime"
31+
"sigs.k8s.io/controller-runtime/pkg/client"
3032
ctrzap "sigs.k8s.io/controller-runtime/pkg/log/zap"
3133

3234
mdbv1 "github.com/mongodb/mongodb-atlas-kubernetes/pkg/api/v1"
@@ -71,11 +73,14 @@ func main() {
7173
os.Exit(1)
7274
}
7375

76+
operatorPod := operatorPodObjectKey()
77+
7478
if err = (&atlascluster.AtlasClusterReconciler{
7579
Client: mgr.GetClient(),
7680
Log: logger.Named("controllers").Named("AtlasCluster").Sugar(),
7781
Scheme: mgr.GetScheme(),
7882
AtlasDomain: config.AtlasDomain,
83+
OperatorPod: operatorPod,
7984
}).SetupWithManager(mgr); err != nil {
8085
setupLog.Error(err, "unable to create controller", "controller", "AtlasCluster")
8186
os.Exit(1)
@@ -87,6 +92,7 @@ func main() {
8792
Scheme: mgr.GetScheme(),
8893
AtlasDomain: config.AtlasDomain,
8994
ResourceWatcher: watch.NewResourceWatcher(),
95+
OperatorPod: operatorPod,
9096
}).SetupWithManager(mgr); err != nil {
9197
setupLog.Error(err, "unable to create controller", "controller", "AtlasProject")
9298
os.Exit(1)
@@ -118,3 +124,16 @@ func parseConfiguration() Config {
118124
flag.Parse()
119125
return config
120126
}
127+
128+
func operatorPodObjectKey() client.ObjectKey {
129+
operatorName := os.Getenv("OPERATOR_NAME")
130+
if operatorName == "" {
131+
log.Fatal(`"OPERATOR_NAME" environment variable must be set!`)
132+
}
133+
operatorNamespace := os.Getenv("OPERATOR_NAMESPACE")
134+
if operatorNamespace == "" {
135+
log.Fatal(`"OPERATOR_NAMESPACE" environment variable must be set!`)
136+
}
137+
138+
return client.ObjectKey{Namespace: operatorNamespace, Name: operatorName}
139+
}

pkg/controller/atlas/connection.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"sigs.k8s.io/controller-runtime/pkg/client"
1010

1111
"github.com/mongodb/mongodb-atlas-kubernetes/pkg/controller/workflow"
12+
"github.com/mongodb/mongodb-atlas-kubernetes/pkg/util/kube"
1213
)
1314

1415
const (
@@ -26,16 +27,15 @@ type Connection struct {
2627

2728
// ReadConnection reads Atlas API connection parameters from AtlasProject Secret or from the default Operator one if the
2829
// former is not specified
29-
func ReadConnection(log *zap.SugaredLogger, kubeClient client.Client, operatorName string, projectOverrideSecretRef *client.ObjectKey) (Connection, workflow.Result) {
30+
func ReadConnection(log *zap.SugaredLogger, kubeClient client.Client, operatorPodObjectKey client.ObjectKey, projectOverrideSecretRef *client.ObjectKey) (Connection, workflow.Result) {
3031
if projectOverrideSecretRef != nil {
3132
// TODO is it possible that part of connection (like orgID is still in the Operator level secret and needs to get merged?)
3233
log.Infof("Reading Atlas API credentials from the AtlasProject Secret %s", projectOverrideSecretRef)
3334
return readAtlasConnectionFromSecret(kubeClient, *projectOverrideSecretRef)
3435
}
3536

36-
// TODO check the default "Operator level" Secret
37-
// return readAtlasConnectionFromSecret(operatorName + "-connection")
38-
return Connection{}, workflow.Terminate(workflow.AtlasCredentialsNotProvided, "the API keys are not configured")
37+
log.Debug("AtlasProject connection Secret is not specified - using the Operator one")
38+
return readAtlasConnectionFromSecret(kubeClient, kube.ObjectKey(operatorPodObjectKey.Namespace, operatorPodObjectKey.Name+"-api-key"))
3939
}
4040

4141
func readAtlasConnectionFromSecret(kubeClient client.Client, secretRef client.ObjectKey) (Connection, workflow.Result) {

pkg/controller/atlascluster/atlascluster_controller.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ type AtlasClusterReconciler struct {
4646
Log *zap.SugaredLogger
4747
Scheme *runtime.Scheme
4848
AtlasDomain string
49+
OperatorPod client.ObjectKey
4950
}
5051

5152
// +kubebuilder:rbac:groups=atlas.mongodb.com,resources=atlasclusters,verbs=get;list;watch;create;update;patch;delete
@@ -70,7 +71,7 @@ func (r *AtlasClusterReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error
7071
return result.ReconcileResult(), nil
7172
}
7273

73-
connection, result := atlas.ReadConnection(log, r.Client, "TODO!", project.ConnectionSecretObjectKey())
74+
connection, result := atlas.ReadConnection(log, r.Client, r.OperatorPod, project.ConnectionSecretObjectKey())
7475
if !result.IsOk() {
7576
// merge result into ctx
7677
ctx.SetConditionFromResult(status.ClusterReadyType, result)
@@ -136,7 +137,7 @@ func (r *AtlasClusterReconciler) Delete(e event.DeleteEvent) error {
136137
return errors.New("cannot read project resource")
137138
}
138139

139-
connection, result := atlas.ReadConnection(log, r.Client, "TODO!", project.ConnectionSecretObjectKey())
140+
connection, result := atlas.ReadConnection(log, r.Client, r.OperatorPod, project.ConnectionSecretObjectKey())
140141
if !result.IsOk() {
141142
return errors.New("cannot read Atlas connection")
142143
}

pkg/controller/atlasproject/atlasproject_controller.go

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ import (
2828
"sigs.k8s.io/controller-runtime/pkg/client"
2929
"sigs.k8s.io/controller-runtime/pkg/controller"
3030
"sigs.k8s.io/controller-runtime/pkg/event"
31-
"sigs.k8s.io/controller-runtime/pkg/reconcile"
3231
"sigs.k8s.io/controller-runtime/pkg/source"
3332

3433
mdbv1 "github.com/mongodb/mongodb-atlas-kubernetes/pkg/api/v1"
@@ -47,6 +46,7 @@ type AtlasProjectReconciler struct {
4746
Log *zap.SugaredLogger
4847
Scheme *runtime.Scheme
4948
AtlasDomain string
49+
OperatorPod client.ObjectKey
5050
}
5151

5252
// +kubebuilder:rbac:groups=atlas.mongodb.com,resources=atlasprojects,verbs=get;list;watch;create;update;patch;delete
@@ -69,14 +69,10 @@ func (r *AtlasProjectReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error
6969

7070
log.Infow("-> Starting AtlasProject reconciliation", "spec", project.Spec)
7171

72-
if project.Spec.ConnectionSecret == nil {
73-
log.Error("So far the Connection Secret in AtlasProject is mandatory!")
74-
return reconcile.Result{}, nil
75-
}
7672
// This update will make sure the status is always updated in case of any errors or successful result
7773
defer statushandler.Update(ctx, r.Client, project)
7874

79-
connection, result := atlas.ReadConnection(log, r.Client, "TODO!", project.ConnectionSecretObjectKey())
75+
connection, result := atlas.ReadConnection(log, r.Client, r.OperatorPod, project.ConnectionSecretObjectKey())
8076
if !result.IsOk() {
8177
// merge result into ctx
8278
ctx.SetConditionFromResult(status.ProjectReadyType, result)
@@ -113,7 +109,7 @@ func (r *AtlasProjectReconciler) Delete(e event.DeleteEvent) error {
113109

114110
log.Infow("-> Starting AtlasProject deletion", "spec", project.Spec)
115111

116-
connection, result := atlas.ReadConnection(log, r.Client, "TODO!", project.ConnectionSecretObjectKey())
112+
connection, result := atlas.ReadConnection(log, r.Client, r.OperatorPod, project.ConnectionSecretObjectKey())
117113
if !result.IsOk() {
118114
return errors.New("cannot read Atlas connection")
119115
}

test/int/integration_suite_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import (
4747
"github.com/mongodb/mongodb-atlas-kubernetes/pkg/controller/atlasproject"
4848
"github.com/mongodb/mongodb-atlas-kubernetes/pkg/controller/watch"
4949
"github.com/mongodb/mongodb-atlas-kubernetes/pkg/util/httputil"
50+
"github.com/mongodb/mongodb-atlas-kubernetes/pkg/util/kube"
5051
// +kubebuilder:scaffold:imports
5152
)
5253

@@ -182,13 +183,15 @@ func prepareControllers() {
182183
Log: logger.Named("controllers").Named("AtlasProject").Sugar(),
183184
AtlasDomain: "https://cloud-qa.mongodb.com",
184185
ResourceWatcher: watch.NewResourceWatcher(),
186+
OperatorPod: kube.ObjectKey(namespace.Name, "atlas-operator"),
185187
}).SetupWithManager(k8sManager)
186188
Expect(err).ToNot(HaveOccurred())
187189

188190
err = (&atlascluster.AtlasClusterReconciler{
189191
Client: k8sManager.GetClient(),
190192
Log: logger.Named("controllers").Named("AtlasCluster").Sugar(),
191193
AtlasDomain: "https://cloud-qa.mongodb.com",
194+
OperatorPod: kube.ObjectKey(namespace.Name, "atlas-operator"),
192195
}).SetupWithManager(k8sManager)
193196
Expect(err).ToNot(HaveOccurred())
194197

test/int/project_test.go

Lines changed: 67 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,7 @@ var _ = Describe("AtlasProject", func() {
3434

3535
createdProject = &mdbv1.AtlasProject{}
3636

37-
connectionSecret = corev1.Secret{
38-
ObjectMeta: metav1.ObjectMeta{
39-
Name: "my-atlas-key",
40-
Namespace: namespace.Name,
41-
},
42-
StringData: map[string]string{"orgId": connection.OrgID, "publicApiKey": connection.PublicKey, "privateApiKey": connection.PrivateKey},
43-
}
37+
connectionSecret = buildConnectionSecret("my-atlas-key")
4438
By(fmt.Sprintf("Creating the Secret %s", kube.ObjectKeyFromObject(&connectionSecret)))
4539
Expect(k8sClient.Create(context.Background(), &connectionSecret)).ToNot(HaveOccurred())
4640
})
@@ -111,7 +105,6 @@ var _ = Describe("AtlasProject", func() {
111105
Eventually(testutil.WaitFor(k8sClient, createdProject, expectedCondition),
112106
20, interval).Should(BeTrue())
113107

114-
Expect(createdProject.Status.ObservedGeneration).To(Equal(createdProject.Generation))
115108
expectedConditionsMatchers := testutil.MatchConditions(
116109
status.FalseCondition(status.ProjectReadyType),
117110
status.FalseCondition(status.ReadyType),
@@ -339,20 +332,83 @@ var _ = Describe("AtlasProject", func() {
339332
})
340333
})
341334
})
335+
336+
Describe("Using the global Connection Secret", func() {
337+
It("Should Succeed", func() {
338+
globalConnectionSecret := buildConnectionSecret("atlas-operator-api-key")
339+
Expect(k8sClient.Create(context.Background(), &globalConnectionSecret)).To(Succeed())
340+
341+
// We don't specify the connection Secret per project - the global one must be used
342+
createdProject = testAtlasProject(namespace.Name, "test-project", namespace.Name, "")
343+
344+
Expect(k8sClient.Create(context.Background(), createdProject)).To(Succeed())
345+
346+
Eventually(testutil.WaitFor(k8sClient, createdProject, status.TrueCondition(status.ReadyType)),
347+
20, interval).Should(BeTrue())
348+
349+
expectedConditionsMatchers := testutil.MatchConditions(
350+
status.TrueCondition(status.ProjectReadyType),
351+
status.TrueCondition(status.IPAccessListReadyType),
352+
status.TrueCondition(status.ReadyType),
353+
)
354+
Expect(createdProject.Status.Conditions).To(ConsistOf(expectedConditionsMatchers))
355+
Expect(createdProject.Status.ObservedGeneration).To(Equal(createdProject.Generation))
356+
})
357+
It("Should Fail if the global Secret doesn't exist", func() {
358+
By("Creating without a global Secret", func() {
359+
createdProject = testAtlasProject(namespace.Name, "test-project", namespace.Name, "")
360+
361+
Expect(k8sClient.Create(context.Background(), createdProject)).ToNot(HaveOccurred())
362+
363+
Eventually(testutil.WaitFor(k8sClient, createdProject, status.FalseCondition(status.ReadyType)),
364+
20, interval).Should(BeTrue())
365+
366+
expectedConditionsMatchers := testutil.MatchConditions(
367+
status.FalseCondition(status.ProjectReadyType).WithReason(string(workflow.AtlasCredentialsNotProvided)),
368+
status.FalseCondition(status.ReadyType),
369+
)
370+
Expect(createdProject.Status.Conditions).To(ConsistOf(expectedConditionsMatchers))
371+
Expect(createdProject.ID()).To(BeEmpty())
372+
Expect(createdProject.Status.ObservedGeneration).To(Equal(createdProject.Generation))
373+
})
374+
By("Creating a global Secret - should get fixed", func() {
375+
globalConnectionSecret := buildConnectionSecret("atlas-operator-api-key")
376+
Expect(k8sClient.Create(context.Background(), &globalConnectionSecret)).To(Succeed())
377+
378+
Eventually(testutil.WaitFor(k8sClient, createdProject, status.TrueCondition(status.ReadyType)),
379+
20, interval).Should(BeTrue())
380+
})
381+
382+
})
383+
})
384+
342385
})
343386

387+
func buildConnectionSecret(name string) corev1.Secret {
388+
return corev1.Secret{
389+
ObjectMeta: metav1.ObjectMeta{
390+
Name: name,
391+
Namespace: namespace.Name,
392+
},
393+
StringData: map[string]string{"orgId": connection.OrgID, "publicApiKey": connection.PublicKey, "privateApiKey": connection.PrivateKey},
394+
}
395+
}
396+
344397
// TODO builders
345398
func testAtlasProject(namespace, name, atlasName, connectionSecretName string) *mdbv1.AtlasProject {
346-
return &mdbv1.AtlasProject{
399+
project := mdbv1.AtlasProject{
347400
ObjectMeta: metav1.ObjectMeta{
348401
Name: name,
349402
Namespace: namespace,
350403
},
351404
Spec: mdbv1.AtlasProjectSpec{
352-
Name: atlasName,
353-
ConnectionSecret: &mdbv1.ResourceRef{Name: connectionSecretName},
405+
Name: atlasName,
354406
},
355407
}
408+
if connectionSecretName != "" {
409+
project.Spec.ConnectionSecret = &mdbv1.ResourceRef{Name: connectionSecretName}
410+
}
411+
return &project
356412
}
357413

358414
func removeAtlasProject(projectID string) func() bool {

0 commit comments

Comments
 (0)