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 e31556ba335..f91f947a788 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 @@ -63,7 +63,7 @@ var fakeTypeConverter = func() fieldmanager.TypeConverter { // TestUpdateApplyConflict tests that applying to an object, which // wasn't created by apply, will give conflicts func TestUpdateApplyConflict(t *testing.T) { - f := fieldmanagertest.NewDefaultTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("apps/v1", "Deployment")) + f := fieldmanagertest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("apps/v1", "Deployment")) patch := []byte(`{ "apiVersion": "apps/v1", @@ -124,7 +124,7 @@ func TestUpdateApplyConflict(t *testing.T) { } func TestApplyStripsFields(t *testing.T) { - f := fieldmanagertest.NewDefaultTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("apps/v1", "Deployment")) + f := fieldmanagertest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("apps/v1", "Deployment")) newObj := &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -156,7 +156,7 @@ func TestApplyStripsFields(t *testing.T) { } func TestVersionCheck(t *testing.T) { - f := fieldmanagertest.NewDefaultTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("apps/v1", "Deployment")) + f := fieldmanagertest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("apps/v1", "Deployment")) appliedObj := &unstructured.Unstructured{Object: map[string]interface{}{}} if err := yaml.Unmarshal([]byte(`{ @@ -196,7 +196,7 @@ func TestVersionCheck(t *testing.T) { } } func TestVersionCheckDoesNotPanic(t *testing.T) { - f := fieldmanagertest.NewDefaultTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("apps/v1", "Deployment")) + f := fieldmanagertest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("apps/v1", "Deployment")) appliedObj := &unstructured.Unstructured{Object: map[string]interface{}{}} if err := yaml.Unmarshal([]byte(`{ @@ -235,7 +235,7 @@ func TestVersionCheckDoesNotPanic(t *testing.T) { } func TestApplyDoesNotStripLabels(t *testing.T) { - f := fieldmanagertest.NewDefaultTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "Pod")) + f := fieldmanagertest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "Pod")) appliedObj := &unstructured.Unstructured{Object: map[string]interface{}{}} if err := yaml.Unmarshal([]byte(`{ @@ -289,7 +289,7 @@ func TestApplyNewObject(t *testing.T) { for _, test := range tests { t.Run(test.gvk.String(), func(t *testing.T) { - f := fieldmanagertest.NewDefaultTestFieldManager(fakeTypeConverter, test.gvk) + f := fieldmanagertest.NewTestFieldManager(fakeTypeConverter, test.gvk) appliedObj := &unstructured.Unstructured{Object: map[string]interface{}{}} if err := yaml.Unmarshal(test.obj, &appliedObj.Object); err != nil { @@ -327,7 +327,7 @@ func BenchmarkNewObject(b *testing.B) { } for _, test := range tests { b.Run(test.gvk.Kind, func(b *testing.B) { - f := fieldmanagertest.NewDefaultTestFieldManager(fakeTypeConverter, test.gvk) + f := fieldmanagertest.NewTestFieldManager(fakeTypeConverter, test.gvk) decoder := serializer.NewCodecFactory(scheme).UniversalDecoder(test.gvk.GroupVersion()) newObj, err := runtime.Decode(decoder, test.obj) @@ -540,7 +540,7 @@ func BenchmarkCompare(b *testing.B) { } func BenchmarkRepeatedUpdate(b *testing.B) { - f := fieldmanagertest.NewDefaultTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "Pod")) + f := fieldmanagertest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "Pod")) podBytes := getObjectBytes("pod.yaml") var obj *corev1.Pod @@ -579,7 +579,7 @@ func BenchmarkRepeatedUpdate(b *testing.B) { } func TestApplyFailsWithManagedFields(t *testing.T) { - f := fieldmanagertest.NewDefaultTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "Pod")) + f := fieldmanagertest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "Pod")) appliedObj := &unstructured.Unstructured{Object: map[string]interface{}{}} if err := yaml.Unmarshal([]byte(`{ @@ -604,7 +604,7 @@ func TestApplyFailsWithManagedFields(t *testing.T) { } func TestApplySuccessWithNoManagedFields(t *testing.T) { - f := fieldmanagertest.NewDefaultTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "Pod")) + f := fieldmanagertest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "Pod")) appliedObj := &unstructured.Unstructured{Object: map[string]interface{}{}} if err := yaml.Unmarshal([]byte(`{ @@ -627,7 +627,7 @@ func TestApplySuccessWithNoManagedFields(t *testing.T) { // Run an update and apply, and make sure that nothing has changed. func TestNoOpChanges(t *testing.T) { - f := fieldmanagertest.NewDefaultTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "Pod")) + f := fieldmanagertest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "Pod")) obj := &unstructured.Unstructured{Object: map[string]interface{}{}} if err := yaml.Unmarshal([]byte(`{ @@ -678,7 +678,7 @@ func TestNoOpChanges(t *testing.T) { // Tests that one can reset the managedFields by sending either an empty // list func TestResetManagedFieldsEmptyList(t *testing.T) { - f := fieldmanagertest.NewDefaultTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "Pod")) + f := fieldmanagertest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "Pod")) obj := &unstructured.Unstructured{Object: map[string]interface{}{}} if err := yaml.Unmarshal([]byte(`{ @@ -719,7 +719,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 := fieldmanagertest.NewDefaultTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "Pod")) + f := fieldmanagertest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "Pod")) obj := &unstructured.Unstructured{Object: map[string]interface{}{}} if err := yaml.Unmarshal([]byte(`{ @@ -759,7 +759,7 @@ func TestResetManagedFieldsEmptyItem(t *testing.T) { } func TestServerSideApplyWithInvalidLastApplied(t *testing.T) { - f := fieldmanagertest.NewDefaultTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("apps/v1", "Deployment")) + f := fieldmanagertest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("apps/v1", "Deployment")) // create object with client-side apply newObj := &unstructured.Unstructured{Object: map[string]interface{}{}} @@ -838,7 +838,7 @@ spec: } func TestInteropForClientSideApplyAndServerSideApply(t *testing.T) { - f := fieldmanagertest.NewDefaultTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("apps/v1", "Deployment")) + f := fieldmanagertest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("apps/v1", "Deployment")) // create object with client-side apply newObj := &unstructured.Unstructured{Object: map[string]interface{}{}} @@ -922,7 +922,7 @@ spec: } func TestNoTrackManagedFieldsForClientSideApply(t *testing.T) { - f := fieldmanagertest.NewDefaultTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("apps/v1", "Deployment")) + f := fieldmanagertest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("apps/v1", "Deployment")) // create object newObj := &unstructured.Unstructured{Object: map[string]interface{}{}} @@ -1102,7 +1102,7 @@ func getLastApplied(obj runtime.Object) (string, error) { } func TestUpdateViaSubresources(t *testing.T) { - f := fieldmanagertest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "Pod"), "scale", nil) + f := fieldmanagertest.NewTestFieldManagerSubresource(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "Pod"), "scale") obj := &unstructured.Unstructured{Object: map[string]interface{}{}} if err := yaml.Unmarshal([]byte(`{ @@ -1157,7 +1157,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 := fieldmanagertest.NewDefaultTestFieldManager(fakeTypeConverter, + f := fieldmanagertest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("apps/v1", "Deployment")) newObj := &unstructured.Unstructured{ Object: map[string]interface{}{}, @@ -1251,7 +1251,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 := fieldmanagertest.NewDefaultTestFieldManager(fakeTypeConverter, + f := fieldmanagertest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("apps/v1", "Deployment")) newObj := &unstructured.Unstructured{ Object: map[string]interface{}{}, @@ -1323,7 +1323,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 := fieldmanagertest.NewDefaultTestFieldManager(fakeTypeConverter, + f := fieldmanagertest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("apps/v1", "Deployment")) newObj := &unstructured.Unstructured{ Object: map[string]interface{}{}, 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 index 71eb9e6ee32..d188d171dd5 100644 --- 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 @@ -17,73 +17,13 @@ limitations under the License. package fieldmanagertest import ( - "errors" - "fmt" - - "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/apiserver/pkg/endpoints/handlers/fieldmanager/internal" - "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/internal/testing" ) -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. @@ -95,105 +35,32 @@ func (f *fakeObjectCreater) New(gvk schema.GroupVersionKind) (runtime.Object, er // 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 *internal.FieldManager - apiVersion string - emptyObj runtime.Object - liveObj runtime.Object +type TestFieldManager interface { + // APIVersion of the object that we're tracking. + APIVersion() string + // Reset resets the state of the liveObject by resetting it to an empty object. + Reset() + // Live returns a copy of the current liveObject. + Live() runtime.Object + // Apply applies the given object on top of the current liveObj, for the + // given manager and force flag. + Apply(obj runtime.Object, manager string, force bool) error + // Update will updates the managed fields in the liveObj based on the + // changes performed by the update. + Update(obj runtime.Object, manager string) error + // ManagedFields returns the list of existing managed fields for the + // liveObj. + ManagedFields() []metav1.ManagedFieldsEntry } -// NewDefaultTestFieldManager returns a new TestFieldManager built for -// the given gvk, on the main resource. -func NewDefaultTestFieldManager(typeConverter fieldmanager.TypeConverter, gvk schema.GroupVersionKind) TestFieldManager { - return NewTestFieldManager(typeConverter, gvk, "", nil) +// NewTestFieldManager returns a new TestFieldManager built for the +// given gvk, on the main resource. +func NewTestFieldManager(typeConverter fieldmanager.TypeConverter, gvk schema.GroupVersionKind) TestFieldManager { + return testing.NewTestFieldManagerImpl(typeConverter, gvk, "", nil) } -// NewTestFieldManager creates a new manager for the given GVK. -func NewTestFieldManager(typeConverter fieldmanager.TypeConverter, gvk schema.GroupVersionKind, subresource string, chainFieldManager func(internal.Manager) internal.Manager) TestFieldManager { - apiVersion := fieldpath.APIVersion(gvk.GroupVersion().String()) - objectConverter := &fakeObjectConvertor{sameVersionConverter{}, apiVersion} - f, err := internal.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()) - // This is different from `internal.NewDefaultFieldManager` because: - // 1. We don't want to create a `internal.FieldManager` - // 2. We don't want to use the CapManager that is tested separately with - // a smaller than the default cap. - f = internal.NewLastAppliedUpdater( - internal.NewLastAppliedManager( - internal.NewProbabilisticSkipNonAppliedManager( - internal.NewBuildManagerInfoManager( - internal.NewManagedFieldsUpdater( - internal.NewStripMetaManager(f), - ), gvk.GroupVersion(), subresource, - ), NewFakeObjectCreater(), gvk, internal.DefaultTrackOnCreateProbability, - ), typeConverter, objectConverter, gvk.GroupVersion(), - ), - ) - if chainFieldManager != nil { - f = chainFieldManager(f) - } - return TestFieldManager{ - fieldManager: internal.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() +// NewTestFieldManagerSubresource returns a new TestFieldManager built +// for the given gvk, on the given sub-resource. +func NewTestFieldManagerSubresource(typeConverter fieldmanager.TypeConverter, gvk schema.GroupVersionKind, subresource string) TestFieldManager { + return testing.NewTestFieldManagerImpl(typeConverter, gvk, subresource, nil) } diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/capmanagers_test.go b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/capmanagers_test.go index b98eb37dab9..bd8dd36664f 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/capmanagers_test.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/capmanagers_test.go @@ -31,6 +31,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/fieldmanagertest" "k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal" + internaltesting "k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/testing" "sigs.k8s.io/structured-merge-diff/v4/fieldpath" ) @@ -48,7 +49,7 @@ func (*fakeManager) Apply(_, _ runtime.Object, _ internal.Managed, _ string, _ b } func TestCapManagersManagerMergesEntries(t *testing.T) { - f := fieldmanagertest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "Pod"), + f := internaltesting.NewTestFieldManagerImpl(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "Pod"), "", func(m internal.Manager) internal.Manager { return internal.NewCapManagersManager(m, 3) @@ -114,7 +115,7 @@ func TestCapManagersManagerMergesEntries(t *testing.T) { } func TestCapUpdateManagers(t *testing.T) { - f := fieldmanagertest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "Pod"), + f := internaltesting.NewTestFieldManagerImpl(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "Pod"), "", func(m internal.Manager) internal.Manager { return internal.NewCapManagersManager(m, 3) diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/lastappliedmanager_test.go b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/lastappliedmanager_test.go index 61f4055e9c7..e3460ca4227 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/lastappliedmanager_test.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/lastappliedmanager_test.go @@ -46,7 +46,7 @@ type testArgs struct { // created with the client-side apply last-applied annotation // will not give conflicts func TestApplyUsingLastAppliedAnnotation(t *testing.T) { - f := fieldmanagertest.NewDefaultTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("apps/v1", "Deployment")) + f := fieldmanagertest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("apps/v1", "Deployment")) tests := []testArgs{ { @@ -566,7 +566,7 @@ spec: } func TestServiceApply(t *testing.T) { - f := fieldmanagertest.NewDefaultTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "Service")) + f := fieldmanagertest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "Service")) tests := []testArgs{ { @@ -677,7 +677,7 @@ spec: } func TestReplicationControllerApply(t *testing.T) { - f := fieldmanagertest.NewDefaultTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "ReplicationController")) + f := fieldmanagertest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "ReplicationController")) tests := []testArgs{ { @@ -740,7 +740,7 @@ spec: } func TestPodApply(t *testing.T) { - f := fieldmanagertest.NewDefaultTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "Pod")) + f := fieldmanagertest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "Pod")) tests := []testArgs{ { diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/lastappliedupdater_test.go b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/lastappliedupdater_test.go index c89f03c3b72..35d3267fb4e 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/lastappliedupdater_test.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/lastappliedupdater_test.go @@ -27,13 +27,13 @@ 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/fieldmanagertest" "k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal" + internaltesting "k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/testing" "sigs.k8s.io/yaml" ) func TestLastAppliedUpdater(t *testing.T) { - f := fieldmanagertest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("apps/v1", "Deployment"), + f := internaltesting.NewTestFieldManagerImpl(fakeTypeConverter, schema.FromAPIVersionAndKind("apps/v1", "Deployment"), "", func(m internal.Manager) internal.Manager { return internal.NewLastAppliedUpdater(m) @@ -190,7 +190,7 @@ func TestLargeLastApplied(t *testing.T) { } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - f := fieldmanagertest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "ConfigMap"), + f := internaltesting.NewTestFieldManagerImpl(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "ConfigMap"), "", func(m internal.Manager) internal.Manager { return internal.NewLastAppliedUpdater(m) diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/managedfieldsupdater_test.go b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/managedfieldsupdater_test.go index 9d9683b23b7..11aaedc2922 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/managedfieldsupdater_test.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/managedfieldsupdater_test.go @@ -35,9 +35,9 @@ import ( func TestManagedFieldsUpdateDoesModifyTime(t *testing.T) { var err error - f := fieldmanagertest.NewDefaultTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "ConfigMap")) + f := fieldmanagertest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "ConfigMap")) - err = updateObject(&f, "fieldmanager_test", []byte(`{ + err = updateObject(f, "fieldmanager_test", []byte(`{ "apiVersion": "v1", "kind": "ConfigMap", "metadata": { @@ -54,7 +54,7 @@ func TestManagedFieldsUpdateDoesModifyTime(t *testing.T) { time.Sleep(time.Second) - err = updateObject(&f, "fieldmanager_test", []byte(`{ + err = updateObject(f, "fieldmanager_test", []byte(`{ "apiVersion": "v1", "kind": "ConfigMap", "metadata": { @@ -76,9 +76,9 @@ func TestManagedFieldsUpdateDoesModifyTime(t *testing.T) { func TestManagedFieldsApplyDoesModifyTime(t *testing.T) { var err error - f := fieldmanagertest.NewDefaultTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "ConfigMap")) + f := fieldmanagertest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "ConfigMap")) - err = applyObject(&f, "fieldmanager_test", []byte(`{ + err = applyObject(f, "fieldmanager_test", []byte(`{ "apiVersion": "v1", "kind": "ConfigMap", "metadata": { @@ -95,7 +95,7 @@ func TestManagedFieldsApplyDoesModifyTime(t *testing.T) { time.Sleep(time.Second) - err = applyObject(&f, "fieldmanager_test", []byte(`{ + err = applyObject(f, "fieldmanager_test", []byte(`{ "apiVersion": "v1", "kind": "ConfigMap", "metadata": { @@ -117,9 +117,9 @@ func TestManagedFieldsApplyDoesModifyTime(t *testing.T) { func TestManagedFieldsUpdateWithoutChangesDoesNotModifyTime(t *testing.T) { var err error - f := fieldmanagertest.NewDefaultTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "ConfigMap")) + f := fieldmanagertest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "ConfigMap")) - err = updateObject(&f, "fieldmanager_test", []byte(`{ + err = updateObject(f, "fieldmanager_test", []byte(`{ "apiVersion": "v1", "kind": "ConfigMap", "metadata": { @@ -136,7 +136,7 @@ func TestManagedFieldsUpdateWithoutChangesDoesNotModifyTime(t *testing.T) { time.Sleep(time.Second) - err = updateObject(&f, "fieldmanager_test", []byte(`{ + err = updateObject(f, "fieldmanager_test", []byte(`{ "apiVersion": "v1", "kind": "ConfigMap", "metadata": { @@ -158,9 +158,9 @@ func TestManagedFieldsUpdateWithoutChangesDoesNotModifyTime(t *testing.T) { func TestManagedFieldsApplyWithoutChangesDoesNotModifyTime(t *testing.T) { var err error - f := fieldmanagertest.NewDefaultTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "ConfigMap")) + f := fieldmanagertest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "ConfigMap")) - err = applyObject(&f, "fieldmanager_test", []byte(`{ + err = applyObject(f, "fieldmanager_test", []byte(`{ "apiVersion": "v1", "kind": "ConfigMap", "metadata": { @@ -177,7 +177,7 @@ func TestManagedFieldsApplyWithoutChangesDoesNotModifyTime(t *testing.T) { time.Sleep(time.Second) - err = applyObject(&f, "fieldmanager_test", []byte(`{ + err = applyObject(f, "fieldmanager_test", []byte(`{ "apiVersion": "v1", "kind": "ConfigMap", "metadata": { @@ -199,9 +199,9 @@ func TestManagedFieldsApplyWithoutChangesDoesNotModifyTime(t *testing.T) { func TestNonManagedFieldsUpdateDoesNotModifyTime(t *testing.T) { var err error - f := fieldmanagertest.NewDefaultTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "ConfigMap")) + f := fieldmanagertest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "ConfigMap")) - err = updateObject(&f, "fieldmanager_a_test", []byte(`{ + err = updateObject(f, "fieldmanager_a_test", []byte(`{ "apiVersion": "v1", "kind": "ConfigMap", "metadata": { @@ -214,7 +214,7 @@ func TestNonManagedFieldsUpdateDoesNotModifyTime(t *testing.T) { if err != nil { t.Fatal(err) } - err = updateObject(&f, "fieldmanager_b_test", []byte(`{ + err = updateObject(f, "fieldmanager_b_test", []byte(`{ "apiVersion": "v1", "kind": "ConfigMap", "metadata": { @@ -235,7 +235,7 @@ func TestNonManagedFieldsUpdateDoesNotModifyTime(t *testing.T) { time.Sleep(time.Second) - err = updateObject(&f, "fieldmanager_a_test", []byte(`{ + err = updateObject(f, "fieldmanager_a_test", []byte(`{ "apiVersion": "v1", "kind": "ConfigMap", "metadata": { @@ -262,9 +262,9 @@ func TestNonManagedFieldsUpdateDoesNotModifyTime(t *testing.T) { func TestNonManagedFieldsApplyDoesNotModifyTime(t *testing.T) { var err error - f := fieldmanagertest.NewDefaultTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "ConfigMap")) + f := fieldmanagertest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "ConfigMap")) - err = applyObject(&f, "fieldmanager_a_test", []byte(`{ + err = applyObject(f, "fieldmanager_a_test", []byte(`{ "apiVersion": "v1", "kind": "ConfigMap", "metadata": { @@ -277,7 +277,7 @@ func TestNonManagedFieldsApplyDoesNotModifyTime(t *testing.T) { if err != nil { t.Fatal(err) } - err = applyObject(&f, "fieldmanager_b_test", []byte(`{ + err = applyObject(f, "fieldmanager_b_test", []byte(`{ "apiVersion": "v1", "kind": "ConfigMap", "metadata": { @@ -298,7 +298,7 @@ func TestNonManagedFieldsApplyDoesNotModifyTime(t *testing.T) { time.Sleep(time.Second) - err = applyObject(&f, "fieldmanager_a_test", []byte(`{ + err = applyObject(f, "fieldmanager_a_test", []byte(`{ "apiVersion": "v1", "kind": "ConfigMap", "metadata": { @@ -325,9 +325,9 @@ func TestNonManagedFieldsApplyDoesNotModifyTime(t *testing.T) { func TestTakingOverManagedFieldsDuringUpdateDoesNotModifyPreviousManagerTime(t *testing.T) { var err error - f := fieldmanagertest.NewDefaultTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "ConfigMap")) + f := fieldmanagertest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "ConfigMap")) - err = updateObject(&f, "fieldmanager_a_test", []byte(`{ + err = updateObject(f, "fieldmanager_a_test", []byte(`{ "apiVersion": "v1", "kind": "ConfigMap", "metadata": { @@ -349,7 +349,7 @@ func TestTakingOverManagedFieldsDuringUpdateDoesNotModifyPreviousManagerTime(t * time.Sleep(time.Second) - err = updateObject(&f, "fieldmanager_b_test", []byte(`{ + err = updateObject(f, "fieldmanager_b_test", []byte(`{ "apiVersion": "v1", "kind": "ConfigMap", "metadata": { @@ -376,9 +376,9 @@ func TestTakingOverManagedFieldsDuringUpdateDoesNotModifyPreviousManagerTime(t * func TestTakingOverManagedFieldsDuringApplyDoesNotModifyPreviousManagerTime(t *testing.T) { var err error - f := fieldmanagertest.NewDefaultTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "ConfigMap")) + f := fieldmanagertest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "ConfigMap")) - err = applyObject(&f, "fieldmanager_a_test", []byte(`{ + err = applyObject(f, "fieldmanager_a_test", []byte(`{ "apiVersion": "v1", "kind": "ConfigMap", "metadata": { @@ -400,7 +400,7 @@ func TestTakingOverManagedFieldsDuringApplyDoesNotModifyPreviousManagerTime(t *t time.Sleep(time.Second) - err = applyObject(&f, "fieldmanager_b_test", []byte(`{ + err = applyObject(f, "fieldmanager_b_test", []byte(`{ "apiVersion": "v1", "kind": "ConfigMap", "metadata": { @@ -435,7 +435,7 @@ func (NoopManager) Update(liveObj, newObj runtime.Object, managed internal.Manag return nil, nil, nil } -func updateObject(f *fieldmanagertest.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) @@ -446,7 +446,7 @@ func updateObject(f *fieldmanagertest.TestFieldManager, fieldManagerName string, return nil } -func applyObject(f *fieldmanagertest.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) diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/skipnonapplied_test.go b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/skipnonapplied_test.go index c5723472d69..cdd01f4091b 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/skipnonapplied_test.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/skipnonapplied_test.go @@ -24,16 +24,16 @@ 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" + internaltesting "k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/testing" "sigs.k8s.io/yaml" ) func TestNoUpdateBeforeFirstApply(t *testing.T) { - f := fieldmanagertest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "Pod"), "", func(m internal.Manager) internal.Manager { + f := internaltesting.NewTestFieldManagerImpl(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "Pod"), "", func(m internal.Manager) internal.Manager { return internal.NewSkipNonAppliedManager( m, - fieldmanagertest.NewFakeObjectCreater(), + internaltesting.NewFakeObjectCreater(), schema.FromAPIVersionAndKind("v1", "Pod"), ) }) @@ -70,10 +70,10 @@ func TestNoUpdateBeforeFirstApply(t *testing.T) { } func TestUpdateBeforeFirstApply(t *testing.T) { - f := fieldmanagertest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "Pod"), "", func(m internal.Manager) internal.Manager { + f := internaltesting.NewTestFieldManagerImpl(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "Pod"), "", func(m internal.Manager) internal.Manager { return internal.NewSkipNonAppliedManager( m, - fieldmanagertest.NewFakeObjectCreater(), + internaltesting.NewFakeObjectCreater(), schema.FromAPIVersionAndKind("v1", "Pod"), ) }) diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/testing/testfieldmanager.go b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/testing/testfieldmanager.go new file mode 100644 index 00000000000..5e9f04971c0 --- /dev/null +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/testing/testfieldmanager.go @@ -0,0 +1,182 @@ +/* +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 testing + +import ( + "errors" + "fmt" + + "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/apiserver/pkg/endpoints/handlers/fieldmanager/internal" + "sigs.k8s.io/structured-merge-diff/v4/fieldpath" + "sigs.k8s.io/structured-merge-diff/v4/merge" + "sigs.k8s.io/structured-merge-diff/v4/typed" +) + +// 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 +} + +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 +} + +type TestFieldManagerImpl struct { + fieldManager *internal.FieldManager + apiVersion string + emptyObj runtime.Object + liveObj runtime.Object +} + +// APIVersion of the object that we're tracking. +func (f *TestFieldManagerImpl) APIVersion() string { + return f.apiVersion +} + +// Reset resets the state of the liveObject by resetting it to an empty object. +func (f *TestFieldManagerImpl) Reset() { + f.liveObj = f.emptyObj.DeepCopyObject() +} + +// Live returns a copy of the current liveObject. +func (f *TestFieldManagerImpl) 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 *TestFieldManagerImpl) 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 *TestFieldManagerImpl) 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 *TestFieldManagerImpl) ManagedFields() []metav1.ManagedFieldsEntry { + accessor, err := meta.Accessor(f.liveObj) + if err != nil { + panic(fmt.Errorf("couldn't get accessor: %v", err)) + } + + return accessor.GetManagedFields() +} + +// NewTestFieldManager creates a new manager for the given GVK. +func NewTestFieldManagerImpl(typeConverter fieldmanager.TypeConverter, gvk schema.GroupVersionKind, subresource string, chainFieldManager func(internal.Manager) internal.Manager) *TestFieldManagerImpl { + apiVersion := fieldpath.APIVersion(gvk.GroupVersion().String()) + objectConverter := &fakeObjectConvertor{sameVersionConverter{}, apiVersion} + f, err := internal.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()) + // This is different from `internal.NewDefaultFieldManager` because: + // 1. We don't want to create a `internal.FieldManager` + // 2. We don't want to use the CapManager that is tested separately with + // a smaller than the default cap. + f = internal.NewLastAppliedUpdater( + internal.NewLastAppliedManager( + internal.NewProbabilisticSkipNonAppliedManager( + internal.NewBuildManagerInfoManager( + internal.NewManagedFieldsUpdater( + internal.NewStripMetaManager(f), + ), gvk.GroupVersion(), subresource, + ), NewFakeObjectCreater(), gvk, internal.DefaultTrackOnCreateProbability, + ), typeConverter, objectConverter, gvk.GroupVersion(), + ), + ) + if chainFieldManager != nil { + f = chainFieldManager(f) + } + return &TestFieldManagerImpl{ + fieldManager: internal.NewFieldManager(f, subresource), + apiVersion: gvk.GroupVersion().String(), + emptyObj: live, + liveObj: live.DeepCopyObject(), + } +}