diff --git a/pkg/state/release.go b/pkg/state/release.go index c1b5c437..8f395215 100644 --- a/pkg/state/release.go +++ b/pkg/state/release.go @@ -193,13 +193,18 @@ func (r ReleaseSpec) ExecuteTemplateExpressions(renderer *tmpl.FileRenderer) (*R } result.SetValuesTemplate[i].File = s.String() } - for j, ts := range val.Values { + for j, tv := range val.Values { // values - s, err := renderer.RenderTemplateContentToBuffer([]byte(ts)) - if err != nil { - return nil, fmt.Errorf("failed executing template expressions in release \"%s\".set[%d].values[%d] = \"%s\": %v", r.Name, i, j, ts, err) + switch ts := tv.(type) { + case string: + s, err := renderer.RenderTemplateContentToBuffer([]byte(ts)) + if err != nil { + return nil, fmt.Errorf("failed executing template expressions in release \"%s\".set[%d].values[%d] = \"%s\": %v", r.Name, i, j, ts, err) + } + result.SetValuesTemplate[i].Values[j] = s.String() + default: + result.SetValuesTemplate[i].Values[j] = ts } - result.SetValuesTemplate[i].Values[j] = s.String() } } diff --git a/pkg/state/state.go b/pkg/state/state.go index b5e027ce..c4e7c5d2 100644 --- a/pkg/state/state.go +++ b/pkg/state/state.go @@ -576,10 +576,10 @@ type Release struct { // SetValue are the key values to set on a helm release type SetValue struct { - Name string `yaml:"name,omitempty"` - Value string `yaml:"value,omitempty"` - File string `yaml:"file,omitempty"` - Values []string `yaml:"values,omitempty"` + Name string `yaml:"name,omitempty"` + Value string `yaml:"value,omitempty"` + File string `yaml:"file,omitempty"` + Values []any `yaml:"values,omitempty"` } // AffectedReleases hold the list of released that where updated, deleted, or in error @@ -4570,7 +4570,7 @@ func (st *HelmState) setFlags(setValues []SetValue) ([]string, error) { } else if set.File != "" { flags = append(flags, "--set-file", fmt.Sprintf("%s=%s", escape(set.Name), st.storage().normalizeSetFilePath(set.File, runtime.GOOS))) } else if len(set.Values) > 0 { - renderedValues, err := renderValsSecrets(st.valsRuntime, set.Values...) + renderedValues, err := renderValsSecretsAny(st.valsRuntime, set.Values) if err != nil { return nil, err } @@ -4598,7 +4598,7 @@ func (st *HelmState) setStringFlags(setValues []SetValue) ([]string, error) { } flags = append(flags, "--set-string", fmt.Sprintf("%s=%s", escape(set.Name), escape(renderedValue[0]))) } else if len(set.Values) > 0 { - renderedValues, err := renderValsSecrets(st.valsRuntime, set.Values...) + renderedValues, err := renderValsSecretsAny(st.valsRuntime, set.Values) if err != nil { return nil, err } @@ -4635,6 +4635,43 @@ func renderValsSecrets(e vals.Evaluator, input ...string) ([]string, error) { return output, nil } +// renderValsSecretsAny renders 'ref+.*' secrets in a slice of any-typed values. +// Map values are serialized to JSON; string values are rendered via vals. +func renderValsSecretsAny(e vals.Evaluator, input []any) ([]string, error) { + output := make([]string, len(input)) + if len(input) == 0 { + return output, nil + } + + strInputs := make([]string, 0, len(input)) + strIndexMap := make([]int, 0, len(input)) + for i, v := range input { + switch tv := v.(type) { + case string: + strInputs = append(strInputs, tv) + strIndexMap = append(strIndexMap, i) + default: + jsonBytes, err := json.Marshal(tv) + if err != nil { + return nil, fmt.Errorf("failed to marshal set value at index %d: %w", i, err) + } + output[i] = string(jsonBytes) + } + } + + if len(strInputs) > 0 { + rendered, err := renderValsSecrets(e, strInputs...) + if err != nil { + return nil, err + } + for idx, renderedIdx := range strIndexMap { + output[renderedIdx] = rendered[idx] + } + } + + return output, nil +} + func hideChartCredentials(chartCredentials string) (string, error) { u, err := url.Parse(chartCredentials) if err != nil { diff --git a/pkg/state/state_exec_tmpl_test.go b/pkg/state/state_exec_tmpl_test.go index 92ec36df..01956a0f 100644 --- a/pkg/state/state_exec_tmpl_test.go +++ b/pkg/state/state_exec_tmpl_test.go @@ -96,7 +96,7 @@ func TestHelmState_executeTemplates(t *testing.T) { SetValuesTemplate: []SetValue{ {Name: "val1", Value: "{{ .Release.Name }}-val1"}, {Name: "val2", File: "{{ .Release.Name }}.yml"}, - {Name: "val3", Values: []string{"{{ .Release.Name }}-val2", "{{ .Release.Name }}-val3"}}, + {Name: "val3", Values: []any{"{{ .Release.Name }}-val2", "{{ .Release.Name }}-val3"}}, {Name: "val4", Value: "{{ .Release.Chart }}-{{ .Release.ChartVersion}}"}, }, }, @@ -108,7 +108,7 @@ func TestHelmState_executeTemplates(t *testing.T) { SetValues: []SetValue{ {Name: "val1", Value: "test-app-val1"}, {Name: "val2", File: "test-app.yml"}, - {Name: "val3", Values: []string{"test-app-val2", "test-app-val3"}}, + {Name: "val3", Values: []any{"test-app-val2", "test-app-val3"}}, {Name: "val4", Value: "test-charts/chart-1.5"}, }, }, diff --git a/pkg/state/state_test.go b/pkg/state/state_test.go index d188cd57..be883d32 100644 --- a/pkg/state/state_test.go +++ b/pkg/state/state_test.go @@ -2096,7 +2096,7 @@ func TestHelmState_SyncReleases(t *testing.T) { SetValues: []SetValue{ { Name: "foo.bar[0]", - Values: []string{ + Values: []any{ "A", "B", }, @@ -2107,6 +2107,26 @@ func TestHelmState_SyncReleases(t *testing.T) { helm: &exectest.Helm{}, wantReleases: []exectest.Release{{Name: "releaseName", Flags: []string{"--set", "foo.bar[0]={A,B}", "--reset-values"}}}, }, + { + name: "set array of map values", + releases: []ReleaseSpec{ + { + Name: "releaseName", + Chart: "foo", + SetValues: []SetValue{ + { + Name: "source.helm.parameters", + Values: []any{ + map[string]any{"name": "demo"}, + map[string]any{"version": "v2"}, + }, + }, + }, + }, + }, + helm: &exectest.Helm{}, + wantReleases: []exectest.Release{{Name: "releaseName", Flags: []string{"--set", "source.helm.parameters={\\{\"name\":\"demo\"\\},\\{\"version\":\"v2\"\\}}", "--reset-values"}}}, + }, { name: "post renderer helm 3", releases: []ReleaseSpec{ @@ -2709,7 +2729,7 @@ func TestHelmState_DiffReleases(t *testing.T) { SetValues: []SetValue{ { Name: "foo.bar[0]", - Values: []string{ + Values: []any{ "A", "B", }, @@ -5698,7 +5718,7 @@ func TestHelmState_setStringFlags(t *testing.T) { setStringValues: []SetValue{ { Name: "key", - Values: []string{"value1", "value2"}, + Values: []any{"value1", "value2"}, }, }, want: []string{"--set-string", "key={value1,value2}"},