github.com/danielqsj/helm@v2.0.0-alpha.4.0.20160908204436-976e0ba5199b+incompatible/pkg/chartutil/load.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 chartutil 18 19 import ( 20 "archive/tar" 21 "bytes" 22 "compress/gzip" 23 "errors" 24 "fmt" 25 "io" 26 "io/ioutil" 27 "os" 28 "path/filepath" 29 "strings" 30 31 "github.com/golang/protobuf/ptypes/any" 32 33 "k8s.io/helm/pkg/ignore" 34 "k8s.io/helm/pkg/proto/hapi/chart" 35 ) 36 37 // Load takes a string name, tries to resolve it to a file or directory, and then loads it. 38 // 39 // This is the preferred way to load a chart. It will discover the chart encoding 40 // and hand off to the appropriate chart reader. 41 // 42 // If a .helmignore file is present, the directory loader will skip loading any files 43 // matching it. But .helmignore is not evaluated when reading out of an archive. 44 func Load(name string) (*chart.Chart, error) { 45 fi, err := os.Stat(name) 46 if err != nil { 47 return nil, err 48 } 49 if fi.IsDir() { 50 return LoadDir(name) 51 } 52 return LoadFile(name) 53 } 54 55 // afile represents an archive file buffered for later processing. 56 type afile struct { 57 name string 58 data []byte 59 } 60 61 // LoadArchive loads from a reader containing a compressed tar archive. 62 func LoadArchive(in io.Reader) (*chart.Chart, error) { 63 unzipped, err := gzip.NewReader(in) 64 if err != nil { 65 return &chart.Chart{}, err 66 } 67 defer unzipped.Close() 68 69 files := []*afile{} 70 tr := tar.NewReader(unzipped) 71 for { 72 b := bytes.NewBuffer(nil) 73 hd, err := tr.Next() 74 if err == io.EOF { 75 break 76 } 77 if err != nil { 78 return &chart.Chart{}, err 79 } 80 81 if hd.FileInfo().IsDir() { 82 // Use this instead of hd.Typeflag because we don't have to do any 83 // inference chasing. 84 continue 85 } 86 87 parts := strings.Split(hd.Name, "/") 88 n := strings.Join(parts[1:], "/") 89 90 if _, err := io.Copy(b, tr); err != nil { 91 return &chart.Chart{}, err 92 } 93 94 files = append(files, &afile{name: n, data: b.Bytes()}) 95 b.Reset() 96 } 97 98 if len(files) == 0 { 99 return nil, errors.New("no files in chart archive") 100 } 101 102 return loadFiles(files) 103 } 104 105 func loadFiles(files []*afile) (*chart.Chart, error) { 106 c := &chart.Chart{} 107 subcharts := map[string][]*afile{} 108 109 for _, f := range files { 110 if f.name == "Chart.yaml" { 111 m, err := UnmarshalChartfile(f.data) 112 if err != nil { 113 return c, err 114 } 115 c.Metadata = m 116 } else if f.name == "values.toml" { 117 return c, errors.New("values.toml is illegal as of 2.0.0-alpha.2") 118 } else if f.name == "values.yaml" { 119 c.Values = &chart.Config{Raw: string(f.data)} 120 } else if strings.HasPrefix(f.name, "templates/") { 121 c.Templates = append(c.Templates, &chart.Template{Name: f.name, Data: f.data}) 122 } else if strings.HasPrefix(f.name, "charts/") { 123 cname := strings.TrimPrefix(f.name, "charts/") 124 parts := strings.SplitN(cname, "/", 2) 125 scname := parts[0] 126 subcharts[scname] = append(subcharts[scname], &afile{name: cname, data: f.data}) 127 } else { 128 c.Files = append(c.Files, &any.Any{TypeUrl: f.name, Value: f.data}) 129 } 130 } 131 132 // Ensure that we got a Chart.yaml file 133 if c.Metadata == nil || c.Metadata.Name == "" { 134 return c, errors.New("chart metadata (Chart.yaml) missing") 135 } 136 137 for n, files := range subcharts { 138 var sc *chart.Chart 139 var err error 140 if filepath.Ext(n) == ".tgz" { 141 file := files[0] 142 if file.name != n { 143 return c, fmt.Errorf("error unpacking tar in %s: expected %s, got %s", c.Metadata.Name, n, file.name) 144 } 145 // Untar the chart and add to c.Dependencies 146 b := bytes.NewBuffer(file.data) 147 sc, err = LoadArchive(b) 148 } else { 149 // We have to trim the prefix off of every file, and ignore any file 150 // that is in charts/, but isn't actually a chart. 151 buff := make([]*afile, 0, len(files)) 152 for _, f := range files { 153 parts := strings.SplitN(f.name, "/", 2) 154 if len(parts) < 2 { 155 continue 156 } 157 f.name = parts[1] 158 buff = append(buff, f) 159 } 160 sc, err = loadFiles(buff) 161 } 162 163 if err != nil { 164 return c, fmt.Errorf("error unpacking %s in %s: %s", n, c.Metadata.Name, err) 165 } 166 167 c.Dependencies = append(c.Dependencies, sc) 168 } 169 170 return c, nil 171 } 172 173 // LoadFile loads from an archive file. 174 func LoadFile(name string) (*chart.Chart, error) { 175 if fi, err := os.Stat(name); err != nil { 176 return nil, err 177 } else if fi.IsDir() { 178 return nil, errors.New("cannot load a directory") 179 } 180 181 raw, err := os.Open(name) 182 if err != nil { 183 return nil, err 184 } 185 defer raw.Close() 186 187 return LoadArchive(raw) 188 } 189 190 // LoadDir loads from a directory. 191 // 192 // This loads charts only from directories. 193 func LoadDir(dir string) (*chart.Chart, error) { 194 topdir, err := filepath.Abs(dir) 195 if err != nil { 196 return nil, err 197 } 198 199 // Just used for errors. 200 c := &chart.Chart{} 201 202 rules := ignore.Empty() 203 ifile := filepath.Join(topdir, ignore.HelmIgnore) 204 if _, err := os.Stat(ifile); err == nil { 205 r, err := ignore.ParseFile(ifile) 206 if err != nil { 207 return c, err 208 } 209 rules = r 210 } 211 rules.AddDefaults() 212 213 files := []*afile{} 214 topdir += string(filepath.Separator) 215 216 err = filepath.Walk(topdir, func(name string, fi os.FileInfo, err error) error { 217 n := strings.TrimPrefix(name, topdir) 218 if err != nil { 219 return err 220 } 221 if fi.IsDir() { 222 // Directory-based ignore rules should involve skipping the entire 223 // contents of that directory. 224 if rules.Ignore(n, fi) { 225 return filepath.SkipDir 226 } 227 return nil 228 } 229 230 // If a .helmignore file matches, skip this file. 231 if rules.Ignore(n, fi) { 232 return nil 233 } 234 235 data, err := ioutil.ReadFile(name) 236 if err != nil { 237 return fmt.Errorf("error reading %s: %s", n, err) 238 } 239 240 files = append(files, &afile{name: n, data: data}) 241 return nil 242 }) 243 if err != nil { 244 return c, err 245 } 246 247 return loadFiles(files) 248 }