blog

How to Watch for Changes to Custom Resources in Golang: A Comprehensive Guide

In the world of Kubernetes, Custom Resources (CRs) allow users to extend Kubernetes capabilities by adding their own resource types. Monitoring changes to these resources is crucial for many applications and microservices architectures. In this guide, we will dive deep into how to watch for changes to custom resources in Golang. We will also discuss related concepts like API governance, basic identity authentication, and how to use an AI gateway effectively.

Table of Contents

Introduction

Kubernetes has become the cornerstone for modern application development and deployment. It allows users to define how they want their applications to be run and managed through the concept of resources. Custom resources add additional flexibility and extensibility within this ecosystem. In this article, we will focus on the practical aspects of watching for changes to these resources in Golang.

Understanding Custom Resources

Custom resources in Kubernetes are extensions of its API that allow you to include your own resource types in a Kubernetes cluster. They are defined using Custom Resource Definitions (CRDs), which are essentially a way to describe a new resource type, including its schema and validation rules.

Here’s a simple example of a custom resource definition:

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: myresources.mygroup.example.com
spec:
  group: mygroup.example.com
  names:
    kind: MyResource
    listKind: MyResourceList
    plural: myresources
    singular: myresource
  scope: Namespaced
  versions:
  - name: v1
    served: true
    storage: true
    schema:
      openAPIV3Schema:
        type: object
        properties:
          spec:
            type: object
            properties:
              foo:
                type: string

In this definition, we created a resource named MyResource which contains a property called foo.

The Role of the Client-Go Library

Before diving into the code, it’s essential to understand the package we will use for interacting with Kubernetes APIs in Golang. The client-go library is the official Kubernetes Go client and is the preferred way to interact with Kubernetes clusters from Go applications.

To get started with client-go, you need to include it in your Go project via the go.mod file:

go get k8s.io/client-go@latest

Setting Up Your Go Environment

To effectively utilize client-go, we must set up our Go environment. Here are the key steps involved:

  1. Install Go: If you haven’t already installed Go, head to golang.org to download and install it.

  2. Create a New Directory: Create a directory for your project:

    bash
    mkdir k8s-watch
    cd k8s-watch

  3. Initialize Go Module: Initialize a new Go module:

    bash
    go mod init k8s-watch

  4. Install Dependencies: Install needed packages:

    bash
    go get k8s.io/client-go@latest
    go get k8s.io/apimachinery@latest

Now you are ready to start coding!

Watching for Changes

To watch for changes to a custom resource, we need to set up a Kubernetes client and implement a watch mechanism using the client-go library. Below is an example code snippet to help you get started:

package main

import (
    "context"
    "fmt"
    "log"
    "os"

    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    "k8s.io/client-go/kubernetes"
    "k8s.io/client-go/tools/clientcmd"
    "k8s.io/client-go/tools/clientcmd/api"
    "k8s.io/apimachinery/pkg/watch"
)

// Function to create a Kubernetes client
func createClient() *kubernetes.Clientset {
    kubeconfig := os.Getenv("KUBECONFIG")
    config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
    if err != nil {
        log.Fatalf("Error building kubeconfig: %s", err.Error())
    }

    clientset, err := kubernetes.NewForConfig(config)
    if err != nil {
        log.Fatalf("Error creating Kubernetes client: %s", err.Error())
    }

    return clientset
}

// Function to watch for changes in the custom resource
func watchCustomResource(clientset *kubernetes.Clientset) {
    watchInterface, err := clientset.RESTClient().
        Get().
        Resource("myresources").
        Namespace("default").
        Watch(context.TODO())
    if err != nil {
        log.Fatalf("Error watching custom resource: %s", err.Error())
    }

    for event := range watchInterface.ResultChan() {
        switch event.Type {
        case watch.Added:
            fmt.Printf("Resource Added: %s\n", event.Object)
        case watch.Modified:
            fmt.Printf("Resource Modified: %s\n", event.Object)
        case watch.Deleted:
            fmt.Printf("Resource Deleted: %s\n", event.Object)
        }
    }
}

func main() {
    clientset := createClient()
    watchCustomResource(clientset)
}

In this code, we establish a connection to the Kubernetes API server and perform a watch operation on the custom resource “myresources” within the “default” namespace.

Error Handling

When working with APIs, especially with Kubernetes, proper error handling is vital. You can enhance your error checking and logging throughout your code. Here’s a revised version of our watch function that includes better error handling:

func watchCustomResource(clientset *kubernetes.Clientset) {
    for {
        watchInterface, err := clientset.RESTClient().
            Get().
            Resource("myresources").
            Namespace("default").
            Watch(context.TODO())
        if err != nil {
            log.Printf("Error watching custom resource: %s, retrying...", err.Error())
            continue
        }

        for event := range watchInterface.ResultChan() {
            switch event.Type {
            case watch.Added:
                fmt.Printf("Resource Added: %s\n", event.Object)
            case watch.Modified:
                fmt.Printf("Resource Modified: %s\n", event.Object)
            case watch.Deleted:
                fmt.Printf("Resource Deleted: %s\n", event.Object)
            }
        }
    }
}

Here, if there’s an error while establishing the watch, we log the error and retry the watch operation.

Implementing Basic Identity Authentication

When deploying microservices in Kubernetes, it’s crucial to manage security, particularly what data and services they can access. Basic identity authentication can be implemented in your Golang application for this purpose. In this guide, we won’t cover in-depth security implementations, but here’s a simple example of how you might handle Basic Auth:

In your request to the Kubernetes API, you would modify your createClient function to include basic auth credentials:

import (
    "k8s.io/client-go/tools/clientcmd/api"
)

func createClient(username, password string) *kubernetes.Clientset {
    kubeconfig := os.Getenv("KUBECONFIG")

    config, err := clientcmd.LoadFromFile(kubeconfig)
    if err != nil {
        log.Fatalf("Error loading kubeconfig: %s", err.Error())
    }

    config.Clusters["kubernetes"].InsecureSkipTLSVerify = true
    config.AuthInfos["user"].Username = username
    config.AuthInfos["user"].Password = password

    clientset, err := kubernetes.NewForConfig(config)
    if err != nil {
        log.Fatalf("Error creating Kubernetes client: %s", err.Error())
    }

    return clientset
}

This way, you can pass the username and password parameters when invoking createClient.

Using an AI Gateway in Your Application

With the increasing demand for AI and machine learning capabilities, an AI gateway can be vital for building intelligent services. Tools like APIPark can serve as an excellent method for integrating AI services into your workflows.

Key Features of AI Gateway

  1. API Service Centralized Management: Manage all your API services in one place.
  2. Multi-Tenant Management: Handle multiple applications and user groups efficiently.
  3. Request Logging: Log all requests for auditing and analytics.
  4. Approval Workflows: Ensure compliance through an approval process for API access.

Example of Implementing an AI API Call

Using the AI gateway, here’s an example of making a request to an AI service through an API:

curl --location 'http://your-api-gateway-url/path' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer your_api_token' \
--data '{
    "data": {
        "text": "Hello AI!"
    }
}'

Make sure to replace your-api-gateway-url and your_api_token with your actual API URL and token for accessing the AI service.

Conclusion

This guide has taken a comprehensive look at how to watch for changes to custom resources in Golang, covering everything from setup and implementation to error handling and identity authentication. We’ve also covered the importance of an AI gateway in modern applications for improving service capabilities. Implementing these strategies will not only enhance your Kubernetes applications but also pave the way for future innovations in your architecture.

References


APIPark is a high-performance AI gateway that allows you to securely access the most comprehensive LLM APIs globally on the APIPark platform, including OpenAI, Anthropic, Mistral, Llama2, Google Gemini, and more.Try APIPark now! 👇👇👇


This guide should serve as a solid foundation for anyone interested in working with custom resources in Kubernetes and implementing effective monitoring solutions with Golang.

🚀You can securely and efficiently call the Wenxin Yiyan API on APIPark in just two steps:

Step 1: Deploy the APIPark AI gateway in 5 minutes.

APIPark is developed based on Golang, offering strong product performance and low development and maintenance costs. You can deploy APIPark with a single command line.

curl -sSO https://download.apipark.com/install/quick-start.sh; bash quick-start.sh

APIPark Command Installation Process

In my experience, you can see the successful deployment interface within 5 to 10 minutes. Then, you can log in to APIPark using your account.

APIPark System Interface 01

Step 2: Call the Wenxin Yiyan API.

APIPark System Interface 02