@@ -16,12 +16,14 @@ import (
1616 "k8s.io/apimachinery/pkg/api/equality"
1717 "k8s.io/apimachinery/pkg/api/meta"
1818 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
19+ "k8s.io/apimachinery/pkg/util/sets"
1920 "k8s.io/klog/v2"
2021
2122 configv1 "github.com/openshift/api/config/v1"
2223 "github.com/openshift/cluster-version-operator/lib/resourcemerge"
2324 "github.com/openshift/cluster-version-operator/pkg/cincinnati"
2425 "github.com/openshift/cluster-version-operator/pkg/clusterconditions"
26+ "github.com/openshift/cluster-version-operator/pkg/internal"
2527)
2628
2729const noArchitecture string = "NoArchitecture"
@@ -50,6 +52,12 @@ func (optr *Operator) syncAvailableUpdates(ctx context.Context, config *configv1
5052 channel := config .Spec .Channel
5153 desiredArch := optr .getDesiredArchitecture (config .Spec .DesiredUpdate )
5254 currentArch := optr .getCurrentArchitecture ()
55+ acceptRisks := sets .New [string ]()
56+ if config .Spec .DesiredUpdate != nil {
57+ for _ , risk := range config .Spec .DesiredUpdate .AcceptRisks {
58+ acceptRisks .Insert (risk .Name )
59+ }
60+ }
5361
5462 // updates are only checked at most once per minimumUpdateCheckInterval or if the generation changes
5563 optrAvailableUpdates := optr .getAvailableUpdates ()
@@ -129,6 +137,8 @@ func (optr *Operator) syncAvailableUpdates(ctx context.Context, config *configv1
129137 optrAvailableUpdates .UpdateService = updateService
130138 optrAvailableUpdates .Channel = channel
131139 optrAvailableUpdates .Architecture = desiredArch
140+ optrAvailableUpdates .ShouldReconcileAcceptRisks = optr .shouldReconcileAcceptRisks
141+ optrAvailableUpdates .AcceptRisks = acceptRisks
132142 optrAvailableUpdates .ConditionRegistry = optr .conditionRegistry
133143 optrAvailableUpdates .Condition = condition
134144
@@ -167,9 +177,11 @@ func (optr *Operator) syncAvailableUpdates(ctx context.Context, config *configv1
167177}
168178
169179type availableUpdates struct {
170- UpdateService string
171- Channel string
172- Architecture string
180+ UpdateService string
181+ Channel string
182+ Architecture string
183+ ShouldReconcileAcceptRisks func () bool
184+ AcceptRisks sets.Set [string ]
173185
174186 // LastAttempt records the time of the most recent attempt at update
175187 // retrieval, regardless of whether it was successful.
@@ -192,6 +204,8 @@ type availableUpdates struct {
192204 ConditionRegistry clusterconditions.ConditionRegistry
193205
194206 Condition configv1.ClusterOperatorStatusCondition
207+
208+ RiskConditions map [string ][]metav1.Condition
195209}
196210
197211func (u * availableUpdates ) RecentlyAttempted (interval time.Duration ) bool {
@@ -291,14 +305,16 @@ func (optr *Operator) getAvailableUpdates() *availableUpdates {
291305 }
292306
293307 u := & availableUpdates {
294- UpdateService : optr .availableUpdates .UpdateService ,
295- Channel : optr .availableUpdates .Channel ,
296- Architecture : optr .availableUpdates .Architecture ,
297- LastAttempt : optr .availableUpdates .LastAttempt ,
298- LastSyncOrConfigChange : optr .availableUpdates .LastSyncOrConfigChange ,
299- Current : * optr .availableUpdates .Current .DeepCopy (),
300- ConditionRegistry : optr .availableUpdates .ConditionRegistry , // intentionally not a copy, to preserve cache state
301- Condition : optr .availableUpdates .Condition ,
308+ UpdateService : optr .availableUpdates .UpdateService ,
309+ Channel : optr .availableUpdates .Channel ,
310+ Architecture : optr .availableUpdates .Architecture ,
311+ ShouldReconcileAcceptRisks : optr .shouldReconcileAcceptRisks ,
312+ AcceptRisks : optr .availableUpdates .AcceptRisks ,
313+ LastAttempt : optr .availableUpdates .LastAttempt ,
314+ LastSyncOrConfigChange : optr .availableUpdates .LastSyncOrConfigChange ,
315+ Current : * optr .availableUpdates .Current .DeepCopy (),
316+ ConditionRegistry : optr .availableUpdates .ConditionRegistry , // intentionally not a copy, to preserve cache state
317+ Condition : optr .availableUpdates .Condition ,
302318 }
303319
304320 if optr .availableUpdates .Updates != nil {
@@ -427,7 +443,7 @@ func (u *availableUpdates) evaluateConditionalUpdates(ctx context.Context) {
427443 return vi .GTE (vj )
428444 })
429445 for i , conditionalUpdate := range u .ConditionalUpdates {
430- condition := evaluateConditionalUpdate (ctx , conditionalUpdate .Risks , u .ConditionRegistry )
446+ condition := evaluateConditionalUpdate (ctx , conditionalUpdate .Risks , u .ConditionRegistry , u . AcceptRisks , u . ShouldReconcileAcceptRisks , u . RiskConditions )
431447
432448 if condition .Status == metav1 .ConditionTrue {
433449 u .addUpdate (conditionalUpdate .Release )
@@ -485,6 +501,8 @@ const (
485501 // recommendedReasonExposed is used instead of the original name if it does
486502 // not match the pattern for a valid k8s condition reason.
487503 recommendedReasonExposed = "ExposedToRisks"
504+
505+ riskConditionReasonEvaluationFailed = "EvaluationFailed"
488506)
489507
490508// Reasons follow same pattern as k8s Condition Reasons
@@ -502,29 +520,57 @@ func newRecommendedReason(now, want string) string {
502520 }
503521}
504522
505- func evaluateConditionalUpdate (ctx context.Context , risks []configv1.ConditionalUpdateRisk , conditionRegistry clusterconditions.ConditionRegistry ) metav1.Condition {
523+ func evaluateConditionalUpdate (
524+ ctx context.Context ,
525+ risks []configv1.ConditionalUpdateRisk ,
526+ conditionRegistry clusterconditions.ConditionRegistry ,
527+ acceptRisks sets.Set [string ],
528+ shouldReconcileAcceptRisks func () bool ,
529+ riskConditions map [string ][]metav1.Condition ,
530+ ) metav1.Condition {
506531 recommended := metav1.Condition {
507- Type : ConditionalUpdateConditionTypeRecommended ,
532+ Type : internal . ConditionalUpdateConditionTypeRecommended ,
508533 Status : metav1 .ConditionTrue ,
509534 // FIXME: ObservedGeneration? That would capture upstream/channel, but not necessarily the currently-reconciling version.
510535 Reason : recommendedReasonRisksNotExposed ,
511536 Message : "The update is recommended, because none of the conditional update risks apply to this cluster." ,
512537 }
513538
514539 var errorMessages []string
515- for _ , risk := range risks {
540+ for i , risk := range risks {
541+ riskCondition := metav1.Condition {
542+ Type : internal .ConditionalUpdateRiskConditionTypeApplies ,
543+ Status : metav1 .ConditionFalse ,
544+ LastTransitionTime : metav1 .Now (),
545+ }
516546 if match , err := conditionRegistry .Match (ctx , risk .MatchingRules ); err != nil {
547+ msg := unknownExposureMessage (risk , err )
517548 recommended .Status = newRecommendedStatus (recommended .Status , metav1 .ConditionUnknown )
518549 recommended .Reason = newRecommendedReason (recommended .Reason , recommendedReasonEvaluationFailed )
519- errorMessages = append (errorMessages , unknownExposureMessage (risk , err ))
550+ errorMessages = append (errorMessages , msg )
551+ riskCondition .Status = metav1 .ConditionUnknown
552+ riskCondition .Reason = riskConditionReasonEvaluationFailed
553+ riskCondition .Message = msg
520554 } else if match {
521- recommended .Status = newRecommendedStatus (recommended .Status , metav1 .ConditionFalse )
522- wantReason := recommendedReasonExposed
523- if reasonPattern .MatchString (risk .Name ) {
524- wantReason = risk .Name
555+ riskCondition .Status = metav1 .ConditionTrue
556+ riskCondition .Message = fmt .Sprintf ("The matchingRules[%d] matches" , i )
557+ if shouldReconcileAcceptRisks () && acceptRisks .Has (risk .Name ) {
558+ klog .V (2 ).Infof ("Risk with name %q is accepted by the cluster admin and thus not in the evaluation of conditional update" , risk .Name )
559+ } else {
560+ recommended .Status = newRecommendedStatus (recommended .Status , metav1 .ConditionFalse )
561+ wantReason := recommendedReasonExposed
562+ if reasonPattern .MatchString (risk .Name ) {
563+ wantReason = risk .Name
564+ }
565+ recommended .Reason = newRecommendedReason (recommended .Reason , wantReason )
566+ errorMessages = append (errorMessages , fmt .Sprintf ("%s %s" , risk .Message , risk .URL ))
525567 }
526- recommended .Reason = newRecommendedReason (recommended .Reason , wantReason )
527- errorMessages = append (errorMessages , fmt .Sprintf ("%s %s" , risk .Message , risk .URL ))
568+ }
569+ if riskConditions == nil {
570+ riskConditions = map [string ][]metav1.Condition {}
571+ }
572+ if _ , ok := riskConditions [risk .Name ]; ! ok {
573+ riskConditions [risk .Name ] = []metav1.Condition {riskCondition }
528574 }
529575 }
530576 if len (errorMessages ) > 0 {
0 commit comments