Skip to content

Commit 6f13032

Browse files
authored
Merge pull request kubernetes#81524 from jennybuckley/apply-group-updates-by-manager
Group managedFieldsEntries for update by manager name
2 parents 8f887ca + 87eabcd commit 6f13032

File tree

4 files changed

+66
-33
lines changed

4 files changed

+66
-33
lines changed

staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/fieldmanager.go

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ func (f *FieldManager) Update(liveObj, newObj runtime.Object, manager string) (r
9898
// If the managed field is empty or we failed to decode it,
9999
// let's try the live object. This is to prevent clients who
100100
// don't understand managedFields from deleting it accidentally.
101-
if err != nil || len(managed) == 0 {
101+
if err != nil || len(managed.Fields) == 0 {
102102
managed, err = internal.DecodeObjectManagedFields(liveObj)
103103
if err != nil {
104104
return nil, fmt.Errorf("failed to decode managed fields: %v", err)
@@ -133,11 +133,14 @@ func (f *FieldManager) Update(liveObj, newObj runtime.Object, manager string) (r
133133
}
134134

135135
// TODO(apelisse) use the first return value when unions are implemented
136-
_, managed, err = f.updater.Update(liveObjTyped, newObjTyped, apiVersion, managed, manager)
136+
_, managed.Fields, err = f.updater.Update(liveObjTyped, newObjTyped, apiVersion, managed.Fields, manager)
137137
if err != nil {
138138
return nil, fmt.Errorf("failed to update ManagedFields: %v", err)
139139
}
140-
managed = f.stripFields(managed, manager)
140+
managed.Fields = f.stripFields(managed.Fields, manager)
141+
142+
// Update the time in the managedFieldsEntry for this operation
143+
managed.Times[manager] = &metav1.Time{Time: time.Now().UTC()}
141144

142145
if err := internal.EncodeObjectManagedFields(newObj, managed); err != nil {
143146
return nil, fmt.Errorf("failed to encode managed fields: %v", err)
@@ -192,14 +195,17 @@ func (f *FieldManager) Apply(liveObj runtime.Object, patch []byte, fieldManager
192195
}
193196

194197
apiVersion := fieldpath.APIVersion(f.groupVersion.String())
195-
newObjTyped, managed, err := f.updater.Apply(liveObjTyped, patchObjTyped, apiVersion, managed, manager, force)
198+
newObjTyped, managedFields, err := f.updater.Apply(liveObjTyped, patchObjTyped, apiVersion, managed.Fields, manager, force)
196199
if err != nil {
197200
if conflicts, ok := err.(merge.Conflicts); ok {
198201
return nil, internal.NewConflictError(conflicts)
199202
}
200203
return nil, err
201204
}
202-
managed = f.stripFields(managed, manager)
205+
managed.Fields = f.stripFields(managedFields, manager)
206+
207+
// Update the time in the managedFieldsEntry for this operation
208+
managed.Times[manager] = &metav1.Time{Time: time.Now().UTC()}
203209

204210
newObj, err := f.typeConverter.TypedToObject(newObjTyped)
205211
if err != nil {
@@ -236,7 +242,6 @@ func (f *FieldManager) buildManagerInfo(prefix string, operation metav1.ManagedF
236242
Manager: prefix,
237243
Operation: operation,
238244
APIVersion: f.groupVersion.String(),
239-
Time: &metav1.Time{Time: time.Now().UTC()},
240245
}
241246
if managerInfo.Manager == "" {
242247
managerInfo.Manager = "unknown"

staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/managedfields.go

Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@ import (
2828
"sigs.k8s.io/structured-merge-diff/fieldpath"
2929
)
3030

31+
// Managed groups a fieldpath.ManagedFields together with the timestamps associated with each operation.
32+
type Managed struct {
33+
Fields fieldpath.ManagedFields
34+
Times map[string]*metav1.Time
35+
}
36+
3137
// RemoveObjectManagedFields removes the ManagedFields from the object
3238
// before we merge so that it doesn't appear in the ManagedFields
3339
// recursively.
@@ -40,9 +46,9 @@ func RemoveObjectManagedFields(obj runtime.Object) {
4046
}
4147

4248
// DecodeObjectManagedFields extracts and converts the objects ManagedFields into a fieldpath.ManagedFields.
43-
func DecodeObjectManagedFields(from runtime.Object) (fieldpath.ManagedFields, error) {
49+
func DecodeObjectManagedFields(from runtime.Object) (Managed, error) {
4450
if from == nil {
45-
return fieldpath.ManagedFields{}, nil
51+
return Managed{}, nil
4652
}
4753
accessor, err := meta.Accessor(from)
4854
if err != nil {
@@ -51,42 +57,44 @@ func DecodeObjectManagedFields(from runtime.Object) (fieldpath.ManagedFields, er
5157

5258
managed, err := decodeManagedFields(accessor.GetManagedFields())
5359
if err != nil {
54-
return nil, fmt.Errorf("failed to convert managed fields from API: %v", err)
60+
return Managed{}, fmt.Errorf("failed to convert managed fields from API: %v", err)
5561
}
5662
return managed, err
5763
}
5864

5965
// EncodeObjectManagedFields converts and stores the fieldpathManagedFields into the objects ManagedFields
60-
func EncodeObjectManagedFields(obj runtime.Object, fields fieldpath.ManagedFields) error {
66+
func EncodeObjectManagedFields(obj runtime.Object, managed Managed) error {
6167
accessor, err := meta.Accessor(obj)
6268
if err != nil {
6369
panic(fmt.Sprintf("couldn't get accessor: %v", err))
6470
}
6571

66-
managed, err := encodeManagedFields(fields)
72+
encodedManagedFields, err := encodeManagedFields(managed)
6773
if err != nil {
6874
return fmt.Errorf("failed to convert back managed fields to API: %v", err)
6975
}
70-
accessor.SetManagedFields(managed)
76+
accessor.SetManagedFields(encodedManagedFields)
7177

7278
return nil
7379
}
7480

7581
// decodeManagedFields converts ManagedFields from the wire format (api format)
7682
// to the format used by sigs.k8s.io/structured-merge-diff
77-
func decodeManagedFields(encodedManagedFields []metav1.ManagedFieldsEntry) (managedFields fieldpath.ManagedFields, err error) {
78-
managedFields = make(fieldpath.ManagedFields, len(encodedManagedFields))
83+
func decodeManagedFields(encodedManagedFields []metav1.ManagedFieldsEntry) (managed Managed, err error) {
84+
managed.Fields = make(fieldpath.ManagedFields, len(encodedManagedFields))
85+
managed.Times = make(map[string]*metav1.Time, len(encodedManagedFields))
7986
for _, encodedVersionedSet := range encodedManagedFields {
8087
manager, err := BuildManagerIdentifier(&encodedVersionedSet)
8188
if err != nil {
82-
return nil, fmt.Errorf("error decoding manager from %v: %v", encodedVersionedSet, err)
89+
return Managed{}, fmt.Errorf("error decoding manager from %v: %v", encodedVersionedSet, err)
8390
}
84-
managedFields[manager], err = decodeVersionedSet(&encodedVersionedSet)
91+
managed.Fields[manager], err = decodeVersionedSet(&encodedVersionedSet)
8592
if err != nil {
86-
return nil, fmt.Errorf("error decoding versioned set from %v: %v", encodedVersionedSet, err)
93+
return Managed{}, fmt.Errorf("error decoding versioned set from %v: %v", encodedVersionedSet, err)
8794
}
95+
managed.Times[manager] = encodedVersionedSet.Time
8896
}
89-
return managedFields, nil
97+
return managed, nil
9098
}
9199

92100
// BuildManagerIdentifier creates a manager identifier string from a ManagedFieldsEntry
@@ -96,11 +104,13 @@ func BuildManagerIdentifier(encodedManager *metav1.ManagedFieldsEntry) (manager
96104
// Never include the fields in the manager identifier
97105
encodedManagerCopy.Fields = nil
98106

99-
// For appliers, don't include the APIVersion or Time in the manager identifier,
107+
// Never include the time in the manager identifier
108+
encodedManagerCopy.Time = nil
109+
110+
// For appliers, don't include the APIVersion in the manager identifier,
100111
// so it will always have the same manager identifier each time it applied.
101112
if encodedManager.Operation == metav1.ManagedFieldsOperationApply {
102113
encodedManagerCopy.APIVersion = ""
103-
encodedManagerCopy.Time = nil
104114
}
105115

106116
// Use the remaining fields to build the manager identifier
@@ -126,21 +136,17 @@ func decodeVersionedSet(encodedVersionedSet *metav1.ManagedFieldsEntry) (version
126136

127137
// encodeManagedFields converts ManagedFields from the format used by
128138
// sigs.k8s.io/structured-merge-diff to the wire format (api format)
129-
func encodeManagedFields(managedFields fieldpath.ManagedFields) (encodedManagedFields []metav1.ManagedFieldsEntry, err error) {
130-
// Sort the keys so a predictable order will be used.
131-
managers := []string{}
132-
for manager := range managedFields {
133-
managers = append(managers, manager)
134-
}
135-
sort.Strings(managers)
136-
139+
func encodeManagedFields(managed Managed) (encodedManagedFields []metav1.ManagedFieldsEntry, err error) {
137140
encodedManagedFields = []metav1.ManagedFieldsEntry{}
138-
for _, manager := range managers {
139-
versionedSet := managedFields[manager]
141+
for manager := range managed.Fields {
142+
versionedSet := managed.Fields[manager]
140143
v, err := encodeManagerVersionedSet(manager, versionedSet)
141144
if err != nil {
142145
return nil, fmt.Errorf("error encoding versioned set for %v: %v", manager, err)
143146
}
147+
if t, ok := managed.Times[manager]; ok {
148+
v.Time = t
149+
}
144150
encodedManagedFields = append(encodedManagedFields, *v)
145151
}
146152
return sortEncodedManagedFields(encodedManagedFields)

staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/managedfields_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ manager: foo
169169
operation: Update
170170
time: "2001-02-03T04:05:06Z"
171171
`,
172-
expected: "{\"manager\":\"foo\",\"operation\":\"Update\",\"apiVersion\":\"v1\",\"time\":\"2001-02-03T04:05:06Z\"}",
172+
expected: "{\"manager\":\"foo\",\"operation\":\"Update\",\"apiVersion\":\"v1\"}",
173173
},
174174
{
175175
managedFieldsEntry: `

test/integration/apiserver/apply/apply_test.go

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,21 @@ func TestApplyManagedFields(t *testing.T) {
299299
t.Fatalf("Failed to create object using Apply patch: %v", err)
300300
}
301301

302+
_, err = client.CoreV1().RESTClient().Patch(types.MergePatchType).
303+
Namespace("default").
304+
Resource("configmaps").
305+
Name("test-cm").
306+
Param("fieldManager", "updater").
307+
Body([]byte(`{"data":{"new-key": "value"}}`)).Do().Get()
308+
if err != nil {
309+
t.Fatalf("Failed to patch object: %v", err)
310+
}
311+
312+
// Sleep for one second to make sure that the times of each update operation is different.
313+
// This will let us check that update entries with the same manager name are grouped together,
314+
// and that the most recent update time is recorded in the grouped entry.
315+
time.Sleep(1 * time.Second)
316+
302317
_, err = client.CoreV1().RESTClient().Patch(types.MergePatchType).
303318
Namespace("default").
304319
Resource("configmaps").
@@ -340,6 +355,7 @@ func TestApplyManagedFields(t *testing.T) {
340355
"manager": "apply_test",
341356
"operation": "Apply",
342357
"apiVersion": "v1",
358+
"time": "` + accessor.GetManagedFields()[0].Time.UTC().Format(time.RFC3339) + `",
343359
"fields": {
344360
"f:metadata": {
345361
"f:labels": {
@@ -355,20 +371,26 @@ func TestApplyManagedFields(t *testing.T) {
355371
"time": "` + accessor.GetManagedFields()[1].Time.UTC().Format(time.RFC3339) + `",
356372
"fields": {
357373
"f:data": {
358-
"f:key": {}
374+
"f:key": {},
375+
"f:new-key": {}
359376
}
360377
}
361378
}
362379
]
363380
},
364381
"data": {
365-
"key": "new value"
382+
"key": "new value",
383+
"new-key": "value"
366384
}
367385
}`)
368386

369387
if string(expected) != string(actual) {
370388
t.Fatalf("Expected:\n%v\nGot:\n%v", string(expected), string(actual))
371389
}
390+
391+
if accessor.GetManagedFields()[0].Time.UTC().Format(time.RFC3339) == accessor.GetManagedFields()[1].Time.UTC().Format(time.RFC3339) {
392+
t.Fatalf("Expected times to be different but got:\n%v", string(actual))
393+
}
372394
}
373395

374396
// TestApplyRemovesEmptyManagedFields there are no empty managers in managedFields

0 commit comments

Comments
 (0)