Skip to content

Commit 6443f37

Browse files
authored
Merge pull request #313 from adracus/fix.create-or-update
🐛 cleanup and fix CreateOrUpdate
2 parents abf0c07 + 4497f36 commit 6443f37

File tree

2 files changed

+48
-41
lines changed

2 files changed

+48
-41
lines changed

pkg/controller/controllerutil/controllerutil.go

+23-38
Original file line numberDiff line numberDiff line change
@@ -121,57 +121,42 @@ const ( // They should complete the sentence "Deployment default/foo has been ..
121121
//
122122
// It returns the executed operation and an error.
123123
func CreateOrUpdate(ctx context.Context, c client.Client, obj runtime.Object, f MutateFn) (OperationResult, error) {
124-
// op is the operation we are going to attempt
125-
op := OperationResultNone
126-
127-
// get the existing object meta
128-
metaObj, ok := obj.(v1.Object)
129-
if !ok {
130-
return OperationResultNone, fmt.Errorf("%T does not implement metav1.Object interface", obj)
124+
key, err := client.ObjectKeyFromObject(obj)
125+
if err != nil {
126+
return OperationResultNone, err
131127
}
132128

133-
// retrieve the existing object
134-
key := client.ObjectKey{
135-
Name: metaObj.GetName(),
136-
Namespace: metaObj.GetNamespace(),
129+
if err := c.Get(ctx, key, obj); err != nil {
130+
if errors.IsNotFound(err) {
131+
if err := c.Create(ctx, obj); err != nil {
132+
return OperationResultNone, err
133+
}
134+
return OperationResultCreated, nil
135+
}
136+
return OperationResultNone, err
137137
}
138-
err := c.Get(ctx, key, obj)
139138

140-
// reconcile the existing object
141139
existing := obj.DeepCopyObject()
142-
existingObjMeta := existing.(v1.Object)
143-
existingObjMeta.SetName(metaObj.GetName())
144-
existingObjMeta.SetNamespace(metaObj.GetNamespace())
145-
146-
if e := f(obj); e != nil {
147-
return OperationResultNone, e
148-
}
149-
150-
if metaObj.GetName() != existingObjMeta.GetName() {
151-
return OperationResultNone, fmt.Errorf("ReconcileFn cannot mutate objects name")
140+
if err := f(obj); err != nil {
141+
return OperationResultNone, err
152142
}
153143

154-
if metaObj.GetNamespace() != existingObjMeta.GetNamespace() {
155-
return OperationResultNone, fmt.Errorf("ReconcileFn cannot mutate objects namespace")
144+
if reflect.DeepEqual(existing, obj) {
145+
return OperationResultNone, nil
156146
}
157147

158-
if errors.IsNotFound(err) {
159-
err = c.Create(ctx, obj)
160-
op = OperationResultCreated
161-
} else if err == nil {
162-
if reflect.DeepEqual(existing, obj) {
163-
return OperationResultNone, nil
164-
}
165-
err = c.Update(ctx, obj)
166-
op = OperationResultUpdated
167-
} else {
148+
newKey, err := client.ObjectKeyFromObject(obj)
149+
if err != nil {
168150
return OperationResultNone, err
169151
}
152+
if key != newKey {
153+
return OperationResultNone, fmt.Errorf("MutateFn cannot mutate object namespace and/or object name")
154+
}
170155

171-
if err != nil {
172-
op = OperationResultNone
156+
if err := c.Update(ctx, obj); err != nil {
157+
return OperationResultNone, err
173158
}
174-
return op, err
159+
return OperationResultUpdated, nil
175160
}
176161

177162
// MutateFn is a function which mutates the existing object into it's desired state.

pkg/controller/controllerutil/controllerutil_test.go

+25-3
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import (
2121
"fmt"
2222
"math/rand"
2323

24+
"sigs.k8s.io/controller-runtime/pkg/client"
25+
2426
. "github.com/onsi/ginkgo"
2527
. "github.com/onsi/gomega"
2628
appsv1 "k8s.io/api/apps/v1"
@@ -140,7 +142,7 @@ var _ = Describe("Controllerutil", func() {
140142
},
141143
Spec: corev1.PodSpec{
142144
Containers: []corev1.Container{
143-
corev1.Container{
145+
{
144146
Name: "busybox",
145147
Image: "busybox",
146148
},
@@ -149,6 +151,8 @@ var _ = Describe("Controllerutil", func() {
149151
},
150152
}
151153

154+
deploy.Spec = deplSpec
155+
152156
deplKey = types.NamespacedName{
153157
Name: deploy.Name,
154158
Namespace: deploy.Namespace,
@@ -158,7 +162,7 @@ var _ = Describe("Controllerutil", func() {
158162
It("creates a new object if one doesn't exists", func() {
159163
op, err := controllerutil.CreateOrUpdate(context.TODO(), c, deploy, deploymentSpecr(deplSpec))
160164

161-
By("returning OperationResultCreatedd")
165+
By("returning OperationResultCreated")
162166
Expect(op).To(BeEquivalentTo(controllerutil.OperationResultCreated))
163167

164168
By("returning no error")
@@ -176,7 +180,7 @@ var _ = Describe("Controllerutil", func() {
176180
Expect(op).To(BeEquivalentTo(controllerutil.OperationResultCreated))
177181

178182
op, err = controllerutil.CreateOrUpdate(context.TODO(), c, deploy, deploymentScaler(scale))
179-
By("returning OperationResultUpdatedd")
183+
By("returning OperationResultUpdated")
180184
Expect(op).To(BeEquivalentTo(controllerutil.OperationResultUpdated))
181185

182186
By("returning no error")
@@ -232,6 +236,16 @@ var _ = Describe("Controllerutil", func() {
232236
By("returning error")
233237
Expect(err).To(HaveOccurred())
234238
})
239+
240+
It("aborts immediately if there was an error initially retrieving the object", func() {
241+
op, err := controllerutil.CreateOrUpdate(context.TODO(), errorReader{c}, deploy, func(_ runtime.Object) error {
242+
Fail("Mutation method should not run")
243+
return nil
244+
})
245+
246+
Expect(op).To(BeEquivalentTo(controllerutil.OperationResultNone))
247+
Expect(err).To(HaveOccurred())
248+
})
235249
})
236250
})
237251

@@ -273,3 +287,11 @@ func deploymentScaler(replicas int32) controllerutil.MutateFn {
273287
}
274288
return fn
275289
}
290+
291+
type errorReader struct {
292+
client.Client
293+
}
294+
295+
func (e errorReader) Get(ctx context.Context, key client.ObjectKey, into runtime.Object) error {
296+
return fmt.Errorf("unexpected error")
297+
}

0 commit comments

Comments
 (0)