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