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