-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Description
Description
The recent release v0.22.0 introduced the following breaking change:
Fakeclient: Clear TypeMeta for structured objects by @alvaroaleman in #3229
This update causes the fake client to strip the TypeMeta Kind and APIVersion fields, aligning its behavior with the live client. However, in the live client, a RESTMapper can be used to restore the Kind and APIVersion information, whereas the fake client does not currently apply the RESTMapper in the same way.
The following example demonstrates the difference in behavior:
package main
import (
"context"
"errors"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
)
func main() {
s := runtime.NewScheme()
if err := v1.AddToScheme(s); err != nil {
panic(err)
}
ctx := ctrl.SetupSignalHandler()
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
Scheme: s,
})
if err != nil {
panic(err)
}
go func() {
if err := mgr.Start(ctx); err != nil {
panic(err)
}
}()
mgr.GetCache().WaitForCacheSync(ctx)
cli := mgr.GetClient()
kinds, err := getConfigMapKinds(ctx, cli)
if err != nil {
panic(err)
}
println("ConfigMap kind:", kinds)
}
func getConfigMapKinds(ctx context.Context, cli client.Client) (string, error) {
configMapList := &v1.ConfigMapList{}
err := cli.List(ctx, configMapList)
if err != nil {
return "", err
}
for _, cm := range configMapList.Items {
return cm.GetObjectKind().GroupVersionKind().Kind, nil
}
return "", errors.New("no configmaps found")
}running this code will output :
ConfigMap kind: ConfigMap
Although the fake client provides a WithRESTMapper option, it appears that this is not respected when restoring Kind information.
A corresponding test might look like this:
package main
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
)
// newSimpleRESTMapper builds a RESTMapper from the scheme and a set of known GVKs
func newSimpleRESTMapper(scheme *runtime.Scheme, gvks []schema.GroupVersionKind) meta.RESTMapper {
groups := scheme.PreferredVersionAllGroups()
rm := meta.NewDefaultRESTMapper(groups)
for _, gvk := range gvks {
rm.Add(gvk, meta.RESTScopeNamespace)
}
return rm
}
func TestGetConfigMapKind(t *testing.T) {
scheme := runtime.NewScheme()
require.NoError(t, corev1.AddToScheme(scheme))
// build a mapper with ConfigMap
gvks := []schema.GroupVersionKind{corev1.SchemeGroupVersion.WithKind("ConfigMap")}
mapper := newSimpleRESTMapper(scheme, gvks)
cm := &corev1.ConfigMap{
TypeMeta: metav1.TypeMeta{
Kind: "ConfigMap",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "foo", Namespace: "bar",
},
}
cl := fake.NewClientBuilder().
WithScheme(scheme).
WithRESTMapper(mapper).
WithObjects(cm).
Build()
// now test
kind, err := getConfigMapKinds(context.Background(), cl)
require.NoError(t, err)
assert.Equal(t, "ConfigMap", kind)
}running this test will output
=== RUN TestGetConfigMapKind
main_test.go:54:
Error: Not equal:
expected: "ConfigMap"
actual : ""Diff: --- Expected +++ Actual @@ -1 +1 @@ -ConfigMap + Test: TestGetConfigMapKind--- FAIL: TestGetConfigMapKind (0.09s)
With the new version, this test fails because the Kind is stripped here, and the RESTMapper is not used to restore it.
Impact
Many codebases rely on the Kind field being set as expected by the live client (when using a RESTMapper). With these changes, it is no longer possible to test such code using the fake client, as the expected Kind cannot be set or restored.
While aligning the fake client’s behavior with the real client is valuable, the current implementation does not provide a way to retrieve the Kind when working with structured objects. This limitation can significantly affect testing scenarios that rely on this information.