github.com/qsis/helm@v3.0.0-beta.3+incompatible/pkg/lint/rules/template.go (about)

     1  /*
     2  Copyright The Helm Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package rules
    18  
    19  import (
    20  	"os"
    21  	"path/filepath"
    22  
    23  	"github.com/pkg/errors"
    24  	"sigs.k8s.io/yaml"
    25  
    26  	"helm.sh/helm/pkg/chart/loader"
    27  	"helm.sh/helm/pkg/chartutil"
    28  	"helm.sh/helm/pkg/engine"
    29  	"helm.sh/helm/pkg/lint/support"
    30  )
    31  
    32  // Templates lints the templates in the Linter.
    33  func Templates(linter *support.Linter, values map[string]interface{}, namespace string, strict bool) {
    34  	path := "templates/"
    35  	templatesPath := filepath.Join(linter.ChartDir, path)
    36  
    37  	templatesDirExist := linter.RunLinterRule(support.WarningSev, path, validateTemplatesDir(templatesPath))
    38  
    39  	// Templates directory is optional for now
    40  	if !templatesDirExist {
    41  		return
    42  	}
    43  
    44  	// Load chart and parse templates, based on tiller/release_server
    45  	chart, err := loader.Load(linter.ChartDir)
    46  
    47  	chartLoaded := linter.RunLinterRule(support.ErrorSev, path, err)
    48  
    49  	if !chartLoaded {
    50  		return
    51  	}
    52  
    53  	options := chartutil.ReleaseOptions{
    54  		Name:      "testRelease",
    55  		Namespace: namespace,
    56  	}
    57  
    58  	cvals, err := chartutil.CoalesceValues(chart, values)
    59  	if err != nil {
    60  		return
    61  	}
    62  	valuesToRender, err := chartutil.ToRenderValues(chart, cvals, options, nil)
    63  	if err != nil {
    64  		linter.RunLinterRule(support.ErrorSev, path, err)
    65  		return
    66  	}
    67  	var e engine.Engine
    68  	e.Strict = strict
    69  	e.LintMode = true
    70  	renderedContentMap, err := e.Render(chart, valuesToRender)
    71  
    72  	renderOk := linter.RunLinterRule(support.ErrorSev, path, err)
    73  
    74  	if !renderOk {
    75  		return
    76  	}
    77  
    78  	/* Iterate over all the templates to check:
    79  	- It is a .yaml file
    80  	- All the values in the template file is defined
    81  	- {{}} include | quote
    82  	- Generated content is a valid Yaml file
    83  	- Metadata.Namespace is not set
    84  	*/
    85  	for _, template := range chart.Templates {
    86  		fileName, _ := template.Name, template.Data
    87  		path = fileName
    88  
    89  		linter.RunLinterRule(support.ErrorSev, path, validateAllowedExtension(fileName))
    90  
    91  		// We only apply the following lint rules to yaml files
    92  		if filepath.Ext(fileName) != ".yaml" || filepath.Ext(fileName) == ".yml" {
    93  			continue
    94  		}
    95  
    96  		// NOTE: disabled for now, Refs https://github.com/helm/helm/issues/1463
    97  		// Check that all the templates have a matching value
    98  		//linter.RunLinterRule(support.WarningSev, path, validateNoMissingValues(templatesPath, valuesToRender, preExecutedTemplate))
    99  
   100  		// NOTE: disabled for now, Refs https://github.com/helm/helm/issues/1037
   101  		// linter.RunLinterRule(support.WarningSev, path, validateQuotes(string(preExecutedTemplate)))
   102  
   103  		renderedContent := renderedContentMap[filepath.Join(chart.Name(), fileName)]
   104  		var yamlStruct K8sYamlStruct
   105  		// Even though K8sYamlStruct only defines Metadata namespace, an error in any other
   106  		// key will be raised as well
   107  		err := yaml.Unmarshal([]byte(renderedContent), &yamlStruct)
   108  
   109  		validYaml := linter.RunLinterRule(support.ErrorSev, path, validateYamlContent(err))
   110  
   111  		if !validYaml {
   112  			continue
   113  		}
   114  	}
   115  }
   116  
   117  // Validation functions
   118  func validateTemplatesDir(templatesPath string) error {
   119  	if fi, err := os.Stat(templatesPath); err != nil {
   120  		return errors.New("directory not found")
   121  	} else if !fi.IsDir() {
   122  		return errors.New("not a directory")
   123  	}
   124  	return nil
   125  }
   126  
   127  func validateAllowedExtension(fileName string) error {
   128  	ext := filepath.Ext(fileName)
   129  	validExtensions := []string{".yaml", ".yml", ".tpl", ".txt"}
   130  
   131  	for _, b := range validExtensions {
   132  		if b == ext {
   133  			return nil
   134  		}
   135  	}
   136  
   137  	return errors.Errorf("file extension '%s' not valid. Valid extensions are .yaml, .yml, .tpl, or .txt", ext)
   138  }
   139  
   140  func validateYamlContent(err error) error {
   141  	return errors.Wrap(err, "unable to parse YAML")
   142  }
   143  
   144  // K8sYamlStruct stubs a Kubernetes YAML file.
   145  // Need to access for now to Namespace only
   146  type K8sYamlStruct struct {
   147  	Metadata struct {
   148  		Namespace string
   149  	}
   150  }