Skip to content

Commit

Permalink
⚠️ Fakeclient: Reject Update with outdated ResourceVersion
Browse files Browse the repository at this point in the history
  • Loading branch information
alvaroaleman committed Mar 5, 2020
1 parent 9f8aab6 commit 25ef372
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 16 deletions.
44 changes: 30 additions & 14 deletions pkg/client/fake/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@ package fake
import (
"context"
"encoding/json"
"errors"
"fmt"
"strconv"
"strings"

apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
Expand Down Expand Up @@ -79,28 +81,42 @@ func NewFakeClientWithScheme(clientScheme *runtime.Scheme, initObjs ...runtime.O
}

func (t versionedTracker) Create(gvr schema.GroupVersionResource, obj runtime.Object, ns string) error {
if accessor, err := meta.Accessor(obj); err == nil {
if accessor.GetResourceVersion() == "" {
accessor.SetResourceVersion("1")
}
} else {
accessor, err := meta.Accessor(obj)
if err != nil {
return err
}
if accessor.GetResourceVersion() != "" {
return apierrors.NewBadRequest("resourceVersion can not be set for Create requests")
}
accessor.SetResourceVersion("1")
return t.ObjectTracker.Create(gvr, obj, ns)
}

func (t versionedTracker) Update(gvr schema.GroupVersionResource, obj runtime.Object, ns string) error {
if accessor, err := meta.Accessor(obj); err == nil {
version := 0
if rv := accessor.GetResourceVersion(); rv != "" {
version, err = strconv.Atoi(rv)
}
if err == nil {
accessor.SetResourceVersion(strconv.Itoa(version + 1))
}
} else {
accessor, err := meta.Accessor(obj)
if err != nil {
return fmt.Errorf("failed to get accessor for object: %v", err)
}
oldObject, err := t.ObjectTracker.Get(gvr, ns, accessor.GetName())
if err != nil {
return err
}
oldAccessor, err := meta.Accessor(oldObject)
if err != nil {
return err
}
if accessor.GetResourceVersion() != oldAccessor.GetResourceVersion() {
return apierrors.NewConflict(gvr.GroupResource(), accessor.GetName(), errors.New("object was modified"))
}
if oldAccessor.GetResourceVersion() == "" {
oldAccessor.SetResourceVersion("0")
}
intResourceVersion, err := strconv.ParseUint(oldAccessor.GetResourceVersion(), 10, 64)
if err != nil {
return fmt.Errorf("can not convert resourceVersion %q to int: %v", oldAccessor.GetResourceVersion(), err)
}
intResourceVersion++
accessor.SetResourceVersion(strconv.FormatUint(intResourceVersion, 10))
return t.ObjectTracker.Update(gvr, obj, ns)
}

Expand Down
45 changes: 43 additions & 2 deletions pkg/client/fake/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
Expand Down Expand Up @@ -172,6 +173,19 @@ var _ = Describe("Fake client", func() {
Expect(obj.ObjectMeta.ResourceVersion).To(Equal("1"))
})

It("should error on create with set resourceVersion", func() {
By("Creating a new configmap")
newcm := &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "new-test-cm",
Namespace: "ns2",
ResourceVersion: "1",
},
}
err := cl.Create(context.Background(), newcm)
Expect(apierrors.IsBadRequest(err)).To(BeTrue())
})

It("should be able to Create with GenerateName", func() {
By("Creating a new configmap")
newcm := &corev1.ConfigMap{
Expand Down Expand Up @@ -211,7 +225,7 @@ var _ = Describe("Fake client", func() {
ObjectMeta: metav1.ObjectMeta{
Name: "test-cm",
Namespace: "ns2",
ResourceVersion: "1",
ResourceVersion: "",
},
Data: map[string]string{
"test-key": "new-value",
Expand All @@ -229,7 +243,34 @@ var _ = Describe("Fake client", func() {
err = cl.Get(context.Background(), namespacedName, obj)
Expect(err).To(BeNil())
Expect(obj).To(Equal(newcm))
Expect(obj.ObjectMeta.ResourceVersion).To(Equal("2"))
Expect(obj.ObjectMeta.ResourceVersion).To(Equal("1"))
})

It("should reject updates with non-matching ResourceVersion", func() {
By("Updating a new configmap")
newcm := &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "test-cm",
Namespace: "ns2",
ResourceVersion: "1",
},
Data: map[string]string{
"test-key": "new-value",
},
}
err := cl.Update(context.Background(), newcm)
Expect(apierrors.IsConflict(err)).To(BeTrue())

By("Getting the configmap")
namespacedName := types.NamespacedName{
Name: "test-cm",
Namespace: "ns2",
}
obj := &corev1.ConfigMap{}
err = cl.Get(context.Background(), namespacedName, obj)
Expect(err).To(BeNil())
Expect(obj).To(Equal(cm))
Expect(obj.ObjectMeta.ResourceVersion).To(Equal(""))
})

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

0 comments on commit 25ef372

Please sign in to comment.