From 9ac127408d48aa0876330c79c933d4f1cd603761 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kevin=20Wiesm=C3=BCller?= Date: Fri, 1 Mar 2019 12:51:01 +0100 Subject: [PATCH] implement sorting of managedFields --- .../handlers/fieldmanager/internal/BUILD | 1 + .../fieldmanager/internal/managedfields.go | 18 ++++ .../internal/managedfields_test.go | 102 ++++++++++++++++-- .../handlers/fieldmanager/internal/sort.go | 61 +++++++++++ 4 files changed, 172 insertions(+), 10 deletions(-) create mode 100644 staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/sort.go diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/BUILD b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/BUILD index 64d62276e9b..c7520962099 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/BUILD +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/BUILD @@ -8,6 +8,7 @@ go_library( "gvkparser.go", "managedfields.go", "pathelement.go", + "sort.go", "typeconverter.go", "versionconverter.go", ], diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/managedfields.go b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/managedfields.go index 4c2ee77639f..76b2c1fb83f 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/managedfields.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/managedfields.go @@ -150,6 +150,24 @@ func encodeManagedFields(managedFields fieldpath.ManagedFields) (encodedManagedF } func sortEncodedManagedFields(encodedManagedFields []metav1.ManagedFieldsEntry) (sortedManagedFields []metav1.ManagedFieldsEntry, err error) { + operation := func(p, q metav1.ManagedFieldsEntry) bool { + return p.Operation < q.Operation + } + timestamp := func(p, q metav1.ManagedFieldsEntry) bool { + if p.Time == nil || q.Time == nil { + return false + } + return q.Time.Before(p.Time) + } + manager := func(p, q metav1.ManagedFieldsEntry) bool { + return p.Manager < q.Manager + } + + sorter := &managedFieldsSorter{ + less: []managedFieldsLessFunc{operation, timestamp, manager}, + } + + sorter.Sort(encodedManagedFields) return encodedManagedFields, nil } diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/managedfields_test.go b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/managedfields_test.go index 1caac623996..c8c59f26c76 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/managedfields_test.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/managedfields_test.go @@ -17,8 +17,10 @@ limitations under the License. package internal import ( + "fmt" "reflect" "testing" + "time" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -29,7 +31,14 @@ import ( // (api format) to the format used by sigs.k8s.io/structured-merge-diff and back func TestRoundTripManagedFields(t *testing.T) { tests := []string{ - `- apiVersion: v1 + `- apiVersion: v1beta1 + fields: + i:5: + f:i: {} + manager: foo + operation: Update + time: "2011-12-13T14:15:16Z" +- apiVersion: v1 fields: v:3: f:alsoPi: {} @@ -40,13 +49,6 @@ func TestRoundTripManagedFields(t *testing.T) { manager: foo operation: Update time: "2001-02-03T04:05:06Z" -- apiVersion: v1beta1 - fields: - i:5: - f:i: {} - manager: foo - operation: Update - time: "2011-12-13T14:15:16Z" `, `- apiVersion: v1 fields: @@ -205,13 +207,85 @@ func TestSortEncodedManagedFields(t *testing.T) { managedFields []metav1.ManagedFieldsEntry expected []metav1.ManagedFieldsEntry }{ + { + name: "empty", + managedFields: []metav1.ManagedFieldsEntry{}, + expected: []metav1.ManagedFieldsEntry{}, + }, + { + name: "nil", + managedFields: nil, + expected: nil, + }, { name: "remains untouched", managedFields: []metav1.ManagedFieldsEntry{ - {Manager: "a", Operation: metav1.ManagedFieldsOperationApply, Time: &metav1.Time{}}, + {Manager: "a", Operation: metav1.ManagedFieldsOperationApply, Time: nil}, + {Manager: "a", Operation: metav1.ManagedFieldsOperationUpdate, Time: parseTimeOrPanic("2001-01-01T01:00:00Z")}, }, expected: []metav1.ManagedFieldsEntry{ - {Manager: "a", Operation: metav1.ManagedFieldsOperationApply, Time: &metav1.Time{}}, + {Manager: "a", Operation: metav1.ManagedFieldsOperationApply, Time: nil}, + {Manager: "a", Operation: metav1.ManagedFieldsOperationUpdate, Time: parseTimeOrPanic("2001-01-01T01:00:00Z")}, + }, + }, + { + name: "apply first", + managedFields: []metav1.ManagedFieldsEntry{ + {Manager: "a", Operation: metav1.ManagedFieldsOperationUpdate, Time: parseTimeOrPanic("2001-01-01T01:00:00Z")}, + {Manager: "a", Operation: metav1.ManagedFieldsOperationApply, Time: nil}, + }, + expected: []metav1.ManagedFieldsEntry{ + {Manager: "a", Operation: metav1.ManagedFieldsOperationApply, Time: nil}, + {Manager: "a", Operation: metav1.ManagedFieldsOperationUpdate, Time: parseTimeOrPanic("2001-01-01T01:00:00Z")}, + }, + }, + { + name: "newest first", + managedFields: []metav1.ManagedFieldsEntry{ + {Manager: "c", Operation: metav1.ManagedFieldsOperationUpdate, Time: parseTimeOrPanic("2001-01-01T01:00:00Z")}, + {Manager: "a", Operation: metav1.ManagedFieldsOperationApply, Time: nil}, + {Manager: "c", Operation: metav1.ManagedFieldsOperationUpdate, Time: parseTimeOrPanic("2002-01-01T01:00:00Z")}, + {Manager: "b", Operation: metav1.ManagedFieldsOperationApply, Time: nil}, + }, + expected: []metav1.ManagedFieldsEntry{ + {Manager: "a", Operation: metav1.ManagedFieldsOperationApply, Time: nil}, + {Manager: "b", Operation: metav1.ManagedFieldsOperationApply, Time: nil}, + {Manager: "c", Operation: metav1.ManagedFieldsOperationUpdate, Time: parseTimeOrPanic("2002-01-01T01:00:00Z")}, + {Manager: "c", Operation: metav1.ManagedFieldsOperationUpdate, Time: parseTimeOrPanic("2001-01-01T01:00:00Z")}, + }, + }, + { + name: "manager last", + managedFields: []metav1.ManagedFieldsEntry{ + {Manager: "c", Operation: metav1.ManagedFieldsOperationUpdate, Time: parseTimeOrPanic("2001-01-01T01:00:00Z")}, + {Manager: "a", Operation: metav1.ManagedFieldsOperationApply, Time: nil}, + {Manager: "d", Operation: metav1.ManagedFieldsOperationUpdate, Time: parseTimeOrPanic("2002-01-01T01:00:00Z")}, + {Manager: "b", Operation: metav1.ManagedFieldsOperationApply, Time: nil}, + }, + expected: []metav1.ManagedFieldsEntry{ + {Manager: "a", Operation: metav1.ManagedFieldsOperationApply, Time: nil}, + {Manager: "b", Operation: metav1.ManagedFieldsOperationApply, Time: nil}, + {Manager: "d", Operation: metav1.ManagedFieldsOperationUpdate, Time: parseTimeOrPanic("2002-01-01T01:00:00Z")}, + {Manager: "c", Operation: metav1.ManagedFieldsOperationUpdate, Time: parseTimeOrPanic("2001-01-01T01:00:00Z")}, + }, + }, + { + name: "manager sorted", + managedFields: []metav1.ManagedFieldsEntry{ + {Manager: "c", Operation: metav1.ManagedFieldsOperationUpdate, Time: parseTimeOrPanic("2001-01-01T01:00:00Z")}, + {Manager: "a", Operation: metav1.ManagedFieldsOperationApply, Time: nil}, + {Manager: "f", Operation: metav1.ManagedFieldsOperationUpdate, Time: parseTimeOrPanic("2002-01-01T01:00:00Z")}, + {Manager: "d", Operation: metav1.ManagedFieldsOperationUpdate, Time: parseTimeOrPanic("2002-01-01T01:00:00Z")}, + {Manager: "e", Operation: metav1.ManagedFieldsOperationUpdate, Time: parseTimeOrPanic("2002-01-01T01:00:00Z")}, + {Manager: "b", Operation: metav1.ManagedFieldsOperationApply, Time: nil}, + }, + expected: []metav1.ManagedFieldsEntry{ + {Manager: "a", Operation: metav1.ManagedFieldsOperationApply, Time: nil}, + {Manager: "b", Operation: metav1.ManagedFieldsOperationApply, Time: nil}, + {Manager: "d", Operation: metav1.ManagedFieldsOperationUpdate, Time: parseTimeOrPanic("2002-01-01T01:00:00Z")}, + {Manager: "e", Operation: metav1.ManagedFieldsOperationUpdate, Time: parseTimeOrPanic("2002-01-01T01:00:00Z")}, + {Manager: "f", Operation: metav1.ManagedFieldsOperationUpdate, Time: parseTimeOrPanic("2002-01-01T01:00:00Z")}, + {Manager: "c", Operation: metav1.ManagedFieldsOperationUpdate, Time: parseTimeOrPanic("2001-01-01T01:00:00Z")}, }, }, } @@ -228,3 +302,11 @@ func TestSortEncodedManagedFields(t *testing.T) { }) } } + +func parseTimeOrPanic(s string) *metav1.Time { + t, err := time.Parse(time.RFC3339, s) + if err != nil { + panic(fmt.Sprintf("failed to parse time %s, got: %v", s, err)) + } + return &metav1.Time{Time: t.UTC()} +} diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/sort.go b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/sort.go new file mode 100644 index 00000000000..6e90f27498c --- /dev/null +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/sort.go @@ -0,0 +1,61 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package internal + +import ( + "sort" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +type managedFieldsLessFunc func(p, q metav1.ManagedFieldsEntry) bool + +type managedFieldsSorter struct { + fields []metav1.ManagedFieldsEntry + less []managedFieldsLessFunc +} + +func (s *managedFieldsSorter) Sort(fields []metav1.ManagedFieldsEntry) { + s.fields = fields + sort.Sort(s) +} + +// Len is the amount of managedFields to sort +func (s *managedFieldsSorter) Len() int { + return len(s.fields) +} + +// Swap is part of sort.Interface. +func (s *managedFieldsSorter) Swap(p, q int) { + s.fields[p], s.fields[q] = s.fields[q], s.fields[p] +} + +// Less is part of sort.Interface +func (s *managedFieldsSorter) Less(p, q int) bool { + a, b := s.fields[p], s.fields[q] + var k int + for k = 0; k < len(s.less)-1; k++ { + less := s.less[k] + switch { + case less(a, b): + return true + case less(b, a): + return false + } + } + return s.less[k](a, b) +}