Skip to content

Commit 8070c8d

Browse files
committed
Support multiple templates in config entries
1 parent 3595835 commit 8070c8d

File tree

3 files changed

+74
-42
lines changed

3 files changed

+74
-42
lines changed

changelog/unreleased/config-templ.md

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
Enhancement: support multiple templates in config entries
2+
3+
This PR introduces support for config entries with multiple templates,
4+
such as `parameter = "{{ vars.v1 }} foo {{ vars.v2 }}"`.
5+
Previously, only one `{{ template }}` was allowed in a given
6+
configuration entry.
7+
8+
https://github.com/cs3org/reva/pull/4282

cmd/revad/pkg/config/templates.go

+60-37
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ func applyTemplateStruct(l Lookuper, p setter, v reflect.Value) error {
5757
return nil
5858
}
5959

60-
// applyTemplateByType applies the template string to a generic type.
60+
// applyTemplateByType (recursively) applies the template string to a generic type.
6161
func applyTemplateByType(l Lookuper, p setter, v reflect.Value) error {
6262
switch v.Kind() {
6363
case reflect.Array, reflect.Slice:
@@ -119,31 +119,34 @@ func applyTemplateString(l Lookuper, p setter, v reflect.Value) error {
119119
panic("called applyTemplateString on non string type")
120120
}
121121
s := v.String()
122-
tmpl, is := isTemplate(s)
123-
if !is {
122+
tmpl, more := isTemplate(s)
123+
if !more {
124124
// nothing to do
125125
return nil
126126
}
127127

128-
key := keyFromTemplate(tmpl)
129-
val, err := l.Lookup(key)
130-
if err != nil {
131-
return err
132-
}
133-
if val == nil {
134-
return nil
135-
}
128+
for more {
129+
key := keyFromTemplate(tmpl)
130+
val, err := l.Lookup(key)
131+
if err != nil {
132+
return err
133+
}
134+
if val == nil {
135+
val = ""
136+
}
136137

137-
new, err := replaceTemplate(s, tmpl, val)
138-
if err != nil {
139-
return err
140-
}
141-
str, ok := convertToString(new)
142-
if !ok {
143-
return fmt.Errorf("value %v cannot be converted as string in the template %s", val, new)
138+
new, err := replaceTemplate(s, tmpl, val)
139+
if err != nil {
140+
return err
141+
}
142+
str, ok := convertToString(new)
143+
if !ok {
144+
return fmt.Errorf("value %v cannot be converted as string in the template %s", val, new)
145+
}
146+
s = str
147+
tmpl, more = isTemplate(s)
144148
}
145-
146-
p.SetValue(str)
149+
p.SetValue(s)
147150
return nil
148151
}
149152

@@ -159,32 +162,49 @@ func applyTemplateInterface(l Lookuper, p setter, v reflect.Value) error {
159162
return applyTemplateByType(l, p, v.Elem())
160163
}
161164

162-
tmpl, is := isTemplate(s)
163-
if !is {
165+
tmpl, more := isTemplate(s)
166+
if !more {
164167
// nothing to do
165168
return nil
166169
}
170+
replaced := 0
171+
for more {
172+
key := keyFromTemplate(tmpl)
173+
val, err := l.Lookup(key)
174+
if err != nil {
175+
return err
176+
}
177+
if val == nil {
178+
val = ""
179+
}
167180

168-
key := keyFromTemplate(tmpl)
169-
val, err := l.Lookup(key)
170-
if err != nil {
171-
return err
172-
}
173-
if val == nil {
174-
return nil
175-
}
181+
new, err := replaceTemplate(s, tmpl, val)
182+
if err != nil {
183+
return err
184+
}
185+
186+
replaced++
187+
str, ok := convertToString(new)
188+
tmpl, more = isTemplate(str)
176189

177-
new, err := replaceTemplate(s, tmpl, val)
178-
if err != nil {
179-
return err
190+
if !more && replaced == 1 {
191+
// a single template was to be replaced, preserve type
192+
p.SetValue(new)
193+
return nil
194+
}
195+
// if more than one template is to be replaced, use the string representation
196+
if !ok {
197+
return fmt.Errorf("value %v cannot be converted as string in the template %s", val, new)
198+
}
199+
s = str
180200
}
181-
p.SetValue(new)
201+
p.SetValue(s)
182202
return nil
183203
}
184204

185205
func replaceTemplate(original, tmpl string, val any) (any, error) {
186206
if strings.TrimSpace(original) == tmpl {
187-
// the value was directly a template, i.e. "{{ grpc.services.gateway.address }}"
207+
// the value was directly a template, e.g. "{{ grpc.services.gateway.address }}"
188208
return val, nil
189209
}
190210
// the value is of something like "something {{ template }} something else"
@@ -227,7 +247,7 @@ func convertToString(val any) (string, bool) {
227247
return "", false
228248
}
229249

230-
var templateRegex = regexp.MustCompile("{{.{1,}}}")
250+
var templateRegex = regexp.MustCompile("{{[^{}]+}}")
231251

232252
func isTemplate(s string) (string, bool) {
233253
m := templateRegex.FindString(s)
@@ -237,7 +257,10 @@ func isTemplate(s string) (string, bool) {
237257
func keyFromTemplate(s string) string {
238258
s = strings.TrimSpace(s)
239259
s = strings.TrimPrefix(s, "{{")
240-
s = strings.TrimSuffix(s, "}}")
260+
for strings.Index(s, "}}") > 0 {
261+
//nolint: gocritic
262+
s = s[:strings.Index(s, "}}")] // this is not offBy1
263+
}
241264
return "." + strings.TrimSpace(s)
242265
}
243266

cmd/revad/pkg/config/templates_test.go

+6-5
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,8 @@ func TestApplyTemplate(t *testing.T) {
8585
Vars: Vars{
8686
"db_username": "root",
8787
"db_password": "secretpassword",
88-
"integer": 10,
88+
"proto": "http",
89+
"port": 1000,
8990
},
9091
GRPC: &GRPC{
9192
Services: map[string]ServicesConfig{
@@ -98,7 +99,7 @@ func TestApplyTemplate(t *testing.T) {
9899
"db_username": "{{ vars.db_username }}",
99100
"db_password": "{{ vars.db_password }}",
100101
"key": "value",
101-
"int": "{{ vars.integer }}",
102+
"port": "{{ vars.port }}",
102103
},
103104
},
104105
},
@@ -110,7 +111,7 @@ func TestApplyTemplate(t *testing.T) {
110111
Config: map[string]any{
111112
"drivers": map[string]any{
112113
"sql": map[string]any{
113-
"db_host": "http://localhost:{{ vars.integer }}",
114+
"db_host": "{{ vars.proto }}://localhost:{{ vars.port }}",
114115
},
115116
},
116117
},
@@ -127,9 +128,9 @@ func TestApplyTemplate(t *testing.T) {
127128
"db_username": "root",
128129
"db_password": "secretpassword",
129130
"key": "value",
130-
"int": 10,
131+
"port": 1000,
131132
}, cfg2.GRPC.Services["authregistry"][0].Config["drivers"].(map[string]any)["sql"])
132133
assert.Equal(t, map[string]any{
133-
"db_host": "http://localhost:10",
134+
"db_host": "http://localhost:1000",
134135
}, cfg2.GRPC.Services["other"][0].Config["drivers"].(map[string]any)["sql"])
135136
}

0 commit comments

Comments
 (0)