How to Create and Delete deployment and service using the Unstructured Dynamic Client in Go
Introduction
In this article, we will explore how to use the Go dynamic client to create and manage Kubernetes deployments and services. The unstructured client allows us to work with Kubernetes resources without having to define strong types for each resource. This can be useful when dealing with custom resources or when you want to write generic code that can handle multiple resource types.
Table of Contents:
- Set Up the Kubernetes Dynamic Client
- Create a deployment and service using the unstructured client
- Delete the created deployment and service
- The Main Function
- Assembling the pieces
- Conclusion
Set Up the Kubernetes Dynamic Client
The first step in working with the unstructured client is to set up a dynamic Kubernetes client. In the code snippet, we define a K8sDynamicClient
struct with a dynamic.Interface
field. The dynamic.Interface
is part of the Go Kubernetes client library and allows us to work with third-party and unstructured resources.
type K8sDynamicClient struct {
Client dynamic.Interface
}
func getK8sClient() *K8sDynamicClient {
userHomeDir, err := os.UserHomeDir()
if err != nil {
fmt.Printf("error getting user home dir: %v\n", err)
os.Exit(1)
}
kubeConfigPath := filepath.Join(userHomeDir, ".kube", "config")
fmt.Printf("Using kubeconfig: %s\n", kubeConfigPath)
kubeConfig, err := clientcmd.BuildConfigFromFlags("", kubeConfigPath)
if err != nil {
err := fmt.Errorf("Error getting kubernetes config: %v\n", err)
log.Fatal(err.Error)
}
client, err := dynamic.NewForConfig(kubeConfig)
if err != nil {
err := fmt.Errorf("error getting kubernetes config: %v\n", err)
log.Fatal(err.Error)
}
fmt.Printf("%T\n", client)
return &K8sDynamicClient{
Client: client,
}
}
In the getK8sClient
function, we build a dynamic client using the clientcmd
package and the user’s kubeconfig
file. The kubeconfig
file is typically found in the user’s home directory under the .kube
folder. We create an instance of the K8sDynamicClient
struct and return it.
Create a deployment and service using the unstructured client
To create a deployment using the unstructured client, we define the CreateDeployment
function. This function accepts the name, namespace, image, port, and replicas for the deployment. Using the provided parameters, we create a unstructured.Unstructured
object with the required fields and use the dynamic client to create the deployment in the specified namespace.
func (c *K8sDynamicClient) CreateDeployment(name, namespace, image string, port, replicas int32) {
deploymentRes := schema.GroupVersionResource{
Group: "apps",
Version: "v1",
Resource: "deployments",
}
deploymentObject := &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": map[string]interface{}{
"name": name,
},
"spec": map[string]interface{}{
"replicas": replicas,
"selector": map[string]interface{}{
"matchLabels": map[string]interface{}{
"app": name,
},
},
"template": map[string]interface{}{
"metadata": map[string]interface{}{
"labels": map[string]interface{}{
"app": name,
},
},
"spec": map[string]interface{}{
"containers": []map[string]interface{}{
{
"name": name,
"image": image,
"ports": []map[string]interface{}{
{
"name": "http",
"protocol": "TCP",
"containerPort": port,
},
},
},
},
},
},
},
},
}
fmt.Println("Creating deployment using the unstructured object")
deployment, err := c.Client.Resource(deploymentRes).Namespace(namespace).Create(context.TODO(), deploymentObject, metav1.CreateOptions{})
if err != nil {
log.Fatalf("Failed to create deploymetn: %v\n", err)
}
fmt.Printf("Created deployment: %s", deployment.GetName())
}
func (c *K8sDynamicClient) CreateService(name, selector, namespace string, port, targetPort int32) {
serviceRes := schema.GroupVersionResource{
Group: "",
Version: "v1",
Resource: "services",
}
serviceObject := &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "v1",
"kind": "Service",
"metadata": map[string]interface{}{
"name": name,
},
"spec": map[string]interface{}{
"selector": map[string]interface{}{
"app": selector,
},
"ports": []map[string]interface{}{
{
"name": "http",
"protocol": "TCP",
"port": port,
"targetPort": targetPort,
},
},
"type": "ClusterIP",
},
},
}
service, err := c.Client.Resource(serviceRes).Namespace(namespace).Create(context.TODO(), serviceObject, metav1.CreateOptions{})
if err != nil {
log.Fatalf("Failed to create service: %v\n", err)
}
fmt.Printf("Service created: %v\n", service.GetName())
}
Similarly, we define the CreateService
function to create a service using the unstructured client. This function accepts the name, selector, namespace, port, and target port for the service. We create a unstructured.Unstructured
object with the required fields and use the dynamic client to create the service in the specified namespace.
Delete the created deployment and service
The DeleteDeployment
and DeleteService
functions demonstrate how to delete a deployment and service using the unstructured client. We create a unstructured.Unstructured
object with the required metadata and use the dynamic client to delete the deployment or service in the specified namespace.
func (c *K8sDynamicClient) DeleteDeployment(name, namespace string) {
deploymentRes := schema.GroupVersionResource{
Group: "apps",
Version: "v1",
Resource: "deployments",
}
deploymentObject := &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": map[string]interface{}{
"name": name,
},
},
}
err := c.Client.Resource(deploymentRes).Namespace(namespace).Delete(context.TODO(), deploymentObject.GetName(), metav1.DeleteOptions{})
if err != nil {
log.Fatalf("Failed to delete deployment: %v\n", err)
}
fmt.Printf("Deployment deleted: %v\n", deploymentObject.GetName())
}
func (c *K8sDynamicClient) DeleteService(name, namespace string) {
serviceRes := schema.GroupVersionResource{
Group: "",
Version: "v1",
Resource: "services",
}
serviceObject := &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "v1",
"kind": "Service",
"metadata": map[string]interface{}{
"name": name,
},
},
}
err := c.Client.Resource(serviceRes).Namespace(namespace).Delete(context.TODO(), serviceObject.GetName(), metav1.DeleteOptions{})
if err != nil {
log.Fatalf("Failed to delete service: %v\n", err)
}
fmt.Printf("Service deleted: %v\n", serviceObject.GetName())
}
The Main Function
In the main function, we initialize the dynamic client and create a deployment and service using the CreateDeployment
and CreateService
functions. We then delete the created deployment and service using the DeleteDeployment
and DeleteService
functions.
func main() {
fmt.Println("How to use the unstructred client to create a Kubernetes Deployment")
client := getK8sClient()
deploymentName := "korn"
serviceName := "korn"
namespace := "podkiller"
image := "docker.io/nginx:latest"
// Create a deployment
client.CreateDeployment(deploymentName, namespace, image, 80, 1)
// Create a service
client.CreateService(serviceName, deploymentName, namespace, 80, 80)
// Cleanup resource
client.DeleteDeployment(deploymentName, namespace)
client.DeleteService(serviceName, namespace)
}
Assembling the Pieces
After putting all the pieces together, we have a main.go
file containing all the code we discussed previously. Secondly, we have a go.mod
file containing the dependencies.
Conclusion
This article has demonstrated how to use the Go unstructured client to create and manage Kubernetes deployments and services. By using the unstructured client, you can write code that works with Kubernetes resources without having to define strong types for each resource. This can be especially helpful when dealing with custom resources or when you want to write generic code that can handle multiple resource types.
Subscribe to Faizan Bashir
Get the latest posts delivered right to your inbox