feat(template) allow linting to be run at template time

refactor lint run to a function

refactor template run to a function

Signed-off-by: Scott Morgan <morgandev@gmail.com>
pull/7660/head
Scott Morgan 6 years ago committed by Scott Morgan
parent 7f3339cb4e
commit b093d2f724

@ -48,7 +48,25 @@ func newLintCmd(out io.Writer) *cobra.Command {
Use: "lint PATH",
Short: "examines a chart for possible issues",
Long: longLintHelp,
RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(_ *cobra.Command, args []string) error {
err := runLint(args, client, valueOpts, out)
if err != nil {
return err
}
return nil
},
}
f := cmd.Flags()
f.BoolVar(&client.Strict, "strict", false, "fail on lint warnings")
f.BoolVar(&client.WithSubcharts, "with-subcharts", false, "lint dependent charts")
addValueOptionsFlags(f, valueOpts)
return cmd
}
func runLint(args []string, client *action.Lint, valueOpts *values.Options, out io.Writer) error {
paths := []string{"."}
if len(args) > 0 {
paths = args
@ -114,13 +132,4 @@ func newLintCmd(out io.Writer) *cobra.Command {
}
fmt.Fprintln(out, summary)
return nil
},
}
f := cmd.Flags()
f.BoolVar(&client.Strict, "strict", false, "fail on lint warnings")
f.BoolVar(&client.WithSubcharts, "with-subcharts", false, "lint dependent charts")
addValueOptionsFlags(f, valueOpts)
return cmd
}

@ -43,13 +43,19 @@ faked locally. Additionally, none of the server-side testing of chart validity
(e.g. whether an API is supported) is done.
`
type templateOptions struct {
extraAPIs []string
showFiles []string
lint bool
includeCrds bool
validate bool
}
func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
var validate bool
var includeCrds bool
client := action.NewInstall(cfg)
o := &templateOptions{}
installClient := action.NewInstall(cfg)
lintClient := action.NewLint()
valueOpts := &values.Options{}
var extraAPIs []string
var showFiles []string
cmd := &cobra.Command{
Use: "template [NAME] [CHART]",
@ -57,12 +63,51 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
Long: templateDesc,
Args: require.MinimumNArgs(1),
RunE: func(_ *cobra.Command, args []string) error {
var err error
if o.lint {
err := runLint(args, lintClient, valueOpts, out)
if err != nil {
return err
}
}
err = runTemplate(args, installClient, valueOpts, o, out)
if err != nil {
return err
}
return nil
},
}
// Function providing dynamic auto-completion
completion.RegisterValidArgsFunc(cmd, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) {
return compInstall(args, toComplete, installClient)
})
f := cmd.Flags()
addInstallFlags(f, installClient, valueOpts)
f.StringArrayVarP(&o.showFiles, "show-only", "s", []string{}, "only show manifests rendered from the given templates")
f.StringVar(&installClient.OutputDir, "output-dir", "", "writes the executed templates to files in output-dir instead of stdout")
f.BoolVar(&installClient.IsUpgrade, "is-upgrade", false, "set .Release.IsUpgrade instead of .Release.IsInstall")
f.BoolVar(&o.validate, "validate", false, "validate your manifests against the Kubernetes cluster you are currently pointing at. This is the same validation performed on an install")
f.BoolVar(&o.includeCrds, "include-crds", false, "include CRDs in the templated output")
f.BoolVar(&o.lint, "lint", false, "examines a chart for possible issues")
f.BoolVar(&lintClient.Strict, "strict", false, "fail on lint warnings")
f.StringArrayVarP(&o.extraAPIs, "api-versions", "a", []string{}, "Kubernetes api versions used for Capabilities.APIVersions")
f.BoolVar(&installClient.UseReleaseName, "release-name", false, "use release name in the output-dir path.")
bindPostRenderFlag(cmd, &installClient.PostRenderer)
return cmd
}
func runTemplate(args []string, client *action.Install, valueOpts *values.Options, o *templateOptions, out io.Writer) error {
client.DryRun = true
client.ReleaseName = "RELEASE-NAME"
client.Replace = true // Skip the name check
client.ClientOnly = !validate
client.APIVersions = chartutil.VersionSet(extraAPIs)
client.IncludeCRDs = includeCrds
client.ClientOnly = !o.validate
client.APIVersions = chartutil.VersionSet(o.extraAPIs)
client.IncludeCRDs = o.includeCrds
rel, err := runInstall(args, client, valueOpts, out)
if err != nil {
return err
@ -79,11 +124,11 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
// if we have a list of files to render, then check that each of the
// provided files exists in the chart.
if len(showFiles) > 0 {
if len(o.showFiles) > 0 {
splitManifests := releaseutil.SplitManifests(manifests.String())
manifestNameRegex := regexp.MustCompile("# Source: [^/]+/(.+)")
var manifestsToRender []string
for _, f := range showFiles {
for _, f := range o.showFiles {
missing := true
for _, manifest := range splitManifests {
submatch := manifestNameRegex.FindStringSubmatch(manifest)
@ -115,24 +160,4 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
}
return nil
},
}
// Function providing dynamic auto-completion
completion.RegisterValidArgsFunc(cmd, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) {
return compInstall(args, toComplete, client)
})
f := cmd.Flags()
addInstallFlags(f, client, valueOpts)
f.StringArrayVarP(&showFiles, "show-only", "s", []string{}, "only show manifests rendered from the given templates")
f.StringVar(&client.OutputDir, "output-dir", "", "writes the executed templates to files in output-dir instead of stdout")
f.BoolVar(&validate, "validate", false, "validate your manifests against the Kubernetes cluster you are currently pointing at. This is the same validation performed on an install")
f.BoolVar(&includeCrds, "include-crds", false, "include CRDs in the templated output")
f.BoolVar(&client.IsUpgrade, "is-upgrade", false, "set .Release.IsUpgrade instead of .Release.IsInstall")
f.StringArrayVarP(&extraAPIs, "api-versions", "a", []string{}, "Kubernetes api versions used for Capabilities.APIVersions")
f.BoolVar(&client.UseReleaseName, "release-name", false, "use release name in the output-dir path.")
bindPostRenderFlag(cmd, &client.PostRenderer)
return cmd
}

@ -74,6 +74,23 @@ func TestTemplateCmd(t *testing.T) {
cmd: fmt.Sprintf("template '%s'", "testdata/testcharts/chart-with-template-lib-archive-dep"),
golden: "output/template-chart-with-template-lib-archive-dep.txt",
},
{
name: "check linting success",
cmd: fmt.Sprintf("template '%s' --lint", "testdata/testcharts/empty"),
golden: "output/template-linting-success.txt",
},
{
name: "check linting fail",
cmd: fmt.Sprintf("template '%s' --lint", "testdata/testcharts/novals"),
wantError: true,
golden: "output/template-linting-fail.txt",
},
{
name: "check linting strict fail",
cmd: fmt.Sprintf("template '%s' --lint --strict", "testdata/testcharts/novals"),
wantError: true,
golden: "output/template-linting-strict-fail.txt",
},
{
name: "check kube api versions",
cmd: fmt.Sprintf("template --api-versions helm.k8s.io/test '%s'", chartPath),

@ -0,0 +1,6 @@
==> Linting testdata/testcharts/novals
[INFO] Chart.yaml: icon is recommended
[INFO] values.yaml: file does not exist
[ERROR] templates/: template: novals/templates/alpine-pod.yaml:18:33: executing "novals/templates/alpine-pod.yaml" at <.Release.Time.Seconds>: nil pointer evaluating interface {}.Seconds
Error: 1 chart(s) linted, 1 chart(s) failed

@ -0,0 +1,6 @@
==> Linting testdata/testcharts/novals
[INFO] Chart.yaml: icon is recommended
[INFO] values.yaml: file does not exist
[ERROR] templates/: template: novals/templates/alpine-pod.yaml:4:36: executing "novals/templates/alpine-pod.yaml" at <.Values.Name>: map has no entry for key "Name"
Error: 1 chart(s) linted, 1 chart(s) failed

@ -0,0 +1,7 @@
==> Linting testdata/testcharts/empty
[INFO] Chart.yaml: icon is recommended
1 chart(s) linted, 0 chart(s) failed
---
# Source: empty/templates/empty.yaml
# This file is intentionally blank
Loading…
Cancel
Save