From 950cfe8ecc92b7e420024b415b9d046e9398e699 Mon Sep 17 00:00:00 2001 From: Ondrej Pokorny Date: Tue, 13 Jan 2026 16:15:02 +0100 Subject: [PATCH] feat: set Progressing condition on cluster update This commit implements functionality to set the Progressing condition of the insights clusteroperator to True when the OpenShift version updates its minor or major version, while ignoring patch version updates. Signed-off-by: Ondrej Pokorny --- pkg/controller/status/controller.go | 50 +++++++++++++++-- pkg/controller/status/controller_test.go | 69 ++++++++++++++++++++++++ 2 files changed, 115 insertions(+), 4 deletions(-) diff --git a/pkg/controller/status/controller.go b/pkg/controller/status/controller.go index 34c8af51f..12194ea22 100644 --- a/pkg/controller/status/controller.go +++ b/pkg/controller/status/controller.go @@ -13,6 +13,7 @@ import ( "golang.org/x/time/rate" + "github.com/blang/semver/v4" configv1 "github.com/openshift/api/config/v1" configv1client "github.com/openshift/client-go/config/clientset/versioned/typed/config/v1" "k8s.io/apimachinery/pkg/api/errors" @@ -181,15 +182,28 @@ func (c *Controller) merge(clusterOperator *configv1.ClusterOperator) *configv1. c.updateControllerConditions(cs, isInitializing) c.updateControllerConditionsByStatus(cs, isInitializing) - // all status conditions from conditions to cluster operator - clusterOperator.Status.Conditions = cs.entries() + if releaseVersion := os.Getenv("RELEASE_VERSION"); len(releaseVersion) > 0 { + setProgressing, err := shouldSetProgressingCondition(releaseVersion, clusterOperator.Status.Versions) + if err != nil { + klog.Errorf("failed checking openshift release version: %s with err: %v", releaseVersion, err) + } - if release := os.Getenv("RELEASE_VERSION"); len(release) > 0 { clusterOperator.Status.Versions = []configv1.OperandVersion{ - {Name: "operator", Version: release}, + {Name: "operator", Version: releaseVersion}, + } + + if setProgressing { + cs.setCondition( + configv1.OperatorProgressing, + configv1.ConditionTrue, + "Openshift Upgrade", + "Cluster version is updated") } } + // all status conditions from conditions to cluster operator + clusterOperator.Status.Conditions = cs.entries() + reported := Reported{LastReportTime: metav1.Time{Time: c.LastReportedTime()}} if data, err := json.Marshal(reported); err != nil { klog.Errorf("Unable to marshal status extension: %v", err) @@ -199,6 +213,34 @@ func (c *Controller) merge(clusterOperator *configv1.ClusterOperator) *configv1. return clusterOperator } +// shouldSetProgressingCondition checks if the openshift version was changed and decides whether we should +// switch the Progressing condition to true or not. We should do that only if the major or minor version +// is changed and ignore the patch version. +func shouldSetProgressingCondition(newVersion string, clusterOperatorVersions []configv1.OperandVersion) (bool, error) { + newVersionParsed, err := semver.Parse(newVersion) + if err != nil { + return false, err + } + + // Skip initial run, the condition is set there + if len(clusterOperatorVersions) == 0 { + return false, nil + } + + for _, cov := range clusterOperatorVersions { + covParsed, err := semver.Parse(cov.Version) + if err != nil { + return false, err + } + + // Change Progressing condition only on major or minor version update + if newVersionParsed.Major != covParsed.Major || newVersionParsed.Minor != covParsed.Minor { + return true, nil + } + } + return false, nil +} + // calculate the current controller status based on its given sources func (c *Controller) currentControllerStatus() (allReady bool) { //nolint: gocyclo var errorReason string diff --git a/pkg/controller/status/controller_test.go b/pkg/controller/status/controller_test.go index ec19761ac..adfd9253b 100644 --- a/pkg/controller/status/controller_test.go +++ b/pkg/controller/status/controller_test.go @@ -253,6 +253,75 @@ func Test_updatingConditionsFromDegradedToDisabled(t *testing.T) { assert.Equal(t, disabledCondition, getConditionByType(updatedCO.Status.Conditions, OperatorDisabled)) } +func Test_shouldSetProgressingCondition(t *testing.T) { + tests := []struct { + name string + newVersion string + clusterOperatorVersions []configv1.OperandVersion + expectedShouldUpdate bool + expectError bool + }{ + { + name: "Invalid new version returns error", + newVersion: "invalid-version", + clusterOperatorVersions: []configv1.OperandVersion{{Name: "operator", Version: "4.21.0-0.nightly-2026-01-07-204315"}}, + expectedShouldUpdate: false, + expectError: true, + }, + { + name: "Empty clusterOperatorVersions returns false", + newVersion: "4.21.0-0.nightly-2026-01-07-204315", + clusterOperatorVersions: []configv1.OperandVersion{}, + expectedShouldUpdate: false, + expectError: false, + }, + { + name: "Major version change triggers update", + newVersion: "5.21.0-0.nightly-2026-01-07-204315", + clusterOperatorVersions: []configv1.OperandVersion{{Name: "operator", Version: "4.21.0-0.nightly-2026-01-07-204315"}}, + expectedShouldUpdate: true, + expectError: false, + }, + { + name: "Minor version change triggers update", + newVersion: "4.22.0-0.nightly-2026-01-07-204315", + clusterOperatorVersions: []configv1.OperandVersion{{Name: "operator", Version: "4.21.0-0.nightly-2026-01-07-204315"}}, + expectedShouldUpdate: true, + expectError: false, + }, + { + name: "Patch version change does not trigger update", + newVersion: "4.21.1-0.nightly-2026-01-07-204315", + clusterOperatorVersions: []configv1.OperandVersion{{Name: "operator", Version: "4.21.0-0.nightly-2026-01-07-204315"}}, + expectedShouldUpdate: false, + expectError: false, + }, + { + name: "Invalid existing version returns error", + newVersion: "4.21.0-0.nightly-2026-01-07-204315", + clusterOperatorVersions: []configv1.OperandVersion{{Name: "operator", Version: "invalid"}}, + expectedShouldUpdate: false, + expectError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + shouldUpdate, err := shouldSetProgressingCondition(tt.newVersion, tt.clusterOperatorVersions) + + if tt.expectError { + assert.Error(t, err, "Expected an error but got nil") + } else { + assert.NoError(t, err, "Expected no error but got: %v", err) + } + + assert.Equal(t, tt.expectedShouldUpdate, shouldUpdate, + "shouldUpdateVersion(%q, %v) = %v, want %v", + tt.newVersion, tt.clusterOperatorVersions, shouldUpdate, tt.expectedShouldUpdate) + }) + } +} + func getConditionByType(conditions []configv1.ClusterOperatorStatusCondition, ctype configv1.ClusterStatusConditionType, ) *configv1.ClusterOperatorStatusCondition {