From 0e81899f5f4f95fcef385bf84fee1edb288abaec Mon Sep 17 00:00:00 2001 From: Justin Scott Date: Tue, 14 Mar 2017 11:04:33 -0700 Subject: [PATCH 01/10] WIP feat(helm): import child values to parent Implements a mechanism in requirements.yaml to allow the import and re-parenting of value table from child chart. Closes #1995 --- pkg/chartutil/requirements.go | 126 ++++++++++++++++++ pkg/chartutil/requirements_test.go | 57 ++++++++ .../subchart1/charts/subchartA/values.yaml | 12 +- .../subpop/charts/subchart1/requirements.yaml | 6 + .../subpop/charts/subchart1/values.yaml | 11 +- .../testdata/subpop/requirements.yaml | 5 + pkg/chartutil/testdata/subpop/values.yaml | 14 ++ pkg/helm/client.go | 8 ++ pkg/resolver/resolver_test.go | 2 +- 9 files changed, 232 insertions(+), 9 deletions(-) diff --git a/pkg/chartutil/requirements.go b/pkg/chartutil/requirements.go index 3a31042d6..326afb36c 100644 --- a/pkg/chartutil/requirements.go +++ b/pkg/chartutil/requirements.go @@ -62,6 +62,8 @@ type Dependency struct { Tags []string `json:"tags"` // Enabled bool determines if chart should be loaded Enabled bool `json:"enabled"` + // ImportValues holds the mapping of source values to parent key to be imported + ImportValues []interface{} `json:"import-values"` } // ErrNoRequirementsFile to detect error condition @@ -266,3 +268,127 @@ func ProcessRequirementsEnabled(c *chart.Chart, v *chart.Config) error { return nil } + +// pathToMap creates a nested map given a YAML path in dot notation +func pathToMap(path string, data map[string]interface{}) map[string]interface{} { + ap := strings.Split(path, ".") + n := []map[string]interface{}{} + for _, v := range ap { + nm := make(map[string]interface{}) + nm[v] = make(map[string]interface{}) + n = append(n, nm) + } + for i, d := range n { + for k := range d { + z := i + 1 + if z == len(n) { + n[i][k] = data + break + } + n[i][k] = n[z] + } + } + + return n[0] +} + +// getParents returns a slice of parent charts in reverse order +func getParents(c *chart.Chart, out []*chart.Chart) []*chart.Chart { + if len(out) == 0 { + out = []*chart.Chart{} + out = append(out, c) + } + for _, ch := range c.Dependencies { + if len(ch.Dependencies) > 0 { + out = append(out, ch) + out = getParents(ch, out) + } + } + + return out +} + +// processImportValues merges values from child to parent based on ImportValues field +func processImportValues(c *chart.Chart, v *chart.Config) error { + reqs, err := LoadRequirements(c) + if err != nil { + log.Printf("Warning: ImportValues cannot load requirements for %s", c.Metadata.Name) + return nil + } + cvals, err := CoalesceValues(c, v) + nv := v.GetValues() + b := make(map[string]interface{}) + for kk, v3 := range nv { + b[kk] = v3 + } + for _, r := range reqs.Dependencies { + if len(r.ImportValues) > 0 { + var outiv []interface{} + for _, riv := range r.ImportValues { + switch tr := riv.(type) { + case map[string]interface{}: + if m, ok := riv.(map[string]interface{}); ok { + nm := make(map[string]string) + nm["child"] = m["child"].(string) + nm["parent"] = m["parent"].(string) + outiv = append(outiv, nm) + s := r.Name + "." + nm["child"] + vv, err := cvals.Table(s) + if err != nil { + log.Printf("Warning: ImportValues missing table %v", err) + continue + } + if nm["parent"] == "." { + coalesceTables(b, vv.AsMap()) + } else { + + vm := pathToMap(nm["parent"], vv.AsMap()) + coalesceTables(b, vm) + } + } + case string: + log.Printf("its a string %v", tr) + // todo validation + nm := make(map[string]string) + nm["child"] = riv.(string) + nm["parent"] = "." + outiv = append(outiv, nm) + s := r.Name + "." + nm["child"] + vv, err := cvals.Table(s) + if err != nil { + log.Printf("Warning: ImportValues missing table %v", err) + continue + } + coalesceTables(b, vv.AsMap()) + } + } + // set our formatted import values + r.ImportValues = outiv + } + } + + cv, err := coalesceValues(c, b) + if err != nil { + log.Fatalf("Error coalescing values for ImportValues %s", err) + } + y, err := yaml.Marshal(cv) + if err != nil { + log.Printf("Warning: ImportValues could not marshall %v", err) + } + bb := &chart.Config{Raw: string(y)} + v = bb + c.Values = bb + + return nil +} + +// ProcessRequirementsImportValues imports specified chart values from child to parent +func ProcessRequirementsImportValues(c *chart.Chart, v *chart.Config) error { + pc := getParents(c, nil) + for i := len(pc) - 1; i >= 0; i-- { + processImportValues(pc[i], v) + + } + + return nil +} diff --git a/pkg/chartutil/requirements_test.go b/pkg/chartutil/requirements_test.go index b9a5ae12a..21268df9d 100644 --- a/pkg/chartutil/requirements_test.go +++ b/pkg/chartutil/requirements_test.go @@ -18,6 +18,8 @@ import ( "sort" "testing" + "strconv" + "k8s.io/helm/pkg/proto/hapi/chart" ) @@ -206,3 +208,58 @@ func extractCharts(c *chart.Chart, out []*chart.Chart) []*chart.Chart { } return out } +func TestProcessRequirementsImportValues(t *testing.T) { + c, err := Load("testdata/subpop") + if err != nil { + t.Fatalf("Failed to load testdata: %s", err) + } + + v := &chart.Config{Raw: ""} + + e := make(map[string]string) + e["imported-from-chart1.type"] = "ClusterIP" + e["imported-from-chart1.name"] = "nginx" + e["imported-from-chart1.externalPort"] = "80" + // this doesn't exist in imported table. it should merge and remain unchanged + e["imported-from-chart1.notimported1"] = "1" + e["imported-from-chartA-via-chart1.limits.cpu"] = "300m" + e["imported-from-chartA-via-chart1.limits.memory"] = "300Mi" + e["imported-from-chartA-via-chart1.limits.volume"] = "11" + e["imported-from-chartA-via-chart1.requests.truthiness"] = "0.01" + + verifyRequirementsImportValues(t, c, v, e) +} +func verifyRequirementsImportValues(t *testing.T, c *chart.Chart, v *chart.Config, e map[string]string) { + + err := ProcessRequirementsImportValues(c, v) + if err != nil { + t.Errorf("Error processing import values requirements %v", err) + } + cv := c.GetValues() + cc, err := ReadValues([]byte(cv.Raw)) + if err != nil { + t.Errorf("Error reading import values %v", err) + } + for kk, vv := range e { + pv, err := cc.PathValue(kk) + if err != nil { + t.Fatalf("Error retrieving import values table %v %v", kk, err) + return + } + + switch pv.(type) { + case float64: + s := strconv.FormatFloat(pv.(float64), 'f', -1, 64) + if s != vv { + t.Errorf("Failed to match imported float value %v with expected %v", s, vv) + return + } + default: + if pv.(string) != vv { + t.Errorf("Failed to match imported string value %v with expected %v", pv, vv) + return + } + } + + } +} diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartA/values.yaml b/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartA/values.yaml index 5e5b21065..8c348e86c 100644 --- a/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartA/values.yaml +++ b/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartA/values.yaml @@ -1,6 +1,7 @@ # Default values for subchart. # This is a YAML-formatted file. # Declare variables to be passed into your templates. +# subchartA replicaCount: 1 image: repository: nginx @@ -13,9 +14,12 @@ service: internalPort: 80 resources: limits: - cpu: 100m - memory: 128Mi + cpu: 300m + memory: 300Mi + plasticity: 1.7331 + volume: 11 requests: - cpu: 100m - memory: 128Mi + cpu: 350m + memory: 350Mi + truthiness: 0.01 diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/requirements.yaml b/pkg/chartutil/testdata/subpop/charts/subchart1/requirements.yaml index 94d278234..5adf2f10a 100644 --- a/pkg/chartutil/testdata/subpop/charts/subchart1/requirements.yaml +++ b/pkg/chartutil/testdata/subpop/charts/subchart1/requirements.yaml @@ -6,6 +6,12 @@ dependencies: tags: - front-end - subcharta + import-values: + - child: resources.limits + parent: imported-from-chartA.limits + - child: resources.requests + parent: imported-from-chartA.requests + - name: subchartb repository: http://localhost:10191 version: 0.1.0 diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/values.yaml b/pkg/chartutil/testdata/subpop/charts/subchart1/values.yaml index 5e5b21065..4c5085d82 100644 --- a/pkg/chartutil/testdata/subpop/charts/subchart1/values.yaml +++ b/pkg/chartutil/testdata/subpop/charts/subchart1/values.yaml @@ -1,6 +1,7 @@ # Default values for subchart. # This is a YAML-formatted file. # Declare variables to be passed into your templates. +# subchart1 replicaCount: 1 image: repository: nginx @@ -13,9 +14,11 @@ service: internalPort: 80 resources: limits: - cpu: 100m - memory: 128Mi + cpu: 200m + memory: 200Mi + plasticity: 0 requests: - cpu: 100m - memory: 128Mi + cpu: 250m + memory: 250Mi + truthiness: 200 diff --git a/pkg/chartutil/testdata/subpop/requirements.yaml b/pkg/chartutil/testdata/subpop/requirements.yaml index 9840e047d..19d320889 100644 --- a/pkg/chartutil/testdata/subpop/requirements.yaml +++ b/pkg/chartutil/testdata/subpop/requirements.yaml @@ -6,6 +6,11 @@ dependencies: tags: - front-end - subchart1 + import-values: + - child: service + parent: imported-from-chart1 + - child: imported-from-chartA + parent: imported-from-chartA-via-chart1 - name: subchart2 repository: http://localhost:10191 version: 0.1.0 diff --git a/pkg/chartutil/testdata/subpop/values.yaml b/pkg/chartutil/testdata/subpop/values.yaml index 85fc7b49d..f03c449cf 100644 --- a/pkg/chartutil/testdata/subpop/values.yaml +++ b/pkg/chartutil/testdata/subpop/values.yaml @@ -1,6 +1,20 @@ # parent/values.yaml # switch-like +imported-from-chart1: + name: bathtubginx + type: None + externalPort: 25 + notimported1: 1 + +imported-from-chartA-via-chart1: + limits: + cpu: 100m + memory: 100Mi + notimported2: 100 + requests: + truthiness: 33.3 + tags: front-end: true back-end: false diff --git a/pkg/helm/client.go b/pkg/helm/client.go index 2e773cca7..82c87e0da 100644 --- a/pkg/helm/client.go +++ b/pkg/helm/client.go @@ -97,6 +97,10 @@ func (h *Client) InstallReleaseFromChart(chart *chart.Chart, ns string, opts ... if err != nil { return nil, err } + err = chartutil.ProcessRequirementsImportValues(req.Chart, req.Values) + if err != nil { + return nil, err + } return h.install(ctx, req) } @@ -166,6 +170,10 @@ func (h *Client) UpdateReleaseFromChart(rlsName string, chart *chart.Chart, opts if err != nil { return nil, err } + err = chartutil.ProcessRequirementsImportValues(req.Chart, req.Values) + if err != nil { + return nil, err + } return h.update(ctx, req) } diff --git a/pkg/resolver/resolver_test.go b/pkg/resolver/resolver_test.go index 8d4b86019..5f0811f20 100644 --- a/pkg/resolver/resolver_test.go +++ b/pkg/resolver/resolver_test.go @@ -141,7 +141,7 @@ func TestResolve(t *testing.T) { } func TestHashReq(t *testing.T) { - expect := "sha256:c8250374210bd909cef274be64f871bd4e376d4ecd34a1589b5abf90b68866ba" + expect := "sha256:1feffe2016ca113f64159d91c1f77d6a83bcd23510b171d9264741bf9d63f741" req := &chartutil.Requirements{ Dependencies: []*chartutil.Dependency{ {Name: "alpine", Version: "0.1.0", Repository: "http://localhost:8879/charts"}, From d1424f6c08ce1eb11c2958ca832e7d937d5c8fd9 Mon Sep 17 00:00:00 2001 From: Justin Scott Date: Tue, 14 Mar 2017 14:50:49 -0700 Subject: [PATCH 02/10] Handle missed error and make error messages unique --- pkg/chartutil/requirements.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pkg/chartutil/requirements.go b/pkg/chartutil/requirements.go index 326afb36c..5ca475241 100644 --- a/pkg/chartutil/requirements.go +++ b/pkg/chartutil/requirements.go @@ -316,6 +316,9 @@ func processImportValues(c *chart.Chart, v *chart.Config) error { return nil } cvals, err := CoalesceValues(c, v) + if err != nil { + log.Fatalf("Error coalescing values for ImportValues %s", err) + } nv := v.GetValues() b := make(map[string]interface{}) for kk, v3 := range nv { @@ -369,7 +372,7 @@ func processImportValues(c *chart.Chart, v *chart.Config) error { cv, err := coalesceValues(c, b) if err != nil { - log.Fatalf("Error coalescing values for ImportValues %s", err) + log.Fatalf("Error coalescing processed values for ImportValues %s", err) } y, err := yaml.Marshal(cv) if err != nil { From 2bd4d1d003abadbe051eb1a72b0e621d8a509db4 Mon Sep 17 00:00:00 2001 From: Justin Scott Date: Tue, 14 Mar 2017 15:09:55 -0700 Subject: [PATCH 03/10] Cleanup old todo, unused log and value for ImportValues feature --- pkg/chartutil/requirements.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pkg/chartutil/requirements.go b/pkg/chartutil/requirements.go index 5ca475241..3f5895c4d 100644 --- a/pkg/chartutil/requirements.go +++ b/pkg/chartutil/requirements.go @@ -328,7 +328,7 @@ func processImportValues(c *chart.Chart, v *chart.Config) error { if len(r.ImportValues) > 0 { var outiv []interface{} for _, riv := range r.ImportValues { - switch tr := riv.(type) { + switch riv.(type) { case map[string]interface{}: if m, ok := riv.(map[string]interface{}); ok { nm := make(map[string]string) @@ -350,8 +350,6 @@ func processImportValues(c *chart.Chart, v *chart.Config) error { } } case string: - log.Printf("its a string %v", tr) - // todo validation nm := make(map[string]string) nm["child"] = riv.(string) nm["parent"] = "." From 007bb9dbae5f893f8a18ed4feee78edd8813a420 Mon Sep 17 00:00:00 2001 From: Justin Scott Date: Mon, 20 Mar 2017 10:40:40 -0700 Subject: [PATCH 04/10] Implement 'exports' convetion for simple list items --- pkg/chartutil/requirements.go | 2 +- pkg/chartutil/requirements_test.go | 12 ++++++++++++ .../charts/subchart1/charts/subchartB/values.yaml | 10 +++++++++- .../subpop/charts/subchart1/requirements.yaml | 3 +++ .../testdata/subpop/charts/subchart1/values.yaml | 3 +++ pkg/chartutil/testdata/subpop/requirements.yaml | 6 ++++++ 6 files changed, 34 insertions(+), 2 deletions(-) diff --git a/pkg/chartutil/requirements.go b/pkg/chartutil/requirements.go index 3f5895c4d..26ea5cf5b 100644 --- a/pkg/chartutil/requirements.go +++ b/pkg/chartutil/requirements.go @@ -351,7 +351,7 @@ func processImportValues(c *chart.Chart, v *chart.Config) error { } case string: nm := make(map[string]string) - nm["child"] = riv.(string) + nm["child"] = "exports." + riv.(string) nm["parent"] = "." outiv = append(outiv, nm) s := r.Name + "." + nm["child"] diff --git a/pkg/chartutil/requirements_test.go b/pkg/chartutil/requirements_test.go index 21268df9d..59c4ecc11 100644 --- a/pkg/chartutil/requirements_test.go +++ b/pkg/chartutil/requirements_test.go @@ -226,6 +226,12 @@ func TestProcessRequirementsImportValues(t *testing.T) { e["imported-from-chartA-via-chart1.limits.memory"] = "300Mi" e["imported-from-chartA-via-chart1.limits.volume"] = "11" e["imported-from-chartA-via-chart1.requests.truthiness"] = "0.01" + // single list items (looks for exports. parent key) + e["imported-from-chartB-via-chart1.databint"] = "1" + e["imported-from-chartB-via-chart1.databstr"] = "x.y.z" + e["parent1c3"] = "true" + // checks that a chartb value was merged in with charta values + e["imported-from-chartA-via-chart1.resources.limits.shares"] = "100" verifyRequirementsImportValues(t, c, v, e) } @@ -254,6 +260,12 @@ func verifyRequirementsImportValues(t *testing.T, c *chart.Chart, v *chart.Confi t.Errorf("Failed to match imported float value %v with expected %v", s, vv) return } + case bool: + b := strconv.FormatBool(pv.(bool)) + if b != vv { + t.Errorf("Failed to match imported bool value %v with expected %v", b, vv) + return + } default: if pv.(string) != vv { t.Errorf("Failed to match imported string value %v with expected %v", pv, vv) diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartB/values.yaml b/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartB/values.yaml index 5e5b21065..5f473ef61 100644 --- a/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartB/values.yaml +++ b/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartB/values.yaml @@ -18,4 +18,12 @@ resources: requests: cpu: 100m memory: 128Mi - +exports: + convention1: + data: + databint: 1 + databstr: x.y.z + convention2: + resources: + limits: + shares: 100 diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/requirements.yaml b/pkg/chartutil/testdata/subpop/charts/subchart1/requirements.yaml index 5adf2f10a..cb62b9f36 100644 --- a/pkg/chartutil/testdata/subpop/charts/subchart1/requirements.yaml +++ b/pkg/chartutil/testdata/subpop/charts/subchart1/requirements.yaml @@ -16,6 +16,9 @@ dependencies: repository: http://localhost:10191 version: 0.1.0 condition: subchartb.enabled + import-values: + - convention1 + - convention2 tags: - front-end - subchartb diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/values.yaml b/pkg/chartutil/testdata/subpop/charts/subchart1/values.yaml index 4c5085d82..15ba88485 100644 --- a/pkg/chartutil/testdata/subpop/charts/subchart1/values.yaml +++ b/pkg/chartutil/testdata/subpop/charts/subchart1/values.yaml @@ -21,4 +21,7 @@ resources: cpu: 250m memory: 250Mi truthiness: 200 +exports: + convention3: + parent1c3: true diff --git a/pkg/chartutil/testdata/subpop/requirements.yaml b/pkg/chartutil/testdata/subpop/requirements.yaml index 19d320889..e299d057b 100644 --- a/pkg/chartutil/testdata/subpop/requirements.yaml +++ b/pkg/chartutil/testdata/subpop/requirements.yaml @@ -11,6 +11,12 @@ dependencies: parent: imported-from-chart1 - child: imported-from-chartA parent: imported-from-chartA-via-chart1 + - convention3 + # this merges chartb "shares" into charta "limits" + - child: resources.limits + parent: imported-from-chartA-via-chart1.resources.limits + - child: data + parent: imported-from-chartB-via-chart1 - name: subchart2 repository: http://localhost:10191 version: 0.1.0 From 4a5721fb360bb25c88163e880ca020f4e460a17a Mon Sep 17 00:00:00 2001 From: Justin Scott Date: Tue, 21 Mar 2017 10:04:54 -0700 Subject: [PATCH 05/10] Fixup style and errors --- pkg/chartutil/requirements.go | 64 +++++++++++++++++------------------ 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/pkg/chartutil/requirements.go b/pkg/chartutil/requirements.go index 26ea5cf5b..189e2c647 100644 --- a/pkg/chartutil/requirements.go +++ b/pkg/chartutil/requirements.go @@ -269,15 +269,20 @@ func ProcessRequirementsEnabled(c *chart.Chart, v *chart.Config) error { return nil } -// pathToMap creates a nested map given a YAML path in dot notation +// pathToMap creates a nested map given a YAML path in dot notation. func pathToMap(path string, data map[string]interface{}) map[string]interface{} { ap := strings.Split(path, ".") + if len(ap) == 0 { + return nil + } n := []map[string]interface{}{} + // created nested map for each key, adding to slice for _, v := range ap { nm := make(map[string]interface{}) nm[v] = make(map[string]interface{}) n = append(n, nm) } + // find the last key (map) and set our data for i, d := range n { for k := range d { z := i + 1 @@ -292,11 +297,10 @@ func pathToMap(path string, data map[string]interface{}) map[string]interface{} return n[0] } -// getParents returns a slice of parent charts in reverse order +// getParents returns a slice of parent charts in reverse order. func getParents(c *chart.Chart, out []*chart.Chart) []*chart.Chart { if len(out) == 0 { - out = []*chart.Chart{} - out = append(out, c) + out = []*chart.Chart{c} } for _, ch := range c.Dependencies { if len(ch.Dependencies) > 0 { @@ -308,16 +312,15 @@ func getParents(c *chart.Chart, out []*chart.Chart) []*chart.Chart { return out } -// processImportValues merges values from child to parent based on ImportValues field +// processImportValues merges values from child to parent based on ImportValues field. func processImportValues(c *chart.Chart, v *chart.Config) error { reqs, err := LoadRequirements(c) if err != nil { - log.Printf("Warning: ImportValues cannot load requirements for %s", c.Metadata.Name) - return nil + return err } cvals, err := CoalesceValues(c, v) if err != nil { - log.Fatalf("Error coalescing values for ImportValues %s", err) + return err } nv := v.GetValues() b := make(map[string]interface{}) @@ -328,30 +331,29 @@ func processImportValues(c *chart.Chart, v *chart.Config) error { if len(r.ImportValues) > 0 { var outiv []interface{} for _, riv := range r.ImportValues { - switch riv.(type) { + switch iv := riv.(type) { case map[string]interface{}: - if m, ok := riv.(map[string]interface{}); ok { - nm := make(map[string]string) - nm["child"] = m["child"].(string) - nm["parent"] = m["parent"].(string) - outiv = append(outiv, nm) - s := r.Name + "." + nm["child"] - vv, err := cvals.Table(s) - if err != nil { - log.Printf("Warning: ImportValues missing table %v", err) - continue - } - if nm["parent"] == "." { - coalesceTables(b, vv.AsMap()) - } else { + nm := map[string]string{ + "child": iv["child"].(string), + "parent": iv["parent"].(string), + } + outiv = append(outiv, nm) + s := r.Name + "." + nm["child"] + vv, err := cvals.Table(s) + if err != nil { + log.Printf("Warning: ImportValues missing table %v", err) + continue + } + if nm["parent"] == "." { + coalesceTables(b, vv.AsMap()) + } else { - vm := pathToMap(nm["parent"], vv.AsMap()) - coalesceTables(b, vm) - } + vm := pathToMap(nm["parent"], vv.AsMap()) + coalesceTables(b, vm) } case string: nm := make(map[string]string) - nm["child"] = "exports." + riv.(string) + nm["child"] = "exports." + iv nm["parent"] = "." outiv = append(outiv, nm) s := r.Name + "." + nm["child"] @@ -367,14 +369,13 @@ func processImportValues(c *chart.Chart, v *chart.Config) error { r.ImportValues = outiv } } - cv, err := coalesceValues(c, b) if err != nil { - log.Fatalf("Error coalescing processed values for ImportValues %s", err) + return err } y, err := yaml.Marshal(cv) if err != nil { - log.Printf("Warning: ImportValues could not marshall %v", err) + return err } bb := &chart.Config{Raw: string(y)} v = bb @@ -383,12 +384,11 @@ func processImportValues(c *chart.Chart, v *chart.Config) error { return nil } -// ProcessRequirementsImportValues imports specified chart values from child to parent +// ProcessRequirementsImportValues imports specified chart values from child to parent. func ProcessRequirementsImportValues(c *chart.Chart, v *chart.Config) error { pc := getParents(c, nil) for i := len(pc) - 1; i >= 0; i-- { processImportValues(pc[i], v) - } return nil From 7ea4d8c7c460eae18bf74c682832acfb009445ce Mon Sep 17 00:00:00 2001 From: Justin Scott Date: Mon, 27 Mar 2017 18:21:26 -0700 Subject: [PATCH 06/10] Refactor so parent's values win --- docs/charts.md | 92 +++++++++++++++++++ pkg/chartutil/requirements.go | 37 ++++---- pkg/chartutil/requirements_test.go | 74 ++++++++++++--- .../subchart1/charts/subchartA/values.yaml | 22 ++--- .../subchart1/charts/subchartB/values.yaml | 48 ++++++---- .../subpop/charts/subchart1/requirements.yaml | 20 ++-- .../subpop/charts/subchart1/values.yaml | 62 +++++++++---- .../testdata/subpop/requirements.yaml | 25 +++-- pkg/chartutil/testdata/subpop/values.yaml | 48 +++++++--- 9 files changed, 312 insertions(+), 116 deletions(-) diff --git a/docs/charts.md b/docs/charts.md index b7d97cc0b..677e9e66d 100644 --- a/docs/charts.md +++ b/docs/charts.md @@ -302,6 +302,98 @@ helm install --set tags.front-end=true --set subchart2.enabled=false * The `tags:` key in values must be a top level key. Globals and nested `tags:` tables are not currently supported. +#### Importing Child Values via requirements.yaml + +In some cases it is desirable to allow a child chart's values to propagate to the parent chart and be shared +as common defaults. The values to be imported can be specified in the parent chart's requirements.yaml +using a YAML list format that contains the source of the values to be imported (child) and the +destination path in the parent chart's values (parent). + +The optional `import-values` in the example below instructs Helm to take any values found at `child:` +path and copy them to the path at `parent:` + +```` +# parent's requirements.yaml +dependencies: + - name: subchart1 + repository: http://localhost:10191 + version: 0.1.0 + ... + import-values: + - child: default.data + parent: myimports +```` +In the above example, values found at `default.data` in the subchart1's values will be imported +to the `myimports` key in the parent chart's values as detailed below: + +```` +# parent's values + +myimports: + myint: 0 + mybool: false + mystring: "helm rocks" + + +```` +```` +# subchart1's values.yaml + +default: + data: + myint: 999 + mybool: true + +```` +The parent chart's resulting values would be: + +```` +# parent's final values + +myimports: + myint: 999 + mybool: true + mystring: "helm rocks" + +```` + +The parent's final values now contains the `myint` and `mybool` fields imported from subchart1. + +##### Using the exports convention + +If a values.yaml contains an `exports` field at the root, it's contents may be imported +directly into the parent's values by using a simple string format as in the example below: + +```` +# parent's requirements.yaml + ... + import-values: + - data +```` +```` +# child values + +... +exports: + data: + myint:99 +```` + +Since we are using the simple string `data` in our import list, Helm looks in the the `exports` +field of the child chart for `data` key and imports its contents. + +The final parent values would contain our exported field. + +```` +# parent's values +... +myint: 99 + +```` + +Please note the parent key `data` is not contained in the parent's final values. If +you need to specify the parent key, use the 'child/parent' format. + ## Templates and Values Helm Chart templates are written in the diff --git a/pkg/chartutil/requirements.go b/pkg/chartutil/requirements.go index 189e2c647..31e4d255a 100644 --- a/pkg/chartutil/requirements.go +++ b/pkg/chartutil/requirements.go @@ -271,6 +271,9 @@ func ProcessRequirementsEnabled(c *chart.Chart, v *chart.Config) error { // pathToMap creates a nested map given a YAML path in dot notation. func pathToMap(path string, data map[string]interface{}) map[string]interface{} { + if path == "." { + return data + } ap := strings.Split(path, ".") if len(ap) == 0 { return nil @@ -318,15 +321,18 @@ func processImportValues(c *chart.Chart, v *chart.Config) error { if err != nil { return err } + // combine chart values and its dependencies' values cvals, err := CoalesceValues(c, v) if err != nil { return err } nv := v.GetValues() b := make(map[string]interface{}) - for kk, v3 := range nv { - b[kk] = v3 + // convert values to map + for kk, vvv := range nv { + b[kk] = vvv } + // import values from each dependency if specified in import-values for _, r := range reqs.Dependencies { if len(r.ImportValues) > 0 { var outiv []interface{} @@ -339,47 +345,40 @@ func processImportValues(c *chart.Chart, v *chart.Config) error { } outiv = append(outiv, nm) s := r.Name + "." + nm["child"] + // get child table vv, err := cvals.Table(s) if err != nil { log.Printf("Warning: ImportValues missing table %v", err) continue } - if nm["parent"] == "." { - coalesceTables(b, vv.AsMap()) - } else { - - vm := pathToMap(nm["parent"], vv.AsMap()) - coalesceTables(b, vm) - } + // create value map from child to be merged into parent + vm := pathToMap(nm["parent"], vv.AsMap()) + b = coalesceTables(cvals, vm) case string: nm := make(map[string]string) nm["child"] = "exports." + iv nm["parent"] = "." outiv = append(outiv, nm) s := r.Name + "." + nm["child"] - vv, err := cvals.Table(s) + vm, err := cvals.Table(s) if err != nil { log.Printf("Warning: ImportValues missing table %v", err) continue } - coalesceTables(b, vv.AsMap()) + b = coalesceTables(b, vm.AsMap()) } } // set our formatted import values r.ImportValues = outiv } } - cv, err := coalesceValues(c, b) - if err != nil { - return err - } - y, err := yaml.Marshal(cv) + b = coalesceTables(b, cvals) + y, err := yaml.Marshal(b) if err != nil { return err } - bb := &chart.Config{Raw: string(y)} - v = bb - c.Values = bb + // set the new values + c.Values.Raw = string(y) return nil } diff --git a/pkg/chartutil/requirements_test.go b/pkg/chartutil/requirements_test.go index 59c4ecc11..c92c9f052 100644 --- a/pkg/chartutil/requirements_test.go +++ b/pkg/chartutil/requirements_test.go @@ -217,21 +217,65 @@ func TestProcessRequirementsImportValues(t *testing.T) { v := &chart.Config{Raw: ""} e := make(map[string]string) - e["imported-from-chart1.type"] = "ClusterIP" - e["imported-from-chart1.name"] = "nginx" - e["imported-from-chart1.externalPort"] = "80" - // this doesn't exist in imported table. it should merge and remain unchanged - e["imported-from-chart1.notimported1"] = "1" - e["imported-from-chartA-via-chart1.limits.cpu"] = "300m" - e["imported-from-chartA-via-chart1.limits.memory"] = "300Mi" - e["imported-from-chartA-via-chart1.limits.volume"] = "11" - e["imported-from-chartA-via-chart1.requests.truthiness"] = "0.01" - // single list items (looks for exports. parent key) - e["imported-from-chartB-via-chart1.databint"] = "1" - e["imported-from-chartB-via-chart1.databstr"] = "x.y.z" - e["parent1c3"] = "true" - // checks that a chartb value was merged in with charta values - e["imported-from-chartA-via-chart1.resources.limits.shares"] = "100" + + e["imported-chart1.SC1bool"] = "true" + e["imported-chart1.SC1float"] = "3.14" + e["imported-chart1.SC1int"] = "100" + e["imported-chart1.SC1string"] = "dollywood" + e["imported-chart1.SC1extra1"] = "11" + e["imported-chart1.SPextra1"] = "helm rocks" + e["imported-chart1.SC1extra1"] = "11" + + e["imported-chartA.SCAbool"] = "false" + e["imported-chartA.SCAfloat"] = "3.1" + e["imported-chartA.SCAint"] = "55" + e["imported-chartA.SCAstring"] = "jabba" + e["imported-chartA.SPextra3"] = "1.337" + e["imported-chartA.SC1extra2"] = "1.337" + e["imported-chartA.SCAnested1.SCAnested2"] = "true" + + e["imported-chartA-B.SCAbool"] = "false" + e["imported-chartA-B.SCAfloat"] = "3.1" + e["imported-chartA-B.SCAint"] = "55" + e["imported-chartA-B.SCAstring"] = "jabba" + + e["imported-chartA-B.SCBbool"] = "true" + e["imported-chartA-B.SCBfloat"] = "7.77" + e["imported-chartA-B.SCBint"] = "33" + e["imported-chartA-B.SCBstring"] = "boba" + e["imported-chartA-B.SPextra5"] = "k8s" + e["imported-chartA-B.SC1extra5"] = "tiller" + + e["overridden-chart1.SC1bool"] = "false" + e["overridden-chart1.SC1float"] = "3.141592" + e["overridden-chart1.SC1int"] = "99" + e["overridden-chart1.SC1string"] = "pollywog" + e["overridden-chart1.SPextra2"] = "42" + + e["overridden-chartA.SCAbool"] = "true" + e["overridden-chartA.SCAfloat"] = "41.3" + e["overridden-chartA.SCAint"] = "808" + e["overridden-chartA.SCAstring"] = "jaberwocky" + e["overridden-chartA.SPextra4"] = "true" + + e["overridden-chartA-B.SCAbool"] = "true" + e["overridden-chartA-B.SCAfloat"] = "41.3" + e["overridden-chartA-B.SCAint"] = "808" + e["overridden-chartA-B.SCAstring"] = "jaberwocky" + e["overridden-chartA-B.SCBbool"] = "false" + e["overridden-chartA-B.SCBfloat"] = "1.99" + e["overridden-chartA-B.SCBint"] = "77" + e["overridden-chartA-B.SCBstring"] = "jango" + e["overridden-chartA-B.SPextra6"] = "111" + e["overridden-chartA-B.SCAextra1"] = "23" + e["overridden-chartA-B.SCBextra1"] = "13" + e["overridden-chartA-B.SC1extra6"] = "77" + + // `exports` style + e["SCBexported1B"] = "1965" + e["SC1extra7"] = "true" + e["SCBexported2A"] = "blaster" + e["global.SC1exported2.all.SC1exported3"] = "SC1expstr" verifyRequirementsImportValues(t, c, v, e) } diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartA/values.yaml b/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartA/values.yaml index 8c348e86c..712b3a2fa 100644 --- a/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartA/values.yaml +++ b/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartA/values.yaml @@ -2,24 +2,16 @@ # This is a YAML-formatted file. # Declare variables to be passed into your templates. # subchartA -replicaCount: 1 -image: - repository: nginx - tag: stable - pullPolicy: IfNotPresent service: name: nginx type: ClusterIP externalPort: 80 internalPort: 80 -resources: - limits: - cpu: 300m - memory: 300Mi - plasticity: 1.7331 - volume: 11 - requests: - cpu: 350m - memory: 350Mi - truthiness: 0.01 +SCAdata: + SCAbool: false + SCAfloat: 3.1 + SCAint: 55 + SCAstring: "jabba" + SCAnested1: + SCAnested2: true diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartB/values.yaml b/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartB/values.yaml index 5f473ef61..aba524d97 100644 --- a/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartB/values.yaml +++ b/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartB/values.yaml @@ -1,29 +1,37 @@ # Default values for subchart. # This is a YAML-formatted file. # Declare variables to be passed into your templates. -replicaCount: 1 -image: - repository: nginx - tag: stable - pullPolicy: IfNotPresent service: name: nginx type: ClusterIP externalPort: 80 internalPort: 80 -resources: - limits: - cpu: 100m - memory: 128Mi - requests: - cpu: 100m - memory: 128Mi + +SCBdata: + SCBbool: true + SCBfloat: 7.77 + SCBint: 33 + SCBstring: "boba" + exports: - convention1: - data: - databint: 1 - databstr: x.y.z - convention2: - resources: - limits: - shares: 100 + SCBexported1: + SCBexported1A: + SCBexported1B: 1965 + + SCBexported2: + SCBexported2A: "blaster" + +global: + kolla: + nova: + api: + all: + port: 8774 + metadata: + all: + port: 8775 +test: + dummy: 1 + + + diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/requirements.yaml b/pkg/chartutil/testdata/subpop/charts/subchart1/requirements.yaml index cb62b9f36..abfe85e76 100644 --- a/pkg/chartutil/testdata/subpop/charts/subchart1/requirements.yaml +++ b/pkg/chartutil/testdata/subpop/charts/subchart1/requirements.yaml @@ -7,18 +7,26 @@ dependencies: - front-end - subcharta import-values: - - child: resources.limits - parent: imported-from-chartA.limits - - child: resources.requests - parent: imported-from-chartA.requests + - child: SCAdata + parent: imported-chartA + - child: SCAdata + parent: overridden-chartA + - child: SCAdata + parent: imported-chartA-B - name: subchartb repository: http://localhost:10191 version: 0.1.0 condition: subchartb.enabled import-values: - - convention1 - - convention2 + - child: SCBdata + parent: imported-chartB + - child: SCBdata + parent: imported-chartA-B + - child: exports.SCBexported2 + parent: exports.SCBexported2 + - SCBexported1 + tags: - front-end - subchartb diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/values.yaml b/pkg/chartutil/testdata/subpop/charts/subchart1/values.yaml index 15ba88485..4b5bd6bd9 100644 --- a/pkg/chartutil/testdata/subpop/charts/subchart1/values.yaml +++ b/pkg/chartutil/testdata/subpop/charts/subchart1/values.yaml @@ -2,26 +2,54 @@ # This is a YAML-formatted file. # Declare variables to be passed into your templates. # subchart1 -replicaCount: 1 -image: - repository: nginx - tag: stable - pullPolicy: IfNotPresent service: name: nginx type: ClusterIP externalPort: 80 internalPort: 80 -resources: - limits: - cpu: 200m - memory: 200Mi - plasticity: 0 - requests: - cpu: 250m - memory: 250Mi - truthiness: 200 -exports: - convention3: - parent1c3: true + +SC1data: + SC1bool: true + SC1float: 3.14 + SC1int: 100 + SC1string: "dollywood" + SC1extra1: 11 + +imported-chartA: + SC1extra2: 1.337 + +overridden-chartA: + SCAbool: true + SCAfloat: 3.14 + SCAint: 100 + SCAstring: "jabathehut" + SC1extra3: true + +imported-chartA-B: + SC1extra5: "tiller" + +overridden-chartA-B: + SCAbool: true + SCAfloat: 3.33 + SCAint: 555 + SCAstring: "wormwood" + SCAextra1: 23 + + SCBbool: true + SCBfloat: 0.25 + SCBint: 98 + SCBstring: "murkwood" + SCBextra1: 13 + + SC1extra6: 77 + +SCBexported1A: + SC1extra7: true + +exports: + SC1exported1: + global: + SC1exported2: + all: + SC1exported3: "SC1expstr" \ No newline at end of file diff --git a/pkg/chartutil/testdata/subpop/requirements.yaml b/pkg/chartutil/testdata/subpop/requirements.yaml index e299d057b..a8eb0aace 100644 --- a/pkg/chartutil/testdata/subpop/requirements.yaml +++ b/pkg/chartutil/testdata/subpop/requirements.yaml @@ -7,16 +7,21 @@ dependencies: - front-end - subchart1 import-values: - - child: service - parent: imported-from-chart1 - - child: imported-from-chartA - parent: imported-from-chartA-via-chart1 - - convention3 - # this merges chartb "shares" into charta "limits" - - child: resources.limits - parent: imported-from-chartA-via-chart1.resources.limits - - child: data - parent: imported-from-chartB-via-chart1 + - child: SC1data + parent: imported-chart1 + - child: SC1data + parent: overridden-chart1 + - child: imported-chartA + parent: imported-chartA + - child: imported-chartA-B + parent: imported-chartA-B + - child: overridden-chartA-B + parent: overridden-chartA-B + - child: SCBexported1A + parent: . + - SCBexported2 + - SC1exported1 + - name: subchart2 repository: http://localhost:10191 version: 0.1.0 diff --git a/pkg/chartutil/testdata/subpop/values.yaml b/pkg/chartutil/testdata/subpop/values.yaml index f03c449cf..55e872d41 100644 --- a/pkg/chartutil/testdata/subpop/values.yaml +++ b/pkg/chartutil/testdata/subpop/values.yaml @@ -1,19 +1,39 @@ # parent/values.yaml -# switch-like -imported-from-chart1: - name: bathtubginx - type: None - externalPort: 25 - notimported1: 1 - -imported-from-chartA-via-chart1: - limits: - cpu: 100m - memory: 100Mi - notimported2: 100 - requests: - truthiness: 33.3 +imported-chart1: + SPextra1: "helm rocks" + +overridden-chart1: + SC1bool: false + SC1float: 3.141592 + SC1int: 99 + SC1string: "pollywog" + SPextra2: 42 + + +imported-chartA: + SPextra3: 1.337 + +overridden-chartA: + SCAbool: true + SCAfloat: 41.3 + SCAint: 808 + SCAstring: "jaberwocky" + SPextra4: true + +imported-chartA-B: + SPextra5: "k8s" + +overridden-chartA-B: + SCAbool: true + SCAfloat: 41.3 + SCAint: 808 + SCAstring: "jaberwocky" + SCBbool: false + SCBfloat: 1.99 + SCBint: 77 + SCBstring: "jango" + SPextra6: 111 tags: front-end: true From 1a8e728ed9a7a29345df856ccc879c2fd4d4868a Mon Sep 17 00:00:00 2001 From: Justin Scott Date: Wed, 29 Mar 2017 08:58:29 -0700 Subject: [PATCH 07/10] Update docs with details about exports --- docs/charts.md | 96 ++++++++++++++++++++++++++++---------------------- 1 file changed, 53 insertions(+), 43 deletions(-) diff --git a/docs/charts.md b/docs/charts.md index 677e9e66d..638b12387 100644 --- a/docs/charts.md +++ b/docs/charts.md @@ -304,13 +304,58 @@ helm install --set tags.front-end=true --set subchart2.enabled=false #### Importing Child Values via requirements.yaml -In some cases it is desirable to allow a child chart's values to propagate to the parent chart and be shared -as common defaults. The values to be imported can be specified in the parent chart's requirements.yaml -using a YAML list format that contains the source of the values to be imported (child) and the -destination path in the parent chart's values (parent). +In some cases it is desirable to allow a child chart's values to propagate to the parent chart and be +shared as common defaults. An additional benefit of using the `exports` format is that it will enable future +tooling to introspect user settable values. -The optional `import-values` in the example below instructs Helm to take any values found at `child:` -path and copy them to the path at `parent:` +The keys containing the values to be imported can be specified in the parent chart's requirements.yaml using +a YAML list. Each item in the list is a key which is imported from the child chart's `exports` field. + +To import values not contained in the `exports` key, use the [child/parent](#using-the-child/parent-format) format. + +##### Using the exports format + +If a child chart's values.yaml contains an `exports` field at the root, it's contents may be imported +directly into the parent's values by specifying the keys to import as in the example below: + +```` +# parent's requirements.yaml + ... + import-values: + - data +```` +```` +# child's values.yaml + +... +exports: + data: + myint: 99 +```` + +Since we are specifying the key `data` in our import list, Helm looks in the the `exports` field of the child +chart for `data` key and imports its contents. + +The final parent values would contain our exported field: + +```` +# parent's values +... +myint: 99 + +```` + +Please note the parent key `data` is not contained in the parent's final values. If you need to specify the +parent key, use the 'child/parent' format. + +##### Using the child/parent format + +To access values that are not contained in the `exports` key of the child chart's values, you will need to +specify the source key of the values to be imported (`child`) and the destination path in the parent chart's +values (`parent`). + +The `import-values` in the example below instructs Helm to take any values found at `child:` path and copy them +to the parent's values at the path specified in `parent:` ```` # parent's requirements.yaml @@ -332,7 +377,7 @@ to the `myimports` key in the parent chart's values as detailed below: myimports: myint: 0 mybool: false - mystring: "helm rocks" + mystring: "helm rocks!" ```` @@ -353,47 +398,12 @@ The parent chart's resulting values would be: myimports: myint: 999 mybool: true - mystring: "helm rocks" + mystring: "helm rocks!" ```` The parent's final values now contains the `myint` and `mybool` fields imported from subchart1. -##### Using the exports convention - -If a values.yaml contains an `exports` field at the root, it's contents may be imported -directly into the parent's values by using a simple string format as in the example below: - -```` -# parent's requirements.yaml - ... - import-values: - - data -```` -```` -# child values - -... -exports: - data: - myint:99 -```` - -Since we are using the simple string `data` in our import list, Helm looks in the the `exports` -field of the child chart for `data` key and imports its contents. - -The final parent values would contain our exported field. - -```` -# parent's values -... -myint: 99 - -```` - -Please note the parent key `data` is not contained in the parent's final values. If -you need to specify the parent key, use the 'child/parent' format. - ## Templates and Values Helm Chart templates are written in the From 3bf143f05223018eec79ca7ea9d91cf3fa559ec8 Mon Sep 17 00:00:00 2001 From: Justin Scott Date: Fri, 31 Mar 2017 10:18:29 -0700 Subject: [PATCH 08/10] Fix codefences and nits in charts.md. Correct whitespace in charts. Add clarity to description of ImportValues requirements field. --- docs/charts.md | 65 +++++++++---------- pkg/chartutil/requirements.go | 19 ++++-- .../subchart1/charts/subchartB/values.yaml | 18 +++-- .../subpop/charts/subchart1/values.yaml | 10 +-- 4 files changed, 57 insertions(+), 55 deletions(-) diff --git a/docs/charts.md b/docs/charts.md index 638b12387..414f80bf7 100644 --- a/docs/charts.md +++ b/docs/charts.md @@ -306,44 +306,44 @@ helm install --set tags.front-end=true --set subchart2.enabled=false In some cases it is desirable to allow a child chart's values to propagate to the parent chart and be shared as common defaults. An additional benefit of using the `exports` format is that it will enable future -tooling to introspect user settable values. +tooling to introspect user-settable values. -The keys containing the values to be imported can be specified in the parent chart's requirements.yaml using -a YAML list. Each item in the list is a key which is imported from the child chart's `exports` field. +The keys containing the values to be imported can be specified in the parent chart's `requirements.yaml` file +using a YAML list. Each item in the list is a key which is imported from the child chart's `exports` field. To import values not contained in the `exports` key, use the [child/parent](#using-the-child/parent-format) format. +Examples of both formats are described below. ##### Using the exports format -If a child chart's values.yaml contains an `exports` field at the root, it's contents may be imported +If a child chart's `values.yaml` file contains an `exports` field at the root, its contents may be imported directly into the parent's values by specifying the keys to import as in the example below: -```` -# parent's requirements.yaml +```yaml +# parent's requirements.yaml file ... import-values: - data -```` -```` -# child's values.yaml - +``` +```yaml +# child's values.yaml file ... exports: data: myint: 99 -```` +``` Since we are specifying the key `data` in our import list, Helm looks in the the `exports` field of the child chart for `data` key and imports its contents. The final parent values would contain our exported field: -```` -# parent's values +```yaml +# parent's values file ... myint: 99 -```` +``` Please note the parent key `data` is not contained in the parent's final values. If you need to specify the parent key, use the 'child/parent' format. @@ -357,42 +357,41 @@ values (`parent`). The `import-values` in the example below instructs Helm to take any values found at `child:` path and copy them to the parent's values at the path specified in `parent:` -```` -# parent's requirements.yaml +```yaml +# parent's requirements.yaml file dependencies: - - name: subchart1 - repository: http://localhost:10191 - version: 0.1.0 - ... - import-values: - - child: default.data - parent: myimports -```` + - name: subchart1 + repository: http://localhost:10191 + version: 0.1.0 + ... + import-values: + - child: default.data + parent: myimports +``` In the above example, values found at `default.data` in the subchart1's values will be imported to the `myimports` key in the parent chart's values as detailed below: -```` -# parent's values +```yaml +# parent's values.yaml file myimports: myint: 0 mybool: false mystring: "helm rocks!" - -```` -```` -# subchart1's values.yaml +``` +```yaml +# subchart1's values.yaml file default: data: myint: 999 mybool: true -```` +``` The parent chart's resulting values would be: -```` +```yaml # parent's final values myimports: @@ -400,7 +399,7 @@ myimports: mybool: true mystring: "helm rocks!" -```` +``` The parent's final values now contains the `myint` and `mybool` fields imported from subchart1. diff --git a/pkg/chartutil/requirements.go b/pkg/chartutil/requirements.go index 31e4d255a..54088f98e 100644 --- a/pkg/chartutil/requirements.go +++ b/pkg/chartutil/requirements.go @@ -62,7 +62,8 @@ type Dependency struct { Tags []string `json:"tags"` // Enabled bool determines if chart should be loaded Enabled bool `json:"enabled"` - // ImportValues holds the mapping of source values to parent key to be imported + // ImportValues holds the mapping of source values to parent key to be imported. Each item can be a + // string or pair of child/parent sublist items. ImportValues []interface{} `json:"import-values"` } @@ -315,7 +316,7 @@ func getParents(c *chart.Chart, out []*chart.Chart) []*chart.Chart { return out } -// processImportValues merges values from child to parent based on ImportValues field. +// processImportValues merges values from child to parent based on the chart's dependencies' ImportValues field. func processImportValues(c *chart.Chart, v *chart.Config) error { reqs, err := LoadRequirements(c) if err != nil { @@ -327,7 +328,7 @@ func processImportValues(c *chart.Chart, v *chart.Config) error { return err } nv := v.GetValues() - b := make(map[string]interface{}) + b := make(map[string]interface{}, len(nv)) // convert values to map for kk, vvv := range nv { b[kk] = vvv @@ -348,21 +349,25 @@ func processImportValues(c *chart.Chart, v *chart.Config) error { // get child table vv, err := cvals.Table(s) if err != nil { - log.Printf("Warning: ImportValues missing table %v", err) + log.Printf("Warning: ImportValues missing table: %v", err) continue } // create value map from child to be merged into parent vm := pathToMap(nm["parent"], vv.AsMap()) b = coalesceTables(cvals, vm) case string: - nm := make(map[string]string) + nm := map[string]string{ + "child": "exports." + iv, + "parent": ".", + } + /*nm := make(map[string]string) nm["child"] = "exports." + iv - nm["parent"] = "." + nm["parent"] = "."*/ outiv = append(outiv, nm) s := r.Name + "." + nm["child"] vm, err := cvals.Table(s) if err != nil { - log.Printf("Warning: ImportValues missing table %v", err) + log.Printf("Warning: ImportValues missing table: %v", err) continue } b = coalesceTables(b, vm.AsMap()) diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartB/values.yaml b/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartB/values.yaml index aba524d97..6d9f9c677 100644 --- a/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartB/values.yaml +++ b/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartB/values.yaml @@ -22,16 +22,14 @@ exports: SCBexported2A: "blaster" global: - kolla: - nova: - api: - all: - port: 8774 - metadata: - all: - port: 8775 -test: - dummy: 1 + kolla: + nova: + api: + all: + port: 8774 + metadata: + all: + port: 8775 diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/values.yaml b/pkg/chartutil/testdata/subpop/charts/subchart1/values.yaml index 4b5bd6bd9..72d3fa5c8 100644 --- a/pkg/chartutil/testdata/subpop/charts/subchart1/values.yaml +++ b/pkg/chartutil/testdata/subpop/charts/subchart1/values.yaml @@ -48,8 +48,8 @@ SCBexported1A: SC1extra7: true exports: - SC1exported1: - global: - SC1exported2: - all: - SC1exported3: "SC1expstr" \ No newline at end of file + SC1exported1: + global: + SC1exported2: + all: + SC1exported3: "SC1expstr" \ No newline at end of file From 31e57d89212d54d621d30d7908a85064d5b27fb0 Mon Sep 17 00:00:00 2001 From: Justin Scott Date: Fri, 31 Mar 2017 10:25:59 -0700 Subject: [PATCH 09/10] Remove commented code --- pkg/chartutil/requirements.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/pkg/chartutil/requirements.go b/pkg/chartutil/requirements.go index 54088f98e..53e28e788 100644 --- a/pkg/chartutil/requirements.go +++ b/pkg/chartutil/requirements.go @@ -360,9 +360,6 @@ func processImportValues(c *chart.Chart, v *chart.Config) error { "child": "exports." + iv, "parent": ".", } - /*nm := make(map[string]string) - nm["child"] = "exports." + iv - nm["parent"] = "."*/ outiv = append(outiv, nm) s := r.Name + "." + nm["child"] vm, err := cvals.Table(s) From 75ea56641318bd8f47cce9f1687c69efeb3068f4 Mon Sep 17 00:00:00 2001 From: Justin Scott Date: Fri, 31 Mar 2017 10:46:50 -0700 Subject: [PATCH 10/10] Correct indention of YAML field in subchartB --- .../subpop/charts/subchart1/charts/subchartB/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartB/values.yaml b/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartB/values.yaml index 6d9f9c677..774fdd75c 100644 --- a/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartB/values.yaml +++ b/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartB/values.yaml @@ -19,7 +19,7 @@ exports: SCBexported1B: 1965 SCBexported2: - SCBexported2A: "blaster" + SCBexported2A: "blaster" global: kolla: