Skip to content

Commit 6f20d70

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 44d4046 commit 6f20d70

17 files changed

+409
-54
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: 107 additions & 49 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
@@ -278,20 +315,21 @@ func (r *MariaDBAccountReconciler) reconcileDelete(
278315
// scheme allows multiple MariaDBAccounts to claim the same MariaDBDatabase
279316
// as a dependency) and allows a delete of the MariaDBDatabase to proceed
280317
// assuming no other finalizers
281-
if controllerutil.RemoveFinalizer(mariadbDatabase, fmt.Sprintf("%s-%s", helper.GetFinalizer(), instance.Name)) {
282-
err = r.Update(ctx, mariadbDatabase)
318+
if instance.IsUserAccount() {
319+
if controllerutil.RemoveFinalizer(mariadbDatabase, fmt.Sprintf("%s-%s", helper.GetFinalizer(), instance.Name)) {
320+
err = r.Update(ctx, mariadbDatabase)
283321

284-
if err != nil && !k8s_errors.IsNotFound(err) {
285-
return ctrl.Result{}, err
322+
if err != nil && !k8s_errors.IsNotFound(err) {
323+
return ctrl.Result{}, err
324+
}
286325
}
287-
288326
}
289327

290328
// remove finalizer "openstack.org/mariadbaccount" from both the
291329
// MariaDBAccount as well as the Secret which is referenced from the
292330
// MariaDBAccount, allowing both to be deleted assuming no other
293331
// finalizers
294-
err = r.removeAccountAndSecretFinalizer(ctx, helper, instance)
332+
err := r.removeAccountAndSecretFinalizer(ctx, helper, instance)
295333
return ctrl.Result{}, err
296334
} else if dbGalera == nil {
297335
return result, err
@@ -300,13 +338,24 @@ func (r *MariaDBAccountReconciler) reconcileDelete(
300338
dbContainerImage := dbGalera.Spec.ContainerImage
301339
serviceAccountName := dbGalera.RbacResourceName()
302340

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

305-
jobDef, err := mariadb.DeleteDbAccountJob(dbGalera, instance, mariadbDatabase.Spec.Name, dbHostname, dbContainerImage, serviceAccountName, dbGalera.Spec.NodeSelector)
306-
if err != nil {
307-
return ctrl.Result{}, err
308-
}
343+
if instance.IsUserAccount() {
344+
345+
log.Info(fmt.Sprintf("Running account delete '%s' MariaDBDatabase '%s'", instance.Name, mariadbDatabase.Spec.Name))
346+
347+
jobDef, err = mariadb.DeleteDbAccountJob(dbGalera, instance, mariadbDatabase.Spec.Name, dbHostname, dbContainerImage, serviceAccountName, dbGalera.Spec.NodeSelector)
348+
if err != nil {
349+
return ctrl.Result{}, err
350+
}
351+
} else {
352+
log.Info(fmt.Sprintf("Running system account delete '%s'", instance.Name))
309353

354+
jobDef, err = mariadb.DeleteDbAccountJob(dbGalera, instance, "", dbHostname, dbContainerImage, serviceAccountName, dbGalera.Spec.NodeSelector)
355+
if err != nil {
356+
return ctrl.Result{}, err
357+
}
358+
}
310359
accountDeleteHash := instance.Status.Hash[databasev1beta1.AccountDeleteHash]
311360
accountDeleteJob := job.NewJob(
312361
jobDef,
@@ -335,18 +384,19 @@ func (r *MariaDBAccountReconciler) reconcileDelete(
335384
// scheme allows multiple MariaDBAccounts to claim the same MariaDBDatabase
336385
// as a dependency) and allows a delete of the MariaDBDatabase to proceed
337386
// assuming no other finalizers
338-
if controllerutil.RemoveFinalizer(mariadbDatabase, fmt.Sprintf("%s-%s", helper.GetFinalizer(), instance.Name)) {
339-
err = r.Update(ctx, mariadbDatabase)
340-
if err != nil {
341-
return ctrl.Result{}, err
387+
if instance.IsUserAccount() {
388+
if controllerutil.RemoveFinalizer(mariadbDatabase, fmt.Sprintf("%s-%s", helper.GetFinalizer(), instance.Name)) {
389+
err = r.Update(ctx, mariadbDatabase)
390+
if err != nil {
391+
return ctrl.Result{}, err
392+
}
342393
}
343394
}
344395

345396
// remove finalizer "openstack.org/mariadbaccount" from
346397
// both the MariaDBAccount as well as the Secret which is referenced
347398
// from the MariaDBAccount, allowing both to be deleted
348399
err = r.removeAccountAndSecretFinalizer(ctx, helper, instance)
349-
350400
return ctrl.Result{}, err
351401
}
352402

@@ -526,9 +576,17 @@ func (r *MariaDBAccountReconciler) getGaleraForCreateOrDelete(
526576
helper *helper.Helper, instance *databasev1beta1.MariaDBAccount,
527577
mariadbDatabase *databasev1beta1.MariaDBDatabase) (*databasev1beta1.Galera, string, ctrl.Result, error) {
528578

529-
dbName := mariadbDatabase.Labels["dbName"]
579+
var dbGalera *databasev1beta1.Galera
580+
var err error
581+
var dbName string
530582

531-
dbGalera, err := GetDatabaseObject(ctx, r.Client, dbName, instance.Namespace)
583+
if instance.IsUserAccount() {
584+
dbName = mariadbDatabase.Labels["dbName"]
585+
} else {
586+
// note mariadbDatabase is passed as nil in this case
587+
dbName = instance.Labels["dbName"]
588+
}
589+
dbGalera, err = GetDatabaseObject(ctx, r.Client, dbName, instance.Namespace)
532590

533591
if err != nil {
534592
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)