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