...

Source file src/sigs.k8s.io/controller-runtime/pkg/builder/controller_test.go

Documentation: sigs.k8s.io/controller-runtime/pkg/builder

     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 builder
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"strings"
    23  	"sync/atomic"
    24  
    25  	"github.com/go-logr/logr"
    26  	. "github.com/onsi/ginkgo/v2"
    27  	. "github.com/onsi/gomega"
    28  	appsv1 "k8s.io/api/apps/v1"
    29  	corev1 "k8s.io/api/core/v1"
    30  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    31  	"k8s.io/apimachinery/pkg/runtime"
    32  	"k8s.io/apimachinery/pkg/runtime/schema"
    33  	"k8s.io/apimachinery/pkg/types"
    34  	"k8s.io/client-go/rest"
    35  	"k8s.io/client-go/util/workqueue"
    36  	"k8s.io/utils/ptr"
    37  
    38  	"sigs.k8s.io/controller-runtime/pkg/cache"
    39  	"sigs.k8s.io/controller-runtime/pkg/client"
    40  	"sigs.k8s.io/controller-runtime/pkg/config"
    41  	"sigs.k8s.io/controller-runtime/pkg/controller"
    42  	"sigs.k8s.io/controller-runtime/pkg/event"
    43  	"sigs.k8s.io/controller-runtime/pkg/handler"
    44  	"sigs.k8s.io/controller-runtime/pkg/manager"
    45  	"sigs.k8s.io/controller-runtime/pkg/predicate"
    46  	"sigs.k8s.io/controller-runtime/pkg/reconcile"
    47  	"sigs.k8s.io/controller-runtime/pkg/scheme"
    48  )
    49  
    50  type typedNoop struct{}
    51  
    52  func (typedNoop) Reconcile(context.Context, reconcile.Request) (reconcile.Result, error) {
    53  	return reconcile.Result{}, nil
    54  }
    55  
    56  type testLogger struct {
    57  	logr.Logger
    58  }
    59  
    60  func (l *testLogger) Init(logr.RuntimeInfo) {
    61  }
    62  
    63  func (l *testLogger) Enabled(int) bool {
    64  	return true
    65  }
    66  
    67  func (l *testLogger) Info(level int, msg string, keysAndValues ...interface{}) {
    68  }
    69  
    70  func (l *testLogger) WithValues(keysAndValues ...interface{}) logr.LogSink {
    71  	return l
    72  }
    73  
    74  func (l *testLogger) WithName(name string) logr.LogSink {
    75  	return l
    76  }
    77  
    78  var _ = Describe("application", func() {
    79  	BeforeEach(func() {
    80  		newController = controller.New
    81  	})
    82  
    83  	noop := reconcile.Func(func(context.Context, reconcile.Request) (reconcile.Result, error) {
    84  		return reconcile.Result{}, nil
    85  	})
    86  
    87  	Describe("New", func() {
    88  		It("should return success if given valid objects", func() {
    89  			By("creating a controller manager")
    90  			m, err := manager.New(cfg, manager.Options{})
    91  			Expect(err).NotTo(HaveOccurred())
    92  
    93  			instance, err := ControllerManagedBy(m).
    94  				For(&appsv1.ReplicaSet{}).
    95  				Owns(&appsv1.ReplicaSet{}).
    96  				Build(noop)
    97  			Expect(err).NotTo(HaveOccurred())
    98  			Expect(instance).NotTo(BeNil())
    99  		})
   100  
   101  		It("should return error if given two apiType objects in For function", func() {
   102  			By("creating a controller manager")
   103  			m, err := manager.New(cfg, manager.Options{})
   104  			Expect(err).NotTo(HaveOccurred())
   105  
   106  			instance, err := ControllerManagedBy(m).
   107  				For(&appsv1.ReplicaSet{}).
   108  				For(&appsv1.Deployment{}).
   109  				Owns(&appsv1.ReplicaSet{}).
   110  				Build(noop)
   111  			Expect(err).To(MatchError(ContainSubstring("For(...) should only be called once, could not assign multiple objects for reconciliation")))
   112  			Expect(instance).To(BeNil())
   113  		})
   114  
   115  		It("should return an error if For and Named function are not called", func() {
   116  			By("creating a controller manager")
   117  			m, err := manager.New(cfg, manager.Options{})
   118  			Expect(err).NotTo(HaveOccurred())
   119  
   120  			instance, err := ControllerManagedBy(m).
   121  				Watches(&appsv1.ReplicaSet{}, &handler.EnqueueRequestForObject{}).
   122  				Build(noop)
   123  			Expect(err).To(MatchError(ContainSubstring("one of For() or Named() must be called")))
   124  			Expect(instance).To(BeNil())
   125  		})
   126  
   127  		It("should return an error when using Owns without For", func() {
   128  			By("creating a controller manager")
   129  			m, err := manager.New(cfg, manager.Options{})
   130  			Expect(err).NotTo(HaveOccurred())
   131  
   132  			instance, err := ControllerManagedBy(m).
   133  				Named("my_controller").
   134  				Owns(&appsv1.ReplicaSet{}).
   135  				Build(noop)
   136  			Expect(err).To(MatchError(ContainSubstring("Owns() can only be used together with For()")))
   137  			Expect(instance).To(BeNil())
   138  
   139  		})
   140  
   141  		It("should return an error when there are no watches", func() {
   142  			By("creating a controller manager")
   143  			m, err := manager.New(cfg, manager.Options{})
   144  			Expect(err).NotTo(HaveOccurred())
   145  
   146  			instance, err := ControllerManagedBy(m).
   147  				Named("my_controller").
   148  				Build(noop)
   149  			Expect(err).To(MatchError(ContainSubstring("there are no watches configured, controller will never get triggered. Use For(), Owns(), Watches() or WatchesRawSource() to set them up")))
   150  			Expect(instance).To(BeNil())
   151  		})
   152  
   153  		It("should allow creating a controllerw without calling For", func() {
   154  			By("creating a controller manager")
   155  			m, err := manager.New(cfg, manager.Options{})
   156  			Expect(err).NotTo(HaveOccurred())
   157  
   158  			instance, err := ControllerManagedBy(m).
   159  				Named("my_controller").
   160  				Watches(&appsv1.ReplicaSet{}, &handler.EnqueueRequestForObject{}).
   161  				Build(noop)
   162  			Expect(err).NotTo(HaveOccurred())
   163  			Expect(instance).NotTo(BeNil())
   164  		})
   165  
   166  		It("should return an error if there is no GVK for an object, and thus we can't default the controller name", func() {
   167  			By("creating a controller manager")
   168  			m, err := manager.New(cfg, manager.Options{})
   169  			Expect(err).NotTo(HaveOccurred())
   170  
   171  			By("creating a controller with a bad For type")
   172  			instance, err := ControllerManagedBy(m).
   173  				For(&fakeType{}).
   174  				Owns(&appsv1.ReplicaSet{}).
   175  				Build(noop)
   176  			Expect(err).To(MatchError(ContainSubstring("no kind is registered for the type builder.fakeType")))
   177  			Expect(instance).To(BeNil())
   178  
   179  			// NB(directxman12): we don't test non-for types, since errors for
   180  			// them now manifest on controller.Start, not controller.Watch.  Errors on the For type
   181  			// manifest when we try to default the controller name, which is good to double check.
   182  		})
   183  
   184  		It("should return an error if it cannot create the controller", func() {
   185  			newController = func(name string, mgr manager.Manager, options controller.Options) (
   186  				controller.Controller, error) {
   187  				return nil, fmt.Errorf("expected error")
   188  			}
   189  
   190  			By("creating a controller manager")
   191  			m, err := manager.New(cfg, manager.Options{})
   192  			Expect(err).NotTo(HaveOccurred())
   193  
   194  			instance, err := ControllerManagedBy(m).
   195  				For(&appsv1.ReplicaSet{}).
   196  				Owns(&appsv1.ReplicaSet{}).
   197  				Build(noop)
   198  			Expect(err).To(HaveOccurred())
   199  			Expect(err.Error()).To(ContainSubstring("expected error"))
   200  			Expect(instance).To(BeNil())
   201  		})
   202  
   203  		It("should override max concurrent reconcilers during creation of controller", func() {
   204  			const maxConcurrentReconciles = 5
   205  			newController = func(name string, mgr manager.Manager, options controller.Options) (
   206  				controller.Controller, error) {
   207  				if options.MaxConcurrentReconciles == maxConcurrentReconciles {
   208  					return controller.New(name, mgr, options)
   209  				}
   210  				return nil, fmt.Errorf("max concurrent reconcilers expected %d but found %d", maxConcurrentReconciles, options.MaxConcurrentReconciles)
   211  			}
   212  
   213  			By("creating a controller manager")
   214  			m, err := manager.New(cfg, manager.Options{})
   215  			Expect(err).NotTo(HaveOccurred())
   216  
   217  			instance, err := ControllerManagedBy(m).
   218  				For(&appsv1.ReplicaSet{}).
   219  				Owns(&appsv1.ReplicaSet{}).
   220  				WithOptions(controller.Options{MaxConcurrentReconciles: maxConcurrentReconciles}).
   221  				Build(noop)
   222  			Expect(err).NotTo(HaveOccurred())
   223  			Expect(instance).NotTo(BeNil())
   224  		})
   225  
   226  		It("should override max concurrent reconcilers during creation of controller, when using", func() {
   227  			const maxConcurrentReconciles = 10
   228  			newController = func(name string, mgr manager.Manager, options controller.Options) (
   229  				controller.Controller, error) {
   230  				if options.MaxConcurrentReconciles == maxConcurrentReconciles {
   231  					return controller.New(name, mgr, options)
   232  				}
   233  				return nil, fmt.Errorf("max concurrent reconcilers expected %d but found %d", maxConcurrentReconciles, options.MaxConcurrentReconciles)
   234  			}
   235  
   236  			By("creating a controller manager")
   237  			m, err := manager.New(cfg, manager.Options{
   238  				Controller: config.Controller{
   239  					GroupKindConcurrency: map[string]int{
   240  						"ReplicaSet.apps": maxConcurrentReconciles,
   241  					},
   242  				},
   243  			})
   244  			Expect(err).NotTo(HaveOccurred())
   245  
   246  			instance, err := ControllerManagedBy(m).
   247  				For(&appsv1.ReplicaSet{}).
   248  				Owns(&appsv1.ReplicaSet{}).
   249  				Build(noop)
   250  			Expect(err).NotTo(HaveOccurred())
   251  			Expect(instance).NotTo(BeNil())
   252  		})
   253  
   254  		It("should override rate limiter during creation of controller", func() {
   255  			rateLimiter := workqueue.DefaultItemBasedRateLimiter()
   256  			newController = func(name string, mgr manager.Manager, options controller.Options) (controller.Controller, error) {
   257  				if options.RateLimiter == rateLimiter {
   258  					return controller.New(name, mgr, options)
   259  				}
   260  				return nil, fmt.Errorf("rate limiter expected %T but found %T", rateLimiter, options.RateLimiter)
   261  			}
   262  
   263  			By("creating a controller manager")
   264  			m, err := manager.New(cfg, manager.Options{})
   265  			Expect(err).NotTo(HaveOccurred())
   266  
   267  			instance, err := ControllerManagedBy(m).
   268  				For(&appsv1.ReplicaSet{}).
   269  				Owns(&appsv1.ReplicaSet{}).
   270  				WithOptions(controller.Options{RateLimiter: rateLimiter}).
   271  				Build(noop)
   272  			Expect(err).NotTo(HaveOccurred())
   273  			Expect(instance).NotTo(BeNil())
   274  		})
   275  
   276  		It("should override logger during creation of controller", func() {
   277  
   278  			logger := &testLogger{}
   279  			newController = func(name string, mgr manager.Manager, options controller.Options) (controller.Controller, error) {
   280  				if options.LogConstructor(nil).GetSink() == logger {
   281  					return controller.New(name, mgr, options)
   282  				}
   283  				return nil, fmt.Errorf("logger expected %T but found %T", logger, options.LogConstructor)
   284  			}
   285  
   286  			By("creating a controller manager")
   287  			m, err := manager.New(cfg, manager.Options{})
   288  			Expect(err).NotTo(HaveOccurred())
   289  
   290  			instance, err := ControllerManagedBy(m).
   291  				For(&appsv1.ReplicaSet{}).
   292  				Owns(&appsv1.ReplicaSet{}).
   293  				WithLogConstructor(func(request *reconcile.Request) logr.Logger {
   294  					return logr.New(logger)
   295  				}).
   296  				Build(noop)
   297  			Expect(err).NotTo(HaveOccurred())
   298  			Expect(instance).NotTo(BeNil())
   299  		})
   300  
   301  		It("should not allow multiple reconcilers during creation of controller", func() {
   302  			newController = func(name string, mgr manager.Manager, options controller.Options) (controller.Controller, error) {
   303  				if options.Reconciler != (typedNoop{}) {
   304  					return nil, fmt.Errorf("Custom reconciler expected %T but found %T", typedNoop{}, options.Reconciler)
   305  				}
   306  				return controller.New(name, mgr, options)
   307  			}
   308  
   309  			By("creating a controller manager")
   310  			m, err := manager.New(cfg, manager.Options{})
   311  			Expect(err).NotTo(HaveOccurred())
   312  
   313  			instance, err := ControllerManagedBy(m).
   314  				For(&appsv1.ReplicaSet{}).
   315  				Owns(&appsv1.ReplicaSet{}).
   316  				WithOptions(controller.Options{Reconciler: typedNoop{}}).
   317  				Build(noop)
   318  			Expect(err).To(HaveOccurred())
   319  			Expect(instance).To(BeNil())
   320  		})
   321  
   322  		It("should allow multiple controllers for the same kind", func() {
   323  			By("creating a controller manager")
   324  			m, err := manager.New(cfg, manager.Options{})
   325  			Expect(err).NotTo(HaveOccurred())
   326  
   327  			By("registering the type in the Scheme")
   328  			builder := scheme.Builder{GroupVersion: testDefaultValidatorGVK.GroupVersion()}
   329  			builder.Register(&TestDefaultValidator{}, &TestDefaultValidatorList{})
   330  			err = builder.AddToScheme(m.GetScheme())
   331  			Expect(err).NotTo(HaveOccurred())
   332  
   333  			By("creating the 1st controller")
   334  			ctrl1, err := ControllerManagedBy(m).
   335  				For(&TestDefaultValidator{}).
   336  				Owns(&appsv1.ReplicaSet{}).
   337  				Build(noop)
   338  			Expect(err).NotTo(HaveOccurred())
   339  			Expect(ctrl1).NotTo(BeNil())
   340  
   341  			By("creating the 2nd controller")
   342  			ctrl2, err := ControllerManagedBy(m).
   343  				For(&TestDefaultValidator{}).
   344  				Owns(&appsv1.ReplicaSet{}).
   345  				Build(noop)
   346  			Expect(err).NotTo(HaveOccurred())
   347  			Expect(ctrl2).NotTo(BeNil())
   348  		})
   349  	})
   350  
   351  	Describe("Start with ControllerManagedBy", func() {
   352  		It("should Reconcile Owns objects", func() {
   353  			m, err := manager.New(cfg, manager.Options{})
   354  			Expect(err).NotTo(HaveOccurred())
   355  
   356  			bldr := ControllerManagedBy(m).
   357  				For(&appsv1.Deployment{}).
   358  				Owns(&appsv1.ReplicaSet{})
   359  
   360  			ctx, cancel := context.WithCancel(context.Background())
   361  			defer cancel()
   362  			doReconcileTest(ctx, "3", m, false, bldr)
   363  		})
   364  
   365  		It("should Reconcile Owns objects for every owner", func() {
   366  			m, err := manager.New(cfg, manager.Options{})
   367  			Expect(err).NotTo(HaveOccurred())
   368  
   369  			bldr := ControllerManagedBy(m).
   370  				For(&appsv1.Deployment{}).
   371  				Owns(&appsv1.ReplicaSet{}, MatchEveryOwner)
   372  
   373  			ctx, cancel := context.WithCancel(context.Background())
   374  			defer cancel()
   375  			doReconcileTest(ctx, "12", m, false, bldr)
   376  		})
   377  
   378  		It("should Reconcile Watches objects", func() {
   379  			m, err := manager.New(cfg, manager.Options{})
   380  			Expect(err).NotTo(HaveOccurred())
   381  
   382  			bldr := ControllerManagedBy(m).
   383  				For(&appsv1.Deployment{}).
   384  				Watches( // Equivalent of Owns
   385  					&appsv1.ReplicaSet{},
   386  					handler.EnqueueRequestForOwner(m.GetScheme(), m.GetRESTMapper(), &appsv1.Deployment{}, handler.OnlyControllerOwner()),
   387  				)
   388  
   389  			ctx, cancel := context.WithCancel(context.Background())
   390  			defer cancel()
   391  			doReconcileTest(ctx, "4", m, true, bldr)
   392  		})
   393  
   394  		It("should Reconcile without For", func() {
   395  			m, err := manager.New(cfg, manager.Options{})
   396  			Expect(err).NotTo(HaveOccurred())
   397  
   398  			bldr := ControllerManagedBy(m).
   399  				Named("Deployment").
   400  				Watches( // Equivalent of For
   401  						&appsv1.Deployment{}, &handler.EnqueueRequestForObject{}).
   402  				Watches( // Equivalent of Owns
   403  					&appsv1.ReplicaSet{},
   404  					handler.EnqueueRequestForOwner(m.GetScheme(), m.GetRESTMapper(), &appsv1.Deployment{}, handler.OnlyControllerOwner()),
   405  				)
   406  
   407  			ctx, cancel := context.WithCancel(context.Background())
   408  			defer cancel()
   409  			doReconcileTest(ctx, "9", m, true, bldr)
   410  		})
   411  	})
   412  
   413  	Describe("Set custom predicates", func() {
   414  		It("should execute registered predicates only for assigned kind", func() {
   415  			m, err := manager.New(cfg, manager.Options{})
   416  			Expect(err).NotTo(HaveOccurred())
   417  
   418  			var (
   419  				deployPrctExecuted     = false
   420  				replicaSetPrctExecuted = false
   421  				allPrctExecuted        = int64(0)
   422  			)
   423  
   424  			deployPrct := predicate.Funcs{
   425  				CreateFunc: func(e event.CreateEvent) bool {
   426  					defer GinkgoRecover()
   427  					// check that it was called only for deployment
   428  					Expect(e.Object).To(BeAssignableToTypeOf(&appsv1.Deployment{}))
   429  					deployPrctExecuted = true
   430  					return true
   431  				},
   432  			}
   433  
   434  			replicaSetPrct := predicate.Funcs{
   435  				CreateFunc: func(e event.CreateEvent) bool {
   436  					defer GinkgoRecover()
   437  					// check that it was called only for replicaset
   438  					Expect(e.Object).To(BeAssignableToTypeOf(&appsv1.ReplicaSet{}))
   439  					replicaSetPrctExecuted = true
   440  					return true
   441  				},
   442  			}
   443  
   444  			allPrct := predicate.Funcs{
   445  				CreateFunc: func(e event.CreateEvent) bool {
   446  					defer GinkgoRecover()
   447  					// check that it was called for all registered kinds
   448  					Expect(e.Object).Should(Or(
   449  						BeAssignableToTypeOf(&appsv1.Deployment{}),
   450  						BeAssignableToTypeOf(&appsv1.ReplicaSet{}),
   451  					))
   452  
   453  					atomic.AddInt64(&allPrctExecuted, 1)
   454  					return true
   455  				},
   456  			}
   457  
   458  			bldr := ControllerManagedBy(m).
   459  				For(&appsv1.Deployment{}, WithPredicates(deployPrct)).
   460  				Owns(&appsv1.ReplicaSet{}, WithPredicates(replicaSetPrct)).
   461  				WithEventFilter(allPrct)
   462  
   463  			ctx, cancel := context.WithCancel(context.Background())
   464  			defer cancel()
   465  			doReconcileTest(ctx, "5", m, true, bldr)
   466  
   467  			Expect(deployPrctExecuted).To(BeTrue(), "Deploy predicated should be called at least once")
   468  			Expect(replicaSetPrctExecuted).To(BeTrue(), "ReplicaSet predicated should be called at least once")
   469  			Expect(allPrctExecuted).To(BeNumerically(">=", 2), "Global Predicated should be called at least twice")
   470  		})
   471  	})
   472  
   473  	Describe("watching with projections", func() {
   474  		var mgr manager.Manager
   475  		BeforeEach(func() {
   476  			// use a cache that intercepts requests for fully typed objects to
   477  			// ensure we use the projected versions
   478  			var err error
   479  			mgr, err = manager.New(cfg, manager.Options{NewCache: newNonTypedOnlyCache})
   480  			Expect(err).NotTo(HaveOccurred())
   481  		})
   482  
   483  		It("should support multiple controllers watching the same metadata kind", func() {
   484  			bldr1 := ControllerManagedBy(mgr).For(&appsv1.Deployment{}, OnlyMetadata)
   485  			bldr2 := ControllerManagedBy(mgr).For(&appsv1.Deployment{}, OnlyMetadata)
   486  
   487  			ctx, cancel := context.WithCancel(context.Background())
   488  			defer cancel()
   489  
   490  			doReconcileTest(ctx, "6", mgr, true, bldr1, bldr2)
   491  		})
   492  
   493  		It("should support watching For, Owns, and Watch as metadata", func() {
   494  			statefulSetMaps := make(chan *metav1.PartialObjectMetadata)
   495  
   496  			bldr := ControllerManagedBy(mgr).
   497  				For(&appsv1.Deployment{}, OnlyMetadata).
   498  				Owns(&appsv1.ReplicaSet{}, OnlyMetadata).
   499  				Watches(&appsv1.StatefulSet{},
   500  					handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, o client.Object) []reconcile.Request {
   501  						defer GinkgoRecover()
   502  
   503  						ometa := o.(*metav1.PartialObjectMetadata)
   504  						statefulSetMaps <- ometa
   505  
   506  						// Validate that the GVK is not empty when dealing with PartialObjectMetadata objects.
   507  						Expect(o.GetObjectKind().GroupVersionKind()).To(Equal(schema.GroupVersionKind{
   508  							Group:   "apps",
   509  							Version: "v1",
   510  							Kind:    "StatefulSet",
   511  						}))
   512  						return nil
   513  					}),
   514  					OnlyMetadata)
   515  
   516  			ctx, cancel := context.WithCancel(context.Background())
   517  			defer cancel()
   518  			doReconcileTest(ctx, "8", mgr, true, bldr)
   519  
   520  			By("Creating a new stateful set")
   521  			set := &appsv1.StatefulSet{
   522  				ObjectMeta: metav1.ObjectMeta{
   523  					Namespace: "default",
   524  					Name:      "test1",
   525  					Labels: map[string]string{
   526  						"foo": "bar",
   527  					},
   528  				},
   529  				Spec: appsv1.StatefulSetSpec{
   530  					Selector: &metav1.LabelSelector{
   531  						MatchLabels: map[string]string{"foo": "bar"},
   532  					},
   533  					Template: corev1.PodTemplateSpec{
   534  						ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"foo": "bar"}},
   535  						Spec: corev1.PodSpec{
   536  							Containers: []corev1.Container{
   537  								{
   538  									Name:  "nginx",
   539  									Image: "nginx",
   540  								},
   541  							},
   542  						},
   543  					},
   544  				},
   545  			}
   546  			err := mgr.GetClient().Create(context.TODO(), set)
   547  			Expect(err).NotTo(HaveOccurred())
   548  
   549  			By("Checking that the mapping function has been called")
   550  			Eventually(func() bool {
   551  				metaSet := <-statefulSetMaps
   552  				Expect(metaSet.Name).To(Equal(set.Name))
   553  				Expect(metaSet.Namespace).To(Equal(set.Namespace))
   554  				Expect(metaSet.Labels).To(Equal(set.Labels))
   555  				return true
   556  			}).Should(BeTrue())
   557  		})
   558  	})
   559  })
   560  
   561  // newNonTypedOnlyCache returns a new cache that wraps the normal cache,
   562  // returning an error if normal, typed objects have informers requested.
   563  func newNonTypedOnlyCache(config *rest.Config, opts cache.Options) (cache.Cache, error) {
   564  	normalCache, err := cache.New(config, opts)
   565  	if err != nil {
   566  		return nil, err
   567  	}
   568  	return &nonTypedOnlyCache{
   569  		Cache: normalCache,
   570  	}, nil
   571  }
   572  
   573  // nonTypedOnlyCache is a cache.Cache that only provides metadata &
   574  // unstructured informers.
   575  type nonTypedOnlyCache struct {
   576  	cache.Cache
   577  }
   578  
   579  func (c *nonTypedOnlyCache) GetInformer(ctx context.Context, obj client.Object, opts ...cache.InformerGetOption) (cache.Informer, error) {
   580  	switch obj.(type) {
   581  	case (*metav1.PartialObjectMetadata):
   582  		return c.Cache.GetInformer(ctx, obj, opts...)
   583  	default:
   584  		return nil, fmt.Errorf("did not want to provide an informer for normal type %T", obj)
   585  	}
   586  }
   587  func (c *nonTypedOnlyCache) GetInformerForKind(ctx context.Context, gvk schema.GroupVersionKind, opts ...cache.InformerGetOption) (cache.Informer, error) {
   588  	return nil, fmt.Errorf("don't try to sidestep the restriction on informer types by calling GetInformerForKind")
   589  }
   590  
   591  // TODO(directxman12): this function has too many arguments, and the whole
   592  // "nameSuffix" think is a bit of a hack It should be cleaned up significantly by someone with a bit of time.
   593  func doReconcileTest(ctx context.Context, nameSuffix string, mgr manager.Manager, complete bool, blders ...*Builder) {
   594  	deployName := "deploy-name-" + nameSuffix
   595  	rsName := "rs-name-" + nameSuffix
   596  
   597  	By("Creating the application")
   598  	ch := make(chan reconcile.Request)
   599  	fn := reconcile.Func(func(_ context.Context, req reconcile.Request) (reconcile.Result, error) {
   600  		defer GinkgoRecover()
   601  		if !strings.HasSuffix(req.Name, nameSuffix) {
   602  			// From different test, ignore this request.  Etcd is shared across tests.
   603  			return reconcile.Result{}, nil
   604  		}
   605  		ch <- req
   606  		return reconcile.Result{}, nil
   607  	})
   608  
   609  	for _, blder := range blders {
   610  		if complete {
   611  			err := blder.Complete(fn)
   612  			Expect(err).NotTo(HaveOccurred())
   613  		} else {
   614  			var err error
   615  			var c controller.Controller
   616  			c, err = blder.Build(fn)
   617  			Expect(err).NotTo(HaveOccurred())
   618  			Expect(c).NotTo(BeNil())
   619  		}
   620  	}
   621  
   622  	By("Starting the application")
   623  	go func() {
   624  		defer GinkgoRecover()
   625  		Expect(mgr.Start(ctx)).NotTo(HaveOccurred())
   626  	}()
   627  
   628  	By("Creating a Deployment")
   629  	// Expect a Reconcile when the Deployment is managedObjects.
   630  	dep := &appsv1.Deployment{
   631  		ObjectMeta: metav1.ObjectMeta{
   632  			Namespace: "default",
   633  			Name:      deployName,
   634  		},
   635  		Spec: appsv1.DeploymentSpec{
   636  			Selector: &metav1.LabelSelector{
   637  				MatchLabels: map[string]string{"foo": "bar"},
   638  			},
   639  			Template: corev1.PodTemplateSpec{
   640  				ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"foo": "bar"}},
   641  				Spec: corev1.PodSpec{
   642  					Containers: []corev1.Container{
   643  						{
   644  							Name:  "nginx",
   645  							Image: "nginx",
   646  						},
   647  					},
   648  				},
   649  			},
   650  		},
   651  	}
   652  	err := mgr.GetClient().Create(ctx, dep)
   653  	Expect(err).NotTo(HaveOccurred())
   654  
   655  	By("Waiting for the Deployment Reconcile")
   656  	Eventually(ch).Should(Receive(Equal(reconcile.Request{
   657  		NamespacedName: types.NamespacedName{Namespace: "default", Name: deployName}})))
   658  
   659  	By("Creating a ReplicaSet")
   660  	// Expect a Reconcile when an Owned object is managedObjects.
   661  	rs := &appsv1.ReplicaSet{
   662  		ObjectMeta: metav1.ObjectMeta{
   663  			Namespace: "default",
   664  			Name:      rsName,
   665  			Labels:    dep.Spec.Selector.MatchLabels,
   666  			OwnerReferences: []metav1.OwnerReference{
   667  				{
   668  					Name:       deployName,
   669  					Kind:       "Deployment",
   670  					APIVersion: "apps/v1",
   671  					Controller: ptr.To(true),
   672  					UID:        dep.UID,
   673  				},
   674  			},
   675  		},
   676  		Spec: appsv1.ReplicaSetSpec{
   677  			Selector: dep.Spec.Selector,
   678  			Template: dep.Spec.Template,
   679  		},
   680  	}
   681  	err = mgr.GetClient().Create(ctx, rs)
   682  	Expect(err).NotTo(HaveOccurred())
   683  
   684  	By("Waiting for the ReplicaSet Reconcile")
   685  	Eventually(ch).Should(Receive(Equal(reconcile.Request{
   686  		NamespacedName: types.NamespacedName{Namespace: "default", Name: deployName}})))
   687  }
   688  
   689  var _ runtime.Object = &fakeType{}
   690  
   691  type fakeType struct {
   692  	metav1.TypeMeta
   693  	metav1.ObjectMeta
   694  }
   695  
   696  func (*fakeType) GetObjectKind() schema.ObjectKind { return nil }
   697  func (*fakeType) DeepCopyObject() runtime.Object   { return nil }
   698  

View as plain text