1
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
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