From 69fbe6712898fae728a76f61478ae5070eab481b Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Tue, 9 Jan 2024 16:25:22 -0500 Subject: [PATCH] add conditions for mariadbdatabase by adding conditions, we allow the mariadbaccount to start its work before the mariadbdatabase is fully completed. this also brings MariaDBDatabase into the same condition style as other controllers in play. --- ...ariadb.openstack.org_mariadbdatabases.yaml | 43 ++++++++++++++ api/v1beta1/conditions.go | 2 + api/v1beta1/mariadbdatabase_types.go | 4 ++ api/v1beta1/zz_generated.deepcopy.go | 7 +++ ...ariadb.openstack.org_mariadbdatabases.yaml | 43 ++++++++++++++ controllers/mariadbaccount_controller.go | 16 ++++-- controllers/mariadbdatabase_controller.go | 56 ++++++++++++++++++- .../tests/database_create/02-assert.yaml | 50 +++++++++++++++++ 8 files changed, 215 insertions(+), 6 deletions(-) diff --git a/api/bases/mariadb.openstack.org_mariadbdatabases.yaml b/api/bases/mariadb.openstack.org_mariadbdatabases.yaml index b8d76956..9cd06a31 100644 --- a/api/bases/mariadb.openstack.org_mariadbdatabases.yaml +++ b/api/bases/mariadb.openstack.org_mariadbdatabases.yaml @@ -58,6 +58,49 @@ spec: properties: completed: type: boolean + conditions: + description: Deployment Conditions + items: + description: Condition defines an observation of a API resource + operational state. + properties: + lastTransitionTime: + description: Last time the condition transitioned from one status + to another. This should be when the underlying condition changed. + If that is not known, then using the time when the API field + changed is acceptable. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition + in CamelCase. + type: string + severity: + description: Severity provides a classification of Reason code, + so the current situation is immediately understandable and + could act accordingly. It is meant for situations where Status=False + and it should be indicated if it is just informational, warning + (next reconciliation might fix it) or an error (e.g. DB create + issue and no actions to automatically resolve the issue can/should + be done). For conditions where Status=Unknown or Status=True + the Severity should be SeverityNone. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition in CamelCase. + type: string + required: + - lastTransitionTime + - status + - type + type: object + type: array hash: additionalProperties: type: string diff --git a/api/v1beta1/conditions.go b/api/v1beta1/conditions.go index b00981b5..66ed0daf 100644 --- a/api/v1beta1/conditions.go +++ b/api/v1beta1/conditions.go @@ -77,6 +77,8 @@ const ( MariaDBServerReadyMessage = "MariaDB / Galera server ready" + MariaDBServerNotBootstrappedMessage = "MariaDB / Galera server not bootstrapped" + MariaDBAccountReadyInitMessage = "MariaDBAccount create / drop not started" MariaDBAccountReadyMessage = "MariaDBAccount creation complete" diff --git a/api/v1beta1/mariadbdatabase_types.go b/api/v1beta1/mariadbdatabase_types.go index 700547c4..51f3c857 100644 --- a/api/v1beta1/mariadbdatabase_types.go +++ b/api/v1beta1/mariadbdatabase_types.go @@ -17,6 +17,7 @@ limitations under the License. package v1beta1 import ( + condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -44,6 +45,9 @@ type MariaDBDatabaseSpec struct { // MariaDBDatabaseStatus defines the observed state of MariaDBDatabase type MariaDBDatabaseStatus struct { + // Deployment Conditions + Conditions condition.Conditions `json:"conditions,omitempty" optional:"true"` + Completed bool `json:"completed,omitempty"` // Map of hashes to track e.g. job status Hash map[string]string `json:"hash,omitempty"` diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index 575c84a9..7e13b08a 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -394,6 +394,13 @@ func (in *MariaDBDatabaseSpec) DeepCopy() *MariaDBDatabaseSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *MariaDBDatabaseStatus) DeepCopyInto(out *MariaDBDatabaseStatus) { *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make(condition.Conditions, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } if in.Hash != nil { in, out := &in.Hash, &out.Hash *out = make(map[string]string, len(*in)) diff --git a/config/crd/bases/mariadb.openstack.org_mariadbdatabases.yaml b/config/crd/bases/mariadb.openstack.org_mariadbdatabases.yaml index b8d76956..9cd06a31 100644 --- a/config/crd/bases/mariadb.openstack.org_mariadbdatabases.yaml +++ b/config/crd/bases/mariadb.openstack.org_mariadbdatabases.yaml @@ -58,6 +58,49 @@ spec: properties: completed: type: boolean + conditions: + description: Deployment Conditions + items: + description: Condition defines an observation of a API resource + operational state. + properties: + lastTransitionTime: + description: Last time the condition transitioned from one status + to another. This should be when the underlying condition changed. + If that is not known, then using the time when the API field + changed is acceptable. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition + in CamelCase. + type: string + severity: + description: Severity provides a classification of Reason code, + so the current situation is immediately understandable and + could act accordingly. It is meant for situations where Status=False + and it should be indicated if it is just informational, warning + (next reconciliation might fix it) or an error (e.g. DB create + issue and no actions to automatically resolve the issue can/should + be done). For conditions where Status=Unknown or Status=True + the Severity should be SeverityNone. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition in CamelCase. + type: string + required: + - lastTransitionTime + - status + - type + type: object + type: array hash: additionalProperties: type: string diff --git a/controllers/mariadbaccount_controller.go b/controllers/mariadbaccount_controller.go index 72e62b45..9da6d603 100644 --- a/controllers/mariadbaccount_controller.go +++ b/controllers/mariadbaccount_controller.go @@ -167,8 +167,8 @@ func (r *MariaDBAccountReconciler) reconcileCreate( instance.Name, instance.ObjectMeta.Labels["mariaDBDatabaseName"])) return ctrl.Result{RequeueAfter: time.Duration(10) * time.Second}, nil - } else if err == nil && !mariadbDatabase.Status.Completed { - // found but not in completed status. + } else if err == nil && !mariadbDatabase.Status.Conditions.IsTrue(databasev1beta1.MariaDBDatabaseReadyCondition) { + // found but database not ready // for the create case, need to wait for the MariaDBDatabase to exists before we can continue; // requeue @@ -356,8 +356,8 @@ func (r *MariaDBAccountReconciler) reconcileDelete( controllerutil.RemoveFinalizer(instance, helper.GetFinalizer()) return ctrl.Result{}, nil - } else if err == nil && !mariadbDatabase.Status.Completed { - // found but not in completed status. + } else if err == nil && !mariadbDatabase.Status.Conditions.IsTrue(databasev1beta1.MariaDBDatabaseReadyCondition) { + // found but database is not ready // for the delete case, the database doesn't exist. so // that means we don't, either. remove finalizer from @@ -427,6 +427,14 @@ func (r *MariaDBAccountReconciler) reconcileDelete( if !dbGalera.Status.Bootstrapped { log.Info("DB bootstrap not complete. Requeue...") + + instance.Status.Conditions.MarkFalse( + databasev1beta1.MariaDBServerReadyCondition, + databasev1beta1.ReasonDBWaitingInitialized, + condition.SeverityInfo, + databasev1beta1.MariaDBServerNotBootstrappedMessage, + ) + return ctrl.Result{RequeueAfter: time.Second * 10}, nil } diff --git a/controllers/mariadbdatabase_controller.go b/controllers/mariadbdatabase_controller.go index 1cb466c2..973d309f 100644 --- a/controllers/mariadbdatabase_controller.go +++ b/controllers/mariadbdatabase_controller.go @@ -28,6 +28,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition" helper "github.com/openstack-k8s-operators/lib-common/modules/common/helper" job "github.com/openstack-k8s-operators/lib-common/modules/common/job" databasev1beta1 "github.com/openstack-k8s-operators/mariadb-operator/api/v1beta1" @@ -73,13 +74,49 @@ func (r *MariaDBDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Requ // Always patch the instance status when exiting this function so we can persist any changes. defer func() { + if instance.Status.Conditions.AllSubConditionIsTrue() { + // update the Ready condition based on the sub conditions + + instance.Status.Conditions.MarkTrue( + condition.ReadyCondition, condition.ReadyMessage) + } else { + // something is not ready so reset the Ready condition + instance.Status.Conditions.MarkUnknown( + condition.ReadyCondition, condition.InitReason, condition.ReadyInitMessage) + // and recalculate it based on the state of the rest of the conditions + instance.Status.Conditions.Set( + instance.Status.Conditions.Mirror(condition.ReadyCondition)) + } + + if instance.Status.Conditions.IsTrue(condition.ReadyCondition) { + // database creation finished... okay to set to completed + instance.Status.Completed = true + } + err := helper.PatchInstance(ctx, instance) + if err != nil { _err = err return } + }() + if instance.Status.Conditions == nil { + instance.Status.Conditions = condition.Conditions{} + + // initialize conditions used later as Status=Unknown + cl := condition.CreateList( + condition.UnknownCondition(databasev1beta1.MariaDBServerReadyCondition, condition.InitReason, databasev1beta1.MariaDBServerReadyInitMessage), + condition.UnknownCondition(databasev1beta1.MariaDBDatabaseReadyCondition, condition.InitReason, databasev1beta1.MariaDBDatabaseReadyInitMessage), + ) + + instance.Status.Conditions.Init(&cl) + + // Register overall status immediately to have an early feedback e.g. in the cli + return ctrl.Result{}, nil + } + // Fetch the Galera instance from which we'll pull the credentials dbGalera, err := r.getDatabaseObject(ctx, instance) @@ -133,6 +170,14 @@ func (r *MariaDBDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Requ if !dbGalera.Status.Bootstrapped { log.Info("DB bootstrap not complete. Requeue...") + + instance.Status.Conditions.MarkFalse( + databasev1beta1.MariaDBServerReadyCondition, + databasev1beta1.ReasonDBWaitingInitialized, + condition.SeverityInfo, + databasev1beta1.MariaDBServerNotBootstrappedMessage, + ) + return ctrl.Result{RequeueAfter: time.Second * 10}, nil } @@ -141,6 +186,11 @@ func (r *MariaDBDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Requ dbContainerImage = dbGalera.Spec.ContainerImage serviceAccount = dbGalera.RbacResourceName() + instance.Status.Conditions.MarkTrue( + databasev1beta1.MariaDBServerReadyCondition, + databasev1beta1.MariaDBServerReadyMessage, + ) + // Define a new Job object (hostname, password, containerImage) jobDef, err := mariadb.DbDatabaseJob(instance, dbName, dbSecret, dbContainerImage, serviceAccount) if err != nil { @@ -173,8 +223,10 @@ func (r *MariaDBDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Requ log.Info("Job hash added", "Job", jobDef.Name, "Hash", instance.Status.Hash[databasev1beta1.DbCreateHash]) } - // database creation finished... okay to set to completed - instance.Status.Completed = true + instance.Status.Conditions.MarkTrue( + databasev1beta1.MariaDBDatabaseReadyCondition, + databasev1beta1.MariaDBDatabaseReadyMessage, + ) return ctrl.Result{}, nil } diff --git a/tests/kuttl/tests/database_create/02-assert.yaml b/tests/kuttl/tests/database_create/02-assert.yaml index ed1dbe73..6fe01bb6 100644 --- a/tests/kuttl/tests/database_create/02-assert.yaml +++ b/tests/kuttl/tests/database_create/02-assert.yaml @@ -12,3 +12,53 @@ metadata: name: kuttldb-latin1-db-create status: succeeded: 1 +--- +apiVersion: mariadb.openstack.org/v1beta1 +kind: MariaDBDatabase +metadata: + name: kuttldb-utf8 + labels: + dbName: openstack +spec: + secret: osp-secret + name: kuttldb_utf8 +status: + conditions: + - message: Setup complete + reason: Ready + status: "True" + type: Ready + - message: MariaDBDatabase ready + reason: Ready + status: "True" + type: MariaDBDatabaseReady + - message: MariaDB / Galera server ready + reason: Ready + status: "True" + type: MariaDBServerReady + completed: true +--- +apiVersion: mariadb.openstack.org/v1beta1 +kind: MariaDBDatabase +metadata: + name: kuttldb-latin1 + labels: + dbName: openstack +spec: + secret: osp-secret + name: kuttldb_latin1 +status: + conditions: + - message: Setup complete + reason: Ready + status: "True" + type: Ready + - message: MariaDBDatabase ready + reason: Ready + status: "True" + type: MariaDBDatabaseReady + - message: MariaDB / Galera server ready + reason: Ready + status: "True" + type: MariaDBServerReady + completed: true