Skip to content

Commit a9c9612

Browse files
committed
mariadbaccount system accounts
introduce a new class of MariaDBAccount called a "system" MariaDBAccount, indicated by a new enumerated field AccountType on the CR. Such accounts link directly to a Galera instance and have no dependency on a MariaDBDatabase CR. The expected targets for "system" accounts will include the Galera/mysql root username and password, as well as a system account used by mariadbbackup for SST. Refactor mariadbaccount_controller to isolate logic used for acquiring MariaDBDatabase and Galera CRs into separate functions, and ensure all MariaDBDatabase logic takes place only for "user" accounts (which would be all current MariaDBAccount CRs). Also correct an oversight where MariaDBAccount would not unconditionally apply a finalizer to its Secret object. This logic now takes place in addition to an unconditional removal of the finalizer when the MariaDBAccount object is deleted. A subsequent change will allow system-level passwords to be changed in place by applying the secret name to two separate fields MariaDBAccount/Spec/Secret and MariaDBAccount/Status/Secret. When these two names differ it will indicate an in-place password change should take place.
1 parent e7b9b59 commit a9c9612

17 files changed

+411
-56
lines changed

api/bases/mariadb.openstack.org_mariadbaccounts.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ spec:
4848
spec:
4949
description: MariaDBAccountSpec defines the desired state of MariaDBAccount
5050
properties:
51+
accountType:
52+
default: User
53+
enum:
54+
- User
55+
- System
56+
type: string
5157
requireTLS:
5258
default: false
5359
description: Account must use TLS to connect to the database

api/v1beta1/mariadbaccount_types.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,19 @@ type MariaDBAccountSpec struct {
4848
// Account must use TLS to connect to the database
4949
// +kubebuilder:default=false
5050
RequireTLS bool `json:"requireTLS"`
51+
52+
// +kubebuilder:validation:Enum=User;System
53+
// +kubebuilder:default=User
54+
AccountType AccountType `json:"accountType,omitempty"`
5155
}
5256

57+
type AccountType string
58+
59+
const (
60+
User AccountType = "User"
61+
System AccountType = "System"
62+
)
63+
5364
// MariaDBAccountStatus defines the observed state of MariaDBAccount
5465
type MariaDBAccountStatus struct {
5566
// Deployment Conditions
@@ -85,3 +96,11 @@ type MariaDBAccountList struct {
8596
func init() {
8697
SchemeBuilder.Register(&MariaDBAccount{}, &MariaDBAccountList{})
8798
}
99+
100+
func (mariadbAccount MariaDBAccount) IsSystemAccount() bool {
101+
return mariadbAccount.Spec.AccountType == System
102+
}
103+
104+
func (mariadbAccount MariaDBAccount) IsUserAccount() bool {
105+
return mariadbAccount.Spec.AccountType == "" || mariadbAccount.Spec.AccountType == User
106+
}

config/crd/bases/mariadb.openstack.org_mariadbaccounts.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ spec:
4848
spec:
4949
description: MariaDBAccountSpec defines the desired state of MariaDBAccount
5050
properties:
51+
accountType:
52+
default: User
53+
enum:
54+
- User
55+
- System
56+
type: string
5157
requireTLS:
5258
default: false
5359
description: Account must use TLS to connect to the database

controllers/galera_controller.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1045,9 +1045,8 @@ func (r *GaleraReconciler) SetupWithManager(mgr ctrl.Manager) error {
10451045
Complete(r)
10461046
}
10471047

1048-
// GetDatabaseObject - returns either a Galera or MariaDB object (and an associated client.Object interface).
1048+
// GetDatabaseObject - returns a Galera object.
10491049
// used by both MariaDBDatabaseReconciler and MariaDBAccountReconciler
1050-
// this will later return only Galera objects, so as a lookup it's part of the galera controller
10511050
func GetDatabaseObject(ctx context.Context, clientObj client.Client, name string, namespace string) (*databasev1beta1.Galera, error) {
10521051
dbGalera := &databasev1beta1.Galera{
10531052
ObjectMeta: metav1.ObjectMeta{

controllers/mariadbaccount_controller.go

Lines changed: 109 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
util "github.com/openstack-k8s-operators/lib-common/modules/common/util"
3131
databasev1beta1 "github.com/openstack-k8s-operators/mariadb-operator/api/v1beta1"
3232
mariadb "github.com/openstack-k8s-operators/mariadb-operator/pkg/mariadb"
33+
batchv1 "k8s.io/api/batch/v1"
3334
k8s_errors "k8s.io/apimachinery/pkg/api/errors"
3435
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3536
"k8s.io/apimachinery/pkg/runtime"
@@ -107,13 +108,23 @@ func (r *MariaDBAccountReconciler) Reconcile(ctx context.Context, req ctrl.Reque
107108
}
108109
}()
109110

110-
// initialize conditions used later as Status=Unknown
111-
cl := condition.CreateList(
112-
condition.UnknownCondition(condition.ReadyCondition, condition.InitReason, condition.ReadyInitMessage),
113-
condition.UnknownCondition(databasev1beta1.MariaDBServerReadyCondition, condition.InitReason, databasev1beta1.MariaDBServerReadyInitMessage),
114-
condition.UnknownCondition(databasev1beta1.MariaDBDatabaseReadyCondition, condition.InitReason, databasev1beta1.MariaDBDatabaseReadyInitMessage),
115-
condition.UnknownCondition(databasev1beta1.MariaDBAccountReadyCondition, condition.InitReason, databasev1beta1.MariaDBAccountReadyInitMessage),
116-
)
111+
var cl condition.Conditions
112+
if instance.IsUserAccount() {
113+
// initialize conditions used later as Status=Unknown
114+
cl = condition.CreateList(
115+
condition.UnknownCondition(condition.ReadyCondition, condition.InitReason, condition.ReadyInitMessage),
116+
condition.UnknownCondition(databasev1beta1.MariaDBServerReadyCondition, condition.InitReason, databasev1beta1.MariaDBServerReadyInitMessage),
117+
condition.UnknownCondition(databasev1beta1.MariaDBDatabaseReadyCondition, condition.InitReason, databasev1beta1.MariaDBDatabaseReadyInitMessage),
118+
condition.UnknownCondition(databasev1beta1.MariaDBAccountReadyCondition, condition.InitReason, databasev1beta1.MariaDBAccountReadyInitMessage),
119+
)
120+
} else {
121+
// initialize conditions used later as Status=Unknown
122+
cl = condition.CreateList(
123+
condition.UnknownCondition(condition.ReadyCondition, condition.InitReason, condition.ReadyInitMessage),
124+
condition.UnknownCondition(databasev1beta1.MariaDBServerReadyCondition, condition.InitReason, databasev1beta1.MariaDBServerReadyInitMessage),
125+
condition.UnknownCondition(databasev1beta1.MariaDBAccountReadyCondition, condition.InitReason, databasev1beta1.MariaDBAccountReadyInitMessage),
126+
)
127+
}
117128
instance.Status.Conditions.Init(&cl)
118129

119130
if instance.DeletionTimestamp.IsZero() || isNewInstance { //revive:disable:indent-error-flow
@@ -129,23 +140,30 @@ func (r *MariaDBAccountReconciler) reconcileCreate(
129140
ctx context.Context, log logr.Logger,
130141
helper *helper.Helper, instance *databasev1beta1.MariaDBAccount) (result ctrl.Result, _err error) {
131142

132-
// get a handle to the current, active MariaDBDatabase.
133-
// if not ready yet, requeue.
134-
mariadbDatabase, result, err := r.getMariaDBDatabaseForCreate(ctx, log, instance)
135-
if mariadbDatabase == nil {
136-
return result, err
143+
var mariadbDatabase *databasev1beta1.MariaDBDatabase
144+
var err error
145+
146+
if instance.IsUserAccount() {
147+
// for User account, get a handle to the current, active MariaDBDatabase.
148+
// if not ready yet, requeue.
149+
mariadbDatabase, result, err = r.getMariaDBDatabaseForCreate(ctx, log, instance)
150+
if mariadbDatabase == nil {
151+
return result, err
152+
}
137153
}
138154

139155
if controllerutil.AddFinalizer(instance, helper.GetFinalizer()) {
140156
// we need to persist this right away
141157
return ctrl.Result{}, nil
142158
}
143159

144-
// MariaDBdatabase exists and we are a create case. ensure finalizers set up
145-
if controllerutil.AddFinalizer(mariadbDatabase, fmt.Sprintf("%s-%s", helper.GetFinalizer(), instance.Name)) {
146-
err = r.Update(ctx, mariadbDatabase)
147-
if err != nil {
148-
return ctrl.Result{}, err
160+
if instance.IsUserAccount() {
161+
// MariaDBdatabase exists and we are a create case. ensure finalizers set up
162+
if controllerutil.AddFinalizer(mariadbDatabase, fmt.Sprintf("%s-%s", helper.GetFinalizer(), instance.Name)) {
163+
err = r.Update(ctx, mariadbDatabase)
164+
if err != nil {
165+
return ctrl.Result{}, err
166+
}
149167
}
150168
}
151169

@@ -169,13 +187,25 @@ func (r *MariaDBAccountReconciler) reconcileCreate(
169187
return result, err
170188
}
171189

172-
log.Info(fmt.Sprintf("Running account create '%s' MariaDBDatabase '%s'",
173-
instance.Name, mariadbDatabase.Spec.Name))
174-
jobDef, err := mariadb.CreateDbAccountJob(
175-
dbGalera, instance, mariadbDatabase.Spec.Name, dbHostname,
176-
dbContainerImage, serviceAccountName, dbGalera.Spec.NodeSelector)
177-
if err != nil {
178-
return ctrl.Result{}, err
190+
var jobDef *batchv1.Job
191+
192+
if instance.IsUserAccount() {
193+
log.Info(fmt.Sprintf("Running account create '%s' MariaDBDatabase '%s'",
194+
instance.Name, mariadbDatabase.Spec.Name))
195+
jobDef, err = mariadb.CreateDbAccountJob(
196+
dbGalera, instance, mariadbDatabase.Spec.Name, dbHostname,
197+
dbContainerImage, serviceAccountName, dbGalera.Spec.NodeSelector)
198+
if err != nil {
199+
return ctrl.Result{}, err
200+
}
201+
} else {
202+
log.Info(fmt.Sprintf("Running system account create '%s'", instance.Name))
203+
jobDef, err = mariadb.CreateDbAccountJob(
204+
dbGalera, instance, "", dbHostname,
205+
dbContainerImage, serviceAccountName, dbGalera.Spec.NodeSelector)
206+
if err != nil {
207+
return ctrl.Result{}, err
208+
}
179209
}
180210

181211
accountCreateHash := instance.Status.Hash[databasev1beta1.AccountCreateHash]
@@ -223,9 +253,14 @@ func (r *MariaDBAccountReconciler) reconcileDelete(
223253
ctx context.Context, log logr.Logger,
224254
helper *helper.Helper, instance *databasev1beta1.MariaDBAccount) (result ctrl.Result, _err error) {
225255

226-
mariadbDatabase, result, err := r.getMariaDBDatabaseForDelete(ctx, log, helper, instance)
227-
if mariadbDatabase == nil {
228-
return result, err
256+
var mariadbDatabase *databasev1beta1.MariaDBDatabase
257+
var err error
258+
259+
if instance.IsUserAccount() {
260+
mariadbDatabase, result, err = r.getMariaDBDatabaseForDelete(ctx, log, helper, instance)
261+
if mariadbDatabase == nil {
262+
return result, err
263+
}
229264
}
230265

231266
// dont do actual DROP USER until finalizers from downstream controllers
@@ -254,10 +289,12 @@ func (r *MariaDBAccountReconciler) reconcileDelete(
254289
databasev1beta1.MariaDBAccountReadyForDeleteMessage,
255290
)
256291

257-
instance.Status.Conditions.MarkTrue(
258-
databasev1beta1.MariaDBDatabaseReadyCondition,
259-
databasev1beta1.MariaDBDatabaseReadyMessage,
260-
)
292+
if instance.IsUserAccount() {
293+
instance.Status.Conditions.MarkTrue(
294+
databasev1beta1.MariaDBDatabaseReadyCondition,
295+
databasev1beta1.MariaDBDatabaseReadyMessage,
296+
)
297+
}
261298

262299
// now proceed to do actual work. acquire the Galera instance
263300
// which will lead us to the hostname and container image to target
@@ -268,18 +305,19 @@ func (r *MariaDBAccountReconciler) reconcileDelete(
268305
// implemented in the database either, so remove all finalizers and
269306
// exit
270307
if k8s_errors.IsNotFound(err) {
271-
// remove finalizer from the MariaDBDatabase instance
272-
if controllerutil.RemoveFinalizer(mariadbDatabase, fmt.Sprintf("%s-%s", helper.GetFinalizer(), instance.Name)) {
273-
err = r.Update(ctx, mariadbDatabase)
274-
275-
if err != nil && !k8s_errors.IsNotFound(err) {
276-
return ctrl.Result{}, err
308+
if instance.IsUserAccount() {
309+
// remove finalizer from the MariaDBDatabase instance
310+
if controllerutil.RemoveFinalizer(mariadbDatabase, fmt.Sprintf("%s-%s", helper.GetFinalizer(), instance.Name)) {
311+
err = r.Update(ctx, mariadbDatabase)
312+
313+
if err != nil && !k8s_errors.IsNotFound(err) {
314+
return ctrl.Result{}, err
315+
}
277316
}
278-
279317
}
280318

281319
// remove local finalizer
282-
err = r.removeAccountAndSecretFinalizer(ctx, helper, instance)
320+
err := r.removeAccountAndSecretFinalizer(ctx, helper, instance)
283321
return ctrl.Result{}, err
284322
} else if dbGalera == nil {
285323
return result, err
@@ -288,13 +326,24 @@ func (r *MariaDBAccountReconciler) reconcileDelete(
288326
dbContainerImage := dbGalera.Spec.ContainerImage
289327
serviceAccountName := dbGalera.RbacResourceName()
290328

291-
log.Info(fmt.Sprintf("Running account delete '%s' MariaDBDatabase '%s'", instance.Name, mariadbDatabase.Spec.Name))
329+
var jobDef *batchv1.Job
292330

293-
jobDef, err := mariadb.DeleteDbAccountJob(dbGalera, instance, mariadbDatabase.Spec.Name, dbHostname, dbContainerImage, serviceAccountName, dbGalera.Spec.NodeSelector)
294-
if err != nil {
295-
return ctrl.Result{}, err
296-
}
331+
if instance.IsUserAccount() {
297332

333+
log.Info(fmt.Sprintf("Running account delete '%s' MariaDBDatabase '%s'", instance.Name, mariadbDatabase.Spec.Name))
334+
335+
jobDef, err = mariadb.DeleteDbAccountJob(dbGalera, instance, mariadbDatabase.Spec.Name, dbHostname, dbContainerImage, serviceAccountName, dbGalera.Spec.NodeSelector)
336+
if err != nil {
337+
return ctrl.Result{}, err
338+
}
339+
} else {
340+
log.Info(fmt.Sprintf("Running system account delete '%s'", instance.Name))
341+
342+
jobDef, err = mariadb.DeleteDbAccountJob(dbGalera, instance, "", dbHostname, dbContainerImage, serviceAccountName, dbGalera.Spec.NodeSelector)
343+
if err != nil {
344+
return ctrl.Result{}, err
345+
}
346+
}
298347
accountDeleteHash := instance.Status.Hash[databasev1beta1.AccountDeleteHash]
299348
accountDeleteJob := job.NewJob(
300349
jobDef,
@@ -319,16 +368,17 @@ func (r *MariaDBAccountReconciler) reconcileDelete(
319368
}
320369

321370
// first, remove finalizer from the MariaDBDatabase instance
322-
if controllerutil.RemoveFinalizer(mariadbDatabase, fmt.Sprintf("%s-%s", helper.GetFinalizer(), instance.Name)) {
323-
err = r.Update(ctx, mariadbDatabase)
324-
if err != nil {
325-
return ctrl.Result{}, err
371+
if instance.IsUserAccount() {
372+
if controllerutil.RemoveFinalizer(mariadbDatabase, fmt.Sprintf("%s-%s", helper.GetFinalizer(), instance.Name)) {
373+
err = r.Update(ctx, mariadbDatabase)
374+
if err != nil {
375+
return ctrl.Result{}, err
376+
}
326377
}
327378
}
328379

329380
// then remove finalizer from our own instance
330381
err = r.removeAccountAndSecretFinalizer(ctx, helper, instance)
331-
332382
return ctrl.Result{}, err
333383
}
334384

@@ -487,9 +537,17 @@ func (r *MariaDBAccountReconciler) getGaleraForCreateOrDelete(
487537
helper *helper.Helper, instance *databasev1beta1.MariaDBAccount,
488538
mariadbDatabase *databasev1beta1.MariaDBDatabase) (*databasev1beta1.Galera, string, ctrl.Result, error) {
489539

490-
dbName := mariadbDatabase.ObjectMeta.Labels["dbName"]
540+
var dbGalera *databasev1beta1.Galera
541+
var err error
542+
var dbName string
491543

492-
dbGalera, err := GetDatabaseObject(ctx, r.Client, dbName, instance.Namespace)
544+
if instance.IsUserAccount() {
545+
dbName = mariadbDatabase.ObjectMeta.Labels["dbName"]
546+
} else {
547+
// note mariadbDatabase is passed as nil in this case
548+
dbName = instance.ObjectMeta.Labels["dbName"]
549+
}
550+
dbGalera, err = GetDatabaseObject(ctx, r.Client, dbName, instance.Namespace)
493551

494552
if err != nil {
495553
log.Error(err, "Error retrieving Galera instance")

templates/account.sh

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@ source /var/lib/operator-scripts/root_auth.sh
44

55
export DatabasePassword=${DatabasePassword:?"Please specify a DatabasePassword variable."}
66

7-
mysql -h {{.DatabaseHostname}} -u {{.DatabaseAdminUsername}} -P 3306 -e "GRANT ALL PRIVILEGES ON {{.DatabaseName}}.* TO '{{.UserName}}'@'localhost' IDENTIFIED BY '$DatabasePassword'{{.RequireTLS}};GRANT ALL PRIVILEGES ON {{.DatabaseName}}.* TO '{{.UserName}}'@'%' IDENTIFIED BY '$DatabasePassword'{{.RequireTLS}};"
8-
7+
if [ -n "{{.DatabaseName}}" ]; then
8+
mysql -h {{.DatabaseHostname}} -u {{.DatabaseAdminUsername}} -P 3306 -e "GRANT ALL PRIVILEGES ON {{.DatabaseName}}.* TO '{{.UserName}}'@'localhost' IDENTIFIED BY '$DatabasePassword'{{.RequireTLS}};GRANT ALL PRIVILEGES ON {{.DatabaseName}}.* TO '{{.UserName}}'@'%' IDENTIFIED BY '$DatabasePassword'{{.RequireTLS}};"
9+
else
10+
mysql -h {{.DatabaseHostname}} -u {{.DatabaseAdminUsername}} -P 3306 -e "GRANT ALL PRIVILEGES ON *.* TO '{{.UserName}}'@'localhost' IDENTIFIED BY '$DatabasePassword'{{.RequireTLS}};GRANT ALL PRIVILEGES ON *.* TO '{{.UserName}}'@'%' IDENTIFIED BY '$DatabasePassword'{{.RequireTLS}};"
11+
fi
912

1013
# search for the account. not using SHOW CREATE USER to avoid displaying
1114
# password hash

tests/kuttl/common/scripts/check_db_account.sh

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,11 @@ fi
2626

2727
# username was found. if we wanted it to be found, then check the login also.
2828
if [ "$found" = "0" ]; then
29-
oc rsh -n ${NAMESPACE} -c galera ${galera} /bin/sh -c "mysql -u${username} -p${password} -Nse 'select database();' ${dbname}" || exit -1
29+
if [ -n "$dbname" ]; then
30+
oc rsh -n ${NAMESPACE} -c galera ${galera} /bin/sh -c "mysql -u${username} -p${password} -Nse 'select database();' ${dbname}" || exit -1
31+
else
32+
oc rsh -n ${NAMESPACE} -c galera ${galera} /bin/sh -c "mysql -u${username} -p${password} -Nse 'select 1'" || exit -1
33+
fi
3034
fi
3135

3236
exit $found

0 commit comments

Comments
 (0)