Exemple #1
0
func TestLintEmptyChartYaml(t *testing.T) {
	tmpHome := test.CreateTmpHome()
	test.FakeUpdate(tmpHome)

	chartName := "badChart"

	Create(chartName, tmpHome)

	badChartYaml, _ := yaml.Marshal(make(map[string]string))

	chartYaml := util.WorkspaceChartDirectory(tmpHome, chartName, Chartfile)

	os.Remove(chartYaml)
	ioutil.WriteFile(chartYaml, badChartYaml, 0644)

	output := test.CaptureOutput(func() {
		Lint(util.WorkspaceChartDirectory(tmpHome, chartName))
	})

	test.ExpectContains(t, output, "Chart.yaml has a name field : false")
	test.ExpectContains(t, output, "Chart.yaml has a version field : false")
	test.ExpectContains(t, output, "Chart.yaml has a description field : false")
	test.ExpectContains(t, output, "Chart.yaml has a maintainers field : false")
	test.ExpectContains(t, output, fmt.Sprintf("Chart [%s] has failed some necessary checks", chartName))
}
Exemple #2
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")
}
Exemple #3
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)
	}

	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)
}
Exemple #4
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)
}
Exemple #5
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)
	}
}
Exemple #6
0
func TestLintMissingReadme(t *testing.T) {
	tmpHome := test.CreateTmpHome()
	test.FakeUpdate(tmpHome)

	chartName := "badChart"

	Create(chartName, tmpHome)

	os.Remove(filepath.Join(util.WorkspaceChartDirectory(tmpHome, chartName), "README.md"))

	output := test.CaptureOutput(func() {
		Lint(util.WorkspaceChartDirectory(tmpHome, chartName))
	})

	test.ExpectContains(t, output, "README.md is present and not empty : false")
}
Exemple #7
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, n %s. Running `helm 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)
	}
}
Exemple #8
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")
}
Exemple #9
0
func lint(c *cli.Context) {
	home := home(c)

	all := c.Bool("all")

	if all {
		action.LintAll(home)
		return
	}

	minArgs(c, 1, "lint")

	a := c.Args()
	chartNameOrPath := a[0]

	fromHome := util.WorkspaceChartDirectory(home, chartNameOrPath)
	fromAbs := filepath.Clean(chartNameOrPath)

	_, err := os.Stat(fromAbs)

	if err == nil {
		action.Lint(fromAbs)
	} else {
		action.Lint(fromHome)
	}
}
Exemple #10
0
func TestLintMissingChartYaml(t *testing.T) {
	tmpHome := test.CreateTmpHome()
	test.FakeUpdate(tmpHome)

	chartName := "badChart"

	Create(chartName, tmpHome)

	os.Remove(filepath.Join(util.WorkspaceChartDirectory(tmpHome, chartName), Chartfile))

	output := test.CaptureOutput(func() {
		Lint(util.WorkspaceChartDirectory(tmpHome, chartName))
	})

	test.ExpectContains(t, output, "Chart.yaml is present : false")
	test.ExpectContains(t, output, "Chart [badChart] has failed some necessary checks.")
}
Exemple #11
0
func TestLintMissingManifestDirectory(t *testing.T) {
	tmpHome := test.CreateTmpHome()
	test.FakeUpdate(tmpHome)

	chartName := "brokeChart"

	Create(chartName, tmpHome)

	os.RemoveAll(filepath.Join(util.WorkspaceChartDirectory(tmpHome, chartName), "manifests"))

	output := test.CaptureOutput(func() {
		Lint(util.WorkspaceChartDirectory(tmpHome, chartName))
	})

	test.ExpectMatches(t, output, "Manifests directory is present : false")
	test.ExpectContains(t, output, "Chart ["+chartName+"] has failed some necessary checks")
}
Exemple #12
0
// 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
}
Exemple #13
0
// Edit charts using the shell-defined $EDITOR
//
// - chartName being edited
// - homeDir is the helm 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)
}
Exemple #14
0
func TestLintBadPath(t *testing.T) {
	tmpHome := test.CreateTmpHome()
	chartName := "badChart"

	output := test.CaptureOutput(func() {
		Lint(util.WorkspaceChartDirectory(tmpHome, chartName))
	})

	msg := "Chart found at " + tmpHome + "/workspace/charts/" + chartName + " : false"
	test.ExpectContains(t, output, msg)
}
Exemple #15
0
func TestFetch(t *testing.T) {
	tmpHome := test.CreateTmpHome()
	test.FakeUpdate(tmpHome)
	chartName := "kitchensink"

	actual := test.CaptureOutput(func() {
		Fetch(chartName, "", tmpHome)
	})

	workspacePath := util.WorkspaceChartDirectory(tmpHome, chartName)
	test.ExpectContains(t, actual, "Fetched chart into workspace "+workspacePath)
}
Exemple #16
0
func TestLintMismatchedChartNameAndDir(t *testing.T) {
	tmpHome := test.CreateTmpHome()
	chartName := "chart-0"
	chartDir := "chart-1"
	chart := newSkelChartfile(chartName)
	createWithChart(chart, chartDir, tmpHome)

	output := test.CaptureOutput(func() {
		Lint(util.WorkspaceChartDirectory(tmpHome, chartDir))
	})

	test.ExpectContains(t, output, "Name declared in Chart.yaml is the same as directory name. : false")
}
Exemple #17
0
func TestLintChartByPath(t *testing.T) {
	home1 := test.CreateTmpHome()
	home2 := test.CreateTmpHome()

	chartName := "goodChart"
	action.Create(chartName, home1)

	output := test.CaptureOutput(func() {
		Cli().Run([]string{"helm", "--home", home2, "lint", util.WorkspaceChartDirectory(home1, chartName)})
	})

	test.ExpectContains(t, output, fmt.Sprintf("Chart [%s] has passed all necessary checks", chartName))
}
Exemple #18
0
// List lists all of the local charts.
func List(homedir string) {
	md := helm.WorkspaceChartDirectory(homedir, "*")
	charts, err := filepath.Glob(md)
	if err != nil {
		log.Warn("Could not find any charts in %q: %s", md, err)
	}
	for _, c := range charts {
		cname := filepath.Base(c)
		if ch, err := chart.LoadChartfile(filepath.Join(c, Chartfile)); err == nil {
			log.Info("\t%s (%s %s) - %s", cname, ch.Name, ch.Version, ch.Description)
			continue
		}
		log.Info("\t%s (unknown)", cname)
	}
}
Exemple #19
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) {
	if abs, err := filepath.Abs(homedir); err == nil {
		homedir = abs
	}
	chartPath := util.WorkspaceChartDirectory(homedir, chart)

	os.Setenv("HELM_HOME", homedir)
	os.Setenv("HELM_DEFAULT_REPO", mustConfig(homedir).Repos.Default)

	count, err := generator.Walk(chartPath, exclude)
	if err != nil {
		log.Die("Failed to complete generation: %s", err)
	}
	log.Info("Ran %d generators.", count)
}
Exemple #20
0
// LintAll vlaidates all charts are well-formed
//
// - homedir is the home directory for the user
func LintAll(homedir string) {
	md := util.WorkspaceChartDirectory(homedir, "*")
	chartPaths, err := filepath.Glob(md)
	if err != nil {
		log.Warn("Could not find any charts in %q: %s", md, err)
	}

	if len(chartPaths) == 0 {
		log.Warn("Could not find any charts in %q", md)
	} else {
		for _, chartPath := range chartPaths {
			Lint(chartPath)
		}
	}
}
Exemple #21
0
func TestLintSuccess(t *testing.T) {
	tmpHome := test.CreateTmpHome()
	test.FakeUpdate(tmpHome)

	chartName := "goodChart"

	Create(chartName, tmpHome)

	output := test.CaptureOutput(func() {
		Lint(util.WorkspaceChartDirectory(tmpHome, chartName))
	})

	expected := "Chart [goodChart] has passed all necessary checks"

	test.ExpectContains(t, output, expected)
}
Exemple #22
0
func TestGenerate(t *testing.T) {
	ch := "generate"
	homedir := test.CreateTmpHome()
	test.FakeUpdate(homedir)
	Fetch(ch, ch, homedir)

	Generate(ch, homedir, []string{"ignore"})

	// Now we should be able to load and read the `pod.yaml` file.
	path := util.WorkspaceChartDirectory(homedir, "generate/manifests/pod.yaml")
	d, err := ioutil.ReadFile(path)
	if err != nil {
		t.Fatal(err)
	}
	pod := string(d)
	test.ExpectContains(t, pod, "image: ozo")
	test.ExpectContains(t, pod, "name: www-server")
}
Exemple #23
0
func TestLintAll(t *testing.T) {
	tmpHome := test.CreateTmpHome()
	test.FakeUpdate(tmpHome)

	missingReadmeChart := "missingReadme"

	action.Create(missingReadmeChart, tmpHome)
	os.Remove(util.WorkspaceChartDirectory(tmpHome, missingReadmeChart, "README.md"))

	action.Create("goodChart", tmpHome)

	output := test.CaptureOutput(func() {
		Cli().Run([]string{"helm", "--home", tmpHome, "lint", "--all"})
	})

	test.ExpectMatches(t, output, "A README file was not found.*"+missingReadmeChart)
	test.ExpectContains(t, output, "Chart [goodChart] has passed all necessary checks")
	test.ExpectContains(t, output, "Chart [missingReadme] failed some checks")
}
Exemple #24
0
// 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()

}
Exemple #25
0
func fetch(chartName, lname, homedir, chartpath string) {
	src := helm.CacheDirectory(homedir, chartpath, chartName)
	dest := helm.WorkspaceChartDirectory(homedir, 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 := 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)
	}
}
Exemple #26
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)
}
Exemple #27
0
func TestCreate(t *testing.T) {
	tmpHome := test.CreateTmpHome()

	Create("mychart", tmpHome)

	// assert chartfile
	chartfile, err := ioutil.ReadFile(util.WorkspaceChartDirectory(tmpHome, "mychart/Chart.yaml"))
	if err != nil {
		t.Errorf("Could not read chartfile: %s", err)
	}
	actualChartfile := string(chartfile)
	expectedChartfile := `name: mychart
home: http://example.com/your/project/home
version: 0.1.0
description: Provide a brief description of your application here.
maintainers:
- Your Name <email@address>
details: |-
  This section allows you to provide additional details about your application.
  Provide any information that would be useful to users at a glance.
`
	test.ExpectEquals(t, actualChartfile, expectedChartfile)

	// asset readme
	readme, err := ioutil.ReadFile(util.WorkspaceChartDirectory(tmpHome, "mychart/README.md"))
	if err != nil {
		t.Errorf("Could not read README.md: %s", err)
	}
	actualReadme := string(readme)
	expectedReadme := `# mychart

Describe your chart here. Link to upstream repositories, Docker images or any
external documentation.

If your application requires any specific configuration like Secrets, you may
include that information here.
`
	test.ExpectEquals(t, expectedReadme, actualReadme)

	// assert example manifest
	manifest, err := ioutil.ReadFile(util.WorkspaceChartDirectory(tmpHome, "mychart/manifests/example-pod.yaml"))
	if err != nil {
		t.Errorf("Could not read manifest: %s", err)
	}
	actualManifest := string(manifest)
	expectedManifest := `---
apiVersion: v1
kind: Pod
metadata:
  name: example-pod
  labels:
    heritage: helm
spec:
  restartPolicy: Never
  containers:
  - name: example
    image: "alpine:3.2"
    command: ["/bin/sleep","9000"]
`
	test.ExpectEquals(t, actualManifest, expectedManifest)
}