Beispiel #1
0
// promptConfirm prompts a user to confirm (or deny) something.
//
// True is returned iff the prompt is confirmed.
// Errors are reported to the log, and return false.
//
// Valid confirmations:
// 	y, yes, true, t, aye-aye
//
// Valid denials:
//	n, no, f, false
//
// Any other prompt response will return false, and issue a warning to the
// user.
func promptConfirm(msg string) bool {
	oldState, err := terminal.MakeRaw(0)
	if err != nil {
		log.Err("Could not get terminal: %s", err)
		return false
	}
	defer terminal.Restore(0, oldState)

	f := readerWriter(log.Stdin, log.Stdout)
	t := terminal.NewTerminal(f, msg+" (y/N) ")
	res, err := t.ReadLine()
	if err != nil {
		log.Err("Could not read line: %s", err)
		return false
	}
	res = strings.ToLower(res)
	switch res {
	case "yes", "y", "true", "t", "aye-aye":
		return true
	case "no", "n", "false", "f":
		return false
	}
	log.Warn("Did not understand answer %q, assuming No", res)
	return false
}
Beispiel #2
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))
}
Beispiel #3
0
// Target displays information about the cluster
func Target(client kubectl.Runner) {
	out, err := client.ClusterInfo()
	if err != nil {
		log.Err(err.Error())
	}
	log.Msg(string(out))
}
Beispiel #4
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)
	}
}
Beispiel #5
0
// NewIndex creats a new Index.
//
// NewIndex indexes all of the chart tables configured in the config.yaml file.
// For that reason, it may cause substantial overhead on a large set of repos.
func NewIndex(cfg *config.Configfile, cachedir string) *Index {
	lines := map[string]string{}
	charts := map[string]*chart.Chartfile{}
	for _, table := range cfg.Repos.Tables {
		def := cfg.Repos.Default == table.Name

		base := filepath.Join(cachedir, table.Name, "*/")
		dirs, err := filepath.Glob(base)
		if err != nil {
			log.Err("Failed to read table %s: %s", table.Name, err)
		}

		for _, dir := range dirs {
			bname := filepath.Base(dir)
			c, err := chart.LoadChartfile(filepath.Join(dir, "Chart.yaml"))
			if err != nil {
				// This is not a chart. Skip it.
				continue
			}
			name := table.Name + "/" + c.Name
			if def {
				name = c.Name
			}
			line := c.Name + sep + table.Name + "/" + bname + sep + c.Description + sep + c.Details
			lines[name] = strings.ToLower(line)
			charts[name] = c
		}
	}
	return &Index{lines: lines, charts: charts}
}
Beispiel #6
0
// Valid returns true if every validation passes.
func (cv *ChartValidation) Valid() bool {
	var valid = true

	fmt.Printf("\nVerifying %s chart is a valid chart...\n", cv.ChartName())
	cv.walk(func(v *Validation) bool {
		v.path = cv.Path
		vv := v.valid()
		if !vv {
			switch v.level {
			case 2:
				cv.ErrorCount = cv.ErrorCount + 1
				msg := v.Message + " : " + strconv.FormatBool(vv)
				log.Err(msg)
			case 1:
				cv.WarningCount = cv.WarningCount + 1
				msg := v.Message + " : " + strconv.FormatBool(vv)
				log.Warn(msg)
			}
		} else {
			msg := v.Message + " : " + strconv.FormatBool(vv)
			log.Info(msg)
		}

		valid = valid && vv
		return valid
	})

	return valid
}
Beispiel #7
0
func optRepoMatch(from, req *chart.Dependency) bool {
	// If no repo is set, this is treated as a match.
	if req.Repo == "" {
		return true
	}
	// Some day we might want to do some git-fu to match different forms of the
	// same Git repo.
	a, err := canonicalRepo(req.Repo)
	if err != nil {
		log.Err("Could not parse %s: %s", req.Repo, err)
		return false
	}
	b, err := canonicalRepo(from.Repo)
	if err != nil {
		log.Err("Could not parse %s: %s", from.Repo, err)
		return false
	}
	return a == b
}
Beispiel #8
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("")
	}
}
Beispiel #9
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)
}
Beispiel #10
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)
	}
}
Beispiel #11
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
}
Beispiel #12
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
}
Beispiel #13
0
// Lint validates that a chart is well-formed
//
// - chartPath path to chart directory
func Lint(chartPath string) {
	cv := new(validation.ChartValidation)

	chartPresenceValidation := cv.AddError("Chart found at "+chartPath, func(path string, v *validation.Validation) bool {
		stat, err := os.Stat(chartPath)
		cv.Path = chartPath

		return err == nil && stat.Mode().IsDir()
	})

	chartYamlPresenceValidation := chartPresenceValidation.AddError("Chart.yaml is present", func(path string, v *validation.Validation) bool {
		stat, err := os.Stat(v.ChartYamlPath())

		return err == nil && stat.Mode().IsRegular()
	})

	chartYamlValidation := chartYamlPresenceValidation.AddError("Chart.yaml is valid yaml", func(path string, v *validation.Validation) bool {
		chartfile, err := v.Chartfile()
		if err == nil {
			cv.Chartfile = chartfile
		}

		return err == nil
	})

	chartYamlNameValidation := chartYamlValidation.AddError("Chart.yaml has a name field", func(path string, v *validation.Validation) bool {
		return cv.Chartfile.Name != ""
	})

	chartYamlNameValidation.AddError("Name declared in Chart.yaml is the same as directory name.", func(path string, v *validation.Validation) bool {
		return cv.Chartfile.Name == cv.ChartName()
	})

	chartYamlValidation.AddError("Chart.yaml has a version field", func(path string, v *validation.Validation) bool {
		return cv.Chartfile.Version != ""
	})

	chartYamlValidation.AddWarning("Chart.yaml has a description field", func(path string, v *validation.Validation) bool {
		return cv.Chartfile.Description != ""
	})

	chartYamlValidation.AddWarning("Chart.yaml has a maintainers field", func(path string, v *validation.Validation) bool {
		return cv.Chartfile.Maintainers != nil
	})

	chartPresenceValidation.AddWarning("README.md is present and not empty", func(path string, v *validation.Validation) bool {
		readmePath := filepath.Join(path, "README.md")
		stat, err := os.Stat(readmePath)

		return err == nil && stat.Mode().IsRegular() && stat.Size() > 0
	})

	manifestsValidation := chartPresenceValidation.AddError("Manifests directory is present", func(path string, v *validation.Validation) bool {
		stat, err := os.Stat(v.ChartManifestsPath())

		return err == nil && stat.Mode().IsDir()
	})

	manifestsParsingValidation := manifestsValidation.AddError("Manifests are valid yaml", func(path string, v *validation.Validation) bool {
		manifests, err := manifest.ParseDir(cv.Path)
		if err == nil {
			cv.Manifests = manifests
		}

		return err == nil && cv.Manifests != nil
	})

	manifestsParsingValidation.AddWarning("Manifests have correct and valid metadata", func(path string, v *validation.Validation) bool {

		success := true
		validKinds := InstallOrder

		for _, m := range cv.Manifests {
			meta, _ := m.VersionedObject.Meta()
			if meta.Name == "" || len(meta.Name) > MaxMetadataNameLength {
				success = false
			}

			if match, _ := regexp.MatchString(`[a-z]([-a-z0-9]*[a-z0-9])?`, meta.Name); !match {
				success = false
			}

			val, ok := meta.Labels["heritage"]
			if !ok || (val != "helm") {
				success = false
			}

			kind := meta.Kind
			validManifestKind := false

			for _, validKind := range validKinds {
				if kind == validKind {
					validManifestKind = true
				}
			}

			if validManifestKind == false {
				success = false
			}
		}

		return success
	})

	if cv.Valid() {
		log.Info("Chart [%s] has passed all necessary checks", cv.ChartName())
	} else {
		if cv.ErrorCount > 0 {
			log.Err("Chart [%s] has failed some necessary checks. Check out the error and warning messages listed.", cv.ChartName())
		} else {
			log.Warn("Chart [%s] has passed all necessary checks but failed some checks as well. Proceed with caution. Check out the warnings listed.", cv.ChartName())
		}
	}
}