github.com/umeshredd/helm@v3.0.0-alpha.1+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/ghodss/yaml"
    24  	"github.com/pkg/errors"
    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  		// FIXME: This seems to generate a duplicate, but I can't find where the first
    65  		// error is coming from.
    66  		//linter.RunLinterRule(support.ErrorSev, err)
    67  		return
    68  	}
    69  	var e engine.Engine
    70  	e.Strict = strict
    71  	renderedContentMap, err := e.Render(chart, valuesToRender)
    72  
    73  	renderOk := linter.RunLinterRule(support.ErrorSev, path, err)
    74  
    75  	if !renderOk {
    76  		return
    77  	}
    78  
    79  	/* Iterate over all the templates to check:
    80  	- It is a .yaml file
    81  	- All the values in the template file is defined
    82  	- {{}} include | quote
    83  	- Generated content is a valid Yaml file
    84  	- Metadata.Namespace is not set
    85  	*/
    86  	for _, template := range chart.Templates {
    87  		fileName, _ := template.Name, template.Data
    88  		path = fileName
    89  
    90  		linter.RunLinterRule(support.ErrorSev, path, validateAllowedExtension(fileName))
    91  
    92  		// We only apply the following lint rules to yaml files
    93  		if filepath.Ext(fileName) != ".yaml" || filepath.Ext(fileName) == ".yml" {
    94  			continue
    95  		}
    96  
    97  		// NOTE: disabled for now, Refs https://github.com/helm/helm/issues/1463
    98  		// Check that all the templates have a matching value
    99  		//linter.RunLinterRule(support.WarningSev, path, validateNoMissingValues(templatesPath, valuesToRender, preExecutedTemplate))
   100  
   101  		// NOTE: disabled for now, Refs https://github.com/helm/helm/issues/1037
   102  		// linter.RunLinterRule(support.WarningSev, path, validateQuotes(string(preExecutedTemplate)))
   103  
   104  		renderedContent := renderedContentMap[filepath.Join(chart.Name(), fileName)]
   105  		var yamlStruct K8sYamlStruct
   106  		// Even though K8sYamlStruct only defines Metadata namespace, an error in any other
   107  		// key will be raised as well
   108  		err := yaml.Unmarshal([]byte(renderedContent), &yamlStruct)
   109  
   110  		validYaml := linter.RunLinterRule(support.ErrorSev, path, validateYamlContent(err))
   111  
   112  		if !validYaml {
   113  			continue
   114  		}
   115  	}
   116  }
   117  
   118  // Validation functions
   119  func validateTemplatesDir(templatesPath string) error {
   120  	if fi, err := os.Stat(templatesPath); err != nil {
   121  		return errors.New("directory not found")
   122  	} else if !fi.IsDir() {
   123  		return errors.New("not a directory")
   124  	}
   125  	return nil
   126  }
   127  
   128  func validateAllowedExtension(fileName string) error {
   129  	ext := filepath.Ext(fileName)
   130  	validExtensions := []string{".yaml", ".yml", ".tpl", ".txt"}
   131  
   132  	for _, b := range validExtensions {
   133  		if b == ext {
   134  			return nil
   135  		}
   136  	}
   137  
   138  	return errors.Errorf("file extension '%s' not valid. Valid extensions are .yaml, .yml, .tpl, or .txt", ext)
   139  }
   140  
   141  func validateYamlContent(err error) error {
   142  	return errors.Wrap(err, "unable to parse YAML")
   143  }
   144  
   145  // K8sYamlStruct stubs a Kubernetes YAML file.
   146  // Need to access for now to Namespace only
   147  type K8sYamlStruct struct {
   148  	Metadata struct {
   149  		Namespace string
   150  	}
   151  }