@@ -144,7 +144,6 @@ func (optr *Operator) syncAvailableUpdates(ctx context.Context, config *configv1
144144 optrAvailableUpdates .Architecture = desiredArch
145145 optrAvailableUpdates .ShouldReconcileAcceptRisks = optr .shouldReconcileAcceptRisks
146146 optrAvailableUpdates .AcceptRisks = acceptRisks
147- optrAvailableUpdates .RiskConditions = map [string ][]metav1.Condition {}
148147 optrAvailableUpdates .ConditionRegistry = optr .conditionRegistry
149148 optrAvailableUpdates .Condition = condition
150149
@@ -342,6 +341,76 @@ func (optr *Operator) getAvailableUpdates() *availableUpdates {
342341 return u
343342}
344343
344+ func loadRiskConditions (ctx context.Context , risks []string , riskVersions map [string ]riskWithVersion , conditionRegistry clusterconditions.ConditionRegistry ) map [string ][]metav1.Condition {
345+ riskConditions := map [string ][]metav1.Condition {}
346+ for _ , riskName := range risks {
347+ risk := riskVersions [riskName ].risk
348+ riskCondition := metav1.Condition {
349+ Type : internal .ConditionalUpdateRiskConditionTypeApplies ,
350+ Status : metav1 .ConditionFalse ,
351+ Reason : riskConditionReasonNotMatch ,
352+ }
353+ if match , err := conditionRegistry .Match (ctx , risk .MatchingRules ); err != nil {
354+ msg := unknownExposureMessage (risk , err )
355+ riskCondition .Status = metav1 .ConditionUnknown
356+ riskCondition .Reason = riskConditionReasonEvaluationFailed
357+ riskCondition .Message = msg
358+ } else if match {
359+ riskCondition .Status = metav1 .ConditionTrue
360+ riskCondition .Reason = riskConditionReasonMatch
361+ }
362+ riskConditions [risk .Name ] = []metav1.Condition {riskCondition }
363+ }
364+ if len (riskConditions ) == 0 {
365+ return nil
366+ }
367+ return riskConditions
368+ }
369+
370+ // risksInOrder returns the list of risk names sorted by the associated version in descending order.
371+ // When versions match, it is sorted by the risk names in ascending order.
372+ func risksInOrder (riskVersions map [string ]riskWithVersion ) []string {
373+ var ret []string
374+ var temp []riskWithVersion
375+ for k := range riskVersions {
376+ temp = append (temp , riskVersions [k ])
377+ }
378+ sort .SliceStable (temp , func (i , j int ) bool {
379+ return temp [i ].version .GT (temp [j ].version ) || temp [i ].version .EQ (temp [j ].version ) && temp [i ].risk .Name < temp [j ].risk .Name
380+ })
381+ for _ , v := range temp {
382+ ret = append (ret , v .risk .Name )
383+ }
384+ return ret
385+ }
386+
387+ type riskWithVersion struct {
388+ version semver.Version
389+ risk configv1.ConditionalUpdateRisk
390+ }
391+
392+ // loadRiskVersions returns the map from risk names to riskWithVersion where
393+ // riskWithVersion.version is the latest version that the risk is exposed to
394+ // and riskWithVersion.risk is the risk itself
395+ func loadRiskVersions (conditionalUpdates []configv1.ConditionalUpdate ) map [string ]riskWithVersion {
396+ riskVersions := map [string ]riskWithVersion {}
397+ for _ , conditionalUpdate := range conditionalUpdates {
398+ for _ , risk := range conditionalUpdate .Risks {
399+ candidate := semver .MustParse (conditionalUpdate .Release .Version )
400+ if v , ok := riskVersions [risk .Name ]; ! ok || candidate .GT (v .version ) {
401+ riskVersions [risk .Name ] = riskWithVersion {
402+ version : candidate ,
403+ risk : risk ,
404+ }
405+ }
406+ }
407+ }
408+ if len (riskVersions ) == 0 {
409+ return nil
410+ }
411+ return riskVersions
412+ }
413+
345414func (optr * Operator ) getDesiredArchitecture (update * configv1.Update ) string {
346415 if update != nil && len (update .Architecture ) > 0 {
347416 return string (update .Architecture )
@@ -445,16 +514,15 @@ func (u *availableUpdates) evaluateConditionalUpdates(ctx context.Context) {
445514 return
446515 }
447516
448- sort .Slice (u .ConditionalUpdates , func (i , j int ) bool {
449- vi := semver .MustParse (u .ConditionalUpdates [i ].Release .Version )
450- vj := semver .MustParse (u .ConditionalUpdates [j ].Release .Version )
451- return vi .GTE (vj )
452- })
517+ riskVersions := loadRiskVersions (u .ConditionalUpdates )
518+ risks := risksInOrder (riskVersions )
519+ u .RiskConditions = loadRiskConditions (ctx , risks , riskVersions , u .ConditionRegistry )
520+
453521 if err := sanityCheck (u .ConditionalUpdates ); err != nil {
454522 klog .Errorf ("Sanity check failed which might impact risk evaluation: %v" , err )
455523 }
456524 for i , conditionalUpdate := range u .ConditionalUpdates {
457- condition := evaluateConditionalUpdate (ctx , conditionalUpdate .Risks , u . ConditionRegistry , u .AcceptRisks , u .ShouldReconcileAcceptRisks , u .RiskConditions )
525+ condition := evaluateConditionalUpdate (conditionalUpdate .Risks , u .AcceptRisks , u .ShouldReconcileAcceptRisks , u .RiskConditions )
458526
459527 if condition .Status == metav1 .ConditionTrue {
460528 u .addUpdate (conditionalUpdate .Release )
@@ -556,9 +624,7 @@ func newRecommendedReason(now, want string) string {
556624}
557625
558626func evaluateConditionalUpdate (
559- ctx context.Context ,
560627 risks []configv1.ConditionalUpdateRisk ,
561- conditionRegistry clusterconditions.ConditionRegistry ,
562628 acceptRisks sets.Set [string ],
563629 shouldReconcileAcceptRisks func () bool ,
564630 riskConditions map [string ][]metav1.Condition ,
@@ -573,22 +639,21 @@ func evaluateConditionalUpdate(
573639
574640 var errorMessages []string
575641 for _ , risk := range risks {
576- riskCondition := metav1.Condition {
577- Type : internal .ConditionalUpdateRiskConditionTypeApplies ,
578- Status : metav1 .ConditionFalse ,
579- Reason : riskConditionReasonNotMatch ,
642+ riskCondition := meta .FindStatusCondition (riskConditions [risk .Name ], internal .ConditionalUpdateRiskConditionTypeApplies )
643+ if riskCondition == nil {
644+ // This should never happen
645+ riskCondition = & metav1.Condition {
646+ Status : metav1 .ConditionUnknown ,
647+ Reason : internal .ConditionalUpdateRiskConditionReasonInternalErrorFoundNoRiskCondition ,
648+ Message : fmt .Sprintf ("failed to find risk condition for risk %s" , risk .Name ),
649+ }
580650 }
581- if match , err := conditionRegistry . Match ( ctx , risk . MatchingRules ); err != nil {
582- msg := unknownExposureMessage ( risk , err )
651+ switch riskCondition . Status {
652+ case metav1 . ConditionUnknown :
583653 recommended .Status = newRecommendedStatus (recommended .Status , metav1 .ConditionUnknown )
584654 recommended .Reason = newRecommendedReason (recommended .Reason , recommendedReasonEvaluationFailed )
585- errorMessages = append (errorMessages , msg )
586- riskCondition .Status = metav1 .ConditionUnknown
587- riskCondition .Reason = riskConditionReasonEvaluationFailed
588- riskCondition .Message = msg
589- } else if match {
590- riskCondition .Status = metav1 .ConditionTrue
591- riskCondition .Reason = riskConditionReasonMatch
655+ errorMessages = append (errorMessages , riskCondition .Message )
656+ case metav1 .ConditionTrue :
592657 if shouldReconcileAcceptRisks () && acceptRisks .Has (risk .Name ) {
593658 recommended .Status = newRecommendedStatus (recommended .Status , metav1 .ConditionTrue )
594659 recommended .Reason = newRecommendedReason (recommended .Reason , recommendedReasonAllExposedRisksAccepted )
@@ -604,9 +669,6 @@ func evaluateConditionalUpdate(
604669 errorMessages = append (errorMessages , fmt .Sprintf ("%s %s" , risk .Message , risk .URL ))
605670 }
606671 }
607- if _ , ok := riskConditions [risk .Name ]; ! ok {
608- riskConditions [risk .Name ] = []metav1.Condition {riskCondition }
609- }
610672 }
611673 if len (errorMessages ) > 0 {
612674 recommended .Message = strings .Join (errorMessages , "\n \n " )
0 commit comments