From 714c482539d22c43e0b4918165c3c165e8ebdaad Mon Sep 17 00:00:00 2001 From: Martin Schuppert Date: Tue, 21 Jan 2025 12:39:54 +0100 Subject: [PATCH] Allow customize http vhost config using HttpdCustomization.CustomConfigSecret This change allows to customize the httpd vhost config using this parameter to specify a secret that contains service config data. The content of each provided snippet gets rendered as a go template and placed into /etc/httpd/conf/httpd_custom__ . At the end of the vhost config in the default httpd template these custom configs get included using `Include conf/httpd_custom__*`. For information on how sections in httpd configuration get merged, check section "How the sections are merged" in https://httpd.apache.org/docs/current/sections.html#merging All possible parameters which can be use in a template can be looked up in the -config-data secret of the service like: $ oc get secret -n openstack glance-config-data -o json | jq -r .data.TemplateParameters | base64 -d or in the running pod of the service in the file: $ cat /var/lib/config-data/default/TemplateParameters The content is a versioned dump of the parameters of the service operator, like: ~~~ DatabaseConnection: mysql+pymysql://user:pwd@openstack.openstack.svc/keystone?read_default_file=/etc/my.cnf KeystoneEndpointInternal: https://keystone-internal.openstack.svc:5000 KeystoneEndpointPublic: https://keystone-public-openstack.apps-crc.testing TransportURL: rabbit://user:pwd@rabbitmq.openstack.svc:5671/?ssl=1 VHosts: internal: Override: false SSLCertificateFile: /etc/pki/tls/certs/internal.crt SSLCertificateKeyFile: /etc/pki/tls/private/internal.key ServerName: glance-internal.openstack.svc TLS: true public: Override: false SSLCertificateFile: /etc/pki/tls/certs/public.crt SSLCertificateKeyFile: /etc/pki/tls/private/public.key ServerName: glance-public.openstack.svc TLS: true ... ~~~ Depends-On: https://github.com/openstack-k8s-operators/lib-common/pull/591 Depends-On: https://github.com/openstack-k8s-operators/lib-common/pull/593 Jira: https://issues.redhat.com/browse/OSPRH-13100 Signed-off-by: Martin Schuppert --- .../glance.openstack.org_glanceapis.yaml | 5 + api/bases/glance.openstack.org_glances.yaml | 10 ++ api/go.mod | 2 +- api/go.sum | 4 +- api/v1beta1/common_types.go | 17 ++ api/v1beta1/glance_types.go | 4 + api/v1beta1/zz_generated.deepcopy.go | 22 +++ .../glance.openstack.org_glanceapis.yaml | 5 + .../bases/glance.openstack.org_glances.yaml | 10 ++ controllers/glance_common.go | 34 ++-- controllers/glance_controller.go | 6 +- controllers/glanceapi_controller.go | 33 +++- go.mod | 4 +- go.sum | 4 +- templates/common/config/10-glance-httpd.conf | 5 + .../common/config/glance-api-config.json | 7 + .../glanceapi/config/glance-httpd-config.json | 7 + test/functional/glance_controller_test.go | 51 ++++++ test/functional/glance_test_data.go | 7 +- test/functional/glanceapi_controller_test.go | 145 ++++++++++++++++++ 20 files changed, 361 insertions(+), 21 deletions(-) diff --git a/api/bases/glance.openstack.org_glanceapis.yaml b/api/bases/glance.openstack.org_glanceapis.yaml index 471a5ece..d16b07fc 100644 --- a/api/bases/glance.openstack.org_glanceapis.yaml +++ b/api/bases/glance.openstack.org_glanceapis.yaml @@ -546,6 +546,11 @@ spec: - extraVol type: object type: array + httpdCustomization: + properties: + customConfigSecret: + type: string + type: object imageCache: properties: cleanerScheduler: diff --git a/api/bases/glance.openstack.org_glances.yaml b/api/bases/glance.openstack.org_glances.yaml index b10020a5..52f4946c 100644 --- a/api/bases/glance.openstack.org_glances.yaml +++ b/api/bases/glance.openstack.org_glances.yaml @@ -559,6 +559,11 @@ spec: items: type: string type: array + httpdCustomization: + properties: + customConfigSecret: + type: string + type: object imageCache: properties: cleanerScheduler: @@ -708,6 +713,11 @@ spec: type: object default: {} type: object + httpdCustomization: + properties: + customConfigSecret: + type: string + type: object imageCache: properties: cleanerScheduler: diff --git a/api/go.mod b/api/go.mod index fc06bafb..86098c99 100644 --- a/api/go.mod +++ b/api/go.mod @@ -4,7 +4,7 @@ go 1.21 require ( github.com/google/go-cmp v0.6.0 - github.com/openstack-k8s-operators/lib-common/modules/common v0.5.1-0.20241216113837-d172b3ac0f4e + github.com/openstack-k8s-operators/lib-common/modules/common v0.5.1-0.20250116145727-01a8948d5dd7 github.com/openstack-k8s-operators/lib-common/modules/storage v0.5.1-0.20241216113837-d172b3ac0f4e k8s.io/api v0.29.13 k8s.io/apimachinery v0.29.13 diff --git a/api/go.sum b/api/go.sum index d12cbefa..45c37c21 100644 --- a/api/go.sum +++ b/api/go.sum @@ -75,8 +75,8 @@ github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= github.com/openshift/api v0.0.0-20240830023148-b7d0481c9094 h1:J1wuGhVxpsHykZBa6Beb1gQ96Ptej9AE/BvwCBiRj1E= github.com/openshift/api v0.0.0-20240830023148-b7d0481c9094/go.mod h1:CxgbWAlvu2iQB0UmKTtRu1YfepRg1/vJ64n2DlIEVz4= -github.com/openstack-k8s-operators/lib-common/modules/common v0.5.1-0.20241216113837-d172b3ac0f4e h1:hf4kVQBkyG79WcHBxdQ25QrDBbGFdarebS1Tc0Xclq4= -github.com/openstack-k8s-operators/lib-common/modules/common v0.5.1-0.20241216113837-d172b3ac0f4e/go.mod h1:YpNTuJhDWhbXM50O3qBkhO7M+OOyRmWkNVmJ4y3cyFs= +github.com/openstack-k8s-operators/lib-common/modules/common v0.5.1-0.20250116145727-01a8948d5dd7 h1:vXHpH93PjbAgg5ZN6n5WmxkybVQOs0nhXvVw62o7aZs= +github.com/openstack-k8s-operators/lib-common/modules/common v0.5.1-0.20250116145727-01a8948d5dd7/go.mod h1:YpNTuJhDWhbXM50O3qBkhO7M+OOyRmWkNVmJ4y3cyFs= github.com/openstack-k8s-operators/lib-common/modules/storage v0.5.1-0.20241216113837-d172b3ac0f4e h1:Qz0JFEoRDUyjEWorNY3LggwxTsmpMtQkcpmZDQulGHQ= github.com/openstack-k8s-operators/lib-common/modules/storage v0.5.1-0.20241216113837-d172b3ac0f4e/go.mod h1:tfgBeLRqmlH/NQkLPe7396rj+t0whv2wPuMb8Ttvh8w= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= diff --git a/api/v1beta1/common_types.go b/api/v1beta1/common_types.go index d9b98663..b60c4996 100644 --- a/api/v1beta1/common_types.go +++ b/api/v1beta1/common_types.go @@ -106,6 +106,10 @@ type GlanceAPITemplate struct { // +kubebuilder:validation:Minimum=1 // APITimeout for HAProxy and Apache defaults to GlanceSpecCore APITimeout APITimeout int `json:"apiTimeout,omitempty"` + + // +kubebuilder:validation:Optional + // HttpdCustomization - customize the httpd service + HttpdCustomization HttpdCustomization `json:"httpdCustomization,omitempty"` } // Storage - @@ -144,6 +148,19 @@ type APIOverrideSpec struct { Service map[service.Endpoint]service.RoutedOverrideSpec `json:"service,omitempty"` } +// HttpdCustomization - customize the httpd service +type HttpdCustomization struct { + // +kubebuilder:validation:Optional + // CustomConfigSecret - customize the httpd vhost config using this parameter to specify + // a secret that contains service config data. The content of each provided snippet gets + // rendered as a go template and placed into /etc/httpd/conf/httpd_custom_ . + // In the default httpd template at the end of the vhost those custom configs get + // included using `Include conf/httpd_custom__*`. + // For information on how sections in httpd configuration get merged, check section + // "How the sections are merged" in https://httpd.apache.org/docs/current/sections.html#merging + CustomConfigSecret *string `json:"customConfigSecret,omitempty"` +} + // SetupDefaults - initializes any CRD field defaults based on environment variables (the defaulting mechanism itself is implemented via webhooks) func SetupDefaults() { // Acquire environmental defaults and initialize Glance defaults with them diff --git a/api/v1beta1/glance_types.go b/api/v1beta1/glance_types.go index c6adaee3..c3234c5c 100644 --- a/api/v1beta1/glance_types.go +++ b/api/v1beta1/glance_types.go @@ -125,6 +125,10 @@ type GlanceSpecCore struct { // +kubebuilder:validation:Minimum=1 // Default APITimeout for HAProxy and Apache, defaults to 60 seconds APITimeout int `json:"apiTimeout"` + + // +kubebuilder:validation:Optional + // HttpdCustomization - customize the httpd service of all GlanceAPIs + HttpdCustomization HttpdCustomization `json:"httpdCustomization,omitempty"` } // GlanceSpec defines the desired state of Glance diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index 84c3c0c8..a100fbce 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -275,6 +275,7 @@ func (in *GlanceAPITemplate) DeepCopyInto(out *GlanceAPITemplate) { out.Storage = in.Storage in.TLS.DeepCopyInto(&out.TLS) out.ImageCache = in.ImageCache + in.HttpdCustomization.DeepCopyInto(&out.HttpdCustomization) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GlanceAPITemplate. @@ -410,6 +411,7 @@ func (in *GlanceSpecCore) DeepCopyInto(out *GlanceSpecCore) { out.Quotas = in.Quotas out.ImageCache = in.ImageCache out.DBPurge = in.DBPurge + in.HttpdCustomization.DeepCopyInto(&out.HttpdCustomization) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GlanceSpecCore. @@ -465,6 +467,26 @@ func (in *GlanceStatus) DeepCopy() *GlanceStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HttpdCustomization) DeepCopyInto(out *HttpdCustomization) { + *out = *in + if in.CustomConfigSecret != nil { + in, out := &in.CustomConfigSecret, &out.CustomConfigSecret + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HttpdCustomization. +func (in *HttpdCustomization) DeepCopy() *HttpdCustomization { + if in == nil { + return nil + } + out := new(HttpdCustomization) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ImageCache) DeepCopyInto(out *ImageCache) { *out = *in diff --git a/config/crd/bases/glance.openstack.org_glanceapis.yaml b/config/crd/bases/glance.openstack.org_glanceapis.yaml index 471a5ece..d16b07fc 100644 --- a/config/crd/bases/glance.openstack.org_glanceapis.yaml +++ b/config/crd/bases/glance.openstack.org_glanceapis.yaml @@ -546,6 +546,11 @@ spec: - extraVol type: object type: array + httpdCustomization: + properties: + customConfigSecret: + type: string + type: object imageCache: properties: cleanerScheduler: diff --git a/config/crd/bases/glance.openstack.org_glances.yaml b/config/crd/bases/glance.openstack.org_glances.yaml index b10020a5..52f4946c 100644 --- a/config/crd/bases/glance.openstack.org_glances.yaml +++ b/config/crd/bases/glance.openstack.org_glances.yaml @@ -559,6 +559,11 @@ spec: items: type: string type: array + httpdCustomization: + properties: + customConfigSecret: + type: string + type: object imageCache: properties: cleanerScheduler: @@ -708,6 +713,11 @@ spec: type: object default: {} type: object + httpdCustomization: + properties: + customConfigSecret: + type: string + type: object imageCache: properties: cleanerScheduler: diff --git a/controllers/glance_common.go b/controllers/glance_common.go index 5af11ac4..249dccf3 100644 --- a/controllers/glance_common.go +++ b/controllers/glance_common.go @@ -23,6 +23,7 @@ import ( networkv1 "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1" "github.com/openstack-k8s-operators/lib-common/modules/common/condition" + "gopkg.in/yaml.v2" "k8s.io/apimachinery/pkg/types" glancev1 "github.com/openstack-k8s-operators/glance-operator/api/v1beta1" @@ -45,10 +46,11 @@ import ( // fields to index to reconcile when change const ( - passwordSecretField = ".spec.secret" - caBundleSecretNameField = ".spec.tls.caBundleSecretName" - tlsAPIInternalField = ".spec.tls.api.internal.secretName" - tlsAPIPublicField = ".spec.tls.api.public.secretName" + passwordSecretField = ".spec.secret" + caBundleSecretNameField = ".spec.tls.caBundleSecretName" + tlsAPIInternalField = ".spec.tls.api.internal.secretName" + tlsAPIPublicField = ".spec.tls.api.public.secretName" + httpdCustomServiceConfigSecretField = ".spec.httpdCustomization.customServiceConfigSecret" ) var ( @@ -60,6 +62,7 @@ var ( caBundleSecretNameField, tlsAPIInternalField, tlsAPIPublicField, + httpdCustomServiceConfigSecretField, } ) @@ -162,18 +165,27 @@ func GenerateConfigsGeneric( customData map[string]string, cmLabels map[string]string, scripts bool, + customTemplates map[string]string, ) error { + // Marshal the templateParameters map to YAML + yamlData, err := yaml.Marshal(templateParameters) + if err != nil { + return fmt.Errorf("Error marshalling to YAML: %w", err) + } + customData[common.TemplateParameters] = string(yamlData) + cms := []util.Template{ // Templates where the GlanceAPI config is stored { - Name: fmt.Sprintf("%s-config-data", instance.GetName()), - Namespace: instance.GetNamespace(), - Type: util.TemplateTypeConfig, - InstanceType: instance.GetObjectKind().GroupVersionKind().Kind, - ConfigOptions: templateParameters, - CustomData: customData, - Labels: cmLabels, + Name: fmt.Sprintf("%s-config-data", instance.GetName()), + Namespace: instance.GetNamespace(), + Type: util.TemplateTypeConfig, + InstanceType: instance.GetObjectKind().GroupVersionKind().Kind, + ConfigOptions: templateParameters, + CustomData: customData, + StringTemplate: customTemplates, + Labels: cmLabels, }, } // TODO: Scripts have no reason to be secrets, should move to configmap diff --git a/controllers/glance_controller.go b/controllers/glance_controller.go index 4bbbb46c..cb2ab648 100644 --- a/controllers/glance_controller.go +++ b/controllers/glance_controller.go @@ -838,6 +838,10 @@ func (r *GlanceReconciler) apiDeploymentCreateOrUpdate( apiSpec.GlanceAPITemplate.NodeSelector = instance.Spec.NodeSelector } + if apiSpec.GlanceAPITemplate.HttpdCustomization.CustomConfigSecret == nil { + apiSpec.GlanceAPITemplate.HttpdCustomization.CustomConfigSecret = instance.Spec.HttpdCustomization.CustomConfigSecret + } + // Inherit the ImageCacheSize from the top level if not specified if apiSpec.ImageCache.Size == "" { apiSpec.ImageCache.Size = instance.Spec.ImageCache.Size @@ -967,7 +971,7 @@ func (r *GlanceReconciler) generateServiceConfig( } // Generate both default 00-config.conf and -scripts - return GenerateConfigsGeneric(ctx, h, instance, envVars, templateParameters, customData, labels, true) + return GenerateConfigsGeneric(ctx, h, instance, envVars, templateParameters, customData, labels, true, map[string]string{}) } // ensureRegisteredLimits - create registered limits in keystone that will be diff --git a/controllers/glanceapi_controller.go b/controllers/glanceapi_controller.go index 16b8736b..bcddb681 100644 --- a/controllers/glanceapi_controller.go +++ b/controllers/glanceapi_controller.go @@ -241,6 +241,18 @@ func (r *GlanceAPIReconciler) SetupWithManager(mgr ctrl.Manager) error { return err } + // index httpdOverrideSecretField + if err := mgr.GetFieldIndexer().IndexField(context.Background(), &glancev1.GlanceAPI{}, httpdCustomServiceConfigSecretField, func(rawObj client.Object) []string { + // Extract the secret name from the spec, if one is provided + cr := rawObj.(*glancev1.GlanceAPI) + if cr.Spec.HttpdCustomization.CustomConfigSecret == nil { + return nil + } + return []string{*cr.Spec.HttpdCustomization.CustomConfigSecret} + }); err != nil { + return err + } + // Watch for changes to any CustomServiceConfigSecrets. Global secrets svcSecretFn := func(_ context.Context, o client.Object) []reconcile.Request { var namespace string = o.GetNamespace() @@ -1011,6 +1023,14 @@ func (r *GlanceAPIReconciler) generateServiceConfig( } customData[glance.CustomServiceConfigSecretsFileName] = customSecrets + httpdOverrideSecret := &corev1.Secret{} + if instance.Spec.HttpdCustomization.CustomConfigSecret != nil && *instance.Spec.HttpdCustomization.CustomConfigSecret != "" { + httpdOverrideSecret, _, err = secret.GetSecret(ctx, h, *instance.Spec.HttpdCustomization.CustomConfigSecret, instance.Namespace) + if err != nil { + return err + } + } + keystoneAPI, err := keystonev1.GetKeystoneAPI(ctx, h, instance.Namespace, map[string]string{}) // KeystoneAPI not available we should not aggregate the error and continue if err != nil { @@ -1043,6 +1063,7 @@ func (r *GlanceAPIReconciler) generateServiceConfig( if instance.Spec.APIType != glancev1.APISingle { endptName = fmt.Sprintf("%s-api", instance.Name) } + customTemplates := map[string]string{} httpdVhostConfig := map[string]interface{}{} for endpt := range glanceEndpoints { endptConfig := map[string]interface{}{} @@ -1055,6 +1076,16 @@ func (r *GlanceAPIReconciler) generateServiceConfig( endptConfig["SSLCertificateFile"] = fmt.Sprintf("/etc/pki/tls/certs/%s.crt", endpt.String()) endptConfig["SSLCertificateKeyFile"] = fmt.Sprintf("/etc/pki/tls/private/%s.key", endpt.String()) } + + endptConfig["Override"] = false + if len(httpdOverrideSecret.Data) > 0 { + endptConfig["Override"] = true + for key, data := range httpdOverrideSecret.Data { + if len(data) > 0 { + customTemplates["httpd_custom_"+endpt.String()+"_"+key] = string(data) + } + } + } httpdVhostConfig[endpt.String()] = endptConfig } @@ -1113,7 +1144,7 @@ func (r *GlanceAPIReconciler) generateServiceConfig( // 00-default.conf will be regenerated as we have a ln -s of the // templates/glance/config directory // Do not generate -scripts as they are inherited from the top-level CR - return GenerateConfigsGeneric(ctx, h, instance, envVars, templateParameters, customData, labels, false) + return GenerateConfigsGeneric(ctx, h, instance, envVars, templateParameters, customData, labels, false, customTemplates) } // createHashOfInputHashes - creates a hash of hashes which gets added to the resources which requires a restart diff --git a/go.mod b/go.mod index 988925bc..1ad9a71e 100644 --- a/go.mod +++ b/go.mod @@ -13,12 +13,13 @@ require ( github.com/openstack-k8s-operators/glance-operator/api v0.0.0-00010101000000-000000000000 github.com/openstack-k8s-operators/infra-operator/apis v0.5.1-0.20250108092548-58707fa645ce github.com/openstack-k8s-operators/keystone-operator/api v0.5.1-0.20250107165241-16c3ed8e549f - github.com/openstack-k8s-operators/lib-common/modules/common v0.5.1-0.20241216113837-d172b3ac0f4e + github.com/openstack-k8s-operators/lib-common/modules/common v0.5.1-0.20250116145727-01a8948d5dd7 github.com/openstack-k8s-operators/lib-common/modules/openstack v0.5.1-0.20241216113837-d172b3ac0f4e github.com/openstack-k8s-operators/lib-common/modules/storage v0.5.1-0.20241216113837-d172b3ac0f4e github.com/openstack-k8s-operators/lib-common/modules/test v0.5.1-0.20241216113837-d172b3ac0f4e github.com/openstack-k8s-operators/mariadb-operator/api v0.5.1-0.20250108071621-aa59f25c5b1a golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 + gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 k8s.io/api v0.29.13 k8s.io/apimachinery v0.29.13 @@ -74,7 +75,6 @@ require ( google.golang.org/appengine v1.6.8 // indirect google.golang.org/protobuf v1.34.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect k8s.io/apiextensions-apiserver v0.29.13 // indirect k8s.io/component-base v0.29.13 // indirect k8s.io/klog/v2 v2.120.1 // indirect diff --git a/go.sum b/go.sum index ade4c4b2..fb425bd1 100644 --- a/go.sum +++ b/go.sum @@ -84,8 +84,8 @@ github.com/openstack-k8s-operators/infra-operator/apis v0.5.1-0.20250108092548-5 github.com/openstack-k8s-operators/infra-operator/apis v0.5.1-0.20250108092548-58707fa645ce/go.mod h1:TDaE7BVQvJwJGFm33R6xcPTeF8LKAnMh+a1ho+YqJHs= github.com/openstack-k8s-operators/keystone-operator/api v0.5.1-0.20250107165241-16c3ed8e549f h1:jlUo93FAwlDll1bJRxJO5B1Vi3t3wCoHQuy5HEO96ME= github.com/openstack-k8s-operators/keystone-operator/api v0.5.1-0.20250107165241-16c3ed8e549f/go.mod h1:CyuEOM1TpXKNUR1n8cudNtRzTEwkzv90JFkpDPPId8E= -github.com/openstack-k8s-operators/lib-common/modules/common v0.5.1-0.20241216113837-d172b3ac0f4e h1:hf4kVQBkyG79WcHBxdQ25QrDBbGFdarebS1Tc0Xclq4= -github.com/openstack-k8s-operators/lib-common/modules/common v0.5.1-0.20241216113837-d172b3ac0f4e/go.mod h1:YpNTuJhDWhbXM50O3qBkhO7M+OOyRmWkNVmJ4y3cyFs= +github.com/openstack-k8s-operators/lib-common/modules/common v0.5.1-0.20250116145727-01a8948d5dd7 h1:vXHpH93PjbAgg5ZN6n5WmxkybVQOs0nhXvVw62o7aZs= +github.com/openstack-k8s-operators/lib-common/modules/common v0.5.1-0.20250116145727-01a8948d5dd7/go.mod h1:YpNTuJhDWhbXM50O3qBkhO7M+OOyRmWkNVmJ4y3cyFs= github.com/openstack-k8s-operators/lib-common/modules/openstack v0.5.1-0.20241216113837-d172b3ac0f4e h1:HFo4OqPY0x4ZQeaWI2YGonTXAGTQFt+rOEJlfZVhS7s= github.com/openstack-k8s-operators/lib-common/modules/openstack v0.5.1-0.20241216113837-d172b3ac0f4e/go.mod h1:IASoGvp5QM/tBJUd/8i8uIjj4DBnI+64Ydh4r7pmnvA= github.com/openstack-k8s-operators/lib-common/modules/storage v0.5.1-0.20241216113837-d172b3ac0f4e h1:Qz0JFEoRDUyjEWorNY3LggwxTsmpMtQkcpmZDQulGHQ= diff --git a/templates/common/config/10-glance-httpd.conf b/templates/common/config/10-glance-httpd.conf index 21f4ad12..bbf7900f 100644 --- a/templates/common/config/10-glance-httpd.conf +++ b/templates/common/config/10-glance-httpd.conf @@ -33,5 +33,10 @@ SSLCertificateFile "{{ $vhost.SSLCertificateFile }}" SSLCertificateKeyFile "{{ $vhost.SSLCertificateKeyFile }}" {{- end }} + +{{- if $vhost.Override }} + Include conf/httpd_custom_{{ $endpt }}_* +{{- end }} + {{ end }} diff --git a/templates/common/config/glance-api-config.json b/templates/common/config/glance-api-config.json index 996d8b24..13b055a1 100644 --- a/templates/common/config/glance-api-config.json +++ b/templates/common/config/glance-api-config.json @@ -61,6 +61,13 @@ "dest": "/usr/local/bin/kolla_extend_start", "owner": "root:root", "perm": "0755" + }, + { + "source": "/var/lib/config-data/default/httpd_custom_*", + "dest": "/etc/httpd/conf/", + "owner": "apache", + "perm": "0444", + "optional": true } ], "permissions": [ diff --git a/templates/glanceapi/config/glance-httpd-config.json b/templates/glanceapi/config/glance-httpd-config.json index 109b5b52..640ce70e 100644 --- a/templates/glanceapi/config/glance-httpd-config.json +++ b/templates/glanceapi/config/glance-httpd-config.json @@ -37,6 +37,13 @@ "owner": "glance:apache", "optional": true, "perm": "0644" + }, + { + "source": "/var/lib/config-data/default/httpd_custom_*", + "dest": "/etc/httpd/conf/", + "owner": "glance:apache", + "perm": "0444", + "optional": true } ], "permissions": [ diff --git a/test/functional/glance_controller_test.go b/test/functional/glance_controller_test.go index 8e38ef1a..1b2e869a 100644 --- a/test/functional/glance_controller_test.go +++ b/test/functional/glance_controller_test.go @@ -655,6 +655,57 @@ var _ = Describe("Glance controller", func() { }) }) + When("Glance is created with HttpdCustomization.CustomConfigSecret", func() { + var customServiceConfigSecretName types.NamespacedName + + BeforeEach(func() { + customServiceConfigSecretName = types.NamespacedName{Name: "foo", Namespace: glanceTest.Instance.Namespace} + customConfig := []byte(`CustomParam "foo" +CustomKeystonePublicURL "{{ .KeystonePublicURL }}"`) + th.CreateSecret( + customServiceConfigSecretName, + map[string][]byte{ + "bar.conf": customConfig, + }, + ) + + DeferCleanup(infra.DeleteMemcached, infra.CreateMemcached(namespace, glanceTest.MemcachedInstance, memcachedSpec)) + infra.SimulateMemcachedReady(glanceTest.GlanceMemcached) + + spec := GetGlanceDefaultSpec() + spec["httpdCustomization"] = map[string]interface{}{ + "customConfigSecret": customServiceConfigSecretName.Name, + } + DeferCleanup(th.DeleteInstance, CreateGlance(glanceTest.Instance, spec)) + // Get Default GlanceAPI + DeferCleanup( + mariadb.DeleteDBService, + mariadb.CreateDBService( + glanceTest.Instance.Namespace, + GetGlance(glanceName).Spec.DatabaseInstance, + corev1.ServiceSpec{ + Ports: []corev1.ServicePort{{Port: 3306}}, + }, + ), + ) + DeferCleanup(keystone.DeleteKeystoneAPI, keystone.CreateKeystoneAPI(glanceTest.Instance.Namespace)) + mariadb.SimulateMariaDBDatabaseCompleted(glanceTest.GlanceDatabaseName) + mariadb.SimulateMariaDBAccountCompleted(glanceTest.GlanceDatabaseAccount) + th.SimulateJobSuccess(glanceTest.GlanceDBSync) + keystone.SimulateKeystoneServiceReady(glanceTest.KeystoneService) + keystone.SimulateKeystoneEndpointReady(glanceTest.GlanceSingle) + }) + + It("it sets the HttpdCustomization.CustomConfigSecret on the GlanceAPIs", func() { + Eventually(func(g Gomega) { + glanceAPI := GetGlanceAPI(glanceTest.GlanceSingle) + g.Expect(glanceAPI.Spec.HttpdCustomization.CustomConfigSecret).ToNot(BeNil()) + g.Expect(*glanceAPI.Spec.HttpdCustomization.CustomConfigSecret).To(Equal(customServiceConfigSecretName.Name)) + }, timeout, interval).Should(Succeed()) + + }) + }) + // Run MariaDBAccount suite tests. these are pre-packaged ginkgo tests // that exercise standard account create / update patterns that should be // common to all controllers that ensure MariaDBAccount CRs. diff --git a/test/functional/glance_test_data.go b/test/functional/glance_test_data.go index 9c649b0f..054ced13 100644 --- a/test/functional/glance_test_data.go +++ b/test/functional/glance_test_data.go @@ -79,6 +79,7 @@ type GlanceTestData struct { GlanceService types.NamespacedName GlanceConfigMapData types.NamespacedName GlanceInternalConfigMapData types.NamespacedName + GlanceExternalConfigMapData types.NamespacedName GlanceSingleConfigMapData types.NamespacedName GlanceConfigMapScripts types.NamespacedName InternalAPINAD types.NamespacedName @@ -163,7 +164,11 @@ func GetGlanceTestData(glanceName types.NamespacedName) GlanceTestData { }, GlanceInternalConfigMapData: types.NamespacedName{ Namespace: glanceName.Namespace, - Name: fmt.Sprintf("%s-%s", glanceName.Name, "internal-config-data"), + Name: fmt.Sprintf("%s-%s", glanceName.Name, "default-internal-config-data"), + }, + GlanceExternalConfigMapData: types.NamespacedName{ + Namespace: glanceName.Namespace, + Name: fmt.Sprintf("%s-%s", glanceName.Name, "default-external-config-data"), }, GlanceSingleConfigMapData: types.NamespacedName{ Namespace: glanceName.Namespace, diff --git a/test/functional/glanceapi_controller_test.go b/test/functional/glanceapi_controller_test.go index d504f7a3..5bf472a2 100644 --- a/test/functional/glanceapi_controller_test.go +++ b/test/functional/glanceapi_controller_test.go @@ -22,6 +22,7 @@ import ( . "github.com/onsi/ginkgo/v2" //revive:disable:dot-imports . "github.com/onsi/gomega" //revive:disable:dot-imports memcachedv1 "github.com/openstack-k8s-operators/infra-operator/apis/memcached/v1beta1" + "github.com/openstack-k8s-operators/lib-common/modules/common" "github.com/openstack-k8s-operators/lib-common/modules/common/condition" //revive:disable-next-line:dot-imports @@ -29,6 +30,7 @@ import ( mariadbv1 "github.com/openstack-k8s-operators/mariadb-operator/api/v1beta1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" ptr "k8s.io/utils/ptr" ) @@ -989,4 +991,147 @@ var _ = Describe("Glanceapi controller", func() { ) }) }) + + When("A single GlanceAPI is created with HttpdCustomization.CustomConfigSecret", func() { + BeforeEach(func() { + customServiceConfigSecretName := types.NamespacedName{Name: "foo", Namespace: namespace} + customConfig := []byte(`CustomParam "foo" +CustomKeystonePublicURL "{{ .KeystonePublicURL }}"`) + th.CreateSecret( + customServiceConfigSecretName, + map[string][]byte{ + "bar.conf": customConfig, + }, + ) + + DeferCleanup(infra.DeleteMemcached, infra.CreateMemcached(namespace, glanceTest.MemcachedInstance, memcachedSpec)) + infra.SimulateMemcachedReady(glanceTest.GlanceMemcached) + DeferCleanup(th.DeleteInstance, CreateDefaultGlance(glanceTest.Instance)) + DeferCleanup( + mariadb.DeleteDBService, + mariadb.CreateDBService( + glanceName.Namespace, + GetGlance(glanceTest.Instance).Spec.DatabaseInstance, + corev1.ServiceSpec{ + Ports: []corev1.ServicePort{{Port: 3306}}, + }, + ), + ) + + mariadb.CreateMariaDBDatabase(glanceTest.GlanceDatabaseName.Namespace, glanceTest.GlanceDatabaseName.Name, mariadbv1.MariaDBDatabaseSpec{}) + DeferCleanup(k8sClient.Delete, ctx, mariadb.GetMariaDBDatabase(glanceTest.GlanceDatabaseName)) + + DeferCleanup(k8sClient.Delete, ctx, th.CreateCABundleSecret(glanceTest.CABundleSecret)) + DeferCleanup(k8sClient.Delete, ctx, th.CreateCertSecret(glanceTest.InternalCertSecret)) + DeferCleanup(k8sClient.Delete, ctx, th.CreateCertSecret(glanceTest.PublicCertSecret)) + spec := GetTLSGlanceAPISpec(GlanceAPITypeSingle) + spec["httpdCustomization"] = map[string]interface{}{ + "customConfigSecret": customServiceConfigSecretName.Name, + } + glance := CreateGlanceAPI(glanceTest.GlanceSingle, spec) + DeferCleanup(keystone.DeleteKeystoneAPI, keystone.CreateKeystoneAPI(glanceTest.GlanceSingle.Namespace)) + th.SimulateStatefulSetReplicaReady(glanceTest.GlanceSingle) + keystone.SimulateKeystoneEndpointReady(glanceTest.GlanceSingle) + DeferCleanup(th.DeleteInstance, glance) + }) + It("it renders the custom template and adds it to the default-single-config-data secret", func() { + scrt := th.GetSecret(glanceTest.GlanceSingleConfigMapData) + Expect(scrt).ShouldNot(BeNil()) + Expect(scrt.Data).Should(HaveKey(common.TemplateParameters)) + configData := string(scrt.Data[common.TemplateParameters]) + keystonePublicURL := "http://keystone-public-openstack.testing" + Expect(configData).Should(ContainSubstring(fmt.Sprintf("KeystonePublicURL: %s", keystonePublicURL))) + + for _, cfg := range []string{"httpd_custom_internal_bar.conf", "httpd_custom_public_bar.conf"} { + Expect(scrt.Data).Should(HaveKey(cfg)) + configData := string(scrt.Data[cfg]) + Expect(configData).Should(ContainSubstring("CustomParam \"foo\"")) + Expect(configData).Should(ContainSubstring(fmt.Sprintf("CustomKeystonePublicURL \"%s\"", keystonePublicURL))) + } + }) + }) + + When("A split GlanceAPI is created with HttpdCustomization.CustomConfigSecret", func() { + BeforeEach(func() { + customServiceConfigSecretName := types.NamespacedName{Name: "foo", Namespace: namespace} + customConfig := []byte(`CustomParam "foo" +CustomKeystonePublicURL "{{ .KeystonePublicURL }}"`) + th.CreateSecret( + customServiceConfigSecretName, + map[string][]byte{ + "bar.conf": customConfig, + }, + ) + + DeferCleanup(infra.DeleteMemcached, infra.CreateMemcached(namespace, glanceTest.MemcachedInstance, memcachedSpec)) + infra.SimulateMemcachedReady(glanceTest.GlanceMemcached) + DeferCleanup(th.DeleteInstance, CreateDefaultGlance(glanceTest.Instance)) + DeferCleanup( + mariadb.DeleteDBService, + mariadb.CreateDBService( + glanceName.Namespace, + GetGlance(glanceTest.Instance).Spec.DatabaseInstance, + corev1.ServiceSpec{ + Ports: []corev1.ServicePort{{Port: 3306}}, + }, + ), + ) + + mariadb.CreateMariaDBDatabase(glanceTest.GlanceDatabaseName.Namespace, glanceTest.GlanceDatabaseName.Name, mariadbv1.MariaDBDatabaseSpec{}) + DeferCleanup(k8sClient.Delete, ctx, mariadb.GetMariaDBDatabase(glanceTest.GlanceDatabaseName)) + mariadb.SimulateMariaDBTLSDatabaseCompleted(glanceTest.GlanceDatabaseName) + + DeferCleanup(k8sClient.Delete, ctx, th.CreateCABundleSecret(glanceTest.CABundleSecret)) + DeferCleanup(k8sClient.Delete, ctx, th.CreateCertSecret(glanceTest.InternalCertSecret)) + DeferCleanup(k8sClient.Delete, ctx, th.CreateCertSecret(glanceTest.PublicCertSecret)) + DeferCleanup(th.DeleteInstance, CreateDefaultGlance(glanceTest.Instance)) + + spec := GetTLSGlanceAPISpec(GlanceAPITypeInternal) + spec["httpdCustomization"] = map[string]interface{}{ + "customConfigSecret": customServiceConfigSecretName.Name, + } + DeferCleanup(th.DeleteInstance, CreateGlanceAPI(glanceTest.GlanceInternal, spec)) + DeferCleanup(th.DeleteInstance, CreateGlanceAPI(glanceTest.GlanceExternal, GetTLSGlanceAPISpec(GlanceAPITypeExternal))) + DeferCleanup(keystone.DeleteKeystoneAPI, keystone.CreateKeystoneAPI(glanceTest.Instance.Namespace)) + keystone.SimulateKeystoneEndpointReady(glanceTest.GlanceExternal) + keystone.SimulateKeystoneEndpointReady(glanceTest.GlanceInternal) + + th.ExpectCondition( + glanceTest.GlanceInternal, + ConditionGetterFunc(GlanceAPIConditionGetter), + condition.ServiceConfigReadyCondition, + corev1.ConditionTrue, + ) + th.ExpectCondition( + glanceTest.GlanceExternal, + ConditionGetterFunc(GlanceAPIConditionGetter), + condition.ServiceConfigReadyCondition, + corev1.ConditionTrue, + ) + }) + It("it renders the custom template and adds it to the config-data secret of the GlanceInternal instance", func() { + scrt := th.GetSecret(glanceTest.GlanceInternalConfigMapData) + Expect(scrt).ShouldNot(BeNil()) + Expect(scrt.Data).Should(HaveKey(common.TemplateParameters)) + configData := string(scrt.Data[common.TemplateParameters]) + keystonePublicURL := "http://keystone-public-openstack.testing" + Expect(configData).Should(ContainSubstring(fmt.Sprintf("KeystonePublicURL: %s", keystonePublicURL))) + + Expect(scrt.Data).Should(HaveKey("httpd_custom_internal_bar.conf")) + cfg := string(scrt.Data["httpd_custom_internal_bar.conf"]) + Expect(cfg).Should(ContainSubstring("CustomParam \"foo\"")) + Expect(cfg).Should(ContainSubstring(fmt.Sprintf("CustomKeystonePublicURL \"%s\"", keystonePublicURL))) + }) + + It("it has NOT renderd the custom template and added it to the config-data secret of the GlanceExternal instance", func() { + scrt := th.GetSecret(glanceTest.GlanceExternalConfigMapData) + Expect(scrt).ShouldNot(BeNil()) + Expect(scrt.Data).Should(HaveKey(common.TemplateParameters)) + configData := string(scrt.Data[common.TemplateParameters]) + keystonePublicURL := "http://keystone-public-openstack.testing" + Expect(configData).Should(ContainSubstring(fmt.Sprintf("KeystonePublicURL: %s", keystonePublicURL))) + + Expect(scrt.Data).Should(Not(HaveKey("httpd_custom_internal_bar.conf"))) + }) + }) })