4. Checkpoint

4. Checkpoint: Check custom resource and codes

4.1. Overview

  1. Create CustomResourceDefinition foos.example.com.
  2. Create Foo object.
  3. Read the Foo object from main.go.

4.2. Implement

  1. Create main.go.

    package main
    
    func main() {
    
    }
    
  2. Init k8s.io/klog/v2

    import (
        "k8s.io/klog/v2"
    )
    
    klog.InitFlags(nil)
    
  3. Set kubeconfig from flag.

    If home is detected, use ~/.kube/config as default, otherwise, -kubeconfig argument is required.

    import (
        "flag"
        "path/filepath"
        ...
        "k8s.io/client-go/util/homedir"
    )
    
    var kubeconfig *string
    if home := homedir.HomeDir(); home != "" {
        kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional)")
    } else {
        kubeconfig = flag.String("kubeconfig", "", "absolute path to kubeconfig file")
    }
    flag.Parse()
    
  4. Set config for Kubernetes client with client-go/tools/clientcmd

    config is *restclient.Config

    import (
        ...
        "k8s.io/client-go/tools/clientcmd"
    )
    
    config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
    if err != nil {
        klog.Fatalf("Error building kubeconfig: %s", err.Error())
        klog.FlushAndExit(klog.ExitFlushTimeout, 1)
    }
    
  5. Initialize clientset for our custom resource.

    import (
        clientset "github.com/nakamasato/sample-controller/pkg/generated/clientset/versioned"
    )
    
    exampleClient, err := clientset.NewForConfig(config)
    if err != nil {
        klog.Fatalf("Error building example clientset: %s", err.Error())
        klog.FlushAndExit(klog.ExitFlushTimeout, 1)
    }
    
  6. List the custom resource Foo.

    import (
        "context"
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    )
    
    foos, err := exampleClient.ExampleV1alpha1().Foos("").List(context.Background(), metav1.ListOptions{})
    if err != nil {
        klog.Fatalf("listing foos %s %s", err.Error())
        klog.FlushAndExit(klog.ExitFlushTimeout, 1)
    }
    klog.Infof("length of foos is %d", len(foos.Items))
    

Final main.go:

package main

import (
    "context"
    "flag"
    "path/filepath"

    "k8s.io/client-go/tools/clientcmd"
    "k8s.io/client-go/util/homedir"
    "k8s.io/klog/v2"

    clientset "github.com/nakamasato/sample-controller/pkg/generated/clientset/versioned"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func main() {
    klog.InitFlags(nil)

    var kubeconfig *string
    if home := homedir.HomeDir(); home != "" {
        kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional)")
    } else {
        kubeconfig = flag.String("kubeconfig", "", "absolute path to kubeconfig file")
    }
    flag.Parse()

    config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
    if err != nil {
        klog.Fatalf("Error building kubeconfig: %s", err.Error())
        klog.FlushAndExit(klog.ExitFlushTimeout, 1)
    }

    exampleClient, err := clientset.NewForConfig(config)
    if err != nil {
        klog.Fatalf("Error building example clientset: %s", err.Error())
        klog.FlushAndExit(klog.ExitFlushTimeout, 1)
    }

    foos, err := exampleClient.ExampleV1alpha1().Foos("").List(context.Background(), metav1.ListOptions{})
    if err != nil {
        klog.Fatalf("listing foos %s %s", err.Error())
        klog.FlushAndExit(klog.ExitFlushTimeout, 1)
    }
    klog.Infof("length of foos is %d", len(foos.Items))
}

4.3. Run and check

  1. Build sample-controller

    go mod tidy
    go build
    
  2. Register the CRD.

    kubectl apply -f config/crd/foos.yaml
    
  3. Run sample-controller.

    ./sample-controller
    

    Result: no Foo exists

    length of foos is 0
    
  4. Create sample foo (custom resource) with config/sample/foo.yaml.

    apiVersion: example.com/v1alpha1
    kind: Foo
    metadata:
        name: foo-sample
    spec:
        deploymentName: foo-sample
        replicas: 1
    
    kubectl apply -f config/sample/foo.yaml
    
  5. Run the controller again.

    ./sample-controller
    length of foos is 1
    
  6. Clean up Foo (custom resource).

    kubectl delete -f config/sample/foo.yaml