diff --git a/mongodb-community-operator/config/samples/mongodb.com_v1_mongodbcommunity_persistent_volume_claim_retention_policy.yaml b/mongodb-community-operator/config/samples/mongodb.com_v1_mongodbcommunity_persistent_volume_claim_retention_policy.yaml new file mode 100644 index 000000000..fe4d83edf --- /dev/null +++ b/mongodb-community-operator/config/samples/mongodb.com_v1_mongodbcommunity_persistent_volume_claim_retention_policy.yaml @@ -0,0 +1,46 @@ +--- +apiVersion: mongodbcommunity.mongodb.com/v1 +kind: MongoDBCommunity +metadata: + name: mongodb-specify-volumeclaimretention-values +spec: + members: 3 + type: ReplicaSet + version: "6.0.5" + security: + authentication: + modes: ["SCRAM"] + users: + - name: my-user + db: admin + passwordSecretRef: # a reference to the secret that will be used to generate the user's password + name: my-user-password + roles: + - name: clusterAdmin + db: admin + - name: userAdminAnyDatabase + db: admin + scramCredentialsSecretName: my-scram + statefulSet: + spec: + persistentVolumeClaimRetentionPolicy: + WhenDeleted: "Delete" + WhenScaled: "Delete" + template: + spec: + containers: + - name: mongodb-agent + readinessProbe: + failureThreshold: 50 + initialDelaySeconds: 10 + +# the user credentials will be generated from this secret +# once the credentials are generated, this secret is no longer required +--- +apiVersion: v1 +kind: Secret +metadata: + name: my-user-password +type: Opaque +stringData: + password: diff --git a/mongodb-community-operator/pkg/util/merge/merge_statefulset.go b/mongodb-community-operator/pkg/util/merge/merge_statefulset.go index 834d33546..66bc3c6e9 100644 --- a/mongodb-community-operator/pkg/util/merge/merge_statefulset.go +++ b/mongodb-community-operator/pkg/util/merge/merge_statefulset.go @@ -53,6 +53,12 @@ func StatefulSetSpecs(defaultSpec, overrideSpec appsv1.StatefulSetSpec) appsv1.S mergedSpec.ServiceName = overrideSpec.ServiceName } + if overrideSpec.PersistentVolumeClaimRetentionPolicy != nil { + overridePersistentVolumeClaimRetentionPolicy := appsv1.StatefulSetPersistentVolumeClaimRetentionPolicy{WhenDeleted: overrideSpec.PersistentVolumeClaimRetentionPolicy.WhenDeleted, + WhenScaled: overrideSpec.PersistentVolumeClaimRetentionPolicy.WhenScaled} + mergedSpec.PersistentVolumeClaimRetentionPolicy = &overridePersistentVolumeClaimRetentionPolicy + } + mergedSpec.Template = PodTemplateSpecs(defaultSpec.Template, overrideSpec.Template) mergedSpec.VolumeClaimTemplates = VolumeClaimTemplates(defaultSpec.VolumeClaimTemplates, overrideSpec.VolumeClaimTemplates) return mergedSpec diff --git a/mongodb-community-operator/test/e2e/mongodbtests/mongodbtests.go b/mongodb-community-operator/test/e2e/mongodbtests/mongodbtests.go index 529fe069e..372999e9b 100644 --- a/mongodb-community-operator/test/e2e/mongodbtests/mongodbtests.go +++ b/mongodb-community-operator/test/e2e/mongodbtests/mongodbtests.go @@ -219,6 +219,18 @@ func StatefulSetHasUpdateStrategy(ctx context.Context, mdb *mdbv1.MongoDBCommuni } } +// StatefulSetHasPersistentVolumeClaimRetentionPolicy verifies that the StatefulSet holding this MongoDB +// resource has the correct PersistentVolumeClaim Retention Policy +func StatefulSetHasPersistentVolumeClaimRetentionPolicy(ctx context.Context, mdb *mdbv1.MongoDBCommunity, persistentVolumeClaimRetentionPolicy appsv1.StatefulSetPersistentVolumeClaimRetentionPolicy) func(t *testing.T) { + return func(t *testing.T) { + err := wait.ForStatefulSetToHavePersistentVolumeClaimRetentionPolicy(ctx, t, mdb, persistentVolumeClaimRetentionPolicy, wait.RetryInterval(time.Second*15), wait.Timeout(time.Minute*8)) + if err != nil { + t.Fatal(err) + } + t.Logf("StatefulSet %s/%s is ready!", mdb.Namespace, mdb.Name) + } +} + // GetPersistentVolumes returns all persistent volumes on the cluster func getPersistentVolumesList(ctx context.Context) (*corev1.PersistentVolumeList, error) { return e2eutil.TestClient.CoreV1Client.PersistentVolumes().List(ctx, metav1.ListOptions{}) diff --git a/mongodb-community-operator/test/e2e/replica_set_custom_persistentvolumeclaimretentionpolicy_test/replica_set_custom_persistentvolumeclaimretentionpolicy_test.go b/mongodb-community-operator/test/e2e/replica_set_custom_persistentvolumeclaimretentionpolicy_test/replica_set_custom_persistentvolumeclaimretentionpolicy_test.go new file mode 100644 index 000000000..541149a22 --- /dev/null +++ b/mongodb-community-operator/test/e2e/replica_set_custom_persistentvolumeclaimretentionpolicy_test/replica_set_custom_persistentvolumeclaimretentionpolicy_test.go @@ -0,0 +1,56 @@ +package replica_set_custom_persistentvolumclaimretentionpolicy_test + +import ( + "context" + "fmt" + "os" + "testing" + + e2eutil "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/test/e2e" + "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/test/e2e/mongodbtests" + "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/test/e2e/setup" + . "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/test/e2e/util/mongotester" + appsv1 "k8s.io/api/apps/v1" +) + +func TestMain(m *testing.M) { + code, err := e2eutil.RunTest(m) + if err != nil { + fmt.Println(err) + } + os.Exit(code) +} + +func TestReplicaSetCustomPersistentVolumeClaimRetentionPolicy(t *testing.T) { + ctx := context.Background() + testCtx := setup.Setup(ctx, t) + defer testCtx.Teardown() + + mdb, user := e2eutil.NewTestMongoDB(testCtx, "mdb0", "") + overridePersistentVolumeClaim := appsv1.StatefulSetPersistentVolumeClaimRetentionPolicy{WhenDeleted: "Delete", WhenScaled: "Delete"} + mdb.Spec.StatefulSetConfiguration.SpecWrapper.Spec.PersistentVolumeClaimRetentionPolicy = &overridePersistentVolumeClaim + scramUser := mdb.GetAuthUsers()[0] + + _, err := setup.GeneratePasswordForUser(testCtx, user, "") + if err != nil { + t.Fatal(err) + } + + tester, err := FromResource(ctx, t, mdb) + if err != nil { + t.Fatal(err) + } + + t.Run("Create MongoDB Resource", mongodbtests.CreateMongoDBResource(&mdb, testCtx)) + t.Run("Basic tests", mongodbtests.BasicFunctionality(ctx, &mdb)) + t.Run("Keyfile authentication is configured", tester.HasKeyfileAuth(3)) + t.Run("Test Basic Connectivity", tester.ConnectivitySucceeds()) + t.Run("Test SRV Connectivity", tester.ConnectivitySucceeds(WithURI(mdb.MongoSRVURI("")), WithoutTls(), WithReplicaSet((mdb.Name)))) + t.Run("Test Basic Connectivity with generated connection string secret", + tester.ConnectivitySucceeds(WithURI(mongodbtests.GetConnectionStringForUser(ctx, mdb, scramUser)))) + t.Run("Test SRV Connectivity with generated connection string secret", + tester.ConnectivitySucceeds(WithURI(mongodbtests.GetSrvConnectionStringForUser(ctx, mdb, scramUser)))) + t.Run("Ensure Authentication", tester.EnsureAuthenticationIsConfigured(3)) + t.Run("AutomationConfig has the correct version", mongodbtests.AutomationConfigVersionHasTheExpectedVersion(ctx, &mdb, 1)) + t.Run("Statefulset has the expected PersistentVolumeClaimRetentionPolicy", mongodbtests.StatefulSetHasPersistentVolumeClaimRetentionPolicy(ctx, &mdb, overridePersistentVolumeClaim)) +} diff --git a/mongodb-community-operator/test/e2e/util/wait/wait.go b/mongodb-community-operator/test/e2e/util/wait/wait.go index 37512664f..2d1022c55 100644 --- a/mongodb-community-operator/test/e2e/util/wait/wait.go +++ b/mongodb-community-operator/test/e2e/util/wait/wait.go @@ -98,6 +98,18 @@ func ForStatefulSetToHaveUpdateStrategy(ctx context.Context, t *testing.T, mdb * }) } +// ForStatefulSetToHavePersistentVolumeClaimRetentionPolicy waits until all replicas of the StatefulSet with the given name +// have reached the ready status +func ForStatefulSetToHavePersistentVolumeClaimRetentionPolicy(ctx context.Context, t *testing.T, mdb *mdbv1.MongoDBCommunity, persistentVolumeClaimRetentionPolicy appsv1.StatefulSetPersistentVolumeClaimRetentionPolicy, opts ...Configuration) error { + options := newOptions(opts...) + return waitForStatefulSetCondition(ctx, t, mdb, options, func(sts appsv1.StatefulSet) bool { + if sts.Spec.PersistentVolumeClaimRetentionPolicy.WhenScaled == persistentVolumeClaimRetentionPolicy.WhenScaled && sts.Spec.PersistentVolumeClaimRetentionPolicy.WhenDeleted == persistentVolumeClaimRetentionPolicy.WhenDeleted { + return true + } + return false + }) +} + // ForStatefulSetToBeReady waits until all replicas of the StatefulSet with the given name // have reached the ready status func ForStatefulSetToBeReady(ctx context.Context, t *testing.T, mdb *mdbv1.MongoDBCommunity, opts ...Configuration) error { diff --git a/pkg/statefulset/merge_statefulset_mco_test.go b/pkg/statefulset/merge_statefulset_mco_test.go index af5c6286f..1da01d844 100644 --- a/pkg/statefulset/merge_statefulset_mco_test.go +++ b/pkg/statefulset/merge_statefulset_mco_test.go @@ -65,6 +65,7 @@ func TestMergeSpec_MCO(t *testing.T) { WithReplicas(3), WithRevisionHistoryLimit(10), WithPodManagementPolicyType(appsv1.OrderedReadyPodManagement), + WithPersistentVolumeClaimRetentionPolicy(appsv1.StatefulSetPersistentVolumeClaimRetentionPolicy{}), WithSelector(&metav1.LabelSelector{ MatchLabels: map[string]string{ "a": "1", @@ -93,6 +94,7 @@ func TestMergeSpec_MCO(t *testing.T) { WithReplicas(5), WithRevisionHistoryLimit(15), WithPodManagementPolicyType(appsv1.ParallelPodManagement), + WithPersistentVolumeClaimRetentionPolicy(appsv1.StatefulSetPersistentVolumeClaimRetentionPolicy{WhenDeleted: "Delete", WhenScaled: "Delete"}), WithSelector(&metav1.LabelSelector{ MatchLabels: map[string]string{ "a": "10", @@ -122,6 +124,7 @@ func TestMergeSpec_MCO(t *testing.T) { assert.Equal(t, int32(5), *mergedSpec.Replicas) assert.Equal(t, int32(15), *mergedSpec.RevisionHistoryLimit) assert.Equal(t, appsv1.ParallelPodManagement, mergedSpec.PodManagementPolicy) + assert.Equal(t, appsv1.StatefulSetPersistentVolumeClaimRetentionPolicy{WhenDeleted: "Delete", WhenScaled: "Delete"}, *mergedSpec.PersistentVolumeClaimRetentionPolicy) }) matchLabels := mergedSpec.Selector.MatchLabels diff --git a/pkg/statefulset/statefulset.go b/pkg/statefulset/statefulset.go index 86cc7e079..829aa2e4e 100644 --- a/pkg/statefulset/statefulset.go +++ b/pkg/statefulset/statefulset.go @@ -314,3 +314,9 @@ func ResetUpdateStrategy(ctx context.Context, mdb annotations.Versioned, kubeCli }) return err } + +func WithPersistentVolumeClaimRetentionPolicy(persistentVolumeClaimRetentionPolicy appsv1.StatefulSetPersistentVolumeClaimRetentionPolicy) Modification { + return func(set *appsv1.StatefulSet) { + set.Spec.PersistentVolumeClaimRetentionPolicy = &persistentVolumeClaimRetentionPolicy + } +}