Skip to content

Commit

Permalink
Allow customize http vhost config using HttpdCustomization.CustomConf…
Browse files Browse the repository at this point in the history
…igSecret

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_<key> .
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 rendered config-data secret of the service like:
$ oc get cm -n openstack horizon-config-data -o json | jq -r .data.templatingParameters | base64 -d

or in the running pod of the service in the file:
$ cat /var/lib/config-data/default/templatingParameters

The content is a versioned dump of the parameters of the service operator, like:

~~~
KeystoneEndpointInternal: https://keystone-internal.openstack.svc:5000
Override: false
SSLCertificateFile: /etc/pki/tls/certs/horizon.crt
SSLCertificateKeyFile: /etc/pki/tls/private/horizon.key
ServerName: horizon.openstack.svc
TLS: true
MemcachedServers: memcached-0.memcached.openstack.svc:11212
...
~~~

Depends-On: openstack-k8s-operators/lib-common#591
Depends-On: openstack-k8s-operators/lib-common#593

Jira: https://issues.redhat.com/browse/OSPRH-13100

Signed-off-by: Martin Schuppert <mschuppert@redhat.com>
  • Loading branch information
stuggi committed Jan 22, 2025
1 parent 8f2f2c4 commit 024ce48
Show file tree
Hide file tree
Showing 13 changed files with 210 additions and 34 deletions.
14 changes: 14 additions & 0 deletions api/bases/horizon.openstack.org_horizons.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1134,6 +1134,20 @@ spec:
- extraVol
type: object
type: array
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_<key> .
In the default httpd template at the end of the vhost those custom configs get
included using `Include conf/httpd_custom_<endpoint>_*`.
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
memcachedInstance:
default: memcached
description: Memcached instance name.
Expand Down
2 changes: 1 addition & 1 deletion api/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/openstack-k8s-operators/horizon-operator/api
go 1.21

require (
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.12
k8s.io/apimachinery v0.29.12
Expand Down
4 changes: 2 additions & 2 deletions api/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,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/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=
Expand Down
17 changes: 17 additions & 0 deletions api/v1beta1/horizon_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ type HorizonSpecCore struct {
// +kubebuilder:validation:Optional
// NetworkAttachments is a list of NetworkAttachment resource names to expose the services to the given network
NetworkAttachments []string `json:"networkAttachments,omitempty"`

// +kubebuilder:validation:Optional
// HttpdCustomization - customize the httpd service
HttpdCustomization HttpdCustomization `json:"httpdCustomization,omitempty"`
}

// HorizionOverrideSpec to override the generated manifest of several child resources.
Expand All @@ -112,6 +116,19 @@ type HorizionOverrideSpec struct {
Service *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_<key> .
// In the default httpd template at the end of the vhost those custom configs get
// included using `Include conf/httpd_custom_<endpoint>_*`.
// 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"`
}

// HorizonStatus defines the observed state of Horizon
type HorizonStatus struct {
// Map of hashes to track e.g. job status
Expand Down
21 changes: 21 additions & 0 deletions api/v1beta1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions config/crd/bases/horizon.openstack.org_horizons.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1134,6 +1134,20 @@ spec:
- extraVol
type: object
type: array
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_<key> .
In the default httpd template at the end of the vhost those custom configs get
included using `Include conf/httpd_custom_<endpoint>_*`.
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
memcachedInstance:
default: memcached
description: Memcached instance name.
Expand Down
81 changes: 62 additions & 19 deletions controllers/horizon_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import (
"github.com/openstack-k8s-operators/lib-common/modules/common/service"
"github.com/openstack-k8s-operators/lib-common/modules/common/tls"
util "github.com/openstack-k8s-operators/lib-common/modules/common/util"
"gopkg.in/yaml.v2"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
Expand Down Expand Up @@ -203,15 +204,17 @@ func (r *HorizonReconciler) Reconcile(ctx context.Context, req ctrl.Request) (re

// fields to index to reconcile when change
const (
passwordSecretField = ".spec.secret"
tlsField = ".spec.tls.secretName"
caBundleSecretNameField = ".spec.tls.caBundleSecretName"
passwordSecretField = ".spec.secret"
tlsField = ".spec.tls.secretName"
caBundleSecretNameField = ".spec.tls.caBundleSecretName"
httpdCustomServiceConfigSecretField = ".spec.httpdCustomization.customServiceConfigSecret"
)

var allWatchFields = []string{
passwordSecretField,
caBundleSecretNameField,
tlsField,
httpdCustomServiceConfigSecretField,
}

// SetupWithManager -
Expand Down Expand Up @@ -254,6 +257,18 @@ func (r *HorizonReconciler) SetupWithManager(mgr ctrl.Manager) error {
return err
}

// index httpdOverrideSecretField
if err := mgr.GetFieldIndexer().IndexField(context.Background(), &horizonv1beta1.Horizon{}, httpdCustomServiceConfigSecretField, func(rawObj client.Object) []string {
// Extract the secret name from the spec, if one is provided
cr := rawObj.(*horizonv1beta1.Horizon)
if cr.Spec.HttpdCustomization.CustomConfigSecret == nil {
return nil
}
return []string{*cr.Spec.HttpdCustomization.CustomConfigSecret}
}); err != nil {
return err
}

memcachedFn := func(_ context.Context, o client.Object) []reconcile.Request {
result := []reconcile.Request{}

Expand Down Expand Up @@ -887,16 +902,24 @@ func (r *HorizonReconciler) generateServiceConfigMaps(
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
}
}

templateParameters := map[string]interface{}{
"keystoneURL": authURL,
"horizonEndpoint": instance.Status.Endpoint,
"horizonEndpointHost": url.Host,
"memcachedServers": mc.GetMemcachedServerListQuotedString(),
"memcachedTLS": mc.GetMemcachedTLSSupport(),
"ServerName": fmt.Sprintf("%s.%s.svc", horizon.ServiceName, instance.Namespace),
"Port": horizon.HorizonPort,
"TLS": false,
"isPublicHTTPS": url.Scheme == "https",
"KeystoneEndpointInternal": authURL,
"HorizonEndpoint": instance.Status.Endpoint,
"HorizonEndpointHost": url.Host,
"MemcachedServers": mc.GetMemcachedServerListQuotedString(),
"MemcachedTLS": mc.GetMemcachedTLSSupport(),
"ServerName": fmt.Sprintf("%s.%s.svc", horizon.ServiceName, instance.Namespace),
"Port": horizon.HorizonPort,
"TLS": false,
"IsPublicHTTPS": url.Scheme == "https",
}

// create httpd tls template parameters
Expand All @@ -907,16 +930,36 @@ func (r *HorizonReconciler) generateServiceConfigMaps(
templateParameters["SSLCertificateKeyFile"] = fmt.Sprintf("/etc/pki/tls/private/%s.key", horizon.ServiceName)
}

// httpd overrides
customTemplates := map[string]string{}
templateParameters["Override"] = false
if len(httpdOverrideSecret.Data) > 0 {
templateParameters["Override"] = true
for key, data := range httpdOverrideSecret.Data {
if len(data) > 0 {
customTemplates["httpd_custom_"+key] = string(data)
}
}
}

// 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{
// ConfigMap
{
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,
},
}
return configmap.EnsureConfigMaps(ctx, h, instance, cms, envVars)
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ require (
github.com/openstack-k8s-operators/horizon-operator/api v0.3.1-0.20240214134444-c675e5f69043
github.com/openstack-k8s-operators/infra-operator/apis v0.5.1-0.20241217184302-c302f3d72ada
github.com/openstack-k8s-operators/keystone-operator/api v0.5.1-0.20241217165019-8e243bd36596
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
github.com/openstack-k8s-operators/lib-common/modules/test v0.5.1-0.20241216113837-d172b3ac0f4e
gopkg.in/yaml.v2 v2.4.0
k8s.io/api v0.29.12
k8s.io/apimachinery v0.29.12
k8s.io/client-go v0.29.12
Expand Down Expand Up @@ -73,7 +74,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.12 // indirect
k8s.io/component-base v0.29.12 // indirect
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,8 @@ github.com/openstack-k8s-operators/infra-operator/apis v0.5.1-0.20241217184302-c
github.com/openstack-k8s-operators/infra-operator/apis v0.5.1-0.20241217184302-c302f3d72ada/go.mod h1:gznNWtIOdZLwyv3/LmWbDqtwRgtyzCw616Rwrn51DT0=
github.com/openstack-k8s-operators/keystone-operator/api v0.5.1-0.20241217165019-8e243bd36596 h1:JKeShCY9BQj6cYDk44bgEIm8jPcvggodGxrW4ECzsv4=
github.com/openstack-k8s-operators/keystone-operator/api v0.5.1-0.20241217165019-8e243bd36596/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=
Expand Down
7 changes: 7 additions & 0 deletions templates/horizon/config/horizon.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@
"perm": "0600",
"optional": true,
"merge": true
},
{
"source": "/var/lib/config-data/default/httpd_custom_*",
"dest": "/etc/httpd/conf/",
"owner": "apache",
"perm": "0444",
"optional": true
}
],
"permissions": [
Expand Down
6 changes: 5 additions & 1 deletion templates/horizon/config/httpd.conf
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,15 @@ LogLevel debug
CustomLog /dev/stdout "%a %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" env=forwarded

## RedirectMatch rules
RedirectMatch permanent ^/$ "{{ .horizonEndpoint }}/dashboard"
RedirectMatch permanent ^/$ "{{ .HorizonEndpoint }}/dashboard"

## WSGI configuration
WSGIApplicationGroup %{GLOBAL}
WSGIDaemonProcess apache display-name=horizon group=apache processes=4 threads=1 user=apache
WSGIProcessGroup apache
WSGIScriptAlias /dashboard "/usr/share/openstack-dashboard/openstack_dashboard/wsgi.py"

{{- if .Override }}
Include conf/httpd_custom_*
{{- end }}
</VirtualHost>
14 changes: 7 additions & 7 deletions templates/horizon/config/local_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ def get_pod_ip():
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
hostport = (
"{{ .horizonEndpointHost }}",
{{- if .isPublicHTTPS }}
"{{ .HorizonEndpointHost }}",
{{- if .IsPublicHTTPS }}
443
{{- else }}
80
Expand All @@ -83,11 +83,11 @@ def get_pod_ip():
finally:
s.close()

ALLOWED_HOSTS = [get_pod_ip(), "{{ .horizonEndpointHost }}"]
ALLOWED_HOSTS = [get_pod_ip(), "{{ .HorizonEndpointHost }}"]

USE_X_FORWARDED_HOST = True

{{- if .isPublicHTTPS }}
{{- if .IsPublicHTTPS }}
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
SECURE_SSL_REDIRECT = True
CSRF_COOKIE_SECURE = True
Expand Down Expand Up @@ -122,11 +122,11 @@ def get_pod_ip():
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache',
'LOCATION': [ {{.memcachedServers}} ],
'LOCATION': [ {{.MemcachedServers}} ],
# To drop the cached sessions when config changes
'KEY_PREFIX': os.environ['CONFIG_HASH'],
'OPTIONS': {
{{- if .memcachedTLS }}
{{- if .MemcachedTLS }}
'tls_context': ssl.create_default_context()
{{- end }}
}
Expand Down Expand Up @@ -154,7 +154,7 @@ def get_pod_ip():
OPENSTACK_HOST = "127.0.0.1"
#OPENSTACK_KEYSTONE_URL = "http://%s/identity/v3" % OPENSTACK_HOST

OPENSTACK_KEYSTONE_URL = "{{ .keystoneURL }}/v3"
OPENSTACK_KEYSTONE_URL = "{{ .KeystoneEndpointInternal }}/v3"

# The timezone of the server. This should correspond with the timezone
# of your entire OpenStack installation, and hopefully be in UTC.
Expand Down
Loading

0 comments on commit 024ce48

Please sign in to comment.