Skip to content

Commit 308ca3c

Browse files
authored
feat: add connection string secret annotations (#119)
# Summary Fixes mongodb/mongodb-kubernetes-operator#1522. This is a port of mongodb/mongodb-kubernetes-operator#1582. In this PR, I've added the ability to add custom annotations to the generated connection string secrets in MongoDB Community Operator. This is useful to handle more deployment scenarios, in particular, scenarios where the operator is not deployed cluster-wide, but to a specific namespace. In these scenarios, the `connectionStringSecretNamespace` property becomes useless because, as stated in the [Kubernetes docs](https://kubernetes.io/docs/concepts/architecture/garbage-collection/#owners-dependents), cross-namespace owner references are disallowed, thus allowing for the secrets to be immediately garbage-collected, as stated in mongodb/mongodb-kubernetes-operator#1578. For the owner references to be valid, the secrets need to be generated in the namespace of the MDBC resource. However, if the user needs the secrets to be present in other namespaces, they can use [reflector](https://github.com/emberstack/kubernetes-reflector), for instance, which allows for the secrets to be copied to other namespaces. The problem is that reflector and other similar controllers require the source secrets to be annotated with specific properties. As such, I've implemented a `connectionStringSecretAnnotations` property that allows MongoDB Community Operator users to specify per-user connection string secret annotations. ## Proof of Work I've added a unit test and an e2e test. The unit test is passing. Regarding the e2e test, it was passing in the mongodb-kubernetes-operator repository, but I couldn't figure out how to run the e2e tests in this repository. ## Checklist - [ ] Have you linked a jira ticket and/or is the ticket in the title? - No, should I create an issue in this repository for that? - [ ] Have you checked whether your jira ticket required DOCSP changes? - What does this mean? - [ ] Have you checked for release_note changes? ## Reminder (Please remove this when merging) - Please try to Approve or Reject Changes the PR, keep PRs in review as short as possible - Our Short Guide for PRs: [Link](https://docs.google.com/document/d/1T93KUtdvONq43vfTfUt8l92uo4e4SEEvFbIEKOxGr44/edit?tab=t.0) - I can't access this link - Remember the following Communication Standards - use comment prefixes for clarity: * **blocking**: Must be addressed before approval. * **follow-up**: Can be addressed in a later PR or ticket. * **q**: Clarifying question. * **nit**: Non-blocking suggestions. * **note**: Side-note, non-actionable. Example: Praise * --> no prefix is considered a question
1 parent 5259c54 commit 308ca3c

File tree

12 files changed

+122
-6
lines changed

12 files changed

+122
-6
lines changed

config/crd/bases/mongodbcommunity.mongodb.com_mongodbcommunity.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,13 @@ spec:
577577
nullable: true
578578
type: object
579579
x-kubernetes-preserve-unknown-fields: true
580+
connectionStringSecretAnnotations:
581+
additionalProperties:
582+
type: string
583+
description: ConnectionStringSecretAnnotations is the annotations
584+
of the secret object created by the operator which exposes
585+
the connection strings for the user.
586+
type: object
580587
connectionStringSecretName:
581588
description: |-
582589
ConnectionStringSecretName is the name of the secret object created by the operator which exposes the connection strings for the user.

docs/mongodbcommunity/users.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ You cannot disable SCRAM authentication.
4242
| `spec.users.roles` | array of objects | Configures roles assigned to the user. | Yes |
4343
| `spec.users.roles.role.name` | string | Name of the role. Valid values are [built-in roles](https://www.mongodb.com/docs/manual/reference/built-in-roles/#built-in-roles) and [custom roles](deploy-configure.md#define-a-custom-database-role) that you have defined. | Yes |
4444
| `spec.users.roles.role.db` | string | Database that the role applies to. | Yes |
45+
| `spec.users.connectionStringSecretAnnotations` | object | Annotations of the secret object created by the operator which exposes the connection strings for the user. | No |
46+
4547

4648
```yaml
4749
---

helm_chart/crds/mongodbcommunity.mongodb.com_mongodbcommunity.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,13 @@ spec:
577577
nullable: true
578578
type: object
579579
x-kubernetes-preserve-unknown-fields: true
580+
connectionStringSecretAnnotations:
581+
additionalProperties:
582+
type: string
583+
description: ConnectionStringSecretAnnotations is the annotations
584+
of the secret object created by the operator which exposes
585+
the connection strings for the user.
586+
type: object
580587
connectionStringSecretName:
581588
description: |-
582589
ConnectionStringSecretName is the name of the secret object created by the operator which exposes the connection strings for the user.

mongodb-community-operator/api/v1/mongodbcommunity_types.go

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,10 @@ type MongoDBUser struct {
449449
// +optional
450450
ConnectionStringSecretNamespace string `json:"connectionStringSecretNamespace,omitempty"`
451451

452+
// ConnectionStringSecretAnnotations is the annotations of the secret object created by the operator which exposes the connection strings for the user.
453+
// +optional
454+
ConnectionStringSecretAnnotations map[string]string `json:"connectionStringSecretAnnotations,omitempty"`
455+
452456
// Additional options to be appended to the connection string.
453457
// These options apply only to this user and will override any existing options in the resource.
454458
// +kubebuilder:validation:Type=object
@@ -748,12 +752,13 @@ func (m *MongoDBCommunity) GetAuthUsers() []authtypes.User {
748752
}
749753

750754
users[i] = authtypes.User{
751-
Username: u.Name,
752-
Database: u.DB,
753-
Roles: roles,
754-
ConnectionStringSecretName: u.GetConnectionStringSecretName(m.Name),
755-
ConnectionStringSecretNamespace: u.GetConnectionStringSecretNamespace(m.Namespace),
756-
ConnectionStringOptions: u.AdditionalConnectionStringConfig.Object,
755+
Username: u.Name,
756+
Database: u.DB,
757+
Roles: roles,
758+
ConnectionStringSecretName: u.GetConnectionStringSecretName(m.Name),
759+
ConnectionStringSecretNamespace: u.GetConnectionStringSecretNamespace(m.Namespace),
760+
ConnectionStringSecretAnnotations: u.ConnectionStringSecretAnnotations,
761+
ConnectionStringOptions: u.AdditionalConnectionStringConfig.Object,
757762
}
758763

759764
if u.DB != constants.ExternalDB {

mongodb-community-operator/api/v1/zz_generated.deepcopy.go

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

mongodb-community-operator/controllers/mongodb_users.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ func (r ReplicaSetReconciler) updateConnectionStringSecrets(ctx context.Context,
7474
connectionStringSecret := secret.Builder().
7575
SetName(secretName).
7676
SetNamespace(secretNamespace).
77+
SetAnnotations(user.ConnectionStringSecretAnnotations).
7778
SetField("connectionString.standard", mdb.MongoAuthUserURI(user, pwd, clusterDomain)).
7879
SetField("connectionString.standardSrv", mdb.MongoAuthUserSRVURI(user, pwd, clusterDomain)).
7980
SetField("username", user.Username).

mongodb-community-operator/controllers/replicaset_controller_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -687,6 +687,43 @@ func assertStatefulsetReady(ctx context.Context, t *testing.T, mgr manager.Manag
687687
assert.True(t, statefulset.IsReady(sts, expectedReplicas))
688688
}
689689

690+
func TestService_connectionStringSecretAnnotationsAreApplied(t *testing.T) {
691+
ctx := context.Background()
692+
secretAnnotations := map[string]string{
693+
"tests.first-annotation": "some-value",
694+
"tests.second-annotation": "other-value",
695+
}
696+
697+
mdb := newScramReplicaSet(mdbv1.MongoDBUser{
698+
Name: "testuser",
699+
PasswordSecretRef: mdbv1.SecretKeyReference{
700+
Name: "password-secret-name",
701+
},
702+
ScramCredentialsSecretName: "scram-credentials",
703+
ConnectionStringSecretAnnotations: secretAnnotations,
704+
})
705+
706+
mgr := client.NewManager(ctx, &mdb)
707+
708+
err := createUserPasswordSecret(ctx, mgr.Client, mdb, "password-secret-name", "pass")
709+
assert.NoError(t, err)
710+
711+
r := NewReconciler(mgr, "fake-mongodbRepoUrl", "fake-mongodbImage", "ubi8", AgentImage, "fake-versionUpgradeHookImage", "fake-readinessProbeImage")
712+
res, err := r.Reconcile(ctx, reconcile.Request{NamespacedName: types.NamespacedName{Namespace: mdb.Namespace, Name: mdb.Name}})
713+
assertReconciliationSuccessful(t, res, err)
714+
assertConnectionStringSecretAnnotations(ctx, t, mgr.Client, mdb, secretAnnotations)
715+
}
716+
717+
func assertConnectionStringSecretAnnotations(ctx context.Context, t *testing.T, c k8sClient.Client, mdb mdbv1.MongoDBCommunity, expectedAnnotations map[string]string) {
718+
connectionStringSecret := corev1.Secret{}
719+
scramUsers := mdb.GetAuthUsers()
720+
require.Len(t, scramUsers, 1)
721+
secretNamespacedName := types.NamespacedName{Name: scramUsers[0].ConnectionStringSecretName, Namespace: scramUsers[0].ConnectionStringSecretNamespace}
722+
err := c.Get(ctx, secretNamespacedName, &connectionStringSecret)
723+
require.NoError(t, err)
724+
assert.Subset(t, connectionStringSecret.Annotations, expectedAnnotations)
725+
}
726+
690727
func TestService_configuresPrometheusCustomPorts(t *testing.T) {
691728
ctx := context.Background()
692729
mdb := newTestReplicaSet()

mongodb-community-operator/pkg/authentication/authtypes/authtypes.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ type User struct {
7474
// ConnectionStringSecretNamespace is the namespace of the secret object created by the operator which exposes the connection strings for the user.
7575
ConnectionStringSecretNamespace string `json:"connectionStringSecretNamespace,omitempty"`
7676

77+
// ConnectionStringSecretAnnotations is the annotations of the secret object created by the operator which exposes the connection strings for the user.
78+
ConnectionStringSecretAnnotations map[string]string
79+
7780
// ConnectionStringOptions contains connection string options for this user
7881
// These options will be appended at the end of the connection string and
7982
// will override any existing options from the resources.

mongodb-community-operator/pkg/kube/secret/secret_builder.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ type builder struct {
1111
labels map[string]string
1212
name string
1313
namespace string
14+
annotations map[string]string
1415
ownerReferences []metav1.OwnerReference
1516
}
1617

@@ -24,6 +25,11 @@ func (b *builder) SetNamespace(namespace string) *builder {
2425
return b
2526
}
2627

28+
func (b *builder) SetAnnotations(annotations map[string]string) *builder {
29+
b.annotations = annotations
30+
return b
31+
}
32+
2733
func (b *builder) SetField(key, value string) *builder {
2834
b.data[key] = []byte(value)
2935
return b
@@ -73,6 +79,7 @@ func (b builder) Build() corev1.Secret {
7379
Namespace: b.namespace,
7480
OwnerReferences: b.ownerReferences,
7581
Labels: b.labels,
82+
Annotations: b.annotations,
7683
},
7784
Data: b.data,
7885
Type: b.dataType,

mongodb-community-operator/test/e2e/mongodbtests/mongodbtests.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ func ConnectionStringSecretsAreConfigured(ctx context.Context, mdb *mdbv1.MongoD
203203

204204
assert.NoError(t, err)
205205
assertEqualOwnerReference(t, "Secret", secretNamespacedName, secret.GetOwnerReferences(), expectedOwnerReference)
206+
containsMetadata(t, secret.ObjectMeta, map[string]string{}, user.ConnectionStringSecretAnnotations, "secret "+secretNamespacedName.Name)
206207
}
207208
}
208209
}
@@ -683,6 +684,18 @@ func AddConnectionStringOptionToUser(ctx context.Context, mdb *mdbv1.MongoDBComm
683684
}
684685
}
685686

687+
func AddConnectionStringAnnotationsToUser(ctx context.Context, mdb *mdbv1.MongoDBCommunity, annotations map[string]string) func(t *testing.T) {
688+
return func(t *testing.T) {
689+
t.Logf("Adding %v to connection string annotations", annotations)
690+
err := e2eutil.UpdateMongoDBResource(ctx, mdb, func(db *mdbv1.MongoDBCommunity) {
691+
db.Spec.Users[0].ConnectionStringSecretAnnotations = annotations
692+
})
693+
if err != nil {
694+
t.Fatal(err)
695+
}
696+
}
697+
}
698+
686699
func StatefulSetContainerConditionIsTrue(ctx context.Context, mdb *mdbv1.MongoDBCommunity, containerName string, condition func(c corev1.Container) bool) func(*testing.T) {
687700
return func(t *testing.T) {
688701
sts := appsv1.StatefulSet{}

mongodb-community-operator/test/e2e/replica_set_connection_string_options/replica_set_connection_string_options_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,4 +106,24 @@ func TestReplicaSetWithConnectionString(t *testing.T) {
106106
t.Run("Test SRV Connectivity with generated connection string secret",
107107
tester.ConnectivityRejected(ctx, WithURI(mongodbtests.GetSrvConnectionStringForUser(ctx, mdb, scramUser))))
108108
})
109+
110+
/**
111+
Connection String Annotations options.
112+
*/
113+
t.Run("Connection String With Annotations", func(t *testing.T) {
114+
t.Run("Resetting Connection String Options", mongodbtests.ResetConnectionStringOptions(ctx, &mdb))
115+
t.Run("Test Add New Connection String Annotations to Resource", mongodbtests.AddConnectionStringAnnotationsToUser(ctx, &mdb, map[string]string{"mongodbcommunity.mongodb.com/test-annotation": "test-value"}))
116+
t.Run("Test Secrets Are Updated", mongodbtests.MongoDBReachesRunningPhase(ctx, &mdb))
117+
118+
scramUser = mdb.GetAuthUsers()[0]
119+
t.Run("Test Basic Connectivity", tester.ConnectivitySucceeds())
120+
t.Run("Test SRV Connectivity", tester.ConnectivitySucceeds(WithURI(mdb.MongoSRVURI("")), WithoutTls(), WithReplicaSet(mdb.Name)))
121+
t.Run("Test Basic Connectivity with generated connection string secret",
122+
tester.ConnectivitySucceeds(WithURI(mongodbtests.GetConnectionStringForUser(ctx, mdb, scramUser))))
123+
t.Run("Test SRV Connectivity with generated connection string secret",
124+
tester.ConnectivitySucceeds(WithURI(mongodbtests.GetSrvConnectionStringForUser(ctx, mdb, scramUser))))
125+
126+
ownerRef := mdb.GetOwnerReferences()[0]
127+
t.Run("Test Connection String Annotations are as expected", mongodbtests.ConnectionStringSecretsAreConfigured(ctx, &mdb, ownerRef))
128+
})
109129
}

public/crds.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7569,6 +7569,13 @@ spec:
75697569
nullable: true
75707570
type: object
75717571
x-kubernetes-preserve-unknown-fields: true
7572+
connectionStringSecretAnnotations:
7573+
additionalProperties:
7574+
type: string
7575+
description: ConnectionStringSecretAnnotations is the annotations
7576+
of the secret object created by the operator which exposes
7577+
the connection strings for the user.
7578+
type: object
75727579
connectionStringSecretName:
75737580
description: |-
75747581
ConnectionStringSecretName is the name of the secret object created by the operator which exposes the connection strings for the user.

0 commit comments

Comments
 (0)