github.com/felipejfc/helm@v2.1.2+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 if err != nil { 102 return err 103 } 104 valid, msg := c.Validate(version) 105 106 if !valid && len(msg) > 0 { 107 return fmt.Errorf("version %v", msg[0]) 108 } 109 110 return nil 111 } 112 113 func validateChartEngine(cf *chart.Metadata) error { 114 if cf.Engine == "" { 115 return nil 116 } 117 118 keys := make([]string, 0, len(chart.Metadata_Engine_value)) 119 for engine := range chart.Metadata_Engine_value { 120 str := strings.ToLower(engine) 121 122 if str == "unknown" { 123 continue 124 } 125 126 if str == cf.Engine { 127 return nil 128 } 129 130 keys = append(keys, str) 131 } 132 133 return fmt.Errorf("engine '%v' not valid. Valid options are %v", cf.Engine, keys) 134 } 135 136 func validateChartMaintainer(cf *chart.Metadata) error { 137 for _, maintainer := range cf.Maintainers { 138 if maintainer.Name == "" { 139 return errors.New("each maintainer requires a name") 140 } else if maintainer.Email != "" && !govalidator.IsEmail(maintainer.Email) { 141 return fmt.Errorf("invalid email '%s' for maintainer '%s'", maintainer.Email, maintainer.Name) 142 } 143 } 144 return nil 145 } 146 147 func validateChartSources(cf *chart.Metadata) error { 148 for _, source := range cf.Sources { 149 if source == "" || !govalidator.IsRequestURL(source) { 150 return fmt.Errorf("invalid source URL '%s'", source) 151 } 152 } 153 return nil 154 }