From 9cbc91b9c6d51786e048fd30fc61383ac82da806 Mon Sep 17 00:00:00 2001 From: Francesco Pantano Date: Thu, 14 May 2026 14:38:02 +0200 Subject: [PATCH 1/2] Dedup some httpd related templates Signed-off-by: Francesco Pantano --- modules/common/util/template_util.go | 36 +++++++++++++++++++ modules/common/util/template_util_test.go | 13 +++++++ .../util/templates/common/config/ssl.conf | 21 +++++++++++ 3 files changed, 70 insertions(+) create mode 100644 modules/common/util/templates/common/config/ssl.conf diff --git a/modules/common/util/template_util.go b/modules/common/util/template_util.go index 5ee4ec25..508437b1 100644 --- a/modules/common/util/template_util.go +++ b/modules/common/util/template_util.go @@ -19,7 +19,9 @@ package util import ( "bufio" "bytes" + "embed" "fmt" + "io/fs" "os" "path" "path/filepath" @@ -29,6 +31,9 @@ import ( corev1 "k8s.io/api/core/v1" ) +//go:embed templates/common/config/* +var commonTemplates embed.FS + // TType - TemplateType type TType string @@ -344,3 +349,34 @@ func GetTemplateData(t Template) (map[string]string, error) { return data, nil } + +// GetCommonTemplates renders the common config templates (ssl.conf, etc.) +// shipped with lib-common using the provided configOptions, and returns +// map[filename]renderedContent. +// Callers merge the result into their Template.CustomData before calling +// EnsureSecrets/EnsureConfigMaps so that common templates are included in the +// generated Secret/ConfigMaps without each operator duplicating the files. +func GetCommonTemplates(configOptions map[string]any) (map[string]string, error) { + dir := "templates/common/config" + entries, err := fs.ReadDir(commonTemplates, dir) + if err != nil { + return nil, err + } + + result := make(map[string]string, len(entries)) + for _, e := range entries { + if e.IsDir() { + continue + } + data, err := fs.ReadFile(commonTemplates, path.Join(dir, e.Name())) + if err != nil { + return nil, err + } + rendered, err := ExecuteTemplateData(string(data), configOptions) + if err != nil { + return nil, err + } + result[e.Name()] = rendered + } + return result, nil +} diff --git a/modules/common/util/template_util_test.go b/modules/common/util/template_util_test.go index 75d3c505..a3ffe9ce 100644 --- a/modules/common/util/template_util_test.go +++ b/modules/common/util/template_util_test.go @@ -618,3 +618,16 @@ baz=1 g.Expect(cleaned2).To(Equal(cleaned)) } + +func TestGetCommonTemplates(t *testing.T) { + t.Run("Returns ssl.conf", func(t *testing.T) { + g := NewWithT(t) + + result, err := GetCommonTemplates(map[string]interface{}{}) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(result).To(HaveKey("ssl.conf")) + g.Expect(result["ssl.conf"]).To(ContainSubstring("mod_ssl")) + g.Expect(result["ssl.conf"]).To(ContainSubstring("SSLProtocol")) + g.Expect(result["ssl.conf"]).To(ContainSubstring("SSLCipherSuite")) + }) +} diff --git a/modules/common/util/templates/common/config/ssl.conf b/modules/common/util/templates/common/config/ssl.conf new file mode 100644 index 00000000..e3da4ecb --- /dev/null +++ b/modules/common/util/templates/common/config/ssl.conf @@ -0,0 +1,21 @@ + + SSLRandomSeed startup builtin + SSLRandomSeed startup file:/dev/urandom 512 + SSLRandomSeed connect builtin + SSLRandomSeed connect file:/dev/urandom 512 + + AddType application/x-x509-ca-cert .crt + AddType application/x-pkcs7-crl .crl + + SSLPassPhraseDialog builtin + SSLSessionCache "shmcb:/var/cache/mod_ssl/scache(512000)" + SSLSessionCacheTimeout 300 + Mutex default + SSLCryptoDevice builtin + SSLHonorCipherOrder On + SSLUseStapling Off + SSLStaplingCache "shmcb:/run/httpd/ssl_stapling(32768)" + SSLCipherSuite HIGH:MEDIUM:!aNULL:!MD5:!RC4:!3DES + SSLProtocol all -SSLv2 -SSLv3 -TLSv1 + SSLOptions StdEnvVars + From 577ad48c751d901bc3aaf9382684e2ee5118ffdc Mon Sep 17 00:00:00 2001 From: Francesco Pantano Date: Fri, 15 May 2026 15:04:26 +0200 Subject: [PATCH 2/2] Add CommonTemplates field for selective common template injection Add a CommonTemplates []string field to the Template struct that allows service operators to specify which embedded common templates to include. This avoids injecting common templates (e.g. ssl.conf) into components that don't need them (e.g. cinder-scheduler, cinder-volume). Signed-off-by: Francesco Pantano --- modules/common/util/errors.go | 2 + modules/common/util/template_util.go | 19 ++++++ modules/common/util/template_util_test.go | 60 +++++++++++++++++++ .../templates/override/config/ssl.conf | 2 + 4 files changed, 83 insertions(+) create mode 100644 modules/common/util/testdata/templates/override/config/ssl.conf diff --git a/modules/common/util/errors.go b/modules/common/util/errors.go index 33f9b2d2..50a681a8 100644 --- a/modules/common/util/errors.go +++ b/modules/common/util/errors.go @@ -29,4 +29,6 @@ var ( ErrTemplateSubdirUnset = errors.New("template subdir not set") // ErrInstanceTypeUnsetWithMultiTemplateDir indicates InstanceType is empty while MultiTemplateDir is set ErrInstanceTypeUnsetWithMultiTemplateDir = errors.New("instance type not set") + // ErrCommonTemplateNotFound indicates a requested common template does not exist + ErrCommonTemplateNotFound = errors.New("common template not found") ) diff --git a/modules/common/util/template_util.go b/modules/common/util/template_util.go index 508437b1..62369d9c 100644 --- a/modules/common/util/template_util.go +++ b/modules/common/util/template_util.go @@ -65,6 +65,7 @@ type Template struct { SkipSetOwner bool // skip setting ownership on the associated configmap Version string // optional version string to separate templates inside the InstanceType/Type directory. E.g. placementapi/config/18.0 MultiTemplateDir string // templates dir for multi-group operators, e.g. nova/api; requires InstanceType to be set + CommonTemplates []string // list of common embedded templates to include (e.g. []string{"ssl.conf"}). } // GetTemplatesPath get path to templates, either running local or deployed as container @@ -298,6 +299,24 @@ func GetTemplateData(t Template) (map[string]string, error) { data := make(map[string]string) + // Render requested common embedded templates as fallback defaults. + // Note that local operator templates rendered below overwrite the common + // ones: if they share the same filename, service operators definition take + // precedence + if len(t.CommonTemplates) > 0 { + commonData, err := GetCommonTemplates(opts) + if err != nil { + return nil, err + } + for _, name := range t.CommonTemplates { + v, ok := commonData[name] + if !ok { + return nil, fmt.Errorf("%w: %s", ErrCommonTemplateNotFound, name) + } + data[name] = v + } + } + if t.Type != TemplateTypeNone { // If MultiTemplateDir is set but InstanceType is not, return an error // though we do not use InstanceType here, but it is used in secret.go diff --git a/modules/common/util/template_util_test.go b/modules/common/util/template_util_test.go index a3ffe9ce..f5baecde 100644 --- a/modules/common/util/template_util_test.go +++ b/modules/common/util/template_util_test.go @@ -575,6 +575,66 @@ function common_func { want: map[string]string{}, error: true, }, + { + name: "CommonTemplates are injected", + tmpl: Template{ + Name: "testservice", + Namespace: "somenamespace", + Type: TemplateTypeConfig, + InstanceType: "testservice", + CommonTemplates: []string{"ssl.conf"}, + ConfigOptions: map[string]any{ + "ServiceUser": "foo", + "Count": 1, + "Upper": "BAR", + }, + }, + want: map[string]string{ + "bar.conf": "[DEFAULT]\nstate_path = /var/lib/nova\ndebug=true\nsome_parameter_with_brackets=[test]\ncompute_driver = libvirt.LibvirtDriver\n\n[oslo_concurrency]\nlock_path = /var/lib/nova/tmp\n", + "config.json": "{\n \"command\": \"/usr/sbin/httpd -DFOREGROUND\",\n}\n", + "foo.conf": "username = foo\ncount = 1\nadd = 3\nlower = bar\n", + "ssl.conf": "", // placeholder, replaced below + }, + error: false, + }, + { + name: "Unknown CommonTemplate returns error", + tmpl: Template{ + Name: "testservice", + Namespace: "somenamespace", + Type: TemplateTypeConfig, + InstanceType: "testservice", + CommonTemplates: []string{"sls.conf"}, + }, + want: nil, + error: true, + }, + { + name: "Local service templates take precedence", + tmpl: Template{ + Name: "override", + Namespace: "somenamespace", + Type: TemplateTypeConfig, + InstanceType: "override", + CommonTemplates: []string{"ssl.conf"}, + ConfigOptions: map[string]any{}, + }, + want: map[string]string{ + "ssl.conf": "# operator-specific ssl.conf override\nSSLProtocol -all +TLSv1.3\n", + }, + error: false, + }, + } + + // Fill in the expected ssl.conf content for the CommonTemplates test + commonTmpls, err := GetCommonTemplates(map[string]any{}) + if err != nil { + panic("Failed to get common templates: " + err.Error()) + } + for i, tt := range tests { + if v, ok := tt.want["ssl.conf"]; ok && v == "" { + tests[i].want["ssl.conf"] = commonTmpls["ssl.conf"] + } } for _, tt := range tests { diff --git a/modules/common/util/testdata/templates/override/config/ssl.conf b/modules/common/util/testdata/templates/override/config/ssl.conf new file mode 100644 index 00000000..5f9765d1 --- /dev/null +++ b/modules/common/util/testdata/templates/override/config/ssl.conf @@ -0,0 +1,2 @@ +# operator-specific ssl.conf override +SSLProtocol -all +TLSv1.3