Skip to content

Commit 1110eb1

Browse files
authored
Allow deeply nested property definition for Helm properties (#4511)
* Allow deeply nested property definition for Helm properties like `ArtifactOverrides`, `SetValues` and `SetValueTemplates`
1 parent a988b46 commit 1110eb1

File tree

5 files changed

+89
-23
lines changed

5 files changed

+89
-23
lines changed

docs/content/en/schemas/v2beta6.json

+3-18
Original file line numberDiff line numberDiff line change
@@ -1243,13 +1243,8 @@
12431243
],
12441244
"properties": {
12451245
"artifactOverrides": {
1246-
"additionalProperties": {
1247-
"type": "string"
1248-
},
1249-
"type": "object",
12501246
"description": "key value pairs. If present, Skaffold will send `--set-string` flag to Helm CLI and append all pairs after the flag.",
1251-
"x-intellij-html-description": "key value pairs. If present, Skaffold will send <code>--set-string</code> flag to Helm CLI and append all pairs after the flag.",
1252-
"default": "{}"
1247+
"x-intellij-html-description": "key value pairs. If present, Skaffold will send <code>--set-string</code> flag to Helm CLI and append all pairs after the flag."
12531248
},
12541249
"chartPath": {
12551250
"type": "string",
@@ -1302,22 +1297,12 @@
13021297
"default": "{}"
13031298
},
13041299
"setValueTemplates": {
1305-
"additionalProperties": {
1306-
"type": "string"
1307-
},
1308-
"type": "object",
13091300
"description": "key-value pairs. If present, Skaffold will try to parse the value part of each key-value pair using environment variables in the system, then send `--set` flag to Helm CLI and append all parsed pairs after the flag.",
1310-
"x-intellij-html-description": "key-value pairs. If present, Skaffold will try to parse the value part of each key-value pair using environment variables in the system, then send <code>--set</code> flag to Helm CLI and append all parsed pairs after the flag.",
1311-
"default": "{}"
1301+
"x-intellij-html-description": "key-value pairs. If present, Skaffold will try to parse the value part of each key-value pair using environment variables in the system, then send <code>--set</code> flag to Helm CLI and append all parsed pairs after the flag."
13121302
},
13131303
"setValues": {
1314-
"additionalProperties": {
1315-
"type": "string"
1316-
},
1317-
"type": "object",
13181304
"description": "key-value pairs. If present, Skaffold will send `--set` flag to Helm CLI and append all pairs after the flag.",
1319-
"x-intellij-html-description": "key-value pairs. If present, Skaffold will send <code>--set</code> flag to Helm CLI and append all pairs after the flag.",
1320-
"default": "{}"
1305+
"x-intellij-html-description": "key-value pairs. If present, Skaffold will send <code>--set</code> flag to Helm CLI and append all pairs after the flag."
13211306
},
13221307
"skipBuildDependencies": {
13231308
"type": "boolean",

integration/examples/helm-deployment-dependencies/skaffold.yaml

+4-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ deploy:
1616
skipBuildDependencies: true # Skip helm dep build
1717
artifactOverrides:
1818
image: skaffold-helm
19-
"skaffold-helm-subchart.image": skaffold-helm
19+
skaffold-helm-subchart:
20+
image: skaffold-helm
2021
#recreatePods will pass --recreate-pods to helm upgrade
2122
#recreatePods: true
2223
#overrides builds an override values.yaml file to run with the helm deploy
@@ -25,4 +26,5 @@ deploy:
2526
# key: someValue
2627
#setValues get appended to the helm deploy with --set.
2728
#setValues:
28-
#some.key: someValue
29+
# some:
30+
# key: someValue

pkg/skaffold/schema/latest/config.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -510,7 +510,7 @@ type HelmRelease struct {
510510

511511
// ArtifactOverrides are key value pairs.
512512
// If present, Skaffold will send `--set-string` flag to Helm CLI and append all pairs after the flag.
513-
ArtifactOverrides map[string]string `yaml:"artifactOverrides,omitempty,omitempty"`
513+
ArtifactOverrides util.FlatMap `yaml:"artifactOverrides,omitempty,omitempty"`
514514

515515
// Namespace is the Kubernetes namespace.
516516
Namespace string `yaml:"namespace,omitempty"`
@@ -520,13 +520,13 @@ type HelmRelease struct {
520520

521521
// SetValues are key-value pairs.
522522
// If present, Skaffold will send `--set` flag to Helm CLI and append all pairs after the flag.
523-
SetValues map[string]string `yaml:"setValues,omitempty"`
523+
SetValues util.FlatMap `yaml:"setValues,omitempty"`
524524

525525
// SetValueTemplates are key-value pairs.
526526
// If present, Skaffold will try to parse the value part of each key-value pair using
527527
// environment variables in the system, then send `--set` flag to Helm CLI and append
528528
// all parsed pairs after the flag.
529-
SetValueTemplates map[string]string `yaml:"setValueTemplates,omitempty"`
529+
SetValueTemplates util.FlatMap `yaml:"setValueTemplates,omitempty"`
530530

531531
// SetFiles are key-value pairs.
532532
// If present, Skaffold will send `--set-file` flag to Helm CLI and append all pairs after the flag.

pkg/skaffold/schema/util/util.go

+42
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package util
1818

1919
import (
2020
"encoding/json"
21+
"fmt"
2122
"reflect"
2223
"strings"
2324

@@ -36,6 +37,9 @@ type HelmOverrides struct {
3637
Values map[string]interface{} `yaml:",inline"`
3738
}
3839

40+
// FlatMap flattens deeply nested yaml into a map with corresponding dot separated keys
41+
type FlatMap map[string]string
42+
3943
// MarshalJSON implements JSON marshalling by including the value as an inline yaml fragment.
4044
func (h *HelmOverrides) MarshalJSON() ([]byte, error) {
4145
return marshalInlineYaml(h)
@@ -82,6 +86,44 @@ func (n *YamlpatchNode) UnmarshalYAML(unmarshal func(interface{}) error) error {
8286
return n.Node.UnmarshalYAML(unmarshal)
8387
}
8488

89+
func (m *FlatMap) UnmarshalYAML(unmarshal func(interface{}) error) error {
90+
var obj map[string]interface{}
91+
if err := unmarshal(&obj); err != nil {
92+
return err
93+
}
94+
result := make(map[string]string)
95+
if err := buildFlatMap(obj, result, ""); err != nil {
96+
return err
97+
}
98+
*m = result
99+
return nil
100+
}
101+
102+
func buildFlatMap(obj map[string]interface{}, result map[string]string, currK string) (err error) {
103+
var prevK string
104+
for k, v := range obj {
105+
prevK = currK
106+
if currK == "" {
107+
currK = fmt.Sprintf("%v", k)
108+
} else {
109+
currK = fmt.Sprintf("%v.%v", currK, k)
110+
}
111+
112+
switch v := v.(type) {
113+
case map[string]interface{}:
114+
if err = buildFlatMap(v, result, currK); err != nil {
115+
return
116+
}
117+
case string:
118+
result[currK] = v
119+
default:
120+
result[currK] = fmt.Sprintf("%v", v)
121+
}
122+
currK = prevK
123+
}
124+
return
125+
}
126+
85127
func marshalInlineYaml(in interface{}) ([]byte, error) {
86128
yaml, err := yaml.Marshal(in)
87129
if err != nil {

pkg/skaffold/schema/util/util_test.go

+37
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,40 @@ func TestYamlpatchNodeWhenEmbedded(t *testing.T) {
9090
localstack: {}
9191
`, string(out))
9292
}
93+
94+
func TestFlatMap_UnmarshalYAML(t *testing.T) {
95+
y1 := `val1: foo1
96+
val2:
97+
val3: bar1
98+
val4: foo2
99+
val5:
100+
val6: bar2
101+
`
102+
y2 := `val1: foo1
103+
val2.val3: bar1
104+
val2.val4: foo2
105+
val2.val5.val6: bar2
106+
`
107+
108+
f1 := &FlatMap{}
109+
f2 := &FlatMap{}
110+
111+
err := yaml.Unmarshal([]byte(y1), &f1)
112+
testutil.CheckError(t, false, err)
113+
114+
err = yaml.Unmarshal([]byte(y2), &f2)
115+
testutil.CheckError(t, false, err)
116+
117+
testutil.CheckDeepEqual(t, *f1, *f2)
118+
119+
out, err := yaml.Marshal(struct {
120+
M *FlatMap `yaml:"value,omitempty"`
121+
}{f1})
122+
123+
testutil.CheckErrorAndDeepEqual(t, false, err, `value:
124+
val1: foo1
125+
val2.val3: bar1
126+
val2.val4: foo2
127+
val2.val5.val6: bar2
128+
`, string(out))
129+
}

0 commit comments

Comments
 (0)