Skip to content

Regression: v0.22.0 clears TypeMeta for structured objects with no way to restore via RestMapper #3302

@DWonMtl

Description

@DWonMtl

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions