// 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 }
// 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)) }
// 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") }
// 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() }
// ListRepos lists the repositories. func ListRepos(homedir string) { rf := mustConfig(homedir).Repos for _, t := range rf.Tables { n := t.Name if t.Name == rf.Default { n += "*" } log.Msg("\t%s\t%s", n, t.Repo) } }
// 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) }
// printSummary prints a diff of charts after upate func printSummary(diff string) { if len(diff) == 0 { log.Msg("Already up-to-date.") return } s := make(repoSummary) // parse git diff-tree for _, line := range strings.Split(diff, "\n") { kv := strings.Split(line, "\t") st, chart := kv[0], kv[1] s.add(st, chart) } // width of columns colwidth := 29 // get console width maxwidth := 118 if w, _, err := terminal.GetSize(int(os.Stdout.Fd())); err == nil { maxwidth = w } // print results for st, charts := range s { switch st { case "A": log.Msg("Added %d charts", len(charts)) case "D": log.Msg("Sent %d charts to the depths", len(charts)) case "M": log.Msg("Updated %d charts", len(charts)) } line := "" for _, ch := range charts { // if adding this column passes the max // print and reset to zero if len(line)+colwidth > maxwidth { log.Msg(line) line = "" } // append to line with padding line = fmt.Sprintf("%s%-29s", line, ch) } log.Msg(line) } }
func uninstallKind(kind []*manifest.Manifest, ns, ktype string, dry bool, client kubectl.Runner) { for _, o := range kind { if dry { log.Msg("%s/%s", ktype, o.Name) } else { // If it's a keeper manifest, skip uninstall. if data, err := o.VersionedObject.JSON(); err == nil { if manifest.IsKeeper(data) { log.Warn("Not uninstalling %s %s because of \"helm-keep\" annotation.\n"+ "---> Use kubectl to uninstall keeper manifests.\n", ktype, o.Name) continue } } out, err := client.Delete(o.Name, ktype, ns) if err != nil { log.Warn("Could not delete %s %s (Skipping): %s", ktype, o.Name, err) } log.Info(string(out)) } } }
// 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) } }
package cli import ( "github.com/codegangsta/cli" "github.com/helm/helm-classic/log" ) var homeCmd = cli.Command{ Name: "home", Usage: "Displays the location of the Helm Classic home.", ArgsUsage: "", Action: func(c *cli.Context) { log.Msg(home(c)) }, }