Skip to content
Permalink
Browse files

Cap number of managers from updates to 10

  • Loading branch information...
jennybuckley committed Aug 23, 2019
1 parent 5713c22 commit 8de9ba5c624044b5bad1540c4a0b7d49369c3ed7
@@ -18,6 +18,7 @@ package fieldmanager

import (
"fmt"
"sort"
"time"

"k8s.io/apimachinery/pkg/api/errors"
@@ -142,6 +143,10 @@ func (f *FieldManager) Update(liveObj, newObj runtime.Object, manager string) (r
// Update the time in the managedFieldsEntry for this operation
managed.Times[manager] = &metav1.Time{Time: time.Now().UTC()}

if managed, err = f.capUpdateManagers(managed); err != nil {
return nil, fmt.Errorf("failed to cap update managers: %v", err)
}

if err := internal.EncodeObjectManagedFields(newObj, managed); err != nil {
return nil, fmt.Errorf("failed to encode managed fields: %v", err)
}
@@ -282,3 +287,55 @@ func (f *FieldManager) stripFields(managed fieldpath.ManagedFields, manager stri

return managed
}

const maxUpdateManagers int = 10

func (f *FieldManager) capUpdateManagers(managed internal.Managed) (newManaged internal.Managed, err error) {
updaters := []string{}
for manager, fields := range managed.Fields {
if fields.Applied() == false {
updaters = append(updaters, manager)
}
}
if len(updaters) <= maxUpdateManagers {
return managed, nil
}
sort.Slice(updaters, func(i, j int) bool {
iTime, jTime, nTime := managed.Times[updaters[i]], managed.Times[updaters[j]], &metav1.Time{Time: time.Time{}}
if iTime == nil {
iTime = nTime
}
if jTime == nil {
jTime = nTime
}
if !iTime.Equal(jTime) {
return iTime.Before(jTime)
}
return updaters[i] < updaters[j]
})

// Merge the oldest updaters with versioned bucket managers until the number of updaters is under the cap
for i := 0; i < len(updaters) - maxUpdateManagers; i++ {
manager := updaters[i]
vs := managed.Fields[manager]
time := managed.Times[manager]
delete(managed.Fields, manager)

if manager, err = internal.BuildManagerIdentifier(&metav1.ManagedFieldsEntry{
Manager: "manager-too-old-to-track",
Operation: metav1.ManagedFieldsOperationUpdate,
APIVersion: string(vs.APIVersion()),
}); err != nil {
return managed, fmt.Errorf("failed to create bucket manager for version %v: %v", string(vs.APIVersion()), err)
}

if previous, ok := managed.Fields[manager]; ok {
managed.Fields[manager] = fieldpath.NewVersionedSet(vs.Set().Union(previous.Set()), vs.APIVersion(), vs.Applied())
} else {
managed.Fields[manager] = vs
}
managed.Times[manager] = time
}

return managed, nil
}
@@ -267,6 +267,63 @@ func TestApplyUpdateApplyConflictForced(t *testing.T) {
}
}

func TestApplyGroupsManySeparateUpdates(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ServerSideApply, true)()

_, client, closeFn := setup(t)
defer closeFn()

obj := []byte(`{
"apiVersion": "admissionregistration.k8s.io/v1",
"kind": "ValidatingWebhookConfiguration",
"metadata": {
"name": "webhook",
},
}`)

object, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
AbsPath("/apis/admissionregistration.k8s.io/v1").
Resource("validatingwebhookconfigurations").
Name("webhook").
Param("fieldManager", "apply_test").
Body(obj).Do().Get()
if err != nil {
t.Fatalf("Failed to create object using Apply patch: %v", err)
}

for i := 0; i < 20; i++ {
time.Sleep(500 * time.Millisecond)
unique := fmt.Sprintf("updater%v", i)
path := "/apis/admissionregistration.k8s.io/v1"
if i%2 == 0 {
path = "/apis/admissionregistration.k8s.io/v1beta1"
}
object, err = client.CoreV1().RESTClient().Patch(types.MergePatchType).
AbsPath(path).
Resource("validatingwebhookconfigurations").
Name("webhook").
Param("fieldManager", unique).
Body([]byte(`{"metadata":{"labels":{"` + unique + `":"new"}}}`)).Do().Get()
if err != nil {
t.Fatalf("Failed to patch object: %v", err)
}
}

accessor, err := meta.Accessor(object)
if err != nil {
t.Fatalf("Failed to get meta accessor: %v", err)
}

actual, err := json.MarshalIndent(object, "\t", "\t")
if err != nil {
t.Fatalf("Failed to marshal object: %v", err)
}

if len(accessor.GetManagedFields()) > 12 {
t.Fatalf("Object contains more managers than expected:\n%v", string(actual))
}
}

// TestApplyManagedFields makes sure that managedFields api does not change
func TestApplyManagedFields(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ServerSideApply, true)()

0 comments on commit 8de9ba5

Please sign in to comment.
You can’t perform that action at this time.