Beispiel #1
0
// Check by chart directory name whether a chart is fetched into the workspace.
//
// This does NOT check the Chart.yaml file.
func chartFetched(chartName, home string) bool {
	p := helm.WorkspaceChartDirectory(home, chartName, Chartfile)
	log.Debug("Looking for %q", p)
	if fi, err := os.Stat(p); err != nil || fi.IsDir() {
		log.Debug("No chart: %s", err)
		return false
	}
	return true
}
Beispiel #2
0
func fetch(chartName, lname, homedir, chartpath string) {
	src := helm.CacheDirectory(homedir, chartpath, chartName)
	dest := helm.WorkspaceChartDirectory(homedir, lname)

	fi, err := os.Stat(src)
	if err != nil {
		log.Warn("Oops. Looks like there was an issue finding the chart, %s, in %s. Running `helmc update` to ensure you have the latest version of all Charts from Github...", lname, src)
		Update(homedir)
		fi, err = os.Stat(src)
		if err != nil {
			log.Die("Chart %s not found in %s", lname, src)
		}
		log.Info("Good news! Looks like that did the trick. Onwards and upwards!")
	}

	if !fi.IsDir() {
		log.Die("Malformed chart %s: Chart must be in a directory.", chartName)
	}

	if err := os.MkdirAll(dest, 0755); err != nil {
		log.Die("Could not create %q: %s", dest, err)
	}

	log.Debug("Fetching %s to %s", src, dest)
	if err := helm.CopyDir(src, dest); err != nil {
		log.Die("Failed copying %s to %s", src, dest)
	}

	if err := updateChartfile(src, dest, lname); err != nil {
		log.Die("Failed to update Chart.yaml: %s", err)
	}
}
Beispiel #3
0
// EnsureHome ensures that a HELMC_HOME exists.
func EnsureHome(home string) {

	must := []string{home, CacheDirectory(home), filepath.Join(home, workspacePath)}

	for _, p := range must {
		if fi, err := os.Stat(p); err != nil {
			log.Debug("Creating %s", p)
			if err := os.MkdirAll(p, 0755); err != nil {
				log.Die("Could not create %q: %s", p, err)
			}
		} else if !fi.IsDir() {
			log.Die("%s must be a directory.", home)
		}
	}

	refi := filepath.Join(home, Configfile)
	if _, err := os.Stat(refi); err != nil {
		log.Info("Creating %s", refi)
		// Attempt to create a Repos.yaml
		if err := ioutil.WriteFile(refi, []byte(DefaultConfigfile), 0755); err != nil {
			log.Die("Could not create %s: %s", refi, err)
		}
	}

	if err := os.Chdir(home); err != nil {
		log.Die("Could not change to directory %q: %s", home, err)
	}
}
Beispiel #4
0
func (r *Repos) deleteRepo(name string) error {
	rpath := filepath.Join(r.Dir, name)
	if fi, err := os.Stat(rpath); err != nil || !fi.IsDir() {
		log.Info("Deleted nothing. No repo named %s", name)
		return nil
	}

	log.Debug("Deleting %s", rpath)
	return os.RemoveAll(rpath)
}
Beispiel #5
0
// renderTemplate renders a template and values into an output stream.
//
// tpl should be a string template.
func renderTemplate(out io.Writer, tpl string, vals interface{}) error {
	t, err := template.New("helmTpl").Funcs(sprig.TxtFuncMap()).Parse(tpl)
	if err != nil {
		return err
	}

	log.Debug("Vals: %#v", vals)

	if err = t.ExecuteTemplate(out, "helmTpl", vals); err != nil {
		return err
	}
	return nil
}
Beispiel #6
0
func repoChartDiff(rpath, initialVersion string) (string, error) {
	// build git diff-tree command
	cmd := exec.Command("git", "-C", rpath, "diff-tree", "--name-status", fmt.Sprintf("%s..HEAD", initialVersion))

	log.Debug("git diff cmd: %s", cmd.Args)

	out, err := cmd.CombinedOutput()
	if err != nil {
		return "", err
	}

	// cleanup any trailing whitespace
	return strings.TrimSpace(string(out)), nil
}
Beispiel #7
0
// uploadManifests sends manifests to Kubectl in a particular order.
func uploadManifests(c *chart.Chart, namespace string, client kubectl.Runner) error {

	// Install known kinds in a predictable order.
	for _, k := range InstallOrder {
		for _, m := range c.Kind[k] {
			o := m.VersionedObject
			o.AddAnnotations(map[string]string{
				chart.AnnFile:         m.Source,
				chart.AnnChartVersion: c.Chartfile.Version,
				chart.AnnChartDesc:    c.Chartfile.Description,
				chart.AnnChartName:    c.Chartfile.Name,
			})
			var data []byte
			var err error
			if data, err = o.JSON(); err != nil {
				return err
			}

			var action = client.Create
			// If it's a keeper manifest, do "kubectl apply" instead of "create."
			if manifest.IsKeeper(data) {
				action = client.Apply
			}
			log.Debug("File: %s", string(data))
			out, err := action(data, namespace)
			log.Msg(string(out))
			if err != nil {
				return err
			}
		}
	}

	// Install unknown kinds afterward. Order here is not predictable.
	for _, k := range c.UnknownKinds(InstallOrder) {
		for _, o := range c.Kind[k] {
			o.VersionedObject.AddAnnotations(map[string]string{chart.AnnFile: o.Source})
			data, err := o.VersionedObject.JSON()
			if err != nil {
				return err
			}
			out, err := client.Create(data, namespace)
			log.Msg(string(out))
			if err != nil {
				return err
			}
		}
	}

	return nil
}
Beispiel #8
0
// ParseDir parses all of the manifests inside of a chart directory.
//
// The directory should be the Chart directory (contains Chart.yaml and manifests/)
//
// This will return an error if the directory does not exist, or if there is an
// error parsing or decoding any yaml files.
func ParseDir(chartDir string) ([]*Manifest, error) {
	dir := filepath.Join(chartDir, "manifests")
	files := []*Manifest{}

	if _, err := os.Stat(dir); err != nil {
		return files, err
	}

	// add manifest files
	walker := func(fname string, fi os.FileInfo, e error) error {
		log.Debug("Parsing %s", fname)
		// Chauncey was right.
		if e != nil {
			return e
		}

		if fi.IsDir() {
			return nil
		}

		if filepath.Ext(fname) != ".yaml" && filepath.Ext(fname) != ".yml" {
			log.Debug("Skipping %s. Not a YAML file.", fname)
			return nil
		}

		m, err := Parse(fname)
		if err != nil {
			return err
		}

		files = append(files, m...)

		return nil
	}

	return files, filepath.Walk(dir, walker)
}
Beispiel #9
0
// PrintREADME prints the README file (if it exists) to the console.
func PrintREADME(chart, home string) {
	p := helm.WorkspaceChartDirectory(home, chart, "README.*")
	files, err := filepath.Glob(p)
	if err != nil || len(files) == 0 {
		// No README. Skip.
		log.Debug("No readme in %s", p)
		return
	}

	f, err := os.Open(files[0])
	if err != nil {
		log.Warn("Could not read README: %s", err)
		return
	}
	log.Msg(strings.Repeat("=", 40))
	io.Copy(log.Stdout, f)
	log.Msg(strings.Repeat("=", 40))
	f.Close()

}
Beispiel #10
0
// checkManifests gets any installed manifests within a chart
func checkManifests(chartPath string) ([]string, error) {
	var foundManifests []string

	manifests, err := manifest.Files(chartPath)
	if err != nil {
		return nil, err
	}

	for _, m := range manifests {
		out := kubeGet(m)

		if strings.Contains(out, "unable to connect") {
			return nil, fmt.Errorf(out)
		}
		if !strings.Contains(out, "not found") {
			foundManifests = append(foundManifests, m)
		}
	}

	log.Debug("Found %d installed manifests", len(foundManifests))

	return foundManifests, nil
}
Beispiel #11
0
// Resolve takes a chart and a location and checks whether the chart's dependencies are satisfied.
//
// The `installdir` is the location where installed charts are located. Typically
// this is in $HELMC_HOME/workspace/charts.
//
// This returns a list of unsatisfied dependencies (NOT an error condition).
//
// It returns an error only if it cannot perform the task of resolving dependencies.
// Failed dependencies to not constitute an error.
func Resolve(cf *chart.Chartfile, installdir string) ([]*chart.Dependency, error) {
	if len(cf.Dependencies) == 0 {
		log.Debug("No dependencies to check. :achievement-unlocked:")
		return []*chart.Dependency{}, nil
	}

	cache, err := dependencyCache(installdir)
	if err != nil {
		log.Debug("Failed to build dependency cache: %s", err)
		return []*chart.Dependency{}, err
	}

	res := []*chart.Dependency{}

	// TODO: This could be made more efficient.
	for _, check := range cf.Dependencies {
		resolved := false
		for n, chart := range cache {
			log.Debug("Checking if %s (%s) %s meets %s %s", chart.Name, n, chart.Version, check.Name, check.Version)
			if chart.From != nil {
				log.Debug("✔︎")
				if satisfies(chart.From, check) {
					resolved = true
					break
				}
			} else {
				log.Info("Chart %s is pre-0.2.0. Legacy mode enabled.", chart.Name)
				if chart.Name == check.Name && check.VersionOK(chart.Version) {
					log.Debug("✔︎")
					resolved = true
					break
				}
			}
		}
		if !resolved {
			log.Debug("No matches found for %s %s", check.Name, check.Version)
			res = append(res, check)
		}
	}
	return res, nil
}
Beispiel #12
0
// Walk walks a chart directory and executes generators as it finds them.
//
// Returns the number of generators executed.
//
// Walking will error out whenever a generator cannot be completely executed.
// This includes cases such as not finding the generator referenced, and
// cases where the generator itself exits with a non-zero exit code.
func Walk(dir string, exclude []string, force bool) (int, error) {

	excludes := make(map[string]bool, len(exclude))
	for i := 0; i < len(exclude); i++ {
		excludes[filepath.Join(dir, exclude[i])] = true
	}

	count := 0
	err := filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error {

		// dive-bomb if we hit an error.
		if err != nil {
			return err
		}

		// Exclude anything explicitly excluded.
		if excludes[path] == true {
			if fi.IsDir() {
				return filepath.SkipDir
			}
			return nil
		}

		// Skip directory entries. If the directory prefix is . or _, skip the
		// contents of the directory as well.
		if fi.IsDir() {
			return skip(path)
		}

		f, err := os.Open(path)
		if err != nil {
			return err
		}
		defer f.Close()

		line, err := readGenerator(f)
		if err != nil {
			return err
		}
		if line == "" {
			return nil
		}
		// Run the generator.
		os.Setenv("HELM_GENERATE_COMMAND", line)
		os.Setenv("HELM_GENERATE_FILE", path)
		os.Setenv("HELM_GENERATE_DIR", dir)
		line = os.ExpandEnv(line)
		os.Setenv("HELM_GENERATE_COMMAND_EXPANDED", line)
		log.Debug("File: %s, Command: %s", path, line)
		count++

		// Execute the command in the file's directory to make relative
		// paths usable.
		origin, err := os.Getwd()
		if err != nil {
			log.Warn("Could not get PWD: %s", err)
		} else if err := os.Chdir(dir); err != nil {
			log.Warn("Could not change directory to %s: %s", dir, err)
		} else {
			origin = dir
			defer func() {
				if e := os.Chdir(origin); e != nil {
					log.Warn("Could not return to %s: %s", origin, e)
				}
			}()
		}
		err = execute(line, force)
		if err != nil {
			return fmt.Errorf("failed to execute %s (%s): %s", line, path, err)
		}
		return nil
	})

	return count, err
}
Beispiel #13
0
import (
	"fmt"
	"os"
	"os/exec"
	"strings"

	"github.com/helm/helm-classic/log"
	"github.com/helm/helm-classic/manifest"
	helm "github.com/helm/helm-classic/util"
)

// kubeGetter wraps the kubectl command, override in tests
type kubeGetter func(string) string

var kubeGet kubeGetter = func(m string) string {
	log.Debug("Getting manifests from %s", m)

	a := []string{"get", "-f", m}
	out, _ := exec.Command("kubectl", a...).CombinedOutput()
	return string(out)
}

// Remove removes a chart from the workdir.
//
// - chart is the source
// - homedir is the home directory for the user
// - force will remove installed charts from workspace
func Remove(chart, homedir string, force bool) {
	chartPath := helm.WorkspaceChartDirectory(homedir, chart)
	if _, err := os.Stat(chartPath); err != nil {
		log.Err("Chart not found. %s", err)