diff --git a/cmd/root.go b/cmd/root.go index 11038b3..cbfd1ab 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -44,7 +44,7 @@ func Execute(version string) { } func loadConfig() (*c.Config, error) { - params := c.LoadParam{ + params := c.LoadConfigInput{ Path: pathToConfig, Stage: stage, } diff --git a/config/config.go b/config/config.go index f78cbc1..1d8cd95 100644 --- a/config/config.go +++ b/config/config.go @@ -1,7 +1,9 @@ package config import ( + "bytes" "fmt" + "html/template" "io/ioutil" "github.com/adikari/safebox/v2/store" @@ -24,12 +26,12 @@ type Config struct { Secrets []store.ConfigInput } -type LoadParam struct { +type LoadConfigInput struct { Path string Stage string } -func Load(param LoadParam) (*Config, error) { +func Load(param LoadConfigInput) (*Config, error) { yamlFile, err := ioutil.ReadFile(param.Path) if err != nil { @@ -61,11 +63,16 @@ func Load(param LoadParam) (*Config, error) { return &c, nil } -func parseConfig(rc rawConfig, c *Config, param LoadParam) { +func parseConfig(rc rawConfig, c *Config, param LoadConfigInput) { + variables := map[string]string{ + "stage": param.Stage, + "service": c.Service, + } + for key, value := range rc.Config["defaults"] { c.Configs = append(c.Configs, store.ConfigInput{ Name: formatPath(param.Stage, c.Service, key), - Value: value, + Value: interpolate(value, variables), Secret: false, }) } @@ -73,7 +80,7 @@ func parseConfig(rc rawConfig, c *Config, param LoadParam) { for key, value := range rc.Config["shared"] { c.Configs = append(c.Configs, store.ConfigInput{ Name: formatSharedPath(param.Stage, key), - Value: value, + Value: interpolate(value, variables), Secret: false, }) } @@ -81,11 +88,13 @@ func parseConfig(rc rawConfig, c *Config, param LoadParam) { for key, value := range rc.Config[param.Stage] { c.Configs = append(c.Configs, store.ConfigInput{ Name: formatPath(param.Stage, c.Service, key), - Value: value, + Value: interpolate(value, variables), Secret: false, }) } + c.Configs = removeDuplicate(c.Configs) + for key, value := range rc.Secret["defaults"] { c.Secrets = append(c.Secrets, store.ConfigInput{ Name: formatPath(param.Stage, c.Service, key), @@ -112,3 +121,28 @@ func formatSharedPath(stage string, key string) string { func formatPath(stage string, service string, key string) string { return fmt.Sprintf("/%s/%s/%s", stage, service, key) } + +func interpolate(value string, variables map[string]string) string { + var result bytes.Buffer + tmpl, _ := template.New("interpolate").Parse(value) + + tmpl.Execute(&result, variables) + return result.String() +} + +func removeDuplicate(input []store.ConfigInput) []store.ConfigInput { + var unique []store.ConfigInput + +loop: + for _, v := range input { + for i, u := range unique { + if v.Name == u.Name { + unique[i] = v + continue loop + } + } + unique = append(unique, v) + } + + return unique +} diff --git a/example/safebox.yml b/example/safebox.yml index 9577772..bc2238e 100644 --- a/example/safebox.yml +++ b/example/safebox.yml @@ -6,13 +6,13 @@ config: DB_NAME: "database name updated" API_ENDPOINT: "endpoint" NEW: "endpoint" - NEW2: "endpoint" + NEW2: "endpoint updated" prod: DB_NAME: "production db name" dev: - DB_NAME: "dev db name" + DB_NAME: "dev db name {{.stage}}" shared: SHARED_KEY: "shared key"