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