feat: add docker recipe + environment with default interpolation in config
Some checks failed
arcad/emissary/pipeline/head There was a failure building this commit
Some checks failed
arcad/emissary/pipeline/head There was a failure building this commit
This commit is contained in:
@ -10,7 +10,41 @@ import (
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
var reVar = regexp.MustCompile(`^\${(\w+)}$`)
|
||||
var (
|
||||
interpolationRegExp = regexp.MustCompile(`^\${((?P<varName>\w+)|((?P<varNameWithDefault>\w+):-(?P<defaultValue>[^}]+)))}$`)
|
||||
varNameGroupIndex = interpolationRegExp.SubexpIndex("varName")
|
||||
varNameWithDefaultGroupIndex = interpolationRegExp.SubexpIndex("varNameWithDefault")
|
||||
defaultValueGroupIndex = interpolationRegExp.SubexpIndex("defaultValue")
|
||||
)
|
||||
|
||||
func interpolate(str string, getValueFunc func(name string) string) string {
|
||||
for _, match := range interpolationRegExp.FindAllStringSubmatch(str, -1) {
|
||||
varName := match[varNameWithDefaultGroupIndex]
|
||||
if varName == "" {
|
||||
varName = match[varNameGroupIndex]
|
||||
}
|
||||
|
||||
if varName == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
defaultValue := ""
|
||||
if defaultValueGroupIndex < len(match) {
|
||||
defaultValue = match[defaultValueGroupIndex]
|
||||
}
|
||||
|
||||
str = getValueFunc(varName)
|
||||
if str == "" {
|
||||
str = defaultValue
|
||||
}
|
||||
}
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
func interpolateEnv(str string) string {
|
||||
return interpolate(str, os.Getenv)
|
||||
}
|
||||
|
||||
type InterpolatedString string
|
||||
|
||||
@ -21,11 +55,7 @@ func (is *InterpolatedString) UnmarshalYAML(value *yaml.Node) error {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
if match := reVar.FindStringSubmatch(str); len(match) > 0 {
|
||||
*is = InterpolatedString(os.Getenv(match[1]))
|
||||
} else {
|
||||
*is = InterpolatedString(str)
|
||||
}
|
||||
*is = InterpolatedString(interpolateEnv(str))
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -39,9 +69,7 @@ func (ii *InterpolatedInt) UnmarshalYAML(value *yaml.Node) error {
|
||||
return errors.Wrapf(err, "could not decode value '%v' (line '%d') into string", value.Value, value.Line)
|
||||
}
|
||||
|
||||
if match := reVar.FindStringSubmatch(str); len(match) > 0 {
|
||||
str = os.Getenv(match[1])
|
||||
}
|
||||
str = interpolateEnv(str)
|
||||
|
||||
intVal, err := strconv.ParseInt(str, 10, 32)
|
||||
if err != nil {
|
||||
@ -62,9 +90,7 @@ func (ib *InterpolatedBool) UnmarshalYAML(value *yaml.Node) error {
|
||||
return errors.Wrapf(err, "could not decode value '%v' (line '%d') into string", value.Value, value.Line)
|
||||
}
|
||||
|
||||
if match := reVar.FindStringSubmatch(str); len(match) > 0 {
|
||||
str = os.Getenv(match[1])
|
||||
}
|
||||
str = interpolateEnv(str)
|
||||
|
||||
boolVal, err := strconv.ParseBool(str)
|
||||
if err != nil {
|
||||
@ -91,9 +117,7 @@ func (im *InterpolatedMap) UnmarshalYAML(value *yaml.Node) error {
|
||||
continue
|
||||
}
|
||||
|
||||
if match := reVar.FindStringSubmatch(strVal); len(match) > 0 {
|
||||
strVal = os.Getenv(match[1])
|
||||
}
|
||||
strVal = interpolateEnv(strVal)
|
||||
|
||||
data[key] = strVal
|
||||
}
|
||||
@ -113,9 +137,7 @@ func (iss *InterpolatedStringSlice) UnmarshalYAML(value *yaml.Node) error {
|
||||
}
|
||||
|
||||
for index, value := range data {
|
||||
if match := reVar.FindStringSubmatch(value); len(match) > 0 {
|
||||
value = os.Getenv(match[1])
|
||||
}
|
||||
value = interpolateEnv(value)
|
||||
|
||||
data[index] = value
|
||||
}
|
||||
@ -134,9 +156,7 @@ func (id *InterpolatedDuration) UnmarshalYAML(value *yaml.Node) error {
|
||||
return errors.Wrapf(err, "could not decode value '%v' (line '%d') into string", value.Value, value.Line)
|
||||
}
|
||||
|
||||
if match := reVar.FindStringSubmatch(str); len(match) > 0 {
|
||||
str = os.Getenv(match[1])
|
||||
}
|
||||
str = interpolateEnv(str)
|
||||
|
||||
duration, err := time.ParseDuration(str)
|
||||
if err != nil {
|
||||
|
63
internal/config/environment_test.go
Normal file
63
internal/config/environment_test.go
Normal file
@ -0,0 +1,63 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type interpolateTestCase struct {
|
||||
String string
|
||||
Data map[string]string
|
||||
Expected string
|
||||
}
|
||||
|
||||
var interpolateTestCases = []interpolateTestCase{
|
||||
{
|
||||
String: "${foo}",
|
||||
Data: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
Expected: "bar",
|
||||
},
|
||||
{
|
||||
String: "${hello:-world}",
|
||||
Data: map[string]string{},
|
||||
Expected: "world",
|
||||
},
|
||||
{
|
||||
String: "${hello:-}",
|
||||
Data: map[string]string{},
|
||||
Expected: "${hello:-}",
|
||||
},
|
||||
{
|
||||
String: "foo",
|
||||
Data: map[string]string{},
|
||||
Expected: "foo",
|
||||
},
|
||||
{
|
||||
String: "",
|
||||
Data: map[string]string{},
|
||||
Expected: "",
|
||||
},
|
||||
}
|
||||
|
||||
func TestInterpolate(t *testing.T) {
|
||||
for idx, tc := range interpolateTestCases {
|
||||
func(idx int, tc interpolateTestCase) {
|
||||
t.Run(fmt.Sprintf("Case_%d", idx), func(t *testing.T) {
|
||||
result := interpolate(tc.String, func(name string) string {
|
||||
value, exists := tc.Data[name]
|
||||
if !exists {
|
||||
return ""
|
||||
}
|
||||
|
||||
return value
|
||||
})
|
||||
|
||||
if e, g := tc.Expected, result; e != g {
|
||||
t.Errorf("result: expected '%v', got '%v'", tc.Expected, result)
|
||||
}
|
||||
})
|
||||
}(idx, tc)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user