diff --git a/api/v1/mongodbcommunity_types.go b/api/v1/mongodbcommunity_types.go index f1e7f8710..117429bac 100644 --- a/api/v1/mongodbcommunity_types.go +++ b/api/v1/mongodbcommunity_types.go @@ -489,6 +489,10 @@ type MongoDBUser struct { // +optional ConnectionStringSecretNamespace string `json:"connectionStringSecretNamespace,omitempty"` + // ConnectionStringSecretAnnotations is the annotations of the secret object created by the operator which exposes the connection strings for the user. + // +optional + ConnectionStringSecretAnnotations map[string]string `json:"connectionStringSecretAnnotations,omitempty"` + // Additional options to be appended to the connection string. // These options apply only to this user and will override any existing options in the resource. // +kubebuilder:validation:Type=object @@ -789,12 +793,13 @@ func (m *MongoDBCommunity) GetAuthUsers() []authtypes.User { } users[i] = authtypes.User{ - Username: u.Name, - Database: u.DB, - Roles: roles, - ConnectionStringSecretName: u.GetConnectionStringSecretName(m.Name), - ConnectionStringSecretNamespace: u.GetConnectionStringSecretNamespace(m.Namespace), - ConnectionStringOptions: u.AdditionalConnectionStringConfig.Object, + Username: u.Name, + Database: u.DB, + Roles: roles, + ConnectionStringSecretName: u.GetConnectionStringSecretName(m.Name), + ConnectionStringSecretNamespace: u.GetConnectionStringSecretNamespace(m.Namespace), + ConnectionStringSecretAnnotations: u.ConnectionStringSecretAnnotations, + ConnectionStringOptions: u.AdditionalConnectionStringConfig.Object, } if u.DB != constants.ExternalDB { diff --git a/api/v1/zz_generated.deepcopy.go b/api/v1/zz_generated.deepcopy.go index bf58f2b77..1754b7600 100644 --- a/api/v1/zz_generated.deepcopy.go +++ b/api/v1/zz_generated.deepcopy.go @@ -314,6 +314,13 @@ func (in *MongoDBUser) DeepCopyInto(out *MongoDBUser) { *out = make([]Role, len(*in)) copy(*out, *in) } + if in.ConnectionStringSecretAnnotations != nil { + in, out := &in.ConnectionStringSecretAnnotations, &out.ConnectionStringSecretAnnotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } in.AdditionalConnectionStringConfig.DeepCopyInto(&out.AdditionalConnectionStringConfig) } diff --git a/config/crd/bases/mongodbcommunity.mongodb.com_mongodbcommunity.yaml b/config/crd/bases/mongodbcommunity.mongodb.com_mongodbcommunity.yaml index e7c3faa69..d126ba112 100644 --- a/config/crd/bases/mongodbcommunity.mongodb.com_mongodbcommunity.yaml +++ b/config/crd/bases/mongodbcommunity.mongodb.com_mongodbcommunity.yaml @@ -552,6 +552,13 @@ spec: nullable: true type: object x-kubernetes-preserve-unknown-fields: true + connectionStringSecretAnnotations: + additionalProperties: + type: string + description: ConnectionStringSecretAnnotations is the annotations + of the secret object created by the operator which exposes + the connection strings for the user. + type: object connectionStringSecretName: description: |- ConnectionStringSecretName is the name of the secret object created by the operator which exposes the connection strings for the user. diff --git a/controllers/mongodb_users.go b/controllers/mongodb_users.go index cd99734ba..b029d05cf 100644 --- a/controllers/mongodb_users.go +++ b/controllers/mongodb_users.go @@ -77,6 +77,7 @@ func (r ReplicaSetReconciler) updateConnectionStringSecrets(ctx context.Context, SetField("username", user.Username). SetField("password", pwd). SetOwnerReferences(mdb.GetOwnerReferences()). + SetAnnotations(user.ConnectionStringSecretAnnotations). Build() if err := secret.CreateOrUpdate(ctx, r.client, connectionStringSecret); err != nil { diff --git a/docs/users.md b/docs/users.md index 96a44570a..47d9fe75e 100644 --- a/docs/users.md +++ b/docs/users.md @@ -42,6 +42,7 @@ You cannot disable SCRAM authentication. | `spec.users.roles` | array of objects | Configures roles assigned to the user. | Yes | | `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 | | `spec.users.roles.role.db` | string | Database that the role applies to. | Yes | + | `spec.users.connectionStringSecretAnnotations` | object | Annotations of the secret object created by the operator which exposes the connection strings for the user. | No | ```yaml --- diff --git a/pkg/authentication/authtypes/authtypes.go b/pkg/authentication/authtypes/authtypes.go index 12d7b0cbb..c5e3b91ee 100644 --- a/pkg/authentication/authtypes/authtypes.go +++ b/pkg/authentication/authtypes/authtypes.go @@ -73,6 +73,9 @@ type User struct { // ConnectionStringSecretNamespace is the namespace of the secret object created by the operator which exposes the connection strings for the user. ConnectionStringSecretNamespace string `json:"connectionStringSecretNamespace,omitempty"` + // ConnectionStringSecretAnnotations is the annotations of the secret object created by the operator which exposes the connection strings for the user. + ConnectionStringSecretAnnotations map[string]string + // ConnectionStringOptions contains connection string options for this user // These options will be appended at the end of the connection string and // will override any existing options from the resources. diff --git a/pkg/kube/secret/secret_builder.go b/pkg/kube/secret/secret_builder.go index f5c3b4c2b..0ed71ba2a 100644 --- a/pkg/kube/secret/secret_builder.go +++ b/pkg/kube/secret/secret_builder.go @@ -11,6 +11,7 @@ type builder struct { labels map[string]string name string namespace string + annotations map[string]string ownerReferences []metav1.OwnerReference } @@ -24,6 +25,11 @@ func (b *builder) SetNamespace(namespace string) *builder { return b } +func (b *builder) SetAnnotations(annotations map[string]string) *builder { + b.annotations = annotations + return b +} + func (b *builder) SetField(key, value string) *builder { b.data[key] = []byte(value) return b @@ -72,6 +78,7 @@ func (b builder) Build() corev1.Secret { Namespace: b.namespace, OwnerReferences: b.ownerReferences, Labels: b.labels, + Annotations: b.annotations, }, Data: b.data, Type: b.dataType, diff --git a/test/e2e/mongodbtests/mongodbtests.go b/test/e2e/mongodbtests/mongodbtests.go index aad73a450..502635096 100644 --- a/test/e2e/mongodbtests/mongodbtests.go +++ b/test/e2e/mongodbtests/mongodbtests.go @@ -205,6 +205,7 @@ func ConnectionStringSecretsAreConfigured(ctx context.Context, mdb *mdbv1.MongoD assert.NoError(t, err) assertEqualOwnerReference(t, "Secret", secretNamespacedName, secret.GetOwnerReferences(), expectedOwnerReference) + containsMetadata(t, secret.ObjectMeta, map[string]string{}, user.ConnectionStringSecretAnnotations, "secret "+secretNamespacedName.Name) } } } @@ -684,6 +685,18 @@ func AddConnectionStringOptionToUser(ctx context.Context, mdb *mdbv1.MongoDBComm } } +func AddConnectionStringAnnotationsToUser(ctx context.Context, mdb *mdbv1.MongoDBCommunity, annotations map[string]string) func(t *testing.T) { + return func(t *testing.T) { + t.Logf("Adding %v to connection string annotations", annotations) + err := e2eutil.UpdateMongoDBResource(ctx, mdb, func(db *mdbv1.MongoDBCommunity) { + db.Spec.Users[0].ConnectionStringSecretAnnotations = annotations + }) + if err != nil { + t.Fatal(err) + } + } +} + func StatefulSetContainerConditionIsTrue(ctx context.Context, mdb *mdbv1.MongoDBCommunity, containerName string, condition func(c corev1.Container) bool) func(*testing.T) { return func(t *testing.T) { sts := appsv1.StatefulSet{} diff --git a/test/e2e/replica_set_connection_string_options/replica_set_connection_string_options_test.go b/test/e2e/replica_set_connection_string_options/replica_set_connection_string_options_test.go index 6358f9d3a..ea71755a5 100644 --- a/test/e2e/replica_set_connection_string_options/replica_set_connection_string_options_test.go +++ b/test/e2e/replica_set_connection_string_options/replica_set_connection_string_options_test.go @@ -108,4 +108,24 @@ func TestReplicaSetWithConnectionString(t *testing.T) { tester.ConnectivityRejected(ctx, WithURI(mongodbtests.GetSrvConnectionStringForUser(ctx, mdb, scramUser)))) }) + /** + Connection String Annotations options. + */ + t.Run("Connection String With Annotations", func(t *testing.T) { + t.Run("Resetting Connection String Options", mongodbtests.ResetConnectionStringOptions(ctx, &mdb)) + t.Run("Test Add New Connection String Annotations to Resource", mongodbtests.AddConnectionStringAnnotationsToUser(ctx, &mdb, map[string]string{"mongodbcommunity.mongodb.com/test-annotation": "test-value"})) + t.Run("Test Secrets Are Updated", mongodbtests.MongoDBReachesRunningPhase(ctx, &mdb)) + + scramUser = mdb.GetAuthUsers()[0] + t.Run("Test Basic Connectivity", tester.ConnectivitySucceeds()) + t.Run("Test SRV Connectivity", tester.ConnectivitySucceeds(WithURI(mdb.MongoSRVURI("")), WithoutTls(), WithReplicaSet(mdb.Name))) + t.Run("Test Basic Connectivity with generated connection string secret", + tester.ConnectivitySucceeds(WithURI(mongodbtests.GetConnectionStringForUser(ctx, mdb, scramUser)))) + t.Run("Test SRV Connectivity with generated connection string secret", + tester.ConnectivitySucceeds(WithURI(mongodbtests.GetSrvConnectionStringForUser(ctx, mdb, scramUser)))) + + ownerRef := mdb.GetOwnerReferences()[0] + t.Run("Test Connection String Annotations are as expected", mongodbtests.ConnectionStringSecretsAreConfigured(ctx, &mdb, ownerRef)) + }) + }