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