原始碼版本
Kubernetes v1.5.0
簡介
k8s裡面有各種資源,如Pod、Service、RC、namespaces等資源,使用者操作的其實也就是這一大堆資源。但這些資源並不是雜亂無章的,使用了GroupVersion的方式組織在一起。每一種資源都屬於一個Group,而資源還有版本之分,如v1、v1beta1等。
k8s目前正在使用的API groups:
-
“core” group:它的REST path是api/v1
-
“extensions” group:它的REST path是/apis/extensions/v1beta1
-
“autoscaling”, “abac” …
伺服器的URL的格式:/prefix/group/version/… (例如:/apis/extensions/v1beta1)
重要結構體
APIGroupVersion:對API資源的組織,裡面包含了Storage、GroupVersion、Mapper、Serializer、Convertor等成員。Storage是etcd的介面,這是一個map型別,每一種資源都會與etcd建立一個連線;GroupVersion表示該APIGroupVersion屬於哪個Group、哪個version;Serializer用於序列化,反序列化;Convertor提供各個不同版本進行轉化的介面;Mapper實現了RESTMapper介面。
type APIGroupVersion struct {
// key存在物件的url,value是一個rest.Storage,用於對接etcd儲存
Storage map[string]rest.Storage
// 該group的prefix,例如核心組的Root是`/api`
Root string
// 包含類似`api/v1`這樣的string,用於標識這個例項
GroupVersion unversioned.GroupVersion
// OptionsExternalVersion controls the Kubernetes APIVersion used for common objects in the apiserver
// schema like api.Status, api.DeleteOptions, and api.ListOptions. Other implementors may
// define a version "v1beta1" but want to use the Kubernetes "v1" internal objects. If
// empty, defaults to GroupVersion.
OptionsExternalVersion *unversioned.GroupVersion
// 關鍵性成員
Mapper meta.RESTMapper
// 物件序列化和反序列化器
Serializer runtime.NegotiatedSerializer
ParameterCodec runtime.ParameterCodec
// 以下4個都是被賦值為Scheme結構
Typer runtime.ObjectTyper
Creater runtime.ObjectCreater
// 相互轉換任意api版本的物件,需要事先註冊轉換函式
Convertor runtime.ObjectConvertor
Copier runtime.ObjectCopier
Linker runtime.SelfLinker
// 用於訪問許可控制
Admit admission.Interface
Context api.RequestContextMapper
MinRequestTimeout time.Duration
// SubresourceGroupVersionKind contains the GroupVersionKind overrides for each subresource that is
// accessible from this API group version. The GroupVersionKind is that of the external version of
// the subresource. The key of this map should be the path of the subresource. The keys here should
// match the keys in the Storage map above for subresources.
SubresourceGroupVersionKind map[string]unversioned.GroupVersionKind
// ResourceLister is an interface that knows how to list resources
// for this API Group.
ResourceLister APIResourceLister
}
APIGroupVersion的建立介面是pkg/genericapiserver/genericapiserver.go中的newAPIGroupVersion()介面,在介面在建立APIGroupVersion還用到了好幾個別的結構:APIGroupInfo、Scheme、GroupMeta。下面一個一個介紹:
APIGroupInfo:
type APIGroupInfo struct {
// 該Group的元資訊
GroupMeta apimachinery.GroupMeta
// 不同版本的所有的Storage
VersionedResourcesStorageMap map[string]map[string]rest.Storage
// OptionsExternalVersion controls the APIVersion used for common objects in the
// schema like api.Status, api.DeleteOptions, and api.ListOptions. Other implementors may
// define a version "v1beta1" but want to use the Kubernetes "v1" internal objects.
// If nil, defaults to groupMeta.GroupVersion.
// TODO: Remove this when https://github.com/kubernetes/kubernetes/issues/19018 is fixed.
OptionsExternalVersion *unversioned.GroupVersion
// core group的話,對應的就是api.Scheme
Scheme *runtime.Scheme
// NegotiatedSerializer controls how this group encodes and decodes data
NegotiatedSerializer runtime.NegotiatedSerializer
// ParameterCodec performs conversions for query parameters passed to API calls
ParameterCodec runtime.ParameterCodec
// 所有resources資訊,key就是resource的path
// 比如:key為"replicationcontrollers/scale",GroupVersionKind: autoscaling, v1, Scale
SubresourceGroupVersionKind map[string]unversioned.GroupVersionKind
}
Scheme: 用於API資源之間的序列化、反序列化、版本轉換。Scheme裡面還有好幾個map,前面的結構體儲存的都是unversioned.GroupVersionKind、unversioned.GroupVersion這些東西,這些東西本質上只是表示資源的字串標識,Scheme儲存了對應著標誌的具體的API資源的結構體,即relect.Type
type Scheme struct {
// versionMap allows one to figure out the go type of an object with
// the given version and name.
gvkToType map[unversioned.GroupVersionKind]reflect.Type
// typeToGroupVersion allows one to find metadata for a given go object.
// The reflect.Type we index by should *not* be a pointer.
typeToGVK map[reflect.Type][]unversioned.GroupVersionKind
// unversionedTypes are transformed without conversion in ConvertToVersion.
unversionedTypes map[reflect.Type]unversioned.GroupVersionKind
// unversionedKinds are the names of kinds that can be created in the context of any group
// or version
// TODO: resolve the status of unversioned types.
unversionedKinds map[string]reflect.Type
// Map from version and resource to the corresponding func to convert
// resource field labels in that version to internal version.
fieldLabelConversionFuncs map[string]map[string]FieldLabelConversionFunc
// defaulterFuncs is an array of interfaces to be called with an object to provide defaulting
// the provided object must be a pointer.
defaulterFuncs map[reflect.Type]func(interface{})
// converter stores all registered conversion functions. It also has
// default coverting behavior.
converter *conversion.Converter
// cloner stores all registered copy functions. It also has default
// deep copy behavior.
cloner *conversion.Cloner
}
GroupMeta: 主要包括Group的元資訊,裡面的成員RESTMapper,與APIGroupVersion一樣,其實APIGroupVersion的RESTMapper直接取之於GroupMeta的RESTMapper.一個Group可能包含多個版本,儲存在GroupVersion中,而GroupVersion是預設儲存在etcd中的版本。
type GroupMeta struct {
// 預設版本
GroupVersion unversioned.GroupVersion
// 該Group中可能會有多個版本,該欄位就包含了所有的versions
GroupVersions []unversioned.GroupVersion
// 用於編解碼
Codec runtime.Codec
// SelfLinker can set or get the SelfLink field of all API types.
// TODO: when versioning changes, make this part of each API definition.
// TODO(lavalamp): Combine SelfLinker & ResourceVersioner interfaces, force all uses
// to go through the InterfacesFor method below.
SelfLinker runtime.SelfLinker
// 用於型別,物件之間的轉換
RESTMapper meta.RESTMapper
// InterfacesFor returns the default Codec and ResourceVersioner for a given version
// string, or an error if the version is not known.
// TODO: make this stop being a func pointer and always use the default
// function provided below once every place that populates this field has been changed.
InterfacesFor func(version unversioned.GroupVersion) (*meta.VersionInterfaces, error)
// InterfacesByVersion stores the per-version interfaces.
InterfacesByVersion map[unversioned.GroupVersion]*meta.VersionInterfaces
}
RESTMapper: 用於管理所有物件的資訊。外部要獲取的話,直接通過version,group獲取到RESTMapper,然後通過kind型別可以獲取到對應的資訊。該RESTMapper其實是實現了一個DefaultRESTMapper結構。
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 is used for mapping aliases to resources
aliasToResource map[string][]string
}
APIRegistrationManager:這個結構體主要提供了已經”registered”的概念,將所有已經註冊的,已經啟用的,第三方的的GroupVersions進行了彙總,還包括了各個GroupVersion的GroupMeta(後設資料)。
type APIRegistrationManager struct {
// 所以已經registered的GroupVersions
registeredVersions map[unversioned.GroupVersion]struct{}
// 第三方註冊的GroupVersions,這些都向apiServer動態註冊的
thirdPartyGroupVersions []unversioned.GroupVersion
// 所有已經enable的GroupVersions,可以通過EnableVersions()將要enable的GroupVersion加入進來。
// 只有enable了,才能使用對應的GroupVersion
enabledVersions map[unversioned.GroupVersion]struct{}
// 所有groups的GroupMeta
groupMetaMap map[string]*apimachinery.GroupMeta
// 跟環境變數`KUBE_API_VERSIONS`有關
envRequestedVersions []unversioned.GroupVersion
}
APIRegistrationManager初始化
該結構的路徑:pkg/apimachinery/registered/registered.go
在該檔案中我們能看到初始化了一個DefaultAPIRegistrationManager物件:
var (
DefaultAPIRegistrationManager = NewOrDie(os.Getenv("KUBE_API_VERSIONS"))
)
進入NewOrDie()介面看下:
func NewOrDie(kubeAPIVersions string) *APIRegistrationManager {
m, err := NewAPIRegistrationManager(kubeAPIVersions)
if err != nil {
glog.Fatalf("Could not construct version manager: %v (KUBE_API_VERSIONS=%q)", err, kubeAPIVersions)
}
return m
}
func NewAPIRegistrationManager(kubeAPIVersions string) (*APIRegistrationManager, error) {
m := &APIRegistrationManager{
registeredVersions: map[unversioned.GroupVersion]struct{}{},
thirdPartyGroupVersions: []unversioned.GroupVersion{},
enabledVersions: map[unversioned.GroupVersion]struct{}{},
groupMetaMap: map[string]*apimachinery.GroupMeta{},
envRequestedVersions: []unversioned.GroupVersion{},
}
// 如果環境變數KUBE_API_VERSIONS進行了設定的話,進行遍歷
if len(kubeAPIVersions) != 0 {
// 通過逗號進行分隔
for _, version := range strings.Split(kubeAPIVersions, ",") {
// 解析version並轉換成GroupVersion格式
// 一般這裡的version是group/version格式,比如`/api/v1`
gv, err := unversioned.ParseGroupVersion(version)
if err != nil {
return nil, fmt.Errorf("invalid api version: %s in KUBE_API_VERSIONS: %s.",
version, kubeAPIVersions)
}
// 然後將該gv放入envRequestedVersions
m.envRequestedVersions = append(m.envRequestedVersions, gv)
}
}
// 否則返回一個空的APIRegistrationManager
return m, nil
}
瞅了下我們正在使用的環境,沒有配置KUBE_API_VERSIONS,即返回了一個空的結構,還提供了好多方法。
var (
ValidateEnvRequestedVersions = DefaultAPIRegistrationManager.ValidateEnvRequestedVersions
AllPreferredGroupVersions = DefaultAPIRegistrationManager.AllPreferredGroupVersions
RESTMapper = DefaultAPIRegistrationManager.RESTMapper
GroupOrDie = DefaultAPIRegistrationManager.GroupOrDie
AddThirdPartyAPIGroupVersions = DefaultAPIRegistrationManager.AddThirdPartyAPIGroupVersions
IsThirdPartyAPIGroupVersion = DefaultAPIRegistrationManager.IsThirdPartyAPIGroupVersion
RegisteredGroupVersions = DefaultAPIRegistrationManager.RegisteredGroupVersions
IsRegisteredVersion = DefaultAPIRegistrationManager.IsRegisteredVersion
IsRegistered = DefaultAPIRegistrationManager.IsRegistered
Group = DefaultAPIRegistrationManager.Group
EnabledVersionsForGroup = DefaultAPIRegistrationManager.EnabledVersionsForGroup
EnabledVersions = DefaultAPIRegistrationManager.EnabledVersions
IsEnabledVersion = DefaultAPIRegistrationManager.IsEnabledVersion
IsAllowedVersion = DefaultAPIRegistrationManager.IsAllowedVersion
EnableVersions = DefaultAPIRegistrationManager.EnableVersions
RegisterGroup = DefaultAPIRegistrationManager.RegisterGroup
RegisterVersions = DefaultAPIRegistrationManager.RegisterVersions
InterfacesFor = DefaultAPIRegistrationManager.InterfacesFor
)
在分析apiServer的啟動流程的時候,你會發現初始化ServerRunOptions物件時,用到了好多上面的變數,比如:
路徑:pkg/genericapiserver/options/server_run_options.go
func NewServerRunOptions() *ServerRunOptions {
return &ServerRunOptions{
AdmissionControl: "AlwaysAdmit",
。。。
// 這裡就使用了AllPreferredGroupVersions介面
DefaultStorageVersions: registered.AllPreferredGroupVersions(),
。。。
StorageVersions: registered.AllPreferredGroupVersions(),
}
}
上面就使用到了registered.AllPreferredGroupVersions()介面,順便看下介面具體實現:
func (m *APIRegistrationManager) AllPreferredGroupVersions() string {
// 如果沒有註冊groupMeta的話,這裡就==0
// 不過不可能沒有註冊,至於在哪裡進行註冊就得看下後面介紹的GroupMeta初始化了
if len(m.groupMetaMap) == 0 {
return ""
}
var defaults []string
for _, groupMeta := range m.groupMetaMap {
defaults = append(defaults, groupMeta.GroupVersion.String())
}
sort.Strings(defaults)
return strings.Join(defaults, ",")
}
該介面比較簡單,就是從m.groupMetaMap中取出所有的groupMeta,然後通過逗號拼接成”group1/version1,group2/version2,…”的字串。
這裡可以想一下,既然有list,那總得有groupMeta啊。而我們看APIRegistrationManager的初始化,如果沒有設定KUBE_API_VERSIONS環境變數的話,根本就沒有groupMeta。
既然不可能沒有groupMeta,那肯定得從別的地方進行register & enable。我們可以從APIRegistrationManager提供的RegisterGroup方法入手:
func (m *APIRegistrationManager) RegisterGroup(groupMeta apimachinery.GroupMeta) error {
groupName := groupMeta.GroupVersion.Group
if _, found := m.groupMetaMap[groupName]; found {
return fmt.Errorf("group %v is already registered", m.groupMetaMap)
}
m.groupMetaMap[groupName] = &groupMeta
return nil
}
該RegisterGroup介面的入參就是GroupMeta,所以我們得繼續檢視該結構的初始化了。
GroupMeta初始化
k8s現階段,API一共分為13個Group:Core、apps、authentication、authorization、autoscaling、batch、certificates、componentconfig、extensions、imagepolicy、policy、rbac、storage。其中Core的Group Name為空,它包含的API是最核心的API,如Pod、Service等,它的程式碼位於pkg/api下面,其它12個Group程式碼位於pkg/apis。每個目錄下都有一個install目錄,裡面有一個install.go檔案,接著通過init()負責初始化。這些程式都是通過下列檔案進行import:
路徑: pkg/master/import_known_versions.go
package master
// These imports are the API groups the API server will support.
import (
"fmt"
_ "k8s.io/kubernetes/pkg/api/install"
"k8s.io/kubernetes/pkg/apimachinery/registered"
_ "k8s.io/kubernetes/pkg/apis/apps/install"
_ "k8s.io/kubernetes/pkg/apis/authentication/install"
_ "k8s.io/kubernetes/pkg/apis/authorization/install"
_ "k8s.io/kubernetes/pkg/apis/autoscaling/install"
_ "k8s.io/kubernetes/pkg/apis/batch/install"
_ "k8s.io/kubernetes/pkg/apis/certificates/install"
_ "k8s.io/kubernetes/pkg/apis/componentconfig/install"
_ "k8s.io/kubernetes/pkg/apis/extensions/install"
_ "k8s.io/kubernetes/pkg/apis/imagepolicy/install"
_ "k8s.io/kubernetes/pkg/apis/policy/install"
_ "k8s.io/kubernetes/pkg/apis/rbac/install"
_ "k8s.io/kubernetes/pkg/apis/storage/install"
)
一共import了13個group。其中”k8s.io/kubernetes/pkg/api/install”就是Core Group,我們就以它為例,檢視下對應的install.go檔案。
路徑: pkg/api/install/install.go
var availableVersions = []unversioned.GroupVersion{v1.SchemeGroupVersion}
func init() {
// 進行Versions註冊,其實就是存入APIRegistrationManager.registeredVersions中
registered.RegisterVersions(availableVersions)
externalVersions := []unversioned.GroupVersion{}
for _, v := range availableVersions {
// 判斷下是否已經註冊,並追加成一個切片
if registered.IsAllowedVersion(v) {
externalVersions = append(externalVersions, v)
}
}
if len(externalVersions) == 0 {
glog.V(4).Infof("No version is registered for group %v", api.GroupName)
return
}
// 再進行enable,其實就是存入APIRegistrationManager.enabledVersions
if err := registered.EnableVersions(externalVersions...); err != nil {
glog.V(4).Infof("%v", err)
return
}
// 該介面比較關鍵,進行單獨介紹
if err := enableVersions(externalVersions); err != nil {
glog.V(4).Infof("%v", err)
return
}
}
首先定義了一個切片availableVersions,裡面只有一個元素v1.SchemeGroupVersion:
const GroupName = ""
var SchemeGroupVersion = unversioned.GroupVersion{Group: GroupName, Version: "v1"}
根據該元素的定義,可以看出availableVersions就定義了一個GroupName為空,Version是`v1`的GroupVersion。接著把該GroupVersion放入APIRegistrationManager的registeredVersions和enabledVersions中。
registered的幾個介面實現比較簡單不進行介紹了,但是執行的enableVersions()是重頭戲,我們繼續深入:
func enableVersions(externalVersions []unversioned.GroupVersion) error {
// 字面意思:將所有的Versions新增到Scheme
// 又牽扯到Scheme,後面會介紹Scheme的初始化
// 越深入看牽扯出的概念越多,該介面也很重要,需要耐心層層挖掘
addVersionsToScheme(externalVersions...)
// 將一個GroupVersion作為預設的,即`/api/v1`
preferredExternalVersion := externalVersions[0]
// 就是這裡! 進行了GroupMeta的初始化。這就是我們這小節要看的關鍵
groupMeta := apimachinery.GroupMeta{
GroupVersion: preferredExternalVersion,
GroupVersions: externalVersions,
// RESTMapper也是關鍵所在,下面也會單做一節進行介紹
RESTMapper: newRESTMapper(externalVersions),
SelfLinker: runtime.SelfLinker(accessor),
InterfacesFor: interfacesFor,
}
// 前面都是register和enable了versions,這裡才是進行了Group的register
// 該介面其實就是以第一個GroupVersion的groupName為key,groupMeta為value
// 對APIRegistrationManager的groupMetaMap,進行了賦值
if err := registered.RegisterGroup(groupMeta); err != nil {
return err
}
return nil
}
到這步,我們再結合之前APIRegistrationManager的初始化,就能知道groupMetaMap中應該有了好幾組groupMeta。那在ServerRunOptions物件初始化中呼叫的registered.AllPreferredGroupVersions()介面,能返回好幾個DefaultStorageVersions,至少肯定有`/api/v1`。至於別的groupMeta,需要再看下別的install.go,大同小異就不展開一個一個講了。
groupMeta的初始化雖然結束了,但是這裡又引出一個關鍵Scheme,那麼繼續下一小節吧。。
Scheme初始化
在上一節介紹enableVersions()函式時,第一行便是呼叫了addVersionsToScheme(externalVersions…),將GroupVersions加到Scheme。我們就來看下該介面:
func addVersionsToScheme(externalVersions ...unversioned.GroupVersion) {
// add the internal version to Scheme
if err := api.AddToScheme(api.Scheme); err != nil {
// Programmer error, detect immediately
panic(err)
}
// add the enabled external versions to Scheme
for _, v := range externalVersions {
if !registered.IsEnabledVersion(v) {
glog.Errorf("Version %s is not enabled, so it will not be added to the Scheme.", v)
continue
}
switch v {
case v1.SchemeGroupVersion:
if err := v1.AddToScheme(api.Scheme); err != nil {
// Programmer error, detect immediately
panic(err)
}
}
}
}
介面中我們可以看到AddToScheme(api.Scheme)都是將GroupVersion加入到api.Scheme。我們先將上面的介面解析放放,先看下api.Scheme是如何初始化的:
路徑:pkg/api/register.go
var Scheme = runtime.NewScheme()
定義了Scheme,再看NewScheme():
路徑:pkg/runtime/scheme.go
func NewScheme() *Scheme {
// 定義空的Scheme
s := &Scheme{
gvkToType: map[unversioned.GroupVersionKind]reflect.Type{},
typeToGVK: map[reflect.Type][]unversioned.GroupVersionKind{},
unversionedTypes: map[reflect.Type]unversioned.GroupVersionKind{},
unversionedKinds: map[string]reflect.Type{},
cloner: conversion.NewCloner(),
fieldLabelConversionFuncs: map[string]map[string]FieldLabelConversionFunc{},
defaulterFuncs: map[reflect.Type]func(interface{}){},
}
// 建立converter,用於不同版本物件轉換
s.converter = conversion.NewConverter(s.nameFunc)
// 增加一些轉換函式
s.AddConversionFuncs(DefaultEmbeddedConversions()...)
// Enable map[string][]string conversions by default
if err := s.AddConversionFuncs(DefaultStringConversions...); err != nil {
panic(err)
}
if err := s.RegisterInputDefaults(&map[string][]string{}, JSONKeyMapper, conversion.AllowDifferentFieldTypeNames|conversion.IgnoreMissingFields); err != nil {
panic(err)
}
if err := s.RegisterInputDefaults(&url.Values{}, JSONKeyMapper, conversion.AllowDifferentFieldTypeNames|conversion.IgnoreMissingFields); err != nil {
panic(err)
}
return s
}
上面就建立了一個空的Scheme。
知道哪裡建立Scheme後,我們繼續回到上面的addVersionsToScheme()函式。
其實主要就是看兩個介面: api.AddToScheme()和v1.AddToScheme()。
先看第一個:
var (
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes, addDefaultingFuncs)
AddToScheme = SchemeBuilder.AddToScheme
)
通過runtime.NewSchemeBuilder()介面傳入兩個函式,然後建立了SchemeBuilder:
type SchemeBuilder []func(*Scheme) error
func (sb *SchemeBuilder) Register(funcs ...func(*Scheme) error) {
for _, f := range funcs {
*sb = append(*sb, f)
}
}
func NewSchemeBuilder(funcs ...func(*Scheme) error) SchemeBuilder {
var sb SchemeBuilder
sb.Register(funcs...)
return sb
}
根據上面的定義和函式可以看出,SchemeBuilder就是一個介面切片,包含了addKnownTypes, addDefaultingFuncs兩個介面。
SchemeBuilder定義好了之後,繼續看AddToScheme:
func (sb *SchemeBuilder) AddToScheme(s *Scheme) error {
for _, f := range *sb {
if err := f(s); err != nil {
return err
}
}
return nil
}
該函式就是呼叫了addKnownTypes, addDefaultingFuncs兩個介面,我們一個一個看:
func addKnownTypes(scheme *runtime.Scheme) error {
if err := scheme.AddIgnoredConversionType(&unversioned.TypeMeta{}, &unversioned.TypeMeta{}); err != nil {
return err
}
// 把下列物件加入到Scheme中
// 該SchemeGroupVersion的GroupName為空,Version是"__internal"
// 所以該介面其實是把k8s內建的version新增到Scheme,而且每個group都有該步
scheme.AddKnownTypes(SchemeGroupVersion,
&Pod{},
&PodList{},
&PodStatusResult{},
&PodTemplate{},
&PodTemplateList{},
&ReplicationControllerList{},
&ReplicationController{},
&ServiceList{},
&Service{},
&ServiceProxyOptions{},
&NodeList{},
&Node{},
&NodeProxyOptions{},
&Endpoints{},
&EndpointsList{},
&Binding{},
&Event{},
&EventList{},
&List{},
&LimitRange{},
&LimitRangeList{},
&ResourceQuota{},
&ResourceQuotaList{},
&Namespace{},
&NamespaceList{},
&ServiceAccount{},
&ServiceAccountList{},
&Secret{},
&SecretList{},
&PersistentVolume{},
&PersistentVolumeList{},
&PersistentVolumeClaim{},
&PersistentVolumeClaimList{},
&DeleteOptions{},
&ListOptions{},
&PodAttachOptions{},
&PodLogOptions{},
&PodExecOptions{},
&PodProxyOptions{},
&ComponentStatus{},
&ComponentStatusList{},
&SerializedReference{},
&RangeAllocation{},
&ConfigMap{},
&ConfigMapList{},
)
// 在GroupName為空,Version為"v1"的groupVersion中,新增這些物件到Scheme
scheme.AddUnversionedTypes(Unversioned,
&unversioned.ExportOptions{},
&unversioned.Status{},
&unversioned.APIVersions{},
&unversioned.APIGroupList{},
&unversioned.APIGroup{},
&unversioned.APIResourceList{},
)
return nil
}
檢視AddKnownTypes()介面:
func (s *Scheme) AddKnownTypes(gv unversioned.GroupVersion, types ...Object) {
if len(gv.Version) == 0 {
panic(fmt.Sprintf("version is required on all types: %s %v", gv, types[0]))
}
for _, obj := range types {
t := reflect.TypeOf(obj)
if t.Kind() != reflect.Ptr {
panic("All types must be pointers to structs.")
}
t = t.Elem()
if t.Kind() != reflect.Struct {
panic("All types must be pointers to structs.")
}
gvk := gv.WithKind(t.Name())
s.gvkToType[gvk] = t
s.typeToGVK[t] = append(s.typeToGVK[t], gvk)
}
}
該介面主要操作了s.gvkToType和s.typeToGVK,用於轉換的目的。
綜上得出,是將internal version新增到Scheme中。
為什麼會有一個internal version呢? 其實每一個Group都有一個internal version。而apiserver操作的也都是internal version.
舉個例子:假如有一個建立Pod的請求來了,apiserver首先會將請求給反序列化,使用者發過來的Pod請求往往是有版本的,比如v1,因此會反序列化為一個v1.Pod。apiserver會立即將這個v1.Pod利用convertor轉換成internal.Pod,然後進行一些操作,最後要把它存到etcd裡面去,etcd裡面的Pod資訊是有版本的,因此會先發生一次轉換,將其轉換為v1.Pod,然後序列化存入etcd。
這樣看上去好像多此一舉?其實這就是k8s對api多版本的支援,這樣使用者可以以一個v1beta1建立一個Pod,然後存入etcd的是一個相對穩定的版本,比如v1版本。
internal version新增完成後,繼續回到最開始的addVersionsToScheme()函式,還要繼續執行v1.AddToScheme(api.Scheme)函式.其實就是把v1版本的api新增到Scheme中,和新增internal版本一樣。
我們看看v1.AddToScheme。
路徑:pkg/api/v1/register.go
var (
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes, addDefaultingFuncs, addConversionFuncs, addFastPathConversionFuncs)
AddToScheme = SchemeBuilder.AddToScheme
)
這裡可以看到v1相比較internal版本,還多了好幾個函式addConversionFuncs, addFastPathConversionFuncs。
這些函式在執行AddToScheme()時其實都會要遍歷執行,可以深入看下。其實就是向Scheme新增了轉換函式,比如將v1.Pod轉換為internal.Pod,將internal.Pod轉換為v1.Pod。如果同時有v1,v2,v3會如何進行轉換?其實也還是先統一轉換成internal,然後再轉換為相應的版本(v1,v2,v3).所以internal相當於轉換的橋樑,更好的支援了不同版本的api。
到這裡Scheme的初始化基本結束了。 上面講GroupMeta初始化時還引出了關鍵性的RESTMapper,所以繼續進行介紹。
RESTMapper初始化
該部分的初始化就直接看GroupMeta初始化時呼叫的介面newRESTMapper():
路徑: pkg/api/install/install.go
func newRESTMapper(externalVersions []unversioned.GroupVersion) meta.RESTMapper {
// 這些是API最頂層的物件,可以理解為沒有namespace的物件
// 根據有無namespace,物件分為兩類:RESTScopeNamespace和RESTScopeRoot
rootScoped := sets.NewString(
"Node",
"Namespace",
"PersistentVolume",
"ComponentStatus",
)
// 需要忽略Scheme中如下的kinds
ignoredKinds := sets.NewString(
"ListOptions",
"DeleteOptions",
"Status",
"PodLogOptions",
"PodExecOptions",
"PodAttachOptions",
"PodProxyOptions",
"NodeProxyOptions",
"ServiceProxyOptions",
"ThirdPartyResource",
"ThirdPartyResourceData",
"ThirdPartyResourceList")
mapper := api.NewDefaultRESTMapper(externalVersions, interfacesFor, importPrefix, ignoredKinds, rootScoped)
return mapper
}
其實所有的api資源可以分為兩類:一類是有namespace,另一類是沒有namespace。比如該介面中的Node、Namespace、PersistentVolume、ComponentStatus不屬於任何namespace。ignoredKinds是下面介面需要用到的引數,表示遍歷Scheme時忽略這些kinds。
然後呼叫api.NewDefaultRESTMapper(),importPrefix引數為:”k8s.io/kubernetes/pkg/api”,
interfacesFor是一個介面。
路徑:pkg/api/mapper.go
func NewDefaultRESTMapper(defaultGroupVersions []unversioned.GroupVersion, interfacesFunc meta.VersionInterfacesFunc,
importPathPrefix string, ignoredKinds, rootScoped sets.String) *meta.DefaultRESTMapper {
// 加入Scheme,並繼續呼叫下面的介面
return NewDefaultRESTMapperFromScheme(defaultGroupVersions, interfacesFunc, importPathPrefix, ignoredKinds, rootScoped, Scheme)
}
func NewDefaultRESTMapperFromScheme(defaultGroupVersions []unversioned.GroupVersion, interfacesFunc meta.VersionInterfacesFunc,
importPathPrefix string, ignoredKinds, rootScoped sets.String, scheme *runtime.Scheme) *meta.DefaultRESTMapper {
// 初始化了一個DefaultRESTMapper物件
mapper := meta.NewDefaultRESTMapper(defaultGroupVersions, interfacesFunc)
// 根據輸入的defaultGroupVersions,比如"/api/v1",從Scheme中遍歷所有的kinds
// 然後進行Add
for _, gv := range defaultGroupVersions {
for kind, oType := range scheme.KnownTypes(gv) {
gvk := gv.WithKind(kind)
// 過濾掉不屬於"k8s.io/kubernetes/pkg/api"路徑下的api,和ignoredKinds
if !strings.Contains(oType.PkgPath(), importPathPrefix) || ignoredKinds.Has(kind) {
continue
}
// 判斷該kind是否有namespace屬性
scope := meta.RESTScopeNamespace
if rootScoped.Has(kind) {
scope = meta.RESTScopeRoot
}
// 然後將該gvk加入到對應的組中
mapper.Add(gvk, scope)
}
}
return mapper
}
再看看該介面,先建立了一個空的DefaultRESTMapper,然後根據”/api/v1″的groupVersion,遍歷Scheme中所有的kinds,接著再呼叫mapper.Add(gvk, scope)去填充這個mapper,最後返回該mapper。
看下mapper.Add()的實現:
func (m *DefaultRESTMapper) Add(kind unversioned.GroupVersionKind, scope RESTScope) {
// resource還分為單數和複數
plural, singular := KindToResource(kind)
// 單數,複數相互轉換
m.singularToPlural[singular] = plural
m.pluralToSingular[plural] = singular
// 根據單複數的resource找到對應的kind
m.resourceToKind[singular] = kind
m.resourceToKind[plural] = kind
// 根據kind找到對應的單複數resource
m.kindToPluralResource[kind] = plural
// kind到scope的轉換
m.kindToScope[kind] = scope
}
RESTMapper其實包含的是一種轉換關係,resource到kind,kind到resource,kind到scope的轉換。resource還分單數和複數。
kind和resource有什麼區別呢?二者都是字串,kind是通過Kind=reflector.TypeOf(&Pod{}).Elem().Name()進行取值,去的就是Pod這個結構體的名字。resource是通過plural, singular := KindToResource(kind)取值。singular是將Kind轉換為小寫字母,而plural是變為複數。
示例:以Pod為例,Kind是{Group:””, Version: “v1”, Kind: “Pod”},那麼singular是{Group:””, Version: “v1”, Kind: “pod”},plural則是{Group:””, Version:”v1″, Resource:”pods”}。
resource要區分單複數,是為了獲取Pods資訊。比如可以kubectl get pod,也可以kubectl get pods.
到這裡RESTMapper也基本初始化完了,綜合上面所有的初始化可以看到,其實主要用internal version和external versions填充Scheme,用external versions去填充GroupMeta以及其成員RESTMapper。
GroupMeta有啥作用呢?主要用於初始化APIGroupVersion。
API資源註冊為restful api
之前所有的初始化都是為了這步做鋪墊,上面還有一個APIGroupInfo和APIGroupVersion都沒有進行介紹,這一節都會出現。
當API資源初始化完成以後,需要將這些API資源註冊為restful api,用來接收使用者的請求。
kube-apiServer使用了go-restful這套框架,裡面主要包括三種物件:
-
Container: 一個Container包含多個WebService
-
WebService: 一個WebService包含多條route
-
Route: 一條route包含一個method(GET、POST、DELETE等),一條具體的path(URL)以及一個響應的handler function。
API註冊的入口函式有兩個: m.InstallAPIs 和 m.InstallLegacyAPI。
檔案路徑:pkg/master/master.go
這兩個函式分別用於註冊”/api”和”/apis”的API,這裡先拿InstallLegacyAPI進行介紹。
這些介面都是在config.Complete().New()函式中被呼叫:
restOptionsFactory := restOptionsFactory{
deleteCollectionWorkers: c.DeleteCollectionWorkers,
enableGarbageCollection: c.GenericConfig.EnableGarbageCollection,
storageFactory: c.StorageFactory,
}
// 判斷是否使能了用於Watch的Cache
// 有無cache賦值的是不同的介面實現
// restOptionsFactory.storageDecorator:是一個各個資源的REST interface(CRUD)裝飾者
// 後面呼叫NewStorage()時會用到該介面,並輸出對應的CRUD介面及銷燬介面。
// 可以參考pkg/registry/core/pod/etcd/etcd.go中的NewStorage()
// 其實這裡有無cache的介面差異就在於:有cache的話,就提供操作cache的介面;無cache的話,就提供直接操作etcd的介面
if c.EnableWatchCache {
restOptionsFactory.storageDecorator = registry.StorageWithCacher
} else {
restOptionsFactory.storageDecorator = generic.UndecoratedStorage
}
// 判斷/api/v1的group是否已經註冊並enable,是的話再進行install
if c.GenericConfig.APIResourceConfigSource.AnyResourcesForVersionEnabled(apiv1.SchemeGroupVersion) {
// 該物件主要提供了一個NewLegacyRESTStorage()的介面
legacyRESTStorageProvider := corerest.LegacyRESTStorageProvider{
StorageFactory: c.StorageFactory,
ProxyTransport: c.ProxyTransport,
KubeletClientConfig: c.KubeletClientConfig,
EventTTL: c.EventTTL,
ServiceIPRange: c.ServiceIPRange,
ServiceNodePortRange: c.ServiceNodePortRange,
LoopbackClientConfig: c.GenericConfig.LoopbackClientConfig,
}
// 進行"/api/v1"的API安裝
m.InstallLegacyAPI(c.Config, restOptionsFactory.NewFor, legacyRESTStorageProvider)
}
繼續檢視m.InstallLegacyAPI():
func (m *Master) InstallLegacyAPI(c *Config, restOptionsGetter genericapiserver.RESTOptionsGetter, legacyRESTStorageProvider corerest.LegacyRESTStorageProvider) {
// 該物件前面介紹過了,比較關鍵,需要深入檢視
// 返回了RESTStorage和apiGroupInfo,都是重量級的成員
// 這些初始化也就在這個介面中
legacyRESTStorage, apiGroupInfo, err := legacyRESTStorageProvider.NewLegacyRESTStorage(restOptionsGetter)
if err != nil {
glog.Fatalf("Error building core storage: %v", err)
}
// 判斷是否enable了controller,預設是true,這裡跟主題關係不大,暫不深入
if c.EnableCoreControllers {
serviceClient := coreclient.NewForConfigOrDie(c.GenericConfig.LoopbackClientConfig)
bootstrapController := c.NewBootstrapController(legacyRESTStorage, serviceClient)
if err := m.GenericAPIServer.AddPostStartHook("bootstrap-controller", bootstrapController.PostStartHook); err != nil {
glog.Fatalf("Error registering PostStartHook %q: %v", "bootstrap-controller", err)
}
}
// install core Group`s API
if err := m.GenericAPIServer.InstallLegacyAPIGroup(genericapiserver.DefaultLegacyAPIPrefix, &apiGroupInfo); err != nil {
glog.Fatalf("Error in registering group versions: %v", err)
}
}
先看下建立APIGroupVersion和RESTStorage物件的介面NewLegacyRESTStorage().
路徑:pkg/registry/core/rest/storage_core.go
func (c LegacyRESTStorageProvider) NewLegacyRESTStorage(restOptionsGetter genericapiserver.RESTOptionsGetter) (LegacyRESTStorage, genericapiserver.APIGroupInfo, error) {
// 初始化建立一個APIGroupVersion
apiGroupInfo := genericapiserver.APIGroupInfo{
// 該GroupMeta是從APIRegistrationManager初始化後的結構體獲取
GroupMeta: *registered.GroupOrDie(api.GroupName),
VersionedResourcesStorageMap: map[string]map[string]rest.Storage{},
// 這個api.Scheme之前已經介紹過其初始化了
Scheme: api.Scheme,
ParameterCodec: api.ParameterCodec,
NegotiatedSerializer: api.Codecs,
SubresourceGroupVersionKind: map[string]unversioned.GroupVersionKind{},
}
// 判斷下autoscaling是否已經註冊並使能,是的話加入到apiGroupInfo.SubresourceGroupVersionKind
// key是該資源的path
if autoscalingGroupVersion := (unversioned.GroupVersion{Group: "autoscaling", Version: "v1"}); registered.IsEnabledVersion(autoscalingGroupVersion) {
apiGroupInfo.SubresourceGroupVersionKind["replicationcontrollers/scale"] = autoscalingGroupVersion.WithKind("Scale")
}
var podDisruptionClient policyclient.PodDisruptionBudgetsGetter
if policyGroupVersion := (unversioned.GroupVersion{Group: "policy", Version: "v1beta1"}); registered.IsEnabledVersion(policyGroupVersion) {
apiGroupInfo.SubresourceGroupVersionKind["pods/eviction"] = policyGroupVersion.WithKind("Eviction")
var err error
podDisruptionClient, err = policyclient.NewForConfig(c.LoopbackClientConfig)
if err != nil {
return LegacyRESTStorage{}, genericapiserver.APIGroupInfo{}, err
}
}
// 初始化一個LegacyRESTStorage物件
// 下面會進行各個介面的初始化,會有Node註冊,IP申請,NodePort申請等等
restStorage := LegacyRESTStorage{}
// 建立各類Storage
podTemplateStorage := podtemplateetcd.NewREST(restOptionsGetter(api.Resource("podTemplates")))
eventStorage := eventetcd.NewREST(restOptionsGetter(api.Resource("events")), uint64(c.EventTTL.Seconds()))
limitRangeStorage := limitrangeetcd.NewREST(restOptionsGetter(api.Resource("limitRanges")))
resourceQuotaStorage, resourceQuotaStatusStorage := resourcequotaetcd.NewREST(restOptionsGetter(api.Resource("resourceQuotas")))
secretStorage := secretetcd.NewREST(restOptionsGetter(api.Resource("secrets")))
serviceAccountStorage := serviceaccountetcd.NewREST(restOptionsGetter(api.Resource("serviceAccounts")))
persistentVolumeStorage, persistentVolumeStatusStorage := pvetcd.NewREST(restOptionsGetter(api.Resource("persistentVolumes")))
persistentVolumeClaimStorage, persistentVolumeClaimStatusStorage := pvcetcd.NewREST(restOptionsGetter(api.Resource("persistentVolumeClaims")))
configMapStorage := configmapetcd.NewREST(restOptionsGetter(api.Resource("configMaps")))
namespaceStorage, namespaceStatusStorage, namespaceFinalizeStorage := namespaceetcd.NewREST(restOptionsGetter(api.Resource("namespaces")))
restStorage.NamespaceRegistry = namespace.NewRegistry(namespaceStorage)
endpointsStorage := endpointsetcd.NewREST(restOptionsGetter(api.Resource("endpoints")))
restStorage.EndpointRegistry = endpoint.NewRegistry(endpointsStorage)
nodeStorage, err := nodeetcd.NewStorage(restOptionsGetter(api.Resource("nodes")), c.KubeletClientConfig, c.ProxyTransport)
if err != nil {
return LegacyRESTStorage{}, genericapiserver.APIGroupInfo{}, err
}
restStorage.NodeRegistry = node.NewRegistry(nodeStorage.Node)
// 建立PodStorage
// api.Resource("pods")是合成了一個GroupResource的結構
podStorage := podetcd.NewStorage(
restOptionsGetter(api.Resource("pods")),
nodeStorage.KubeletConnectionInfo,
c.ProxyTransport,
podDisruptionClient,
)
serviceRESTStorage, serviceStatusStorage := serviceetcd.NewREST(restOptionsGetter(api.Resource("services")))
restStorage.ServiceRegistry = service.NewRegistry(serviceRESTStorage)
var serviceClusterIPRegistry rangeallocation.RangeRegistry
serviceClusterIPRange := c.ServiceIPRange
if serviceClusterIPRange.IP == nil {
return LegacyRESTStorage{}, genericapiserver.APIGroupInfo{}, fmt.Errorf("service clusterIPRange is missing")
}
serviceStorageConfig, err := c.StorageFactory.NewConfig(api.Resource("services"))
if err != nil {
return LegacyRESTStorage{}, genericapiserver.APIGroupInfo{}, err
}
ServiceClusterIPAllocator := ipallocator.NewAllocatorCIDRRange(&serviceClusterIPRange, func(max int, rangeSpec string) allocator.Interface {
mem := allocator.NewAllocationMap(max, rangeSpec)
// TODO etcdallocator package to return a storage interface via the storageFactory
etcd := etcdallocator.NewEtcd(mem, "/ranges/serviceips", api.Resource("serviceipallocations"), serviceStorageConfig)
serviceClusterIPRegistry = etcd
return etcd
})
restStorage.ServiceClusterIPAllocator = serviceClusterIPRegistry
var serviceNodePortRegistry rangeallocation.RangeRegistry
ServiceNodePortAllocator := portallocator.NewPortAllocatorCustom(c.ServiceNodePortRange, func(max int, rangeSpec string) allocator.Interface {
mem := allocator.NewAllocationMap(max, rangeSpec)
// TODO etcdallocator package to return a storage interface via the storageFactory
etcd := etcdallocator.NewEtcd(mem, "/ranges/servicenodeports", api.Resource("servicenodeportallocations"), serviceStorageConfig)
serviceNodePortRegistry = etcd
return etcd
})
restStorage.ServiceNodePortAllocator = serviceNodePortRegistry
controllerStorage := controlleretcd.NewStorage(restOptionsGetter(api.Resource("replicationControllers")))
serviceRest := service.NewStorage(restStorage.ServiceRegistry, restStorage.EndpointRegistry, ServiceClusterIPAllocator, ServiceNodePortAllocator, c.ProxyTransport)
// 初始化了一個restStorage的map,然後賦值給APIGroupInfo.VersionedResourcesStorageMap["v1"]
restStorageMap := map[string]rest.Storage{
"pods": podStorage.Pod,
"pods/attach": podStorage.Attach,
"pods/status": podStorage.Status,
"pods/log": podStorage.Log,
"pods/exec": podStorage.Exec,
"pods/portforward": podStorage.PortForward,
"pods/proxy": podStorage.Proxy,
"pods/binding": podStorage.Binding,
"bindings": podStorage.Binding,
"podTemplates": podTemplateStorage,
"replicationControllers": controllerStorage.Controller,
"replicationControllers/status": controllerStorage.Status,
"services": serviceRest.Service,
"services/proxy": serviceRest.Proxy,
"services/status": serviceStatusStorage,
"endpoints": endpointsStorage,
"nodes": nodeStorage.Node,
"nodes/status": nodeStorage.Status,
"nodes/proxy": nodeStorage.Proxy,
"events": eventStorage,
"limitRanges": limitRangeStorage,
"resourceQuotas": resourceQuotaStorage,
"resourceQuotas/status": resourceQuotaStatusStorage,
"namespaces": namespaceStorage,
"namespaces/status": namespaceStatusStorage,
"namespaces/finalize": namespaceFinalizeStorage,
"secrets": secretStorage,
"serviceAccounts": serviceAccountStorage,
"persistentVolumes": persistentVolumeStorage,
"persistentVolumes/status": persistentVolumeStatusStorage,
"persistentVolumeClaims": persistentVolumeClaimStorage,
"persistentVolumeClaims/status": persistentVolumeClaimStatusStorage,
"configMaps": configMapStorage,
"componentStatuses": componentstatus.NewStorage(componentStatusStorage{c.StorageFactory}.serversToValidate),
}
if registered.IsEnabledVersion(unversioned.GroupVersion{Group: "autoscaling", Version: "v1"}) {
restStorageMap["replicationControllers/scale"] = controllerStorage.Scale
}
if registered.IsEnabledVersion(unversioned.GroupVersion{Group: "policy", Version: "v1beta1"}) {
restStorageMap["pods/eviction"] = podStorage.Eviction
}
// 將上面的restStorageMap賦值給v1
apiGroupInfo.VersionedResourcesStorageMap["v1"] = restStorageMap
return restStorage, apiGroupInfo, nil
}
看完這個介面後,我們繼續回到前面,看下m.GenericAPIServer.InstallLegacyAPIGroup()介面:
路徑:pkg/genericapiserver/genericapiserver.go
func (s *GenericAPIServer) InstallLegacyAPIGroup(apiPrefix string, apiGroupInfo *APIGroupInfo) error {
// 判斷字首引數是否正確
if !s.legacyAPIGroupPrefixes.Has(apiPrefix) {
return fmt.Errorf("%q is not in the allowed legacy API prefixes: %v", apiPrefix, s.legacyAPIGroupPrefixes.List())
}
// 關鍵介面,真正install API
if err := s.installAPIResources(apiPrefix, apiGroupInfo); err != nil {
return err
}
// 獲取了該Group下所有的version資訊
// 應該用於發現當前的所有版本資訊
apiVersions := []string{}
for _, groupVersion := range apiGroupInfo.GroupMeta.GroupVersions {
apiVersions = append(apiVersions, groupVersion.Version)
}
// Install the version handler.
// Add a handler at /<apiPrefix> to enumerate the supported api versions.
apiserver.AddApiWebService(s.Serializer, s.HandlerContainer.Container, apiPrefix, func(req *restful.Request) *unversioned.APIVersions {
clientIP := utilnet.GetClientIP(req.Request)
apiVersionsForDiscovery := unversioned.APIVersions{
ServerAddressByClientCIDRs: s.discoveryAddresses.ServerAddressByClientCIDRs(clientIP),
Versions: apiVersions,
}
return &apiVersionsForDiscovery
})
return nil
}
那我們繼續進入關鍵介面s.installAPIResources(apiPrefix, apiGroupInfo):
func (s *GenericAPIServer) installAPIResources(apiPrefix string, apiGroupInfo *APIGroupInfo) error {
// 遍歷該Group下的所有GroupVersons
for _, groupVersion := range apiGroupInfo.GroupMeta.GroupVersions {
// 建立APIGroupVersion
apiGroupVersion, err := s.getAPIGroupVersion(apiGroupInfo, groupVersion, apiPrefix)
if err != nil {
return err
}
if apiGroupInfo.OptionsExternalVersion != nil {
apiGroupVersion.OptionsExternalVersion = apiGroupInfo.OptionsExternalVersion
}
// 根據之前建立的APIGroupVersion,然後安裝restful API
// 該s.HandlerContainer.Container就是go-restful的Container
if err := apiGroupVersion.InstallREST(s.HandlerContainer.Container); err != nil {
return fmt.Errorf("Unable to setup API %v: %v", apiGroupInfo, err)
}
}
return nil
}
func (s *GenericAPIServer) getAPIGroupVersion(apiGroupInfo *APIGroupInfo, groupVersion unversioned.GroupVersion, apiPrefix string) (*apiserver.APIGroupVersion, error) {
storage := make(map[string]rest.Storage)
// 如果是核心組的話,Version為"v1",該VersionedResourcesStorageMap的初始化要看
// 之前的NewLegacyRESTStorage()介面,在該介面中進行的初始化
// 遍歷所有的ResourcesStorage,並賦值給storage
for k, v := range apiGroupInfo.VersionedResourcesStorageMap[groupVersion.Version] {
storage[strings.ToLower(k)] = v
}
// 建立APIGroupVersion
version, err := s.newAPIGroupVersion(apiGroupInfo, groupVersion)
// 設定Prefix, 核心組的話是"/api"
version.Root = apiPrefix
version.Storage = storage
return version, err
}
到這裡從API資源到restful API,就已經註冊完成了。
至於apiGroupVersion.InstallREST()介面,我們這裡先簡單介紹,後面會另起一篇文章結合go-restful進行介紹。
InstallREST()介面路徑:pkg/apiserver/apiserver.go
func (g *APIGroupVersion) InstallREST(container *restful.Container) error {
installer := g.newInstaller()
ws := installer.NewWebService()
apiResources, registrationErrors := installer.Install(ws)
lister := g.ResourceLister
if lister == nil {
lister = staticLister{apiResources}
}
AddSupportedResourcesWebService(g.Serializer, ws, g.GroupVersion, lister)
container.Add(ws)
return utilerrors.NewAggregate(registrationErrors)
}
func (a *APIInstaller) Install(ws *restful.WebService) (apiResources []unversioned.APIResource, errors []error) {
errors = make([]error, 0)
proxyHandler := (&ProxyHandler{
prefix: a.prefix + "/proxy/",
storage: a.group.Storage,
serializer: a.group.Serializer,
mapper: a.group.Context,
})
// Register the paths in a deterministic (sorted) order to get a deterministic swagger spec.
paths := make([]string, len(a.group.Storage))
var i int = 0
for path := range a.group.Storage {
paths[i] = path
i++
}
sort.Strings(paths)
for _, path := range paths {
// 該介面是關鍵,最終將一個rest.Storage物件轉換成實際的restful api,比如getter、lister等處理函式,並將實際的URL關聯起來
apiResource, err := a.registerResourceHandlers(path, a.group.Storage[path], ws, proxyHandler)
if err != nil {
errors = append(errors, fmt.Errorf("error in registering resource: %s, %v", path, err))
}
if apiResource != nil {
apiResources = append(apiResources, *apiResource)
}
}
return apiResources, errors
}
在這個註冊的過程中,InstallREST最終呼叫了registerResourceHandlers()介面,該介面最終會把一個rest.Storage物件轉換成實際的getter、lister等處理函式,並和實際的URL關聯起來。
使用者引數配置
-
runtime-config: 用於enable/disable extensions group。預設的情況下DaemonSets、Deployments、HorizontalPodAutoscalers、Ingress、Jobs和ReplicaSets是使能的,還有v1下的預設都是使能的。另外的功能就可以通過該配置進行設定. 例如:disable deployments: –runtime-config=extensions/v1beta1/deployments=false.
參考資料
1.api-group.md: https://github.com/kubernetes…