package chartutil import ( "archive/tar" "compress/gzip" "errors" "fmt" "os" "path/filepath" "github.com/ghodss/yaml" "k8s.io/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 }