github.com/redhat-appstudio/release-service@v0.0.0-20240507143925-083712697924/controllers/utils/predicates/predicates.go (about) 1 /* 2 Copyright 2023. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package predicates 18 19 import ( 20 "reflect" 21 22 "github.com/redhat-appstudio/release-service/api/v1alpha1" 23 "github.com/redhat-appstudio/release-service/metadata" 24 "k8s.io/apimachinery/pkg/api/meta" 25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 "sigs.k8s.io/controller-runtime/pkg/client" 27 "sigs.k8s.io/controller-runtime/pkg/event" 28 "sigs.k8s.io/controller-runtime/pkg/predicate" 29 ) 30 31 // MatchPredicate returns a predicate which returns true when a ReleasePlan or ReleasePlanAdmission 32 // is created, deleted, or when the auto-release label, target, application, or the matched 33 // resource of one changes. 34 func MatchPredicate() predicate.Predicate { 35 return predicate.Funcs{ 36 CreateFunc: func(createEvent event.CreateEvent) bool { 37 return true 38 }, 39 DeleteFunc: func(deleteEvent event.DeleteEvent) bool { 40 return true 41 }, 42 GenericFunc: func(genericEvent event.GenericEvent) bool { 43 return false 44 }, 45 UpdateFunc: func(e event.UpdateEvent) bool { 46 return haveApplicationsChanged(e.ObjectOld, e.ObjectNew) || 47 hasAutoReleaseLabelChanged(e.ObjectOld, e.ObjectNew) || 48 hasMatchConditionChanged(e.ObjectOld, e.ObjectNew) || 49 hasSourceChanged(e.ObjectOld, e.ObjectNew) 50 }, 51 } 52 } 53 54 // hasConditionChanged returns true if one, but not both, of the conditions 55 // are nil or if both are not nil and have different lastTransitionTimes. 56 func hasConditionChanged(conditionOld, conditionNew *metav1.Condition) bool { 57 if conditionOld == nil || conditionNew == nil { 58 return conditionOld != conditionNew 59 } 60 // both not nil, check lastTransitionTime for equality 61 return !conditionOld.LastTransitionTime.Equal(&conditionNew.LastTransitionTime) 62 } 63 64 // hasAutoReleaseLabelChanged returns true if the auto-release label value is 65 // different between the two objects. 66 func hasAutoReleaseLabelChanged(objectOld, objectNew client.Object) bool { 67 return objectOld.GetLabels()[metadata.AutoReleaseLabel] != objectNew.GetLabels()[metadata.AutoReleaseLabel] 68 } 69 70 // haveApplicationsChanged returns true if passed objects are of the same kind and the 71 // Spec.Application(s) values between them is different. 72 func haveApplicationsChanged(objectOld, objectNew client.Object) bool { 73 if releasePlanOld, ok := objectOld.(*v1alpha1.ReleasePlan); ok { 74 if releasePlanNew, ok := objectNew.(*v1alpha1.ReleasePlan); ok { 75 return releasePlanOld.Spec.Application != releasePlanNew.Spec.Application 76 } 77 } 78 79 if releasePlanAdmissionOld, ok := objectOld.(*v1alpha1.ReleasePlanAdmission); ok { 80 if releasePlanAdmissionNew, ok := objectNew.(*v1alpha1.ReleasePlanAdmission); ok { 81 return !reflect.DeepEqual( 82 releasePlanAdmissionOld.Spec.Applications, 83 releasePlanAdmissionNew.Spec.Applications, 84 ) 85 } 86 } 87 88 return false 89 } 90 91 // hasMatchConditionChanged returns true if the lastTransitionTime of the Matched condition 92 // is different between the two objects or if one (but not both) of the objects is missing 93 // the Matched condition. 94 func hasMatchConditionChanged(objectOld, objectNew client.Object) bool { 95 if releasePlanOld, ok := objectOld.(*v1alpha1.ReleasePlan); ok { 96 if releasePlanNew, ok := objectNew.(*v1alpha1.ReleasePlan); ok { 97 oldCondition := meta.FindStatusCondition(releasePlanOld.Status.Conditions, 98 v1alpha1.MatchedConditionType.String()) 99 newCondition := meta.FindStatusCondition(releasePlanNew.Status.Conditions, 100 v1alpha1.MatchedConditionType.String()) 101 return hasConditionChanged(oldCondition, newCondition) 102 } 103 } else if releasePlanAdmissionOld, ok := objectOld.(*v1alpha1.ReleasePlanAdmission); ok { 104 if releasePlanAdmissionNew, ok := objectNew.(*v1alpha1.ReleasePlanAdmission); ok { 105 oldCondition := meta.FindStatusCondition(releasePlanAdmissionOld.Status.Conditions, 106 v1alpha1.MatchedConditionType.String()) 107 newCondition := meta.FindStatusCondition(releasePlanAdmissionNew.Status.Conditions, 108 v1alpha1.MatchedConditionType.String()) 109 return hasConditionChanged(oldCondition, newCondition) 110 } 111 } 112 return false 113 } 114 115 // hasSourceChanged returns true if the objects are ReleasePlans and the Spec.Target value is 116 // different between the two objects or if the objects are ReleasePlanAdmissions and the 117 // Spec.Origin value is different between the two. 118 func hasSourceChanged(objectOld, objectNew client.Object) bool { 119 if releasePlanOld, ok := objectOld.(*v1alpha1.ReleasePlan); ok { 120 if releasePlanNew, ok := objectNew.(*v1alpha1.ReleasePlan); ok { 121 return releasePlanOld.Spec.Target != releasePlanNew.Spec.Target 122 } 123 } 124 125 if releasePlanAdmissionOld, ok := objectOld.(*v1alpha1.ReleasePlanAdmission); ok { 126 if releasePlanAdmissionNew, ok := objectNew.(*v1alpha1.ReleasePlanAdmission); ok { 127 return releasePlanAdmissionOld.Spec.Origin != releasePlanAdmissionNew.Spec.Origin 128 } 129 } 130 131 return false 132 }