Way of filter Pods by FieldSelector spec.nodeName when testing kubernetes go client api calls

6/3/2021

As part of writing logic for listing pods within a give k8s node I've the following api call:

func ListRunningPodsByNodeName(kubeClient kubernetes.Interface, nodeName string (*v1.PodList, error) {
  return kubeClient.
	CoreV1().
	Pods("").
    List(context.TODO(), metav1.ListOptions{
        FieldSelector: "spec.nodeName=" + nodeName,
    })
}

In order to test ListRunningPodsByNodeName using the fake client provided by k8s, I came up with the following test initialization:

func TestListRunningPodsByNodeName(t *testing.T) {

	// happy path
	kubeClient := fake.NewSimpleClientset(&v1.Pod{
    ObjectMeta: metav1.ObjectMeta{
        Name:        "pod1",
        Namespace:   "default",
        Annotations: map[string]string{},
    },
		Spec: v1.PodSpec{
			NodeName: "foo",
		},
	}, &v1.Pod{
			ObjectMeta: metav1.ObjectMeta{
					Name:        "pod2",
					Namespace:   "default",
					Annotations: map[string]string{},
			},
			Spec: v1.PodSpec{
				NodeName: "bar",
			},
	})

	got, _ := ListRunningPodsByNodeName(kubeClient, "foo")

	for i, pod := range got.Items {
		fmt.Println(fmt.Sprintf("[%2d] %s", i, pod.GetName()))
	}

	t.Errorf("Error, expecting only one pod")
}

When debugging, I got pod1 and pod2 Pods returned despite I'm filtering by those running within foo node. Using this same approach for filtering by certain metadata work like a charm but can't make this to work in case of filtering by nodeName. ¿Anyone knows why please? I suspect it might be a limitation with the fake client capabilities but not completely sure to open an issue yet

thanks by advance

-- Borja Tur
go
kubernetes

1 Answer

6/3/2021

The fake k8s client does not support filtering by field selector (see this comment). When unit testing with the fake k8s client, it's best to assume that the k8s client will work as expected in the real world (return the correct pods based on your field selector query). In your test, provide the pods to the fake k8s client that your application expects and test your own logic, rather than also testing the query logic of the k8s client.

If it's absolutely critical that the fake client perform the filtering for you, you may be able to use the fake client reactors to inject this custom behavior into the fake client. It just means more boilerplate code.

Anything non-generic (like field selection behavior) can be injected in your tests by adding reactors that deal with specific types of actions, use additional info in the action (in this case, ListAction#GetListRestrictions().Fields), and customize the data returned

I haven't tested this at all but hopefully it gives you something to start with.

client := fake.NewSimpleClientset()
client.AddReactor("*", "MyResource", func(action testing.Action) (handled bool, ret runtime.Object, err error) {
	// Add custom filtering logic here
})
-- Clark McCauley
Source: StackOverflow