介绍
什么是client-go?client-go是Kubernetes官方提供的Go语言客户端库,用于与Kubernetes API交互。它提供了一组丰富的功能和工具,使开发人员能够轻松地编写Go语言应用程序来管理和操作Kubernetes集群中的资源。
github地址:https://github.com/kubernetes/client-go
主要的目录如下:
- discovery:提供DisconveryClient客户端。
- dynamic:提供DynamicClient客户端。
- examples:提供代码示例。
- informers:每种Kubernetes资源的Informer实现。
- kubernetes:提供ClientSet客户端。
- listers:为每一个Kubernetes资源提供Lister功能,提供只读缓存数据。
- plugin/pkg/client/auth:提供云服务商授权插件。
- rest:提供RESTClient客户端。
- tools:提供常用工具。
- transport:提供安全的TCP连接。
- util:提供常用方法。
主要功能如下:
- 与Kubernetes ApiServer进行通信:client-go提供了对Kubernetes API资源的CRUD(创建、读取、更新、删除)操作。开发人员可以使用client-go来创建、查询、更新和删除各种 Kubernetes 资源,如Pod、Service、Deployment等。
- 控制器开发工具:client-go 提供了一组工具和框架,用于开发自定义控制器。控制器是 Kubernetes 中用于监控和管理资源状态的核心组件,而client-go提供了构建控制器所需的事件监听、状态更新等功能。
- 动态客户端:client-go提供了动态客户端库,允许开发人员与Kubernetes API进行交互。包括使用ClientSet进行基于对象的访问和使用DynamicClient进行基于无类型的访问。
- Informers 和 Lister:client-go提供了Informer和Lister工具,用于监听Kubernetes资源的变化并保持本地缓存的同步。这使得开发人员能够高效地处理资源事件,并避免频繁地向API服务器发出查询请求。
- 身份验证和授权:client-go提供了对Kubernetes集群的身份验证和授权支持。它可以与Kubernetes API服务器进行安全通信,并根据用户的RBAC角色进行授权。
安装
我k8s集群版本是1.24.16,所以这里安装1.24.16版本的client-go,安装文档参考:https://github.com/kubernetes/client-go/blob/master/INSTALL.md
go get k8s.io/client-go@v0.24.16
go get k8s.io/apimachinery/pkg/apis/meta/v1
# apimachinery是最基础的库,包括了核心的数据结构,比如 Scheme、Group、Version、Kind、Resource,即GK、GV、GVK、GVR等
# 如果有的包没有就执行 go mod tidy
客户端
client-go支持4种客户端,RESTClient是最基础的客户端。ClientSet、DynamicClient、DiscoveryClient客户端都是基于RESTClient进行实现的。
下面分别介绍一下每种客户端的用法。
RESTClient
源码地址:https://github.com/kubernetes/client-go/blob/306b201a2d292fd78483fdf6147131926ed25a78/rest/client.go
包rest定义了一个REST客户端库,包括了1个接口Interface和两个结构体ClienContentConfig,RESTClient。
Interface接口:
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
}
Interface接口定义了与Kubernetes REST API进行通信的一组操作,包括Post,Get,Patch,Delete等。
ClienContentConfig结构体:
// ClientContentConfig controls how RESTClient communicates with the server.
type ClientContentConfig struct {
// AcceptContentTypes指定客户端将接受的类型,这是可选的。如果没有设置,ContentType将被用来定义Accept报头
AcceptContentTypes string
// ContentType指定用于与服务器通信格式。此值将被设置为向服务器发出请求时的Accept标头。如果未设置AcceptContentTypes,将使用默认content类型。即使用“application/json”。
ContentType string
// GroupVersion是要通信的API版本。必须在直接初始化RESTClient时提供。初始化客户端时,将使用默认代码版本进行设置。这被用作VersionedParams的默认组版本。
GroupVersion schema.GroupVersion
// 协商器用于获取多种支持的媒体类型的编码器和解码器。
Negotiator runtime.ClientNegotiator
}
RESTClient结构体:
- base:用于构建客户端的URL。
- versionedAPIPath:连接base URL和root 资源的路径段。
- content:描述RESTClient如何对响应进行编码和解码的配置。
- createBackoffMgr:用于创建BackoffManager的函数,该函数将传递给请求。
- rateLimiter:在没有特别指定的情况下,该速率限制器将在此客户端创建的所有请求之间共享。
- warningHandler:在此客户端创建的所有请求之间共享的警告处理程序。如果未设置,将使用默认的警告处理程序。
- Client:用于发送HTTP请求的特定客户端行为。如果未设置,将使用http.DefaultClient。
NewRESTClient函数
func NewRESTClient(baseURL *url.URL, versionedAPIPath string, config ClientContentConfig, rateLimiter flowcontrol.RateLimiter, client *http.Client) (*RESTClient, error) {
NewRESTClient是一个函数,用于创建一个新的RESTClient实例。它接受base、versionedAPIPath、ClientContentConfig、rateLimiter和Client作为参数,并返回一个新的RESTClient实例。检查并设置默认ContentType,检查base的路径是否以斜杠结尾,没有则添加斜杠,并清除URL中的查询参数和片段标识符。
GetRateLimiter方法
返回RESTClient实例的速率限制器。
readExpBackoffConfig函数
func readExpBackoffConfig() BackoffManager {
backoffBase := os.Getenv(envBackoffBase)
backoffDuration := os.Getenv(envBackoffDuration)
backoffBaseInt, errBase := strconv.ParseInt(backoffBase, 10, 64)
backoffDurationInt, errDuration := strconv.ParseInt(backoffDuration, 10, 64)
if errBase != nil || errDuration != nil {
return &NoBackoff{}
}
return &URLBackoff{
Backoff: flowcontrol.NewBackOff(
time.Duration(backoffBaseInt)*time.Second,
time.Duration(backoffDurationInt)*time.Second)}
}
readExpBackoffConfig函数用于读取环境变量配置并返回一个BackoffManager实例。先获取环境变量并赋值,使用strconv.ParseInt函数将backoffBase和backoffDuration解析为int64类型的整数值,并分别赋给backoffBaseInt和backoffDurationInt变量。
如果有解析错误发生,则返回一个NoBackoff的实例。如果没有解析错误发生,则使用flowcontrol.NewBackOff函数创建一个新的BackOff实例,该实例具有指定的backoffBaseInt和backoffDurationInt,使用time.Duration转换为time.Duration类型,然后将其乘以time.Second确保退避时间以秒为单位。
最后将URLBackoff示例作为BackoffManager返回。
Verb方法
返回一个新的Request实例,用于执行指定的REST操作,包括Post、Put、Patch、Get和Delete方法,分别返回相应的Request实例。
APIVersion方法
返回RESTClient实例的API版本。
代码示例
要想在集群外连接k8s集群,首先得有kubeconfig,这就需要k8s.io/client-go/tools/clientcmd
包。或者在集群中使用rest.InClusterConfig()
来挂载serviceaccount到pod的/var/run/secrets/kubernetes.io/serviceaccount
目录来和apiserver进行认证。
下面的代码会列出kube-system命名空间下的所有Pod的状态信息。
package main
import (
"context"
"fmt"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
)
var configFile = "E:\\client-go\\config"
var ApiPath = "api"
var namespace = "kube-system"
var resource = "pods"
func main() {
// 生成kubeconfig配置
config, err := clientcmd.BuildConfigFromFlags("", configFile)
if err != nil {
panic(err)
}
// 指定Kubernetes API路径
config.APIPath = ApiPath
// 指定要使用的API组和版本
config.GroupVersion = &corev1.SchemeGroupVersion
// 指定编解码器
config.NegotiatedSerializer = scheme.Codecs
// 生成RESTClient实例
restClient, err := rest.RESTClientFor(config)
if err != nil {
panic(err)
}
// 创建空的结构体,存储pod列表
podList := &corev1.PodList{}
// 构建HTTP请求参数
// Get请求
if restClient.Get().
// 指定命名空间
Namespace(namespace).
// 指定要获取的资源类型
Resource(resource).
// 设置请求参数,使用metav1.ListOptions结构体设置了Limit参数为500,并使用scheme.ParameterCodec进行参数编码。
VersionedParams(&metav1.ListOptions{Limit: 500}, scheme.ParameterCodec).
// 发送请求并获取响应,使用context.TODO()作为上下文
Do(context.TODO()).
// 将响应解码为podList
Into(podList); err != nil {
panic(err)
}
for _, v := range podList.Items {
fmt.Printf("NameSpace: %v Name: %v Status: %v \n", v.Namespace, v.Name, v.Status.Phase)
}
}
运行结果:
ClientSet
从上面的例子中可以看到,在连接RestClient之前,需要配置基础路径(api)以及资源的GroupVersion和编解码的方式,对于不同的资源(属于不同的GroupVersion)需要不同的配置,操作还是过于繁琐。
所以就出现了ClientSet,它是在RESTClient的基础上封装了对Resource和Version的管理方法。每一个Resource可以理解为一个客户端,而ClientSet则是多个客户端的集合。通过ClientSet,开发人员只需要建立一次连接,便可以通过不同的接口操作k8s中不同的资源,不用再重新定义GroupVersion、再重新建立连接。ClientSet是最常用的客户端对象。
ClientSet操作资源对象时需要指定Group、Version,然后根据Resource获取,仅能访问Kubernetes内置资源,不能直接访问CRD。
首先定义了一个Interface接口和一个Clientset结构体,包括了k8s资源的很多字段,然后实现接口。Discovery()
方法返回了DiscoveryClient客户端。
NewForConfig函数
- 该函数接受一个
rest.Config
对象作为参数,并返回一个新的Clientset实例和一个可选的错误。 - 创建了
configShallowCopy
变量,将传入的rest.Config
参数进行浅拷贝。避免对rest.Config
进行修改。 - 检查UserAgent是否为空,如果为空设置默认的UserAgent,UserAgent通常用于标识客户端的身份和版本信息。
- 使用
rest.HTTPClientFor(&configShallowCopy)
函数创建一个HTTP客户端。该函数根据配置对象创建一个适当的HTTP客户端,并将其与配置对象共享。这样做可以确保多个客户端共享同一个底层的HTTP传输实例,以提高性能和效率。 - 调用
NewForConfigAndClient
函数,将拷贝的配置对象&configShallowCopy
和创建的HTTP客户端作为参数传递给它,以创建一个新的Clientset实例。
NewForConfigAndClient函数
- 该函数接受一个
rest.Config
对象和一个http.Client
对象作为参数,并返回一个新的Clientset实例和一个可选的错误。 - 首先同样对rest.Config参数进行浅拷贝。
- 检查
configShallowCopy
的RateLimiter
字段是否为nil,并且QPS大于0。如果满足这两个条件,说明需要设置速率限制器。同时检查Burst是否大于0,因为当RateLimiter
未设置且QPS大于0时,要求Burst大于 0。如果Burst不满足要求,将返回一个错误。 - 如果满足设置速率限制器的条件,将使用
flowcontrol.NewTokenBucketRateLimiter
函数基于QPS和Burst创建一个令牌桶速率限制器,并将其赋值给configShallowCopy
的RateLimiter
字段。 - 调用资源对应的函数,创建对应的客户端并赋值。
- 返回指向新创建的Clientset实例的指针
&cs
和可能存在的错误。
NewForConfigOrDie函数
- 该函数接受一个
rest.Config
对象作为参数,并返回一个新的Clientset实例。 - 在创建Clientset实例时,如果发生错误则终止程序的执行,以确保创建过程中的任何问题都能被立即发现和处理。
New函数
- 该函数接受一个
rest.Interface
接口类型的参数c,并返回一个新的Clientset实例。 - 首先,创建一个空的Clientset实例cs。然后,使用对象c通过调用相应API包中的New函数创建对应的API客户端。
- 再通过调用
discovery.NewDiscoveryClient
函数,使用传入的对象c创建客户端,并将其赋值给cs.DiscoveryClient
字段。 - 最终,返回指向新创建的Clientset实例的指针
&cs
。 - 这样通过New函数创建的Clientset实例包含了各个API客户端,可以通过调用相应的客户端方法来与Kubernetes API进行交互。调用方可以使用返回的Clientset实例对不同API资源类型进行操作,如创建、更新、删除等。
代码示例
package main
import (
"context"
"fmt"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
)
func generateClient() (*kubernetes.Clientset, error) {
var kubeConfig = "E:\\client-go\\config"
config, err := clientcmd.BuildConfigFromFlags("", kubeConfig)
if err != nil {
return nil, err
}
clientSet, err := kubernetes.NewForConfig(config)
if err != nil {
return clientSet, err
}
return clientSet, err
}
// clientset通过CoreV1()去访问核心api资源
func ListNodes(c *kubernetes.Clientset) error {
nodeList, err := c.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{})
if err != nil {
return err
}
for _, node := range nodeList.Items {
fmt.Printf("nodeName: %v, hostName: %v \n", node.Name, node.Status.Addresses[1].Address)
}
return nil
}
func ListPods(c *kubernetes.Clientset, ns string) error {
pods, err := c.CoreV1().Pods(ns).List(context.TODO(), metav1.ListOptions{})
if err != nil {
return err
}
for _, pod := range pods.Items {
fmt.Printf("NameSpace: %v Name: %v Status: %v \n", pod.Namespace, pod.Name, pod.Status.Phase)
}
return nil
}
func ListDeployments(c *kubernetes.Clientset, ns string) error {
deployment, err := c.AppsV1().Deployments(ns).List(context.TODO(), metav1.ListOptions{})
if err != nil {
return err
}
for _, deploy := range deployment.Items {
fmt.Printf("deploymentName: %v, availableReplicas: %v \n", deploy.Name, deploy.Status.AvailableReplicas)
}
return nil
}
func ListStatefulSets(c *kubernetes.Clientset, ns string) error {
statefulSet, err := c.AppsV1().StatefulSets(ns).List(context.TODO(), metav1.ListOptions{})
if err != nil {
return err
}
for _, sts := range statefulSet.Items {
fmt.Printf("statefulSetName: %v, replicas: %v \n", sts.Name, sts.Status.Replicas)
}
return nil
}
func ListCm(c *kubernetes.Clientset, ns string) error {
cm, err := c.CoreV1().ConfigMaps(ns).List(context.TODO(), metav1.ListOptions{})
if err != nil {
return err
}
for _, cm := range cm.Items {
fmt.Printf("configMapName: %v \n", cm.Name)
}
return nil
}
func ListSvc(c *kubernetes.Clientset, ns string) error {
svc, err := c.CoreV1().Services(ns).List(context.TODO(), metav1.ListOptions{})
if err != nil {
return err
}
for _, svc := range svc.Items {
fmt.Printf("serviceName: %v \n", svc.Name)
}
return nil
}
func ListSecret(c *kubernetes.Clientset, ns string) error {
secret, err := c.CoreV1().Secrets(ns).List(context.TODO(), metav1.ListOptions{})
if err != nil {
return err
}
for _, secret := range secret.Items {
fmt.Printf("secretName: %v \n", secret.Name)
}
return nil
}
func ListPvc(c *kubernetes.Clientset, ns string) error {
pvc, err := c.CoreV1().PersistentVolumeClaims(ns).List(context.TODO(), metav1.ListOptions{})
if err != nil {
return err
}
for _, pvc := range pvc.Items {
fmt.Printf("pvcName: %v \n", pvc.Name)
}
return nil
}
func main() {
var namespace = "kube-system"
// 生成clientSet
client, err := generateClient()
if err != nil {
fmt.Printf("failed to generate client: %v", err)
}
fmt.Println("List Nodes:")
if err = ListNodes(client); err != nil {
fmt.Printf("failed to list nodes: %v", err)
}
fmt.Println("List Pods:")
if err = ListPods(client, namespace); err != nil {
fmt.Printf("failed to list pods: %v", err)
}
fmt.Println("List Deployments:")
if err = ListDeployments(client, namespace); err != nil {
fmt.Printf("failed to list deployments: %v", err)
}
fmt.Println("List StatefulSets:")
if err = ListStatefulSets(client, namespace); err != nil {
fmt.Printf("failed to list statefulSets: %v", err)
}
fmt.Println("List ConfigMaps:")
if err = ListCm(client, namespace); err != nil {
fmt.Printf("failed to list configMaps: %v", err)
}
fmt.Println("List Services:")
if err = ListSvc(client, namespace); err != nil {
fmt.Printf("failed to list services: %v", err)
}
fmt.Println("List Secrets:")
if err = ListSecret(client, namespace); err != nil {
fmt.Printf("failed to list secrets: %v", err)
}
fmt.Println("List PersistentVolumeClaims:")
if err = ListPvc(client, namespace); err != nil {
fmt.Printf("failed to list pvc: %v", err)
}
}
通过kubeconfig字节创建ClientSet客户端
如果没有kubeconfig文件,只有kubeconfig字节,该如何构造ClientSet客户端对象,进而访问APIServer呢?
可以通过clientcmd.RESTConfigFromKubeConfig
方法来构造restConfig,通过restConfig构造clientset,进而访问Kubernetes集群。
源码地址:https://github.com/kubernetes/client-go/blob/master/tools/clientcmd/client_config.go#L127
func RESTConfigFromKubeConfig(configBytes []byte) (*restclient.Config, error) {
clientConfig, err := NewClientConfigFromBytes(configBytes)
if err != nil {
return nil, err
}
return clientConfig.ClientConfig()
}
代码示例
package main
import (
"bytes"
"context"
"encoding/json"
"fmt"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
)
var kubeConfig string = `apiVersion: v1
clusters:
- cluster:
certificate-authority-data: LS0tLS1...LS0tLQo=
server: https://1.2.3.4:6443
name: kubernetes
contexts:
- context:
cluster: kubernetes
user: kubernetes-admin
name: kubernetes-admin@kubernetes
current-context: kubernetes-admin@kubernetes
kind: Config
preferences: {}
users:
- name: kubernetes-admin
user:
client-certificate-data: LS0tLS1...LS0tCg==
client-key-data: LS0tLS1...LS0tCg==`
func genConfig() (*rest.Config, error) {
restConfig, err := clientcmd.RESTConfigFromKubeConfig([]byte(kubeConfig))
if err != nil {
fmt.Printf("failed to generate config: %v", err)
}
return restConfig, err
}
func main() {
// 生成rest.Config
restConfig, err := genConfig()
if err != nil {
fmt.Printf("failed to generate config: %v", err)
}
// 生成clientSet
clientSet, err := kubernetes.NewForConfig(restConfig)
if err != nil {
fmt.Printf("failed to generate client: %v", err)
}
// 获取default namespace
nsList, err := clientSet.CoreV1().Namespaces().List(context.TODO(), metav1.ListOptions{
FieldSelector: "metadata.name=default",
})
if err != nil {
fmt.Printf("failed to list namespaces: %v", err)
}
// 将namespace列表转为json
nsListEncode, err := json.Marshal(nsList)
if err != nil {
fmt.Printf("failed to encode namespaces: %v", err)
}
// 创建bytes.Buffer类型的目标缓冲区
var nsListBuffer bytes.Buffer
// 将json格式化输出
err = json.Indent(&nsListBuffer, nsListEncode, "", " ")
fmt.Printf("namespace list: %v \n", nsListBuffer.String())
}
其中json.Indent
用法如下:
func Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error
dst:目标缓冲区,用于存储格式化后的 JSON 数据。
src:源 JSON 数据,以字节切片形式传入。
prefix:每一行缩进的前缀字符串,通常是空格或制表符。
indent:每一个缩进级别的字符串,通常是空格或制表符。
DynamicClient客户端
DynamicClient是一种动态客户端,可以对任意k8s资源进行操作,包括CRD自定义资源。DynamicClient内部实现了Unstructured,用于处理非结构化数据结构。将Resource例如podlist转换为unstructured类型,k8s的所有resource都可以转换为这个结构类型,处理完之后在转换为podlist。
unstructured源码地址:https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/unstructured.go
需要处理的对象元数据必须是v1类型,才可以使用unstructured。
type Unstructured struct {
// Object可以是string、float、int、bool、[]interface{}或map[string]interface{}等任意JSON对象。
Object map[string]interface{}
}
如果一个controller中需要控制所有的API,那么可以使用DynamicClient。
代码示例
package main
import (
"context"
"fmt"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/tools/clientcmd"
)
func main() {
var configFile = "E:\\client-go\\config"
var ns = "kube-system"
config, err := clientcmd.BuildConfigFromFlags("", configFile)
if err != nil {
panic(err)
}
// 根据配置创建dynamicClient
dynamicClient, err := dynamic.NewForConfig(config)
if err != nil {
panic(err)
}
// 指定要操作资源的版本和类型
gvr := schema.GroupVersionResource{
Version: "v1",
Resource: "pods",
}
// 获取资源,返回的是*unstructured.UnstructuredList指针类型
unStructObj, err := dynamicClient.Resource(gvr).Namespace(ns).List(
context.TODO(),
metav1.ListOptions{},
)
if err != nil {
panic(err)
}
podList := &corev1.PodList{}
// 将unstructured对象转换为podList
if err = runtime.DefaultUnstructuredConverter.FromUnstructured(unStructObj.UnstructuredContent(), podList); err != nil {
panic(err)
}
for _, v := range podList.Items {
fmt.Printf("namespace: %s, name: %s, status: %s\n", v.Namespace, v.Name, v.Status.Phase)
}
}
代码示例2
上面的代码是获取集群中的资源,这个例子是根据提供的deploy.yaml文件内容,使用client-go与apiserver交互,在default namespace下创建一个deployment。
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:stable-alpine
package main
import (
"context"
_ "embed"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/yaml"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/tools/clientcmd"
"log"
)
//go:embed deploy.yaml
var deployYaml string
func main() {
var configFile = "E:\\client-go\\config"
config, err := clientcmd.BuildConfigFromFlags("", configFile)
if err != nil {
panic(err)
}
// 根据配置创建dynamicClient
dynamicClient, err := dynamic.NewForConfig(config)
if err != nil {
panic(err)
}
deployGVR := schema.GroupVersionResource{
Group: "apps",
Version: "v1",
Resource: "deployments",
}
// 使用yaml.Unmarshal()函数将deployYaml解析为unstructured.Unstructured对象
deployObj := &unstructured.Unstructured{}
if err := yaml.Unmarshal([]byte(deployYaml), deployObj); err != nil {
panic(err)
}
// 使用dynamicClient.Resource()指定命名空间和资源选项, Create()方法创建deployment
_, err = dynamicClient.Resource(deployGVR).Namespace("default").Create(context.TODO(), deployObj, v1.CreateOptions{})
if err != nil {
log.Fatalln(err)
}
log.Println("create deployment success")
}
DiscoveryClient
DiscoveryClient是发现客户端,主要用于发现apiserver支持的资源组,资源版本,资源信息。
kubectl的api-version和api-resource也是通过DiscoveryClient来实现的,还可以将信息缓存在本地cache,以减轻api的访问压力,默认在./kube/cache下。
代码示例
func (d *DiscoveryClient) ServerGroupsAndResources() ([]*v1.APIGroup, []*v1.APIResourceList, error)
// ServerGroupsAndResources 返回所有组和版本支持的资源
func ParseGroupVersion(gv string) (GroupVersion, error)
// ParseGroupVersion 将“group/version”字符串转换为 GroupVersion 结构。如果无法解析字符串,则报告错误。
package main
import (
"fmt"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/discovery"
"k8s.io/client-go/tools/clientcmd"
)
func main() {
var configFile = "E:\\client-go\\config"
config, err := clientcmd.BuildConfigFromFlags("", configFile)
if err != nil {
panic(err)
}
// 根据配置创建dynamicClient
discoveryClient, err := discovery.NewDiscoveryClientForConfig(config)
if err != nil {
panic(err)
}
// 获取api资源租和资源列表
_, apiResourceList, err := discoveryClient.ServerGroupsAndResources()
for _, v := range apiResourceList {
// 遍历apiResourceList
gv, err := schema.ParseGroupVersion(v.GroupVersion)
if err != nil {
panic(err)
}
// 太多了加了limit
limit := 3
count := 0
for _, resource := range v.APIResources {
// 输出每个资源组中的前三个资源的组、版本和名称。
fmt.Printf("group: %s, version: %s, name: %s\n", gv.Group, gv.Version, resource.Name)
count++
if count >= limit {
break
}
}
}
}