DaveUK
DaveUK

Reputation: 1580

Use client-go to simulate 'kubectl wait' for a pod to be ready

In bash scripts, I normally use 'kubectl wait' to block until a certain pod resource is ready, e.g. something similar to this:

kubectl wait --for=condition=Ready --timeout=2m -n mynamespace pod -l myselector

This works well because often times I don't know the exact name of the pod I need to wait for, and 'kubectl wait' allows me to locate the pod based on selectors and then block until it's ready.

Now I need to do something similar in golang code. I have seen examples of using the client-go library to authenticate and 'get' a specific pod by name. But I have a few questions on how to best adapt this example to my needs...

  1. I do not know the exact/full name of the pod to be able to 'Get()' it, which is why 'kubectl wait' is perfect as it allows me to find the pod using selectors. I assume that I should use client-go library to instead do a CoreV1().Pods().List() call instead of a Get() in order to allow me to find the pod I want using selectors?

  2. Also, the pod may not exist immediately and might be created only after 1 minute or so, which 'kubectl wait' handles for me. In code, do I need to loop/sleep and keep performing List() until the pod exists?

  3. Similar question to #2...once the List() does return a pod name, what is the best way in golang to 'wait' for that pod to be in a 'ready' state? I do not want to do any ugly polls with sleeps if avoidable...so is there is a better option using golang 'wait' package or similar? What do you recommend?

Upvotes: 7

Views: 6444

Answers (1)

Rico
Rico

Reputation: 61521

How about doing it exactly the way kubectl does it? Basically, using List(...) to list based on a field selector and then Watch(...)

Snippet:

...
        // List with a name field selector to get the current resourceVersion to watch from (not the object's resourceVersion)
        gottenObjList, err := o.DynamicClient.Resource(info.Mapping.Resource).Namespace(info.Namespace).List(context.TODO(), metav1.ListOptions{FieldSelector: nameSelector})
        if apierrors.IsNotFound(err) {
            return info.Object, true, nil
        }
        if err != nil {
            // TODO this could do something slightly fancier if we wish
            return info.Object, false, err
        }
        if len(gottenObjList.Items) != 1 {
            return info.Object, true, nil
        }
        gottenObj := &gottenObjList.Items[0]
        resourceLocation := ResourceLocation{
            GroupResource: info.Mapping.Resource.GroupResource(),
            Namespace:     gottenObj.GetNamespace(),
            Name:          gottenObj.GetName(),
        }
        if uid, ok := o.UIDMap[resourceLocation]; ok {
            if gottenObj.GetUID() != uid {
                return gottenObj, true, nil
            }
        }

        watchOptions := metav1.ListOptions{}
        watchOptions.FieldSelector = nameSelector
        watchOptions.ResourceVersion = gottenObjList.GetResourceVersion()
        objWatch, err := o.DynamicClient.Resource(info.Mapping.Resource).Namespace(info.Namespace).Watch(context.TODO(), watchOptions)
        if err != nil {
            return gottenObj, false, err
        }
...

✌️

Upvotes: 6

Related Questions