diff --git a/api/bases/octavia.openstack.org_octaviaapis.yaml b/api/bases/octavia.openstack.org_octaviaapis.yaml index d0304580..b4ec3b19 100644 --- a/api/bases/octavia.openstack.org_octaviaapis.yaml +++ b/api/bases/octavia.openstack.org_octaviaapis.yaml @@ -89,6 +89,20 @@ spec: But can also be used to add additional files. Those get added to the service config dir in /etc/ . TODO: -> implement type: object + httpdCustomization: + description: HttpdCustomization - customize the httpd service + properties: + customConfigSecret: + description: |- + 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 + type: string + type: object networkAttachments: description: NetworkAttachments is a list of NetworkAttachment resource names to expose the services to the given network diff --git a/api/bases/octavia.openstack.org_octavias.yaml b/api/bases/octavia.openstack.org_octavias.yaml index 2e99aef0..4045533a 100644 --- a/api/bases/octavia.openstack.org_octavias.yaml +++ b/api/bases/octavia.openstack.org_octavias.yaml @@ -196,6 +196,20 @@ spec: But can also be used to add additional files. Those get added to the service config dir in /etc/ . TODO: -> implement type: object + httpdCustomization: + description: HttpdCustomization - customize the httpd service + properties: + customConfigSecret: + description: |- + 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 + type: string + type: object networkAttachments: description: NetworkAttachments is a list of NetworkAttachment resource names to expose the services to the given network diff --git a/api/go.mod b/api/go.mod index ea1d3958..d4bae385 100644 --- a/api/go.mod +++ b/api/go.mod @@ -5,7 +5,7 @@ go 1.21 require ( github.com/onsi/ginkgo/v2 v2.20.1 github.com/onsi/gomega v1.34.1 - 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 k8s.io/api v0.29.13 k8s.io/apimachinery v0.29.13 k8s.io/client-go v0.29.13 diff --git a/api/go.sum b/api/go.sum index 94d0e3f1..7b9d8694 100644 --- a/api/go.sum +++ b/api/go.sum @@ -72,8 +72,8 @@ github.com/onsi/ginkgo/v2 v2.20.1 h1:YlVIbqct+ZmnEph770q9Q7NVAz4wwIiVNahee6JyUzo github.com/onsi/ginkgo/v2 v2.20.1/go.mod h1:lG9ey2Z29hR41WMVthyJBGUBcBhGOtoPF2VFMvBXFCI= github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= -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/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= diff --git a/api/v1beta1/octaviaapi_types.go b/api/v1beta1/octaviaapi_types.go index 795deeac..d875794c 100644 --- a/api/v1beta1/octaviaapi_types.go +++ b/api/v1beta1/octaviaapi_types.go @@ -150,6 +150,10 @@ type OctaviaAPISpecCore struct { // +kubebuilder:validation:Optional // APITimeout for HAProxy and Apache defaults to OctaviaSpecCore APITimeout (seconds) APITimeout int `json:"apiTimeout"` + + // +kubebuilder:validation:Optional + // HttpdCustomization - customize the httpd service + HttpdCustomization HttpdCustomization `json:"httpdCustomization,omitempty"` } type OctaviaApiTLS struct { @@ -174,6 +178,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"` +} + // OctaviaAPIStatus defines the observed state of OctaviaAPI type OctaviaAPIStatus struct { // ReadyCount of octavia API instances diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index 20b0e744..b176a7b4 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -48,6 +48,26 @@ func (in *APIOverrideSpec) DeepCopy() *APIOverrideSpec { 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 *Octavia) DeepCopyInto(out *Octavia) { *out = *in @@ -185,6 +205,7 @@ func (in *OctaviaAPISpecCore) DeepCopyInto(out *OctaviaAPISpecCore) { copy(*out, *in) } in.TLS.DeepCopyInto(&out.TLS) + in.HttpdCustomization.DeepCopyInto(&out.HttpdCustomization) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OctaviaAPISpecCore. diff --git a/config/crd/bases/octavia.openstack.org_octaviaapis.yaml b/config/crd/bases/octavia.openstack.org_octaviaapis.yaml index d0304580..b4ec3b19 100644 --- a/config/crd/bases/octavia.openstack.org_octaviaapis.yaml +++ b/config/crd/bases/octavia.openstack.org_octaviaapis.yaml @@ -89,6 +89,20 @@ spec: But can also be used to add additional files. Those get added to the service config dir in /etc/ . TODO: -> implement type: object + httpdCustomization: + description: HttpdCustomization - customize the httpd service + properties: + customConfigSecret: + description: |- + 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 + type: string + type: object networkAttachments: description: NetworkAttachments is a list of NetworkAttachment resource names to expose the services to the given network diff --git a/config/crd/bases/octavia.openstack.org_octavias.yaml b/config/crd/bases/octavia.openstack.org_octavias.yaml index 2e99aef0..4045533a 100644 --- a/config/crd/bases/octavia.openstack.org_octavias.yaml +++ b/config/crd/bases/octavia.openstack.org_octavias.yaml @@ -196,6 +196,20 @@ spec: But can also be used to add additional files. Those get added to the service config dir in /etc/ . TODO: -> implement type: object + httpdCustomization: + description: HttpdCustomization - customize the httpd service + properties: + customConfigSecret: + description: |- + 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 + type: string + type: object networkAttachments: description: NetworkAttachments is a list of NetworkAttachment resource names to expose the services to the given network diff --git a/controllers/amphoracontroller_controller.go b/controllers/amphoracontroller_controller.go index cc2671b4..b761bbf6 100644 --- a/controllers/amphoracontroller_controller.go +++ b/controllers/amphoracontroller_controller.go @@ -35,6 +35,7 @@ import ( "github.com/openstack-k8s-operators/lib-common/modules/common/tls" "github.com/openstack-k8s-operators/lib-common/modules/common/util" mariadbv1 "github.com/openstack-k8s-operators/mariadb-operator/api/v1beta1" + "gopkg.in/yaml.v2" networkv1 "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1" keystonev1 "github.com/openstack-k8s-operators/keystone-operator/api/v1beta1" @@ -643,6 +644,13 @@ func (r *OctaviaAmphoraControllerReconciler) generateServiceSecrets( } templateParameters["HeartbeatKey"] = string(ospSecret.Data["OctaviaHeartbeatKey"]) + // 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) + // TODO(beagles): populate the template parameters cms := []util.Template{ { diff --git a/controllers/octavia_controller.go b/controllers/octavia_controller.go index 96d6da9c..8dfe0583 100644 --- a/controllers/octavia_controller.go +++ b/controllers/octavia_controller.go @@ -43,6 +43,7 @@ import ( mariadbv1 "github.com/openstack-k8s-operators/mariadb-operator/api/v1beta1" octaviav1 "github.com/openstack-k8s-operators/octavia-operator/api/v1beta1" "github.com/openstack-k8s-operators/octavia-operator/pkg/octavia" + "gopkg.in/yaml.v2" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/intstr" @@ -223,11 +224,12 @@ func (r *OctaviaReconciler) Reconcile(ctx context.Context, req ctrl.Request) (re // 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" - tlsOvnField = ".spec.tls.ovn.secretName" + passwordSecretField = ".spec.secret" + caBundleSecretNameField = ".spec.tls.caBundleSecretName" + tlsAPIInternalField = ".spec.tls.api.internal.secretName" + tlsAPIPublicField = ".spec.tls.api.public.secretName" + tlsOvnField = ".spec.tls.ovn.secretName" + httpdCustomServiceConfigSecretField = ".spec.httpdCustomization.customServiceConfigSecret" ) var ( @@ -237,6 +239,7 @@ var ( tlsAPIInternalField, tlsAPIPublicField, tlsOvnField, + httpdCustomServiceConfigSecretField, } ) @@ -1423,6 +1426,13 @@ func (r *OctaviaReconciler) generateServiceSecrets( templateParameters["ServiceUser"] = instance.Spec.ServiceUser templateParameters["TenantName"] = instance.Spec.TenantName + // 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{ { Name: fmt.Sprintf("%s-scripts", instance.Name), @@ -1442,7 +1452,7 @@ func (r *OctaviaReconciler) generateServiceSecrets( Labels: cmLabels, }, } - err := oko_secret.EnsureSecrets(ctx, h, instance, cms, envVars) + err = oko_secret.EnsureSecrets(ctx, h, instance, cms, envVars) if err != nil { return err } diff --git a/controllers/octaviaapi_controller.go b/controllers/octaviaapi_controller.go index 9403be8b..04a0a336 100644 --- a/controllers/octaviaapi_controller.go +++ b/controllers/octaviaapi_controller.go @@ -42,6 +42,7 @@ import ( "github.com/openstack-k8s-operators/octavia-operator/pkg/octavia" "github.com/openstack-k8s-operators/octavia-operator/pkg/octaviaapi" ovnclient "github.com/openstack-k8s-operators/ovn-operator/api/v1beta1" + "gopkg.in/yaml.v2" appsv1 "k8s.io/api/apps/v1" batchv1 "k8s.io/api/batch/v1" @@ -259,6 +260,18 @@ func (r *OctaviaAPIReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Ma return err } + // index httpdOverrideSecretField + if err := mgr.GetFieldIndexer().IndexField(ctx, &octaviav1.OctaviaAPI{}, httpdCustomServiceConfigSecretField, func(rawObj client.Object) []string { + // Extract the secret name from the spec, if one is provided + cr := rawObj.(*octaviav1.OctaviaAPI) + if cr.Spec.HttpdCustomization.CustomConfigSecret == nil { + return nil + } + return []string{*cr.Spec.HttpdCustomization.CustomConfigSecret} + }); err != nil { + return err + } + return ctrl.NewControllerManagedBy(mgr). For(&octaviav1.OctaviaAPI{}). Owns(&keystonev1.KeystoneService{}). @@ -920,6 +933,14 @@ func (r *OctaviaAPIReconciler) generateServiceSecrets( return err } + httpdOverrideSecret := &corev1.Secret{} + if instance.Spec.HttpdCustomization.CustomConfigSecret != nil && *instance.Spec.HttpdCustomization.CustomConfigSecret != "" { + httpdOverrideSecret, _, err = oko_secret.GetSecret(ctx, h, *instance.Spec.HttpdCustomization.CustomConfigSecret, instance.Namespace) + if err != nil { + return err + } + } + databaseAccount, dbSecret, err := mariadbv1.GetAccountAndSecret( ctx, h, instance.Spec.DatabaseAccount, instance.Namespace) @@ -988,6 +1009,7 @@ func (r *OctaviaAPIReconciler) generateServiceSecrets( templateParameters["TimeOut"] = instance.Spec.APITimeout // create httpd vhost template parameters + customTemplates := map[string]string{} httpdVhostConfig := map[string]interface{}{} for _, endpt := range []service.Endpoint{service.EndpointInternal, service.EndpointPublic} { endptConfig := map[string]interface{}{} @@ -998,10 +1020,27 @@ func (r *OctaviaAPIReconciler) generateServiceSecrets( 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 } templateParameters["VHosts"] = httpdVhostConfig + // 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{ { Name: fmt.Sprintf("%s-scripts", instance.Name), @@ -1012,13 +1051,14 @@ func (r *OctaviaAPIReconciler) generateServiceSecrets( Labels: cmLabels, }, { - Name: fmt.Sprintf("%s-config-data", instance.Name), - Namespace: instance.Namespace, - Type: util.TemplateTypeConfig, - InstanceType: instance.Kind, - CustomData: customData, - ConfigOptions: templateParameters, - Labels: cmLabels, + Name: fmt.Sprintf("%s-config-data", instance.Name), + Namespace: instance.Namespace, + Type: util.TemplateTypeConfig, + InstanceType: instance.Kind, + CustomData: customData, + ConfigOptions: templateParameters, + StringTemplate: customTemplates, + Labels: cmLabels, }, } err = oko_secret.EnsureSecrets(ctx, h, instance, cms, envVars) diff --git a/controllers/octaviarsyslog_controller.go b/controllers/octaviarsyslog_controller.go index 006fac35..47e102d5 100644 --- a/controllers/octaviarsyslog_controller.go +++ b/controllers/octaviarsyslog_controller.go @@ -31,6 +31,7 @@ import ( "github.com/openstack-k8s-operators/lib-common/modules/common/labels" nad "github.com/openstack-k8s-operators/lib-common/modules/common/networkattachment" "github.com/openstack-k8s-operators/lib-common/modules/common/util" + "gopkg.in/yaml.v2" oko_secret "github.com/openstack-k8s-operators/lib-common/modules/common/secret" octaviav1 "github.com/openstack-k8s-operators/octavia-operator/api/v1beta1" @@ -387,6 +388,13 @@ func (r *OctaviaRsyslogReconciler) generateServiceSecrets( spec := instance.Spec templateParameters["ServiceUser"] = spec.ServiceUser + // 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) + // TODO(beagles): populate the template parameters cms := []util.Template{ // ScriptsConfigMap @@ -409,7 +417,7 @@ func (r *OctaviaRsyslogReconciler) generateServiceSecrets( }, } - err := oko_secret.EnsureSecrets(ctx, helper, instance, cms, envVars) + err = oko_secret.EnsureSecrets(ctx, helper, instance, cms, envVars) if err != nil { r.Log.Error(err, "unable to process config map") return err diff --git a/go.mod b/go.mod index dfa9d554..ebb3057e 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/onsi/gomega v1.34.1 github.com/openstack-k8s-operators/infra-operator/apis v0.5.1-0.20241213080025-18e54a028c8b github.com/openstack-k8s-operators/keystone-operator/api v0.5.1-0.20241212135809-dc78e7221d12 - 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/mariadb-operator/api v0.5.0 github.com/openstack-k8s-operators/octavia-operator/api v0.0.0-00010101000000-000000000000 @@ -26,6 +26,7 @@ require ( github.com/google/uuid v1.6.0 github.com/openstack-k8s-operators/lib-common/modules/test v0.5.1-0.20241216113837-d172b3ac0f4e golang.org/x/crypto v0.32.0 + gopkg.in/yaml.v2 v2.4.0 k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 ) @@ -76,7 +77,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 gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apiextensions-apiserver v0.29.13 // indirect k8s.io/component-base v0.29.13 // indirect diff --git a/go.sum b/go.sum index 3481fc51..3efe2a54 100644 --- a/go.sum +++ b/go.sum @@ -82,8 +82,8 @@ github.com/openstack-k8s-operators/infra-operator/apis v0.5.1-0.20241213080025-1 github.com/openstack-k8s-operators/infra-operator/apis v0.5.1-0.20241213080025-18e54a028c8b/go.mod h1:SSYBbFbgQbOwyY2cQNet7fSdQHHPb2rLo6GXE97Awp8= github.com/openstack-k8s-operators/keystone-operator/api v0.5.1-0.20241212135809-dc78e7221d12 h1:37tN4oVifWqkerafFrx3DFDDTOOzn2H+c67WIQ1Vkss= github.com/openstack-k8s-operators/keystone-operator/api v0.5.1-0.20241212135809-dc78e7221d12/go.mod h1:AZhHY6dZzGyG9iVOf1poD7pTS9c7ZG/f99Fg+GdFVEk= -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/test v0.5.1-0.20241216113837-d172b3ac0f4e h1:/iWDp3j+ET3gE5IjKHtdZaPd4SQyLHB/4L5jB16cV3I= diff --git a/templates/octaviaapi/config/httpd.conf b/templates/octaviaapi/config/httpd.conf index fbf161e5..b6333590 100644 --- a/templates/octaviaapi/config/httpd.conf +++ b/templates/octaviaapi/config/httpd.conf @@ -53,6 +53,10 @@ ErrorLog /dev/stdout WSGIPassAuthorization On WSGIDaemonProcess {{ $endpt }} processes=5 threads=1 user=octavia group=octavia display-name={{ $endpt }} WSGIScriptAlias / /usr/bin/octavia-wsgi + + {{- if $vhost.Override }} + Include conf/httpd_custom_{{ $endpt }}_* + {{- end }} {{ end }} diff --git a/templates/octaviaapi/config/octavia-api-config.json b/templates/octaviaapi/config/octavia-api-config.json index 2d6245f6..d4a3ce28 100644 --- a/templates/octaviaapi/config/octavia-api-config.json +++ b/templates/octaviaapi/config/octavia-api-config.json @@ -53,6 +53,13 @@ "dest": "/etc/my.cnf", "owner": "octavia", "perm": "0644" + }, + { + "source": "/var/lib/config-data/merged/httpd_custom_*", + "dest": "/etc/httpd/conf/", + "owner": "apache", + "perm": "0444", + "optional": true } ], "permissions": [ diff --git a/tests/functional/octaviaapi_controller_test.go b/tests/functional/octaviaapi_controller_test.go index 7bcfc065..395ec3eb 100644 --- a/tests/functional/octaviaapi_controller_test.go +++ b/tests/functional/octaviaapi_controller_test.go @@ -26,7 +26,9 @@ import ( corev1 "k8s.io/api/core/v1" + "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 . "github.com/openstack-k8s-operators/lib-common/modules/common/test/helpers" "github.com/openstack-k8s-operators/octavia-operator/pkg/octavia" @@ -304,6 +306,107 @@ var _ = Describe("OctaviaAPI controller", func() { }) }) + When("A OctaviaAPI is created with HttpdCustomization.CustomConfigSecret", func() { + var keystoneInternalEndpoint string + var keystonePublicEndpoint string + + BeforeEach(func() { + customServiceConfigSecretName := types.NamespacedName{Name: "foo", Namespace: namespace} + customConfig := []byte(`CustomParam "foo" +CustomKeystonePublicURL "{{ .KeystonePublicURL }}"`) + th.CreateSecret( + customServiceConfigSecretName, + map[string][]byte{ + "bar.conf": customConfig, + }, + ) + + keystoneName := keystone.CreateKeystoneAPI(namespace) + DeferCleanup(keystone.DeleteKeystoneAPI, keystoneName) + keystoneInternalEndpoint = fmt.Sprintf("http://keystone-for-%s-internal", octaviaAPIName.Name) + keystonePublicEndpoint = fmt.Sprintf("http://keystone-for-%s-public", octaviaAPIName.Name) + SimulateKeystoneReady(keystoneName, keystonePublicEndpoint, keystoneInternalEndpoint) + + DeferCleanup(k8sClient.Delete, ctx, CreateOctaviaSecret(namespace)) + DeferCleanup(k8sClient.Delete, ctx, CreateTransportURLSecret(transportURLSecretName)) + + spec["httpdCustomization"] = map[string]interface{}{ + "customConfigSecret": customServiceConfigSecretName.Name, + } + + DeferCleanup(th.DeleteInstance, CreateOctaviaAPI(octaviaAPIName, spec)) + + mariaDBDatabaseName := mariadb.CreateMariaDBDatabase(namespace, octavia.DatabaseCRName, mariadbv1.MariaDBDatabaseSpec{}) + mariaDBDatabase := mariadb.GetMariaDBDatabase(mariaDBDatabaseName) + DeferCleanup(k8sClient.Delete, ctx, mariaDBDatabase) + + octaviaAPI := GetOctaviaAPI(octaviaAPIName) + apiMariaDBAccount, apiMariaDBSecret := mariadb.CreateMariaDBAccountAndSecret( + types.NamespacedName{ + Namespace: namespace, + Name: octaviaAPI.Spec.DatabaseAccount, + }, mariadbv1.MariaDBAccountSpec{}) + DeferCleanup(k8sClient.Delete, ctx, apiMariaDBAccount) + DeferCleanup(k8sClient.Delete, ctx, apiMariaDBSecret) + + mariaDBDatabaseName = mariadb.CreateMariaDBDatabase(namespace, octavia.PersistenceDatabaseCRName, mariadbv1.MariaDBDatabaseSpec{}) + mariaDBDatabase = mariadb.GetMariaDBDatabase(mariaDBDatabaseName) + DeferCleanup(k8sClient.Delete, ctx, mariaDBDatabase) + + apiMariaDBAccount, apiMariaDBSecret = mariadb.CreateMariaDBAccountAndSecret( + types.NamespacedName{ + Namespace: namespace, + Name: octaviaAPI.Spec.PersistenceDatabaseAccount, + }, mariadbv1.MariaDBAccountSpec{}) + DeferCleanup(k8sClient.Delete, ctx, apiMariaDBAccount) + DeferCleanup(k8sClient.Delete, ctx, apiMariaDBSecret) + + ovndbCluster := ovn.CreateOVNDBCluster(namespace, + ovnv1.OVNDBClusterSpec{ + OVNDBClusterSpecCore: ovnv1.OVNDBClusterSpecCore{ + DBType: ovnv1.NBDBType, + }}) + ovndb := ovn.GetOVNDBCluster(ovndbCluster) + DeferCleanup(k8sClient.Delete, ctx, ovndb) + Eventually(func(g Gomega) { + ovndb.Status.InternalDBAddress = OVNNBDBEndpoint + g.Expect(k8sClient.Status().Update(ctx, ovndb)).To(Succeed()) + }).Should(Succeed()) + ovn.SimulateOVNDBClusterReady(ovndbCluster) + + ovndbCluster = ovn.CreateOVNDBCluster(namespace, + ovnv1.OVNDBClusterSpec{ + OVNDBClusterSpecCore: ovnv1.OVNDBClusterSpecCore{ + DBType: ovnv1.SBDBType, + }}) + ovndb = ovn.GetOVNDBCluster(ovndbCluster) + DeferCleanup(k8sClient.Delete, ctx, ovndb) + Eventually(func(g Gomega) { + ovndb.Status.InternalDBAddress = OVNSBDBEndpoint + g.Expect(k8sClient.Status().Update(ctx, ovndb)).To(Succeed()) + }).Should(Succeed()) + ovn.SimulateOVNDBClusterReady(ovndbCluster) + }) + + It("it renders the custom template and adds it to the placement-config-data secret", func() { + scrt := th.GetSecret( + types.NamespacedName{ + Namespace: octaviaAPIName.Namespace, + Name: fmt.Sprintf("%s-config-data", octaviaAPIName.Name)}) + Expect(scrt).ShouldNot(BeNil()) + Expect(scrt.Data).Should(HaveKey(common.TemplateParameters)) + configData := string(scrt.Data[common.TemplateParameters]) + Expect(configData).Should(ContainSubstring(fmt.Sprintf("KeystonePublicURL: %s", keystonePublicEndpoint))) + + 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\"", keystonePublicEndpoint))) + } + }) + }) + // NAD // Networks Annotation