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