From 9f53adfa5390be691a14b3db392df3a19d37bd4d Mon Sep 17 00:00:00 2001 From: Martin Schuppert Date: Mon, 5 Feb 2024 15:31:53 +0100 Subject: [PATCH] [tlse] TLS database connection The my.cnf file gets added to the secret holding the service configs. The content of my.cnf is centrally managed in the mariadb-operator and retrieved calling db.GetDatabaseClientConfig(tlsCfg) Depends-On: https://github.com/openstack-k8s-operators/mariadb-operator/pull/190 Depends-On: https://github.com/openstack-k8s-operators/mariadb-operator/pull/191 Jira: OSPRH-4547 --- controllers/keystoneapi_controller.go | 132 +++++++++++------- .../config/keystone-api-config.json | 6 + .../functional/keystoneapi_controller_test.go | 14 +- 3 files changed, 95 insertions(+), 57 deletions(-) diff --git a/controllers/keystoneapi_controller.go b/controllers/keystoneapi_controller.go index 42a3ab23..360ce8ba 100644 --- a/controllers/keystoneapi_controller.go +++ b/controllers/keystoneapi_controller.go @@ -733,60 +733,12 @@ func (r *KeystoneAPIReconciler) reconcileNormal( // // create service DB instance // - db := mariadbv1.NewDatabase( - instance.Name, - instance.Spec.DatabaseUser, - instance.Spec.Secret, - map[string]string{ - "dbName": instance.Spec.DatabaseInstance, - }, - ) - // create or patch the DB - ctrlResult, err := db.CreateOrPatchDB( - ctx, - helper, - ) + db, result, err := r.ensureDB(ctx, helper, instance) if err != nil { - instance.Status.Conditions.Set(condition.FalseCondition( - condition.DBReadyCondition, - condition.ErrorReason, - condition.SeverityWarning, - condition.DBReadyErrorMessage, - err.Error())) return ctrl.Result{}, err + } else if (result != ctrl.Result{}) { + return result, nil } - if (ctrlResult != ctrl.Result{}) { - instance.Status.Conditions.Set(condition.FalseCondition( - condition.DBReadyCondition, - condition.RequestedReason, - condition.SeverityInfo, - condition.DBReadyRunningMessage)) - return ctrlResult, nil - } - - // wait for the DB to be setup - ctrlResult, err = db.WaitForDBCreated(ctx, helper) - if err != nil { - instance.Status.Conditions.Set(condition.FalseCondition( - condition.DBReadyCondition, - condition.ErrorReason, - condition.SeverityWarning, - condition.DBReadyErrorMessage, - err.Error())) - return ctrlResult, err - } - if (ctrlResult != ctrl.Result{}) { - instance.Status.Conditions.Set(condition.FalseCondition( - condition.DBReadyCondition, - condition.RequestedReason, - condition.SeverityInfo, - condition.DBReadyRunningMessage)) - return ctrlResult, nil - } - // update Status.DatabaseHostname, used to bootstrap/config the service - instance.Status.DatabaseHostname = db.GetDatabaseHostname() - instance.Status.Conditions.MarkTrue(condition.DBReadyCondition, condition.DBReadyMessage) - // create service DB - end // @@ -880,7 +832,7 @@ func (r *KeystoneAPIReconciler) reconcileNormal( // - %-config configmap holding minimal keystone config required to get the service up, user can add additional files to be added to the service // - parameters which has passwords gets added from the OpenStack secret via the init container // - err = r.generateServiceConfigMaps(ctx, instance, helper, &configMapVars, memcached) + err = r.generateServiceConfigMaps(ctx, instance, helper, &configMapVars, memcached, db) if err != nil { instance.Status.Conditions.Set(condition.FalseCondition( condition.ServiceConfigReadyCondition, @@ -1158,6 +1110,7 @@ func (r *KeystoneAPIReconciler) generateServiceConfigMaps( h *helper.Helper, envVars *map[string]env.Setter, mc *memcachedv1.Memcached, + db *mariadbv1.Database, ) error { // // create Configmap/Secret required for keystone input @@ -1168,12 +1121,18 @@ func (r *KeystoneAPIReconciler) generateServiceConfigMaps( cmLabels := labels.GetLabels(instance, labels.GetGroupLabel(keystone.ServiceName), map[string]string{}) + var tlsCfg *tls.Service + if instance.Spec.TLS.Ca.CaBundleSecretName != "" { + tlsCfg = &tls.Service{} + } + // customData hold any customization for the service. // custom.conf is going to /etc//.conf.d // all other files get placed into /etc/ to allow overwrite of e.g. policy.json // TODO: make sure custom.conf can not be overwritten customData := map[string]string{ common.CustomServiceConfigFileName: instance.Spec.CustomServiceConfig, + "my.cnf": db.GetDatabaseClientConfig(tlsCfg), //(mschuppert) for now just get the default my.cnf } for key, data := range instance.Spec.DefaultConfigOverwrite { customData[key] = data @@ -1192,7 +1151,7 @@ func (r *KeystoneAPIReconciler) generateServiceConfigMaps( templateParameters := map[string]interface{}{ "memcachedServers": strings.Join(mc.Status.ServerList, ","), "TransportURL": string(transportURLSecret.Data["transport_url"]), - "DatabaseConnection": fmt.Sprintf("mysql+pymysql://%s:%s@%s/%s", + "DatabaseConnection": fmt.Sprintf("mysql+pymysql://%s:%s@%s/%s?read_default_file=/etc/my.cnf", instance.Spec.DatabaseUser, string(ospSecret.Data[instance.Spec.PasswordSelectors.Database]), instance.Status.DatabaseHostname, @@ -1415,3 +1374,70 @@ func (r *KeystoneAPIReconciler) getKeystoneMemcached( } return memcached, err } + +func (r *KeystoneAPIReconciler) ensureDB( + ctx context.Context, + h *helper.Helper, + instance *keystonev1.KeystoneAPI, +) (*mariadbv1.Database, ctrl.Result, error) { + // + // create service DB instance + // + db := mariadbv1.NewDatabase( + instance.Name, + instance.Spec.DatabaseUser, + instance.Spec.Secret, + map[string]string{ + "dbName": instance.Spec.DatabaseInstance, + }, + ) + + // create or patch the DB + ctrlResult, err := db.CreateOrPatchDBByName( + ctx, + h, + instance.Spec.DatabaseInstance, + ) + if err != nil { + instance.Status.Conditions.Set(condition.FalseCondition( + condition.DBReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + condition.DBReadyErrorMessage, + err.Error())) + return db, ctrl.Result{}, err + } + if (ctrlResult != ctrl.Result{}) { + instance.Status.Conditions.Set(condition.FalseCondition( + condition.DBReadyCondition, + condition.RequestedReason, + condition.SeverityInfo, + condition.DBReadyRunningMessage)) + return db, ctrlResult, nil + } + // wait for the DB to be setup + // (ksambor) should we use WaitForDBCreatedWithTimeout instead? + ctrlResult, err = db.WaitForDBCreated(ctx, h) + if err != nil { + instance.Status.Conditions.Set(condition.FalseCondition( + condition.DBReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + condition.DBReadyErrorMessage, + err.Error())) + return db, ctrlResult, err + } + if (ctrlResult != ctrl.Result{}) { + instance.Status.Conditions.Set(condition.FalseCondition( + condition.DBReadyCondition, + condition.RequestedReason, + condition.SeverityInfo, + condition.DBReadyRunningMessage)) + return db, ctrlResult, nil + } + + // update Status.DatabaseHostname, used to config the service + instance.Status.DatabaseHostname = db.GetDatabaseHostname() + instance.Status.Conditions.MarkTrue(condition.DBReadyCondition, condition.DBReadyMessage) + return db, ctrlResult, nil +} diff --git a/templates/keystoneapi/config/keystone-api-config.json b/templates/keystoneapi/config/keystone-api-config.json index d1a69baf..8b255455 100644 --- a/templates/keystoneapi/config/keystone-api-config.json +++ b/templates/keystoneapi/config/keystone-api-config.json @@ -52,6 +52,12 @@ "dest": "/etc/keystone/", "owner": "keystone:keystone", "perm": "0700" + }, + { + "source": "/var/lib/config-data/default/my.cnf", + "dest": "/etc/my.cnf", + "owner": "keystone", + "perm": "0644" } ] } diff --git a/tests/functional/keystoneapi_controller_test.go b/tests/functional/keystoneapi_controller_test.go index 2eb12688..e464bd26 100644 --- a/tests/functional/keystoneapi_controller_test.go +++ b/tests/functional/keystoneapi_controller_test.go @@ -374,13 +374,16 @@ var _ = Describe("Keystone controller", func() { ) }) - It("should create a Secret for keystone.conf", func() { + It("should create a Secret for keystone.conf and my.cnf", func() { scrt := th.GetSecret(keystoneApiConfigDataName) configData := string(scrt.Data["keystone.conf"]) Expect(configData).To( ContainSubstring("memcache_servers=memcached-0.memcached:11211,memcached-1.memcached:11211,memcached-2.memcached:11211")) Expect(configData).To( - ContainSubstring(fmt.Sprintf("connection=mysql+pymysql://keystone:12345678@hostname-for-openstack.%s.svc/keystone", namespace))) + ContainSubstring(fmt.Sprintf("connection=mysql+pymysql://keystone:12345678@hostname-for-openstack.%s.svc/keystone?read_default_file=/etc/my.cnf", namespace))) + configData = string(scrt.Data["my.cnf"]) + Expect(configData).To( + ContainSubstring("[client]\nssl=0")) }) It("should create a Secret for fernet keys", func() { th.GetSecret(types.NamespacedName{ @@ -904,13 +907,16 @@ var _ = Describe("Keystone controller", func() { th.AssertVolumeMountExists(caBundleSecretName.Name, "tls-ca-bundle.pem", j.Spec.Template.Spec.Containers[0].VolumeMounts) }) - It("should create a Secret for keystone.conf", func() { + It("should create a Secret for keystone.conf and my.cnf", func() { scrt := th.GetSecret(keystoneApiConfigDataName) configData := string(scrt.Data["keystone.conf"]) Expect(configData).To( ContainSubstring("memcache_servers=memcached-0.memcached:11211,memcached-1.memcached:11211,memcached-2.memcached:11211")) Expect(configData).To( - ContainSubstring(fmt.Sprintf("connection=mysql+pymysql://keystone:12345678@hostname-for-openstack.%s.svc/keystone", namespace))) + ContainSubstring(fmt.Sprintf("connection=mysql+pymysql://keystone:12345678@hostname-for-openstack.%s.svc/keystone?read_default_file=/etc/my.cnf", namespace))) + configData = string(scrt.Data["my.cnf"]) + Expect(configData).To( + ContainSubstring("[client]\nssl-ca=/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem\nssl=1")) }) It("it creates deployment with CA and service certs mounted", func() {