github.com/appscode/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 }