Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add multi-master option to galera #260

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions api/bases/mariadb.openstack.org_galeras.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ spec:
description: When TLS is configured, only allow connections to the
DB over TLS
type: boolean
enableMultiMaster:
default: false
description: Use multi-master service routing
type: boolean
logToDisk:
description: Log Galera pod's output to disk
type: boolean
Expand Down Expand Up @@ -104,6 +108,7 @@ spec:
type: object
required:
- containerImage
- enableMultiMaster
- replicas
- secret
- storageClass
Expand Down
3 changes: 3 additions & 0 deletions api/bases/mariadb.openstack.org_mariadbdatabases.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@ spec:
- type
type: object
type: array
enableMultiMaster:
description: whether the DB instance is using multi-master routing
type: boolean
hash:
additionalProperties:
type: string
Expand Down
3 changes: 3 additions & 0 deletions api/v1beta1/galera_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ type GaleraSpecCore struct {
// +kubebuilder:validation:Optional
// Log Galera pod's output to disk
LogToDisk bool `json:"logToDisk"`
// +kubebuilder:default=false
// Use multi-master service routing
EnableMultiMaster bool `json:"enableMultiMaster"`
}

// GaleraAttributes holds startup information for a Galera host
Expand Down
3 changes: 3 additions & 0 deletions api/v1beta1/mariadbdatabase_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ type MariaDBDatabaseStatus struct {

// Whether TLS is supported by the DB instance
TLSSupport bool `json:"tlsSupport,omitempty"`

// whether the DB instance is using multi-master routing
EnableMultiMaster bool `json:"enableMultiMaster,omitempty"`
}

//+kubebuilder:object:root=true
Expand Down
5 changes: 5 additions & 0 deletions config/crd/bases/mariadb.openstack.org_galeras.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ spec:
description: When TLS is configured, only allow connections to the
DB over TLS
type: boolean
enableMultiMaster:
default: false
description: Use multi-master service routing
type: boolean
logToDisk:
description: Log Galera pod's output to disk
type: boolean
Expand Down Expand Up @@ -104,6 +108,7 @@ spec:
type: object
required:
- containerImage
- enableMultiMaster
- replicas
- secret
- storageClass
Expand Down
3 changes: 3 additions & 0 deletions config/crd/bases/mariadb.openstack.org_mariadbdatabases.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@ spec:
- type
type: object
type: array
enableMultiMaster:
description: whether the DB instance is using multi-master routing
type: boolean
hash:
additionalProperties:
type: string
Expand Down
25 changes: 16 additions & 9 deletions controllers/galera_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -509,16 +509,23 @@ func (r *GaleraReconciler) Reconcile(ctx context.Context, req ctrl.Request) (res
// service get stuck.
controllerutil.AddFinalizer(service, helper.GetFinalizer())

// NOTE(dciabrin) We deploy Galera as an A/P service (i.e. no multi-master writes)
// by setting labels in the service's label selectors.
// This label is dynamically set based on the status of the Galera cluster,
// so in this CreateOrPatch block we must reuse whatever is present in
// the existing service CR in case we're patching it.
activePod, present := service.Spec.Selector[mariadb.ActivePodSelectorKey]
service.Spec = pkgsvc.Spec
if present {
service.Spec.Selector[mariadb.ActivePodSelectorKey] = activePod
if !instance.Spec.EnableMultiMaster {
// NOTE(dciabrin) We deploy Galera as an A/P service (i.e. no multi-master writes)
// by setting labels in the service's label selectors.
// This label is dynamically set based on the status of the Galera cluster,
// so in this CreateOrPatch block we must reuse whatever is present in
// the existing service CR in case we're patching it.
activePod, present := service.Spec.Selector[mariadb.ActivePodSelectorKey]
service.Spec = pkgsvc.Spec

if present {
service.Spec.Selector[mariadb.ActivePodSelectorKey] = activePod
}
} else {
service.Spec = pkgsvc.Spec
delete(service.Spec.Selector, mariadb.ActivePodSelectorKey)
zzzeek marked this conversation as resolved.
Show resolved Hide resolved
}

err := controllerutil.SetControllerReference(instance, service, r.Client.Scheme())
if err != nil {
return err
Expand Down
42 changes: 41 additions & 1 deletion controllers/mariadbdatabase_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import (
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/reconcile"

condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition"
helper "github.com/openstack-k8s-operators/lib-common/modules/common/helper"
Expand Down Expand Up @@ -145,7 +147,7 @@ func (r *MariaDBDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Requ

// here we know that Galera exists so add a finalizer to ourselves and to the db CR. Before this point there is no reason to have a finalizer on ourselves as nothing to cleanup.
if instance.DeletionTimestamp.IsZero() || isNewInstance { // this condition can be removed if you wish as it is always true at this point otherwise we would returned earlier.
if controllerutil.AddFinalizer(dbGalera, fmt.Sprintf("%s-%s", helper.GetFinalizer(), instance.Name)) {
if dbGalera.DeletionTimestamp.IsZero() && controllerutil.AddFinalizer(dbGalera, fmt.Sprintf("%s-%s", helper.GetFinalizer(), instance.Name)) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dciabrin can you review this line, without this the kuttl tests would run into a stack trace as trying to reconcile the mariadbdatabase after galera were deleted would raise here (this happens more frequently due to the Watch I added in this PR). note that by checking this, we do continue on to add a finalizer to the mariadbdatabase itself if one was not there already, but I think that would have been there anyway

err := r.Update(ctx, dbGalera)
if err != nil {
return ctrl.Result{}, err
Expand Down Expand Up @@ -200,6 +202,9 @@ func (r *MariaDBDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Requ
return ctrl.Result{}, err
}

// DB instances setup for multi master
instance.Status.EnableMultiMaster = dbGalera.Spec.EnableMultiMaster

dbCreateHash := instance.Status.Hash[databasev1beta1.DbCreateHash]
dbCreateJob := job.NewJob(
jobDef,
Expand Down Expand Up @@ -245,8 +250,43 @@ func (r *MariaDBDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Requ

// SetupWithManager -
func (r *MariaDBDatabaseReconciler) SetupWithManager(mgr ctrl.Manager) error {

updateStatusFn := func(ctx context.Context, o client.Object) []reconcile.Request {
log := GetLog(ctx, "MariaDBDatabase")

result := []reconcile.Request{}

mariaDBDatabases := &databasev1beta1.MariaDBDatabaseList{}

listOpts := []client.ListOption{
client.InNamespace(o.GetNamespace()),
}
if err := r.Client.List(ctx, mariaDBDatabases, listOpts...); err != nil {
log.Error(err, "Unable to retrieve MariaDBDatabase CRs %w")
return nil
}

for _, cr := range mariaDBDatabases.Items {

if o.GetName() == cr.GetLabels()["dbName"] {
name := client.ObjectKey{
Namespace: o.GetNamespace(),
Name: cr.Name,
}
log.Info(fmt.Sprintf("Galera %s is used by MariaDBDatabase CR %s", o.GetName(), cr.Name))
result = append(result, reconcile.Request{NamespacedName: name})
}
}

if len(result) > 0 {
return result
}
return nil
}

return ctrl.NewControllerManagedBy(mgr).
For(&databasev1beta1.MariaDBDatabase{}).
Watches(&databasev1beta1.Galera{}, handler.EnqueueRequestsFromMapFunc(updateStatusFn)).
Complete(r)
}

Expand Down
10 changes: 10 additions & 0 deletions tests/kuttl/tests/galera_cluster_restart/04-teardown.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
apiVersion: kuttl.dev/v1beta
kind: TestStep
delete:
- apiVersion: mariadb.openstack.org/v1beta1
kind: Galera
name: openstack
commands:
- script: |
oc delete -n $NAMESPACE pvc mysql-db-openstack-galera-0 mysql-db-openstack-galera-1 mysql-db-openstack-galera-2
for i in `oc get pv | awk '/mysql-db.*galera/ {print $1}'`; do oc patch pv $i -p '{"spec":{"claimRef": null}}'; done
10 changes: 10 additions & 0 deletions tests/kuttl/tests/galera_log_to_disk/03-teardown.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
apiVersion: kuttl.dev/v1beta
kind: TestStep
delete:
- apiVersion: mariadb.openstack.org/v1beta1
kind: Galera
name: openstack
commands:
- script: |
oc delete -n $NAMESPACE pvc mysql-db-openstack-galera-0 mysql-db-openstack-galera-1 mysql-db-openstack-galera-2
for i in `oc get pv | awk '/mysql-db.*galera/ {print $1}'`; do oc patch pv $i -p '{"spec":{"claimRef": null}}'; done
19 changes: 19 additions & 0 deletions tests/kuttl/tests/galera_multi_master/01-deploy_ap_galera.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
apiVersion: mariadb.openstack.org/v1beta1
kind: Galera
metadata:
name: openstack
spec:
secret: osp-secret
storageClass: local-storage
storageRequest: 500M
replicas: 3
enableMultiMaster: false
---
apiVersion: mariadb.openstack.org/v1beta1
kind: MariaDBDatabase
metadata:
name: mydatabase
labels:
dbName: openstack
spec:
name: mydatabase
131 changes: 131 additions & 0 deletions tests/kuttl/tests/galera_multi_master/02-assert.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
#
# Check for:
#
# - 1 MariaDB CR
# - 1 Pod for MariaDB CR
#

apiVersion: mariadb.openstack.org/v1beta1
kind: Galera
metadata:
name: openstack
spec:
replicas: 3
secret: osp-secret
storageRequest: 500M
status:
bootstrapped: true
conditions:
- message: Setup complete
reason: Ready
status: "True"
type: Ready
- message: Deployment completed
reason: Ready
status: "True"
type: DeploymentReady
- message: Exposing service completed
reason: Ready
status: "True"
type: ExposeServiceReady
- message: Input data complete
reason: Ready
status: "True"
type: InputReady
- message: RoleBinding created
reason: Ready
status: "True"
type: RoleBindingReady
- message: Role created
reason: Ready
status: "True"
type: RoleReady
- message: ServiceAccount created
reason: Ready
status: "True"
type: ServiceAccountReady
- message: Service config create completed
reason: Ready
status: "True"
type: ServiceConfigReady
- message: Input data complete
reason: Ready
status: "True"
type: TLSInputReady
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: openstack-galera
spec:
replicas: 3
selector:
matchLabels:
app: galera
cr: galera-openstack
galera/name: openstack
serviceName: openstack-galera
template:
metadata:
labels:
app: galera
cr: galera-openstack
galera/name: openstack
spec:
containers:
- command:
- /usr/bin/dumb-init
- --
- /usr/local/bin/kolla_start
name: galera
ports:
- containerPort: 3306
name: mysql
protocol: TCP
- containerPort: 4567
name: galera
protocol: TCP
serviceAccount: galera-openstack
serviceAccountName: galera-openstack
status:
availableReplicas: 3
readyReplicas: 3
replicas: 3
---
apiVersion: v1
kind: Pod
metadata:
name: openstack-galera-0
---
apiVersion: v1
kind: Pod
metadata:
name: openstack-galera-1
---
apiVersion: v1
kind: Pod
metadata:
name: openstack-galera-2
---
apiVersion: v1
kind: Service
metadata:
name: openstack-galera
spec:
ports:
- name: mysql
port: 3306
protocol: TCP
targetPort: 3306
selector:
app: galera
cr: galera-openstack
---
apiVersion: mariadb.openstack.org/v1beta1
kind: MariaDBDatabase
metadata:
labels:
dbName: openstack
name: mydatabase
spec:
name: mydatabase
7 changes: 7 additions & 0 deletions tests/kuttl/tests/galera_multi_master/03-assert.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
apiVersion: kuttl.dev/v1beta1
kind: TestAssert
commands:
- script: |
# ensure kubernetes pod key present
oc get -n ${NAMESPACE} service openstack -o yaml | grep "statefulset.kubernetes.io/pod-name: openstack-galera-"
10 changes: 10 additions & 0 deletions tests/kuttl/tests/galera_multi_master/04-deploy_mm_galera.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
apiVersion: mariadb.openstack.org/v1beta1
kind: Galera
metadata:
name: openstack
spec:
secret: osp-secret
storageClass: local-storage
storageRequest: 500M
replicas: 3
enableMultiMaster: true
6 changes: 6 additions & 0 deletions tests/kuttl/tests/galera_multi_master/05-assert.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
apiVersion: kuttl.dev/v1beta1
kind: TestAssert
commands:
- script: |
# ensure kubernetes pod key *not* present
! oc get -n ${NAMESPACE} service openstack -o yaml | grep "statefulset.kubernetes.io/pod-name: openstack-galera-"
Loading
Loading