Context
I'm writing a script that uses the k8s.io/client-go library (godocs here) to manipulate Deployments. In particular, I want to add a label selector to every Deployment in my cluster. Deployment label selectors are immutable. So my approach is to:
- Create a copy of each Deployment with the only difference being the name is suffixed with "-temp". This is to minimize downtime of existing Deployments.
- Delete the original Deployments.
- Recreate the original Deployments with the only difference being an additional label selector.
- Delete the temporary Deployments.
I can't just use the client-go library to go through steps 1-4 sequentially because I only want to go onto the next step when the API server considers the previous step to be done. For example, I don't want to do step 3 until the API server says the original Deployments have been deleted. Otherwise, I'll get the error that the Deployment with the same name already exists.
Question
What's the best way to use the client-go library to detect when a Deployment is done being created and deleted and to attach callback functions? I came across the following packages.
But I'm not sure what the differences are between them and which one to use.
I read examples of watch here and informer here. Here's two related SO questions.
Update
It seems like watch provides a lower-level way to watch for changes to resources and receive events about changes. Seems like using the SharedInformerFactory to create a SharedInformer is the way to go.
So far I have
import (
"encoding/json"
"errors"
"flag"
"fmt"
"io/ioutil"
"k8s.io/api/apps/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes"
typedv1 "k8s.io/client-go/kubernetes/typed/apps/v1"
"k8s.io/client-go/tools/cache"
"path/filepath"
"strings"
// We need this import to load the GCP auth plugin which is required to authenticate against GKE clusters.
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
"k8s.io/client-go/tools/clientcmd"
"log"
"os"
)
func main() {
...
factory := informers.NewSharedInformerFactory(kubeclient, 0)
informer := factory.Apps().V1().Deployments().Informer()
stopper := make(chan struct{})
defer close(stopper)
informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
d := obj.(v1.Deployment)
fmt.Printf("Created deployment in namespace %s, name %s.\n", d.GetNamespace(), d.GetName())
if _, ok := d.GetLabels()[tempLabelKey]; ok {
fmt.Printf("Detected temporary deployment created in namespace %s, name %s.\n", d.GetNamespace(), d.GetName())
deploymentToDelete := strings.Replace(d.GetName(), tempSuffix, "", -1)
fmt.Printf("Now deleting previous deployment in namespace %s, name %s.\n", d.GetNamespace(), deploymentToDelete)
deleteDeployment(deploymentToDelete, d.GetNamespace(), kubeclient)
}
},
DeleteFunc: func(obj interface{}) {
d := obj.(v1.Deployment)
fmt.Printf("Deleted deployment in namespace %s, name %s.\n", d.GetNamespace(), d.GetName())
if _, ok := d.GetLabels()[stageLabelKey]; !ok {
fmt.Printf("Detected deployment without stage label was deleted in namespace %s, name %s.\n", d.GetNamespace(), d.GetName())
fmt.Printf("Now creating normal deployment with stage label in namespace %s, name %s.\n", d.GetNamespace(), d.GetName())
deployment := createDeploymentWithNewLabel(stageLabelKey, "production", d)
createDeploymentsOnApi(deployment, kubeclient)
}
},
})
informer.Run(stopper)
}
I ended up using a SharedInformer.
These resources were helpful.
.