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  }