@ -42,35 +42,66 @@ import (
)
)
// Templates lints the templates in the Linter.
// Templates lints the templates in the Linter.
func Templates ( linter * support . Linter , values map [ string ] interface { } , namespace string , _ bool ) {
func Templates ( linter * support . Linter , namespace string , values map [ string ] any , options ... TemplateLinterOption ) {
TemplatesWithKubeVersion ( linter , values , namespace , nil )
templateLinter := newTemplateLinter ( linter , namespace , values , options ... )
templateLinter . Lint ( )
}
}
// TemplatesWithKubeVersion lints the templates in the Linter, allowing to specify the kubernetes version.
type TemplateLinterOption func ( * templateLinter )
func TemplatesWithKubeVersion ( linter * support . Linter , values map [ string ] interface { } , namespace string , kubeVersion * common . KubeVersion ) {
TemplatesWithSkipSchemaValidation ( linter , values , namespace , kubeVersion , false )
func TemplateLinterKubeVersion ( kubeVersion * common . KubeVersion ) TemplateLinterOption {
return func ( tl * templateLinter ) {
tl . kubeVersion = kubeVersion
}
}
func TemplateLinterSkipSchemaValidation ( skipSchemaValidation bool ) TemplateLinterOption {
return func ( tl * templateLinter ) {
tl . skipSchemaValidation = skipSchemaValidation
}
}
}
// TemplatesWithSkipSchemaValidation lints the templates in the Linter, allowing to specify the kubernetes version and if schema validation is enabled or not.
func newTemplateLinter ( linter * support . Linter , namespace string , values map [ string ] any , options ... TemplateLinterOption ) templateLinter {
func TemplatesWithSkipSchemaValidation ( linter * support . Linter , values map [ string ] interface { } , namespace string , kubeVersion * common . KubeVersion , skipSchemaValidation bool ) {
fpath := "templates/"
result := templateLinter {
templatesPath := filepath . Join ( linter . ChartDir , fpath )
linter : linter ,
values : values ,
namespace : namespace ,
}
// Templates directory is optional for now
for _ , o := range options {
templatesDirExists := linter . RunLinterRule ( support . WarningSev , fpath , templatesDirExists ( templatesPath ) )
o ( & result )
}
return result
}
type templateLinter struct {
linter * support . Linter
values map [ string ] any
namespace string
kubeVersion * common . KubeVersion
skipSchemaValidation bool
}
func ( t * templateLinter ) Lint ( ) {
templatesDir := "templates/"
templatesPath := filepath . Join ( t . linter . ChartDir , templatesDir )
templatesDirExists := t . linter . RunLinterRule ( support . WarningSev , templatesDir , templatesDirExists ( templatesPath ) )
if ! templatesDirExists {
if ! templatesDirExists {
return
return
}
}
validTemplatesDir := linter . RunLinterRule ( support . ErrorSev , fpath , validateTemplatesDir ( templatesPath ) )
validTemplatesDir := t. linter. RunLinterRule ( support . ErrorSev , templatesDir , validateTemplatesDir ( templatesPath ) )
if ! validTemplatesDir {
if ! validTemplatesDir {
return
return
}
}
// Load chart and parse templates
// Load chart and parse templates
chart , err := loader . Load ( linter . ChartDir )
chart , err := loader . Load ( t. linter. ChartDir )
chartLoaded := linter . RunLinterRule ( support . ErrorSev , fpath , err )
chartLoaded := t. linter. RunLinterRule ( support . ErrorSev , templatesDir , err )
if ! chartLoaded {
if ! chartLoaded {
return
return
@ -78,35 +109,35 @@ func TemplatesWithSkipSchemaValidation(linter *support.Linter, values map[string
options := common . ReleaseOptions {
options := common . ReleaseOptions {
Name : "test-release" ,
Name : "test-release" ,
Namespace : namespace,
Namespace : t. namespace,
}
}
caps := common . DefaultCapabilities . Copy ( )
caps := common . DefaultCapabilities . Copy ( )
if kubeVersion != nil {
if t. kubeVersion != nil {
caps . KubeVersion = * kubeVersion
caps . KubeVersion = * t. kubeVersion
}
}
// lint ignores import-values
// lint ignores import-values
// See https://github.com/helm/helm/issues/9658
// See https://github.com/helm/helm/issues/9658
if err := chartutil . ProcessDependencies ( chart , values) ; err != nil {
if err := chartutil . ProcessDependencies ( chart , t. values) ; err != nil {
return
return
}
}
cvals , err := util . CoalesceValues ( chart , values)
cvals , err := util . CoalesceValues ( chart , t. values)
if err != nil {
if err != nil {
return
return
}
}
valuesToRender , err := util . ToRenderValuesWithSchemaValidation ( chart , cvals , options , caps , skipSchemaValidation)
valuesToRender , err := util . ToRenderValuesWithSchemaValidation ( chart , cvals , options , caps , t. skipSchemaValidation)
if err != nil {
if err != nil {
linter. RunLinterRule ( support . ErrorSev , fpath , err )
t. linter. RunLinterRule ( support . ErrorSev , templatesDir , err )
return
return
}
}
var e engine . Engine
var e engine . Engine
e . LintMode = true
e . LintMode = true
renderedContentMap , err := e . Render ( chart , valuesToRender )
renderedContentMap , err := e . Render ( chart , valuesToRender )
renderOk := linter. RunLinterRule ( support . ErrorSev , fpath , err )
renderOk := t. linter. RunLinterRule ( support . ErrorSev , templatesDir , err )
if ! renderOk {
if ! renderOk {
return
return
@ -121,9 +152,8 @@ func TemplatesWithSkipSchemaValidation(linter *support.Linter, values map[string
* /
* /
for _ , template := range chart . Templates {
for _ , template := range chart . Templates {
fileName := template . Name
fileName := template . Name
fpath = fileName
linter. RunLinterRule ( support . ErrorSev , f path , validateAllowedExtension ( fileName ) )
t. linter. RunLinterRule ( support . ErrorSev , f ileName , validateAllowedExtension ( fileName ) )
// We only apply the following lint rules to yaml files
// We only apply the following lint rules to yaml files
if filepath . Ext ( fileName ) != ".yaml" || filepath . Ext ( fileName ) == ".yml" {
if filepath . Ext ( fileName ) != ".yaml" || filepath . Ext ( fileName ) == ".yml" {
@ -139,7 +169,7 @@ func TemplatesWithSkipSchemaValidation(linter *support.Linter, values map[string
renderedContent := renderedContentMap [ path . Join ( chart . Name ( ) , fileName ) ]
renderedContent := renderedContentMap [ path . Join ( chart . Name ( ) , fileName ) ]
if strings . TrimSpace ( renderedContent ) != "" {
if strings . TrimSpace ( renderedContent ) != "" {
linter. RunLinterRule ( support . WarningSev , f path , validateTopIndentLevel ( renderedContent ) )
t. linter. RunLinterRule ( support . WarningSev , f ileName , validateTopIndentLevel ( renderedContent ) )
decoder := yaml . NewYAMLOrJSONDecoder ( strings . NewReader ( renderedContent ) , 4096 )
decoder := yaml . NewYAMLOrJSONDecoder ( strings . NewReader ( renderedContent ) , 4096 )
@ -156,17 +186,17 @@ func TemplatesWithSkipSchemaValidation(linter *support.Linter, values map[string
// If YAML linting fails here, it will always fail in the next block as well, so we should return here.
// If YAML linting fails here, it will always fail in the next block as well, so we should return here.
// fix https://github.com/helm/helm/issues/11391
// fix https://github.com/helm/helm/issues/11391
if ! linter. RunLinterRule ( support . ErrorSev , f path , validateYamlContent ( err ) ) {
if ! t. linter. RunLinterRule ( support . ErrorSev , f ileName , validateYamlContent ( err ) ) {
return
return
}
}
if yamlStruct != nil {
if yamlStruct != nil {
// NOTE: set to warnings to allow users to support out-of-date kubernetes
// NOTE: set to warnings to allow users to support out-of-date kubernetes
// Refs https://github.com/helm/helm/issues/8596
// Refs https://github.com/helm/helm/issues/8596
linter. RunLinterRule ( support . WarningSev , f path , validateMetadataName ( yamlStruct ) )
t. linter. RunLinterRule ( support . WarningSev , f ileName , validateMetadataName ( yamlStruct ) )
linter. RunLinterRule ( support . WarningSev , f path , validateNoDeprecations ( yamlStruct , kubeVersion ) )
t. linter. RunLinterRule ( support . WarningSev , f ileName , validateNoDeprecations ( yamlStruct , t . kubeVersion ) )
linter. RunLinterRule ( support . ErrorSev , f path , validateMatchSelector ( yamlStruct , renderedContent ) )
t. linter. RunLinterRule ( support . ErrorSev , f ileName , validateMatchSelector ( yamlStruct , renderedContent ) )
linter. RunLinterRule ( support . ErrorSev , f path , validateListAnnotations ( yamlStruct , renderedContent ) )
t. linter. RunLinterRule ( support . ErrorSev , f ileName , validateListAnnotations ( yamlStruct , renderedContent ) )
}
}
}
}
}
}
@ -234,6 +264,7 @@ func validateYamlContent(err error) error {
if err != nil {
if err != nil {
return fmt . Errorf ( "unable to parse YAML: %w" , err )
return fmt . Errorf ( "unable to parse YAML: %w" , err )
}
}
return nil
return nil
}
}