From dfb5a5e8ccc2ae41c50159e8cdcf5066b63ec931 Mon Sep 17 00:00:00 2001 From: Nandor Kracser Date: Thu, 8 Oct 2020 13:27:51 +0200 Subject: [PATCH] lint: lint all documents in a multi-doc yaml file Signed-off-by: Nandor Kracser --- pkg/lint/rules/template.go | 37 ++++++++++++------- pkg/lint/rules/template_test.go | 14 +++++++ .../testdata/multi-template-fail/Chart.yaml | 21 +++++++++++ .../templates/multi-fail.yaml | 13 +++++++ 4 files changed, 72 insertions(+), 13 deletions(-) create mode 100644 pkg/lint/rules/testdata/multi-template-fail/Chart.yaml create mode 100644 pkg/lint/rules/testdata/multi-template-fail/templates/multi-fail.yaml diff --git a/pkg/lint/rules/template.go b/pkg/lint/rules/template.go index 3e4e0ebd1..0bb9f8671 100644 --- a/pkg/lint/rules/template.go +++ b/pkg/lint/rules/template.go @@ -20,6 +20,7 @@ import ( "bufio" "bytes" "fmt" + "io" "os" "path" "path/filepath" @@ -27,7 +28,7 @@ import ( "strings" "github.com/pkg/errors" - "sigs.k8s.io/yaml" + "k8s.io/apimachinery/pkg/util/yaml" "helm.sh/helm/v3/pkg/chart/loader" "helm.sh/helm/v3/pkg/chartutil" @@ -117,20 +118,30 @@ func Templates(linter *support.Linter, values map[string]interface{}, namespace renderedContent := renderedContentMap[path.Join(chart.Name(), fileName)] if strings.TrimSpace(renderedContent) != "" { linter.RunLinterRule(support.WarningSev, fpath, validateTopIndentLevel(renderedContent)) - var yamlStruct K8sYamlStruct - // Even though K8sYamlStruct only defines a few fields, an error in any other - // key will be raised as well - err := yaml.Unmarshal([]byte(renderedContent), &yamlStruct) - if (K8sYamlStruct{}) == yamlStruct { - continue + decoder := yaml.NewYAMLOrJSONDecoder(strings.NewReader(renderedContent), 4096) + + // Lint all resources if the file contains multiple documents separated by --- + for { + // Even though K8sYamlStruct only defines a few fields, an error in any other + // key will be raised as well + var yamlStruct *K8sYamlStruct + + err := decoder.Decode(&yamlStruct) + if err == io.EOF { + break + } + + // If YAML linting fails, we sill progress. So we don't capture the returned state + // on this linter run. + linter.RunLinterRule(support.ErrorSev, fpath, validateYamlContent(err)) + + if yamlStruct != nil { + linter.RunLinterRule(support.ErrorSev, fpath, validateMetadataName(yamlStruct)) + linter.RunLinterRule(support.ErrorSev, fpath, validateNoDeprecations(yamlStruct)) + linter.RunLinterRule(support.ErrorSev, fpath, validateMatchSelector(yamlStruct, renderedContent)) + } } - // If YAML linting fails, we sill progress. So we don't capture the returned state - // on this linter run. - linter.RunLinterRule(support.ErrorSev, fpath, validateYamlContent(err)) - linter.RunLinterRule(support.ErrorSev, fpath, validateMetadataName(&yamlStruct)) - linter.RunLinterRule(support.ErrorSev, fpath, validateNoDeprecations(&yamlStruct)) - linter.RunLinterRule(support.ErrorSev, fpath, validateMatchSelector(&yamlStruct, renderedContent)) } } } diff --git a/pkg/lint/rules/template_test.go b/pkg/lint/rules/template_test.go index 50cd562aa..eb076a1bf 100644 --- a/pkg/lint/rules/template_test.go +++ b/pkg/lint/rules/template_test.go @@ -107,6 +107,20 @@ func TestV3Fail(t *testing.T) { } } +func TestMultiTemplateFail(t *testing.T) { + linter := support.Linter{ChartDir: "./testdata/multi-template-fail"} + Templates(&linter, values, namespace, strict) + res := linter.Messages + + if len(res) != 1 { + t.Fatalf("Expected 1 error, got %d, %v", len(res), res) + } + + if !strings.Contains(res[0].Err.Error(), "object name does not conform to Kubernetes naming requirements") { + t.Errorf("Unexpected error: %s", res[0].Err) + } +} + func TestValidateMetadataName(t *testing.T) { names := map[string]bool{ "": false, diff --git a/pkg/lint/rules/testdata/multi-template-fail/Chart.yaml b/pkg/lint/rules/testdata/multi-template-fail/Chart.yaml new file mode 100644 index 000000000..f022d5ad9 --- /dev/null +++ b/pkg/lint/rules/testdata/multi-template-fail/Chart.yaml @@ -0,0 +1,21 @@ +apiVersion: v2 +name: multi-template-fail +description: A Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +version: 0.1.0 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. +appVersion: 1.16.0 diff --git a/pkg/lint/rules/testdata/multi-template-fail/templates/multi-fail.yaml b/pkg/lint/rules/testdata/multi-template-fail/templates/multi-fail.yaml new file mode 100644 index 000000000..835be07be --- /dev/null +++ b/pkg/lint/rules/testdata/multi-template-fail/templates/multi-fail.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: game-config +data: + game.properties: cheat +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: -this:name-is-not_valid$ +data: + game.properties: empty