diff --git a/apis/k8ssandra/v1alpha1/constants.go b/apis/k8ssandra/v1alpha1/constants.go index 2d61209e4..68b99364d 100644 --- a/apis/k8ssandra/v1alpha1/constants.go +++ b/apis/k8ssandra/v1alpha1/constants.go @@ -59,6 +59,8 @@ const ( K8ssandraClusterNamespaceLabel = "k8ssandra.io/cluster-namespace" DatacenterLabel = "k8ssandra.io/datacenter" + // Forces refresh of secrets which relate to roles and authn in Cassandra. + RefreshAnnotation = "k8ssandra.io/refresh" ) var ( diff --git a/controllers/medusa/medusarestorejob_controller.go b/controllers/medusa/medusarestorejob_controller.go index 3308acc96..822169086 100644 --- a/controllers/medusa/medusarestorejob_controller.go +++ b/controllers/medusa/medusarestorejob_controller.go @@ -177,6 +177,8 @@ func (r *MedusaRestoreJobReconciler) Reconcile(ctx context.Context, req ctrl.Req } request.Log.Info("The restore operation is complete") + medusa.RefreshSecrets(request.Datacenter, ctx, r.Client, request.Log, r.DefaultDelay) + return ctrl.Result{}, nil } diff --git a/pkg/medusa/refresh_secrets.go b/pkg/medusa/refresh_secrets.go new file mode 100644 index 000000000..62be2f8f3 --- /dev/null +++ b/pkg/medusa/refresh_secrets.go @@ -0,0 +1,49 @@ +package medusa + +import ( + "context" + "fmt" + "time" + + "github.com/go-logr/logr" + cassdcapi "github.com/k8ssandra/cass-operator/apis/cassandra/v1beta1" + k8ssandraapi "github.com/k8ssandra/k8ssandra-operator/apis/k8ssandra/v1alpha1" + "github.com/k8ssandra/k8ssandra-operator/pkg/reconciliation" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +const ( + defaultSUSecretName = "cass-superuser" +) + +func RefreshSecrets(dc *cassdcapi.CassandraDatacenter, ctx context.Context, client client.Client, logger logr.Logger, requeueDelay time.Duration) error { + logger.Info(fmt.Sprintf("Restore complete for DC %#v, Refreshing secrets", dc.ObjectMeta)) + userSecrets := []string{} + + for _, user := range dc.Spec.Users { + userSecrets = append(userSecrets, user.SecretName) + } + if dc.Spec.SuperuserSecretName == "" { + userSecrets = append(userSecrets, defaultSUSecretName) //default SU secret + } else { + userSecrets = append(userSecrets, dc.Spec.SuperuserSecretName) + } + // Both Reaper and medusa secrets go into the userSecrets, so they don't need special handling. + for _, i := range userSecrets { + secret := &corev1.Secret{} + err := client.Get(ctx, types.NamespacedName{Name: i, Namespace: dc.Namespace}, secret) + if err != nil { + logger.Error(err, fmt.Sprintf("Failed to get secret %s", i)) + return err + } + if secret.ObjectMeta.Annotations == nil { + secret.ObjectMeta.Annotations = make(map[string]string) + } + secret.ObjectMeta.Annotations[k8ssandraapi.RefreshAnnotation] = time.Now().String() + reconciliation.ReconcileObject(ctx, client, requeueDelay, *secret) + } + return nil + +} diff --git a/pkg/medusa/refresh_secrets_test.go b/pkg/medusa/refresh_secrets_test.go new file mode 100644 index 000000000..c06a15854 --- /dev/null +++ b/pkg/medusa/refresh_secrets_test.go @@ -0,0 +1,68 @@ +package medusa + +import ( + "context" + "testing" + + "github.com/go-logr/logr" + cassdcapi "github.com/k8ssandra/cass-operator/apis/cassandra/v1beta1" + "github.com/k8ssandra/k8ssandra-operator/pkg/test" + "github.com/stretchr/testify/assert" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" +) + +func TestRefreshSecrets_defaultSUSecret(t *testing.T) { + fakeClient := test.NewFakeClientWRestMapper() + cassDC := test.NewCassandraDatacenter("dc1", "test") + cassDC.Spec.Users = []cassdcapi.CassandraUser{ + {SecretName: "custom-user"}, + } + assert.NoError(t, fakeClient.Create(context.Background(), &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "test"}})) + assert.NoError(t, fakeClient.Create(context.Background(), &cassDC)) + secrets := []corev1.Secret{ + {ObjectMeta: metav1.ObjectMeta{Name: "custom-user", Namespace: "test"}, Data: map[string][]byte{"username": []byte("test")}}, + {ObjectMeta: metav1.ObjectMeta{Name: "cass-superuser", Namespace: "test"}, Data: map[string][]byte{"username": []byte("test")}}, + } + for _, i := range secrets { + assert.NoError(t, fakeClient.Create(context.Background(), &i)) + } + assert.NoError(t, RefreshSecrets(&cassDC, context.Background(), fakeClient, logr.Logger{}, 0)) + suSecret := &corev1.Secret{} + assert.NoError(t, fakeClient.Get(context.Background(), types.NamespacedName{Name: "cass-superuser", Namespace: "test"}, suSecret)) + _, exists := suSecret.ObjectMeta.Annotations["k8ssandra.io/refresh"] + assert.True(t, exists) + userSecret := &corev1.Secret{} + assert.NoError(t, fakeClient.Get(context.Background(), types.NamespacedName{Name: "custom-user", Namespace: "test"}, userSecret)) + _, exists = userSecret.ObjectMeta.Annotations["k8ssandra.io/refresh"] + assert.True(t, exists) +} + +func TestRefreshSecrets_customSecrets(t *testing.T) { + fakeClient := test.NewFakeClientWRestMapper() + cassDC := test.NewCassandraDatacenter("dc1", "test") + cassDC.Spec.Users = []cassdcapi.CassandraUser{ + {SecretName: "custom-user"}, + } + cassDC.Spec.SuperuserSecretName = "cass-custom-superuser" + assert.NoError(t, fakeClient.Create(context.Background(), &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "test"}})) + assert.NoError(t, fakeClient.Create(context.Background(), &cassDC)) + secrets := []corev1.Secret{ + {ObjectMeta: metav1.ObjectMeta{Name: "custom-user", Namespace: "test"}}, + {ObjectMeta: metav1.ObjectMeta{Name: "cass-custom-superuser", Namespace: "test"}}, + } + for _, i := range secrets { + assert.NoError(t, fakeClient.Create(context.Background(), &i)) + } + assert.NoError(t, RefreshSecrets(&cassDC, context.Background(), fakeClient, logr.Logger{}, 0)) + suSecret := &corev1.Secret{} + assert.NoError(t, fakeClient.Get(context.Background(), types.NamespacedName{Name: "cass-custom-superuser", Namespace: "test"}, suSecret)) + _, exists := suSecret.ObjectMeta.Annotations["k8ssandra.io/refresh"] + assert.True(t, exists) + userSecret := &corev1.Secret{} + assert.NoError(t, fakeClient.Get(context.Background(), types.NamespacedName{Name: "custom-user", Namespace: "test"}, userSecret)) + _, exists = userSecret.ObjectMeta.Annotations["k8ssandra.io/refresh"] + assert.True(t, exists) + +}