Performing Crud operation on Nginx Deployment with Go-Gin framework

Vasudev Parmar
4 min readMay 20, 2023

--

The client-go library is a Go client library provided by Kubernetes, which allows you to interact with Kubernetes clusters programmatically.

Why use code when directly with ‘kubectl’ commands any Kubernetes manifest can be applied here are the following advantages of the client-go library over ‘kubectl’ commands :-

  • Automation: Enables programmatically automating Kubernetes operations.
  • Customization: Provides flexibility and extensibility for tailored interactions with the Kubernetes API.
  • Integration: Seamlessly integrates Kubernetes operations with Go applications.
  • Performance: Offers faster and more efficient execution compared to external kubectl commands.
  • Error Handling: Provides structured error types and better error handling capabilities.

Here I used the following steps to create nginx deployment through client-go library

Steps

  1. Pull the Gin and client-go library.
go get -u github.com/gin-gonic/gin
go get -u k8s.io/client-go@v0.24.0

2. import libraries into the project

import (
"github.com/gin-gonic/gin"
v1 "k8s.io/api/apps/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
)

3. Set up the route

func main() {

r := gin.Default()
r.GET("/", func(c *gin.Context) {
c.String(http.StatusOK, "Hello, world!")
})
r.Run(":8080")
}

4. Now create a client that talks to K8s APIs


func GetKubernetesClient() (*kubernetes.Clientset, error) {
var kubeconfig = "your kube config file path"
/***
BuildConfigFromFlags used as helper function that
builds configs from a master url or a kubeconfig filepath
***/
config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
if err != nil {
return nil, err
}

/**
setup the client with configuration config
**/

clientset, err := kubernetes.NewForConfig(config)
if err != nil {
return nil, err
}

return clientset, nil
}

5. Create a Route to trigger the deployment

func main() {
r := gin.Default()
r.GET("/", func(c *gin.Context) {
c.String(http.StatusOK, "Hello, world!")
})
r.POST("/create", allRoutes)
r.Run(":8080")
}
//payload 
type DeploymentRequest struct {
Replicas int32 `json:"replicas"`
DeploymentName string `json:"deploymentName"`
}

func int32Ptr(i int32) *int32 {
return &i
}
func allRoutes(c *gin.Context) {
//Get the client
clientset, err := GetKubernetesClient()
//handle the error
if err != nil {
fmt.Print(err)
c.String(http.StatusInternalServerError, "Failed to connect to the Kubernetes cluster")
return
}

switch c.Request.Method {
case "POST":
/**
DeploymentRequest is a payload
**/
var req DeploymentRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.String(http.StatusBadRequest, "Invalid request payload")
return
}

if err := createNginxDeployment(clientset, req); err != nil {
c.String(http.StatusInternalServerError, "Failed to create Nginx deployment")
return
}

c.String(http.StatusOK, "Nginx deployment created successfully")

default:
c.String(http.StatusMethodNotAllowed, "Method not allowed")
}
}

create the createNginxDeployment function

/**
just like the kubernetes manifest for the nginx deployment here
also we will create a template

**/
func createNginxDeployment(clientset *kubernetes.Clientset, req DeploymentRequest) error {
deployment := &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: req.DeploymentName,
},
Spec: appsv1.DeploymentSpec{
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"app": "nginx",
},
},
Replicas: int32Ptr(req.Replicas),
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{
"app": "nginx",
},
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "nginx",
Image: "nginx:latest",
},
},
},
},
},
}
//using inbuild function to create the deployment in kubernetes config
_, err := clientset.AppsV1().Deployments("default").Create(context.TODO(), deployment, metav1.CreateOptions{})
if err != nil {
return err
}

return nil
}

6. To list down all the pods that are running

update the main funtion with GET route

func main() {
r := gin.Default()
r.GET("/", func(c *gin.Context) {
c.String(http.StatusOK, "Hello, world!")
})
r.GET("/getPods", allRoutes)
r.POST("/create", allRoutes)
r.Run(":8080")
}

update the allRoute function to handle the GET request

case "GET":
// List pods

podList, err := clientset.CoreV1().Pods("default").List(context.TODO(), metav1.ListOptions{})
if err != nil {
c.String(http.StatusInternalServerError, "Failed to list pods")
return
}

var podNames []string
for _, pod := range podList.Items {
podNames = append(podNames, pod.Name)
}

for i, val := range podNames {
fmt.Println(i, " ", val)
}

c.String(http.StatusOK, "Pods listed successfully")

7. To update the Nginx deployment

update the main function with PUT route

func main() {
r := gin.Default()
r.GET("/", func(c *gin.Context) {
c.String(http.StatusOK, "Hello, world!")
})
r.GET("/getPods", allRoutes)
r.POST("/create", allRoutes)
r.PUT("/update/:name", allRoutes)
r.Run(":8080")
}

Handle put request in the allroutes function

 case "PUT":
deploymentName := c.Param("name")
var req DeploymentRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.String(http.StatusBadRequest, "Invalid request payload")
return
}

if err := updateNginxDeployment(clientset, deploymentName, req.Replicas); err != nil {
c.String(http.StatusInternalServerError, "Failed to update Nginx deployment")
return
}

c.String(http.StatusOK, "Nginx deployment updated successfully")

create the updateNginxDeployment function

func updateNginxDeployment(clientset *kubernetes.Clientset, deploymentName string, replicas int32) error {
deploymentClient := clientset.AppsV1().Deployments("default")

deployment, err := deploymentClient.Get(context.TODO(), deploymentName, metav1.GetOptions{})
if err != nil {
return err
}

deployment.Spec.Replicas = int32Ptr(replicas)

_, err = deploymentClient.Update(context.TODO(), deployment, metav1.UpdateOptions{})
if err != nil {
return err
}

return nil
}

8. To delete the Nginx deployment

update the main function with DELETE route

func main() {
r := gin.Default()
r.GET("/", func(c *gin.Context) {
c.String(http.StatusOK, "Hello, world!")
})
r.GET("/getPods", allRoutes)
r.POST("/create", allRoutes)
r.PUT("/update/:name", allRoutes)
r.DELETE("/delete/:name", allRoutes)
r.Run(":8080")
}

Handle delete request in the allroutes function

 case "DELETE":
deploymentName := c.Param("name")
if err := deleteNginxDeployment(clientset, deploymentName); err != nil {
c.String(http.StatusInternalServerError, "Failed to delete Nginx deployment")
return
}

c.String(http.StatusOK, "Nginx deployment deleted successfully")

create the deleteNginxDeployment function

func deleteNginxDeployment(clientset *kubernetes.Clientset, deploymentName string) error {
deploymentClient := clientset.AppsV1().Deployments("default")
deletePolicy := metav1.DeletePropagationForeground
if err := deploymentClient.Delete(context.TODO(), deploymentName, metav1.DeleteOptions{
PropagationPolicy: &deletePolicy,
}); err != nil {
return err
}
return nil
}

With client-go, you can create deployments with different configurations, such as defining the number of replicas, specifying container images, setting environment variables, configuring volume mounts, defining resource limits, and more.

The client-go library provides a comprehensive set of APIs and resources for interacting with the Kubernetes API server. You can use it to create deployments, services, pods, config maps, secrets, and other Kubernetes resources programmatically.

In conclusion, by utilizing the client-go library, you have the flexibility to define and customize deployments according to your specific requirements, enabling you to create and manage various types of deployments in Kubernetes.

In my upcoming blog posts, I’ll be writing about how to create custom resources using the client-go library.

--

--