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