Example #1
0
// prepareUpdate builds an updated release for an update operation.
func (s *releaseServer) prepareUpdate(req *services.UpdateReleaseRequest) (*release.Release, *release.Release, error) {
	if req.Name == "" {
		return nil, nil, errMissingRelease
	}

	if req.Chart == nil {
		return nil, nil, errMissingChart
	}

	// finds the non-deleted release with the given name
	currentRelease, err := s.env.Releases.Deployed(req.Name)
	if err != nil {
		return nil, nil, err
	}

	// If new values were not supplied in the upgrade, re-use the existing values.
	s.reuseValues(req, currentRelease)

	ts := timeconv.Now()
	options := chartutil.ReleaseOptions{
		Name:      req.Name,
		Time:      ts,
		Namespace: currentRelease.Namespace,
	}

	valuesToRender, err := chartutil.ToRenderValues(req.Chart, req.Values, options)
	if err != nil {
		return nil, nil, err
	}

	hooks, manifestDoc, notesTxt, err := s.renderResources(req.Chart, valuesToRender)
	if err != nil {
		return nil, nil, err
	}

	// Store an updated release.
	updatedRelease := &release.Release{
		Name:      req.Name,
		Namespace: currentRelease.Namespace,
		Chart:     req.Chart,
		Config:    req.Values,
		Info: &release.Info{
			FirstDeployed: currentRelease.Info.FirstDeployed,
			LastDeployed:  ts,
			Status:        &release.Status{Code: release.Status_UNKNOWN},
		},
		Version:  currentRelease.Version + 1,
		Manifest: manifestDoc.String(),
		Hooks:    hooks,
	}

	if len(notesTxt) > 0 {
		updatedRelease.Info.Status.Notes = notesTxt
	}
	return currentRelease, updatedRelease, nil
}
Example #2
0
// prepareRelease builds a release for an install operation.
func (s *releaseServer) prepareRelease(req *services.InstallReleaseRequest) (*release.Release, error) {
	if req.Chart == nil {
		return nil, errMissingChart
	}

	name, err := s.uniqName(req.Name, req.ReuseName)
	if err != nil {
		return nil, err
	}

	ts := timeconv.Now()
	options := chartutil.ReleaseOptions{Name: name, Time: ts, Namespace: req.Namespace}
	valuesToRender, err := chartutil.ToRenderValues(req.Chart, req.Values, options)
	if err != nil {
		return nil, err
	}

	hooks, manifestDoc, notesTxt, err := s.renderResources(req.Chart, valuesToRender)
	if err != nil {
		return nil, err
	}

	// Store a release.
	rel := &release.Release{
		Name:      name,
		Namespace: req.Namespace,
		Chart:     req.Chart,
		Config:    req.Values,
		Info: &release.Info{
			FirstDeployed: ts,
			LastDeployed:  ts,
			Status:        &release.Status{Code: release.Status_UNKNOWN},
		},
		Manifest: manifestDoc.String(),
		Hooks:    hooks,
		Version:  1,
	}
	if len(notesTxt) > 0 {
		rel.Info.Status.Notes = notesTxt
	}
	return rel, nil
}
Example #3
0
// prepareRelease builds a release for an install operation.
func (s *ReleaseServer) prepareRelease(req *services.InstallReleaseRequest) (*release.Release, error) {
	if req.Chart == nil {
		return nil, errMissingChart
	}

	name, err := s.uniqName(req.Name, req.ReuseName)
	if err != nil {
		return nil, err
	}

	revision := 1
	ts := timeconv.Now()
	options := chartutil.ReleaseOptions{
		Name:      name,
		Time:      ts,
		Namespace: req.Namespace,
		Revision:  revision,
		IsInstall: true,
	}
	valuesToRender, err := chartutil.ToRenderValues(req.Chart, req.Values, options)
	if err != nil {
		return nil, err
	}

	hooks, manifestDoc, notesTxt, err := s.renderResources(req.Chart, valuesToRender)
	if err != nil {
		// Return a release with partial data so that client can show debugging
		// information.
		rel := &release.Release{
			Name:      name,
			Namespace: req.Namespace,
			Chart:     req.Chart,
			Config:    req.Values,
			Info: &release.Info{
				FirstDeployed: ts,
				LastDeployed:  ts,
				Status:        &release.Status{Code: release.Status_UNKNOWN},
			},
			Version: 0,
		}
		if manifestDoc != nil {
			rel.Manifest = manifestDoc.String()
		}
		return rel, err
	}

	// Store a release.
	rel := &release.Release{
		Name:      name,
		Namespace: req.Namespace,
		Chart:     req.Chart,
		Config:    req.Values,
		Info: &release.Info{
			FirstDeployed: ts,
			LastDeployed:  ts,
			Status:        &release.Status{Code: release.Status_UNKNOWN},
		},
		Manifest: manifestDoc.String(),
		Hooks:    hooks,
		Version:  int32(revision),
	}
	if len(notesTxt) > 0 {
		rel.Info.Status.Notes = notesTxt
	}
	return rel, nil
}
Example #4
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))
	}
}
Example #5
0
func (s *releaseServer) InstallRelease(c ctx.Context, req *services.InstallReleaseRequest) (*services.InstallReleaseResponse, error) {
	if req.Chart == nil {
		return nil, errMissingChart
	}

	name, err := s.uniqName(req.Name)
	if err != nil {
		return nil, err
	}

	ts := timeconv.Now()
	options := chartutil.ReleaseOptions{Name: name, Time: ts, Namespace: s.env.Namespace}
	valuesToRender, err := chartutil.ToRenderValues(req.Chart, req.Values, options)
	if err != nil {
		return nil, err
	}

	renderer := s.engine(req.Chart)
	files, err := renderer.Render(req.Chart, valuesToRender)
	if err != nil {
		return nil, err
	}

	b := bytes.NewBuffer(nil)
	for name, file := range files {
		// Ignore templates that starts with underscore to handle them as partials
		if strings.HasPrefix(path.Base(name), "_") {
			continue
		}

		// Ignore empty documents because the Kubernetes library can't handle
		// them.
		if len(file) > 0 {
			b.WriteString("\n---\n# Source: " + name + "\n")
			b.WriteString(file)
		}
	}

	// Store a release.
	r := &release.Release{
		Name:   name,
		Chart:  req.Chart,
		Config: req.Values,
		Info: &release.Info{
			FirstDeployed: ts,
			LastDeployed:  ts,
			Status:        &release.Status{Code: release.Status_UNKNOWN},
		},
		Manifest: b.String(),
	}

	res := &services.InstallReleaseResponse{Release: r}

	if req.DryRun {
		log.Printf("Dry run for %s", name)
		return res, nil
	}

	if err := s.env.KubeClient.Create(s.env.Namespace, b); err != nil {
		r.Info.Status.Code = release.Status_FAILED
		log.Printf("warning: Release %q failed: %s", name, err)
		if err := s.env.Releases.Create(r); err != nil {
			log.Printf("warning: Failed to record release %q: %s", name, err)
		}
		return res, fmt.Errorf("release %s failed: %s", name, err)
	}

	// This is a tricky case. The release has been created, but the result
	// cannot be recorded. The truest thing to tell the user is that the
	// release was created. However, the user will not be able to do anything
	// further with this release.
	//
	// One possible strategy would be to do a timed retry to see if we can get
	// this stored in the future.
	if err := s.env.Releases.Create(r); err != nil {
		log.Printf("warning: Failed to record release %q: %s", name, err)
		return res, nil
	}

	r.Info.Status.Code = release.Status_DEPLOYED
	return res, nil
}