client-go之scale包源码分析

scale包

获取实现了RESTMapper(通过discovery client操作apiserver,通过gvr获取gvr/gvk,或者获取RESTMapping)接口的结构体

util.go

  • 接口
    // PreferredResourceMapper 确定要scale资源的首选版本
    type PreferredResourceMapper interface {
    	// ResourceFor 获取部分资源并返回其中首选资源。
    	ResourceFor(resource schema.GroupVersionResource) (preferredResource schema.GroupVersionResource, err error)
    }
    
    // ScaleKindResolver 知道资源与其scale子资源的GroupVersionKind 之间的关系.
    type ScaleKindResolver interface {
    	// ScaleForResource 返回给定 GroupVersionResource 的scale子资源的 GroupVersionKind。
    	ScaleForResource(resource schema.GroupVersionResource) (scaleVersion schema.GroupVersionKind, err error)
    }
    
    
  • 函数
    // NewDiscoveryScaleKindResolver 创建一个新的 ScaleKindResolver,使用参数disovery client来为不同的资源解析正确的 Scale GroupVersionKind。
    func NewDiscoveryScaleKindResolver(client discovery.ServerResourcesInterface) ScaleKindResolver {
      // 构建代理ScaleKindResolver
    	base := &discoveryScaleResolver{
    		discoveryClient: client,
    	}
      // 初始化cachedScaleKindResolver
    	return &cachedScaleKindResolver{
    		base:  base,
    		cache: make(map[schema.GroupVersionResource]schema.GroupVersionKind),
    	}
    }
    
    // NewScaleConverter 创建一个新的 ScaleConverter,用于在autoscaling/v1 和 extensions/v1beta1 和apps/(v1/v1beta1/v1beta2)之间进行转换 Scales。
    func NewScaleConverter() *ScaleConverter {
      // k8s/apimachine中的scheme
    	scheme := runtime.NewScheme()
      // 添加autoscaling/v1 和 extensions/v1beta1 和apps/(v1/v1beta1/v1beta2)到scheme
    	utilruntime.Must(scaleautoscaling.AddToScheme(scheme))
    	utilruntime.Must(scalescheme.AddToScheme(scheme))
    	utilruntime.Must(scaleext.AddToScheme(scheme))
    	utilruntime.Must(scaleextint.AddToScheme(scheme))
    	utilruntime.Must(scaleappsint.AddToScheme(scheme))
    	utilruntime.Must(scaleappsv1beta1.AddToScheme(scheme))
    	utilruntime.Must(scaleappsv1beta2.AddToScheme(scheme))
      // 初始化ScaleConverter
    	return &ScaleConverter{
    		scheme: scheme,
    		codecs: serializer.NewCodecFactory(scheme),
    		internalVersioner: runtime.NewMultiGroupVersioner(
    			scalescheme.SchemeGroupVersion,
    			schema.GroupKind{Group: scaleext.GroupName, Kind: "Scale"},
    			schema.GroupKind{Group: scaleautoscaling.GroupName, Kind: "Scale"},
              // 其实下面这两个这需要存在一个即可,因为在使用MultiGroupVersioner的KindForGroupVersionKinds获取target gvk时,判断的是group和kind,这两个的group和kind都一样
    			schema.GroupKind{Group: scaleappsv1beta1.GroupName, Kind: "Scale"},
    			schema.GroupKind{Group: scaleappsv1beta2.GroupName, Kind: "Scale"},
    		),
    	}
    }
    
  • 结构体
    // discoveryScaleResolver 是一个 ScaleKindResolver,它使用 DiscoveryInterface 将资源与其scale子资源类型相关联
    type discoveryScaleResolver struct {
      discoveryClient discovery.ServerResourcesInterface
    }
    
    func (r *discoveryScaleResolver) ScaleForResource(inputRes schema.GroupVersionResource) (scaleVersion schema.GroupVersionKind, err error) {
      // 通过discovery client获取gv下的resources
    	groupVerResources, err := r.discoveryClient.ServerResourcesForGroupVersion(inputRes.GroupVersion().String())
    	if err != nil {
    		return schema.GroupVersionKind{}, fmt.Errorf("unable to fetch discovery information for %s: %v", inputRes.String(), err)
    	}
      // 遍历resources
    	for _, resource := range groupVerResources.APIResources {
          // 把resource 的name按照匹配的"/"切分成两部分
    		resourceParts := strings.SplitN(resource.Name, "/", 2)
          // 如果resourceParts的长度不为2或者resourceParts的首个str不等于参数gvr的Resource或者resourceParts的第二个str不等于scale
    		if len(resourceParts) != 2 || resourceParts[0] != inputRes.Resource || resourceParts[1] != "scale" {
    			// 跳过非scale资源,或不是我们寻找的scale资源
    			continue
    		}
          
    		scaleGV := inputRes.GroupVersion()
          // 如果从apiserver获取的resrouces的item的Group和Version都不为空
    		if resource.Group != "" && resource.Version != "" {
              // 替换参数gvr的gv
    			scaleGV = schema.GroupVersion{
    				Group:   resource.Group,
    				Version: resource.Version,
    			}
    		}
          // 以item的Kind和gv获取gvk
    		return scaleGV.WithKind(resource.Kind), nil
    	}
      // 遍历完成后  未找到,则返回err
    	return schema.GroupVersionKind{}, fmt.Errorf("could not find scale subresource for %s in discovery information", inputRes.String())
    }
    
    // cachedScaleKindResolver 是一个 ScaleKindResolver,它缓存来自另一个 ScaleKindResolver 的结果,在缓存未命中时重新获取。
    type cachedScaleKindResolver struct {
      // 代理
    	base ScaleKindResolver
      // 缓存map  key:gvr value: gvk
    	cache map[schema.GroupVersionResource]schema.GroupVersionKind
    	mu    sync.RWMutex
    }
    
    func (r *cachedScaleKindResolver) ScaleForResource(resource schema.GroupVersionResource) (schema.GroupVersionKind, error) {
    	r.mu.RLock()
      // 获取缓存中的gvk
    	gvk, isCached := r.cache[resource]
    	r.mu.RUnlock()
      // 如果缓存中存在,则返回
    	if isCached {
    		return gvk, nil
    	}
    
    	// 使用内部代理ScaleKindResolver获取
    	gvk, err := r.base.ScaleForResource(resource)
    	if err != nil {
    		return schema.GroupVersionKind{}, err
    	}
    
    	r.mu.Lock()
    	defer r.mu.Unlock()
      // 插入map缓存中
    	r.cache[resource] = gvk
    
    	return gvk, nil
    }
    
    // ScaleConverter 知道如何在外部scale版本之间进行转换。
    type ScaleConverter struct {
      // scheme识别type和gvk
    	scheme            *runtime.Scheme
      // 序列化和反序列化obj
    	codecs            serializer.CodecFactory
      // 其中方法KindForGroupVersionKinds用来获取所需要的gvk
    	internalVersioner runtime.GroupVersioner
    }
    
    // ConvertToVersion 将给定的 *external* 输入对象转换为给定的输出 *external* 输出组版本。(方法内部用ScaleConverter中的internalVersioner做中转)
    func (c *ScaleConverter) ConvertToVersion(in runtime.Object, outVersion schema.GroupVersion) (runtime.Object, error) {
      // 使用ScaleConverter中的scheme,以参数in和ScaleConverter的internalVersioner为参数,调用ConvertToVersion来获取内部可以识别的gvk对应的obj
    	scaleInt, err := c.scheme.ConvertToVersion(in, c.internalVersioner)
    	if err != nil {
    		return nil, err
    	}
      // 用上面获取的内部gvk的obj和参数outVersion,来实现转化
    	return c.scheme.ConvertToVersion(scaleInt, outVersion)
    }
    

interface.go

  • 接口
    // ScalesGetter 可以产生一个 ScaleInterface(用于操作scale在apiserver )
    type ScalesGetter interface {
    	// Scales 为特定的命名空间生成一个 ScaleInterface.
    	// 对于非命名空间资源,将命名空间设置为空字符串。
    	Scales(namespace string) ScaleInterface
    }
    
    // ScaleInterface 可以获取和更新特定命名空间的资源中实现 scale 子资源的资源的比例.
    type ScaleInterface interface {
    	// Get 获取给定的可扩展资源的scale信息。
    	Get(ctx context.Context, resource schema.GroupResource, name string, opts metav1.GetOptions) (*autoscalingapi.Scale, error)
    
    	// Update 更新给定的可扩展资源的scale信息。
    	Update(ctx context.Context, resource schema.GroupResource, scale *autoscalingapi.Scale, opts metav1.UpdateOptions) (*autoscalingapi.Scale, error)
    
    	// Patch patch给定的可扩展资源的scale信息。
    	Patch(ctx context.Context, gvr schema.GroupVersionResource, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions) (*autoscalingapi.Scale, error)
    }
    

client.go

  • 定义变量
    // 本文中util.go的函数中有做分析,用来获取scale子资源的外部给定版本和内部可使用版本转化
    var scaleConverter = NewScaleConverter()
    // 根据scheme获取编解码器   
    var codecs = serializer.NewCodecFactory(scaleConverter.Scheme())
    // 创建新的scheme,用来处理请求参数
    var parameterScheme = runtime.NewScheme()
    // 请求参数的编解码器
    var dynamicParameterCodec = runtime.NewParameterCodec(parameterScheme)
    // 定义core api gv
    var versionV1 = schema.GroupVersion{Version: "v1"}
    
    // 初始化函数,用来初始化必要的gvk(WatchEvent和UnversionedTypes)到parameterScheme
    func init() {
      metav1.AddToGroupVersion(parameterScheme, versionV1)
    }
    
  • 函数
    // NewForConfig 创建一个新的 ScalesGetter,它使用给定的 RESTMapper将kind解析为resource,并使用给定的 dynamic.APIPathResolverFunc 解析 API 路径。
    func NewForConfig(cfg *restclient.Config, mapper PreferredResourceMapper, resolver dynamic.APIPathResolverFunc, scaleKindResolver ScaleKindResolver) (ScalesGetter, error) {
    	// 避免RESTClientFor 出现异常(因为其内部验证了GroupVersion不能为空)
    	cfg.GroupVersion = &schema.GroupVersion{}
      // 用来解析request的content type中的支持的media types
    	cfg.NegotiatedSerializer = codecs.WithoutConversion()
    	if len(cfg.UserAgent) == 0 {
    		cfg.UserAgent = restclient.DefaultKubernetesUserAgent()
    	}
      // 构建rest client
    	client, err := restclient.RESTClientFor(cfg)
    	if err != nil {
    		return nil, err
    	}
      // 调用New创建ScalesGetter
    	return New(client, mapper, resolver, scaleKindResolver), nil
    }
    
    // New 使用给定的rest client创建一个新的 ScalesGetter 来发出请求.
    // 客户端上的 GroupVersion 被忽略。
    func New(baseClient restclient.Interface, mapper PreferredResourceMapper, resolver dynamic.APIPathResolverFunc, scaleKindResolver ScaleKindResolver) ScalesGetter {
      return &scaleClient{
      	mapper: mapper,
      
      	apiPathResolverFunc: resolver,
      	scaleKindResolver:   scaleKindResolver,
      	clientBase:          baseClient,
      }
    }
    
    // convertToScale 将响应体转换为 autoscaling/v1.Scale
    func convertToScale(result *restclient.Result) (*autoscaling.Scale, error) {
      // 获取result中的原始数据
    	scaleBytes, err := result.Raw()
    	if err != nil {
    		return nil, err
    	} 
      // 获取不进行类型转换的解码器
    	decoder := scaleConverter.codecs.UniversalDecoder(scaleConverter.ScaleVersions()...)
      // 解码响应result中的原始数据
    	rawScaleObj, err := runtime.Decode(decoder, scaleBytes)
    	if err != nil {
    		return nil, err
    	}
    
    	// 无论任何内容都转换为特定的gvk autoscaling/v1.Scale
    	scaleObj, err := scaleConverter.ConvertToVersion(rawScaleObj, autoscaling.SchemeGroupVersion)
    	if err != nil {
    		return nil, fmt.Errorf("received an object from a /scale endpoint which was not convertible to autoscaling Scale: %v", err)
    	}
      
    	return scaleObj.(*autoscaling.Scale), nil
    }
    
  • 结构体
    // scaleClient 是 ScalesGetter 的实现,它使用 RESTMapper 和通用 REST 客户端来支持可发现的资源.
    // 它的行为与动态 ClientPool 有点相似,但更具体地适用于 Scale.
    type scaleClient struct {
    	mapper PreferredResourceMapper
    
    	apiPathResolverFunc dynamic.APIPathResolverFunc
    	scaleKindResolver   ScaleKindResolver
    	clientBase          restclient.Interface
    }
    
    // apiPathFor 返回给定 GroupVersion 的绝对 api 路径
    func (c *scaleClient) apiPathFor(groupVer schema.GroupVersion) string {
    	// 我们需要根据 GroupVersion 设置 API 路径(如果没有设置,则默认为旧路径)
    	apiPath := c.apiPathResolverFunc(groupVer.WithKind(""))
    	if apiPath == "" {
    		apiPath = "/api"
    	}
      // 在apiPath后添加gv信息
    	return restclient.DefaultVersionedAPIPath(apiPath, groupVer)
    }
    
    // pathAndVersionFor 为给定的 GroupResource 返回适当的基本路径和关联的完整 GroupVersionResource
    func (c *scaleClient) pathAndVersionFor(resource schema.GroupResource) (string, schema.GroupVersionResource, error) {
      // 通过gvr获取mapper中符合条件的首选gvr
    	gvr, err := c.mapper.ResourceFor(resource.WithVersion(""))
    	if err != nil {
    		return "", gvr, fmt.Errorf("unable to get full preferred group-version-resource for %s: %v", resource.String(), err)
    	}
      // 获取gvr的gv,这一步也是apiPathFor的参数需求
    	groupVer := gvr.GroupVersion()
      // 获取rest client的绝对路径
    	return c.apiPathFor(groupVer), gvr, nil
    }
    
    // namespacedScaleClient 是一个用于在给定命名空间中获取Scale的 ScaleInterface.
    type namespacedScaleClient struct {
      // 代理ScaleClient
    	client    *scaleClient
      // 限定该代理ScaleClient的namespace
    	namespace string
    }
    
    // 以namespace为参数,构建一个namespacedScaleClient
    func (c *scaleClient) Scales(namespace string) ScaleInterface {
    	return &namespacedScaleClient{
    		client:    c,
    		namespace: namespace,
    	}
    }
    
    // 实现ScaleInterface接口的Get方法
    func (c *namespacedScaleClient) Get(ctx context.Context, resource schema.GroupResource, name string, opts metav1.GetOptions) (*autoscaling.Scale, error) {
      // 根据c中的client获取和apserver交互的path和gvr
    	path, gvr, err := c.client.pathAndVersionFor(resource)
    	if err != nil {
    		return nil, fmt.Errorf("unable to get client for %s: %v", resource.String(), err)
    	}
      // 调用rest client和apiserver交互,获取resource的子资源scale
    	result := c.client.clientBase.Get().
    		AbsPath(path).
    		NamespaceIfScoped(c.namespace, c.namespace != "").
    		Resource(gvr.Resource).
    		Name(name).
    		SubResource("scale").
    		SpecificallyVersionedParams(&opts, dynamicParameterCodec, versionV1).
    		Do(ctx)
    	if err := result.Error(); err != nil {
    		return nil, err
    	}
      // 转化为autoscaling/v1.Scale
    	return convertToScale(&result)
    }
    
    // 实现ScaleInterface接口的Update方法
    func (c *namespacedScaleClient) Update(ctx context.Context, resource schema.GroupResource, scale *autoscaling.Scale, opts metav1.UpdateOptions) (*autoscaling.Scale, error) {
    	// 根据c中的client获取和apserver交互的path和gvr
      path, gvr, err := c.client.pathAndVersionFor(resource)
    	if err != nil {
    		return nil, fmt.Errorf("unable to get client for %s: %v", resource.String(), err)
    	}
    
    	// 弄清楚我们在这里实际需要gvk(scale)
    	desiredGVK, err := c.client.scaleKindResolver.ScaleForResource(gvr)
    	if err != nil {
    		return nil, fmt.Errorf("could not find proper group-version for scale subresource of %s: %v", gvr.String(), err)
    	}
    
    	// 将此scale转换为想要的gv
    	scaleUpdate, err := scaleConverter.ConvertToVersion(scale, desiredGVK.GroupVersion())
    	if err != nil {
    		return nil, fmt.Errorf("could not convert scale update to external Scale: %v", err)
    	}
      // LegacyCodec形成的codecs(encode编码为符合参数gv的json格式byte[],decode时只解码而不类型转化)
    	encoder := scaleConverter.codecs.LegacyCodec(desiredGVK.GroupVersion())
      // 编码scaleUpdate为符合desiredGVK.GroupVersion()的json格式byte[]
    	scaleUpdateBytes, err := runtime.Encode(encoder, scaleUpdate)
    	if err != nil {
    		return nil, fmt.Errorf("could not encode scale update to external Scale: %v", err)
    	}
      // 调用rest client操作apiserver修改对应gvr的scale子资源
    	result := c.client.clientBase.Put().
    		AbsPath(path).
    		NamespaceIfScoped(c.namespace, c.namespace != "").
    		Resource(gvr.Resource).
    		Name(scale.Name).
    		SubResource("scale").
    		SpecificallyVersionedParams(&opts, dynamicParameterCodec, versionV1).
    		Body(scaleUpdateBytes).
    		Do(ctx)
    	if err := result.Error(); err != nil {
    		return nil, err
    	}
      // 转化为autoscaling/v1.Scale
    	return convertToScale(&result)
    }
    
    // 实现ScaleInterface接口的Patch方法
    func (c *namespacedScaleClient) Patch(ctx context.Context, gvr schema.GroupVersionResource, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions) (*autoscaling.Scale, error) {
    	// 获取gvr对应的gv
      groupVersion := gvr.GroupVersion()
      // 调用rest client操作apiserver patch对应gvr的scale子资源
    	result := c.client.clientBase.Patch(pt).
          // 获取操作对应gv的apiserver path
    		AbsPath(c.client.apiPathFor(groupVersion)).
    		NamespaceIfScoped(c.namespace, c.namespace != "").
    		Resource(gvr.Resource).
    		Name(name).
    		SubResource("scale").
    		SpecificallyVersionedParams(&opts, dynamicParameterCodec, versionV1).
    		Body(data).
    		Do(ctx)
    	if err := result.Error(); err != nil {
    		return nil, err
    	}
      // 转化为autoscaling/v1.Scale
    	return convertToScale(&result)
    }
    
  • scheme包
    主要是定义了注册到scheme和内部scale的定义
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值