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  }