github.com/Beeketing/helm@v2.12.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  	"errors"
    21  	"fmt"
    22  	"os"
    23  	"path/filepath"
    24  
    25  	"github.com/ghodss/yaml"
    26  	"k8s.io/helm/pkg/chartutil"
    27  	"k8s.io/helm/pkg/engine"
    28  	"k8s.io/helm/pkg/lint/support"
    29  	cpb "k8s.io/helm/pkg/proto/hapi/chart"
    30  	"k8s.io/helm/pkg/timeconv"
    31  	tversion "k8s.io/helm/pkg/version"
    32  )
    33  
    34  // Templates lints the templates in the Linter.
    35  func Templates(linter *support.Linter, values []byte, namespace string, strict bool) {
    36  	path := "templates/"
    37  	templatesPath := filepath.Join(linter.ChartDir, path)
    38  
    39  	templatesDirExist := linter.RunLinterRule(support.WarningSev, path, validateTemplatesDir(templatesPath))
    40  
    41  	// Templates directory is optional for now
    42  	if !templatesDirExist {
    43  		return
    44  	}
    45  
    46  	// Load chart and parse templates, based on tiller/release_server
    47  	chart, err := chartutil.Load(linter.ChartDir)
    48  
    49  	chartLoaded := linter.RunLinterRule(support.ErrorSev, path, err)
    50  
    51  	if !chartLoaded {
    52  		return
    53  	}
    54  
    55  	options := chartutil.ReleaseOptions{Name: "testRelease", Time: timeconv.Now(), Namespace: namespace}
    56  	caps := &chartutil.Capabilities{
    57  		APIVersions:   chartutil.DefaultVersionSet,
    58  		KubeVersion:   chartutil.DefaultKubeVersion,
    59  		TillerVersion: tversion.GetVersionProto(),
    60  	}
    61  	cvals, err := chartutil.CoalesceValues(chart, &cpb.Config{Raw: string(values)})
    62  	if err != nil {
    63  		return
    64  	}
    65  	// convert our values back into config
    66  	yvals, err := cvals.YAML()
    67  	if err != nil {
    68  		return
    69  	}
    70  	cc := &cpb.Config{Raw: yvals}
    71  	valuesToRender, err := chartutil.ToRenderValuesCaps(chart, cc, options, caps)
    72  	if err != nil {
    73  		// FIXME: This seems to generate a duplicate, but I can't find where the first
    74  		// error is coming from.
    75  		//linter.RunLinterRule(support.ErrorSev, err)
    76  		return
    77  	}
    78  	e := engine.New()
    79  	e.LintMode = true
    80  	if strict {
    81  		e.Strict = true
    82  	}
    83  	renderedContentMap, err := e.Render(chart, valuesToRender)
    84  
    85  	renderOk := linter.RunLinterRule(support.ErrorSev, path, err)
    86  
    87  	if !renderOk {
    88  		return
    89  	}
    90  
    91  	/* Iterate over all the templates to check:
    92  	- It is a .yaml file
    93  	- All the values in the template file is defined
    94  	- {{}} include | quote
    95  	- Generated content is a valid Yaml file
    96  	- Metadata.Namespace is not set
    97  	*/
    98  	for _, template := range chart.Templates {
    99  		fileName, _ := template.Name, template.Data
   100  		path = fileName
   101  
   102  		linter.RunLinterRule(support.ErrorSev, path, validateAllowedExtension(fileName))
   103  
   104  		// We only apply the following lint rules to yaml files
   105  		if filepath.Ext(fileName) != ".yaml" || filepath.Ext(fileName) == ".yml" {
   106  			continue
   107  		}
   108  
   109  		// NOTE: disabled for now, Refs https://github.com/kubernetes/helm/issues/1463
   110  		// Check that all the templates have a matching value
   111  		//linter.RunLinterRule(support.WarningSev, path, validateNoMissingValues(templatesPath, valuesToRender, preExecutedTemplate))
   112  
   113  		// NOTE: disabled for now, Refs https://github.com/kubernetes/helm/issues/1037
   114  		// linter.RunLinterRule(support.WarningSev, path, validateQuotes(string(preExecutedTemplate)))
   115  
   116  		renderedContent := renderedContentMap[filepath.Join(chart.GetMetadata().Name, fileName)]
   117  		var yamlStruct K8sYamlStruct
   118  		// Even though K8sYamlStruct only defines Metadata namespace, an error in any other
   119  		// key will be raised as well
   120  		err := yaml.Unmarshal([]byte(renderedContent), &yamlStruct)
   121  
   122  		validYaml := linter.RunLinterRule(support.ErrorSev, path, validateYamlContent(err))
   123  
   124  		if !validYaml {
   125  			continue
   126  		}
   127  	}
   128  }
   129  
   130  // Validation functions
   131  func validateTemplatesDir(templatesPath string) error {
   132  	if fi, err := os.Stat(templatesPath); err != nil {
   133  		return errors.New("directory not found")
   134  	} else if err == nil && !fi.IsDir() {
   135  		return errors.New("not a directory")
   136  	}
   137  	return nil
   138  }
   139  
   140  func validateAllowedExtension(fileName string) error {
   141  	ext := filepath.Ext(fileName)
   142  	validExtensions := []string{".yaml", ".yml", ".tpl", ".txt"}
   143  
   144  	for _, b := range validExtensions {
   145  		if b == ext {
   146  			return nil
   147  		}
   148  	}
   149  
   150  	return fmt.Errorf("file extension '%s' not valid. Valid extensions are .yaml, .yml, .tpl, or .txt", ext)
   151  }
   152  
   153  func validateYamlContent(err error) error {
   154  	if err != nil {
   155  		return fmt.Errorf("unable to parse YAML\n\t%s", err)
   156  	}
   157  	return nil
   158  }
   159  
   160  // K8sYamlStruct stubs a Kubernetes YAML file.
   161  // Need to access for now to Namespace only
   162  type K8sYamlStruct struct {
   163  	Metadata struct {
   164  		Namespace string
   165  	}
   166  }