什么是RESTMapper 先来看来什么是RESTMapper。RESTMapper是一个interface,定义在/pkg/meta/interfaces.go中: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
29
30
type RESTMapper interface {
KindFor(resource unversioned.GroupVersionResource) (unversioned.GroupVersionKind, error)
KindsFor(resource unversioned.GroupVersionResource) ([]unversioned.GroupVersionKind, error)
ResourceFor(input unversioned.GroupVersionResource) (unversioned.GroupVersionResource, error)
ResourcesFor(input unversioned.GroupVersionResource) ([]unversioned.GroupVersionResource, error)
RESTMapping(gk unversioned.GroupKind, versions ...string ) (*RESTMapping, error)
RESTMappings(gk unversioned.GroupKind) ([]*RESTMapping, error)
AliasesForResource(resource string ) ([]string , bool )
ResourceSingularizer(resource string ) (singular string , err error)
}
关于RESTMapper的注释非常重要,“RESTMapper allows clients to map resources to kind, and map kind and version to interfaces for manipulating those objects”。也就是说,RESTMapper映射是指GVR(GroupVersionResource)和GVK(GroupVersionKind)的关系,可以通过GVR找到合适的GVK,并可以通过GVK生成一个RESTMapping。
什么是RESTMapping 再来看来RESTMapping,同样定义在/pkg/api/meta/interfaces.go中:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
type RESTMapping struct {
Resource string
GroupVersionKind unversioned.GroupVersionKind
Scope RESTScope
runtime.ObjectConvertor
MetadataAccessor
}
RESTMapping包含Resource名称,及其对应的GVK,还有一个Scope(标明资源是否为root或者namespaced),还有一个Convertor用来转换该GVK对应的Object和一个MetadataAccessor用来提取Object的meta信息。
那么RESTMapping怎么用呢?
比如/pkg/apiserver/api_installer.go中就有使用到RESTMapping中的Scope用来生成合适的URL(RESTScopeNameRoot和RESTScopeNameNamespace处理不同,详见以后对Apiserver的分析)。
再比如/pkg/kubectl/resource_printer.go中的VersionedPrinter中的converter也是来自RESTMapping中的Convertor(以后和Scheme一起分析)。
什么是RESTScope 这里一并把RESTScope介绍掉,因为RESTScope接口也定义在/pkg/api/meta/interfaces.go中:1
2
3
4
5
6
7
8
9
10
11
12
type RESTScope interface {
Name() RESTScopeName
ParamName() string
ArgumentName() string
ParamDescription() string
}
RESTScope具体由restScope之实现。restScope定义在/pkg/api/meta/restmapper.go中,比较简单,这里就不在详细分析。目前有两种RESTScope:1
2
3
4
5
6
7
8
9
10
var RESTScopeNamespace = &restScope{
name: RESTScopeNameNamespace,
paramName: "namespaces" ,
argumentName: "namespace" ,
paramDescription: "object name and auth scope, such as for teams and projects" ,
}
var RESTScopeRoot = &restScope{
name: RESTScopeNameRoot,
}
这里的RESTScopeNameNamespace和RESTScopeNameRoot定义在/pkg/api/meta/interfaces.go中:1
2
3
4
const (
RESTScopeNameNamespace RESTScopeName = "namespace"
RESTScopeNameRoot RESTScopeName = "root"
)
这里只要记住,RESTScopeNamespace表明该资源是在Namespace下的,如pods,rc等;RESTScopeRoot标明资源是全局的,如nodes, pv等。
DefaultRESTMapper DefaultRESTMapper实现了RESTMapper interface。为什么称为DefaultRESTMapper呢,因为DefaultRESTMapper定义了defaultGroupVersions。DefaultRESTMapper定义在/pkg/api/metamapper.go中:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
type DefaultRESTMapper struct {
defaultGroupVersions []unversioned.GroupVersion
resourceToKind map [unversioned.GroupVersionResource]unversioned.GroupVersionKind
kindToPluralResource map [unversioned.GroupVersionKind]unversioned.GroupVersionResource
kindToScope map [unversioned.GroupVersionKind]RESTScope
singularToPlural map [unversioned.GroupVersionResource]unversioned.GroupVersionResource
pluralToSingular map [unversioned.GroupVersionResource]unversioned.GroupVersionResource
interfacesFunc VersionInterfacesFunc
aliasToResource map [string ][]string
}
现在来详细分析DefaultRESTMapper的字段的涵义。
defaultGroupVersions: 默认的GroupVersion,如v1,apps/v1beta1等,一般一个DefaultRESTMapper只设一个默认的GroupVersion;
resourceToKind:GVR(单数,复数)到GVK的map;
kindToPluralResource:GVK到GVR(复数)的map;
kindToScope:GVK到Scope的map;
singularToPlural:GVR(单数)到GVR(复数)的map;
interfacesFunc:用来产生Convertor和MetadataAccessor,具体实现为/pkg/api/install/install.go中的interfacesFor()函数。
aliasToResource:管理别名,但貌似系统中没怎么用这功能。 这里要说明白的是,DefaultRESTMapper中的Resource有单数和复数的区别。但为什么要有单复数的区别,现在还不知道。
下面来分析DefaultRESTMapper的重要方法的实现。
NewDefaultRESTMapper() NewDefaultRESTMapper()生成一个新的DefaultRESTMapper。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
func NewDefaultRESTMapper (defaultGroupVersions []unversioned.GroupVersion, f VersionInterfacesFunc) *DefaultRESTMapper {
resourceToKind := make (map [unversioned.GroupVersionResource]unversioned.GroupVersionKind)
kindToPluralResource := make (map [unversioned.GroupVersionKind]unversioned.GroupVersionResource)
kindToScope := make (map [unversioned.GroupVersionKind]RESTScope)
singularToPlural := make (map [unversioned.GroupVersionResource]unversioned.GroupVersionResource)
pluralToSingular := make (map [unversioned.GroupVersionResource]unversioned.GroupVersionResource)
aliasToResource := make (map [string ][]string )
return &DefaultRESTMapper{
resourceToKind: resourceToKind,
kindToPluralResource: kindToPluralResource,
kindToScope: kindToScope,
defaultGroupVersions: defaultGroupVersions,
singularToPlural: singularToPlural,
pluralToSingular: pluralToSingular,
aliasToResource: aliasToResource,
interfacesFunc: f,
}
}
NewDefaultRESTMapper()会被/pkg/api/mapper.go的NewDefaultRESTMapperFromScheme()函数调用。NewDefaultRESTMapperFromScheme()是生成DefaultRESTMapper的入口。这就很方便我们看系统中有多少个DefaultRESTMapper了: [v1], [apps/v1beta1], [authentication.k8s.io/v1beta1], [authorization.k8s.io/v1beta1], [autoscaling/v1], [batch/v1 batch/v2alpha1], [certificates.k8s.io/v1alpha1], [componentconfig/v1alpha1], [extensions/v1beta1], [policy/v1beta1], [rbac.authorization.k8s.io/v1alpha1], [storage.k8s.io/v1beta1], [imagepolicy.k8s.io/v1alpha1]
Add() Add()方法主要是把GVK和GVK对应的scope加入到DefaultRESTMapper对应的字段中。其中KindToResource()返回GVK对应的GVR(复数)和GVR(单数)。1
2
3
4
5
6
7
8
9
10
11
12
13
func (m *DefaultRESTMapper) Add (kind unversioned.GroupVersionKind, scope RESTScope) {
plural, singular := KindToResource(kind)
m.singularToPlural[singular] = plural
m.pluralToSingular[plural] = singular
m.resourceToKind[singular] = kind
m.resourceToKind[plural] = kind
m.kindToPluralResource[kind] = plural
m.kindToScope[kind] = scope
}
ResourceFor() ResourceFor()通过GVR(信息不一定要全)找到一个最合适的注册的GVR。规则如下:
如果参数GVR没有有Resource,则返回错误。
如果参数GVR限定Group,Version和Resource,则匹配Group,Version和Resource;
如果参数GVR限定Group和Resource,则匹配Group和Resource;
如果参数GVR限定Version和Resource,则匹配Version和Resource;
如果参数GVR只有Resource,则匹配Resource。
如果系统中存在多个匹配,则返回错误(系统现在还不支持在不同的Group中定义相同的type)。
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
func (m *DefaultRESTMapper) ResourceFor (resource unversioned.GroupVersionResource) (unversioned.GroupVersionResource, error) {
resources, err := m.ResourcesFor(resource)
if err != nil {
return unversioned.GroupVersionResource{}, err
}
if len (resources) == 1 {
return resources[0 ], nil
}
return unversioned.GroupVersionResource{}, &AmbiguousResourceError{PartialResource: resource, MatchingResources: resources}
}
func (m *DefaultRESTMapper) ResourcesFor (input unversioned.GroupVersionResource) ([]unversioned.GroupVersionResource, error) {
resource := coerceResourceForMatching(input)
hasResource := len (resource.Resource) > 0
hasGroup := len (resource.Group) > 0
hasVersion := len (resource.Version) > 0
if !hasResource {
return nil , fmt.Errorf("a resource must be present, got: %v" , resource)
}
ret := []unversioned.GroupVersionResource{}
switch {
case hasGroup && hasVersion:
for plural, singular := range m.pluralToSingular {
if singular == resource {
ret = append (ret, plural)
break
}
if plural == resource {
ret = append (ret, plural)
break
}
}
case hasGroup:
foundExactMatch := false
requestedGroupResource := resource.GroupResource()
for plural, singular := range m.pluralToSingular {
if singular.GroupResource() == requestedGroupResource {
foundExactMatch = true
ret = append (ret, plural)
}
if plural.GroupResource() == requestedGroupResource {
foundExactMatch = true
ret = append (ret, plural)
}
}
if !foundExactMatch {
for plural, singular := range m.pluralToSingular {
if !strings.HasPrefix(plural.Group, requestedGroupResource.Group) {
continue
}
if singular.Resource == requestedGroupResource.Resource {
ret = append (ret, plural)
}
if plural.Resource == requestedGroupResource.Resource {
ret = append (ret, plural)
}
}
}
case hasVersion:
for plural, singular := range m.pluralToSingular {
if singular.Version == resource.Version && singular.Resource == resource.Resource {
ret = append (ret, plural)
}
if plural.Version == resource.Version && plural.Resource == resource.Resource {
ret = append (ret, plural)
}
}
default :
for plural, singular := range m.pluralToSingular {
if singular.Resource == resource.Resource {
ret = append (ret, plural)
}
if plural.Resource == resource.Resource {
ret = append (ret, plural)
}
}
}
if len (ret) == 0 {
return nil , &NoResourceMatchError{PartialResource: resource}
}
sort.Sort(resourceByPreferredGroupVersion{ret, m.defaultGroupVersions})
return ret, nil
}
KindFor() KindFor()通过GVR(信息不一定要全)找到一个最合适的注册的GVK。规则和ResourceFor()一样:
如果参数GVR没有有Resource,则返回错误。
如果参数GVR限定Group,Version和Resource,则匹配Group,Version和Resource;
如果参数GVR限定Group和Resource,则匹配Group和Resource;
如果参数GVR限定Version和Resource,则匹配Version和Resource;
如果参数GVR只有Resource,则匹配Resource。
如果系统中存在多个匹配,则返回错误(系统现在还不支持在不同的Group中定义相同的type)。
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
func (m *DefaultRESTMapper) KindFor (resource unversioned.GroupVersionResource) (unversioned.GroupVersionKind, error) {
kinds, err := m.KindsFor(resource)
if err != nil {
return unversioned.GroupVersionKind{}, err
}
if len (kinds) == 1 {
return kinds[0 ], nil
}
return unversioned.GroupVersionKind{}, &AmbiguousResourceError{PartialResource: resource, MatchingKinds: kinds}
}
func (m *DefaultRESTMapper) KindsFor (input unversioned.GroupVersionResource) ([]unversioned.GroupVersionKind, error) {
resource := coerceResourceForMatching(input)
hasResource := len (resource.Resource) > 0
hasGroup := len (resource.Group) > 0
hasVersion := len (resource.Version) > 0
if !hasResource {
return nil , fmt.Errorf("a resource must be present, got: %v" , resource)
}
ret := []unversioned.GroupVersionKind{}
switch {
case hasGroup && hasVersion:
kind, exists := m.resourceToKind[resource]
if exists {
ret = append (ret, kind)
}
case hasGroup:
foundExactMatch := false
requestedGroupResource := resource.GroupResource()
for currResource, currKind := range m.resourceToKind {
if currResource.GroupResource() == requestedGroupResource {
foundExactMatch = true
ret = append (ret, currKind)
}
}
if !foundExactMatch {
for currResource, currKind := range m.resourceToKind {
if !strings.HasPrefix(currResource.Group, requestedGroupResource.Group) {
continue
}
if currResource.Resource == requestedGroupResource.Resource {
ret = append (ret, currKind)
}
}
}
case hasVersion:
for currResource, currKind := range m.resourceToKind {
if currResource.Version == resource.Version && currResource.Resource == resource.Resource {
ret = append (ret, currKind)
}
}
default :
for currResource, currKind := range m.resourceToKind {
if currResource.Resource == resource.Resource {
ret = append (ret, currKind)
}
}
}
if len (ret) == 0 {
return nil , &NoResourceMatchError{PartialResource: input}
}
sort.Sort(kindByPreferredGroupVersion{ret, m.defaultGroupVersions})
return ret, nil
}
RESTMapping() RESTMapping()的参数是GK和versions,通常的做法是把一个GVK直接拆成GK和Version,然后获取mapping。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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
func (m *DefaultRESTMapper) RESTMapping (gk unversioned.GroupKind, versions ...string ) (*RESTMapping, error) {
var gvk *unversioned.GroupVersionKind
hadVersion := false
for _, version := range versions {
if len (version) == 0 || version == runtime.APIVersionInternal {
continue
}
currGVK := gk.WithVersion(version)
hadVersion = true
if _, ok := m.kindToPluralResource[currGVK]; ok {
gvk = &currGVK
break
}
}
if !hadVersion && (gvk == nil ) {
for _, gv := range m.defaultGroupVersions {
if gv.Group != gk.Group {
continue
}
currGVK := gk.WithVersion(gv.Version)
if _, ok := m.kindToPluralResource[currGVK]; ok {
gvk = &currGVK
break
}
}
}
if gvk == nil {
return nil , &NoKindMatchError{PartialKind: gk.WithVersion("" )}
}
resource, ok := m.kindToPluralResource[*gvk]
if !ok {
found := []unversioned.GroupVersion{}
for _, gv := range m.defaultGroupVersions {
if _, ok := m.kindToPluralResource[*gvk]; ok {
found = append (found, gv)
}
}
if len (found) > 0 {
return nil , fmt.Errorf("object with kind %q exists in versions %v, not %v" , gvk.Kind, found, gvk.GroupVersion().String())
}
return nil , fmt.Errorf("the provided version %q and kind %q cannot be mapped to a supported object" , gvk.GroupVersion().String(), gvk.Kind)
}
scope, ok := m.kindToScope[*gvk]
if !ok {
return nil , fmt.Errorf("the provided version %q and kind %q cannot be mapped to a supported scope" , gvk.GroupVersion().String(), gvk.Kind)
}
interfaces, err := m.interfacesFunc(gvk.GroupVersion())
if err != nil {
return nil , fmt.Errorf("the provided version %q has no relevant versions: %v" , gvk.GroupVersion().String(), err)
}
retVal := &RESTMapping{
Resource: resource.Resource,
GroupVersionKind: *gvk,
Scope: scope,
ObjectConvertor: interfaces.ObjectConvertor,
MetadataAccessor: interfaces.MetadataAccessor,
}
return retVal, nil
}
RESTMapping()的流程如下:
构造GVK:使用GK和Versions,或GK和DefaultGroupVersions,构造GVK;
获取GVR:从kindToPluralResource中获取GVR;
获取scope:从kindToScope中获取scope;
使用interfacesFunc()获取Convertor和MetadataAccessor;
组装成RESTMapping并返回。
总结 RESTMapper可以从GVR获取GVK,并生成一个RESTMapping来处理该GVR。RESTMapping中有Resource名称,GVK,Scope,Convertor,Accessor等和GVR有关的信息。