FieldManagedObjectTracker: Fix to work with unstructured

Prior to this patch, this fails because the skipnonappliedfieldmanager
uses the `objectcreater` (aka `*runtime.Scheme`) to create a new object
for which it never sets the GVK. In the case of
`*unstructured.Unstructured`, the GVK can not be derived from the object
itself so the operation would subsequently fail [here][0] with an
```
Object 'Kind' is missing in 'unstructured object has no kind'
```

error. Fix this by explicitly setting the GVK in case of unstructured.

[0]: 02eb7d424a/staging/src/k8s.io/apimachinery/pkg/util/managedfields/internal/structuredmerge.go (L98)

Kubernetes-commit: dbdd6a3b4358d91a064de9c0f01d3050e606d553
This commit is contained in:
Alvaro Aleman 2025-05-30 15:30:33 -04:00 committed by Kubernetes Publisher
parent e9ca982cec
commit 81ecec406a
2 changed files with 59 additions and 2 deletions

View File

@ -19,11 +19,12 @@ package testing
import (
"fmt"
"reflect"
"sigs.k8s.io/yaml"
"sort"
"strings"
"sync"
"sigs.k8s.io/yaml"
jsonpatch "gopkg.in/evanphx/json-patch.v4"
apierrors "k8s.io/apimachinery/pkg/api/errors"
@ -702,7 +703,6 @@ func (t *managedFieldObjectTracker) Update(gvr schema.GroupVersionResource, obj
}
gvk, err := t.mapper().KindFor(gvr)
if err != nil {
println("kindfor")
return err
}
mgr, err := t.fieldManagerFor(gvk)

View File

@ -23,7 +23,9 @@ import (
"sync"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"sigs.k8s.io/structured-merge-diff/v4/typed"
v1 "k8s.io/api/core/v1"
@ -683,3 +685,58 @@ func TestManagedFieldsObjectTrackerReloadsScheme(t *testing.T) {
err := tracker.Create(cmResource, cm, "default", metav1.CreateOptions{FieldManager: "test-manager"})
assert.NoError(t, err, "Create should succeed after registering type")
}
func TestManagedFielsdObjectTrackerWithUnstructured(t *testing.T) {
cmResource := schema.GroupVersionResource{Version: "v1", Resource: "configmaps"}
cmGVK := schema.GroupVersionKind{Version: "v1", Kind: "ConfigMap"}
scheme := runtime.NewScheme()
scheme.AddKnownTypeWithName(cmGVK, &unstructured.Unstructured{})
codecs := serializer.NewCodecFactory(scheme)
tracker := NewFieldManagedObjectTracker(scheme, codecs.UniversalDecoder(), managedfields.NewDeducedTypeConverter())
cm := &unstructured.Unstructured{}
cm.SetAPIVersion("v1")
cm.SetKind("ConfigMap")
cm.SetName("test-cm")
cm.SetNamespace("default")
require.NoError(t, unstructured.SetNestedMap(cm.Object,
map[string]any{
"key": "value",
},
"data"),
)
// Validate creating through apply works
cmOriginal := cm.DeepCopy()
require.NoError(t, tracker.Apply(cmResource, cm, "default", metav1.PatchOptions{FieldManager: "test-manager"}))
cmActualUntyped, err := tracker.Get(cmResource, "default", cm.GetName())
require.NoError(t, err)
cmActual, ok := cmActualUntyped.(*unstructured.Unstructured)
require.True(t, ok)
unstructured.RemoveNestedField(cmActual.Object, "metadata", "managedFields")
require.Empty(t, cmp.Diff(cmOriginal, cmActual))
// Validate updating through apply works
require.NoError(t, unstructured.SetNestedMap(cmActual.Object,
map[string]any{
"key": "value",
"another-key": "another-value",
},
"data"),
)
cmOriginal = cmActual.DeepCopy()
require.NoError(t, tracker.Apply(cmResource, cmActual, "default", metav1.PatchOptions{FieldManager: "test-manager"}))
cmActualUntyped, err = tracker.Get(cmResource, "default", cm.GetName())
require.NoError(t, err)
cmActual, ok = cmActualUntyped.(*unstructured.Unstructured)
require.True(t, ok)
unstructured.RemoveNestedField(cmActual.Object, "metadata", "managedFields")
require.Empty(t, cmp.Diff(cmOriginal, cmActual))
}