什么是Mapper
这里的Mapper是指kubectl中的Mapper,是对ObjectTyper, RESTMapper, ClientMapper和Decoder的封装。Mapper定义在/pkg/kubectl/resource/mapper.go中:
1 2 3 4 5 6 7 8
| type Mapper struct { runtime.ObjectTyper meta.RESTMapper ClientMapper runtime.Decoder }
|
由于Mapper嵌入了很多涉及其他模块的结构体,所以我们这次的分析仅分析到这些对象是如何生成的,细节将放到其他模块的分析中。
生成Mapper
一切得从/pkg/kubectl/cmd/util/factory.go的NewBuilder()方法说起。
1 2 3 4 5 6
| func (f *factory) NewBuilder() *resource.Builder { mapper, typer := f.Object() return resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)) }
|
再来看/pkg/kubectl/ressourcce/builder.go中的NewBuilder():
1 2 3 4 5 6 7 8
| func NewBuilder(mapper meta.RESTMapper, typer runtime.ObjectTyper, clientMapper ClientMapper, decoder runtime.Decoder) *Builder { return &Builder{ mapper: &Mapper{typer, mapper, clientMapper, decoder}, requireObject: true, } }
|
可以看出,Mapper的创建在builder.go中,但真正准备创建Mapper的其他结构体的地方还是在factory.go中。
- runtime.ObjectTyper: 由f.Object()生成;
- meta.RESTMapper: 由f.Object()生成;
- ClientMapper: resource.ClientMapperFunc(f.ClientForMapping)
- runtime.Decoder: f.Decoder(true)
f.Object()
Object()返回一个RESTMapper及api.Scheme。这里api.Scheme将传递给Typer。关于RESTMapper,现在只需知道存储了GVK和GVR的关系,可以通过RESTMapping()返回一个mapping表示GVK和Resource的关系。可以用mapping生成一个config,再生成Client。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| func (f *factory) Object() (meta.RESTMapper, runtime.ObjectTyper) { mapper := registered.RESTMapper() discoveryClient, err := f.DiscoveryClient() if err == nil { mapper = meta.FirstHitRESTMapper{ MultiRESTMapper: meta.MultiRESTMapper{ discovery.NewDeferredDiscoveryRESTMapper(discoveryClient, registered.InterfacesFor), registered.RESTMapper(), }, } } mapper = NewShortcutExpander(mapper, discoveryClient) cfg, err := f.clients.ClientConfigForVersion(nil) checkErrWithPrefix("failed to get client config: ", err) cmdApiVersion := unversioned.GroupVersion{} if cfg.GroupVersion != nil { cmdApiVersion = *cfg.GroupVersion } mapper = kubectl.OutputVersionMapper{RESTMapper: mapper, OutputVersions: []unversioned.GroupVersion{cmdApiVersion}} return mapper, api.Scheme }
|
CLientForMapping()可以依据mapping中的信息生成一个config,然后再生成RESTClient。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| func (f *factory) ClientForMapping(mapping *meta.RESTMapping) (resource.RESTClient, error) { cfg, err := f.clientConfig.ClientConfig() if err != nil { return nil, err } if err := client.SetKubernetesDefaults(cfg); err != nil { return nil, err } gvk := mapping.GroupVersionKind switch gvk.Group { case federation.GroupName: mappingVersion := mapping.GroupVersionKind.GroupVersion() return f.clients.FederationClientForVersion(&mappingVersion) case api.GroupName: cfg.APIPath = "/api" default: cfg.APIPath = "/apis" } gv := gvk.GroupVersion() cfg.GroupVersion = &gv if registered.IsThirdPartyAPIGroupVersion(gvk.GroupVersion()) { cfg.NegotiatedSerializer = thirdpartyresourcedata.NewNegotiatedSerializer(api.Codecs, gvk.Kind, gv, gv) } return restclient.RESTClientFor(cfg) }
|
而resource.ClientMapperFunc定义在/pkg/kubectl/resource/interfaces.go中:
1 2 3 4 5 6
| type ClientMapperFunc func(mapping *meta.RESTMapping) (RESTClient, error) // ClientForMapping implements ClientMapper func (f ClientMapperFunc) ClientForMapping(mapping *meta.RESTMapping) (RESTClient, error) { return f(mapping) }
|
所以,调用ClientMapper的ClientForMapping()方法就相关于调用factory的ClientForMapping()方法。
f.Decoder()
Decoder()定义在/pkg/kubectl/cmd/util/factory.go中:
1 2 3 4 5 6 7 8 9 10
| func (f *factory) Decoder(toInternal bool) runtime.Decoder { var decoder runtime.Decoder if toInternal { decoder = api.Codecs.UniversalDecoder() } else { decoder = api.Codecs.UniversalDeserializer() } return thirdpartyresourcedata.NewDecoder(decoder, "") }
|
因为传入的toInternal为true,所以Decoder为api.Codecs.UniversalDecoder()。这里不再向下分析,只需知道该Decoder可以把相关版本的对象转换成内部版本的对象。
RESTMapping()
调用的RESTMapper的RESTMapping()。RESTMapper的RESTMapping()前面已经介绍过。
调用的就是f.ClientForMapping(),前面已经介绍过。