Skip to content

Commit e7b9b59

Browse files
committed
add MariaDBAccount finalizer to Secret (and remove on delete)
in mariadbdatabase_funcs, the EnsureMariaDBAccount function called by external controllers adds a finalizer for that calling controller to the Secret referenced by the MariaDBAccount. This seems a little off, since the Secret is most immediately needed by the MariaDBAccount CR itself, and the controller refers to that MariaDBAccount CR also. It seems more appropriate that MariaDBAccount itself should maintain its own finalizer on that Secret, so this logic is added there. The existing API logic that adds the calling controllers finalizer is not changed here but maybe at some point we could simplfy this. This comes up now because we are seeking to add a new class of system-level MariaDBAccount that is used only by the Galera controller itself, but also that these accounts (really all accounts, but mainly the system ones) will support in-place password changes by updating the name of the Secret to be used, implying the old one is no longer needed once the change takes place; it therefore is most appropriate that MariaDBAccount maintain its own finalizers on these secrets. While the plan is that Galera controller will probably also use EnsureMariaDBAccount and thus add a Galera object finalizer, we may seek to modify this for system accounts so that the extra non-mariadbaccount finalizers aren't added in this case.
1 parent 968eea6 commit e7b9b59

File tree

4 files changed

+126
-33
lines changed

4 files changed

+126
-33
lines changed

api/v1beta1/mariadbdatabase_funcs.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,8 @@ func (d *Database) DeleteFinalizer(
449449
h *helper.Helper,
450450
) error {
451451

452+
// TODO: if we rely only on MariaDBAccount local secret finalizer, this would
453+
// not be needed
452454
if d.secretObj != nil && controllerutil.RemoveFinalizer(d.secretObj, h.GetFinalizer()) {
453455
err := h.GetClient().Update(ctx, d.secretObj)
454456
if err != nil && !k8s_errors.IsNotFound(err) {
@@ -548,6 +550,8 @@ func DeleteDatabaseAndAccountFinalizers(
548550
return err
549551
}
550552

553+
// TODO: if we rely only on MariaDBAccount local secret finalizer, this would
554+
// not be needed
551555
if err == nil && controllerutil.RemoveFinalizer(dbSecret, h.GetFinalizer()) {
552556
err := h.GetClient().Update(ctx, dbSecret)
553557
if err != nil && !k8s_errors.IsNotFound(err) {
@@ -624,6 +628,8 @@ func DeleteUnusedMariaDBAccountFinalizers(
624628
return err
625629
}
626630

631+
// TODO: if we rely only on MariaDBAccount local secret finalizer, this would
632+
// not be needed
627633
if dbSecret != nil && controllerutil.RemoveFinalizer(dbSecret, h.GetFinalizer()) {
628634
err := h.GetClient().Update(ctx, dbSecret)
629635
if err != nil && !k8s_errors.IsNotFound(err) {
@@ -708,6 +714,9 @@ func createOrPatchAccountAndSecret(
708714
// GetDatabaseByNameAndAccount to locate the Database which is how
709715
// they remove finalizers. this will return not found if secret
710716
// is not present, so finalizer will keep it around
717+
718+
// TODO: since MariaDBAccount adds a finalizer to Secret, we may
719+
// want to look at removing this
711720
controllerutil.AddFinalizer(accountSecret, h.GetFinalizer())
712721

713722
return nil

controllers/mariadbaccount_controller.go

Lines changed: 92 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,12 @@ import (
2727
helper "github.com/openstack-k8s-operators/lib-common/modules/common/helper"
2828
job "github.com/openstack-k8s-operators/lib-common/modules/common/job"
2929
"github.com/openstack-k8s-operators/lib-common/modules/common/secret"
30+
util "github.com/openstack-k8s-operators/lib-common/modules/common/util"
3031
databasev1beta1 "github.com/openstack-k8s-operators/mariadb-operator/api/v1beta1"
3132
mariadb "github.com/openstack-k8s-operators/mariadb-operator/pkg/mariadb"
3233
k8s_errors "k8s.io/apimachinery/pkg/api/errors"
3334
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3435
"k8s.io/apimachinery/pkg/runtime"
35-
"k8s.io/apimachinery/pkg/types"
3636
"k8s.io/client-go/kubernetes"
3737
ctrl "sigs.k8s.io/controller-runtime"
3838
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -163,27 +163,10 @@ func (r *MariaDBAccountReconciler) reconcileCreate(
163163

164164
// account create
165165

166-
// ensure secret is present before running a job
167-
_, secretResult, err := secret.VerifySecret(
168-
ctx,
169-
types.NamespacedName{Name: instance.Spec.Secret, Namespace: instance.Namespace},
170-
[]string{databasev1beta1.DatabasePasswordSelector},
171-
r.Client,
172-
time.Duration(30)*time.Second,
173-
)
174-
if (err != nil || secretResult != ctrl.Result{}) {
175-
176-
instance.Status.Conditions.Set(condition.FalseCondition(
177-
databasev1beta1.MariaDBAccountReadyCondition,
178-
secret.ReasonSecretMissing,
179-
condition.SeverityInfo,
180-
databasev1beta1.MariaDBAccountSecretNotReadyMessage, err))
181-
182-
log.Info(fmt.Sprintf(
183-
"MariaDBAccount '%s' didn't find Secret '%s'; requeueing",
184-
instance.Name, instance.Spec.Secret))
185-
186-
return secretResult, client.IgnoreNotFound(err)
166+
// ensure secret is present, add a finalizer
167+
result, err = r.ensureAccountSecret(ctx, log, helper, instance)
168+
if (result != ctrl.Result{} || err != nil) {
169+
return result, err
187170
}
188171

189172
log.Info(fmt.Sprintf("Running account create '%s' MariaDBDatabase '%s'",
@@ -296,9 +279,8 @@ func (r *MariaDBAccountReconciler) reconcileDelete(
296279
}
297280

298281
// remove local finalizer
299-
controllerutil.RemoveFinalizer(instance, helper.GetFinalizer())
300-
301-
return ctrl.Result{}, nil
282+
err = r.removeAccountAndSecretFinalizer(ctx, helper, instance)
283+
return ctrl.Result{}, err
302284
} else if dbGalera == nil {
303285
return result, err
304286
}
@@ -345,9 +327,9 @@ func (r *MariaDBAccountReconciler) reconcileDelete(
345327
}
346328

347329
// then remove finalizer from our own instance
348-
controllerutil.RemoveFinalizer(instance, helper.GetFinalizer())
330+
err = r.removeAccountAndSecretFinalizer(ctx, helper, instance)
349331

350-
return ctrl.Result{}, nil
332+
return ctrl.Result{}, err
351333
}
352334

353335
// getMariaDBDatabaseForCreate - waits for a MariaDBDatabase to be available in preparation
@@ -440,8 +422,9 @@ func (r *MariaDBAccountReconciler) getMariaDBDatabaseForDelete(ctx context.Conte
440422
instance.Name,
441423
))
442424

443-
controllerutil.RemoveFinalizer(instance, helper.GetFinalizer())
444-
return nil, ctrl.Result{}, nil
425+
// remove local finalizer
426+
err := r.removeAccountAndSecretFinalizer(ctx, helper, instance)
427+
return nil, ctrl.Result{}, err
445428
}
446429

447430
// locate the MariaDBDatabase object itself
@@ -455,8 +438,9 @@ func (r *MariaDBAccountReconciler) getMariaDBDatabaseForDelete(ctx context.Conte
455438
"MariaDBAccount '%s' Didn't find MariaDBDatabase '%s'; no account delete needed",
456439
instance.Name, mariadbDatabaseName))
457440

458-
controllerutil.RemoveFinalizer(instance, helper.GetFinalizer())
459-
return nil, ctrl.Result{}, nil
441+
// remove local finalizer
442+
err = r.removeAccountAndSecretFinalizer(ctx, helper, instance)
443+
return nil, ctrl.Result{}, err
460444
} else {
461445
// unhandled error; exit without change
462446
log.Error(err, "unhandled error retrieving MariaDBDatabase instance")
@@ -486,8 +470,9 @@ func (r *MariaDBAccountReconciler) getMariaDBDatabaseForDelete(ctx context.Conte
486470
}
487471
}
488472

489-
controllerutil.RemoveFinalizer(instance, helper.GetFinalizer())
490-
return nil, ctrl.Result{}, nil
473+
// remove local finalizer
474+
err = r.removeAccountAndSecretFinalizer(ctx, helper, instance)
475+
return nil, ctrl.Result{}, err
491476
}
492477

493478
// return MariaDBDatabase where account delete flow will then continue
@@ -566,3 +551,77 @@ func (r *MariaDBAccountReconciler) getMariaDBDatabaseObject(ctx context.Context,
566551
return mariaDBDatabase, nil
567552

568553
}
554+
555+
// ensureAccountSecret - ensures the Secret exists, is valid, adds a finalizer.
556+
// includes requeue for secret does not exist
557+
func (r *MariaDBAccountReconciler) ensureAccountSecret(
558+
ctx context.Context,
559+
log logr.Logger,
560+
h *helper.Helper,
561+
instance *databasev1beta1.MariaDBAccount,
562+
) (ctrl.Result, error) {
563+
564+
secretName := instance.Spec.Secret
565+
secretNamespace := instance.Namespace
566+
secretObj, _, err := secret.GetSecret(ctx, h, secretName, secretNamespace)
567+
if err != nil {
568+
if k8s_errors.IsNotFound(err) {
569+
instance.Status.Conditions.Set(condition.FalseCondition(
570+
databasev1beta1.MariaDBAccountReadyCondition,
571+
secret.ReasonSecretMissing,
572+
condition.SeverityInfo,
573+
databasev1beta1.MariaDBAccountSecretNotReadyMessage, err))
574+
575+
log.Info(fmt.Sprintf(
576+
"MariaDBAccount '%s' didn't find Secret '%s'; requeueing",
577+
instance.Name, instance.Spec.Secret))
578+
579+
return ctrl.Result{RequeueAfter: time.Duration(30) * time.Second}, nil
580+
581+
} else {
582+
return ctrl.Result{}, err
583+
}
584+
}
585+
586+
var expectedFields = []string{databasev1beta1.DatabasePasswordSelector}
587+
588+
// collect the secret values the caller expects to exist
589+
for _, field := range expectedFields {
590+
_, ok := secretObj.Data[field]
591+
if !ok {
592+
err := fmt.Errorf("%w: field %s not found in Secret %s", util.ErrFieldNotFound, field, secretName)
593+
return ctrl.Result{}, err
594+
}
595+
}
596+
if controllerutil.AddFinalizer(secretObj, h.GetFinalizer()) {
597+
err = r.Update(ctx, secretObj)
598+
if err != nil {
599+
return ctrl.Result{}, err
600+
}
601+
}
602+
603+
return ctrl.Result{}, err
604+
}
605+
606+
// removeAccountAndSecretFinalizer - removes finalizer from mariadbaccount as well
607+
// as current primary secret
608+
func (r *MariaDBAccountReconciler) removeAccountAndSecretFinalizer(ctx context.Context,
609+
helper *helper.Helper, instance *databasev1beta1.MariaDBAccount) error {
610+
controllerutil.RemoveFinalizer(instance, helper.GetFinalizer())
611+
612+
accountSecret, _, err := secret.GetSecret(ctx, helper, instance.Spec.Secret, instance.Namespace)
613+
614+
if err == nil {
615+
if controllerutil.RemoveFinalizer(accountSecret, helper.GetFinalizer()) {
616+
err = r.Update(ctx, accountSecret)
617+
if err != nil {
618+
return err
619+
}
620+
}
621+
} else if !k8s_errors.IsNotFound(err) {
622+
return err
623+
}
624+
625+
return nil
626+
627+
}

tests/chainsaw/tests/account/chainsaw-test.yaml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ spec:
2525

2626
- name: create account without secret
2727
description: account CR has to wait for a secret to create account in the database
28+
# we will delete the account manually
29+
skipDelete: true
2830
try:
2931
- apply:
3032
file: account.yaml
@@ -33,8 +35,20 @@ spec:
3335

3436
- name: add secret and finish account creation
3537
description: make sure the account is created in the database
38+
# we will delete the secret manually
39+
skipDelete: true
3640
try:
3741
- apply:
3842
file: account-secret.yaml
3943
- assert:
4044
file: account-assert.yaml
45+
finally:
46+
- delete:
47+
# delete account first, otherwise the secret can't be deleted b.c.
48+
# it has a finalizer from the account
49+
# perhaps I shouldn't have allowed MariaDBAccount to be created without
50+
# a secret, but here we are...
51+
file: account.yaml
52+
- delete:
53+
# then delete the secret
54+
file: account-secret.yaml

tests/kuttl/tests/account_create/04-assert.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,14 @@ status:
2323
reason: Ready
2424
status: "True"
2525
type: MariaDBServerReady
26+
---
27+
apiVersion: v1
28+
data:
29+
DatabasePassword: ZGJzZWNyZXQx
30+
kind: Secret
31+
metadata:
32+
name: some-db-secret
33+
# ensure finalizer was added
34+
finalizers:
35+
- openstack.org/mariadbaccount
36+
type: Opaque

0 commit comments

Comments
 (0)