From 58f831d550aff7c6f4fef5654834ab8ef27686ad Mon Sep 17 00:00:00 2001 From: filipcirtog Date: Fri, 31 Oct 2025 22:10:43 +0100 Subject: [PATCH 1/5] feature: create secret for agent password --- .evergreen-tasks.yml | 5 + .evergreen.yml | 1 + controllers/om/automation_config.go | 68 ++++- .../operator/appdbreplicaset_controller.go | 2 +- .../operator/authentication/authentication.go | 20 +- .../authentication_mechanism.go | 5 +- .../configure_authentication_test.go | 39 ++- controllers/operator/authentication/ldap.go | 6 +- .../operator/authentication/ldap_test.go | 9 +- controllers/operator/authentication/oidc.go | 5 +- .../operator/authentication/oidc_test.go | 9 +- .../operator/authentication/scramsha.go | 12 +- .../operator/authentication/scramsha_test.go | 9 +- controllers/operator/authentication/x509.go | 5 +- .../operator/authentication/x509_test.go | 9 +- controllers/operator/common_controller.go | 6 +- .../mongodbmultireplicaset_controller.go | 2 +- .../mongodbreplicaset_controller_test.go | 2 +- .../mongodbshardedcluster_controller_test.go | 2 +- .../mongodbstandalone_controller_test.go | 2 +- ...-cluster-scram-sha-256-switch-project.yaml | 25 ++ ...ed_cluster_scram_sha_256_switch_project.py | 112 +++++++++ public/samples/community/setup | 236 ++++++++++++++++++ 23 files changed, 551 insertions(+), 40 deletions(-) create mode 100644 docker/mongodb-kubernetes-tests/tests/authentication/fixtures/sharded-cluster-scram-sha-256-switch-project.yaml create mode 100644 docker/mongodb-kubernetes-tests/tests/authentication/sharded_cluster_scram_sha_256_switch_project.py create mode 100644 public/samples/community/setup diff --git a/.evergreen-tasks.yml b/.evergreen-tasks.yml index 2d0803097..8e5259c62 100644 --- a/.evergreen-tasks.yml +++ b/.evergreen-tasks.yml @@ -585,6 +585,11 @@ tasks: commands: - func: "e2e_test" + - name: e2e_sharded_cluster_scram_sha_256_switch_project + tags: [ "patch-run" ] + commands: + - func: "e2e_test" + - name: e2e_sharded_cluster_scram_sha_1_user_connectivity tags: [ "patch-run" ] commands: diff --git a/.evergreen.yml b/.evergreen.yml index 1732b36b6..7a90bc1e1 100644 --- a/.evergreen.yml +++ b/.evergreen.yml @@ -710,6 +710,7 @@ task_groups: - e2e_replica_set_scram_x509_ic_manual_certs - e2e_sharded_cluster_scram_sha_1_upgrade - e2e_sharded_cluster_scram_sha_256_user_connectivity + - e2e_sharded_cluster_scram_sha_256_switch_project - e2e_sharded_cluster_scram_sha_1_user_connectivity - e2e_sharded_cluster_scram_x509_ic_manual_certs - e2e_sharded_cluster_external_access diff --git a/controllers/om/automation_config.go b/controllers/om/automation_config.go index bfb61c369..f0f0835cb 100644 --- a/controllers/om/automation_config.go +++ b/controllers/om/automation_config.go @@ -1,19 +1,28 @@ package om import ( + "context" "encoding/json" + "fmt" "github.com/google/go-cmp/cmp" "github.com/spf13/cast" "k8s.io/apimachinery/pkg/api/equality" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" "github.com/mongodb/mongodb-kubernetes/controllers/operator/ldap" "github.com/mongodb/mongodb-kubernetes/controllers/operator/oidc" + "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/pkg/kube/secret" "github.com/mongodb/mongodb-kubernetes/pkg/util" "github.com/mongodb/mongodb-kubernetes/pkg/util/generate" "github.com/mongodb/mongodb-kubernetes/pkg/util/maputil" ) +// The constants for the authentication secret +const agentAuthenticationSecretSuffix = "-agent-auth-secret" +const autoPwdSecretKey = "automation-agent-password" + // AutomationConfig maintains the raw map in the Deployment field // and constructs structs to make use of go's type safety // Dev notes: actually, this object is just a wrapper for the `Deployment` object which is received from Ops Manager, @@ -429,16 +438,63 @@ func (ac *AutomationConfig) EnsureKeyFileContents() error { // EnsurePassword makes sure that there is an Automation Agent password // that the agents will use to communicate with the deployments. The password // is returned, so it can be provided to the other agents -func (ac *AutomationConfig) EnsurePassword() (string, error) { - if ac.Auth.AutoPwd == "" || ac.Auth.AutoPwd == util.InvalidAutomationAgentPassword { - automationAgentBackupPassword, err := generate.KeyFileContents() +// EnsurePassword makes sure that there is an Automation Agent password +// that the agents will use to communicate with the deployments. The password +// is returned, so it can be provided to the other agents. +func (ac *AutomationConfig) EnsurePassword(k8sClient secret.GetUpdateCreator, ctx context.Context, mdbNamespacedName *types.NamespacedName) (string, error) { + secretNamespacedName := client.ObjectKey{Name: mdbNamespacedName.Name + agentAuthenticationSecretSuffix, Namespace: mdbNamespacedName.Namespace} + + var password string + + data, err := secret.ReadStringData(ctx, k8sClient, secretNamespacedName) + if err == nil { + if val, ok := data[autoPwdSecretKey]; ok && len(val) > 0 { + password = val + } + } else if secret.SecretNotExist(err) { + if ac.Auth.AutoPwd != "" && ac.Auth.AutoPwd != util.InvalidAutomationAgentPassword { + password = ac.Auth.AutoPwd + } + + err := EnsureEmptySecret(ctx, k8sClient, secretNamespacedName) if err != nil { return "", err } - ac.Auth.AutoPwd = automationAgentBackupPassword - return automationAgentBackupPassword, nil } - return ac.Auth.AutoPwd, nil + + if password == "" { + generatedPassword, genErr := generate.KeyFileContents() + if genErr != nil { + return "", genErr + } + password = generatedPassword + } + + ac.Auth.AutoPwd = password + err = secret.UpdateField(ctx, k8sClient, secretNamespacedName, autoPwdSecretKey, password) + if err != nil { + return "", fmt.Errorf("failed to update password field in shared secret %s/%s: %w", secretNamespacedName.Namespace, secretNamespacedName.Name, err) + } + + return password, nil +} + +func EnsureEmptySecret(ctx context.Context, k8sClient secret.GetUpdateCreator, secretNamespacedName types.NamespacedName) error { + dataFields := map[string]string{ + autoPwdSecretKey: "", + } + + emptySecret := secret.Builder(). + SetName(secretNamespacedName.Name). + SetNamespace(secretNamespacedName.Namespace). + SetStringMapToData(dataFields). + Build() + + if err := secret.CreateOrUpdateIfNeeded(ctx, k8sClient, emptySecret); err != nil { + return fmt.Errorf("failed to create or update empty secret %s/%s: %w", secretNamespacedName.Namespace, secretNamespacedName.Name, err) + } + + return nil } func (ac *AutomationConfig) CanEnableX509ProjectAuthentication() (bool, string) { diff --git a/controllers/operator/appdbreplicaset_controller.go b/controllers/operator/appdbreplicaset_controller.go index fe68e07c6..d2fe09d0d 100644 --- a/controllers/operator/appdbreplicaset_controller.go +++ b/controllers/operator/appdbreplicaset_controller.go @@ -1667,7 +1667,7 @@ func (r *ReconcileAppDbReplicaSet) tryConfigureMonitoringInOpsManager(ctx contex AutoPEMKeyFilePath: agentCertPath, CAFilePath: util.CAFilePathInContainer, } - err = authentication.Configure(conn, opts, false, log) + err = authentication.Configure(r.client, ctx, &types.NamespacedName{Namespace: opsManager.Namespace, Name: opsManager.Name}, conn, opts, false, log) if err != nil { log.Errorf("Could not set Automation Authentication options in Ops/Cloud Manager for the Application Database. "+ "Application Database is always configured with authentication enabled, but this will not be "+ diff --git a/controllers/operator/authentication/authentication.go b/controllers/operator/authentication/authentication.go index c2e36735b..6858414a4 100644 --- a/controllers/operator/authentication/authentication.go +++ b/controllers/operator/authentication/authentication.go @@ -1,13 +1,17 @@ package authentication import ( + "context" + "go.uber.org/zap" "golang.org/x/xerrors" + "k8s.io/apimachinery/pkg/types" mdbv1 "github.com/mongodb/mongodb-kubernetes/api/v1/mdb" "github.com/mongodb/mongodb-kubernetes/controllers/om" "github.com/mongodb/mongodb-kubernetes/controllers/operator/ldap" "github.com/mongodb/mongodb-kubernetes/controllers/operator/oidc" + kubernetesClient "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/pkg/kube/client" "github.com/mongodb/mongodb-kubernetes/pkg/util" ) @@ -82,7 +86,7 @@ type UserOptions struct { // Configure will configure all the specified authentication Mechanisms. We need to ensure we wait for // the agents to reach ready state after each operation as prematurely updating the automation config can cause the agents to get stuck. -func Configure(conn om.Connection, opts Options, isRecovering bool, log *zap.SugaredLogger) error { +func Configure(client kubernetesClient.Client, ctx context.Context, mdbNamespacedName *types.NamespacedName, conn om.Connection, opts Options, isRecovering bool, log *zap.SugaredLogger) error { log.Infow("ensuring correct deployment mechanisms", "ProcessNames", opts.ProcessNames, "Mechanisms", opts.Mechanisms) // In case we're recovering, we can push all changes at once, because the mechanism is triggered after 20min by default. @@ -113,7 +117,7 @@ func Configure(conn om.Connection, opts Options, isRecovering bool, log *zap.Sug // once we have made sure that the deployment authentication mechanism array contains the desired auth mechanism // we can then configure the agent authentication. - if err := enableAgentAuthentication(conn, opts, log); err != nil { + if err := enableAgentAuthentication(client, ctx, mdbNamespacedName, conn, opts, log); err != nil { return xerrors.Errorf("error enabling agent authentication: %w", err) } if err := waitForReadyStateIfNeeded(); err != nil { @@ -151,7 +155,7 @@ func Configure(conn om.Connection, opts Options, isRecovering bool, log *zap.Sug // Disable disables all authentication mechanisms, and waits for the agents to reach goal state. It is still required to provide // automation agent username, password and keyfile contents to ensure a valid Automation Config. -func Disable(conn om.Connection, opts Options, deleteUsers bool, log *zap.SugaredLogger) error { +func Disable(client kubernetesClient.Client, ctx context.Context, mdbNamespacedName *types.NamespacedName, conn om.Connection, opts Options, deleteUsers bool, log *zap.SugaredLogger) error { ac, err := conn.ReadAutomationConfig() if err != nil { return xerrors.Errorf("error reading automation config: %w", err) @@ -181,7 +185,7 @@ func Disable(conn om.Connection, opts Options, deleteUsers bool, log *zap.Sugare if err := ac.EnsureKeyFileContents(); err != nil { return xerrors.Errorf("error ensuring keyfile contents: %w", err) } - if _, err := ac.EnsurePassword(); err != nil { + if _, err := ac.EnsurePassword(client, ctx, mdbNamespacedName); err != nil { return xerrors.Errorf("error ensuring agent password: %w", err) } @@ -258,7 +262,7 @@ func removeUnsupportedAgentMechanisms(conn om.Connection, opts Options, log *zap // enableAgentAuthentication determines which agent authentication mechanism should be configured // and enables it in Ops Manager -func enableAgentAuthentication(conn om.Connection, opts Options, log *zap.SugaredLogger) error { +func enableAgentAuthentication(client kubernetesClient.Client, ctx context.Context, mdbNamespacedName *types.NamespacedName, conn om.Connection, opts Options, log *zap.SugaredLogger) error { ac, err := conn.ReadAutomationConfig() if err != nil { return xerrors.Errorf("error reading automation config: %w", err) @@ -267,7 +271,7 @@ func enableAgentAuthentication(conn om.Connection, opts Options, log *zap.Sugare // we then configure the agent authentication for that type mechanism := convertToMechanismOrPanic(opts.AgentMechanism, ac) - if err := ensureAgentAuthenticationIsConfigured(conn, opts, ac, mechanism, log); err != nil { + if err := ensureAgentAuthenticationIsConfigured(client, ctx, mdbNamespacedName, conn, opts, ac, mechanism, log); err != nil { return xerrors.Errorf("error ensuring agent authentication is configured: %w", err) } @@ -365,14 +369,14 @@ func addOrRemoveAgentClientCertificate(conn om.Connection, opts Options, log *za } // ensureAgentAuthenticationIsConfigured will configure the agent authentication settings based on the desiredAgentAuthMechanism -func ensureAgentAuthenticationIsConfigured(conn om.Connection, opts Options, ac *om.AutomationConfig, mechanism Mechanism, log *zap.SugaredLogger) error { +func ensureAgentAuthenticationIsConfigured(client kubernetesClient.Client, ctx context.Context, mdbNamespacedName *types.NamespacedName, conn om.Connection, opts Options, ac *om.AutomationConfig, mechanism Mechanism, log *zap.SugaredLogger) error { if mechanism.IsAgentAuthenticationConfigured(ac, opts) { log.Infof("Agent authentication mechanism %s is already configured", mechanism.GetName()) return nil } log.Infof("Enabling %s agent authentication", mechanism.GetName()) - return mechanism.EnableAgentAuthentication(conn, opts, log) + return mechanism.EnableAgentAuthentication(client, ctx, mdbNamespacedName, conn, opts, log) } // ensureDeploymentMechanisms configures the given AutomationConfig to allow deployments to diff --git a/controllers/operator/authentication/authentication_mechanism.go b/controllers/operator/authentication/authentication_mechanism.go index 90e6877e8..ba65e1324 100644 --- a/controllers/operator/authentication/authentication_mechanism.go +++ b/controllers/operator/authentication/authentication_mechanism.go @@ -1,19 +1,22 @@ package authentication import ( + "context" "slices" "strings" "go.uber.org/zap" "golang.org/x/xerrors" + "k8s.io/apimachinery/pkg/types" "github.com/mongodb/mongodb-kubernetes/controllers/om" + kubernetesClient "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/pkg/kube/client" "github.com/mongodb/mongodb-kubernetes/pkg/util" ) // Mechanism is an interface that needs to be implemented for any Ops Manager authentication mechanism type Mechanism interface { - EnableAgentAuthentication(conn om.Connection, opts Options, log *zap.SugaredLogger) error + EnableAgentAuthentication(client kubernetesClient.Client, ctx context.Context, mdbNamespacedName *types.NamespacedName, conn om.Connection, opts Options, log *zap.SugaredLogger) error DisableAgentAuthentication(conn om.Connection, log *zap.SugaredLogger) error EnableDeploymentAuthentication(conn om.Connection, opts Options, log *zap.SugaredLogger) error DisableDeploymentAuthentication(conn om.Connection, log *zap.SugaredLogger) error diff --git a/controllers/operator/authentication/configure_authentication_test.go b/controllers/operator/authentication/configure_authentication_test.go index 8e8ae95df..5a6dcc79e 100644 --- a/controllers/operator/authentication/configure_authentication_test.go +++ b/controllers/operator/authentication/configure_authentication_test.go @@ -1,13 +1,16 @@ package authentication import ( + "context" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap" + "k8s.io/apimachinery/pkg/types" "github.com/mongodb/mongodb-kubernetes/controllers/om" + "github.com/mongodb/mongodb-kubernetes/controllers/operator/mock" "github.com/mongodb/mongodb-kubernetes/pkg/util" ) @@ -17,6 +20,10 @@ func init() { } func TestConfigureScramSha256(t *testing.T) { + ctx := context.Background() + kubeClient, _ := mock.NewDefaultFakeClient() + mdbNamespacedName := &types.NamespacedName{Namespace: "test", Name: "test"} + dep := om.NewDeployment() conn := om.NewMockedOmConnection(dep) @@ -27,7 +34,7 @@ func TestConfigureScramSha256(t *testing.T) { AgentMechanism: "SCRAM", } - if err := Configure(conn, opts, false, zap.S()); err != nil { + if err := Configure(kubeClient, ctx, mdbNamespacedName, conn, opts, false, zap.S()); err != nil { t.Fatal(err) } @@ -41,6 +48,10 @@ func TestConfigureScramSha256(t *testing.T) { } func TestConfigureX509(t *testing.T) { + ctx := context.Background() + kubeClient, _ := mock.NewDefaultFakeClient() + mdbNamespacedName := &types.NamespacedName{Namespace: "test", Name: "test"} + dep := om.NewDeployment() conn := om.NewMockedOmConnection(dep) @@ -55,7 +66,7 @@ func TestConfigureX509(t *testing.T) { }, } - if err := Configure(conn, opts, false, zap.S()); err != nil { + if err := Configure(kubeClient, ctx, mdbNamespacedName, conn, opts, false, zap.S()); err != nil { t.Fatal(err) } @@ -69,6 +80,10 @@ func TestConfigureX509(t *testing.T) { } func TestConfigureScramSha1(t *testing.T) { + ctx := context.Background() + kubeClient, _ := mock.NewDefaultFakeClient() + mdbNamespacedName := &types.NamespacedName{Namespace: "test", Name: "test"} + dep := om.NewDeployment() conn := om.NewMockedOmConnection(dep) @@ -79,7 +94,7 @@ func TestConfigureScramSha1(t *testing.T) { AgentMechanism: "SCRAM-SHA-1", } - if err := Configure(conn, opts, false, zap.S()); err != nil { + if err := Configure(kubeClient, ctx, mdbNamespacedName, conn, opts, false, zap.S()); err != nil { t.Fatal(err) } @@ -91,6 +106,10 @@ func TestConfigureScramSha1(t *testing.T) { } func TestConfigureMultipleAuthenticationMechanisms(t *testing.T) { + ctx := context.Background() + kubeClient, _ := mock.NewDefaultFakeClient() + mdbNamespacedName := &types.NamespacedName{Namespace: "test", Name: "test"} + dep := om.NewDeployment() conn := om.NewMockedOmConnection(dep) @@ -104,7 +123,7 @@ func TestConfigureMultipleAuthenticationMechanisms(t *testing.T) { }, } - if err := Configure(conn, opts, false, zap.S()); err != nil { + if err := Configure(kubeClient, ctx, mdbNamespacedName, conn, opts, false, zap.S()); err != nil { t.Fatal(err) } @@ -124,6 +143,10 @@ func TestConfigureMultipleAuthenticationMechanisms(t *testing.T) { } func TestDisableAuthentication(t *testing.T) { + ctx := context.Background() + kubeClient, _ := mock.NewDefaultFakeClient() + mdbNamespacedName := &types.NamespacedName{Namespace: "test", Name: "test"} + dep := om.NewDeployment() conn := om.NewMockedOmConnection(dep) @@ -133,7 +156,7 @@ func TestDisableAuthentication(t *testing.T) { return nil }, zap.S()) - if err := Disable(conn, Options{}, true, zap.S()); err != nil { + if err := Disable(kubeClient, ctx, mdbNamespacedName, conn, Options{}, true, zap.S()); err != nil { t.Fatal(err) } @@ -212,7 +235,11 @@ func assertDeploymentMechanismsConfigured(t *testing.T, authMechanism Mechanism, } func assertAgentAuthenticationDisabled(t *testing.T, authMechanism Mechanism, conn om.Connection, opts Options) { - err := authMechanism.EnableAgentAuthentication(conn, opts, zap.S()) + ctx := context.Background() + kubeClient, _ := mock.NewDefaultFakeClient() + mdbNamespacedName := &types.NamespacedName{Namespace: "test", Name: "test"} + + err := authMechanism.EnableAgentAuthentication(kubeClient, ctx, mdbNamespacedName, conn, opts, zap.S()) require.NoError(t, err) ac, err := conn.ReadAutomationConfig() diff --git a/controllers/operator/authentication/ldap.go b/controllers/operator/authentication/ldap.go index 1cd8a0b6c..9d7a95445 100644 --- a/controllers/operator/authentication/ldap.go +++ b/controllers/operator/authentication/ldap.go @@ -1,10 +1,14 @@ package authentication import ( + "context" + "go.uber.org/zap" + "k8s.io/apimachinery/pkg/types" "github.com/mongodb/mongodb-kubernetes/controllers/om" "github.com/mongodb/mongodb-kubernetes/controllers/operator/ldap" + kubernetesClient "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/pkg/kube/client" "github.com/mongodb/mongodb-kubernetes/pkg/util" "github.com/mongodb/mongodb-kubernetes/pkg/util/stringutil" ) @@ -15,7 +19,7 @@ func (l *ldapAuthMechanism) GetName() MechanismName { return LDAPPlain } -func (l *ldapAuthMechanism) EnableAgentAuthentication(conn om.Connection, opts Options, log *zap.SugaredLogger) error { +func (l *ldapAuthMechanism) EnableAgentAuthentication(client kubernetesClient.Client, ctx context.Context, mdbNamespacedName *types.NamespacedName, conn om.Connection, opts Options, log *zap.SugaredLogger) error { log.Info("Configuring LDAP authentication") err := conn.ReadUpdateAutomationConfig(func(ac *om.AutomationConfig) error { if err := ac.EnsureKeyFileContents(); err != nil { diff --git a/controllers/operator/authentication/ldap_test.go b/controllers/operator/authentication/ldap_test.go index 0b619e3df..54d96336e 100644 --- a/controllers/operator/authentication/ldap_test.go +++ b/controllers/operator/authentication/ldap_test.go @@ -1,14 +1,17 @@ package authentication import ( + "context" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap" + "k8s.io/apimachinery/pkg/types" "github.com/mongodb/mongodb-kubernetes/controllers/om" "github.com/mongodb/mongodb-kubernetes/controllers/operator/ldap" + "github.com/mongodb/mongodb-kubernetes/controllers/operator/mock" ) var ldapPlainMechanism = getMechanismByName(LDAPPlain) @@ -45,6 +48,10 @@ func TestLdapDeploymentMechanism(t *testing.T) { } func TestLdapEnableAgentAuthentication(t *testing.T) { + ctx := context.Background() + kubeClient, _ := mock.NewDefaultFakeClient() + mdbNamespacedName := &types.NamespacedName{Namespace: "test", Name: "test"} + conn := om.NewMockedOmConnection(om.NewDeployment()) opts := Options{ AgentMechanism: "LDAP", @@ -55,7 +62,7 @@ func TestLdapEnableAgentAuthentication(t *testing.T) { AutoPwd: "LDAPPassword.", } - err := ldapPlainMechanism.EnableAgentAuthentication(conn, opts, zap.S()) + err := ldapPlainMechanism.EnableAgentAuthentication(kubeClient, ctx, mdbNamespacedName, conn, opts, zap.S()) require.NoError(t, err) ac, err := conn.ReadAutomationConfig() diff --git a/controllers/operator/authentication/oidc.go b/controllers/operator/authentication/oidc.go index 7e9bf4c2c..dec32e27f 100644 --- a/controllers/operator/authentication/oidc.go +++ b/controllers/operator/authentication/oidc.go @@ -1,16 +1,19 @@ package authentication import ( + "context" "fmt" "slices" "strings" "go.uber.org/zap" "golang.org/x/xerrors" + "k8s.io/apimachinery/pkg/types" mdbv1 "github.com/mongodb/mongodb-kubernetes/api/v1/mdb" "github.com/mongodb/mongodb-kubernetes/controllers/om" "github.com/mongodb/mongodb-kubernetes/controllers/operator/oidc" + kubernetesClient "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/pkg/kube/client" "github.com/mongodb/mongodb-kubernetes/pkg/util/stringutil" ) @@ -20,7 +23,7 @@ func (o *oidcAuthMechanism) GetName() MechanismName { return MongoDBOIDC } -func (o *oidcAuthMechanism) EnableAgentAuthentication(_ om.Connection, _ Options, _ *zap.SugaredLogger) error { +func (o *oidcAuthMechanism) EnableAgentAuthentication(_ kubernetesClient.Client, _ context.Context, _ *types.NamespacedName, _ om.Connection, _ Options, _ *zap.SugaredLogger) error { return xerrors.Errorf("OIDC agent authentication is not supported") } diff --git a/controllers/operator/authentication/oidc_test.go b/controllers/operator/authentication/oidc_test.go index 6460db803..5191eaf0e 100644 --- a/controllers/operator/authentication/oidc_test.go +++ b/controllers/operator/authentication/oidc_test.go @@ -1,14 +1,17 @@ package authentication import ( + "context" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap" + "k8s.io/apimachinery/pkg/types" "k8s.io/utils/ptr" "github.com/mongodb/mongodb-kubernetes/controllers/om" + "github.com/mongodb/mongodb-kubernetes/controllers/operator/mock" "github.com/mongodb/mongodb-kubernetes/controllers/operator/oidc" ) @@ -77,6 +80,10 @@ func TestOIDC_EnableDeploymentAuthentication(t *testing.T) { } func TestOIDC_EnableAgentAuthentication(t *testing.T) { + ctx := context.Background() + kubeClient, _ := mock.NewDefaultFakeClient() + mdbNamespacedName := &types.NamespacedName{Namespace: "test", Name: "test"} + conn := om.NewMockedOmConnection(om.NewDeployment()) opts := Options{ Mechanisms: []string{string(MongoDBOIDC)}, @@ -88,7 +95,7 @@ func TestOIDC_EnableAgentAuthentication(t *testing.T) { configured := mongoDBOIDCMechanism.IsAgentAuthenticationConfigured(ac, opts) assert.False(t, configured) - err = mongoDBOIDCMechanism.EnableAgentAuthentication(conn, opts, zap.S()) + err = mongoDBOIDCMechanism.EnableAgentAuthentication(kubeClient, ctx, mdbNamespacedName, conn, opts, zap.S()) require.Error(t, err) err = mongoDBOIDCMechanism.DisableAgentAuthentication(conn, zap.S()) diff --git a/controllers/operator/authentication/scramsha.go b/controllers/operator/authentication/scramsha.go index ee186b869..b03b8b94d 100644 --- a/controllers/operator/authentication/scramsha.go +++ b/controllers/operator/authentication/scramsha.go @@ -1,9 +1,13 @@ package authentication import ( + "context" + "go.uber.org/zap" + "k8s.io/apimachinery/pkg/types" "github.com/mongodb/mongodb-kubernetes/controllers/om" + kubernetesClient "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/pkg/kube/client" "github.com/mongodb/mongodb-kubernetes/pkg/util" "github.com/mongodb/mongodb-kubernetes/pkg/util/stringutil" ) @@ -18,9 +22,9 @@ func (s *automationConfigScramSha) GetName() MechanismName { return s.MechanismName } -func (s *automationConfigScramSha) EnableAgentAuthentication(conn om.Connection, opts Options, log *zap.SugaredLogger) error { +func (s *automationConfigScramSha) EnableAgentAuthentication(client kubernetesClient.Client, ctx context.Context, mdbNamespacedName *types.NamespacedName, conn om.Connection, opts Options, log *zap.SugaredLogger) error { return conn.ReadUpdateAutomationConfig(func(ac *om.AutomationConfig) error { - if err := configureScramAgentUsers(ac, opts); err != nil { + if err := configureScramAgentUsers(client, ctx, mdbNamespacedName, ac, opts); err != nil { return err } if err := ac.EnsureKeyFileContents(); err != nil { @@ -91,8 +95,8 @@ func (s *automationConfigScramSha) IsDeploymentAuthenticationEnabled(ac *om.Auto } // configureScramAgentUsers makes sure that the given automation config always has the correct SCRAM-SHA users -func configureScramAgentUsers(ac *om.AutomationConfig, authOpts Options) error { - agentPassword, err := ac.EnsurePassword() +func configureScramAgentUsers(client kubernetesClient.Client, ctx context.Context, mdbNamespacedName *types.NamespacedName, ac *om.AutomationConfig, authOpts Options) error { + agentPassword, err := ac.EnsurePassword(client, ctx, mdbNamespacedName) if err != nil { return err } diff --git a/controllers/operator/authentication/scramsha_test.go b/controllers/operator/authentication/scramsha_test.go index 1c97e9943..f998f7153 100644 --- a/controllers/operator/authentication/scramsha_test.go +++ b/controllers/operator/authentication/scramsha_test.go @@ -1,13 +1,16 @@ package authentication import ( + "context" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap" + "k8s.io/apimachinery/pkg/types" "github.com/mongodb/mongodb-kubernetes/controllers/om" + "github.com/mongodb/mongodb-kubernetes/controllers/operator/mock" "github.com/mongodb/mongodb-kubernetes/pkg/util" ) @@ -34,6 +37,10 @@ func TestAgentsAuthentication(t *testing.T) { } for testName, testConfig := range tests { t.Run(testName, func(t *testing.T) { + ctx := context.Background() + kubeClient, _ := mock.NewDefaultFakeClient() + mdbNamespacedName := &types.NamespacedName{Namespace: "test", Name: "test"} + conn := om.NewMockedOmConnection(om.NewDeployment()) s := testConfig.mechanism @@ -43,7 +50,7 @@ func TestAgentsAuthentication(t *testing.T) { CAFilePath: util.CAFilePathInContainer, } - err := s.EnableAgentAuthentication(conn, opts, zap.S()) + err := s.EnableAgentAuthentication(kubeClient, ctx, mdbNamespacedName, conn, opts, zap.S()) require.NoError(t, err) err = s.EnableDeploymentAuthentication(conn, opts, zap.S()) diff --git a/controllers/operator/authentication/x509.go b/controllers/operator/authentication/x509.go index e863b7780..02e429ce5 100644 --- a/controllers/operator/authentication/x509.go +++ b/controllers/operator/authentication/x509.go @@ -1,11 +1,14 @@ package authentication import ( + "context" "regexp" "go.uber.org/zap" + "k8s.io/apimachinery/pkg/types" "github.com/mongodb/mongodb-kubernetes/controllers/om" + kubernetesClient "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/pkg/kube/client" "github.com/mongodb/mongodb-kubernetes/pkg/util" "github.com/mongodb/mongodb-kubernetes/pkg/util/stringutil" ) @@ -16,7 +19,7 @@ func (x *connectionX509) GetName() MechanismName { return MongoDBX509 } -func (x *connectionX509) EnableAgentAuthentication(conn om.Connection, opts Options, log *zap.SugaredLogger) error { +func (x *connectionX509) EnableAgentAuthentication(client kubernetesClient.Client, ctx context.Context, mdbNamespacedName *types.NamespacedName, conn om.Connection, opts Options, log *zap.SugaredLogger) error { log.Info("Configuring x509 authentication") err := conn.ReadUpdateAutomationConfig(func(ac *om.AutomationConfig) error { if err := ac.EnsureKeyFileContents(); err != nil { diff --git a/controllers/operator/authentication/x509_test.go b/controllers/operator/authentication/x509_test.go index f342b6702..b7022f57e 100644 --- a/controllers/operator/authentication/x509_test.go +++ b/controllers/operator/authentication/x509_test.go @@ -1,20 +1,27 @@ package authentication import ( + "context" "fmt" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap" + "k8s.io/apimachinery/pkg/types" "github.com/mongodb/mongodb-kubernetes/controllers/om" + "github.com/mongodb/mongodb-kubernetes/controllers/operator/mock" "github.com/mongodb/mongodb-kubernetes/pkg/util" ) var mongoDBX509Mechanism = getMechanismByName(MongoDBX509) func TestX509EnableAgentAuthentication(t *testing.T) { + ctx := context.Background() + kubeClient, _ := mock.NewDefaultFakeClient() + mdbNamespacedName := &types.NamespacedName{Namespace: "test", Name: "test"} + conn := om.NewMockedOmConnection(om.NewDeployment()) options := Options{ @@ -25,7 +32,7 @@ func TestX509EnableAgentAuthentication(t *testing.T) { }, AuthoritativeSet: true, } - if err := mongoDBX509Mechanism.EnableAgentAuthentication(conn, options, zap.S()); err != nil { + if err := mongoDBX509Mechanism.EnableAgentAuthentication(kubeClient, ctx, mdbNamespacedName, conn, options, zap.S()); err != nil { t.Fatal(err) } diff --git a/controllers/operator/common_controller.go b/controllers/operator/common_controller.go index e76cf4e81..72babde36 100644 --- a/controllers/operator/common_controller.go +++ b/controllers/operator/common_controller.go @@ -514,7 +514,7 @@ func (r *ReconcileCommonController) updateOmAuthentication(ctx context.Context, authOpts.UserOptions = userOpts } - if err := authentication.Configure(conn, authOpts, isRecovering, log); err != nil { + if err := authentication.Configure(r.client, ctx, &types.NamespacedName{Namespace: ar.GetNamespace(), Name: ar.GetName()}, conn, authOpts, isRecovering, log); err != nil { return workflow.Failed(err), false } } else if wantToEnableAuthentication { @@ -533,7 +533,7 @@ func (r *ReconcileCommonController) updateOmAuthentication(ctx context.Context, } authOpts.UserOptions = userOpts - if err := authentication.Disable(conn, authOpts, false, log); err != nil { + if err := authentication.Disable(r.client, ctx, &types.NamespacedName{Namespace: ar.GetNamespace(), Name: ar.GetName()}, conn, authOpts, false, log); err != nil { return workflow.Failed(err), false } } @@ -597,7 +597,7 @@ func (r *ReconcileCommonController) clearProjectAuthenticationSettings(ctx conte UserOptions: userOpts, } - return authentication.Disable(conn, disableOpts, true, log) + return authentication.Disable(r.client, ctx, &types.NamespacedName{Namespace: mdb.Namespace, Name: mdb.Name}, conn, disableOpts, true, log) } // ensureX509SecretAndCheckTLSType checks if the secrets containing the certificates are present and whether the certificate are of kubernetes.io/tls type. diff --git a/controllers/operator/mongodbmultireplicaset_controller.go b/controllers/operator/mongodbmultireplicaset_controller.go index 386ce8a6b..119d5cf12 100644 --- a/controllers/operator/mongodbmultireplicaset_controller.go +++ b/controllers/operator/mongodbmultireplicaset_controller.go @@ -1236,7 +1236,7 @@ func (r *ReconcileMongoDbMultiReplicaSet) cleanOpsManagerState(ctx context.Conte ProcessNames: processNames, } - if err := authentication.Disable(conn, opts, true, log); err != nil { + if err := authentication.Disable(r.client, ctx, &types.NamespacedName{Namespace: mrs.GetNamespace(), Name: mrs.GetName()}, conn, opts, true, log); err != nil { return err } log.Infof("Removed deployment %s from Ops Manager at %s", mrs.Name, conn.BaseURL()) diff --git a/controllers/operator/mongodbreplicaset_controller_test.go b/controllers/operator/mongodbreplicaset_controller_test.go index 803de4f3b..6c39fdeab 100644 --- a/controllers/operator/mongodbreplicaset_controller_test.go +++ b/controllers/operator/mongodbreplicaset_controller_test.go @@ -61,7 +61,7 @@ func TestCreateReplicaSet(t *testing.T) { assert.Len(t, mock.GetMapForObject(client, &corev1.Service{}), 1) assert.Len(t, mock.GetMapForObject(client, &appsv1.StatefulSet{}), 1) - assert.Len(t, mock.GetMapForObject(client, &corev1.Secret{}), 2) + assert.Len(t, mock.GetMapForObject(client, &corev1.Secret{}), 3) sts, err := client.GetStatefulSet(ctx, rs.ObjectKey()) assert.NoError(t, err) diff --git a/controllers/operator/mongodbshardedcluster_controller_test.go b/controllers/operator/mongodbshardedcluster_controller_test.go index d7b2aefe4..15cc1c459 100644 --- a/controllers/operator/mongodbshardedcluster_controller_test.go +++ b/controllers/operator/mongodbshardedcluster_controller_test.go @@ -81,7 +81,7 @@ func TestReconcileCreateShardedCluster(t *testing.T) { require.NoError(t, err) checkReconcileSuccessful(ctx, t, reconciler, sc, c) - assert.Len(t, mock.GetMapForObject(c, &corev1.Secret{}), 2) + assert.Len(t, mock.GetMapForObject(c, &corev1.Secret{}), 3) assert.Len(t, mock.GetMapForObject(c, &corev1.Service{}), 3) assert.Len(t, mock.GetMapForObject(c, &appsv1.StatefulSet{}), 4) assert.Equal(t, getStsReplicas(ctx, c, kube.ObjectKey(sc.Namespace, sc.ConfigRsName()), t), int32(sc.Spec.ConfigServerCount)) diff --git a/controllers/operator/mongodbstandalone_controller_test.go b/controllers/operator/mongodbstandalone_controller_test.go index 69bdfbb87..a9c6bd8f4 100644 --- a/controllers/operator/mongodbstandalone_controller_test.go +++ b/controllers/operator/mongodbstandalone_controller_test.go @@ -71,7 +71,7 @@ func TestOnAddStandalone(t *testing.T) { assert.Len(t, mock.GetMapForObject(kubeClient, &corev1.Service{}), 1) assert.Len(t, mock.GetMapForObject(kubeClient, &appsv1.StatefulSet{}), 1) assert.Equal(t, *mock.GetMapForObject(kubeClient, &appsv1.StatefulSet{})[st.ObjectKey()].(*appsv1.StatefulSet).Spec.Replicas, int32(1)) - assert.Len(t, mock.GetMapForObject(kubeClient, &corev1.Secret{}), 2) + assert.Len(t, mock.GetMapForObject(kubeClient, &corev1.Secret{}), 3) omConn.(*om.MockedOmConnection).CheckDeployment(t, createDeploymentFromStandalone(st), "auth", "tls") omConn.(*om.MockedOmConnection).CheckNumberOfUpdateRequests(t, 1) diff --git a/docker/mongodb-kubernetes-tests/tests/authentication/fixtures/sharded-cluster-scram-sha-256-switch-project.yaml b/docker/mongodb-kubernetes-tests/tests/authentication/fixtures/sharded-cluster-scram-sha-256-switch-project.yaml new file mode 100644 index 000000000..1bf83d70d --- /dev/null +++ b/docker/mongodb-kubernetes-tests/tests/authentication/fixtures/sharded-cluster-scram-sha-256-switch-project.yaml @@ -0,0 +1,25 @@ +--- +apiVersion: mongodb.com/v1 +kind: MongoDB +metadata: + name: sharded-cluster-scram-sha-256-switch-project +spec: + shardCount: 1 + mongodsPerShardCount: 1 + mongosCount: 1 + configServerCount: 1 + version: 4.4.0 + type: ShardedCluster + + opsManager: + configMapRef: + name: my-project + credentials: my-credentials + + persistent: true + security: + authentication: + enabled: true + modes: ["SCRAM"] + + diff --git a/docker/mongodb-kubernetes-tests/tests/authentication/sharded_cluster_scram_sha_256_switch_project.py b/docker/mongodb-kubernetes-tests/tests/authentication/sharded_cluster_scram_sha_256_switch_project.py new file mode 100644 index 000000000..8086e8d50 --- /dev/null +++ b/docker/mongodb-kubernetes-tests/tests/authentication/sharded_cluster_scram_sha_256_switch_project.py @@ -0,0 +1,112 @@ +import pytest + +from kubetester.kubetester import KubernetesTester +from kubetester.kubetester import fixture as load_fixture +from kubetester.mongodb import MongoDB +from kubetester.mongotester import ShardedClusterTester +from kubetester.phase import Phase + +from kubetester import ( + read_configmap, + random_k8s_name, + create_or_update_configmap, +) + +CONFIG_MAP_KEYS = { + "BASE_URL": "baseUrl", + "PROJECT_NAME": "projectName", + "ORG_ID": "orgId", +} + +MDB_RESOURCE_NAME = "sharded-cluster-scram-sha-256-switch-project" +MDB_FIXTURE_NAME = MDB_RESOURCE_NAME + + +@pytest.fixture(scope="module") +def project_name_prefix(namespace: str) -> str: + """ + Generates a random Kubernetes project name prefix based on the namespace. + + Ensures test isolation in a multi-namespace test environment. + """ + return random_k8s_name(f"{namespace}-project-") + + +@pytest.fixture(scope="module") +def sharded_cluster(namespace: str) -> MongoDB: + """ + Fixture to initialize the MongoDB resource for the sharded cluster. + + Dynamically updates the resource Ops Manager reference based on the test context. + """ + resource = MongoDB.from_yaml(load_fixture(f"{MDB_FIXTURE_NAME}.yaml"), namespace=namespace) + return resource + + + +@pytest.mark.e2e_sharded_cluster_scram_sha_256_switch_project +class TestShardedClusterCreationAndProjectSwitch(KubernetesTester): + """ + E2E test suite for sharded cluster creation and switching Ops Manager project reference. + """ + + def test_create_sharded_cluster(self, custom_mdb_version: str, sharded_cluster: MongoDB): + """ + Test cluster creation ensuring resources are applied correctly and cluster reaches Running phase. + """ + sharded_cluster.set_version(custom_mdb_version) + sharded_cluster.update() + sharded_cluster.assert_reaches_phase(Phase.Running, timeout=600) + + def test_sharded_cluster_connectivity(self): + """ + Verify connectivity to the original sharded cluster. + """ + ShardedClusterTester(MDB_RESOURCE_NAME, 1).assert_connectivity() + + def test_ops_manager_state_correctly_updated_in_initial_cluster(self, sharded_cluster: MongoDB): + """ + Ensure Ops Manager state is correctly updated in the original cluster. + """ + tester = sharded_cluster.get_automation_config_tester() + tester.assert_authentication_mechanism_enabled("SCRAM-SHA-256") + tester.assert_authentication_enabled() + + def test_switch_sharded_cluster_project( + self, custom_mdb_version: str, sharded_cluster: MongoDB, namespace: str, project_name_prefix: str + ): + """ + Modify the sharded cluster to switch its Ops Manager reference to a new project and verify lifecycle. + """ + original_configmap = read_configmap(namespace=namespace, name="my-project") + new_project_name = f"{project_name_prefix}-second" + new_project_configmap = create_or_update_configmap( + namespace=namespace, + name=new_project_name, + data={ + CONFIG_MAP_KEYS["BASE_URL"]: original_configmap[CONFIG_MAP_KEYS["BASE_URL"]], + CONFIG_MAP_KEYS["PROJECT_NAME"]: new_project_name, + CONFIG_MAP_KEYS["ORG_ID"]: original_configmap[CONFIG_MAP_KEYS["ORG_ID"]], + }, + ) + + sharded_cluster.load() + sharded_cluster["spec"]["opsManager"]["configMapRef"]["name"] = new_project_configmap + sharded_cluster.set_version(custom_mdb_version) + sharded_cluster.update() + + sharded_cluster.assert_reaches_phase(Phase.Running, timeout=600) + + def test_moved_sharded_cluster_connectivity(self): + """ + Verify connectivity to the sharded cluster after project switch. + """ + ShardedClusterTester(MDB_RESOURCE_NAME, 1).assert_connectivity() + + def test_ops_manager_state_correctly_updated_in_moved_cluster(self, sharded_cluster: MongoDB): + """ + Ensure Ops Manager state is correctly updated in the moved cluster after the project switch. + """ + tester = sharded_cluster.get_automation_config_tester() + tester.assert_authentication_mechanism_enabled("SCRAM-SHA-256") + tester.assert_authentication_enabled() diff --git a/public/samples/community/setup b/public/samples/community/setup new file mode 100644 index 000000000..10301541e --- /dev/null +++ b/public/samples/community/setup @@ -0,0 +1,236 @@ +cat < Date: Mon, 3 Nov 2025 09:28:56 +0100 Subject: [PATCH 2/5] improvments --- .evergreen-tasks.yml | 5 + .evergreen.yml | 1 + ...lica-set-scram-sha-256-switch-project.yaml | 19 ++ ...eplica_set_scram_sha_256_switch_project.py | 113 +++++++++ public/samples/community/setup | 236 ------------------ 5 files changed, 138 insertions(+), 236 deletions(-) create mode 100644 docker/mongodb-kubernetes-tests/tests/authentication/fixtures/replica-set-scram-sha-256-switch-project.yaml create mode 100644 docker/mongodb-kubernetes-tests/tests/authentication/replica_set_scram_sha_256_switch_project.py delete mode 100644 public/samples/community/setup diff --git a/.evergreen-tasks.yml b/.evergreen-tasks.yml index 8e5259c62..72a8a8763 100644 --- a/.evergreen-tasks.yml +++ b/.evergreen-tasks.yml @@ -590,6 +590,11 @@ tasks: commands: - func: "e2e_test" + - name: e2e_replica_set_scram_sha_256_switch_project + tags: [ "patch-run" ] + commands: + - func: "e2e_test" + - name: e2e_sharded_cluster_scram_sha_1_user_connectivity tags: [ "patch-run" ] commands: diff --git a/.evergreen.yml b/.evergreen.yml index 7a90bc1e1..8fe1c06e6 100644 --- a/.evergreen.yml +++ b/.evergreen.yml @@ -711,6 +711,7 @@ task_groups: - e2e_sharded_cluster_scram_sha_1_upgrade - e2e_sharded_cluster_scram_sha_256_user_connectivity - e2e_sharded_cluster_scram_sha_256_switch_project + - e2e_replica_set_scram_sha_256_switch_project - e2e_sharded_cluster_scram_sha_1_user_connectivity - e2e_sharded_cluster_scram_x509_ic_manual_certs - e2e_sharded_cluster_external_access diff --git a/docker/mongodb-kubernetes-tests/tests/authentication/fixtures/replica-set-scram-sha-256-switch-project.yaml b/docker/mongodb-kubernetes-tests/tests/authentication/fixtures/replica-set-scram-sha-256-switch-project.yaml new file mode 100644 index 000000000..be90421fc --- /dev/null +++ b/docker/mongodb-kubernetes-tests/tests/authentication/fixtures/replica-set-scram-sha-256-switch-project.yaml @@ -0,0 +1,19 @@ +--- +apiVersion: mongodb.com/v1 +kind: MongoDB +metadata: + name: replica-set-scram-sha-256-switch-project +spec: + members: 3 + version: 4.4.0 + type: ReplicaSet + opsManager: + configMapRef: + name: my-project + credentials: my-credentials + logLevel: DEBUG + persistent: false + security: + authentication: + enabled: true + modes: ["SCRAM"] diff --git a/docker/mongodb-kubernetes-tests/tests/authentication/replica_set_scram_sha_256_switch_project.py b/docker/mongodb-kubernetes-tests/tests/authentication/replica_set_scram_sha_256_switch_project.py new file mode 100644 index 000000000..ff05148bb --- /dev/null +++ b/docker/mongodb-kubernetes-tests/tests/authentication/replica_set_scram_sha_256_switch_project.py @@ -0,0 +1,113 @@ +import pytest + +from kubetester.kubetester import KubernetesTester +from kubetester.kubetester import fixture as load_fixture +from kubetester.mongodb import MongoDB +from kubetester.mongotester import ReplicaSetTester +from kubetester.phase import Phase + +from kubetester import ( + random_k8s_name, + create_or_update_configmap, + read_configmap +) + +# Constants +MDB_RESOURCE_NAME = "replica-set-scram-sha-256-switch-project" +MDB_FIXTURE_NAME = MDB_RESOURCE_NAME + +CONFIG_MAP_KEYS = { + "BASE_URL": "baseUrl", + "PROJECT_NAME": "projectName", + "ORG_ID": "orgId", +} + + + +@pytest.fixture(scope="module") +def replica_set(namespace: str) -> MongoDB: + """ + Fixture to initialize the MongoDB resource for the replica set. + + Dynamically updates the resource configuration based on the test context. + """ + resource = MongoDB.from_yaml(load_fixture(f"{MDB_FIXTURE_NAME}.yaml"), namespace=namespace) + return resource + + +@pytest.fixture(scope="module") +def project_name_prefix(namespace: str) -> str: + """ + Generates a random Kubernetes project name prefix based on the namespace. + + Ensures test isolation in a multi-namespace test environment. + """ + return random_k8s_name(f"{namespace}-project-") + + +@pytest.mark.e2e_replica_set_scram_sha_256_switch_project +class TestReplicaSetCreationAndProjectSwitch(KubernetesTester): + """ + E2E test suite for replica set creation and user connectivity with SCRAM-SHA-256 authentication. + """ + + def test_create_replica_set(self, custom_mdb_version: str, replica_set: MongoDB): + """ + Test replica set creation ensuring resources are applied correctly and set reaches Running phase. + """ + replica_set.set_version(custom_mdb_version) + replica_set.update() + replica_set.assert_reaches_phase(Phase.Running, timeout=600) + + def test_replica_set_connectivity(self): + """ + Verify connectivity to the original replica set. + """ + ReplicaSetTester(MDB_RESOURCE_NAME, 3).assert_connectivity() # Adjust node count as appropriate + + def test_ops_manager_state_correctly_updated_in_initial_replica_set(self, replica_set: MongoDB): + """ + Ensure Ops Manager state is correctly updated in the original replica set. + """ + tester = replica_set.get_automation_config_tester() + tester.assert_authentication_mechanism_enabled("SCRAM-SHA-256") + tester.assert_authentication_enabled() + + def test_switch_replica_set_project( + self, custom_mdb_version: str, replica_set: MongoDB, namespace: str, project_name_prefix: str + ): + """ + Modify the replica set to switch its Ops Manager reference to a new project and verify lifecycle. + """ + original_configmap = read_configmap(namespace=namespace, name="my-project") + new_project_name = f"{project_name_prefix}-second" + new_project_configmap = create_or_update_configmap( + namespace=namespace, + name=new_project_name, + data={ + CONFIG_MAP_KEYS["BASE_URL"]: original_configmap[CONFIG_MAP_KEYS["BASE_URL"]], + CONFIG_MAP_KEYS["PROJECT_NAME"]: new_project_name, + CONFIG_MAP_KEYS["ORG_ID"]: original_configmap[CONFIG_MAP_KEYS["ORG_ID"]], + }, + ) + + replica_set.load() + replica_set["spec"]["opsManager"]["configMapRef"]["name"] = new_project_configmap + replica_set.set_version(custom_mdb_version) + replica_set.update() + + replica_set.assert_reaches_phase(Phase.Running, timeout=600) + + def test_moved_replica_set_connectivity(self): + """ + Verify connectivity to the replica set after switching projects. + """ + ReplicaSetTester(MDB_RESOURCE_NAME, 3).assert_connectivity() # Adjust node count as appropriate + + def test_ops_manager_state_correctly_updated_in_moved_replica_set(self, replica_set: MongoDB): + """ + Ensure Ops Manager state is correctly updated in the moved replica set after the project switch. + """ + tester = replica_set.get_automation_config_tester() + tester.assert_authentication_mechanism_enabled("SCRAM-SHA-256") + tester.assert_authentication_enabled() diff --git a/public/samples/community/setup b/public/samples/community/setup deleted file mode 100644 index 10301541e..000000000 --- a/public/samples/community/setup +++ /dev/null @@ -1,236 +0,0 @@ -cat < Date: Tue, 4 Nov 2025 15:02:24 +0100 Subject: [PATCH 3/5] tests + lint --- .evergreen-tasks.yml | 40 +++- .evergreen.yml | 8 +- controllers/om/automation_config.go | 15 +- ...eplica-set-scram-sha-1-switch-project.yaml | 23 ++ ...lica-set-scram-sha-256-switch-project.yaml | 0 .../replica-set-x509-switch-project.yaml | 23 ++ ...ed-cluster-scram-sha-1-switch-project.yaml | 26 +++ ...-cluster-scram-sha-256-switch-project.yaml | 0 .../sharded-cluster-x509-switch-project.yaml | 27 +++ .../replica_set_scram_sha_1_switch_project.py | 110 +++++++++ ...eplica_set_scram_sha_256_switch_project.py | 219 +++++++++--------- .../replica_set_x509_switch_project.py | 114 +++++++++ ...rded_cluster_scram_sha_1_switch_project.py | 113 +++++++++ ...ed_cluster_scram_sha_256_switch_project.py | 219 +++++++++--------- .../sharded_cluster_x509_switch_project.py | 124 ++++++++++ 15 files changed, 821 insertions(+), 240 deletions(-) create mode 100644 docker/mongodb-kubernetes-tests/tests/authentication/fixtures/switch-project/replica-set-scram-sha-1-switch-project.yaml rename docker/mongodb-kubernetes-tests/tests/authentication/fixtures/{ => switch-project}/replica-set-scram-sha-256-switch-project.yaml (100%) create mode 100644 docker/mongodb-kubernetes-tests/tests/authentication/fixtures/switch-project/replica-set-x509-switch-project.yaml create mode 100644 docker/mongodb-kubernetes-tests/tests/authentication/fixtures/switch-project/sharded-cluster-scram-sha-1-switch-project.yaml rename docker/mongodb-kubernetes-tests/tests/authentication/fixtures/{ => switch-project}/sharded-cluster-scram-sha-256-switch-project.yaml (100%) create mode 100644 docker/mongodb-kubernetes-tests/tests/authentication/fixtures/switch-project/sharded-cluster-x509-switch-project.yaml create mode 100644 docker/mongodb-kubernetes-tests/tests/authentication/replica_set_scram_sha_1_switch_project.py create mode 100644 docker/mongodb-kubernetes-tests/tests/authentication/replica_set_x509_switch_project.py create mode 100644 docker/mongodb-kubernetes-tests/tests/authentication/sharded_cluster_scram_sha_1_switch_project.py create mode 100644 docker/mongodb-kubernetes-tests/tests/authentication/sharded_cluster_x509_switch_project.py diff --git a/.evergreen-tasks.yml b/.evergreen-tasks.yml index 72a8a8763..4a8de81e5 100644 --- a/.evergreen-tasks.yml +++ b/.evergreen-tasks.yml @@ -585,16 +585,6 @@ tasks: commands: - func: "e2e_test" - - name: e2e_sharded_cluster_scram_sha_256_switch_project - tags: [ "patch-run" ] - commands: - - func: "e2e_test" - - - name: e2e_replica_set_scram_sha_256_switch_project - tags: [ "patch-run" ] - commands: - - func: "e2e_test" - - name: e2e_sharded_cluster_scram_sha_1_user_connectivity tags: [ "patch-run" ] commands: @@ -700,6 +690,36 @@ tasks: commands: - func: "e2e_test" + - name: e2e_sharded_cluster_scram_sha_256_switch_project + tags: [ "patch-run" ] + commands: + - func: "e2e_test" + + - name: e2e_sharded_cluster_scram_sha_1_switch_project + tags: [ "patch-run" ] + commands: + - func: "e2e_test" + + - name: e2e_sharded_cluster_x509_switch_project + tags: [ "patch-run" ] + commands: + - func: "e2e_test" + + - name: e2e_replica_set_scram_sha_256_switch_project + tags: [ "patch-run" ] + commands: + - func: "e2e_test" + + - name: e2e_replica_set_scram_sha_1_switch_project + tags: [ "patch-run" ] + commands: + - func: "e2e_test" + + - name: e2e_replica_set_x509_switch_project + tags: [ "patch-run" ] + commands: + - func: "e2e_test" + # TODO: not used in any variant - name: e2e_replica_set_scram_x509_internal_cluster tags: [ "patch-run" ] diff --git a/.evergreen.yml b/.evergreen.yml index 8fe1c06e6..6205479f4 100644 --- a/.evergreen.yml +++ b/.evergreen.yml @@ -710,11 +710,15 @@ task_groups: - e2e_replica_set_scram_x509_ic_manual_certs - e2e_sharded_cluster_scram_sha_1_upgrade - e2e_sharded_cluster_scram_sha_256_user_connectivity - - e2e_sharded_cluster_scram_sha_256_switch_project - - e2e_replica_set_scram_sha_256_switch_project - e2e_sharded_cluster_scram_sha_1_user_connectivity - e2e_sharded_cluster_scram_x509_ic_manual_certs - e2e_sharded_cluster_external_access + - e2e_sharded_cluster_scram_sha_256_switch_project + - e2e_sharded_cluster_scram_sha_1_switch_project + - e2e_sharded_cluster_x509_switch_project + - e2e_replica_set_scram_sha_256_switch_project + - e2e_replica_set_scram_sha_1_switch_project + - e2e_replica_set_x509_switch_project # e2e_auth_transitions_task_group - e2e_replica_set_scram_sha_and_x509 - e2e_replica_set_x509_to_scram_transition diff --git a/controllers/om/automation_config.go b/controllers/om/automation_config.go index f0f0835cb..9582cd77d 100644 --- a/controllers/om/automation_config.go +++ b/controllers/om/automation_config.go @@ -20,8 +20,9 @@ import ( ) // The constants for the authentication secret -const agentAuthenticationSecretSuffix = "-agent-auth-secret" -const autoPwdSecretKey = "automation-agent-password" +const ( + autoPwdSecretKey = "automation-agent-password" +) // AutomationConfig maintains the raw map in the Deployment field // and constructs structs to make use of go's type safety @@ -435,6 +436,12 @@ func (ac *AutomationConfig) EnsureKeyFileContents() error { return nil } +// AuthSecretName for a given mdbName (`mdbName`) returns the name of +// the secret associated with it. +func AuthSecretName(mdbName string) string { + return fmt.Sprintf("%s-agent-auth-secre", mdbName) +} + // EnsurePassword makes sure that there is an Automation Agent password // that the agents will use to communicate with the deployments. The password // is returned, so it can be provided to the other agents @@ -442,8 +449,8 @@ func (ac *AutomationConfig) EnsureKeyFileContents() error { // that the agents will use to communicate with the deployments. The password // is returned, so it can be provided to the other agents. func (ac *AutomationConfig) EnsurePassword(k8sClient secret.GetUpdateCreator, ctx context.Context, mdbNamespacedName *types.NamespacedName) (string, error) { - secretNamespacedName := client.ObjectKey{Name: mdbNamespacedName.Name + agentAuthenticationSecretSuffix, Namespace: mdbNamespacedName.Namespace} - + secretName := AuthSecretName(mdbNamespacedName.Name) + secretNamespacedName := client.ObjectKey{Name: secretName, Namespace: mdbNamespacedName.Namespace} var password string data, err := secret.ReadStringData(ctx, k8sClient, secretNamespacedName) diff --git a/docker/mongodb-kubernetes-tests/tests/authentication/fixtures/switch-project/replica-set-scram-sha-1-switch-project.yaml b/docker/mongodb-kubernetes-tests/tests/authentication/fixtures/switch-project/replica-set-scram-sha-1-switch-project.yaml new file mode 100644 index 000000000..c4cf66ea5 --- /dev/null +++ b/docker/mongodb-kubernetes-tests/tests/authentication/fixtures/switch-project/replica-set-scram-sha-1-switch-project.yaml @@ -0,0 +1,23 @@ +--- +apiVersion: mongodb.com/v1 +kind: MongoDB +metadata: + name: replica-set-scram-sha-1-switch-project +spec: + members: 3 + version: 5.0.5 + type: ReplicaSet + opsManager: + configMapRef: + name: my-project + credentials: my-credentials + logLevel: DEBUG + persistent: false + security: + authentication: + agents: + # This may look weird, but without it we'll get this from OpsManager: + # Cannot configure SCRAM-SHA-1 without using MONGODB-CR in te Agent Mode","reason":"Cannot configure SCRAM-SHA-1 without using MONGODB-CR in te Agent Mode + mode: MONGODB-CR + enabled: true + modes: ["SCRAM-SHA-1", "MONGODB-CR"] diff --git a/docker/mongodb-kubernetes-tests/tests/authentication/fixtures/replica-set-scram-sha-256-switch-project.yaml b/docker/mongodb-kubernetes-tests/tests/authentication/fixtures/switch-project/replica-set-scram-sha-256-switch-project.yaml similarity index 100% rename from docker/mongodb-kubernetes-tests/tests/authentication/fixtures/replica-set-scram-sha-256-switch-project.yaml rename to docker/mongodb-kubernetes-tests/tests/authentication/fixtures/switch-project/replica-set-scram-sha-256-switch-project.yaml diff --git a/docker/mongodb-kubernetes-tests/tests/authentication/fixtures/switch-project/replica-set-x509-switch-project.yaml b/docker/mongodb-kubernetes-tests/tests/authentication/fixtures/switch-project/replica-set-x509-switch-project.yaml new file mode 100644 index 000000000..ee79c38f9 --- /dev/null +++ b/docker/mongodb-kubernetes-tests/tests/authentication/fixtures/switch-project/replica-set-x509-switch-project.yaml @@ -0,0 +1,23 @@ +--- +apiVersion: mongodb.com/v1 +kind: MongoDB +metadata: + name: replica-set-x509-switch-project +spec: + members: 3 + version: 4.4.0-ent + type: ReplicaSet + opsManager: + configMapRef: + name: my-project + credentials: my-credentials + logLevel: DEBUG + persistent: false + security: + tls: + enabled: true + authentication: + agents: + mode: X509 + enabled: true + modes: ["X509"] diff --git a/docker/mongodb-kubernetes-tests/tests/authentication/fixtures/switch-project/sharded-cluster-scram-sha-1-switch-project.yaml b/docker/mongodb-kubernetes-tests/tests/authentication/fixtures/switch-project/sharded-cluster-scram-sha-1-switch-project.yaml new file mode 100644 index 000000000..837ff206e --- /dev/null +++ b/docker/mongodb-kubernetes-tests/tests/authentication/fixtures/switch-project/sharded-cluster-scram-sha-1-switch-project.yaml @@ -0,0 +1,26 @@ +--- +apiVersion: mongodb.com/v1 +kind: MongoDB +metadata: + name: sharded-cluster-scram-sha-1-switch-project +spec: + shardCount: 1 + type: ShardedCluster + mongodsPerShardCount: 1 + mongosCount: 1 + configServerCount: 1 + version: 5.0.5 + opsManager: + configMapRef: + name: my-project + credentials: my-credentials + logLevel: DEBUG + persistent: true + security: + authentication: + agents: + # This may look weird, but without it we'll get this from OpsManager: + # Cannot configure SCRAM-SHA-1 without using MONGODB-CR in te Agent Mode","reason":"Cannot configure SCRAM-SHA-1 without using MONGODB-CR in te Agent Mode + mode: MONGODB-CR + enabled: true + modes: ["SCRAM-SHA-1", "MONGODB-CR"] diff --git a/docker/mongodb-kubernetes-tests/tests/authentication/fixtures/sharded-cluster-scram-sha-256-switch-project.yaml b/docker/mongodb-kubernetes-tests/tests/authentication/fixtures/switch-project/sharded-cluster-scram-sha-256-switch-project.yaml similarity index 100% rename from docker/mongodb-kubernetes-tests/tests/authentication/fixtures/sharded-cluster-scram-sha-256-switch-project.yaml rename to docker/mongodb-kubernetes-tests/tests/authentication/fixtures/switch-project/sharded-cluster-scram-sha-256-switch-project.yaml diff --git a/docker/mongodb-kubernetes-tests/tests/authentication/fixtures/switch-project/sharded-cluster-x509-switch-project.yaml b/docker/mongodb-kubernetes-tests/tests/authentication/fixtures/switch-project/sharded-cluster-x509-switch-project.yaml new file mode 100644 index 000000000..2169679fc --- /dev/null +++ b/docker/mongodb-kubernetes-tests/tests/authentication/fixtures/switch-project/sharded-cluster-x509-switch-project.yaml @@ -0,0 +1,27 @@ +--- +apiVersion: mongodb.com/v1 +kind: MongoDB +metadata: + name: sharded-cluster-x509-switch-project +spec: + shardCount: 1 + mongodsPerShardCount: 1 + mongosCount: 1 + configServerCount: 1 + version: 4.4.0-ent + type: ShardedCluster + + opsManager: + configMapRef: + name: my-project + credentials: my-credentials + + persistent: true + security: + tls: + enabled: true + authentication: + agents: + mode: X509 + enabled: true + modes: ["X509"] diff --git a/docker/mongodb-kubernetes-tests/tests/authentication/replica_set_scram_sha_1_switch_project.py b/docker/mongodb-kubernetes-tests/tests/authentication/replica_set_scram_sha_1_switch_project.py new file mode 100644 index 000000000..5cf39477e --- /dev/null +++ b/docker/mongodb-kubernetes-tests/tests/authentication/replica_set_scram_sha_1_switch_project.py @@ -0,0 +1,110 @@ +import pytest +from kubetester import create_or_update_configmap, random_k8s_name, read_configmap +from kubetester.kubetester import KubernetesTester +from kubetester.kubetester import fixture as load_fixture +from kubetester.mongodb import MongoDB +from kubetester.mongotester import ReplicaSetTester +from kubetester.phase import Phase + +# Constants +MDB_RESOURCE_NAME = "replica-set-scram-sha-1-switch-project" +MDB_FIXTURE_NAME = MDB_RESOURCE_NAME + +CONFIG_MAP_KEYS = { + "BASE_URL": "baseUrl", + "PROJECT_NAME": "projectName", + "ORG_ID": "orgId", +} + + +@pytest.fixture(scope="module") +def replica_set(namespace: str) -> MongoDB: + """ + Fixture to initialize the MongoDB resource for the replica set. + + Dynamically updates the resource configuration based on the test context. + """ + resource = MongoDB.from_yaml(load_fixture(f"switch-project/{MDB_FIXTURE_NAME}.yaml"), namespace=namespace) + return resource + + +@pytest.fixture(scope="module") +def project_name_prefix(namespace: str) -> str: + """ + Generates a random Kubernetes project name prefix based on the namespace. + + Ensures test isolation in a multi-namespace test environment. + """ + return random_k8s_name(f"{namespace}-project-") + + +@pytest.mark.e2e_replica_set_scram_sha_1_switch_project +class TestReplicaSetCreationAndProjectSwitch(KubernetesTester): + """ + E2E test suite for replica set creation, user connectivity with SCRAM-SHA-1 authentication and switching Ops Manager project reference. + """ + + def test_create_replica_set(self, custom_mdb_version: str, replica_set: MongoDB): + """ + Test replica set creation ensuring resources are applied correctly and set reaches Running phase. + """ + replica_set.set_version(custom_mdb_version) + replica_set.update() + replica_set.assert_reaches_phase(Phase.Running, timeout=600) + + def test_replica_set_connectivity(self): + """ + Verify connectivity to the original replica set. + """ + ReplicaSetTester(MDB_RESOURCE_NAME, 3).assert_connectivity() + + def test_ops_manager_state_correctly_updated_in_initial_replica_set(self, replica_set: MongoDB): + """ + Ensure Ops Manager state is correctly updated in the original replica set. + """ + tester = replica_set.get_automation_config_tester() + tester.assert_authentication_mechanism_enabled("MONGODB-CR") + tester.assert_authoritative_set(True) + tester.assert_authentication_enabled(2) + tester.assert_expected_users(0) + + def test_switch_replica_set_project( + self, custom_mdb_version: str, replica_set: MongoDB, namespace: str, project_name_prefix: str + ): + """ + Modify the replica set to switch its Ops Manager reference to a new project and verify lifecycle. + """ + original_configmap = read_configmap(namespace=namespace, name="my-project") + new_project_name = f"{project_name_prefix}-second" + new_project_configmap = create_or_update_configmap( + namespace=namespace, + name=new_project_name, + data={ + CONFIG_MAP_KEYS["BASE_URL"]: original_configmap[CONFIG_MAP_KEYS["BASE_URL"]], + CONFIG_MAP_KEYS["PROJECT_NAME"]: new_project_name, + CONFIG_MAP_KEYS["ORG_ID"]: original_configmap[CONFIG_MAP_KEYS["ORG_ID"]], + }, + ) + + replica_set.load() + replica_set["spec"]["opsManager"]["configMapRef"]["name"] = new_project_configmap + replica_set.set_version(custom_mdb_version) + replica_set.update() + + replica_set.assert_reaches_phase(Phase.Running, timeout=600) + + def test_moved_replica_set_connectivity(self): + """ + Verify connectivity to the replica set after switching projects. + """ + ReplicaSetTester(MDB_RESOURCE_NAME, 3).assert_connectivity() + + def test_ops_manager_state_correctly_updated_in_moved_replica_set(self, replica_set: MongoDB): + """ + Ensure Ops Manager state is correctly updated in the moved replica set after the project switch. + """ + tester = replica_set.get_automation_config_tester() + tester.assert_authentication_mechanism_enabled("MONGODB-CR") + tester.assert_authoritative_set(True) + tester.assert_authentication_enabled(2) + tester.assert_expected_users(0) diff --git a/docker/mongodb-kubernetes-tests/tests/authentication/replica_set_scram_sha_256_switch_project.py b/docker/mongodb-kubernetes-tests/tests/authentication/replica_set_scram_sha_256_switch_project.py index ff05148bb..8269df337 100644 --- a/docker/mongodb-kubernetes-tests/tests/authentication/replica_set_scram_sha_256_switch_project.py +++ b/docker/mongodb-kubernetes-tests/tests/authentication/replica_set_scram_sha_256_switch_project.py @@ -1,113 +1,106 @@ -import pytest - -from kubetester.kubetester import KubernetesTester -from kubetester.kubetester import fixture as load_fixture -from kubetester.mongodb import MongoDB -from kubetester.mongotester import ReplicaSetTester -from kubetester.phase import Phase - -from kubetester import ( - random_k8s_name, - create_or_update_configmap, - read_configmap -) - -# Constants -MDB_RESOURCE_NAME = "replica-set-scram-sha-256-switch-project" -MDB_FIXTURE_NAME = MDB_RESOURCE_NAME - -CONFIG_MAP_KEYS = { - "BASE_URL": "baseUrl", - "PROJECT_NAME": "projectName", - "ORG_ID": "orgId", -} - - - -@pytest.fixture(scope="module") -def replica_set(namespace: str) -> MongoDB: - """ - Fixture to initialize the MongoDB resource for the replica set. - - Dynamically updates the resource configuration based on the test context. - """ - resource = MongoDB.from_yaml(load_fixture(f"{MDB_FIXTURE_NAME}.yaml"), namespace=namespace) - return resource - - -@pytest.fixture(scope="module") -def project_name_prefix(namespace: str) -> str: - """ - Generates a random Kubernetes project name prefix based on the namespace. - - Ensures test isolation in a multi-namespace test environment. - """ - return random_k8s_name(f"{namespace}-project-") - - -@pytest.mark.e2e_replica_set_scram_sha_256_switch_project -class TestReplicaSetCreationAndProjectSwitch(KubernetesTester): - """ - E2E test suite for replica set creation and user connectivity with SCRAM-SHA-256 authentication. - """ - - def test_create_replica_set(self, custom_mdb_version: str, replica_set: MongoDB): - """ - Test replica set creation ensuring resources are applied correctly and set reaches Running phase. - """ - replica_set.set_version(custom_mdb_version) - replica_set.update() - replica_set.assert_reaches_phase(Phase.Running, timeout=600) - - def test_replica_set_connectivity(self): - """ - Verify connectivity to the original replica set. - """ - ReplicaSetTester(MDB_RESOURCE_NAME, 3).assert_connectivity() # Adjust node count as appropriate - - def test_ops_manager_state_correctly_updated_in_initial_replica_set(self, replica_set: MongoDB): - """ - Ensure Ops Manager state is correctly updated in the original replica set. - """ - tester = replica_set.get_automation_config_tester() - tester.assert_authentication_mechanism_enabled("SCRAM-SHA-256") - tester.assert_authentication_enabled() - - def test_switch_replica_set_project( - self, custom_mdb_version: str, replica_set: MongoDB, namespace: str, project_name_prefix: str - ): - """ - Modify the replica set to switch its Ops Manager reference to a new project and verify lifecycle. - """ - original_configmap = read_configmap(namespace=namespace, name="my-project") - new_project_name = f"{project_name_prefix}-second" - new_project_configmap = create_or_update_configmap( - namespace=namespace, - name=new_project_name, - data={ - CONFIG_MAP_KEYS["BASE_URL"]: original_configmap[CONFIG_MAP_KEYS["BASE_URL"]], - CONFIG_MAP_KEYS["PROJECT_NAME"]: new_project_name, - CONFIG_MAP_KEYS["ORG_ID"]: original_configmap[CONFIG_MAP_KEYS["ORG_ID"]], - }, - ) - - replica_set.load() - replica_set["spec"]["opsManager"]["configMapRef"]["name"] = new_project_configmap - replica_set.set_version(custom_mdb_version) - replica_set.update() - - replica_set.assert_reaches_phase(Phase.Running, timeout=600) - - def test_moved_replica_set_connectivity(self): - """ - Verify connectivity to the replica set after switching projects. - """ - ReplicaSetTester(MDB_RESOURCE_NAME, 3).assert_connectivity() # Adjust node count as appropriate - - def test_ops_manager_state_correctly_updated_in_moved_replica_set(self, replica_set: MongoDB): - """ - Ensure Ops Manager state is correctly updated in the moved replica set after the project switch. - """ - tester = replica_set.get_automation_config_tester() - tester.assert_authentication_mechanism_enabled("SCRAM-SHA-256") - tester.assert_authentication_enabled() +import pytest +from kubetester import create_or_update_configmap, random_k8s_name, read_configmap +from kubetester.kubetester import KubernetesTester +from kubetester.kubetester import fixture as load_fixture +from kubetester.mongodb import MongoDB +from kubetester.mongotester import ReplicaSetTester +from kubetester.phase import Phase + +# Constants +MDB_RESOURCE_NAME = "replica-set-scram-sha-256-switch-project" +MDB_FIXTURE_NAME = MDB_RESOURCE_NAME + +CONFIG_MAP_KEYS = { + "BASE_URL": "baseUrl", + "PROJECT_NAME": "projectName", + "ORG_ID": "orgId", +} + + +@pytest.fixture(scope="module") +def replica_set(namespace: str) -> MongoDB: + """ + Fixture to initialize the MongoDB resource for the replica set. + + Dynamically updates the resource configuration based on the test context. + """ + resource = MongoDB.from_yaml(load_fixture(f"switch-project/{MDB_FIXTURE_NAME}.yaml"), namespace=namespace) + return resource + + +@pytest.fixture(scope="module") +def project_name_prefix(namespace: str) -> str: + """ + Generates a random Kubernetes project name prefix based on the namespace. + + Ensures test isolation in a multi-namespace test environment. + """ + return random_k8s_name(f"{namespace}-project-") + + +@pytest.mark.e2e_replica_set_scram_sha_256_switch_project +class TestReplicaSetCreationAndProjectSwitch(KubernetesTester): + """ + E2E test suite for replica set creation, user connectivity with SCRAM-SHA-256 authentication and switching Ops Manager project reference. + """ + + def test_create_replica_set(self, custom_mdb_version: str, replica_set: MongoDB): + """ + Test replica set creation ensuring resources are applied correctly and set reaches Running phase. + """ + replica_set.set_version(custom_mdb_version) + replica_set.update() + replica_set.assert_reaches_phase(Phase.Running, timeout=600) + + def test_replica_set_connectivity(self): + """ + Verify connectivity to the original replica set. + """ + ReplicaSetTester(MDB_RESOURCE_NAME, 3).assert_connectivity() + + def test_ops_manager_state_correctly_updated_in_initial_replica_set(self, replica_set: MongoDB): + """ + Ensure Ops Manager state is correctly updated in the original replica set. + """ + tester = replica_set.get_automation_config_tester() + tester.assert_authentication_mechanism_enabled("SCRAM-SHA-256") + tester.assert_authentication_enabled() + + def test_switch_replica_set_project( + self, custom_mdb_version: str, replica_set: MongoDB, namespace: str, project_name_prefix: str + ): + """ + Modify the replica set to switch its Ops Manager reference to a new project and verify lifecycle. + """ + original_configmap = read_configmap(namespace=namespace, name="my-project") + new_project_name = f"{project_name_prefix}-second" + new_project_configmap = create_or_update_configmap( + namespace=namespace, + name=new_project_name, + data={ + CONFIG_MAP_KEYS["BASE_URL"]: original_configmap[CONFIG_MAP_KEYS["BASE_URL"]], + CONFIG_MAP_KEYS["PROJECT_NAME"]: new_project_name, + CONFIG_MAP_KEYS["ORG_ID"]: original_configmap[CONFIG_MAP_KEYS["ORG_ID"]], + }, + ) + + replica_set.load() + replica_set["spec"]["opsManager"]["configMapRef"]["name"] = new_project_configmap + replica_set.set_version(custom_mdb_version) + replica_set.update() + + replica_set.assert_reaches_phase(Phase.Running, timeout=600) + + def test_moved_replica_set_connectivity(self): + """ + Verify connectivity to the replica set after switching projects. + """ + ReplicaSetTester(MDB_RESOURCE_NAME, 3).assert_connectivity() + + def test_ops_manager_state_correctly_updated_in_moved_replica_set(self, replica_set: MongoDB): + """ + Ensure Ops Manager state is correctly updated in the moved replica set after the project switch. + """ + tester = replica_set.get_automation_config_tester() + tester.assert_authentication_mechanism_enabled("SCRAM-SHA-256") + tester.assert_authentication_enabled() diff --git a/docker/mongodb-kubernetes-tests/tests/authentication/replica_set_x509_switch_project.py b/docker/mongodb-kubernetes-tests/tests/authentication/replica_set_x509_switch_project.py new file mode 100644 index 000000000..7b23a901d --- /dev/null +++ b/docker/mongodb-kubernetes-tests/tests/authentication/replica_set_x509_switch_project.py @@ -0,0 +1,114 @@ +import pytest +from kubetester import create_or_update_configmap, random_k8s_name, read_configmap +from kubetester.certs import ( + ISSUER_CA_NAME, + create_agent_tls_certs, + create_mongodb_tls_certs, +) +from kubetester.kubetester import KubernetesTester +from kubetester.kubetester import fixture as load_fixture +from kubetester.mongodb import MongoDB +from kubetester.mongotester import ReplicaSetTester +from kubetester.phase import Phase + +# Constants +MDB_RESOURCE_NAME = "replica-set-x509-switch-project" +MDB_FIXTURE_NAME = MDB_RESOURCE_NAME + +CONFIG_MAP_KEYS = { + "BASE_URL": "baseUrl", + "PROJECT_NAME": "projectName", + "ORG_ID": "orgId", +} + + +@pytest.fixture(scope="module") +def replica_set(namespace: str, server_certs: str, agent_certs: str, issuer_ca_configmap: str) -> MongoDB: + """ + Fixture to initialize the MongoDB resource for the replica set. + + Dynamically updates the resource configuration based on the test context. + """ + resource = MongoDB.from_yaml(load_fixture(f"switch-project/{MDB_FIXTURE_NAME}.yaml"), namespace=namespace) + resource["spec"]["security"]["tls"]["ca"] = issuer_ca_configmap + return resource + + +@pytest.fixture(scope="module") +def server_certs(issuer: str, namespace: str): + return create_mongodb_tls_certs(ISSUER_CA_NAME, namespace, MDB_RESOURCE_NAME, f"{MDB_RESOURCE_NAME}-cert") + + +@pytest.fixture(scope="module") +def agent_certs(issuer: str, namespace: str) -> str: + return create_agent_tls_certs(issuer, namespace, MDB_RESOURCE_NAME) + + +@pytest.fixture(scope="module") +def project_name_prefix(namespace: str) -> str: + """ + Generates a random Kubernetes project name prefix based on the namespace. + + Ensures test isolation in a multi-namespace test environment. + """ + return random_k8s_name(f"{namespace}-project-") + + +@pytest.mark.e2e_replica_set_x509_switch_project +class TestReplicaSetCreationAndProjectSwitch(KubernetesTester): + """ + E2E test suite for replica set creation, user connectivity with X509 authentication and switching Ops Manager project reference. + """ + + def test_create_replica_set(self, custom_mdb_version: str, replica_set: MongoDB): + """ + Test replica set creation ensuring resources are applied correctly and set reaches Running phase. + """ + replica_set.set_version(custom_mdb_version) + replica_set.update() + replica_set.assert_reaches_phase(Phase.Running, timeout=600) + + def test_ops_manager_state_correctly_updated_in_initial_replica_set(self, replica_set: MongoDB): + """ + Ensure Ops Manager state is correctly updated in the original replica set. + """ + tester = replica_set.get_automation_config_tester() + tester.assert_authentication_mechanism_enabled("MONGODB-X509") + tester.assert_authoritative_set(True) + tester.assert_authentication_enabled() + tester.assert_expected_users(0) + + def test_switch_replica_set_project( + self, custom_mdb_version: str, replica_set: MongoDB, namespace: str, project_name_prefix: str + ): + """ + Modify the replica set to switch its Ops Manager reference to a new project and verify lifecycle. + """ + original_configmap = read_configmap(namespace=namespace, name="my-project") + new_project_name = f"{project_name_prefix}-second" + new_project_configmap = create_or_update_configmap( + namespace=namespace, + name=new_project_name, + data={ + CONFIG_MAP_KEYS["BASE_URL"]: original_configmap[CONFIG_MAP_KEYS["BASE_URL"]], + CONFIG_MAP_KEYS["PROJECT_NAME"]: new_project_name, + CONFIG_MAP_KEYS["ORG_ID"]: original_configmap[CONFIG_MAP_KEYS["ORG_ID"]], + }, + ) + + replica_set.load() + replica_set["spec"]["opsManager"]["configMapRef"]["name"] = new_project_configmap + replica_set.set_version(custom_mdb_version) + replica_set.update() + + replica_set.assert_reaches_phase(Phase.Running, timeout=600) + + def test_ops_manager_state_correctly_updated_in_moved_replica_set(self, replica_set: MongoDB): + """ + Ensure Ops Manager state is correctly updated in the moved replica set after the project switch. + """ + tester = replica_set.get_automation_config_tester() + tester.assert_authentication_mechanism_enabled("MONGODB-X509") + tester.assert_authoritative_set(True) + tester.assert_authentication_enabled() + tester.assert_expected_users(0) diff --git a/docker/mongodb-kubernetes-tests/tests/authentication/sharded_cluster_scram_sha_1_switch_project.py b/docker/mongodb-kubernetes-tests/tests/authentication/sharded_cluster_scram_sha_1_switch_project.py new file mode 100644 index 000000000..f950758ca --- /dev/null +++ b/docker/mongodb-kubernetes-tests/tests/authentication/sharded_cluster_scram_sha_1_switch_project.py @@ -0,0 +1,113 @@ +import pytest +from kubetester import ( + create_or_update_configmap, + random_k8s_name, + read_configmap, +) +from kubetester.kubetester import KubernetesTester +from kubetester.kubetester import fixture as load_fixture +from kubetester.mongodb import MongoDB +from kubetester.mongotester import ShardedClusterTester +from kubetester.phase import Phase + +CONFIG_MAP_KEYS = { + "BASE_URL": "baseUrl", + "PROJECT_NAME": "projectName", + "ORG_ID": "orgId", +} + +MDB_RESOURCE_NAME = "sharded-cluster-scram-sha-1-switch-project" +MDB_FIXTURE_NAME = MDB_RESOURCE_NAME + + +@pytest.fixture(scope="module") +def project_name_prefix(namespace: str) -> str: + """ + Generates a random Kubernetes project name prefix based on the namespace. + + Ensures test isolation in a multi-namespace test environment. + """ + return random_k8s_name(f"{namespace}-project-") + + +@pytest.fixture(scope="module") +def sharded_cluster(namespace: str) -> MongoDB: + """ + Fixture to initialize the MongoDB resource for the sharded cluster. + + Dynamically updates the resource Ops Manager reference based on the test context. + """ + resource = MongoDB.from_yaml(load_fixture(f"switch-project/{MDB_FIXTURE_NAME}.yaml"), namespace=namespace) + return resource + + +@pytest.mark.e2e_sharded_cluster_scram_sha_1_switch_project +class TestShardedClusterCreationAndProjectSwitch(KubernetesTester): + """ + E2E test suite for sharded cluster creation, user connectivity with SCRAM-SHA-1 authentication and switching Ops Manager project reference. + """ + + def test_create_sharded_cluster(self, custom_mdb_version: str, sharded_cluster: MongoDB): + """ + Test cluster creation ensuring resources are applied correctly and cluster reaches Running phase. + """ + sharded_cluster.set_version(custom_mdb_version) + sharded_cluster.update() + sharded_cluster.assert_reaches_phase(Phase.Running, timeout=600) + + def test_sharded_cluster_connectivity(self): + """ + Verify connectivity to the original sharded cluster. + """ + ShardedClusterTester(MDB_RESOURCE_NAME, 1).assert_connectivity() + + def test_ops_manager_state_correctly_updated_in_initial_cluster(self, sharded_cluster: MongoDB): + """ + Ensure Ops Manager state is correctly updated in the original cluster. + """ + tester = sharded_cluster.get_automation_config_tester() + tester.assert_authentication_mechanism_enabled("MONGODB-CR") + tester.assert_authoritative_set(True) + tester.assert_authentication_enabled(2) + tester.assert_expected_users(0) + + def test_switch_sharded_cluster_project( + self, custom_mdb_version: str, sharded_cluster: MongoDB, namespace: str, project_name_prefix: str + ): + """ + Modify the sharded cluster to switch its Ops Manager reference to a new project and verify lifecycle. + """ + original_configmap = read_configmap(namespace=namespace, name="my-project") + new_project_name = f"{project_name_prefix}-second" + new_project_configmap = create_or_update_configmap( + namespace=namespace, + name=new_project_name, + data={ + CONFIG_MAP_KEYS["BASE_URL"]: original_configmap[CONFIG_MAP_KEYS["BASE_URL"]], + CONFIG_MAP_KEYS["PROJECT_NAME"]: new_project_name, + CONFIG_MAP_KEYS["ORG_ID"]: original_configmap[CONFIG_MAP_KEYS["ORG_ID"]], + }, + ) + + sharded_cluster.load() + sharded_cluster["spec"]["opsManager"]["configMapRef"]["name"] = new_project_configmap + sharded_cluster.set_version(custom_mdb_version) + sharded_cluster.update() + + sharded_cluster.assert_reaches_phase(Phase.Running, timeout=600) + + def test_moved_sharded_cluster_connectivity(self): + """ + Verify connectivity to the sharded cluster after project switch. + """ + ShardedClusterTester(MDB_RESOURCE_NAME, 1).assert_connectivity() + + def test_ops_manager_state_correctly_updated_in_moved_cluster(self, sharded_cluster: MongoDB): + """ + Ensure Ops Manager state is correctly updated in the moved cluster after the project switch. + """ + tester = sharded_cluster.get_automation_config_tester() + tester.assert_authentication_mechanism_enabled("MONGODB-CR") + tester.assert_authoritative_set(True) + tester.assert_authentication_enabled(2) + tester.assert_expected_users(0) diff --git a/docker/mongodb-kubernetes-tests/tests/authentication/sharded_cluster_scram_sha_256_switch_project.py b/docker/mongodb-kubernetes-tests/tests/authentication/sharded_cluster_scram_sha_256_switch_project.py index 8086e8d50..6bf798388 100644 --- a/docker/mongodb-kubernetes-tests/tests/authentication/sharded_cluster_scram_sha_256_switch_project.py +++ b/docker/mongodb-kubernetes-tests/tests/authentication/sharded_cluster_scram_sha_256_switch_project.py @@ -1,112 +1,109 @@ -import pytest - -from kubetester.kubetester import KubernetesTester -from kubetester.kubetester import fixture as load_fixture -from kubetester.mongodb import MongoDB -from kubetester.mongotester import ShardedClusterTester -from kubetester.phase import Phase - -from kubetester import ( - read_configmap, - random_k8s_name, - create_or_update_configmap, -) - -CONFIG_MAP_KEYS = { - "BASE_URL": "baseUrl", - "PROJECT_NAME": "projectName", - "ORG_ID": "orgId", -} - -MDB_RESOURCE_NAME = "sharded-cluster-scram-sha-256-switch-project" +import pytest +from kubetester import ( + create_or_update_configmap, + random_k8s_name, + read_configmap, +) +from kubetester.kubetester import KubernetesTester +from kubetester.kubetester import fixture as load_fixture +from kubetester.mongodb import MongoDB +from kubetester.mongotester import ShardedClusterTester +from kubetester.phase import Phase + +CONFIG_MAP_KEYS = { + "BASE_URL": "baseUrl", + "PROJECT_NAME": "projectName", + "ORG_ID": "orgId", +} + +MDB_RESOURCE_NAME = "sharded-cluster-scram-sha-256-switch-project" MDB_FIXTURE_NAME = MDB_RESOURCE_NAME - - -@pytest.fixture(scope="module") -def project_name_prefix(namespace: str) -> str: - """ - Generates a random Kubernetes project name prefix based on the namespace. - - Ensures test isolation in a multi-namespace test environment. - """ - return random_k8s_name(f"{namespace}-project-") - - -@pytest.fixture(scope="module") -def sharded_cluster(namespace: str) -> MongoDB: - """ - Fixture to initialize the MongoDB resource for the sharded cluster. - - Dynamically updates the resource Ops Manager reference based on the test context. - """ - resource = MongoDB.from_yaml(load_fixture(f"{MDB_FIXTURE_NAME}.yaml"), namespace=namespace) - return resource - - - -@pytest.mark.e2e_sharded_cluster_scram_sha_256_switch_project -class TestShardedClusterCreationAndProjectSwitch(KubernetesTester): - """ - E2E test suite for sharded cluster creation and switching Ops Manager project reference. - """ - - def test_create_sharded_cluster(self, custom_mdb_version: str, sharded_cluster: MongoDB): - """ - Test cluster creation ensuring resources are applied correctly and cluster reaches Running phase. - """ - sharded_cluster.set_version(custom_mdb_version) - sharded_cluster.update() - sharded_cluster.assert_reaches_phase(Phase.Running, timeout=600) - - def test_sharded_cluster_connectivity(self): - """ - Verify connectivity to the original sharded cluster. - """ - ShardedClusterTester(MDB_RESOURCE_NAME, 1).assert_connectivity() - - def test_ops_manager_state_correctly_updated_in_initial_cluster(self, sharded_cluster: MongoDB): - """ - Ensure Ops Manager state is correctly updated in the original cluster. - """ - tester = sharded_cluster.get_automation_config_tester() - tester.assert_authentication_mechanism_enabled("SCRAM-SHA-256") - tester.assert_authentication_enabled() - - def test_switch_sharded_cluster_project( - self, custom_mdb_version: str, sharded_cluster: MongoDB, namespace: str, project_name_prefix: str - ): - """ - Modify the sharded cluster to switch its Ops Manager reference to a new project and verify lifecycle. - """ - original_configmap = read_configmap(namespace=namespace, name="my-project") - new_project_name = f"{project_name_prefix}-second" - new_project_configmap = create_or_update_configmap( - namespace=namespace, - name=new_project_name, - data={ - CONFIG_MAP_KEYS["BASE_URL"]: original_configmap[CONFIG_MAP_KEYS["BASE_URL"]], - CONFIG_MAP_KEYS["PROJECT_NAME"]: new_project_name, - CONFIG_MAP_KEYS["ORG_ID"]: original_configmap[CONFIG_MAP_KEYS["ORG_ID"]], - }, - ) - - sharded_cluster.load() - sharded_cluster["spec"]["opsManager"]["configMapRef"]["name"] = new_project_configmap - sharded_cluster.set_version(custom_mdb_version) - sharded_cluster.update() - - sharded_cluster.assert_reaches_phase(Phase.Running, timeout=600) - - def test_moved_sharded_cluster_connectivity(self): - """ - Verify connectivity to the sharded cluster after project switch. - """ - ShardedClusterTester(MDB_RESOURCE_NAME, 1).assert_connectivity() - - def test_ops_manager_state_correctly_updated_in_moved_cluster(self, sharded_cluster: MongoDB): - """ - Ensure Ops Manager state is correctly updated in the moved cluster after the project switch. - """ - tester = sharded_cluster.get_automation_config_tester() - tester.assert_authentication_mechanism_enabled("SCRAM-SHA-256") - tester.assert_authentication_enabled() + + +@pytest.fixture(scope="module") +def project_name_prefix(namespace: str) -> str: + """ + Generates a random Kubernetes project name prefix based on the namespace. + + Ensures test isolation in a multi-namespace test environment. + """ + return random_k8s_name(f"{namespace}-project-") + + +@pytest.fixture(scope="module") +def sharded_cluster(namespace: str) -> MongoDB: + """ + Fixture to initialize the MongoDB resource for the sharded cluster. + + Dynamically updates the resource Ops Manager reference based on the test context. + """ + resource = MongoDB.from_yaml(load_fixture(f"switch-project/{MDB_FIXTURE_NAME}.yaml"), namespace=namespace) + return resource + + +@pytest.mark.e2e_sharded_cluster_scram_sha_256_switch_project +class TestShardedClusterCreationAndProjectSwitch(KubernetesTester): + """ + E2E test suite for sharded cluster creation, user connectivity with SCRAM-SHA-256 authentication and switching Ops Manager project reference. + """ + + def test_create_sharded_cluster(self, custom_mdb_version: str, sharded_cluster: MongoDB): + """ + Test cluster creation ensuring resources are applied correctly and cluster reaches Running phase. + """ + sharded_cluster.set_version(custom_mdb_version) + sharded_cluster.update() + sharded_cluster.assert_reaches_phase(Phase.Running, timeout=600) + + def test_sharded_cluster_connectivity(self): + """ + Verify connectivity to the original sharded cluster. + """ + ShardedClusterTester(MDB_RESOURCE_NAME, 1).assert_connectivity() + + def test_ops_manager_state_correctly_updated_in_initial_cluster(self, sharded_cluster: MongoDB): + """ + Ensure Ops Manager state is correctly updated in the original cluster. + """ + tester = sharded_cluster.get_automation_config_tester() + tester.assert_authentication_mechanism_enabled("SCRAM-SHA-256") + tester.assert_authentication_enabled() + + def test_switch_sharded_cluster_project( + self, custom_mdb_version: str, sharded_cluster: MongoDB, namespace: str, project_name_prefix: str + ): + """ + Modify the sharded cluster to switch its Ops Manager reference to a new project and verify lifecycle. + """ + original_configmap = read_configmap(namespace=namespace, name="my-project") + new_project_name = f"{project_name_prefix}-second" + new_project_configmap = create_or_update_configmap( + namespace=namespace, + name=new_project_name, + data={ + CONFIG_MAP_KEYS["BASE_URL"]: original_configmap[CONFIG_MAP_KEYS["BASE_URL"]], + CONFIG_MAP_KEYS["PROJECT_NAME"]: new_project_name, + CONFIG_MAP_KEYS["ORG_ID"]: original_configmap[CONFIG_MAP_KEYS["ORG_ID"]], + }, + ) + + sharded_cluster.load() + sharded_cluster["spec"]["opsManager"]["configMapRef"]["name"] = new_project_configmap + sharded_cluster.set_version(custom_mdb_version) + sharded_cluster.update() + + sharded_cluster.assert_reaches_phase(Phase.Running, timeout=600) + + def test_moved_sharded_cluster_connectivity(self): + """ + Verify connectivity to the sharded cluster after project switch. + """ + ShardedClusterTester(MDB_RESOURCE_NAME, 1).assert_connectivity() + + def test_ops_manager_state_correctly_updated_in_moved_cluster(self, sharded_cluster: MongoDB): + """ + Ensure Ops Manager state is correctly updated in the moved cluster after the project switch. + """ + tester = sharded_cluster.get_automation_config_tester() + tester.assert_authentication_mechanism_enabled("SCRAM-SHA-256") + tester.assert_authentication_enabled() diff --git a/docker/mongodb-kubernetes-tests/tests/authentication/sharded_cluster_x509_switch_project.py b/docker/mongodb-kubernetes-tests/tests/authentication/sharded_cluster_x509_switch_project.py new file mode 100644 index 000000000..7138be3a6 --- /dev/null +++ b/docker/mongodb-kubernetes-tests/tests/authentication/sharded_cluster_x509_switch_project.py @@ -0,0 +1,124 @@ +import pytest +from kubetester import ( + create_or_update_configmap, + random_k8s_name, + read_configmap, +) +from kubetester.certs import ( + ISSUER_CA_NAME, + create_sharded_cluster_certs, + create_x509_agent_tls_certs, +) +from kubetester.kubetester import KubernetesTester +from kubetester.kubetester import fixture as load_fixture +from kubetester.mongodb import MongoDB +from kubetester.mongotester import ShardedClusterTester +from kubetester.phase import Phase + +CONFIG_MAP_KEYS = { + "BASE_URL": "baseUrl", + "PROJECT_NAME": "projectName", + "ORG_ID": "orgId", +} + +MDB_RESOURCE_NAME = "sharded-cluster-x509-switch-project" +MDB_FIXTURE_NAME = MDB_RESOURCE_NAME + + +@pytest.fixture(scope="module") +def project_name_prefix(namespace: str) -> str: + """ + Generates a random Kubernetes project name prefix based on the namespace. + + Ensures test isolation in a multi-namespace test environment. + """ + return random_k8s_name(f"{namespace}-project-") + + +@pytest.fixture(scope="module") +def sharded_cluster(namespace: str, server_certs: str, agent_certs: str, issuer_ca_configmap: str) -> MongoDB: + """ + Fixture to initialize the MongoDB resource for the sharded cluster. + + Dynamically updates the resource Ops Manager reference based on the test context. + """ + resource = MongoDB.from_yaml(load_fixture(f"switch-project/{MDB_FIXTURE_NAME}.yaml"), namespace=namespace) + resource["spec"]["security"]["tls"]["ca"] = issuer_ca_configmap + return resource + + +@pytest.fixture(scope="module") +def server_certs(issuer: str, namespace: str): + create_sharded_cluster_certs( + namespace, + MDB_RESOURCE_NAME, + shards=1, + mongod_per_shard=1, + config_servers=1, + mongos=1, + ) + + +@pytest.fixture(scope="module") +def agent_certs(issuer: str, namespace: str) -> str: + return create_x509_agent_tls_certs(issuer, namespace, MDB_RESOURCE_NAME) + + +@pytest.mark.e2e_sharded_cluster_x509_switch_project +class TestShardedClusterCreationAndProjectSwitch(KubernetesTester): + """ + E2E test suite for sharded cluster creation, user connectivity with X509 authentication and switching Ops Manager project reference. + """ + + def test_create_sharded_cluster(self, custom_mdb_version: str, sharded_cluster: MongoDB): + """ + Test cluster creation ensuring resources are applied correctly and cluster reaches Running phase. + """ + sharded_cluster.set_version(custom_mdb_version) + sharded_cluster.update() + sharded_cluster.assert_reaches_phase(Phase.Running, timeout=600) + + def test_ops_manager_state_correctly_updated_in_initial_cluster(self, sharded_cluster: MongoDB): + """ + Ensure Ops Manager state is correctly updated in the original cluster. + """ + tester = sharded_cluster.get_automation_config_tester() + tester.assert_authentication_mechanism_enabled("MONGODB-X509") + tester.assert_authoritative_set(True) + tester.assert_authentication_enabled() + tester.assert_expected_users(0) + + def test_switch_sharded_cluster_project( + self, custom_mdb_version: str, sharded_cluster: MongoDB, namespace: str, project_name_prefix: str + ): + """ + Modify the sharded cluster to switch its Ops Manager reference to a new project and verify lifecycle. + """ + original_configmap = read_configmap(namespace=namespace, name="my-project") + new_project_name = f"{project_name_prefix}-second" + new_project_configmap = create_or_update_configmap( + namespace=namespace, + name=new_project_name, + data={ + CONFIG_MAP_KEYS["BASE_URL"]: original_configmap[CONFIG_MAP_KEYS["BASE_URL"]], + CONFIG_MAP_KEYS["PROJECT_NAME"]: new_project_name, + CONFIG_MAP_KEYS["ORG_ID"]: original_configmap[CONFIG_MAP_KEYS["ORG_ID"]], + }, + ) + + sharded_cluster.load() + sharded_cluster["spec"]["opsManager"]["configMapRef"]["name"] = new_project_configmap + sharded_cluster.set_version(custom_mdb_version) + sharded_cluster.update() + + sharded_cluster.assert_reaches_phase(Phase.Running, timeout=600) + + def test_ops_manager_state_correctly_updated_in_moved_cluster(self, sharded_cluster: MongoDB): + """ + Ensure Ops Manager state is correctly updated in the moved cluster after the project switch. + """ + tester = sharded_cluster.get_automation_config_tester() + tester.assert_authentication_mechanism_enabled("MONGODB-X509") + tester.assert_authoritative_set(True) + tester.assert_authentication_enabled() + tester.assert_expected_users(0) From 5db3adb263e026a739031ac7c225e9d29524b4ff Mon Sep 17 00:00:00 2001 From: filipcirtog Date: Thu, 6 Nov 2025 10:47:44 +0100 Subject: [PATCH 4/5] tests: refactoring+improvements --- .evergreen-tasks.yml | 10 + .evergreen.yml | 2 + controllers/om/automation_config.go | 2 +- .../replica-set-ldap-switch-project.yaml | 29 +++ .../sharded-cluster-ldap-switch-project.yaml | 33 ++++ ...-cluster-scram-sha-256-switch-project.yaml | 2 - .../replica_set_ldap_switch_project.py | 174 ++++++++++++++++++ .../replica_set_scram_sha_1_switch_project.py | 60 +++++- ...eplica_set_scram_sha_256_switch_project.py | 59 +++++- .../sharded_cluster_ldap_switch_project.py | 163 ++++++++++++++++ ...rded_cluster_scram_sha_1_switch_project.py | 56 +++++- ...ed_cluster_scram_sha_256_switch_project.py | 56 +++++- 12 files changed, 637 insertions(+), 9 deletions(-) create mode 100644 docker/mongodb-kubernetes-tests/tests/authentication/fixtures/switch-project/replica-set-ldap-switch-project.yaml create mode 100644 docker/mongodb-kubernetes-tests/tests/authentication/fixtures/switch-project/sharded-cluster-ldap-switch-project.yaml create mode 100644 docker/mongodb-kubernetes-tests/tests/authentication/replica_set_ldap_switch_project.py create mode 100644 docker/mongodb-kubernetes-tests/tests/authentication/sharded_cluster_ldap_switch_project.py diff --git a/.evergreen-tasks.yml b/.evergreen-tasks.yml index 4a8de81e5..2b1b8e989 100644 --- a/.evergreen-tasks.yml +++ b/.evergreen-tasks.yml @@ -720,6 +720,16 @@ tasks: commands: - func: "e2e_test" + - name: e2e_replica_set_ldap_switch_project + tags: [ "patch-run" ] + commands: + - func: "e2e_test" + + - name: e2e_sharded_cluster_ldap_switch_project + tags: [ "patch-run" ] + commands: + - func: "e2e_test" + # TODO: not used in any variant - name: e2e_replica_set_scram_x509_internal_cluster tags: [ "patch-run" ] diff --git a/.evergreen.yml b/.evergreen.yml index 6205479f4..d760e779e 100644 --- a/.evergreen.yml +++ b/.evergreen.yml @@ -719,6 +719,8 @@ task_groups: - e2e_replica_set_scram_sha_256_switch_project - e2e_replica_set_scram_sha_1_switch_project - e2e_replica_set_x509_switch_project + - e2e_replica_set_ldap_switch_project + - e2e_sharded_cluster_ldap_switch_project # e2e_auth_transitions_task_group - e2e_replica_set_scram_sha_and_x509 - e2e_replica_set_x509_to_scram_transition diff --git a/controllers/om/automation_config.go b/controllers/om/automation_config.go index 9582cd77d..cfc8f49c2 100644 --- a/controllers/om/automation_config.go +++ b/controllers/om/automation_config.go @@ -439,7 +439,7 @@ func (ac *AutomationConfig) EnsureKeyFileContents() error { // AuthSecretName for a given mdbName (`mdbName`) returns the name of // the secret associated with it. func AuthSecretName(mdbName string) string { - return fmt.Sprintf("%s-agent-auth-secre", mdbName) + return fmt.Sprintf("%s-agent-auth-secret", mdbName) } // EnsurePassword makes sure that there is an Automation Agent password diff --git a/docker/mongodb-kubernetes-tests/tests/authentication/fixtures/switch-project/replica-set-ldap-switch-project.yaml b/docker/mongodb-kubernetes-tests/tests/authentication/fixtures/switch-project/replica-set-ldap-switch-project.yaml new file mode 100644 index 000000000..84574130a --- /dev/null +++ b/docker/mongodb-kubernetes-tests/tests/authentication/fixtures/switch-project/replica-set-ldap-switch-project.yaml @@ -0,0 +1,29 @@ +--- +apiVersion: mongodb.com/v1 +kind: MongoDB +metadata: + name: replica-set-ldap-switch-project +spec: + type: ReplicaSet + members: 3 + version: 4.4.0-ent + + opsManager: + configMapRef: + name: my-project + credentials: my-credentials + + security: + authentication: + agents: + mode: "SCRAM" + enabled: true + # Enabled LDAP and SCRAM Authentication Mode + modes: ["LDAP", "SCRAM"] + ldap: + servers: "" + transportSecurity: "" + bindQueryUser: "" + bindQueryPasswordSecretRef: + name: "" + \ No newline at end of file diff --git a/docker/mongodb-kubernetes-tests/tests/authentication/fixtures/switch-project/sharded-cluster-ldap-switch-project.yaml b/docker/mongodb-kubernetes-tests/tests/authentication/fixtures/switch-project/sharded-cluster-ldap-switch-project.yaml new file mode 100644 index 000000000..5ddf246e3 --- /dev/null +++ b/docker/mongodb-kubernetes-tests/tests/authentication/fixtures/switch-project/sharded-cluster-ldap-switch-project.yaml @@ -0,0 +1,33 @@ +--- +apiVersion: mongodb.com/v1 +kind: MongoDB +metadata: + name: sharded-cluster-ldap-switch-project +spec: + type: ShardedCluster + + shardCount: 1 + mongodsPerShardCount: 1 + mongosCount: 1 + configServerCount: 1 + + version: 4.4.0-ent + + opsManager: + configMapRef: + name: my-project + credentials: my-credentials + + security: + authentication: + enabled: true + # Enabled LDAP Authentication Mode + modes: ["LDAP"] + ldap: + servers: "" + transportSecurity: "tls" + # Specify the LDAP Distinguished Name to which + # MongoDB binds when connecting to the LDAP server + bindQueryUser: "cn=admin,dc=example,dc=org" + bindQueryPasswordSecretRef: + name: "" \ No newline at end of file diff --git a/docker/mongodb-kubernetes-tests/tests/authentication/fixtures/switch-project/sharded-cluster-scram-sha-256-switch-project.yaml b/docker/mongodb-kubernetes-tests/tests/authentication/fixtures/switch-project/sharded-cluster-scram-sha-256-switch-project.yaml index 1bf83d70d..ff558d3a6 100644 --- a/docker/mongodb-kubernetes-tests/tests/authentication/fixtures/switch-project/sharded-cluster-scram-sha-256-switch-project.yaml +++ b/docker/mongodb-kubernetes-tests/tests/authentication/fixtures/switch-project/sharded-cluster-scram-sha-256-switch-project.yaml @@ -21,5 +21,3 @@ spec: authentication: enabled: true modes: ["SCRAM"] - - diff --git a/docker/mongodb-kubernetes-tests/tests/authentication/replica_set_ldap_switch_project.py b/docker/mongodb-kubernetes-tests/tests/authentication/replica_set_ldap_switch_project.py new file mode 100644 index 000000000..b3f76e345 --- /dev/null +++ b/docker/mongodb-kubernetes-tests/tests/authentication/replica_set_ldap_switch_project.py @@ -0,0 +1,174 @@ +import tempfile +from typing import List + +import pytest +from kubetester import ( + create_or_update_configmap, + create_secret, + find_fixture, + random_k8s_name, + read_configmap, +) +from kubetester.certs import create_mongodb_tls_certs, create_x509_user_cert +from kubetester.kubetester import KubernetesTester +from kubetester.ldap import LDAP_AUTHENTICATION_MECHANISM, LDAPUser, OpenLDAP +from kubetester.mongodb import MongoDB +from kubetester.mongodb_user import MongoDBUser, Role, generic_user +from kubetester.phase import Phase + +MDB_RESOURCE_NAME = "replica-set-ldap-switch-project" +MDB_FIXTURE_NAME = MDB_RESOURCE_NAME + +CONFIG_MAP_KEYS = { + "BASE_URL": "baseUrl", + "PROJECT_NAME": "projectName", + "ORG_ID": "orgId", +} + + +@pytest.fixture(scope="module") +def project_name_prefix(namespace: str) -> str: + """ + Generates a random Kubernetes project name prefix based on the namespace. + + Ensures test isolation in a multi-namespace test environment. + """ + return random_k8s_name(f"{namespace}-project-") + + +@pytest.fixture(scope="module") +def server_certs(namespace: str, issuer: str): + create_mongodb_tls_certs(issuer, namespace, MDB_RESOURCE_NAME, "certs-" + MDB_RESOURCE_NAME + "-cert") + return "certs" + + +@pytest.fixture(scope="module") +def replica_set( + openldap: OpenLDAP, + issuer_ca_configmap: str, + ldap_mongodb_agent_user: LDAPUser, + server_certs: str, + namespace: str, +) -> MongoDB: + resource = MongoDB.from_yaml(find_fixture(f"switch-project/{MDB_FIXTURE_NAME}.yaml"), namespace=namespace) + + secret_name = "bind-query-password" + create_secret(namespace, secret_name, {"password": openldap.admin_password}) + ac_secret_name = "automation-config-password" + create_secret( + namespace, + ac_secret_name, + {"automationConfigPassword": ldap_mongodb_agent_user.password}, + ) + + resource["spec"]["security"] = { + "tls": { + "enabled": True, + "ca": issuer_ca_configmap, + }, + "certsSecretPrefix": server_certs, + "authentication": { + "enabled": True, + "modes": ["LDAP", "SCRAM", "X509"], + "ldap": { + "servers": [openldap.servers], + "bindQueryUser": "cn=admin,dc=example,dc=org", + "bindQueryPasswordSecretRef": {"name": secret_name}, + }, + "agents": { + "mode": "LDAP", + "automationPasswordSecretRef": { + "name": ac_secret_name, + "key": "automationConfigPassword", + }, + "automationUserName": ldap_mongodb_agent_user.uid, + }, + }, + } + + return resource + + +@pytest.fixture(scope="module") +def user_ldap(replica_set: MongoDB, namespace: str, ldap_mongodb_users: List[LDAPUser]) -> MongoDBUser: + mongodb_user = ldap_mongodb_users[0] + user = generic_user( + namespace, + username=mongodb_user.username, + db="$external", + password=mongodb_user.password, + mongodb_resource=replica_set, + ) + user.add_roles( + [ + Role(db="admin", role="clusterAdmin"), + Role(db="admin", role="readWriteAnyDatabase"), + Role(db="admin", role="dbAdminAnyDatabase"), + ] + ) + + return user.create() + + +@pytest.mark.e2e_replica_set_ldap_switch_project +class TestReplicaSetLDAPProjectSwitch(KubernetesTester): + + def test_create_replica_set(self, replica_set: MongoDB, ldap_mongodb_users: List[LDAPUser]): + replica_set.update() + replica_set.assert_reaches_phase(Phase.Running, timeout=600) + + def test_create_ldap_user(self, replica_set: MongoDB, user_ldap: MongoDBUser): + user_ldap.assert_reaches_phase(Phase.Updated) + + tester = replica_set.get_automation_config_tester() + tester.assert_authentication_mechanism_enabled(LDAP_AUTHENTICATION_MECHANISM, active_auth_mechanism=True) + tester.assert_expected_users(1) + + def test_new_mdb_users_are_created_and_can_authenticate( + self, replica_set: MongoDB, user_ldap: MongoDBUser, ca_path: str + ): + tester = replica_set.tester() + + tester.assert_ldap_authentication( + username=user_ldap["spec"]["username"], + password=user_ldap.password, + tls_ca_file=ca_path, + attempts=10, + ) + + def test_switch_replica_set_project( + self, replica_set: MongoDB, namespace: str, project_name_prefix: str, user_ldap: MongoDBUser + ): + """ + Modify the replica set to switch its Ops Manager reference to a new project and verify lifecycle. + """ + original_configmap = read_configmap(namespace=namespace, name="my-project") + new_project_name = f"{project_name_prefix}-second" + new_project_configmap = create_or_update_configmap( + namespace=namespace, + name=new_project_name, + data={ + CONFIG_MAP_KEYS["BASE_URL"]: original_configmap[CONFIG_MAP_KEYS["BASE_URL"]], + CONFIG_MAP_KEYS["PROJECT_NAME"]: new_project_name, + CONFIG_MAP_KEYS["ORG_ID"]: original_configmap[CONFIG_MAP_KEYS["ORG_ID"]], + }, + ) + + replica_set.load() + replica_set["spec"]["opsManager"]["configMapRef"]["name"] = new_project_configmap + replica_set.update() + + replica_set.assert_reaches_phase(Phase.Running, timeout=600) + + def test_ops_manager_state_correctly_updated_in_moved_cluster(self, replica_set: MongoDB, user_ldap: MongoDBUser, ca_path: str): + tester = replica_set.get_automation_config_tester() + tester.assert_authentication_mechanism_enabled(LDAP_AUTHENTICATION_MECHANISM, active_auth_mechanism=True) + # tester.assert_expected_users(1) + + # tester = replica_set.tester() + # tester.assert_ldap_authentication( + # username=user_ldap["spec"]["username"], + # password=user_ldap.password, + # tls_ca_file=ca_path, + # attempts=10, + # ) diff --git a/docker/mongodb-kubernetes-tests/tests/authentication/replica_set_scram_sha_1_switch_project.py b/docker/mongodb-kubernetes-tests/tests/authentication/replica_set_scram_sha_1_switch_project.py index 5cf39477e..4a55c52a6 100644 --- a/docker/mongodb-kubernetes-tests/tests/authentication/replica_set_scram_sha_1_switch_project.py +++ b/docker/mongodb-kubernetes-tests/tests/authentication/replica_set_scram_sha_1_switch_project.py @@ -1,8 +1,14 @@ import pytest -from kubetester import create_or_update_configmap, random_k8s_name, read_configmap +from kubetester import ( + create_or_update_configmap, + create_or_update_secret, + random_k8s_name, + read_configmap, +) from kubetester.kubetester import KubernetesTester from kubetester.kubetester import fixture as load_fixture from kubetester.mongodb import MongoDB +from kubetester.mongodb_user import MongoDBUser from kubetester.mongotester import ReplicaSetTester from kubetester.phase import Phase @@ -44,6 +50,10 @@ class TestReplicaSetCreationAndProjectSwitch(KubernetesTester): E2E test suite for replica set creation, user connectivity with SCRAM-SHA-1 authentication and switching Ops Manager project reference. """ + PASSWORD_SECRET_NAME = "mms-user-1-password" + USER_PASSWORD = "my-password" + USER_NAME = "mms-user-1" + def test_create_replica_set(self, custom_mdb_version: str, replica_set: MongoDB): """ Test replica set creation ensuring resources are applied correctly and set reaches Running phase. @@ -68,6 +78,40 @@ def test_ops_manager_state_correctly_updated_in_initial_replica_set(self, replic tester.assert_authentication_enabled(2) tester.assert_expected_users(0) + def test_create_secret(self): + print(f"creating password for MongoDBUser {self.USER_NAME} in secret/{self.PASSWORD_SECRET_NAME} ") + + create_or_update_secret( + KubernetesTester.get_namespace(), + self.PASSWORD_SECRET_NAME, + { + "password": self.USER_PASSWORD, + }, + ) + + def test_create_user(self, namespace: str): + mdb = MongoDBUser.from_yaml( + load_fixture("scram-sha-user.yaml"), + namespace=namespace, + ) + mdb["spec"]["mongodbResourceRef"]["name"] = MDB_RESOURCE_NAME + + mdb.update() + mdb.assert_reaches_phase(Phase.Updated, timeout=150) + + def test_ops_manager_state_with_users_correctly_updated(self, replica_set: MongoDB): + expected_roles = { + ("admin", "clusterAdmin"), + ("admin", "userAdminAnyDatabase"), + ("admin", "readWrite"), + ("admin", "userAdminAnyDatabase"), + } + + tester = replica_set.get_automation_config_tester() + tester.assert_has_user(self.USER_NAME) + tester.assert_user_has_roles(self.USER_NAME, expected_roles) + tester.assert_expected_users(1) + def test_switch_replica_set_project( self, custom_mdb_version: str, replica_set: MongoDB, namespace: str, project_name_prefix: str ): @@ -107,4 +151,16 @@ def test_ops_manager_state_correctly_updated_in_moved_replica_set(self, replica_ tester.assert_authentication_mechanism_enabled("MONGODB-CR") tester.assert_authoritative_set(True) tester.assert_authentication_enabled(2) - tester.assert_expected_users(0) + + def test_ops_manager_state_with_users_correctly_updated_after_switch(self, replica_set: MongoDB): + expected_roles = { + ("admin", "clusterAdmin"), + ("admin", "userAdminAnyDatabase"), + ("admin", "readWrite"), + ("admin", "userAdminAnyDatabase"), + } + + tester = replica_set.get_automation_config_tester() + tester.assert_has_user(self.USER_NAME) + tester.assert_user_has_roles(self.USER_NAME, expected_roles) + tester.assert_expected_users(1) diff --git a/docker/mongodb-kubernetes-tests/tests/authentication/replica_set_scram_sha_256_switch_project.py b/docker/mongodb-kubernetes-tests/tests/authentication/replica_set_scram_sha_256_switch_project.py index 8269df337..c9be17391 100644 --- a/docker/mongodb-kubernetes-tests/tests/authentication/replica_set_scram_sha_256_switch_project.py +++ b/docker/mongodb-kubernetes-tests/tests/authentication/replica_set_scram_sha_256_switch_project.py @@ -1,8 +1,14 @@ import pytest -from kubetester import create_or_update_configmap, random_k8s_name, read_configmap +from kubetester import ( + create_or_update_configmap, + create_or_update_secret, + random_k8s_name, + read_configmap, +) from kubetester.kubetester import KubernetesTester from kubetester.kubetester import fixture as load_fixture from kubetester.mongodb import MongoDB +from kubetester.mongodb_user import MongoDBUser from kubetester.mongotester import ReplicaSetTester from kubetester.phase import Phase @@ -43,6 +49,10 @@ class TestReplicaSetCreationAndProjectSwitch(KubernetesTester): """ E2E test suite for replica set creation, user connectivity with SCRAM-SHA-256 authentication and switching Ops Manager project reference. """ + + PASSWORD_SECRET_NAME = "mms-user-1-password" + USER_PASSWORD = "my-password" + USER_NAME = "mms-user-1" def test_create_replica_set(self, custom_mdb_version: str, replica_set: MongoDB): """ @@ -66,6 +76,40 @@ def test_ops_manager_state_correctly_updated_in_initial_replica_set(self, replic tester.assert_authentication_mechanism_enabled("SCRAM-SHA-256") tester.assert_authentication_enabled() + def test_create_secret(self): + print(f"creating password for MongoDBUser {self.USER_NAME} in secret/{self.PASSWORD_SECRET_NAME} ") + + create_or_update_secret( + KubernetesTester.get_namespace(), + self.PASSWORD_SECRET_NAME, + { + "password": self.USER_PASSWORD, + }, + ) + + def test_create_user(self, namespace: str): + mdb = MongoDBUser.from_yaml( + load_fixture("scram-sha-user.yaml"), + namespace=namespace, + ) + mdb["spec"]["mongodbResourceRef"]["name"] = MDB_RESOURCE_NAME + + mdb.update() + mdb.assert_reaches_phase(Phase.Updated, timeout=150) + + def test_ops_manager_state_with_users_correctly_updated(self, replica_set: MongoDB): + expected_roles = { + ("admin", "clusterAdmin"), + ("admin", "userAdminAnyDatabase"), + ("admin", "readWrite"), + ("admin", "userAdminAnyDatabase"), + } + + tester = replica_set.get_automation_config_tester() + tester.assert_has_user(self.USER_NAME) + tester.assert_user_has_roles(self.USER_NAME, expected_roles) + tester.assert_expected_users(1) + def test_switch_replica_set_project( self, custom_mdb_version: str, replica_set: MongoDB, namespace: str, project_name_prefix: str ): @@ -104,3 +148,16 @@ def test_ops_manager_state_correctly_updated_in_moved_replica_set(self, replica_ tester = replica_set.get_automation_config_tester() tester.assert_authentication_mechanism_enabled("SCRAM-SHA-256") tester.assert_authentication_enabled() + + def test_ops_manager_state_with_users_correctly_updated_after_switch(self, replica_set: MongoDB): + expected_roles = { + ("admin", "clusterAdmin"), + ("admin", "userAdminAnyDatabase"), + ("admin", "readWrite"), + ("admin", "userAdminAnyDatabase"), + } + + tester = replica_set.get_automation_config_tester() + tester.assert_has_user(self.USER_NAME) + tester.assert_user_has_roles(self.USER_NAME, expected_roles) + tester.assert_expected_users(1) diff --git a/docker/mongodb-kubernetes-tests/tests/authentication/sharded_cluster_ldap_switch_project.py b/docker/mongodb-kubernetes-tests/tests/authentication/sharded_cluster_ldap_switch_project.py new file mode 100644 index 000000000..3c6039054 --- /dev/null +++ b/docker/mongodb-kubernetes-tests/tests/authentication/sharded_cluster_ldap_switch_project.py @@ -0,0 +1,163 @@ +import time +from typing import Dict, List + +import pytest +from kubetester import ( + create_or_update_configmap, + create_secret, + find_fixture, + random_k8s_name, + read_configmap, + wait_until, +) +from kubetester.kubetester import KubernetesTester +from kubetester.kubetester import fixture as yaml_fixture +from kubetester.ldap import LDAP_AUTHENTICATION_MECHANISM, LDAPUser, OpenLDAP +from kubetester.mongodb import MongoDB +from kubetester.mongodb_user import MongoDBUser, Role, generic_user +from kubetester.mongotester import ShardedClusterTester +from kubetester.phase import Phase + +CONFIG_MAP_KEYS = { + "BASE_URL": "baseUrl", + "PROJECT_NAME": "projectName", + "ORG_ID": "orgId", +} + +MDB_RESOURCE_NAME = "sharded-cluster-ldap-switch-project" +MDB_FIXTURE_NAME = MDB_RESOURCE_NAME + + +@pytest.fixture(scope="module") +def operator_installation_config(operator_installation_config_quick_recovery: Dict[str, str]) -> Dict[str, str]: + return operator_installation_config_quick_recovery + + +@pytest.fixture(scope="module") +def project_name_prefix(namespace: str) -> str: + return random_k8s_name(f"{namespace}-project-") + + +@pytest.fixture(scope="module") +def sharded_cluster(namespace: str, openldap_tls: OpenLDAP, issuer_ca_configmap: str) -> MongoDB: + + bind_query_password_secret = "bind-query-password" + resource = MongoDB.from_yaml(find_fixture(f"switch-project/{MDB_FIXTURE_NAME}.yaml"), namespace=namespace) + + KubernetesTester.create_secret(namespace, bind_query_password_secret, {"password": openldap_tls.admin_password}) + + resource["spec"]["security"]["authentication"]["ldap"] = { + "servers": [openldap_tls.servers], + "bindQueryPasswordSecretRef": {"name": bind_query_password_secret}, + "transportSecurity": "none", # For testing CLOUDP-229222 + "caConfigMapRef": {"name": issuer_ca_configmap, "key": "ca-pem"}, + } + resource["spec"]["security"]["authentication"]["agents"] = {"mode": "SCRAM"} + resource["spec"]["security"]["authentication"]["modes"] = ["LDAP", "SCRAM"] + + return resource + + +@pytest.fixture(scope="module") +def ldap_user_mongodb(sharded_cluster: MongoDB, namespace: str, ldap_mongodb_user_tls: LDAPUser) -> MongoDBUser: + """Returns a list of MongoDBUsers (already created) and their corresponding passwords.""" + user = generic_user( + namespace, + username=ldap_mongodb_user_tls.username, + db="$external", + mongodb_resource=sharded_cluster, + password=ldap_mongodb_user_tls.password, + ) + user.add_roles( + [ + Role(db="admin", role="clusterAdmin"), + Role(db="admin", role="readWriteAnyDatabase"), + Role(db="admin", role="dbAdminAnyDatabase"), + ] + ) + + return user.create() + + +@pytest.mark.e2e_sharded_cluster_ldap_switch_project +class TestShardedClusterLDAPProjectSwitch(KubernetesTester): + + def test_create_sharded_cluster(self, sharded_cluster: MongoDB): + sharded_cluster.update() + sharded_cluster.assert_reaches_phase(Phase.Pending, timeout=600) + + def test_sharded_cluster_turn_tls_on_CLOUDP_229222(self, sharded_cluster: MongoDB): + """ + This function tests CLOUDP-229222. The user attempts to fix the AutomationConfig. + Before updating the AutomationConfig, we need to ensure the operator pushed the wrong one to Ops Manager. + """ + + def wait_for_ac_exists() -> bool: + ac = sharded_cluster.get_automation_config_tester().automation_config + try: + _ = ac["ldap"]["transportSecurity"] + _ = ac["version"] + return True + except KeyError: + return False + + wait_until(wait_for_ac_exists, timeout=800) + current_version = sharded_cluster.get_automation_config_tester().automation_config["version"] + + def wait_for_ac_pushed() -> bool: + ac = sharded_cluster.get_automation_config_tester().automation_config + try: + transport_security = ac["ldap"]["transportSecurity"] + new_version = ac["version"] + if transport_security != "none": + return False + if new_version <= current_version: + return False + return True + except KeyError: + return False + + wait_until(wait_for_ac_pushed, timeout=800) + + resource = sharded_cluster.load() + resource["spec"]["security"]["authentication"]["ldap"]["transportSecurity"] = "tls" + resource.update() + + def test_sharded_cluster_CLOUDP_229222(self, sharded_cluster: MongoDB, ldap_mongodb_users: List[LDAPUser]): + """ + This function tests CLOUDP-229222. The recovery mechanism kicks in and pushes Automation Config. The ReplicaSet + goes into running state. + """ + sharded_cluster.assert_reaches_phase(Phase.Running, timeout=800) + + def test_ops_manager_state_correctly_updated_in_initial_cluster( + self, sharded_cluster: MongoDB, ldap_user_mongodb: MongoDBUser + ): + + ldap_user_mongodb.assert_reaches_phase(Phase.Updated) + ac_tester = sharded_cluster.get_automation_config_tester() + ac_tester.assert_authentication_mechanism_enabled(LDAP_AUTHENTICATION_MECHANISM, active_auth_mechanism=False) + ac_tester.assert_expected_users(1) + + def test_switch_sharded_cluster_project(self, sharded_cluster: MongoDB, namespace: str, project_name_prefix: str): + original_configmap = read_configmap(namespace=namespace, name="my-project") + new_project_name = f"{project_name_prefix}-second" + new_project_configmap = create_or_update_configmap( + namespace=namespace, + name=new_project_name, + data={ + CONFIG_MAP_KEYS["BASE_URL"]: original_configmap[CONFIG_MAP_KEYS["BASE_URL"]], + CONFIG_MAP_KEYS["PROJECT_NAME"]: new_project_name, + CONFIG_MAP_KEYS["ORG_ID"]: original_configmap[CONFIG_MAP_KEYS["ORG_ID"]], + }, + ) + + sharded_cluster.reload() + sharded_cluster["spec"]["opsManager"]["configMapRef"]["name"] = new_project_configmap + sharded_cluster.update() + sharded_cluster.assert_reaches_phase(Phase.Running, timeout=600) + + def test_ops_manager_state_correctly_updated_in_moved_cluster(self, sharded_cluster: MongoDB): + ac_tester = sharded_cluster.get_automation_config_tester() + ac_tester.assert_authentication_mechanism_enabled(LDAP_AUTHENTICATION_MECHANISM, active_auth_mechanism=False) + # ac_tester.assert_expected_users(1) diff --git a/docker/mongodb-kubernetes-tests/tests/authentication/sharded_cluster_scram_sha_1_switch_project.py b/docker/mongodb-kubernetes-tests/tests/authentication/sharded_cluster_scram_sha_1_switch_project.py index f950758ca..5b908b0b7 100644 --- a/docker/mongodb-kubernetes-tests/tests/authentication/sharded_cluster_scram_sha_1_switch_project.py +++ b/docker/mongodb-kubernetes-tests/tests/authentication/sharded_cluster_scram_sha_1_switch_project.py @@ -1,12 +1,14 @@ import pytest from kubetester import ( create_or_update_configmap, + create_or_update_secret, random_k8s_name, read_configmap, ) from kubetester.kubetester import KubernetesTester from kubetester.kubetester import fixture as load_fixture from kubetester.mongodb import MongoDB +from kubetester.mongodb_user import MongoDBUser from kubetester.mongotester import ShardedClusterTester from kubetester.phase import Phase @@ -47,6 +49,10 @@ class TestShardedClusterCreationAndProjectSwitch(KubernetesTester): E2E test suite for sharded cluster creation, user connectivity with SCRAM-SHA-1 authentication and switching Ops Manager project reference. """ + PASSWORD_SECRET_NAME = "mms-user-1-password" + USER_PASSWORD = "my-password" + USER_NAME = "mms-user-1" + def test_create_sharded_cluster(self, custom_mdb_version: str, sharded_cluster: MongoDB): """ Test cluster creation ensuring resources are applied correctly and cluster reaches Running phase. @@ -71,6 +77,40 @@ def test_ops_manager_state_correctly_updated_in_initial_cluster(self, sharded_cl tester.assert_authentication_enabled(2) tester.assert_expected_users(0) + # def test_create_secret(self): + # print(f"creating password for MongoDBUser {self.USER_NAME} in secret/{self.PASSWORD_SECRET_NAME} ") + + # create_or_update_secret( + # KubernetesTester.get_namespace(), + # self.PASSWORD_SECRET_NAME, + # { + # "password": self.USER_PASSWORD, + # }, + # ) + + # def test_create_user(self, namespace: str): + # mdb = MongoDBUser.from_yaml( + # load_fixture("scram-sha-user.yaml"), + # namespace=namespace, + # ) + # mdb["spec"]["mongodbResourceRef"]["name"] = MDB_RESOURCE_NAME + + # mdb.update() + # mdb.assert_reaches_phase(Phase.Updated, timeout=150) + + # def test_ops_manager_state_with_users_correctly_updated(self, sharded_cluster: MongoDB): + # expected_roles = { + # ("admin", "clusterAdmin"), + # ("admin", "userAdminAnyDatabase"), + # ("admin", "readWrite"), + # ("admin", "userAdminAnyDatabase"), + # } + + # tester = sharded_cluster.get_automation_config_tester() + # tester.assert_has_user(self.USER_NAME) + # tester.assert_user_has_roles(self.USER_NAME, expected_roles) + # tester.assert_expected_users(1) + def test_switch_sharded_cluster_project( self, custom_mdb_version: str, sharded_cluster: MongoDB, namespace: str, project_name_prefix: str ): @@ -94,7 +134,7 @@ def test_switch_sharded_cluster_project( sharded_cluster.set_version(custom_mdb_version) sharded_cluster.update() - sharded_cluster.assert_reaches_phase(Phase.Running, timeout=600) + sharded_cluster.assert_reaches_phase(Phase.Running, timeout=800) def test_moved_sharded_cluster_connectivity(self): """ @@ -110,4 +150,16 @@ def test_ops_manager_state_correctly_updated_in_moved_cluster(self, sharded_clus tester.assert_authentication_mechanism_enabled("MONGODB-CR") tester.assert_authoritative_set(True) tester.assert_authentication_enabled(2) - tester.assert_expected_users(0) + + # def test_ops_manager_state_with_users_correctly_updated_after_switch(self, sharded_cluster: MongoDB): + # expected_roles = { + # ("admin", "clusterAdmin"), + # ("admin", "userAdminAnyDatabase"), + # ("admin", "readWrite"), + # ("admin", "userAdminAnyDatabase"), + # } + + # tester = sharded_cluster.get_automation_config_tester() + # tester.assert_has_user(self.USER_NAME) + # tester.assert_user_has_roles(self.USER_NAME, expected_roles) + # tester.assert_expected_users(1) diff --git a/docker/mongodb-kubernetes-tests/tests/authentication/sharded_cluster_scram_sha_256_switch_project.py b/docker/mongodb-kubernetes-tests/tests/authentication/sharded_cluster_scram_sha_256_switch_project.py index 6bf798388..4089cd045 100644 --- a/docker/mongodb-kubernetes-tests/tests/authentication/sharded_cluster_scram_sha_256_switch_project.py +++ b/docker/mongodb-kubernetes-tests/tests/authentication/sharded_cluster_scram_sha_256_switch_project.py @@ -1,12 +1,15 @@ import pytest from kubetester import ( create_or_update_configmap, + create_or_update_secret, random_k8s_name, read_configmap, + try_load, ) from kubetester.kubetester import KubernetesTester from kubetester.kubetester import fixture as load_fixture from kubetester.mongodb import MongoDB +from kubetester.mongodb_user import MongoDBUser from kubetester.mongotester import ShardedClusterTester from kubetester.phase import Phase @@ -47,6 +50,10 @@ class TestShardedClusterCreationAndProjectSwitch(KubernetesTester): E2E test suite for sharded cluster creation, user connectivity with SCRAM-SHA-256 authentication and switching Ops Manager project reference. """ + PASSWORD_SECRET_NAME = "mms-user-1-password" + USER_PASSWORD = "my-password" + USER_NAME = "mms-user-1" + def test_create_sharded_cluster(self, custom_mdb_version: str, sharded_cluster: MongoDB): """ Test cluster creation ensuring resources are applied correctly and cluster reaches Running phase. @@ -69,6 +76,40 @@ def test_ops_manager_state_correctly_updated_in_initial_cluster(self, sharded_cl tester.assert_authentication_mechanism_enabled("SCRAM-SHA-256") tester.assert_authentication_enabled() + # def test_create_secret(self): + # print(f"creating password for MongoDBUser {self.USER_NAME} in secret/{self.PASSWORD_SECRET_NAME} ") + + # create_or_update_secret( + # KubernetesTester.get_namespace(), + # self.PASSWORD_SECRET_NAME, + # { + # "password": self.USER_PASSWORD, + # }, + # ) + + # def test_create_user(self, namespace: str): + # mdb = MongoDBUser.from_yaml( + # load_fixture("scram-sha-user.yaml"), + # namespace=namespace, + # ) + # mdb["spec"]["mongodbResourceRef"]["name"] = MDB_RESOURCE_NAME + + # mdb.update() + # mdb.assert_reaches_phase(Phase.Updated, timeout=150) + + # def test_ops_manager_state_with_users_correctly_updated(self, sharded_cluster: MongoDB): + # expected_roles = { + # ("admin", "clusterAdmin"), + # ("admin", "userAdminAnyDatabase"), + # ("admin", "readWrite"), + # ("admin", "userAdminAnyDatabase"), + # } + + # tester = sharded_cluster.get_automation_config_tester() + # tester.assert_has_user(self.USER_NAME) + # tester.assert_user_has_roles(self.USER_NAME, expected_roles) + # tester.assert_expected_users(1) + def test_switch_sharded_cluster_project( self, custom_mdb_version: str, sharded_cluster: MongoDB, namespace: str, project_name_prefix: str ): @@ -92,7 +133,7 @@ def test_switch_sharded_cluster_project( sharded_cluster.set_version(custom_mdb_version) sharded_cluster.update() - sharded_cluster.assert_reaches_phase(Phase.Running, timeout=600) + sharded_cluster.assert_reaches_phase(Phase.Running, timeout=800) def test_moved_sharded_cluster_connectivity(self): """ @@ -107,3 +148,16 @@ def test_ops_manager_state_correctly_updated_in_moved_cluster(self, sharded_clus tester = sharded_cluster.get_automation_config_tester() tester.assert_authentication_mechanism_enabled("SCRAM-SHA-256") tester.assert_authentication_enabled() + + # def test_ops_manager_state_with_users_correctly_updated_after_switch(self, sharded_cluster: MongoDB): + # expected_roles = { + # ("admin", "clusterAdmin"), + # ("admin", "userAdminAnyDatabase"), + # ("admin", "readWrite"), + # ("admin", "userAdminAnyDatabase"), + # } + + # tester = sharded_cluster.get_automation_config_tester() + # tester.assert_has_user(self.USER_NAME) + # tester.assert_user_has_roles(self.USER_NAME, expected_roles) + # tester.assert_expected_users(1) From 863e34748140abffc943ce483fe5fb40db8621ac Mon Sep 17 00:00:00 2001 From: filipcirtog Date: Thu, 6 Nov 2025 10:49:00 +0100 Subject: [PATCH 5/5] lint --- .../tests/authentication/replica_set_ldap_switch_project.py | 4 +++- .../replica_set_scram_sha_256_switch_project.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docker/mongodb-kubernetes-tests/tests/authentication/replica_set_ldap_switch_project.py b/docker/mongodb-kubernetes-tests/tests/authentication/replica_set_ldap_switch_project.py index b3f76e345..224cfff50 100644 --- a/docker/mongodb-kubernetes-tests/tests/authentication/replica_set_ldap_switch_project.py +++ b/docker/mongodb-kubernetes-tests/tests/authentication/replica_set_ldap_switch_project.py @@ -160,7 +160,9 @@ def test_switch_replica_set_project( replica_set.assert_reaches_phase(Phase.Running, timeout=600) - def test_ops_manager_state_correctly_updated_in_moved_cluster(self, replica_set: MongoDB, user_ldap: MongoDBUser, ca_path: str): + def test_ops_manager_state_correctly_updated_in_moved_cluster( + self, replica_set: MongoDB, user_ldap: MongoDBUser, ca_path: str + ): tester = replica_set.get_automation_config_tester() tester.assert_authentication_mechanism_enabled(LDAP_AUTHENTICATION_MECHANISM, active_auth_mechanism=True) # tester.assert_expected_users(1) diff --git a/docker/mongodb-kubernetes-tests/tests/authentication/replica_set_scram_sha_256_switch_project.py b/docker/mongodb-kubernetes-tests/tests/authentication/replica_set_scram_sha_256_switch_project.py index c9be17391..5cb9de414 100644 --- a/docker/mongodb-kubernetes-tests/tests/authentication/replica_set_scram_sha_256_switch_project.py +++ b/docker/mongodb-kubernetes-tests/tests/authentication/replica_set_scram_sha_256_switch_project.py @@ -49,7 +49,7 @@ class TestReplicaSetCreationAndProjectSwitch(KubernetesTester): """ E2E test suite for replica set creation, user connectivity with SCRAM-SHA-256 authentication and switching Ops Manager project reference. """ - + PASSWORD_SECRET_NAME = "mms-user-1-password" USER_PASSWORD = "my-password" USER_NAME = "mms-user-1"