Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Completion of a restore triggers secrets refresh #1134

Merged
merged 26 commits into from
Jan 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
e8831f3
Completion of a restore triggers secrets refresh.
Miles-Garnsey Dec 11, 2023
1ce6ce1
Fix error check.
Miles-Garnsey Dec 11, 2023
8b29f68
Add a test to the envtests too.
Miles-Garnsey Dec 11, 2023
281b556
Fix envtest.
Miles-Garnsey Dec 11, 2023
85d9aed
Delete envtest test.
Miles-Garnsey Dec 11, 2023
111c5ff
Add e2e test.
Miles-Garnsey Dec 11, 2023
a0e6f02
more logging for medusa tests.
Miles-Garnsey Dec 11, 2023
227a42e
Add some logging to allow debugging.
Miles-Garnsey Dec 12, 2023
8277e18
Ensure right default username is used.
Miles-Garnsey Dec 12, 2023
66c45af
Deal with multiple different cluster names leading to different super…
Miles-Garnsey Dec 12, 2023
c4e1eee
Alex's suggested test change.
Miles-Garnsey Dec 13, 2023
5ec13af
Fix error introduced by last change.
Miles-Garnsey Dec 13, 2023
2166919
Fix tests so that they always retrieve the right secret.
Miles-Garnsey Dec 13, 2023
0cee309
Changelog.
Miles-Garnsey Dec 13, 2023
5d63dfb
More debugging.
Miles-Garnsey Dec 13, 2023
e922327
More debugging.
Miles-Garnsey Dec 14, 2023
9a218fb
Error handling, restore old logging style.
Miles-Garnsey Dec 14, 2023
f7019cb
Print version and commit information at startup.
Miles-Garnsey Dec 15, 2023
d4c5188
Reinstate other tests.
Miles-Garnsey Dec 15, 2023
d823aa4
Fix infinite requeue problem.
Miles-Garnsey Jan 4, 2024
824732d
Longer timeout for restore e2e test.
Miles-Garnsey Jan 4, 2024
c98f5f2
Ensure we always get most recent version of secret even after failure.
Miles-Garnsey Jan 4, 2024
a9e707f
Back out changes to e2e test timeout so we can get logs again.
Miles-Garnsey Jan 4, 2024
b28e8d1
Fix up build variables in makefile.
Miles-Garnsey Jan 5, 2024
2ba393e
Fix requeue handling and switch to making timestamp based on finishTi…
Miles-Garnsey Jan 8, 2024
905a912
Maybe let's use StartTime instead of finishTime to ensure that we don…
Miles-Garnsey Jan 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG/CHANGELOG-1.10.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ When cutting a new release, update the `unreleased` heading to the tag being gen

## unreleased

* [FEATURE] [#1080](https://github.com/k8ssandra/k8ssandra-operator/issues/1080) When a restore is completed, an annotation is added to the user secrets that will cause cass-operator to refresh them on the Cassandra side.

## v1.10.3 - 2023-11-15

* [BUGFIX] [#1110](https://github.com/k8ssandra/k8ssandra-operator/issues/1110) Fix cluster name being set to "Test Cluster" when running Cassandra 4.1+
Expand Down
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,11 @@ endif

##@ Build

DATE ?= $(shell date)
COMMIT ?= $(shell git rev-parse --short HEAD)

build: generate fmt vet ## Build manager binary.
go build -o bin/manager main.go
go build --ldflags "-X \"main.version=${VERSION}\" -X \"main.date=${DATE}\" -X \"main.commit=${COMMIT}\"" -o bin/manager main.go

run: manifests generate fmt vet ## Run a controller from your host.
go run ./main.go
Expand Down
2 changes: 2 additions & 0 deletions apis/k8ssandra/v1alpha1/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down
11 changes: 10 additions & 1 deletion controllers/medusa/medusarestorejob_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,16 @@
return ctrl.Result{RequeueAfter: r.DefaultDelay}, err
}

request.Log.Info("The restore operation is complete")
request.Log.Info("The restore operation is complete for DC", "CassandraDatacenter", request.Datacenter.Name)

recRes := medusa.RefreshSecrets(request.Datacenter, ctx, r.Client, request.Log, r.DefaultDelay, request.RestoreJob.Status.StartTime)
switch {
case recRes.IsError():
return ctrl.Result{RequeueAfter: r.DefaultDelay}, recRes.GetError()
case recRes.IsRequeue():
return ctrl.Result{RequeueAfter: r.DefaultDelay}, nil

Check warning on line 186 in controllers/medusa/medusarestorejob_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/medusa/medusarestorejob_controller.go#L185-L186

Added lines #L185 - L186 were not covered by tests
}

return ctrl.Result{}, nil
}

Expand Down
3 changes: 2 additions & 1 deletion controllers/medusa/medusarestorejob_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -292,8 +292,9 @@ func testMedusaRestoreDatacenter(t *testing.T, ctx context.Context, f *framework
return !restore.Status.FinishTime.IsZero()
}, timeout, interval)

err = f.DeleteK8ssandraCluster(ctx, client.ObjectKey{Namespace: kc.Namespace, Name: kc.Name}, timeout, interval)
err = f.DeleteK8ssandraCluster(ctx, client.ObjectKey{Namespace: dc.Namespace, Name: kc.Name}, timeout, interval)
require.NoError(err, "failed to delete K8ssandraCluster")

}

func testValidationErrorStopsRestore(t *testing.T, ctx context.Context, f *framework.Framework, namespace string) {
Expand Down
11 changes: 11 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,15 @@ import (
// +kubebuilder:scaffold:imports
)

var (
version = "dev"
commit = "n/a"
date = "n/a"
versionMessage = "#######################" +
fmt.Sprintf("#### version %s commit %s date %s ####", version, commit, date) +
"#######################"
Comment on lines +71 to +76
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this shouldn't be part of this PR I guess.

)

var (
scheme = runtime.NewScheme()
setupLog = ctrl.Log.WithName("setup")
Expand Down Expand Up @@ -114,6 +123,8 @@ func main() {
setupLog.Info("watch namespace configured", "namespace", watchNamespace)
}

setupLog.Info(versionMessage)

options := ctrl.Options{
Scheme: scheme,
MetricsBindAddress: metricsAddr,
Expand Down
63 changes: 63 additions & 0 deletions pkg/medusa/refresh_secrets.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
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"
"github.com/k8ssandra/k8ssandra-operator/pkg/result"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
)

func RefreshSecrets(dc *cassdcapi.CassandraDatacenter, ctx context.Context, client client.Client, logger logr.Logger, requeueDelay time.Duration, restoreTimestamp metav1.Time) result.ReconcileResult {
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, cassdcapi.CleanupForKubernetes(dc.Spec.ClusterName)+"-superuser") //default SU secret
} else {
userSecrets = append(userSecrets, dc.Spec.SuperuserSecretName)
}
logger.Info(fmt.Sprintf("refreshing user secrets for %v", userSecrets))
// Both Reaper and medusa secrets go into the userSecrets, so they don't need special handling.
requeues := 0
for _, i := range userSecrets {
secret := &corev1.Secret{}
// We need to do our own retries here instead of delegating it back up to the reconciler, because of
// the nature (time based) of the annotation we're adding. Otherwise we never complete because the
// object on the server never matches the desired object with the new time.
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 result.Error(err)
}

Check warning on line 43 in pkg/medusa/refresh_secrets.go

View check run for this annotation

Codecov / codecov/patch

pkg/medusa/refresh_secrets.go#L41-L43

Added lines #L41 - L43 were not covered by tests
if secret.ObjectMeta.Annotations == nil {
secret.ObjectMeta.Annotations = make(map[string]string)
}
secret.ObjectMeta.Annotations[k8ssandraapi.RefreshAnnotation] = restoreTimestamp.String()
recRes := reconciliation.ReconcileObject(ctx, client, requeueDelay, *secret)
switch {
case recRes.IsError():
return recRes

Check warning on line 51 in pkg/medusa/refresh_secrets.go

View check run for this annotation

Codecov / codecov/patch

pkg/medusa/refresh_secrets.go#L50-L51

Added lines #L50 - L51 were not covered by tests
case recRes.IsRequeue():
requeues++
continue
case recRes.IsDone():
continue

Check warning on line 56 in pkg/medusa/refresh_secrets.go

View check run for this annotation

Codecov / codecov/patch

pkg/medusa/refresh_secrets.go#L55-L56

Added lines #L55 - L56 were not covered by tests
}
if requeues > 0 {
return result.RequeueSoon(requeueDelay)
}

Check warning on line 60 in pkg/medusa/refresh_secrets.go

View check run for this annotation

Codecov / codecov/patch

pkg/medusa/refresh_secrets.go#L58-L60

Added lines #L58 - L60 were not covered by tests
}
return result.Done()
}
72 changes: 72 additions & 0 deletions pkg/medusa/refresh_secrets_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
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: "test-cluster-superuser", Namespace: "test"}, Data: map[string][]byte{"username": []byte("test")}},
}
for _, i := range secrets {
assert.NoError(t, fakeClient.Create(context.Background(), &i))
}

recRes := RefreshSecrets(&cassDC, context.Background(), fakeClient, logr.Logger{}, 0, metav1.Now())
assert.True(t, recRes.IsDone())
suSecret := &corev1.Secret{}
assert.NoError(t, fakeClient.Get(context.Background(), types.NamespacedName{Name: "test-cluster-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))
}

recRes := RefreshSecrets(&cassDC, context.Background(), fakeClient, logr.Logger{}, 0, metav1.Now())
assert.True(t, recRes.IsDone())
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)

}
23 changes: 23 additions & 0 deletions test/e2e/medusa_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

cassdcapi "github.com/k8ssandra/cass-operator/apis/cassandra/v1beta1"
api "github.com/k8ssandra/k8ssandra-operator/apis/k8ssandra/v1alpha1"
k8ssandraapi "github.com/k8ssandra/k8ssandra-operator/apis/k8ssandra/v1alpha1"
medusa "github.com/k8ssandra/k8ssandra-operator/apis/medusa/v1alpha1"
"github.com/k8ssandra/k8ssandra-operator/pkg/cassandra"
medusapkg "github.com/k8ssandra/k8ssandra-operator/pkg/medusa"
Expand Down Expand Up @@ -211,6 +212,28 @@ func verifyRestoreJobFinished(t *testing.T, ctx context.Context, f *framework.E2

return !restore.Status.FinishTime.IsZero()
}, polling.medusaRestoreDone.timeout, polling.medusaRestoreDone.interval, "restore didn't finish within timeout")

require.Eventually(func() bool {
dc := &cassdcapi.CassandraDatacenter{}
err := f.Get(ctx, dcKey, dc)
if err != nil {
t.Log(err)
return false
}
superUserSecret := dc.Spec.SuperuserSecretName
if dc.Spec.SuperuserSecretName == "" {
superUserSecret = cassdcapi.CleanupForKubernetes(dcKey.Name) + "-superuser"
}
secret := &corev1.Secret{}
err = f.Get(ctx, framework.NewClusterKey(restoreClusterKey.K8sContext, restoreClusterKey.Namespace, superUserSecret), secret)
if err != nil {
t.Log(err)
return false
}
_, exists := secret.Annotations[k8ssandraapi.RefreshAnnotation]
return exists
}, polling.medusaRestoreDone.timeout, polling.medusaRestoreDone.interval, "superuser secret wasn't updated with refresh annotation")

}

func checkMedusaStandaloneDeploymentExists(t *testing.T, ctx context.Context, dcKey framework.ClusterKey, f *framework.E2eFramework, kc *api.K8ssandraCluster) {
Expand Down
Loading