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 }