Skip to content

Commit 32c7c7b

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 23e764e commit 32c7c7b

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
@@ -1058,9 +1058,8 @@ func (r *GaleraReconciler) SetupWithManager(mgr ctrl.Manager) error {
10581058
Complete(r)
10591059
}
10601060

1061-
// GetDatabaseObject - returns either a Galera or MariaDB object (and an associated client.Object interface).
1061+
// GetDatabaseObject - returns a Galera object.
10621062
// used by both MariaDBDatabaseReconciler and MariaDBAccountReconciler
1063-
// this will later return only Galera objects, so as a lookup it's part of the galera controller
10641063
func GetDatabaseObject(ctx context.Context, clientObj client.Client, name string, namespace string) (*mariadbv1.Galera, error) {
10651064
dbGalera := &mariadbv1.Galera{
10661065
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"
@@ -112,13 +113,23 @@ func (r *MariaDBAccountReconciler) Reconcile(ctx context.Context, req ctrl.Reque
112113
}
113114
}()
114115

115-
// initialize conditions used later as Status=Unknown
116-
cl := condition.CreateList(
117-
condition.UnknownCondition(condition.ReadyCondition, condition.InitReason, condition.ReadyInitMessage),
118-
condition.UnknownCondition(databasev1beta1.MariaDBServerReadyCondition, condition.InitReason, databasev1beta1.MariaDBServerReadyInitMessage),
119-
condition.UnknownCondition(databasev1beta1.MariaDBDatabaseReadyCondition, condition.InitReason, databasev1beta1.MariaDBDatabaseReadyInitMessage),
120-
condition.UnknownCondition(databasev1beta1.MariaDBAccountReadyCondition, condition.InitReason, databasev1beta1.MariaDBAccountReadyInitMessage),
121-
)
116+
var cl condition.Conditions
117+
if instance.IsUserAccount() {
118+
// initialize conditions used later as Status=Unknown
119+
cl = condition.CreateList(
120+
condition.UnknownCondition(condition.ReadyCondition, condition.InitReason, condition.ReadyInitMessage),
121+
condition.UnknownCondition(databasev1beta1.MariaDBServerReadyCondition, condition.InitReason, databasev1beta1.MariaDBServerReadyInitMessage),
122+
condition.UnknownCondition(databasev1beta1.MariaDBDatabaseReadyCondition, condition.InitReason, databasev1beta1.MariaDBDatabaseReadyInitMessage),
123+
condition.UnknownCondition(databasev1beta1.MariaDBAccountReadyCondition, condition.InitReason, databasev1beta1.MariaDBAccountReadyInitMessage),
124+
)
125+
} else {
126+
// initialize conditions used later as Status=Unknown
127+
cl = condition.CreateList(
128+
condition.UnknownCondition(condition.ReadyCondition, condition.InitReason, condition.ReadyInitMessage),
129+
condition.UnknownCondition(databasev1beta1.MariaDBServerReadyCondition, condition.InitReason, databasev1beta1.MariaDBServerReadyInitMessage),
130+
condition.UnknownCondition(databasev1beta1.MariaDBAccountReadyCondition, condition.InitReason, databasev1beta1.MariaDBAccountReadyInitMessage),
131+
)
132+
}
122133
instance.Status.Conditions.Init(&cl)
123134

124135
if instance.DeletionTimestamp.IsZero() || isNewInstance { //revive:disable:indent-error-flow
@@ -134,23 +145,30 @@ func (r *MariaDBAccountReconciler) reconcileCreate(
134145
ctx context.Context, log logr.Logger,
135146
helper *helper.Helper, instance *databasev1beta1.MariaDBAccount) (result ctrl.Result, _err error) {
136147

137-
// get a handle to the current, active MariaDBDatabase.
138-
// if not ready yet, requeue.
139-
mariadbDatabase, result, err := r.getMariaDBDatabaseForCreate(ctx, log, instance)
140-
if mariadbDatabase == nil {
141-
return result, err
148+
var mariadbDatabase *databasev1beta1.MariaDBDatabase
149+
var err error
150+
151+
if instance.IsUserAccount() {
152+
// for User account, get a handle to the current, active MariaDBDatabase.
153+
// if not ready yet, requeue.
154+
mariadbDatabase, result, err = r.getMariaDBDatabaseForCreate(ctx, log, instance)
155+
if mariadbDatabase == nil {
156+
return result, err
157+
}
142158
}
143159

144160
if controllerutil.AddFinalizer(instance, helper.GetFinalizer()) {
145161
// we need to persist this right away
146162
return ctrl.Result{}, nil
147163
}
148164

149-
// MariaDBdatabase exists and we are a create case. ensure finalizers set up
150-
if controllerutil.AddFinalizer(mariadbDatabase, fmt.Sprintf("%s-%s", helper.GetFinalizer(), instance.Name)) {
151-
err = r.Update(ctx, mariadbDatabase)
152-
if err != nil {
153-
return ctrl.Result{}, err
165+
if instance.IsUserAccount() {
166+
// MariaDBdatabase exists and we are a create case. ensure finalizers set up
167+
if controllerutil.AddFinalizer(mariadbDatabase, fmt.Sprintf("%s-%s", helper.GetFinalizer(), instance.Name)) {
168+
err = r.Update(ctx, mariadbDatabase)
169+
if err != nil {
170+
return ctrl.Result{}, err
171+
}
154172
}
155173
}
156174

@@ -174,13 +192,25 @@ func (r *MariaDBAccountReconciler) reconcileCreate(
174192
return result, err
175193
}
176194

177-
log.Info(fmt.Sprintf("Running account create '%s' MariaDBDatabase '%s'",
178-
instance.Name, mariadbDatabase.Spec.Name))
179-
jobDef, err := mariadb.CreateDbAccountJob(
180-
dbGalera, instance, mariadbDatabase.Spec.Name, dbHostname,
181-
dbContainerImage, serviceAccountName, dbGalera.Spec.NodeSelector)
182-
if err != nil {
183-
return ctrl.Result{}, err
195+
var jobDef *batchv1.Job
196+
197+
if instance.IsUserAccount() {
198+
log.Info(fmt.Sprintf("Running account create '%s' MariaDBDatabase '%s'",
199+
instance.Name, mariadbDatabase.Spec.Name))
200+
jobDef, err = mariadb.CreateDbAccountJob(
201+
dbGalera, instance, mariadbDatabase.Spec.Name, dbHostname,
202+
dbContainerImage, serviceAccountName, dbGalera.Spec.NodeSelector)
203+
if err != nil {
204+
return ctrl.Result{}, err
205+
}
206+
} else {
207+
log.Info(fmt.Sprintf("Running system account create '%s'", instance.Name))
208+
jobDef, err = mariadb.CreateDbAccountJob(
209+
dbGalera, instance, "", dbHostname,
210+
dbContainerImage, serviceAccountName, dbGalera.Spec.NodeSelector)
211+
if err != nil {
212+
return ctrl.Result{}, err
213+
}
184214
}
185215

186216
accountCreateHash := instance.Status.Hash[databasev1beta1.AccountCreateHash]
@@ -228,9 +258,14 @@ func (r *MariaDBAccountReconciler) reconcileDelete(
228258
ctx context.Context, log logr.Logger,
229259
helper *helper.Helper, instance *databasev1beta1.MariaDBAccount) (result ctrl.Result, _err error) {
230260

231-
mariadbDatabase, result, err := r.getMariaDBDatabaseForDelete(ctx, log, helper, instance)
232-
if mariadbDatabase == nil {
233-
return result, err
261+
var mariadbDatabase *databasev1beta1.MariaDBDatabase
262+
var err error
263+
264+
if instance.IsUserAccount() {
265+
mariadbDatabase, result, err = r.getMariaDBDatabaseForDelete(ctx, log, helper, instance)
266+
if mariadbDatabase == nil {
267+
return result, err
268+
}
234269
}
235270

236271
// dont do actual DROP USER until finalizers from downstream controllers
@@ -259,10 +294,12 @@ func (r *MariaDBAccountReconciler) reconcileDelete(
259294
databasev1beta1.MariaDBAccountReadyForDeleteMessage,
260295
)
261296

262-
instance.Status.Conditions.MarkTrue(
263-
databasev1beta1.MariaDBDatabaseReadyCondition,
264-
databasev1beta1.MariaDBDatabaseReadyMessage,
265-
)
297+
if instance.IsUserAccount() {
298+
instance.Status.Conditions.MarkTrue(
299+
databasev1beta1.MariaDBDatabaseReadyCondition,
300+
databasev1beta1.MariaDBDatabaseReadyMessage,
301+
)
302+
}
266303

267304
// now proceed to do actual work. acquire the Galera instance
268305
// which will lead us to the hostname and container image to target
@@ -273,18 +310,19 @@ func (r *MariaDBAccountReconciler) reconcileDelete(
273310
// implemented in the database either, so remove all finalizers and
274311
// exit
275312
if k8s_errors.IsNotFound(err) {
276-
// remove finalizer from the MariaDBDatabase instance
277-
if controllerutil.RemoveFinalizer(mariadbDatabase, fmt.Sprintf("%s-%s", helper.GetFinalizer(), instance.Name)) {
278-
err = r.Update(ctx, mariadbDatabase)
279-
280-
if err != nil && !k8s_errors.IsNotFound(err) {
281-
return ctrl.Result{}, err
313+
if instance.IsUserAccount() {
314+
// remove finalizer from the MariaDBDatabase instance
315+
if controllerutil.RemoveFinalizer(mariadbDatabase, fmt.Sprintf("%s-%s", helper.GetFinalizer(), instance.Name)) {
316+
err = r.Update(ctx, mariadbDatabase)
317+
318+
if err != nil && !k8s_errors.IsNotFound(err) {
319+
return ctrl.Result{}, err
320+
}
282321
}
283-
284322
}
285323

286324
// remove local finalizer
287-
err = r.removeAccountAndSecretFinalizer(ctx, helper, instance)
325+
err := r.removeAccountAndSecretFinalizer(ctx, helper, instance)
288326
return ctrl.Result{}, err
289327
} else if dbGalera == nil {
290328
return result, err
@@ -293,13 +331,24 @@ func (r *MariaDBAccountReconciler) reconcileDelete(
293331
dbContainerImage := dbGalera.Spec.ContainerImage
294332
serviceAccountName := dbGalera.RbacResourceName()
295333

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

298-
jobDef, err := mariadb.DeleteDbAccountJob(dbGalera, instance, mariadbDatabase.Spec.Name, dbHostname, dbContainerImage, serviceAccountName, dbGalera.Spec.NodeSelector)
299-
if err != nil {
300-
return ctrl.Result{}, err
301-
}
336+
if instance.IsUserAccount() {
302337

338+
log.Info(fmt.Sprintf("Running account delete '%s' MariaDBDatabase '%s'", instance.Name, mariadbDatabase.Spec.Name))
339+
340+
jobDef, err = mariadb.DeleteDbAccountJob(dbGalera, instance, mariadbDatabase.Spec.Name, dbHostname, dbContainerImage, serviceAccountName, dbGalera.Spec.NodeSelector)
341+
if err != nil {
342+
return ctrl.Result{}, err
343+
}
344+
} else {
345+
log.Info(fmt.Sprintf("Running system account delete '%s'", instance.Name))
346+
347+
jobDef, err = mariadb.DeleteDbAccountJob(dbGalera, instance, "", dbHostname, dbContainerImage, serviceAccountName, dbGalera.Spec.NodeSelector)
348+
if err != nil {
349+
return ctrl.Result{}, err
350+
}
351+
}
303352
accountDeleteHash := instance.Status.Hash[databasev1beta1.AccountDeleteHash]
304353
accountDeleteJob := job.NewJob(
305354
jobDef,
@@ -324,16 +373,17 @@ func (r *MariaDBAccountReconciler) reconcileDelete(
324373
}
325374

326375
// first, remove finalizer from the MariaDBDatabase instance
327-
if controllerutil.RemoveFinalizer(mariadbDatabase, fmt.Sprintf("%s-%s", helper.GetFinalizer(), instance.Name)) {
328-
err = r.Update(ctx, mariadbDatabase)
329-
if err != nil {
330-
return ctrl.Result{}, err
376+
if instance.IsUserAccount() {
377+
if controllerutil.RemoveFinalizer(mariadbDatabase, fmt.Sprintf("%s-%s", helper.GetFinalizer(), instance.Name)) {
378+
err = r.Update(ctx, mariadbDatabase)
379+
if err != nil {
380+
return ctrl.Result{}, err
381+
}
331382
}
332383
}
333384

334385
// then remove finalizer from our own instance
335386
err = r.removeAccountAndSecretFinalizer(ctx, helper, instance)
336-
337387
return ctrl.Result{}, err
338388
}
339389

@@ -492,9 +542,17 @@ func (r *MariaDBAccountReconciler) getGaleraForCreateOrDelete(
492542
helper *helper.Helper, instance *databasev1beta1.MariaDBAccount,
493543
mariadbDatabase *databasev1beta1.MariaDBDatabase) (*databasev1beta1.Galera, string, ctrl.Result, error) {
494544

495-
dbName := mariadbDatabase.Labels["dbName"]
545+
var dbGalera *databasev1beta1.Galera
546+
var err error
547+
var dbName string
496548

497-
dbGalera, err := GetDatabaseObject(ctx, r.Client, dbName, instance.Namespace)
549+
if instance.IsUserAccount() {
550+
dbName = mariadbDatabase.Labels["dbName"]
551+
} else {
552+
// note mariadbDatabase is passed as nil in this case
553+
dbName = instance.Labels["dbName"]
554+
}
555+
dbGalera, err = GetDatabaseObject(ctx, r.Client, dbName, instance.Namespace)
498556

499557
if err != nil {
500558
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 @@ MYSQL_REMOTE_HOST={{.DatabaseHostname}} source /var/lib/operator-scripts/mysql_r
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)