// Check by chart directory name whether a chart is fetched into the workspace. // // This does NOT check the Chart.yaml file. func chartFetched(chartName, home string) bool { p := helm.WorkspaceChartDirectory(home, chartName, Chartfile) log.Debug("Looking for %q", p) if fi, err := os.Stat(p); err != nil || fi.IsDir() { log.Debug("No chart: %s", err) return false } return true }
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) } }
// 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) } }
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) }
// renderTemplate renders a template and values into an output stream. // // tpl should be a string template. func renderTemplate(out io.Writer, tpl string, vals interface{}) error { t, err := template.New("helmTpl").Funcs(sprig.TxtFuncMap()).Parse(tpl) if err != nil { return err } log.Debug("Vals: %#v", vals) if err = t.ExecuteTemplate(out, "helmTpl", vals); err != nil { return err } return nil }
func repoChartDiff(rpath, initialVersion string) (string, error) { // build git diff-tree command cmd := exec.Command("git", "-C", rpath, "diff-tree", "--name-status", fmt.Sprintf("%s..HEAD", initialVersion)) log.Debug("git diff cmd: %s", cmd.Args) out, err := cmd.CombinedOutput() if err != nil { return "", err } // cleanup any trailing whitespace return strings.TrimSpace(string(out)), nil }
// 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 }
// ParseDir parses all of the manifests inside of a chart directory. // // The directory should be the Chart directory (contains Chart.yaml and manifests/) // // This will return an error if the directory does not exist, or if there is an // error parsing or decoding any yaml files. func ParseDir(chartDir string) ([]*Manifest, error) { dir := filepath.Join(chartDir, "manifests") files := []*Manifest{} if _, err := os.Stat(dir); err != nil { return files, err } // add manifest files walker := func(fname string, fi os.FileInfo, e error) error { log.Debug("Parsing %s", fname) // Chauncey was right. if e != nil { return e } if fi.IsDir() { return nil } if filepath.Ext(fname) != ".yaml" && filepath.Ext(fname) != ".yml" { log.Debug("Skipping %s. Not a YAML file.", fname) return nil } m, err := Parse(fname) if err != nil { return err } files = append(files, m...) return nil } return files, filepath.Walk(dir, walker) }
// 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() }
// checkManifests gets any installed manifests within a chart func checkManifests(chartPath string) ([]string, error) { var foundManifests []string manifests, err := manifest.Files(chartPath) if err != nil { return nil, err } for _, m := range manifests { out := kubeGet(m) if strings.Contains(out, "unable to connect") { return nil, fmt.Errorf(out) } if !strings.Contains(out, "not found") { foundManifests = append(foundManifests, m) } } log.Debug("Found %d installed manifests", len(foundManifests)) return foundManifests, nil }
// Resolve takes a chart and a location and checks whether the chart's dependencies are satisfied. // // The `installdir` is the location where installed charts are located. Typically // this is in $HELMC_HOME/workspace/charts. // // This returns a list of unsatisfied dependencies (NOT an error condition). // // It returns an error only if it cannot perform the task of resolving dependencies. // Failed dependencies to not constitute an error. func Resolve(cf *chart.Chartfile, installdir string) ([]*chart.Dependency, error) { if len(cf.Dependencies) == 0 { log.Debug("No dependencies to check. :achievement-unlocked:") return []*chart.Dependency{}, nil } cache, err := dependencyCache(installdir) if err != nil { log.Debug("Failed to build dependency cache: %s", err) return []*chart.Dependency{}, err } res := []*chart.Dependency{} // TODO: This could be made more efficient. for _, check := range cf.Dependencies { resolved := false for n, chart := range cache { log.Debug("Checking if %s (%s) %s meets %s %s", chart.Name, n, chart.Version, check.Name, check.Version) if chart.From != nil { log.Debug("✔︎") if satisfies(chart.From, check) { resolved = true break } } else { log.Info("Chart %s is pre-0.2.0. Legacy mode enabled.", chart.Name) if chart.Name == check.Name && check.VersionOK(chart.Version) { log.Debug("✔︎") resolved = true break } } } if !resolved { log.Debug("No matches found for %s %s", check.Name, check.Version) res = append(res, check) } } return res, nil }
// Walk walks a chart directory and executes generators as it finds them. // // Returns the number of generators executed. // // Walking will error out whenever a generator cannot be completely executed. // This includes cases such as not finding the generator referenced, and // cases where the generator itself exits with a non-zero exit code. func Walk(dir string, exclude []string, force bool) (int, error) { excludes := make(map[string]bool, len(exclude)) for i := 0; i < len(exclude); i++ { excludes[filepath.Join(dir, exclude[i])] = true } count := 0 err := filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error { // dive-bomb if we hit an error. if err != nil { return err } // Exclude anything explicitly excluded. if excludes[path] == true { if fi.IsDir() { return filepath.SkipDir } return nil } // Skip directory entries. If the directory prefix is . or _, skip the // contents of the directory as well. if fi.IsDir() { return skip(path) } f, err := os.Open(path) if err != nil { return err } defer f.Close() line, err := readGenerator(f) if err != nil { return err } if line == "" { return nil } // Run the generator. os.Setenv("HELM_GENERATE_COMMAND", line) os.Setenv("HELM_GENERATE_FILE", path) os.Setenv("HELM_GENERATE_DIR", dir) line = os.ExpandEnv(line) os.Setenv("HELM_GENERATE_COMMAND_EXPANDED", line) log.Debug("File: %s, Command: %s", path, line) count++ // Execute the command in the file's directory to make relative // paths usable. origin, err := os.Getwd() if err != nil { log.Warn("Could not get PWD: %s", err) } else if err := os.Chdir(dir); err != nil { log.Warn("Could not change directory to %s: %s", dir, err) } else { origin = dir defer func() { if e := os.Chdir(origin); e != nil { log.Warn("Could not return to %s: %s", origin, e) } }() } err = execute(line, force) if err != nil { return fmt.Errorf("failed to execute %s (%s): %s", line, path, err) } return nil }) return count, err }
import ( "fmt" "os" "os/exec" "strings" "github.com/helm/helm-classic/log" "github.com/helm/helm-classic/manifest" helm "github.com/helm/helm-classic/util" ) // kubeGetter wraps the kubectl command, override in tests type kubeGetter func(string) string var kubeGet kubeGetter = func(m string) string { log.Debug("Getting manifests from %s", m) a := []string{"get", "-f", m} out, _ := exec.Command("kubectl", a...).CombinedOutput() return string(out) } // 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)