mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-09 12:07:47 +00:00
move check for noop managed field timestamp updates
this check needs to go after any mutations. After the mutating admission chain, rest.BeforeUpdate (which is responsible for reverting updates to immutable timestamp fields, among other things.) is called in the store.Update function. Without moving this check, it will be possible for an object to be written to etcd with only a change to its managed fields timestamp.
This commit is contained in:
parent
3cf9f66e90
commit
2b01f63b11
@ -654,9 +654,6 @@ func (p *patcher) patchResource(ctx context.Context, scope *RequestScope) (runti
|
|||||||
}
|
}
|
||||||
|
|
||||||
transformers := []rest.TransformFunc{p.applyPatch, p.applyAdmission, dedupOwnerReferencesTransformer}
|
transformers := []rest.TransformFunc{p.applyPatch, p.applyAdmission, dedupOwnerReferencesTransformer}
|
||||||
if scope.FieldManager != nil {
|
|
||||||
transformers = append(transformers, fieldmanager.IgnoreManagedFieldsTimestampsTransformer)
|
|
||||||
}
|
|
||||||
|
|
||||||
wasCreated := false
|
wasCreated := false
|
||||||
p.updatedObjectInfo = rest.DefaultUpdatedObjectInfo(nil, transformers...)
|
p.updatedObjectInfo = rest.DefaultUpdatedObjectInfo(nil, transformers...)
|
||||||
|
@ -189,15 +189,6 @@ func UpdateResource(r rest.Updater, scope *RequestScope, admit admission.Interfa
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ignore changes that only affect managed fields
|
|
||||||
// timestamps. FieldManager can't know about changes
|
|
||||||
// like normalized fields, defaulted fields and other
|
|
||||||
// mutations.
|
|
||||||
// Only makes sense when SSA field manager is being used
|
|
||||||
if scope.FieldManager != nil {
|
|
||||||
transformers = append(transformers, fieldmanager.IgnoreManagedFieldsTimestampsTransformer)
|
|
||||||
}
|
|
||||||
|
|
||||||
createAuthorizerAttributes := authorizer.AttributesRecord{
|
createAuthorizerAttributes := authorizer.AttributesRecord{
|
||||||
User: userInfo,
|
User: userInfo,
|
||||||
ResourceRequest: true,
|
ResourceRequest: true,
|
||||||
|
@ -38,6 +38,7 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
"k8s.io/apimachinery/pkg/watch"
|
"k8s.io/apimachinery/pkg/watch"
|
||||||
|
"k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager"
|
||||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||||
"k8s.io/apiserver/pkg/registry/generic"
|
"k8s.io/apiserver/pkg/registry/generic"
|
||||||
"k8s.io/apiserver/pkg/registry/rest"
|
"k8s.io/apiserver/pkg/registry/rest"
|
||||||
@ -671,6 +672,15 @@ func (e *Store) Update(ctx context.Context, name string, objInfo rest.UpdatedObj
|
|||||||
if err := rest.BeforeUpdate(e.UpdateStrategy, ctx, obj, existing); err != nil {
|
if err := rest.BeforeUpdate(e.UpdateStrategy, ctx, obj, existing); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ignore changes that only affect managed fields timestamps.
|
||||||
|
// FieldManager can't know about changes like normalized fields, defaulted
|
||||||
|
// fields and other mutations.
|
||||||
|
obj, err = fieldmanager.IgnoreManagedFieldsTimestampsTransformer(ctx, obj, existing)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// at this point we have a fully formed object. It is time to call the validators that the apiserver
|
// at this point we have a fully formed object. It is time to call the validators that the apiserver
|
||||||
// handling chain wants to enforce.
|
// handling chain wants to enforce.
|
||||||
if updateValidation != nil {
|
if updateValidation != nil {
|
||||||
|
@ -27,6 +27,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
"sigs.k8s.io/yaml"
|
"sigs.k8s.io/yaml"
|
||||||
|
|
||||||
appsv1 "k8s.io/api/apps/v1"
|
appsv1 "k8s.io/api/apps/v1"
|
||||||
@ -249,6 +250,55 @@ func getRV(obj runtime.Object) (string, error) {
|
|||||||
return acc.GetResourceVersion(), nil
|
return acc.GetResourceVersion(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNoopChangeCreationTime(t *testing.T) {
|
||||||
|
client, closeFn := setup(t)
|
||||||
|
defer closeFn()
|
||||||
|
|
||||||
|
ssBytes := []byte(`{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "ConfigMap",
|
||||||
|
"metadata": {
|
||||||
|
"name": "myconfig",
|
||||||
|
"creationTimestamp": null,
|
||||||
|
"resourceVersion": null
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"key": "value"
|
||||||
|
}
|
||||||
|
}`)
|
||||||
|
|
||||||
|
obj, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
|
||||||
|
Namespace("default").
|
||||||
|
Param("fieldManager", "apply_test").
|
||||||
|
Resource("configmaps").
|
||||||
|
Name("myconfig").
|
||||||
|
Body(ssBytes).
|
||||||
|
Do(context.TODO()).
|
||||||
|
Get()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create object: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
// Sleep for one second to make sure that the times of each update operation is different.
|
||||||
|
time.Sleep(1200 * time.Millisecond)
|
||||||
|
|
||||||
|
newObj, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
|
||||||
|
Namespace("default").
|
||||||
|
Param("fieldManager", "apply_test").
|
||||||
|
Resource("configmaps").
|
||||||
|
Name("myconfig").
|
||||||
|
Body(ssBytes).
|
||||||
|
Do(context.TODO()).
|
||||||
|
Get()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create object: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, obj, newObj)
|
||||||
|
}
|
||||||
|
|
||||||
// TestNoSemanticUpdateAppleSameResourceVersion makes sure that APPLY requests which makes no semantic changes
|
// TestNoSemanticUpdateAppleSameResourceVersion makes sure that APPLY requests which makes no semantic changes
|
||||||
// will not change the resource version (no write to etcd is done)
|
// will not change the resource version (no write to etcd is done)
|
||||||
//
|
//
|
||||||
|
Loading…
Reference in New Issue
Block a user