ref(pkg/chartutil): simplify chart dependency unit tests

- simplify unit tests
- refactor typed errors
- unexport internal functions

Signed-off-by: Adam Reese <adam@reese.io>
pull/4983/head
Adam Reese 6 years ago
parent 825b15c763
commit 6fc8c9e079
No known key found for this signature in database
GPG Key ID: 06F35E60A7A18DD6

@ -171,10 +171,7 @@ func (o *templateOptions) run(out io.Writer) error {
if err := yaml.Unmarshal(config, &m); err != nil { if err := yaml.Unmarshal(config, &m); err != nil {
return err return err
} }
if err := chartutil.ProcessDependencyEnabled(c, m); err != nil { if err := chartutil.ProcessDependencies(c, m); err != nil {
return err
}
if err := chartutil.ProcessDependencyImportValues(c); err != nil {
return err return err
} }

@ -25,8 +25,15 @@ import (
"k8s.io/helm/pkg/version" "k8s.io/helm/pkg/version"
) )
// ProcessDependencyConditions disables charts based on condition path value in values func ProcessDependencies(c *chart.Chart, v Values) error {
func ProcessDependencyConditions(reqs []*chart.Dependency, cvals Values) { if err := processDependencyEnabled(c, v); err != nil {
return err
}
return processDependencyImportValues(c)
}
// processDependencyConditions disables charts based on condition path value in values
func processDependencyConditions(reqs []*chart.Dependency, cvals Values) {
if reqs == nil { if reqs == nil {
return return
} }
@ -66,8 +73,8 @@ func ProcessDependencyConditions(reqs []*chart.Dependency, cvals Values) {
} }
} }
// ProcessDependencyTags disables charts based on tags in values // processDependencyTags disables charts based on tags in values
func ProcessDependencyTags(reqs []*chart.Dependency, cvals Values) { func processDependencyTags(reqs []*chart.Dependency, cvals Values) {
if reqs == nil { if reqs == nil {
return return
} }
@ -99,34 +106,32 @@ func ProcessDependencyTags(reqs []*chart.Dependency, cvals Values) {
} }
} }
func getAliasDependency(charts []*chart.Chart, aliasChart *chart.Dependency) *chart.Chart { func getAliasDependency(charts []*chart.Chart, dep *chart.Dependency) *chart.Chart {
var chartFound chart.Chart for _, c := range charts {
for _, existingChart := range charts { if c == nil {
if existingChart == nil {
continue continue
} }
if existingChart.Metadata == nil { if c.Name() != dep.Name {
continue continue
} }
if existingChart.Metadata.Name != aliasChart.Name { if !version.IsCompatibleRange(dep.Version, c.Metadata.Version) {
continue continue
} }
if !version.IsCompatibleRange(aliasChart.Version, existingChart.Metadata.Version) {
continue out := *c
} md := *c.Metadata
chartFound = *existingChart out.Metadata = &md
newMetadata := *existingChart.Metadata
if aliasChart.Alias != "" { if dep.Alias != "" {
newMetadata.Name = aliasChart.Alias md.Name = dep.Alias
} }
chartFound.Metadata = &newMetadata return &out
return &chartFound
} }
return nil return nil
} }
// ProcessDependencyEnabled removes disabled charts from dependencies // processDependencyEnabled removes disabled charts from dependencies
func ProcessDependencyEnabled(c *chart.Chart, v map[string]interface{}) error { func processDependencyEnabled(c *chart.Chart, v map[string]interface{}) error {
if c.Metadata.Dependencies == nil { if c.Metadata.Dependencies == nil {
return nil return nil
} }
@ -137,17 +142,14 @@ func ProcessDependencyEnabled(c *chart.Chart, v map[string]interface{}) error {
// However, if the dependency is already specified in Chart.yaml // However, if the dependency is already specified in Chart.yaml
// we should not add it, as it would be anyways processed from Chart.yaml // we should not add it, as it would be anyways processed from Chart.yaml
for _, existingDependency := range c.Dependencies() { Loop:
var dependencyFound bool for _, existing := range c.Dependencies() {
for _, req := range c.Metadata.Dependencies { for _, req := range c.Metadata.Dependencies {
if existingDependency.Metadata.Name == req.Name && version.IsCompatibleRange(req.Version, existingDependency.Metadata.Version) { if existing.Name() == req.Name && version.IsCompatibleRange(req.Version, existing.Metadata.Version) {
dependencyFound = true continue Loop
break
} }
} }
if !dependencyFound { chartDependencies = append(chartDependencies, existing)
chartDependencies = append(chartDependencies, existingDependency)
}
} }
for _, req := range c.Metadata.Dependencies { for _, req := range c.Metadata.Dependencies {
@ -170,8 +172,8 @@ func ProcessDependencyEnabled(c *chart.Chart, v map[string]interface{}) error {
return err return err
} }
// flag dependencies as enabled/disabled // flag dependencies as enabled/disabled
ProcessDependencyTags(c.Metadata.Dependencies, cvals) processDependencyTags(c.Metadata.Dependencies, cvals)
ProcessDependencyConditions(c.Metadata.Dependencies, cvals) processDependencyConditions(c.Metadata.Dependencies, cvals)
// make a map of charts to remove // make a map of charts to remove
rm := map[string]struct{}{} rm := map[string]struct{}{}
for _, r := range c.Metadata.Dependencies { for _, r := range c.Metadata.Dependencies {
@ -191,7 +193,7 @@ func ProcessDependencyEnabled(c *chart.Chart, v map[string]interface{}) error {
// recursively call self to process sub dependencies // recursively call self to process sub dependencies
for _, t := range cd { for _, t := range cd {
if err := ProcessDependencyEnabled(t, cvals); err != nil { if err := processDependencyEnabled(t, cvals); err != nil {
return err return err
} }
} }
@ -276,11 +278,11 @@ func processImportValues(c *chart.Chart) error {
return nil return nil
} }
// ProcessDependencyImportValues imports specified chart values from child to parent. // processDependencyImportValues imports specified chart values from child to parent.
func ProcessDependencyImportValues(c *chart.Chart) error { func processDependencyImportValues(c *chart.Chart) error {
for _, d := range c.Dependencies() { for _, d := range c.Dependencies() {
// recurse // recurse
if err := ProcessDependencyImportValues(d); err != nil { if err := processDependencyImportValues(d); err != nil {
return err return err
} }
} }

@ -16,141 +16,134 @@ package chartutil
import ( import (
"sort" "sort"
"testing"
"strconv" "strconv"
"testing"
"github.com/ghodss/yaml"
"k8s.io/helm/pkg/chart" "k8s.io/helm/pkg/chart"
"k8s.io/helm/pkg/chart/loader" "k8s.io/helm/pkg/chart/loader"
"k8s.io/helm/pkg/version" "k8s.io/helm/pkg/version"
) )
func TestLoadDependency(t *testing.T) { func loadChart(t *testing.T, path string) *chart.Chart {
c, err := loader.Load("testdata/frobnitz") c, err := loader.Load(path)
if err != nil { if err != nil {
t.Fatalf("Failed to load testdata: %s", err) t.Fatalf("failed to load testdata: %s", err)
} }
verifyDependency(t, c) return c
} }
func TestLoadChartLock(t *testing.T) { func TestLoadDependency(t *testing.T) {
c, err := loader.Load("testdata/frobnitz") tests := []*chart.Dependency{
if err != nil { {Name: "alpine", Version: "0.1.0", Repository: "https://example.com/charts"},
t.Fatalf("Failed to load testdata: %s", err) {Name: "mariner", Version: "4.3.2", Repository: "https://example.com/charts"},
} }
verifyChartLock(t, c)
check := func(deps []*chart.Dependency) {
if len(deps) != 2 {
t.Errorf("expected 2 dependencies, got %d", len(deps))
}
for i, tt := range tests {
if deps[i].Name != tt.Name {
t.Errorf("expected dependency named %q, got %q", tt.Name, deps[i].Name)
}
if deps[i].Version != tt.Version {
t.Errorf("expected dependency named %q to have version %q, got %q", tt.Name, tt.Version, deps[i].Version)
}
if deps[i].Repository != tt.Repository {
t.Errorf("expected dependency named %q to have repository %q, got %q", tt.Name, tt.Repository, deps[i].Repository)
}
}
}
c := loadChart(t, "testdata/frobnitz")
check(c.Metadata.Dependencies)
check(c.Lock.Dependencies)
} }
func TestDependencyEnabled(t *testing.T) { func TestDependencyEnabled(t *testing.T) {
type M = map[string]interface{}
tests := []struct { tests := []struct {
name string name string
v []byte v M
e []string // expected charts including duplicates in alphanumeric order e []string // expected charts including duplicates in alphanumeric order
}{{ }{{
"tags with no effect", "tags with no effect",
[]byte("tags:\n nothinguseful: false\n\n"), M{"tags": M{"nothinguseful": false}},
[]string{"parentchart", "subchart1", "subcharta", "subchartb"}, []string{"parentchart", "parentchart.subchart1", "parentchart.subchart1.subcharta", "parentchart.subchart1.subchartb"},
}, {
"tags with no effect",
[]byte("tags:\n nothinguseful: false\n\n"),
[]string{"parentchart", "subchart1", "subcharta", "subchartb"},
}, { }, {
"tags disabling a group", "tags disabling a group",
[]byte("tags:\n front-end: false\n\n"), M{"tags": M{"front-end": false}},
[]string{"parentchart"}, []string{"parentchart"},
}, { }, {
"tags disabling a group and enabling a different group", "tags disabling a group and enabling a different group",
[]byte("tags:\n front-end: false\n\n back-end: true\n"), M{"tags": M{"front-end": false, "back-end": true}},
[]string{"parentchart", "subchart2", "subchartb", "subchartc"}, []string{"parentchart", "parentchart.subchart2", "parentchart.subchart2.subchartb", "parentchart.subchart2.subchartc"},
}, { }, {
"tags disabling only children, children still enabled since tag front-end=true in values.yaml", "tags disabling only children, children still enabled since tag front-end=true in values.yaml",
[]byte("tags:\n subcharta: false\n\n subchartb: false\n"), M{"tags": M{"subcharta": false, "subchartb": false}},
[]string{"parentchart", "subchart1", "subcharta", "subchartb"}, []string{"parentchart", "parentchart.subchart1", "parentchart.subchart1.subcharta", "parentchart.subchart1.subchartb"},
}, { }, {
"tags disabling all parents/children with additional tag re-enabling a parent", "tags disabling all parents/children with additional tag re-enabling a parent",
[]byte("tags:\n front-end: false\n\n subchart1: true\n\n back-end: false\n"), M{"tags": M{"front-end": false, "subchart1": true, "back-end": false}},
[]string{"parentchart", "subchart1"}, []string{"parentchart", "parentchart.subchart1"},
}, {
"tags with no effect",
[]byte("subchart1:\n nothinguseful: false\n\n"),
[]string{"parentchart", "subchart1", "subcharta", "subchartb"},
}, { }, {
"conditions enabling the parent charts, but back-end (b, c) is still disabled via values.yaml", "conditions enabling the parent charts, but back-end (b, c) is still disabled via values.yaml",
[]byte("subchart1:\n enabled: true\nsubchart2:\n enabled: true\n"), M{"subchart1": M{"enabled": true}, "subchart2": M{"enabled": true}},
[]string{"parentchart", "subchart1", "subchart2", "subcharta", "subchartb"}, []string{"parentchart", "parentchart.subchart1", "parentchart.subchart1.subcharta", "parentchart.subchart1.subchartb", "parentchart.subchart2"},
}, { }, {
"conditions disabling the parent charts, effectively disabling children", "conditions disabling the parent charts, effectively disabling children",
[]byte("subchart1:\n enabled: false\nsubchart2:\n enabled: false\n"), M{"subchart1": M{"enabled": false}, "subchart2": M{"enabled": false}},
[]string{"parentchart"}, []string{"parentchart"},
}, { }, {
"conditions a child using the second condition path of child's condition", "conditions a child using the second condition path of child's condition",
[]byte("subchart1:\n subcharta:\n enabled: false\n"), M{"subchart1": M{"subcharta": M{"enabled": false}}},
[]string{"parentchart", "subchart1", "subchartb"}, []string{"parentchart", "parentchart.subchart1", "parentchart.subchart1.subchartb"},
}, { }, {
"tags enabling a parent/child group with condition disabling one child", "tags enabling a parent/child group with condition disabling one child",
[]byte("subchartc:\n enabled: false\ntags:\n back-end: true\n"), M{"subchartc": M{"enabled": false}, "tags": M{"back-end": true}},
[]string{"parentchart", "subchart1", "subchart2", "subcharta", "subchartb", "subchartb"}, []string{"parentchart", "parentchart.subchart1", "parentchart.subchart1.subcharta", "parentchart.subchart1.subchartb", "parentchart.subchart2", "parentchart.subchart2.subchartb"},
}, { }, {
"tags will not enable a child if parent is explicitly disabled with condition", "tags will not enable a child if parent is explicitly disabled with condition",
[]byte("subchart1:\n enabled: false\ntags:\n front-end: true\n"), M{"subchart1": M{"enabled": false}, "tags": M{"front-end": true}},
[]string{"parentchart"}, []string{"parentchart"},
}} }}
for _, tc := range tests { for _, tc := range tests {
c, err := loader.Load("testdata/subpop") c := loadChart(t, "testdata/subpop")
if err != nil {
t.Fatalf("Failed to load testdata: %s", err)
}
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
verifyDependencyEnabled(t, c, tc.v, tc.e) if err := processDependencyEnabled(c, tc.v); err != nil {
}) t.Fatalf("error processing enabled dependencies %v", err)
} }
}
func verifyDependencyEnabled(t *testing.T, c *chart.Chart, v []byte, e []string) {
var m map[string]interface{}
yaml.Unmarshal(v, &m)
if err := ProcessDependencyEnabled(c, m); err != nil {
t.Errorf("Error processing enabled dependencies %v", err)
}
out := extractCharts(c, nil) names := extractChartNames(c)
// build list of chart names if len(names) != len(tc.e) {
var p []string t.Fatalf("slice lengths do not match got %v, expected %v", len(names), len(tc.e))
for _, r := range out { }
p = append(p, r.Name()) for i := range names {
} if names[i] != tc.e[i] {
//sort alphanumeric and compare to expectations t.Fatalf("slice values do not match got %v, expected %v", names, tc.e)
sort.Strings(p) }
if len(p) != len(e) { }
t.Errorf("Error slice lengths do not match got %v, expected %v", len(p), len(e)) })
return
}
for i := range p {
if p[i] != e[i] {
t.Errorf("Error slice values do not match got %v, expected %v", p[i], e[i])
}
} }
} }
// extractCharts recursively searches chart dependencies returning all charts found // extractCharts recursively searches chart dependencies returning all charts found
func extractCharts(c *chart.Chart, out []*chart.Chart) []*chart.Chart { func extractChartNames(c *chart.Chart) []string {
if len(c.Name()) > 0 { var out []string
out = append(out, c) var fn func(c *chart.Chart)
} fn = func(c *chart.Chart) {
for _, d := range c.Dependencies() { out = append(out, c.ChartPath())
out = extractCharts(d, out) for _, d := range c.Dependencies() {
fn(d)
}
} }
fn(c)
sort.Strings(out)
return out return out
} }
func TestProcessDependencyImportValues(t *testing.T) { func TestProcessDependencyImportValues(t *testing.T) {
c, err := loader.Load("testdata/subpop") c := loadChart(t, "testdata/subpop")
if err != nil {
t.Fatalf("Failed to load testdata: %s", err)
}
e := make(map[string]string) e := make(map[string]string)
@ -213,71 +206,57 @@ func TestProcessDependencyImportValues(t *testing.T) {
e["SCBexported2A"] = "blaster" e["SCBexported2A"] = "blaster"
e["global.SC1exported2.all.SC1exported3"] = "SC1expstr" e["global.SC1exported2.all.SC1exported3"] = "SC1expstr"
verifyDependencyImportValues(t, c, e) if err := processDependencyImportValues(c); err != nil {
} t.Fatalf("processing import values dependencies %v", err)
func verifyDependencyImportValues(t *testing.T, c *chart.Chart, e map[string]string) {
if err := ProcessDependencyImportValues(c); err != nil {
t.Fatalf("Error processing import values dependencies %v", err)
} }
cc := Values(c.Values) cc := Values(c.Values)
for kk, vv := range e { for kk, vv := range e {
pv, err := cc.PathValue(kk) pv, err := cc.PathValue(kk)
if err != nil { if err != nil {
t.Fatalf("Error retrieving import values table %v %v", kk, err) t.Fatalf("retrieving import values table %v %v", kk, err)
return
} }
switch pv.(type) { switch pv.(type) {
case float64: case float64:
s := strconv.FormatFloat(pv.(float64), 'f', -1, 64) if s := strconv.FormatFloat(pv.(float64), 'f', -1, 64); s != vv {
if s != vv { t.Errorf("failed to match imported float value %v with expected %v", s, vv)
t.Errorf("Failed to match imported float value %v with expected %v", s, vv)
return
} }
case bool: case bool:
b := strconv.FormatBool(pv.(bool)) if b := strconv.FormatBool(pv.(bool)); b != vv {
if b != vv { t.Errorf("failed to match imported bool value %v with expected %v", b, vv)
t.Errorf("Failed to match imported bool value %v with expected %v", b, vv)
return
} }
default: default:
if pv.(string) != vv { if pv.(string) != vv {
t.Errorf("Failed to match imported string value %q with expected %q", pv, vv) t.Errorf("failed to match imported string value %q with expected %q", pv, vv)
return
} }
} }
} }
} }
func TestGetAliasDependency(t *testing.T) { func TestGetAliasDependency(t *testing.T) {
c, err := loader.Load("testdata/frobnitz") c := loadChart(t, "testdata/frobnitz")
if err != nil {
t.Fatalf("Failed to load testdata: %s", err)
}
req := c.Metadata.Dependencies req := c.Metadata.Dependencies
if len(req) == 0 { if len(req) == 0 {
t.Fatalf("There are no dependencies to test") t.Fatalf("there are no dependencies to test")
} }
// Success case // Success case
aliasChart := getAliasDependency(c.Dependencies(), req[0]) aliasChart := getAliasDependency(c.Dependencies(), req[0])
if aliasChart == nil { if aliasChart == nil {
t.Fatalf("Failed to get dependency chart for alias %s", req[0].Name) t.Fatalf("failed to get dependency chart for alias %s", req[0].Name)
} }
if req[0].Alias != "" { if req[0].Alias != "" {
if aliasChart.Name() != req[0].Alias { if aliasChart.Name() != req[0].Alias {
t.Fatalf("Dependency chart name should be %s but got %s", req[0].Alias, aliasChart.Name()) t.Fatalf("dependency chart name should be %s but got %s", req[0].Alias, aliasChart.Name())
} }
} else if aliasChart.Name() != req[0].Name { } else if aliasChart.Name() != req[0].Name {
t.Fatalf("Dependency chart name should be %s but got %s", req[0].Name, aliasChart.Name()) t.Fatalf("dependency chart name should be %s but got %s", req[0].Name, aliasChart.Name())
} }
if req[0].Version != "" { if req[0].Version != "" {
if !version.IsCompatibleRange(req[0].Version, aliasChart.Metadata.Version) { if !version.IsCompatibleRange(req[0].Version, aliasChart.Metadata.Version) {
t.Fatalf("Dependency chart version is not in the compatible range") t.Fatalf("dependency chart version is not in the compatible range")
} }
} }
@ -289,161 +268,99 @@ func TestGetAliasDependency(t *testing.T) {
req[0].Version = "something else which is not in the compatible range" req[0].Version = "something else which is not in the compatible range"
if version.IsCompatibleRange(req[0].Version, aliasChart.Metadata.Version) { if version.IsCompatibleRange(req[0].Version, aliasChart.Metadata.Version) {
t.Fatalf("Dependency chart version which is not in the compatible range should cause a failure other than a success ") t.Fatalf("dependency chart version which is not in the compatible range should cause a failure other than a success ")
} }
} }
func TestDependentChartAliases(t *testing.T) { func TestDependentChartAliases(t *testing.T) {
c, err := loader.Load("testdata/dependent-chart-alias") c := loadChart(t, "testdata/dependent-chart-alias")
if err != nil {
t.Fatalf("Failed to load testdata: %s", err)
}
if len(c.Dependencies()) == 0 { if len(c.Dependencies()) != 2 {
t.Fatal("There are no dependencies to run this test") t.Fatalf("expected 2 dependencies for this chart, but got %d", len(c.Dependencies()))
} }
origLength := len(c.Dependencies()) if err := processDependencyEnabled(c, c.Values); err != nil {
if err := ProcessDependencyEnabled(c, c.Values); err != nil { t.Fatalf("expected no errors but got %q", err)
t.Fatalf("Expected no errors but got %q", err)
} }
if len(c.Dependencies()) == origLength { if len(c.Dependencies()) != 3 {
t.Fatal("Expected alias dependencies to be added, but did not got that") t.Fatal("expected alias dependencies to be added")
} }
if len(c.Dependencies()) != len(c.Metadata.Dependencies) { if len(c.Dependencies()) != len(c.Metadata.Dependencies) {
t.Fatalf("Expected number of chart dependencies %d, but got %d", len(c.Metadata.Dependencies), len(c.Dependencies())) t.Fatalf("expected number of chart dependencies %d, but got %d", len(c.Metadata.Dependencies), len(c.Dependencies()))
} }
// FIXME test for correct aliases
} }
func TestDependentChartWithSubChartsAbsentInDependency(t *testing.T) { func TestDependentChartWithSubChartsAbsentInDependency(t *testing.T) {
c, err := loader.Load("testdata/dependent-chart-no-requirements-yaml") c := loadChart(t, "testdata/dependent-chart-no-requirements-yaml")
if err != nil {
t.Fatalf("Failed to load testdata: %s", err)
}
if len(c.Dependencies()) != 2 { if len(c.Dependencies()) != 2 {
t.Fatalf("Expected 2 dependencies for this chart, but got %d", len(c.Dependencies())) t.Fatalf("expected 2 dependencies for this chart, but got %d", len(c.Dependencies()))
} }
origLength := len(c.Dependencies()) if err := processDependencyEnabled(c, c.Values); err != nil {
if err := ProcessDependencyEnabled(c, c.Values); err != nil { t.Fatalf("expected no errors but got %q", err)
t.Fatalf("Expected no errors but got %q", err)
} }
if len(c.Dependencies()) != origLength { if len(c.Dependencies()) != 2 {
t.Fatal("Expected no changes in dependencies to be, but did something got changed") t.Fatal("expected no changes in dependencies")
} }
} }
func TestDependentChartWithSubChartsHelmignore(t *testing.T) { func TestDependentChartWithSubChartsHelmignore(t *testing.T) {
if _, err := loader.Load("testdata/dependent-chart-helmignore"); err != nil { // FIXME what does this test?
t.Fatalf("Failed to load testdata: %s", err) loadChart(t, "testdata/dependent-chart-helmignore")
}
} }
func TestDependentChartsWithSubChartsSymlink(t *testing.T) { func TestDependentChartsWithSubChartsSymlink(t *testing.T) {
c, err := loader.Load("testdata/joonix") c := loadChart(t, "testdata/joonix")
if err != nil {
t.Fatalf("Failed to load testdata: %s", err)
}
if c.Name() != "joonix" { if c.Name() != "joonix" {
t.Fatalf("Unexpected chart name: %s", c.Name()) t.Fatalf("unexpected chart name: %s", c.Name())
} }
if n := len(c.Dependencies()); n != 1 { if n := len(c.Dependencies()); n != 1 {
t.Fatalf("Expected 1 dependency for this chart, but got %d", n) t.Fatalf("expected 1 dependency for this chart, but got %d", n)
} }
} }
func TestDependentChartsWithSubchartsAllSpecifiedInDependency(t *testing.T) { func TestDependentChartsWithSubchartsAllSpecifiedInDependency(t *testing.T) {
c, err := loader.Load("testdata/dependent-chart-with-all-in-requirements-yaml") c := loadChart(t, "testdata/dependent-chart-with-all-in-requirements-yaml")
if err != nil {
t.Fatalf("Failed to load testdata: %s", err)
}
if len(c.Dependencies()) == 0 { if len(c.Dependencies()) != 2 {
t.Fatal("There are no dependencies to run this test") t.Fatalf("expected 2 dependencies for this chart, but got %d", len(c.Dependencies()))
} }
origLength := len(c.Dependencies()) if err := processDependencyEnabled(c, c.Values); err != nil {
if err := ProcessDependencyEnabled(c, c.Values); err != nil { t.Fatalf("expected no errors but got %q", err)
t.Fatalf("Expected no errors but got %q", err)
} }
if len(c.Dependencies()) != origLength { if len(c.Dependencies()) != 2 {
t.Fatal("Expected no changes in dependencies to be, but did something got changed") t.Fatal("expected no changes in dependencies")
} }
if len(c.Dependencies()) != len(c.Metadata.Dependencies) { if len(c.Dependencies()) != len(c.Metadata.Dependencies) {
t.Fatalf("Expected number of chart dependencies %d, but got %d", len(c.Metadata.Dependencies), len(c.Dependencies())) t.Fatalf("expected number of chart dependencies %d, but got %d", len(c.Metadata.Dependencies), len(c.Dependencies()))
} }
} }
func TestDependentChartsWithSomeSubchartsSpecifiedInDependency(t *testing.T) { func TestDependentChartsWithSomeSubchartsSpecifiedInDependency(t *testing.T) {
c, err := loader.Load("testdata/dependent-chart-with-mixed-requirements-yaml") c := loadChart(t, "testdata/dependent-chart-with-mixed-requirements-yaml")
if err != nil {
t.Fatalf("Failed to load testdata: %s", err)
}
if len(c.Dependencies()) == 0 {
t.Fatal("There are no dependencies to run this test")
}
origLength := len(c.Dependencies())
if err := ProcessDependencyEnabled(c, c.Values); err != nil {
t.Fatalf("Expected no errors but got %q", err)
}
if len(c.Dependencies()) != origLength { if len(c.Dependencies()) != 2 {
t.Fatal("Expected no changes in dependencies to be, but did something got changed") t.Fatalf("expected 2 dependencies for this chart, but got %d", len(c.Dependencies()))
} }
if len(c.Dependencies()) <= len(c.Metadata.Dependencies) { if err := processDependencyEnabled(c, c.Values); err != nil {
t.Fatalf("Expected more dependencies than specified in Chart.yaml(%d), but got %d", len(c.Metadata.Dependencies), len(c.Dependencies())) t.Fatalf("expected no errors but got %q", err)
} }
}
func verifyDependency(t *testing.T, c *chart.Chart) { if len(c.Dependencies()) != 2 {
if len(c.Metadata.Dependencies) != 2 { t.Fatal("expected no changes in dependencies")
t.Errorf("Expected 2 dependencies, got %d", len(c.Metadata.Dependencies))
}
tests := []*chart.Dependency{
{Name: "alpine", Version: "0.1.0", Repository: "https://example.com/charts"},
{Name: "mariner", Version: "4.3.2", Repository: "https://example.com/charts"},
}
for i, tt := range tests {
d := c.Metadata.Dependencies[i]
if d.Name != tt.Name {
t.Errorf("Expected dependency named %q, got %q", tt.Name, d.Name)
}
if d.Version != tt.Version {
t.Errorf("Expected dependency named %q to have version %q, got %q", tt.Name, tt.Version, d.Version)
}
if d.Repository != tt.Repository {
t.Errorf("Expected dependency named %q to have repository %q, got %q", tt.Name, tt.Repository, d.Repository)
}
} }
}
func verifyChartLock(t *testing.T, c *chart.Chart) { if len(c.Metadata.Dependencies) != 1 {
if len(c.Metadata.Dependencies) != 2 { t.Fatalf("expected 1 dependency specified in Chart.yaml, got %d", len(c.Metadata.Dependencies))
t.Errorf("Expected 2 dependencies, got %d", len(c.Metadata.Dependencies))
}
tests := []*chart.Dependency{
{Name: "alpine", Version: "0.1.0", Repository: "https://example.com/charts"},
{Name: "mariner", Version: "4.3.2", Repository: "https://example.com/charts"},
}
for i, tt := range tests {
d := c.Metadata.Dependencies[i]
if d.Name != tt.Name {
t.Errorf("Expected dependency named %q, got %q", tt.Name, d.Name)
}
if d.Version != tt.Version {
t.Errorf("Expected dependency named %q to have version %q, got %q", tt.Name, tt.Version, d.Version)
}
if d.Repository != tt.Repository {
t.Errorf("Expected dependency named %q to have repository %q, got %q", tt.Name, tt.Repository, d.Repository)
}
} }
} }

@ -3,33 +3,33 @@ description: A Helm chart for Kubernetes
name: parentchart name: parentchart
version: 0.1.0 version: 0.1.0
dependencies: dependencies:
- name: subchart1 - name: subchart1
repository: http://localhost:10191 repository: http://localhost:10191
version: 0.1.0 version: 0.1.0
condition: subchart1.enabled condition: subchart1.enabled
tags: tags:
- front-end - front-end
- subchart1 - subchart1
import-values: import-values:
- child: SC1data - child: SC1data
parent: imported-chart1 parent: imported-chart1
- child: SC1data - child: SC1data
parent: overridden-chart1 parent: overridden-chart1
- child: imported-chartA - child: imported-chartA
parent: imported-chartA parent: imported-chartA
- child: imported-chartA-B - child: imported-chartA-B
parent: imported-chartA-B parent: imported-chartA-B
- child: overridden-chartA-B - child: overridden-chartA-B
parent: overridden-chartA-B parent: overridden-chartA-B
- child: SCBexported1A - child: SCBexported1A
parent: . parent: .
- SCBexported2 - SCBexported2
- SC1exported1 - SC1exported1
- name: subchart2 - name: subchart2
repository: http://localhost:10191 repository: http://localhost:10191
version: 0.1.0 version: 0.1.0
condition: subchart2.enabled condition: subchart2.enabled
tags: tags:
- back-end - back-end
- subchart2 - subchart2

@ -3,34 +3,34 @@ description: A Helm chart for Kubernetes
name: subchart1 name: subchart1
version: 0.1.0 version: 0.1.0
dependencies: dependencies:
- name: subcharta - name: subcharta
repository: http://localhost:10191 repository: http://localhost:10191
version: 0.1.0 version: 0.1.0
condition: subcharta.enabled,subchart1.subcharta.enabled condition: subcharta.enabled,subchart1.subcharta.enabled
tags: tags:
- front-end - front-end
- subcharta - subcharta
import-values: import-values:
- child: SCAdata - child: SCAdata
parent: imported-chartA parent: imported-chartA
- child: SCAdata - child: SCAdata
parent: overridden-chartA parent: overridden-chartA
- child: SCAdata - child: SCAdata
parent: imported-chartA-B parent: imported-chartA-B
- name: subchartb - name: subchartb
repository: http://localhost:10191 repository: http://localhost:10191
version: 0.1.0 version: 0.1.0
condition: subchartb.enabled condition: subchartb.enabled
import-values: import-values:
- child: SCBdata - child: SCBdata
parent: imported-chartB parent: imported-chartB
- child: SCBdata - child: SCBdata
parent: imported-chartA-B parent: imported-chartA-B
- child: exports.SCBexported2 - child: exports.SCBexported2
parent: exports.SCBexported2 parent: exports.SCBexported2
- SCBexported1 - SCBexported1
tags: tags:
- front-end - front-end
- subchartb - subchartb

@ -3,17 +3,17 @@ description: A Helm chart for Kubernetes
name: subchart2 name: subchart2
version: 0.1.0 version: 0.1.0
dependencies: dependencies:
- name: subchartb - name: subchartb
repository: http://localhost:10191 repository: http://localhost:10191
version: 0.1.0 version: 0.1.0
condition: subchartb.enabled,subchart2.subchartb.enabled condition: subchartb.enabled,subchart2.subchartb.enabled
tags: tags:
- back-end - back-end
- subchartb - subchartb
- name: subchartc - name: subchartc
repository: http://localhost:10191 repository: http://localhost:10191
version: 0.1.0 version: 0.1.0
condition: subchartc.enabled condition: subchartc.enabled
tags: tags:
- back-end - back-end
- subchartc - subchartc

@ -38,4 +38,3 @@ overridden-chartA-B:
tags: tags:
front-end: true front-end: true
back-end: false back-end: false

@ -17,6 +17,7 @@ limitations under the License.
package chartutil package chartutil
import ( import (
"fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"log" "log"
@ -29,10 +30,14 @@ import (
) )
// ErrNoTable indicates that a chart does not have a matching table. // ErrNoTable indicates that a chart does not have a matching table.
type ErrNoTable error type ErrNoTable string
func (e ErrNoTable) Error() string { return fmt.Sprintf("%q is not a table", e) }
// ErrNoValue indicates that Values does not contain a key with a value // ErrNoValue indicates that Values does not contain a key with a value
type ErrNoValue error type ErrNoValue string
func (e ErrNoValue) Error() string { return fmt.Sprintf("%q is not a value", e) }
// GlobalKey is the name of the Values key that is used for storing global vars. // GlobalKey is the name of the Values key that is used for storing global vars.
const GlobalKey = "global" const GlobalKey = "global"
@ -63,9 +68,8 @@ func (v Values) Table(name string) (Values, error) {
var err error var err error
for _, n := range parsePath(name) { for _, n := range parsePath(name) {
table, err = tableLookup(table, n) if table, err = tableLookup(table, n); err != nil {
if err != nil { break
return table, err
} }
} }
return table, err return table, err
@ -95,7 +99,7 @@ func (v Values) Encode(w io.Writer) error {
func tableLookup(v Values, simple string) (Values, error) { func tableLookup(v Values, simple string) (Values, error) {
v2, ok := v[simple] v2, ok := v[simple]
if !ok { if !ok {
return v, ErrNoTable(errors.Errorf("no table named %q (%v)", simple, v)) return v, ErrNoTable(simple)
} }
if vv, ok := v2.(map[string]interface{}); ok { if vv, ok := v2.(map[string]interface{}); ok {
return vv, nil return vv, nil
@ -108,8 +112,7 @@ func tableLookup(v Values, simple string) (Values, error) {
return vv, nil return vv, nil
} }
var e ErrNoTable = errors.Errorf("no table named %q", simple) return Values{}, ErrNoTable(simple)
return Values{}, e
} }
// ReadValues will parse YAML byte data into a Values. // ReadValues will parse YAML byte data into a Values.
@ -150,19 +153,18 @@ func CoalesceValues(chrt *chart.Chart, vals []byte) (Values, error) {
return cvals, err return cvals, err
} }
} }
if _, err := coalesce(chrt, cvals); err != nil {
coalesce(chrt, cvals) return cvals, err
}
return coalesceDeps(chrt, cvals) return coalesceDeps(chrt, cvals)
} }
// coalesce coalesces the dest values and the chart values, giving priority to the dest values. // coalesce coalesces the dest values and the chart values, giving priority to the dest values.
// //
// This is a helper function for CoalesceValues. // This is a helper function for CoalesceValues.
func coalesce(ch *chart.Chart, dest map[string]interface{}) map[string]interface{} { func coalesce(ch *chart.Chart, dest map[string]interface{}) (map[string]interface{}, error) {
coalesceValues(ch, dest) coalesceValues(ch, dest)
coalesceDeps(ch, dest) return coalesceDeps(ch, dest)
return dest
} }
// coalesceDeps coalesces the dependencies of the given chart. // coalesceDeps coalesces the dependencies of the given chart.
@ -181,7 +183,11 @@ func coalesceDeps(chrt *chart.Chart, dest map[string]interface{}) (map[string]in
coalesceGlobals(dvmap, dest) coalesceGlobals(dvmap, dest)
// Now coalesce the rest of the values. // Now coalesce the rest of the values.
dest[subchart.Name()] = coalesce(subchart, dvmap) var err error
dest[subchart.Name()], err = coalesce(subchart, dvmap)
if err != nil {
return dest, err
}
} }
} }
return dest, nil return dest, nil
@ -363,20 +369,20 @@ func (v Values) pathValue(path []string) (interface{}, error) {
if _, ok := v[path[0]]; ok && !istable(v[path[0]]) { if _, ok := v[path[0]]; ok && !istable(v[path[0]]) {
return v[path[0]], nil return v[path[0]], nil
} }
return nil, ErrNoValue(errors.Errorf("%v is not a value", path[0])) return nil, ErrNoValue(path[0])
} }
key, path := path[len(path)-1], path[:len(path)-1] key, path := path[len(path)-1], path[:len(path)-1]
// get our table for table path // get our table for table path
t, err := v.Table(joinPath(path...)) t, err := v.Table(joinPath(path...))
if err != nil { if err != nil {
return nil, ErrNoValue(errors.Errorf("%v is not a value", key)) return nil, ErrNoValue(key)
} }
// check table for key and ensure value is not a table // check table for key and ensure value is not a table
if k, ok := t[key]; ok && !istable(k) { if k, ok := t[key]; ok && !istable(k) {
return k, nil return k, nil
} }
return nil, ErrNoValue(errors.Errorf("key not found: %s", key)) return nil, ErrNoValue(key)
} }
func parsePath(key string) []string { return strings.Split(key, ".") } func parsePath(key string) []string { return strings.Split(key, ".") }

@ -26,7 +26,6 @@ import (
kversion "k8s.io/apimachinery/pkg/version" kversion "k8s.io/apimachinery/pkg/version"
"k8s.io/helm/pkg/chart" "k8s.io/helm/pkg/chart"
"k8s.io/helm/pkg/chart/loader"
"k8s.io/helm/pkg/version" "k8s.io/helm/pkg/version"
) )
@ -290,11 +289,7 @@ pequod:
` `
func TestCoalesceValues(t *testing.T) { func TestCoalesceValues(t *testing.T) {
tchart := "testdata/moby" c := loadChart(t, "testdata/moby")
c, err := loader.LoadDir(tchart)
if err != nil {
t.Fatal(err)
}
v, err := CoalesceValues(c, []byte(testCoalesceValuesYaml)) v, err := CoalesceValues(c, []byte(testCoalesceValuesYaml))
if err != nil { if err != nil {

@ -97,15 +97,10 @@ func (c *Client) InstallReleaseFromChart(chart *chart.Chart, ns string, opts ...
} }
var m map[string]interface{} var m map[string]interface{}
yaml.Unmarshal(req.Values, &m) yaml.Unmarshal(req.Values, &m)
err := chartutil.ProcessDependencyEnabled(req.Chart, m) err := chartutil.ProcessDependencies(req.Chart, m)
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = chartutil.ProcessDependencyImportValues(req.Chart)
if err != nil {
return nil, err
}
return c.tiller.InstallRelease(req) return c.tiller.InstallRelease(req)
} }
@ -171,13 +166,9 @@ func (c *Client) UpdateReleaseFromChart(rlsName string, chart *chart.Chart, opts
if err := yaml.Unmarshal(req.Values, &m); err != nil { if err := yaml.Unmarshal(req.Values, &m); err != nil {
return nil, err return nil, err
} }
if err := chartutil.ProcessDependencyEnabled(req.Chart, m); err != nil { if err := chartutil.ProcessDependencies(req.Chart, m); err != nil {
return nil, err return nil, err
} }
if err := chartutil.ProcessDependencyImportValues(req.Chart); err != nil {
return nil, err
}
return c.tiller.UpdateRelease(req) return c.tiller.UpdateRelease(req)
} }

Loading…
Cancel
Save