ref(chartutil): introduce chartutil.Save

chartutil.Save has been renamed to chartutil.SaveArchive.

chartutil.Save accepts a chart and an io.Writer, wrapping the logic around serializing a chart to an io.Writer.

This allows other packages to write higher-level abstractions above chart.Save. For example, streaming a chart to a *bytes.Buffer.

Signed-off-by: Matthew Fisher <matt.fisher@microsoft.com>
pull/5873/head
Matthew Fisher 6 years ago
parent f109b1ba5c
commit 16ee16be8e
No known key found for this signature in database
GPG Key ID: 92AA783CBAAE8E3B

@ -94,7 +94,7 @@ func (p *Package) Run(path string) (string, error) {
dest = p.Destination dest = p.Destination
} }
name, err := chartutil.Save(ch, dest) name, err := chartutil.SaveArchive(ch, dest)
if err != nil { if err != nil {
return "", errors.Wrap(err, "failed to save") return "", errors.Wrap(err, "failed to save")
} }

@ -20,6 +20,7 @@ import (
"archive/tar" "archive/tar"
"compress/gzip" "compress/gzip"
"fmt" "fmt"
"io"
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
@ -33,6 +34,11 @@ import (
var headerBytes = []byte("+aHR0cHM6Ly95b3V0dS5iZS96OVV6MWljandyTQo=") var headerBytes = []byte("+aHR0cHM6Ly95b3V0dS5iZS96OVV6MWljandyTQo=")
// SaveDir saves a chart as files in a directory. // 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 { func SaveDir(c *chart.Chart, dest string) error {
// Create the chart directory // Create the chart directory
outdir := filepath.Join(dest, c.Name()) outdir := filepath.Join(dest, c.Name())
@ -89,14 +95,28 @@ func SaveDir(c *chart.Chart, dest string) error {
base := filepath.Join(outdir, ChartsDir) base := filepath.Join(outdir, ChartsDir)
for _, dep := range c.Dependencies() { for _, dep := range c.Dependencies() {
// Here, we write each dependency as a tar file. // 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 err
} }
} }
return nil 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. // 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. // will generate /foo/bar-1.0.0.tgz.
// //
// This returns the absolute path to the chart archive file. // 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 // Create archive
if fi, err := os.Stat(outDir); err != nil { if fi, err := os.Stat(outDir); err != nil {
return "", err return "", err
@ -131,24 +151,15 @@ func Save(c *chart.Chart, outDir string) (string, error) {
return "", err 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 rollback := false
defer func() { defer func() {
twriter.Close()
zipper.Close()
f.Close() f.Close()
if rollback { if rollback {
os.Remove(filename) os.Remove(filename)
} }
}() }()
if err := writeTarContents(twriter, c, ""); err != nil { if err := Save(c, f); err != nil {
rollback = true rollback = true
} }
return filename, err return filename, err

@ -17,6 +17,7 @@ limitations under the License.
package chartutil package chartutil
import ( import (
"bytes"
"io/ioutil" "io/ioutil"
"os" "os"
"strings" "strings"
@ -27,6 +28,36 @@ import (
) )
func TestSave(t *testing.T) { 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-") tmp, err := ioutil.TempDir("", "helm-")
if err != nil { if err != nil {
t.Fatal(err) 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 { if err != nil {
t.Fatalf("Failed to save: %s", err) t.Fatalf("Failed to save: %s", err)
} }

@ -623,7 +623,7 @@ func tarFromLocalDir(chartpath, name, repo, version string) (string, error) {
} }
if constraint.Check(v) { if constraint.Check(v) {
_, err = chartutil.Save(ch, destPath) _, err = chartutil.SaveArchive(ch, destPath)
return ch.Metadata.Version, err return ch.Metadata.Version, err
} }

@ -113,7 +113,7 @@ func (cache *filesystemCache) ChartToLayers(ch *chart.Chart) ([]ocispec.Descript
metaLayer := cache.store.Add(HelmChartMetaFileName, HelmChartMetaMediaType, metaJSONRaw) metaLayer := cache.store.Add(HelmChartMetaFileName, HelmChartMetaMediaType, metaJSONRaw)
// Create content layer // 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 // If metadata does not contain Name or Version, an error is returned
// such as "no chart name specified (Chart.yaml)" // such as "no chart name specified (Chart.yaml)"
ch.Metadata = &chart.Metadata{ ch.Metadata = &chart.Metadata{
@ -122,7 +122,7 @@ func (cache *filesystemCache) ChartToLayers(ch *chart.Chart) ([]ocispec.Descript
Version: "0.1.0", Version: "0.1.0",
} }
destDir := mkdir(filepath.Join(cache.rootDir, "blobs", ".build")) destDir := mkdir(filepath.Join(cache.rootDir, "blobs", ".build"))
tmpFile, err := chartutil.Save(ch, destDir) tmpFile, err := chartutil.SaveArchive(ch, destDir)
defer os.Remove(tmpFile) defer os.Remove(tmpFile)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to save") return nil, errors.Wrap(err, "failed to save")

Loading…
Cancel
Save