diff --git a/CHANGELOG.md b/CHANGELOG.md index 3739fd76..fe68355c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ Changelog for Cass Operator, new PRs should update the `main / unreleased` secti * [FEATURE] [#651](https://github.com/k8ssandra/cass-operator/issues/651) Add tsreload task for DSE deployments and ability to check if sync operation is available on the mgmt-api side * [FEATURE] [#701](https://github.com/k8ssandra/cass-operator/issues/701) Allow ReadOnlyRootFilesystem for DSE also with extra mounts to provide support for cass-config-builder setups +* [BUGFIX] [#708](https://github.com/k8ssandra/cass-operator/issues/708) Preserve existing SecurityContext when ReadOnlyRootFilesystem is set ## v1.22.2 diff --git a/pkg/reconciliation/construct_podtemplatespec.go b/pkg/reconciliation/construct_podtemplatespec.go index 17934205..4caa80ed 100644 --- a/pkg/reconciliation/construct_podtemplatespec.go +++ b/pkg/reconciliation/construct_podtemplatespec.go @@ -706,9 +706,10 @@ func buildContainers(dc *api.CassandraDatacenter, baseTemplate *corev1.PodTempla } if dc.ReadOnlyFs() { - cassContainer.SecurityContext = &corev1.SecurityContext{ - ReadOnlyRootFilesystem: ptr.To[bool](true), + if cassContainer.SecurityContext == nil { + cassContainer.SecurityContext = &corev1.SecurityContext{} } + cassContainer.SecurityContext.ReadOnlyRootFilesystem = ptr.To[bool](true) } // Combine env vars diff --git a/pkg/reconciliation/construct_podtemplatespec_test.go b/pkg/reconciliation/construct_podtemplatespec_test.go index 293a26c5..43ef4265 100644 --- a/pkg/reconciliation/construct_podtemplatespec_test.go +++ b/pkg/reconciliation/construct_podtemplatespec_test.go @@ -2310,3 +2310,90 @@ func TestReadOnlyRootFilesystemVolumeChangesDSEWithClient(t *testing.T) { mcacDisabled := corev1.EnvVar{Name: "MGMT_API_DISABLE_MCAC", Value: "true"} assert.True(envVarsContains(containers[0].Env, mcacDisabled)) } + +func TestReadOnlyRootFilesystemWithSecurityContext(t *testing.T) { + assert := assert.New(t) + dc := &api.CassandraDatacenter{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + api.UseClientBuilderAnnotation: "true", + }, + }, + Spec: api.CassandraDatacenterSpec{ + ClusterName: "bob", + ServerType: "dse", + ServerVersion: "6.9.2", + PodTemplateSpec: &corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + InitContainers: []corev1.Container{ + { + Name: ServerBaseConfigContainerName, + SecurityContext: &corev1.SecurityContext{ + Capabilities: &corev1.Capabilities{Drop: []corev1.Capability{"ALL"}}, + }, + }, + { + Name: ServerConfigContainerName, + SecurityContext: &corev1.SecurityContext{ + Capabilities: &corev1.Capabilities{Drop: []corev1.Capability{"ALL"}}, + }, + }, + }, + Containers: []corev1.Container{ + { + Name: CassandraContainerName, + SecurityContext: &corev1.SecurityContext{ + Capabilities: &corev1.Capabilities{Drop: []corev1.Capability{"ALL"}}, + }, + }, + { + Name: SystemLoggerContainerName, + SecurityContext: &corev1.SecurityContext{ + Capabilities: &corev1.Capabilities{Drop: []corev1.Capability{"ALL"}}, + }, + }, + }, + }, + }, + ReadOnlyRootFilesystem: ptr.To[bool](true), + Racks: []api.Rack{ + { + Name: "r1", + }, + }, + }, + } + + podTemplateSpec, err := buildPodTemplateSpec(dc, dc.Spec.Racks[0], false) + assert.NoError(err, "failed to build PodTemplateSpec") + + initContainers := podTemplateSpec.Spec.InitContainers + assert.Len(initContainers, 2, "Unexpected number of init containers returned") + + containers := podTemplateSpec.Spec.Containers + assert.Len(containers, 2, "Unexpected number of containers returned") + + // The cassandra container should get readOnlyRootFilesystem from the top-level field, but also retain the + // capabilities from the podTemplateSpec. + assert.Equal(CassandraContainerName, containers[0].Name) + assert.True(reflect.DeepEqual(containers[0].SecurityContext, &corev1.SecurityContext{ + ReadOnlyRootFilesystem: ptr.To[bool](true), + Capabilities: &corev1.Capabilities{Drop: []corev1.Capability{"ALL"}}, + })) + + // Other containers should just get the podTemplateSpec contents. + assert.Equal(ServerBaseConfigContainerName, initContainers[0].Name) + assert.True(reflect.DeepEqual(initContainers[0].SecurityContext, &corev1.SecurityContext{ + Capabilities: &corev1.Capabilities{Drop: []corev1.Capability{"ALL"}}, + })) + + assert.Equal(ServerConfigContainerName, initContainers[1].Name) + assert.True(reflect.DeepEqual(initContainers[1].SecurityContext, &corev1.SecurityContext{ + Capabilities: &corev1.Capabilities{Drop: []corev1.Capability{"ALL"}}, + })) + + assert.Equal(SystemLoggerContainerName, containers[1].Name) + assert.True(reflect.DeepEqual(containers[1].SecurityContext, &corev1.SecurityContext{ + Capabilities: &corev1.Capabilities{Drop: []corev1.Capability{"ALL"}}, + })) +}