func loadTar(r *tar.Reader) (*tarChart, error) { td, err := ioutil.TempDir("", "chart-") if err != nil { return nil, err } // ioutil.TempDir uses Getenv("TMPDIR"), so there are no guarantees dir, err := filepath.Abs(td) if err != nil { return nil, fmt.Errorf("%s is not a valid path", td) } c := &tarChart{ chartyaml: &Chartfile{}, tmpDir: dir, } firstDir := "" hdr, err := r.Next() for err == nil { log.Debug("Reading %s", hdr.Name) // This is to prevent malformed tar attacks. hdr.Name = filepath.Clean(hdr.Name) if firstDir == "" { fi := hdr.FileInfo() if fi.IsDir() { log.Debug("Discovered app named %s", hdr.Name) firstDir = hdr.Name } else { log.Warn("Unexpected file at root of archive: %s", hdr.Name) } } else if strings.HasPrefix(hdr.Name, firstDir) { log.Debug("Extracting %s to %s", hdr.Name, c.tmpDir) // We know this has the prefix, so we know there won't be an error. rel, _ := filepath.Rel(firstDir, hdr.Name) // If tar record is a directory, create one in the tmpdir and return. if hdr.FileInfo().IsDir() { os.MkdirAll(filepath.Join(c.tmpDir, rel), 0755) hdr, err = r.Next() continue } dest := filepath.Join(c.tmpDir, rel) f, err := os.Create(filepath.Join(c.tmpDir, rel)) if err != nil { log.Warn("Could not create %s: %s", dest, err) hdr, err = r.Next() continue } if _, err := io.Copy(f, r); err != nil { log.Warn("Failed to copy %s: %s", dest, err) } f.Close() } else { log.Warn("Unexpected file outside of chart: %s", hdr.Name) } hdr, err = r.Next() } if err != nil && err != io.EOF { log.Warn("Unexpected error reading tar: %s", err) c.close() return c, err } log.Info("Reached end of Tar file") return c, nil }
// 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, 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) } cfile := c.Chartfile() dir := c.Dir() pdir := filepath.Dir(dir) filename := fmt.Sprintf("%s-%s.tgz", fname(cfile.Name), cfile.Version) filename = filepath.Join(outDir, filename) // Fail early if the YAML is borked. if err := cfile.Save(filepath.Join(dir, ChartfileName)); err != nil { return "", err } // Create file. 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 { log.Warn("Removing incomplete archive %s", filename) os.Remove(filename) } }() err = filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error { if err != nil { return err } hdr, err := tar.FileInfoHeader(fi, ".") if err != nil { return err } relpath, err := filepath.Rel(pdir, path) if err != nil { return err } hdr.Name = relpath twriter.WriteHeader(hdr) // Skip directories. if fi.IsDir() { return nil } in, err := os.Open(path) if err != nil { return err } _, err = io.Copy(twriter, in) in.Close() if err != nil { return err } return nil }) if err != nil { rollback = true return filename, err } return filename, nil }