github.com/danielqsj/helm@v2.0.0-alpha.4.0.20160908204436-976e0ba5199b+incompatible/pkg/repo/repo.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 "crypto/sha1" 21 "errors" 22 "fmt" 23 "io/ioutil" 24 "net/url" 25 "os" 26 "path/filepath" 27 "strings" 28 "time" 29 30 "gopkg.in/yaml.v2" 31 32 "k8s.io/helm/pkg/chartutil" 33 ) 34 35 // ChartRepository represents a chart repository 36 type ChartRepository struct { 37 RootPath string 38 URL string // URL of repository 39 ChartPaths []string 40 IndexFile *IndexFile 41 } 42 43 // RepoFile represents the repositories.yaml file in $HELM_HOME 44 type RepoFile struct { 45 Repositories map[string]string 46 } 47 48 // LoadRepositoriesFile takes a file at the given path and returns a RepoFile object 49 func LoadRepositoriesFile(path string) (*RepoFile, error) { 50 b, err := ioutil.ReadFile(path) 51 if err != nil { 52 return nil, err 53 } 54 55 var r RepoFile 56 err = yaml.Unmarshal(b, &r) 57 if err != nil { 58 return nil, err 59 } 60 61 return &r, nil 62 } 63 64 // UnmarshalYAML unmarshals the repo file 65 func (rf *RepoFile) UnmarshalYAML(unmarshal func(interface{}) error) error { 66 var repos map[string]string 67 if err := unmarshal(&repos); err != nil { 68 if _, ok := err.(*yaml.TypeError); !ok { 69 return err 70 } 71 } 72 rf.Repositories = repos 73 return nil 74 } 75 76 // LoadChartRepository takes in a path to a local chart repository 77 // which contains packaged charts and an index.yaml file 78 // 79 // This function evaluates the contents of the directory and 80 // returns a ChartRepository 81 func LoadChartRepository(dir, url string) (*ChartRepository, error) { 82 dirInfo, err := os.Stat(dir) 83 if err != nil { 84 return nil, err 85 } 86 87 if !dirInfo.IsDir() { 88 return nil, errors.New(dir + "is not a directory") 89 } 90 91 r := &ChartRepository{RootPath: dir, URL: url} 92 93 filepath.Walk(dir, func(path string, f os.FileInfo, err error) error { 94 if !f.IsDir() { 95 if strings.Contains(f.Name(), "index.yaml") { 96 i, err := LoadIndexFile(path) 97 if err != nil { 98 return nil 99 } 100 r.IndexFile = i 101 } else if strings.HasSuffix(f.Name(), ".tgz") { 102 r.ChartPaths = append(r.ChartPaths, path) 103 } 104 } 105 return nil 106 }) 107 return r, nil 108 } 109 110 func (r *ChartRepository) saveIndexFile() error { 111 index, err := yaml.Marshal(&r.IndexFile.Entries) 112 if err != nil { 113 return err 114 } 115 116 return ioutil.WriteFile(filepath.Join(r.RootPath, indexPath), index, 0644) 117 } 118 119 // Index generates an index for the chart repository and writes an index.yaml file 120 func (r *ChartRepository) Index() error { 121 if r.IndexFile == nil { 122 r.IndexFile = &IndexFile{Entries: make(map[string]*ChartRef)} 123 } 124 125 existCharts := map[string]bool{} 126 127 for _, path := range r.ChartPaths { 128 ch, err := chartutil.Load(path) 129 if err != nil { 130 return err 131 } 132 133 chartfile := ch.Metadata 134 hash, err := generateChecksum(path) 135 if err != nil { 136 return err 137 } 138 139 key := chartfile.Name + "-" + chartfile.Version 140 if r.IndexFile.Entries == nil { 141 r.IndexFile.Entries = make(map[string]*ChartRef) 142 } 143 144 ref, ok := r.IndexFile.Entries[key] 145 var created string 146 if ok && ref.Created != "" { 147 created = ref.Created 148 } else { 149 created = time.Now().UTC().String() 150 } 151 152 url, _ := url.Parse(r.URL) 153 url.Path = filepath.Join(url.Path, key+".tgz") 154 155 entry := &ChartRef{Chartfile: chartfile, Name: chartfile.Name, URL: url.String(), Created: created, Checksum: hash, Removed: false} 156 157 r.IndexFile.Entries[key] = entry 158 159 // chart is existing 160 existCharts[key] = true 161 } 162 163 // update deleted charts with Removed = true 164 for k := range r.IndexFile.Entries { 165 if _, ok := existCharts[k]; !ok { 166 r.IndexFile.Entries[k].Removed = true 167 } 168 } 169 170 return r.saveIndexFile() 171 } 172 173 func generateChecksum(path string) (string, error) { 174 f, err := os.Open(path) 175 if err != nil { 176 return "", err 177 } 178 179 b, err := ioutil.ReadAll(f) 180 if err != nil { 181 return "", err 182 } 183 184 result := sha1.Sum(b) 185 186 return fmt.Sprintf("%x", result), nil 187 }