client-go源码学习(一):client-go源码结构、Client客户端对象

news2025/1/16 21:50:30

本文基于Kubernetes v1.22.4版本进行源码学习,对应的client-go版本为v0.22.4

1、client-go源码结构

client-go的代码库已经集成到Kubernetes源码中了,源码结构示例如下:

$ tree vendor/k8s.io/client-go -L 1
vendor/k8s.io/client-go
├── discovery
├── dynamic
├── informers
├── kubernetes
├── listers
├── plugin
├── rest
├── scale
├── tools
├── transport
└── util
源码目录说明
discovery提供DiscoveryClient发现客户端
dynamic提供DynamicClient动态客户端
informers每种Kubernetes资源的Informer实现
kubernetes提供ClientSet客户端
listers为每一个Kubernetes资源提供Lister功能,该功能对Get和List请求提供只读的缓存数据
plugin提供OpenStack、GCP和Azure等云服务商授权插件
rest提供RESTClient客户端,对Kubernetes API Server执行RESTful操作
scale提供ScaleClient客户端,用于扩容或缩容Deployment、ReplicaSet、Replication Controller等资源对象
tools提供常用工具,例如SharedInformer、Reflector、DealtFIFO及Indexers。提供Client查询和缓存机制,以减少向kube-apiserver发起的请求数等
transport提供安全的TCP连接,支持Http Stream,某些操作需要在客户端和容器之间传输二进制流,例如exec、attach等操作。该功能由内部的spdy包提供支持
util提供常用方法,例如WorkQueue工作队列、Certificate证书管理等

2、Client客户端对象

client-go支持4种Client客户端对象与Kubernetes API Server交互的方式,Client交互对象如下图所示:

  • RESTClient:最基础的客户端,对HTTP Request进行了封装,实现了RESTful风格的API。ClientSet、DynamicClient及DiscoveryClient客户端都是基于RESTClient实现的
  • ClientSet:在RESTClient的基础上封装了对Resource和Version的管理方法。每一个Resource可以理解为一个客户端,而ClientSet则是多个客户端的集合,每一个Resource和Version都以函数的方式暴露给开发者。ClientSet只能够处理内置资源,它是通过client-gen代码生成器自动生成的
  • DynamicClient:ClientSet仅能访问Kubernetes自带的资源(即Client集合内的资源),不能直接访问CRD自定义资源。DynamicClient能够处理Kubernetes中的所有资源对象,包括Kubernetes内置资源与CRD自定义资源
  • DiscoveryClient:用于发现kube-apiserver所支持的资源组、资源版本、资源信息(即Group、Versions、Resources)

1)、RESTClient

RESTClient是最基础的客户端,对HTTP Request进行了封装,实现了RESTful风格的API。ClientSet、DynamicClient及DiscoveryClient客户端都是基于RESTClient实现的

1)示例代码

使用RESTClient列出default命名空间下的所有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"
)

func main() {
	// 加载kubeconfig配置信息
	config, err := clientcmd.BuildConfigFromFlags("", clientcmd.RecommendedHomeFile)
	if err != nil {
		panic(err)
	}
	// 设置config.APIPath请求的HTTP路径
	config.APIPath = "/api"
	// 设置config.GroupVersion请求的资源组/资源版本
	config.GroupVersion = &corev1.SchemeGroupVersion
	// 设置config.NegotiatedSerializer数据的编解码器
	config.NegotiatedSerializer = scheme.Codecs

	// 通过kubeconfig配置信息实例化RESTClient对象
	restClient, err := rest.RESTClientFor(config)
	if err != nil {
		panic(err)
	}

	result := &corev1.PodList{}
	// RESTClient对象构建HTTP请求参数
	err = restClient.Get().
		// 设置请求的命名空间
		Namespace("default").
		// 设置请求的资源名称
		Resource("pods").
		// VersionedParams函数将一些查询选项(如limit、TimeoutSeconds等)添加到请求参数中
		VersionedParams(&metav1.ListOptions{Limit: 500}, scheme.ParameterCodec).
		// 通过Do函数执行该请求
		Do(context.TODO()).
		// 将kube-apiserver返回的结果(Result对象)解析到corev1.PodList对象中
		Into(result)
	if err != nil {
		panic(err)
	}

	for _, pod := range result.Items {
		fmt.Printf("namespcae:%v,name:%v,status:%v\n", pod.Namespace, pod.Name, pod.Status.Phase)
	}
}

2)源码解析

RESTClient中的Request结构体代码如下,当调用Get()Namespace()Resource()VersionedParams()等函数时,实际是对Request的属性进行赋值:

代码路径:vendor/k8s.io/client-go/rest/request.go

type Request struct {
	c *RESTClient

	warningHandler WarningHandler

	rateLimiter flowcontrol.RateLimiter
	backoff     BackoffManager
	timeout     time.Duration

	// generic components accessible via method setters
	verb       string
	pathPrefix string
	subpath    string
	params     url.Values
	headers    http.Header

	// structural elements of the request that are part of the Kubernetes API conventions
	namespace    string
	namespaceSet bool
	resource     string
	resourceName string
	subresource  string

	// output
	err   error
	body  io.Reader
	retry WithRetry
}

RESTClient发送请求的过程对Go标准库net/http进行了封装,在Do函数中真正执行请求,相关源码如下:

func (r *Request) Do(ctx context.Context) Result {
	var result Result
    // 调用request方法执行请求
	err := r.request(ctx, func(req *http.Request, resp *http.Response) {
        // 将response转换为Result对象
		result = r.transformResponse(resp, req)
	})
	if err != nil {
		return Result{err: err}
	}
	return result
}

func (r *Request) request(ctx context.Context, fn func(*http.Request, *http.Response)) error {
	//Metrics for total request latency
	start := time.Now()
	defer func() {
		metrics.RequestLatency.Observe(ctx, r.verb, r.finalURLTemplate(), time.Since(start))
	}()

	if r.err != nil {
		klog.V(4).Infof("Error in request: %v", r.err)
		return r.err
	}

	if err := r.requestPreflightCheck(); err != nil {
		return err
	}

	client := r.c.Client
	if client == nil {
		client = http.DefaultClient
	}

	// Throttle the first try before setting up the timeout configured on the
	// client. We don't want a throttled client to return timeouts to callers
	// before it makes a single request.
	if err := r.tryThrottle(ctx); err != nil {
		return err
	}

	if r.timeout > 0 {
		var cancel context.CancelFunc
		ctx, cancel = context.WithTimeout(ctx, r.timeout)
		defer cancel()
	}

	// Right now we make about ten retry attempts if we get a Retry-After response.
	var retryAfter *RetryAfter
	for {
        // 1)初始化request对象
		req, err := r.newHTTPRequest(ctx)
		if err != nil {
			return err
		}

		r.backoff.Sleep(r.backoff.CalculateBackoff(r.URL()))
		if retryAfter != nil {
			// We are retrying the request that we already send to apiserver
			// at least once before.
			// This request should also be throttled with the client-internal rate limiter.
			if err := r.tryThrottleWithInfo(ctx, retryAfter.Reason); err != nil {
				return err
			}
			retryAfter = nil
		}
    	// 2)发送请求
		resp, err := client.Do(req)
		updateURLMetrics(ctx, r, resp, err)
		if err != nil {
			r.backoff.UpdateBackoff(r.URL(), err, 0)
		} else {
			r.backoff.UpdateBackoff(r.URL(), err, resp.StatusCode)
		}

		done := func() bool {
			defer readAndCloseResponseBody(resp)

			// if the the server returns an error in err, the response will be nil.
			f := func(req *http.Request, resp *http.Response) {
				if resp == nil {
					return
				}
        		// 3)fn函数(即transformResponse)将response转换为Result对象
				fn(req, resp)
			}

			var retry bool
			retryAfter, retry = r.retry.NextRetry(req, resp, err, func(req *http.Request, err error) bool {
				// "Connection reset by peer" or "apiserver is shutting down" are usually a transient errors.
				// Thus in case of "GET" operations, we simply retry it.
				// We are not automatically retrying "write" operations, as they are not idempotent.
				if r.verb != "GET" {
					return false
				}
				// For connection errors and apiserver shutdown errors retry.
				if net.IsConnectionReset(err) || net.IsProbableEOF(err) {
					return true
				}
				return false
			})
			if retry {
				err := r.retry.BeforeNextRetry(ctx, r.backoff, retryAfter, req.URL.String(), r.body)
				if err == nil {
					return false
				}
				klog.V(4).Infof("Could not retry request - %v", err)
			}
			f(req, resp)
			return true
		}()
		if done {
			return err
		}
	}
}

核心函数request逻辑如下:

  1. 代码1)处初始化request对象,其中会调用r.URL().String()生成请求的RESTful URL,实际是使用RESTClient中Request对象的属性进行拼接,在RESTClient示例代码中生成请求的URL为http://127.0.0.1:8001/api/v1/namespaces/default/pods?limit=500
  2. 代码2)处通过Go标准库net/http向kube-apiserver发送请求,请求得到的结果放到response对象中
  3. 代码3)处fn函数(即transformResponse)将response转换为Result对象

2)、ClientSet

ClientSet在RESTClient的基础上封装了对Resource和Version的管理方法。每一个Resource可以理解为一个客户端,而ClientSet则是多个客户端的集合,每一个Resource和Version都以函数的方式暴露给开发者。多ClientSet多资源集合如下图所示:

ClientSet仅能访问Kubernetes自身内置的资源(即客户端集合内的资源),不能直接访问CRD自定义资源。如果需要ClientSet访问CRD自定义资源,可以通过client-gen代码生成器重新生成ClientSet,在ClientSet集合中自动生成与CRD操作相关的接口

1)示例代码

使用ClientSet列出default命名空间下的所有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"
	"k8s.io/client-go/tools/clientcmd"
)

func main() {
	// 加载kubeconfig配置信息
	config, err := clientcmd.BuildConfigFromFlags("", clientcmd.RecommendedHomeFile)
	if err != nil {
		panic(err)
	}

	// 通过kubeconfig配置信息实例化Clientset对象,该对象用于管理所有Resource的客户端
	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		panic(err)
	}
	// clientset.CoreV1().Pods表示请求core资源组的v1资源版本下的Pod资源对象
	// Pods函数是一个资源接口对象,用于Pod资源对象的管理
	podClient := clientset.CoreV1().Pods(corev1.NamespaceDefault)

	// podClient.List函数通过RESTClient获得Pod列表
	result, err := podClient.List(context.TODO(), metav1.ListOptions{Limit: 500})
	if err != nil {
		panic(err)
	}
	for _, pod := range result.Items {
		fmt.Printf("namespcae:%v,name:%v,status:%v\n", pod.Namespace, pod.Name, pod.Status.Phase)
	}
}

2)源码解析

Clientset中包含了一组Client,每个Group Version都有一个Client,Clientset结构体定义如下:

代码路径:vendor/k8s.io/client-go/kubernetes/clientset.go

type Clientset struct {
	*discovery.DiscoveryClient
	admissionregistrationV1      *admissionregistrationv1.AdmissionregistrationV1Client
	admissionregistrationV1beta1 *admissionregistrationv1beta1.AdmissionregistrationV1beta1Client
	internalV1alpha1             *internalv1alpha1.InternalV1alpha1Client
	appsV1                       *appsv1.AppsV1Client
	appsV1beta1                  *appsv1beta1.AppsV1beta1Client
	appsV1beta2                  *appsv1beta2.AppsV1beta2Client
	authenticationV1             *authenticationv1.AuthenticationV1Client
	authenticationV1beta1        *authenticationv1beta1.AuthenticationV1beta1Client
	authorizationV1              *authorizationv1.AuthorizationV1Client
	authorizationV1beta1         *authorizationv1beta1.AuthorizationV1beta1Client
	autoscalingV1                *autoscalingv1.AutoscalingV1Client
	autoscalingV2beta1           *autoscalingv2beta1.AutoscalingV2beta1Client
	autoscalingV2beta2           *autoscalingv2beta2.AutoscalingV2beta2Client
	batchV1                      *batchv1.BatchV1Client
	batchV1beta1                 *batchv1beta1.BatchV1beta1Client
	certificatesV1               *certificatesv1.CertificatesV1Client
	certificatesV1beta1          *certificatesv1beta1.CertificatesV1beta1Client
	coordinationV1beta1          *coordinationv1beta1.CoordinationV1beta1Client
	coordinationV1               *coordinationv1.CoordinationV1Client
	coreV1                       *corev1.CoreV1Client
	discoveryV1                  *discoveryv1.DiscoveryV1Client
	discoveryV1beta1             *discoveryv1beta1.DiscoveryV1beta1Client
	eventsV1                     *eventsv1.EventsV1Client
	eventsV1beta1                *eventsv1beta1.EventsV1beta1Client
	extensionsV1beta1            *extensionsv1beta1.ExtensionsV1beta1Client
	flowcontrolV1alpha1          *flowcontrolv1alpha1.FlowcontrolV1alpha1Client
	flowcontrolV1beta1           *flowcontrolv1beta1.FlowcontrolV1beta1Client
	networkingV1                 *networkingv1.NetworkingV1Client
	networkingV1beta1            *networkingv1beta1.NetworkingV1beta1Client
	nodeV1                       *nodev1.NodeV1Client
	nodeV1alpha1                 *nodev1alpha1.NodeV1alpha1Client
	nodeV1beta1                  *nodev1beta1.NodeV1beta1Client
	policyV1                     *policyv1.PolicyV1Client
	policyV1beta1                *policyv1beta1.PolicyV1beta1Client
	rbacV1                       *rbacv1.RbacV1Client
	rbacV1beta1                  *rbacv1beta1.RbacV1beta1Client
	rbacV1alpha1                 *rbacv1alpha1.RbacV1alpha1Client
	schedulingV1alpha1           *schedulingv1alpha1.SchedulingV1alpha1Client
	schedulingV1beta1            *schedulingv1beta1.SchedulingV1beta1Client
	schedulingV1                 *schedulingv1.SchedulingV1Client
	storageV1beta1               *storagev1beta1.StorageV1beta1Client
	storageV1                    *storagev1.StorageV1Client
	storageV1alpha1              *storagev1alpha1.StorageV1alpha1Client
}

clientset.CoreV1().Pods函数表示请求core资源组的v1资源版本下的Pod资源对象,其内部设置了APIPath请求的HTTP路径,GroupVersion请求的资源组、资源版本,NegotiatedSerializer数据的编解码器

其中,Pods函数是一个资源接口对象,用于Pod资源对象的管理,例如,对Pod资源执行Create、Update、Delete、Get、List、Watch、Patch等操作,这些操作实际上是对RESTClient进行了封装,可以设置选项(如Limit、TimeoutSeconds等)。podClient.List函数通过RESTClient获得Pod列表,代码如下:

代码路径:vendor/k8s.io/client-go/kubernetes/typed/core/v1/pod.go

func (c *pods) List(ctx context.Context, opts metav1.ListOptions) (result *v1.PodList, err error) {
	var timeout time.Duration
	if opts.TimeoutSeconds != nil {
		timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
	}
	result = &v1.PodList{}
	err = c.client.Get().
		Namespace(c.ns).
		Resource("pods").
		VersionedParams(&opts, scheme.ParameterCodec).
		Timeout(timeout).
		Do(ctx).
		Into(result)
	return
}

3)、DynamicClient

DynamicClient是一种动态客户端,它可以对任意Kubernetes资源进行RESTful操作,包括CRD自定义资源。DynamicClient与ClientSet操作类似,同样封装了RESTClient,同样提供了Create、Update、Delete、Get、List、Watch、Patch等方法

DynamicClient与ClientSet最大的不同之处是,ClientSet仅能访问Kubernetes自带的资源(即Client集合内的资源),不能直接访问CRD自定义资源。ClientSet需要预先实现每种Resource和Version的操作,其内部的数据都是结构化数据(即已知数据结构)。而DynamicClient内部实现了Unstructured,用于处理非结构化数据结构(即无法提前预知数据结构),这也是DynamicClient能够处理CRD自定义资源的关键

DynamicClient的处理过程将Resource(例如PodList)转换成Unstructured结构类型,Kubernetes的所有Resource都可以转换成该结构类型。处理完成后,再将Unstructured转换成PodList。整个过程类似于Go的interface{}断言转换过程。另外,Unstructured结构类型是通过map[string]interface{}转换的

1)示例代码

使用DynamicClient列出default命名空间下的所有Pod资源对象的相关信息,代码如下:

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() {
	// 加载kubeconfig配置信息
	config, err := clientcmd.BuildConfigFromFlags("", clientcmd.RecommendedHomeFile)
	if err != nil {
		panic(err)
	}

	// 通过kubeconfig配置信息实例化dynamicClient对象,该对象用于管理Kubernetes的所有Resource的客户端
	dynamicClient, err := dynamic.NewForConfig(config)
	if err != nil {
		panic(err)
	}

	gvr := schema.GroupVersionResource{Version: "v1", Resource: "pods"}
	unstructObj, err := dynamicClient.
		// 设置请求的资源组、资源版本、资源名称
		Resource(gvr).
		// 设置请求的命名空间
		Namespace(corev1.NamespaceDefault).
		// 获取Pod列表,得到的Pod列表为unstructured.UnstructuredList指针类型
		List(context.TODO(), metav1.ListOptions{Limit: 500})
	if err != nil {
		panic(err)
	}

	podList := &corev1.PodList{}
	// 将unstructured.UnstructuredList转换成PodList类型
	err = runtime.DefaultUnstructuredConverter.FromUnstructured(
		unstructObj.UnstructuredContent(), podList)
	if err != nil {
		panic(err)
	}

	for _, pod := range podList.Items {
		fmt.Printf("namespcae:%v,name:%v,status:%v\n", pod.Namespace, pod.Name, pod.Status.Phase)
	}
}

2)源码解析

DynamicClient对RESTClient进行了封装,List函数代码如下:

代码路径:vendor/k8s.io/client-go/dynamic/simple.go

func (c *dynamicResourceClient) List(ctx context.Context, opts metav1.ListOptions) (*unstructured.UnstructuredList, error) {
	result := c.client.client.Get().AbsPath(c.makeURLSegments("")...).SpecificallyVersionedParams(&opts, dynamicParameterCodec, versionV1).Do(ctx)
	if err := result.Error(); err != nil {
		return nil, err
	}
	retBytes, err := result.Raw()
	if err != nil {
		return nil, err
	}
	uncastObj, err := runtime.Decode(unstructured.UnstructuredJSONScheme, retBytes)
	if err != nil {
		return nil, err
	}
	if list, ok := uncastObj.(*unstructured.UnstructuredList); ok {
		return list, nil
	}

	list, err := uncastObj.(*unstructured.Unstructured).ToList()
	if err != nil {
		return nil, err
	}
	return list, nil
}

c.client.client.Get().AbsPath(c.makeURLSegments("")...).SpecificallyVersionedParams(&opts, dynamicParameterCodec, versionV1)使用RESTClient来构建Request对象,对请求的RESTful URL进行拼接,最终调用RESTClient的Do函数真正执行请求

4)、DiscoveryClient

DiscoveryClient是发现客户端,用于发现Kubernetes API Server所支持的资源组、资源版本、资源信息,还可以将这些信息存储到本地,用于本地缓存,以减轻对Kubernetes API Server访问的压力,缓存信息默认存储于~/.kube/cache

kubectl的api-versions和api-resources命令输出也是通过DiscoveryClient实现的。另外,DiscoveryClient同样在RESTClient的基础上进行了封装

1)示例代码

package main

import (
	"fmt"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/client-go/discovery"
	"k8s.io/client-go/tools/clientcmd"
)

func main() {
	// 加载kubeconfig配置信息
	config, err := clientcmd.BuildConfigFromFlags("", clientcmd.RecommendedHomeFile)
	if err != nil {
		panic(err)
	}

	// 通过kubeconfig配置信息实例化DiscoveryClient对象,该对象用于发现Kubernetes API Server所支持的资源组、资源版本、资源信息的客户端
	discoveryClient, err := discovery.NewDiscoveryClientForConfig(config)
	if err != nil {
		panic(err)
	}

	// 获取Kubernetes API Server所支持的资源组、资源版本、资源信息
	_, apiResourceList, err := discoveryClient.ServerGroupsAndResources()
	if err != nil {
		panic(err)
	}

	for _, list := range apiResourceList {
		gv, err := schema.ParseGroupVersion(list.GroupVersion)
		if err != nil {
			panic(err)
		}
		fmt.Printf("group:%v,version:%v\nresources:\n", gv.Group, gv.Version)
		for _, resource := range list.APIResources {
			fmt.Printf("%v\n", resource.Name)
		}
		fmt.Println()
	}
}

2)源码解析

代码路径:vendor/k8s.io/client-go/discovery/discovery_client.go

func ServerGroupsAndResources(d DiscoveryInterface) ([]*metav1.APIGroup, []*metav1.APIResourceList, error) {
  	// 1)获取Kubernetes API Server所支持的GroupVersion
	sgs, err := d.ServerGroups()
	if sgs == nil {
		return nil, nil, err
	}
	resultGroups := []*metav1.APIGroup{}
	for i := range sgs.Groups {
		resultGroups = append(resultGroups, &sgs.Groups[i])
	}

  	// 2)获取Kubernetes API Server所支持的GroupVersionResources
	groupVersionResources, failedGroups := fetchGroupVersionResources(d, sgs)

	// order results by group/version discovery order
	result := []*metav1.APIResourceList{}
	for _, apiGroup := range sgs.Groups {
		for _, version := range apiGroup.Versions {
			gv := schema.GroupVersion{Group: apiGroup.Name, Version: version.Version}
			if resources, ok := groupVersionResources[gv]; ok {
				result = append(result, resources)
			}
		}
	}

	if len(failedGroups) == 0 {
		return resultGroups, result, nil
	}

	return resultGroups, result, &ErrGroupDiscoveryFailed{Groups: failedGroups}
}

ServerGroups函数实现如下:

func (d *DiscoveryClient) ServerGroups() (apiGroupList *metav1.APIGroupList, err error) {
  	// Get the groupVersions exposed at /api
	v := &metav1.APIVersions{}
	err = d.restClient.Get().AbsPath(d.LegacyPrefix).Do(context.TODO()).Into(v)
	apiGroup := metav1.APIGroup{}
	if err == nil && len(v.Versions) != 0 {
		apiGroup = apiVersionsToAPIGroup(v)
	}
	if err != nil && !errors.IsNotFound(err) && !errors.IsForbidden(err) {
		return nil, err
	}

  	// Get the groupVersions exposed at /apis
	apiGroupList = &metav1.APIGroupList{}
	err = d.restClient.Get().AbsPath("/apis").Do(context.TODO()).Into(apiGroupList)
	if err != nil && !errors.IsNotFound(err) && !errors.IsForbidden(err) {
		return nil, err
	}
	// to be compatible with a v1.0 server, if it's a 403 or 404, ignore and return whatever we got from /api
	if err != nil && (errors.IsNotFound(err) || errors.IsForbidden(err)) {
		apiGroupList = &metav1.APIGroupList{}
	}

	// prepend the group retrieved from /api to the list if not empty
	if len(v.Versions) != 0 {
		apiGroupList.Groups = append([]metav1.APIGroup{apiGroup}, apiGroupList.Groups...)
	}
	return apiGroupList, nil
}

通过RESTClient分别请求/api和/apis接口获取Kubernetes API Server所支持的资源组及对应的版本

Kubernetes中支持两类资源组,分别是拥有组名的资源组和没有组名的资源组

  • 拥有组名的资源组:其表现形式为<group>/<version>/<resource>,例如apps/v1/deployments
  • 没有组名的资源组:被称为Core Groups(核心资源组),其表现形式为/<version>/<resource>,例如/v1/pods

两类资源组表现形式不同,形成的HTTP PATH路径也不同。拥有组名的资源组的HTTP PATH以/apis为前缀,其表现形式为/apis/<group>/<version>/<resource>,例如http://127.0.0.1:8001/apis/app/v1/deployments。没有组名的资源组的HTTP PATH以/api为前缀,其表现形式为/api/<version>/<resource>,例如http://127.0.0.1:8001/api/v1/pods

fetchGroupVersionResources函数中遍历所有的GroupVersion获取其对应的资源信息,实现如下:

func fetchGroupVersionResources(d DiscoveryInterface, apiGroups *metav1.APIGroupList) (map[schema.GroupVersion]*metav1.APIResourceList, map[schema.GroupVersion]error) {
	groupVersionResources := make(map[schema.GroupVersion]*metav1.APIResourceList)
	failedGroups := make(map[schema.GroupVersion]error)

	wg := &sync.WaitGroup{}
	resultLock := &sync.Mutex{}
	for _, apiGroup := range apiGroups.Groups {
		for _, version := range apiGroup.Versions {
			groupVersion := schema.GroupVersion{Group: apiGroup.Name, Version: version.Version}
			wg.Add(1)
			go func() {
				defer wg.Done()
				defer utilruntime.HandleCrash()

        		// 获取GroupVersion对应的资源信息
				apiResourceList, err := d.ServerResourcesForGroupVersion(groupVersion.String())

				// lock to record results
				resultLock.Lock()
				defer resultLock.Unlock()

				if err != nil {
					// TODO: maybe restrict this to NotFound errors
					failedGroups[groupVersion] = err
				}
				if apiResourceList != nil {
					// even in case of error, some fallback might have been returned
					groupVersionResources[groupVersion] = apiResourceList
				}
			}()
		}
	}
	wg.Wait()

	return groupVersionResources, failedGroups
}

参考:

《Kubernetes源码剖析》

2022年最新k8s编程operator篇

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/117221.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《业务安全大讲堂》——2022全年大回顾!

数字化的深入普及&#xff0c;让企业的业务愈加开放互联。企业的核心业务、关键数据、用户信息、基础设施、运营过程等均处于边界模糊且日益开放的环境中&#xff0c;在电商、支付、信贷、账户、交互、交易等各种形态的业务场景中&#xff0c;存在着形式多样的欺诈行为。而业务…

企业数字化转型迫切,团队协同工具何以成为“杀手锏”?

不久前&#xff0c;2022世界互联网大会乌镇峰会开幕&#xff0c;360创始人周鸿祎以“构建SaaS生态&#xff0c;助力数字化共同富裕”为主题发表分论坛演讲&#xff0c;并宣布360集团正式上线SaaS商店&#xff0c;为中小微企业和实体产业提供一站式数字化转型服务&#xff0c;填…

elasticsearch之metric聚合

文章目录1、背景2、准备数据2.1 准备mapping2.2 准备数据3、metric聚合3.1 max 平均值3.1.1 dsl3.1.2 java代码3.2 min最小值3.2.1 dsl3.2.2 java3.3 min最小值3.3.1 dsl3.3.2 java3.4 min最小值3.4.1 dsl3.4.2 java3.5 count(*)3.5.1 dsl3.5.2 java3.6 count(distinct)3.6.1 d…

一款非常萌的桌面工具 --- Bongo Cat Mver 附使用教程

最近看B站的时候发现了一个很好玩的桌面工具&#xff0c;Bongo Cat Mver 通过多方查找资源&#xff0c;终于找到了&#xff0c;并且已经下载使用 O(∩_∩)O 不知道这只小猫是不是特别好看呢&#xff1f;放在你的桌面上 Bongo Cat Mver简介 Bongo Cat Mver 是一款画风非常萌的…

[论文解析] NeRF-Art: Text-Driven Neural Radiance Fields Stylization

文章目录OverviewWhat problem is addressed in the paper?Is it a new problem? If so, why does it matter? If not, why does it still matter?What is the key to the solution?What is the main contribution?What can we learn from ablation studies&#xff1f;P…

vue3 路由 vite方式新建项目【适合新手】

一 配环境、并初始化项目 安装nodejs https://blog.csdn.net/lh155136/article/details/128444850 参考官网https://cn.vuejs.org/guide/quick-start.html#creating-a-vue-application 找个空目录cmd打开黑窗口 初始化项目 npm init vuelatest输入y 输入项目名字&#xff…

大聪明教你学Java | 带你了解 Binlog 实现 MySQL 主从同步的原理及实现方式

前言 &#x1f34a;作者简介&#xff1a; 不肯过江东丶&#xff0c;一个来自二线城市的程序员&#xff0c;致力于用“猥琐”办法解决繁琐问题&#xff0c;让复杂的问题变得通俗易懂。 &#x1f34a;支持作者&#xff1a; 点赞&#x1f44d;、关注&#x1f496;、留言&#x1f4…

MYSQL性能分析

MYSQL性能分析 1.慢SQL查询 开启慢sql查询机制 set global slow_query_logon set global long_query_time2; set global log_queries_not_using_indexesonslow_query_log&#xff1a;慢查询log日志记录 on(开启)/off(关闭)&#xff1b; long_query_time:SQL语句执行时间超过2…

Django学习 Day8

1.查询语句 主要目的是学习在ORM中使用条件查询 Filter&#xff08;条件&#xff09; 语法&#xff1a; MyModel.objects.filter(属性1值1&#xff0c;属性2值2) 作用&#xff1a;返回包含此条件的全部数据集 返回值&#xff1a;QuerySet容器对象&#xff0c;内部存放MyModel实…

元旦礼!2022年国家高新技术企业

​近日&#xff0c;2022年度第一批高新技术企业证书已正式发放。根据《高新技术企业认定管理办法》&#xff08;国科发火〔2016〕32号&#xff09;和《高新技术企业认定管理工作指引》&#xff08;国科发火〔2016〕195号&#xff09;有关规定&#xff0c;北京市高新技术企业认定…

项目实战之旅游网(七)后台分类管理 后台产品管理(上)

目录 一.后台分类管理 二.后台产品管理&#xff08;上&#xff09; 1.产品列表 2.新增产品 3.富文本编辑器 一.后台分类管理 mapper层&#xff1a; public interface CategoryMapper extends BaseMapper<Category> { } service层&#xff1a; Service Transactio…

什么是机器视觉计算机?

机器视觉是用于通过分析视频和图像帮助计算机做出决策的硬件和软件的集成。在制造业中&#xff0c;机器视觉通常用于生产线上&#xff0c;以便在产品的每个阶段进行自动检查。机器视觉还扩展了其在高端监控和安全、生物医学成像甚至视觉引导机器人、车辆中的应用。随着视觉技术…

“虚拟试衣间”项目可行性分析报告

目录 “虚拟试衣间”项目可行性分析报告 1 引言 1.1 标识 1.2 背景 1.3 项目概述 1.3.1 软件的基本描述 1.3.2 项目开发基本描述 1.3.3 投资方 1.4 文档概述 3 可行性分析的前提 3.1 项目的要求 一、功能 二、性能 三、输出 四、输入 五、基本数据流程和处理流程 六、安全与保…

如何从0开始画出一张优秀的架构图

你好&#xff0c;我是悟空。 最近在画项目的技术架构图&#xff0c;找到了一些不错的模板&#xff0c;分享给大家~ 画图工具&#xff1a;ProcessOn。 画图技巧&#xff1a;如何从0开始画出一张优秀的架构图 文末再送 5 本书给大家&#xff01; 业务架构图 定义&#xff1…

深圳东方英文书院顺利通过IB五年评估

2021年12月中旬&#xff0c;国际文凭组织&#xff08;IB&#xff09;正式发布官方的评估报告&#xff0c;我院国际小学第一个五年评估顺利通过&#xff01;书院整个社区一片欢腾&#xff01; 何为IB五年评估&#xff1f;为什么它对我们这么重要&#xff1f;不通过会怎么样&…

六十天训练总结

终于啊&#xff0c;六十天说长不长说短不短&#xff0c;但是按时坚持下来的人也确实是少&#xff0c;非常高兴自己能够做到打卡标语的那样&#xff08;完美坚持&#xff09; 要总结的话&#xff0c;东西还是很多的 开了个目录来存放各个题目&#xff0c;并且道题目都有自己看来…

远程桌面控制工具---NoMachine踩坑记录

NoMachine for mac是一款免费的远程桌面访问工具&#xff0c;这款软件的连接到远程桌面后延迟可以非常低&#xff0c;NX协议在高延迟低带宽的链路上提供了近乎本地速度的响应能力&#xff0c;打破空间和时间的障碍&#xff0c;让您的桌面环游世界。 1.环境配置 服务端&#x…

客快物流大数据项目(一百零一):实时OLAP开发

文章目录 实时OLAP开发 一、实时ETL处理 二、SparkSQL基于DataSourceV2自定义数据源

ABAP Function ALV 使用教程

Function ALV 是实现最简单,使用频率最高的报表 使用Function ALV的关键就是调用SAP标准的程序 REUSE_ALV_LIST_DISPLAY 简单实现: 生成报表 示例代码&#xff1a; REPORT ZABAP_FUNCTION_ALV.DATA gt_itab TYPE TABLE OF SFLIGHT.SELECT * FROM SFLIGHT INTO TABLE gt_it…

线程数,512是否合理?

Web-Server有个配置&#xff0c;工作线程数。Service一般也有个配置&#xff0c;工作线程数。经验丰富的架构师&#xff0c;懂得如何配置这些参数&#xff0c;使得系统的性能达到最优&#xff1a;有些业务设置为CPU核数的2倍&#xff0c;有些业务设置为CPU核数的8倍&#xff0c…