Skip to content

Commit 2c79615

Browse files
authored
CLOUDP-131647: Update to stateful set ignored existing volumes (#1064)
* CLOUDP-131647: Update to stateful set ignored existing volumes * Release notes, review fixes
1 parent 5c78a24 commit 2c79615

File tree

7 files changed

+181
-37
lines changed

7 files changed

+181
-37
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,4 @@ diagnostics
9999

100100
Pipfile
101101
Pipfile.lock
102+
.community-operator-dev

controllers/mongodb_tls_test.go

+74-19
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88

99
mdbv1 "github.com/mongodb/mongodb-kubernetes-operator/api/v1"
1010
"github.com/mongodb/mongodb-kubernetes-operator/pkg/automationconfig"
11-
"github.com/mongodb/mongodb-kubernetes-operator/pkg/kube/client"
11+
kubeClient "github.com/mongodb/mongodb-kubernetes-operator/pkg/kube/client"
1212
mdbClient "github.com/mongodb/mongodb-kubernetes-operator/pkg/kube/client"
1313
"github.com/mongodb/mongodb-kubernetes-operator/pkg/kube/configmap"
1414
"github.com/mongodb/mongodb-kubernetes-operator/pkg/kube/secret"
@@ -21,7 +21,7 @@ import (
2121

2222
func TestStatefulSet_IsCorrectlyConfiguredWithTLS(t *testing.T) {
2323
mdb := newTestReplicaSetWithTLS()
24-
mgr := client.NewManager(&mdb)
24+
mgr := kubeClient.NewManager(&mdb)
2525

2626
client := mdbClient.NewClient(mgr.GetClient())
2727
err := createTLSSecret(client, mdb, "CERT", "KEY", "")
@@ -37,14 +37,17 @@ func TestStatefulSet_IsCorrectlyConfiguredWithTLS(t *testing.T) {
3737
err = mgr.GetClient().Get(context.TODO(), types.NamespacedName{Name: mdb.Name, Namespace: mdb.Namespace}, &sts)
3838
assert.NoError(t, err)
3939

40-
// Assert that all TLS volumes have been added.
40+
assertStatefulsetVolumesAndVolumeMounts(t, sts, mdb.TLSOperatorCASecretNamespacedName().Name, mdb.TLSOperatorSecretNamespacedName().Name)
41+
}
42+
43+
func assertStatefulsetVolumesAndVolumeMounts(t *testing.T, sts appsv1.StatefulSet, expectedTLSCASecretName string, expectedTLSOperatorSecretName string) {
4144
assert.Len(t, sts.Spec.Template.Spec.Volumes, 7)
4245
permission := int32(416)
4346
assert.Contains(t, sts.Spec.Template.Spec.Volumes, corev1.Volume{
4447
Name: "tls-ca",
4548
VolumeSource: corev1.VolumeSource{
4649
Secret: &corev1.SecretVolumeSource{
47-
SecretName: mdb.TLSOperatorCASecretNamespacedName().Name,
50+
SecretName: expectedTLSCASecretName,
4851
DefaultMode: &permission,
4952
},
5053
},
@@ -53,7 +56,7 @@ func TestStatefulSet_IsCorrectlyConfiguredWithTLS(t *testing.T) {
5356
Name: "tls-secret",
5457
VolumeSource: corev1.VolumeSource{
5558
Secret: &corev1.SecretVolumeSource{
56-
SecretName: mdb.TLSOperatorSecretNamespacedName().Name,
59+
SecretName: expectedTLSOperatorSecretName,
5760
DefaultMode: &permission,
5861
},
5962
},
@@ -81,9 +84,57 @@ func TestStatefulSet_IsCorrectlyConfiguredWithTLS(t *testing.T) {
8184
assert.Contains(t, mongodbContainer.VolumeMounts, tlsCAVolumeMount)
8285
}
8386

87+
func TestStatefulSet_IsCorrectlyConfiguredWithTLSAfterChangingExistingVolumes(t *testing.T) {
88+
mdb := newTestReplicaSetWithTLS()
89+
mgr := kubeClient.NewManager(&mdb)
90+
91+
cli := mdbClient.NewClient(mgr.GetClient())
92+
err := createTLSSecret(cli, mdb, "CERT", "KEY", "")
93+
assert.NoError(t, err)
94+
95+
tlsCAVolumeSecretName := mdb.TLSOperatorCASecretNamespacedName().Name
96+
changedTLSCAVolumeSecretName := tlsCAVolumeSecretName + "-old"
97+
98+
err = createTLSSecretWithNamespaceAndName(cli, mdb.Namespace, changedTLSCAVolumeSecretName, "CERT", "KEY", "")
99+
assert.NoError(t, err)
100+
101+
err = createTLSConfigMap(cli, mdb)
102+
assert.NoError(t, err)
103+
104+
r := NewReconciler(mgr)
105+
res, err := r.Reconcile(context.TODO(), reconcile.Request{NamespacedName: types.NamespacedName{Namespace: mdb.Namespace, Name: mdb.Name}})
106+
assertReconciliationSuccessful(t, res, err)
107+
108+
sts := appsv1.StatefulSet{}
109+
err = mgr.GetClient().Get(context.TODO(), types.NamespacedName{Name: mdb.Name, Namespace: mdb.Namespace}, &sts)
110+
assert.NoError(t, err)
111+
112+
assertStatefulsetVolumesAndVolumeMounts(t, sts, tlsCAVolumeSecretName, mdb.TLSOperatorSecretNamespacedName().Name)
113+
114+
// updating sts tls-ca volume directly to simulate changing of underlying volume's secret
115+
for i := range sts.Spec.Template.Spec.Volumes {
116+
if sts.Spec.Template.Spec.Volumes[i].Name == "tls-ca" {
117+
sts.Spec.Template.Spec.Volumes[i].VolumeSource.Secret.SecretName = changedTLSCAVolumeSecretName
118+
}
119+
}
120+
121+
err = mgr.GetClient().Update(context.TODO(), &sts)
122+
assert.NoError(t, err)
123+
124+
assertStatefulsetVolumesAndVolumeMounts(t, sts, changedTLSCAVolumeSecretName, mdb.TLSOperatorSecretNamespacedName().Name)
125+
126+
res, err = r.Reconcile(context.TODO(), reconcile.Request{NamespacedName: types.NamespacedName{Namespace: mdb.Namespace, Name: mdb.Name}})
127+
assertReconciliationSuccessful(t, res, err)
128+
129+
sts = appsv1.StatefulSet{}
130+
err = mgr.GetClient().Get(context.TODO(), types.NamespacedName{Name: mdb.Name, Namespace: mdb.Namespace}, &sts)
131+
assert.NoError(t, err)
132+
assertStatefulsetVolumesAndVolumeMounts(t, sts, tlsCAVolumeSecretName, mdb.TLSOperatorSecretNamespacedName().Name)
133+
}
134+
84135
func TestAutomationConfig_IsCorrectlyConfiguredWithTLS(t *testing.T) {
85136
createAC := func(mdb mdbv1.MongoDBCommunity) automationconfig.AutomationConfig {
86-
client := mdbClient.NewClient(client.NewManager(&mdb).GetClient())
137+
client := mdbClient.NewClient(kubeClient.NewManager(&mdb).GetClient())
87138
err := createTLSSecret(client, mdb, "CERT", "KEY", "")
88139
assert.NoError(t, err)
89140
err = createTLSConfigMap(client, mdb)
@@ -154,13 +205,13 @@ func TestAutomationConfig_IsCorrectlyConfiguredWithTLS(t *testing.T) {
154205
func TestTLSOperatorSecret(t *testing.T) {
155206
t.Run("Secret is created if it doesn't exist", func(t *testing.T) {
156207
mdb := newTestReplicaSetWithTLS()
157-
c := mdbClient.NewClient(client.NewManager(&mdb).GetClient())
208+
c := mdbClient.NewClient(kubeClient.NewManager(&mdb).GetClient())
158209
err := createTLSSecret(c, mdb, "CERT", "KEY", "")
159210
assert.NoError(t, err)
160211
err = createTLSConfigMap(c, mdb)
161212
assert.NoError(t, err)
162213

163-
r := NewReconciler(client.NewManagerWithClient(c))
214+
r := NewReconciler(kubeClient.NewManagerWithClient(c))
164215

165216
err = r.ensureTLSResources(mdb)
166217
assert.NoError(t, err)
@@ -175,7 +226,7 @@ func TestTLSOperatorSecret(t *testing.T) {
175226

176227
t.Run("Secret is updated if it already exists", func(t *testing.T) {
177228
mdb := newTestReplicaSetWithTLS()
178-
k8sclient := mdbClient.NewClient(client.NewManager(&mdb).GetClient())
229+
k8sclient := mdbClient.NewClient(kubeClient.NewManager(&mdb).GetClient())
179230
err := createTLSSecret(k8sclient, mdb, "CERT", "KEY", "")
180231
assert.NoError(t, err)
181232
err = createTLSConfigMap(k8sclient, mdb)
@@ -190,7 +241,7 @@ func TestTLSOperatorSecret(t *testing.T) {
190241
err = k8sclient.CreateSecret(s)
191242
assert.NoError(t, err)
192243

193-
r := NewReconciler(client.NewManagerWithClient(k8sclient))
244+
r := NewReconciler(kubeClient.NewManagerWithClient(k8sclient))
194245

195246
err = r.ensureTLSResources(mdb)
196247
assert.NoError(t, err)
@@ -226,13 +277,13 @@ func TestCombineCertificateAndKey(t *testing.T) {
226277
func TestPemSupport(t *testing.T) {
227278
t.Run("Success if only pem is provided", func(t *testing.T) {
228279
mdb := newTestReplicaSetWithTLS()
229-
c := mdbClient.NewClient(client.NewManager(&mdb).GetClient())
280+
c := mdbClient.NewClient(kubeClient.NewManager(&mdb).GetClient())
230281
err := createTLSSecret(c, mdb, "", "", "CERT\nKEY")
231282
assert.NoError(t, err)
232283
err = createTLSConfigMap(c, mdb)
233284
assert.NoError(t, err)
234285

235-
r := NewReconciler(client.NewManagerWithClient(c))
286+
r := NewReconciler(kubeClient.NewManagerWithClient(c))
236287

237288
err = r.ensureTLSResources(mdb)
238289
assert.NoError(t, err)
@@ -246,13 +297,13 @@ func TestPemSupport(t *testing.T) {
246297
})
247298
t.Run("Success if pem is equal to cert+key", func(t *testing.T) {
248299
mdb := newTestReplicaSetWithTLS()
249-
c := mdbClient.NewClient(client.NewManager(&mdb).GetClient())
300+
c := mdbClient.NewClient(kubeClient.NewManager(&mdb).GetClient())
250301
err := createTLSSecret(c, mdb, "CERT", "KEY", "CERT\nKEY")
251302
assert.NoError(t, err)
252303
err = createTLSConfigMap(c, mdb)
253304
assert.NoError(t, err)
254305

255-
r := NewReconciler(client.NewManagerWithClient(c))
306+
r := NewReconciler(kubeClient.NewManagerWithClient(c))
256307

257308
err = r.ensureTLSResources(mdb)
258309
assert.NoError(t, err)
@@ -266,13 +317,13 @@ func TestPemSupport(t *testing.T) {
266317
})
267318
t.Run("Failure if pem is different from cert+key", func(t *testing.T) {
268319
mdb := newTestReplicaSetWithTLS()
269-
c := mdbClient.NewClient(client.NewManager(&mdb).GetClient())
320+
c := mdbClient.NewClient(kubeClient.NewManager(&mdb).GetClient())
270321
err := createTLSSecret(c, mdb, "CERT1", "KEY1", "CERT\nKEY")
271322
assert.NoError(t, err)
272323
err = createTLSConfigMap(c, mdb)
273324
assert.NoError(t, err)
274325

275-
r := NewReconciler(client.NewManagerWithClient(c))
326+
r := NewReconciler(kubeClient.NewManagerWithClient(c))
276327

277328
err = r.ensureTLSResources(mdb)
278329
assert.Error(t, err)
@@ -295,10 +346,10 @@ func createTLSConfigMap(c k8sClient.Client, mdb mdbv1.MongoDBCommunity) error {
295346
return c.Create(context.TODO(), &configMap)
296347
}
297348

298-
func createTLSSecret(c k8sClient.Client, mdb mdbv1.MongoDBCommunity, crt string, key string, pem string) error {
349+
func createTLSSecretWithNamespaceAndName(c k8sClient.Client, namespace string, name string, crt string, key string, pem string) error {
299350
sBuilder := secret.Builder().
300-
SetName(mdb.Spec.Security.TLS.CertificateKeySecret.Name).
301-
SetNamespace(mdb.Namespace)
351+
SetName(name).
352+
SetNamespace(namespace)
302353

303354
if crt != "" {
304355
sBuilder.SetField(tlsSecretCertName, crt)
@@ -314,6 +365,10 @@ func createTLSSecret(c k8sClient.Client, mdb mdbv1.MongoDBCommunity, crt string,
314365
return c.Create(context.TODO(), &s)
315366
}
316367

368+
func createTLSSecret(c k8sClient.Client, mdb mdbv1.MongoDBCommunity, crt string, key string, pem string) error {
369+
return createTLSSecretWithNamespaceAndName(c, mdb.Namespace, mdb.Spec.Security.TLS.CertificateKeySecret.Name, crt, key, pem)
370+
}
371+
317372
func createUserPasswordSecret(c k8sClient.Client, mdb mdbv1.MongoDBCommunity, userPasswordSecretName string, password string) error {
318373
sBuilder := secret.Builder().
319374
SetName(userPasswordSecretName).

docs/RELEASE_NOTES.md

+15
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
1+
# MongoDB Kubernetes Operator 0.7.5
2+
3+
## Upgrade breaking change notice
4+
Versions 0.7.3, 0.7.4 have an issue that breaks deployment of MongoDB replica set when:
5+
* TLS is enabled
6+
* Replica set was deployed using the operator with version <=0.7.2
7+
8+
If above conditions are met, it is strongly advised to upgrade the MongoDB Kubernetes Operator to version 0.7.5 or higher.
9+
10+
## Kubernetes Operator
11+
12+
- Bug fixes
13+
- Fixed ignoring changes to existing volumes in the StatefulSet, i.e. changes of the volumes' underlying secret. This could cause that TLS enabled MongoDB deployment was not able to locate TLS certificates when upgrading the operator to versions 0.7.3 or 0.7.4.
14+
15+
116
# MongoDB Kubernetes Operator 0.7.4
217

318
## Kubernetes Operator

pkg/kube/podtemplatespec/podspec_template.go

+9-13
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package podtemplatespec
22

33
import (
44
"github.com/mongodb/mongodb-kubernetes-operator/pkg/kube/container"
5+
"github.com/mongodb/mongodb-kubernetes-operator/pkg/util/merge"
56
corev1 "k8s.io/api/core/v1"
67
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
78
)
@@ -107,30 +108,25 @@ func WithServiceAccount(serviceAccountName string) Modification {
107108
}
108109
}
109110

110-
// WithVolumes appends the given volumes to the existing volume, it ensures not
111-
// to override existing volumes
111+
// WithVolumes ensures given volume is present in the PodTemplateSpec. It merges the volumes with existing ones.
112112
func WithVolumes(volumes []corev1.Volume) Modification {
113113
return func(template *corev1.PodTemplateSpec) {
114-
present := make(map[string]struct{})
115-
for _, v := range template.Spec.Volumes {
116-
present[v.Name] = struct{}{}
117-
}
118-
for _, v := range volumes {
119-
if _, ok := present[v.Name]; !ok {
120-
template.Spec.Volumes = append(template.Spec.Volumes, v)
121-
}
114+
for _, volume := range volumes {
115+
WithVolume(volume)(template)
122116
}
123117
}
124118
}
125119

126-
// WithVolume ensures the given volume exists
120+
// WithVolume ensures given volume is present in the PodTemplateSpec. It merges the volume if it already exists.
127121
func WithVolume(volume corev1.Volume) Modification {
128122
return func(template *corev1.PodTemplateSpec) {
129-
for _, v := range template.Spec.Volumes {
130-
if v.Name == volume.Name {
123+
for i := range template.Spec.Volumes {
124+
if template.Spec.Volumes[i].Name == volume.Name {
125+
template.Spec.Volumes[i] = merge.Volume(template.Spec.Volumes[i], volume)
131126
return
132127
}
133128
}
129+
134130
template.Spec.Volumes = append(template.Spec.Volumes, volume)
135131
}
136132
}

pkg/kube/podtemplatespec/podspec_template_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -369,9 +369,9 @@ func TestAddVolumes(t *testing.T) {
369369

370370
p := New(volumeModification, volumesModification)
371371
assert.Len(t, p.Spec.Volumes, 2)
372-
assert.Equal(t, p.Spec.Volumes[0].Name, "new-volume")
373-
assert.Equal(t, p.Spec.Volumes[1].Name, "new-volume-2")
374-
372+
assert.Equal(t, "new-volume", p.Spec.Volumes[0].Name)
373+
assert.Equal(t, "new-volume-2", p.Spec.Volumes[1].Name)
374+
assert.Equal(t, "new-host-path", p.Spec.Volumes[0].VolumeSource.HostPath.Path)
375375
}
376376

377377
func int64Ref(i int64) *int64 {

test/e2e/replica_set_operator_upgrade/replica_set_operator_upgrade_test.go

+59-2
Original file line numberDiff line numberDiff line change
@@ -41,19 +41,76 @@ func TestReplicaSetOperatorUpgrade(t *testing.T) {
4141
t.Run("Basic tests", mongodbtests.BasicFunctionality(&mdb, true))
4242
t.Run("Keyfile authentication is configured", tester.HasKeyfileAuth(3))
4343
t.Run("Test Basic Connectivity", tester.ConnectivitySucceeds())
44-
t.Run("Test SRV Connectivity", tester.ConnectivitySucceeds(WithURI(mdb.MongoSRVURI("")), WithoutTls(), WithReplicaSet((mdb.Name))))
44+
t.Run("Test SRV Connectivity", tester.ConnectivitySucceeds(WithURI(mdb.MongoSRVURI("")), WithoutTls(), WithReplicaSet(mdb.Name)))
4545
t.Run("Test Basic Connectivity with generated connection string secret",
4646
tester.ConnectivitySucceeds(WithURI(mongodbtests.GetConnectionStringForUser(mdb, scramUser))))
4747
t.Run("Test SRV Connectivity with generated connection string secret",
4848
tester.ConnectivitySucceeds(WithURI(mongodbtests.GetSrvConnectionStringForUser(mdb, scramUser))))
4949
t.Run("Ensure Authentication", tester.EnsureAuthenticationIsConfigured(3))
5050
t.Run("AutomationConfig has the correct version", mongodbtests.AutomationConfigVersionHasTheExpectedVersion(&mdb, 1))
5151

52-
// upgrade the uperator to master
52+
// upgrade the operator to master
5353
config := setup.LoadTestConfigFromEnv()
5454
err = setup.DeployOperator(config, "mdb", false, false)
5555
assert.NoError(t, err)
5656

5757
// Perform the basic tests
5858
t.Run("Basic tests", mongodbtests.BasicFunctionality(&mdb, true))
5959
}
60+
61+
// TestReplicaSetOperatorUpgradeFrom0_7_2 is intended to be run locally not in CI.
62+
// It simulates deploying cluster using community operator 0.7.2 and then upgrading it using newer version.
63+
func TestReplicaSetOperatorUpgradeFrom0_7_2(t *testing.T) {
64+
t.Skip("Supporting this test in CI requires installing also CRDs from release v0.7.2")
65+
resourceName := "mdb-upg"
66+
testConfig := setup.LoadTestConfigFromEnv()
67+
68+
// deploy operator and other components as it was at version 0.7.2
69+
testConfig.OperatorImage = "quay.io/mongodb/mongodb-kubernetes-operator:0.7.2"
70+
testConfig.VersionUpgradeHookImage = "quay.io/mongodb/mongodb-kubernetes-operator-version-upgrade-post-start-hook:1.0.3"
71+
testConfig.ReadinessProbeImage = "quay.io/mongodb/mongodb-kubernetes-readinessprobe:1.0.6"
72+
testConfig.AgentImage = "quay.io/mongodb/mongodb-agent:11.0.5.6963-1"
73+
74+
ctx := setup.SetupWithTestConfig(t, testConfig, true, resourceName)
75+
defer ctx.Teardown()
76+
77+
mdb, user := e2eutil.NewTestMongoDB(ctx, resourceName, "")
78+
scramUser := mdb.GetScramUsers()[0]
79+
mdb.Spec.Security.TLS = e2eutil.NewTestTLSConfig(false)
80+
81+
_, err := setup.GeneratePasswordForUser(ctx, user, "")
82+
if err != nil {
83+
t.Fatal(err)
84+
}
85+
86+
tester, err := FromResource(t, mdb)
87+
if err != nil {
88+
t.Fatal(err)
89+
}
90+
91+
runTests := func(t *testing.T) {
92+
t.Run("Create MongoDB Resource", mongodbtests.CreateMongoDBResource(&mdb, ctx))
93+
t.Run("Basic tests", mongodbtests.BasicFunctionality(&mdb, true))
94+
t.Run("AutomationConfig has the correct version", mongodbtests.AutomationConfigVersionHasTheExpectedVersion(&mdb, 1))
95+
t.Run("Keyfile authentication is configured", tester.HasKeyfileAuth(3))
96+
t.Run("Has TLS Mode", tester.HasTlsMode("requireSSL", 60, WithTls(mdb)))
97+
t.Run("Test Basic Connectivity", tester.ConnectivitySucceeds())
98+
t.Run("Test SRV Connectivity", tester.ConnectivitySucceeds(WithURI(mdb.MongoSRVURI("")), WithoutTls(), WithReplicaSet(mdb.Name)))
99+
t.Run("Test Basic Connectivity with generated connection string secret",
100+
tester.ConnectivitySucceeds(WithURI(mongodbtests.GetConnectionStringForUser(mdb, scramUser))))
101+
t.Run("Test SRV Connectivity with generated connection string secret",
102+
tester.ConnectivitySucceeds(WithURI(mongodbtests.GetSrvConnectionStringForUser(mdb, scramUser))))
103+
t.Run("Ensure Authentication", tester.EnsureAuthenticationIsConfigured(3))
104+
}
105+
106+
runTests(t)
107+
108+
// When running against local operator we could stop here,
109+
// rescale helm operator deployment to zero and run local operator then.
110+
111+
testConfig = setup.LoadTestConfigFromEnv()
112+
err = setup.DeployOperator(testConfig, resourceName, true, false)
113+
assert.NoError(t, err)
114+
115+
runTests(t)
116+
}

0 commit comments

Comments
 (0)