github.com/azure-devops-engineer/helm@v3.0.0-alpha.2+incompatible/pkg/chart/loader/load.go (about) 1 /* 2 Copyright The Helm Authors. 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 loader 18 19 import ( 20 "bytes" 21 "os" 22 "path/filepath" 23 "strings" 24 25 "github.com/pkg/errors" 26 "sigs.k8s.io/yaml" 27 28 "helm.sh/helm/pkg/chart" 29 ) 30 31 // ChartLoader loads a chart. 32 type ChartLoader interface { 33 Load() (*chart.Chart, error) 34 } 35 36 // Loader returns a new ChartLoader appropriate for the given chart name 37 func Loader(name string) (ChartLoader, error) { 38 fi, err := os.Stat(name) 39 if err != nil { 40 return nil, err 41 } 42 if fi.IsDir() { 43 return DirLoader(name), nil 44 } 45 return FileLoader(name), nil 46 47 } 48 49 // Load takes a string name, tries to resolve it to a file or directory, and then loads it. 50 // 51 // This is the preferred way to load a chart. It will discover the chart encoding 52 // and hand off to the appropriate chart reader. 53 // 54 // If a .helmignore file is present, the directory loader will skip loading any files 55 // matching it. But .helmignore is not evaluated when reading out of an archive. 56 func Load(name string) (*chart.Chart, error) { 57 l, err := Loader(name) 58 if err != nil { 59 return nil, err 60 } 61 return l.Load() 62 } 63 64 // BufferedFile represents an archive file buffered for later processing. 65 type BufferedFile struct { 66 Name string 67 Data []byte 68 } 69 70 // LoadFiles loads from in-memory files. 71 func LoadFiles(files []*BufferedFile) (*chart.Chart, error) { 72 c := new(chart.Chart) 73 subcharts := make(map[string][]*BufferedFile) 74 75 for _, f := range files { 76 switch { 77 case f.Name == "Chart.yaml": 78 if c.Metadata == nil { 79 c.Metadata = new(chart.Metadata) 80 } 81 if err := yaml.Unmarshal(f.Data, c.Metadata); err != nil { 82 return c, errors.Wrap(err, "cannot load Chart.yaml") 83 } 84 // NOTE(bacongobbler): while the chart specification says that APIVersion must be set, 85 // Helm 2 accepted charts that did not provide an APIVersion in their chart metadata. 86 // Because of that, if APIVersion is unset, we should assume we're loading a v1 chart. 87 if c.Metadata.APIVersion == "" { 88 c.Metadata.APIVersion = chart.APIVersionV1 89 } 90 case f.Name == "Chart.lock": 91 c.Lock = new(chart.Lock) 92 if err := yaml.Unmarshal(f.Data, &c.Lock); err != nil { 93 return c, errors.Wrap(err, "cannot load Chart.lock") 94 } 95 case f.Name == "values.yaml": 96 c.Values = make(map[string]interface{}) 97 if err := yaml.Unmarshal(f.Data, &c.Values); err != nil { 98 return c, errors.Wrap(err, "cannot load values.yaml") 99 } 100 case f.Name == "values.schema.json": 101 c.Schema = f.Data 102 103 // Deprecated: requirements.yaml is deprecated use Chart.yaml. 104 // We will handle it for you because we are nice people 105 case f.Name == "requirements.yaml": 106 if c.Metadata == nil { 107 c.Metadata = new(chart.Metadata) 108 } 109 if err := yaml.Unmarshal(f.Data, c.Metadata); err != nil { 110 return c, errors.Wrap(err, "cannot load requirements.yaml") 111 } 112 // Deprecated: requirements.lock is deprecated use Chart.lock. 113 case f.Name == "requirements.lock": 114 c.Lock = new(chart.Lock) 115 if err := yaml.Unmarshal(f.Data, &c.Lock); err != nil { 116 return c, errors.Wrap(err, "cannot load requirements.lock") 117 } 118 119 case strings.HasPrefix(f.Name, "templates/"): 120 c.Templates = append(c.Templates, &chart.File{Name: f.Name, Data: f.Data}) 121 case strings.HasPrefix(f.Name, "charts/"): 122 if filepath.Ext(f.Name) == ".prov" { 123 c.Files = append(c.Files, &chart.File{Name: f.Name, Data: f.Data}) 124 continue 125 } 126 127 fname := strings.TrimPrefix(f.Name, "charts/") 128 cname := strings.SplitN(fname, "/", 2)[0] 129 subcharts[cname] = append(subcharts[cname], &BufferedFile{Name: fname, Data: f.Data}) 130 default: 131 c.Files = append(c.Files, &chart.File{Name: f.Name, Data: f.Data}) 132 } 133 } 134 135 if err := c.Validate(); err != nil { 136 return c, err 137 } 138 139 for n, files := range subcharts { 140 var sc *chart.Chart 141 var err error 142 switch { 143 case strings.IndexAny(n, "_.") == 0: 144 continue 145 case filepath.Ext(n) == ".tgz": 146 file := files[0] 147 if file.Name != n { 148 return c, errors.Errorf("error unpacking tar in %s: expected %s, got %s", c.Name(), n, file.Name) 149 } 150 // Untar the chart and add to c.Dependencies 151 sc, err = LoadArchive(bytes.NewBuffer(file.Data)) 152 default: 153 // We have to trim the prefix off of every file, and ignore any file 154 // that is in charts/, but isn't actually a chart. 155 buff := make([]*BufferedFile, 0, len(files)) 156 for _, f := range files { 157 parts := strings.SplitN(f.Name, "/", 2) 158 if len(parts) < 2 { 159 continue 160 } 161 f.Name = parts[1] 162 buff = append(buff, f) 163 } 164 sc, err = LoadFiles(buff) 165 } 166 167 if err != nil { 168 return c, errors.Wrapf(err, "error unpacking %s in %s", n, c.Name()) 169 } 170 c.AddDependency(sc) 171 } 172 173 return c, nil 174 }