mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-09-15 06:01:50 +00:00
Allow update without resource version
This commit is contained in:
@@ -271,6 +271,14 @@ func (e *Etcd) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
// If AllowUnconditionalUpdate() is true and the object specified by the user does not have a resource version,
|
||||
// then we populate it with the latest version.
|
||||
// Else, we check that the version specified by the user matches the version of latest etcd object.
|
||||
resourceVersion, err := e.Helper.Versioner.ObjectResourceVersion(obj)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
doUnconditionalUpdate := resourceVersion == 0 && e.UpdateStrategy.AllowUnconditionalUpdate()
|
||||
// TODO: expose TTL
|
||||
creating := false
|
||||
out := e.NewFunc()
|
||||
@@ -295,13 +303,21 @@ func (e *Etcd) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool
|
||||
}
|
||||
|
||||
creating = false
|
||||
newVersion, err := e.Helper.Versioner.ObjectResourceVersion(obj)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if newVersion != version {
|
||||
// TODO: return the most recent version to a client?
|
||||
return nil, nil, kubeerr.NewConflict(e.EndpointName, name, fmt.Errorf("the object has been modified; please apply your changes to the latest version and try again"))
|
||||
if doUnconditionalUpdate {
|
||||
// Update the object's resource version to match the latest etcd object's resource version.
|
||||
err = e.Helper.Versioner.UpdateObject(obj, res.Expiration, res.ResourceVersion)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
} else {
|
||||
// Check if the object's resource version matches the latest resource version.
|
||||
newVersion, err := e.Helper.Versioner.ObjectResourceVersion(obj)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if newVersion != version {
|
||||
return nil, nil, kubeerr.NewConflict(e.EndpointName, name, fmt.Errorf("the object has been modified; please apply your changes to the latest version and try again"))
|
||||
}
|
||||
}
|
||||
if err := rest.BeforeUpdate(e.UpdateStrategy, ctx, obj, existing); err != nil {
|
||||
return nil, nil, err
|
||||
|
@@ -37,12 +37,14 @@ import (
|
||||
type testRESTStrategy struct {
|
||||
runtime.ObjectTyper
|
||||
api.NameGenerator
|
||||
namespaceScoped bool
|
||||
allowCreateOnUpdate bool
|
||||
namespaceScoped bool
|
||||
allowCreateOnUpdate bool
|
||||
allowUnconditionalUpdate bool
|
||||
}
|
||||
|
||||
func (t *testRESTStrategy) NamespaceScoped() bool { return t.namespaceScoped }
|
||||
func (t *testRESTStrategy) AllowCreateOnUpdate() bool { return t.allowCreateOnUpdate }
|
||||
func (t *testRESTStrategy) NamespaceScoped() bool { return t.namespaceScoped }
|
||||
func (t *testRESTStrategy) AllowCreateOnUpdate() bool { return t.allowCreateOnUpdate }
|
||||
func (t *testRESTStrategy) AllowUnconditionalUpdate() bool { return t.allowUnconditionalUpdate }
|
||||
|
||||
func (t *testRESTStrategy) PrepareForCreate(obj runtime.Object) {}
|
||||
func (t *testRESTStrategy) PrepareForUpdate(obj, old runtime.Object) {}
|
||||
@@ -68,7 +70,7 @@ func NewTestGenericEtcdRegistry(t *testing.T) (*tools.FakeEtcdClient, *Etcd) {
|
||||
f := tools.NewFakeEtcdClient(t)
|
||||
f.TestIndex = true
|
||||
h := tools.NewEtcdHelper(f, testapi.Codec(), etcdtest.PathPrefix())
|
||||
strategy := &testRESTStrategy{api.Scheme, api.SimpleNameGenerator, true, false}
|
||||
strategy := &testRESTStrategy{api.Scheme, api.SimpleNameGenerator, true, false, true}
|
||||
podPrefix := "/pods"
|
||||
return f, &Etcd{
|
||||
NewFunc: func() runtime.Object { return &api.Pod{} },
|
||||
@@ -390,7 +392,10 @@ func TestEtcdUpdate(t *testing.T) {
|
||||
ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: api.NamespaceDefault, ResourceVersion: "1"},
|
||||
Spec: api.PodSpec{NodeName: "machine2"},
|
||||
}
|
||||
|
||||
podAWithResourceVersion := &api.Pod{
|
||||
ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: api.NamespaceDefault, ResourceVersion: "3"},
|
||||
Spec: api.PodSpec{NodeName: "machine"},
|
||||
}
|
||||
nodeWithPodA := tools.EtcdResponseWithError{
|
||||
R: &etcd.Response{
|
||||
Node: &etcd.Node{
|
||||
@@ -424,18 +429,29 @@ func TestEtcdUpdate(t *testing.T) {
|
||||
E: nil,
|
||||
}
|
||||
|
||||
nodeWithPodAWithResourceVersion := tools.EtcdResponseWithError{
|
||||
R: &etcd.Response{
|
||||
Node: &etcd.Node{
|
||||
Value: runtime.EncodeOrDie(testapi.Codec(), podAWithResourceVersion),
|
||||
ModifiedIndex: 3,
|
||||
CreatedIndex: 1,
|
||||
},
|
||||
},
|
||||
E: nil,
|
||||
}
|
||||
emptyNode := tools.EtcdResponseWithError{
|
||||
R: &etcd.Response{},
|
||||
E: tools.EtcdErrorNotFound,
|
||||
}
|
||||
|
||||
table := map[string]struct {
|
||||
existing tools.EtcdResponseWithError
|
||||
expect tools.EtcdResponseWithError
|
||||
toUpdate runtime.Object
|
||||
allowCreate bool
|
||||
objOK func(obj runtime.Object) bool
|
||||
errOK func(error) bool
|
||||
existing tools.EtcdResponseWithError
|
||||
expect tools.EtcdResponseWithError
|
||||
toUpdate runtime.Object
|
||||
allowCreate bool
|
||||
allowUnconditionalUpdate bool
|
||||
objOK func(obj runtime.Object) bool
|
||||
errOK func(error) bool
|
||||
}{
|
||||
"normal": {
|
||||
existing: nodeWithPodA,
|
||||
@@ -462,11 +478,19 @@ func TestEtcdUpdate(t *testing.T) {
|
||||
toUpdate: podB,
|
||||
errOK: func(err error) bool { return errors.IsConflict(err) },
|
||||
},
|
||||
"unconditionalUpdate": {
|
||||
existing: nodeWithPodAWithResourceVersion,
|
||||
allowUnconditionalUpdate: true,
|
||||
toUpdate: podA,
|
||||
objOK: func(obj runtime.Object) bool { return true },
|
||||
errOK: func(err error) bool { return err == nil },
|
||||
},
|
||||
}
|
||||
|
||||
for name, item := range table {
|
||||
fakeClient, registry := NewTestGenericEtcdRegistry(t)
|
||||
registry.UpdateStrategy.(*testRESTStrategy).allowCreateOnUpdate = item.allowCreate
|
||||
registry.UpdateStrategy.(*testRESTStrategy).allowUnconditionalUpdate = item.allowUnconditionalUpdate
|
||||
path := etcdtest.AddPrefix("pods/foo")
|
||||
fakeClient.Data[path] = item.existing
|
||||
obj, _, err := registry.Update(api.NewDefaultContext(), item.toUpdate)
|
||||
|
Reference in New Issue
Block a user