Background
Objective is to write a unit test in golang to check whether given deployment in a namespace is up or not. I am using the library "k8s.io/client-go/kubernetes/fake" to do the same.
These are the steps I plan to execute:
- Creata mock kubernetes API using client-go:
kubectl := &KubeClient{
testclient.NewSimpleClientset(),
}
- Create mock deployment with replicas = 1 (initially):
deploymentSpec := &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: RESOURCE_NAME,
Namespace: NAMESPACE,
Labels: resourceLabel,
},
Spec: appsv1.DeploymentSpec{
Replicas: &replicas,
Template: v1.PodTemplateSpec{
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: RESOURCE_NAME,
Image: image,
},
},
},
},
},
}
- Update the replica count to 3 using the client-go Update API:
var updateReplicas int32 = 3
replicaCountPtr := &updateReplicas
result.Spec.Replicas = replicaCountPtr
_, err = kubectl.AppsV1().Deployments(NAMESPACE).Update(context.TODO(), result, metav1.UpdateOptions{})
- Run a goroutine that checks for the deployment is up or not.
Problem
When I query (for example, using kubectl.AppsV1().Deployments(NAMESPACE).Get(...).Status.Replicas
) for Replicas, ReadyReplicas, AvailableReplicas, UpdatedReplicas - These values return 0, and not 3.
Why are the replica status values not getting updated? Could trying to test using "k8s.io/client-go/kubernetes/fake" be the problem here?
Test code
package main
import (
"context"
"log"
"time"
appsv1 "k8s.io/api/apps/v1"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
testclient "k8s.io/client-go/kubernetes/fake"
)
type KubeClient struct {
kubernetes.Interface
}
const NAMESPACE = "default"
const RESOURCE_NAME = "TestResource"
func main() {
var replicas int32 = 1
image := NAMESPACE
resourceLabel := map[string]string{
"env": "test",
}
kubectl := &KubeClient{
testclient.NewSimpleClientset(),
}
// Create deployment
deploymentSpec := &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: RESOURCE_NAME,
Namespace: NAMESPACE,
Labels: resourceLabel,
},
Spec: appsv1.DeploymentSpec{
Replicas: &replicas,
Template: v1.PodTemplateSpec{
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: RESOURCE_NAME,
Image: image,
},
},
},
},
},
}
deployResult, err := kubectl.AppsV1().Deployments(NAMESPACE).Create(context.TODO(), deploymentSpec, metav1.CreateOptions{})
if err != nil {
panic(err)
}
log.Printf("Created deployment %q in namespace '%s'\n", deployResult.GetObjectMeta().GetName(), deployResult.GetNamespace())
result, err := kubectl.AppsV1().Deployments(NAMESPACE).Get(context.TODO(), RESOURCE_NAME, metav1.GetOptions{})
if err != nil {
log.Printf("Failed to fetch deployment %s in the namespace %s with error: %s\n", RESOURCE_NAME, NAMESPACE, err.Error())
}
var updateReplicas int32 = 3
replicaCountPtr := &updateReplicas
result.Spec.Replicas = replicaCountPtr
_, err = kubectl.AppsV1().Deployments(NAMESPACE).Update(context.TODO(), result, metav1.UpdateOptions{})
if err != nil {
log.Printf("Failed to update replica value: %v\n", err)
}
time.Sleep(1 * time.Second)
log.Printf("Updated replica count of deployment %q in namespace %q : (%d)\n", RESOURCE_NAME, NAMESPACE, &replicas)
var status bool
channel := make(chan bool, 1)
go func(bool) {
for {
getResult, getErr := kubectl.AppsV1().Deployments(NAMESPACE).Get(context.TODO(), RESOURCE_NAME, metav1.GetOptions{})
if getErr != nil {
log.Printf("Failed to fetch deployment %s in the namespace %s with error: %s\n", RESOURCE_NAME, NAMESPACE, getErr.Error())
status = false
break
}
log.Printf("Replicas status: %d\n", getResult.Status.ReadyReplicas)
log.Printf("ReadyReplicas status: %d\n", getResult.Status.Replicas)
log.Printf("AvailableReplicas status: %d\n", getResult.Status.AvailableReplicas)
log.Printf("UpdatedReplicas status: %d\n", getResult.Status.UpdatedReplicas)
log.Printf("Replicas spec: %d\n", *(getResult.Spec.Replicas))
if getResult.Status.ReadyReplicas == *(getResult.Spec.Replicas) {
status = true
break
}
status = false
}
channel <- (status)
}(status)
select {
case <-channel:
if status {
log.Printf("Deployment '%s' is up", RESOURCE_NAME)
} else {
log.Printf("Deployment '%s' is NOT up", RESOURCE_NAME)
}
case <-time.After(1 * time.Second):
log.Printf("Deployment '%s' timed out", RESOURCE_NAME)
}
}
Logs
...
2022/11/25 17:46:55 UpdatedReplicas status: 0
2022/11/25 17:46:55 Replicas spec: 3
2022/11/25 17:46:55 Replicas status: 0
2022/11/25 17:46:55 ReadyReplicas status: 0
2022/11/25 17:46:55 AvailableReplicas status: 0
2022/11/25 17:46:55 UpdatedReplicas status: 0
2022/11/25 17:46:55 Replicas spec: 3
2022/11/25 17:46:55 Replicas status: 0
2022/11/25 17:46:55 ReadyReplicas status: 0
2022/11/25 17:46:55 AvailableReplicas status: 0
2022/11/25 17:46:55 UpdatedReplicas status: 0
2022/11/25 17:46:55 Replicas spec: 3
2022/11/25 17:46:55 Deployment 'TestResource' timed out
2022/11/25 17:46:55 Replicas status: 0
2022/11/25 17:46:55 ReadyReplicas status: 0
2022/11/25 17:46:55 AvailableReplicas status: 0
2022/11/25 17:46:55 UpdatedReplicas status: 0
2022/11/25 17:46:55 Replicas spec: 3