...

Source file src/sigs.k8s.io/controller-runtime/pkg/internal/source/internal_test.go

Documentation: sigs.k8s.io/controller-runtime/pkg/internal/source

     1  /*
     2  Copyright 2018 The Kubernetes Authors.
     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 internal_test
    18  
    19  import (
    20  	"context"
    21  
    22  	. "github.com/onsi/ginkgo/v2"
    23  	. "github.com/onsi/gomega"
    24  	"k8s.io/client-go/tools/cache"
    25  	"k8s.io/client-go/util/workqueue"
    26  	"sigs.k8s.io/controller-runtime/pkg/client"
    27  	"sigs.k8s.io/controller-runtime/pkg/event"
    28  	"sigs.k8s.io/controller-runtime/pkg/handler"
    29  	internal "sigs.k8s.io/controller-runtime/pkg/internal/source"
    30  
    31  	corev1 "k8s.io/api/core/v1"
    32  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    33  	"k8s.io/apimachinery/pkg/runtime"
    34  	"k8s.io/apimachinery/pkg/runtime/schema"
    35  	"sigs.k8s.io/controller-runtime/pkg/controller/controllertest"
    36  	"sigs.k8s.io/controller-runtime/pkg/predicate"
    37  )
    38  
    39  var _ = Describe("Internal", func() {
    40  	var ctx = context.Background()
    41  	var instance *internal.EventHandler[client.Object]
    42  	var funcs, setfuncs *handler.Funcs
    43  	var set bool
    44  	BeforeEach(func() {
    45  		funcs = &handler.Funcs{
    46  			CreateFunc: func(context.Context, event.CreateEvent, workqueue.RateLimitingInterface) {
    47  				defer GinkgoRecover()
    48  				Fail("Did not expect CreateEvent to be called.")
    49  			},
    50  			DeleteFunc: func(context.Context, event.DeleteEvent, workqueue.RateLimitingInterface) {
    51  				defer GinkgoRecover()
    52  				Fail("Did not expect DeleteEvent to be called.")
    53  			},
    54  			UpdateFunc: func(context.Context, event.UpdateEvent, workqueue.RateLimitingInterface) {
    55  				defer GinkgoRecover()
    56  				Fail("Did not expect UpdateEvent to be called.")
    57  			},
    58  			GenericFunc: func(context.Context, event.GenericEvent, workqueue.RateLimitingInterface) {
    59  				defer GinkgoRecover()
    60  				Fail("Did not expect GenericEvent to be called.")
    61  			},
    62  		}
    63  
    64  		setfuncs = &handler.Funcs{
    65  			CreateFunc: func(context.Context, event.CreateEvent, workqueue.RateLimitingInterface) {
    66  				set = true
    67  			},
    68  			DeleteFunc: func(context.Context, event.DeleteEvent, workqueue.RateLimitingInterface) {
    69  				set = true
    70  			},
    71  			UpdateFunc: func(context.Context, event.UpdateEvent, workqueue.RateLimitingInterface) {
    72  				set = true
    73  			},
    74  			GenericFunc: func(context.Context, event.GenericEvent, workqueue.RateLimitingInterface) {
    75  				set = true
    76  			},
    77  		}
    78  		instance = internal.NewEventHandler(ctx, &controllertest.Queue{}, funcs, nil)
    79  	})
    80  
    81  	Describe("EventHandler", func() {
    82  		var pod, newPod *corev1.Pod
    83  
    84  		BeforeEach(func() {
    85  			pod = &corev1.Pod{
    86  				Spec: corev1.PodSpec{
    87  					Containers: []corev1.Container{{Name: "test", Image: "test"}},
    88  				},
    89  			}
    90  			newPod = pod.DeepCopy()
    91  			newPod.Labels = map[string]string{"foo": "bar"}
    92  		})
    93  
    94  		It("should create a CreateEvent", func() {
    95  			funcs.CreateFunc = func(ctx context.Context, evt event.CreateEvent, q workqueue.RateLimitingInterface) {
    96  				defer GinkgoRecover()
    97  				Expect(evt.Object).To(Equal(pod))
    98  			}
    99  			instance.OnAdd(pod)
   100  		})
   101  
   102  		It("should used Predicates to filter CreateEvents", func() {
   103  			instance = internal.NewEventHandler(ctx, &controllertest.Queue{}, setfuncs, []predicate.Predicate{
   104  				predicate.Funcs{CreateFunc: func(event.CreateEvent) bool { return false }},
   105  			})
   106  			set = false
   107  			instance.OnAdd(pod)
   108  			Expect(set).To(BeFalse())
   109  
   110  			set = false
   111  			instance = internal.NewEventHandler(ctx, &controllertest.Queue{}, setfuncs, []predicate.Predicate{
   112  				predicate.Funcs{CreateFunc: func(event.CreateEvent) bool { return true }},
   113  			})
   114  			instance.OnAdd(pod)
   115  			Expect(set).To(BeTrue())
   116  
   117  			set = false
   118  			instance = internal.NewEventHandler(ctx, &controllertest.Queue{}, setfuncs, []predicate.Predicate{
   119  				predicate.Funcs{CreateFunc: func(event.CreateEvent) bool { return true }},
   120  				predicate.Funcs{CreateFunc: func(event.CreateEvent) bool { return false }},
   121  			})
   122  			instance.OnAdd(pod)
   123  			Expect(set).To(BeFalse())
   124  
   125  			set = false
   126  			instance = internal.NewEventHandler(ctx, &controllertest.Queue{}, setfuncs, []predicate.Predicate{
   127  				predicate.Funcs{CreateFunc: func(event.CreateEvent) bool { return false }},
   128  				predicate.Funcs{CreateFunc: func(event.CreateEvent) bool { return true }},
   129  			})
   130  			instance.OnAdd(pod)
   131  			Expect(set).To(BeFalse())
   132  
   133  			set = false
   134  			instance = internal.NewEventHandler(ctx, &controllertest.Queue{}, setfuncs, []predicate.Predicate{
   135  				predicate.Funcs{CreateFunc: func(event.CreateEvent) bool { return true }},
   136  				predicate.Funcs{CreateFunc: func(event.CreateEvent) bool { return true }},
   137  			})
   138  			instance.OnAdd(pod)
   139  			Expect(set).To(BeTrue())
   140  		})
   141  
   142  		It("should not call Create EventHandler if the object is not a runtime.Object", func() {
   143  			instance.OnAdd(&metav1.ObjectMeta{})
   144  		})
   145  
   146  		It("should not call Create EventHandler if the object does not have metadata", func() {
   147  			instance.OnAdd(FooRuntimeObject{})
   148  		})
   149  
   150  		It("should create an UpdateEvent", func() {
   151  			funcs.UpdateFunc = func(ctx context.Context, evt event.UpdateEvent, q workqueue.RateLimitingInterface) {
   152  				defer GinkgoRecover()
   153  				Expect(evt.ObjectOld).To(Equal(pod))
   154  				Expect(evt.ObjectNew).To(Equal(newPod))
   155  			}
   156  			instance.OnUpdate(pod, newPod)
   157  		})
   158  
   159  		It("should used Predicates to filter UpdateEvents", func() {
   160  			set = false
   161  			instance = internal.NewEventHandler(ctx, &controllertest.Queue{}, setfuncs, []predicate.Predicate{
   162  				predicate.Funcs{UpdateFunc: func(updateEvent event.UpdateEvent) bool { return false }},
   163  			})
   164  			instance.OnUpdate(pod, newPod)
   165  			Expect(set).To(BeFalse())
   166  
   167  			set = false
   168  			instance = internal.NewEventHandler(ctx, &controllertest.Queue{}, setfuncs, []predicate.Predicate{
   169  				predicate.Funcs{UpdateFunc: func(event.UpdateEvent) bool { return true }},
   170  			})
   171  			instance.OnUpdate(pod, newPod)
   172  			Expect(set).To(BeTrue())
   173  
   174  			set = false
   175  			instance = internal.NewEventHandler(ctx, &controllertest.Queue{}, setfuncs, []predicate.Predicate{
   176  				predicate.Funcs{UpdateFunc: func(event.UpdateEvent) bool { return true }},
   177  				predicate.Funcs{UpdateFunc: func(event.UpdateEvent) bool { return false }},
   178  			})
   179  			instance.OnUpdate(pod, newPod)
   180  			Expect(set).To(BeFalse())
   181  
   182  			set = false
   183  			instance = internal.NewEventHandler(ctx, &controllertest.Queue{}, setfuncs, []predicate.Predicate{
   184  				predicate.Funcs{UpdateFunc: func(event.UpdateEvent) bool { return false }},
   185  				predicate.Funcs{UpdateFunc: func(event.UpdateEvent) bool { return true }},
   186  			})
   187  			instance.OnUpdate(pod, newPod)
   188  			Expect(set).To(BeFalse())
   189  
   190  			set = false
   191  			instance = internal.NewEventHandler(ctx, &controllertest.Queue{}, setfuncs, []predicate.Predicate{
   192  				predicate.Funcs{CreateFunc: func(event.CreateEvent) bool { return true }},
   193  				predicate.Funcs{CreateFunc: func(event.CreateEvent) bool { return true }},
   194  			})
   195  			instance.OnUpdate(pod, newPod)
   196  			Expect(set).To(BeTrue())
   197  		})
   198  
   199  		It("should not call Update EventHandler if the object is not a runtime.Object", func() {
   200  			instance.OnUpdate(&metav1.ObjectMeta{}, &corev1.Pod{})
   201  			instance.OnUpdate(&corev1.Pod{}, &metav1.ObjectMeta{})
   202  		})
   203  
   204  		It("should not call Update EventHandler if the object does not have metadata", func() {
   205  			instance.OnUpdate(FooRuntimeObject{}, &corev1.Pod{})
   206  			instance.OnUpdate(&corev1.Pod{}, FooRuntimeObject{})
   207  		})
   208  
   209  		It("should create a DeleteEvent", func() {
   210  			funcs.DeleteFunc = func(ctx context.Context, evt event.DeleteEvent, q workqueue.RateLimitingInterface) {
   211  				defer GinkgoRecover()
   212  				Expect(evt.Object).To(Equal(pod))
   213  			}
   214  			instance.OnDelete(pod)
   215  		})
   216  
   217  		It("should used Predicates to filter DeleteEvents", func() {
   218  			set = false
   219  			instance = internal.NewEventHandler(ctx, &controllertest.Queue{}, setfuncs, []predicate.Predicate{
   220  				predicate.Funcs{DeleteFunc: func(event.DeleteEvent) bool { return false }},
   221  			})
   222  			instance.OnDelete(pod)
   223  			Expect(set).To(BeFalse())
   224  
   225  			set = false
   226  			instance = internal.NewEventHandler(ctx, &controllertest.Queue{}, setfuncs, []predicate.Predicate{
   227  				predicate.Funcs{DeleteFunc: func(event.DeleteEvent) bool { return true }},
   228  			})
   229  			instance.OnDelete(pod)
   230  			Expect(set).To(BeTrue())
   231  
   232  			set = false
   233  			instance = internal.NewEventHandler(ctx, &controllertest.Queue{}, setfuncs, []predicate.Predicate{
   234  				predicate.Funcs{DeleteFunc: func(event.DeleteEvent) bool { return true }},
   235  				predicate.Funcs{DeleteFunc: func(event.DeleteEvent) bool { return false }},
   236  			})
   237  			instance.OnDelete(pod)
   238  			Expect(set).To(BeFalse())
   239  
   240  			set = false
   241  			instance = internal.NewEventHandler(ctx, &controllertest.Queue{}, setfuncs, []predicate.Predicate{
   242  				predicate.Funcs{DeleteFunc: func(event.DeleteEvent) bool { return false }},
   243  				predicate.Funcs{DeleteFunc: func(event.DeleteEvent) bool { return true }},
   244  			})
   245  			instance.OnDelete(pod)
   246  			Expect(set).To(BeFalse())
   247  
   248  			set = false
   249  			instance = internal.NewEventHandler(ctx, &controllertest.Queue{}, setfuncs, []predicate.Predicate{
   250  				predicate.Funcs{DeleteFunc: func(event.DeleteEvent) bool { return true }},
   251  				predicate.Funcs{DeleteFunc: func(event.DeleteEvent) bool { return true }},
   252  			})
   253  			instance.OnDelete(pod)
   254  			Expect(set).To(BeTrue())
   255  		})
   256  
   257  		It("should not call Delete EventHandler if the object is not a runtime.Object", func() {
   258  			instance.OnDelete(&metav1.ObjectMeta{})
   259  		})
   260  
   261  		It("should not call Delete EventHandler if the object does not have metadata", func() {
   262  			instance.OnDelete(FooRuntimeObject{})
   263  		})
   264  
   265  		It("should create a DeleteEvent from a tombstone", func() {
   266  
   267  			tombstone := cache.DeletedFinalStateUnknown{
   268  				Obj: pod,
   269  			}
   270  			funcs.DeleteFunc = func(ctx context.Context, evt event.DeleteEvent, q workqueue.RateLimitingInterface) {
   271  				defer GinkgoRecover()
   272  				Expect(evt.Object).To(Equal(pod))
   273  				Expect(evt.DeleteStateUnknown).Should(BeTrue())
   274  			}
   275  
   276  			instance.OnDelete(tombstone)
   277  		})
   278  
   279  		It("should ignore tombstone objects without meta", func() {
   280  			tombstone := cache.DeletedFinalStateUnknown{Obj: Foo{}}
   281  			instance.OnDelete(tombstone)
   282  		})
   283  		It("should ignore objects without meta", func() {
   284  			instance.OnAdd(Foo{})
   285  			instance.OnUpdate(Foo{}, Foo{})
   286  			instance.OnDelete(Foo{})
   287  		})
   288  	})
   289  
   290  	Describe("Kind", func() {
   291  		It("should return kind source type", func() {
   292  			kind := internal.Kind[*corev1.Pod]{
   293  				Type: &corev1.Pod{},
   294  			}
   295  			Expect(kind.String()).Should(Equal("kind source: *v1.Pod"))
   296  		})
   297  	})
   298  })
   299  
   300  type Foo struct{}
   301  
   302  var _ runtime.Object = FooRuntimeObject{}
   303  
   304  type FooRuntimeObject struct{}
   305  
   306  func (FooRuntimeObject) GetObjectKind() schema.ObjectKind { return nil }
   307  func (FooRuntimeObject) DeepCopyObject() runtime.Object   { return nil }
   308  

View as plain text