github.com/koderover/helm@v2.17.0+incompatible/pkg/resolver/resolver.go (about) 1 /* 2 Copyright The Helm Authors. 3 Licensed under the Apache License, Version 2.0 (the "License"); 4 you may not use this file except in compliance with the License. 5 You may obtain a copy of the License at 6 7 http://www.apache.org/licenses/LICENSE-2.0 8 9 Unless required by applicable law or agreed to in writing, software 10 distributed under the License is distributed on an "AS IS" BASIS, 11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 See the License for the specific language governing permissions and 13 limitations under the License. 14 */ 15 16 package resolver 17 18 import ( 19 "bytes" 20 "encoding/json" 21 "fmt" 22 "os" 23 "path/filepath" 24 "strings" 25 "time" 26 27 "github.com/Masterminds/semver" 28 29 "k8s.io/helm/pkg/chartutil" 30 "k8s.io/helm/pkg/helm/helmpath" 31 "k8s.io/helm/pkg/provenance" 32 "k8s.io/helm/pkg/repo" 33 ) 34 35 // Resolver resolves dependencies from semantic version ranges to a particular version. 36 type Resolver struct { 37 chartpath string 38 helmhome helmpath.Home 39 } 40 41 // New creates a new resolver for a given chart and a given helm home. 42 func New(chartpath string, helmhome helmpath.Home) *Resolver { 43 return &Resolver{ 44 chartpath: chartpath, 45 helmhome: helmhome, 46 } 47 } 48 49 // Resolve resolves dependencies and returns a lock file with the resolution. 50 func (r *Resolver) Resolve(reqs *chartutil.Requirements, repoNames map[string]string, d string) (*chartutil.RequirementsLock, error) { 51 52 // Now we clone the dependencies, locking as we go. 53 locked := make([]*chartutil.Dependency, len(reqs.Dependencies)) 54 missing := []string{} 55 for i, d := range reqs.Dependencies { 56 if d.Repository == "" { 57 // Local chart subfolder 58 if _, err := GetLocalPath(filepath.Join("charts", d.Name), r.chartpath); err != nil { 59 return nil, err 60 } 61 62 locked[i] = &chartutil.Dependency{ 63 Name: d.Name, 64 Repository: "", 65 Version: d.Version, 66 } 67 continue 68 } 69 if strings.HasPrefix(d.Repository, "file://") { 70 71 if _, err := GetLocalPath(d.Repository, r.chartpath); err != nil { 72 return nil, err 73 } 74 75 locked[i] = &chartutil.Dependency{ 76 Name: d.Name, 77 Repository: d.Repository, 78 Version: d.Version, 79 } 80 continue 81 } 82 constraint, err := semver.NewConstraint(d.Version) 83 if err != nil { 84 return nil, fmt.Errorf("dependency %q has an invalid version/constraint format: %s", d.Name, err) 85 } 86 87 // repo does not exist in cache but has url info 88 cacheRepoName := repoNames[d.Name] 89 if cacheRepoName == "" && d.Repository != "" { 90 locked[i] = &chartutil.Dependency{ 91 Name: d.Name, 92 Repository: d.Repository, 93 Version: d.Version, 94 } 95 continue 96 } 97 98 repoIndex, err := repo.LoadIndexFile(r.helmhome.CacheIndex(cacheRepoName)) 99 if err != nil { 100 return nil, fmt.Errorf("no cached repo found. (try 'helm repo update'). %s", err) 101 } 102 103 vs, ok := repoIndex.Entries[d.Name] 104 if !ok { 105 return nil, fmt.Errorf("%s chart not found in repo %s", d.Name, d.Repository) 106 } 107 108 locked[i] = &chartutil.Dependency{ 109 Name: d.Name, 110 Repository: d.Repository, 111 } 112 found := false 113 // The version are already sorted and hence the first one to satisfy the constraint is used 114 for _, ver := range vs { 115 v, err := semver.NewVersion(ver.Version) 116 if err != nil || len(ver.URLs) == 0 { 117 // Not a legit entry. 118 continue 119 } 120 if constraint.Check(v) { 121 found = true 122 locked[i].Version = v.Original() 123 break 124 } 125 } 126 127 if !found { 128 missing = append(missing, d.Name) 129 } 130 } 131 if len(missing) > 0 { 132 return nil, fmt.Errorf("Can't get a valid version for repositories %s. Try changing the version constraint in requirements.yaml", strings.Join(missing, ", ")) 133 } 134 return &chartutil.RequirementsLock{ 135 Generated: time.Now(), 136 Digest: d, 137 Dependencies: locked, 138 }, nil 139 } 140 141 // HashReq generates a hash of the requirements. 142 // 143 // This should be used only to compare against another hash generated by this 144 // function. 145 func HashReq(req *chartutil.Requirements) (string, error) { 146 data, err := json.Marshal(req) 147 if err != nil { 148 return "", err 149 } 150 s, err := provenance.Digest(bytes.NewBuffer(data)) 151 return "sha256:" + s, err 152 } 153 154 // GetLocalPath generates absolute local path when use 155 // "file://" in repository of requirements 156 func GetLocalPath(repo string, chartpath string) (string, error) { 157 var depPath string 158 var err error 159 p := strings.TrimPrefix(repo, "file://") 160 161 // root path is absolute 162 if strings.HasPrefix(p, "/") { 163 if depPath, err = filepath.Abs(p); err != nil { 164 return "", err 165 } 166 } else { 167 depPath = filepath.Join(chartpath, p) 168 } 169 170 if _, err = os.Stat(depPath); os.IsNotExist(err) { 171 return "", fmt.Errorf("directory %s not found", depPath) 172 } else if err != nil { 173 return "", err 174 } 175 176 return depPath, nil 177 }