func (s *releaseServer) GetReleaseStatus(c ctx.Context, req *services.GetReleaseStatusRequest) (*services.GetReleaseStatusResponse, error) { if !checkClientVersion(c) { return nil, errIncompatibleVersion } if req.Name == "" { return nil, errMissingRelease } var rel *release.Release if req.Version <= 0 { h, err := s.env.Releases.History(req.Name) if err != nil { return nil, fmt.Errorf("getting deployed release '%s': %s", req.Name, err) } if len(h) < 1 { return nil, errMissingRelease } relutil.Reverse(h, relutil.SortByRevision) rel = h[0] } else { var err error if rel, err = s.env.Releases.Get(req.Name, req.Version); err != nil { return nil, fmt.Errorf("getting release '%s' (v%d): %s", req.Name, req.Version, err) } } if rel.Info == nil { return nil, errors.New("release info is missing") } if rel.Chart == nil { return nil, errors.New("release chart is missing") } sc := rel.Info.Status.Code statusResp := &services.GetReleaseStatusResponse{Info: rel.Info, Namespace: rel.Namespace} // Ok, we got the status of the release as we had jotted down, now we need to match the // manifest we stashed away with reality from the cluster. kubeCli := s.env.KubeClient resp, err := kubeCli.Get(rel.Namespace, bytes.NewBufferString(rel.Manifest)) if sc == release.Status_DELETED || sc == release.Status_FAILED { // Skip errors if this is already deleted or failed. return statusResp, nil } else if err != nil { log.Printf("warning: Get for %s failed: %v", rel.Name, err) return nil, err } rel.Info.Status.Resources = resp return statusResp, nil }
// Last fetches the last revision of the named release. func (s *Storage) Last(name string) (*rspb.Release, error) { h, err := s.History(name) if err != nil { return nil, err } if len(h) == 0 { return nil, fmt.Errorf("no revision for release %q", name) } relutil.Reverse(h, relutil.SortByRevision) return h[0], nil }
func (s *releaseServer) uniqName(start string, reuse bool) (string, error) { // If a name is supplied, we check to see if that name is taken. If not, it // is granted. If reuse is true and a deleted release with that name exists, // we re-grant it. Otherwise, an error is returned. if start != "" { if len(start) > releaseNameMaxLen { return "", fmt.Errorf("release name %q exceeds max length of %d", start, releaseNameMaxLen) } h, err := s.env.Releases.History(start) if err != nil || len(h) < 1 { return start, nil } relutil.Reverse(h, relutil.SortByRevision) rel := h[0] if st := rel.Info.Status.Code; reuse && (st == release.Status_DELETED || st == release.Status_FAILED) { // Allowe re-use of names if the previous release is marked deleted. log.Printf("reusing name %q", start) return start, nil } else if reuse { return "", errors.New("cannot re-use a name that is still in use") } return "", fmt.Errorf("a release named %q already exists", start) } maxTries := 5 for i := 0; i < maxTries; i++ { namer := moniker.New() name := namer.NameSep("-") if len(name) > releaseNameMaxLen { name = name[:releaseNameMaxLen] } if _, err := s.env.Releases.Get(name, 1); err == driver.ErrReleaseNotFound { return name, nil } log.Printf("info: Name %q is taken. Searching again.", name) } log.Printf("warning: No available release names found after %d tries", maxTries) return "ERROR", errors.New("no available release name found") }
func (s *releaseServer) GetHistory(ctx context.Context, req *tpb.GetHistoryRequest) (*tpb.GetHistoryResponse, error) { if !checkClientVersion(ctx) { return nil, errIncompatibleVersion } h, err := s.env.Releases.History(req.Name) if err != nil { return nil, err } relutil.Reverse(h, relutil.SortByRevision) var resp tpb.GetHistoryResponse for i := 0; i < min(len(h), int(req.Max)); i++ { resp.Releases = append(resp.Releases, h[i]) } return &resp, nil }
// performRelease runs a release. func (s *releaseServer) performRelease(r *release.Release, req *services.InstallReleaseRequest) (*services.InstallReleaseResponse, error) { res := &services.InstallReleaseResponse{Release: r} if req.DryRun { log.Printf("Dry run for %s", r.Name) return res, nil } // pre-install hooks if !req.DisableHooks { if err := s.execHook(r.Hooks, r.Name, r.Namespace, preInstall); err != nil { return res, err } } switch h, err := s.env.Releases.History(req.Name); { // if this is a replace operation, append to the release history case req.ReuseName && err == nil && len(h) >= 1: // get latest release revision relutil.Reverse(h, relutil.SortByRevision) // old release old := h[0] // update old release status old.Info.Status.Code = release.Status_SUPERSEDED s.recordRelease(old, true) // update new release with next revision number // so as to append to the old release's history r.Version = old.Version + 1 if err := s.performKubeUpdate(old, r); err != nil { log.Printf("warning: Release replace %q failed: %s", r.Name, err) old.Info.Status.Code = release.Status_SUPERSEDED r.Info.Status.Code = release.Status_FAILED s.recordRelease(old, true) s.recordRelease(r, false) return res, err } default: // nothing to replace, create as normal // regular manifests b := bytes.NewBufferString(r.Manifest) if err := s.env.KubeClient.Create(r.Namespace, b); err != nil { log.Printf("warning: Release %q failed: %s", r.Name, err) r.Info.Status.Code = release.Status_FAILED s.recordRelease(r, false) return res, fmt.Errorf("release %s failed: %s", r.Name, err) } } // post-install hooks if !req.DisableHooks { if err := s.execHook(r.Hooks, r.Name, r.Namespace, postInstall); err != nil { log.Printf("warning: Release %q failed post-install: %s", r.Name, err) r.Info.Status.Code = release.Status_FAILED s.recordRelease(r, false) return res, 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. r.Info.Status.Code = release.Status_DEPLOYED s.recordRelease(r, false) return res, nil }