diff --git a/pkg/action/package.go b/pkg/action/package.go index c3208b44b..c85c54d56 100644 --- a/pkg/action/package.go +++ b/pkg/action/package.go @@ -94,7 +94,7 @@ func (p *Package) Run(path string) (string, error) { dest = p.Destination } - name, err := chartutil.Save(ch, dest) + name, err := chartutil.SaveArchive(ch, dest) if err != nil { return "", errors.Wrap(err, "failed to save") } diff --git a/pkg/chartutil/save.go b/pkg/chartutil/save.go index bde902c98..4a5214600 100644 --- a/pkg/chartutil/save.go +++ b/pkg/chartutil/save.go @@ -20,6 +20,7 @@ import ( "archive/tar" "compress/gzip" "fmt" + "io" "io/ioutil" "os" "path/filepath" @@ -33,6 +34,11 @@ import ( var headerBytes = []byte("+aHR0cHM6Ly95b3V0dS5iZS96OVV6MWljandyTQo=") // SaveDir saves a chart as files in a directory. +// +// This takes an existing chart and a destination directory. +// +// If the directory is /foo, and the chart is named bar, with version 1.0.0, this +// will generate /foo/bar/Chart.yaml, /foo/bar/values.yaml, etc. func SaveDir(c *chart.Chart, dest string) error { // Create the chart directory outdir := filepath.Join(dest, c.Name()) @@ -89,14 +95,28 @@ func SaveDir(c *chart.Chart, dest string) error { base := filepath.Join(outdir, ChartsDir) for _, dep := range c.Dependencies() { // Here, we write each dependency as a tar file. - if _, err := Save(dep, base); err != nil { + if _, err := SaveArchive(dep, base); err != nil { return err } } return nil } -// Save creates an archived chart to the given directory. +// Save creates an archived chart and writes it to w. +func Save(c *chart.Chart, w io.Writer) error { + zipper := gzip.NewWriter(w) + defer zipper.Close() + zipper.Header.Extra = headerBytes + zipper.Header.Comment = "Helm" + + // Wrap in tar writer + twriter := tar.NewWriter(zipper) + defer twriter.Close() + + return writeTarContents(twriter, c, "") +} + +// SaveArchive creates an archived chart to the given directory. // // This takes an existing chart and a destination directory. // @@ -104,7 +124,7 @@ func SaveDir(c *chart.Chart, dest string) error { // will generate /foo/bar-1.0.0.tgz. // // This returns the absolute path to the chart archive file. -func Save(c *chart.Chart, outDir string) (string, error) { +func SaveArchive(c *chart.Chart, outDir string) (string, error) { // Create archive if fi, err := os.Stat(outDir); err != nil { return "", err @@ -131,24 +151,15 @@ func Save(c *chart.Chart, outDir string) (string, error) { return "", err } - // Wrap in gzip writer - zipper := gzip.NewWriter(f) - zipper.Header.Extra = headerBytes - zipper.Header.Comment = "Helm" - - // Wrap in tar writer - twriter := tar.NewWriter(zipper) rollback := false defer func() { - twriter.Close() - zipper.Close() f.Close() if rollback { os.Remove(filename) } }() - if err := writeTarContents(twriter, c, ""); err != nil { + if err := Save(c, f); err != nil { rollback = true } return filename, err diff --git a/pkg/chartutil/save_test.go b/pkg/chartutil/save_test.go index 80b31bb5d..17c5133ab 100644 --- a/pkg/chartutil/save_test.go +++ b/pkg/chartutil/save_test.go @@ -17,6 +17,7 @@ limitations under the License. package chartutil import ( + "bytes" "io/ioutil" "os" "strings" @@ -27,6 +28,36 @@ import ( ) func TestSave(t *testing.T) { + c := &chart.Chart{ + Metadata: &chart.Metadata{ + APIVersion: chart.APIVersionV1, + Name: "ahab", + Version: "1.2.3", + }, + Files: []*chart.File{ + {Name: "scheherazade/shahryar.txt", Data: []byte("1,001 Nights")}, + }, + } + + buf := bytes.NewBuffer(nil) + + if err := Save(c, buf); err != nil { + t.Fatalf("Failed to save: %s", err) + } + + c2, err := loader.LoadArchive(buf) + if err != nil { + t.Fatal(err) + } + if c2.Name() != c.Name() { + t.Fatalf("Expected chart archive to have %q, got %q", c.Name(), c2.Name()) + } + if len(c2.Files) != 1 || c2.Files[0].Name != "scheherazade/shahryar.txt" { + t.Fatal("Files data did not match") + } +} + +func TestSaveArchive(t *testing.T) { tmp, err := ioutil.TempDir("", "helm-") if err != nil { t.Fatal(err) @@ -44,7 +75,7 @@ func TestSave(t *testing.T) { }, } - where, err := Save(c, tmp) + where, err := SaveArchive(c, tmp) if err != nil { t.Fatalf("Failed to save: %s", err) } diff --git a/pkg/downloader/manager.go b/pkg/downloader/manager.go index 779857405..9c38b3e99 100644 --- a/pkg/downloader/manager.go +++ b/pkg/downloader/manager.go @@ -623,7 +623,7 @@ func tarFromLocalDir(chartpath, name, repo, version string) (string, error) { } if constraint.Check(v) { - _, err = chartutil.Save(ch, destPath) + _, err = chartutil.SaveArchive(ch, destPath) return ch.Metadata.Version, err } diff --git a/pkg/registry/cache.go b/pkg/registry/cache.go index ccedd1e54..a989db7c6 100644 --- a/pkg/registry/cache.go +++ b/pkg/registry/cache.go @@ -113,7 +113,7 @@ func (cache *filesystemCache) ChartToLayers(ch *chart.Chart) ([]ocispec.Descript metaLayer := cache.store.Add(HelmChartMetaFileName, HelmChartMetaMediaType, metaJSONRaw) // Create content layer - // TODO: something better than this hack. Currently needed for chartutil.Save() + // TODO: something better than this hack. Currently needed for chartutil.SaveArchive() // If metadata does not contain Name or Version, an error is returned // such as "no chart name specified (Chart.yaml)" ch.Metadata = &chart.Metadata{ @@ -122,7 +122,7 @@ func (cache *filesystemCache) ChartToLayers(ch *chart.Chart) ([]ocispec.Descript Version: "0.1.0", } destDir := mkdir(filepath.Join(cache.rootDir, "blobs", ".build")) - tmpFile, err := chartutil.Save(ch, destDir) + tmpFile, err := chartutil.SaveArchive(ch, destDir) defer os.Remove(tmpFile) if err != nil { return nil, errors.Wrap(err, "failed to save")