TTL is not preserved automatically during edit
Change the signature of GuaranteedUpdate so that TTL can be more easily preserved. Allow a simpler (no ttl) and more complex (response and node directly available, set ttl) path for GuaranteedUpdate. Add some tests to ensure this doesn't blow up again.
This commit is contained in:
@@ -72,8 +72,9 @@ type Etcd struct {
|
||||
ObjectNameFunc func(obj runtime.Object) (string, error)
|
||||
|
||||
// Return the TTL objects should be persisted with. Update is true if this
|
||||
// is an operation against an existing object.
|
||||
TTLFunc func(obj runtime.Object, update bool) (uint64, error)
|
||||
// is an operation against an existing object. Existing is the current TTL
|
||||
// or the default for this operation.
|
||||
TTLFunc func(obj runtime.Object, existing uint64, update bool) (uint64, error)
|
||||
|
||||
// Returns a matcher corresponding to the provided labels and fields.
|
||||
PredicateFunc func(label labels.Selector, field fields.Selector) generic.Matcher
|
||||
@@ -184,12 +185,9 @@ func (e *Etcd) CreateWithName(ctx api.Context, name string, obj runtime.Object)
|
||||
return err
|
||||
}
|
||||
}
|
||||
ttl := uint64(0)
|
||||
if e.TTLFunc != nil {
|
||||
ttl, err = e.TTLFunc(obj, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ttl, err := e.calculateTTL(obj, 0, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = e.Helper.CreateObj(key, obj, nil, ttl)
|
||||
err = etcderr.InterpretCreateError(err, e.EndpointName, name)
|
||||
@@ -214,12 +212,9 @@ func (e *Etcd) Create(ctx api.Context, obj runtime.Object) (runtime.Object, erro
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ttl := uint64(0)
|
||||
if e.TTLFunc != nil {
|
||||
ttl, err = e.TTLFunc(obj, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ttl, err := e.calculateTTL(obj, 0, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
trace.Step("About to create object")
|
||||
out := e.NewFunc()
|
||||
@@ -249,12 +244,9 @@ func (e *Etcd) UpdateWithName(ctx api.Context, name string, obj runtime.Object)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ttl := uint64(0)
|
||||
if e.TTLFunc != nil {
|
||||
ttl, err = e.TTLFunc(obj, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ttl, err := e.calculateTTL(obj, 0, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = e.Helper.SetObj(key, obj, nil, ttl)
|
||||
err = etcderr.InterpretUpdateError(err, e.EndpointName, name)
|
||||
@@ -281,49 +273,46 @@ func (e *Etcd) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool
|
||||
// TODO: expose TTL
|
||||
creating := false
|
||||
out := e.NewFunc()
|
||||
err = e.Helper.GuaranteedUpdate(key, out, true, func(existing runtime.Object) (runtime.Object, uint64, error) {
|
||||
err = e.Helper.GuaranteedUpdate(key, out, true, func(existing runtime.Object, res tools.ResponseMeta) (runtime.Object, *uint64, error) {
|
||||
version, err := e.Helper.Versioner.ObjectResourceVersion(existing)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
return nil, nil, err
|
||||
}
|
||||
if version == 0 {
|
||||
if !e.UpdateStrategy.AllowCreateOnUpdate() {
|
||||
return nil, 0, kubeerr.NewNotFound(e.EndpointName, name)
|
||||
return nil, nil, kubeerr.NewNotFound(e.EndpointName, name)
|
||||
}
|
||||
creating = true
|
||||
if err := rest.BeforeCreate(e.CreateStrategy, ctx, obj); err != nil {
|
||||
return nil, 0, err
|
||||
return nil, nil, err
|
||||
}
|
||||
ttl := uint64(0)
|
||||
if e.TTLFunc != nil {
|
||||
ttl, err = e.TTLFunc(obj, false)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
ttl, err := e.calculateTTL(obj, 0, false)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return obj, ttl, nil
|
||||
return obj, &ttl, nil
|
||||
}
|
||||
|
||||
creating = false
|
||||
newVersion, err := e.Helper.Versioner.ObjectResourceVersion(obj)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
return nil, nil, err
|
||||
}
|
||||
if newVersion != version {
|
||||
// TODO: return the most recent version to a client?
|
||||
return nil, 0, kubeerr.NewConflict(e.EndpointName, name, fmt.Errorf("the resource was updated to %d", version))
|
||||
return nil, nil, kubeerr.NewConflict(e.EndpointName, name, fmt.Errorf("the resource was updated to %d", version))
|
||||
}
|
||||
if err := rest.BeforeUpdate(e.UpdateStrategy, ctx, obj, existing); err != nil {
|
||||
return nil, 0, err
|
||||
return nil, nil, err
|
||||
}
|
||||
ttl := uint64(0)
|
||||
if e.TTLFunc != nil {
|
||||
ttl, err = e.TTLFunc(obj, true)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
ttl, err := e.calculateTTL(obj, res.TTL, true)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return obj, ttl, nil
|
||||
if int64(ttl) != res.TTL {
|
||||
return obj, &ttl, nil
|
||||
}
|
||||
return obj, nil, nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
@@ -479,3 +468,19 @@ func (e *Etcd) WatchPredicate(ctx api.Context, m generic.Matcher, resourceVersio
|
||||
|
||||
return e.Helper.WatchList(e.KeyRootFunc(ctx), version, filterFunc)
|
||||
}
|
||||
|
||||
// calculateTTL is a helper for retrieving the updated TTL for an object or returning an error
|
||||
// if the TTL cannot be calculated. The defaultTTL is changed to 1 if less than zero. Zero means
|
||||
// no TTL, not expire immediately.
|
||||
func (e *Etcd) calculateTTL(obj runtime.Object, defaultTTL int64, update bool) (ttl uint64, err error) {
|
||||
// etcd may return a negative TTL for a node if the expiration has not occured due
|
||||
// to server lag - we will ensure that the value is at least set.
|
||||
if defaultTTL < 0 {
|
||||
defaultTTL = 1
|
||||
}
|
||||
ttl = uint64(defaultTTL)
|
||||
if e.TTLFunc != nil {
|
||||
ttl, err = e.TTLFunc(obj, ttl, update)
|
||||
}
|
||||
return ttl, err
|
||||
}
|
||||
|
Reference in New Issue
Block a user