Skip to content

Commit e10bf72

Browse files
authored
Merge pull request #1403 from dmvolod/issue-1392
🐛 controllerutil.CreateOrPatch doesn't update not empty Status fields if Spec fields are specified
2 parents 2d19f01 + f0abe78 commit e10bf72

File tree

2 files changed

+67
-0
lines changed

2 files changed

+67
-0
lines changed

pkg/controller/controllerutil/controllerutil.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,20 @@ func CreateOrPatch(ctx context.Context, c client.Client, obj client.Object, f Mu
309309
if (hasBeforeStatus || hasAfterStatus) && !reflect.DeepEqual(beforeStatus, afterStatus) {
310310
// Only issue a Status Patch if the resource has a status and the beforeStatus
311311
// and afterStatus copies differ
312+
if result == OperationResultUpdated {
313+
// If Status was replaced by Patch before, set it to afterStatus
314+
objectAfterPatch, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj)
315+
if err != nil {
316+
return result, err
317+
}
318+
if err = unstructured.SetNestedField(objectAfterPatch, afterStatus, "status"); err != nil {
319+
return result, err
320+
}
321+
// If Status was replaced by Patch before, restore patched structure to the obj
322+
if err = runtime.DefaultUnstructuredConverter.FromUnstructured(objectAfterPatch, obj); err != nil {
323+
return result, err
324+
}
325+
}
312326
if err := c.Status().Patch(ctx, obj, statusPatch); err != nil {
313327
return result, err
314328
}

pkg/controller/controllerutil/controllerutil_test.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
"k8s.io/apimachinery/pkg/runtime"
3131
"k8s.io/apimachinery/pkg/types"
3232
"k8s.io/client-go/kubernetes/scheme"
33+
"k8s.io/utils/pointer"
3334
"sigs.k8s.io/controller-runtime/pkg/client"
3435
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
3536
)
@@ -460,6 +461,18 @@ var _ = Describe("Controllerutil", func() {
460461
ExpectWithOffset(1, fetched.Status).To(BeEquivalentTo(deploy.Status))
461462
}
462463

464+
assertLocalDeployStatusWasUpdated := func(fetched *appsv1.Deployment) {
465+
By("local deploy object was updated during patch & has same spec, status, resource version as fetched")
466+
if fetched == nil {
467+
fetched = &appsv1.Deployment{}
468+
ExpectWithOffset(1, c.Get(context.TODO(), deplKey, fetched)).To(Succeed())
469+
}
470+
ExpectWithOffset(1, fetched.ResourceVersion).To(Equal(deploy.ResourceVersion))
471+
ExpectWithOffset(1, *fetched.Spec.Replicas).To(BeEquivalentTo(int32(5)))
472+
ExpectWithOffset(1, fetched.Status).To(BeEquivalentTo(deploy.Status))
473+
ExpectWithOffset(1, len(fetched.Status.Conditions)).To(BeEquivalentTo(1))
474+
}
475+
463476
It("creates a new object if one doesn't exists", func() {
464477
op, err := controllerutil.CreateOrPatch(context.TODO(), c, deploy, specr)
465478

@@ -559,6 +572,46 @@ var _ = Describe("Controllerutil", func() {
559572
assertLocalDeployWasUpdated(nil)
560573
})
561574

575+
It("patches resource and not empty status", func() {
576+
op, err := controllerutil.CreateOrPatch(context.TODO(), c, deploy, specr)
577+
578+
Expect(op).To(BeEquivalentTo(controllerutil.OperationResultCreated))
579+
Expect(err).NotTo(HaveOccurred())
580+
581+
replicas := int32(3)
582+
deployStatus := appsv1.DeploymentStatus{
583+
ReadyReplicas: 1,
584+
Replicas: replicas,
585+
}
586+
op, err = controllerutil.CreateOrPatch(context.TODO(), c, deploy, func() error {
587+
Expect(deploymentScaler(deploy, replicas)()).To(Succeed())
588+
return deploymentStatusr(deploy, deployStatus)()
589+
})
590+
By("returning no error")
591+
Expect(err).NotTo(HaveOccurred())
592+
593+
By("returning OperationResultUpdatedStatus")
594+
Expect(op).To(BeEquivalentTo(controllerutil.OperationResultUpdatedStatus))
595+
596+
assertLocalDeployWasUpdated(nil)
597+
598+
op, err = controllerutil.CreateOrPatch(context.TODO(), c, deploy, func() error {
599+
deploy.Spec.Replicas = pointer.Int32Ptr(5)
600+
deploy.Status.Conditions = []appsv1.DeploymentCondition{{
601+
Type: appsv1.DeploymentProgressing,
602+
Status: corev1.ConditionTrue,
603+
}}
604+
return nil
605+
})
606+
By("returning no error")
607+
Expect(err).NotTo(HaveOccurred())
608+
609+
By("returning OperationResultUpdatedStatus")
610+
Expect(op).To(BeEquivalentTo(controllerutil.OperationResultUpdatedStatus))
611+
612+
assertLocalDeployStatusWasUpdated(nil)
613+
})
614+
562615
It("errors when MutateFn changes object name on creation", func() {
563616
op, err := controllerutil.CreateOrPatch(context.TODO(), c, deploy, func() error {
564617
Expect(specr()).To(Succeed())

0 commit comments

Comments
 (0)