fieldmanagertest: Reduce API surface of the test package

This commit is contained in:
Antoine Pelisse 2022-12-15 20:50:38 -08:00
parent 7e97b4b322
commit 7899157345
8 changed files with 270 additions and 220 deletions

View File

@ -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{}{},

View File

@ -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)
}

View File

@ -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)

View File

@ -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{
{

View File

@ -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)

View File

@ -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)

View File

@ -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"),
)
})

View File

@ -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(),
}
}