github.com/x-helm/helm@v3.0.0-beta.3+incompatible/pkg/action/dependency.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 action
    18  
    19  import (
    20  	"fmt"
    21  	"io"
    22  	"os"
    23  	"path/filepath"
    24  
    25  	"github.com/Masterminds/semver"
    26  	"github.com/gosuri/uitable"
    27  
    28  	"helm.sh/helm/pkg/chart"
    29  	"helm.sh/helm/pkg/chart/loader"
    30  )
    31  
    32  // Dependency is the action for building a given chart's dependency tree.
    33  //
    34  // It provides the implementation of 'helm dependency' and its respective subcommands.
    35  type Dependency struct {
    36  	Verify      bool
    37  	Keyring     string
    38  	SkipRefresh bool
    39  }
    40  
    41  // NewDependency creates a new Dependency object with the given configuration.
    42  func NewDependency() *Dependency {
    43  	return &Dependency{}
    44  }
    45  
    46  // List executes 'helm dependency list'.
    47  func (d *Dependency) List(chartpath string, out io.Writer) error {
    48  	c, err := loader.Load(chartpath)
    49  	if err != nil {
    50  		return err
    51  	}
    52  
    53  	if c.Metadata.Dependencies == nil {
    54  		fmt.Fprintf(out, "WARNING: no dependencies at %s\n", filepath.Join(chartpath, "charts"))
    55  		return nil
    56  	}
    57  
    58  	d.printDependencies(chartpath, out, c.Metadata.Dependencies)
    59  	fmt.Fprintln(out)
    60  	d.printMissing(chartpath, out, c.Metadata.Dependencies)
    61  	return nil
    62  }
    63  
    64  func (d *Dependency) dependencyStatus(chartpath string, dep *chart.Dependency) string {
    65  	filename := fmt.Sprintf("%s-%s.tgz", dep.Name, "*")
    66  
    67  	switch archives, err := filepath.Glob(filepath.Join(chartpath, "charts", filename)); {
    68  	case err != nil:
    69  		return "bad pattern"
    70  	case len(archives) > 1:
    71  		return "too many matches"
    72  	case len(archives) == 1:
    73  		archive := archives[0]
    74  		if _, err := os.Stat(archive); err == nil {
    75  			c, err := loader.Load(archive)
    76  			if err != nil {
    77  				return "corrupt"
    78  			}
    79  			if c.Name() != dep.Name {
    80  				return "misnamed"
    81  			}
    82  
    83  			if c.Metadata.Version != dep.Version {
    84  				constraint, err := semver.NewConstraint(dep.Version)
    85  				if err != nil {
    86  					return "invalid version"
    87  				}
    88  
    89  				v, err := semver.NewVersion(c.Metadata.Version)
    90  				if err != nil {
    91  					return "invalid version"
    92  				}
    93  
    94  				if constraint.Check(v) {
    95  					return "ok"
    96  				}
    97  				return "wrong version"
    98  			}
    99  			return "ok"
   100  		}
   101  	}
   102  
   103  	folder := filepath.Join(chartpath, "charts", dep.Name)
   104  	if fi, err := os.Stat(folder); err != nil {
   105  		return "missing"
   106  	} else if !fi.IsDir() {
   107  		return "mispackaged"
   108  	}
   109  
   110  	c, err := loader.Load(folder)
   111  	if err != nil {
   112  		return "corrupt"
   113  	}
   114  
   115  	if c.Name() != dep.Name {
   116  		return "misnamed"
   117  	}
   118  
   119  	if c.Metadata.Version != dep.Version {
   120  		constraint, err := semver.NewConstraint(dep.Version)
   121  		if err != nil {
   122  			return "invalid version"
   123  		}
   124  
   125  		v, err := semver.NewVersion(c.Metadata.Version)
   126  		if err != nil {
   127  			return "invalid version"
   128  		}
   129  
   130  		if constraint.Check(v) {
   131  			return "unpacked"
   132  		}
   133  		return "wrong version"
   134  	}
   135  
   136  	return "unpacked"
   137  }
   138  
   139  // printDependencies prints all of the dependencies in the yaml file.
   140  func (d *Dependency) printDependencies(chartpath string, out io.Writer, reqs []*chart.Dependency) {
   141  	table := uitable.New()
   142  	table.MaxColWidth = 80
   143  	table.AddRow("NAME", "VERSION", "REPOSITORY", "STATUS")
   144  	for _, row := range reqs {
   145  		table.AddRow(row.Name, row.Version, row.Repository, d.dependencyStatus(chartpath, row))
   146  	}
   147  	fmt.Fprintln(out, table)
   148  }
   149  
   150  // printMissing prints warnings about charts that are present on disk, but are
   151  // not in Charts.yaml.
   152  func (d *Dependency) printMissing(chartpath string, out io.Writer, reqs []*chart.Dependency) {
   153  	folder := filepath.Join(chartpath, "charts/*")
   154  	files, err := filepath.Glob(folder)
   155  	if err != nil {
   156  		fmt.Fprintln(out, err)
   157  		return
   158  	}
   159  
   160  	for _, f := range files {
   161  		fi, err := os.Stat(f)
   162  		if err != nil {
   163  			fmt.Fprintf(out, "Warning: %s\n", err)
   164  		}
   165  		// Skip anything that is not a directory and not a tgz file.
   166  		if !fi.IsDir() && filepath.Ext(f) != ".tgz" {
   167  			continue
   168  		}
   169  		c, err := loader.Load(f)
   170  		if err != nil {
   171  			fmt.Fprintf(out, "WARNING: %q is not a chart.\n", f)
   172  			continue
   173  		}
   174  		found := false
   175  		for _, d := range reqs {
   176  			if d.Name == c.Name() {
   177  				found = true
   178  				break
   179  			}
   180  		}
   181  		if !found {
   182  			fmt.Fprintf(out, "WARNING: %q is not in Chart.yaml.\n", f)
   183  		}
   184  	}
   185  }