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