Only update managedFields on update if it already exists

This commit is contained in:
jennybuckley 2019-06-05 14:09:08 -07:00 committed by Jennifer Buckley
parent a1f1f0b599
commit 9a12e37a6d
5 changed files with 48 additions and 11 deletions

View File

@ -47,6 +47,7 @@ go_test(
"//staging/src/k8s.io/api/core/v1:go_default_library", "//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/meta:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/api/meta:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",

View File

@ -104,6 +104,10 @@ func (f *FieldManager) Update(liveObj, newObj runtime.Object, manager string) (r
return nil, fmt.Errorf("failed to decode managed fields: %v", err) return nil, fmt.Errorf("failed to decode managed fields: %v", err)
} }
} }
// if managed field is still empty, skip updating managed fields altogether
if len(managed.Fields) == 0 {
return newObj, nil
}
newObjVersioned, err := f.toVersioned(newObj) newObjVersioned, err := f.toVersioned(newObj)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to convert new object to proper version: %v", err) return nil, fmt.Errorf("failed to convert new object to proper version: %v", err)
@ -165,9 +169,11 @@ func (f *FieldManager) Update(liveObj, newObj runtime.Object, manager string) (r
// object and update the managed fields. // object and update the managed fields.
func (f *FieldManager) Apply(liveObj runtime.Object, patch []byte, fieldManager string, force bool) (runtime.Object, error) { func (f *FieldManager) Apply(liveObj runtime.Object, patch []byte, fieldManager string, force bool) (runtime.Object, error) {
// If the object doesn't have metadata, apply isn't allowed. // If the object doesn't have metadata, apply isn't allowed.
if _, err := meta.Accessor(liveObj); err != nil { accessor, err := meta.Accessor(liveObj)
if err != nil {
return nil, fmt.Errorf("couldn't get accessor: %v", err) return nil, fmt.Errorf("couldn't get accessor: %v", err)
} }
missingManagedFields := (len(accessor.GetManagedFields()) == 0)
managed, err := internal.DecodeObjectManagedFields(liveObj) managed, err := internal.DecodeObjectManagedFields(liveObj)
if err != nil { if err != nil {
@ -207,6 +213,23 @@ func (f *FieldManager) Apply(liveObj runtime.Object, patch []byte, fieldManager
} }
apiVersion := fieldpath.APIVersion(f.groupVersion.String()) apiVersion := fieldpath.APIVersion(f.groupVersion.String())
// if managed field is missing, create a single entry for all the fields
if missingManagedFields {
unknownManager, err := internal.BuildManagerIdentifier(&metav1.ManagedFieldsEntry{
Manager: "before-first-apply",
Operation: metav1.ManagedFieldsOperationUpdate,
APIVersion: f.groupVersion.String(),
})
if err != nil {
return nil, fmt.Errorf("failed to create manager for existing fields: %v", err)
}
unknownFieldSet, err := liveObjTyped.ToFieldSet()
if err != nil {
return nil, fmt.Errorf("failed to create fieldset for existing fields: %v", err)
}
managed.Fields[unknownManager] = fieldpath.NewVersionedSet(unknownFieldSet, apiVersion, false)
f.stripFields(managed.Fields, unknownManager)
}
newObjTyped, managedFields, err := f.updater.Apply(liveObjTyped, patchObjTyped, apiVersion, managed.Fields, manager, force) newObjTyped, managedFields, err := f.updater.Apply(liveObjTyped, patchObjTyped, apiVersion, managed.Fields, manager, force)
if err != nil { if err != nil {
if conflicts, ok := err.(merge.Conflicts); ok { if conflicts, ok := err.(merge.Conflicts); ok {

View File

@ -25,6 +25,7 @@ import (
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors" apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
@ -75,6 +76,7 @@ func TestApplyStripsFields(t *testing.T) {
f := NewTestFieldManager() f := NewTestFieldManager()
obj := &corev1.Pod{} obj := &corev1.Pod{}
obj.ObjectMeta.ManagedFields = []metav1.ManagedFieldsEntry{{}}
newObj, err := f.Apply(obj, []byte(`{ newObj, err := f.Apply(obj, []byte(`{
"apiVersion": "apps/v1", "apiVersion": "apps/v1",
@ -153,6 +155,7 @@ func TestApplyDoesNotStripLabels(t *testing.T) {
f := NewTestFieldManager() f := NewTestFieldManager()
obj := &corev1.Pod{} obj := &corev1.Pod{}
obj.ObjectMeta.ManagedFields = []metav1.ManagedFieldsEntry{{}}
newObj, err := f.Apply(obj, []byte(`{ newObj, err := f.Apply(obj, []byte(`{
"apiVersion": "apps/v1", "apiVersion": "apps/v1",

View File

@ -421,7 +421,7 @@ func (p *applyPatcher) applyPatchToCurrentObject(obj runtime.Object) (runtime.Ob
func (p *applyPatcher) createNewObject() (runtime.Object, error) { func (p *applyPatcher) createNewObject() (runtime.Object, error) {
obj, err := p.creater.New(p.kind) obj, err := p.creater.New(p.kind)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to create new object: %v", obj) return nil, fmt.Errorf("failed to create new object: %v", err)
} }
return p.applyPatchToCurrentObject(obj) return p.applyPatchToCurrentObject(obj)
} }

View File

@ -1178,7 +1178,6 @@ var podBytes = []byte(`
apiVersion: v1 apiVersion: v1
kind: Pod kind: Pod
metadata: metadata:
creationTimestamp: "2019-07-08T09:31:18Z"
labels: labels:
app: some-app app: some-app
plugin1: some-value plugin1: some-value
@ -1194,8 +1193,6 @@ metadata:
kind: ReplicaSet kind: ReplicaSet
name: some-name name: some-name
uid: 0a9d2b9e-779e-11e7-b422-42010a8001be uid: 0a9d2b9e-779e-11e7-b422-42010a8001be
selfLink: /api/v1/namespaces/pah
uid: 23e8f548-a163-11e9-abe4-42010a80026b
spec: spec:
containers: containers:
- args: - args:
@ -1329,22 +1326,27 @@ func BenchmarkNoServerSideApply(b *testing.B) {
} }
func getPodSizeWhenEnabled(b *testing.B, pod v1.Pod) int { func getPodSizeWhenEnabled(b *testing.B, pod v1.Pod) int {
return len(getPodBytesWhenEnabled(b, pod, "application/vnd.kubernetes.protobuf"))
}
func getPodBytesWhenEnabled(b *testing.B, pod v1.Pod, format string) []byte {
defer featuregatetesting.SetFeatureGateDuringTest(b, utilfeature.DefaultFeatureGate, genericfeatures.ServerSideApply, true)() defer featuregatetesting.SetFeatureGateDuringTest(b, utilfeature.DefaultFeatureGate, genericfeatures.ServerSideApply, true)()
_, client, closeFn := setup(b) _, client, closeFn := setup(b)
defer closeFn() defer closeFn()
flag.Lookup("v").Value.Set("0") flag.Lookup("v").Value.Set("0")
pod.Name = "size-pod" pod.Name = "size-pod"
podB, err := client.CoreV1().RESTClient().Post(). podB, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
Name(pod.Name).
Namespace("default"). Namespace("default").
Param("fieldManager", "apply_test").
Resource("pods"). Resource("pods").
SetHeader("Content-Type", "application/yaml"). SetHeader("Accept", format).
SetHeader("Accept", "application/vnd.kubernetes.protobuf").
Body(encodePod(pod)).DoRaw() Body(encodePod(pod)).DoRaw()
if err != nil { if err != nil {
b.Fatalf("Failed to create object: %v", err) b.Fatalf("Failed to create object: %#v", err)
} }
return len(podB) return podB
} }
func BenchmarkNoServerSideApplyButSameSize(b *testing.B) { func BenchmarkNoServerSideApplyButSameSize(b *testing.B) {
@ -1385,16 +1387,24 @@ func BenchmarkNoServerSideApplyButSameSize(b *testing.B) {
} }
func BenchmarkServerSideApply(b *testing.B) { func BenchmarkServerSideApply(b *testing.B) {
podBytesWhenEnabled := getPodBytesWhenEnabled(b, decodePod(podBytes), "application/yaml")
defer featuregatetesting.SetFeatureGateDuringTest(b, utilfeature.DefaultFeatureGate, genericfeatures.ServerSideApply, true)() defer featuregatetesting.SetFeatureGateDuringTest(b, utilfeature.DefaultFeatureGate, genericfeatures.ServerSideApply, true)()
_, client, closeFn := setup(b) _, client, closeFn := setup(b)
defer closeFn() defer closeFn()
flag.Lookup("v").Value.Set("0") flag.Lookup("v").Value.Set("0")
benchAll(b, client, decodePod(podBytes)) benchAll(b, client, decodePod(podBytesWhenEnabled))
} }
func benchAll(b *testing.B, client kubernetes.Interface, pod v1.Pod) { func benchAll(b *testing.B, client kubernetes.Interface, pod v1.Pod) {
// Make sure pod is ready to post
pod.ObjectMeta.CreationTimestamp = metav1.Time{}
pod.ObjectMeta.ResourceVersion = ""
pod.ObjectMeta.UID = ""
pod.ObjectMeta.SelfLink = ""
// Create pod for repeated-updates // Create pod for repeated-updates
pod.Name = "repeated-pod" pod.Name = "repeated-pod"
_, err := client.CoreV1().RESTClient().Post(). _, err := client.CoreV1().RESTClient().Post().