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