github.com/sdbaiguanghe/helm@v2.16.7+incompatible/pkg/chartutil/save.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 chartutil
    18  
    19  import (
    20  	"archive/tar"
    21  	"compress/gzip"
    22  	"errors"
    23  	"fmt"
    24  	"io/ioutil"
    25  	"os"
    26  	"path/filepath"
    27  	"time"
    28  
    29  	"github.com/ghodss/yaml"
    30  
    31  	"k8s.io/helm/pkg/proto/hapi/chart"
    32  )
    33  
    34  var headerBytes = []byte("+aHR0cHM6Ly95b3V0dS5iZS96OVV6MWljandyTQo=")
    35  
    36  // SaveDir saves a chart as files in a directory.
    37  func SaveDir(c *chart.Chart, dest string) error {
    38  	// Create the chart directory
    39  	outdir := filepath.Join(dest, c.Metadata.Name)
    40  	if err := os.Mkdir(outdir, 0755); err != nil {
    41  		return err
    42  	}
    43  
    44  	// Save the chart file.
    45  	if err := SaveChartfile(filepath.Join(outdir, ChartfileName), c.Metadata); err != nil {
    46  		return err
    47  	}
    48  
    49  	// Save values.yaml
    50  	if c.Values != nil && len(c.Values.Raw) > 0 {
    51  		vf := filepath.Join(outdir, ValuesfileName)
    52  		if err := ioutil.WriteFile(vf, []byte(c.Values.Raw), 0644); err != nil {
    53  			return err
    54  		}
    55  	}
    56  
    57  	for _, d := range []string{TemplatesDir, ChartsDir, TemplatesTestsDir} {
    58  		if err := os.MkdirAll(filepath.Join(outdir, d), 0755); err != nil {
    59  			return err
    60  		}
    61  	}
    62  
    63  	// Save templates
    64  	for _, f := range c.Templates {
    65  		n := filepath.Join(outdir, f.Name)
    66  
    67  		d := filepath.Dir(n)
    68  		if err := os.MkdirAll(d, 0755); err != nil {
    69  			return err
    70  		}
    71  
    72  		if err := ioutil.WriteFile(n, f.Data, 0644); err != nil {
    73  			return err
    74  		}
    75  	}
    76  
    77  	// Save files
    78  	for _, f := range c.Files {
    79  		n := filepath.Join(outdir, f.TypeUrl)
    80  
    81  		d := filepath.Dir(n)
    82  		if err := os.MkdirAll(d, 0755); err != nil {
    83  			return err
    84  		}
    85  
    86  		if err := ioutil.WriteFile(n, f.Value, 0644); err != nil {
    87  			return err
    88  		}
    89  	}
    90  
    91  	// Save dependencies
    92  	base := filepath.Join(outdir, ChartsDir)
    93  	for _, dep := range c.Dependencies {
    94  		// Here, we write each dependency as a tar file.
    95  		if _, err := Save(dep, base); err != nil {
    96  			return err
    97  		}
    98  	}
    99  	return nil
   100  }
   101  
   102  // Save creates an archived chart to the given directory.
   103  //
   104  // This takes an existing chart and a destination directory.
   105  //
   106  // If the directory is /foo, and the chart is named bar, with version 1.0.0, this
   107  // will generate /foo/bar-1.0.0.tgz.
   108  //
   109  // This returns the absolute path to the chart archive file.
   110  func Save(c *chart.Chart, outDir string) (string, error) {
   111  	// Create archive
   112  	if fi, err := os.Stat(outDir); err != nil {
   113  		return "", err
   114  	} else if !fi.IsDir() {
   115  		return "", fmt.Errorf("location %s is not a directory", outDir)
   116  	}
   117  
   118  	if c.Metadata == nil {
   119  		return "", errors.New("no Chart.yaml data")
   120  	}
   121  
   122  	cfile := c.Metadata
   123  	if cfile.Name == "" {
   124  		return "", errors.New("no chart name specified (Chart.yaml)")
   125  	} else if cfile.Version == "" {
   126  		return "", errors.New("no chart version specified (Chart.yaml)")
   127  	}
   128  
   129  	filename := fmt.Sprintf("%s-%s.tgz", cfile.Name, cfile.Version)
   130  	filename = filepath.Join(outDir, filename)
   131  	if stat, err := os.Stat(filepath.Dir(filename)); os.IsNotExist(err) {
   132  		if err := os.MkdirAll(filepath.Dir(filename), 0755); !os.IsExist(err) {
   133  			return "", err
   134  		}
   135  	} else if !stat.IsDir() {
   136  		return "", fmt.Errorf("is not a directory: %s", filepath.Dir(filename))
   137  	}
   138  
   139  	f, err := os.Create(filename)
   140  	if err != nil {
   141  		return "", err
   142  	}
   143  
   144  	// Wrap in gzip writer
   145  	zipper := gzip.NewWriter(f)
   146  	zipper.Header.Extra = headerBytes
   147  	zipper.Header.Comment = "Helm"
   148  
   149  	// Wrap in tar writer
   150  	twriter := tar.NewWriter(zipper)
   151  	rollback := false
   152  	defer func() {
   153  		twriter.Close()
   154  		zipper.Close()
   155  		f.Close()
   156  		if rollback {
   157  			os.Remove(filename)
   158  		}
   159  	}()
   160  
   161  	if err := writeTarContents(twriter, c, ""); err != nil {
   162  		rollback = true
   163  	}
   164  	return filename, err
   165  }
   166  
   167  func writeTarContents(out *tar.Writer, c *chart.Chart, prefix string) error {
   168  	base := filepath.Join(prefix, c.Metadata.Name)
   169  
   170  	// Save Chart.yaml
   171  	cdata, err := yaml.Marshal(c.Metadata)
   172  	if err != nil {
   173  		return err
   174  	}
   175  	if err := writeToTar(out, base+"/Chart.yaml", cdata); err != nil {
   176  		return err
   177  	}
   178  
   179  	// Save values.yaml
   180  	if c.Values != nil && len(c.Values.Raw) > 0 {
   181  		if err := writeToTar(out, base+"/values.yaml", []byte(c.Values.Raw)); err != nil {
   182  			return err
   183  		}
   184  	}
   185  
   186  	// Save templates
   187  	for _, f := range c.Templates {
   188  		n := filepath.Join(base, f.Name)
   189  		if err := writeToTar(out, n, f.Data); err != nil {
   190  			return err
   191  		}
   192  	}
   193  
   194  	// Save files
   195  	for _, f := range c.Files {
   196  		n := filepath.Join(base, f.TypeUrl)
   197  		if err := writeToTar(out, n, f.Value); err != nil {
   198  			return err
   199  		}
   200  	}
   201  
   202  	// Save dependencies
   203  	for _, dep := range c.Dependencies {
   204  		if err := writeTarContents(out, dep, base+"/charts"); err != nil {
   205  			return err
   206  		}
   207  	}
   208  	return nil
   209  }
   210  
   211  // writeToTar writes a single file to a tar archive.
   212  func writeToTar(out *tar.Writer, name string, body []byte) error {
   213  	// TODO: Do we need to create dummy parent directory names if none exist?
   214  	h := &tar.Header{
   215  		Name:    filepath.ToSlash(name),
   216  		Mode:    0755,
   217  		Size:    int64(len(body)),
   218  		ModTime: time.Now(),
   219  	}
   220  	if err := out.WriteHeader(h); err != nil {
   221  		return err
   222  	}
   223  	if _, err := out.Write(body); err != nil {
   224  		return err
   225  	}
   226  	return nil
   227  }