Esempio n. 1
0
// Fetch gets a chart from the source repo and copies to the workdir.
//
// - chartName 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
func Fetch(chartName, lname, homedir string) {

	r := mustConfig(homedir).Repos
	repository, chartName := r.RepoChart(chartName)

	if lname == "" {
		lname = chartName
	}

	fetch(chartName, lname, homedir, repository)

	chartFilePath := filepath.Join(homedir, WorkspaceChartPath, lname, "Chart.yaml")
	cfile, err := chart.LoadChartfile(chartFilePath)
	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)
		}
	}

	log.Info("Fetched chart into workspace %s", filepath.Join(homedir, WorkspaceChartPath, lname))
	log.Info("Done")
}
Esempio n. 2
0
// List lists all of the local charts.
func List(homedir string) {
	md := filepath.Join(homedir, WorkspaceChartPath, "*")
	charts, err := filepath.Glob(md)
	if err != nil {
		log.Warn("Could not find any charts in %q: %s", md, err)
	}
	for _, c := range charts {
		cname := filepath.Base(c)
		if ch, err := chart.LoadChartfile(filepath.Join(c, "Chart.yaml")); err == nil {
			log.Info("\t%s (%s %s) - %s", cname, ch.Name, ch.Version, ch.Description)
			continue
		}
		log.Info("\t%s (unknown)", cname)
	}
}
Esempio n. 3
0
// CheckLatest checks whether this version of Helm is the latest version.
//
// This does not ensure that this is the latest. If a newer version is found,
// this generates a message indicating that.
//
// The passed-in version is the base version that will be checked against the
// remote release list.
func CheckLatest(version string) {
	ver, err := release.LatestVersion()
	if err != nil {
		log.Warn("Skipped Helm version check: %s", err)
		return
	}

	current, err := semver.NewVersion(version)
	if err != nil {
		log.Warn("Local version %s is not well-formed", version)
		return
	}
	remote, err := semver.NewVersion(ver)
	if err != nil {
		log.Warn("Remote version %s is not well-formed", ver)
		return
	}

	if remote.GreaterThan(current) {
		log.Warn("A new version of Helm is available. You have %s. The latest is %v", version, ver)
		if dl, err := release.LatestDownloadURL(); err == nil {
			log.Info("Download version %s here: %s", ver, dl)
		}
	}

}
Esempio n. 4
0
// ensureHome ensures that a HELM_HOME exists.
func ensureHome(home string) {

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

	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(config.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)
	}
}
Esempio n. 5
0
// Create a chart
//
// - chartName being created
// - homeDir is the helm home directory for the user
func Create(chartName, homeDir string) {

	chart := newSkelChartfile(chartName)
	chartDir := filepath.Join(homeDir, WorkspaceChartPath, chartName)

	// create directories
	if err := os.MkdirAll(filepath.Join(chartDir, "manifests"), 0755); err != nil {
		log.Die("Could not create %q: %s", chartDir, err)
	}

	// create Chartfile.yaml
	if err := chart.Save(filepath.Join(chartDir, "Chart.yaml")); err != nil {
		log.Die("Could not create Chart.yaml: err", err)
	}

	// create README.md
	if err := createReadme(chartDir, chart); err != nil {
		log.Die("Could not create README.md: err", err)
	}

	// create example-pod
	if err := createExampleManifest(chartDir); err != nil {
		log.Die("Could not create example manifest: err", err)
	}

	log.Info("Created chart in %s", chartDir)
}
Esempio n. 6
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(chartName, home, namespace string, force bool, dryRun bool) {

	ochart := chartName
	r := mustConfig(home).Repos
	table, chartName := r.RepoChart(chartName)

	if !chartFetched(chartName, home) {
		log.Info("No chart named %q in your workspace. Fetching now.", ochart)
		fetch(chartName, chartName, home, table)
	}

	cd := filepath.Join(home, WorkspaceChartPath, chartName)
	c, err := chart.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.")
		}
	}

	msg := "Running `kubectl create -f` ..."
	if dryRun {
		msg = "Performing a dry run of `kubectl create -f` ..."
	}
	log.Info(msg)
	if err := uploadManifests(c, namespace, dryRun); err != nil {
		log.Die("Failed to upload manifests: %s", err)
	}
	log.Info("Done")

	PrintREADME(chartName, home)
}
Esempio n. 7
0
// Uninstall removes a chart from Kubernetes.
//
// Manifests are removed from Kubernetes in the following order:
//
// 	- Services (to shut down traffic)
// 	- Pods (which can be part of RCs)
// 	- ReplicationControllers
// 	- Volumes
// 	- Secrets
//	- Namespaces
func Uninstall(chartName, home, namespace string) {
	if !chartFetched(chartName, home) {
		log.Info("No chart named %q in your workspace. Nothing to delete.", chartName)
		return
	}

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

	log.Info("Running `kubectl delete` ...")
	if err := deleteChart(c, namespace); err != nil {
		log.Die("Failed to completely delete chart: %s", err)
	}
	log.Info("Done")
}
Esempio n. 8
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)
}
Esempio n. 9
0
// Remove removes a chart from the workdir.
//
// - chart is the source
// - homedir is the home directory for the user
func Remove(chart string, homedir string) {
	chartPath := filepath.Join(homedir, WorkspaceChartPath, chart)
	if _, err := os.Stat(chartPath); err != nil {
		log.Die("Chart not found. %s", err)
	}

	if err := os.RemoveAll(chartPath); err != nil {
		log.Die("%s", err)
	}

	log.Info("All clear! You have successfully removed %s from your workspace.", chart)
}
Esempio n. 10
0
// AltInstall allows loading a chart from the current directory.
//
// It does not directly support chart tables (repos).
func AltInstall(chartName, cachedir, home, namespace string, force bool, dryRun 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, chartName)
	if err := copyDir(cachedir, dest); err != nil {
		log.Die("Failed to copy %s to %s: %s", cachedir, dest, err)
	}

	// Load the chart.
	c, err := chart.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.")
		}
	}

	msg := "Running `kubectl create -f` ..."
	if dryRun {
		msg = "Performing a dry run of `kubectl create -f` ..."
	}
	log.Info(msg)
	if err := uploadManifests(c, namespace, dryRun); err != nil {
		log.Die("Failed to upload manifests: %s", err)
	}
}
Esempio n. 11
0
// UpdateAll does a git fast-forward pull from each remote repo.
func (r *Repos) UpdateAll() error {
	for _, table := range r.Tables {
		log.Info("Updating %s", table.Name)
		rpath := filepath.Join(r.Dir, table.Name)
		g, err := ensureRepo(table.Repo, rpath)
		if err != nil {
			return err
		}
		if err := g.Update(); err != nil {
			return err
		}
	}
	return nil
}
Esempio n. 12
0
// Update fetches the remote repo into the home directory.
func Update(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)

	rc := mustConfig(home).Repos
	if err := rc.UpdateAll(); err != nil {
		log.Die("Not all repos could be updated: %s", err)
	}
	log.Info("Done")
}
Esempio n. 13
0
// Publish a chart from the workspace to the cache directory
//
// - chartName being published
// - homeDir is the helm home directory for the user
// - force publishing even if the chart directory already exists
func Publish(chartName, homeDir string, force bool) {

	src := path.Join(homeDir, WorkspaceChartPath, chartName)
	dst := path.Join(homeDir, CacheChartPath, chartName)

	if _, err := os.Stat(dst); err == nil {
		if force != true {
			log.Info("chart already exists, use -f to force")
			return
		}
	}

	if err := copyDir(src, dst); err != nil {
		log.Die("failed to publish directory: %v", err)
	}
}
Esempio n. 14
0
// kubectlCreate calls `kubectl create` and sends the data via Stdin.
//
// If dryRun is set to true, then we just output the command that was
// going to be run to os.Stdout and return nil.
func kubectlCreate(data []byte, ns string, dryRun bool) error {
	a := []string{"create", "-f", "-"}

	if ns != "" {
		a = append([]string{"--namespace=" + ns}, a...)
	}

	if dryRun {
		cmd := "kubectl"
		for _, arg := range a {
			cmd = fmt.Sprintf("%s %s", cmd, arg)
		}
		cmd = fmt.Sprintf("%s < %s", cmd, data)
		log.Info(cmd)
		return nil
	}

	c := exec.Command("kubectl", a...)
	in, err := c.StdinPipe()
	if err != nil {
		return err
	}

	c.Stdout = os.Stdout
	c.Stderr = os.Stderr

	if err := c.Start(); err != nil {
		return err
	}

	log.Debug("File: %s", string(data))
	in.Write(data)
	in.Close()

	return c.Wait()
}