...

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

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

     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 controller_test
    18  
    19  import (
    20  	"context"
    21  
    22  	appsv1 "k8s.io/api/apps/v1"
    23  	corev1 "k8s.io/api/core/v1"
    24  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    25  	"k8s.io/apimachinery/pkg/runtime/schema"
    26  	"k8s.io/apimachinery/pkg/types"
    27  	"sigs.k8s.io/controller-runtime/pkg/cache"
    28  	"sigs.k8s.io/controller-runtime/pkg/controller"
    29  	"sigs.k8s.io/controller-runtime/pkg/controller/controllertest"
    30  	"sigs.k8s.io/controller-runtime/pkg/handler"
    31  	"sigs.k8s.io/controller-runtime/pkg/reconcile"
    32  	"sigs.k8s.io/controller-runtime/pkg/source"
    33  
    34  	. "github.com/onsi/ginkgo/v2"
    35  	. "github.com/onsi/gomega"
    36  	"sigs.k8s.io/controller-runtime/pkg/manager"
    37  )
    38  
    39  var _ = Describe("controller", func() {
    40  	var reconciled chan reconcile.Request
    41  	ctx := context.Background()
    42  
    43  	BeforeEach(func() {
    44  		reconciled = make(chan reconcile.Request)
    45  		Expect(cfg).NotTo(BeNil())
    46  	})
    47  
    48  	Describe("controller", func() {
    49  		// TODO(directxman12): write a whole suite of controller-client interaction tests
    50  
    51  		It("should reconcile", func() {
    52  			By("Creating the Manager")
    53  			cm, err := manager.New(cfg, manager.Options{})
    54  			Expect(err).NotTo(HaveOccurred())
    55  
    56  			By("Creating the Controller")
    57  			instance, err := controller.New("foo-controller", cm, controller.Options{
    58  				Reconciler: reconcile.Func(
    59  					func(_ context.Context, request reconcile.Request) (reconcile.Result, error) {
    60  						reconciled <- request
    61  						return reconcile.Result{}, nil
    62  					}),
    63  			})
    64  			Expect(err).NotTo(HaveOccurred())
    65  
    66  			By("Watching Resources")
    67  			err = instance.Watch(
    68  				source.Kind(cm.GetCache(), &appsv1.ReplicaSet{},
    69  					handler.TypedEnqueueRequestForOwner[*appsv1.ReplicaSet](cm.GetScheme(), cm.GetRESTMapper(), &appsv1.Deployment{}),
    70  				),
    71  			)
    72  			Expect(err).NotTo(HaveOccurred())
    73  
    74  			err = instance.Watch(source.Kind(cm.GetCache(), &appsv1.Deployment{}, &handler.TypedEnqueueRequestForObject[*appsv1.Deployment]{}))
    75  			Expect(err).NotTo(HaveOccurred())
    76  
    77  			err = cm.GetClient().Get(ctx, types.NamespacedName{Name: "foo"}, &corev1.Namespace{})
    78  			Expect(err).To(Equal(&cache.ErrCacheNotStarted{}))
    79  			err = cm.GetClient().List(ctx, &corev1.NamespaceList{})
    80  			Expect(err).To(Equal(&cache.ErrCacheNotStarted{}))
    81  
    82  			By("Starting the Manager")
    83  			ctx, cancel := context.WithCancel(context.Background())
    84  			defer cancel()
    85  			go func() {
    86  				defer GinkgoRecover()
    87  				Expect(cm.Start(ctx)).NotTo(HaveOccurred())
    88  			}()
    89  
    90  			deployment := &appsv1.Deployment{
    91  				ObjectMeta: metav1.ObjectMeta{Name: "deployment-name"},
    92  				Spec: appsv1.DeploymentSpec{
    93  					Selector: &metav1.LabelSelector{
    94  						MatchLabels: map[string]string{"foo": "bar"},
    95  					},
    96  					Template: corev1.PodTemplateSpec{
    97  						ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"foo": "bar"}},
    98  						Spec: corev1.PodSpec{
    99  							Containers: []corev1.Container{
   100  								{
   101  									Name:  "nginx",
   102  									Image: "nginx",
   103  									SecurityContext: &corev1.SecurityContext{
   104  										Privileged: truePtr(),
   105  									},
   106  								},
   107  							},
   108  						},
   109  					},
   110  				},
   111  			}
   112  			expectedReconcileRequest := reconcile.Request{NamespacedName: types.NamespacedName{
   113  				Namespace: "default",
   114  				Name:      "deployment-name",
   115  			}}
   116  
   117  			By("Invoking Reconciling for Create")
   118  			deployment, err = clientset.AppsV1().Deployments("default").Create(ctx, deployment, metav1.CreateOptions{})
   119  			Expect(err).NotTo(HaveOccurred())
   120  			Expect(<-reconciled).To(Equal(expectedReconcileRequest))
   121  
   122  			By("Invoking Reconciling for Update")
   123  			newDeployment := deployment.DeepCopy()
   124  			newDeployment.Labels = map[string]string{"foo": "bar"}
   125  			_, err = clientset.AppsV1().Deployments("default").Update(ctx, newDeployment, metav1.UpdateOptions{})
   126  			Expect(err).NotTo(HaveOccurred())
   127  			Expect(<-reconciled).To(Equal(expectedReconcileRequest))
   128  
   129  			By("Invoking Reconciling for an OwnedObject when it is created")
   130  			replicaset := &appsv1.ReplicaSet{
   131  				ObjectMeta: metav1.ObjectMeta{
   132  					Name: "rs-name",
   133  					OwnerReferences: []metav1.OwnerReference{
   134  						*metav1.NewControllerRef(deployment, schema.GroupVersionKind{
   135  							Group:   "apps",
   136  							Version: "v1",
   137  							Kind:    "Deployment",
   138  						}),
   139  					},
   140  				},
   141  				Spec: appsv1.ReplicaSetSpec{
   142  					Selector: &metav1.LabelSelector{
   143  						MatchLabels: map[string]string{"foo": "bar"},
   144  					},
   145  					Template: deployment.Spec.Template,
   146  				},
   147  			}
   148  			replicaset, err = clientset.AppsV1().ReplicaSets("default").Create(ctx, replicaset, metav1.CreateOptions{})
   149  			Expect(err).NotTo(HaveOccurred())
   150  			Expect(<-reconciled).To(Equal(expectedReconcileRequest))
   151  
   152  			By("Invoking Reconciling for an OwnedObject when it is updated")
   153  			newReplicaset := replicaset.DeepCopy()
   154  			newReplicaset.Labels = map[string]string{"foo": "bar"}
   155  			_, err = clientset.AppsV1().ReplicaSets("default").Update(ctx, newReplicaset, metav1.UpdateOptions{})
   156  			Expect(err).NotTo(HaveOccurred())
   157  			Expect(<-reconciled).To(Equal(expectedReconcileRequest))
   158  
   159  			By("Invoking Reconciling for an OwnedObject when it is deleted")
   160  			err = clientset.AppsV1().ReplicaSets("default").Delete(ctx, replicaset.Name, metav1.DeleteOptions{})
   161  			Expect(err).NotTo(HaveOccurred())
   162  			Expect(<-reconciled).To(Equal(expectedReconcileRequest))
   163  
   164  			By("Invoking Reconciling for Delete")
   165  			err = clientset.AppsV1().Deployments("default").
   166  				Delete(ctx, "deployment-name", metav1.DeleteOptions{})
   167  			Expect(err).NotTo(HaveOccurred())
   168  			Expect(<-reconciled).To(Equal(expectedReconcileRequest))
   169  
   170  			By("Listing a type with a slice of pointers as items field")
   171  			err = cm.GetClient().
   172  				List(context.Background(), &controllertest.UnconventionalListTypeList{})
   173  			Expect(err).NotTo(HaveOccurred())
   174  		})
   175  	})
   176  })
   177  
   178  func truePtr() *bool {
   179  	t := true
   180  	return &t
   181  }
   182  

View as plain text