Пример #1
0
func TestUpgradeCmd(t *testing.T) {
	tmpChart, _ := ioutil.TempDir("testdata", "tmp")
	defer os.RemoveAll(tmpChart)
	cfile := &chart.Metadata{
		Name:        "testUpgradeChart",
		Description: "A Helm chart for Kubernetes",
		Version:     "0.1.0",
	}
	chartPath, err := chartutil.Create(cfile, tmpChart)
	if err != nil {
		t.Errorf("Error creating chart for upgrade: %v", err)
	}
	ch, _ := chartutil.Load(chartPath)
	_ = releaseMock(&releaseOptions{
		name:  "funny-bunny",
		chart: ch,
	})

	// update chart version
	cfile = &chart.Metadata{
		Name:        "testUpgradeChart",
		Description: "A Helm chart for Kubernetes",
		Version:     "0.1.2",
	}

	chartPath, err = chartutil.Create(cfile, tmpChart)
	if err != nil {
		t.Errorf("Error creating chart: %v", err)
	}
	ch, err = chartutil.Load(chartPath)
	if err != nil {
		t.Errorf("Error loading updated chart: %v", err)
	}

	tests := []releaseCase{
		{
			name:     "upgrade a release",
			args:     []string{"funny-bunny", chartPath},
			resp:     releaseMock(&releaseOptions{name: "funny-bunny", version: 2, chart: ch}),
			expected: "funny-bunny has been upgraded. Happy Helming!\n",
		},
		{
			name:     "install a release with 'upgrade --install'",
			args:     []string{"zany-bunny", chartPath},
			flags:    []string{"-i"},
			resp:     releaseMock(&releaseOptions{name: "zany-bunny", version: 1, chart: ch}),
			expected: "zany-bunny has been upgraded. Happy Helming!\n",
		},
	}

	cmd := func(c *fakeReleaseClient, out io.Writer) *cobra.Command {
		return newUpgradeCmd(c, out)
	}

	runReleaseCases(t, tests, cmd)

}
Пример #2
0
// InstallRelease installs a new chart and returns the release response.
func (h *Client) InstallRelease(chstr, ns string, opts ...InstallOption) (*rls.InstallReleaseResponse, error) {
	// load the chart to install
	chart, err := chartutil.Load(chstr)
	if err != nil {
		return nil, err
	}

	// apply the install options
	for _, opt := range opts {
		opt(&h.opts)
	}
	req := &h.opts.instReq
	req.Chart = chart
	req.Namespace = ns
	req.DryRun = h.opts.dryRun
	req.DisableHooks = h.opts.disableHooks
	req.ReuseName = h.opts.reuseName
	ctx := NewContext()

	if h.opts.before != nil {
		if err := h.opts.before(ctx, req); err != nil {
			return nil, err
		}
	}
	return h.install(ctx, req)
}
Пример #3
0
// UpdateRelease updates a release to a new/different chart
func (h *Client) UpdateRelease(rlsName string, chstr string, opts ...UpdateOption) (*rls.UpdateReleaseResponse, error) {
	// load the chart to update
	chart, err := chartutil.Load(chstr)
	if err != nil {
		return nil, err
	}

	// apply the update options
	for _, opt := range opts {
		opt(&h.opts)
	}
	req := &h.opts.updateReq
	req.Chart = chart
	req.DryRun = h.opts.dryRun
	req.Name = rlsName
	req.DisableHooks = h.opts.disableHooks
	req.Recreate = h.opts.recreate
	ctx := NewContext()

	if h.opts.before != nil {
		if err := h.opts.before(ctx, req); err != nil {
			return nil, err
		}
	}
	return h.update(ctx, req)
}
Пример #4
0
// printMissing prints warnings about charts that are present on disk, but are not in the requirements.
func (l *dependencyListCmd) printMissing(reqs *chartutil.Requirements, out io.Writer) {
	folder := filepath.Join(l.chartpath, "charts/*")
	files, err := filepath.Glob(folder)
	if err != nil {
		fmt.Fprintln(l.out, err)
		return
	}

	for _, f := range files {
		fi, err := os.Stat(f)
		if err != nil {
			fmt.Fprintf(l.out, "Warning: %s\n", err)
		}
		// Skip anything that is not a directory and not a tgz file.
		if !fi.IsDir() && filepath.Ext(f) != ".tgz" {
			continue
		}
		c, err := chartutil.Load(f)
		if err != nil {
			fmt.Fprintf(l.out, "WARNING: %q is not a chart.\n", f)
			continue
		}
		found := false
		for _, d := range reqs.Dependencies {
			if d.Name == c.Metadata.Name {
				found = true
				break
			}
		}
		if !found {
			fmt.Fprintf(l.out, "WARNING: %q is not in requirements.yaml.\n", f)
		}
	}

}
Пример #5
0
func loadChart(t *testing.T, name string) *cpb.Chart {
	c, err := chartutil.Load(filepath.Join(chartsDir, name))
	if err != nil {
		t.Fatalf("failed to load test chart (%q): %s\n", name, err)
	}
	return c
}
Пример #6
0
func (l *dependencyListCmd) dependencyStatus(dep *chartutil.Dependency) string {
	filename := fmt.Sprintf("%s-%s.tgz", dep.Name, dep.Version)
	archive := filepath.Join(l.chartpath, "charts", filename)
	if _, err := os.Stat(archive); err == nil {
		c, err := chartutil.Load(archive)
		if err != nil {
			return "corrupt"
		}
		if c.Metadata.Name != dep.Name {
			return "misnamed"
		}

		if c.Metadata.Version != dep.Version {
			return "wrong version"
		}
		return "ok"
	}

	folder := filepath.Join(l.chartpath, "charts", dep.Name)
	if fi, err := os.Stat(folder); err != nil {
		return "missing"
	} else if !fi.IsDir() {
		return "mispackaged"
	}

	c, err := chartutil.Load(folder)
	if err != nil {
		return "corrupt"
	}

	if c.Metadata.Name != dep.Name {
		return "misnamed"
	}

	if c.Metadata.Version != dep.Version {
		return "wrong version"
	}

	return "unpacked"
}
Пример #7
0
// Index generates an index for the chart repository and writes an index.yaml file
func (r *ChartRepository) Index() error {
	if r.IndexFile == nil {
		r.IndexFile = &IndexFile{Entries: make(map[string]*ChartRef)}
	}

	existCharts := map[string]bool{}

	for _, path := range r.ChartPaths {
		ch, err := chartutil.Load(path)
		if err != nil {
			return err
		}

		chartfile := ch.Metadata
		digest, err := generateDigest(path)
		if err != nil {
			return err
		}

		key := chartfile.Name + "-" + chartfile.Version
		if r.IndexFile.Entries == nil {
			r.IndexFile.Entries = make(map[string]*ChartRef)
		}

		ref, ok := r.IndexFile.Entries[key]
		var created string
		if ok && ref.Created != "" {
			created = ref.Created
		} else {
			created = nowString()
		}

		url, _ := url.Parse(r.URL)
		url.Path = filepath.Join(url.Path, key+".tgz")

		entry := &ChartRef{Chartfile: chartfile, Name: chartfile.Name, URL: url.String(), Created: created, Digest: digest, Removed: false}

		r.IndexFile.Entries[key] = entry

		// chart is existing
		existCharts[key] = true
	}

	// update deleted charts with Removed = true
	for k := range r.IndexFile.Entries {
		if _, ok := existCharts[k]; !ok {
			r.IndexFile.Entries[k].Removed = true
		}
	}

	return r.saveIndexFile()
}
Пример #8
0
// InstallRelease installs a new chart and returns the release response.
func (h *Client) InstallRelease(chStr string, opts ...InstallOption) (*rls.InstallReleaseResponse, error) {
	c, err := grpc.Dial(h.opts.host, grpc.WithInsecure())
	if err != nil {
		return nil, err
	}
	defer c.Close()

	chart, err := chartutil.Load(chStr)
	if err != nil {
		return nil, err
	}

	return h.opts.rpcInstallRelease(chart, rls.NewReleaseServiceClient(c), opts...)
}
Пример #9
0
func (l *dependencyListCmd) run() error {
	c, err := chartutil.Load(l.chartpath)
	if err != nil {
		return err
	}

	r, err := chartutil.LoadRequirements(c)
	if err != nil {
		if err == chartutil.ErrRequirementsNotFound {
			fmt.Fprintf(l.out, "WARNING: no requirements at %s/charts", l.chartpath)
			return nil
		}
		return err
	}

	l.printRequirements(r, l.out)
	fmt.Fprintln(l.out)
	l.printMissing(r, l.out)
	return nil
}
Пример #10
0
func (r *ChartRepository) Index() error {
	if r.IndexFile == nil {
		r.IndexFile = &IndexFile{Entries: make(map[string]*ChartRef)}
	}

	for _, path := range r.ChartPaths {
		ch, err := chartutil.Load(path)
		if err != nil {
			return err
		}

		chartfile := ch.Metadata
		hash, err := generateChecksum(path)
		if err != nil {
			return err
		}

		key := chartfile.Name + "-" + chartfile.Version
		if r.IndexFile.Entries == nil {
			r.IndexFile.Entries = make(map[string]*ChartRef)
		}

		ref, ok := r.IndexFile.Entries[key]
		var created string
		if ok && ref.Created != "" {
			created = ref.Created
		} else {
			created = time.Now().UTC().String()
		}

		url, _ := url.Parse(r.URL)
		url.Path = filepath.Join(url.Path, key+".tgz")

		entry := &ChartRef{Chartfile: chartfile, Name: chartfile.Name, URL: url.String(), Created: created, Checksum: hash, Removed: false}

		r.IndexFile.Entries[key] = entry

	}

	return r.saveIndexFile()
}
Пример #11
0
// IndexDirectory reads a (flat) directory and generates an index.
//
// It indexes only charts that have been packaged (*.tgz).
//
// It writes the results to dir/index.yaml.
func IndexDirectory(dir, baseURL string) (*IndexFile, error) {
	archives, err := filepath.Glob(filepath.Join(dir, "*.tgz"))
	if err != nil {
		return nil, err
	}
	index := NewIndexFile()
	for _, arch := range archives {
		fname := filepath.Base(arch)
		c, err := chartutil.Load(arch)
		if err != nil {
			// Assume this is not a chart.
			continue
		}
		hash, err := provenance.DigestFile(arch)
		if err != nil {
			return index, err
		}
		index.Add(c.Metadata, fname, baseURL, hash)
	}
	return index, nil
}
Пример #12
0
func (i *inspectCmd) run() error {
	chrt, err := chartutil.Load(i.chartpath)
	if err != nil {
		return err
	}
	cf, err := yaml.Marshal(chrt.Metadata)
	if err != nil {
		return err
	}

	if i.output == chartOnly || i.output == both {
		fmt.Fprintln(i.out, string(cf))
	}

	if (i.output == valuesOnly || i.output == both) && chrt.Values != nil {
		if i.output == both {
			fmt.Fprintln(i.out, "---")
		}
		fmt.Fprintln(i.out, chrt.Values.Raw)
	}

	return nil
}
Пример #13
0
func (r *ChartRepository) generateIndex() error {
	if r.IndexFile == nil {
		r.IndexFile = NewIndexFile()
	}

	for _, path := range r.ChartPaths {
		ch, err := chartutil.Load(path)
		if err != nil {
			return err
		}

		digest, err := provenance.DigestFile(path)
		if err != nil {
			return err
		}

		if !r.IndexFile.Has(ch.Metadata.Name, ch.Metadata.Version) {
			r.IndexFile.Add(ch.Metadata, path, r.URL, digest)
		}
		// TODO: If a chart exists, but has a different Digest, should we error?
	}
	r.IndexFile.SortEntries()
	return nil
}
Пример #14
0
// Templates lints the templates in the Linter.
func Templates(linter *support.Linter) {
	templatesPath := filepath.Join(linter.ChartDir, "templates")

	templatesDirExist := linter.RunLinterRule(support.WarningSev, validateTemplatesDir(templatesPath))

	// Templates directory is optional for now
	if !templatesDirExist {
		return
	}

	// Load chart and parse templates, based on tiller/release_server
	chart, err := chartutil.Load(linter.ChartDir)

	chartLoaded := linter.RunLinterRule(support.ErrorSev, validateNoError(err))

	if !chartLoaded {
		return
	}

	options := chartutil.ReleaseOptions{Name: "testRelease", Time: timeconv.Now(), Namespace: "testNamespace"}
	valuesToRender, err := chartutil.ToRenderValues(chart, chart.Values, options)
	if err != nil {
		// FIXME: This seems to generate a duplicate, but I can't find where the first
		// error is coming from.
		//linter.RunLinterRule(support.ErrorSev, err)
		return
	}
	renderedContentMap, err := engine.New().Render(chart, valuesToRender)

	renderOk := linter.RunLinterRule(support.ErrorSev, validateNoError(err))

	if !renderOk {
		return
	}

	/* Iterate over all the templates to check:
	   - It is a .yaml file
		 - All the values in the template file is defined
		 - {{}} include | quote
		 - Generated content is a valid Yaml file
		 - Metadata.Namespace is not set
	*/
	for _, template := range chart.Templates {
		fileName, preExecutedTemplate := template.Name, template.Data

		linter.RunLinterRule(support.ErrorSev, validateAllowedExtension(fileName))

		// We only apply the following lint rules to yaml files
		if filepath.Ext(fileName) != ".yaml" {
			continue
		}

		// Check that all the templates have a matching value
		linter.RunLinterRule(support.WarningSev, validateNonMissingValues(fileName, templatesPath, valuesToRender, preExecutedTemplate))

		linter.RunLinterRule(support.WarningSev, validateQuotes(fileName, string(preExecutedTemplate)))

		renderedContent := renderedContentMap[fileName]
		var yamlStruct K8sYamlStruct
		// Even though K8sYamlStruct only defines Metadata namespace, an error in any other
		// key will be raised as well
		err := yaml.Unmarshal([]byte(renderedContent), &yamlStruct)

		validYaml := linter.RunLinterRule(support.ErrorSev, validateYamlContent(fileName, err))

		if !validYaml {
			continue
		}

		linter.RunLinterRule(support.ErrorSev, validateNoNamespace(fileName, yamlStruct))
	}
}