Merge pull request #114218 from apelisse/move-fieldmanager

Re-factor FieldManager tests for better re-use
This commit is contained in:
Kubernetes Prow Robot 2022-12-10 07:54:30 -08:00 committed by GitHub
commit a125912cd5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 389 additions and 291 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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