client-go之dynamic包(上篇)源码分析

dynamic包

用于动态生成informer/lister/client等

interface.go

  • 接口.go
    // ResourceInterface的工厂模式,方法Resource可以获取对应gvr的ResourceInterface接口
    type Interface interface {
    	Resource(resource schema.GroupVersionResource) NamespaceableResourceInterface
    }
    // 操作对应obj的接口
    type ResourceInterface interface {
    	Create(ctx context.Context, obj *unstructured.Unstructured, options metav1.CreateOptions, subresources ...string) (*unstructured.Unstructured, error)
    	Update(ctx context.Context, obj *unstructured.Unstructured, options metav1.UpdateOptions, subresources ...string) (*unstructured.Unstructured, error)
    	UpdateStatus(ctx context.Context, obj *unstructured.Unstructured, options metav1.UpdateOptions) (*unstructured.Unstructured, error)
    	Delete(ctx context.Context, name string, options metav1.DeleteOptions, subresources ...string) error
    	DeleteCollection(ctx context.Context, options metav1.DeleteOptions, listOptions metav1.ListOptions) error
    	Get(ctx context.Context, name string, options metav1.GetOptions, subresources ...string) (*unstructured.Unstructured, error)
    	List(ctx context.Context, opts metav1.ListOptions) (*unstructured.UnstructuredList, error)
    	Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error)
    	Patch(ctx context.Context, name string, pt types.PatchType, data []byte, options metav1.PatchOptions, subresources ...string) (*unstructured.Unstructured, error)
    }
    // 限定了namespace的ResourceInterface
    type NamespaceableResourceInterface interface {
    	Namespace(string) ResourceInterface
    	ResourceInterface
    }
    

simple.go 对于上面提到的接口的简单实现

  • 函数
    // ConfigFor 返回提供的配置的副本,并设置了适当的动态客户端默认值。
    func ConfigFor(inConfig *rest.Config) *rest.Config {
      // 复制参数配置文件
    	config := rest.CopyConfig(inConfig)
      // 设置默认属性
    	config.AcceptContentTypes = "application/json"
    	config.ContentType = "application/json"
    	config.NegotiatedSerializer = basicNegotiatedSerializer{}
    	if config.UserAgent == "" {
    		config.UserAgent = rest.DefaultKubernetesUserAgent()
    	}
    	return config
    }
    
    // NewForConfig 创建新的动态客户端或返回错误。
    func NewForConfig(inConfig *rest.Config) (Interface, error) {
      // 复制并设置默认的属性
    	config := ConfigFor(inConfig)
    	// 用于序列化选项
    	config.GroupVersion = &schema.GroupVersion{}
    	config.APIPath = "/if-you-see-this-search-for-the-break"
      // 获取包装了http.client的client,操作obj,后面章节讲解
    	restClient, err := rest.RESTClientFor(config)
    	if err != nil {
    		return nil, err
    	}
    	return &dynamicClient{client: restClient}, nil
    }
    
  • 结构体
    // rest.RESTClient(操作obj的具体执行client)的包装结构体
    type dynamicClient struct {
    	client *rest.RESTClient
    }
    // 操作对应obj的接口
    type ResourceInterface interface {
    	Create(ctx context.Context, obj *unstructured.Unstructured, options metav1.CreateOptions, subresources ...string) (*unstructured.Unstructured, error)
    	Update(ctx context.Context, obj *unstructured.Unstructured, options metav1.UpdateOptions, subresources ...string) (*unstructured.Unstructured, error)
    	UpdateStatus(ctx context.Context, obj *unstructured.Unstructured, options metav1.UpdateOptions) (*unstructured.Unstructured, error)
    	Delete(ctx context.Context, name string, options metav1.DeleteOptions, subresources ...string) error
    	DeleteCollection(ctx context.Context, options metav1.DeleteOptions, listOptions metav1.ListOptions) error
    	Get(ctx context.Context, name string, options metav1.GetOptions, subresources ...string) (*unstructured.Unstructured, error)
    	List(ctx context.Context, opts metav1.ListOptions) (*unstructured.UnstructuredList, error)
    	Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error)
    	Patch(ctx context.Context, name string, pt types.PatchType, data []byte, options metav1.PatchOptions, subresources ...string) (*unstructured.Unstructured, error)
    }
    // 限定了namespace的ResourceInterface
    type NamespaceableResourceInterface interface {
    	Namespace(string) ResourceInterface
    	ResourceInterface
    }
    
    // NamespaceableResourceInterface接口的实现
    type dynamicResourceClient struct {
    	client    *dynamicClient
    	namespace string // 限定namespace
    	resource  schema.GroupVersionResource // 限定gvr
    }
    
    // 实现Interface接口的Resource方法,获取操作资源的client
    func (c *dynamicClient) Resource(resource schema.GroupVersionResource) NamespaceableResourceInterface {
    	return &dynamicResourceClient{client: c, resource: resource}
    }
    
    // 实现了NamespaceableResourceInterface接口的Namespace方法
    func (c *dynamicResourceClient) Namespace(ns string) ResourceInterface {
    	ret := *c // 获取指针地址
    	ret.namespace = ns // 修改对应地址的值
    	return &ret // 返回指针地址对应的value
    }
    
    // 只分析create方法,其他方法类似
    // 实现了ResourceInterface接口的Create方法,用来创建参数对应的obj
    func (c *dynamicResourceClient) Create(ctx context.Context, obj *unstructured.Unstructured, opts metav1.CreateOptions, subresources ...string) (*unstructured.Unstructured, error) {
      // 把Unstructured obj(内部其实是map类型)转化为字节数组
    	outBytes, err := runtime.Encode(unstructured.UnstructuredJSONScheme, obj)
    	if err != nil {
    		return nil, err
    	}
    	name := ""
      // 如果subresources不为空,这里会判定obj的name不为空
    	if len(subresources) > 0 {
    		accessor, err := meta.Accessor(obj)
    		if err != nil {
    			return nil, err
    		}
    		name = accessor.GetName()
    		if len(name) == 0 {
    			return nil, fmt.Errorf("name is required")
    		}
    	}
      
      // 如果name不为空,再创建时则使用该name,否则随机生成。(这里只是建立了tcp长链接和解码相应,具体的执行逻辑还是在k8s api-server中调用k8s api的etcd相关接口)
    	result := c.client.client.
    		Post().
    		AbsPath(append(c.makeURLSegments(name), subresources...)...).
    		Body(outBytes).
    		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
    	}
      // 使用json编解码器  解码原始数据,得到一个Unstructured类型的数据
    	uncastObj, err := runtime.Decode(unstructured.UnstructuredJSONScheme, retBytes)
    	if err != nil {
    		return nil, err
    	}
    	return uncastObj.(*unstructured.Unstructured), nil
    }
    

scheme.go 实现kubernetes/apimachinery的序列化和编解码(ListOptions/GetOptions/DeleteOptions/CreateOptions/UpdateOptions/PatchOptions),供上面ResourceClient使用。

  • 变量
    // 暂时没有什么用途(估计是备用)
    var watchScheme = runtime.NewScheme()
    // 供basicNegotiatedSerializer来获取gvk/类型转化/创建obj
    var basicScheme = runtime.NewScheme()
    // 为上面ResourceClient删除操作的Options创建删除CodecFactory(编解码器工厂),提供Scheme参数
    var deleteScheme = runtime.NewScheme()
    // 为上面ResourceClient各种操作创建参数CodecFactory(编解码器工厂),提供Scheme参数
    var parameterScheme = runtime.NewScheme()
    // 创建删除CodecFactory(编解码器工厂),编解码DeleteOptions对象
    var deleteOptionsCodec = serializer.NewCodecFactory(deleteScheme)
    // 创建参数CodecFactory(编解码器工厂),编解码ListOptions/GetOptions/CreateOptions/UpdateOptions/PatchOptions对象,作为请求参数
    var dynamicParameterCodec = runtime.NewParameterCodec(parameterScheme)
    // 定义gv
    var versionV1 = schema.GroupVersion{Version: "v1"}
    // 添加ListOptions/GetOptions/DeleteOptions/CreateOptions/UpdateOptions/PatchOptions,初始化各个schema
    func init() {
    	metav1.AddToGroupVersion(watchScheme, versionV1)
    	metav1.AddToGroupVersion(basicScheme, versionV1)
    	metav1.AddToGroupVersion(parameterScheme, versionV1)
    	metav1.AddToGroupVersion(deleteScheme, versionV1)
    }
    
  • 结构体
    // 实现了NegotiatedSerializer接口,根据content判断,以何种方式编解码obj
    type basicNegotiatedSerializer struct{}
    
    // 实现NegotiatedSerializer的SupportedMediaTypes方法,自定义可以处理的MediaType,并定义各种MediaType的Serializer
    func (s basicNegotiatedSerializer) SupportedMediaTypes() []runtime.SerializerInfo {
    	return []runtime.SerializerInfo{
    		{
    			MediaType:        "application/json",
    			MediaTypeType:    "application",
    			MediaTypeSubType: "json",
    			EncodesAsText:    true,
    			Serializer:       json.NewSerializer(json.DefaultMetaFactory, unstructuredCreater{basicScheme}, unstructuredTyper{basicScheme}, false),
    			PrettySerializer: json.NewSerializer(json.DefaultMetaFactory, unstructuredCreater{basicScheme}, unstructuredTyper{basicScheme}, true),
    			StreamSerializer: &runtime.StreamSerializerInfo{
    				EncodesAsText: true,
    				Serializer:    json.NewSerializer(json.DefaultMetaFactory, basicScheme, basicScheme, false),
    				Framer:        json.Framer,
    			},
    		},
    	}
    }
    // 实现NegotiatedSerializer的EncoderForVersion方法,获取一个版本化的Encoder(编码为指定版本)
    func (s basicNegotiatedSerializer) EncoderForVersion(encoder runtime.Encoder, gv runtime.GroupVersioner) runtime.Encoder {
    	return runtime.WithVersionEncoder{
    		Version:     gv,
    		Encoder:     encoder,
    		ObjectTyper: unstructuredTyper{basicScheme},
    	}
    }
    
    // 实现NegotiatedSerializer的DecoderToVersion方法, 获取原始的decoder
    func (s basicNegotiatedSerializer) DecoderToVersion(decoder runtime.Decoder, gv runtime.GroupVersioner) runtime.Decoder {
    	return decoder
    }
    
    // unstructuredCreater 包装了ObjectCreater(用于New一个gvk对应的obj),如果有err,那么和ObjectCreater的区别是返回了空的obj,而err为空
    type unstructuredCreater struct {
    	nested runtime.ObjectCreater
    }
    
    // 实现了ObjectCreater接口的New方法
    func (c unstructuredCreater) New(kind schema.GroupVersionKind) (runtime.Object, error) {
    	out, err := c.nested.New(kind)
    	if err == nil {
    		return out, nil
    	}
      // 这里和ObjectCreater有区别,如果有err,那么和ObjectCreater的区别是返回了空的obj,而err为空
    	out = &unstructured.Unstructured{}
    	out.GetObjectKind().SetGroupVersionKind(kind)
    	return out, nil
    }
    
    // unstructuredTyper 包装了ObjectTyper(用于获取obj对应的gvk列表),如果有err,那么和ObjectTyper的区别是会判断是否是Unstructured类型且obj对应的gvk是否为空
    type unstructuredTyper struct {
    	nested runtime.ObjectTyper
    }
    
    // 实现了ObjectTyper接口的ObjectKinds方法
    func (t unstructuredTyper) ObjectKinds(obj runtime.Object) ([]schema.GroupVersionKind, bool, error) {
    	kinds, unversioned, err := t.nested.ObjectKinds(obj)
    	if err == nil {
    		return kinds, unversioned, nil
    	}
      // 判断obj是否是Unstructured类型且对应的gvk是否为空
    	if _, ok := obj.(runtime.Unstructured); ok && !obj.GetObjectKind().GroupVersionKind().Empty() {
    		return []schema.GroupVersionKind{obj.GetObjectKind().GroupVersionKind()}, false, nil
    	}
    	return nil, false, err
    }
    
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值