Welcome to my GitHub

https://github.com/zq2599/blog_demos

Content: Classification and summary of all original articles and supporting source code, involving Java, Docker, Kubernetes, DevOPS, etc.;

Links to series of articles

  1. client-go actual combat one: preparations
  2. client-go actual combat 2: RESTClient
  3. client-go actual combat three: Clientset
  4. client-go actual combat four: dynamicClient
  5. client-go actual combat five: DiscoveryClient

Overview of this article

  • This article is the second in the "client-go combat" series. We mentioned earlier that there are four client-go clients: RESTClient, ClientSet, DynamicClient, and DiscoveryClient. RESTClient is the most basic version, and the other three are Based on the RESTClient package, today we will learn RESTClient through actual combat coding, and be familiar with the most basic remote operation steps;
  • This article consists of the following parts:
  • Introduction to RESTClient
  • Preparation before each coding
  • Official code
  • verification
  • Key source code analysis

Introduction to RESTClient

  • RESTClient is the most basic client of client-go. It mainly encapsulates HTTP Reqeust, provides RESTful APIs to the outside, and provides rich APIs for various settings. It is more complicated than other clients, but Also more flexible;
  • The basic steps for adding, deleting, modifying and checking kubernetes resources using RESTClient are as follows:
  • Determine the type of resource to be operated (for example, look up the deployment list), go to the official API document to find the path, data structure and other information, which will be used later;
  • Load and configure the kubernetes configuration file (it is exactly the same as the kubeconfig used by kubectl);
  • Generate configuration objects according to the configuration file, and set the configuration objects through the API (such as the requested path, Group, Version, serialization and deserialization tools, etc.);
  • Create a RESTClient instance, the input parameter is the configuration object;
  • Invoke the method of RESTClient instance to initiate a request to the API Server of kubernetes, and use the fluent style to pass in various parameters (for example, specify namespace, resources, etc.). If it is a query request, also pass in the pointer of the data structure instance, and change the data. The structure is used to accept the query results returned by kubernetes;
  • The following actual coding combat is also carried out in accordance with the above process;

Actual content

  • The actual content of this battle is very simple: query all pods under the namespace of <font color="blue">kube-system</font>, and then print several key fields of each pod on the console;
  • Thank you for your patience to listen to me a lot, let’s start the actual combat;

Source download

namelinkRemarks
Project homepagehttps://github.com/zq2599/blog_demosThe project's homepage on GitHub
git warehouse address (https)https://github.com/zq2599/blog_demos.gitThe warehouse address of the source code of the project, https protocol
git warehouse address (ssh)git@github.com:zq2599/blog_demos.gitThe warehouse address of the source code of the project, ssh protocol
  • There are multiple folders in this git project, and client-go related applications are under the <font color="blue">client-go-tutorials</font> folder, as shown in the red box in the following figure:

在这里插入图片描述

  • There are multiple subfolders under the client-go-tutorials folder. The source code for this article is in the <font color="blue">restclientdemo</font> directory, as shown in the red box below:

在这里插入图片描述

Check the official documentation to get the content needed for encoding

在这里插入图片描述

  • Then pay attention to the data structure of the response, as shown in the red box in the following figure, what is returned is:

在这里插入图片描述

  • Click on the content in the red box above to see the details of PodList, which is the data structure we need when coding:

在这里插入图片描述

  • Once you have mastered the detailed information of the request and response, you can start coding;
  • coding

  • Create a new folder restclientdemo, execute the following command in it to create a new module:
go mod init restclientdemo
  • Add the two dependencies of k8s.io/api and k8s.io/client-go. Note that the version must match the kubernetes environment:
go get k8s.io/api@v0.20.0
go get k8s.io/client-go@v0.20.0
  • Create a new main.go, the content is as follows, and detailed comments have been added, so I won’t go into details:
package main

import (
    "context"
    "flag"
    "fmt"
    "k8s.io/client-go/kubernetes/scheme"
    "k8s.io/client-go/rest"
    "k8s.io/client-go/tools/clientcmd"
    "k8s.io/client-go/util/homedir"
    corev1 "k8s.io/api/core/v1"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    "path/filepath"
)

func main() {
    var kubeconfig *string

    // home是家目录,如果能取得家目录的值,就可以用来做默认值
    if home:=homedir.HomeDir(); home != "" {
        // 如果输入了kubeconfig参数,该参数的值就是kubeconfig文件的绝对路径,
        // 如果没有输入kubeconfig参数,就用默认路径~/.kube/config
        kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")
    } else {
        // 如果取不到当前用户的家目录,就没办法设置kubeconfig的默认目录了,只能从入参中取
        kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file")
    }

    flag.Parse()

    // 从本机加载kubeconfig配置文件,因此第一个参数为空字符串
    config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)

    // kubeconfig加载失败就直接退出了
    if err != nil {
        panic(err.Error())
    }

    // 参考path : /api/v1/namespaces/{namespace}/pods
    config.APIPath = "api"
    // pod的group是空字符串
    config.GroupVersion = &corev1.SchemeGroupVersion
    // 指定序列化工具
    config.NegotiatedSerializer = scheme.Codecs

    // 根据配置信息构建restClient实例
    restClient, err := rest.RESTClientFor(config)

    if err!=nil {
        panic(err.Error())
    }

    // 保存pod结果的数据结构实例
    result := &corev1.PodList{}

    //  指定namespace
    namespace := "kube-system"
    // 设置请求参数,然后发起请求
    // GET请求
    err = restClient.Get().
        //  指定namespace,参考path : /api/v1/namespaces/{namespace}/pods
        Namespace(namespace).
        // 查找多个pod,参考path : /api/v1/namespaces/{namespace}/pods
        Resource("pods").
        // 指定大小限制和序列化工具
        VersionedParams(&metav1.ListOptions{Limit:100}, scheme.ParameterCodec).
        // 请求
        Do(context.TODO()).
        // 结果存入result
        Into(result)

    if err != nil {
        panic(err.Error())
    }

    // 表头
    fmt.Printf("namespace\t status\t\t name\n")

    // 每个pod都打印namespace、status.Phase、name三个字段
    for _, d := range result.Items {
        fmt.Printf("%v\t %v\t %v\n",
            d.Namespace,
            d.Status.Phase,
            d.Name)
    }
}
  • After the encoding is completed, execute <font color="blue">go run main.go</font> to obtain the information of all pods under the specified namespace. The console output is as follows:
(base) zhaoqindeMBP:restclientdemo zhaoqin$ go run main.go
namespace     status         name
kube-system     Running     coredns-7f89b7bc75-5pdwc
kube-system     Running     coredns-7f89b7bc75-nvbvm
kube-system     Running     etcd-hedy
kube-system     Running     kube-apiserver-hedy
kube-system     Running     kube-controller-manager-hedy
kube-system     Running     kube-flannel-ds-v84vc
kube-system     Running     kube-proxy-hlppx
kube-system     Running     kube-scheduler-hedy
  • At this point, the RESTClient client is completed from coding to verification;

How to deserialize the received data into a PodList object?

  • The previous code is relatively simple, but there is one thing that arouses my interest. As shown in the red box in the following figure, result is a structure pointer of corev1.PodList type. After restClient receives the data returned by kubernetes, how does it know to deserialize the data into What about the corev1.PodList type (the input parameter type of the Into method is runtime.Object)?

在这里插入图片描述

  • There is a line in the previous code that sets the codec tool: <font color="blue">config.NegotiatedSerializer = scheme.Codecs</font>, expand this scheme.Codecs, you can see that the serialization tool is set to runtime when setting it. Serializer:

在这里插入图片描述

  • The typer field type of Serializer is runtime.ObjectTyper, which is actually runtime.Scheme here, so the ObjectTyper.ObjectKinds method is actually the Scheme.ObjectKinds method, in which GVK is obtained according to s.typeToGVK[t], which is v1.PodList :

在这里插入图片描述

  • With this GVK determined the type of returned data, finally call <font color="blue">caseSensitiveJSONIterator.Unmarshal(data, obj)</font> to complete the deserialization operation from byte array to object:

在这里插入图片描述

  • Finally, there is a key line of code that writes the content of data into the input parameters of the outermost Into method:

在这里插入图片描述

  • The source code analysis is complete. In short, in addition to using reflection to obtain the actual type, there is also the relationship mapping table between the data type maintained by Scheme and GVK;
  • At this point, the actual combat of RESTClient is complete. I hope this article can help you lay the foundation, so that when you experience the other three clients, you will have a clear understanding of the underlying implementation principles;

You are not lonely, Xinchen is with you all the way

  1. Java series
  2. Spring series
  3. Docker series
  4. kubernetes series
  5. database + middleware series
  6. DevOps series

You are not lonely, Xinchen is with you all the way

  1. Java series
  2. Spring series
  3. Docker series
  4. kubernetes series
  5. database + middleware series
  6. DevOps series

Welcome to pay attention to the public account: programmer Xin Chen

Search "Programmer Xin Chen" on WeChat, I am Xin Chen, and I look forward to traveling the Java world with you...
https://github.com/zq2599/blog_demos

程序员欣宸
147 声望24 粉丝

热爱Java和Docker