github.com/strongmonkey/helm@v2.7.2+incompatible/pkg/chartutil/save.go (about)

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