mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-22 19:31:44 +00:00
Merge pull request #114218 from apelisse/move-fieldmanager
Re-factor FieldManager tests for better re-use
This commit is contained in:
commit
a125912cd5
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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()
|
||||
}
|
@ -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
|
||||
}
|
@ -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())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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"),
|
||||
)
|
||||
})
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user