Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions pkg/cvo/availableupdates_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"k8s.io/client-go/util/workqueue"

configv1 "github.com/openshift/api/config/v1"
"github.com/openshift/api/features"

"github.com/openshift/cluster-version-operator/pkg/clusterconditions"
"github.com/openshift/cluster-version-operator/pkg/clusterconditions/always"
Expand Down Expand Up @@ -1175,3 +1176,86 @@ func Test_evaluateConditionalUpdates(t *testing.T) {
})
}
}

// The setup is to satisfy needsConditionalUpdateEval == true and
// needFreshFetch == false in the targeting function optr.syncAvailableUpdates
// so that we simulate the scenario where the operator re-evaluates conditional updates
// without fetching the updates from the upstream.
func TestOperator_syncAvailableUpdates_noticeResolvedAlertsQuickly(t *testing.T) {
now := time.Now()
optr := &Operator{
queue: workqueue.NewTypedRateLimitingQueue[any](workqueue.DefaultTypedControllerRateLimiter[any]()),
availableUpdates: &availableUpdates{
LastSyncOrConfigChange: now,
LastAttempt: now,
AcceptRisks: sets.New[string]("RiskA"),
Architecture: "amd64",
upstreamUpdates: []configv1.Release{
{
Version: "4.21.2",
},
},
// it becomes conditional because a firing alert
ConditionalUpdates: []configv1.ConditionalUpdate{
{
Release: configv1.Release{
Version: "4.21.2",
},
Conditions: []metav1.Condition{
{
Type: "Recommended",
Status: "False",
},
},
Risks: []configv1.ConditionalUpdateRisk{
{
Name: "TestAlert",
Conditions: []metav1.Condition{},
},
},
},
},
Comment thread
hongkailiu marked this conversation as resolved.
},
}
optr.minimumUpdateCheckInterval = 10 * time.Minute
cvgGates := featuregates.CvoGatesFromFeatureGate(&configv1.FeatureGate{
Status: configv1.FeatureGateStatus{
FeatureGates: []configv1.FeatureGateDetails{
{
Enabled: []configv1.FeatureGateAttributes{
{
Name: features.FeatureGateClusterUpdateAcceptRisks,
},
},
},
},
},
}, "")
if !cvgGates.AcceptRisks() {
t.Fatalf("accept risk feature is not enabled")
}
optr.enabledCVOFeatureGates = cvgGates
err := optr.syncAvailableUpdates(context.Background(), &configv1.ClusterVersion{
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

syncAvailableUpdates calls evaluateConditionalUpdates which has some logic to process Risks if the risks property is non-nil. To simulate the production situation more closely, you might want to use the mock risk Source like this (but without setting InternalRisks) to more closely simulate having an alert risk Source that detects no risks.

Also fine with me if you think the nil-risks situation you're already working is close enough to production to confirm this works as expected.

Spec: configv1.ClusterVersionSpec{
DesiredUpdate: &configv1.Update{
Architecture: "amd64",
},
},
})
if err != nil {
t.Fatalf("failed to sync available updates: %v", err)
}

// The conditional update is back to be an available update after evaluation.
// There was no available updates before the sync call.
Comment thread
hongkailiu marked this conversation as resolved.
expected := &availableUpdates{
Architecture: "amd64",
Updates: []configv1.Release{{
Version: "4.21.2",
}},
}

if diff := cmp.Diff(expected, optr.availableUpdates, availableUpdatesCmpOpts...); diff != "" {
t.Errorf("syncAvailableUpdates mismatch (-want +got):\n%s", diff)
}
}