Cantech Knowledge Base

Your Go-To Hosting Resource

How to Access Kubernetes Custom Resource Definitions (CRDs) Using Client-go?

Introduction

Kubernetes is a powerful container orchestration platform that enables seamless scaling and management of containerized applications. It provides built-in resources such as Pods and Deployments, and also supports custom resource definitions Kubernetes users rely on to define resources tailored to their specific needs.

Benefits of Kubernetes CRDs:

– CRDs allow you to extend Kubernetes’ capabilities by defining resources with customized formats.
– You can manage custom resources using `kubectl`, just like built-in resources.
– Kubernetes automatically handles the scaling of custom resources as needed.
– CRDs integrate seamlessly with Kubernetes API and client tools for programmatic interaction.

One such client tool is client-go, a Go-based library that facilitates interaction with Kubernetes resources programmatically. In this guide, we’ll explore how to access and manage Kubernetes CRDs using client-go. Learn more on What is Kubernetes.

Demonstration Scenario

Imagine your development team relies on Kubernetes for application deployment. When building a new application, you need to verify whether a required database is already available within the cluster. To streamline database management, you create a custom resource that maintains information about databases, such as:

– The list of supported databases.
– The total number of instances.
– Available instances for each database type.

By defining a CRD for databases, you can programmatically retrieve and manage this information using client-go.

Prerequisites

Before proceeding, ensure you have the following:

– A newly deployed Ubuntu 20.04 server on Cantech.
– A Cantech Kubernetes Engine (CKE) cluster (this guide uses version 1.24.4).
– A Go development environment (Go version 1.19 is used in this demo).
– The kubectl command-line tool installed to interact with Kubernetes.

Accessing the CKE Cluster Using kubectl

Once your CKE cluster is deployed, follow these steps to configure access:

1. Log in to your Cantech customer portal.

2. Navigate to the CKE section and select your cluster.

3. Click Download Configuration to get the Kubernetes config file.

The downloaded file will have a name similar to `cke-example-6b5a-4e5e-a92e-example.yaml`. Rename it to `cke.yaml` and move it to your home directory:

$ cd ~/Downloads
$ mv cke-example-6b5a-4e5e-a92e-example.yaml ~/cke.yaml

Next, export the config file as an environment variable so `kubectl` can access the cluster:

$ cd ~
$ echo $HOME  Get the home directory path
$ export KUBECONFIG='${HOME}/cke.yaml'

Verify the connection by running:

$ kubectl get node

If successful, the output should resemble:

NAME                               STATUS   ROLES    AGE     VERSION
k8s-crd-ba11fd0aaa9b   Ready    <none>   6d20h   v1.24.4
k8s-crd-e29c4afea916   Ready    <none>   6d20h   v1.24.4

Now that you have access to the Kubernetes cluster using kubectl, let’s proceed with creating a CRD.
Creating a Custom Resource Definition (CRD) Using kubectl
Kubernetes uses YAML configuration files to define resources and instruct the server on how to handle them. A CRD file provides essential information, including:

apiVersion: The Kubernetes API version.
metadata: Identifies the resource.
spec: Specifies whether the resource is namespaced or cluster-wide.
scope: Defines the structure and scope of the custom resource.

Refer to the official Kubernetes documentation on creating CRDs for a detailed guide on structuring these YAML files.
By defining a CRD for database management, you gain improved visibility and control over your Kubernetes cluster resources. In the next steps, we’ll explore how to interact with CRDs programmatically using client-go.

Creating a Database Custom Resource Definition (CRD)

To define a new database custom resource in Kubernetes, follow these steps:

Step 1: Create a CRD Definition File

Run the following commands to create a directory and a new CRD YAML file:

$ mkdir k8s-crd-demo
$ cd k8s-crd-demo
$ nano dbs_crd.k8s.yaml

Step 2: Define the CRD in YAML

Copy and paste the following YAML code into the dbs_crd.k8s.yaml file and save it:

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: databases.resource.example.com
spec:
group: resource.example.com
versions:
      - name: v1
      served: true
      storage: true
      schema:
          openAPIV3Schema:
          type: object
          properties:
              spec:
              type: object
              properties:
                  dbName:
                    type: string
                    nullable: false
                description:
                  type: string
                  nullable: false
                total:
                  type: integer
                  default: 10
                  minimum: 1
                  maximum: 100
                available:
                  type: integer
                  default: 10
                  minimum: 1
                  maximum: 100
                dbType:
                  type: string
                  enum:
                    - sql
                    - noSQL
                    - timeSeries
                    - messageQueue
                    - caching
                    nullable: false
                tags:
                  type: string
                  nullable: true
            required: ["dbName", "total", "available", "dbType"]
        required: ["spec"]
scope: Cluster
names:
    plural: databases
    singular: database
    kind: Database
    shortNames:
    - db

Step 3: Understanding the CRD Definition

  1.  The apiVersion is set to apiextensions.k8s.io/v1, the stable version for defining CRDs in Kubernetes.
  2. The metadata.name is databases.resource.example.com, which uniquely identifies the CRD.
  3. The group is resource.example.com, which is required when using the Kubernetes Go client to interact with custom resources.
  4. The scope is set to Cluster, meaning the resource can be accessed from anywhere in the cluster. To limit access to a specific namespace, change scope to Namespace.
  5. The database custom resource includes fields like dbName, description, total, available, dbType, and tags.
  6. total and available are integers constrained to values between 1 and 100.
  7. dbType is a string and must be one of sql, noSQL, timeSeries, messageQueue, or caching.

Step 4: Apply the CRD to the Cluster

Run the following command to create the database CRD in your Kubernetes cluster:

$ kubectl apply -f dbs_crd.k8s.yaml

This command uses the apply option to create or update the custom resource, while the -f option specifies the YAML file to apply. If successful, you should see output similar to:

customresourcedefinition.apiextensions.k8s.io/databases.resource.example.com created

The custom resource definition is ready. Now, add a new database to it.

Create a file named mysql_resource_object.yaml with your editor to add a new database resource to the custom resource definition.

$ nano mysql_resource_object.yaml

Copy the following content into mysql_resource_object.yaml:

apiVersion: "resource.example.com/v1"
kind: Database
metadata:
  name: mysql
spec:
   dbName: mysql
   description: Used for storing relation structured data.
   total: 50 
   available: 50 
   dbType: sql
   tags: Web Development, Data Engineering, Embedded software
  • You set the apiVersion for the resource definition with the value resource.example.com/v1.
  • The apiVersion must be in the format of resourceGroup.version.
  • The resource type should be database and must match the custom resource definition you created earlier.
  • The name of the database item is “mysql” with dbType as “sql” and available instances are 50.

Run the following command to add the mysql database item to the database resource definition.

$ kubectl apply -f mysql_resource_object.yaml

Similar to creating the resource definition, use kubectl with the apply option to add a new resource. You should be able to see similar output like:

database.resource.example.com/mysql created

You now successfully added the “mysql” resource to the database custom resource definition. To check the available databases in the Kubernetes cluster, run the following:

$ kubectl get db

You should be able to see the output like:

NAME    AGE
mysql   2m58s

Or you can get detailed information for the database custom resource definition using the following command:

$ kubectl get db -o yaml

The output should look like this:

apiVersion: v1
items:
  - apiVersion: resource.example.com/v1
kind: Database
metadata:
    annotations:
    kubectl.kubernetes.io/last-applied-configuration: |       {"apiVersion":"resource.example.com/v1","kind":"Database","metadata":{"annotations":{},"name":"mysql"},"spec":   {"available":50,"dbName":"mysql","dbType":"sql","description":"Used for storing relation structured data.","tags":"Web  Development, Data Engineering, Embedded software","total":50}}
    creationTimestamp: "2022-11-17T17:58:30Z"
    generation: 1
    name: mysql
    resourceVersion: "1419745"
    uid: 40ed6d7e-a372-4f64-8400-20376fd8fdba
spec:
    available: 50
    dbName: mysql
    dbType: sql
    description: Used for storing relation structured data.
    tags: Web Development, Data Engineering, Embedded software
    total: 50
kind: List
metadata:
resourceVersion: ""

At this point, you’ve set up the database custom resource definition and included the MySQL database.

Let’s move on to see how you can programmatically access the database custom resource definition using Go with the help of Kubernetes go-client tool.

Interact with Kubernetes custom resources using go-client

You must initiate a go module environment and install the needed dependencies to build an app that interacts with the Kubernetes custom resources.

1. Install needed dependencies

Open the terminal and type the following go mod command to initialize the go module environment.

$ go mod init k8s-resource.com/m

The go module will automatically create a go.mod file. Add the following dependencies into your app’s go.mod file to connect with the Kubernetes cluster.

require k8s.io/client-go v0.24.4
require (
    github.com/google/go-cmp v0.5.9 // indirect
    github.com/kr/pretty v0.3.0 // indirect
    github.com/rogpeppe/go-internal v1.8.0 // indirect
    github.com/stretchr/testify v1.7.1 // indirect
    gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
    sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect
    sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
    sigs.k8s.io/yaml v1.2.0 // indirect
    )
require (
    k8s.io/api v0.24.4 // indirect
    k8s.io/apimachinery v0.24.4
    )
require (
    github.com/davecgh/go-spew v1.1.1 // indirect
    github.com/go-logr/logr v1.2.3 // indirect
    github.com/gogo/protobuf v1.3.2 // indirect
    github.com/golang/protobuf v1.5.2 // indirect
    github.com/google/gofuzz v1.2.0 // indirect
    github.com/imdario/mergo v0.3.13 // indirect; indirectap
    github.com/json-iterator/go v1.1.12 // indirect
    github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
    github.com/modern-go/reflect2 v1.0.2 // indirect
    github.com/spf13/pflag v1.0.5 // indirect
    golang.org/x/net v0.2.0 // indirect
    golang.org/x/oauth2 v0.2.0 // indirect
    golang.org/x/sys v0.2.0 // indirect
    golang.org/x/term v0.2.0 // indirect
    golang.org/x/text v0.4.0 // indirect
    golang.org/x/time v0.2.0 // indirect
    google.golang.org/appengine v1.6.7 // indirect
    google.golang.org/protobuf v1.28.1 // indirect
    gopkg.in/inf.v0 v0.9.1 // indirect
    gopkg.in/yaml.v2 v2.4.0 // indirect
    k8s.io/klog/v2 v2.80.1 // indirect
    k8s.io/utils v0.0.0-20221108210102-8e77b1f39fe2 // indirect
    )

NOTE: The version of the go-client library should match the Kubernetes cluster version to prevent incompatible issues. Check out this guide for compatibility matrix details.

Then run go mod tidy to install these dependencies:

$ go mod tidy

Now that you’ve installed the dependencies, let’s write code to interact with the Kubernetes database custom resources.

2. Write the code to interact with the Kubernetes custom resources

Let’s write the code that allows the app to:

  • Create a new custom resource
  • Remove an existing one
  • Get all the current custom resources
  • Get the custom resource by the resource name

To do it, you use several built-in methods from Kubernetes go-client:

type Interface interface {
    GetRateLimiter() flowcontrol.RateLimiter
    Verb(verb string) *Request
    Post() *Request
    Put() *Request
    Patch(pt types.PatchType) *Request
    Get() *Request
    Delete() *Request
    APIVersion() schema.GroupVersion
}

You use the Post method to create a new resource, Get to retrieve all the resources or a specific resource by its name, and Delete to remove an existing resource.

2.1 Implemented Database structs and methods to interact with Kubernetes runtime

  • Create Database structs
    You must create structs for DatabaseSpec, Database, and DatabaseList to interact with the existing database custom resource definition. Run the following commands to create a new database.go file.
$ mkdir api
$ cd api
$ nano database.go

Copy the following codes into the database.go file:

package api

import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 type DatabaseSpec struct {
 DbName      string `json:"dbName"`
 Description string `json:"description,omitempty"`
 Total       int    `json:"total"`
 Available   int    `json:"available"`
 DbType      string `json:"dbType"`
 Tags        string `json:"tags,omitempty"`
 }
 // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
 type Database struct {
 metav1.TypeMeta   `json:",inline"`
 metav1.ObjectMeta `json:"metadata,omitempty"`
 Spec DatabaseSpec `json:"spec"`
 }
 // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
 type DatabaseList struct {
 metav1.TypeMeta `json:",inline"`
 metav1.ListMeta `json:"metadata,omitempty"`
 Items []Database `json:"items"`
 }

The DatabaseSpec have fields that match with the current spec database resource definition are dbName, description, total, available,dbType, and tags. Similarly, the Database and DatabaseList structs consist of fields that match with database resource definition metadata information.

  • Creating DeepCopy Methods

To enable deep copying of custom resources, create a deepcopy.go file and define methods so your application can interact with the Kubernetes runtime.

Create the deepcopy.go File

$ nano deepcopy.go

Copy and paste the following code into deepcopy.go:

package api
import "k8s.io/apimachinery/pkg/runtime"
// DeepCopyInto copies the receiver into the provided object.
func (in *Database) DeepCopyInto(out *Database) {
    out.TypeMeta = in.TypeMeta
    out.ObjectMeta = in.ObjectMeta
    out.Spec = DatabaseSpec{
        DbName:      in.Spec.DbName,
        Description: in.Spec.Description,
        Total:       in.Spec.Total,
        Available:   in.Spec.Available,
        DbType:      in.Spec.DbType,
        Tags:        in.Spec.Tags,
    }
}
// DeepCopyObject creates a new instance of Database and copies the data.
func (in *Database) DeepCopyObject() runtime.Object {
    out := Database{}
    in.DeepCopyInto(&out)
    return &out
}
// DeepCopyObject creates a new instance of DatabaseList and copies the data.
func (in *DatabaseList) DeepCopyObject() runtime.Object {
    out := DatabaseList{}
    out.TypeMeta = in.TypeMeta
    out.ListMeta = in.ListMeta
    if in.Items != nil {
        out.Items = make([]Database, len(in.Items))
        for i := range in.Items {
            in.Items[i].DeepCopyInto(&out.Items[i])
        }
    }
    return &out
}

This code defines the DeepCopyInto method for the Database struct and DeepCopyObject methods for both Database and DatabaseList structs, ensuring proper interaction with the Kubernetes runtime.

  • Adding Schema Types for Kubernetes Runtime

To register custom resources with the Kubernetes API, create a register.go file.

Create the register.go File

$ nano register.go

Copy and paste the following code into register.go:

package api
import (
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    "k8s.io/apimachinery/pkg/runtime"
    "k8s.io/apimachinery/pkg/runtime/schema"
)
const (
    GroupName    = "resource.example.com"
    GroupVersion = "v1"
)
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: GroupVersion}
var (
    SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
    AddToScheme   = SchemeBuilder.AddToScheme
)

// addKnownTypes registers custom resource types with the Kubernetes runtime.
func addKnownTypes(scheme *runtime.Scheme) error {
    scheme.AddKnownTypes(SchemeGroupVersion,
        &Database{},
        &DatabaseList{},
    )
    metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
    return nil
}

This code:
– Defines GroupName and GroupVersion to match the custom resource definition (CRD).
– Implements addKnownTypes to register Database and DatabaseList with the Kubernetes runtime.

At this stage, you have successfully implemented the Go structs, functions, and methods required to interact with the Kubernetes runtime. The next section will focus on defining the Kubernetes client and methods to manage the custom resources.
In this section, we will implement a Kubernetes client and define methods for managing custom resources. Specifically, we will:

  • Create a new resource
  • Retrieve existing resources
  • Delete an existing resource

2.2 Implementing Kubernetes Client and Methods

To interact with Kubernetes custom resources, we need to define the configuration for the Kubernetes REST client. Follow the steps below to set up the client.

Step 1: Define the Kubernetes REST Client Configuration

First, create a new file named api.go to configure the REST client. Run the following commands:

$ cd ..
$ mkdir clientset
$ cd clientset
$ nano api.go

Step 2: Copy the Following Code into api.go:

package clientset
import (
    "context"
    "k8s-resource.com/m/api"
    "k8s.io/apimachinery/pkg/runtime/schema"
    "k8s.io/client-go/kubernetes/scheme"
    "k8s.io/client-go/rest"
)
// ExampleInterface defines the contract for interacting with Database custom resources
type ExampleInterface interface {
    Databases(ctx context.Context) DatabaseInterface
}
// ExampleClient represents a Kubernetes client for handling custom resources
type ExampleClient struct {
    restClient rest.Interface
}
// NewForConfig initializes a new Kubernetes client with the given configuration
func NewForConfig(c *rest.Config) (*ExampleClient, error) {
    config := *c
    config.ContentConfig.GroupVersion = &schema.GroupVersion{
        Group:   api.GroupName,
        Version: api.GroupVersion,
    }
    config.APIPath = "/apis"
    config.NegotiatedSerializer = scheme.Codecs.WithoutConversion()
    config.UserAgent = rest.DefaultKubernetesUserAgent()

    client, err := rest.RESTClientFor(&config)
    if err != nil {
        return nil, err
    }
    return &ExampleClient{restClient: client}, nil
}
// Databases returns a client instance for interacting with Database resources
func (c *ExampleClient) Databases(ctx context.Context) DatabaseInterface {
    return &databaseClient{
        restClient: c.restClient,
        ctx:        ctx,
    }
}

Explanation of the Code

  • The ExampleInterface defines the available database operations.
  • The ExampleClient struct wraps the Kubernetes REST client.
  • The NewForConfig function initializes the client by setting API paths, group versions, and serialization options.
  • The Databases function provides an instance for interacting with Database resources.

Step 3: Add Methods for Managing Custom Resources

Next, you need to define methods for creating, retrieving, and deleting custom resources. For this, create a new file named databases.go.

$ nano databases.go

Copy the following code into the databases.go file.

package clientset
 import (
 "context"
 "k8s-resource.com/m/api"
 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 "k8s.io/client-go/kubernetes/scheme"
 "k8s.io/client-go/rest"
 )
 type DatabaseInterface interface {
 List(opts metav1.ListOptions) (*api.DatabaseList, error)
 Get(name string, options metav1.GetOptions) (*api.Database, error)
 Create(*api.Database) (*api.Database, error)
 Delete(name string, options metav1.DeleteOptions) (*api.Database, error)
 }
 type databaseClient struct {
 restClient rest.Interface
 ctx        context.Context
 }
 func (c *databaseClient) List(opts metav1.ListOptions) (*api.DatabaseList, error) {
 result := api.DatabaseList{}
 err := c.restClient.
     Get().
     AbsPath("/apis/resource.example.com/v1/databases").
     Do(c.ctx).
     Into(&result)
 return &result, err
 }
 func (c *databaseClient) Get(name string, opts metav1.GetOptions) (*api.Database, error) {
 result := api.Database{}
 err := c.restClient.
     Get().
     AbsPath("/apis/resource.example.com/v1/databases").
     Name(name).
     VersionedParams(&opts, scheme.ParameterCodec).
     Do(c.ctx).
     Into(&result)
 return &result, err
 }
 func (c *databaseClient) Create(database *api.Database) (*api.Database, error) {
 result := api.Database{}
 err := c.restClient.
     Post().
     AbsPath("/apis/resource.example.com/v1/databases").
     Body(database).
     Do(c.ctx).
     Into(&result)
 return &result, err
 }
 func (c *databaseClient) Delete(name string, opts metav1.DeleteOptions) (*api.Database, error) {
 result := api.Database{}
 err := c.restClient.
     Delete().
     AbsPath("/apis/resource.example.com/v1/databases").
     Name(name).
     VersionedParams(&opts, scheme.ParameterCodec).
     Do(c.ctx).Into(&result)
 return &result, err
 }

Next, we define essential methods for interacting with Kubernetes custom resources. These methods include:

  • Create: Adds a new resource.
  • Get: Retrieves a resource by name.
  • List: Fetches all available resources.
  • Delete: Removes an existing resource that is no longer needed.

With these methods in place, the Kubernetes client is now fully equipped to interact with custom resources.

2.3 Creating main.gofile to Interact with Kubernetes Resources.

In your next software project, suppose you need to use MongoDB as a database for storing application data. To add a mongodb database into the Database Custom Resource Definition (CRD), follow these steps:

  • Copy the cke.yaml configuration file into the current directory.

Proceed with setting up main.go to interact with the Kubernetes cluster and manage resources efficiently.

$ cd ..
$ cp ~/cke.yaml .
  • Create a main.go file.
$ cd ..
$ nano main.go
  • Add the following code to the main.go file:
package main
 import (
 "context"
 "flag"
 "fmt"
 "log"
 "os"
 "k8s-resource.com/m/api"
 client "k8s-resource.com/m/clientset"
 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 "k8s.io/client-go/tools/clientcmd"
 "k8s.io/client-go/kubernetes/scheme"
 "k8s.io/client-go/rest"
 )
 var kubeconfig string

 func init() {
 path, err := os.Getwd()
 if err != nil {
     log.Println(err)
 }
 flag.StringVar(&kubeconfig, "kubeconfig", path+"/cke.yaml", "path to Kubernetes config file")
 flag.Parse()
 }
 func main() {
 var config *rest.Config
 var err error
 if kubeconfig == "" {
     log.Printf("using in-cluster configuration")
     config, err = rest.InClusterConfig()
 } else {
     log.Printf("using configuration from '%s'", kubeconfig)
     config, err = clientcmd.BuildConfigFromFlags("", kubeconfig)
 }

 if err != nil {
     panic(err)
 }
 api.AddToScheme(scheme.Scheme)
 clientSet, err := client.NewForConfig(config)
 if err != nil {
     panic(err)
 }
 context := context.TODO()

 newDatabase := new(api.Database) // pa == &Student{"", 0}
 newDatabase.Name = "mongodb"
 newDatabase.Kind = "Database" // pa == &Student{"Alice", 0}
 newDatabase.APIVersion = "resource.example.com/v1"
 newDatabase.Spec.DbName = "mongodb"
 newDatabase.Spec.Description = "Used storing unstructured data"
 newDatabase.Spec.Total = 100
 newDatabase.Spec.Available = 50
 newDatabase.Spec.DbType = "noSQL"
 newDatabase.Spec.Tags = "Web Development, nosql data"
 newDatabase.Spec.Available = 70

 projectCreated, err := clientSet.Databases(context).Create(newDatabase)
 if err != nil {
     panic(err)
 }

 fmt.Println(projectCreated)
 }

Here you call the Create method to add mongodb database to the database custom resource definition.

  • Execute the action. Run the main.go file.
$ go run main.go

After running this command, you should see a similar output below:

2022/11/18 02:14:55 using configuration from '/home/example/Projects/Personal/cantech/k8s-crd/k8s-crd-full-    demo/cke.yaml'
 &{{ } {mongodb    f8ba273e-fd1f-4b40-b036-cf13b8c72366 1430720 1 2022-11-18 02:14:55 +0700 +07 <nil> <nil>  map[] map[] [] []  [{main Update resource.example.com/v1 2022-11-18 02:14:55 +0700 +07 FieldsV1 {"f:spec":{".":{},"f:available":{},"f:dbName":{},"f:dbType":{},"f:description":{},"f:tags":{},"f:total":{}}} }]} {mongodb Used storing unstructured data 100 70 noSQL Web Development, nosql data}}

You just added the “mongodb” database. Let’s try to get detailed information about the “mongodb” database using the Get method.

  • Get detailed information for “mongodb” database. To do this, replace the main.go code with the below code.
package main
 import (
 "context"
 "flag"
 "fmt"
 "log"
 "os"
 "k8s-resource.com/m/api"
 client "k8s-resource.com/m/clientset"
 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 "k8s.io/client-go/tools/clientcmd"
 "k8s.io/client-go/kubernetes/scheme"
 "k8s.io/client-go/rest"
 )

 var kubeconfig string
 func init() {
 path, err := os.Getwd()
 if err != nil {
     log.Println(err)
 }
 flag.StringVar(&kubeconfig, "kubeconfig", path+"/vke.yaml", "path to Kubernetes config file")
 flag.Parse()
 }
 func main() {
 var config *rest.Config
 var err error
 if kubeconfig == "" {
     log.Printf("using in-cluster configuration")
     config, err = rest.InClusterConfig()
 } else {
     log.Printf("using configuration from '%s'", kubeconfig)
     config, err = clientcmd.BuildConfigFromFlags("", kubeconfig)
 }
 if err != nil {
     panic(err)
 }
 api.AddToScheme(scheme.Scheme)
 clientSet, err := client.NewForConfig(config)
 if err != nil {
     panic(err)
 }
 context := context.TODO()
 projectGet, err := clientSet.Databases(context).Get("mongodb", metav1.GetOptions{})
 if err != nil {
     panic(err)
 }
 fmt.Println(projectGet)
 }

Then run the command:

$ go run main.go

You should see a similar output as below:

2022/11/18 02:18:20 using configuration from '/home/example/Projects/Personal/cantech/k8s-crd/k8s-crd-full-demo/cke.yaml'
 &{{ } {mongodb    f8ba273e-fd1f-4b40-b036-cf13b8c72366 1430720 1 2022-11-18 02:14:55 +0700 +07 <nil> <nil> map[] map[] [] []  [{main Update resource.example.com/v1 2022-11-18 02:14:55 +0700 +07 FieldsV1 {"f:spec":{".":{},"f:available":{},"f:dbName":{},"f:dbType":{},"f:description":{},"f:tags":{},"f:total":{}}} }]} {mongodb Used storing unstructured data 100 70 noSQL Web Development, nosql data}}

If the MySQL database is no longer needed in the Kubernetes cluster, you can remove it by updating the main.go file with the appropriate deletion logic.

  • Steps to Remove MySQL Resource:

Open the main.go file in your project directory. Replace its content with the following code, which ensures the MySQL database resource is deleted from the cluster. Run the updated code to execute the deletion process. By following these steps, you will successfully remove the MySQL database resource from the Kubernetes cluster.

 package main
 import (
 "context"
 "flag"
 "log"
 "os"
"k8s-resource.com/m/api"
 client "k8s-resource.com/m/clientset"
 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 "k8s.io/client-go/tools/clientcmd"
 "k8s.io/client-go/kubernetes/scheme"
 "k8s.io/client-go/rest"
 )

 var kubeconfig string
 func init() {
 path, err := os.Getwd()
 if err != nil {
     log.Println(err)
 }
 flag.StringVar(&kubeconfig, "kubeconfig", path+"/vke.yaml", "path to Kubernetes config file")
 flag.Parse()
 }
 func main() {
 var config *rest.Config
 var err error
 if kubeconfig == "" {
     log.Printf("using in-cluster configuration")
     config, err = rest.InClusterConfig()
 } else {
     log.Printf("using configuration from '%s'", kubeconfig)
     config, err = clientcmd.BuildConfigFromFlags("", kubeconfig)
 }
 if err != nil {
     panic(err)
 }
 api.AddToScheme(scheme.Scheme)
 clientSet, err := client.NewForConfig(config)
 if err != nil {
     panic(err)
 }
 context := context.TODO()
_, err = clientSet.Databases(context).Delete("mysql", metav1.DeleteOptions{})
 if err != nil {
     panic(err)
 }
 }

Then run:

$ go run main.go
  • Check if the “mysql” database is actually removed. Now, let’s try to get all the current custom resources to see whether you successfully removed the “mysql” database. Replace the existing code in the main.go file with the following content:
package main
 import (
 "context"
 "flag"
 "fmt"
 "log"
 "os"
 "k8s-resource.com/m/api"
 client "k8s-resource.com/m/clientset"
 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 "k8s.io/client-go/tools/clientcmd"
 "k8s.io/client-go/kubernetes/scheme"
 "k8s.io/client-go/rest"
 )
 var kubeconfig string
 func init() {
 path, err := os.Getwd()
 if err != nil {
 log.Println(err)
 }
 flag.StringVar(&kubeconfig, "kubeconfig", path+"/vke.yaml", "path to Kubernetes config file")
 flag.Parse()
 }
 func main() {
 var config *rest.Config
 var err error

 if kubeconfig == "" {
 log.Printf("using in-cluster configuration")
 config, err = rest.InClusterConfig()
 } else {
 log.Printf("using configuration from '%s'", kubeconfig)
 config, err = clientcmd.BuildConfigFromFlags("", kubeconfig)
 }

 if err != nil {
 panic(err)
 }
 api.AddToScheme(scheme.Scheme)
 clientSet, err := client.NewForConfig(config)
 if err != nil {
 panic(err)
 }
 context := context.TODO()
 projects, err := clientSet.Databases(context).List(metav1.ListOptions{})
 if err != nil {
 panic(err)
 }

 for _, k := range projects.Items {
 fmt.Println(k.Name)
 }
 }

Let’s run the main.go file:

$ go run main.go

You should only see the mongodb database displayed in the output.

2022/11/18 02:24:08 using configuration from '/home/example/Projects/Personal/cantech/k8s-crd/k8s-crd-full-  demo/cke.yaml'
mongodb

And that’s how you can interact with Kubernetes custom resources using Kubernetes go-client tool.

Conclusion

This article covered the concept of Kubernetes CRD, its benefits for your projects, and how to use the Kubernetes Go client to interact with CRDs programmatically. Working with Kubernetes can be both exciting and challenging, so be ready to tackle new obstacles along the way.

September 16, 2025