Skip to content

Commit 7471369

Browse files
author
Shawn Hurley
committed
Status Writer working with unstructured as well as typed objects
1 parent 00c5c79 commit 7471369

File tree

4 files changed

+239
-109
lines changed

4 files changed

+239
-109
lines changed

pkg/client/client.go

+5-16
Original file line numberDiff line numberDiff line change
@@ -153,21 +153,10 @@ type statusWriter struct {
153153
var _ StatusWriter = &statusWriter{}
154154

155155
// Update implements client.StatusWriter
156-
func (sw *statusWriter) Update(_ context.Context, obj runtime.Object) error {
157-
o, err := sw.client.typedClient.cache.getObjMeta(obj)
158-
if err != nil {
159-
return err
156+
func (sw *statusWriter) Update(ctx context.Context, obj runtime.Object) error {
157+
_, ok := obj.(*unstructured.Unstructured)
158+
if ok {
159+
return sw.client.unstructuredClient.UpdateStatus(ctx, obj)
160160
}
161-
// TODO(droot): examine the returned error and check if it error needs to be
162-
// wrapped to improve the UX ?
163-
// It will be nice to receive an error saying the object doesn't implement
164-
// status subresource and check CRD definition
165-
return o.Put().
166-
NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()).
167-
Resource(o.resource()).
168-
Name(o.GetName()).
169-
SubResource("status").
170-
Body(obj).
171-
Do().
172-
Into(obj)
161+
return sw.client.typedClient.UpdateStatus(ctx, obj)
173162
}

pkg/client/client_test.go

+187-83
Original file line numberDiff line numberDiff line change
@@ -541,114 +541,218 @@ var _ = Describe("Client", func() {
541541
})
542542

543543
Describe("StatusClient", func() {
544-
It("should update status of an existing object", func(done Done) {
545-
cl, err := client.New(cfg, client.Options{})
546-
Expect(err).NotTo(HaveOccurred())
547-
Expect(cl).NotTo(BeNil())
544+
Context("with structured objects", func() {
545+
It("should update status of an existing object", func(done Done) {
546+
cl, err := client.New(cfg, client.Options{})
547+
Expect(err).NotTo(HaveOccurred())
548+
Expect(cl).NotTo(BeNil())
548549

549-
By("initially creating a Deployment")
550-
dep, err := clientset.AppsV1().Deployments(ns).Create(dep)
551-
Expect(err).NotTo(HaveOccurred())
550+
By("initially creating a Deployment")
551+
dep, err := clientset.AppsV1().Deployments(ns).Create(dep)
552+
Expect(err).NotTo(HaveOccurred())
552553

553-
By("updating the status of Deployment")
554-
dep.Status.Replicas = 1
555-
err = cl.Status().Update(context.TODO(), dep)
556-
Expect(err).NotTo(HaveOccurred())
554+
By("updating the status of Deployment")
555+
dep.Status.Replicas = 1
556+
err = cl.Status().Update(context.TODO(), dep)
557+
Expect(err).NotTo(HaveOccurred())
557558

558-
By("validating updated Deployment has new status")
559-
actual, err := clientset.AppsV1().Deployments(ns).Get(dep.Name, metav1.GetOptions{})
560-
Expect(err).NotTo(HaveOccurred())
561-
Expect(actual).NotTo(BeNil())
562-
Expect(actual.Status.Replicas).To(BeEquivalentTo(1))
559+
By("validating updated Deployment has new status")
560+
actual, err := clientset.AppsV1().Deployments(ns).Get(dep.Name, metav1.GetOptions{})
561+
Expect(err).NotTo(HaveOccurred())
562+
Expect(actual).NotTo(BeNil())
563+
Expect(actual.Status.Replicas).To(BeEquivalentTo(1))
563564

564-
close(done)
565-
})
565+
close(done)
566+
})
566567

567-
It("should not update spec of an existing object", func(done Done) {
568-
cl, err := client.New(cfg, client.Options{})
569-
Expect(err).NotTo(HaveOccurred())
570-
Expect(cl).NotTo(BeNil())
568+
It("should not update spec of an existing object", func(done Done) {
569+
cl, err := client.New(cfg, client.Options{})
570+
Expect(err).NotTo(HaveOccurred())
571+
Expect(cl).NotTo(BeNil())
571572

572-
By("initially creating a Deployment")
573-
dep, err := clientset.AppsV1().Deployments(ns).Create(dep)
574-
Expect(err).NotTo(HaveOccurred())
573+
By("initially creating a Deployment")
574+
dep, err := clientset.AppsV1().Deployments(ns).Create(dep)
575+
Expect(err).NotTo(HaveOccurred())
575576

576-
By("updating the spec and status of Deployment")
577-
var rc int32 = 1
578-
dep.Status.Replicas = 1
579-
dep.Spec.Replicas = &rc
580-
err = cl.Status().Update(context.TODO(), dep)
581-
Expect(err).NotTo(HaveOccurred())
577+
By("updating the spec and status of Deployment")
578+
var rc int32 = 1
579+
dep.Status.Replicas = 1
580+
dep.Spec.Replicas = &rc
581+
err = cl.Status().Update(context.TODO(), dep)
582+
Expect(err).NotTo(HaveOccurred())
582583

583-
By("validating updated Deployment has new status and unchanged spec")
584-
actual, err := clientset.AppsV1().Deployments(ns).Get(dep.Name, metav1.GetOptions{})
585-
Expect(err).NotTo(HaveOccurred())
586-
Expect(actual).NotTo(BeNil())
587-
Expect(actual.Status.Replicas).To(BeEquivalentTo(1))
588-
Expect(*actual.Spec.Replicas).To(BeEquivalentTo(replicaCount))
584+
By("validating updated Deployment has new status and unchanged spec")
585+
actual, err := clientset.AppsV1().Deployments(ns).Get(dep.Name, metav1.GetOptions{})
586+
Expect(err).NotTo(HaveOccurred())
587+
Expect(actual).NotTo(BeNil())
588+
Expect(actual.Status.Replicas).To(BeEquivalentTo(1))
589+
Expect(*actual.Spec.Replicas).To(BeEquivalentTo(replicaCount))
589590

590-
close(done)
591-
})
591+
close(done)
592+
})
592593

593-
It("should update an existing object non-namespace object", func(done Done) {
594-
cl, err := client.New(cfg, client.Options{})
595-
Expect(err).NotTo(HaveOccurred())
596-
Expect(cl).NotTo(BeNil())
594+
It("should update an existing object non-namespace object", func(done Done) {
595+
cl, err := client.New(cfg, client.Options{})
596+
Expect(err).NotTo(HaveOccurred())
597+
Expect(cl).NotTo(BeNil())
597598

598-
node, err := clientset.CoreV1().Nodes().Create(node)
599-
Expect(err).NotTo(HaveOccurred())
599+
node, err := clientset.CoreV1().Nodes().Create(node)
600+
Expect(err).NotTo(HaveOccurred())
600601

601-
By("updating status of the object")
602-
node.Status.Phase = corev1.NodeRunning
603-
err = cl.Status().Update(context.TODO(), node)
604-
Expect(err).NotTo(HaveOccurred())
602+
By("updating status of the object")
603+
node.Status.Phase = corev1.NodeRunning
604+
err = cl.Status().Update(context.TODO(), node)
605+
Expect(err).NotTo(HaveOccurred())
605606

606-
By("validate updated Node had new annotation")
607-
actual, err := clientset.CoreV1().Nodes().Get(node.Name, metav1.GetOptions{})
608-
Expect(err).NotTo(HaveOccurred())
609-
Expect(actual).NotTo(BeNil())
610-
Expect(actual.Status.Phase).To(Equal(corev1.NodeRunning))
607+
By("validate updated Node had new annotation")
608+
actual, err := clientset.CoreV1().Nodes().Get(node.Name, metav1.GetOptions{})
609+
Expect(err).NotTo(HaveOccurred())
610+
Expect(actual).NotTo(BeNil())
611+
Expect(actual.Status.Phase).To(Equal(corev1.NodeRunning))
611612

612-
close(done)
613-
})
613+
close(done)
614+
})
614615

615-
It("should fail if the object does not exists", func(done Done) {
616-
cl, err := client.New(cfg, client.Options{})
617-
Expect(err).NotTo(HaveOccurred())
618-
Expect(cl).NotTo(BeNil())
616+
It("should fail if the object does not exists", func(done Done) {
617+
cl, err := client.New(cfg, client.Options{})
618+
Expect(err).NotTo(HaveOccurred())
619+
Expect(cl).NotTo(BeNil())
619620

620-
By("updating status of a non-existent object")
621-
err = cl.Status().Update(context.TODO(), dep)
622-
Expect(err).To(HaveOccurred())
621+
By("updating status of a non-existent object")
622+
err = cl.Status().Update(context.TODO(), dep)
623+
Expect(err).To(HaveOccurred())
623624

624-
close(done)
625-
})
625+
close(done)
626+
})
626627

627-
It("should fail if the object cannot be mapped to a GVK", func(done Done) {
628-
By("creating client with empty Scheme")
629-
emptyScheme := runtime.NewScheme()
630-
cl, err := client.New(cfg, client.Options{Scheme: emptyScheme})
631-
Expect(err).NotTo(HaveOccurred())
632-
Expect(cl).NotTo(BeNil())
628+
It("should fail if the object cannot be mapped to a GVK", func(done Done) {
629+
By("creating client with empty Scheme")
630+
emptyScheme := runtime.NewScheme()
631+
cl, err := client.New(cfg, client.Options{Scheme: emptyScheme})
632+
Expect(err).NotTo(HaveOccurred())
633+
Expect(cl).NotTo(BeNil())
633634

634-
By("initially creating a Deployment")
635-
dep, err := clientset.AppsV1().Deployments(ns).Create(dep)
636-
Expect(err).NotTo(HaveOccurred())
635+
By("initially creating a Deployment")
636+
dep, err := clientset.AppsV1().Deployments(ns).Create(dep)
637+
Expect(err).NotTo(HaveOccurred())
637638

638-
By("updating status of the Deployment")
639-
dep.Status.Replicas = 1
640-
err = cl.Status().Update(context.TODO(), dep)
641-
Expect(err).To(HaveOccurred())
642-
Expect(err.Error()).To(ContainSubstring("no kind is registered for the type"))
639+
By("updating status of the Deployment")
640+
dep.Status.Replicas = 1
641+
err = cl.Status().Update(context.TODO(), dep)
642+
Expect(err).To(HaveOccurred())
643+
Expect(err.Error()).To(ContainSubstring("no kind is registered for the type"))
643644

644-
close(done)
645-
})
645+
close(done)
646+
})
646647

647-
PIt("should fail if the GVK cannot be mapped to a Resource", func() {
648+
PIt("should fail if the GVK cannot be mapped to a Resource", func() {
649+
650+
})
648651

652+
PIt("should fail if an API does not implement Status subresource", func() {
653+
654+
})
649655
})
650656

651-
PIt("should fail if an API does not implement Status subresource", func() {
657+
Context("with unstructured objects", func() {
658+
It("should update status of an existing object", func(done Done) {
659+
cl, err := client.New(cfg, client.Options{})
660+
Expect(err).NotTo(HaveOccurred())
661+
Expect(cl).NotTo(BeNil())
662+
663+
By("initially creating a Deployment")
664+
dep, err := clientset.AppsV1().Deployments(ns).Create(dep)
665+
Expect(err).NotTo(HaveOccurred())
666+
667+
By("updating the status of Deployment")
668+
u := &unstructured.Unstructured{}
669+
dep.Status.Replicas = 1
670+
scheme.Convert(dep, u, nil)
671+
err = cl.Status().Update(context.TODO(), u)
672+
Expect(err).NotTo(HaveOccurred())
673+
674+
By("validating updated Deployment has new status")
675+
actual, err := clientset.AppsV1().Deployments(ns).Get(dep.Name, metav1.GetOptions{})
676+
Expect(err).NotTo(HaveOccurred())
677+
Expect(actual).NotTo(BeNil())
678+
Expect(actual.Status.Replicas).To(BeEquivalentTo(1))
679+
680+
close(done)
681+
})
682+
683+
It("should not update spec of an existing object", func(done Done) {
684+
cl, err := client.New(cfg, client.Options{})
685+
Expect(err).NotTo(HaveOccurred())
686+
Expect(cl).NotTo(BeNil())
687+
688+
By("initially creating a Deployment")
689+
dep, err := clientset.AppsV1().Deployments(ns).Create(dep)
690+
Expect(err).NotTo(HaveOccurred())
691+
692+
By("updating the spec and status of Deployment")
693+
u := &unstructured.Unstructured{}
694+
var rc int32 = 1
695+
dep.Status.Replicas = 1
696+
dep.Spec.Replicas = &rc
697+
scheme.Convert(dep, u, nil)
698+
err = cl.Status().Update(context.TODO(), u)
699+
Expect(err).NotTo(HaveOccurred())
700+
701+
By("validating updated Deployment has new status and unchanged spec")
702+
actual, err := clientset.AppsV1().Deployments(ns).Get(dep.Name, metav1.GetOptions{})
703+
Expect(err).NotTo(HaveOccurred())
704+
Expect(actual).NotTo(BeNil())
705+
Expect(actual.Status.Replicas).To(BeEquivalentTo(1))
706+
Expect(*actual.Spec.Replicas).To(BeEquivalentTo(replicaCount))
707+
708+
close(done)
709+
})
710+
711+
It("should update an existing object non-namespace object", func(done Done) {
712+
cl, err := client.New(cfg, client.Options{})
713+
Expect(err).NotTo(HaveOccurred())
714+
Expect(cl).NotTo(BeNil())
715+
716+
node, err := clientset.CoreV1().Nodes().Create(node)
717+
Expect(err).NotTo(HaveOccurred())
718+
719+
By("updating status of the object")
720+
u := &unstructured.Unstructured{}
721+
node.Status.Phase = corev1.NodeRunning
722+
scheme.Convert(node, u, nil)
723+
err = cl.Status().Update(context.TODO(), u)
724+
Expect(err).NotTo(HaveOccurred())
725+
726+
By("validate updated Node had new annotation")
727+
actual, err := clientset.CoreV1().Nodes().Get(node.Name, metav1.GetOptions{})
728+
Expect(err).NotTo(HaveOccurred())
729+
Expect(actual).NotTo(BeNil())
730+
Expect(actual.Status.Phase).To(Equal(corev1.NodeRunning))
731+
732+
close(done)
733+
})
734+
735+
It("should fail if the object does not exists", func(done Done) {
736+
cl, err := client.New(cfg, client.Options{})
737+
Expect(err).NotTo(HaveOccurred())
738+
Expect(cl).NotTo(BeNil())
739+
740+
By("updating status of a non-existent object")
741+
u := &unstructured.Unstructured{}
742+
scheme.Convert(dep, u, nil)
743+
err = cl.Status().Update(context.TODO(), u)
744+
Expect(err).To(HaveOccurred())
745+
746+
close(done)
747+
})
748+
749+
PIt("should fail if the GVK cannot be mapped to a Resource", func() {
750+
751+
})
752+
753+
PIt("should fail if an API does not implement Status subresource", func() {
754+
755+
})
652756

653757
})
654758
})

pkg/client/typed_client.go

+25-5
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ type typedClient struct {
3030
}
3131

3232
// Create implements client.Client
33-
func (c *typedClient) Create(ctx context.Context, obj runtime.Object) error {
33+
func (c *typedClient) Create(_ context.Context, obj runtime.Object) error {
3434
o, err := c.cache.getObjMeta(obj)
3535
if err != nil {
3636
return err
@@ -44,7 +44,7 @@ func (c *typedClient) Create(ctx context.Context, obj runtime.Object) error {
4444
}
4545

4646
// Update implements client.Client
47-
func (c *typedClient) Update(ctx context.Context, obj runtime.Object) error {
47+
func (c *typedClient) Update(_ context.Context, obj runtime.Object) error {
4848
o, err := c.cache.getObjMeta(obj)
4949
if err != nil {
5050
return err
@@ -59,7 +59,7 @@ func (c *typedClient) Update(ctx context.Context, obj runtime.Object) error {
5959
}
6060

6161
// Delete implements client.Client
62-
func (c *typedClient) Delete(ctx context.Context, obj runtime.Object, opts ...DeleteOptionFunc) error {
62+
func (c *typedClient) Delete(_ context.Context, obj runtime.Object, opts ...DeleteOptionFunc) error {
6363
o, err := c.cache.getObjMeta(obj)
6464
if err != nil {
6565
return err
@@ -76,7 +76,7 @@ func (c *typedClient) Delete(ctx context.Context, obj runtime.Object, opts ...De
7676
}
7777

7878
// Get implements client.Client
79-
func (c *typedClient) Get(ctx context.Context, key ObjectKey, obj runtime.Object) error {
79+
func (c *typedClient) Get(_ context.Context, key ObjectKey, obj runtime.Object) error {
8080
r, err := c.cache.getResource(obj)
8181
if err != nil {
8282
return err
@@ -88,7 +88,7 @@ func (c *typedClient) Get(ctx context.Context, key ObjectKey, obj runtime.Object
8888
}
8989

9090
// List implements client.Client
91-
func (c *typedClient) List(ctx context.Context, opts *ListOptions, obj runtime.Object) error {
91+
func (c *typedClient) List(_ context.Context, opts *ListOptions, obj runtime.Object) error {
9292
r, err := c.cache.getResource(obj)
9393
if err != nil {
9494
return err
@@ -105,3 +105,23 @@ func (c *typedClient) List(ctx context.Context, opts *ListOptions, obj runtime.O
105105
Do().
106106
Into(obj)
107107
}
108+
109+
// UpdateStatus used by StatusWriter to write status.
110+
func (c *typedClient) UpdateStatus(_ context.Context, obj runtime.Object) error {
111+
o, err := c.cache.getObjMeta(obj)
112+
if err != nil {
113+
return err
114+
}
115+
// TODO(droot): examine the returned error and check if it error needs to be
116+
// wrapped to improve the UX ?
117+
// It will be nice to receive an error saying the object doesn't implement
118+
// status subresource and check CRD definition
119+
return o.Put().
120+
NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()).
121+
Resource(o.resource()).
122+
Name(o.GetName()).
123+
SubResource("status").
124+
Body(obj).
125+
Do().
126+
Into(obj)
127+
}

0 commit comments

Comments
 (0)