github.com/migueleliasweb/helm@v2.6.1+incompatible/pkg/repo/chartrepo.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 repo // import "k8s.io/helm/pkg/repo" 18 19 import ( 20 "fmt" 21 "io/ioutil" 22 "net/url" 23 "os" 24 "path/filepath" 25 "strings" 26 27 "github.com/ghodss/yaml" 28 29 "k8s.io/helm/pkg/chartutil" 30 "k8s.io/helm/pkg/getter" 31 "k8s.io/helm/pkg/provenance" 32 ) 33 34 // Entry represents a collection of parameters for chart repository 35 type Entry struct { 36 Name string `json:"name"` 37 Cache string `json:"cache"` 38 URL string `json:"url"` 39 CertFile string `json:"certFile"` 40 KeyFile string `json:"keyFile"` 41 CAFile string `json:"caFile"` 42 } 43 44 // ChartRepository represents a chart repository 45 type ChartRepository struct { 46 Config *Entry 47 ChartPaths []string 48 IndexFile *IndexFile 49 Client getter.Getter 50 } 51 52 // NewChartRepository constructs ChartRepository 53 func NewChartRepository(cfg *Entry, getters getter.Providers) (*ChartRepository, error) { 54 u, err := url.Parse(cfg.URL) 55 if err != nil { 56 return nil, fmt.Errorf("invalid chart URL format: %s", cfg.URL) 57 } 58 59 getterConstructor, err := getters.ByScheme(u.Scheme) 60 if err != nil { 61 return nil, fmt.Errorf("Could not find protocol handler for: %s", u.Scheme) 62 } 63 client, err := getterConstructor(cfg.URL, cfg.CertFile, cfg.KeyFile, cfg.CAFile) 64 if err != nil { 65 return nil, fmt.Errorf("Could not construct protocol handler for: %s", u.Scheme) 66 } 67 68 return &ChartRepository{ 69 Config: cfg, 70 IndexFile: NewIndexFile(), 71 Client: client, 72 }, nil 73 } 74 75 // Load loads a directory of charts as if it were a repository. 76 // 77 // It requires the presence of an index.yaml file in the directory. 78 func (r *ChartRepository) Load() error { 79 dirInfo, err := os.Stat(r.Config.Name) 80 if err != nil { 81 return err 82 } 83 if !dirInfo.IsDir() { 84 return fmt.Errorf("%q is not a directory", r.Config.Name) 85 } 86 87 // FIXME: Why are we recursively walking directories? 88 // FIXME: Why are we not reading the repositories.yaml to figure out 89 // what repos to use? 90 filepath.Walk(r.Config.Name, func(path string, f os.FileInfo, err error) error { 91 if !f.IsDir() { 92 if strings.Contains(f.Name(), "-index.yaml") { 93 i, err := LoadIndexFile(path) 94 if err != nil { 95 return nil 96 } 97 r.IndexFile = i 98 } else if strings.HasSuffix(f.Name(), ".tgz") { 99 r.ChartPaths = append(r.ChartPaths, path) 100 } 101 } 102 return nil 103 }) 104 return nil 105 } 106 107 // DownloadIndexFile fetches the index from a repository. 108 // 109 // cachePath is prepended to any index that does not have an absolute path. This 110 // is for pre-2.2.0 repo files. 111 func (r *ChartRepository) DownloadIndexFile(cachePath string) error { 112 var indexURL string 113 114 indexURL = strings.TrimSuffix(r.Config.URL, "/") + "/index.yaml" 115 resp, err := r.Client.Get(indexURL) 116 if err != nil { 117 return err 118 } 119 120 index, err := ioutil.ReadAll(resp) 121 if err != nil { 122 return err 123 } 124 125 if _, err := loadIndex(index); err != nil { 126 return err 127 } 128 129 // In Helm 2.2.0 the config.cache was accidentally switched to an absolute 130 // path, which broke backward compatibility. This fixes it by prepending a 131 // global cache path to relative paths. 132 // 133 // It is changed on DownloadIndexFile because that was the method that 134 // originally carried the cache path. 135 cp := r.Config.Cache 136 if !filepath.IsAbs(cp) { 137 cp = filepath.Join(cachePath, cp) 138 } 139 140 return ioutil.WriteFile(cp, index, 0644) 141 } 142 143 // Index generates an index for the chart repository and writes an index.yaml file. 144 func (r *ChartRepository) Index() error { 145 err := r.generateIndex() 146 if err != nil { 147 return err 148 } 149 return r.saveIndexFile() 150 } 151 152 func (r *ChartRepository) saveIndexFile() error { 153 index, err := yaml.Marshal(r.IndexFile) 154 if err != nil { 155 return err 156 } 157 return ioutil.WriteFile(filepath.Join(r.Config.Name, indexPath), index, 0644) 158 } 159 160 func (r *ChartRepository) generateIndex() error { 161 for _, path := range r.ChartPaths { 162 ch, err := chartutil.Load(path) 163 if err != nil { 164 return err 165 } 166 167 digest, err := provenance.DigestFile(path) 168 if err != nil { 169 return err 170 } 171 172 if !r.IndexFile.Has(ch.Metadata.Name, ch.Metadata.Version) { 173 r.IndexFile.Add(ch.Metadata, path, r.Config.URL, digest) 174 } 175 // TODO: If a chart exists, but has a different Digest, should we error? 176 } 177 r.IndexFile.SortEntries() 178 return nil 179 } 180 181 // FindChartInRepoURL finds chart in chart repository pointed by repoURL 182 // without adding repo to repostiories 183 func FindChartInRepoURL(repoURL, chartName, chartVersion, certFile, keyFile, caFile string, getters getter.Providers) (string, error) { 184 185 // Download and write the index file to a temporary location 186 tempIndexFile, err := ioutil.TempFile("", "tmp-repo-file") 187 if err != nil { 188 return "", fmt.Errorf("cannot write index file for repository requested") 189 } 190 defer os.Remove(tempIndexFile.Name()) 191 192 c := Entry{ 193 URL: repoURL, 194 CertFile: certFile, 195 KeyFile: keyFile, 196 CAFile: caFile, 197 } 198 r, err := NewChartRepository(&c, getters) 199 if err != nil { 200 return "", err 201 } 202 if err := r.DownloadIndexFile(tempIndexFile.Name()); err != nil { 203 return "", fmt.Errorf("Looks like %q is not a valid chart repository or cannot be reached: %s", repoURL, err) 204 } 205 206 // Read the index file for the repository to get chart information and return chart URL 207 repoIndex, err := LoadIndexFile(tempIndexFile.Name()) 208 if err != nil { 209 return "", err 210 } 211 212 errMsg := fmt.Sprintf("chart %q", chartName) 213 if chartVersion != "" { 214 errMsg = fmt.Sprintf("%s version %q", errMsg, chartVersion) 215 } 216 cv, err := repoIndex.Get(chartName, chartVersion) 217 if err != nil { 218 return "", fmt.Errorf("%s not found in %s repository", errMsg, repoURL) 219 } 220 221 if len(cv.URLs) == 0 { 222 return "", fmt.Errorf("%s has no downloadable URLs", errMsg) 223 } 224 225 return cv.URLs[0], nil 226 }