github.com/danielqsj/helm@v2.0.0-alpha.4.0.20160908204436-976e0ba5199b+incompatible/pkg/lint/rules/chartfile.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 // import "k8s.io/helm/pkg/lint/rules" 18 19 import ( 20 "errors" 21 "fmt" 22 "os" 23 "path/filepath" 24 "strings" 25 26 "github.com/Masterminds/semver" 27 28 "github.com/asaskevich/govalidator" 29 "k8s.io/helm/pkg/chartutil" 30 "k8s.io/helm/pkg/lint/support" 31 "k8s.io/helm/pkg/proto/hapi/chart" 32 ) 33 34 // Chartfile runs a set of linter rules related to Chart.yaml file 35 func Chartfile(linter *support.Linter) { 36 chartFileName := "Chart.yaml" 37 chartPath := filepath.Join(linter.ChartDir, chartFileName) 38 39 linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartYamlNotDirectory(chartPath)) 40 41 chartFile, err := chartutil.LoadChartfile(chartPath) 42 validChartFile := linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartYamlFormat(err)) 43 44 // Guard clause. Following linter rules require a parseable ChartFile 45 if !validChartFile { 46 return 47 } 48 49 linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartName(chartFile)) 50 linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartNameDirMatch(linter.ChartDir, chartFile)) 51 52 // Chart metadata 53 linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartVersion(chartFile)) 54 linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartEngine(chartFile)) 55 linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartMaintainer(chartFile)) 56 linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartSources(chartFile)) 57 } 58 59 func validateChartYamlNotDirectory(chartPath string) error { 60 fi, err := os.Stat(chartPath) 61 62 if err == nil && fi.IsDir() { 63 return errors.New("should be a file, not a directory") 64 } 65 return nil 66 } 67 68 func validateChartYamlFormat(chartFileError error) error { 69 if chartFileError != nil { 70 return fmt.Errorf("unable to parse YAML\n\t%s", chartFileError.Error()) 71 } 72 return nil 73 } 74 75 func validateChartName(cf *chart.Metadata) error { 76 if cf.Name == "" { 77 return errors.New("name is required") 78 } 79 return nil 80 } 81 82 func validateChartNameDirMatch(chartDir string, cf *chart.Metadata) error { 83 if cf.Name != filepath.Base(chartDir) { 84 return fmt.Errorf("directory name (%s) and chart name (%s) must be the same", filepath.Base(chartDir), cf.Name) 85 } 86 return nil 87 } 88 89 func validateChartVersion(cf *chart.Metadata) error { 90 if cf.Version == "" { 91 return errors.New("version is required") 92 } 93 94 version, err := semver.NewVersion(cf.Version) 95 96 if err != nil { 97 return fmt.Errorf("version '%s' is not a valid SemVer", cf.Version) 98 } 99 100 c, err := semver.NewConstraint("> 0") 101 valid, msg := c.Validate(version) 102 103 if !valid && len(msg) > 0 { 104 return fmt.Errorf("version %v", msg[0]) 105 } 106 107 return nil 108 } 109 110 func validateChartEngine(cf *chart.Metadata) error { 111 if cf.Engine == "" { 112 return nil 113 } 114 115 keys := make([]string, 0, len(chart.Metadata_Engine_value)) 116 for engine := range chart.Metadata_Engine_value { 117 str := strings.ToLower(engine) 118 119 if str == "unknown" { 120 continue 121 } 122 123 if str == cf.Engine { 124 return nil 125 } 126 127 keys = append(keys, str) 128 } 129 130 return fmt.Errorf("engine '%v' not valid. Valid options are %v", cf.Engine, keys) 131 } 132 133 func validateChartMaintainer(cf *chart.Metadata) error { 134 for _, maintainer := range cf.Maintainers { 135 if maintainer.Name == "" { 136 return errors.New("each maintainer requires a name") 137 } else if maintainer.Email != "" && !govalidator.IsEmail(maintainer.Email) { 138 return fmt.Errorf("invalid email '%s' for maintainer '%s'", maintainer.Email, maintainer.Name) 139 } 140 } 141 return nil 142 } 143 144 func validateChartSources(cf *chart.Metadata) error { 145 for _, source := range cf.Sources { 146 if source == "" || !govalidator.IsRequestURL(source) { 147 return fmt.Errorf("invalid source URL '%s'", source) 148 } 149 } 150 return nil 151 } 152 153 func validateChartHome(cf *chart.Metadata) error { 154 if cf.Home != "" && !govalidator.IsRequestURL(cf.Home) { 155 return fmt.Errorf("invalid home URL '%s'", cf.Home) 156 } 157 return nil 158 }