// 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 $HELM_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.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 }
// 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 := filepath.Join(home, WorkspaceChartPath, chartName, "Chart.yaml") 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 }
// 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) } }
// gitUpdate updates a Git repo. func gitUpdate(git *vcs.GitRepo) error { if err := git.Update(); err != nil { return err } log.Debug("Updated %s from %s", git.LocalPath(), git.Remote()) return nil }
// Copy a directory and its subdirectories. func copyDir(src, dst string) error { var failure error walker := func(fname string, fi os.FileInfo, e error) error { if e != nil { log.Warn("Encounter error walking %q: %s", fname, e) failure = e return nil } log.Debug("Copying %s", fname) rf, err := filepath.Rel(src, fname) if err != nil { log.Warn("Could not find relative path: %s", err) return nil } df := filepath.Join(dst, rf) // Handle directories by creating mirrors. if fi.IsDir() { if err := os.MkdirAll(df, fi.Mode()); err != nil { log.Warn("Could not create %q: %s", df, err) failure = err } return nil } // Otherwise, copy files. in, err := os.Open(fname) if err != nil { log.Warn("Skipping file %s: %s", fname, err) return nil } out, err := os.Create(df) if err != nil { in.Close() log.Warn("Skipping file copy %s: %s", fname, err) return nil } if _, err = io.Copy(out, in); err != nil { log.Warn("Copy from %s to %s failed: %s", fname, df, err) } if err := out.Close(); err != nil { log.Warn("Failed to close %q: %s", df, err) } if err := in.Close(); err != nil { log.Warn("Failed to close reader %q: %s", fname, err) } return nil } filepath.Walk(src, walker) return failure }
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) }
func kubectlDelete(name, ktype, ns string) error { log.Debug("Deleting %s (%s)", name, ktype) a := []string{"delete", ktype, name} if ns != "" { a = append([]string{fmt.Sprintf("--namespace=%q", ns)}, a...) } cmd := exec.Command("kubectl", a...) if d, err := cmd.CombinedOutput(); err != nil { return fmt.Errorf("%s: %s", string(d), 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" { 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) }
func fetch(chartName, lname, homedir, chartpath string) { src := filepath.Join(homedir, CachePath, chartpath, chartName) dest := filepath.Join(homedir, WorkspaceChartPath, lname) if fi, err := os.Stat(src); err != nil { log.Die("Chart %s not found in %s", lname, src) } else 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 := copyDir(src, dest); err != nil { log.Die("Failed copying %s to %s", src, dest) } }
// PrintREADME prints the README file (if it exists) to the console. func PrintREADME(chart, home string) { p := filepath.Join(home, WorkspaceChartPath, 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() }
// ensureRepo ensures that the repo exists and is checked out. // DEPRECATED: You should use the functions in package `repo` instead. func ensureRepo(repo, home string) *vcs.GitRepo { if err := os.Chdir(home); err != nil { log.Die("Could not change to directory %q: %s", home, err) } git, err := vcs.NewGitRepo(repo, home) if err != nil { log.Die("Could not get repository %q: %s", repo, err) } git.Logger = log.New() if !git.CheckLocal() { log.Debug("Cloning repo into %q. Please wait.", home) if err := git.Get(); err != nil { log.Die("Could not create repository in %q: %s", home, err) } } return git }
// 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() }