diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/capmanagers_test.go b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/capmanagers_test.go index 221dc9eae06..0fa20913886 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/capmanagers_test.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/capmanagers_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package fieldmanager +package fieldmanager_test import ( "bytes" @@ -29,27 +29,29 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager" + "k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/fieldmanagertest" "sigs.k8s.io/structured-merge-diff/v4/fieldpath" ) type fakeManager struct{} -var _ Manager = &fakeManager{} +var _ fieldmanager.Manager = &fakeManager{} -func (*fakeManager) Update(_, newObj runtime.Object, managed Managed, _ string) (runtime.Object, Managed, error) { +func (*fakeManager) Update(_, newObj runtime.Object, managed fieldmanager.Managed, _ string) (runtime.Object, fieldmanager.Managed, error) { return newObj, managed, nil } -func (*fakeManager) Apply(_, _ runtime.Object, _ Managed, _ string, _ bool) (runtime.Object, Managed, error) { +func (*fakeManager) Apply(_, _ runtime.Object, _ fieldmanager.Managed, _ string, _ bool) (runtime.Object, fieldmanager.Managed, error) { panic("not implemented") return nil, nil, nil } func TestCapManagersManagerMergesEntries(t *testing.T) { - f := NewTestFieldManager(schema.FromAPIVersionAndKind("v1", "Pod"), + f := fieldmanagertest.NewTestFieldManager(schema.FromAPIVersionAndKind("v1", "Pod"), "", - func(m Manager) Manager { - return NewCapManagersManager(m, 3) + func(m fieldmanager.Manager) fieldmanager.Manager { + return fieldmanager.NewCapManagersManager(m, 3) }) podWithLabels := func(labels ...string) runtime.Object { @@ -112,10 +114,10 @@ func TestCapManagersManagerMergesEntries(t *testing.T) { } func TestCapUpdateManagers(t *testing.T) { - f := NewTestFieldManager(schema.FromAPIVersionAndKind("v1", "Pod"), + f := fieldmanagertest.NewTestFieldManager(schema.FromAPIVersionAndKind("v1", "Pod"), "", - func(m Manager) Manager { - return NewCapManagersManager(m, 3) + func(m fieldmanager.Manager) fieldmanager.Manager { + return fieldmanager.NewCapManagersManager(m, 3) }) set := func(fields ...string) *metav1.FieldsV1 { @@ -232,12 +234,13 @@ func TestCapUpdateManagers(t *testing.T) { for _, tc := range testCases { f.Reset() - accessor, err := meta.Accessor(f.liveObj) + live := f.Live() + accessor, err := meta.Accessor(live) if err != nil { t.Fatalf("%v: couldn't get accessor: %v", tc.name, err) } accessor.SetManagedFields(tc.input) - if err := f.Update(f.liveObj, "no-op-update"); err != nil { + if err := f.Update(live, "no-op-update"); err != nil { t.Fatalf("%v: failed to do no-op update to object: %v", tc.name, err) } @@ -249,13 +252,13 @@ func TestCapUpdateManagers(t *testing.T) { } // expectIdempotence does a no-op update and ensures that managedFields doesn't change by calling capUpdateManagers. -func expectIdempotence(t *testing.T, f TestFieldManager) { +func expectIdempotence(t *testing.T, f fieldmanagertest.TestFieldManager) { before := []metav1.ManagedFieldsEntry{} for _, m := range f.ManagedFields() { before = append(before, *m.DeepCopy()) } - if err := f.Update(f.liveObj, "no-op-update"); err != nil { + if err := f.Update(f.Live(), "no-op-update"); err != nil { t.Fatalf("failed to do no-op update to object: %v", err) } @@ -265,7 +268,7 @@ func expectIdempotence(t *testing.T, f TestFieldManager) { } // expectManagesField ensures that manager m currently manages field path p. -func expectManagesField(t *testing.T, f TestFieldManager, m string, p fieldpath.Path) { +func expectManagesField(t *testing.T, f fieldmanagertest.TestFieldManager, m string, p fieldpath.Path) { for _, e := range f.ManagedFields() { if e.Manager == m { var s fieldpath.Set diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/fieldmanager_test.go b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/fieldmanager_test.go index d73b6bfc229..b4b0ad88b35 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/fieldmanager_test.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/fieldmanager_test.go @@ -14,14 +14,12 @@ See the License for the specific language governing permissions and limitations under the License. */ -package fieldmanager +package fieldmanager_test import ( - "errors" "fmt" "io/ioutil" "net/http" - "path/filepath" "reflect" "strings" "testing" @@ -36,165 +34,15 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/serializer" yamlutil "k8s.io/apimachinery/pkg/util/yaml" - "k8s.io/kube-openapi/pkg/util/proto" - prototesting "k8s.io/kube-openapi/pkg/util/proto/testing" - "sigs.k8s.io/structured-merge-diff/v4/fieldpath" - "sigs.k8s.io/structured-merge-diff/v4/merge" - "sigs.k8s.io/structured-merge-diff/v4/typed" + "k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/fieldmanagertest" + "k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal" "sigs.k8s.io/yaml" ) -var kubernetesSwaggerSchema = prototesting.Fake{ - Path: filepath.Join( - strings.Repeat(".."+string(filepath.Separator), 8), - "api", "openapi-spec", "swagger.json"), -} - -type fakeObjectConvertor struct { - converter merge.Converter - apiVersion fieldpath.APIVersion -} - -//nolint:staticcheck,ineffassign // SA4009 backwards compatibility -func (c *fakeObjectConvertor) Convert(in, out, context interface{}) error { - if typedValue, ok := in.(*typed.TypedValue); ok { - var err error - out, err = c.converter.Convert(typedValue, c.apiVersion) - return err - } - return nil -} - -func (c *fakeObjectConvertor) ConvertToVersion(in runtime.Object, _ runtime.GroupVersioner) (runtime.Object, error) { - return in, nil -} - -func (c *fakeObjectConvertor) ConvertFieldLabel(_ schema.GroupVersionKind, _, _ string) (string, string, error) { - return "", "", errors.New("not implemented") -} - -type fakeObjectDefaulter struct{} - -func (d *fakeObjectDefaulter) Default(in runtime.Object) {} - -type TestFieldManager struct { - fieldManager *FieldManager - apiVersion string - emptyObj runtime.Object - liveObj runtime.Object -} - -func NewDefaultTestFieldManager(gvk schema.GroupVersionKind) TestFieldManager { - return NewTestFieldManager(gvk, "", nil) -} - -func NewSubresourceTestFieldManager(gvk schema.GroupVersionKind) TestFieldManager { - return NewTestFieldManager(gvk, "scale", nil) -} - -func NewTestFieldManager(gvk schema.GroupVersionKind, subresource string, chainFieldManager func(Manager) Manager) TestFieldManager { - m := NewFakeOpenAPIModels() - typeConverter := NewFakeTypeConverter(m) - converter := newVersionConverter(typeConverter, &fakeObjectConvertor{}, gvk.GroupVersion()) - apiVersion := fieldpath.APIVersion(gvk.GroupVersion().String()) - objectConverter := &fakeObjectConvertor{converter, apiVersion} - f, err := NewStructuredMergeManager( - typeConverter, - objectConverter, - &fakeObjectDefaulter{}, - gvk.GroupVersion(), - gvk.GroupVersion(), - nil, - ) - if err != nil { - panic(err) - } - live := &unstructured.Unstructured{} - live.SetKind(gvk.Kind) - live.SetAPIVersion(gvk.GroupVersion().String()) - f = NewLastAppliedUpdater( - NewLastAppliedManager( - NewProbabilisticSkipNonAppliedManager( - NewBuildManagerInfoManager( - NewManagedFieldsUpdater( - NewStripMetaManager(f), - ), gvk.GroupVersion(), subresource, - ), &fakeObjectCreater{gvk: gvk}, gvk, DefaultTrackOnCreateProbability, - ), typeConverter, objectConverter, gvk.GroupVersion(), - ), - ) - if chainFieldManager != nil { - f = chainFieldManager(f) - } - return TestFieldManager{ - fieldManager: NewFieldManager(f, subresource), - apiVersion: gvk.GroupVersion().String(), - emptyObj: live, - liveObj: live.DeepCopyObject(), - } -} - -func NewFakeTypeConverter(m proto.Models) TypeConverter { - tc, err := NewTypeConverter(m, false) - if err != nil { - panic(fmt.Sprintf("Failed to build TypeConverter: %v", err)) - } - return tc -} - -func NewFakeOpenAPIModels() proto.Models { - d, err := kubernetesSwaggerSchema.OpenAPISchema() - if err != nil { - panic(err) - } - m, err := proto.NewOpenAPIData(d) - if err != nil { - panic(err) - } - return m -} - -func (f *TestFieldManager) APIVersion() string { - return f.apiVersion -} - -func (f *TestFieldManager) Reset() { - f.liveObj = f.emptyObj.DeepCopyObject() -} - -func (f *TestFieldManager) Get() runtime.Object { - return f.liveObj.DeepCopyObject() -} - -func (f *TestFieldManager) Apply(obj runtime.Object, manager string, force bool) error { - out, err := f.fieldManager.Apply(f.liveObj, obj, manager, force) - if err == nil { - f.liveObj = out - } - return err -} - -func (f *TestFieldManager) Update(obj runtime.Object, manager string) error { - out, err := f.fieldManager.Update(f.liveObj, obj, manager) - if err == nil { - f.liveObj = out - } - return err -} - -func (f *TestFieldManager) ManagedFields() []metav1.ManagedFieldsEntry { - accessor, err := meta.Accessor(f.liveObj) - if err != nil { - panic(fmt.Errorf("couldn't get accessor: %v", err)) - } - - return accessor.GetManagedFields() -} - // TestUpdateApplyConflict tests that applying to an object, which // wasn't created by apply, will give conflicts func TestUpdateApplyConflict(t *testing.T) { - f := NewDefaultTestFieldManager(schema.FromAPIVersionAndKind("apps/v1", "Deployment")) + f := fieldmanagertest.NewDefaultTestFieldManager(schema.FromAPIVersionAndKind("apps/v1", "Deployment")) patch := []byte(`{ "apiVersion": "apps/v1", @@ -255,7 +103,7 @@ func TestUpdateApplyConflict(t *testing.T) { } func TestApplyStripsFields(t *testing.T) { - f := NewDefaultTestFieldManager(schema.FromAPIVersionAndKind("apps/v1", "Deployment")) + f := fieldmanagertest.NewDefaultTestFieldManager(schema.FromAPIVersionAndKind("apps/v1", "Deployment")) newObj := &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -287,7 +135,7 @@ func TestApplyStripsFields(t *testing.T) { } func TestVersionCheck(t *testing.T) { - f := NewDefaultTestFieldManager(schema.FromAPIVersionAndKind("apps/v1", "Deployment")) + f := fieldmanagertest.NewDefaultTestFieldManager(schema.FromAPIVersionAndKind("apps/v1", "Deployment")) appliedObj := &unstructured.Unstructured{Object: map[string]interface{}{}} if err := yaml.Unmarshal([]byte(`{ @@ -327,7 +175,7 @@ func TestVersionCheck(t *testing.T) { } } func TestVersionCheckDoesNotPanic(t *testing.T) { - f := NewDefaultTestFieldManager(schema.FromAPIVersionAndKind("apps/v1", "Deployment")) + f := fieldmanagertest.NewDefaultTestFieldManager(schema.FromAPIVersionAndKind("apps/v1", "Deployment")) appliedObj := &unstructured.Unstructured{Object: map[string]interface{}{}} if err := yaml.Unmarshal([]byte(`{ @@ -366,7 +214,7 @@ func TestVersionCheckDoesNotPanic(t *testing.T) { } func TestApplyDoesNotStripLabels(t *testing.T) { - f := NewDefaultTestFieldManager(schema.FromAPIVersionAndKind("v1", "Pod")) + f := fieldmanagertest.NewDefaultTestFieldManager(schema.FromAPIVersionAndKind("v1", "Pod")) appliedObj := &unstructured.Unstructured{Object: map[string]interface{}{}} if err := yaml.Unmarshal([]byte(`{ @@ -420,7 +268,7 @@ func TestApplyNewObject(t *testing.T) { for _, test := range tests { t.Run(test.gvk.String(), func(t *testing.T) { - f := NewDefaultTestFieldManager(test.gvk) + f := fieldmanagertest.NewDefaultTestFieldManager(test.gvk) appliedObj := &unstructured.Unstructured{Object: map[string]interface{}{}} if err := yaml.Unmarshal(test.obj, &appliedObj.Object); err != nil { @@ -458,7 +306,7 @@ func BenchmarkNewObject(b *testing.B) { } for _, test := range tests { b.Run(test.gvk.Kind, func(b *testing.B) { - f := NewDefaultTestFieldManager(test.gvk) + f := fieldmanagertest.NewDefaultTestFieldManager(test.gvk) decoder := serializer.NewCodecFactory(scheme).UniversalDecoder(test.gvk.GroupVersion()) newObj, err := runtime.Decode(decoder, test.obj) @@ -564,8 +412,7 @@ func BenchmarkConvertObjectToTyped(b *testing.B) { for _, test := range tests { b.Run(test.gvk.Kind, func(b *testing.B) { decoder := serializer.NewCodecFactory(scheme).UniversalDecoder(test.gvk.GroupVersion()) - m := NewFakeOpenAPIModels() - typeConverter := NewFakeTypeConverter(m) + typeConverter := fieldmanagertest.NewBuiltinTypeConverter() structured, err := runtime.Decode(decoder, test.obj) if err != nil { @@ -626,8 +473,7 @@ func BenchmarkCompare(b *testing.B) { for _, test := range tests { b.Run(test.gvk.Kind, func(b *testing.B) { decoder := serializer.NewCodecFactory(scheme).UniversalDecoder(test.gvk.GroupVersion()) - m := NewFakeOpenAPIModels() - typeConverter := NewFakeTypeConverter(m) + typeConverter := fieldmanagertest.NewBuiltinTypeConverter() structured, err := runtime.Decode(decoder, test.obj) if err != nil { @@ -677,7 +523,7 @@ func BenchmarkCompare(b *testing.B) { } func BenchmarkRepeatedUpdate(b *testing.B) { - f := NewDefaultTestFieldManager(schema.FromAPIVersionAndKind("v1", "Pod")) + f := fieldmanagertest.NewDefaultTestFieldManager(schema.FromAPIVersionAndKind("v1", "Pod")) podBytes := getObjectBytes("pod.yaml") var obj *corev1.Pod @@ -716,7 +562,7 @@ func BenchmarkRepeatedUpdate(b *testing.B) { } func TestApplyFailsWithManagedFields(t *testing.T) { - f := NewDefaultTestFieldManager(schema.FromAPIVersionAndKind("v1", "Pod")) + f := fieldmanagertest.NewDefaultTestFieldManager(schema.FromAPIVersionAndKind("v1", "Pod")) appliedObj := &unstructured.Unstructured{Object: map[string]interface{}{}} if err := yaml.Unmarshal([]byte(`{ @@ -741,7 +587,7 @@ func TestApplyFailsWithManagedFields(t *testing.T) { } func TestApplySuccessWithNoManagedFields(t *testing.T) { - f := NewDefaultTestFieldManager(schema.FromAPIVersionAndKind("v1", "Pod")) + f := fieldmanagertest.NewDefaultTestFieldManager(schema.FromAPIVersionAndKind("v1", "Pod")) appliedObj := &unstructured.Unstructured{Object: map[string]interface{}{}} if err := yaml.Unmarshal([]byte(`{ @@ -764,7 +610,7 @@ func TestApplySuccessWithNoManagedFields(t *testing.T) { // Run an update and apply, and make sure that nothing has changed. func TestNoOpChanges(t *testing.T) { - f := NewDefaultTestFieldManager(schema.FromAPIVersionAndKind("v1", "Pod")) + f := fieldmanagertest.NewDefaultTestFieldManager(schema.FromAPIVersionAndKind("v1", "Pod")) obj := &unstructured.Unstructured{Object: map[string]interface{}{}} if err := yaml.Unmarshal([]byte(`{ @@ -782,40 +628,40 @@ func TestNoOpChanges(t *testing.T) { if err := f.Apply(obj.DeepCopyObject(), "fieldmanager_test_apply", false); err != nil { t.Fatalf("failed to apply object: %v", err) } - before := f.liveObj.DeepCopyObject() + before := f.Live() // Wait to make sure the timestamp is different time.Sleep(time.Second) // Applying with a different fieldmanager will create an entry.. if err := f.Apply(obj.DeepCopyObject(), "fieldmanager_test_apply_other", false); err != nil { t.Fatalf("failed to update object: %v", err) } - if reflect.DeepEqual(before, f.liveObj) { - t.Fatalf("Applying no-op apply with new manager didn't change object: \n%v", f.liveObj) + if reflect.DeepEqual(before, f.Live()) { + t.Fatalf("Applying no-op apply with new manager didn't change object: \n%v", f.Live()) } - before = f.liveObj.DeepCopyObject() + before = f.Live() // Wait to make sure the timestamp is different time.Sleep(time.Second) if err := f.Update(obj.DeepCopyObject(), "fieldmanager_test_update"); err != nil { t.Fatalf("failed to update object: %v", err) } - if !reflect.DeepEqual(before, f.liveObj) { - t.Fatalf("No-op update has changed the object:\n%v\n---\n%v", before, f.liveObj) + if !reflect.DeepEqual(before, f.Live()) { + t.Fatalf("No-op update has changed the object:\n%v\n---\n%v", before, f.Live()) } - before = f.liveObj.DeepCopyObject() + before = f.Live() // Wait to make sure the timestamp is different time.Sleep(time.Second) if err := f.Apply(obj.DeepCopyObject(), "fieldmanager_test_apply", true); err != nil { t.Fatalf("failed to re-apply object: %v", err) } - if !reflect.DeepEqual(before, f.liveObj) { - t.Fatalf("No-op apply has changed the object:\n%v\n---\n%v", before, f.liveObj) + if !reflect.DeepEqual(before, f.Live()) { + t.Fatalf("No-op apply has changed the object:\n%v\n---\n%v", before, f.Live()) } } // Tests that one can reset the managedFields by sending either an empty // list func TestResetManagedFieldsEmptyList(t *testing.T) { - f := NewDefaultTestFieldManager(schema.FromAPIVersionAndKind("v1", "Pod")) + f := fieldmanagertest.NewDefaultTestFieldManager(schema.FromAPIVersionAndKind("v1", "Pod")) obj := &unstructured.Unstructured{Object: map[string]interface{}{}} if err := yaml.Unmarshal([]byte(`{ @@ -856,7 +702,7 @@ func TestResetManagedFieldsEmptyList(t *testing.T) { // Tests that one can reset the managedFields by sending either a list with one empty item. func TestResetManagedFieldsEmptyItem(t *testing.T) { - f := NewDefaultTestFieldManager(schema.FromAPIVersionAndKind("v1", "Pod")) + f := fieldmanagertest.NewDefaultTestFieldManager(schema.FromAPIVersionAndKind("v1", "Pod")) obj := &unstructured.Unstructured{Object: map[string]interface{}{}} if err := yaml.Unmarshal([]byte(`{ @@ -896,7 +742,7 @@ func TestResetManagedFieldsEmptyItem(t *testing.T) { } func TestServerSideApplyWithInvalidLastApplied(t *testing.T) { - f := NewDefaultTestFieldManager(schema.FromAPIVersionAndKind("apps/v1", "Deployment")) + f := fieldmanagertest.NewDefaultTestFieldManager(schema.FromAPIVersionAndKind("apps/v1", "Deployment")) // create object with client-side apply newObj := &unstructured.Unstructured{Object: map[string]interface{}{}} @@ -915,7 +761,7 @@ spec: } invalidLastApplied := "invalid-object" - if err := setLastApplied(newObj, invalidLastApplied); err != nil { + if err := internal.SetLastApplied(newObj, invalidLastApplied); err != nil { t.Errorf("failed to set last applied: %v", err) } @@ -923,7 +769,7 @@ spec: t.Errorf("failed to update object: %v", err) } - lastApplied, err := getLastApplied(f.liveObj) + lastApplied, err := getLastApplied(f.Live()) if err != nil { t.Errorf("failed to get last applied: %v", err) } @@ -951,7 +797,7 @@ spec: t.Errorf("expected conflict when applying with invalid last-applied annotation, but got no error for object: \n%+v", appliedObj) } - lastApplied, err = getLastApplied(f.liveObj) + lastApplied, err = getLastApplied(f.Live()) if err != nil { t.Errorf("failed to get last applied: %v", err) } @@ -964,7 +810,7 @@ spec: t.Errorf("failed to force server-side apply with: %v", err) } - lastApplied, err = getLastApplied(f.liveObj) + lastApplied, err = getLastApplied(f.Live()) if err != nil { t.Errorf("failed to get last applied: %v", err) } @@ -975,7 +821,7 @@ spec: } func TestInteropForClientSideApplyAndServerSideApply(t *testing.T) { - f := NewDefaultTestFieldManager(schema.FromAPIVersionAndKind("apps/v1", "Deployment")) + f := fieldmanagertest.NewDefaultTestFieldManager(schema.FromAPIVersionAndKind("apps/v1", "Deployment")) // create object with client-side apply newObj := &unstructured.Unstructured{Object: map[string]interface{}{}} @@ -1010,7 +856,7 @@ spec: if err := f.Update(newObj, "kubectl-client-side-apply-test"); err != nil { t.Errorf("failed to update object: %v", err) } - lastApplied, err := getLastApplied(f.liveObj) + lastApplied, err := getLastApplied(f.Live()) if err != nil { t.Errorf("failed to get last applied: %v", err) } @@ -1049,7 +895,7 @@ spec: t.Errorf("error applying object: %v", err) } - lastApplied, err = getLastApplied(f.liveObj) + lastApplied, err = getLastApplied(f.Live()) if err != nil { t.Errorf("failed to get last applied: %v", err) } @@ -1059,7 +905,7 @@ spec: } func TestNoTrackManagedFieldsForClientSideApply(t *testing.T) { - f := NewDefaultTestFieldManager(schema.FromAPIVersionAndKind("apps/v1", "Deployment")) + f := fieldmanagertest.NewDefaultTestFieldManager(schema.FromAPIVersionAndKind("apps/v1", "Deployment")) // create object newObj := &unstructured.Unstructured{Object: map[string]interface{}{}} @@ -1131,7 +977,7 @@ spec: if m := f.ManagedFields(); len(m) != 0 { t.Errorf("expected to continue to not track managed fields, but got: %v", m) } - lastApplied, err := getLastApplied(f.liveObj) + lastApplied, err := getLastApplied(f.Live()) if err != nil { t.Errorf("failed to get last applied: %v", err) } @@ -1188,7 +1034,7 @@ spec: if m := f.ManagedFields(); len(m) == 0 { t.Errorf("expected to track managed fields, but got: %v", m) } - lastApplied, err = getLastApplied(f.liveObj) + lastApplied, err = getLastApplied(f.Live()) if err != nil { t.Errorf("failed to get last applied: %v", err) } @@ -1218,7 +1064,7 @@ func setLastAppliedFromEncoded(obj runtime.Object, lastApplied []byte) error { if err != nil { return err } - return setLastApplied(obj, lastAppliedJSON) + return internal.SetLastApplied(obj, lastAppliedJSON) } func getLastApplied(obj runtime.Object) (string, error) { @@ -1239,7 +1085,7 @@ func getLastApplied(obj runtime.Object) (string, error) { } func TestUpdateViaSubresources(t *testing.T) { - f := NewSubresourceTestFieldManager(schema.FromAPIVersionAndKind("v1", "Pod")) + f := fieldmanagertest.NewTestFieldManager(schema.FromAPIVersionAndKind("v1", "Pod"), "scale", nil) obj := &unstructured.Unstructured{Object: map[string]interface{}{}} if err := yaml.Unmarshal([]byte(`{ @@ -1294,7 +1140,7 @@ func TestUpdateViaSubresources(t *testing.T) { // Ensures that a no-op Apply does not mutate managed fields func TestApplyDoesNotChangeManagedFields(t *testing.T) { originalManagedFields := []metav1.ManagedFieldsEntry{} - f := NewDefaultTestFieldManager( + f := fieldmanagertest.NewDefaultTestFieldManager( schema.FromAPIVersionAndKind("apps/v1", "Deployment")) newObj := &unstructured.Unstructured{ Object: map[string]interface{}{}, @@ -1388,7 +1234,7 @@ func TestApplyDoesNotChangeManagedFields(t *testing.T) { // Ensures that a no-op Update does not mutate managed fields func TestUpdateDoesNotChangeManagedFields(t *testing.T) { originalManagedFields := []metav1.ManagedFieldsEntry{} - f := NewDefaultTestFieldManager( + f := fieldmanagertest.NewDefaultTestFieldManager( schema.FromAPIVersionAndKind("apps/v1", "Deployment")) newObj := &unstructured.Unstructured{ Object: map[string]interface{}{}, @@ -1460,7 +1306,7 @@ func TestUpdateDoesNotChangeManagedFields(t *testing.T) { // This test makes sure that the liveObject during a patch does not mutate // its managed fields. func TestLiveObjectManagedFieldsNotRemoved(t *testing.T) { - f := NewDefaultTestFieldManager( + f := fieldmanagertest.NewDefaultTestFieldManager( schema.FromAPIVersionAndKind("apps/v1", "Deployment")) newObj := &unstructured.Unstructured{ Object: map[string]interface{}{}, @@ -1518,7 +1364,7 @@ func TestLiveObjectManagedFieldsNotRemoved(t *testing.T) { t.Fatalf("failed to apply object: %v", err) } - originalLiveObj := f.liveObj + originalLiveObj := f.Live() accessor, err := meta.Accessor(originalLiveObj) if err != nil { diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/fieldmanagertest/testfieldmanager.go b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/fieldmanagertest/testfieldmanager.go new file mode 100644 index 00000000000..e6c3283285c --- /dev/null +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/fieldmanagertest/testfieldmanager.go @@ -0,0 +1,227 @@ +/* +Copyright 2022 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 fieldmanagertest + +import ( + "errors" + "fmt" + "path/filepath" + "strings" + + "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/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager" + "k8s.io/kube-openapi/pkg/util/proto" + prototesting "k8s.io/kube-openapi/pkg/util/proto/testing" + "sigs.k8s.io/structured-merge-diff/v4/fieldpath" + "sigs.k8s.io/structured-merge-diff/v4/merge" + "sigs.k8s.io/structured-merge-diff/v4/typed" +) + +var kubernetesSwaggerSchema = prototesting.Fake{ + Path: filepath.Join( + strings.Repeat(".."+string(filepath.Separator), 8), + "api", "openapi-spec", "swagger.json"), +} + +// NewBuiltinTypeConverter creates a TypeConverter with all the built-in +// types defined, given the committed kubernetes swagger.json. +func NewBuiltinTypeConverter() fieldmanager.TypeConverter { + tc, err := fieldmanager.NewTypeConverter(newFakeOpenAPIModels(), false) + if err != nil { + panic(fmt.Errorf("Failed to build TypeConverter: %v", err)) + } + return tc +} + +func newFakeOpenAPIModels() proto.Models { + d, err := kubernetesSwaggerSchema.OpenAPISchema() + if err != nil { + panic(err) + } + m, err := proto.NewOpenAPIData(d) + if err != nil { + panic(err) + } + return m +} + +type fakeObjectConvertor struct { + converter merge.Converter + apiVersion fieldpath.APIVersion +} + +//nolint:staticcheck,ineffassign // SA4009 backwards compatibility +func (c *fakeObjectConvertor) Convert(in, out, context interface{}) error { + if typedValue, ok := in.(*typed.TypedValue); ok { + var err error + out, err = c.converter.Convert(typedValue, c.apiVersion) + return err + } + return nil +} + +func (c *fakeObjectConvertor) ConvertToVersion(in runtime.Object, _ runtime.GroupVersioner) (runtime.Object, error) { + return in, nil +} + +func (c *fakeObjectConvertor) ConvertFieldLabel(_ schema.GroupVersionKind, _, _ string) (string, string, error) { + return "", "", errors.New("not implemented") +} + +type fakeObjectDefaulter struct{} + +func (d *fakeObjectDefaulter) Default(in runtime.Object) {} + +type sameVersionConverter struct{} + +func (sameVersionConverter) Convert(object *typed.TypedValue, version fieldpath.APIVersion) (*typed.TypedValue, error) { + return object, nil +} + +func (sameVersionConverter) IsMissingVersionError(error) bool { + return false +} + +// NewFakeObjectCreater implements ObjectCreater, it can create empty +// objects (unstructured) of the given GVK. +func NewFakeObjectCreater() runtime.ObjectCreater { + return &fakeObjectCreater{} +} + +type fakeObjectCreater struct{} + +func (f *fakeObjectCreater) New(gvk schema.GroupVersionKind) (runtime.Object, error) { + u := unstructured.Unstructured{Object: map[string]interface{}{}} + u.SetAPIVersion(gvk.GroupVersion().String()) + u.SetKind(gvk.Kind) + return &u, nil +} + +// TestFieldManager is a FieldManager that can be used in test to +// simulate the behavior of Server-Side Apply and field tracking. This +// also has a few methods to get a sense of the state of the object. +// +// This TestFieldManager uses a series of "fake" objects to simulate +// some behavior which come with the limitation that you can only use +// one version since there is no version conversion logic. +// +// You can use this rather than NewDefaultTestFieldManager if you want +// to specify either a sub-resource, or a set of modified Manager to +// test them specifically. +type TestFieldManager struct { + fieldManager *fieldmanager.FieldManager + apiVersion string + emptyObj runtime.Object + liveObj runtime.Object +} + +// NewDefaultTestFieldManager returns a new TestFieldManager built for +// the given gvk, on the main resource. +func NewDefaultTestFieldManager(gvk schema.GroupVersionKind) TestFieldManager { + return NewTestFieldManager(gvk, "", nil) +} + +// NewTestFieldManager creates a new manager for the given GVK. +func NewTestFieldManager(gvk schema.GroupVersionKind, subresource string, chainFieldManager func(fieldmanager.Manager) fieldmanager.Manager) TestFieldManager { + typeConverter := NewBuiltinTypeConverter() + apiVersion := fieldpath.APIVersion(gvk.GroupVersion().String()) + objectConverter := &fakeObjectConvertor{sameVersionConverter{}, apiVersion} + f, err := fieldmanager.NewStructuredMergeManager( + typeConverter, + objectConverter, + &fakeObjectDefaulter{}, + gvk.GroupVersion(), + gvk.GroupVersion(), + nil, + ) + if err != nil { + panic(err) + } + live := &unstructured.Unstructured{} + live.SetKind(gvk.Kind) + live.SetAPIVersion(gvk.GroupVersion().String()) + f = fieldmanager.NewLastAppliedUpdater( + fieldmanager.NewLastAppliedManager( + fieldmanager.NewProbabilisticSkipNonAppliedManager( + fieldmanager.NewBuildManagerInfoManager( + fieldmanager.NewManagedFieldsUpdater( + fieldmanager.NewStripMetaManager(f), + ), gvk.GroupVersion(), subresource, + ), NewFakeObjectCreater(), gvk, fieldmanager.DefaultTrackOnCreateProbability, + ), typeConverter, objectConverter, gvk.GroupVersion(), + ), + ) + if chainFieldManager != nil { + f = chainFieldManager(f) + } + return TestFieldManager{ + fieldManager: fieldmanager.NewFieldManager(f, subresource), + apiVersion: gvk.GroupVersion().String(), + emptyObj: live, + liveObj: live.DeepCopyObject(), + } +} + +// APIVersion of the object that we're tracking. +func (f *TestFieldManager) APIVersion() string { + return f.apiVersion +} + +// Reset resets the state of the liveObject by resetting it to an empty object. +func (f *TestFieldManager) Reset() { + f.liveObj = f.emptyObj.DeepCopyObject() +} + +// Live returns a copy of the current liveObject. +func (f *TestFieldManager) Live() runtime.Object { + return f.liveObj.DeepCopyObject() +} + +// Apply applies the given object on top of the current liveObj, for the +// given manager and force flag. +func (f *TestFieldManager) Apply(obj runtime.Object, manager string, force bool) error { + out, err := f.fieldManager.Apply(f.liveObj, obj, manager, force) + if err == nil { + f.liveObj = out + } + return err +} + +// Update will updates the managed fields in the liveObj based on the +// changes performed by the update. +func (f *TestFieldManager) Update(obj runtime.Object, manager string) error { + out, err := f.fieldManager.Update(f.liveObj, obj, manager) + if err == nil { + f.liveObj = out + } + return err +} + +// ManagedFields returns the list of existing managed fields for the +// liveObj. +func (f *TestFieldManager) ManagedFields() []metav1.ManagedFieldsEntry { + accessor, err := meta.Accessor(f.liveObj) + if err != nil { + panic(fmt.Errorf("couldn't get accessor: %v", err)) + } + + return accessor.GetManagedFields() +} diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/lastapplied.go b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/lastapplied.go new file mode 100644 index 00000000000..5264c82772f --- /dev/null +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/lastapplied.go @@ -0,0 +1,45 @@ +/* +Copyright 2022 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 ( + "fmt" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/meta" + apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation" + "k8s.io/apimachinery/pkg/runtime" +) + +// SetLastApplied sets the last-applied annotation the given value in +// the object. +func SetLastApplied(obj runtime.Object, value string) error { + accessor, err := meta.Accessor(obj) + if err != nil { + panic(fmt.Sprintf("couldn't get accessor: %v", err)) + } + var annotations = accessor.GetAnnotations() + if annotations == nil { + annotations = map[string]string{} + } + annotations[corev1.LastAppliedConfigAnnotation] = value + if err := apimachineryvalidation.ValidateAnnotationsSize(annotations); err != nil { + delete(annotations, corev1.LastAppliedConfigAnnotation) + } + accessor.SetAnnotations(annotations) + return nil +} diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/lastappliedmanager_test.go b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/lastappliedmanager_test.go index a7efed2ea68..9c49b9c19c3 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/lastappliedmanager_test.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/lastappliedmanager_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package fieldmanager +package fieldmanager_test import ( "fmt" @@ -25,6 +25,7 @@ import ( apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/fieldmanagertest" "k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal" "sigs.k8s.io/structured-merge-diff/v4/fieldpath" "sigs.k8s.io/structured-merge-diff/v4/merge" @@ -43,7 +44,7 @@ type testArgs struct { // created with the client-side apply last-applied annotation // will not give conflicts func TestApplyUsingLastAppliedAnnotation(t *testing.T) { - f := NewDefaultTestFieldManager(schema.FromAPIVersionAndKind("apps/v1", "Deployment")) + f := fieldmanagertest.NewDefaultTestFieldManager(schema.FromAPIVersionAndKind("apps/v1", "Deployment")) tests := []testArgs{ { @@ -563,7 +564,7 @@ spec: } func TestServiceApply(t *testing.T) { - f := NewDefaultTestFieldManager(schema.FromAPIVersionAndKind("v1", "Service")) + f := fieldmanagertest.NewDefaultTestFieldManager(schema.FromAPIVersionAndKind("v1", "Service")) tests := []testArgs{ { @@ -674,7 +675,7 @@ spec: } func TestReplicationControllerApply(t *testing.T) { - f := NewDefaultTestFieldManager(schema.FromAPIVersionAndKind("v1", "ReplicationController")) + f := fieldmanagertest.NewDefaultTestFieldManager(schema.FromAPIVersionAndKind("v1", "ReplicationController")) tests := []testArgs{ { @@ -737,7 +738,7 @@ spec: } func TestPodApply(t *testing.T) { - f := NewDefaultTestFieldManager(schema.FromAPIVersionAndKind("v1", "Pod")) + f := fieldmanagertest.NewDefaultTestFieldManager(schema.FromAPIVersionAndKind("v1", "Pod")) tests := []testArgs{ { @@ -914,7 +915,7 @@ spec: testConflicts(t, f, tests) } -func testConflicts(t *testing.T, f TestFieldManager, tests []testArgs) { +func testConflicts(t *testing.T, f fieldmanagertest.TestFieldManager, tests []testArgs) { for i, test := range tests { t.Run(fmt.Sprintf("test %d", i), func(t *testing.T) { f.Reset() @@ -972,8 +973,8 @@ func testConflicts(t *testing.T, f TestFieldManager, tests []testArgs) { } // Eventually resource should contain applied changes - if !apiequality.Semantic.DeepDerivative(appliedObj, f.Get()) { - t.Errorf("expected equal resource: \n%#v, got: \n%#v", appliedObj, f.Get()) + if !apiequality.Semantic.DeepDerivative(appliedObj, f.Live()) { + t.Errorf("expected equal resource: \n%#v, got: \n%#v", appliedObj, f.Live()) } }) } diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/lastappliedupdater.go b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/lastappliedupdater.go index 7cd4eb1289b..e414e167f44 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/lastappliedupdater.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/lastappliedupdater.go @@ -21,9 +21,9 @@ import ( corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/meta" - apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal" ) type lastAppliedUpdater struct { @@ -62,7 +62,7 @@ func (f *lastAppliedUpdater) Apply(liveObj, newObj runtime.Object, managed Manag if err != nil { return nil, nil, fmt.Errorf("failed to build last-applied annotation: %v", err) } - err = setLastApplied(liveObj, lastAppliedValue) + err = internal.SetLastApplied(liveObj, lastAppliedValue) if err != nil { return nil, nil, fmt.Errorf("failed to set last-applied annotation: %v", err) } @@ -83,23 +83,6 @@ func hasLastApplied(obj runtime.Object) bool { return ok && len(lastApplied) > 0 } -func setLastApplied(obj runtime.Object, value string) error { - accessor, err := meta.Accessor(obj) - if err != nil { - panic(fmt.Sprintf("couldn't get accessor: %v", err)) - } - var annotations = accessor.GetAnnotations() - if annotations == nil { - annotations = map[string]string{} - } - annotations[corev1.LastAppliedConfigAnnotation] = value - if err := apimachineryvalidation.ValidateAnnotationsSize(annotations); err != nil { - delete(annotations, corev1.LastAppliedConfigAnnotation) - } - accessor.SetAnnotations(annotations) - return nil -} - func buildLastApplied(obj runtime.Object) (string, error) { obj = obj.DeepCopyObject() diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/lastappliedupdater_test.go b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/lastappliedupdater_test.go index e2832ed06b0..8b7d6160583 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/lastappliedupdater_test.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/lastappliedupdater_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package fieldmanager +package fieldmanager_test import ( "fmt" @@ -26,14 +26,16 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager" + "k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/fieldmanagertest" "sigs.k8s.io/yaml" ) func TestLastAppliedUpdater(t *testing.T) { - f := NewTestFieldManager(schema.FromAPIVersionAndKind("apps/v1", "Deployment"), + f := fieldmanagertest.NewTestFieldManager(schema.FromAPIVersionAndKind("apps/v1", "Deployment"), "", - func(m Manager) Manager { - return NewLastAppliedUpdater(m) + func(m fieldmanager.Manager) fieldmanager.Manager { + return fieldmanager.NewLastAppliedUpdater(m) }) originalLastApplied := `nonempty` @@ -69,7 +71,7 @@ spec: t.Errorf("error applying object: %v", err) } - lastApplied, err := getLastApplied(f.liveObj) + lastApplied, err := getLastApplied(f.Live()) if err != nil { t.Errorf("failed to get last applied: %v", err) } @@ -82,7 +84,7 @@ spec: t.Errorf("error applying object: %v", err) } - lastApplied, err = getLastApplied(f.liveObj) + lastApplied, err = getLastApplied(f.Live()) if err != nil { t.Errorf("failed to get last applied: %v", err) } @@ -187,17 +189,17 @@ func TestLargeLastApplied(t *testing.T) { } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - f := NewTestFieldManager(schema.FromAPIVersionAndKind("v1", "ConfigMap"), + f := fieldmanagertest.NewTestFieldManager(schema.FromAPIVersionAndKind("v1", "ConfigMap"), "", - func(m Manager) Manager { - return NewLastAppliedUpdater(m) + func(m fieldmanager.Manager) fieldmanager.Manager { + return fieldmanager.NewLastAppliedUpdater(m) }) if err := f.Apply(test.oldObject, "kubectl", false); err != nil { t.Errorf("Error applying object: %v", err) } - lastApplied, err := getLastApplied(f.liveObj) + lastApplied, err := getLastApplied(f.Live()) if err != nil { t.Errorf("Failed to access last applied annotation: %v", err) } @@ -210,12 +212,12 @@ func TestLargeLastApplied(t *testing.T) { } accessor := meta.NewAccessor() - annotations, err := accessor.Annotations(f.liveObj) + annotations, err := accessor.Annotations(f.Live()) if err != nil { t.Errorf("Failed to access annotations: %v", err) } if annotations == nil { - t.Errorf("No annotations on obj: %v", f.liveObj) + t.Errorf("No annotations on obj: %v", f.Live()) } lastApplied, ok := annotations[corev1.LastAppliedConfigAnnotation] if ok || len(lastApplied) > 0 { diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/managedfieldsupdater_test.go b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/managedfieldsupdater_test.go index b8ff34d40cf..b0c717a70a5 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/managedfieldsupdater_test.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/managedfieldsupdater_test.go @@ -14,26 +14,29 @@ See the License for the specific language governing permissions and limitations under the License. */ -package fieldmanager +package fieldmanager_test import ( "fmt" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "reflect" "testing" "time" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager" + "k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/fieldmanagertest" "k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal" "sigs.k8s.io/yaml" ) func TestManagedFieldsUpdateDoesModifyTime(t *testing.T) { var err error - f := NewTestFieldManager(schema.FromAPIVersionAndKind("v1", "ConfigMap"), "", nil) + f := fieldmanagertest.NewDefaultTestFieldManager(schema.FromAPIVersionAndKind("v1", "ConfigMap")) err = updateObject(&f, "fieldmanager_test", []byte(`{ "apiVersion": "v1", @@ -74,7 +77,7 @@ func TestManagedFieldsUpdateDoesModifyTime(t *testing.T) { func TestManagedFieldsApplyDoesModifyTime(t *testing.T) { var err error - f := NewTestFieldManager(schema.FromAPIVersionAndKind("v1", "ConfigMap"), "", nil) + f := fieldmanagertest.NewDefaultTestFieldManager(schema.FromAPIVersionAndKind("v1", "ConfigMap")) err = applyObject(&f, "fieldmanager_test", []byte(`{ "apiVersion": "v1", @@ -115,7 +118,7 @@ func TestManagedFieldsApplyDoesModifyTime(t *testing.T) { func TestManagedFieldsUpdateWithoutChangesDoesNotModifyTime(t *testing.T) { var err error - f := NewTestFieldManager(schema.FromAPIVersionAndKind("v1", "ConfigMap"), "", nil) + f := fieldmanagertest.NewDefaultTestFieldManager(schema.FromAPIVersionAndKind("v1", "ConfigMap")) err = updateObject(&f, "fieldmanager_test", []byte(`{ "apiVersion": "v1", @@ -156,7 +159,7 @@ func TestManagedFieldsUpdateWithoutChangesDoesNotModifyTime(t *testing.T) { func TestManagedFieldsApplyWithoutChangesDoesNotModifyTime(t *testing.T) { var err error - f := NewTestFieldManager(schema.FromAPIVersionAndKind("v1", "ConfigMap"), "", nil) + f := fieldmanagertest.NewDefaultTestFieldManager(schema.FromAPIVersionAndKind("v1", "ConfigMap")) err = applyObject(&f, "fieldmanager_test", []byte(`{ "apiVersion": "v1", @@ -197,7 +200,7 @@ func TestManagedFieldsApplyWithoutChangesDoesNotModifyTime(t *testing.T) { func TestNonManagedFieldsUpdateDoesNotModifyTime(t *testing.T) { var err error - f := NewTestFieldManager(schema.FromAPIVersionAndKind("v1", "ConfigMap"), "", nil) + f := fieldmanagertest.NewDefaultTestFieldManager(schema.FromAPIVersionAndKind("v1", "ConfigMap")) err = updateObject(&f, "fieldmanager_a_test", []byte(`{ "apiVersion": "v1", @@ -260,7 +263,7 @@ func TestNonManagedFieldsUpdateDoesNotModifyTime(t *testing.T) { func TestNonManagedFieldsApplyDoesNotModifyTime(t *testing.T) { var err error - f := NewTestFieldManager(schema.FromAPIVersionAndKind("v1", "ConfigMap"), "", nil) + f := fieldmanagertest.NewDefaultTestFieldManager(schema.FromAPIVersionAndKind("v1", "ConfigMap")) err = applyObject(&f, "fieldmanager_a_test", []byte(`{ "apiVersion": "v1", @@ -323,7 +326,7 @@ func TestNonManagedFieldsApplyDoesNotModifyTime(t *testing.T) { func TestTakingOverManagedFieldsDuringUpdateDoesNotModifyPreviousManagerTime(t *testing.T) { var err error - f := NewTestFieldManager(schema.FromAPIVersionAndKind("v1", "ConfigMap"), "", nil) + f := fieldmanagertest.NewDefaultTestFieldManager(schema.FromAPIVersionAndKind("v1", "ConfigMap")) err = updateObject(&f, "fieldmanager_a_test", []byte(`{ "apiVersion": "v1", @@ -374,7 +377,7 @@ func TestTakingOverManagedFieldsDuringUpdateDoesNotModifyPreviousManagerTime(t * func TestTakingOverManagedFieldsDuringApplyDoesNotModifyPreviousManagerTime(t *testing.T) { var err error - f := NewTestFieldManager(schema.FromAPIVersionAndKind("v1", "ConfigMap"), "", nil) + f := fieldmanagertest.NewDefaultTestFieldManager(schema.FromAPIVersionAndKind("v1", "ConfigMap")) err = applyObject(&f, "fieldmanager_a_test", []byte(`{ "apiVersion": "v1", @@ -425,15 +428,15 @@ func TestTakingOverManagedFieldsDuringApplyDoesNotModifyPreviousManagerTime(t *t type NoopManager struct{} -func (NoopManager) Apply(liveObj, appliedObj runtime.Object, managed Managed, fieldManager string, force bool) (runtime.Object, Managed, error) { +func (NoopManager) Apply(liveObj, appliedObj runtime.Object, managed fieldmanager.Managed, fieldManager string, force bool) (runtime.Object, fieldmanager.Managed, error) { return nil, managed, nil } -func (NoopManager) Update(liveObj, newObj runtime.Object, managed Managed, manager string) (runtime.Object, Managed, error) { +func (NoopManager) Update(liveObj, newObj runtime.Object, managed fieldmanager.Managed, manager string) (runtime.Object, fieldmanager.Managed, error) { return nil, nil, nil } -func updateObject(f *TestFieldManager, fieldManagerName string, object []byte) error { +func updateObject(f *fieldmanagertest.TestFieldManager, fieldManagerName string, object []byte) error { obj := &unstructured.Unstructured{Object: map[string]interface{}{}} if err := yaml.Unmarshal(object, &obj.Object); err != nil { return fmt.Errorf("error decoding YAML: %v", err) @@ -444,7 +447,7 @@ func updateObject(f *TestFieldManager, fieldManagerName string, object []byte) e return nil } -func applyObject(f *TestFieldManager, fieldManagerName string, object []byte) error { +func applyObject(f *fieldmanagertest.TestFieldManager, fieldManagerName string, object []byte) error { obj := &unstructured.Unstructured{Object: map[string]interface{}{}} if err := yaml.Unmarshal(object, &obj.Object); err != nil { return fmt.Errorf("error decoding YAML: %v", err) @@ -495,12 +498,12 @@ func TestNilNewObjectReplacedWithDeepCopyExcludingManagedFields(t *testing.T) { } // Decode the managed fields in the live object, since it isn't allowed in the patch. - managed, err := DecodeManagedFields(accessor.GetManagedFields()) + managed, err := fieldmanager.DecodeManagedFields(accessor.GetManagedFields()) if err != nil { t.Fatalf("failed to decode managed fields: %v", err) } - updater := NewManagedFieldsUpdater(NoopManager{}) + updater := fieldmanager.NewManagedFieldsUpdater(NoopManager{}) newObject, _, err := updater.Apply(obj, obj.DeepCopyObject(), managed, "some_manager", false) if err != nil { diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/skipnonapplied_test.go b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/skipnonapplied_test.go index a001dd0c02e..fc99a9b2fc2 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/skipnonapplied_test.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/skipnonapplied_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package fieldmanager +package fieldmanager_test import ( "strings" @@ -23,30 +23,18 @@ import ( corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager" + "k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/fieldmanagertest" "sigs.k8s.io/yaml" ) -type fakeObjectCreater struct { - gvk schema.GroupVersionKind -} - -var _ runtime.ObjectCreater = &fakeObjectCreater{} - -func (f *fakeObjectCreater) New(_ schema.GroupVersionKind) (runtime.Object, error) { - u := unstructured.Unstructured{Object: map[string]interface{}{}} - u.SetAPIVersion(f.gvk.GroupVersion().String()) - u.SetKind(f.gvk.Kind) - return &u, nil -} - func TestNoUpdateBeforeFirstApply(t *testing.T) { - f := NewTestFieldManager(schema.FromAPIVersionAndKind("v1", "Pod"), "", func(m Manager) Manager { - return NewSkipNonAppliedManager( + f := fieldmanagertest.NewTestFieldManager(schema.FromAPIVersionAndKind("v1", "Pod"), "", func(m fieldmanager.Manager) fieldmanager.Manager { + return fieldmanager.NewSkipNonAppliedManager( m, - &fakeObjectCreater{gvk: schema.GroupVersionKind{Version: "v1", Kind: "Pod"}}, - schema.GroupVersionKind{}, + fieldmanagertest.NewFakeObjectCreater(), + schema.FromAPIVersionAndKind("v1", "Pod"), ) }) @@ -82,11 +70,11 @@ func TestNoUpdateBeforeFirstApply(t *testing.T) { } func TestUpdateBeforeFirstApply(t *testing.T) { - f := NewTestFieldManager(schema.FromAPIVersionAndKind("v1", "Pod"), "", func(m Manager) Manager { - return NewSkipNonAppliedManager( + f := fieldmanagertest.NewTestFieldManager(schema.FromAPIVersionAndKind("v1", "Pod"), "", func(m fieldmanager.Manager) fieldmanager.Manager { + return fieldmanager.NewSkipNonAppliedManager( m, - &fakeObjectCreater{gvk: schema.GroupVersionKind{Version: "v1", Kind: "Pod"}}, - schema.GroupVersionKind{}, + fieldmanagertest.NewFakeObjectCreater(), + schema.FromAPIVersionAndKind("v1", "Pod"), ) })