mirror of https://github.com/helm/helm
This replicates the previous Save function, which can save an in-memory Chart proto to a tgz.pull/790/head
parent
4bb596d187
commit
15a1cb4816
@ -0,0 +1,133 @@
|
|||||||
|
package chartutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"archive/tar"
|
||||||
|
"compress/gzip"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/ghodss/yaml"
|
||||||
|
"github.com/kubernetes/helm/pkg/proto/hapi/chart"
|
||||||
|
)
|
||||||
|
|
||||||
|
var headerBytes = []byte("+aHR0cHM6Ly95b3V0dS5iZS96OVV6MWljandyTQo=")
|
||||||
|
|
||||||
|
// Save creates an archived chart to the given 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-1.0.0.tgz.
|
||||||
|
//
|
||||||
|
// This returns the absolute path to the chart archive file.
|
||||||
|
func Save(c *chart.Chart, outDir string) (string, error) {
|
||||||
|
// Create archive
|
||||||
|
if fi, err := os.Stat(outDir); err != nil {
|
||||||
|
return "", err
|
||||||
|
} else if !fi.IsDir() {
|
||||||
|
return "", fmt.Errorf("location %s is not a directory", outDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Metadata == nil {
|
||||||
|
return "", errors.New("no Chart.yaml data")
|
||||||
|
}
|
||||||
|
|
||||||
|
cfile := c.Metadata
|
||||||
|
if cfile.Name == "" {
|
||||||
|
return "", errors.New("no chart name specified (Chart.yaml)")
|
||||||
|
} else if cfile.Version == "" {
|
||||||
|
return "", errors.New("no chart version specified (Chart.yaml)")
|
||||||
|
}
|
||||||
|
|
||||||
|
filename := fmt.Sprintf("%s-%s.tgz", cfile.Name, cfile.Version)
|
||||||
|
filename = filepath.Join(outDir, filename)
|
||||||
|
f, err := os.Create(filename)
|
||||||
|
if err != nil {
|
||||||
|
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 {
|
||||||
|
rollback = true
|
||||||
|
}
|
||||||
|
return filename, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeTarContents(out *tar.Writer, c *chart.Chart, prefix string) error {
|
||||||
|
base := filepath.Join(prefix, c.Metadata.Name)
|
||||||
|
|
||||||
|
// Save Chart.yaml
|
||||||
|
cdata, err := yaml.Marshal(c.Metadata)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := writeToTar(out, base+"/Chart.yaml", cdata); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save values.yaml
|
||||||
|
if c.Values != nil && len(c.Values.Raw) > 0 {
|
||||||
|
if err := writeToTar(out, base+"/values.yaml", []byte(c.Values.Raw)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save templates
|
||||||
|
for _, f := range c.Templates {
|
||||||
|
n := filepath.Join(base, f.Name)
|
||||||
|
if err := writeToTar(out, n, f.Data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save files
|
||||||
|
for _, f := range c.Files {
|
||||||
|
n := filepath.Join(base, f.TypeUrl)
|
||||||
|
if err := writeToTar(out, n, f.Value); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save dependencies
|
||||||
|
for _, dep := range c.Dependencies {
|
||||||
|
if err := writeTarContents(out, dep, base+"/charts"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeToTar writes a single file to a tar archive.
|
||||||
|
func writeToTar(out *tar.Writer, name string, body []byte) error {
|
||||||
|
// TODO: Do we need to create dummy parent directory names if none exist?
|
||||||
|
h := &tar.Header{
|
||||||
|
Name: name,
|
||||||
|
Mode: 0755,
|
||||||
|
Size: int64(len(body)),
|
||||||
|
}
|
||||||
|
if err := out.WriteHeader(h); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := out.Write(body); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
package chartutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/kubernetes/helm/pkg/proto/hapi/chart"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSave(t *testing.T) {
|
||||||
|
tmp, err := ioutil.TempDir("", "helm-")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tmp)
|
||||||
|
|
||||||
|
c := &chart.Chart{
|
||||||
|
Metadata: &chart.Metadata{
|
||||||
|
Name: "ahab",
|
||||||
|
Version: "1.2.3.4",
|
||||||
|
},
|
||||||
|
Values: &chart.Config{
|
||||||
|
Raw: "ship: Pequod",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
where, err := Save(c, tmp)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to save: %s", err)
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(where, tmp) {
|
||||||
|
t.Fatalf("Expected %q to start with %q", where, tmp)
|
||||||
|
}
|
||||||
|
if !strings.HasSuffix(where, ".tgz") {
|
||||||
|
t.Fatalf("Expected %q to end with .tgz", where)
|
||||||
|
}
|
||||||
|
|
||||||
|
c2, err := LoadFile(where)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c2.Metadata.Name != c.Metadata.Name {
|
||||||
|
t.Fatalf("Expected chart archive to have %q, got %q", c.Metadata.Name, c2.Metadata.Name)
|
||||||
|
}
|
||||||
|
if c2.Values.Raw != c.Values.Raw {
|
||||||
|
t.Fatal("Values data did not match")
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in new issue