什么是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
//***定义在/pkg/kubectl/resource/interfaces.go中***//
//***ClientMapper可以根据config和mapping通过调用ClientForMapping()生成RESTClient***//
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()
//***此处有kubectl mapper的各项参数的定义***//
//***其中Decoder为f.Decoder(true)***//
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
// NewBuilder creates a builder that operates on generic objects.
func NewBuilder(mapper meta.RESTMapper, typer runtime.ObjectTyper, clientMapper ClientMapper, decoder runtime.Decoder) *Builder {
return &Builder{
//***把RESTMapper, ObjectTyper, ClientMapper, Decoder封装成kubectl的Mapper***//
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) {
//***registered.RESTMapper()定义在/pkg/apimachinery/registerd/registered.go中***//
mapper := registered.RESTMapper()
//***生成discoveryClient***//
discoveryClient, err := f.DiscoveryClient()
if err == nil {
//***discoveryClient生成成功的情况下,mapper为一个FistHitRESTMapper***//
mapper = meta.FirstHitRESTMapper{
MultiRESTMapper: meta.MultiRESTMapper{
discovery.NewDeferredDiscoveryRESTMapper(discoveryClient, registered.InterfacesFor),
registered.RESTMapper(), // hardcoded fall back
},
}
}
// wrap with shortcuts
mapper = NewShortcutExpander(mapper, discoveryClient)
// wrap with output preferences
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
}

f.ClientForMapping()

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
//***依据mapping生成RESTClient***//
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)
}
//***Fankang***//
//***定义在/pkg/client/restclient/config.go***//
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 {
//***api.Codecs定义在pkg/api/register.go中***//
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()前面已经介绍过。

ClientForMapping()

调用的就是f.ClientForMapping(),前面已经介绍过。