github.com/verrazzano/verrazzano@v1.7.0/application-operator/controllers/reconcileresults/reconcile_results.go (about) 1 // Copyright (c) 2020, 2022, Oracle and/or its affiliates. 2 // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 3 4 package reconcileresults 5 6 import ( 7 "reflect" 8 9 oamrt "github.com/crossplane/crossplane-runtime/apis/common/v1" 10 "github.com/verrazzano/verrazzano/application-operator/apis/oam/v1alpha1" 11 apierrors "k8s.io/apimachinery/pkg/api/errors" 12 "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" 13 ) 14 15 // ReconcileResults is used to collect the results of creating or updating child resources during reconciliation. 16 // The contained arrays are parallel arrays 17 type ReconcileResults struct { 18 Relations []v1alpha1.QualifiedResourceRelation 19 Results []controllerutil.OperationResult 20 Errors []error 21 } 22 23 // ContainsErrors scans the errors to determine if any errors were recorded. 24 func (s *ReconcileResults) ContainsErrors() bool { 25 for _, err := range s.Errors { 26 if err != nil { 27 return true 28 } 29 } 30 return false 31 } 32 33 // ContainsUpdates scans the updates to determine if any updates were recorded. 34 func (s *ReconcileResults) ContainsUpdates() bool { 35 for _, result := range s.Results { 36 if result != controllerutil.OperationResultNone { 37 return true 38 } 39 } 40 return false 41 } 42 43 // ContainsRelation determines if the reconcile result contains the provided relation. 44 func (s *ReconcileResults) ContainsRelation(relation v1alpha1.QualifiedResourceRelation) bool { 45 for _, existing := range s.Relations { 46 if reflect.DeepEqual(relation, existing) { 47 return true 48 } 49 } 50 return false 51 } 52 53 // CreateConditionedStatus creates conditioned status for use in object status. 54 // If no errors are found in the reconcile status a success condition is returned. 55 // Otherwise reconcile errors statuses are returned for the first error. 56 func (s *ReconcileResults) CreateConditionedStatus() oamrt.ConditionedStatus { 57 // Return the first error if there are any. 58 for _, err := range s.Errors { 59 if err != nil { 60 return oamrt.ConditionedStatus{Conditions: []oamrt.Condition{oamrt.ReconcileError(err)}} 61 } 62 } 63 // If no errors are found then return success. 64 return oamrt.ConditionedStatus{Conditions: []oamrt.Condition{oamrt.ReconcileSuccess()}} 65 } 66 67 // CreateResources creates a typed reference slice for use in an object status. 68 func (s *ReconcileResults) CreateResources() []oamrt.TypedReference { 69 resources := []oamrt.TypedReference{} 70 for _, relation := range s.Relations { 71 resources = append(resources, oamrt.TypedReference{ 72 APIVersion: relation.APIVersion, 73 Kind: relation.Kind, 74 Name: relation.Name, 75 }) 76 } 77 return resources 78 } 79 80 // CreateRelations creates a qualified resource relation slice for use in an object status. 81 func (s *ReconcileResults) CreateRelations() []v1alpha1.QualifiedResourceRelation { 82 // Copies the slice. 83 return append([]v1alpha1.QualifiedResourceRelation{}, s.Relations...) 84 } 85 86 // RecordOutcome records the outcome of an operation during a reconcile. 87 func (s *ReconcileResults) RecordOutcome(rel v1alpha1.QualifiedResourceRelation, res controllerutil.OperationResult, err error) { 88 // Don't record the inability to update a deleted resource as an error. 89 if err == nil || !apierrors.IsNotFound(err) { 90 s.Relations = append(s.Relations, rel) 91 s.Results = append(s.Results, res) 92 s.Errors = append(s.Errors, err) 93 } 94 } 95 96 // RecordOutcomeIfError records the outcome of an operation during a reconcile only the err is non-nil. 97 func (s *ReconcileResults) RecordOutcomeIfError(rel v1alpha1.QualifiedResourceRelation, res controllerutil.OperationResult, err error) { 98 // Don't record the inability to update a deleted resource as an error. 99 if err != nil && !apierrors.IsNotFound(err) { 100 s.Relations = append(s.Relations, rel) 101 s.Results = append(s.Results, res) 102 s.Errors = append(s.Errors, err) 103 } 104 } 105 106 // ConditionsEquivalent determines if two conditions are equivalent. 107 // The type, status, reason and message are compared to determine equivalence. 108 func ConditionsEquivalent(left *oamrt.Condition, right *oamrt.Condition) bool { 109 if left == nil && right == nil { 110 return true 111 } 112 if left == nil || right == nil { 113 return false 114 } 115 if left.Type != right.Type || left.Status != right.Status || left.Reason != right.Reason || left.Message != right.Message { 116 return false 117 } 118 return true 119 } 120 121 // ConditionedStatusEquivalent determines if two conditioned status are equivalent. 122 // This is done by searching for all of the conditions from the left in the right. 123 // Then the conditions in the right are searched for in the left. 124 // False is returned at any point when a condition cannot be found. 125 // True is returned if all conditions in the left can be found in the right and vice versa. 126 func ConditionedStatusEquivalent(left *oamrt.ConditionedStatus, right *oamrt.ConditionedStatus) bool { 127 if left == nil && right == nil { 128 return true 129 } 130 if left == nil || right == nil { 131 return false 132 } 133 if len(left.Conditions) != len(right.Conditions) { 134 return false 135 } 136 for i := range left.Conditions { 137 if !ConditionsEquivalent(&left.Conditions[i], &right.Conditions[i]) { 138 return false 139 } 140 } 141 return true 142 }