Skip to content

Commit 23e764e

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 change here causes the API function EnsureMariaDBAccount to add a finalizer to the secret that is local to the mariadbaccount, rather than the helper passed for the calling controller. Existing "remove finalizer" calls which look for the calling controller's finalizer tag in the secret are maintained however to assist with backwards compatibility. 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.
1 parent 8f997c8 commit 23e764e

File tree

4 files changed

+130
-35
lines changed

4 files changed

+130
-35
lines changed

api/v1beta1/mariadbdatabase_funcs.go

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

452+
// LEGACY: remove finalizer from the secret in terms of the caller.
453+
// we now don't add the caller's finalizer to the secret, we only add
454+
// the mariadbaccount finalizer.
452455
if d.secretObj != nil && controllerutil.RemoveFinalizer(d.secretObj, h.GetFinalizer()) {
453456
err := h.GetClient().Update(ctx, d.secretObj)
454457
if err != nil && !k8s_errors.IsNotFound(err) {
@@ -548,6 +551,9 @@ func DeleteDatabaseAndAccountFinalizers(
548551
return err
549552
}
550553

554+
// LEGACY: remove finalizer from the secret in terms of the caller.
555+
// we now don't add the caller's finalizer to the secret, we only add
556+
// the mariadbaccount finalizer.
551557
if err == nil && controllerutil.RemoveFinalizer(dbSecret, h.GetFinalizer()) {
552558
err := h.GetClient().Update(ctx, dbSecret)
553559
if err != nil && !k8s_errors.IsNotFound(err) {
@@ -624,6 +630,9 @@ func DeleteUnusedMariaDBAccountFinalizers(
624630
return err
625631
}
626632

633+
// LEGACY: remove finalizer from the secret in terms of the caller.
634+
// we now don't add the caller's finalizer to the secret, we only add
635+
// the mariadbaccount finalizer.
627636
if dbSecret != nil && controllerutil.RemoveFinalizer(dbSecret, h.GetFinalizer()) {
628637
err := h.GetClient().Update(ctx, dbSecret)
629638
if err != nil && !k8s_errors.IsNotFound(err) {
@@ -708,7 +717,6 @@ func createOrPatchAccountAndSecret(
708717
// GetDatabaseByNameAndAccount to locate the Database which is how
709718
// they remove finalizers. this will return not found if secret
710719
// is not present, so finalizer will keep it around
711-
controllerutil.AddFinalizer(accountSecret, h.GetFinalizer())
712720

713721
return nil
714722
})

controllers/mariadbaccount_controller.go

Lines changed: 96 additions & 34 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"
@@ -168,28 +168,10 @@ func (r *MariaDBAccountReconciler) reconcileCreate(
168168

169169
// account create
170170

171-
// ensure secret is present before running a job
172-
_, secretResult, err := secret.VerifySecret(
173-
ctx,
174-
types.NamespacedName{Name: instance.Spec.Secret, Namespace: instance.Namespace},
175-
[]string{databasev1beta1.DatabasePasswordSelector},
176-
r.Client,
177-
time.Duration(30)*time.Second,
178-
)
179-
if (err != nil || secretResult != ctrl.Result{}) {
180-
// Since the account secret should have been manually created by the user and referenced in the spec,
181-
// we treat this as a warning because it means that the service will not be able to start.
182-
instance.Status.Conditions.Set(condition.FalseCondition(
183-
databasev1beta1.MariaDBAccountReadyCondition,
184-
secret.ReasonSecretMissing,
185-
condition.SeverityWarning,
186-
databasev1beta1.MariaDBAccountSecretNotReadyMessage, err))
187-
188-
log.Info(fmt.Sprintf(
189-
"MariaDBAccount '%s' didn't find Secret '%s'; requeueing",
190-
instance.Name, instance.Spec.Secret))
191-
192-
return secretResult, client.IgnoreNotFound(err)
171+
// ensure secret is present, add a finalizer for mariadbaccount
172+
result, err = r.ensureAccountSecret(ctx, log, helper, instance)
173+
if (result != ctrl.Result{} || err != nil) {
174+
return result, err
193175
}
194176

195177
log.Info(fmt.Sprintf("Running account create '%s' MariaDBDatabase '%s'",
@@ -302,9 +284,8 @@ func (r *MariaDBAccountReconciler) reconcileDelete(
302284
}
303285

304286
// remove local finalizer
305-
controllerutil.RemoveFinalizer(instance, helper.GetFinalizer())
306-
307-
return ctrl.Result{}, nil
287+
err = r.removeAccountAndSecretFinalizer(ctx, helper, instance)
288+
return ctrl.Result{}, err
308289
} else if dbGalera == nil {
309290
return result, err
310291
}
@@ -351,9 +332,9 @@ func (r *MariaDBAccountReconciler) reconcileDelete(
351332
}
352333

353334
// then remove finalizer from our own instance
354-
controllerutil.RemoveFinalizer(instance, helper.GetFinalizer())
335+
err = r.removeAccountAndSecretFinalizer(ctx, helper, instance)
355336

356-
return ctrl.Result{}, nil
337+
return ctrl.Result{}, err
357338
}
358339

359340
// getMariaDBDatabaseForCreate - waits for a MariaDBDatabase to be available in preparation
@@ -446,8 +427,9 @@ func (r *MariaDBAccountReconciler) getMariaDBDatabaseForDelete(ctx context.Conte
446427
instance.Name,
447428
))
448429

449-
controllerutil.RemoveFinalizer(instance, helper.GetFinalizer())
450-
return nil, ctrl.Result{}, nil
430+
// remove local finalizer
431+
err := r.removeAccountAndSecretFinalizer(ctx, helper, instance)
432+
return nil, ctrl.Result{}, err
451433
}
452434

453435
// locate the MariaDBDatabase object itself
@@ -461,8 +443,9 @@ func (r *MariaDBAccountReconciler) getMariaDBDatabaseForDelete(ctx context.Conte
461443
"MariaDBAccount '%s' Didn't find MariaDBDatabase '%s'; no account delete needed",
462444
instance.Name, mariadbDatabaseName))
463445

464-
controllerutil.RemoveFinalizer(instance, helper.GetFinalizer())
465-
return nil, ctrl.Result{}, nil
446+
// remove local finalizer
447+
err = r.removeAccountAndSecretFinalizer(ctx, helper, instance)
448+
return nil, ctrl.Result{}, err
466449
} else {
467450
// unhandled error; exit without change
468451
log.Error(err, "unhandled error retrieving MariaDBDatabase instance")
@@ -492,8 +475,9 @@ func (r *MariaDBAccountReconciler) getMariaDBDatabaseForDelete(ctx context.Conte
492475
}
493476
}
494477

495-
controllerutil.RemoveFinalizer(instance, helper.GetFinalizer())
496-
return nil, ctrl.Result{}, nil
478+
// remove local finalizer
479+
err = r.removeAccountAndSecretFinalizer(ctx, helper, instance)
480+
return nil, ctrl.Result{}, err
497481
}
498482

499483
// return MariaDBDatabase where account delete flow will then continue
@@ -572,3 +556,81 @@ func (r *MariaDBAccountReconciler) getMariaDBDatabaseObject(ctx context.Context,
572556
return mariaDBDatabase, nil
573557

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

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)