Skip to content

Commit e4eef7d

Browse files
committed
⚠️ Fakeclient: Reject Update with outdated ResourceVersion
1 parent 9f8aab6 commit e4eef7d

File tree

2 files changed

+73
-16
lines changed

2 files changed

+73
-16
lines changed

pkg/client/fake/client.go

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,12 @@ package fake
1919
import (
2020
"context"
2121
"encoding/json"
22+
"errors"
2223
"fmt"
2324
"strconv"
2425
"strings"
2526

27+
kerrors "k8s.io/apimachinery/pkg/api/errors"
2628
"k8s.io/apimachinery/pkg/api/meta"
2729
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2830
"k8s.io/apimachinery/pkg/runtime"
@@ -79,28 +81,42 @@ func NewFakeClientWithScheme(clientScheme *runtime.Scheme, initObjs ...runtime.O
7981
}
8082

8183
func (t versionedTracker) Create(gvr schema.GroupVersionResource, obj runtime.Object, ns string) error {
82-
if accessor, err := meta.Accessor(obj); err == nil {
83-
if accessor.GetResourceVersion() == "" {
84-
accessor.SetResourceVersion("1")
85-
}
86-
} else {
84+
accessor, err := meta.Accessor(obj)
85+
if err != nil {
8786
return err
8887
}
88+
if accessor.GetResourceVersion() != "" {
89+
return kerrors.NewBadRequest("resourceVersion can not be set for Create requests")
90+
}
91+
accessor.SetResourceVersion("1")
8992
return t.ObjectTracker.Create(gvr, obj, ns)
9093
}
9194

9295
func (t versionedTracker) Update(gvr schema.GroupVersionResource, obj runtime.Object, ns string) error {
93-
if accessor, err := meta.Accessor(obj); err == nil {
94-
version := 0
95-
if rv := accessor.GetResourceVersion(); rv != "" {
96-
version, err = strconv.Atoi(rv)
97-
}
98-
if err == nil {
99-
accessor.SetResourceVersion(strconv.Itoa(version + 1))
100-
}
101-
} else {
96+
accessor, err := meta.Accessor(obj)
97+
if err != nil {
98+
return fmt.Errorf("failed to get accessor for object: %v", err)
99+
}
100+
oldObject, err := t.ObjectTracker.Get(gvr, ns, accessor.GetName())
101+
if err != nil {
102+
return err
103+
}
104+
oldAccessor, err := meta.Accessor(oldObject)
105+
if err != nil {
102106
return err
103107
}
108+
if accessor.GetResourceVersion() != oldAccessor.GetResourceVersion() {
109+
return kerrors.NewConflict(gvr.GroupResource(), accessor.GetName(), errors.New("object was modified"))
110+
}
111+
if oldAccessor.GetResourceVersion() == "" {
112+
oldAccessor.SetResourceVersion("0")
113+
}
114+
intResourceVersion, err := strconv.Atoi(oldAccessor.GetResourceVersion())
115+
if err != nil {
116+
return fmt.Errorf("can not convert resourceVersion %q to int: %v", oldAccessor.GetResourceVersion(), err)
117+
}
118+
intResourceVersion++
119+
accessor.SetResourceVersion(strconv.Itoa(intResourceVersion))
104120
return t.ObjectTracker.Update(gvr, obj, ns)
105121
}
106122

pkg/client/fake/client_test.go

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
appsv1 "k8s.io/api/apps/v1"
2828
corev1 "k8s.io/api/core/v1"
2929
"k8s.io/apimachinery/pkg/api/errors"
30+
kerrors "k8s.io/apimachinery/pkg/api/errors"
3031
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3132
"k8s.io/apimachinery/pkg/runtime"
3233
"k8s.io/apimachinery/pkg/types"
@@ -172,6 +173,19 @@ var _ = Describe("Fake client", func() {
172173
Expect(obj.ObjectMeta.ResourceVersion).To(Equal("1"))
173174
})
174175

176+
It("should error on create with set resourceVersion", func() {
177+
By("Creating a new configmap")
178+
newcm := &corev1.ConfigMap{
179+
ObjectMeta: metav1.ObjectMeta{
180+
Name: "new-test-cm",
181+
Namespace: "ns2",
182+
ResourceVersion: "1",
183+
},
184+
}
185+
err := cl.Create(context.Background(), newcm)
186+
Expect(kerrors.IsBadRequest(err)).To(BeTrue())
187+
})
188+
175189
It("should be able to Create with GenerateName", func() {
176190
By("Creating a new configmap")
177191
newcm := &corev1.ConfigMap{
@@ -211,7 +225,7 @@ var _ = Describe("Fake client", func() {
211225
ObjectMeta: metav1.ObjectMeta{
212226
Name: "test-cm",
213227
Namespace: "ns2",
214-
ResourceVersion: "1",
228+
ResourceVersion: "",
215229
},
216230
Data: map[string]string{
217231
"test-key": "new-value",
@@ -229,7 +243,34 @@ var _ = Describe("Fake client", func() {
229243
err = cl.Get(context.Background(), namespacedName, obj)
230244
Expect(err).To(BeNil())
231245
Expect(obj).To(Equal(newcm))
232-
Expect(obj.ObjectMeta.ResourceVersion).To(Equal("2"))
246+
Expect(obj.ObjectMeta.ResourceVersion).To(Equal("1"))
247+
})
248+
249+
It("should reject updates with non-matching ResourceVersion", func() {
250+
By("Updating a new configmap")
251+
newcm := &corev1.ConfigMap{
252+
ObjectMeta: metav1.ObjectMeta{
253+
Name: "test-cm",
254+
Namespace: "ns2",
255+
ResourceVersion: "1",
256+
},
257+
Data: map[string]string{
258+
"test-key": "new-value",
259+
},
260+
}
261+
err := cl.Update(context.Background(), newcm)
262+
Expect(kerrors.IsConflict(err)).To(BeTrue())
263+
264+
By("Getting the configmap")
265+
namespacedName := types.NamespacedName{
266+
Name: "test-cm",
267+
Namespace: "ns2",
268+
}
269+
obj := &corev1.ConfigMap{}
270+
err = cl.Get(context.Background(), namespacedName, obj)
271+
Expect(err).To(BeNil())
272+
Expect(obj).To(Equal(cm))
273+
Expect(obj.ObjectMeta.ResourceVersion).To(Equal(""))
233274
})
234275

235276
It("should be able to Delete", func() {

0 commit comments

Comments
 (0)