diff --git a/cmd/helm/lint.go b/cmd/helm/lint.go index d0159d34b..ca7fd9f39 100644 --- a/cmd/helm/lint.go +++ b/cmd/helm/lint.go @@ -52,6 +52,7 @@ type lintCmd struct { strict bool paths []string out io.Writer + version string } func newLintCmd(out io.Writer) *cobra.Command { @@ -77,6 +78,7 @@ func newLintCmd(out io.Writer) *cobra.Command { cmd.Flags().StringArrayVar(&l.fValues, "set-file", []string{}, "set values from respective files specified via the command line (can specify multiple or separate values with commas: key1=path1,key2=path2)") cmd.Flags().StringVar(&l.namespace, "namespace", "default", "namespace to put the release into") cmd.Flags().BoolVar(&l.strict, "strict", false, "fail on lint warnings") + cmd.Flags().StringVar(&l.version, "version", "", "version of the chart") return cmd } @@ -100,7 +102,7 @@ func (l *lintCmd) run() error { var total int var failures int for _, path := range l.paths { - if linter, err := lintChart(path, rvals, l.namespace, l.strict); err != nil { + if linter, err := lintChart(path, rvals, l.namespace, l.strict, l.version); err != nil { fmt.Println("==> Skipping", path) fmt.Println(err) if err == errLintNoChart { @@ -135,7 +137,7 @@ func (l *lintCmd) run() error { return nil } -func lintChart(path string, vals []byte, namespace string, strict bool) (support.Linter, error) { +func lintChart(path string, vals []byte, namespace string, strict bool, version string) (support.Linter, error) { var chartPath string linter := support.Linter{} @@ -171,7 +173,7 @@ func lintChart(path string, vals []byte, namespace string, strict bool) (support return linter, errLintNoChart } - return lint.All(chartPath, vals, namespace, strict), nil + return lint.All(chartPath, vals, namespace, strict, version), nil } // vals merges values from files specified via -f/--values and diff --git a/cmd/helm/lint_test.go b/cmd/helm/lint_test.go index 67775893b..327dfe971 100644 --- a/cmd/helm/lint_test.go +++ b/cmd/helm/lint_test.go @@ -29,26 +29,32 @@ var ( invalidArchivedChartPath = "testdata/testcharts/invalidcompressedchart0.1.0.tgz" chartDirPath = "testdata/testcharts/decompressedchart/" chartMissingManifest = "testdata/testcharts/chart-missing-manifest" + chartMissingVersion = "testdata/testcharts/chart-missing-version" ) func TestLintChart(t *testing.T) { - if _, err := lintChart(chartDirPath, values, namespace, strict); err != nil { + if _, err := lintChart(chartDirPath, values, namespace, strict, ""); err != nil { t.Errorf("%s", err) } - if _, err := lintChart(archivedChartPath, values, namespace, strict); err != nil { + if _, err := lintChart(archivedChartPath, values, namespace, strict, ""); err != nil { t.Errorf("%s", err) } - if _, err := lintChart(archivedChartPathWithHyphens, values, namespace, strict); err != nil { + if _, err := lintChart(archivedChartPathWithHyphens, values, namespace, strict, ""); err != nil { t.Errorf("%s", err) } - if _, err := lintChart(invalidArchivedChartPath, values, namespace, strict); err == nil { + if _, err := lintChart(chartMissingVersion, values, namespace, strict, "0.1.0"); err != nil { + t.Errorf("%s", err) + } + + if _, err := lintChart(invalidArchivedChartPath, values, namespace, strict, ""); err == nil { t.Errorf("Expected a chart parsing error") } - if _, err := lintChart(chartMissingManifest, values, namespace, strict); err == nil { + if _, err := lintChart(chartMissingManifest, values, namespace, strict, ""); err == nil { t.Errorf("Expected a chart parsing error") } + } diff --git a/cmd/helm/testdata/testcharts/chart-missing-version/.helmignore b/cmd/helm/testdata/testcharts/chart-missing-version/.helmignore new file mode 100644 index 000000000..435b756d8 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-missing-version/.helmignore @@ -0,0 +1,5 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +.git diff --git a/cmd/helm/testdata/testcharts/chart-missing-version/Chart.yaml b/cmd/helm/testdata/testcharts/chart-missing-version/Chart.yaml new file mode 100644 index 000000000..b4aef7e02 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-missing-version/Chart.yaml @@ -0,0 +1,2 @@ +description: A Helm chart for Kubernetes +name: decompressedchart \ No newline at end of file diff --git a/cmd/helm/testdata/testcharts/chart-missing-version/values.yaml b/cmd/helm/testdata/testcharts/chart-missing-version/values.yaml new file mode 100644 index 000000000..a940d1fd9 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-missing-version/values.yaml @@ -0,0 +1,4 @@ +# Default values for decompressedchart. +# This is a YAML-formatted file. +# Declare name/value pairs to be passed into your templates. + name: my-decompressed-chart diff --git a/docs/helm/helm_lint.md b/docs/helm/helm_lint.md index bf168184e..720f97d7e 100644 --- a/docs/helm/helm_lint.md +++ b/docs/helm/helm_lint.md @@ -27,6 +27,7 @@ helm lint [flags] PATH --set-string stringArray set STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2) --strict fail on lint warnings -f, --values valueFiles specify values in a YAML file (can specify multiple) (default []) + --version string version of the chart ``` ### Options inherited from parent commands @@ -45,4 +46,4 @@ helm lint [flags] PATH * [helm](helm.md) - The Helm package manager for Kubernetes. -###### Auto generated by spf13/cobra on 1-Aug-2018 +###### Auto generated by spf13/cobra on 20-Dec-2018 diff --git a/pkg/lint/lint.go b/pkg/lint/lint.go index aa8df5814..d9be16d7d 100644 --- a/pkg/lint/lint.go +++ b/pkg/lint/lint.go @@ -24,12 +24,12 @@ import ( ) // All runs all of the available linters on the given base directory. -func All(basedir string, values []byte, namespace string, strict bool) support.Linter { +func All(basedir string, values []byte, namespace string, strict bool, version string) support.Linter { // Using abs path to get directory context chartDir, _ := filepath.Abs(basedir) linter := support.Linter{ChartDir: chartDir} - rules.Chartfile(&linter) + rules.Chartfile(&linter, version) rules.Values(&linter) rules.Templates(&linter, values, namespace, strict) return linter diff --git a/pkg/lint/lint_test.go b/pkg/lint/lint_test.go index 84dfbf508..03b2cfce6 100644 --- a/pkg/lint/lint_test.go +++ b/pkg/lint/lint_test.go @@ -28,14 +28,16 @@ var values = []byte{} const namespace = "testNamespace" const strict = false +const version = "199.44.12345-Alpha.1+cafe009" const badChartDir = "rules/testdata/badchartfile" const badValuesFileDir = "rules/testdata/badvaluesfile" const badYamlFileDir = "rules/testdata/albatross" const goodChartDir = "rules/testdata/goodone" +const chartNoVersionDir = "rules/testdata/noversionchart" func TestBadChart(t *testing.T) { - m := All(badChartDir, values, namespace, strict).Messages + m := All(badChartDir, values, namespace, strict, "").Messages if len(m) != 5 { t.Errorf("Number of errors %v", len(m)) t.Errorf("All didn't fail with expected errors, got %#v", m) @@ -68,10 +70,28 @@ func TestBadChart(t *testing.T) { if !e || !e2 || !e3 || !w || !i { t.Errorf("Didn't find all the expected errors, got %#v", m) } + + m = All(chartNoVersionDir, values, namespace, strict, "").Messages + if len(m) != 1 { + t.Errorf("Number of errors %v, expected 1", len(m)) + } + + e = false + // There should be one ERROR messages, check for them + for _, msg := range m { + if msg.Severity == support.ErrorSev { + if strings.Contains(msg.Err.Error(), "version is required") { + e = true + } + } + if !e { + t.Errorf("Didn't find all the expected errors, got %#v expected: \"version is required\"", msg.Err.Error()) + } + } } func TestInvalidYaml(t *testing.T) { - m := All(badYamlFileDir, values, namespace, strict).Messages + m := All(badYamlFileDir, values, namespace, strict, "").Messages if len(m) != 1 { t.Fatalf("All didn't fail with expected errors, got %#v", m) } @@ -81,7 +101,7 @@ func TestInvalidYaml(t *testing.T) { } func TestBadValues(t *testing.T) { - m := All(badValuesFileDir, values, namespace, strict).Messages + m := All(badValuesFileDir, values, namespace, strict, "").Messages if len(m) != 1 { t.Fatalf("All didn't fail with expected errors, got %#v", m) } @@ -91,8 +111,13 @@ func TestBadValues(t *testing.T) { } func TestGoodChart(t *testing.T) { - m := All(goodChartDir, values, namespace, strict).Messages + m := All(goodChartDir, values, namespace, strict, "").Messages if len(m) != 0 { t.Errorf("All failed but shouldn't have: %#v", m) } + + m = All(chartNoVersionDir, values, namespace, strict, "199.44.12345-Alpha.1+cafe009").Messages + if len(m) != 0 { + t.Errorf("All failed but shouldn't have: %#v", m[0].Err) + } } diff --git a/pkg/lint/rules/chartfile.go b/pkg/lint/rules/chartfile.go index 12f028af1..ce375bd66 100644 --- a/pkg/lint/rules/chartfile.go +++ b/pkg/lint/rules/chartfile.go @@ -32,7 +32,7 @@ import ( ) // Chartfile runs a set of linter rules related to Chart.yaml file -func Chartfile(linter *support.Linter) { +func Chartfile(linter *support.Linter, version string) { chartFileName := "Chart.yaml" chartPath := filepath.Join(linter.ChartDir, chartFileName) @@ -46,6 +46,10 @@ func Chartfile(linter *support.Linter) { return } + if chartFile.Version == "" && version != "" { + chartFile.Version = version + } + linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartName(chartFile)) linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartNameDirMatch(linter.ChartDir, chartFile)) diff --git a/pkg/lint/rules/chartfile_test.go b/pkg/lint/rules/chartfile_test.go index 235e5fc4c..f6eb168af 100644 --- a/pkg/lint/rules/chartfile_test.go +++ b/pkg/lint/rules/chartfile_test.go @@ -29,8 +29,10 @@ import ( ) const ( - badChartDir = "testdata/badchartfile" - goodChartDir = "testdata/goodone" + badChartDir = "testdata/badchartfile" + goodChartDir = "testdata/goodone" + noVersionChartDir = "testdata/noversionchart" + version = "1.0" ) var ( @@ -75,7 +77,7 @@ func TestValidateChartName(t *testing.T) { func TestValidateChartNameDirMatch(t *testing.T) { err := validateChartNameDirMatch(goodChartDir, goodChart) if err != nil { - t.Errorf("validateChartNameDirMatch to return no error, gor a linter error") + t.Errorf("validateChartNameDirMatch to return no error, got a linter error") } // It has not name err = validateChartNameDirMatch(badChartDir, badChart) @@ -223,7 +225,7 @@ func TestValidateChartIconURL(t *testing.T) { func TestChartfile(t *testing.T) { linter := support.Linter{ChartDir: badChartDir} - Chartfile(&linter) + Chartfile(&linter, "") msgs := linter.Messages if len(msgs) != 4 { @@ -246,4 +248,15 @@ func TestChartfile(t *testing.T) { t.Errorf("Unexpected message 3: %s", msgs[3].Err) } + linter = support.Linter{ChartDir: noVersionChartDir} + Chartfile(&linter, "") + msgs = linter.Messages + + if len(msgs) != 1 { + t.Errorf("Expected 1 errors, got %d", len(msgs)) + } + + if !strings.Contains(msgs[0].Err.Error(), "version is required") { + t.Errorf("Unexpected message 0: %s, expected \"version is required\"", msgs[0].Err) + } } diff --git a/pkg/lint/rules/testdata/noversionchart/Chart.yaml b/pkg/lint/rules/testdata/noversionchart/Chart.yaml new file mode 100644 index 000000000..5a267162f --- /dev/null +++ b/pkg/lint/rules/testdata/noversionchart/Chart.yaml @@ -0,0 +1,3 @@ +name: noversionchart +description: no version chart for testing +icon: http://riverrun.io diff --git a/pkg/lint/rules/testdata/noversionchart/templates/noversionchart.yaml b/pkg/lint/rules/testdata/noversionchart/templates/noversionchart.yaml new file mode 100644 index 000000000..0e77f46f2 --- /dev/null +++ b/pkg/lint/rules/testdata/noversionchart/templates/noversionchart.yaml @@ -0,0 +1,2 @@ +metadata: + name: {{.Values.name | default "foo" | title}} diff --git a/pkg/lint/rules/testdata/noversionchart/values.yaml b/pkg/lint/rules/testdata/noversionchart/values.yaml new file mode 100644 index 000000000..c143703b8 --- /dev/null +++ b/pkg/lint/rules/testdata/noversionchart/values.yaml @@ -0,0 +1 @@ +name: "noversionchart here"