1
16
17 package client_test
18
19 import (
20 "context"
21 "encoding/json"
22 "errors"
23 "fmt"
24 "reflect"
25 "sync/atomic"
26 "time"
27
28 . "github.com/onsi/ginkgo/v2"
29 . "github.com/onsi/gomega"
30 appsv1 "k8s.io/api/apps/v1"
31 authenticationv1 "k8s.io/api/authentication/v1"
32 autoscalingv1 "k8s.io/api/autoscaling/v1"
33 certificatesv1 "k8s.io/api/certificates/v1"
34 corev1 "k8s.io/api/core/v1"
35 policyv1 "k8s.io/api/policy/v1"
36 apierrors "k8s.io/apimachinery/pkg/api/errors"
37 "k8s.io/apimachinery/pkg/api/meta"
38 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
39 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
40 "k8s.io/apimachinery/pkg/runtime"
41 "k8s.io/apimachinery/pkg/runtime/schema"
42 "k8s.io/apimachinery/pkg/types"
43 kscheme "k8s.io/client-go/kubernetes/scheme"
44 "k8s.io/utils/ptr"
45
46 "sigs.k8s.io/controller-runtime/examples/crd/pkg"
47 "sigs.k8s.io/controller-runtime/pkg/cache"
48 "sigs.k8s.io/controller-runtime/pkg/client"
49 )
50
51 func deleteDeployment(ctx context.Context, dep *appsv1.Deployment, ns string) {
52 _, err := clientset.AppsV1().Deployments(ns).Get(ctx, dep.Name, metav1.GetOptions{})
53 if err == nil {
54 err = clientset.AppsV1().Deployments(ns).Delete(ctx, dep.Name, metav1.DeleteOptions{})
55 Expect(err).NotTo(HaveOccurred())
56 }
57 }
58
59 func deleteNamespace(ctx context.Context, ns *corev1.Namespace) {
60 ns, err := clientset.CoreV1().Namespaces().Get(ctx, ns.Name, metav1.GetOptions{})
61 if err != nil {
62 return
63 }
64
65 err = clientset.CoreV1().Namespaces().Delete(ctx, ns.Name, metav1.DeleteOptions{})
66 Expect(err).NotTo(HaveOccurred())
67
68
69 pos := -1
70 finalizers := ns.Spec.Finalizers
71 for i, fin := range finalizers {
72 if fin == "kubernetes" {
73 pos = i
74 break
75 }
76 }
77 if pos == -1 {
78
79 return
80 }
81
82
83 ns, err = clientset.CoreV1().Namespaces().Get(ctx, ns.Name, metav1.GetOptions{})
84 if err != nil {
85 return
86 }
87
88 ns.Spec.Finalizers = append(finalizers[:pos], finalizers[pos+1:]...)
89 _, err = clientset.CoreV1().Namespaces().Finalize(ctx, ns, metav1.UpdateOptions{})
90 Expect(err).NotTo(HaveOccurred())
91
92 WAIT_LOOP:
93 for i := 0; i < 10; i++ {
94 ns, err = clientset.CoreV1().Namespaces().Get(ctx, ns.Name, metav1.GetOptions{})
95 if apierrors.IsNotFound(err) {
96
97 return
98 }
99 select {
100 case <-ctx.Done():
101 break WAIT_LOOP
102
103 case <-time.After(100 * time.Millisecond):
104
105 }
106 }
107 Fail(fmt.Sprintf("timed out waiting for namespace %q to be deleted", ns.Name))
108 }
109
110 type mockPatchOption struct {
111 applied bool
112 }
113
114 func (o *mockPatchOption) ApplyToPatch(_ *client.PatchOptions) {
115 o.applied = true
116 }
117
118
119
120
121
122 func metaOnlyFromObj(obj interface {
123 runtime.Object
124 metav1.ObjectMetaAccessor
125 }, scheme *runtime.Scheme) *metav1.PartialObjectMetadata {
126 metaObj := metav1.PartialObjectMetadata{}
127 obj.GetObjectMeta().(*metav1.ObjectMeta).DeepCopyInto(&metaObj.ObjectMeta)
128 kinds, _, err := scheme.ObjectKinds(obj)
129 if err != nil {
130 panic(err)
131 }
132 metaObj.SetGroupVersionKind(kinds[0])
133 return &metaObj
134 }
135
136 var _ = Describe("Client", func() {
137
138 var scheme *runtime.Scheme
139 var depGvk schema.GroupVersionKind
140 var dep *appsv1.Deployment
141 var pod *corev1.Pod
142 var node *corev1.Node
143 var serviceAccount *corev1.ServiceAccount
144 var csr *certificatesv1.CertificateSigningRequest
145 var count uint64 = 0
146 var replicaCount int32 = 2
147 var ns = "default"
148 var errNotCached *cache.ErrResourceNotCached
149 ctx := context.TODO()
150
151 BeforeEach(func() {
152 atomic.AddUint64(&count, 1)
153 dep = &appsv1.Deployment{
154 ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("deployment-name-%v", count), Namespace: ns, Labels: map[string]string{"app": fmt.Sprintf("bar-%v", count)}},
155 Spec: appsv1.DeploymentSpec{
156 Replicas: &replicaCount,
157 Selector: &metav1.LabelSelector{
158 MatchLabels: map[string]string{"foo": "bar"},
159 },
160 Template: corev1.PodTemplateSpec{
161 ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"foo": "bar"}},
162 Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "nginx", Image: "nginx"}}},
163 },
164 },
165 }
166 depGvk = schema.GroupVersionKind{
167 Group: "apps",
168 Kind: "Deployment",
169 Version: "v1",
170 }
171
172 pod = &corev1.Pod{
173 ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("pod-%v", count), Namespace: ns},
174 Spec: corev1.PodSpec{},
175 }
176 node = &corev1.Node{
177 ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("node-name-%v", count)},
178 Spec: corev1.NodeSpec{},
179 }
180 serviceAccount = &corev1.ServiceAccount{ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("sa-%v", count), Namespace: ns}}
181 csr = &certificatesv1.CertificateSigningRequest{
182 ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("csr-%v", count)},
183 Spec: certificatesv1.CertificateSigningRequestSpec{
184 SignerName: "org.io/my-signer",
185 Request: []byte(`-----BEGIN CERTIFICATE REQUEST-----
186 MIIChzCCAW8CAQAwQjELMAkGA1UEBhMCWFgxFTATBgNVBAcMDERlZmF1bHQgQ2l0
187 eTEcMBoGA1UECgwTRGVmYXVsdCBDb21wYW55IEx0ZDCCASIwDQYJKoZIhvcNAQEB
188 BQADggEPADCCAQoCggEBANe06dLX/bDNm6mVEnKdJexcJM6WKMFSt5o6BEdD1+Ki
189 WyUcvfNgIBbwAZjkF9U1r7+KuDcc6XYFnb6ky1wPo4C+XwcIIx7Nnbf8IdWJukPb
190 2BCsqO4NCsG6kKFavmH9J3q//nwKUvlQE+AJ2MPuOAZTwZ4KskghiGuS8hyk6/PZ
191 XH9QhV7Jma43bDzQozd2C7OujRBhLsuP94KSu839RRFWd9ms3XHgTxLxb7nxwZDx
192 9l7/ZVAObJoQYlHENqs12NCVP4gpJfbcY8/rd+IG4ftcZEmpeO4kKO+d2TpRKQqw
193 bjCMoAdD5Y43iLTtyql4qRnbMe3nxYG2+1inEryuV/cCAwEAAaAAMA0GCSqGSIb3
194 DQEBCwUAA4IBAQDH5hDByRN7wERQtC/o6uc8Y+yhjq9YcBJjjbnD6Vwru5pOdWtx
195 qfKkkXI5KNOdEhWzLnJyOcWHjj8UoHqI3AjxGC7dTM95eGjxQGUpsUOX8JSd4MiZ
196 cct4g4BKBj02AGqZLiEgN+PLCYAmEaYU7oZc4OAh6WzMrljNRsj66awMQpw8O1eY
197 YuBa8vwz8ko8vn/pn7IrFu8cZ+EA3rluJ+budX/QrEGi1hijg27q7/Qr0wNI9f1v
198 086mLKdqaBTkblXWEvF3WP4CcLNyrSNi4eu+G0fcAgGp1F/Nqh0MuWKSOLprv5Om
199 U5wwSivyi7vmegHKmblOzNVKA5qPO8zWzqBC
200 -----END CERTIFICATE REQUEST-----`),
201 Usages: []certificatesv1.KeyUsage{certificatesv1.UsageClientAuth},
202 },
203 }
204 scheme = kscheme.Scheme
205 })
206
207 var delOptions *metav1.DeleteOptions
208 AfterEach(func() {
209
210 var zero int64 = 0
211 policy := metav1.DeletePropagationForeground
212 delOptions = &metav1.DeleteOptions{
213 GracePeriodSeconds: &zero,
214 PropagationPolicy: &policy,
215 }
216 deleteDeployment(ctx, dep, ns)
217 _, err := clientset.CoreV1().Nodes().Get(ctx, node.Name, metav1.GetOptions{})
218 if err == nil {
219 err = clientset.CoreV1().Nodes().Delete(ctx, node.Name, *delOptions)
220 Expect(err).NotTo(HaveOccurred())
221 }
222 err = clientset.CoreV1().ServiceAccounts(ns).Delete(ctx, serviceAccount.Name, *delOptions)
223 Expect(client.IgnoreNotFound(err)).NotTo(HaveOccurred())
224
225 err = clientset.CertificatesV1().CertificateSigningRequests().Delete(ctx, csr.Name, *delOptions)
226 Expect(client.IgnoreNotFound(err)).NotTo(HaveOccurred())
227 })
228
229 Describe("New", func() {
230 It("should return a new Client", func() {
231 cl, err := client.New(cfg, client.Options{})
232 Expect(err).NotTo(HaveOccurred())
233 Expect(cl).NotTo(BeNil())
234 })
235
236 It("should fail if the config is nil", func() {
237 cl, err := client.New(nil, client.Options{})
238 Expect(err).To(HaveOccurred())
239 Expect(cl).To(BeNil())
240 })
241
242 It("should use the provided Scheme if provided", func() {
243 cl, err := client.New(cfg, client.Options{Scheme: scheme})
244 Expect(err).NotTo(HaveOccurred())
245 Expect(cl).NotTo(BeNil())
246 Expect(cl.Scheme()).ToNot(BeNil())
247 Expect(cl.Scheme()).To(Equal(scheme))
248 })
249
250 It("should default the Scheme if not provided", func() {
251 cl, err := client.New(cfg, client.Options{})
252 Expect(err).NotTo(HaveOccurred())
253 Expect(cl).NotTo(BeNil())
254 Expect(cl.Scheme()).ToNot(BeNil())
255 Expect(cl.Scheme()).To(Equal(kscheme.Scheme))
256 })
257
258 It("should use the provided Mapper if provided", func() {
259 mapper := meta.NewDefaultRESTMapper([]schema.GroupVersion{})
260 cl, err := client.New(cfg, client.Options{Mapper: mapper})
261 Expect(err).NotTo(HaveOccurred())
262 Expect(cl).NotTo(BeNil())
263 Expect(cl.RESTMapper()).ToNot(BeNil())
264 Expect(cl.RESTMapper()).To(Equal(mapper))
265 })
266
267 It("should create a Mapper if not provided", func() {
268 cl, err := client.New(cfg, client.Options{})
269 Expect(err).NotTo(HaveOccurred())
270 Expect(cl).NotTo(BeNil())
271 Expect(cl.RESTMapper()).ToNot(BeNil())
272 })
273
274 It("should use the provided reader cache if provided, on get and list", func() {
275 cache := &fakeReader{}
276 cl, err := client.New(cfg, client.Options{Cache: &client.CacheOptions{Reader: cache}})
277 Expect(err).NotTo(HaveOccurred())
278 Expect(cl).NotTo(BeNil())
279 Expect(cl.Get(ctx, client.ObjectKey{Name: "test"}, &appsv1.Deployment{})).To(Succeed())
280 Expect(cl.List(ctx, &appsv1.DeploymentList{})).To(Succeed())
281 Expect(cache.Called).To(Equal(2))
282 })
283
284 It("should propagate ErrResourceNotCached errors", func() {
285 c := &fakeUncachedReader{}
286 cl, err := client.New(cfg, client.Options{Cache: &client.CacheOptions{Reader: c}})
287 Expect(err).NotTo(HaveOccurred())
288 Expect(cl).NotTo(BeNil())
289 Expect(errors.As(cl.Get(ctx, client.ObjectKey{Name: "test"}, &appsv1.Deployment{}), &errNotCached)).To(BeTrue())
290 Expect(errors.As(cl.List(ctx, &appsv1.DeploymentList{}), &errNotCached)).To(BeTrue())
291 Expect(c.Called).To(Equal(2))
292 })
293
294 It("should not use the provided reader cache if provided, on get and list for uncached GVKs", func() {
295 cache := &fakeReader{}
296 cl, err := client.New(cfg, client.Options{Cache: &client.CacheOptions{Reader: cache, DisableFor: []client.Object{&corev1.Namespace{}}}})
297 Expect(err).NotTo(HaveOccurred())
298 Expect(cl).NotTo(BeNil())
299 Expect(cl.Get(ctx, client.ObjectKey{Name: "default"}, &corev1.Namespace{})).To(Succeed())
300 Expect(cl.List(ctx, &corev1.NamespaceList{})).To(Succeed())
301 Expect(cache.Called).To(Equal(0))
302 })
303 })
304
305 Describe("Create", func() {
306 Context("with structured objects", func() {
307 It("should create a new object from a go struct", func() {
308 cl, err := client.New(cfg, client.Options{})
309 Expect(err).NotTo(HaveOccurred())
310 Expect(cl).NotTo(BeNil())
311
312 By("creating the object")
313 err = cl.Create(context.TODO(), dep)
314 Expect(err).NotTo(HaveOccurred())
315
316 actual, err := clientset.AppsV1().Deployments(ns).Get(ctx, dep.Name, metav1.GetOptions{})
317 Expect(err).NotTo(HaveOccurred())
318 Expect(actual).NotTo(BeNil())
319
320 By("writing the result back to the go struct")
321 Expect(dep).To(Equal(actual))
322 })
323
324 It("should create a new object non-namespace object from a go struct", func() {
325 cl, err := client.New(cfg, client.Options{})
326 Expect(err).NotTo(HaveOccurred())
327 Expect(cl).NotTo(BeNil())
328
329 By("creating the object")
330 err = cl.Create(context.TODO(), node)
331 Expect(err).NotTo(HaveOccurred())
332
333 actual, err := clientset.CoreV1().Nodes().Get(ctx, node.Name, metav1.GetOptions{})
334 Expect(err).NotTo(HaveOccurred())
335 Expect(actual).NotTo(BeNil())
336
337 By("writing the result back to the go struct")
338 Expect(node).To(Equal(actual))
339 })
340
341 It("should fail if the object already exists", func() {
342 cl, err := client.New(cfg, client.Options{})
343 Expect(err).NotTo(HaveOccurred())
344 Expect(cl).NotTo(BeNil())
345
346 old := dep.DeepCopy()
347
348 By("creating the object")
349 err = cl.Create(context.TODO(), dep)
350 Expect(err).NotTo(HaveOccurred())
351
352 actual, err := clientset.AppsV1().Deployments(ns).Get(ctx, dep.Name, metav1.GetOptions{})
353 Expect(err).NotTo(HaveOccurred())
354 Expect(actual).NotTo(BeNil())
355
356 By("creating the object a second time")
357 err = cl.Create(context.TODO(), old)
358 Expect(err).To(HaveOccurred())
359 Expect(apierrors.IsAlreadyExists(err)).To(BeTrue())
360 })
361
362 It("should fail if the object does not pass server-side validation", func() {
363 cl, err := client.New(cfg, client.Options{})
364 Expect(err).NotTo(HaveOccurred())
365 Expect(cl).NotTo(BeNil())
366
367 By("creating the pod, since required field Containers is empty")
368 err = cl.Create(context.TODO(), pod)
369 Expect(err).To(HaveOccurred())
370
371
372 })
373
374 It("should fail if the object cannot be mapped to a GVK", func() {
375 By("creating client with empty Scheme")
376 emptyScheme := runtime.NewScheme()
377 cl, err := client.New(cfg, client.Options{Scheme: emptyScheme})
378 Expect(err).NotTo(HaveOccurred())
379 Expect(cl).NotTo(BeNil())
380
381 By("creating the object fails")
382 err = cl.Create(context.TODO(), dep)
383 Expect(err).To(HaveOccurred())
384 Expect(err.Error()).To(ContainSubstring("no kind is registered for the type"))
385 })
386
387 PIt("should fail if the GVK cannot be mapped to a Resource", func() {
388
389
390 })
391
392 Context("with the DryRun option", func() {
393 It("should not create a new object, global option", func() {
394 cl, err := client.New(cfg, client.Options{DryRun: ptr.To(true)})
395 Expect(err).NotTo(HaveOccurred())
396 Expect(cl).NotTo(BeNil())
397
398 By("creating the object (with DryRun)")
399 err = cl.Create(context.TODO(), dep)
400 Expect(err).NotTo(HaveOccurred())
401
402 actual, err := clientset.AppsV1().Deployments(ns).Get(ctx, dep.Name, metav1.GetOptions{})
403 Expect(err).To(HaveOccurred())
404 Expect(apierrors.IsNotFound(err)).To(BeTrue())
405 Expect(actual).To(Equal(&appsv1.Deployment{}))
406 })
407
408 It("should not create a new object, inline option", func() {
409 cl, err := client.New(cfg, client.Options{})
410 Expect(err).NotTo(HaveOccurred())
411 Expect(cl).NotTo(BeNil())
412
413 By("creating the object (with DryRun)")
414 err = cl.Create(context.TODO(), dep, client.DryRunAll)
415 Expect(err).NotTo(HaveOccurred())
416
417 actual, err := clientset.AppsV1().Deployments(ns).Get(ctx, dep.Name, metav1.GetOptions{})
418 Expect(err).To(HaveOccurred())
419 Expect(apierrors.IsNotFound(err)).To(BeTrue())
420 Expect(actual).To(Equal(&appsv1.Deployment{}))
421 })
422 })
423 })
424
425 Context("with unstructured objects", func() {
426 It("should create a new object from a go struct", func() {
427 cl, err := client.New(cfg, client.Options{})
428 Expect(err).NotTo(HaveOccurred())
429 Expect(cl).NotTo(BeNil())
430
431 By("encoding the deployment as unstructured")
432 u := &unstructured.Unstructured{}
433 Expect(scheme.Convert(dep, u, nil)).To(Succeed())
434 u.SetGroupVersionKind(schema.GroupVersionKind{
435 Group: "apps",
436 Kind: "Deployment",
437 Version: "v1",
438 })
439
440 By("creating the object")
441 err = cl.Create(context.TODO(), u)
442 Expect(err).NotTo(HaveOccurred())
443
444 actual, err := clientset.AppsV1().Deployments(ns).Get(ctx, dep.Name, metav1.GetOptions{})
445 Expect(err).NotTo(HaveOccurred())
446 Expect(actual).NotTo(BeNil())
447 })
448
449 It("should create a new non-namespace object ", func() {
450 cl, err := client.New(cfg, client.Options{})
451 Expect(err).NotTo(HaveOccurred())
452 Expect(cl).NotTo(BeNil())
453
454 By("encoding the deployment as unstructured")
455 u := &unstructured.Unstructured{}
456 Expect(scheme.Convert(node, u, nil)).To(Succeed())
457 u.SetGroupVersionKind(schema.GroupVersionKind{
458 Group: "",
459 Kind: "Node",
460 Version: "v1",
461 })
462
463 By("creating the object")
464 err = cl.Create(context.TODO(), node)
465 Expect(err).NotTo(HaveOccurred())
466
467 actual, err := clientset.CoreV1().Nodes().Get(ctx, node.Name, metav1.GetOptions{})
468 Expect(err).NotTo(HaveOccurred())
469 Expect(actual).NotTo(BeNil())
470 au := &unstructured.Unstructured{}
471 Expect(scheme.Convert(actual, au, nil)).To(Succeed())
472 Expect(scheme.Convert(node, u, nil)).To(Succeed())
473 By("writing the result back to the go struct")
474
475 Expect(u).To(Equal(au))
476 })
477
478 It("should fail if the object already exists", func() {
479 cl, err := client.New(cfg, client.Options{})
480 Expect(err).NotTo(HaveOccurred())
481 Expect(cl).NotTo(BeNil())
482
483 old := dep.DeepCopy()
484
485 By("creating the object")
486 err = cl.Create(context.TODO(), dep)
487 Expect(err).NotTo(HaveOccurred())
488 actual, err := clientset.AppsV1().Deployments(ns).Get(ctx, dep.Name, metav1.GetOptions{})
489 Expect(err).NotTo(HaveOccurred())
490 Expect(actual).NotTo(BeNil())
491
492 By("encoding the deployment as unstructured")
493 u := &unstructured.Unstructured{}
494 Expect(scheme.Convert(old, u, nil)).To(Succeed())
495 u.SetGroupVersionKind(schema.GroupVersionKind{
496 Group: "apps",
497 Kind: "Deployment",
498 Version: "v1",
499 })
500
501 By("creating the object a second time")
502 err = cl.Create(context.TODO(), u)
503 Expect(err).To(HaveOccurred())
504 Expect(apierrors.IsAlreadyExists(err)).To(BeTrue())
505 })
506
507 It("should fail if the object does not pass server-side validation", func() {
508 cl, err := client.New(cfg, client.Options{})
509 Expect(err).NotTo(HaveOccurred())
510 Expect(cl).NotTo(BeNil())
511
512 By("creating the pod, since required field Containers is empty")
513 u := &unstructured.Unstructured{}
514 Expect(scheme.Convert(pod, u, nil)).To(Succeed())
515 u.SetGroupVersionKind(schema.GroupVersionKind{
516 Group: "",
517 Version: "v1",
518 Kind: "Pod",
519 })
520 err = cl.Create(context.TODO(), u)
521 Expect(err).To(HaveOccurred())
522
523
524 })
525
526 })
527
528 Context("with metadata objects", func() {
529 It("should fail with an error", func() {
530 cl, err := client.New(cfg, client.Options{})
531 Expect(err).NotTo(HaveOccurred())
532
533 obj := metaOnlyFromObj(dep, scheme)
534 Expect(cl.Create(context.TODO(), obj)).NotTo(Succeed())
535 })
536 })
537
538 Context("with the DryRun option", func() {
539 It("should not create a new object from a go struct", func() {
540 cl, err := client.New(cfg, client.Options{})
541 Expect(err).NotTo(HaveOccurred())
542 Expect(cl).NotTo(BeNil())
543
544 By("encoding the deployment as unstructured")
545 u := &unstructured.Unstructured{}
546 Expect(scheme.Convert(dep, u, nil)).To(Succeed())
547 u.SetGroupVersionKind(schema.GroupVersionKind{
548 Group: "apps",
549 Kind: "Deployment",
550 Version: "v1",
551 })
552
553 By("creating the object")
554 err = cl.Create(context.TODO(), u, client.DryRunAll)
555 Expect(err).NotTo(HaveOccurred())
556
557 actual, err := clientset.AppsV1().Deployments(ns).Get(ctx, dep.Name, metav1.GetOptions{})
558 Expect(err).To(HaveOccurred())
559 Expect(apierrors.IsNotFound(err)).To(BeTrue())
560 Expect(actual).To(Equal(&appsv1.Deployment{}))
561 })
562 })
563 })
564
565 Describe("Update", func() {
566 Context("with structured objects", func() {
567 It("should update an existing object from a go struct", func() {
568 cl, err := client.New(cfg, client.Options{})
569 Expect(err).NotTo(HaveOccurred())
570 Expect(cl).NotTo(BeNil())
571
572 By("initially creating a Deployment")
573 dep, err := clientset.AppsV1().Deployments(ns).Create(ctx, dep, metav1.CreateOptions{})
574 Expect(err).NotTo(HaveOccurred())
575
576 By("updating the Deployment")
577 dep.Annotations = map[string]string{"foo": "bar"}
578 err = cl.Update(context.TODO(), dep)
579 Expect(err).NotTo(HaveOccurred())
580
581 By("validating updated Deployment has new annotation")
582 actual, err := clientset.AppsV1().Deployments(ns).Get(ctx, dep.Name, metav1.GetOptions{})
583 Expect(err).NotTo(HaveOccurred())
584 Expect(actual).NotTo(BeNil())
585 Expect(actual.Annotations["foo"]).To(Equal("bar"))
586 })
587
588 It("should update and preserve type information", func() {
589 cl, err := client.New(cfg, client.Options{})
590 Expect(err).NotTo(HaveOccurred())
591 Expect(cl).NotTo(BeNil())
592
593 By("initially creating a Deployment")
594 dep, err := clientset.AppsV1().Deployments(ns).Create(ctx, dep, metav1.CreateOptions{})
595 Expect(err).NotTo(HaveOccurred())
596
597 By("updating the Deployment")
598 dep.SetGroupVersionKind(depGvk)
599 err = cl.Update(context.TODO(), dep)
600 Expect(err).NotTo(HaveOccurred())
601
602 By("validating updated Deployment has type information")
603 Expect(dep.GroupVersionKind()).To(Equal(depGvk))
604 })
605
606 It("should update an existing object non-namespace object from a go struct", func() {
607 cl, err := client.New(cfg, client.Options{})
608 Expect(err).NotTo(HaveOccurred())
609 Expect(cl).NotTo(BeNil())
610
611 node, err := clientset.CoreV1().Nodes().Create(ctx, node, metav1.CreateOptions{})
612 Expect(err).NotTo(HaveOccurred())
613
614 By("updating the object")
615 node.Annotations = map[string]string{"foo": "bar"}
616 err = cl.Update(context.TODO(), node)
617 Expect(err).NotTo(HaveOccurred())
618
619 By("validate updated Node had new annotation")
620 actual, err := clientset.CoreV1().Nodes().Get(ctx, node.Name, metav1.GetOptions{})
621 Expect(err).NotTo(HaveOccurred())
622 Expect(actual).NotTo(BeNil())
623 Expect(actual.Annotations["foo"]).To(Equal("bar"))
624 })
625
626 It("should fail if the object does not exist", func() {
627 cl, err := client.New(cfg, client.Options{})
628 Expect(err).NotTo(HaveOccurred())
629 Expect(cl).NotTo(BeNil())
630
631 By("updating non-existent object")
632 err = cl.Update(context.TODO(), dep)
633 Expect(err).To(HaveOccurred())
634 })
635
636 PIt("should fail if the object does not pass server-side validation", func() {
637
638 })
639
640 PIt("should fail if the object doesn't have meta", func() {
641
642 })
643
644 It("should fail if the object cannot be mapped to a GVK", func() {
645 By("creating client with empty Scheme")
646 emptyScheme := runtime.NewScheme()
647 cl, err := client.New(cfg, client.Options{Scheme: emptyScheme})
648 Expect(err).NotTo(HaveOccurred())
649 Expect(cl).NotTo(BeNil())
650
651 By("initially creating a Deployment")
652 dep, err := clientset.AppsV1().Deployments(ns).Create(ctx, dep, metav1.CreateOptions{})
653 Expect(err).NotTo(HaveOccurred())
654
655 By("updating the Deployment")
656 dep.Annotations = map[string]string{"foo": "bar"}
657 err = cl.Update(context.TODO(), dep)
658 Expect(err).To(HaveOccurred())
659 Expect(err.Error()).To(ContainSubstring("no kind is registered for the type"))
660 })
661
662 PIt("should fail if the GVK cannot be mapped to a Resource", func() {
663
664 })
665 })
666 Context("with unstructured objects", func() {
667 It("should update an existing object from a go struct", func() {
668 cl, err := client.New(cfg, client.Options{})
669 Expect(err).NotTo(HaveOccurred())
670 Expect(cl).NotTo(BeNil())
671
672 By("initially creating a Deployment")
673 dep, err := clientset.AppsV1().Deployments(ns).Create(ctx, dep, metav1.CreateOptions{})
674 Expect(err).NotTo(HaveOccurred())
675
676 By("updating the Deployment")
677 u := &unstructured.Unstructured{}
678 Expect(scheme.Convert(dep, u, nil)).To(Succeed())
679 u.SetGroupVersionKind(schema.GroupVersionKind{
680 Group: "apps",
681 Kind: "Deployment",
682 Version: "v1",
683 })
684 u.SetAnnotations(map[string]string{"foo": "bar"})
685 err = cl.Update(context.TODO(), u)
686 Expect(err).NotTo(HaveOccurred())
687
688 By("validating updated Deployment has new annotation")
689 actual, err := clientset.AppsV1().Deployments(ns).Get(ctx, dep.Name, metav1.GetOptions{})
690 Expect(err).NotTo(HaveOccurred())
691 Expect(actual).NotTo(BeNil())
692 Expect(actual.Annotations["foo"]).To(Equal("bar"))
693 })
694
695 It("should update and preserve type information", func() {
696 cl, err := client.New(cfg, client.Options{})
697 Expect(err).NotTo(HaveOccurred())
698 Expect(cl).NotTo(BeNil())
699
700 By("initially creating a Deployment")
701 dep, err := clientset.AppsV1().Deployments(ns).Create(ctx, dep, metav1.CreateOptions{})
702 Expect(err).NotTo(HaveOccurred())
703
704 By("updating the Deployment")
705 u := &unstructured.Unstructured{}
706 Expect(scheme.Convert(dep, u, nil)).To(Succeed())
707 u.SetGroupVersionKind(depGvk)
708 u.SetAnnotations(map[string]string{"foo": "bar"})
709 err = cl.Update(context.TODO(), u)
710 Expect(err).NotTo(HaveOccurred())
711
712 By("validating updated Deployment has type information")
713 Expect(u.GroupVersionKind()).To(Equal(depGvk))
714 })
715
716 It("should update an existing object non-namespace object from a go struct", func() {
717 cl, err := client.New(cfg, client.Options{})
718 Expect(err).NotTo(HaveOccurred())
719 Expect(cl).NotTo(BeNil())
720
721 node, err := clientset.CoreV1().Nodes().Create(ctx, node, metav1.CreateOptions{})
722 Expect(err).NotTo(HaveOccurred())
723
724 By("updating the object")
725 u := &unstructured.Unstructured{}
726 Expect(scheme.Convert(node, u, nil)).To(Succeed())
727 u.SetGroupVersionKind(schema.GroupVersionKind{
728 Group: "",
729 Kind: "Node",
730 Version: "v1",
731 })
732 u.SetAnnotations(map[string]string{"foo": "bar"})
733 err = cl.Update(context.TODO(), u)
734 Expect(err).NotTo(HaveOccurred())
735
736 By("validate updated Node had new annotation")
737 actual, err := clientset.CoreV1().Nodes().Get(ctx, node.Name, metav1.GetOptions{})
738 Expect(err).NotTo(HaveOccurred())
739 Expect(actual).NotTo(BeNil())
740 Expect(actual.Annotations["foo"]).To(Equal("bar"))
741 })
742 It("should fail if the object does not exist", func() {
743 cl, err := client.New(cfg, client.Options{})
744 Expect(err).NotTo(HaveOccurred())
745 Expect(cl).NotTo(BeNil())
746
747 By("updating non-existent object")
748 u := &unstructured.Unstructured{}
749 Expect(scheme.Convert(dep, u, nil)).To(Succeed())
750 u.SetGroupVersionKind(depGvk)
751 err = cl.Update(context.TODO(), dep)
752 Expect(err).To(HaveOccurred())
753 })
754 })
755 Context("with metadata objects", func() {
756 It("should fail with an error", func() {
757 cl, err := client.New(cfg, client.Options{})
758 Expect(err).NotTo(HaveOccurred())
759
760 obj := metaOnlyFromObj(dep, scheme)
761
762 Expect(cl.Update(context.TODO(), obj)).NotTo(Succeed())
763 })
764 })
765 })
766
767 Describe("Patch", func() {
768 Context("Metadata Client", func() {
769 It("should merge patch with options", func() {
770 cl, err := client.New(cfg, client.Options{})
771 Expect(err).NotTo(HaveOccurred())
772 Expect(cl).NotTo(BeNil())
773
774 By("initially creating a Deployment")
775 dep, err := clientset.AppsV1().Deployments(ns).Create(ctx, dep, metav1.CreateOptions{})
776 Expect(err).NotTo(HaveOccurred())
777
778 metadata := metaOnlyFromObj(dep, scheme)
779 if metadata.Labels == nil {
780 metadata.Labels = make(map[string]string)
781 }
782 metadata.Labels["foo"] = "bar"
783
784 testOption := &mockPatchOption{}
785 Expect(cl.Patch(context.TODO(), metadata, client.Merge, testOption)).To(Succeed())
786
787 By("validating that patched metadata has new labels")
788 actual, err := clientset.AppsV1().Deployments(ns).Get(ctx, dep.Name, metav1.GetOptions{})
789 Expect(err).NotTo(HaveOccurred())
790 Expect(actual).NotTo(BeNil())
791 Expect(actual.Labels["foo"]).To(Equal("bar"))
792
793 By("validating patch options were applied")
794 Expect(testOption.applied).To(BeTrue())
795 })
796 })
797 })
798
799 Describe("SubResourceClient", func() {
800 Context("with structured objects", func() {
801 It("should be able to read the Scale subresource", func() {
802 cl, err := client.New(cfg, client.Options{})
803 Expect(err).NotTo(HaveOccurred())
804 Expect(cl).NotTo(BeNil())
805
806 By("Creating a deployment")
807 dep, err := clientset.AppsV1().Deployments(dep.Namespace).Create(ctx, dep, metav1.CreateOptions{})
808 Expect(err).NotTo(HaveOccurred())
809
810 By("reading the scale subresource")
811 scale := &autoscalingv1.Scale{}
812 err = cl.SubResource("scale").Get(ctx, dep, scale, &client.SubResourceGetOptions{})
813 Expect(err).NotTo(HaveOccurred())
814 Expect(scale.Spec.Replicas).To(Equal(*dep.Spec.Replicas))
815 })
816 It("should be able to create ServiceAccount tokens", func() {
817 cl, err := client.New(cfg, client.Options{})
818 Expect(err).NotTo(HaveOccurred())
819 Expect(cl).NotTo(BeNil())
820
821 By("Creating the serviceAccount")
822 _, err = clientset.CoreV1().ServiceAccounts(serviceAccount.Namespace).Create(ctx, serviceAccount, metav1.CreateOptions{})
823 Expect((err)).NotTo(HaveOccurred())
824
825 token := &authenticationv1.TokenRequest{}
826 err = cl.SubResource("token").Create(ctx, serviceAccount, token, &client.SubResourceCreateOptions{})
827 Expect(err).NotTo(HaveOccurred())
828
829 Expect(token.Status.Token).NotTo(Equal(""))
830 })
831
832 It("should be able to create Pod evictions", func() {
833 cl, err := client.New(cfg, client.Options{})
834 Expect(err).NotTo(HaveOccurred())
835 Expect(cl).NotTo(BeNil())
836
837
838 pod.Spec.Containers = []corev1.Container{{Name: "foo", Image: "busybox"}}
839
840 By("Creating the pod")
841 pod, err = clientset.CoreV1().Pods(pod.Namespace).Create(ctx, pod, metav1.CreateOptions{})
842 Expect(err).NotTo(HaveOccurred())
843
844 By("Creating the eviction")
845 eviction := &policyv1.Eviction{
846 DeleteOptions: &metav1.DeleteOptions{GracePeriodSeconds: ptr.To(int64(0))},
847 }
848 err = cl.SubResource("eviction").Create(ctx, pod, eviction, &client.SubResourceCreateOptions{})
849 Expect((err)).NotTo(HaveOccurred())
850
851 By("Asserting the pod is gone")
852 _, err = clientset.CoreV1().Pods(pod.Namespace).Get(ctx, pod.Name, metav1.GetOptions{})
853 Expect(apierrors.IsNotFound(err)).To(BeTrue())
854 })
855
856 It("should be able to create Pod bindings", func() {
857 cl, err := client.New(cfg, client.Options{})
858 Expect(err).NotTo(HaveOccurred())
859 Expect(cl).NotTo(BeNil())
860
861
862 pod.Spec.Containers = []corev1.Container{{Name: "foo", Image: "busybox"}}
863
864 By("Creating the pod")
865 pod, err = clientset.CoreV1().Pods(pod.Namespace).Create(ctx, pod, metav1.CreateOptions{})
866 Expect(err).NotTo(HaveOccurred())
867
868 By("Creating the binding")
869 binding := &corev1.Binding{
870 Target: corev1.ObjectReference{Name: node.Name},
871 }
872 err = cl.SubResource("binding").Create(ctx, pod, binding, &client.SubResourceCreateOptions{})
873 Expect((err)).NotTo(HaveOccurred())
874
875 By("Asserting the pod is bound")
876 pod, err = clientset.CoreV1().Pods(pod.Namespace).Get(ctx, pod.Name, metav1.GetOptions{})
877 Expect(err).NotTo(HaveOccurred())
878 Expect(pod.Spec.NodeName).To(Equal(node.Name))
879 })
880
881 It("should be able to approve CSRs", func() {
882 cl, err := client.New(cfg, client.Options{})
883 Expect(err).NotTo(HaveOccurred())
884 Expect(cl).NotTo(BeNil())
885
886 By("Creating the CSR")
887 csr, err := clientset.CertificatesV1().CertificateSigningRequests().Create(ctx, csr, metav1.CreateOptions{})
888 Expect(err).NotTo(HaveOccurred())
889
890 By("Approving the CSR")
891 csr.Status.Conditions = append(csr.Status.Conditions, certificatesv1.CertificateSigningRequestCondition{
892 Type: certificatesv1.CertificateApproved,
893 Status: corev1.ConditionTrue,
894 })
895 err = cl.SubResource("approval").Update(ctx, csr, &client.SubResourceUpdateOptions{})
896 Expect(err).NotTo(HaveOccurred())
897
898 By("Asserting the CSR is approved")
899 csr, err = clientset.CertificatesV1().CertificateSigningRequests().Get(ctx, csr.Name, metav1.GetOptions{})
900 Expect(err).NotTo(HaveOccurred())
901 Expect(csr.Status.Conditions[0].Type).To(Equal(certificatesv1.CertificateApproved))
902 Expect(csr.Status.Conditions[0].Status).To(Equal(corev1.ConditionTrue))
903 })
904
905 It("should be able to approve CSRs using Patch", func() {
906 cl, err := client.New(cfg, client.Options{})
907 Expect(err).NotTo(HaveOccurred())
908 Expect(cl).NotTo(BeNil())
909
910 By("Creating the CSR")
911 csr, err := clientset.CertificatesV1().CertificateSigningRequests().Create(ctx, csr, metav1.CreateOptions{})
912 Expect(err).NotTo(HaveOccurred())
913
914 By("Approving the CSR")
915 patch := client.MergeFrom(csr.DeepCopy())
916 csr.Status.Conditions = append(csr.Status.Conditions, certificatesv1.CertificateSigningRequestCondition{
917 Type: certificatesv1.CertificateApproved,
918 Status: corev1.ConditionTrue,
919 })
920 err = cl.SubResource("approval").Patch(ctx, csr, patch, &client.SubResourcePatchOptions{})
921 Expect(err).NotTo(HaveOccurred())
922
923 By("Asserting the CSR is approved")
924 csr, err = clientset.CertificatesV1().CertificateSigningRequests().Get(ctx, csr.Name, metav1.GetOptions{})
925 Expect(err).NotTo(HaveOccurred())
926 Expect(csr.Status.Conditions[0].Type).To(Equal(certificatesv1.CertificateApproved))
927 Expect(csr.Status.Conditions[0].Status).To(Equal(corev1.ConditionTrue))
928 })
929
930 It("should be able to update the scale subresource", func() {
931 cl, err := client.New(cfg, client.Options{})
932 Expect(err).NotTo(HaveOccurred())
933 Expect(cl).NotTo(BeNil())
934
935 By("Creating a deployment")
936 dep, err := clientset.AppsV1().Deployments(dep.Namespace).Create(ctx, dep, metav1.CreateOptions{})
937 Expect(err).NotTo(HaveOccurred())
938
939 By("Updating the scale subresource")
940 replicaCount := *dep.Spec.Replicas
941 scale := &autoscalingv1.Scale{Spec: autoscalingv1.ScaleSpec{Replicas: replicaCount}}
942 err = cl.SubResource("scale").Update(ctx, dep, client.WithSubResourceBody(scale), &client.SubResourceUpdateOptions{})
943 Expect(err).NotTo(HaveOccurred())
944
945 By("Asserting replicas got updated")
946 dep, err = clientset.AppsV1().Deployments(dep.Namespace).Get(ctx, dep.Name, metav1.GetOptions{})
947 Expect(err).NotTo(HaveOccurred())
948 Expect(*dep.Spec.Replicas).To(Equal(replicaCount))
949 })
950
951 It("should be able to patch the scale subresource", func() {
952 cl, err := client.New(cfg, client.Options{})
953 Expect(err).NotTo(HaveOccurred())
954 Expect(cl).NotTo(BeNil())
955
956 By("Creating a deployment")
957 dep, err := clientset.AppsV1().Deployments(dep.Namespace).Create(ctx, dep, metav1.CreateOptions{})
958 Expect(err).NotTo(HaveOccurred())
959
960 By("Updating the scale subresurce")
961 replicaCount := *dep.Spec.Replicas
962 patch := client.MergeFrom(&autoscalingv1.Scale{})
963 scale := &autoscalingv1.Scale{Spec: autoscalingv1.ScaleSpec{Replicas: replicaCount}}
964 err = cl.SubResource("scale").Patch(ctx, dep, patch, client.WithSubResourceBody(scale), &client.SubResourcePatchOptions{})
965 Expect(err).NotTo(HaveOccurred())
966
967 By("Asserting replicas got updated")
968 dep, err = clientset.AppsV1().Deployments(dep.Namespace).Get(ctx, dep.Name, metav1.GetOptions{})
969 Expect(err).NotTo(HaveOccurred())
970 Expect(*dep.Spec.Replicas).To(Equal(replicaCount))
971 })
972 })
973
974 Context("with unstructured objects", func() {
975 It("should be able to read the Scale subresource", func() {
976 cl, err := client.New(cfg, client.Options{})
977 Expect(err).NotTo(HaveOccurred())
978 Expect(cl).NotTo(BeNil())
979
980 By("Creating a deployment")
981 dep, err := clientset.AppsV1().Deployments(dep.Namespace).Create(ctx, dep, metav1.CreateOptions{})
982 Expect(err).NotTo(HaveOccurred())
983 dep.APIVersion = appsv1.SchemeGroupVersion.String()
984 dep.Kind = reflect.TypeOf(dep).Elem().Name()
985 depUnstructured, err := toUnstructured(dep)
986 Expect(err).NotTo(HaveOccurred())
987
988 By("reading the scale subresource")
989 scale := &unstructured.Unstructured{}
990 scale.SetAPIVersion("autoscaling/v1")
991 scale.SetKind("Scale")
992 err = cl.SubResource("scale").Get(ctx, depUnstructured, scale)
993 Expect(err).NotTo(HaveOccurred())
994
995 val, found, err := unstructured.NestedInt64(scale.UnstructuredContent(), "spec", "replicas")
996 Expect(err).NotTo(HaveOccurred())
997 Expect(found).To(BeTrue())
998 Expect(int32(val)).To(Equal(*dep.Spec.Replicas))
999 })
1000 It("should be able to create ServiceAccount tokens", func() {
1001 cl, err := client.New(cfg, client.Options{})
1002 Expect(err).NotTo(HaveOccurred())
1003 Expect(cl).NotTo(BeNil())
1004
1005 By("Creating the serviceAccount")
1006 _, err = clientset.CoreV1().ServiceAccounts(serviceAccount.Namespace).Create(ctx, serviceAccount, metav1.CreateOptions{})
1007 Expect((err)).NotTo(HaveOccurred())
1008
1009 serviceAccount.APIVersion = "v1"
1010 serviceAccount.Kind = "ServiceAccount"
1011 serviceAccountUnstructured, err := toUnstructured(serviceAccount)
1012 Expect(err).NotTo(HaveOccurred())
1013
1014 token := &unstructured.Unstructured{}
1015 token.SetAPIVersion("authentication.k8s.io/v1")
1016 token.SetKind("TokenRequest")
1017 err = cl.SubResource("token").Create(ctx, serviceAccountUnstructured, token)
1018 Expect(err).NotTo(HaveOccurred())
1019 Expect(token.GetAPIVersion()).To(Equal("authentication.k8s.io/v1"))
1020 Expect(token.GetKind()).To(Equal("TokenRequest"))
1021
1022 val, found, err := unstructured.NestedString(token.UnstructuredContent(), "status", "token")
1023 Expect(err).NotTo(HaveOccurred())
1024 Expect(found).To(BeTrue())
1025 Expect(val).NotTo(Equal(""))
1026 })
1027
1028 It("should be able to create Pod evictions", func() {
1029 cl, err := client.New(cfg, client.Options{})
1030 Expect(err).NotTo(HaveOccurred())
1031 Expect(cl).NotTo(BeNil())
1032
1033
1034 pod.Spec.Containers = []corev1.Container{{Name: "foo", Image: "busybox"}}
1035
1036 By("Creating the pod")
1037 pod, err = clientset.CoreV1().Pods(pod.Namespace).Create(ctx, pod, metav1.CreateOptions{})
1038 Expect(err).NotTo(HaveOccurred())
1039
1040 pod.APIVersion = "v1"
1041 pod.Kind = "Pod"
1042 podUnstructured, err := toUnstructured(pod)
1043 Expect(err).NotTo(HaveOccurred())
1044
1045 By("Creating the eviction")
1046 eviction := &unstructured.Unstructured{}
1047 eviction.SetAPIVersion("policy/v1")
1048 eviction.SetKind("Eviction")
1049 err = unstructured.SetNestedField(eviction.UnstructuredContent(), int64(0), "deleteOptions", "gracePeriodSeconds")
1050 Expect(err).NotTo(HaveOccurred())
1051 err = cl.SubResource("eviction").Create(ctx, podUnstructured, eviction)
1052 Expect(err).NotTo(HaveOccurred())
1053 Expect(eviction.GetAPIVersion()).To(Equal("policy/v1"))
1054 Expect(eviction.GetKind()).To(Equal("Eviction"))
1055
1056 By("Asserting the pod is gone")
1057 _, err = clientset.CoreV1().Pods(pod.Namespace).Get(ctx, pod.Name, metav1.GetOptions{})
1058 Expect(apierrors.IsNotFound(err)).To(BeTrue())
1059 })
1060
1061 It("should be able to create Pod bindings", func() {
1062 cl, err := client.New(cfg, client.Options{})
1063 Expect(err).NotTo(HaveOccurred())
1064 Expect(cl).NotTo(BeNil())
1065
1066
1067 pod.Spec.Containers = []corev1.Container{{Name: "foo", Image: "busybox"}}
1068
1069 By("Creating the pod")
1070 pod, err = clientset.CoreV1().Pods(pod.Namespace).Create(ctx, pod, metav1.CreateOptions{})
1071 Expect(err).NotTo(HaveOccurred())
1072
1073 pod.APIVersion = "v1"
1074 pod.Kind = "Pod"
1075 podUnstructured, err := toUnstructured(pod)
1076 Expect(err).NotTo(HaveOccurred())
1077
1078 By("Creating the binding")
1079 binding := &unstructured.Unstructured{}
1080 binding.SetAPIVersion("v1")
1081 binding.SetKind("Binding")
1082 err = unstructured.SetNestedField(binding.UnstructuredContent(), node.Name, "target", "name")
1083 Expect(err).NotTo(HaveOccurred())
1084
1085 err = cl.SubResource("binding").Create(ctx, podUnstructured, binding)
1086 Expect((err)).NotTo(HaveOccurred())
1087 Expect(binding.GetAPIVersion()).To(Equal("v1"))
1088 Expect(binding.GetKind()).To(Equal("Binding"))
1089
1090 By("Asserting the pod is bound")
1091 pod, err = clientset.CoreV1().Pods(pod.Namespace).Get(ctx, pod.Name, metav1.GetOptions{})
1092 Expect(err).NotTo(HaveOccurred())
1093 Expect(pod.Spec.NodeName).To(Equal(node.Name))
1094 })
1095
1096 It("should be able to approve CSRs", func() {
1097 cl, err := client.New(cfg, client.Options{})
1098 Expect(err).NotTo(HaveOccurred())
1099 Expect(cl).NotTo(BeNil())
1100
1101 By("Creating the CSR")
1102 csr, err := clientset.CertificatesV1().CertificateSigningRequests().Create(ctx, csr, metav1.CreateOptions{})
1103 Expect(err).NotTo(HaveOccurred())
1104
1105 By("Approving the CSR")
1106 csr.Status.Conditions = append(csr.Status.Conditions, certificatesv1.CertificateSigningRequestCondition{
1107 Type: certificatesv1.CertificateApproved,
1108 Status: corev1.ConditionTrue,
1109 })
1110 csr.APIVersion = "certificates.k8s.io/v1"
1111 csr.Kind = "CertificateSigningRequest"
1112 csrUnstructured, err := toUnstructured(csr)
1113 Expect(err).NotTo(HaveOccurred())
1114
1115 err = cl.SubResource("approval").Update(ctx, csrUnstructured)
1116 Expect(err).NotTo(HaveOccurred())
1117 Expect(csrUnstructured.GetAPIVersion()).To(Equal("certificates.k8s.io/v1"))
1118 Expect(csrUnstructured.GetKind()).To(Equal("CertificateSigningRequest"))
1119
1120 By("Asserting the CSR is approved")
1121 csr, err = clientset.CertificatesV1().CertificateSigningRequests().Get(ctx, csr.Name, metav1.GetOptions{})
1122 Expect(err).NotTo(HaveOccurred())
1123 Expect(csr.Status.Conditions[0].Type).To(Equal(certificatesv1.CertificateApproved))
1124 Expect(csr.Status.Conditions[0].Status).To(Equal(corev1.ConditionTrue))
1125 })
1126
1127 It("should be able to approve CSRs using Patch", func() {
1128 cl, err := client.New(cfg, client.Options{})
1129 Expect(err).NotTo(HaveOccurred())
1130 Expect(cl).NotTo(BeNil())
1131
1132 By("Creating the CSR")
1133 csr, err := clientset.CertificatesV1().CertificateSigningRequests().Create(ctx, csr, metav1.CreateOptions{})
1134 Expect(err).NotTo(HaveOccurred())
1135
1136 By("Approving the CSR")
1137 patch := client.MergeFrom(csr.DeepCopy())
1138 csr.Status.Conditions = append(csr.Status.Conditions, certificatesv1.CertificateSigningRequestCondition{
1139 Type: certificatesv1.CertificateApproved,
1140 Status: corev1.ConditionTrue,
1141 })
1142 csr.APIVersion = "certificates.k8s.io/v1"
1143 csr.Kind = "CertificateSigningRequest"
1144 csrUnstructured, err := toUnstructured(csr)
1145 Expect(err).NotTo(HaveOccurred())
1146
1147 err = cl.SubResource("approval").Patch(ctx, csrUnstructured, patch)
1148 Expect(err).NotTo(HaveOccurred())
1149 Expect(csrUnstructured.GetAPIVersion()).To(Equal("certificates.k8s.io/v1"))
1150 Expect(csrUnstructured.GetKind()).To(Equal("CertificateSigningRequest"))
1151
1152 By("Asserting the CSR is approved")
1153 csr, err = clientset.CertificatesV1().CertificateSigningRequests().Get(ctx, csr.Name, metav1.GetOptions{})
1154 Expect(err).NotTo(HaveOccurred())
1155 Expect(csr.Status.Conditions[0].Type).To(Equal(certificatesv1.CertificateApproved))
1156 Expect(csr.Status.Conditions[0].Status).To(Equal(corev1.ConditionTrue))
1157 })
1158
1159 It("should be able to update the scale subresource", func() {
1160 cl, err := client.New(cfg, client.Options{})
1161 Expect(err).NotTo(HaveOccurred())
1162 Expect(cl).NotTo(BeNil())
1163
1164 By("Creating a deployment")
1165 dep, err := clientset.AppsV1().Deployments(dep.Namespace).Create(ctx, dep, metav1.CreateOptions{})
1166 Expect(err).NotTo(HaveOccurred())
1167 dep.APIVersion = "apps/v1"
1168 dep.Kind = "Deployment"
1169 depUnstructured, err := toUnstructured(dep)
1170 Expect(err).NotTo(HaveOccurred())
1171
1172 By("Updating the scale subresurce")
1173 replicaCount := *dep.Spec.Replicas
1174 scale := &unstructured.Unstructured{}
1175 scale.SetAPIVersion("autoscaling/v1")
1176 scale.SetKind("Scale")
1177 Expect(unstructured.SetNestedField(scale.Object, int64(replicaCount), "spec", "replicas")).NotTo(HaveOccurred())
1178 err = cl.SubResource("scale").Update(ctx, depUnstructured, client.WithSubResourceBody(scale))
1179 Expect(err).NotTo(HaveOccurred())
1180 Expect(scale.GetAPIVersion()).To(Equal("autoscaling/v1"))
1181 Expect(scale.GetKind()).To(Equal("Scale"))
1182
1183 By("Asserting replicas got updated")
1184 dep, err = clientset.AppsV1().Deployments(dep.Namespace).Get(ctx, dep.Name, metav1.GetOptions{})
1185 Expect(err).NotTo(HaveOccurred())
1186 Expect(*dep.Spec.Replicas).To(Equal(replicaCount))
1187 })
1188
1189 It("should be able to patch the scale subresource", func() {
1190 cl, err := client.New(cfg, client.Options{})
1191 Expect(err).NotTo(HaveOccurred())
1192 Expect(cl).NotTo(BeNil())
1193
1194 By("Creating a deployment")
1195 dep, err := clientset.AppsV1().Deployments(dep.Namespace).Create(ctx, dep, metav1.CreateOptions{})
1196 Expect(err).NotTo(HaveOccurred())
1197 dep.APIVersion = "apps/v1"
1198 dep.Kind = "Deployment"
1199 depUnstructured, err := toUnstructured(dep)
1200 Expect(err).NotTo(HaveOccurred())
1201
1202 By("Updating the scale subresurce")
1203 replicaCount := *dep.Spec.Replicas
1204 scale := &unstructured.Unstructured{}
1205 scale.SetAPIVersion("autoscaling/v1")
1206 scale.SetKind("Scale")
1207 patch := client.MergeFrom(scale.DeepCopy())
1208 Expect(unstructured.SetNestedField(scale.Object, int64(replicaCount), "spec", "replicas")).NotTo(HaveOccurred())
1209 err = cl.SubResource("scale").Patch(ctx, depUnstructured, patch, client.WithSubResourceBody(scale))
1210 Expect(err).NotTo(HaveOccurred())
1211 Expect(scale.GetAPIVersion()).To(Equal("autoscaling/v1"))
1212 Expect(scale.GetKind()).To(Equal("Scale"))
1213
1214 By("Asserting replicas got updated")
1215 dep, err = clientset.AppsV1().Deployments(dep.Namespace).Get(ctx, dep.Name, metav1.GetOptions{})
1216 Expect(err).NotTo(HaveOccurred())
1217 Expect(*dep.Spec.Replicas).To(Equal(replicaCount))
1218 })
1219 })
1220
1221 })
1222
1223 Describe("StatusClient", func() {
1224 Context("with structured objects", func() {
1225 It("should update status of an existing object", func() {
1226 cl, err := client.New(cfg, client.Options{})
1227 Expect(err).NotTo(HaveOccurred())
1228 Expect(cl).NotTo(BeNil())
1229
1230 By("initially creating a Deployment")
1231 dep, err := clientset.AppsV1().Deployments(ns).Create(ctx, dep, metav1.CreateOptions{})
1232 Expect(err).NotTo(HaveOccurred())
1233
1234 By("updating the status of Deployment")
1235 dep.Status.Replicas = 1
1236 err = cl.Status().Update(context.TODO(), dep)
1237 Expect(err).NotTo(HaveOccurred())
1238
1239 By("validating updated Deployment has new status")
1240 actual, err := clientset.AppsV1().Deployments(ns).Get(ctx, dep.Name, metav1.GetOptions{})
1241 Expect(err).NotTo(HaveOccurred())
1242 Expect(actual).NotTo(BeNil())
1243 Expect(actual.Status.Replicas).To(BeEquivalentTo(1))
1244 })
1245
1246 It("should update status and preserve type information", func() {
1247 cl, err := client.New(cfg, client.Options{})
1248 Expect(err).NotTo(HaveOccurred())
1249 Expect(cl).NotTo(BeNil())
1250
1251 By("initially creating a Deployment")
1252 dep, err := clientset.AppsV1().Deployments(ns).Create(ctx, dep, metav1.CreateOptions{})
1253 Expect(err).NotTo(HaveOccurred())
1254
1255 By("updating the status of Deployment")
1256 dep.SetGroupVersionKind(depGvk)
1257 dep.Status.Replicas = 1
1258 err = cl.Status().Update(context.TODO(), dep)
1259 Expect(err).NotTo(HaveOccurred())
1260
1261 By("validating updated Deployment has type information")
1262 Expect(dep.GroupVersionKind()).To(Equal(depGvk))
1263 })
1264
1265 It("should patch status and preserve type information", func() {
1266 cl, err := client.New(cfg, client.Options{})
1267 Expect(err).NotTo(HaveOccurred())
1268 Expect(cl).NotTo(BeNil())
1269
1270 By("initially creating a Deployment")
1271 dep, err := clientset.AppsV1().Deployments(ns).Create(ctx, dep, metav1.CreateOptions{})
1272 Expect(err).NotTo(HaveOccurred())
1273
1274 By("patching the status of Deployment")
1275 dep.SetGroupVersionKind(depGvk)
1276 depPatch := client.MergeFrom(dep.DeepCopy())
1277 dep.Status.Replicas = 1
1278 err = cl.Status().Patch(context.TODO(), dep, depPatch)
1279 Expect(err).NotTo(HaveOccurred())
1280
1281 By("validating updated Deployment has type information")
1282 Expect(dep.GroupVersionKind()).To(Equal(depGvk))
1283 })
1284
1285 It("should not update spec of an existing object", func() {
1286 cl, err := client.New(cfg, client.Options{})
1287 Expect(err).NotTo(HaveOccurred())
1288 Expect(cl).NotTo(BeNil())
1289
1290 By("initially creating a Deployment")
1291 dep, err := clientset.AppsV1().Deployments(ns).Create(ctx, dep, metav1.CreateOptions{})
1292 Expect(err).NotTo(HaveOccurred())
1293
1294 By("updating the spec and status of Deployment")
1295 var rc int32 = 1
1296 dep.Status.Replicas = 1
1297 dep.Spec.Replicas = &rc
1298 err = cl.Status().Update(context.TODO(), dep)
1299 Expect(err).NotTo(HaveOccurred())
1300
1301 By("validating updated Deployment has new status and unchanged spec")
1302 actual, err := clientset.AppsV1().Deployments(ns).Get(ctx, dep.Name, metav1.GetOptions{})
1303 Expect(err).NotTo(HaveOccurred())
1304 Expect(actual).NotTo(BeNil())
1305 Expect(actual.Status.Replicas).To(BeEquivalentTo(1))
1306 Expect(*actual.Spec.Replicas).To(BeEquivalentTo(replicaCount))
1307 })
1308
1309 It("should update an existing object non-namespace object", func() {
1310 cl, err := client.New(cfg, client.Options{})
1311 Expect(err).NotTo(HaveOccurred())
1312 Expect(cl).NotTo(BeNil())
1313
1314 node, err := clientset.CoreV1().Nodes().Create(ctx, node, metav1.CreateOptions{})
1315 Expect(err).NotTo(HaveOccurred())
1316
1317 By("updating status of the object")
1318 node.Status.Phase = corev1.NodeRunning
1319 err = cl.Status().Update(context.TODO(), node)
1320 Expect(err).NotTo(HaveOccurred())
1321
1322 By("validate updated Node had new annotation")
1323 actual, err := clientset.CoreV1().Nodes().Get(ctx, node.Name, metav1.GetOptions{})
1324 Expect(err).NotTo(HaveOccurred())
1325 Expect(actual).NotTo(BeNil())
1326 Expect(actual.Status.Phase).To(Equal(corev1.NodeRunning))
1327 })
1328
1329 It("should fail if the object does not exist", func() {
1330 cl, err := client.New(cfg, client.Options{})
1331 Expect(err).NotTo(HaveOccurred())
1332 Expect(cl).NotTo(BeNil())
1333
1334 By("updating status of a non-existent object")
1335 err = cl.Status().Update(context.TODO(), dep)
1336 Expect(err).To(HaveOccurred())
1337 })
1338
1339 It("should fail if the object cannot be mapped to a GVK", func() {
1340 By("creating client with empty Scheme")
1341 emptyScheme := runtime.NewScheme()
1342 cl, err := client.New(cfg, client.Options{Scheme: emptyScheme})
1343 Expect(err).NotTo(HaveOccurred())
1344 Expect(cl).NotTo(BeNil())
1345
1346 By("initially creating a Deployment")
1347 dep, err := clientset.AppsV1().Deployments(ns).Create(ctx, dep, metav1.CreateOptions{})
1348 Expect(err).NotTo(HaveOccurred())
1349
1350 By("updating status of the Deployment")
1351 dep.Status.Replicas = 1
1352 err = cl.Status().Update(context.TODO(), dep)
1353 Expect(err).To(HaveOccurred())
1354 Expect(err.Error()).To(ContainSubstring("no kind is registered for the type"))
1355 })
1356
1357 PIt("should fail if the GVK cannot be mapped to a Resource", func() {
1358
1359 })
1360
1361 PIt("should fail if an API does not implement Status subresource", func() {
1362
1363 })
1364 })
1365
1366 Context("with unstructured objects", func() {
1367 It("should update status of an existing object", func() {
1368 cl, err := client.New(cfg, client.Options{})
1369 Expect(err).NotTo(HaveOccurred())
1370 Expect(cl).NotTo(BeNil())
1371
1372 By("initially creating a Deployment")
1373 dep, err := clientset.AppsV1().Deployments(ns).Create(ctx, dep, metav1.CreateOptions{})
1374 Expect(err).NotTo(HaveOccurred())
1375
1376 By("updating the status of Deployment")
1377 u := &unstructured.Unstructured{}
1378 dep.Status.Replicas = 1
1379 Expect(scheme.Convert(dep, u, nil)).To(Succeed())
1380 err = cl.Status().Update(context.TODO(), u)
1381 Expect(err).NotTo(HaveOccurred())
1382
1383 By("validating updated Deployment has new status")
1384 actual, err := clientset.AppsV1().Deployments(ns).Get(ctx, dep.Name, metav1.GetOptions{})
1385 Expect(err).NotTo(HaveOccurred())
1386 Expect(actual).NotTo(BeNil())
1387 Expect(actual.Status.Replicas).To(BeEquivalentTo(1))
1388 })
1389
1390 It("should update status and preserve type information", func() {
1391 cl, err := client.New(cfg, client.Options{})
1392 Expect(err).NotTo(HaveOccurred())
1393 Expect(cl).NotTo(BeNil())
1394
1395 By("initially creating a Deployment")
1396 dep, err := clientset.AppsV1().Deployments(ns).Create(ctx, dep, metav1.CreateOptions{})
1397 Expect(err).NotTo(HaveOccurred())
1398
1399 By("updating the status of Deployment")
1400 u := &unstructured.Unstructured{}
1401 dep.Status.Replicas = 1
1402 Expect(scheme.Convert(dep, u, nil)).To(Succeed())
1403 err = cl.Status().Update(context.TODO(), u)
1404 Expect(err).NotTo(HaveOccurred())
1405
1406 By("validating updated Deployment has type information")
1407 Expect(u.GroupVersionKind()).To(Equal(depGvk))
1408 })
1409
1410 It("should patch status and preserve type information", func() {
1411 cl, err := client.New(cfg, client.Options{})
1412 Expect(err).NotTo(HaveOccurred())
1413 Expect(cl).NotTo(BeNil())
1414
1415 By("initially creating a Deployment")
1416 dep, err := clientset.AppsV1().Deployments(ns).Create(ctx, dep, metav1.CreateOptions{})
1417 Expect(err).NotTo(HaveOccurred())
1418
1419 By("patching the status of Deployment")
1420 u := &unstructured.Unstructured{}
1421 depPatch := client.MergeFrom(dep.DeepCopy())
1422 dep.Status.Replicas = 1
1423 Expect(scheme.Convert(dep, u, nil)).To(Succeed())
1424 err = cl.Status().Patch(context.TODO(), u, depPatch)
1425 Expect(err).NotTo(HaveOccurred())
1426
1427 By("validating updated Deployment has type information")
1428 Expect(u.GroupVersionKind()).To(Equal(depGvk))
1429
1430 By("validating patched Deployment has new status")
1431 actual, err := clientset.AppsV1().Deployments(ns).Get(ctx, dep.Name, metav1.GetOptions{})
1432 Expect(err).NotTo(HaveOccurred())
1433 Expect(actual).NotTo(BeNil())
1434 Expect(actual.Status.Replicas).To(BeEquivalentTo(1))
1435 })
1436
1437 It("should not update spec of an existing object", func() {
1438 cl, err := client.New(cfg, client.Options{})
1439 Expect(err).NotTo(HaveOccurred())
1440 Expect(cl).NotTo(BeNil())
1441
1442 By("initially creating a Deployment")
1443 dep, err := clientset.AppsV1().Deployments(ns).Create(ctx, dep, metav1.CreateOptions{})
1444 Expect(err).NotTo(HaveOccurred())
1445
1446 By("updating the spec and status of Deployment")
1447 u := &unstructured.Unstructured{}
1448 var rc int32 = 1
1449 dep.Status.Replicas = 1
1450 dep.Spec.Replicas = &rc
1451 Expect(scheme.Convert(dep, u, nil)).To(Succeed())
1452 err = cl.Status().Update(context.TODO(), u)
1453 Expect(err).NotTo(HaveOccurred())
1454
1455 By("validating updated Deployment has new status and unchanged spec")
1456 actual, err := clientset.AppsV1().Deployments(ns).Get(ctx, dep.Name, metav1.GetOptions{})
1457 Expect(err).NotTo(HaveOccurred())
1458 Expect(actual).NotTo(BeNil())
1459 Expect(actual.Status.Replicas).To(BeEquivalentTo(1))
1460 Expect(*actual.Spec.Replicas).To(BeEquivalentTo(replicaCount))
1461 })
1462
1463 It("should update an existing object non-namespace object", func() {
1464 cl, err := client.New(cfg, client.Options{})
1465 Expect(err).NotTo(HaveOccurred())
1466 Expect(cl).NotTo(BeNil())
1467
1468 node, err := clientset.CoreV1().Nodes().Create(ctx, node, metav1.CreateOptions{})
1469 Expect(err).NotTo(HaveOccurred())
1470
1471 By("updating status of the object")
1472 u := &unstructured.Unstructured{}
1473 node.Status.Phase = corev1.NodeRunning
1474 Expect(scheme.Convert(node, u, nil)).To(Succeed())
1475 err = cl.Status().Update(context.TODO(), u)
1476 Expect(err).NotTo(HaveOccurred())
1477
1478 By("validate updated Node had new annotation")
1479 actual, err := clientset.CoreV1().Nodes().Get(ctx, node.Name, metav1.GetOptions{})
1480 Expect(err).NotTo(HaveOccurred())
1481 Expect(actual).NotTo(BeNil())
1482 Expect(actual.Status.Phase).To(Equal(corev1.NodeRunning))
1483 })
1484
1485 It("should fail if the object does not exist", func() {
1486 cl, err := client.New(cfg, client.Options{})
1487 Expect(err).NotTo(HaveOccurred())
1488 Expect(cl).NotTo(BeNil())
1489
1490 By("updating status of a non-existent object")
1491 u := &unstructured.Unstructured{}
1492 Expect(scheme.Convert(dep, u, nil)).To(Succeed())
1493 err = cl.Status().Update(context.TODO(), u)
1494 Expect(err).To(HaveOccurred())
1495 })
1496
1497 PIt("should fail if the GVK cannot be mapped to a Resource", func() {
1498
1499 })
1500
1501 PIt("should fail if an API does not implement Status subresource", func() {
1502
1503 })
1504
1505 })
1506
1507 Context("with metadata objects", func() {
1508 It("should fail to update with an error", func() {
1509 cl, err := client.New(cfg, client.Options{})
1510 Expect(err).NotTo(HaveOccurred())
1511
1512 obj := metaOnlyFromObj(dep, scheme)
1513 Expect(cl.Status().Update(context.TODO(), obj)).NotTo(Succeed())
1514 })
1515
1516 It("should patch status and preserve type information", func() {
1517 cl, err := client.New(cfg, client.Options{})
1518 Expect(err).NotTo(HaveOccurred())
1519 Expect(cl).NotTo(BeNil())
1520
1521 By("initially creating a Deployment")
1522 dep, err := clientset.AppsV1().Deployments(ns).Create(ctx, dep, metav1.CreateOptions{})
1523 Expect(err).NotTo(HaveOccurred())
1524
1525 By("patching the status of Deployment")
1526 objPatch := client.MergeFrom(metaOnlyFromObj(dep, scheme))
1527 dep.Annotations = map[string]string{"some-new-annotation": "some-new-value"}
1528 obj := metaOnlyFromObj(dep, scheme)
1529 err = cl.Status().Patch(context.TODO(), obj, objPatch)
1530 Expect(err).NotTo(HaveOccurred())
1531
1532 By("validating updated Deployment has type information")
1533 Expect(obj.GroupVersionKind()).To(Equal(depGvk))
1534
1535 By("validating patched Deployment has new status")
1536 actual, err := clientset.AppsV1().Deployments(ns).Get(ctx, dep.Name, metav1.GetOptions{})
1537 Expect(err).NotTo(HaveOccurred())
1538 Expect(actual).NotTo(BeNil())
1539 Expect(actual.Annotations).To(HaveKeyWithValue("some-new-annotation", "some-new-value"))
1540 })
1541 })
1542 })
1543
1544 Describe("Delete", func() {
1545 Context("with structured objects", func() {
1546 It("should delete an existing object from a go struct", func() {
1547 cl, err := client.New(cfg, client.Options{})
1548 Expect(err).NotTo(HaveOccurred())
1549 Expect(cl).NotTo(BeNil())
1550
1551 By("initially creating a Deployment")
1552 dep, err := clientset.AppsV1().Deployments(ns).Create(ctx, dep, metav1.CreateOptions{})
1553 Expect(err).NotTo(HaveOccurred())
1554
1555 By("deleting the Deployment")
1556 depName := dep.Name
1557 err = cl.Delete(context.TODO(), dep)
1558 Expect(err).NotTo(HaveOccurred())
1559
1560 By("validating the Deployment no longer exists")
1561 _, err = clientset.AppsV1().Deployments(ns).Get(ctx, depName, metav1.GetOptions{})
1562 Expect(err).To(HaveOccurred())
1563 })
1564
1565 It("should delete an existing object non-namespace object from a go struct", func() {
1566 cl, err := client.New(cfg, client.Options{})
1567 Expect(err).NotTo(HaveOccurred())
1568 Expect(cl).NotTo(BeNil())
1569
1570 By("initially creating a Node")
1571 node, err := clientset.CoreV1().Nodes().Create(ctx, node, metav1.CreateOptions{})
1572 Expect(err).NotTo(HaveOccurred())
1573
1574 By("deleting the Node")
1575 nodeName := node.Name
1576 err = cl.Delete(context.TODO(), node)
1577 Expect(err).NotTo(HaveOccurred())
1578
1579 By("validating the Node no longer exists")
1580 _, err = clientset.CoreV1().Nodes().Get(ctx, nodeName, metav1.GetOptions{})
1581 Expect(err).To(HaveOccurred())
1582 })
1583
1584 It("should fail if the object does not exist", func() {
1585 cl, err := client.New(cfg, client.Options{})
1586 Expect(err).NotTo(HaveOccurred())
1587 Expect(cl).NotTo(BeNil())
1588
1589 By("Deleting node before it is ever created")
1590 err = cl.Delete(context.TODO(), node)
1591 Expect(err).To(HaveOccurred())
1592 })
1593
1594 PIt("should fail if the object doesn't have meta", func() {
1595
1596 })
1597
1598 It("should fail if the object cannot be mapped to a GVK", func() {
1599 By("creating client with empty Scheme")
1600 emptyScheme := runtime.NewScheme()
1601 cl, err := client.New(cfg, client.Options{Scheme: emptyScheme})
1602 Expect(err).NotTo(HaveOccurred())
1603 Expect(cl).NotTo(BeNil())
1604
1605 By("initially creating a Deployment")
1606 dep, err := clientset.AppsV1().Deployments(ns).Create(ctx, dep, metav1.CreateOptions{})
1607 Expect(err).NotTo(HaveOccurred())
1608
1609 By("deleting the Deployment fails")
1610 err = cl.Delete(context.TODO(), dep)
1611 Expect(err).To(HaveOccurred())
1612 Expect(err.Error()).To(ContainSubstring("no kind is registered for the type"))
1613 })
1614
1615 PIt("should fail if the GVK cannot be mapped to a Resource", func() {
1616
1617 })
1618
1619 It("should delete a collection of objects", func() {
1620 cl, err := client.New(cfg, client.Options{})
1621 Expect(err).NotTo(HaveOccurred())
1622 Expect(cl).NotTo(BeNil())
1623
1624 By("initially creating two Deployments")
1625
1626 dep2 := dep.DeepCopy()
1627 dep2.Name += "-2"
1628
1629 dep, err = clientset.AppsV1().Deployments(ns).Create(ctx, dep, metav1.CreateOptions{})
1630 Expect(err).NotTo(HaveOccurred())
1631 dep2, err = clientset.AppsV1().Deployments(ns).Create(ctx, dep2, metav1.CreateOptions{})
1632 Expect(err).NotTo(HaveOccurred())
1633
1634 depName := dep.Name
1635 dep2Name := dep2.Name
1636
1637 By("deleting Deployments")
1638 err = cl.DeleteAllOf(context.TODO(), dep, client.InNamespace(ns), client.MatchingLabels(dep.ObjectMeta.Labels))
1639 Expect(err).NotTo(HaveOccurred())
1640
1641 By("validating the Deployment no longer exists")
1642 _, err = clientset.AppsV1().Deployments(ns).Get(ctx, depName, metav1.GetOptions{})
1643 Expect(err).To(HaveOccurred())
1644 _, err = clientset.AppsV1().Deployments(ns).Get(ctx, dep2Name, metav1.GetOptions{})
1645 Expect(err).To(HaveOccurred())
1646 })
1647 })
1648 Context("with unstructured objects", func() {
1649 It("should delete an existing object from a go struct", func() {
1650 cl, err := client.New(cfg, client.Options{})
1651 Expect(err).NotTo(HaveOccurred())
1652 Expect(cl).NotTo(BeNil())
1653
1654 By("initially creating a Deployment")
1655 dep, err := clientset.AppsV1().Deployments(ns).Create(ctx, dep, metav1.CreateOptions{})
1656 Expect(err).NotTo(HaveOccurred())
1657
1658 By("deleting the Deployment")
1659 depName := dep.Name
1660 u := &unstructured.Unstructured{}
1661 Expect(scheme.Convert(dep, u, nil)).To(Succeed())
1662 u.SetGroupVersionKind(schema.GroupVersionKind{
1663 Group: "apps",
1664 Kind: "Deployment",
1665 Version: "v1",
1666 })
1667 err = cl.Delete(context.TODO(), u)
1668 Expect(err).NotTo(HaveOccurred())
1669
1670 By("validating the Deployment no longer exists")
1671 _, err = clientset.AppsV1().Deployments(ns).Get(ctx, depName, metav1.GetOptions{})
1672 Expect(err).To(HaveOccurred())
1673 })
1674
1675 It("should delete an existing object non-namespace object from a go struct", func() {
1676 cl, err := client.New(cfg, client.Options{})
1677 Expect(err).NotTo(HaveOccurred())
1678 Expect(cl).NotTo(BeNil())
1679
1680 By("initially creating a Node")
1681 node, err := clientset.CoreV1().Nodes().Create(ctx, node, metav1.CreateOptions{})
1682 Expect(err).NotTo(HaveOccurred())
1683
1684 By("deleting the Node")
1685 nodeName := node.Name
1686 u := &unstructured.Unstructured{}
1687 Expect(scheme.Convert(node, u, nil)).To(Succeed())
1688 u.SetGroupVersionKind(schema.GroupVersionKind{
1689 Group: "",
1690 Kind: "Node",
1691 Version: "v1",
1692 })
1693 err = cl.Delete(context.TODO(), u)
1694 Expect(err).NotTo(HaveOccurred())
1695
1696 By("validating the Node no longer exists")
1697 _, err = clientset.CoreV1().Nodes().Get(ctx, nodeName, metav1.GetOptions{})
1698 Expect(err).To(HaveOccurred())
1699 })
1700
1701 It("should fail if the object does not exist", func() {
1702 cl, err := client.New(cfg, client.Options{})
1703 Expect(err).NotTo(HaveOccurred())
1704 Expect(cl).NotTo(BeNil())
1705
1706 By("Deleting node before it is ever created")
1707 u := &unstructured.Unstructured{}
1708 Expect(scheme.Convert(node, u, nil)).To(Succeed())
1709 u.SetGroupVersionKind(schema.GroupVersionKind{
1710 Group: "",
1711 Kind: "Node",
1712 Version: "v1",
1713 })
1714 err = cl.Delete(context.TODO(), node)
1715 Expect(err).To(HaveOccurred())
1716 })
1717
1718 It("should delete a collection of object", func() {
1719 cl, err := client.New(cfg, client.Options{})
1720 Expect(err).NotTo(HaveOccurred())
1721 Expect(cl).NotTo(BeNil())
1722
1723 By("initially creating two Deployments")
1724
1725 dep2 := dep.DeepCopy()
1726 dep2.Name += "-2"
1727
1728 dep, err = clientset.AppsV1().Deployments(ns).Create(ctx, dep, metav1.CreateOptions{})
1729 Expect(err).NotTo(HaveOccurred())
1730 dep2, err = clientset.AppsV1().Deployments(ns).Create(ctx, dep2, metav1.CreateOptions{})
1731 Expect(err).NotTo(HaveOccurred())
1732
1733 depName := dep.Name
1734 dep2Name := dep2.Name
1735
1736 By("deleting Deployments")
1737 u := &unstructured.Unstructured{}
1738 Expect(scheme.Convert(dep, u, nil)).To(Succeed())
1739 u.SetGroupVersionKind(schema.GroupVersionKind{
1740 Group: "apps",
1741 Kind: "Deployment",
1742 Version: "v1",
1743 })
1744 err = cl.DeleteAllOf(context.TODO(), u, client.InNamespace(ns), client.MatchingLabels(dep.ObjectMeta.Labels))
1745 Expect(err).NotTo(HaveOccurred())
1746
1747 By("validating the Deployment no longer exists")
1748 _, err = clientset.AppsV1().Deployments(ns).Get(ctx, depName, metav1.GetOptions{})
1749 Expect(err).To(HaveOccurred())
1750 _, err = clientset.AppsV1().Deployments(ns).Get(ctx, dep2Name, metav1.GetOptions{})
1751 Expect(err).To(HaveOccurred())
1752 })
1753 })
1754 Context("with metadata objects", func() {
1755 It("should delete an existing object from a go struct", func() {
1756 cl, err := client.New(cfg, client.Options{})
1757 Expect(err).NotTo(HaveOccurred())
1758 Expect(cl).NotTo(BeNil())
1759
1760 By("initially creating a Deployment")
1761 dep, err := clientset.AppsV1().Deployments(ns).Create(ctx, dep, metav1.CreateOptions{})
1762 Expect(err).NotTo(HaveOccurred())
1763
1764 By("deleting the Deployment")
1765 metaObj := metaOnlyFromObj(dep, scheme)
1766 err = cl.Delete(context.TODO(), metaObj)
1767 Expect(err).NotTo(HaveOccurred())
1768
1769 By("validating the Deployment no longer exists")
1770 _, err = clientset.AppsV1().Deployments(ns).Get(ctx, dep.Name, metav1.GetOptions{})
1771 Expect(err).To(HaveOccurred())
1772 })
1773
1774 It("should delete an existing object non-namespace object from a go struct", func() {
1775 cl, err := client.New(cfg, client.Options{})
1776 Expect(err).NotTo(HaveOccurred())
1777 Expect(cl).NotTo(BeNil())
1778
1779 By("initially creating a Node")
1780 node, err := clientset.CoreV1().Nodes().Create(ctx, node, metav1.CreateOptions{})
1781 Expect(err).NotTo(HaveOccurred())
1782
1783 By("deleting the Node")
1784 metaObj := metaOnlyFromObj(node, scheme)
1785 err = cl.Delete(context.TODO(), metaObj)
1786 Expect(err).NotTo(HaveOccurred())
1787
1788 By("validating the Node no longer exists")
1789 _, err = clientset.CoreV1().Nodes().Get(ctx, node.Name, metav1.GetOptions{})
1790 Expect(err).To(HaveOccurred())
1791 })
1792
1793 It("should fail if the object does not exist", func() {
1794 cl, err := client.New(cfg, client.Options{})
1795 Expect(err).NotTo(HaveOccurred())
1796 Expect(cl).NotTo(BeNil())
1797
1798 By("Deleting node before it is ever created")
1799 metaObj := metaOnlyFromObj(node, scheme)
1800 err = cl.Delete(context.TODO(), metaObj)
1801 Expect(err).To(HaveOccurred())
1802 })
1803
1804 It("should delete a collection of object", func() {
1805 cl, err := client.New(cfg, client.Options{})
1806 Expect(err).NotTo(HaveOccurred())
1807 Expect(cl).NotTo(BeNil())
1808
1809 By("initially creating two Deployments")
1810
1811 dep2 := dep.DeepCopy()
1812 dep2.Name += "-2"
1813
1814 dep, err = clientset.AppsV1().Deployments(ns).Create(ctx, dep, metav1.CreateOptions{})
1815 Expect(err).NotTo(HaveOccurred())
1816 dep2, err = clientset.AppsV1().Deployments(ns).Create(ctx, dep2, metav1.CreateOptions{})
1817 Expect(err).NotTo(HaveOccurred())
1818
1819 depName := dep.Name
1820 dep2Name := dep2.Name
1821
1822 By("deleting Deployments")
1823 metaObj := metaOnlyFromObj(dep, scheme)
1824 err = cl.DeleteAllOf(context.TODO(), metaObj, client.InNamespace(ns), client.MatchingLabels(dep.ObjectMeta.Labels))
1825 Expect(err).NotTo(HaveOccurred())
1826
1827 By("validating the Deployment no longer exists")
1828 _, err = clientset.AppsV1().Deployments(ns).Get(ctx, depName, metav1.GetOptions{})
1829 Expect(err).To(HaveOccurred())
1830 _, err = clientset.AppsV1().Deployments(ns).Get(ctx, dep2Name, metav1.GetOptions{})
1831 Expect(err).To(HaveOccurred())
1832 })
1833 })
1834 })
1835
1836 Describe("Get", func() {
1837 Context("with structured objects", func() {
1838 It("should fetch an existing object for a go struct", func() {
1839 By("first creating the Deployment")
1840 dep, err := clientset.AppsV1().Deployments(ns).Create(ctx, dep, metav1.CreateOptions{})
1841 Expect(err).NotTo(HaveOccurred())
1842
1843 cl, err := client.New(cfg, client.Options{})
1844 Expect(err).NotTo(HaveOccurred())
1845 Expect(cl).NotTo(BeNil())
1846
1847 By("fetching the created Deployment")
1848 var actual appsv1.Deployment
1849 key := client.ObjectKey{Namespace: ns, Name: dep.Name}
1850 err = cl.Get(context.TODO(), key, &actual)
1851 Expect(err).NotTo(HaveOccurred())
1852 Expect(actual).NotTo(BeNil())
1853
1854 By("validating the fetched deployment equals the created one")
1855 Expect(dep).To(Equal(&actual))
1856 })
1857
1858 It("should fetch an existing non-namespace object for a go struct", func() {
1859 By("first creating the object")
1860 node, err := clientset.CoreV1().Nodes().Create(ctx, node, metav1.CreateOptions{})
1861 Expect(err).NotTo(HaveOccurred())
1862
1863 cl, err := client.New(cfg, client.Options{})
1864 Expect(err).NotTo(HaveOccurred())
1865 Expect(cl).NotTo(BeNil())
1866
1867 By("retrieving node through client")
1868 var actual corev1.Node
1869 key := client.ObjectKey{Namespace: ns, Name: node.Name}
1870 err = cl.Get(context.TODO(), key, &actual)
1871 Expect(err).NotTo(HaveOccurred())
1872 Expect(actual).NotTo(BeNil())
1873
1874 Expect(node).To(Equal(&actual))
1875 })
1876
1877 It("should fail if the object does not exist", func() {
1878 cl, err := client.New(cfg, client.Options{})
1879 Expect(err).NotTo(HaveOccurred())
1880 Expect(cl).NotTo(BeNil())
1881
1882 By("fetching object that has not been created yet")
1883 key := client.ObjectKey{Namespace: ns, Name: dep.Name}
1884 var actual appsv1.Deployment
1885 err = cl.Get(context.TODO(), key, &actual)
1886 Expect(err).To(HaveOccurred())
1887 })
1888
1889 PIt("should fail if the object doesn't have meta", func() {
1890
1891 })
1892
1893 It("should fail if the object cannot be mapped to a GVK", func() {
1894 By("first creating the Deployment")
1895 dep, err := clientset.AppsV1().Deployments(ns).Create(ctx, dep, metav1.CreateOptions{})
1896 Expect(err).NotTo(HaveOccurred())
1897
1898 By("creating a client with an empty Scheme")
1899 emptyScheme := runtime.NewScheme()
1900 cl, err := client.New(cfg, client.Options{Scheme: emptyScheme})
1901 Expect(err).NotTo(HaveOccurred())
1902 Expect(cl).NotTo(BeNil())
1903
1904 By("fetching the created Deployment fails")
1905 var actual appsv1.Deployment
1906 key := client.ObjectKey{Namespace: ns, Name: dep.Name}
1907 err = cl.Get(context.TODO(), key, &actual)
1908 Expect(err).To(HaveOccurred())
1909 Expect(err.Error()).To(ContainSubstring("no kind is registered for the type"))
1910 })
1911
1912 PIt("should fail if the GVK cannot be mapped to a Resource", func() {
1913
1914 })
1915
1916
1917
1918 for idx, object := range []client.Object{&corev1.ConfigMap{}, &pkg.ChaosPod{}} {
1919 idx, object := idx, object
1920 It(fmt.Sprintf("should not retain any data in the obj variable that is not on the server for %T", object), func() {
1921 cl, err := client.New(cfg, client.Options{})
1922 Expect(err).NotTo(HaveOccurred())
1923 Expect(cl).NotTo(BeNil())
1924
1925 object.SetName(fmt.Sprintf("retain-test-%d", idx))
1926 object.SetNamespace(ns)
1927
1928 By("First creating the object")
1929 toCreate := object.DeepCopyObject().(client.Object)
1930 Expect(cl.Create(ctx, toCreate)).NotTo(HaveOccurred())
1931
1932 By("Fetching it into a variable that has finalizers set")
1933 toGetInto := object.DeepCopyObject().(client.Object)
1934 toGetInto.SetFinalizers([]string{"some-finalizer"})
1935 Expect(cl.Get(ctx, client.ObjectKeyFromObject(object), toGetInto)).NotTo(HaveOccurred())
1936
1937 By("Ensuring the created and the received object are equal")
1938 Expect(toCreate).Should(Equal(toGetInto))
1939 })
1940 }
1941
1942 })
1943
1944 Context("with unstructured objects", func() {
1945 It("should fetch an existing object", func() {
1946 By("first creating the Deployment")
1947 dep, err := clientset.AppsV1().Deployments(ns).Create(ctx, dep, metav1.CreateOptions{})
1948 Expect(err).NotTo(HaveOccurred())
1949
1950 cl, err := client.New(cfg, client.Options{})
1951 Expect(err).NotTo(HaveOccurred())
1952 Expect(cl).NotTo(BeNil())
1953
1954 By("encoding the Deployment as unstructured")
1955 var u runtime.Unstructured = &unstructured.Unstructured{}
1956 Expect(scheme.Convert(dep, u, nil)).To(Succeed())
1957
1958 By("fetching the created Deployment")
1959 var actual unstructured.Unstructured
1960 actual.SetGroupVersionKind(schema.GroupVersionKind{
1961 Group: "apps",
1962 Kind: "Deployment",
1963 Version: "v1",
1964 })
1965 key := client.ObjectKey{Namespace: ns, Name: dep.Name}
1966 err = cl.Get(context.TODO(), key, &actual)
1967 Expect(err).NotTo(HaveOccurred())
1968 Expect(actual).NotTo(BeNil())
1969
1970 By("validating the fetched Deployment equals the created one")
1971 Expect(u).To(Equal(&actual))
1972 })
1973
1974 It("should fetch an existing non-namespace object", func() {
1975 By("first creating the Node")
1976 node, err := clientset.CoreV1().Nodes().Create(ctx, node, metav1.CreateOptions{})
1977 Expect(err).NotTo(HaveOccurred())
1978
1979 By("encoding the Node as unstructured")
1980 var u runtime.Unstructured = &unstructured.Unstructured{}
1981 Expect(scheme.Convert(node, u, nil)).To(Succeed())
1982
1983 cl, err := client.New(cfg, client.Options{})
1984 Expect(err).NotTo(HaveOccurred())
1985 Expect(cl).NotTo(BeNil())
1986
1987 By("fetching the created Node")
1988 var actual unstructured.Unstructured
1989 actual.SetGroupVersionKind(schema.GroupVersionKind{
1990 Group: "",
1991 Kind: "Node",
1992 Version: "v1",
1993 })
1994 key := client.ObjectKey{Namespace: ns, Name: node.Name}
1995 err = cl.Get(context.TODO(), key, &actual)
1996 Expect(err).NotTo(HaveOccurred())
1997 Expect(actual).NotTo(BeNil())
1998
1999 By("validating the fetched Node equals the created one")
2000 Expect(u).To(Equal(&actual))
2001 })
2002
2003 It("should fail if the object does not exist", func() {
2004 cl, err := client.New(cfg, client.Options{})
2005 Expect(err).NotTo(HaveOccurred())
2006 Expect(cl).NotTo(BeNil())
2007
2008 By("fetching object that has not been created yet")
2009 key := client.ObjectKey{Namespace: ns, Name: dep.Name}
2010 u := &unstructured.Unstructured{}
2011 err = cl.Get(context.TODO(), key, u)
2012 Expect(err).To(HaveOccurred())
2013 })
2014
2015 It("should not retain any data in the obj variable that is not on the server", func() {
2016 object := &unstructured.Unstructured{}
2017 cl, err := client.New(cfg, client.Options{})
2018 Expect(err).NotTo(HaveOccurred())
2019 Expect(cl).NotTo(BeNil())
2020
2021 object.SetName("retain-unstructured")
2022 object.SetNamespace(ns)
2023 object.SetAPIVersion("chaosapps.metamagical.io/v1")
2024 object.SetKind("ChaosPod")
2025
2026 By("First creating the object")
2027 toCreate := object.DeepCopyObject().(client.Object)
2028 Expect(cl.Create(ctx, toCreate)).NotTo(HaveOccurred())
2029
2030 By("Fetching it into a variable that has finalizers set")
2031 toGetInto := object.DeepCopyObject().(client.Object)
2032 toGetInto.SetFinalizers([]string{"some-finalizer"})
2033 Expect(cl.Get(ctx, client.ObjectKeyFromObject(object), toGetInto)).NotTo(HaveOccurred())
2034
2035 By("Ensuring the created and the received object are equal")
2036 Expect(toCreate).Should(Equal(toGetInto))
2037 })
2038 })
2039 Context("with metadata objects", func() {
2040 It("should fetch an existing object for a go struct", func() {
2041 By("first creating the Deployment")
2042 dep, err := clientset.AppsV1().Deployments(ns).Create(ctx, dep, metav1.CreateOptions{})
2043 Expect(err).NotTo(HaveOccurred())
2044
2045 cl, err := client.New(cfg, client.Options{})
2046 Expect(err).NotTo(HaveOccurred())
2047 Expect(cl).NotTo(BeNil())
2048
2049 By("fetching the created Deployment")
2050 var actual metav1.PartialObjectMetadata
2051 gvk := schema.GroupVersionKind{
2052 Group: "apps",
2053 Version: "v1",
2054 Kind: "Deployment",
2055 }
2056 actual.SetGroupVersionKind(gvk)
2057 key := client.ObjectKey{Namespace: ns, Name: dep.Name}
2058 err = cl.Get(context.TODO(), key, &actual)
2059 Expect(err).NotTo(HaveOccurred())
2060 Expect(actual).NotTo(BeNil())
2061
2062 By("validating that the GVK has been preserved")
2063 Expect(actual.GroupVersionKind()).To(Equal(gvk))
2064
2065 By("validating the fetched deployment equals the created one")
2066 Expect(metaOnlyFromObj(dep, scheme)).To(Equal(&actual))
2067 })
2068
2069 It("should fetch an existing non-namespace object for a go struct", func() {
2070 By("first creating the object")
2071 node, err := clientset.CoreV1().Nodes().Create(ctx, node, metav1.CreateOptions{})
2072 Expect(err).NotTo(HaveOccurred())
2073
2074 cl, err := client.New(cfg, client.Options{})
2075 Expect(err).NotTo(HaveOccurred())
2076 Expect(cl).NotTo(BeNil())
2077
2078 By("retrieving node through client")
2079 var actual metav1.PartialObjectMetadata
2080 actual.SetGroupVersionKind(schema.GroupVersionKind{
2081 Version: "v1",
2082 Kind: "Node",
2083 })
2084 key := client.ObjectKey{Namespace: ns, Name: node.Name}
2085 err = cl.Get(context.TODO(), key, &actual)
2086 Expect(err).NotTo(HaveOccurred())
2087 Expect(actual).NotTo(BeNil())
2088
2089 Expect(metaOnlyFromObj(node, scheme)).To(Equal(&actual))
2090 })
2091
2092 It("should fail if the object does not exist", func() {
2093 cl, err := client.New(cfg, client.Options{})
2094 Expect(err).NotTo(HaveOccurred())
2095 Expect(cl).NotTo(BeNil())
2096
2097 By("fetching object that has not been created yet")
2098 key := client.ObjectKey{Namespace: ns, Name: dep.Name}
2099 var actual metav1.PartialObjectMetadata
2100 actual.SetGroupVersionKind(schema.GroupVersionKind{
2101 Group: "apps",
2102 Version: "v1",
2103 Kind: "Deployment",
2104 })
2105 err = cl.Get(context.TODO(), key, &actual)
2106 Expect(err).To(HaveOccurred())
2107 })
2108
2109 PIt("should fail if the object doesn't have meta", func() {
2110
2111 })
2112
2113 PIt("should fail if the GVK cannot be mapped to a Resource", func() {
2114
2115 })
2116
2117 It("should not retain any data in the obj variable that is not on the server", func() {
2118 cl, err := client.New(cfg, client.Options{})
2119 Expect(err).NotTo(HaveOccurred())
2120 Expect(cl).NotTo(BeNil())
2121
2122 By("First creating the object")
2123 toCreate := &pkg.ChaosPod{ObjectMeta: metav1.ObjectMeta{Name: "retain-metadata", Namespace: ns}}
2124 Expect(cl.Create(ctx, toCreate)).NotTo(HaveOccurred())
2125
2126 By("Fetching it into a variable that has finalizers set")
2127 toGetInto := &metav1.PartialObjectMetadata{
2128 TypeMeta: metav1.TypeMeta{APIVersion: "chaosapps.metamagical.io/v1", Kind: "ChaosPod"},
2129 ObjectMeta: metav1.ObjectMeta{Namespace: ns, Name: "retain-metadata"},
2130 }
2131 toGetInto.SetFinalizers([]string{"some-finalizer"})
2132 Expect(cl.Get(ctx, client.ObjectKeyFromObject(toGetInto), toGetInto)).NotTo(HaveOccurred())
2133
2134 By("Ensuring the created and the received objects metadata are equal")
2135 Expect(toCreate.ObjectMeta).Should(Equal(toGetInto.ObjectMeta))
2136 })
2137 })
2138 })
2139
2140 Describe("List", func() {
2141 Context("with structured objects", func() {
2142 It("should fetch collection of objects", func() {
2143 By("creating an initial object")
2144 dep, err := clientset.AppsV1().Deployments(ns).Create(ctx, dep, metav1.CreateOptions{})
2145 Expect(err).NotTo(HaveOccurred())
2146
2147 cl, err := client.New(cfg, client.Options{})
2148 Expect(err).NotTo(HaveOccurred())
2149
2150 By("listing all objects of that type in the cluster")
2151 deps := &appsv1.DeploymentList{}
2152 Expect(cl.List(context.Background(), deps)).NotTo(HaveOccurred())
2153
2154 Expect(deps.Items).NotTo(BeEmpty())
2155 hasDep := false
2156 for _, item := range deps.Items {
2157 if item.Name == dep.Name && item.Namespace == dep.Namespace {
2158 hasDep = true
2159 break
2160 }
2161 }
2162 Expect(hasDep).To(BeTrue())
2163 })
2164
2165 It("should fetch unstructured collection of objects", func() {
2166 By("create an initial object")
2167 _, err := clientset.AppsV1().Deployments(ns).Create(ctx, dep, metav1.CreateOptions{})
2168 Expect(err).NotTo(HaveOccurred())
2169
2170 cl, err := client.New(cfg, client.Options{})
2171 Expect(err).NotTo(HaveOccurred())
2172
2173 By("listing all objects of that type in the cluster")
2174 deps := &unstructured.UnstructuredList{}
2175 deps.SetGroupVersionKind(schema.GroupVersionKind{
2176 Group: "apps",
2177 Kind: "DeploymentList",
2178 Version: "v1",
2179 })
2180 err = cl.List(context.Background(), deps)
2181 Expect(err).NotTo(HaveOccurred())
2182
2183 Expect(deps.Items).NotTo(BeEmpty())
2184 hasDep := false
2185 for _, item := range deps.Items {
2186 Expect(item.GroupVersionKind()).To(Equal(schema.GroupVersionKind{
2187 Group: "apps",
2188 Kind: "Deployment",
2189 Version: "v1",
2190 }))
2191 if item.GetName() == dep.Name && item.GetNamespace() == dep.Namespace {
2192 hasDep = true
2193 break
2194 }
2195 }
2196 Expect(hasDep).To(BeTrue())
2197 })
2198
2199 It("should fetch unstructured collection of objects, even if scheme is empty", func() {
2200 By("create an initial object")
2201 _, err := clientset.AppsV1().Deployments(ns).Create(ctx, dep, metav1.CreateOptions{})
2202 Expect(err).NotTo(HaveOccurred())
2203
2204 cl, err := client.New(cfg, client.Options{Scheme: runtime.NewScheme()})
2205 Expect(err).NotTo(HaveOccurred())
2206
2207 By("listing all objects of that type in the cluster")
2208 deps := &unstructured.UnstructuredList{}
2209 deps.SetGroupVersionKind(schema.GroupVersionKind{
2210 Group: "apps",
2211 Kind: "DeploymentList",
2212 Version: "v1",
2213 })
2214 err = cl.List(context.Background(), deps)
2215 Expect(err).NotTo(HaveOccurred())
2216
2217 Expect(deps.Items).NotTo(BeEmpty())
2218 hasDep := false
2219 for _, item := range deps.Items {
2220 if item.GetName() == dep.Name && item.GetNamespace() == dep.Namespace {
2221 hasDep = true
2222 break
2223 }
2224 }
2225 Expect(hasDep).To(BeTrue())
2226 })
2227
2228 It("should return an empty list if there are no matching objects", func() {
2229 cl, err := client.New(cfg, client.Options{})
2230 Expect(err).NotTo(HaveOccurred())
2231
2232 By("listing all Deployments in the cluster")
2233 deps := &appsv1.DeploymentList{}
2234 Expect(cl.List(context.Background(), deps)).NotTo(HaveOccurred())
2235
2236 By("validating no Deployments are returned")
2237 Expect(deps.Items).To(BeEmpty())
2238 })
2239
2240
2241 It("should filter results by label selector", func() {
2242 By("creating a Deployment with the app=frontend label")
2243 depFrontend := &appsv1.Deployment{
2244 ObjectMeta: metav1.ObjectMeta{
2245 Name: "deployment-frontend",
2246 Namespace: ns,
2247 Labels: map[string]string{"app": "frontend"},
2248 },
2249 Spec: appsv1.DeploymentSpec{
2250 Selector: &metav1.LabelSelector{
2251 MatchLabels: map[string]string{"app": "frontend"},
2252 },
2253 Template: corev1.PodTemplateSpec{
2254 ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"app": "frontend"}},
2255 Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "nginx", Image: "nginx"}}},
2256 },
2257 },
2258 }
2259 depFrontend, err := clientset.AppsV1().Deployments(ns).Create(ctx, depFrontend, metav1.CreateOptions{})
2260 Expect(err).NotTo(HaveOccurred())
2261
2262 By("creating a Deployment with the app=backend label")
2263 depBackend := &appsv1.Deployment{
2264 ObjectMeta: metav1.ObjectMeta{
2265 Name: "deployment-backend",
2266 Namespace: ns,
2267 Labels: map[string]string{"app": "backend"},
2268 },
2269 Spec: appsv1.DeploymentSpec{
2270 Selector: &metav1.LabelSelector{
2271 MatchLabels: map[string]string{"app": "backend"},
2272 },
2273 Template: corev1.PodTemplateSpec{
2274 ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"app": "backend"}},
2275 Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "nginx", Image: "nginx"}}},
2276 },
2277 },
2278 }
2279 depBackend, err = clientset.AppsV1().Deployments(ns).Create(ctx, depBackend, metav1.CreateOptions{})
2280 Expect(err).NotTo(HaveOccurred())
2281
2282 cl, err := client.New(cfg, client.Options{})
2283 Expect(err).NotTo(HaveOccurred())
2284
2285 By("listing all Deployments with label app=backend")
2286 deps := &appsv1.DeploymentList{}
2287 labels := map[string]string{"app": "backend"}
2288 err = cl.List(context.Background(), deps, client.MatchingLabels(labels))
2289 Expect(err).NotTo(HaveOccurred())
2290
2291 By("only the Deployment with the backend label is returned")
2292 Expect(deps.Items).NotTo(BeEmpty())
2293 Expect(1).To(Equal(len(deps.Items)))
2294 actual := deps.Items[0]
2295 Expect(actual.Name).To(Equal("deployment-backend"))
2296
2297 deleteDeployment(ctx, depFrontend, ns)
2298 deleteDeployment(ctx, depBackend, ns)
2299 })
2300
2301 It("should filter results by namespace selector", func() {
2302 By("creating a Deployment in test-namespace-1")
2303 tns1 := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "test-namespace-1"}}
2304 _, err := clientset.CoreV1().Namespaces().Create(ctx, tns1, metav1.CreateOptions{})
2305 Expect(err).NotTo(HaveOccurred())
2306 depFrontend := &appsv1.Deployment{
2307 ObjectMeta: metav1.ObjectMeta{Name: "deployment-frontend", Namespace: "test-namespace-1"},
2308 Spec: appsv1.DeploymentSpec{
2309 Selector: &metav1.LabelSelector{
2310 MatchLabels: map[string]string{"app": "frontend"},
2311 },
2312 Template: corev1.PodTemplateSpec{
2313 ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"app": "frontend"}},
2314 Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "nginx", Image: "nginx"}}},
2315 },
2316 },
2317 }
2318 depFrontend, err = clientset.AppsV1().Deployments("test-namespace-1").Create(ctx, depFrontend, metav1.CreateOptions{})
2319 Expect(err).NotTo(HaveOccurred())
2320
2321 By("creating a Deployment in test-namespace-2")
2322 tns2 := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "test-namespace-2"}}
2323 _, err = clientset.CoreV1().Namespaces().Create(ctx, tns2, metav1.CreateOptions{})
2324 Expect(err).NotTo(HaveOccurred())
2325 depBackend := &appsv1.Deployment{
2326 ObjectMeta: metav1.ObjectMeta{Name: "deployment-backend", Namespace: "test-namespace-2"},
2327 Spec: appsv1.DeploymentSpec{
2328 Selector: &metav1.LabelSelector{
2329 MatchLabels: map[string]string{"app": "backend"},
2330 },
2331 Template: corev1.PodTemplateSpec{
2332 ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"app": "backend"}},
2333 Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "nginx", Image: "nginx"}}},
2334 },
2335 },
2336 }
2337 depBackend, err = clientset.AppsV1().Deployments("test-namespace-2").Create(ctx, depBackend, metav1.CreateOptions{})
2338 Expect(err).NotTo(HaveOccurred())
2339
2340 cl, err := client.New(cfg, client.Options{})
2341 Expect(err).NotTo(HaveOccurred())
2342
2343 By("listing all Deployments in test-namespace-1")
2344 deps := &appsv1.DeploymentList{}
2345 err = cl.List(context.Background(), deps, client.InNamespace("test-namespace-1"))
2346 Expect(err).NotTo(HaveOccurred())
2347
2348 By("only the Deployment in test-namespace-1 is returned")
2349 Expect(deps.Items).NotTo(BeEmpty())
2350 Expect(1).To(Equal(len(deps.Items)))
2351 actual := deps.Items[0]
2352 Expect(actual.Name).To(Equal("deployment-frontend"))
2353
2354 deleteDeployment(ctx, depFrontend, "test-namespace-1")
2355 deleteDeployment(ctx, depBackend, "test-namespace-2")
2356 deleteNamespace(ctx, tns1)
2357 deleteNamespace(ctx, tns2)
2358 })
2359
2360 It("should filter results by field selector", func() {
2361 By("creating a Deployment with name deployment-frontend")
2362 depFrontend := &appsv1.Deployment{
2363 ObjectMeta: metav1.ObjectMeta{Name: "deployment-frontend", Namespace: ns},
2364 Spec: appsv1.DeploymentSpec{
2365 Selector: &metav1.LabelSelector{
2366 MatchLabels: map[string]string{"app": "frontend"},
2367 },
2368 Template: corev1.PodTemplateSpec{
2369 ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"app": "frontend"}},
2370 Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "nginx", Image: "nginx"}}},
2371 },
2372 },
2373 }
2374 depFrontend, err := clientset.AppsV1().Deployments(ns).Create(ctx, depFrontend, metav1.CreateOptions{})
2375 Expect(err).NotTo(HaveOccurred())
2376
2377 By("creating a Deployment with name deployment-backend")
2378 depBackend := &appsv1.Deployment{
2379 ObjectMeta: metav1.ObjectMeta{Name: "deployment-backend", Namespace: ns},
2380 Spec: appsv1.DeploymentSpec{
2381 Selector: &metav1.LabelSelector{
2382 MatchLabels: map[string]string{"app": "backend"},
2383 },
2384 Template: corev1.PodTemplateSpec{
2385 ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"app": "backend"}},
2386 Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "nginx", Image: "nginx"}}},
2387 },
2388 },
2389 }
2390 depBackend, err = clientset.AppsV1().Deployments(ns).Create(ctx, depBackend, metav1.CreateOptions{})
2391 Expect(err).NotTo(HaveOccurred())
2392
2393 cl, err := client.New(cfg, client.Options{})
2394 Expect(err).NotTo(HaveOccurred())
2395
2396 By("listing all Deployments with field metadata.name=deployment-backend")
2397 deps := &appsv1.DeploymentList{}
2398 err = cl.List(context.Background(), deps,
2399 client.MatchingFields{"metadata.name": "deployment-backend"})
2400 Expect(err).NotTo(HaveOccurred())
2401
2402 By("only the Deployment with the backend field is returned")
2403 Expect(deps.Items).NotTo(BeEmpty())
2404 Expect(1).To(Equal(len(deps.Items)))
2405 actual := deps.Items[0]
2406 Expect(actual.Name).To(Equal("deployment-backend"))
2407
2408 deleteDeployment(ctx, depFrontend, ns)
2409 deleteDeployment(ctx, depBackend, ns)
2410 })
2411
2412 It("should filter results by namespace selector and label selector", func() {
2413 By("creating a Deployment in test-namespace-3 with the app=frontend label")
2414 tns3 := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "test-namespace-3"}}
2415 _, err := clientset.CoreV1().Namespaces().Create(ctx, tns3, metav1.CreateOptions{})
2416 Expect(err).NotTo(HaveOccurred())
2417 depFrontend3 := &appsv1.Deployment{
2418 ObjectMeta: metav1.ObjectMeta{
2419 Name: "deployment-frontend",
2420 Namespace: "test-namespace-3",
2421 Labels: map[string]string{"app": "frontend"},
2422 },
2423 Spec: appsv1.DeploymentSpec{
2424 Selector: &metav1.LabelSelector{
2425 MatchLabels: map[string]string{"app": "frontend"},
2426 },
2427 Template: corev1.PodTemplateSpec{
2428 ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"app": "frontend"}},
2429 Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "nginx", Image: "nginx"}}},
2430 },
2431 },
2432 }
2433 depFrontend3, err = clientset.AppsV1().Deployments("test-namespace-3").Create(ctx, depFrontend3, metav1.CreateOptions{})
2434 Expect(err).NotTo(HaveOccurred())
2435
2436 By("creating a Deployment in test-namespace-3 with the app=backend label")
2437 depBackend3 := &appsv1.Deployment{
2438 ObjectMeta: metav1.ObjectMeta{
2439 Name: "deployment-backend",
2440 Namespace: "test-namespace-3",
2441 Labels: map[string]string{"app": "backend"},
2442 },
2443 Spec: appsv1.DeploymentSpec{
2444 Selector: &metav1.LabelSelector{
2445 MatchLabels: map[string]string{"app": "backend"},
2446 },
2447 Template: corev1.PodTemplateSpec{
2448 ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"app": "backend"}},
2449 Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "nginx", Image: "nginx"}}},
2450 },
2451 },
2452 }
2453 depBackend3, err = clientset.AppsV1().Deployments("test-namespace-3").Create(ctx, depBackend3, metav1.CreateOptions{})
2454 Expect(err).NotTo(HaveOccurred())
2455
2456 By("creating a Deployment in test-namespace-4 with the app=frontend label")
2457 tns4 := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "test-namespace-4"}}
2458 _, err = clientset.CoreV1().Namespaces().Create(ctx, tns4, metav1.CreateOptions{})
2459 Expect(err).NotTo(HaveOccurred())
2460 depFrontend4 := &appsv1.Deployment{
2461 ObjectMeta: metav1.ObjectMeta{
2462 Name: "deployment-frontend",
2463 Namespace: "test-namespace-4",
2464 Labels: map[string]string{"app": "frontend"},
2465 },
2466 Spec: appsv1.DeploymentSpec{
2467 Selector: &metav1.LabelSelector{
2468 MatchLabels: map[string]string{"app": "frontend"},
2469 },
2470 Template: corev1.PodTemplateSpec{
2471 ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"app": "frontend"}},
2472 Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "nginx", Image: "nginx"}}},
2473 },
2474 },
2475 }
2476 depFrontend4, err = clientset.AppsV1().Deployments("test-namespace-4").Create(ctx, depFrontend4, metav1.CreateOptions{})
2477 Expect(err).NotTo(HaveOccurred())
2478
2479 cl, err := client.New(cfg, client.Options{})
2480 Expect(err).NotTo(HaveOccurred())
2481
2482 By("listing all Deployments in test-namespace-3 with label app=frontend")
2483 deps := &appsv1.DeploymentList{}
2484 labels := map[string]string{"app": "frontend"}
2485 err = cl.List(context.Background(), deps,
2486 client.InNamespace("test-namespace-3"),
2487 client.MatchingLabels(labels),
2488 )
2489 Expect(err).NotTo(HaveOccurred())
2490
2491 By("only the Deployment in test-namespace-3 with label app=frontend is returned")
2492 Expect(deps.Items).NotTo(BeEmpty())
2493 Expect(1).To(Equal(len(deps.Items)))
2494 actual := deps.Items[0]
2495 Expect(actual.Name).To(Equal("deployment-frontend"))
2496 Expect(actual.Namespace).To(Equal("test-namespace-3"))
2497
2498 deleteDeployment(ctx, depFrontend3, "test-namespace-3")
2499 deleteDeployment(ctx, depBackend3, "test-namespace-3")
2500 deleteDeployment(ctx, depFrontend4, "test-namespace-4")
2501 deleteNamespace(ctx, tns3)
2502 deleteNamespace(ctx, tns4)
2503 })
2504
2505 It("should filter results using limit and continue options", func() {
2506
2507 makeDeployment := func(suffix string) *appsv1.Deployment {
2508 return &appsv1.Deployment{
2509 ObjectMeta: metav1.ObjectMeta{
2510 Name: fmt.Sprintf("deployment-%s", suffix),
2511 },
2512 Spec: appsv1.DeploymentSpec{
2513 Selector: &metav1.LabelSelector{
2514 MatchLabels: map[string]string{"foo": "bar"},
2515 },
2516 Template: corev1.PodTemplateSpec{
2517 ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"foo": "bar"}},
2518 Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "nginx", Image: "nginx"}}},
2519 },
2520 },
2521 }
2522 }
2523
2524 By("creating 4 deployments")
2525 dep1 := makeDeployment("1")
2526 dep1, err := clientset.AppsV1().Deployments(ns).Create(ctx, dep1, metav1.CreateOptions{})
2527 Expect(err).NotTo(HaveOccurred())
2528 defer deleteDeployment(ctx, dep1, ns)
2529
2530 dep2 := makeDeployment("2")
2531 dep2, err = clientset.AppsV1().Deployments(ns).Create(ctx, dep2, metav1.CreateOptions{})
2532 Expect(err).NotTo(HaveOccurred())
2533 defer deleteDeployment(ctx, dep2, ns)
2534
2535 dep3 := makeDeployment("3")
2536 dep3, err = clientset.AppsV1().Deployments(ns).Create(ctx, dep3, metav1.CreateOptions{})
2537 Expect(err).NotTo(HaveOccurred())
2538 defer deleteDeployment(ctx, dep3, ns)
2539
2540 dep4 := makeDeployment("4")
2541 dep4, err = clientset.AppsV1().Deployments(ns).Create(ctx, dep4, metav1.CreateOptions{})
2542 Expect(err).NotTo(HaveOccurred())
2543 defer deleteDeployment(ctx, dep4, ns)
2544
2545 cl, err := client.New(cfg, client.Options{})
2546 Expect(err).NotTo(HaveOccurred())
2547
2548 By("listing 1 deployment when limit=1 is used")
2549 deps := &appsv1.DeploymentList{}
2550 err = cl.List(context.Background(), deps,
2551 client.Limit(1),
2552 )
2553 Expect(err).NotTo(HaveOccurred())
2554
2555 Expect(deps.Items).To(HaveLen(1))
2556 Expect(deps.Continue).NotTo(BeEmpty())
2557 Expect(deps.Items[0].Name).To(Equal(dep1.Name))
2558
2559 continueToken := deps.Continue
2560
2561 By("listing the next deployment when previous continuation token is used and limit=1")
2562 deps = &appsv1.DeploymentList{}
2563 err = cl.List(context.Background(), deps,
2564 client.Limit(1),
2565 client.Continue(continueToken),
2566 )
2567 Expect(err).NotTo(HaveOccurred())
2568
2569 Expect(deps.Items).To(HaveLen(1))
2570 Expect(deps.Continue).NotTo(BeEmpty())
2571 Expect(deps.Items[0].Name).To(Equal(dep2.Name))
2572
2573 continueToken = deps.Continue
2574
2575 By("listing the 2 remaining deployments when previous continuation token is used without a limit")
2576 deps = &appsv1.DeploymentList{}
2577 err = cl.List(context.Background(), deps,
2578 client.Continue(continueToken),
2579 )
2580 Expect(err).NotTo(HaveOccurred())
2581
2582 Expect(deps.Items).To(HaveLen(2))
2583 Expect(deps.Continue).To(BeEmpty())
2584 Expect(deps.Items[0].Name).To(Equal(dep3.Name))
2585 Expect(deps.Items[1].Name).To(Equal(dep4.Name))
2586 })
2587
2588 PIt("should fail if the object doesn't have meta", func() {
2589
2590 })
2591
2592 PIt("should fail if the object cannot be mapped to a GVK", func() {
2593
2594 })
2595
2596 PIt("should fail if the GVK cannot be mapped to a Resource", func() {
2597
2598 })
2599 })
2600
2601 Context("with unstructured objects", func() {
2602 It("should fetch collection of objects", func() {
2603 By("create an initial object")
2604 _, err := clientset.AppsV1().Deployments(ns).Create(ctx, dep, metav1.CreateOptions{})
2605 Expect(err).NotTo(HaveOccurred())
2606
2607 cl, err := client.New(cfg, client.Options{})
2608 Expect(err).NotTo(HaveOccurred())
2609
2610 By("listing all objects of that type in the cluster")
2611 deps := &unstructured.UnstructuredList{}
2612 deps.SetGroupVersionKind(schema.GroupVersionKind{
2613 Group: "apps",
2614 Kind: "DeploymentList",
2615 Version: "v1",
2616 })
2617 err = cl.List(context.Background(), deps)
2618 Expect(err).NotTo(HaveOccurred())
2619
2620 Expect(deps.Items).NotTo(BeEmpty())
2621 hasDep := false
2622 for _, item := range deps.Items {
2623 if item.GetName() == dep.Name && item.GetNamespace() == dep.Namespace {
2624 hasDep = true
2625 break
2626 }
2627 }
2628 Expect(hasDep).To(BeTrue())
2629 })
2630
2631 It("should return an empty list if there are no matching objects", func() {
2632 cl, err := client.New(cfg, client.Options{})
2633 Expect(err).NotTo(HaveOccurred())
2634
2635 By("listing all Deployments in the cluster")
2636 deps := &unstructured.UnstructuredList{}
2637 deps.SetGroupVersionKind(schema.GroupVersionKind{
2638 Group: "apps",
2639 Kind: "DeploymentList",
2640 Version: "v1",
2641 })
2642 Expect(cl.List(context.Background(), deps)).NotTo(HaveOccurred())
2643
2644 By("validating no Deployments are returned")
2645 Expect(deps.Items).To(BeEmpty())
2646 })
2647
2648 It("should filter results by namespace selector", func() {
2649 By("creating a Deployment in test-namespace-5")
2650 tns1 := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "test-namespace-5"}}
2651 _, err := clientset.CoreV1().Namespaces().Create(ctx, tns1, metav1.CreateOptions{})
2652 Expect(err).NotTo(HaveOccurred())
2653 depFrontend := &appsv1.Deployment{
2654 ObjectMeta: metav1.ObjectMeta{Name: "deployment-frontend", Namespace: "test-namespace-5"},
2655 Spec: appsv1.DeploymentSpec{
2656 Selector: &metav1.LabelSelector{
2657 MatchLabels: map[string]string{"app": "frontend"},
2658 },
2659 Template: corev1.PodTemplateSpec{
2660 ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"app": "frontend"}},
2661 Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "nginx", Image: "nginx"}}},
2662 },
2663 },
2664 }
2665 depFrontend, err = clientset.AppsV1().Deployments("test-namespace-5").Create(ctx, depFrontend, metav1.CreateOptions{})
2666 Expect(err).NotTo(HaveOccurred())
2667
2668 By("creating a Deployment in test-namespace-6")
2669 tns2 := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "test-namespace-6"}}
2670 _, err = clientset.CoreV1().Namespaces().Create(ctx, tns2, metav1.CreateOptions{})
2671 Expect(err).NotTo(HaveOccurred())
2672 depBackend := &appsv1.Deployment{
2673 ObjectMeta: metav1.ObjectMeta{Name: "deployment-backend", Namespace: "test-namespace-6"},
2674 Spec: appsv1.DeploymentSpec{
2675 Selector: &metav1.LabelSelector{
2676 MatchLabels: map[string]string{"app": "backend"},
2677 },
2678 Template: corev1.PodTemplateSpec{
2679 ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"app": "backend"}},
2680 Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "nginx", Image: "nginx"}}},
2681 },
2682 },
2683 }
2684 depBackend, err = clientset.AppsV1().Deployments("test-namespace-6").Create(ctx, depBackend, metav1.CreateOptions{})
2685 Expect(err).NotTo(HaveOccurred())
2686
2687 cl, err := client.New(cfg, client.Options{})
2688 Expect(err).NotTo(HaveOccurred())
2689
2690 By("listing all Deployments in test-namespace-5")
2691 deps := &unstructured.UnstructuredList{}
2692 deps.SetGroupVersionKind(schema.GroupVersionKind{
2693 Group: "apps",
2694 Kind: "DeploymentList",
2695 Version: "v1",
2696 })
2697 err = cl.List(context.Background(), deps, client.InNamespace("test-namespace-5"))
2698 Expect(err).NotTo(HaveOccurred())
2699
2700 By("only the Deployment in test-namespace-5 is returned")
2701 Expect(deps.Items).NotTo(BeEmpty())
2702 Expect(1).To(Equal(len(deps.Items)))
2703 actual := deps.Items[0]
2704 Expect(actual.GetName()).To(Equal("deployment-frontend"))
2705
2706 deleteDeployment(ctx, depFrontend, "test-namespace-5")
2707 deleteDeployment(ctx, depBackend, "test-namespace-6")
2708 deleteNamespace(ctx, tns1)
2709 deleteNamespace(ctx, tns2)
2710 })
2711
2712 It("should filter results by field selector", func() {
2713 By("creating a Deployment with name deployment-frontend")
2714 depFrontend := &appsv1.Deployment{
2715 ObjectMeta: metav1.ObjectMeta{Name: "deployment-frontend", Namespace: ns},
2716 Spec: appsv1.DeploymentSpec{
2717 Selector: &metav1.LabelSelector{
2718 MatchLabels: map[string]string{"app": "frontend"},
2719 },
2720 Template: corev1.PodTemplateSpec{
2721 ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"app": "frontend"}},
2722 Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "nginx", Image: "nginx"}}},
2723 },
2724 },
2725 }
2726 depFrontend, err := clientset.AppsV1().Deployments(ns).Create(ctx, depFrontend, metav1.CreateOptions{})
2727 Expect(err).NotTo(HaveOccurred())
2728
2729 By("creating a Deployment with name deployment-backend")
2730 depBackend := &appsv1.Deployment{
2731 ObjectMeta: metav1.ObjectMeta{Name: "deployment-backend", Namespace: ns},
2732 Spec: appsv1.DeploymentSpec{
2733 Selector: &metav1.LabelSelector{
2734 MatchLabels: map[string]string{"app": "backend"},
2735 },
2736 Template: corev1.PodTemplateSpec{
2737 ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"app": "backend"}},
2738 Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "nginx", Image: "nginx"}}},
2739 },
2740 },
2741 }
2742 depBackend, err = clientset.AppsV1().Deployments(ns).Create(ctx, depBackend, metav1.CreateOptions{})
2743 Expect(err).NotTo(HaveOccurred())
2744
2745 cl, err := client.New(cfg, client.Options{})
2746 Expect(err).NotTo(HaveOccurred())
2747
2748 By("listing all Deployments with field metadata.name=deployment-backend")
2749 deps := &unstructured.UnstructuredList{}
2750 deps.SetGroupVersionKind(schema.GroupVersionKind{
2751 Group: "apps",
2752 Kind: "DeploymentList",
2753 Version: "v1",
2754 })
2755 err = cl.List(context.Background(), deps,
2756 client.MatchingFields{"metadata.name": "deployment-backend"})
2757 Expect(err).NotTo(HaveOccurred())
2758
2759 By("only the Deployment with the backend field is returned")
2760 Expect(deps.Items).NotTo(BeEmpty())
2761 Expect(1).To(Equal(len(deps.Items)))
2762 actual := deps.Items[0]
2763 Expect(actual.GetName()).To(Equal("deployment-backend"))
2764
2765 deleteDeployment(ctx, depFrontend, ns)
2766 deleteDeployment(ctx, depBackend, ns)
2767 })
2768
2769 It("should filter results by namespace selector and label selector", func() {
2770 By("creating a Deployment in test-namespace-7 with the app=frontend label")
2771 tns3 := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "test-namespace-7"}}
2772 _, err := clientset.CoreV1().Namespaces().Create(ctx, tns3, metav1.CreateOptions{})
2773 Expect(err).NotTo(HaveOccurred())
2774 depFrontend3 := &appsv1.Deployment{
2775 ObjectMeta: metav1.ObjectMeta{
2776 Name: "deployment-frontend",
2777 Namespace: "test-namespace-7",
2778 Labels: map[string]string{"app": "frontend"},
2779 },
2780 Spec: appsv1.DeploymentSpec{
2781 Selector: &metav1.LabelSelector{
2782 MatchLabels: map[string]string{"app": "frontend"},
2783 },
2784 Template: corev1.PodTemplateSpec{
2785 ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"app": "frontend"}},
2786 Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "nginx", Image: "nginx"}}},
2787 },
2788 },
2789 }
2790 depFrontend3, err = clientset.AppsV1().Deployments("test-namespace-7").Create(ctx, depFrontend3, metav1.CreateOptions{})
2791 Expect(err).NotTo(HaveOccurred())
2792
2793 By("creating a Deployment in test-namespace-7 with the app=backend label")
2794 depBackend3 := &appsv1.Deployment{
2795 ObjectMeta: metav1.ObjectMeta{
2796 Name: "deployment-backend",
2797 Namespace: "test-namespace-7",
2798 Labels: map[string]string{"app": "backend"},
2799 },
2800 Spec: appsv1.DeploymentSpec{
2801 Selector: &metav1.LabelSelector{
2802 MatchLabels: map[string]string{"app": "backend"},
2803 },
2804 Template: corev1.PodTemplateSpec{
2805 ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"app": "backend"}},
2806 Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "nginx", Image: "nginx"}}},
2807 },
2808 },
2809 }
2810 depBackend3, err = clientset.AppsV1().Deployments("test-namespace-7").Create(ctx, depBackend3, metav1.CreateOptions{})
2811 Expect(err).NotTo(HaveOccurred())
2812
2813 By("creating a Deployment in test-namespace-8 with the app=frontend label")
2814 tns4 := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "test-namespace-8"}}
2815 _, err = clientset.CoreV1().Namespaces().Create(ctx, tns4, metav1.CreateOptions{})
2816 Expect(err).NotTo(HaveOccurred())
2817 depFrontend4 := &appsv1.Deployment{
2818 ObjectMeta: metav1.ObjectMeta{
2819 Name: "deployment-frontend",
2820 Namespace: "test-namespace-8",
2821 Labels: map[string]string{"app": "frontend"},
2822 },
2823 Spec: appsv1.DeploymentSpec{
2824 Selector: &metav1.LabelSelector{
2825 MatchLabels: map[string]string{"app": "frontend"},
2826 },
2827 Template: corev1.PodTemplateSpec{
2828 ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"app": "frontend"}},
2829 Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "nginx", Image: "nginx"}}},
2830 },
2831 },
2832 }
2833 depFrontend4, err = clientset.AppsV1().Deployments("test-namespace-8").Create(ctx, depFrontend4, metav1.CreateOptions{})
2834 Expect(err).NotTo(HaveOccurred())
2835
2836 cl, err := client.New(cfg, client.Options{})
2837 Expect(err).NotTo(HaveOccurred())
2838
2839 By("listing all Deployments in test-namespace-8 with label app=frontend")
2840 deps := &unstructured.UnstructuredList{}
2841 deps.SetGroupVersionKind(schema.GroupVersionKind{
2842 Group: "apps",
2843 Kind: "DeploymentList",
2844 Version: "v1",
2845 })
2846 labels := map[string]string{"app": "frontend"}
2847 err = cl.List(context.Background(), deps,
2848 client.InNamespace("test-namespace-7"), client.MatchingLabels(labels))
2849 Expect(err).NotTo(HaveOccurred())
2850
2851 By("only the Deployment in test-namespace-7 with label app=frontend is returned")
2852 Expect(deps.Items).NotTo(BeEmpty())
2853 Expect(1).To(Equal(len(deps.Items)))
2854 actual := deps.Items[0]
2855 Expect(actual.GetName()).To(Equal("deployment-frontend"))
2856 Expect(actual.GetNamespace()).To(Equal("test-namespace-7"))
2857
2858 deleteDeployment(ctx, depFrontend3, "test-namespace-7")
2859 deleteDeployment(ctx, depBackend3, "test-namespace-7")
2860 deleteDeployment(ctx, depFrontend4, "test-namespace-8")
2861 deleteNamespace(ctx, tns3)
2862 deleteNamespace(ctx, tns4)
2863 })
2864
2865 PIt("should fail if the object doesn't have meta", func() {
2866
2867 })
2868
2869 PIt("should filter results by namespace selector", func() {
2870
2871 })
2872 })
2873 Context("with metadata objects", func() {
2874 It("should fetch collection of objects", func() {
2875 By("creating an initial object")
2876 dep, err := clientset.AppsV1().Deployments(ns).Create(ctx, dep, metav1.CreateOptions{})
2877 Expect(err).NotTo(HaveOccurred())
2878
2879 cl, err := client.New(cfg, client.Options{})
2880 Expect(err).NotTo(HaveOccurred())
2881
2882 By("listing all objects of that type in the cluster")
2883 gvk := schema.GroupVersionKind{
2884 Group: "apps",
2885 Version: "v1",
2886 Kind: "DeploymentList",
2887 }
2888 metaList := &metav1.PartialObjectMetadataList{}
2889 metaList.SetGroupVersionKind(gvk)
2890 Expect(cl.List(context.Background(), metaList)).NotTo(HaveOccurred())
2891
2892 By("validating that the list GVK has been preserved")
2893 Expect(metaList.GroupVersionKind()).To(Equal(gvk))
2894
2895 By("validating that the list has the expected deployment")
2896 Expect(metaList.Items).NotTo(BeEmpty())
2897 hasDep := false
2898 for _, item := range metaList.Items {
2899 Expect(item.GroupVersionKind()).To(Equal(schema.GroupVersionKind{
2900 Group: "apps",
2901 Version: "v1",
2902 Kind: "Deployment",
2903 }))
2904
2905 if item.Name == dep.Name && item.Namespace == dep.Namespace {
2906 hasDep = true
2907 break
2908 }
2909 }
2910 Expect(hasDep).To(BeTrue())
2911 })
2912
2913 It("should return an empty list if there are no matching objects", func() {
2914 cl, err := client.New(cfg, client.Options{})
2915 Expect(err).NotTo(HaveOccurred())
2916
2917 By("listing all Deployments in the cluster")
2918 metaList := &metav1.PartialObjectMetadataList{}
2919 metaList.SetGroupVersionKind(schema.GroupVersionKind{
2920 Group: "apps",
2921 Version: "v1",
2922 Kind: "DeploymentList",
2923 })
2924 Expect(cl.List(context.Background(), metaList)).NotTo(HaveOccurred())
2925
2926 By("validating no Deployments are returned")
2927 Expect(metaList.Items).To(BeEmpty())
2928 })
2929
2930
2931 It("should filter results by label selector", func() {
2932 By("creating a Deployment with the app=frontend label")
2933 depFrontend := &appsv1.Deployment{
2934 ObjectMeta: metav1.ObjectMeta{
2935 Name: "deployment-frontend",
2936 Namespace: ns,
2937 Labels: map[string]string{"app": "frontend"},
2938 },
2939 Spec: appsv1.DeploymentSpec{
2940 Selector: &metav1.LabelSelector{
2941 MatchLabels: map[string]string{"app": "frontend"},
2942 },
2943 Template: corev1.PodTemplateSpec{
2944 ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"app": "frontend"}},
2945 Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "nginx", Image: "nginx"}}},
2946 },
2947 },
2948 }
2949 depFrontend, err := clientset.AppsV1().Deployments(ns).Create(ctx, depFrontend, metav1.CreateOptions{})
2950 Expect(err).NotTo(HaveOccurred())
2951
2952 By("creating a Deployment with the app=backend label")
2953 depBackend := &appsv1.Deployment{
2954 ObjectMeta: metav1.ObjectMeta{
2955 Name: "deployment-backend",
2956 Namespace: ns,
2957 Labels: map[string]string{"app": "backend"},
2958 },
2959 Spec: appsv1.DeploymentSpec{
2960 Selector: &metav1.LabelSelector{
2961 MatchLabels: map[string]string{"app": "backend"},
2962 },
2963 Template: corev1.PodTemplateSpec{
2964 ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"app": "backend"}},
2965 Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "nginx", Image: "nginx"}}},
2966 },
2967 },
2968 }
2969 depBackend, err = clientset.AppsV1().Deployments(ns).Create(ctx, depBackend, metav1.CreateOptions{})
2970 Expect(err).NotTo(HaveOccurred())
2971
2972 cl, err := client.New(cfg, client.Options{})
2973 Expect(err).NotTo(HaveOccurred())
2974
2975 By("listing all Deployments with label app=backend")
2976 metaList := &metav1.PartialObjectMetadataList{}
2977 metaList.SetGroupVersionKind(schema.GroupVersionKind{
2978 Group: "apps",
2979 Version: "v1",
2980 Kind: "DeploymentList",
2981 })
2982 labels := map[string]string{"app": "backend"}
2983 err = cl.List(context.Background(), metaList, client.MatchingLabels(labels))
2984 Expect(err).NotTo(HaveOccurred())
2985
2986 By("only the Deployment with the backend label is returned")
2987 Expect(metaList.Items).NotTo(BeEmpty())
2988 Expect(1).To(Equal(len(metaList.Items)))
2989 actual := metaList.Items[0]
2990 Expect(actual.Name).To(Equal("deployment-backend"))
2991
2992 deleteDeployment(ctx, depFrontend, ns)
2993 deleteDeployment(ctx, depBackend, ns)
2994 })
2995
2996 It("should filter results by namespace selector", func() {
2997 By("creating a Deployment in test-namespace-1")
2998 tns1 := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "test-namespace-1"}}
2999 _, err := clientset.CoreV1().Namespaces().Create(ctx, tns1, metav1.CreateOptions{})
3000 Expect(err).NotTo(HaveOccurred())
3001 depFrontend := &appsv1.Deployment{
3002 ObjectMeta: metav1.ObjectMeta{Name: "deployment-frontend", Namespace: "test-namespace-1"},
3003 Spec: appsv1.DeploymentSpec{
3004 Selector: &metav1.LabelSelector{
3005 MatchLabels: map[string]string{"app": "frontend"},
3006 },
3007 Template: corev1.PodTemplateSpec{
3008 ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"app": "frontend"}},
3009 Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "nginx", Image: "nginx"}}},
3010 },
3011 },
3012 }
3013 depFrontend, err = clientset.AppsV1().Deployments("test-namespace-1").Create(ctx, depFrontend, metav1.CreateOptions{})
3014 Expect(err).NotTo(HaveOccurred())
3015
3016 By("creating a Deployment in test-namespace-2")
3017 tns2 := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "test-namespace-2"}}
3018 _, err = clientset.CoreV1().Namespaces().Create(ctx, tns2, metav1.CreateOptions{})
3019 Expect(err).NotTo(HaveOccurred())
3020 depBackend := &appsv1.Deployment{
3021 ObjectMeta: metav1.ObjectMeta{Name: "deployment-backend", Namespace: "test-namespace-2"},
3022 Spec: appsv1.DeploymentSpec{
3023 Selector: &metav1.LabelSelector{
3024 MatchLabels: map[string]string{"app": "backend"},
3025 },
3026 Template: corev1.PodTemplateSpec{
3027 ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"app": "backend"}},
3028 Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "nginx", Image: "nginx"}}},
3029 },
3030 },
3031 }
3032 depBackend, err = clientset.AppsV1().Deployments("test-namespace-2").Create(ctx, depBackend, metav1.CreateOptions{})
3033 Expect(err).NotTo(HaveOccurred())
3034
3035 cl, err := client.New(cfg, client.Options{})
3036 Expect(err).NotTo(HaveOccurred())
3037
3038 By("listing all Deployments in test-namespace-1")
3039 metaList := &metav1.PartialObjectMetadataList{}
3040 metaList.SetGroupVersionKind(schema.GroupVersionKind{
3041 Group: "apps",
3042 Version: "v1",
3043 Kind: "DeploymentList",
3044 })
3045 err = cl.List(context.Background(), metaList, client.InNamespace("test-namespace-1"))
3046 Expect(err).NotTo(HaveOccurred())
3047
3048 By("only the Deployment in test-namespace-1 is returned")
3049 Expect(metaList.Items).NotTo(BeEmpty())
3050 Expect(1).To(Equal(len(metaList.Items)))
3051 actual := metaList.Items[0]
3052 Expect(actual.Name).To(Equal("deployment-frontend"))
3053
3054 deleteDeployment(ctx, depFrontend, "test-namespace-1")
3055 deleteDeployment(ctx, depBackend, "test-namespace-2")
3056 deleteNamespace(ctx, tns1)
3057 deleteNamespace(ctx, tns2)
3058 })
3059
3060 It("should filter results by field selector", func() {
3061 By("creating a Deployment with name deployment-frontend")
3062 depFrontend := &appsv1.Deployment{
3063 ObjectMeta: metav1.ObjectMeta{Name: "deployment-frontend", Namespace: ns},
3064 Spec: appsv1.DeploymentSpec{
3065 Selector: &metav1.LabelSelector{
3066 MatchLabels: map[string]string{"app": "frontend"},
3067 },
3068 Template: corev1.PodTemplateSpec{
3069 ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"app": "frontend"}},
3070 Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "nginx", Image: "nginx"}}},
3071 },
3072 },
3073 }
3074 depFrontend, err := clientset.AppsV1().Deployments(ns).Create(ctx, depFrontend, metav1.CreateOptions{})
3075 Expect(err).NotTo(HaveOccurred())
3076
3077 By("creating a Deployment with name deployment-backend")
3078 depBackend := &appsv1.Deployment{
3079 ObjectMeta: metav1.ObjectMeta{Name: "deployment-backend", Namespace: ns},
3080 Spec: appsv1.DeploymentSpec{
3081 Selector: &metav1.LabelSelector{
3082 MatchLabels: map[string]string{"app": "backend"},
3083 },
3084 Template: corev1.PodTemplateSpec{
3085 ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"app": "backend"}},
3086 Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "nginx", Image: "nginx"}}},
3087 },
3088 },
3089 }
3090 depBackend, err = clientset.AppsV1().Deployments(ns).Create(ctx, depBackend, metav1.CreateOptions{})
3091 Expect(err).NotTo(HaveOccurred())
3092
3093 cl, err := client.New(cfg, client.Options{})
3094 Expect(err).NotTo(HaveOccurred())
3095
3096 By("listing all Deployments with field metadata.name=deployment-backend")
3097 metaList := &metav1.PartialObjectMetadataList{}
3098 metaList.SetGroupVersionKind(schema.GroupVersionKind{
3099 Group: "apps",
3100 Version: "v1",
3101 Kind: "DeploymentList",
3102 })
3103 err = cl.List(context.Background(), metaList,
3104 client.MatchingFields{"metadata.name": "deployment-backend"})
3105 Expect(err).NotTo(HaveOccurred())
3106
3107 By("only the Deployment with the backend field is returned")
3108 Expect(metaList.Items).NotTo(BeEmpty())
3109 Expect(1).To(Equal(len(metaList.Items)))
3110 actual := metaList.Items[0]
3111 Expect(actual.Name).To(Equal("deployment-backend"))
3112
3113 deleteDeployment(ctx, depFrontend, ns)
3114 deleteDeployment(ctx, depBackend, ns)
3115 })
3116
3117 It("should filter results by namespace selector and label selector", func() {
3118 By("creating a Deployment in test-namespace-3 with the app=frontend label")
3119 tns3 := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "test-namespace-3"}}
3120 _, err := clientset.CoreV1().Namespaces().Create(ctx, tns3, metav1.CreateOptions{})
3121 Expect(err).NotTo(HaveOccurred())
3122 depFrontend3 := &appsv1.Deployment{
3123 ObjectMeta: metav1.ObjectMeta{
3124 Name: "deployment-frontend",
3125 Namespace: "test-namespace-3",
3126 Labels: map[string]string{"app": "frontend"},
3127 },
3128 Spec: appsv1.DeploymentSpec{
3129 Selector: &metav1.LabelSelector{
3130 MatchLabels: map[string]string{"app": "frontend"},
3131 },
3132 Template: corev1.PodTemplateSpec{
3133 ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"app": "frontend"}},
3134 Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "nginx", Image: "nginx"}}},
3135 },
3136 },
3137 }
3138 depFrontend3, err = clientset.AppsV1().Deployments("test-namespace-3").Create(ctx, depFrontend3, metav1.CreateOptions{})
3139 Expect(err).NotTo(HaveOccurred())
3140
3141 By("creating a Deployment in test-namespace-3 with the app=backend label")
3142 depBackend3 := &appsv1.Deployment{
3143 ObjectMeta: metav1.ObjectMeta{
3144 Name: "deployment-backend",
3145 Namespace: "test-namespace-3",
3146 Labels: map[string]string{"app": "backend"},
3147 },
3148 Spec: appsv1.DeploymentSpec{
3149 Selector: &metav1.LabelSelector{
3150 MatchLabels: map[string]string{"app": "backend"},
3151 },
3152 Template: corev1.PodTemplateSpec{
3153 ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"app": "backend"}},
3154 Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "nginx", Image: "nginx"}}},
3155 },
3156 },
3157 }
3158 depBackend3, err = clientset.AppsV1().Deployments("test-namespace-3").Create(ctx, depBackend3, metav1.CreateOptions{})
3159 Expect(err).NotTo(HaveOccurred())
3160
3161 By("creating a Deployment in test-namespace-4 with the app=frontend label")
3162 tns4 := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "test-namespace-4"}}
3163 _, err = clientset.CoreV1().Namespaces().Create(ctx, tns4, metav1.CreateOptions{})
3164 Expect(err).NotTo(HaveOccurred())
3165 depFrontend4 := &appsv1.Deployment{
3166 ObjectMeta: metav1.ObjectMeta{
3167 Name: "deployment-frontend",
3168 Namespace: "test-namespace-4",
3169 Labels: map[string]string{"app": "frontend"},
3170 },
3171 Spec: appsv1.DeploymentSpec{
3172 Selector: &metav1.LabelSelector{
3173 MatchLabels: map[string]string{"app": "frontend"},
3174 },
3175 Template: corev1.PodTemplateSpec{
3176 ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"app": "frontend"}},
3177 Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "nginx", Image: "nginx"}}},
3178 },
3179 },
3180 }
3181 depFrontend4, err = clientset.AppsV1().Deployments("test-namespace-4").Create(ctx, depFrontend4, metav1.CreateOptions{})
3182 Expect(err).NotTo(HaveOccurred())
3183
3184 cl, err := client.New(cfg, client.Options{})
3185 Expect(err).NotTo(HaveOccurred())
3186
3187 By("listing all Deployments in test-namespace-3 with label app=frontend")
3188 metaList := &metav1.PartialObjectMetadataList{}
3189 metaList.SetGroupVersionKind(schema.GroupVersionKind{
3190 Group: "apps",
3191 Version: "v1",
3192 Kind: "DeploymentList",
3193 })
3194 labels := map[string]string{"app": "frontend"}
3195 err = cl.List(context.Background(), metaList,
3196 client.InNamespace("test-namespace-3"),
3197 client.MatchingLabels(labels),
3198 )
3199 Expect(err).NotTo(HaveOccurred())
3200
3201 By("only the Deployment in test-namespace-3 with label app=frontend is returned")
3202 Expect(metaList.Items).NotTo(BeEmpty())
3203 Expect(1).To(Equal(len(metaList.Items)))
3204 actual := metaList.Items[0]
3205 Expect(actual.Name).To(Equal("deployment-frontend"))
3206 Expect(actual.Namespace).To(Equal("test-namespace-3"))
3207
3208 deleteDeployment(ctx, depFrontend3, "test-namespace-3")
3209 deleteDeployment(ctx, depBackend3, "test-namespace-3")
3210 deleteDeployment(ctx, depFrontend4, "test-namespace-4")
3211 deleteNamespace(ctx, tns3)
3212 deleteNamespace(ctx, tns4)
3213 })
3214
3215 It("should filter results using limit and continue options", func() {
3216
3217 makeDeployment := func(suffix string) *appsv1.Deployment {
3218 return &appsv1.Deployment{
3219 ObjectMeta: metav1.ObjectMeta{
3220 Name: fmt.Sprintf("deployment-%s", suffix),
3221 },
3222 Spec: appsv1.DeploymentSpec{
3223 Selector: &metav1.LabelSelector{
3224 MatchLabels: map[string]string{"foo": "bar"},
3225 },
3226 Template: corev1.PodTemplateSpec{
3227 ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"foo": "bar"}},
3228 Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "nginx", Image: "nginx"}}},
3229 },
3230 },
3231 }
3232 }
3233
3234 By("creating 4 deployments")
3235 dep1 := makeDeployment("1")
3236 dep1, err := clientset.AppsV1().Deployments(ns).Create(ctx, dep1, metav1.CreateOptions{})
3237 Expect(err).NotTo(HaveOccurred())
3238 defer deleteDeployment(ctx, dep1, ns)
3239
3240 dep2 := makeDeployment("2")
3241 dep2, err = clientset.AppsV1().Deployments(ns).Create(ctx, dep2, metav1.CreateOptions{})
3242 Expect(err).NotTo(HaveOccurred())
3243 defer deleteDeployment(ctx, dep2, ns)
3244
3245 dep3 := makeDeployment("3")
3246 dep3, err = clientset.AppsV1().Deployments(ns).Create(ctx, dep3, metav1.CreateOptions{})
3247 Expect(err).NotTo(HaveOccurred())
3248 defer deleteDeployment(ctx, dep3, ns)
3249
3250 dep4 := makeDeployment("4")
3251 dep4, err = clientset.AppsV1().Deployments(ns).Create(ctx, dep4, metav1.CreateOptions{})
3252 Expect(err).NotTo(HaveOccurred())
3253 defer deleteDeployment(ctx, dep4, ns)
3254
3255 cl, err := client.New(cfg, client.Options{})
3256 Expect(err).NotTo(HaveOccurred())
3257
3258 By("listing 1 deployment when limit=1 is used")
3259 metaList := &metav1.PartialObjectMetadataList{}
3260 metaList.SetGroupVersionKind(schema.GroupVersionKind{
3261 Group: "apps",
3262 Version: "v1",
3263 Kind: "DeploymentList",
3264 })
3265 err = cl.List(context.Background(), metaList,
3266 client.Limit(1),
3267 )
3268 Expect(err).NotTo(HaveOccurred())
3269
3270 Expect(metaList.Items).To(HaveLen(1))
3271 Expect(metaList.Continue).NotTo(BeEmpty())
3272 Expect(metaList.Items[0].Name).To(Equal(dep1.Name))
3273
3274 continueToken := metaList.Continue
3275
3276 By("listing the next deployment when previous continuation token is used and limit=1")
3277 metaList = &metav1.PartialObjectMetadataList{}
3278 metaList.SetGroupVersionKind(schema.GroupVersionKind{
3279 Group: "apps",
3280 Version: "v1",
3281 Kind: "DeploymentList",
3282 })
3283 err = cl.List(context.Background(), metaList,
3284 client.Limit(1),
3285 client.Continue(continueToken),
3286 )
3287 Expect(err).NotTo(HaveOccurred())
3288
3289 Expect(metaList.Items).To(HaveLen(1))
3290 Expect(metaList.Continue).NotTo(BeEmpty())
3291 Expect(metaList.Items[0].Name).To(Equal(dep2.Name))
3292
3293 continueToken = metaList.Continue
3294
3295 By("listing the 2 remaining deployments when previous continuation token is used without a limit")
3296 metaList = &metav1.PartialObjectMetadataList{}
3297 metaList.SetGroupVersionKind(schema.GroupVersionKind{
3298 Group: "apps",
3299 Version: "v1",
3300 Kind: "DeploymentList",
3301 })
3302 err = cl.List(context.Background(), metaList,
3303 client.Continue(continueToken),
3304 )
3305 Expect(err).NotTo(HaveOccurred())
3306
3307 Expect(metaList.Items).To(HaveLen(2))
3308 Expect(metaList.Continue).To(BeEmpty())
3309 Expect(metaList.Items[0].Name).To(Equal(dep3.Name))
3310 Expect(metaList.Items[1].Name).To(Equal(dep4.Name))
3311 })
3312
3313 PIt("should fail if the object doesn't have meta", func() {
3314
3315 })
3316
3317 PIt("should fail if the object cannot be mapped to a GVK", func() {
3318
3319 })
3320
3321 PIt("should fail if the GVK cannot be mapped to a Resource", func() {
3322
3323 })
3324 })
3325 })
3326
3327 Describe("CreateOptions", func() {
3328 It("should allow setting DryRun to 'all'", func() {
3329 co := &client.CreateOptions{}
3330 client.DryRunAll.ApplyToCreate(co)
3331 all := []string{metav1.DryRunAll}
3332 Expect(co.AsCreateOptions().DryRun).To(Equal(all))
3333 })
3334
3335 It("should allow setting the field manager", func() {
3336 po := &client.CreateOptions{}
3337 client.FieldOwner("some-owner").ApplyToCreate(po)
3338 Expect(po.AsCreateOptions().FieldManager).To(Equal("some-owner"))
3339 })
3340
3341 It("should produce empty metav1.CreateOptions if nil", func() {
3342 var co *client.CreateOptions
3343 Expect(co.AsCreateOptions()).To(Equal(&metav1.CreateOptions{}))
3344 co = &client.CreateOptions{}
3345 Expect(co.AsCreateOptions()).To(Equal(&metav1.CreateOptions{}))
3346 })
3347 })
3348
3349 Describe("DeleteOptions", func() {
3350 It("should allow setting GracePeriodSeconds", func() {
3351 do := &client.DeleteOptions{}
3352 client.GracePeriodSeconds(1).ApplyToDelete(do)
3353 gp := int64(1)
3354 Expect(do.AsDeleteOptions().GracePeriodSeconds).To(Equal(&gp))
3355 })
3356
3357 It("should allow setting Precondition", func() {
3358 do := &client.DeleteOptions{}
3359 pc := metav1.NewUIDPreconditions("uid")
3360 client.Preconditions(*pc).ApplyToDelete(do)
3361 Expect(do.AsDeleteOptions().Preconditions).To(Equal(pc))
3362 Expect(do.Preconditions).To(Equal(pc))
3363 })
3364
3365 It("should allow setting PropagationPolicy", func() {
3366 do := &client.DeleteOptions{}
3367 client.PropagationPolicy(metav1.DeletePropagationForeground).ApplyToDelete(do)
3368 dp := metav1.DeletePropagationForeground
3369 Expect(do.AsDeleteOptions().PropagationPolicy).To(Equal(&dp))
3370 })
3371
3372 It("should allow setting DryRun", func() {
3373 do := &client.DeleteOptions{}
3374 client.DryRunAll.ApplyToDelete(do)
3375 all := []string{metav1.DryRunAll}
3376 Expect(do.AsDeleteOptions().DryRun).To(Equal(all))
3377 })
3378
3379 It("should produce empty metav1.DeleteOptions if nil", func() {
3380 var do *client.DeleteOptions
3381 Expect(do.AsDeleteOptions()).To(Equal(&metav1.DeleteOptions{}))
3382 do = &client.DeleteOptions{}
3383 Expect(do.AsDeleteOptions()).To(Equal(&metav1.DeleteOptions{}))
3384 })
3385
3386 It("should merge multiple options together", func() {
3387 gp := int64(1)
3388 pc := metav1.NewUIDPreconditions("uid")
3389 dp := metav1.DeletePropagationForeground
3390 do := &client.DeleteOptions{}
3391 do.ApplyOptions([]client.DeleteOption{
3392 client.GracePeriodSeconds(gp),
3393 client.Preconditions(*pc),
3394 client.PropagationPolicy(dp),
3395 })
3396 Expect(do.GracePeriodSeconds).To(Equal(&gp))
3397 Expect(do.Preconditions).To(Equal(pc))
3398 Expect(do.PropagationPolicy).To(Equal(&dp))
3399 })
3400 })
3401
3402 Describe("DeleteCollectionOptions", func() {
3403 It("should be convertable to list options", func() {
3404 gp := int64(1)
3405 do := &client.DeleteAllOfOptions{}
3406 do.ApplyOptions([]client.DeleteAllOfOption{
3407 client.GracePeriodSeconds(gp),
3408 client.MatchingLabels{"foo": "bar"},
3409 })
3410
3411 listOpts := do.AsListOptions()
3412 Expect(listOpts).NotTo(BeNil())
3413 Expect(listOpts.LabelSelector).To(Equal("foo=bar"))
3414 })
3415
3416 It("should be convertable to delete options", func() {
3417 gp := int64(1)
3418 do := &client.DeleteAllOfOptions{}
3419 do.ApplyOptions([]client.DeleteAllOfOption{
3420 client.GracePeriodSeconds(gp),
3421 client.MatchingLabels{"foo": "bar"},
3422 })
3423
3424 deleteOpts := do.AsDeleteOptions()
3425 Expect(deleteOpts).NotTo(BeNil())
3426 Expect(deleteOpts.GracePeriodSeconds).To(Equal(&gp))
3427 })
3428 })
3429
3430 Describe("GetOptions", func() {
3431 It("should be convertable to metav1.GetOptions", func() {
3432 o := (&client.GetOptions{}).ApplyOptions([]client.GetOption{
3433 &client.GetOptions{Raw: &metav1.GetOptions{ResourceVersion: "RV0"}},
3434 })
3435 mo := o.AsGetOptions()
3436 Expect(mo).NotTo(BeNil())
3437 Expect(mo.ResourceVersion).To(Equal("RV0"))
3438 })
3439
3440 It("should produce empty metav1.GetOptions if nil", func() {
3441 var o *client.GetOptions
3442 Expect(o.AsGetOptions()).To(Equal(&metav1.GetOptions{}))
3443 o = &client.GetOptions{}
3444 Expect(o.AsGetOptions()).To(Equal(&metav1.GetOptions{}))
3445 })
3446 })
3447
3448 Describe("ListOptions", func() {
3449 It("should be convertable to metav1.ListOptions", func() {
3450 lo := (&client.ListOptions{}).ApplyOptions([]client.ListOption{
3451 client.MatchingFields{"field1": "bar"},
3452 client.InNamespace("test-namespace"),
3453 client.MatchingLabels{"foo": "bar"},
3454 client.Limit(1),
3455 client.Continue("foo"),
3456 })
3457 mlo := lo.AsListOptions()
3458 Expect(mlo).NotTo(BeNil())
3459 Expect(mlo.LabelSelector).To(Equal("foo=bar"))
3460 Expect(mlo.FieldSelector).To(Equal("field1=bar"))
3461 Expect(mlo.Limit).To(Equal(int64(1)))
3462 Expect(mlo.Continue).To(Equal("foo"))
3463 })
3464
3465 It("should be populated by MatchingLabels", func() {
3466 lo := &client.ListOptions{}
3467 client.MatchingLabels{"foo": "bar"}.ApplyToList(lo)
3468 Expect(lo).NotTo(BeNil())
3469 Expect(lo.LabelSelector.String()).To(Equal("foo=bar"))
3470 })
3471
3472 It("should be populated by MatchingField", func() {
3473 lo := &client.ListOptions{}
3474 client.MatchingFields{"field1": "bar"}.ApplyToList(lo)
3475 Expect(lo).NotTo(BeNil())
3476 Expect(lo.FieldSelector.String()).To(Equal("field1=bar"))
3477 })
3478
3479 It("should be populated by InNamespace", func() {
3480 lo := &client.ListOptions{}
3481 client.InNamespace("test").ApplyToList(lo)
3482 Expect(lo).NotTo(BeNil())
3483 Expect(lo.Namespace).To(Equal("test"))
3484 })
3485
3486 It("should produce empty metav1.ListOptions if nil", func() {
3487 var do *client.ListOptions
3488 Expect(do.AsListOptions()).To(Equal(&metav1.ListOptions{}))
3489 do = &client.ListOptions{}
3490 Expect(do.AsListOptions()).To(Equal(&metav1.ListOptions{}))
3491 })
3492
3493 It("should be populated by Limit", func() {
3494 lo := &client.ListOptions{}
3495 client.Limit(1).ApplyToList(lo)
3496 Expect(lo).NotTo(BeNil())
3497 Expect(lo.Limit).To(Equal(int64(1)))
3498 })
3499
3500 It("should ignore Limit when converted to metav1.ListOptions and watch is true", func() {
3501 lo := &client.ListOptions{
3502 Raw: &metav1.ListOptions{Watch: true},
3503 }
3504 lo.ApplyOptions([]client.ListOption{
3505 client.Limit(1),
3506 })
3507 mlo := lo.AsListOptions()
3508 Expect(mlo).NotTo(BeNil())
3509 Expect(mlo.Limit).To(BeZero())
3510 })
3511
3512 It("should be populated by Continue", func() {
3513 lo := &client.ListOptions{}
3514 client.Continue("foo").ApplyToList(lo)
3515 Expect(lo).NotTo(BeNil())
3516 Expect(lo.Continue).To(Equal("foo"))
3517 })
3518
3519 It("should ignore Continue token when converted to metav1.ListOptions and watch is true", func() {
3520 lo := &client.ListOptions{
3521 Raw: &metav1.ListOptions{Watch: true},
3522 }
3523 lo.ApplyOptions([]client.ListOption{
3524 client.Continue("foo"),
3525 })
3526 mlo := lo.AsListOptions()
3527 Expect(mlo).NotTo(BeNil())
3528 Expect(mlo.Continue).To(BeEmpty())
3529 })
3530
3531 It("should ignore both Limit and Continue token when converted to metav1.ListOptions and watch is true", func() {
3532 lo := &client.ListOptions{
3533 Raw: &metav1.ListOptions{Watch: true},
3534 }
3535 lo.ApplyOptions([]client.ListOption{
3536 client.Limit(1),
3537 client.Continue("foo"),
3538 })
3539 mlo := lo.AsListOptions()
3540 Expect(mlo).NotTo(BeNil())
3541 Expect(mlo.Limit).To(BeZero())
3542 Expect(mlo.Continue).To(BeEmpty())
3543 })
3544 })
3545
3546 Describe("UpdateOptions", func() {
3547 It("should allow setting DryRun to 'all'", func() {
3548 uo := &client.UpdateOptions{}
3549 client.DryRunAll.ApplyToUpdate(uo)
3550 all := []string{metav1.DryRunAll}
3551 Expect(uo.AsUpdateOptions().DryRun).To(Equal(all))
3552 })
3553
3554 It("should allow setting the field manager", func() {
3555 po := &client.UpdateOptions{}
3556 client.FieldOwner("some-owner").ApplyToUpdate(po)
3557 Expect(po.AsUpdateOptions().FieldManager).To(Equal("some-owner"))
3558 })
3559
3560 It("should produce empty metav1.UpdateOptions if nil", func() {
3561 var co *client.UpdateOptions
3562 Expect(co.AsUpdateOptions()).To(Equal(&metav1.UpdateOptions{}))
3563 co = &client.UpdateOptions{}
3564 Expect(co.AsUpdateOptions()).To(Equal(&metav1.UpdateOptions{}))
3565 })
3566 })
3567
3568 Describe("PatchOptions", func() {
3569 It("should allow setting DryRun to 'all'", func() {
3570 po := &client.PatchOptions{}
3571 client.DryRunAll.ApplyToPatch(po)
3572 all := []string{metav1.DryRunAll}
3573 Expect(po.AsPatchOptions().DryRun).To(Equal(all))
3574 })
3575
3576 It("should allow setting Force to 'true'", func() {
3577 po := &client.PatchOptions{}
3578 client.ForceOwnership.ApplyToPatch(po)
3579 mpo := po.AsPatchOptions()
3580 Expect(mpo.Force).NotTo(BeNil())
3581 Expect(*mpo.Force).To(BeTrue())
3582 })
3583
3584 It("should allow setting the field manager", func() {
3585 po := &client.PatchOptions{}
3586 client.FieldOwner("some-owner").ApplyToPatch(po)
3587 Expect(po.AsPatchOptions().FieldManager).To(Equal("some-owner"))
3588 })
3589
3590 It("should produce empty metav1.PatchOptions if nil", func() {
3591 var po *client.PatchOptions
3592 Expect(po.AsPatchOptions()).To(Equal(&metav1.PatchOptions{}))
3593 po = &client.PatchOptions{}
3594 Expect(po.AsPatchOptions()).To(Equal(&metav1.PatchOptions{}))
3595 })
3596 })
3597 })
3598
3599 var _ = Describe("ClientWithCache", func() {
3600 Describe("Get", func() {
3601 It("should call cache reader when structured object", func() {
3602 cachedReader := &fakeReader{}
3603 cl, err := client.New(cfg, client.Options{
3604 Cache: &client.CacheOptions{
3605 Reader: cachedReader,
3606 },
3607 })
3608 Expect(err).NotTo(HaveOccurred())
3609 var actual appsv1.Deployment
3610 key := client.ObjectKey{Namespace: "ns", Name: "name"}
3611 Expect(cl.Get(context.TODO(), key, &actual)).To(Succeed())
3612 Expect(1).To(Equal(cachedReader.Called))
3613 })
3614
3615 When("getting unstructured objects", func() {
3616 var dep *appsv1.Deployment
3617
3618 BeforeEach(func() {
3619 dep = &appsv1.Deployment{
3620 ObjectMeta: metav1.ObjectMeta{
3621 Name: "deployment1",
3622 Labels: map[string]string{"app": "frontend"},
3623 },
3624 Spec: appsv1.DeploymentSpec{
3625 Selector: &metav1.LabelSelector{
3626 MatchLabels: map[string]string{"app": "frontend"},
3627 },
3628 Template: corev1.PodTemplateSpec{
3629 ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"app": "frontend"}},
3630 Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "x", Image: "x"}}},
3631 },
3632 },
3633 }
3634 var err error
3635 dep, err = clientset.AppsV1().Deployments("default").Create(context.Background(), dep, metav1.CreateOptions{})
3636 Expect(err).NotTo(HaveOccurred())
3637 })
3638 AfterEach(func() {
3639 Expect(clientset.AppsV1().Deployments("default").Delete(
3640 context.Background(),
3641 dep.Name,
3642 metav1.DeleteOptions{},
3643 )).To(Succeed())
3644 })
3645 It("should call client reader when not cached", func() {
3646 cachedReader := &fakeReader{}
3647 cl, err := client.New(cfg, client.Options{
3648 Cache: &client.CacheOptions{
3649 Reader: cachedReader,
3650 },
3651 })
3652 Expect(err).NotTo(HaveOccurred())
3653
3654 actual := &unstructured.Unstructured{}
3655 actual.SetGroupVersionKind(schema.GroupVersionKind{
3656 Group: "apps",
3657 Kind: "Deployment",
3658 Version: "v1",
3659 })
3660 actual.SetName(dep.Name)
3661 key := client.ObjectKey{Namespace: dep.Namespace, Name: dep.Name}
3662 Expect(cl.Get(context.TODO(), key, actual)).To(Succeed())
3663 Expect(0).To(Equal(cachedReader.Called))
3664 })
3665 It("should call cache reader when cached", func() {
3666 cachedReader := &fakeReader{}
3667 cl, err := client.New(cfg, client.Options{
3668 Cache: &client.CacheOptions{
3669 Reader: cachedReader,
3670 Unstructured: true,
3671 },
3672 })
3673 Expect(err).NotTo(HaveOccurred())
3674
3675 actual := &unstructured.Unstructured{}
3676 actual.SetGroupVersionKind(schema.GroupVersionKind{
3677 Group: "apps",
3678 Kind: "Deployment",
3679 Version: "v1",
3680 })
3681 actual.SetName(dep.Name)
3682 key := client.ObjectKey{Namespace: dep.Namespace, Name: dep.Name}
3683 Expect(cl.Get(context.TODO(), key, actual)).To(Succeed())
3684 Expect(1).To(Equal(cachedReader.Called))
3685 })
3686 })
3687 })
3688 Describe("List", func() {
3689 It("should call cache reader when structured object", func() {
3690 cachedReader := &fakeReader{}
3691 cl, err := client.New(cfg, client.Options{
3692 Cache: &client.CacheOptions{
3693 Reader: cachedReader,
3694 },
3695 })
3696 Expect(err).NotTo(HaveOccurred())
3697 var actual appsv1.DeploymentList
3698 Expect(cl.List(context.Background(), &actual)).To(Succeed())
3699 Expect(1).To(Equal(cachedReader.Called))
3700 })
3701
3702 When("listing unstructured objects", func() {
3703 It("should call client reader when not cached", func() {
3704 cachedReader := &fakeReader{}
3705 cl, err := client.New(cfg, client.Options{
3706 Cache: &client.CacheOptions{
3707 Reader: cachedReader,
3708 },
3709 })
3710 Expect(err).NotTo(HaveOccurred())
3711
3712 actual := &unstructured.UnstructuredList{}
3713 actual.SetGroupVersionKind(schema.GroupVersionKind{
3714 Group: "apps",
3715 Kind: "DeploymentList",
3716 Version: "v1",
3717 })
3718 Expect(cl.List(context.Background(), actual)).To(Succeed())
3719 Expect(0).To(Equal(cachedReader.Called))
3720 })
3721 It("should call cache reader when cached", func() {
3722 cachedReader := &fakeReader{}
3723 cl, err := client.New(cfg, client.Options{
3724 Cache: &client.CacheOptions{
3725 Reader: cachedReader,
3726 Unstructured: true,
3727 },
3728 })
3729 Expect(err).NotTo(HaveOccurred())
3730
3731 actual := &unstructured.UnstructuredList{}
3732 actual.SetGroupVersionKind(schema.GroupVersionKind{
3733 Group: "apps",
3734 Kind: "DeploymentList",
3735 Version: "v1",
3736 })
3737 Expect(cl.List(context.Background(), actual)).To(Succeed())
3738 Expect(1).To(Equal(cachedReader.Called))
3739 })
3740 })
3741 })
3742 })
3743
3744 var _ = Describe("Patch", func() {
3745 Describe("MergeFrom", func() {
3746 var cm *corev1.ConfigMap
3747
3748 BeforeEach(func() {
3749 cm = &corev1.ConfigMap{
3750 ObjectMeta: metav1.ObjectMeta{
3751 Namespace: metav1.NamespaceDefault,
3752 Name: "cm",
3753 ResourceVersion: "10",
3754 },
3755 }
3756 })
3757
3758 It("creates a merge patch with the modifications applied during the mutation", func() {
3759 const (
3760 annotationKey = "test"
3761 annotationValue = "foo"
3762 )
3763
3764 By("creating a merge patch")
3765 patch := client.MergeFrom(cm.DeepCopy())
3766
3767 By("returning a patch with type MergePatch")
3768 Expect(patch.Type()).To(Equal(types.MergePatchType))
3769
3770 By("retrieving modifying the config map")
3771 metav1.SetMetaDataAnnotation(&cm.ObjectMeta, annotationKey, annotationValue)
3772
3773 By("computing the patch data")
3774 data, err := patch.Data(cm)
3775
3776 By("returning no error")
3777 Expect(err).NotTo(HaveOccurred())
3778
3779 By("returning a patch with data only containing the annotation change")
3780 Expect(data).To(Equal([]byte(fmt.Sprintf(`{"metadata":{"annotations":{"%s":"%s"}}}`, annotationKey, annotationValue))))
3781 })
3782
3783 It("creates a merge patch with the modifications applied during the mutation, using optimistic locking", func() {
3784 const (
3785 annotationKey = "test"
3786 annotationValue = "foo"
3787 )
3788
3789 By("creating a merge patch")
3790 patch := client.MergeFromWithOptions(cm.DeepCopy(), client.MergeFromWithOptimisticLock{})
3791
3792 By("returning a patch with type MergePatch")
3793 Expect(patch.Type()).To(Equal(types.MergePatchType))
3794
3795 By("retrieving modifying the config map")
3796 metav1.SetMetaDataAnnotation(&cm.ObjectMeta, annotationKey, annotationValue)
3797
3798 By("computing the patch data")
3799 data, err := patch.Data(cm)
3800
3801 By("returning no error")
3802 Expect(err).NotTo(HaveOccurred())
3803
3804 By("returning a patch with data containing the annotation change and the resourceVersion change")
3805 Expect(data).To(Equal([]byte(fmt.Sprintf(`{"metadata":{"annotations":{"%s":"%s"},"resourceVersion":"%s"}}`, annotationKey, annotationValue, cm.ResourceVersion))))
3806 })
3807 })
3808
3809 Describe("StrategicMergeFrom", func() {
3810 var dep *appsv1.Deployment
3811
3812 BeforeEach(func() {
3813 dep = &appsv1.Deployment{
3814 ObjectMeta: metav1.ObjectMeta{
3815 Namespace: metav1.NamespaceDefault,
3816 Name: "dep",
3817 ResourceVersion: "10",
3818 },
3819 Spec: appsv1.DeploymentSpec{
3820 Template: corev1.PodTemplateSpec{
3821 Spec: corev1.PodSpec{Containers: []corev1.Container{{
3822 Name: "main",
3823 Image: "foo:v1",
3824 }, {
3825 Name: "sidecar",
3826 Image: "bar:v1",
3827 }}},
3828 },
3829 },
3830 }
3831 })
3832
3833 It("creates a strategic merge patch with the modifications applied during the mutation", func() {
3834 By("creating a strategic merge patch")
3835 patch := client.StrategicMergeFrom(dep.DeepCopy())
3836
3837 By("returning a patch with type StrategicMergePatchType")
3838 Expect(patch.Type()).To(Equal(types.StrategicMergePatchType))
3839
3840 By("updating the main container's image")
3841 for i, c := range dep.Spec.Template.Spec.Containers {
3842 if c.Name == "main" {
3843 c.Image = "foo:v2"
3844 }
3845 dep.Spec.Template.Spec.Containers[i] = c
3846 }
3847
3848 By("computing the patch data")
3849 data, err := patch.Data(dep)
3850
3851 By("returning no error")
3852 Expect(err).NotTo(HaveOccurred())
3853
3854 By("returning a patch with data only containing the image change")
3855 Expect(data).To(Equal([]byte(`{"spec":{"template":{"spec":{"$setElementOrder/containers":[{"name":"main"},` +
3856 `{"name":"sidecar"}],"containers":[{"image":"foo:v2","name":"main"}]}}}}`)))
3857 })
3858
3859 It("creates a strategic merge patch with the modifications applied during the mutation, using optimistic locking", func() {
3860 By("creating a strategic merge patch")
3861 patch := client.StrategicMergeFrom(dep.DeepCopy(), client.MergeFromWithOptimisticLock{})
3862
3863 By("returning a patch with type StrategicMergePatchType")
3864 Expect(patch.Type()).To(Equal(types.StrategicMergePatchType))
3865
3866 By("updating the main container's image")
3867 for i, c := range dep.Spec.Template.Spec.Containers {
3868 if c.Name == "main" {
3869 c.Image = "foo:v2"
3870 }
3871 dep.Spec.Template.Spec.Containers[i] = c
3872 }
3873
3874 By("computing the patch data")
3875 data, err := patch.Data(dep)
3876
3877 By("returning no error")
3878 Expect(err).NotTo(HaveOccurred())
3879
3880 By("returning a patch with data containing the image change and the resourceVersion change")
3881 Expect(data).To(Equal([]byte(fmt.Sprintf(`{"metadata":{"resourceVersion":"%s"},`+
3882 `"spec":{"template":{"spec":{"$setElementOrder/containers":[{"name":"main"},{"name":"sidecar"}],"containers":[{"image":"foo:v2","name":"main"}]}}}}`,
3883 dep.ResourceVersion))))
3884 })
3885 })
3886 })
3887
3888 var _ = Describe("IgnoreNotFound", func() {
3889 It("should return nil on a 'NotFound' error", func() {
3890 By("creating a NotFound error")
3891 err := apierrors.NewNotFound(schema.GroupResource{}, "")
3892
3893 By("returning no error")
3894 Expect(client.IgnoreNotFound(err)).To(Succeed())
3895 })
3896
3897 It("should return the error on a status other than not found", func() {
3898 By("creating a BadRequest error")
3899 err := apierrors.NewBadRequest("")
3900
3901 By("returning an error")
3902 Expect(client.IgnoreNotFound(err)).To(HaveOccurred())
3903 })
3904
3905 It("should return the error on a non-status error", func() {
3906 By("creating an fmt error")
3907 err := fmt.Errorf("arbitrary error")
3908
3909 By("returning an error")
3910 Expect(client.IgnoreNotFound(err)).To(HaveOccurred())
3911 })
3912 })
3913
3914 var _ = Describe("IgnoreAlreadyExists", func() {
3915 It("should return nil on a 'AlreadyExists' error", func() {
3916 By("creating a AlreadyExists error")
3917 err := apierrors.NewAlreadyExists(schema.GroupResource{}, "")
3918
3919 By("returning no error")
3920 Expect(client.IgnoreAlreadyExists(err)).To(Succeed())
3921 })
3922
3923 It("should return the error on a status other than already exists", func() {
3924 By("creating a BadRequest error")
3925 err := apierrors.NewBadRequest("")
3926
3927 By("returning an error")
3928 Expect(client.IgnoreAlreadyExists(err)).To(HaveOccurred())
3929 })
3930
3931 It("should return the error on a non-status error", func() {
3932 By("creating an fmt error")
3933 err := fmt.Errorf("arbitrary error")
3934
3935 By("returning an error")
3936 Expect(client.IgnoreAlreadyExists(err)).To(HaveOccurred())
3937 })
3938 })
3939
3940 type fakeReader struct {
3941 Called int
3942 }
3943
3944 func (f *fakeReader) Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error {
3945 f.Called++
3946 return nil
3947 }
3948
3949 func (f *fakeReader) List(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error {
3950 f.Called++
3951 return nil
3952 }
3953
3954 type fakeUncachedReader struct {
3955 Called int
3956 }
3957
3958 func (f *fakeUncachedReader) Get(_ context.Context, _ client.ObjectKey, _ client.Object, opts ...client.GetOption) error {
3959 f.Called++
3960 return &cache.ErrResourceNotCached{}
3961 }
3962
3963 func (f *fakeUncachedReader) List(_ context.Context, _ client.ObjectList, _ ...client.ListOption) error {
3964 f.Called++
3965 return &cache.ErrResourceNotCached{}
3966 }
3967
3968 func toUnstructured(o client.Object) (*unstructured.Unstructured, error) {
3969 serialized, err := json.Marshal(o)
3970 if err != nil {
3971 return nil, err
3972 }
3973 u := &unstructured.Unstructured{}
3974 return u, json.Unmarshal(serialized, u)
3975 }
3976
View as plain text