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  }