diff --git a/apis/bases/core.openstack.org_openstackcontrolplanes.yaml b/apis/bases/core.openstack.org_openstackcontrolplanes.yaml index 90726b98..670f10d0 100644 --- a/apis/bases/core.openstack.org_openstackcontrolplanes.yaml +++ b/apis/bases/core.openstack.org_openstackcontrolplanes.yaml @@ -13965,6 +13965,58 @@ spec: - extraVol type: object type: array + galera: + description: Galera - Parameters related to the Galera services + properties: + enabled: + default: true + description: Enabled - Whether Galera services should be deployed + and managed + type: boolean + templates: + additionalProperties: + description: GaleraSpec defines the desired state of Galera + properties: + adoptionRedirect: + description: Adoption configuration + properties: + host: + description: MariaDB host to redirect to (IP or name) + type: string + type: object + containerImage: + default: quay.io/tripleozedcentos9/openstack-mariadb:current-tripleo + description: Name of the galera container image to run + type: string + replicas: + default: 1 + description: Size of the galera cluster deployment + enum: + - 1 + - 3 + format: int32 + minimum: 1 + type: integer + secret: + description: Name of the secret to look for password keys + type: string + storageClass: + description: Storage class to host the mariadb databases + type: string + storageRequest: + description: Storage size allocated for the mariadb databases + type: string + required: + - containerImage + - replicas + - secret + - storageClass + - storageRequest + type: object + description: Templates - Overrides to use when creating the Galera + databases + type: object + type: object glance: description: Glance - Parameters related to the Glance service properties: diff --git a/apis/core/v1beta1/openstackcontrolplane_types.go b/apis/core/v1beta1/openstackcontrolplane_types.go index c0f4088a..2808dbf6 100644 --- a/apis/core/v1beta1/openstackcontrolplane_types.go +++ b/apis/core/v1beta1/openstackcontrolplane_types.go @@ -70,6 +70,10 @@ type OpenStackControlPlaneSpec struct { // Mariadb - Parameters related to the Mariadb service Mariadb MariadbSection `json:"mariadb,omitempty"` + // +kubebuilder:validation:Optional + // Galera - Parameters related to the Galera services + Galera GaleraSection `json:"galera,omitempty"` + // +kubebuilder:validation:Optional // Rabbitmq - Parameters related to the Rabbitmq service Rabbitmq RabbitmqSection `json:"rabbitmq,omitempty"` @@ -162,6 +166,18 @@ type MariadbSection struct { Templates map[string]mariadbv1.MariaDBSpec `json:"templates,omitempty"` } +// GaleraSection defines the desired state of Galera services +type GaleraSection struct { + // +kubebuilder:validation:Optional + // +kubebuilder:default=true + // Enabled - Whether Galera services should be deployed and managed + Enabled bool `json:"enabled,omitempty"` + + // +kubebuilder:validation:Optional + // Templates - Overrides to use when creating the Galera databases + Templates map[string]mariadbv1.GaleraSpec `json:"templates,omitempty"` +} + // RabbitmqSection defines the desired state of RabbitMQ service type RabbitmqSection struct { // +kubebuilder:validation:Optional diff --git a/apis/core/v1beta1/openstackcontrolplane_webhook.go b/apis/core/v1beta1/openstackcontrolplane_webhook.go index c4a5297f..b83b865b 100644 --- a/apis/core/v1beta1/openstackcontrolplane_webhook.go +++ b/apis/core/v1beta1/openstackcontrolplane_webhook.go @@ -66,19 +66,24 @@ func (r *OpenStackControlPlane) ValidateDelete() error { func (r *OpenStackControlPlane) checkDepsEnabled(name string) bool { switch name { case "Keystone": - return r.Spec.Mariadb.Enabled + return (r.Spec.Mariadb.Enabled || r.Spec.Galera.Enabled) case "Glance": - return r.Spec.Mariadb.Enabled && r.Spec.Keystone.Enabled + return (r.Spec.Mariadb.Enabled || r.Spec.Galera.Enabled) && + r.Spec.Keystone.Enabled case "Cinder": - return (r.Spec.Mariadb.Enabled && r.Spec.Rabbitmq.Enabled && + return ((r.Spec.Mariadb.Enabled || r.Spec.Galera.Enabled) && + r.Spec.Rabbitmq.Enabled && r.Spec.Keystone.Enabled) case "Placement": - return r.Spec.Mariadb.Enabled && r.Spec.Keystone.Enabled + return (r.Spec.Mariadb.Enabled || r.Spec.Galera.Enabled) && + r.Spec.Keystone.Enabled case "Neutron": - return (r.Spec.Mariadb.Enabled && r.Spec.Rabbitmq.Enabled && + return ((r.Spec.Mariadb.Enabled || r.Spec.Galera.Enabled) && + r.Spec.Rabbitmq.Enabled && r.Spec.Keystone.Enabled) case "Nova": - return (r.Spec.Mariadb.Enabled && r.Spec.Rabbitmq.Enabled && + return ((r.Spec.Mariadb.Enabled || r.Spec.Galera.Enabled) && + r.Spec.Rabbitmq.Enabled && r.Spec.Keystone.Enabled && r.Spec.Placement.Enabled && r.Spec.Neutron.Enabled && r.Spec.Glance.Enabled) } @@ -88,6 +93,11 @@ func (r *OpenStackControlPlane) checkDepsEnabled(name string) bool { // ValidateServices implements common function for validating services func (r *OpenStackControlPlane) ValidateServices() error { + // Temporary check until MariaDB is deprecated + if r.Spec.Mariadb.Enabled && r.Spec.Galera.Enabled { + return fmt.Errorf("Mariadb and Galera are mutually exclusive") + } + // Add service dependency validations errorMsg := "%s service dependencies are not enabled." diff --git a/apis/core/v1beta1/zz_generated.deepcopy.go b/apis/core/v1beta1/zz_generated.deepcopy.go index 38881d4f..01507922 100644 --- a/apis/core/v1beta1/zz_generated.deepcopy.go +++ b/apis/core/v1beta1/zz_generated.deepcopy.go @@ -45,6 +45,28 @@ func (in *CinderSection) DeepCopy() *CinderSection { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GaleraSection) DeepCopyInto(out *GaleraSection) { + *out = *in + if in.Templates != nil { + in, out := &in.Templates, &out.Templates + *out = make(map[string]apiv1beta1.GaleraSpec, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GaleraSection. +func (in *GaleraSection) DeepCopy() *GaleraSection { + if in == nil { + return nil + } + out := new(GaleraSection) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *GlanceSection) DeepCopyInto(out *GlanceSection) { *out = *in @@ -241,6 +263,7 @@ func (in *OpenStackControlPlaneSpec) DeepCopyInto(out *OpenStackControlPlaneSpec in.Glance.DeepCopyInto(&out.Glance) in.Cinder.DeepCopyInto(&out.Cinder) in.Mariadb.DeepCopyInto(&out.Mariadb) + in.Galera.DeepCopyInto(&out.Galera) in.Rabbitmq.DeepCopyInto(&out.Rabbitmq) in.Ovn.DeepCopyInto(&out.Ovn) in.Ovs.DeepCopyInto(&out.Ovs) diff --git a/config/crd/bases/core.openstack.org_openstackcontrolplanes.yaml b/config/crd/bases/core.openstack.org_openstackcontrolplanes.yaml index 90726b98..670f10d0 100644 --- a/config/crd/bases/core.openstack.org_openstackcontrolplanes.yaml +++ b/config/crd/bases/core.openstack.org_openstackcontrolplanes.yaml @@ -13965,6 +13965,58 @@ spec: - extraVol type: object type: array + galera: + description: Galera - Parameters related to the Galera services + properties: + enabled: + default: true + description: Enabled - Whether Galera services should be deployed + and managed + type: boolean + templates: + additionalProperties: + description: GaleraSpec defines the desired state of Galera + properties: + adoptionRedirect: + description: Adoption configuration + properties: + host: + description: MariaDB host to redirect to (IP or name) + type: string + type: object + containerImage: + default: quay.io/tripleozedcentos9/openstack-mariadb:current-tripleo + description: Name of the galera container image to run + type: string + replicas: + default: 1 + description: Size of the galera cluster deployment + enum: + - 1 + - 3 + format: int32 + minimum: 1 + type: integer + secret: + description: Name of the secret to look for password keys + type: string + storageClass: + description: Storage class to host the mariadb databases + type: string + storageRequest: + description: Storage size allocated for the mariadb databases + type: string + required: + - containerImage + - replicas + - secret + - storageClass + - storageRequest + type: object + description: Templates - Overrides to use when creating the Galera + databases + type: object + type: object glance: description: Glance - Parameters related to the Glance service properties: diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 5768cf48..4cb08ce7 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -91,6 +91,18 @@ rules: - patch - update - watch +- apiGroups: + - mariadb.openstack.org + resources: + - galeras + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - mariadb.openstack.org resources: diff --git a/config/samples/core_v1beta1_openstackcontrolplane.yaml b/config/samples/core_v1beta1_openstackcontrolplane.yaml index 884ee9c6..f7362422 100644 --- a/config/samples/core_v1beta1_openstackcontrolplane.yaml +++ b/config/samples/core_v1beta1_openstackcontrolplane.yaml @@ -15,6 +15,15 @@ spec: openstack: containerImage: quay.io/tripleozedcentos9/openstack-mariadb:current-tripleo storageRequest: 500M + galera: + enabled: false + templates: + openstack: + containerImage: quay.io/tripleozedcentos9/openstack-mariadb:current-tripleo + storageClass: local-storage + storageRequest: 500M + secret: osp-secret + replicas: 1 rabbitmq: templates: rabbitmq: diff --git a/config/samples/core_v1beta1_openstackcontrolplane_galera.yaml b/config/samples/core_v1beta1_openstackcontrolplane_galera.yaml new file mode 100644 index 00000000..23543bd1 --- /dev/null +++ b/config/samples/core_v1beta1_openstackcontrolplane_galera.yaml @@ -0,0 +1,118 @@ +apiVersion: core.openstack.org/v1beta1 +kind: OpenStackControlPlane +metadata: + name: openstack +spec: + secret: osp-secret + storageClass: local-storage + keystone: + template: + containerImage: quay.io/tripleozedcentos9/openstack-keystone:current-tripleo + databaseInstance: openstack + secret: osp-secret + mariadb: + enabled: false + templates: + openstack: + containerImage: quay.io/tripleozedcentos9/openstack-mariadb:current-tripleo + storageRequest: 500M + galera: + enabled: true + templates: + openstack: + containerImage: quay.io/tripleozedcentos9/openstack-mariadb:current-tripleo + storageClass: local-storage + storageRequest: 500M + secret: osp-secret + replicas: 1 + rabbitmq: + templates: + rabbitmq: + replicas: 1 + #resources: + # requests: + # cpu: 500m + # memory: 1Gi + # limits: + # cpu: 800m + # memory: 1Gi + rabbitmq-cell1: + replicas: 1 + placement: + template: + databaseInstance: openstack + containerImage: quay.io/tripleozedcentos9/openstack-placement-api:current-tripleo + secret: osp-secret + glance: + template: + databaseInstance: openstack + containerImage: quay.io/tripleozedcentos9/openstack-glance-api:current-tripleo + storageClass: "" + storageRequest: 10G + glanceAPIInternal: + containerImage: quay.io/tripleozedcentos9/openstack-glance-api:current-tripleo + glanceAPIExternal: + containerImage: quay.io/tripleozedcentos9/openstack-glance-api:current-tripleo + cinder: + template: + cinderAPI: + replicas: 1 + containerImage: quay.io/tripleozedcentos9/openstack-cinder-api:current-tripleo + cinderScheduler: + replicas: 1 + containerImage: quay.io/tripleozedcentos9/openstack-cinder-scheduler:current-tripleo + cinderBackup: + replicas: 1 + containerImage: quay.io/tripleozedcentos9/openstack-cinder-backup:current-tripleo + cinderVolumes: + volume1: + containerImage: quay.io/tripleozedcentos9/openstack-cinder-volume:current-tripleo + replicas: 1 + ovn: + template: + ovnDBCluster: + ovndbcluster-nb: + replicas: 1 + containerImage: quay.io/tripleozedcentos9/openstack-ovn-nb-db-server:current-tripleo + dbType: NB + storageRequest: 10G + ovndbcluster-sb: + replicas: 1 + containerImage: quay.io/tripleozedcentos9/openstack-ovn-sb-db-server:current-tripleo + dbType: SB + storageRequest: 10G + ovnNorthd: + replicas: 1 + containerImage: quay.io/tripleozedcentos9/openstack-ovn-northd:current-tripleo + ovs: + template: + ovsContainerImage: "quay.io/skaplons/ovs:latest" + ovnContainerImage: "quay.io/tripleozedcentos9/openstack-ovn-controller:current-tripleo" + external-ids: + system-id: "random" + ovn-bridge: "br-int" + ovn-encap-type: "geneve" + neutron: + template: + databaseInstance: openstack + containerImage: quay.io/tripleozedcentos9/openstack-neutron-server:current-tripleo + secret: osp-secret + nova: + template: + secret: osp-secret + ironic: + template: + databaseInstance: openstack + ironicAPI: + replicas: 1 + containerImage: quay.io/tripleozedcentos9/openstack-ironic-api:current-tripleo + ironicConductors: + - replicas: 1 + containerImage: quay.io/tripleozedcentos9/openstack-ironic-conductor:current-tripleo + pxeContainerImage: quay.io/tripleozedcentos9/openstack-ironic-pxe:current-tripleo + storageRequest: 10G + ironicInspector: + replicas: 1 + containerImage: quay.io/tripleozedcentos9/openstack-ironic-inspector:current-tripleo + pxeContainerImage: quay.io/tripleozedcentos9/openstack-ironic-pxe:current-tripleo + secret: osp-secret diff --git a/controllers/core/openstackcontrolplane_controller.go b/controllers/core/openstackcontrolplane_controller.go index 5d93dc27..b22f192b 100644 --- a/controllers/core/openstackcontrolplane_controller.go +++ b/controllers/core/openstackcontrolplane_controller.go @@ -69,6 +69,7 @@ type OpenStackControlPlaneReconciler struct { //+kubebuilder:rbac:groups=cinder.openstack.org,resources=cinders,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups=nova.openstack.org,resources=nova,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups=mariadb.openstack.org,resources=mariadbs,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=mariadb.openstack.org,resources=galeras,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups=neutron.openstack.org,resources=neutronapis,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups=ovn.openstack.org,resources=ovndbclusters,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups=ovn.openstack.org,resources=ovnnorthds,verbs=get;list;watch;create;update;patch;delete @@ -161,6 +162,13 @@ func (r *OpenStackControlPlaneReconciler) reconcileNormal(ctx context.Context, i return ctrlResult, nil } + ctrlResult, err = openstack.ReconcileGaleras(ctx, instance, helper) + if err != nil { + return ctrl.Result{}, err + } else if (ctrlResult != ctrl.Result{}) { + return ctrlResult, nil + } + ctrlResult, err = openstack.ReconcileKeystoneAPI(ctx, instance, helper) if err != nil { return ctrl.Result{}, err @@ -292,6 +300,7 @@ func (r *OpenStackControlPlaneReconciler) SetupWithManager(mgr ctrl.Manager) err return ctrl.NewControllerManagedBy(mgr). For(&corev1beta1.OpenStackControlPlane{}). Owns(&mariadbv1.MariaDB{}). + Owns(&mariadbv1.Galera{}). Owns(&keystonev1.KeystoneAPI{}). Owns(&placementv1.PlacementAPI{}). Owns(&glancev1.Glance{}). diff --git a/pkg/openstack/galera.go b/pkg/openstack/galera.go new file mode 100644 index 00000000..b8324052 --- /dev/null +++ b/pkg/openstack/galera.go @@ -0,0 +1,134 @@ +package openstack + +import ( + "context" + "fmt" + "strings" + + "github.com/openstack-k8s-operators/lib-common/modules/common/condition" + "github.com/openstack-k8s-operators/lib-common/modules/common/helper" + mariadbv1 "github.com/openstack-k8s-operators/mariadb-operator/api/v1beta1" + + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + + corev1beta1 "github.com/openstack-k8s-operators/openstack-operator/apis/core/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + ctrl "sigs.k8s.io/controller-runtime" +) + +type galeraStatus int + +const ( + galeraFailed galeraStatus = iota + galeraCreating galeraStatus = iota + galeraReady galeraStatus = iota +) + +// ReconcileGaleras - +func ReconcileGaleras( + ctx context.Context, + instance *corev1beta1.OpenStackControlPlane, + helper *helper.Helper, +) (ctrl.Result, error) { + if !instance.Spec.Galera.Enabled { + return ctrl.Result{}, nil + } + + var failures []string = []string{} + var inprogress []string = []string{} + + for name, spec := range instance.Spec.Galera.Templates { + status, err := reconcileGalera(ctx, instance, helper, name, &spec) + + switch status { + case galeraFailed: + failures = append(failures, fmt.Sprintf("%s(%v)", name, err.Error())) + case galeraCreating: + inprogress = append(inprogress, name) + case galeraReady: + default: + return ctrl.Result{}, fmt.Errorf("Invalid galeraStatus from reconcileGalera: %d for Galera %s", status, name) + } + } + + if len(failures) > 0 { + errors := strings.Join(failures, ",") + + instance.Status.Conditions.Set(condition.FalseCondition( + corev1beta1.OpenStackControlPlaneMariaDBReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + corev1beta1.OpenStackControlPlaneMariaDBReadyErrorMessage, + errors)) + + return ctrl.Result{}, fmt.Errorf(errors) + + } else if len(inprogress) > 0 { + instance.Status.Conditions.Set(condition.FalseCondition( + corev1beta1.OpenStackControlPlaneMariaDBReadyCondition, + condition.RequestedReason, + condition.SeverityInfo, + corev1beta1.OpenStackControlPlaneMariaDBReadyRunningMessage)) + } else { + instance.Status.Conditions.MarkTrue( + corev1beta1.OpenStackControlPlaneMariaDBReadyCondition, + corev1beta1.OpenStackControlPlaneMariaDBReadyMessage, + ) + } + + return ctrl.Result{}, nil +} + +// reconcileGalera - +func reconcileGalera( + ctx context.Context, + instance *corev1beta1.OpenStackControlPlane, + helper *helper.Helper, + name string, + spec *mariadbv1.GaleraSpec, +) (galeraStatus, error) { + galera := &mariadbv1.Galera{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: instance.Namespace, + }, + } + + if !instance.Spec.Galera.Enabled { + if _, err := EnsureDeleted(ctx, helper, galera); err != nil { + return galeraFailed, err + } + instance.Status.Conditions.Remove(corev1beta1.OpenStackControlPlaneMariaDBReadyCondition) + return galeraReady, nil + } + + helper.GetLogger().Info("Reconciling Galera", "Galera.Namespace", instance.Namespace, "Galera.Name", name) + op, err := controllerutil.CreateOrPatch(ctx, helper.GetClient(), galera, func() error { + spec.DeepCopyInto(&galera.Spec) + if galera.Spec.Secret == "" { + galera.Spec.Secret = instance.Spec.Secret + } + if galera.Spec.StorageClass == "" { + galera.Spec.StorageClass = instance.Spec.StorageClass + } + err := controllerutil.SetControllerReference(helper.GetBeforeObject(), galera, helper.GetScheme()) + if err != nil { + return err + } + + return nil + }) + + if err != nil { + return galeraFailed, err + } + if op != controllerutil.OperationResultNone { + helper.GetLogger().Info(fmt.Sprintf("Galera %s - %s", galera.Name, op)) + } + + if galera.IsReady() { + return galeraReady, nil + } + + return galeraCreating, nil +}