Example #1
0
// Install loads a chart into Kubernetes.
//
// If the chart is not found in the workspace, it is fetched and then installed.
//
// During install, manifests are sent to Kubernetes in the following order:
//
//	- Namespaces
// 	- Secrets
// 	- Volumes
// 	- Services
// 	- Pods
// 	- ReplicationControllers
func Install(chart, home, namespace string, force bool) {
	if !chartInstalled(chart, home) {
		log.Info("No installed chart named %q. Installing now.", chart)
		fetch(chart, chart, home)
	}

	cd := filepath.Join(home, WorkspaceChartPath, chart)
	c, err := model.Load(cd)
	if err != nil {
		log.Die("Failed to load chart: %s", err)
	}

	// Give user the option to bale if dependencies are not satisfied.
	nope, err := dependency.Resolve(c.Chartfile, filepath.Join(home, WorkspaceChartPath))
	if err != nil {
		log.Warn("Failed to check dependencies: %s", err)
		if !force {
			log.Die("Re-run with --force to install anyway.")
		}
	} else if len(nope) > 0 {
		log.Warn("Unsatisfied dependencies:")
		for _, d := range nope {
			log.Msg("\t%s %s", d.Name, d.Version)
		}
		if !force {
			log.Die("Stopping install. Re-run with --force to install anyway.")
		}
	}

	if err := uploadManifests(c, namespace); err != nil {
		log.Die("Failed to upload manifests: %s", err)
	}
	PrintREADME(chart, home)
}
Example #2
0
// Edit charts using the shell-defined $EDITOR
//
// - chartName being edited
// - homeDir is the helm home directory for the user
func Edit(chartName, homeDir string) {

	chartDir := path.Join(homeDir, "workspace", "charts", chartName)

	// enumerate chart files
	files, err := listChart(chartDir)
	if err != nil {
		log.Die("could not list chart: %v", err)
	}

	// join chart with YAML delimeters
	contents, err := joinChart(chartDir, files)
	if err != nil {
		log.Die("could not join chart data: %v", err)
	}

	// write chart to temporary file
	f, err := ioutil.TempFile(os.TempDir(), "helm-edit")
	if err != nil {
		log.Die("could not open tempfile: %v", err)
	}
	defer os.Remove(f.Name())
	f.Write(contents.Bytes())
	f.Close()

	openEditor(f.Name())
	saveChart(chartDir, f.Name())

}
Example #3
0
// ensurePrereqs verifies that Git and Kubectl are both available.
func ensurePrereqs() {
	if _, err := exec.LookPath("git"); err != nil {
		log.Die("Could not find 'git' on $PATH: %s", err)
	}
	if _, err := exec.LookPath("kubectl"); err != nil {
		log.Die("Could not find 'kubectl' on $PATH: %s", err)
	}
}
Example #4
0
// saveChart reads a delimited chart and write out its parts
// to the workspace directory
func saveChart(chartDir string, filename string) error {

	// read the serialized chart file
	contents, err := ioutil.ReadFile(filename)
	if err != nil {
		return err
	}

	chartData := make(map[string][]byte)

	// use a regular expression to read file paths and content
	match := delimeterRegexp.FindAllSubmatch(contents, -1)
	for _, m := range match {
		chartData[string(m[1])] = m[2]
	}

	// save edited chart data to the workspace
	for k, v := range chartData {
		fp := path.Join(chartDir, k)
		if err := ioutil.WriteFile(fp, v, 0644); err != nil {
			log.Die("could not write chart file", err)
		}
	}
	return nil

}
Example #5
0
// Update fetches the remote repo into the home directory.
func Update(repo, home string) {
	home, err := filepath.Abs(home)
	if err != nil {
		log.Die("Could not generate absolute path for %q: %s", home, err)
	}

	// Basically, install if this is the first run.
	ensurePrereqs()
	ensureHome(home)
	gitrepo := filepath.Join(home, CachePath)
	git := ensureRepo(repo, gitrepo)

	if err := gitUpdate(git); err != nil {
		log.Die("Failed to update from Git: %s", err)
	}
}
Example #6
0
// Fetch gets a chart from the source repo and copies to the workdir.
//
// - chart is the source
// - lname is the local name for that chart (chart-name); if blank, it is set to the chart.
// - homedir is the home directory for the user
// - ns is the namespace for this package. If blank, it is set to the DefaultNS.
func Fetch(chart, lname, homedir string) {

	if lname == "" {
		lname = chart
	}

	fetch(chart, lname, homedir)

	cfile, err := model.LoadChartfile(filepath.Join(homedir, WorkspaceChartPath, chart, "Chart.yaml"))
	if err != nil {
		log.Die("Source is not a valid chart. Missing Chart.yaml: %s", err)
	}

	deps, err := dependency.Resolve(cfile, filepath.Join(homedir, WorkspaceChartPath))
	if err != nil {
		log.Warn("Could not check dependencies: %s", err)
		return
	}

	if len(deps) > 0 {
		log.Warn("Unsatisfied dependencies:")
		for _, d := range deps {
			log.Msg("\t%s %s", d.Name, d.Version)
		}
	}

	PrintREADME(lname, homedir)
}
Example #7
0
func Uninstall(chart, home, namespace string) {
	if !chartInstalled(chart, home) {
		log.Info("No installed chart named %q. Nothing to delete.", chart)
		return
	}

	cd := filepath.Join(home, WorkspaceChartPath, chart)
	c, err := model.Load(cd)
	if err != nil {
		log.Die("Failed to load chart: %s", err)
	}

	if err := deleteChart(c, namespace); err != nil {
		log.Die("Failed to completely delete chart: %s", err)
	}
}
Example #8
0
// ensureHome ensures that a HELM_HOME exists.
func ensureHome(home string) {
	if fi, err := os.Stat(home); err != nil {
		log.Info("Creating %s", home)
		for _, p := range helmpaths {
			pp := filepath.Join(home, p)
			if err := os.MkdirAll(pp, 0755); err != nil {
				log.Die("Could not create %q: %s", pp, err)
			}
		}
	} else if !fi.IsDir() {
		log.Die("%s must be a directory.", home)
	}

	if err := os.Chdir(home); err != nil {
		log.Die("Could not change to directory %q: %s", home, err)
	}
}
Example #9
0
// Target displays information about the cluster
func Target() {
	if _, err := exec.LookPath("kubectl"); err != nil {
		log.Die("Could not find 'kubectl' on $PATH: %s", err)
	}

	c, _ := exec.Command("kubectl", "cluster-info").Output()
	fmt.Println(string(c))
}
Example #10
0
// Create a chart
//
// - chartName being created
// - homeDir is the helm home directory for the user
func Create(chartName, homeDir string) {

	skeletonDir, _ := filepath.Abs("skel")
	if fi, err := os.Stat(skeletonDir); err != nil {
		log.Die("Could not find %s: %s", skeletonDir, err)
	} else if !fi.IsDir() {
		log.Die("Malformed skeleton: %s: Must be a directory.", skeletonDir)
	}

	chartDir := path.Join(homeDir, "workspace", "charts", chartName)

	// copy skeleton to chart directory
	if err := copyDir(skeletonDir, chartDir); err != nil {
		log.Die("failed to copy skeleton directory: %v", err)
	}

}
Example #11
0
// ensureRepo ensures that the repo exists and is checked out.
func ensureRepo(repo, home string) *vcs.GitRepo {
	if err := os.Chdir(home); err != nil {
		log.Die("Could not change to directory %q: %s", home, err)
	}
	git, err := vcs.NewGitRepo(repo, home)
	if err != nil {
		log.Die("Could not get repository %q: %s", repo, err)
	}

	if !git.CheckLocal() {
		log.Info("Cloning repo into %q. Please wait.", home)
		if err := git.Get(); err != nil {
			log.Die("Could not create repository in %q: %s", home, err)
		}
	}

	return git
}
Example #12
0
func fetch(chart, lname, homedir string) {
	src := filepath.Join(homedir, CacheChartPath, chart)
	dest := filepath.Join(homedir, WorkspaceChartPath, lname)

	if fi, err := os.Stat(src); err != nil {
	} else if !fi.IsDir() {
		log.Die("Malformed chart %s: Chart must be in a directory.", chart)
	}

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

	log.Info("Fetching %s to %s", src, dest)
	if err := copyDir(src, dest); err != nil {
		log.Die("Failed copying %s to %s", src, dest)
	}
}
Example #13
0
func info(c *cli.Context) {
	a := c.Args()

	if len(a) == 0 {
		log.Die("Info requires at least a Chart name")
	}

	action.Info(c.Args()[0], home(c))
}
Example #14
0
// ensureHome ensures that a HELM_HOME exists.
func ensureHome(home string) {

	must := []string{home, filepath.Join(home, CachePath), filepath.Join(home, WorkspacePath)}

	for _, p := range must {
		if fi, err := os.Stat(p); err != nil {
			log.Info("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)
		}
	}

	if err := os.Chdir(home); err != nil {
		log.Die("Could not change to directory %q: %s", home, err)
	}
}
Example #15
0
// minArgs checks to see if the right number of args are passed.
//
// If not, it prints an error and quits.
func minArgs(c *cli.Context, i int, name string) {
	if len(c.Args()) < i {
		m := "arguments"
		if i == 1 {
			m = "argument"
		}
		log.Err("Expected %d %s", i, m)
		cli.ShowCommandHelp(c, name)
		log.Die("")
	}
}
Example #16
0
func AltInstall(chart, cachedir, home, namespace string, force bool) {
	// Make sure there is a chart in the cachedir.
	if _, err := os.Stat(filepath.Join(cachedir, "Chart.yaml")); err != nil {
		log.Die("Expected a Chart.yaml in %s: %s", cachedir, err)
	}
	// Make sure there is a manifests dir.
	if fi, err := os.Stat(filepath.Join(cachedir, "manifests")); err != nil {
		log.Die("Expected 'manifests/' in %s: %s", cachedir, err)
	} else if !fi.IsDir() {
		log.Die("Expected 'manifests/' to be a directory in %s: %s", cachedir, err)
	}

	// Copy the source chart to the workspace. We ruthlessly overwrite in
	// this case.
	dest := filepath.Join(home, WorkspaceChartPath, chart)
	if err := copyDir(cachedir, dest); err != nil {
		log.Die("Failed to copy %s to %s: %s", cachedir, dest, err)
	}

	// Load the chart.
	c, err := model.Load(dest)
	if err != nil {
		log.Die("Failed to load chart: %s", err)
	}

	// Give user the option to bale if dependencies are not satisfied.
	nope, err := dependency.Resolve(c.Chartfile, filepath.Join(home, WorkspaceChartPath))
	if err != nil {
		log.Warn("Failed to check dependencies: %s", err)
		if !force {
			log.Die("Re-run with --force to install anyway.")
		}
	} else if len(nope) > 0 {
		log.Warn("Unsatisfied dependencies:")
		for _, d := range nope {
			log.Msg("\t%s %s", d.Name, d.Version)
		}
		if !force {
			log.Die("Stopping install. Re-run with --force to install anyway.")
		}
	}

	if err := uploadManifests(c, namespace); err != nil {
		log.Die("Failed to upload manifests: %s", err)
	}
}
Example #17
0
// openEditor opens the given filename in an interactive editor
func openEditor(filename string) {
	var cmd *exec.Cmd

	editor := os.ExpandEnv("$EDITOR")
	if editor == "" {
		log.Die("must set shell $EDITOR")
	}

	args := []string{filename}
	cmd = exec.Command(editor, args...)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Run()
}
Example #18
0
// Search looks for packages with 'term' in their name.
func Search(term, homedir string) {
	charts, err := search(term, homedir)
	if err != nil {
		log.Die(err.Error())
	}

	log.Info("\n=================")
	log.Info("Available Charts")
	log.Info("=================\n")

	log.Info("")

	for dir, chart := range charts {
		log.Info("\t%s (%s %s) - %s", filepath.Base(dir), chart.Name, chart.Version, chart.Description)
	}
}
Example #19
0
// openEditor opens the given filename in an interactive editor
func openEditor(filename string) {
	var cmd *exec.Cmd

	editor := os.ExpandEnv("$EDITOR")
	if editor == "" {
		log.Die("must set shell $EDITOR")
	}

	cmd = exec.Command(editor, filename)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	// TODO: figure out why editor always exits non-zero
	cmd.Run()
}
Example #20
0
func Info(chart, homedir string) {
	chartPath := filepath.Join(homedir, CacheChartPath, chart, "Chart.yaml")

	log.Info("%s", chartPath)

	chartModel, err := model.LoadChartfile(chartPath)
	if err != nil {
		log.Die("%s - UNKNOWN", chart)
	}

	log.Info("Chart: %s", chartModel.Name)
	log.Info("Description: %s", chartModel.Description)
	log.Info("Details: %s", chartModel.Details)
	log.Info("Version: %s", chartModel.Version)
	log.Info("Website: %s", chartModel.Home)
	log.Info("From: %s", chartPath)
	log.Info("Dependencies: %s", chartModel.Dependencies)
}
Example #21
0
func fetch(c *cli.Context) {
	home := home(c)

	a := c.Args()

	if len(a) == 0 {
		log.Die("Fetch requires at least a Chart name")
	}

	chart := a[0]

	var lname string
	if len(a) == 2 {
		lname = a[1]
	}

	action.Fetch(chart, lname, home)
}
Example #22
0
func Install(chart, home, namespace string) {
	Fetch(chart, chart, home)
	log.Info("kubectl --namespace=%q create -f %s.yaml", namespace, chart)
	if !chartInstalled(chart, home) {
		log.Info("No installed chart named %q. Installing now.", chart)
		Fetch(chart, chart, home)
	}

	d := filepath.Join(home, WorkspaceChartPath, chart, "manifests")
	log.Debug("Looking for manifests in %q", d)
	files, err := manifestFiles(d)
	if err != nil {
		log.Die("No manifests to install: %s", err)
	}

	for _, f := range files {
		if err := kubectlCreate(f, namespace); err != nil {
			log.Warn("Failed to install manifest %q: %s", f, err)
		}
	}
}
Example #23
0
// Search looks for packages with 'term' in their name.
func Search(term, homedir string) {

	dirs, err := search(term, homedir)
	if err != nil {
		log.Die(err.Error())
	}

	log.Info("\n=================")
	log.Info("Available Charts")
	log.Info("=================\n")

	for _, d := range dirs {
		y, err := model.LoadChartfile(filepath.Join(d, "Chart.yaml"))
		if err != nil {
			log.Info("\t%s - UNKNOWN", filepath.Base(d))
			continue
		}
		log.Info("\t%s (%s %s) - %s", filepath.Base(d), y.Name, y.Version, y.Description)
	}

	log.Info("")
}
Example #24
0
// Search looks for packages with 'term' in their name.
func Search(term, homedir string) {
	term = sanitizeTerm(term)
	sp := filepath.Join(homedir, CacheChartPath, "*"+term+"*")
	dirs, err := filepath.Glob(sp)
	if err != nil {
		log.Die("No results found. %s", err)
	}

	log.Info("\n=================")
	log.Info("Available Charts")
	log.Info("=================\n")

	for _, d := range dirs {
		y, err := model.Load(filepath.Join(d, "Chart.yaml"))
		if err != nil {
			log.Info("\t%s - UNKNOWN", filepath.Base(d))
			continue
		}
		log.Info("\t%s (%s %s) - %s", filepath.Base(d), y.Name, y.Version, y.Description)
	}

	log.Info("")
}