Пример #1
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)
	}
}
Пример #2
0
func createWithChart(chart *chart.Chartfile, chartName, homeDir string) {
	chartDir := helm.WorkspaceChartDirectory(homeDir, 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, Chartfile)); 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)
}
Пример #3
0
// Uninstall removes a chart from Kubernetes.
//
// Manifests are removed from Kubernetes in the order specified by
// chart.UninstallOrder. Any unknown types are removed before that sequence
// is run.
func Uninstall(chartName, home, namespace string, force bool, client kubectl.Runner) {
	// This is a stop-gap until kubectl respects namespaces in manifests.
	if namespace == "" {
		log.Die("This command requires a namespace. Did you mean '-n default'?")
	}
	if !chartFetched(chartName, home) {
		log.Info("No chart named %q in your workspace. Nothing to delete.", chartName)
		return
	}

	cd := helm.WorkspaceChartDirectory(home, chartName)
	c, err := chart.Load(cd)
	if err != nil {
		log.Die("Failed to load chart: %s", err)
	}
	if err := deleteChart(c, namespace, true, client); err != nil {
		log.Die("Failed to list charts: %s", err)
	}
	if !force && !promptConfirm("Uninstall the listed objects?") {
		log.Info("Aborted uninstall")
		return
	}

	CheckKubePrereqs()

	log.Info("Running `kubectl delete` ...")
	if err := deleteChart(c, namespace, false, client); err != nil {
		log.Die("Failed to completely delete chart: %s", err)
	}
	log.Info("Done")
}
Пример #4
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)
	}
}
Пример #5
0
// DeleteRepo deletes a repository.
func DeleteRepo(homedir, name string) {
	cfg := mustConfig(homedir)

	if err := cfg.Repos.Delete(name); err != nil {
		log.Die("Failed to delete repository: %s", err)
	}
	if err := cfg.Save(""); err != nil {
		log.Die("Deleted repo, but could not save settings: %s", err)
	}
}
Пример #6
0
//GenerateTemplate evaluates a template and writes it to an io.Writer
func GenerateTemplate(out io.Writer, in io.Reader, vals interface{}) {
	tpl, err := ioutil.ReadAll(in)
	if err != nil {
		log.Die("Failed to read template file: %s", err)
	}

	if err := renderTemplate(out, string(tpl), vals); err != nil {
		log.Die("Template rendering failed: %s", err)
	}
}
Пример #7
0
// AddRepo adds a repo to the list of repositories.
func AddRepo(homedir, name, repository string) {
	cfg := mustConfig(homedir)

	if err := cfg.Repos.Add(name, repository); err != nil {
		log.Die(err.Error())
	}
	if err := cfg.Save(""); err != nil {
		log.Die("Could not save configuration: %s", err)
	}

	log.Info("Hooray! Successfully added the repo.")
}
Пример #8
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)
	}

	CheckLocalPrereqs(home)

	rc := mustConfig(home).Repos
	if err := rc.UpdateAll(); err != nil {
		log.Die("Not all repos could be updated: %s", err)
	}
	log.Info("Done")
}
Пример #9
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 ordered specified by InstallOrder.
func Install(chartName, home, namespace string, force bool, generate bool, exclude []string, client kubectl.Runner) {
	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 := helm.WorkspaceChartDirectory(home, 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, helm.WorkspaceChartDirectory(home))

	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.")
		}
	}

	// Run the generator if -g is set.
	if generate {
		Generate(chartName, home, exclude, force)
	}

	CheckKubePrereqs()

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

	PrintREADME(chartName, home)
}
Пример #10
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, repo string, force bool) {
	if repo == "" {
		repo = "charts"
	}

	if !mustConfig(homeDir).Repos.Exists(repo) {
		log.Err("Repo %s does not exist", repo)
		log.Info("Available repositories")
		ListRepos(homeDir)
		return
	}

	src := helm.WorkspaceChartDirectory(homeDir, chartName)
	dst := helm.CacheDirectory(homeDir, repo, chartName)

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

	if err := helm.CopyDir(src, dst); err != nil {
		log.Die("failed to publish directory: %v", err)
	}
}
Пример #11
0
// RepoName gets the name of the Git repo, or an empty string if none is found.
func RepoName(chartpath string) string {
	wd, err := os.Getwd()
	if err != nil {
		log.Err("Could not get working directory: %s", err)
		return ""
	}
	defer func() {
		if err := os.Chdir(wd); err != nil {
			log.Die("Unrecoverable error: %s", err)
		}
	}()

	if err := os.Chdir(chartpath); err != nil {
		log.Err("Could not find chartpath %s: %s", chartpath, err)
		return ""
	}

	out, err := exec.Command("git", "config", "--get", "remote.origin.url").CombinedOutput()
	if err != nil {
		log.Err("Git failed to get the origin name: %s %s", err, string(out))
		return ""
	}

	return strings.TrimSpace(string(out))
}
Пример #12
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 := helm.WorkspaceChartDirectory(homedir, lname, Chartfile)
	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, helm.WorkspaceChartDirectory(homedir))
	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", helm.WorkspaceChartDirectory(homedir, lname))
	log.Info("Done")
}
Пример #13
0
// openEditor opens the given filename in an interactive editor
func openEditor(path string) {
	editor := os.Getenv("EDITOR")
	if editor == "" {
		log.Die("must set shell $EDITOR")
	}

	editorPath, err := exec.LookPath(editor)
	if err != nil {
		log.Die("Could not find %s in PATH", editor)
	}

	cmd := exec.Command(editorPath, path)
	cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
	if err := cmd.Run(); err != nil {
		log.Die("Could not open $EDITOR: %s", err)
	}
}
Пример #14
0
// Edit charts using the shell-defined $EDITOR
//
// - chartName being edited
// - homeDir is the Helm Classic home directory for the user
func Edit(chartName, homeDir string) {

	chartDir := util.WorkspaceChartDirectory(homeDir, chartName)

	if _, err := os.Stat(chartDir); os.IsNotExist(err) {
		log.Die("Could not find chart: %s", chartName)
	}

	openEditor(chartDir)
}
Пример #15
0
func execPlugin(name string, args []string) {
	cmd := exec.Command(name, args...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Stdin = os.Stdin

	if err := cmd.Run(); err != nil {
		log.Die(err.Error())
	}
}
Пример #16
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("")
	}
}
Пример #17
0
// Template renders a template to an output file.
func Template(out, in, data string, force bool) error {
	var dest io.Writer
	_, err = os.Stat(out)
	if !(force || os.Getenv("HELM_FORCE_FLAG") == "true") && err == nil {
		return fmt.Errorf("File %s already exists. To overwrite it, please re-run this command with the --force/-f flag.", out)
	}
	if out != "" {
		f, err := os.Create(out)
		if err != nil {
			log.Die("Failed to open %s: %s", out, err)
		}
		defer func() {
			if err := f.Close(); err != nil {
				log.Err("Error closing file: %s", err)
			}
		}()
		dest = f
	} else {
		dest = log.Stdout
	}

	inReader, err := os.Open(in)
	if err != nil {
		log.Die("Failed to open template file: %s", err)
	}

	var vals interface{}
	if data != "" {
		var err error
		vals, err = openValues(data)
		if err != nil {
			log.Die("Error opening value file: %s", err)
		}
	}

	GenerateTemplate(dest, inReader, vals)
	return nil
}
Пример #18
0
// Info prints information about a chart.
//
// - chartName to display
// - homeDir is the helm home directory for the user
// - format is a optional Go template
func Info(chartName, homedir, format string) {
	r := mustConfig(homedir).Repos
	table, chartLocal := r.RepoChart(chartName)
	chartPath := helm.CacheDirectory(homedir, table, chartLocal, Chartfile)

	if format == "" {
		format = defaultInfoFormat
	}

	chart, err := chart.LoadChartfile(chartPath)
	if err != nil {
		log.Die("Could not find chart %s: %s", chartName, err.Error())
	}

	tmpl, err := template.New("info").Parse(format)
	if err != nil {
		log.Die("%s", err)
	}

	if err = tmpl.Execute(log.Stdout, chart); err != nil {
		log.Die("%s", err)
	}
}
Пример #19
0
// 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)
		return
	}

	if !force {
		var connectionFailure bool

		// check if any chart manifests are installed
		installed, err := checkManifests(chartPath)
		if err != nil {
			if strings.Contains(err.Error(), "unable to connect") {
				connectionFailure = true
			} else {
				log.Die(err.Error())
			}
		}

		if connectionFailure {
			log.Err("Could not determine if %s is installed.  To remove the chart --force flag must be set.", chart)
			return
		} else if len(installed) > 0 {
			log.Err("Found %d installed manifests for %s.  To remove a chart that has been installed the --force flag must be set.", len(installed), chart)
			return
		}
	}

	// remove local chart files
	if err := os.RemoveAll(chartPath); err != nil {
		log.Die("Could not remove chart. %s", err)
	}

	log.Info("All clear! You have successfully removed %s from your workspace.", chart)
}
Пример #20
0
// Generate runs generators on the entire chart.
//
// By design, this only operates on workspaces, as it should never be run
// on the cache.
func Generate(chart, homedir string, exclude []string, force bool) {
	if abs, err := filepath.Abs(homedir); err == nil {
		homedir = abs
	}
	chartPath := util.WorkspaceChartDirectory(homedir, chart)

	// Although helmc itself may use the new HELMC_HOME environment variable to optionally define its
	// home directory, to maintain compatibility with charts created for the ORIGINAL helm, we
	// continue to support expansion of these "legacy" environment variables, including HELM_HOME.
	os.Setenv("HELM_HOME", homedir)
	os.Setenv("HELM_DEFAULT_REPO", mustConfig(homedir).Repos.Default)
	os.Setenv("HELM_FORCE_FLAG", strconv.FormatBool(force))

	count, err := generator.Walk(chartPath, exclude, force)
	if err != nil {
		log.Die("Failed to complete generation: %s", err)
	}
	log.Info("Ran %d generators.", count)
}
Пример #21
0
// Search looks for packages with 'term' in their name.
func Search(term, homedir string, regexp bool) {
	cfg := mustConfig(homedir)
	cdir := helm.CacheDirectory(homedir)

	i := search.NewIndex(cfg, cdir)
	res, err := i.Search(term, 5, regexp)
	if err != nil {
		log.Die("Failed to search: %s", err)
	}

	if len(res) == 0 {
		log.Err("No results found. Try using '--regexp'.")
		return
	}

	search.SortScore(res)

	for _, r := range res {
		c, _ := i.Chart(r.Name)
		log.Msg("%s - %s", r.Name, c.Description)
	}
}
Пример #22
0
func sec(c *cli.Context) {
	a := c.Args()

	f, err := fileOrStdout(c)
	if err != nil {
		log.Die("Could not open file for writing: %s", err)
	}
	defer f.Close()

	if len(a) < 1 {
		log.Die("At least one argument (name) is required.")
	}
	name := a[0]
	mdname := c.String("name")
	if mdname == "" {
		mdname = name
	}

	// These take multiple values
	if c.Bool("box") {
		vals, err := boxKeys(c, name)
		if err != nil {
			log.Die("Could not generate keys: %s", err)
		}
		if err := renderSecret(f, c, mdname, vals); err != nil {
			log.Die("Failed to generate keys: %s", err)
		}
		return
	}

	// Default is to handle just one value.
	v, err := resolveValue(a, c)
	if err != nil {
		log.Die("Failed to get the secret: %s", err)
	}

	if err := renderSecret(f, c, mdname, map[string]interface{}{name: v}); err != nil {
		log.Die("Failed to generate secret: %s", err)
	}
}
Пример #23
0
// Cli is the main entrypoint for the Helm Classic CLI.
func Cli() *cli.App {
	app := cli.NewApp()
	app.Name = "helmc"
	app.Usage = globalUsage
	app.Version = version
	app.EnableBashCompletion = true
	app.After = func(c *cli.Context) error {
		if log.ErrorState {
			return errors.New("Exiting with errors")
		}
		return nil
	}

	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:   "home",
			Value:  "$HOME/.helmc",
			Usage:  "The location of your Helm Classic files",
			EnvVar: "HELMC_HOME",
		},
		cli.BoolFlag{
			Name:  "debug",
			Usage: "Enable verbose debugging output",
		},
	}

	app.Commands = []cli.Command{
		createCmd,
		doctorCmd,
		editCmd,
		fetchCmd,
		homeCmd,
		infoCmd,
		installCmd,
		lintCmd,
		listCmd,
		publishCmd,
		removeCmd,
		repositoryCmd,
		searchCmd,
		targetCmd,
		uninstallCmd,
		updateCmd,
		generateCmd,
		tplCmd,
	}

	app.CommandNotFound = func(c *cli.Context, command string) {
		if action.HasPlugin(command) {
			action.Plugin(home(c), command, c.Args())
			return
		}
		log.Err("No matching command '%s'", command)
		cli.ShowAppHelp(c)
		log.Die("")
	}

	app.Before = func(c *cli.Context) error {
		log.IsDebugging = c.Bool("debug")
		return nil
	}

	return app
}
Пример #24
0
	Name:      "template",
	Aliases:   []string{"tpl"},
	Usage:     "Run a template command on a file.",
	ArgsUsage: "[file]",
	Flags: []cli.Flag{
		cli.StringFlag{
			Name:  "out,o",
			Usage: "The destination file. If unset, results are written to STDOUT.",
		},
		cli.StringFlag{
			Name:  "values,d",
			Usage: "A file containing values to substitute into the template. TOML (.toml), JSON (.json), and YAML (.yaml, .yml) are supported.",
		},
		cli.BoolFlag{
			Name:  "force,f",
			Usage: "Forces to overwrite an exiting file",
		},
	},
	Action: func(c *cli.Context) {
		minArgs(c, 1, "template")

		a := c.Args()
		force := c.Bool("force")
		filename := a[0]
		err := action.Template(c.String("out"), filename, c.String("values"), force)
		if err != nil {
			log.Die(err.Error())
		}
	},
}
Пример #25
0
func ensureCommand(command string) {
	if _, err := exec.LookPath(command); err != nil {
		log.Die("Could not find '%s' on $PATH: %s", command, err)
	}
}