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