diff --git a/cmd/release-controller-api/http.go b/cmd/release-controller-api/http.go index 953519915..7e057fd47 100644 --- a/cmd/release-controller-api/http.go +++ b/cmd/release-controller-api/http.go @@ -1716,16 +1716,18 @@ func (c *Controller) httpReleases(w http.ResponseWriter, req *http.Request) { Tags: releasecontroller.SortedReleaseTags(r), } var delays []string - if r.Config.As != releasecontroller.ReleaseConfigModeStable && len(s.Tags) > 0 { + if r.Config.EndOfSupport { + s.Delayed = &ReleaseDelay{Message: "Release has reached end of support"} + } else if r.Config.As != releasecontroller.ReleaseConfigModeStable && len(s.Tags) > 0 { if ok, _, queueAfter := releasecontroller.IsReleaseDelayedForInterval(r, s.Tags[0]); ok { delays = append(delays, fmt.Sprintf("waiting for %s", queueAfter.Truncate(time.Second))) } if r.Config.MaxUnreadyReleases > 0 && releasecontroller.CountUnreadyReleases(r, s.Tags) >= r.Config.MaxUnreadyReleases { delays = append(delays, fmt.Sprintf("no more than %d pending", r.Config.MaxUnreadyReleases)) } - } - if len(delays) > 0 { - s.Delayed = &ReleaseDelay{Message: fmt.Sprintf("Next release may not start: %s", strings.Join(delays, ", "))} + if len(delays) > 0 { + s.Delayed = &ReleaseDelay{Message: fmt.Sprintf("Next release may not start: %s", strings.Join(delays, ", "))} + } } if r.Config.As != releasecontroller.ReleaseConfigModeStable { s.Upgrades = calculateReleaseUpgrades(r, s.Tags, c.graph, false) @@ -1966,16 +1968,18 @@ func (c *Controller) httpReleaseStreamTable(w http.ResponseWriter, req *http.Req Tags: releasecontroller.SortedReleaseTags(r), } var delays []string - if r.Config.As != releasecontroller.ReleaseConfigModeStable && len(s.Tags) > 0 { + if r.Config.EndOfSupport { + s.Delayed = &ReleaseDelay{Message: "Release has reached end of support"} + } else if r.Config.As != releasecontroller.ReleaseConfigModeStable && len(s.Tags) > 0 { if ok, _, queueAfter := releasecontroller.IsReleaseDelayedForInterval(r, s.Tags[0]); ok { delays = append(delays, fmt.Sprintf("waiting for %s", queueAfter.Truncate(time.Second))) } if r.Config.MaxUnreadyReleases > 0 && releasecontroller.CountUnreadyReleases(r, s.Tags) >= r.Config.MaxUnreadyReleases { delays = append(delays, fmt.Sprintf("no more than %d pending", r.Config.MaxUnreadyReleases)) } - } - if len(delays) > 0 { - s.Delayed = &ReleaseDelay{Message: fmt.Sprintf("Next release may not start: %s", strings.Join(delays, ", "))} + if len(delays) > 0 { + s.Delayed = &ReleaseDelay{Message: fmt.Sprintf("Next release may not start: %s", strings.Join(delays, ", "))} + } } if r.Config.As != releasecontroller.ReleaseConfigModeStable { s.Upgrades = calculateReleaseUpgrades(r, s.Tags, c.graph, false) @@ -2123,21 +2127,22 @@ func (c *Controller) httpDashboardOverview(w http.ResponseWriter, req *http.Requ Tags: releasecontroller.SortedReleaseTags(r), } var delays []string - if r.Config.As != releasecontroller.ReleaseConfigModeStable && len(s.Tags) > 0 { + if r.Config.EndOfSupport { + s.Delayed = &ReleaseDelay{Message: "Release has reached end of support"} + } else if r.Config.As != releasecontroller.ReleaseConfigModeStable && len(s.Tags) > 0 { if ok, _, queueAfter := releasecontroller.IsReleaseDelayedForInterval(r, s.Tags[0]); ok { delays = append(delays, fmt.Sprintf("waiting for %s", queueAfter.Truncate(time.Second))) } if r.Config.MaxUnreadyReleases > 0 && releasecontroller.CountUnreadyReleases(r, s.Tags) >= r.Config.MaxUnreadyReleases { delays = append(delays, fmt.Sprintf("no more than %d pending", r.Config.MaxUnreadyReleases)) } + if len(delays) > 0 { + s.Delayed = &ReleaseDelay{Message: fmt.Sprintf("Next release may not start: %s", strings.Join(delays, ", "))} + } } if isReleaseFailing(s.Tags, r.Config.MaxUnreadyReleases) { s.Failing = true } - - if len(delays) > 0 { - s.Delayed = &ReleaseDelay{Message: fmt.Sprintf("Next release may not start: %s", strings.Join(delays, ", "))} - } if r.Config.As != releasecontroller.ReleaseConfigModeStable { s.Upgrades = calculateReleaseUpgrades(r, s.Tags, c.graph, true) } diff --git a/cmd/release-controller/periodic.go b/cmd/release-controller/periodic.go index 853215eae..a4fb6af3f 100644 --- a/cmd/release-controller/periodic.go +++ b/cmd/release-controller/periodic.go @@ -61,6 +61,10 @@ func (c *Controller) syncPeriodicJobs(prowInformers cache.SharedIndexInformer, s klog.V(6).Infof("release %s has reached the end of life", r.Config.Name) continue } + if r.Config.EndOfSupport { + klog.V(6).Infof("release %s has reached end of support, skipping periodic jobs", r.Config.Name) + continue + } for name, releasePeriodic := range r.Config.Periodic { periodicConfig, ok := hasProwJob(cfg, releasePeriodic.ProwJob.Name) if !ok { diff --git a/cmd/release-controller/sync.go b/cmd/release-controller/sync.go index 544f923d7..f530dda02 100644 --- a/cmd/release-controller/sync.go +++ b/cmd/release-controller/sync.go @@ -240,7 +240,11 @@ func calculateSyncActions(release *releasecontroller.Release, now time.Time) (ad inputImageHash = "" removeTags = nil default: - // gate creating new releases when we already are at max unready or in the cooldown interval + // gate creating new releases when end of support, at max unready, or in the cooldown interval + if release.Config.EndOfSupport { + klog.V(2).Infof("Release %s has reached end of support, will not launch new tags", release.Config.Name) + hasNewImages = false + } if release.Config.MaxUnreadyReleases > 0 && unreadyTagCount >= release.Config.MaxUnreadyReleases { klog.V(2).Infof("Release %s at max %d unready releases, will not launch new tags", release.Config.Name, release.Config.MaxUnreadyReleases) hasNewImages = false diff --git a/pkg/release-controller/types.go b/pkg/release-controller/types.go index 9a8922d50..40bcb8e6f 100644 --- a/pkg/release-controller/types.go +++ b/pkg/release-controller/types.go @@ -87,6 +87,11 @@ type ReleaseConfig struct { // displayed by the release-controller EndOfLife bool `json:"endOfLife"` + // EndOfSupport indicates this release stream is no longer supported and should + // stop creating new releases, but remain visible on the status pages without + // showing a countdown timer. + EndOfSupport bool `json:"endOfSupport"` + // As defines what this image stream provides. The default value is "Integration" // and the images in the image stream will be used to build payloads. An optional // mode is "Stable" and tags are assumed to be release payloads that should be promoted