pull/12280/merge
Artem Bass 2 months ago committed by GitHub
commit b2d9265853
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -32,6 +32,7 @@ import (
// It provides the implementation of 'helm lint'.
type Lint struct {
Strict bool
ReleaseName string
Namespace string
WithSubcharts bool
Quiet bool
@ -59,7 +60,7 @@ func (l *Lint) Run(paths []string, vals map[string]interface{}) *LintResult {
}
result := &LintResult{}
for _, path := range paths {
linter, err := lintChart(path, vals, l.Namespace, l.KubeVersion, l.SkipSchemaValidation)
linter, err := lintChart(path, vals, l.ReleaseName, l.Namespace, l.KubeVersion, l.SkipSchemaValidation)
if err != nil {
result.Errors = append(result.Errors, err)
continue
@ -86,7 +87,7 @@ func HasWarningsOrErrors(result *LintResult) bool {
return len(result.Errors) > 0
}
func lintChart(path string, vals map[string]interface{}, namespace string, kubeVersion *chartutil.KubeVersion, skipSchemaValidation bool) (support.Linter, error) {
func lintChart(path string, vals map[string]interface{}, releaseName, namespace string, kubeVersion *chartutil.KubeVersion, skipSchemaValidation bool) (support.Linter, error) {
var chartPath string
linter := support.Linter{}
@ -125,10 +126,11 @@ func lintChart(path string, vals map[string]interface{}, namespace string, kubeV
return linter, fmt.Errorf("unable to check Chart.yaml file in chart: %w", err)
}
return lint.RunAll(
return lint.AllWithOptions(
chartPath,
vals,
namespace,
lint.WithReleaseName(releaseName),
lint.WithKubeVersion(kubeVersion),
lint.WithSkipSchemaValidation(skipSchemaValidation),
), nil

@ -22,6 +22,7 @@ import (
var (
values = make(map[string]interface{})
defaultName = "test-release"
namespace = "testNamespace"
chart1MultipleChartLint = "testdata/charts/multiplecharts-lint-chart-1"
chart2MultipleChartLint = "testdata/charts/multiplecharts-lint-chart-2"
@ -83,7 +84,7 @@ func TestLintChart(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
_, err := lintChart(tt.chartPath, map[string]interface{}{}, namespace, nil, tt.skipSchemaValidation)
_, err := lintChart(tt.chartPath, map[string]interface{}{}, defaultName, namespace, nil, tt.skipSchemaValidation)
switch {
case err != nil && !tt.err:
t.Errorf("%s", err)

@ -145,6 +145,7 @@ func newLintCmd(out io.Writer) *cobra.Command {
}
f := cmd.Flags()
f.StringVar(&client.ReleaseName, "release-name", "test-release", "release name")
f.BoolVar(&client.Strict, "strict", false, "fail on lint warnings")
f.BoolVar(&client.WithSubcharts, "with-subcharts", false, "lint dependent charts")
f.BoolVar(&client.Quiet, "quiet", false, "print only warnings and errors")

@ -24,42 +24,35 @@ import (
"helm.sh/helm/v4/pkg/lint/support"
)
type linterOptions struct {
KubeVersion *chartutil.KubeVersion
SkipSchemaValidation bool
}
type LinterOption func(lo *linterOptions)
func AllWithOptions(baseDir string, values map[string]interface{}, namespace string, options ...LinterOption) support.Linter {
// Using abs path to get directory context
chartDir, _ := filepath.Abs(baseDir)
func WithKubeVersion(kubeVersion *chartutil.KubeVersion) LinterOption {
return func(lo *linterOptions) {
lo.KubeVersion = kubeVersion
}
}
linter := support.Linter{ChartDir: chartDir}
func WithSkipSchemaValidation(skipSchemaValidation bool) LinterOption {
return func(lo *linterOptions) {
lo.SkipSchemaValidation = skipSchemaValidation
}
for _, option := range options {
option(&linter)
}
func RunAll(baseDir string, values map[string]interface{}, namespace string, options ...LinterOption) support.Linter {
chartDir, _ := filepath.Abs(baseDir)
rules.Chartfile(&linter)
rules.ValuesWithOverrides(&linter, values)
rules.TemplatesV2(&linter, values, namespace)
rules.Dependencies(&linter)
lo := linterOptions{}
for _, option := range options {
option(&lo)
return linter
}
result := support.Linter{
ChartDir: chartDir,
// All runs all the available linters on the given base directory.
// Deprecated, use AllWithOptions instead.
func All(basedir string, values map[string]interface{}, namespace string, _ bool) support.Linter {
return AllWithOptions(basedir, values, namespace)
}
rules.Chartfile(&result)
rules.ValuesWithOverrides(&result, values)
rules.TemplatesWithSkipSchemaValidation(&result, values, namespace, lo.KubeVersion, lo.SkipSchemaValidation)
rules.Dependencies(&result)
return result
// AllWithKubeVersion runs all the available linters on the given base directory, allowing to specify the kubernetes version.
// Deprecated, use AllWithOptions instead.
func AllWithKubeVersion(basedir string, values map[string]interface{}, namespace string, kubeVersion *chartutil.KubeVersion) support.Linter {
return AllWithOptions(basedir, values, namespace,
WithKubeVersion(kubeVersion),
WithSkipSchemaValidation(false),
)
}

@ -0,0 +1,28 @@
package lint
import (
chartutil "helm.sh/helm/v4/pkg/chart/v2/util"
"helm.sh/helm/v4/pkg/lint/support"
)
type LinterOption func(linter *support.Linter)
// WithReleaseName specifies chart release name
func WithReleaseName(name string) LinterOption {
return func(linter *support.Linter) {
linter.ReleaseName = name
}
}
// WithKubeVersion specifies kube version
func WithKubeVersion(version *chartutil.KubeVersion) LinterOption {
return func(linter *support.Linter) {
linter.KubeVersion = version
}
}
func WithSkipSchemaValidation(enabled bool) LinterOption {
return func(linter *support.Linter) {
linter.SkipSchemaValidation = enabled
}
}

@ -27,6 +27,7 @@ import (
var values map[string]interface{}
const defaultName = "test-release"
const namespace = "testNamespace"
const badChartDir = "rules/testdata/badchartfile"
@ -38,7 +39,7 @@ const malformedTemplate = "rules/testdata/malformed-template"
const invalidChartFileDir = "rules/testdata/invalidchartfile"
func TestBadChart(t *testing.T) {
m := RunAll(badChartDir, values, namespace).Messages
m := AllWithOptions(badChartDir, values, namespace, WithReleaseName(defaultName)).Messages
if len(m) != 8 {
t.Errorf("Number of errors %v", len(m))
t.Errorf("All didn't fail with expected errors, got %#v", m)
@ -82,7 +83,7 @@ func TestBadChart(t *testing.T) {
}
func TestInvalidYaml(t *testing.T) {
m := RunAll(badYamlFileDir, values, namespace).Messages
m := AllWithOptions(badYamlFileDir, values, namespace, WithReleaseName(defaultName)).Messages
if len(m) != 1 {
t.Fatalf("All didn't fail with expected errors, got %#v", m)
}
@ -92,7 +93,7 @@ func TestInvalidYaml(t *testing.T) {
}
func TestInvalidChartYaml(t *testing.T) {
m := RunAll(invalidChartFileDir, values, namespace).Messages
m := AllWithOptions(invalidChartFileDir, values, namespace).Messages
if len(m) != 1 {
t.Fatalf("All didn't fail with expected errors, got %#v", m)
}
@ -102,7 +103,7 @@ func TestInvalidChartYaml(t *testing.T) {
}
func TestBadValues(t *testing.T) {
m := RunAll(badValuesFileDir, values, namespace).Messages
m := AllWithOptions(badValuesFileDir, values, namespace, WithReleaseName(defaultName)).Messages
if len(m) < 1 {
t.Fatalf("All didn't fail with expected errors, got %#v", m)
}
@ -112,7 +113,7 @@ func TestBadValues(t *testing.T) {
}
func TestGoodChart(t *testing.T) {
m := RunAll(goodChartDir, values, namespace).Messages
m := AllWithOptions(goodChartDir, values, namespace, WithReleaseName(defaultName)).Messages
if len(m) != 0 {
t.Error("All returned linter messages when it shouldn't have")
for i, msg := range m {
@ -136,7 +137,7 @@ func TestHelmCreateChart(t *testing.T) {
// Note: we test with strict=true here, even though others have
// strict = false.
m := RunAll(createdChart, values, namespace, WithSkipSchemaValidation(true)).Messages
m := AllWithOptions(createdChart, values, namespace, WithReleaseName(defaultName)).Messages
if ll := len(m); ll != 1 {
t.Errorf("All should have had exactly 1 error. Got %d", ll)
for i, msg := range m {
@ -183,7 +184,7 @@ func TestHelmCreateChart_CheckDeprecatedWarnings(t *testing.T) {
},
}
linterRunDetails := RunAll(createdChart, updatedValues, namespace, WithSkipSchemaValidation(true))
linterRunDetails := AllWithOptions(createdChart, updatedValues, namespace, WithSkipSchemaValidation(true))
for _, msg := range linterRunDetails.Messages {
if strings.HasPrefix(msg.Error(), "[WARNING]") &&
strings.Contains(msg.Error(), "deprecated") {
@ -197,7 +198,7 @@ func TestHelmCreateChart_CheckDeprecatedWarnings(t *testing.T) {
// lint ignores import-values
// See https://github.com/helm/helm/issues/9658
func TestSubChartValuesChart(t *testing.T) {
m := RunAll(subChartValuesDir, values, namespace).Messages
m := AllWithOptions(subChartValuesDir, values, namespace, WithReleaseName(defaultName)).Messages
if len(m) != 0 {
t.Error("All returned linter messages when it shouldn't have")
for i, msg := range m {
@ -213,7 +214,7 @@ func TestMalformedTemplate(t *testing.T) {
ch := make(chan int, 1)
var m []support.Message
go func() {
m = RunAll(malformedTemplate, values, namespace).Messages
m = AllWithOptions(malformedTemplate, values, namespace, WithReleaseName(defaultName)).Messages
ch <- 1
}()
select {

@ -40,17 +40,20 @@ import (
)
// Templates lints the templates in the Linter.
// Deprecated, use TemplatesV2 instead.
func Templates(linter *support.Linter, values map[string]interface{}, namespace string, _ bool) {
TemplatesWithKubeVersion(linter, values, namespace, nil)
TemplatesV2(linter, values, namespace)
}
// TemplatesWithKubeVersion lints the templates in the Linter, allowing to specify the kubernetes version.
// Deprecated, use TemplatesV2 instead.
func TemplatesWithKubeVersion(linter *support.Linter, values map[string]interface{}, namespace string, kubeVersion *chartutil.KubeVersion) {
TemplatesWithSkipSchemaValidation(linter, values, namespace, kubeVersion, false)
linter.KubeVersion = kubeVersion
TemplatesV2(linter, values, namespace)
}
// TemplatesWithSkipSchemaValidation lints the templates in the Linter, allowing to specify the kubernetes version and if schema validation is enabled or not.
func TemplatesWithSkipSchemaValidation(linter *support.Linter, values map[string]interface{}, namespace string, kubeVersion *chartutil.KubeVersion, skipSchemaValidation bool) {
// TemplatesV2 lints the templates in the Linter.
func TemplatesV2(linter *support.Linter, values map[string]interface{}, namespace string) {
fpath := "templates/"
templatesPath := filepath.Join(linter.ChartDir, fpath)
@ -71,18 +74,18 @@ func TemplatesWithSkipSchemaValidation(linter *support.Linter, values map[string
}
options := chartutil.ReleaseOptions{
Name: "test-release",
Name: linter.ReleaseName,
Namespace: namespace,
}
caps := chartutil.DefaultCapabilities.Copy()
if kubeVersion != nil {
caps.KubeVersion = *kubeVersion
if linter.KubeVersion != nil {
caps.KubeVersion = *linter.KubeVersion
}
// lint ignores import-values
// See https://github.com/helm/helm/issues/9658
if err := chartutil.ProcessDependencies(chart, values); err != nil {
if err = chartutil.ProcessDependencies(chart, values); err != nil {
return
}
@ -91,7 +94,7 @@ func TemplatesWithSkipSchemaValidation(linter *support.Linter, values map[string
return
}
valuesToRender, err := chartutil.ToRenderValuesWithSchemaValidation(chart, cvals, options, caps, skipSchemaValidation)
valuesToRender, err := chartutil.ToRenderValuesWithSchemaValidation(chart, cvals, options, caps, linter.SkipSchemaValidation)
if err != nil {
linter.RunLinterRule(support.ErrorSev, fpath, err)
return
@ -157,7 +160,7 @@ func TemplatesWithSkipSchemaValidation(linter *support.Linter, values map[string
// NOTE: set to warnings to allow users to support out-of-date kubernetes
// Refs https://github.com/helm/helm/issues/8596
linter.RunLinterRule(support.WarningSev, fpath, validateMetadataName(yamlStruct))
linter.RunLinterRule(support.WarningSev, fpath, validateNoDeprecations(yamlStruct, kubeVersion))
linter.RunLinterRule(support.WarningSev, fpath, validateNoDeprecations(yamlStruct, linter.KubeVersion))
linter.RunLinterRule(support.ErrorSev, fpath, validateMatchSelector(yamlStruct, renderedContent))
linter.RunLinterRule(support.ErrorSev, fpath, validateListAnnotations(yamlStruct, renderedContent))

@ -49,12 +49,12 @@ func TestValidateAllowedExtension(t *testing.T) {
var values = map[string]interface{}{"nameOverride": "", "httpPort": 80}
const defaultName = "test-release"
const namespace = "testNamespace"
const strict = false
func TestTemplateParsing(t *testing.T) {
linter := support.Linter{ChartDir: templateTestBasedir}
Templates(&linter, values, namespace, strict)
linter := support.Linter{ChartDir: templateTestBasedir, ReleaseName: defaultName}
TemplatesV2(&linter, values, namespace)
res := linter.Messages
if len(res) != 1 {
@ -76,8 +76,8 @@ func TestTemplateIntegrationHappyPath(t *testing.T) {
os.Rename(wrongTemplatePath, ignoredTemplatePath)
defer os.Rename(ignoredTemplatePath, wrongTemplatePath)
linter := support.Linter{ChartDir: templateTestBasedir}
Templates(&linter, values, namespace, strict)
linter := support.Linter{ChartDir: templateTestBasedir, ReleaseName: defaultName}
TemplatesV2(&linter, values, namespace)
res := linter.Messages
if len(res) != 0 {
@ -86,8 +86,8 @@ func TestTemplateIntegrationHappyPath(t *testing.T) {
}
func TestMultiTemplateFail(t *testing.T) {
linter := support.Linter{ChartDir: "./testdata/multi-template-fail"}
Templates(&linter, values, namespace, strict)
linter := support.Linter{ChartDir: "./testdata/multi-template-fail", ReleaseName: defaultName}
TemplatesV2(&linter, values, namespace)
res := linter.Messages
if len(res) != 1 {
@ -206,8 +206,8 @@ func TestDeprecatedAPIFails(t *testing.T) {
t.Fatal(err)
}
linter := support.Linter{ChartDir: filepath.Join(tmpdir, mychart.Name())}
Templates(&linter, values, namespace, strict)
linter := support.Linter{ChartDir: filepath.Join(tmpdir, mychart.Name()), ReleaseName: defaultName}
TemplatesV2(&linter, values, namespace)
if l := len(linter.Messages); l != 1 {
for i, msg := range linter.Messages {
t.Logf("Message %d: %s", i, msg)
@ -262,8 +262,9 @@ func TestStrictTemplateParsingMapError(t *testing.T) {
}
linter := &support.Linter{
ChartDir: filepath.Join(dir, ch.Metadata.Name),
ReleaseName: defaultName,
}
Templates(linter, ch.Values, namespace, strict)
TemplatesV2(linter, ch.Values, namespace)
if len(linter.Messages) != 0 {
t.Errorf("expected zero messages, got %d", len(linter.Messages))
for i, msg := range linter.Messages {
@ -391,8 +392,8 @@ func TestEmptyWithCommentsManifests(t *testing.T) {
t.Fatal(err)
}
linter := support.Linter{ChartDir: filepath.Join(tmpdir, mychart.Name())}
Templates(&linter, values, namespace, strict)
linter := support.Linter{ChartDir: filepath.Join(tmpdir, mychart.Name()), ReleaseName: defaultName}
TemplatesV2(&linter, values, namespace)
if l := len(linter.Messages); l > 0 {
for i, msg := range linter.Messages {
t.Logf("Message %d: %s", i, msg)

@ -16,7 +16,11 @@ limitations under the License.
package support
import "fmt"
import (
"fmt"
chartutil "helm.sh/helm/v4/pkg/chart/v2/util"
)
// Severity indicates the severity of a Message.
const (
@ -39,6 +43,9 @@ type Linter struct {
// The highest severity of all the failing lint rules
HighestSeverity int
ChartDir string
ReleaseName string
KubeVersion *chartutil.KubeVersion
SkipSchemaValidation bool
}
// Message describes an error encountered while linting.

Loading…
Cancel
Save