Cantech Knowledge Base

Your Go-To Hosting Resource

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

Kubernetes is a powerful container orchestration platform that enables seamless scaling and management of containerized applications. While Kubernetes provides built-in resources such as Pods and Deployments, it also supports Custom Resource Definitions (CRDs), allowing users to define custom 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.

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 Vultr.
– A Vultr Kubernetes Engine (VKE) 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 VKE Cluster Using kubectl
Once your VKE cluster is deployed, follow these steps to configure access:

1. Log in to your Vultr customer portal.

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

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

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

```sh
$ cd ~/Downloads
$ mv vke-example-6b5a-4e5e-a92e-example.yaml ~/vke.yaml
```

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

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

Verify the connection by running:

```sh
$ 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`: Defines the structure and scope of the custom resource.
– `scope`: Specifies whether the resource is namespaced or cluster-wide.

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:

```sh
$ 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:

```yaml
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

– The `apiVersion` is set to `apiextensions.k8s.io/v1`, the stable version for defining CRDs in Kubernetes.
– The `metadata.name` is `databases.resource.example.com`, which uniquely identifies the CRD.
– The `group` is `resource.example.com`, which is required when using the Kubernetes Go client to interact with custom resources.
– 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`.
– The database custom resource includes fields like `dbName`, `description`, `total`, `available`, `dbType`, and `tags`.
– `total` and `available` are integers constrained to values between 1 and 100.
– `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:

```sh
$ 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:

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

Now you successfully created the custom resource definition. Let’s move on to add a new database to database custom resource definition.
Add a new database resource item into the database custom resource definition. To do it, create mysql_resource_object.yaml with your editor:

$ 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 kind of resource is Database and must match the kind of the custom resource definition you already 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 step, you successfully create the database custom resource definition and added 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.

I. 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.

II. 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.

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

```sh
$ nano deepcopy.go
```

Copy and paste the following code into `deepcopy.go`:

```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

```sh
$ nano register.go
```

Copy and paste the following code into `register.go`:

```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. 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.gogo

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

Now, proceed with implementing the required functions inside 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.

3. Creating main.go 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 vke.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 ~/vke.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+"/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()
 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/vultr/k8s-crd/k8s-crd-full-    demo/vke.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/vultr/k8s-crd/k8s-crd-full-demo/vke.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/vultr/k8s-crd/k8s-crd-full-  demo/vke.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.

April 11, 2025