Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions modules/common/util/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
)
55 changes: 55 additions & 0 deletions modules/common/util/template_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ package util
import (
"bufio"
"bytes"
"embed"
"fmt"
"io/fs"
"os"
"path"
"path/filepath"
Expand All @@ -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

Expand Down Expand Up @@ -60,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
Expand Down Expand Up @@ -293,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)
Comment thread
fmount marked this conversation as resolved.
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
Expand Down Expand Up @@ -344,3 +368,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
}
73 changes: 73 additions & 0 deletions modules/common/util/template_util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -618,3 +678,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"))
})
}
21 changes: 21 additions & 0 deletions modules/common/util/templates/common/config/ssl.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<IfModule mod_ssl.c>
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
</IfModule>
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# operator-specific ssl.conf override
SSLProtocol -all +TLSv1.3
Loading