diff --git a/pkg/app/desired_state_file_loader.go b/pkg/app/desired_state_file_loader.go index be176bf9..c81e770b 100644 --- a/pkg/app/desired_state_file_loader.go +++ b/pkg/app/desired_state_file_loader.go @@ -55,7 +55,7 @@ func (ld *desiredStateLoader) Load(f string, opts LoadOpts) (*state.HelmState, e storage := state.NewStorage(opts.CalleePath, ld.logger, ld.fs) envld := state.NewEnvironmentValuesLoader(storage, ld.fs, ld.logger, ld.remote) handler := state.MissingFileHandlerError - vals, err := envld.LoadEnvironmentValues(&handler, args, environment.New(ld.env), ld.env) + vals, err := envld.LoadEnvironmentValues(&handler, args, environment.New(ld.env), ld.env, nil) if err != nil { return nil, err } diff --git a/pkg/state/create.go b/pkg/state/create.go index 606134fd..a984cbdf 100644 --- a/pkg/state/create.go +++ b/pkg/state/create.go @@ -460,7 +460,7 @@ func (st *HelmState) loadValuesEntries(missingFileHandler *string, entries []any valuesEntries := append([]any{}, entries...) ld := NewEnvironmentValuesLoader(st.storage(), st.fs, st.logger, remote) var err error - envVals, err = ld.LoadEnvironmentValues(missingFileHandler, valuesEntries, ctxEnv, envName) + envVals, err = ld.LoadEnvironmentValues(missingFileHandler, valuesEntries, ctxEnv, envName, st.Features) if err != nil { return nil, err } diff --git a/pkg/state/envvals_loader.go b/pkg/state/envvals_loader.go index 731cc70d..9292933a 100644 --- a/pkg/state/envvals_loader.go +++ b/pkg/state/envvals_loader.go @@ -3,6 +3,7 @@ package state import ( "fmt" "path/filepath" + "slices" "strings" "dario.cat/mergo" @@ -36,13 +37,15 @@ func NewEnvironmentValuesLoader(storage *Storage, fs *filesystem.FileSystem, log } } -func (ld *EnvironmentValuesLoader) LoadEnvironmentValues(missingFileHandler *string, valuesEntries []any, ctxEnv *environment.Environment, envName string) (map[string]any, error) { +func (ld *EnvironmentValuesLoader) LoadEnvironmentValues(missingFileHandler *string, valuesEntries []any, ctxEnv *environment.Environment, envName string, features []string) (map[string]any, error) { var ( result = map[string]any{} hclLoader = hcllang.NewHCLLoader(ld.fs, ld.logger) err error ) + layeredValues := slices.Contains(features, FeatureLayeredEnvironmentValues) + for _, entry := range valuesEntries { maps := []any{} @@ -65,7 +68,14 @@ func (ld *EnvironmentValuesLoader) LoadEnvironmentValues(missingFileHandler *str if strings.HasSuffix(f, ".hcl") { hclLoader.AddFile(f) } else { - tmplData := NewEnvironmentTemplateData(env, "", env.Values) + values := env.Values + if layeredValues { + values, err = mapMerge(values, []any{result}) + if err != nil { + return nil, err + } + } + tmplData := NewEnvironmentTemplateData(env, "", values) r := tmpl.NewFileRenderer(ld.fs, filepath.Dir(f), tmplData) bytes, err := r.RenderToBytes(f) if err != nil { diff --git a/pkg/state/envvals_loader_test.go b/pkg/state/envvals_loader_test.go index 764ada78..02ad2e4c 100644 --- a/pkg/state/envvals_loader_test.go +++ b/pkg/state/envvals_loader_test.go @@ -29,7 +29,7 @@ func newLoader() *EnvironmentValuesLoader { func TestEnvValsLoad_SingleValuesFile(t *testing.T) { l := newLoader() - actual, err := l.LoadEnvironmentValues(nil, []any{"testdata/values.5.yaml"}, nil, "") + actual, err := l.LoadEnvironmentValues(nil, []any{"testdata/values.5.yaml"}, nil, "", nil) if err != nil { t.Fatal(err) } @@ -87,7 +87,7 @@ func TestEnvValsLoad_EnvironmentNameFile(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - actual, err := l.LoadEnvironmentValues(nil, []any{"testdata/values.6.yaml.gotmpl"}, tt.env, tt.envName) + actual, err := l.LoadEnvironmentValues(nil, []any{"testdata/values.6.yaml.gotmpl"}, tt.env, tt.envName, nil) if err != nil { t.Fatal(err) } @@ -103,7 +103,7 @@ func TestEnvValsLoad_EnvironmentNameFile(t *testing.T) { func TestEnvValsLoad_SingleValuesFileRemote(t *testing.T) { l := newLoader() - actual, err := l.LoadEnvironmentValues(nil, []any{"git::https://github.com/helm/helm.git@cmd/helm/testdata/output/values.yaml?ref=v3.8.1"}, nil, "") + actual, err := l.LoadEnvironmentValues(nil, []any{"git::https://github.com/helm/helm.git@cmd/helm/testdata/output/values.yaml?ref=v3.8.1"}, nil, "", nil) if err != nil { t.Fatal(err) } @@ -121,7 +121,7 @@ func TestEnvValsLoad_SingleValuesFileRemote(t *testing.T) { func TestEnvValsLoad_OverwriteNilValue_Issue1150(t *testing.T) { l := newLoader() - actual, err := l.LoadEnvironmentValues(nil, []any{"testdata/values.1.yaml", "testdata/values.2.yaml"}, nil, "") + actual, err := l.LoadEnvironmentValues(nil, []any{"testdata/values.1.yaml", "testdata/values.2.yaml"}, nil, "", nil) if err != nil { t.Fatal(err) } @@ -143,7 +143,7 @@ func TestEnvValsLoad_OverwriteNilValue_Issue1150(t *testing.T) { func TestEnvValsLoad_OverwriteWithNilValue_Issue1154(t *testing.T) { l := newLoader() - actual, err := l.LoadEnvironmentValues(nil, []any{"testdata/values.3.yaml", "testdata/values.4.yaml"}, nil, "") + actual, err := l.LoadEnvironmentValues(nil, []any{"testdata/values.3.yaml", "testdata/values.4.yaml"}, nil, "", nil) if err != nil { t.Fatal(err) } @@ -166,7 +166,7 @@ func TestEnvValsLoad_OverwriteWithNilValue_Issue1154(t *testing.T) { func TestEnvValsLoad_OverwriteEmptyValue_Issue1168(t *testing.T) { l := newLoader() - actual, err := l.LoadEnvironmentValues(nil, []any{"testdata/issues/1168/addons.yaml", "testdata/issues/1168/addons2.yaml"}, nil, "") + actual, err := l.LoadEnvironmentValues(nil, []any{"testdata/issues/1168/addons.yaml", "testdata/issues/1168/addons2.yaml"}, nil, "", nil) if err != nil { t.Fatal(err) } @@ -191,7 +191,7 @@ func TestEnvValsLoad_OverwriteEmptyValue_Issue1168(t *testing.T) { func TestEnvValsLoad_MultiHCL(t *testing.T) { l := newLoader() - actual, err := l.LoadEnvironmentValues(nil, []any{"testdata/values.7.hcl", "testdata/values.8.hcl"}, nil, "") + actual, err := l.LoadEnvironmentValues(nil, []any{"testdata/values.7.hcl", "testdata/values.8.hcl"}, nil, "", nil) if err != nil { t.Fatal(err) } @@ -234,7 +234,7 @@ func TestEnvValsLoad_EnvironmentValues(t *testing.T) { env := environment.New("test") env.Values["foo"] = "bar" - actual, err := l.LoadEnvironmentValues(nil, []any{"testdata/values.9.yaml.gotmpl"}, env, "") + actual, err := l.LoadEnvironmentValues(nil, []any{"testdata/values.9.yaml.gotmpl"}, env, "", nil) if err != nil { t.Fatal(err) } @@ -247,3 +247,22 @@ func TestEnvValsLoad_EnvironmentValues(t *testing.T) { t.Error(diff) } } + +func TestEnvValsLoad_LayeredValues(t *testing.T) { + l := newLoader() + + actual, err := l.LoadEnvironmentValues(nil, []any{"testdata/layered.1.yaml", "testdata/layered.2.yaml.gotmpl", "testdata/layered.3.yaml.gotmpl"}, nil, "", []string{FeatureLayeredEnvironmentValues}) + if err != nil { + t.Fatal(err) + } + + expected := map[string]any{ + "somevalue": string("foo"), + "someothervalue": string("new foo"), + "greeting": string("hello new foo"), + } + + if diff := cmp.Diff(expected, actual); diff != "" { + t.Error(diff) + } +} diff --git a/pkg/state/features.go b/pkg/state/features.go new file mode 100644 index 00000000..bd3bdc64 --- /dev/null +++ b/pkg/state/features.go @@ -0,0 +1,6 @@ +package state + +const ( + // FeatureLayeredEnvironmentValues is the feature flag for layered environment values + FeatureLayeredEnvironmentValues = "layeredEnvironmentValues" +) diff --git a/pkg/state/state.go b/pkg/state/state.go index d8e4013c..261b4a4f 100644 --- a/pkg/state/state.go +++ b/pkg/state/state.go @@ -89,6 +89,8 @@ type ReleaseSetSpec struct { MissingFileHandlerConfig *MissingFileHandlerConfig `yaml:"missingFileHandlerConfig,omitempty"` LockFile string `yaml:"lockFilePath,omitempty"` + + Features []string `yaml:"features,omitempty"` } type MissingFileHandlerConfig struct { diff --git a/pkg/state/testdata/layered.1.yaml b/pkg/state/testdata/layered.1.yaml new file mode 100644 index 00000000..29cd3cbb --- /dev/null +++ b/pkg/state/testdata/layered.1.yaml @@ -0,0 +1,2 @@ +somevalue: foo +greeting: hello diff --git a/pkg/state/testdata/layered.2.yaml.gotmpl b/pkg/state/testdata/layered.2.yaml.gotmpl new file mode 100644 index 00000000..3f7744c0 --- /dev/null +++ b/pkg/state/testdata/layered.2.yaml.gotmpl @@ -0,0 +1 @@ +someothervalue: "new {{ .Values.somevalue }}" diff --git a/pkg/state/testdata/layered.3.yaml.gotmpl b/pkg/state/testdata/layered.3.yaml.gotmpl new file mode 100644 index 00000000..1f377717 --- /dev/null +++ b/pkg/state/testdata/layered.3.yaml.gotmpl @@ -0,0 +1 @@ +greeting: "{{ .Values.greeting }} {{ .Values.someothervalue }}"