Skip to content

Instantly share code, notes, and snippets.

@ydp
Created July 21, 2022 00:49
Show Gist options
  • Save ydp/8b0655c9cba069f92e89dd2eb8714d36 to your computer and use it in GitHub Desktop.
Save ydp/8b0655c9cba069f92e89dd2eb8714d36 to your computer and use it in GitHub Desktop.
test readyReplicas in unstructured object with client-go
/*
Copyright 2019 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Note: the example only works with the code within the same release/branch.
// Borrowed from https://github.com/kubernetes/client-go/blob/master/examples/dynamic-create-update-delete-deployment
package main
import (
"bufio"
"context"
"flag"
"fmt"
"os"
"path/filepath"
apiv1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/util/homedir"
"k8s.io/client-go/util/retry"
//
// Uncomment to load all auth plugins
// _ "k8s.io/client-go/plugin/pkg/client/auth"
//
// Or uncomment to load specific auth plugins
// _ "k8s.io/client-go/plugin/pkg/client/auth/azure"
// _ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
// _ "k8s.io/client-go/plugin/pkg/client/auth/oidc"
// _ "k8s.io/client-go/plugin/pkg/client/auth/openstack"
)
func main() {
var kubeconfig *string
if home := homedir.HomeDir(); home != "" {
kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")
} else {
kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file")
}
flag.Parse()
namespace := "default"
config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
if err != nil {
panic(err)
}
client, err := dynamic.NewForConfig(config)
if err != nil {
panic(err)
}
deploymentRes := schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"}
deployment := &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": map[string]interface{}{
"name": "demo-deployment",
},
"spec": map[string]interface{}{
"replicas": 2,
"selector": map[string]interface{}{
"matchLabels": map[string]interface{}{
"app": "demo",
},
},
"template": map[string]interface{}{
"metadata": map[string]interface{}{
"labels": map[string]interface{}{
"app": "demo",
},
},
"spec": map[string]interface{}{
"containers": []map[string]interface{}{
{
"name": "web",
"image": "nginx:1.12",
"ports": []map[string]interface{}{
{
"name": "http",
"protocol": "TCP",
"containerPort": 80,
},
},
},
},
},
},
},
},
}
// Create Deployment
fmt.Println("Creating deployment...")
result, err := client.Resource(deploymentRes).Namespace(namespace).Create(context.TODO(), deployment, metav1.CreateOptions{})
if err != nil {
panic(err)
}
fmt.Printf("Created deployment %q.\n", result.GetName())
// Update Deployment
prompt()
fmt.Println("Updating deployment...")
// You have two options to Update() this Deployment:
//
// 1. Modify the "deployment" variable and call: Update(deployment).
// This works like the "kubectl replace" command and it overwrites/loses changes
// made by other clients between you Create() and Update() the object.
// 2. Modify the "result" returned by Get() and retry Update(result) until
// you no longer get a conflict error. This way, you can preserve changes made
// by other clients between Create() and Update(). This is implemented below
// using the retry utility package included with client-go. (RECOMMENDED)
//
// More Info:
// https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency
retryErr := retry.RetryOnConflict(retry.DefaultRetry, func() error {
// Retrieve the latest version of Deployment before attempting update
// RetryOnConflict uses exponential backoff to avoid exhausting the apiserver
result, getErr := client.Resource(deploymentRes).Namespace(namespace).Get(context.TODO(), "demo-deployment", metav1.GetOptions{})
if getErr != nil {
panic(fmt.Errorf("failed to get latest version of Deployment: %v", getErr))
}
// update replicas to 1
if err := unstructured.SetNestedField(result.Object, int64(1), "spec", "replicas"); err != nil {
panic(fmt.Errorf("failed to set replica value: %v", err))
}
// extract spec containers
containers, found, err := unstructured.NestedSlice(result.Object, "spec", "template", "spec", "containers")
if err != nil || !found || containers == nil {
panic(fmt.Errorf("deployment containers not found or error in spec: %v", err))
}
// update container[0] image
if err := unstructured.SetNestedField(containers[0].(map[string]interface{}), "nginx:1.13", "image"); err != nil {
panic(err)
}
if err := unstructured.SetNestedField(result.Object, containers, "spec", "template", "spec", "containers"); err != nil {
panic(err)
}
_, updateErr := client.Resource(deploymentRes).Namespace(namespace).Update(context.TODO(), result, metav1.UpdateOptions{})
return updateErr
})
if retryErr != nil {
panic(fmt.Errorf("update failed: %v", retryErr))
}
fmt.Println("Updated deployment...")
// List Deployments
prompt()
fmt.Printf("Listing deployments in namespace %q:\n", apiv1.NamespaceDefault)
list, err := client.Resource(deploymentRes).Namespace(namespace).List(context.TODO(), metav1.ListOptions{})
if err != nil {
panic(err)
}
for _, d := range list.Items {
fmt.Println()
fmt.Println("==========================================")
fmt.Println("Object:")
fmt.Printf("%v", d.Object)
fmt.Println("==========================================")
fmt.Println("UnstructuredContent:")
fmt.Printf("%v", d.UnstructuredContent())
fmt.Println("==========================================")
fmt.Println()
fmt.Println()
replicas, found, err := unstructured.NestedInt64(d.Object, "spec", "replicas")
if err != nil || !found {
fmt.Printf("Replicas not found for deployment %s: error=%s\n", d.GetName(), err)
continue
}
fmt.Printf(" * %s (%d replicas)\n", d.GetName(), replicas)
readyReplicas, found, err := unstructured.NestedInt64(d.Object, "status", "readyReplicas")
if err != nil || !found {
fmt.Printf("readyReplicas not found for deployment %s: error=%s\n", d.GetName(), err)
continue
}
fmt.Printf(" * %s (%d readyReplicas)\n", d.GetName(), readyReplicas)
}
// Delete Deployment
prompt()
fmt.Println("Deleting deployment...")
deletePolicy := metav1.DeletePropagationForeground
deleteOptions := metav1.DeleteOptions{
PropagationPolicy: &deletePolicy,
}
if err := client.Resource(deploymentRes).Namespace(namespace).Delete(context.TODO(), "demo-deployment", deleteOptions); err != nil {
panic(err)
}
fmt.Println("Deleted deployment.")
}
func prompt() {
fmt.Printf("-> Press Return key to continue.")
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
break
}
if err := scanner.Err(); err != nil {
panic(err)
}
fmt.Println()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment