diff --git a/CHANGELOG/CHANGELOG-1.13.md b/CHANGELOG/CHANGELOG-1.13.md index 6200def71..00f1c3173 100644 --- a/CHANGELOG/CHANGELOG-1.13.md +++ b/CHANGELOG/CHANGELOG-1.13.md @@ -18,3 +18,4 @@ When cutting a new release, update the `unreleased` heading to the tag being gen * [ENHANCEMENT] [#1203](https://github.com/k8ssandra/k8ssandra-operator/issues/1203) Add new setting ConcurrencyPolicy to MedusaBackupSchedules * [ENHANCEMENT] [#1209](https://github.com/k8ssandra/k8ssandra-operator/issues/1209) Expose the option to disable the cert-manager presence check in the Helm charts * [ENHANCEMENT] [#1206](https://github.com/k8ssandra/k8ssandra-operator/issues/1206) Allow setting https proxy for CRD upgrader job in Helm charts +* [ENHANCEMENT] [#1214](https://github.com/k8ssandra/k8ssandra-operator/issues/1214) Update Medusa to 0.19.0 and support mounting clientSecret to medusa container \ No newline at end of file diff --git a/controllers/k8ssandra/k8ssandracluster_controller_test.go b/controllers/k8ssandra/k8ssandracluster_controller_test.go index eb3431982..1484240ac 100644 --- a/controllers/k8ssandra/k8ssandracluster_controller_test.go +++ b/controllers/k8ssandra/k8ssandracluster_controller_test.go @@ -102,6 +102,7 @@ func TestK8ssandraCluster(t *testing.T) { t.Run("CreateMultiDcClusterWithReaper", testEnv.ControllerTest(ctx, createMultiDcClusterWithReaper)) t.Run("CreateMultiDcClusterWithMedusa", testEnv.ControllerTest(ctx, createMultiDcClusterWithMedusa)) t.Run("CreateSingleDcClusterWithMedusaConfigRef", testEnv.ControllerTest(ctx, createSingleDcClusterWithMedusaConfigRef)) + t.Run("CreateSingleDcClusterWithManagementApiSecured", testEnv.ControllerTest(ctx, createSingleDcClusterWithManagementApiSecured)) t.Run("CreatingSingleDcClusterWithoutPrefixInClusterSpecFail", testEnv.ControllerTest(ctx, creatingSingleDcClusterWithoutPrefixInClusterSpecFails)) t.Run("CreateMultiDcClusterWithReplicatedSecrets", testEnv.ControllerTest(ctx, createMultiDcClusterWithReplicatedSecrets)) t.Run("CreateSingleDcClusterNoAuth", testEnv.ControllerTest(ctx, createSingleDcClusterNoAuth)) diff --git a/controllers/k8ssandra/medusa_reconciler_test.go b/controllers/k8ssandra/medusa_reconciler_test.go index 70ea3f07f..91580a3e5 100644 --- a/controllers/k8ssandra/medusa_reconciler_test.go +++ b/controllers/k8ssandra/medusa_reconciler_test.go @@ -681,3 +681,52 @@ func verifyBucketSecretMounted(ctx context.Context, t *testing.T, f *framework.F // check its mount assert.True(t, f.ContainerHasVolumeMount(medusaContainer, clusterSecretName, "/etc/medusa-secrets"), "Missing Volume Mount for Medusa bucket key") } + +func createSingleDcClusterWithManagementApiSecured(t *testing.T, ctx context.Context, f *framework.Framework, namespace string) { + require := require.New(t) + kc := &api.K8ssandraCluster{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: k8ssandraClusterName, + }, + Spec: api.K8ssandraClusterSpec{ + Cassandra: &api.CassandraClusterTemplate{ + DatacenterOptions: api.DatacenterOptions{ + ManagementApiAuth: &cassdcapi.ManagementApiAuthConfig{ + Manual: &cassdcapi.ManagementApiAuthManualConfig{ + ClientSecretName: "test-client-secret", + }, + }, + }, + Datacenters: []api.CassandraDatacenterTemplate{ + dcTemplate("dc1", f.DataPlaneContexts[0]), + }, + }, + Medusa: medusaTemplateWithoutConfigRef(), + }, + } + + require.NoError(f.Client.Create(ctx, kc)) + verifyReplicatedSecretReconciled(ctx, t, f, kc) + + reconcileMedusaStandaloneDeployment(ctx, t, f, kc, "dc1", f.DataPlaneContexts[0]) + + dc1Key := framework.ClusterKey{NamespacedName: types.NamespacedName{Namespace: namespace, Name: "dc1"}, K8sContext: f.DataPlaneContexts[0]} + require.Eventually(f.DatacenterExists(ctx, dc1Key), timeout, interval) + + dc := &cassdcapi.CassandraDatacenter{} + require.NoError(f.Get(ctx, dc1Key, dc)) + + checkMedusaObjectsCompliance(t, f, dc, kc) + + containerIndex, found := cassandra.FindContainer(dc.Spec.PodTemplateSpec, "medusa") + require.True(found, fmt.Sprintf("%s doesn't have medusa container", dc.Name)) + mainContainer := dc.Spec.PodTemplateSpec.Spec.Containers[containerIndex] + + require.True(f.ContainerHasVolumeMount(mainContainer, "mgmt-encryption", "/etc/encryption/mgmt")) + + volumeIndex, foundMgmtEncryptionClient := cassandra.FindVolume(dc.Spec.PodTemplateSpec, "mgmt-encryption") + require.True(foundMgmtEncryptionClient) + vol := dc.Spec.PodTemplateSpec.Spec.Volumes[volumeIndex] + require.Equal(kc.Spec.Cassandra.DatacenterOptions.ManagementApiAuth.Manual.ClientSecretName, vol.Secret.SecretName) +} diff --git a/pkg/medusa/reconcile.go b/pkg/medusa/reconcile.go index a48f5c5bb..70d0384cb 100644 --- a/pkg/medusa/reconcile.go +++ b/pkg/medusa/reconcile.go @@ -27,7 +27,7 @@ import ( const ( DefaultMedusaImageRepository = "k8ssandra" DefaultMedusaImageName = "medusa" - DefaultMedusaVersion = "0.17.2" + DefaultMedusaVersion = "0.19.0" DefaultMedusaPort = 50051 DefaultProbeInitialDelay = 10 DefaultProbeTimeout = 1 @@ -122,6 +122,11 @@ func CreateMedusaIni(kc *k8ss.K8ssandraCluster) string { cassandra_url = http://127.0.0.1:8080/api/v0/ops/node/snapshots use_mgmt_api = 1 enabled = 1 + {{- if and .Spec.Cassandra.DatacenterOptions.ManagementApiAuth .Spec.Cassandra.DatacenterOptions.ManagementApiAuth.Manual }} + ca_cert = /etc/encryption/mgmt/ca.crt + tls_cert = /etc/encryption/mgmt/tls.crt + tls_key = /etc/encryption/mgmt/tls.key + {{- end }} [logging] level = DEBUG` @@ -171,7 +176,7 @@ func UpdateMedusaInitContainer(dcConfig *cassandra.DatacenterConfig, medusaSpec setImage(medusaSpec.ContainerImage, restoreContainer) restoreContainer.SecurityContext = medusaSpec.SecurityContext restoreContainer.Env = medusaEnvVars(medusaSpec, k8cName, useExternalSecrets, "RESTORE") - restoreContainer.VolumeMounts = medusaVolumeMounts(medusaSpec, k8cName) + restoreContainer.VolumeMounts = medusaVolumeMounts(dcConfig, medusaSpec, k8cName) restoreContainer.Resources = medusaInitContainerResources(medusaSpec) if !found { @@ -231,7 +236,7 @@ func CreateMedusaMainContainer(dcConfig *cassandra.DatacenterConfig, medusaSpec medusaContainer.ReadinessProbe = readinessProbe medusaContainer.LivenessProbe = livenessProbe - medusaContainer.VolumeMounts = medusaVolumeMounts(medusaSpec, k8cName) + medusaContainer.VolumeMounts = medusaVolumeMounts(dcConfig, medusaSpec, k8cName) medusaContainer.Resources = medusaMainContainerResources(medusaSpec) return medusaContainer, nil } @@ -265,7 +270,7 @@ func setImage(containerImage *images.Image, container *corev1.Container) { container.ImagePullPolicy = image.PullPolicy } -func medusaVolumeMounts(medusaSpec *api.MedusaClusterTemplate, k8cName string) []corev1.VolumeMount { +func medusaVolumeMounts(dcConfig *cassandra.DatacenterConfig, medusaSpec *api.MedusaClusterTemplate, k8cName string) []corev1.VolumeMount { volumeMounts := []corev1.VolumeMount{ { // Cassandra config volume Name: "server-config", @@ -301,6 +306,14 @@ func medusaVolumeMounts(medusaSpec *api.MedusaClusterTemplate, k8cName string) [ }) } + // Management-api encryption client certificates + if dcConfig.ManagementApiAuth != nil && dcConfig.ManagementApiAuth.Manual != nil { + volumeMounts = append(volumeMounts, corev1.VolumeMount{ + Name: "mgmt-encryption", + MountPath: "/etc/encryption/mgmt", + }) + } + return volumeMounts } @@ -445,6 +458,26 @@ func GenerateMedusaVolumes(dcConfig *cassandra.DatacenterConfig, medusaSpec *api Exists: found, }) } + + // Management-api client certificates + if dcConfig.ManagementApiAuth != nil && dcConfig.ManagementApiAuth.Manual != nil { + managementApiVolumeIndex, found := cassandra.FindVolume(&dcConfig.PodTemplateSpec, "mgmt-encryption") + managementApiVolume := &corev1.Volume{ + Name: "mgmt-encryption", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: dcConfig.ManagementApiAuth.Manual.ClientSecretName, + }, + }, + } + + newVolumes = append(newVolumes, medusaVolume{ + Volume: managementApiVolume, + VolumeIndex: managementApiVolumeIndex, + Exists: found, + }) + } + return newVolumes } diff --git a/pkg/medusa/reconcile_test.go b/pkg/medusa/reconcile_test.go index af072803a..9f1796ceb 100644 --- a/pkg/medusa/reconcile_test.go +++ b/pkg/medusa/reconcile_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/go-logr/logr" + cassdcapi "github.com/k8ssandra/cass-operator/apis/cassandra/v1beta1" api "github.com/k8ssandra/k8ssandra-operator/apis/k8ssandra/v1alpha1" medusaapi "github.com/k8ssandra/k8ssandra-operator/apis/medusa/v1alpha1" "github.com/k8ssandra/k8ssandra-operator/pkg/cassandra" @@ -160,6 +161,13 @@ func testMedusaIniSecured(t *testing.T) { }, Spec: api.K8ssandraClusterSpec{ Cassandra: &api.CassandraClusterTemplate{ + DatacenterOptions: api.DatacenterOptions{ + ManagementApiAuth: &cassdcapi.ManagementApiAuthConfig{ + Manual: &cassdcapi.ManagementApiAuthManualConfig{ + ClientSecretName: "test-client-secret", + }, + }, + }, Datacenters: []api.CassandraDatacenterTemplate{ { Meta: api.EmbeddedObjectMeta{ @@ -200,22 +208,26 @@ func testMedusaIniSecured(t *testing.T) { }, } + assert := assert.New(t) medusaIni := CreateMedusaIni(kc) - assert.Contains(t, medusaIni, "storage_provider = s3") - assert.Contains(t, medusaIni, "bucket_name = bucket") - assert.Contains(t, medusaIni, "prefix = demo") - assert.Contains(t, medusaIni, "max_backup_age = 10") - assert.Contains(t, medusaIni, "max_backup_count = 20") - assert.Contains(t, medusaIni, "api_profile = default") - assert.Contains(t, medusaIni, "transfer_max_bandwidth = 100MB/s") - assert.Contains(t, medusaIni, "concurrent_transfers = 2") - assert.Contains(t, medusaIni, "multi_part_upload_threshold = 204857600") - assert.Contains(t, medusaIni, "host = 192.168.0.1") - assert.Contains(t, medusaIni, "region = us-east-1") - assert.Contains(t, medusaIni, "port = 9001") - assert.Contains(t, medusaIni, "secure = True") - assert.Contains(t, medusaIni, "ssl_verify = True") - assert.Contains(t, medusaIni, "backup_grace_period_in_days = 7") + assert.Contains(medusaIni, "storage_provider = s3") + assert.Contains(medusaIni, "bucket_name = bucket") + assert.Contains(medusaIni, "prefix = demo") + assert.Contains(medusaIni, "max_backup_age = 10") + assert.Contains(medusaIni, "max_backup_count = 20") + assert.Contains(medusaIni, "api_profile = default") + assert.Contains(medusaIni, "transfer_max_bandwidth = 100MB/s") + assert.Contains(medusaIni, "concurrent_transfers = 2") + assert.Contains(medusaIni, "multi_part_upload_threshold = 204857600") + assert.Contains(medusaIni, "host = 192.168.0.1") + assert.Contains(medusaIni, "region = us-east-1") + assert.Contains(medusaIni, "port = 9001") + assert.Contains(medusaIni, "secure = True") + assert.Contains(medusaIni, "ssl_verify = True") + assert.Contains(medusaIni, "backup_grace_period_in_days = 7") + assert.Contains(medusaIni, "ca_cert = /etc/encryption/mgmt/ca.crt") + assert.Contains(medusaIni, "tls_cert = /etc/encryption/mgmt/tls.crt") + assert.Contains(medusaIni, "tls_key = /etc/encryption/mgmt/tls.key") } func testMedusaIniUnsecured(t *testing.T) {