Unit test kubernetes client in Go

10 Jan 2020 · Two minute read · on Gianluca's blog

News! Tech related notes are NOW published to ShippingBytes. See you there! I always felt this was not the right place for me to write consistently about tech and tools. So if you want to read more about that see you at the other side

I write a lot of operations and integrations with Kubernetes those days. You can follow my journey in its dedicated section on this blog “Building Kubernetes”.

I had to write a function recently capable of filtering pods based on assigned annotations.


const (
	ProfefeEnabledAnnotation = "profefe.com/enable"
)

// GetSelectedPods returns all the pods with the profefe annotation enabled
// filtered by the selected labels
func GetSelectedPods(clientset kubernetes.Interface,
	namespace string,
	listOpt metav1.ListOptions) ([]v1.Pod, error) {

	target := []v1.Pod{}
	pods, err := clientset.CoreV1().Pods(namespace).List(listOpt)
	if err != nil {
		return target, err
	}
	for _, pod := range pods.Items {
		enabled, ok := pod.Annotations[ProfefeEnabledAnnotation]
		if ok && enabled == "true" && pod.Status.Phase == v1.PodRunning {
			target = append(target, pod)
		}
	}
	return target, nil
}

This function is pretty easy, but it has a good amount of assertions that we can check. Even more when we have a so well scoped functions writing tests should be almost mandatory.

Covering those use cases will give us a solid foundation to avoid regression when this function will get more complicated (usually that’s the evolution for successful piece of code).

Kubernetes Client Mock

Kubernetes offers a simple and powerful fake client that has a very efficient mechanism to simulate the desired output from a specific request, in our case clientset.CoreV1().Pods(namespace).List(listOpt). You have to pass the slice of runtime.Object you desire when you create a new fake client. Awesome and easy.

clientset: fake.NewSimpleClientset(&v1.Pod{
    ObjectMeta: metav1.ObjectMeta{
        Name:        "influxdb-v2",
        Namespace:   "default",
        Annotations: map[string]string{},
    },
}, &v1.Pod{
    ObjectMeta: metav1.ObjectMeta{
        Name:        "chronograf",
        Namespace:   "default",
        Annotations: map[string]string{},
    },
}),

For example this clientset will return two pods, one called influxdb-v2 and one called chronograf, but you can return what ever you need: Services, Deployments, Ingress, Custom Resource Definition or even a mix of everything.

In practice

I wrote a bunch of tests for kube-profefe that are using a fake client. You can get inspiration over there.

Conclusion

fake client is easy to use, so easy that since I added it in my tool chain for some functions like the one I described here I efficiently do TDD because it makes the iteration over my code way faster.

Something weird with this website? Let me know.