// prepareRollback finds the previous release and prepares a new release object with // the previous release's configuration func (s *releaseServer) prepareRollback(req *services.RollbackReleaseRequest) (*release.Release, *release.Release, error) { switch { case req.Name == "": return nil, nil, errMissingRelease case req.Version < 0: return nil, nil, errInvalidRevision } // finds the non-deleted release with the given name h, err := s.env.Releases.History(req.Name) if err != nil { return nil, nil, err } if len(h) <= 1 { return nil, nil, errors.New("no revision to rollback") } relutil.SortByRevision(h) crls := h[len(h)-1] rbv := req.Version if req.Version == 0 { rbv = crls.Version - 1 } log.Printf("rolling back %s (current: v%d, target: v%d)", req.Name, crls.Version, rbv) prls, err := s.env.Releases.Get(req.Name, rbv) if err != nil { return nil, nil, err } // Store a new release object with previous release's configuration // Store a new release object with previous release's configuration target := &release.Release{ Name: req.Name, Namespace: crls.Namespace, Chart: prls.Chart, Config: prls.Config, Info: &release.Info{ FirstDeployed: crls.Info.FirstDeployed, LastDeployed: timeconv.Now(), Status: &release.Status{ Code: release.Status_UNKNOWN, Notes: prls.Info.Status.Notes, }, }, Version: crls.Version + 1, Manifest: prls.Manifest, Hooks: prls.Hooks, } return crls, target, nil }
func (s *releaseServer) UninstallRelease(c ctx.Context, req *services.UninstallReleaseRequest) (*services.UninstallReleaseResponse, error) { if !checkClientVersion(c) { return nil, errIncompatibleVersion } if req.Name == "" { log.Printf("uninstall: Release not found: %s", req.Name) return nil, errMissingRelease } rels, err := s.env.Releases.History(req.Name) if err != nil { log.Printf("uninstall: Release not loaded: %s", req.Name) return nil, err } if len(rels) < 1 { return nil, errMissingRelease } relutil.SortByRevision(rels) rel := rels[len(rels)-1] // TODO: Are there any cases where we want to force a delete even if it's // already marked deleted? if rel.Info.Status.Code == release.Status_DELETED { if req.Purge { if err := s.purgeReleases(rels...); err != nil { log.Printf("uninstall: Failed to purge the release: %s", err) return nil, err } return &services.UninstallReleaseResponse{Release: rel}, nil } return nil, fmt.Errorf("the release named %q is already deleted", req.Name) } log.Printf("uninstall: Deleting %s", req.Name) rel.Info.Status.Code = release.Status_DELETED rel.Info.Deleted = timeconv.Now() res := &services.UninstallReleaseResponse{Release: rel} if !req.DisableHooks { if err := s.execHook(rel.Hooks, rel.Name, rel.Namespace, preDelete); err != nil { return res, err } } vs, err := s.getVersionSet() if err != nil { return nil, fmt.Errorf("Could not get apiVersions from Kubernetes: %s", err) } manifests := splitManifests(rel.Manifest) _, files, err := sortManifests(manifests, vs, UninstallOrder) if err != nil { // We could instead just delete everything in no particular order. return nil, err } // Collect the errors, and return them later. es := []string{} for _, file := range files { b := bytes.NewBufferString(file.content) if err := s.env.KubeClient.Delete(rel.Namespace, b); err != nil { log.Printf("uninstall: Failed deletion of %q: %s", req.Name, err) es = append(es, err.Error()) } } if !req.DisableHooks { if err := s.execHook(rel.Hooks, rel.Name, rel.Namespace, postDelete); err != nil { es = append(es, err.Error()) } } if !req.Purge { if err := s.env.Releases.Update(rel); err != nil { log.Printf("uninstall: Failed to store updated release: %s", err) } } else { if err := s.purgeReleases(rels...); err != nil { log.Printf("uninstall: Failed to purge the release: %s", err) } } var errs error if len(es) > 0 { errs = fmt.Errorf("deletion error count %d: %s", len(es), strings.Join(es, "; ")) } return res, errs }
// UninstallRelease deletes all of the resources associated with this release, and marks the release DELETED. func (s *ReleaseServer) UninstallRelease(c ctx.Context, req *services.UninstallReleaseRequest) (*services.UninstallReleaseResponse, error) { if !checkClientVersion(c) { return nil, errIncompatibleVersion } if !ValidName.MatchString(req.Name) { log.Printf("uninstall: Release not found: %s", req.Name) return nil, errMissingRelease } rels, err := s.env.Releases.History(req.Name) if err != nil { log.Printf("uninstall: Release not loaded: %s", req.Name) return nil, err } if len(rels) < 1 { return nil, errMissingRelease } relutil.SortByRevision(rels) rel := rels[len(rels)-1] // TODO: Are there any cases where we want to force a delete even if it's // already marked deleted? if rel.Info.Status.Code == release.Status_DELETED { if req.Purge { if err := s.purgeReleases(rels...); err != nil { log.Printf("uninstall: Failed to purge the release: %s", err) return nil, err } return &services.UninstallReleaseResponse{Release: rel}, nil } return nil, fmt.Errorf("the release named %q is already deleted", req.Name) } log.Printf("uninstall: Deleting %s", req.Name) rel.Info.Status.Code = release.Status_DELETING rel.Info.Deleted = timeconv.Now() res := &services.UninstallReleaseResponse{Release: rel} if !req.DisableHooks { if err := s.execHook(rel.Hooks, rel.Name, rel.Namespace, preDelete); err != nil { return res, err } } vs, err := getVersionSet(s.clientset.Discovery()) if err != nil { return nil, fmt.Errorf("Could not get apiVersions from Kubernetes: %s", err) } // From here on out, the release is currently considered to be in Status_DELETING // state. if err := s.env.Releases.Update(rel); err != nil { log.Printf("uninstall: Failed to store updated release: %s", err) } manifests := splitManifests(rel.Manifest) _, files, err := sortManifests(manifests, vs, UninstallOrder) if err != nil { // We could instead just delete everything in no particular order. // FIXME: One way to delete at this point would be to try a label-based // deletion. The problem with this is that we could get a false positive // and delete something that was not legitimately part of this release. return nil, fmt.Errorf("corrupted release record. You must manually delete the resources: %s", err) } filesToKeep, filesToDelete := filterManifestsToKeep(files) if len(filesToKeep) > 0 { res.Info = summarizeKeptManifests(filesToKeep) } // Collect the errors, and return them later. es := []string{} for _, file := range filesToDelete { b := bytes.NewBufferString(file.content) if err := s.env.KubeClient.Delete(rel.Namespace, b); err != nil { log.Printf("uninstall: Failed deletion of %q: %s", req.Name, err) if err == kube.ErrNoObjectsVisited { // Rewrite the message from "no objects visited" err = errors.New("object not found, skipping delete") } es = append(es, err.Error()) } } if !req.DisableHooks { if err := s.execHook(rel.Hooks, rel.Name, rel.Namespace, postDelete); err != nil { es = append(es, err.Error()) } } if req.Purge { if err := s.purgeReleases(rels...); err != nil { log.Printf("uninstall: Failed to purge the release: %s", err) } } rel.Info.Status.Code = release.Status_DELETED if err := s.env.Releases.Update(rel); err != nil { log.Printf("uninstall: Failed to store updated release: %s", err) } var errs error if len(es) > 0 { errs = fmt.Errorf("deletion completed with %d error(s): %s", len(es), strings.Join(es, "; ")) } return res, errs }