github.com/defensepoint-snyk-test/helm-new@v0.0.0-20211130153739-c57ea64d6603/pkg/lint/rules/chartfile.go (about)

     1  /*
     2  Copyright The Helm Authors.
     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  	linter.RunLinterRule(support.InfoSev, chartFileName, validateChartIconPresence(chartFile))
    58  	linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartIconURL(chartFile))
    59  }
    60  
    61  func validateChartYamlNotDirectory(chartPath string) error {
    62  	fi, err := os.Stat(chartPath)
    63  
    64  	if err == nil && fi.IsDir() {
    65  		return errors.New("should be a file, not a directory")
    66  	}
    67  	return nil
    68  }
    69  
    70  func validateChartYamlFormat(chartFileError error) error {
    71  	if chartFileError != nil {
    72  		return fmt.Errorf("unable to parse YAML\n\t%s", chartFileError.Error())
    73  	}
    74  	return nil
    75  }
    76  
    77  func validateChartName(cf *chart.Metadata) error {
    78  	if cf.Name == "" {
    79  		return errors.New("name is required")
    80  	}
    81  	return nil
    82  }
    83  
    84  func validateChartNameDirMatch(chartDir string, cf *chart.Metadata) error {
    85  	if cf.Name != filepath.Base(chartDir) {
    86  		return fmt.Errorf("directory name (%s) and chart name (%s) must be the same", filepath.Base(chartDir), cf.Name)
    87  	}
    88  	return nil
    89  }
    90  
    91  func validateChartVersion(cf *chart.Metadata) error {
    92  	if cf.Version == "" {
    93  		return errors.New("version is required")
    94  	}
    95  
    96  	version, err := semver.NewVersion(cf.Version)
    97  
    98  	if err != nil {
    99  		return fmt.Errorf("version '%s' is not a valid SemVer", cf.Version)
   100  	}
   101  
   102  	c, err := semver.NewConstraint("> 0")
   103  	if err != nil {
   104  		return err
   105  	}
   106  	valid, msg := c.Validate(version)
   107  
   108  	if !valid && len(msg) > 0 {
   109  		return fmt.Errorf("version %v", msg[0])
   110  	}
   111  
   112  	return nil
   113  }
   114  
   115  func validateChartEngine(cf *chart.Metadata) error {
   116  	if cf.Engine == "" {
   117  		return nil
   118  	}
   119  
   120  	keys := make([]string, 0, len(chart.Metadata_Engine_value))
   121  	for engine := range chart.Metadata_Engine_value {
   122  		str := strings.ToLower(engine)
   123  
   124  		if str == "unknown" {
   125  			continue
   126  		}
   127  
   128  		if str == cf.Engine {
   129  			return nil
   130  		}
   131  
   132  		keys = append(keys, str)
   133  	}
   134  
   135  	return fmt.Errorf("engine '%v' not valid. Valid options are %v", cf.Engine, keys)
   136  }
   137  
   138  func validateChartMaintainer(cf *chart.Metadata) error {
   139  	for _, maintainer := range cf.Maintainers {
   140  		if maintainer.Name == "" {
   141  			return errors.New("each maintainer requires a name")
   142  		} else if maintainer.Email != "" && !govalidator.IsEmail(maintainer.Email) {
   143  			return fmt.Errorf("invalid email '%s' for maintainer '%s'", maintainer.Email, maintainer.Name)
   144  		} else if maintainer.Url != "" && !govalidator.IsURL(maintainer.Url) {
   145  			return fmt.Errorf("invalid url '%s' for maintainer '%s'", maintainer.Url, maintainer.Name)
   146  		}
   147  	}
   148  	return nil
   149  }
   150  
   151  func validateChartSources(cf *chart.Metadata) error {
   152  	for _, source := range cf.Sources {
   153  		if source == "" || !govalidator.IsRequestURL(source) {
   154  			return fmt.Errorf("invalid source URL '%s'", source)
   155  		}
   156  	}
   157  	return nil
   158  }
   159  
   160  func validateChartIconPresence(cf *chart.Metadata) error {
   161  	if cf.Icon == "" {
   162  		return errors.New("icon is recommended")
   163  	}
   164  	return nil
   165  }
   166  
   167  func validateChartIconURL(cf *chart.Metadata) error {
   168  	if cf.Icon != "" && !govalidator.IsRequestURL(cf.Icon) {
   169  		return fmt.Errorf("invalid icon URL '%s'", cf.Icon)
   170  	}
   171  	return nil
   172  }