Example #1
0
File: chart.go Project: jackgr/helm
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
}
Example #2
0
File: save.go Project: jackgr/helm
// 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
}