mirror of https://github.com/helm/helm.git
				
				
				
			
		
			
				
	
	
		
			154 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			154 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			Go
		
	
	
	
| /*
 | |
| Copyright The Helm Authors.
 | |
| Licensed under the Apache License, Version 2.0 (the "License");
 | |
| you may not use this file except in compliance with the License.
 | |
| You may obtain a copy of the License at
 | |
| 
 | |
| http://www.apache.org/licenses/LICENSE-2.0
 | |
| 
 | |
| Unless required by applicable law or agreed to in writing, software
 | |
| distributed under the License is distributed on an "AS IS" BASIS,
 | |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
| See the License for the specific language governing permissions and
 | |
| limitations under the License.
 | |
| */
 | |
| 
 | |
| package resolver
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"encoding/json"
 | |
| 	"os"
 | |
| 	"path/filepath"
 | |
| 	"strings"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/Masterminds/semver"
 | |
| 	"github.com/pkg/errors"
 | |
| 
 | |
| 	"helm.sh/helm/pkg/chart"
 | |
| 	"helm.sh/helm/pkg/helmpath"
 | |
| 	"helm.sh/helm/pkg/provenance"
 | |
| 	"helm.sh/helm/pkg/repo"
 | |
| )
 | |
| 
 | |
| // Resolver resolves dependencies from semantic version ranges to a particular version.
 | |
| type Resolver struct {
 | |
| 	chartpath string
 | |
| 	helmhome  helmpath.Home
 | |
| }
 | |
| 
 | |
| // New creates a new resolver for a given chart and a given helm home.
 | |
| func New(chartpath string, helmhome helmpath.Home) *Resolver {
 | |
| 	return &Resolver{
 | |
| 		chartpath: chartpath,
 | |
| 		helmhome:  helmhome,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Resolve resolves dependencies and returns a lock file with the resolution.
 | |
| func (r *Resolver) Resolve(reqs []*chart.Dependency, repoNames map[string]string, d string) (*chart.Lock, error) {
 | |
| 
 | |
| 	// Now we clone the dependencies, locking as we go.
 | |
| 	locked := make([]*chart.Dependency, len(reqs))
 | |
| 	missing := []string{}
 | |
| 	for i, d := range reqs {
 | |
| 		if strings.HasPrefix(d.Repository, "file://") {
 | |
| 
 | |
| 			if _, err := GetLocalPath(d.Repository, r.chartpath); err != nil {
 | |
| 				return nil, err
 | |
| 			}
 | |
| 
 | |
| 			locked[i] = &chart.Dependency{
 | |
| 				Name:       d.Name,
 | |
| 				Repository: d.Repository,
 | |
| 				Version:    d.Version,
 | |
| 			}
 | |
| 			continue
 | |
| 		}
 | |
| 		constraint, err := semver.NewConstraint(d.Version)
 | |
| 		if err != nil {
 | |
| 			return nil, errors.Wrapf(err, "dependency %q has an invalid version/constraint format", d.Name)
 | |
| 		}
 | |
| 
 | |
| 		repoIndex, err := repo.LoadIndexFile(r.helmhome.CacheIndex(repoNames[d.Name]))
 | |
| 		if err != nil {
 | |
| 			return nil, errors.Wrap(err, "no cached repo found. (try 'helm repo update')")
 | |
| 		}
 | |
| 
 | |
| 		vs, ok := repoIndex.Entries[d.Name]
 | |
| 		if !ok {
 | |
| 			return nil, errors.Errorf("%s chart not found in repo %s", d.Name, d.Repository)
 | |
| 		}
 | |
| 
 | |
| 		locked[i] = &chart.Dependency{
 | |
| 			Name:       d.Name,
 | |
| 			Repository: d.Repository,
 | |
| 		}
 | |
| 		found := false
 | |
| 		// The version are already sorted and hence the first one to satisfy the constraint is used
 | |
| 		for _, ver := range vs {
 | |
| 			v, err := semver.NewVersion(ver.Version)
 | |
| 			if err != nil || len(ver.URLs) == 0 {
 | |
| 				// Not a legit entry.
 | |
| 				continue
 | |
| 			}
 | |
| 			if constraint.Check(v) {
 | |
| 				found = true
 | |
| 				locked[i].Version = v.Original()
 | |
| 				break
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if !found {
 | |
| 			missing = append(missing, d.Name)
 | |
| 		}
 | |
| 	}
 | |
| 	if len(missing) > 0 {
 | |
| 		return nil, errors.Errorf("can't get a valid version for repositories %s. Try changing the version constraint in Chart.yaml", strings.Join(missing, ", "))
 | |
| 	}
 | |
| 	return &chart.Lock{
 | |
| 		Generated:    time.Now(),
 | |
| 		Digest:       d,
 | |
| 		Dependencies: locked,
 | |
| 	}, nil
 | |
| }
 | |
| 
 | |
| // HashReq generates a hash of the dependencies.
 | |
| //
 | |
| // This should be used only to compare against another hash generated by this
 | |
| // function.
 | |
| func HashReq(req []*chart.Dependency) (string, error) {
 | |
| 	data, err := json.Marshal(req)
 | |
| 	if err != nil {
 | |
| 		return "", err
 | |
| 	}
 | |
| 	s, err := provenance.Digest(bytes.NewBuffer(data))
 | |
| 	return "sha256:" + s, err
 | |
| }
 | |
| 
 | |
| // GetLocalPath generates absolute local path when use
 | |
| // "file://" in repository of dependencies
 | |
| func GetLocalPath(repo, chartpath string) (string, error) {
 | |
| 	var depPath string
 | |
| 	var err error
 | |
| 	p := strings.TrimPrefix(repo, "file://")
 | |
| 
 | |
| 	// root path is absolute
 | |
| 	if strings.HasPrefix(p, "/") {
 | |
| 		if depPath, err = filepath.Abs(p); err != nil {
 | |
| 			return "", err
 | |
| 		}
 | |
| 	} else {
 | |
| 		depPath = filepath.Join(chartpath, p)
 | |
| 	}
 | |
| 
 | |
| 	if _, err = os.Stat(depPath); os.IsNotExist(err) {
 | |
| 		return "", errors.Errorf("directory %s not found", depPath)
 | |
| 	} else if err != nil {
 | |
| 		return "", err
 | |
| 	}
 | |
| 
 | |
| 	return depPath, nil
 | |
| }
 |