github.com/valdemarpavesi/helm@v2.9.1+incompatible/pkg/lint/rules/template.go (about)

     1  /*
     2  Copyright 2016 The Kubernetes Authors All rights reserved.
     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  	if strict {
    80  		e.Strict = true
    81  	}
    82  	renderedContentMap, err := e.Render(chart, valuesToRender)
    83  
    84  	renderOk := linter.RunLinterRule(support.ErrorSev, path, err)
    85  
    86  	if !renderOk {
    87  		return
    88  	}
    89  
    90  	/* Iterate over all the templates to check:
    91  	- It is a .yaml file
    92  	- All the values in the template file is defined
    93  	- {{}} include | quote
    94  	- Generated content is a valid Yaml file
    95  	- Metadata.Namespace is not set
    96  	*/
    97  	for _, template := range chart.Templates {
    98  		fileName, _ := template.Name, template.Data
    99  		path = fileName
   100  
   101  		linter.RunLinterRule(support.ErrorSev, path, validateAllowedExtension(fileName))
   102  
   103  		// We only apply the following lint rules to yaml files
   104  		if filepath.Ext(fileName) != ".yaml" || filepath.Ext(fileName) == ".yml" {
   105  			continue
   106  		}
   107  
   108  		// NOTE: disabled for now, Refs https://github.com/kubernetes/helm/issues/1463
   109  		// Check that all the templates have a matching value
   110  		//linter.RunLinterRule(support.WarningSev, path, validateNoMissingValues(templatesPath, valuesToRender, preExecutedTemplate))
   111  
   112  		// NOTE: disabled for now, Refs https://github.com/kubernetes/helm/issues/1037
   113  		// linter.RunLinterRule(support.WarningSev, path, validateQuotes(string(preExecutedTemplate)))
   114  
   115  		renderedContent := renderedContentMap[filepath.Join(chart.GetMetadata().Name, fileName)]
   116  		var yamlStruct K8sYamlStruct
   117  		// Even though K8sYamlStruct only defines Metadata namespace, an error in any other
   118  		// key will be raised as well
   119  		err := yaml.Unmarshal([]byte(renderedContent), &yamlStruct)
   120  
   121  		validYaml := linter.RunLinterRule(support.ErrorSev, path, validateYamlContent(err))
   122  
   123  		if !validYaml {
   124  			continue
   125  		}
   126  	}
   127  }
   128  
   129  // Validation functions
   130  func validateTemplatesDir(templatesPath string) error {
   131  	if fi, err := os.Stat(templatesPath); err != nil {
   132  		return errors.New("directory not found")
   133  	} else if err == nil && !fi.IsDir() {
   134  		return errors.New("not a directory")
   135  	}
   136  	return nil
   137  }
   138  
   139  func validateAllowedExtension(fileName string) error {
   140  	ext := filepath.Ext(fileName)
   141  	validExtensions := []string{".yaml", ".yml", ".tpl", ".txt"}
   142  
   143  	for _, b := range validExtensions {
   144  		if b == ext {
   145  			return nil
   146  		}
   147  	}
   148  
   149  	return fmt.Errorf("file extension '%s' not valid. Valid extensions are .yaml, .yml, .tpl, or .txt", ext)
   150  }
   151  
   152  func validateYamlContent(err error) error {
   153  	if err != nil {
   154  		return fmt.Errorf("unable to parse YAML\n\t%s", err)
   155  	}
   156  	return nil
   157  }
   158  
   159  // K8sYamlStruct stubs a Kubernetes YAML file.
   160  // Need to access for now to Namespace only
   161  type K8sYamlStruct struct {
   162  	Metadata struct {
   163  		Namespace string
   164  	}
   165  }