@@ -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,10 @@ 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+ for _ , risk := range config .Spec .DesiredUpdate .AcceptRisks {
57+ acceptRisks .Insert (risk .Name )
58+ }
5359
5460 // updates are only checked at most once per minimumUpdateCheckInterval or if the generation changes
5561 optrAvailableUpdates := optr .getAvailableUpdates ()
@@ -129,6 +135,8 @@ func (optr *Operator) syncAvailableUpdates(ctx context.Context, config *configv1
129135 optrAvailableUpdates .UpdateService = updateService
130136 optrAvailableUpdates .Channel = channel
131137 optrAvailableUpdates .Architecture = desiredArch
138+ optrAvailableUpdates .ShouldReconcileAcceptRisks = optr .shouldReconcileAcceptRisks
139+ optrAvailableUpdates .AcceptRisks = acceptRisks
132140 optrAvailableUpdates .ConditionRegistry = optr .conditionRegistry
133141 optrAvailableUpdates .Condition = condition
134142
@@ -167,9 +175,11 @@ func (optr *Operator) syncAvailableUpdates(ctx context.Context, config *configv1
167175}
168176
169177type availableUpdates struct {
170- UpdateService string
171- Channel string
172- Architecture string
178+ UpdateService string
179+ Channel string
180+ Architecture string
181+ ShouldReconcileAcceptRisks func () bool
182+ AcceptRisks sets.Set [string ]
173183
174184 // LastAttempt records the time of the most recent attempt at update
175185 // retrieval, regardless of whether it was successful.
@@ -192,6 +202,8 @@ type availableUpdates struct {
192202 ConditionRegistry clusterconditions.ConditionRegistry
193203
194204 Condition configv1.ClusterOperatorStatusCondition
205+
206+ RiskConditions map [string ][]metav1.Condition
195207}
196208
197209func (u * availableUpdates ) RecentlyAttempted (interval time.Duration ) bool {
@@ -291,14 +303,16 @@ func (optr *Operator) getAvailableUpdates() *availableUpdates {
291303 }
292304
293305 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 ,
306+ UpdateService : optr .availableUpdates .UpdateService ,
307+ Channel : optr .availableUpdates .Channel ,
308+ Architecture : optr .availableUpdates .Architecture ,
309+ ShouldReconcileAcceptRisks : optr .shouldReconcileAcceptRisks ,
310+ AcceptRisks : optr .availableUpdates .AcceptRisks ,
311+ LastAttempt : optr .availableUpdates .LastAttempt ,
312+ LastSyncOrConfigChange : optr .availableUpdates .LastSyncOrConfigChange ,
313+ Current : * optr .availableUpdates .Current .DeepCopy (),
314+ ConditionRegistry : optr .availableUpdates .ConditionRegistry , // intentionally not a copy, to preserve cache state
315+ Condition : optr .availableUpdates .Condition ,
302316 }
303317
304318 if optr .availableUpdates .Updates != nil {
@@ -427,7 +441,7 @@ func (u *availableUpdates) evaluateConditionalUpdates(ctx context.Context) {
427441 return vi .GTE (vj )
428442 })
429443 for i , conditionalUpdate := range u .ConditionalUpdates {
430- condition := evaluateConditionalUpdate (ctx , conditionalUpdate .Risks , u .ConditionRegistry )
444+ condition := evaluateConditionalUpdate (ctx , conditionalUpdate .Risks , u .ConditionRegistry , u . AcceptRisks , u . ShouldReconcileAcceptRisks , u . RiskConditions )
431445
432446 if condition .Status == metav1 .ConditionTrue {
433447 u .addUpdate (conditionalUpdate .Release )
@@ -485,6 +499,8 @@ const (
485499 // recommendedReasonExposed is used instead of the original name if it does
486500 // not match the pattern for a valid k8s condition reason.
487501 recommendedReasonExposed = "ExposedToRisks"
502+
503+ riskConditionReasonEvaluationFailed = "EvaluationFailed"
488504)
489505
490506// Reasons follow same pattern as k8s Condition Reasons
@@ -502,29 +518,57 @@ func newRecommendedReason(now, want string) string {
502518 }
503519}
504520
505- func evaluateConditionalUpdate (ctx context.Context , risks []configv1.ConditionalUpdateRisk , conditionRegistry clusterconditions.ConditionRegistry ) metav1.Condition {
521+ func evaluateConditionalUpdate (
522+ ctx context.Context ,
523+ risks []configv1.ConditionalUpdateRisk ,
524+ conditionRegistry clusterconditions.ConditionRegistry ,
525+ acceptRisks sets.Set [string ],
526+ shouldReconcileAcceptRisks func () bool ,
527+ riskConditions map [string ][]metav1.Condition ,
528+ ) metav1.Condition {
506529 recommended := metav1.Condition {
507- Type : ConditionalUpdateConditionTypeRecommended ,
530+ Type : internal . ConditionalUpdateConditionTypeRecommended ,
508531 Status : metav1 .ConditionTrue ,
509532 // FIXME: ObservedGeneration? That would capture upstream/channel, but not necessarily the currently-reconciling version.
510533 Reason : recommendedReasonRisksNotExposed ,
511534 Message : "The update is recommended, because none of the conditional update risks apply to this cluster." ,
512535 }
513536
514537 var errorMessages []string
515- for _ , risk := range risks {
538+ for i , risk := range risks {
539+ riskCondition := metav1.Condition {
540+ Type : internal .ConditionalUpdateRiskConditionTypeApplies ,
541+ Status : metav1 .ConditionFalse ,
542+ LastTransitionTime : metav1 .Now (),
543+ }
516544 if match , err := conditionRegistry .Match (ctx , risk .MatchingRules ); err != nil {
545+ msg := unknownExposureMessage (risk , err )
517546 recommended .Status = newRecommendedStatus (recommended .Status , metav1 .ConditionUnknown )
518547 recommended .Reason = newRecommendedReason (recommended .Reason , recommendedReasonEvaluationFailed )
519- errorMessages = append (errorMessages , unknownExposureMessage (risk , err ))
548+ errorMessages = append (errorMessages , msg )
549+ riskCondition .Status = metav1 .ConditionUnknown
550+ riskCondition .Reason = riskConditionReasonEvaluationFailed
551+ riskCondition .Message = msg
520552 } else if match {
521- recommended .Status = newRecommendedStatus (recommended .Status , metav1 .ConditionFalse )
522- wantReason := recommendedReasonExposed
523- if reasonPattern .MatchString (risk .Name ) {
524- wantReason = risk .Name
553+ riskCondition .Status = metav1 .ConditionTrue
554+ riskCondition .Message = fmt .Sprintf ("The matchingRules[%d] matches" , i )
555+ if shouldReconcileAcceptRisks () && acceptRisks .Has (risk .Name ) {
556+ 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 )
557+ } else {
558+ recommended .Status = newRecommendedStatus (recommended .Status , metav1 .ConditionFalse )
559+ wantReason := recommendedReasonExposed
560+ if reasonPattern .MatchString (risk .Name ) {
561+ wantReason = risk .Name
562+ }
563+ recommended .Reason = newRecommendedReason (recommended .Reason , wantReason )
564+ errorMessages = append (errorMessages , fmt .Sprintf ("%s %s" , risk .Message , risk .URL ))
525565 }
526- recommended .Reason = newRecommendedReason (recommended .Reason , wantReason )
527- errorMessages = append (errorMessages , fmt .Sprintf ("%s %s" , risk .Message , risk .URL ))
566+ }
567+ if riskConditions == nil {
568+ riskConditions = map [string ][]metav1.Condition {}
569+ }
570+ if _ , ok := riskConditions [risk .Name ]; ! ok {
571+ riskConditions [risk .Name ] = []metav1.Condition {riskCondition }
528572 }
529573 }
530574 if len (errorMessages ) > 0 {
0 commit comments