Skip to content

Commit c020dd5

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 c020dd5

File tree

12 files changed

+322
-52
lines changed

12 files changed

+322
-52
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: 102 additions & 48 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"
@@ -113,12 +114,18 @@ func (r *MariaDBAccountReconciler) Reconcile(ctx context.Context, req ctrl.Reque
113114
}()
114115

115116
// 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-
)
117+
cl := condition.Conditions{
118+
*condition.UnknownCondition(condition.ReadyCondition, condition.InitReason, condition.ReadyInitMessage),
119+
*condition.UnknownCondition(databasev1beta1.MariaDBServerReadyCondition, condition.InitReason, databasev1beta1.MariaDBServerReadyInitMessage),
120+
}
121+
if instance.IsUserAccount() {
122+
// user accounts also need the database ready condition
123+
// the element is being inserted into the list in a specific location
124+
// to preseve expectations of tests suites that are hardcoded to
125+
// expect a specific ordering of Conditions in YAML displays
126+
cl = append(cl, *condition.UnknownCondition(databasev1beta1.MariaDBDatabaseReadyCondition, condition.InitReason, databasev1beta1.MariaDBDatabaseReadyInitMessage))
127+
}
128+
cl = append(cl, *condition.UnknownCondition(databasev1beta1.MariaDBAccountReadyCondition, condition.InitReason, databasev1beta1.MariaDBAccountReadyInitMessage))
122129
instance.Status.Conditions.Init(&cl)
123130

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

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
144+
var mariadbDatabase *databasev1beta1.MariaDBDatabase
145+
var err error
146+
147+
if instance.IsUserAccount() {
148+
// for User account, get a handle to the current, active MariaDBDatabase.
149+
// if not ready yet, requeue.
150+
mariadbDatabase, result, err = r.getMariaDBDatabaseForCreate(ctx, log, instance)
151+
if mariadbDatabase == nil {
152+
return result, err
153+
}
142154
}
143155

144156
if controllerutil.AddFinalizer(instance, helper.GetFinalizer()) {
145157
// we need to persist this right away
146158
return ctrl.Result{}, nil
147159
}
148160

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
161+
if instance.IsUserAccount() {
162+
// MariaDBdatabase exists and we are a create case. ensure finalizers set up
163+
if controllerutil.AddFinalizer(mariadbDatabase, fmt.Sprintf("%s-%s", helper.GetFinalizer(), instance.Name)) {
164+
err = r.Update(ctx, mariadbDatabase)
165+
if err != nil {
166+
return ctrl.Result{}, err
167+
}
154168
}
155169
}
156170

@@ -174,13 +188,25 @@ func (r *MariaDBAccountReconciler) reconcileCreate(
174188
return result, err
175189
}
176190

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
191+
var jobDef *batchv1.Job
192+
193+
if instance.IsUserAccount() {
194+
log.Info(fmt.Sprintf("Running account create '%s' MariaDBDatabase '%s'",
195+
instance.Name, mariadbDatabase.Spec.Name))
196+
jobDef, err = mariadb.CreateDbAccountJob(
197+
dbGalera, instance, mariadbDatabase.Spec.Name, dbHostname,
198+
dbContainerImage, serviceAccountName, dbGalera.Spec.NodeSelector)
199+
if err != nil {
200+
return ctrl.Result{}, err
201+
}
202+
} else {
203+
log.Info(fmt.Sprintf("Running system account create '%s'", instance.Name))
204+
jobDef, err = mariadb.CreateDbAccountJob(
205+
dbGalera, instance, "", dbHostname,
206+
dbContainerImage, serviceAccountName, dbGalera.Spec.NodeSelector)
207+
if err != nil {
208+
return ctrl.Result{}, err
209+
}
184210
}
185211

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

231-
mariadbDatabase, result, err := r.getMariaDBDatabaseForDelete(ctx, log, helper, instance)
232-
if mariadbDatabase == nil {
233-
return result, err
257+
var mariadbDatabase *databasev1beta1.MariaDBDatabase
258+
var err error
259+
260+
if instance.IsUserAccount() {
261+
mariadbDatabase, result, err = r.getMariaDBDatabaseForDelete(ctx, log, helper, instance)
262+
if mariadbDatabase == nil {
263+
return result, err
264+
}
234265
}
235266

236267
// dont do actual DROP USER until finalizers from downstream controllers
@@ -259,10 +290,12 @@ func (r *MariaDBAccountReconciler) reconcileDelete(
259290
databasev1beta1.MariaDBAccountReadyForDeleteMessage,
260291
)
261292

262-
instance.Status.Conditions.MarkTrue(
263-
databasev1beta1.MariaDBDatabaseReadyCondition,
264-
databasev1beta1.MariaDBDatabaseReadyMessage,
265-
)
293+
if instance.IsUserAccount() {
294+
instance.Status.Conditions.MarkTrue(
295+
databasev1beta1.MariaDBDatabaseReadyCondition,
296+
databasev1beta1.MariaDBDatabaseReadyMessage,
297+
)
298+
}
266299

267300
// now proceed to do actual work. acquire the Galera instance
268301
// which will lead us to the hostname and container image to target
@@ -278,20 +311,21 @@ func (r *MariaDBAccountReconciler) reconcileDelete(
278311
// scheme allows multiple MariaDBAccounts to claim the same MariaDBDatabase
279312
// as a dependency) and allows a delete of the MariaDBDatabase to proceed
280313
// assuming no other finalizers
281-
if controllerutil.RemoveFinalizer(mariadbDatabase, fmt.Sprintf("%s-%s", helper.GetFinalizer(), instance.Name)) {
282-
err = r.Update(ctx, mariadbDatabase)
314+
if instance.IsUserAccount() {
315+
if controllerutil.RemoveFinalizer(mariadbDatabase, fmt.Sprintf("%s-%s", helper.GetFinalizer(), instance.Name)) {
316+
err = r.Update(ctx, mariadbDatabase)
283317

284-
if err != nil && !k8s_errors.IsNotFound(err) {
285-
return ctrl.Result{}, err
318+
if err != nil && !k8s_errors.IsNotFound(err) {
319+
return ctrl.Result{}, err
320+
}
286321
}
287-
288322
}
289323

290324
// remove finalizer "openstack.org/mariadbaccount" from both the
291325
// MariaDBAccount as well as the Secret which is referenced from the
292326
// MariaDBAccount, allowing both to be deleted assuming no other
293327
// finalizers
294-
err = r.removeAccountAndSecretFinalizer(ctx, helper, instance)
328+
err := r.removeAccountAndSecretFinalizer(ctx, helper, instance)
295329
return ctrl.Result{}, err
296330
} else if dbGalera == nil {
297331
return result, err
@@ -300,13 +334,24 @@ func (r *MariaDBAccountReconciler) reconcileDelete(
300334
dbContainerImage := dbGalera.Spec.ContainerImage
301335
serviceAccountName := dbGalera.RbacResourceName()
302336

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

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-
}
339+
if instance.IsUserAccount() {
340+
341+
log.Info(fmt.Sprintf("Running account delete '%s' MariaDBDatabase '%s'", instance.Name, mariadbDatabase.Spec.Name))
342+
343+
jobDef, err = mariadb.DeleteDbAccountJob(dbGalera, instance, mariadbDatabase.Spec.Name, dbHostname, dbContainerImage, serviceAccountName, dbGalera.Spec.NodeSelector)
344+
if err != nil {
345+
return ctrl.Result{}, err
346+
}
347+
} else {
348+
log.Info(fmt.Sprintf("Running system account delete '%s'", instance.Name))
309349

350+
jobDef, err = mariadb.DeleteDbAccountJob(dbGalera, instance, "", dbHostname, dbContainerImage, serviceAccountName, dbGalera.Spec.NodeSelector)
351+
if err != nil {
352+
return ctrl.Result{}, err
353+
}
354+
}
310355
accountDeleteHash := instance.Status.Hash[databasev1beta1.AccountDeleteHash]
311356
accountDeleteJob := job.NewJob(
312357
jobDef,
@@ -335,18 +380,19 @@ func (r *MariaDBAccountReconciler) reconcileDelete(
335380
// scheme allows multiple MariaDBAccounts to claim the same MariaDBDatabase
336381
// as a dependency) and allows a delete of the MariaDBDatabase to proceed
337382
// 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
383+
if instance.IsUserAccount() {
384+
if controllerutil.RemoveFinalizer(mariadbDatabase, fmt.Sprintf("%s-%s", helper.GetFinalizer(), instance.Name)) {
385+
err = r.Update(ctx, mariadbDatabase)
386+
if err != nil {
387+
return ctrl.Result{}, err
388+
}
342389
}
343390
}
344391

345392
// remove finalizer "openstack.org/mariadbaccount" from
346393
// both the MariaDBAccount as well as the Secret which is referenced
347394
// from the MariaDBAccount, allowing both to be deleted
348395
err = r.removeAccountAndSecretFinalizer(ctx, helper, instance)
349-
350396
return ctrl.Result{}, err
351397
}
352398

@@ -526,9 +572,17 @@ func (r *MariaDBAccountReconciler) getGaleraForCreateOrDelete(
526572
helper *helper.Helper, instance *databasev1beta1.MariaDBAccount,
527573
mariadbDatabase *databasev1beta1.MariaDBDatabase) (*databasev1beta1.Galera, string, ctrl.Result, error) {
528574

529-
dbName := mariadbDatabase.Labels["dbName"]
575+
var dbGalera *databasev1beta1.Galera
576+
var err error
577+
var dbName string
530578

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

533587
if err != nil {
534588
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
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#!/bin/sh
2+
3+
set -x
4+
5+
galera="$1"
6+
dbname="$2"
7+
username="$3"
8+
password="$4"
9+
10+
11+
found=0
12+
not_found=1
13+
14+
if [ "$5" = "--reverse" ];then
15+
# sometimes we want to check that a user does not exist
16+
found=1
17+
not_found=0
18+
fi
19+
20+
found_username=$(oc exec -n ${NAMESPACE} -c galera ${galera} -- /bin/sh -c 'source /var/lib/operator-scripts/mysql_root_auth.sh; mysql -uroot -p${DB_ROOT_PASSWORD} -Nse "select user from mysql.user"' | grep -o -w ${username})
21+
22+
# username was not found, exit
23+
if [ -z "$found_username" ]; then
24+
exit $not_found
25+
fi
26+
27+
# username was found. if we wanted it to be found, then check the login also.
28+
if [ "$found" = "0" ]; then
29+
if [ -n "$dbname" ]; then
30+
oc exec -n ${NAMESPACE} -c galera ${galera} -- /bin/sh -c "mysql -u${username} -p${password} -Nse 'select database();' ${dbname}" || exit -1
31+
else
32+
oc exec -n ${NAMESPACE} -c galera ${galera} -- /bin/sh -c "mysql -u${username} -p${password} -Nse 'select 1'" || exit -1
33+
fi
34+
fi
35+
36+
exit $found

0 commit comments

Comments
 (0)