k8s APIServer源码: api注册详细细节
基于版本 1.6.7
前面介绍了, api注册过程
问题: go-restful github的route
中, handler
和path
是如何绑定在一起的? handler
在哪里定义的?
以/api
为例
前面介绍到/api
和/apis
分别注册加入到Container
, 而最终, 二者调用installer.Install(ws)
. 这一步, 我们需要进一步了解细节.
webservice.add(route)
问题: 构建Route加入到WebService在哪里处理的?
- vendor/k8s.io/apiserver/pkg/endpoints/installer.go
func (a *APIInstaller) Install(ws *restful.WebService) (apiResources []metav1.APIResource, errors []error) { // 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 { apiResource, err := a.registerResourceHandlers(path, a.group.Storage[path], ws, proxyHandler) } }
- apiserver/pkg/endpoints/installer.go
仅摘录部分核心代码, 这里, 获取handler
之后, 构建route
, 然后加入到webservice
中
func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storage, ws *restful.WebService, proxyHandler http.Handler) (*metav1.APIResource, error) { ... creater, isCreater := storage.(rest.Creater) .... actions = appendIf(actions, action{"POST", resourcePath, resourceParams, namer, false}, isCreater) case "POST": // 获取handler handler = handlers.CreateResource(creater, reqScope, a.group.Typer, admit) // => next // 构建route, action.Path -> handler route := ws.POST(action.Path).To(handler). Doc(doc). Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")). Operation("create"+namespaced+kind+strings.Title(subresource)+operationSuffix). Produces(append(storageMeta.ProducesMIMETypes(action.Verb), mediaTypes...)...). Returns(http.StatusOK, "OK", versionedObject). Reads(versionedObject). Writes(versionedObject) addParams(route, action.Params) // 添加route到webservice ws.Route(route) }
create handler
- vendor/k8s.io/apiserver/pkg/endpoints/handlers/rest.go
// CreateResource returns a function that will handle a resource creation. func CreateResource(r rest.Creater, scope RequestScope, typer runtime.ObjectTyper, admit admission.Interface) restful.RouteFunction { return createHandler(&namedCreaterAdapter{r}, scope, typer, admit, false) } func createHandler(r rest.NamedCreater, scope RequestScope, typer runtime.ObjectTyper, admit admission.Interface, includeName bool) restful.RouteFunction { return func(req *restful.Request, res *restful.Response) { original := r.New() // => here responsewriters.WriteObject(http.StatusCreated, scope.Kind.GroupVersion(), scope.Serializer, result, w, req.Request) } }
即, 最终handler
执行时, 调用的是rest.Creater.New()
这里的 Creater
是一个interface
# vendor/k8s.io/apiserver/pkg/registry/rest/rest.go // Creater is an object that can create an instance of a RESTful object. type Creater interface { // New returns an empty object that can be used with Create after request data has been put into it. // This object must be a pointer type for use with Codec.DecodeInto([]byte, runtime.Object) New() runtime.Object // Create creates a new version of a resource. Create(ctx genericapirequest.Context, obj runtime.Object) (runtime.Object, error) }
回到最初的问题
最终, handler
调用的是rest.Creater.New()
而creater
声明的位置
- apiserver/pkg/endpoints/installer.go
creater, isCreater := storage.(rest.Creater)
这里, 想要知道handler
最终调用的是哪里定义的方法, 我们需要分析storage
的来源
第一步: 链路分析
调用链
// pkg/master/master.go // => got: apiGroupInfo 初始化 func (m *Master) InstallLegacyAPI(c *Config, restOptionsGetter generic.RESTOptionsGetter, legacyRESTStorageProvider corerest.LegacyRESTStorageProvider) { legacyRESTStorage, apiGroupInfo, err := legacyRESTStorageProvider.NewLegacyRESTStorage(restOptionsGetter) m.GenericAPIServer.InstallLegacyAPIGroup(genericapiserver.DefaultLegacyAPIPrefix, &apiGroupInfo) } // vendor/k8s.io/apiserver/pkg/server/genericapiserver.go func (s *GenericAPIServer) InstallLegacyAPIGroup(apiPrefix string, apiGroupInfo *APIGroupInfo) error { s.installAPIResources(apiPrefix, apiGroupInfo) } // vendor/k8s.io/apiserver/pkg/server/genericapiserver.go // NOTE => apigroup TO apigroupversion // => got: apigroupversion func (s *GenericAPIServer) installAPIResources(apiPrefix string, apiGroupInfo *APIGroupInfo) error { for _, groupVersion := range apiGroupInfo.GroupMeta.GroupVersions { apiGroupVersion := s.getAPIGroupVersion(apiGroupInfo, groupVersion, apiPrefix) apiGroupVersion.InstallREST(s.HandlerContainer.Container); } } // vendor/k8s.io/apiserver/pkg/server/genericapiserver.go // => got: APIGroupVersion.Storage = make(map[string]rest.Storage // APIGroupVersion.Storage[path] = apiGroupInfo.VersionedResourcesStorageMap[groupVersion.Version][path] func (s *GenericAPIServer) getAPIGroupVersion(apiGroupInfo *APIGroupInfo, groupVersion schema.GroupVersion, apiPrefix string) *genericapi.APIGroupVersion { storage := make(map[string]rest.Storage) for k, v := range apiGroupInfo.VersionedResourcesStorageMap[groupVersion.Version] { storage[strings.ToLower(k)] = v } version := s.newAPIGroupVersion(apiGroupInfo, groupVersion) version.Root = apiPrefix version.Storage = storage return version } // vendor/k8s.io/apiserver/pkg/endpoints/groupversion.go // => got: installer.group = APIGroupVersion func (g *APIGroupVersion) InstallREST(container *restful.Container) error { installer := g.newInstaller() installer.Install(ws) } func (g *APIGroupVersion) newInstaller() *APIInstaller { prefix := path.Join(g.Root, g.GroupVersion.Group, g.GroupVersion.Version) installer := &APIInstaller{ group: g, // group = APIGroupVersion prefix: prefix, minRequestTimeout: g.MinRequestTimeout, } return installer } // vendor/k8s.io/apiserver/pkg/endpoints/installer.go // got: a.group.Storage[path] = APIInstaller.group.Storage[path] = APIGroupVersion.Storage[path] // APIGroupVersion.Storage[path] = apiGroupInfo.VersionedResourcesStorageMap[groupVersion.Version][path] func (a *APIInstaller) Install(ws *restful.WebService) (apiResources []metav1.APIResource, errors []error) { // 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 { apiResource, err := a.registerResourceHandlers(path, a.group.Storage[path], ws, proxyHandler) } } // apiserver/pkg/endpoints/installer.go // got: storage = apiGroupInfo.VersionedResourcesStorageMap[groupVersion.Version][path] func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storage, ws *restful.WebService, proxyHandler http.Handler) (*metav1.APIResource, error) { creater, isCreater := storage.(rest.Creater) }
到了这里, 其实有了一个清晰的结论
// apiGroupInfo.VersionedResourcesStorageMap storage = apiGroupInfo.VersionedResourcesStorageMap[groupVersion.Version][path] creater, isCreater := storage.(rest.Creater)
此时, 我们再反向寻找apiGroupInfo
初始化的位置
第二步: apiGroupInfo 初始化
- pkg/master/master.go
func (m *Master) InstallLegacyAPI(c *Config, restOptionsGetter generic.RESTOptionsGetter, legacyRESTStorageProvider corerest.LegacyRESTStorageProvider) { legacyRESTStorage, apiGroupInfo, err := legacyRESTStorageProvider.NewLegacyRESTStorage(restOptionsGetter) // => next m.GenericAPIServer.InstallLegacyAPIGroup(genericapiserver.DefaultLegacyAPIPrefix, &apiGroupInfo) }
- pkg/registry/core/rest/storage_core.go
func (c LegacyRESTStorageProvider) NewLegacyRESTStorage(restOptionsGetter generic.RESTOptionsGetter) (LegacyRESTStorage, genericapiserver.APIGroupInfo, error) { // 初始化: VersionedResourcesStorageMap apiGroupInfo := genericapiserver.APIGroupInfo{ GroupMeta: *api.Registry.GroupOrDie(api.GroupName), VersionedResourcesStorageMap: map[string]map[string]rest.Storage{}, Scheme: api.Scheme, ParameterCodec: api.ParameterCodec, NegotiatedSerializer: api.Codecs, SubresourceGroupVersionKind: map[string]schema.GroupVersionKind{}, } // ...... // 初始化了一个restStorage的map,然后赋值给APIGroupInfo.VersionedResourcesStorageMap["v1"] restStorageMap := map[string]rest.Storage{ "pods": podStorage.Pod, "pods/attach": podStorage.Attach, "pods/status": podStorage.Status, "services": serviceRest.Service, "nodes": nodeStorage.Node, ..... } apiGroupInfo.VersionedResourcesStorageMap["v1"] = restStorageMap return restStorage, apiGroupInfo, nil }
即
apiGroupInfo.VersionedResourcesStorageMap["v1"] = map[string]rest.Storage{ "pods": podStorage.Pod, "pods/attach": podStorage.Attach, "pods/status": podStorage.Status, "services": serviceRest.Service, "nodes": nodeStorage.Node, ..... }
此时, 根据
// apiGroupInfo.VersionedResourcesStorageMap storage = apiGroupInfo.VersionedResourcesStorageMap[groupVersion.Version][path] creater, isCreater := storage.(rest.Creater)
我们可以得到
storage = apiGroupInfo.VersionedResourcesStorageMap["v1"]["pods"] // equals storage = podStorage.Pod creater, isCreater := (podStorage.Pod).(rest.Creater)
然后, 我们再看下podStorage.Pod
的实现
第三步: podStorage.Pod
- pkg/registry/core/pod/storage/storage.go
type PodStorage struct { Pod *REST ... } // REST implements a RESTStorage for pods type REST struct { *genericregistry.Store // => NOTE proxyTransport http.RoundTripper }
即, PodStorage.Pod
类型是 REST
, 而REST.genericregistry.Store
, 其定义文件中存在
- vendor/k8s.io/apiserver/pkg/registry/generic/registry/store.go
// New implements RESTStorage.New. func (e *Store) New() runtime.Object { return e.NewFunc() } func (e *Store) Create(ctx genericapirequest.Context, obj runtime.Object) (runtime.Object, error) { }
即,
storage = apiGroupInfo.VersionedResourcesStorageMap["v1"]["pods"] // equals storage = podStorage.Pod creater, isCreater := (podStorage.Pod).(rest.Creater) // equals creater, isCreater := (REST).(rest.Creater) creater, isCreater := (*genericregistry.Store).(rest.Creater)
第四步: creater.New()
- vendor/k8s.io/apiserver/pkg/registry/generic/registry/store.go
// New implements RESTStorage.New. func (e *Store) New() runtime.Object { return e.NewFunc() }
- pkg/registry/core/pod/storage/storage.go
func NewStorage(optsGetter generic.RESTOptionsGetter, k client.ConnectionInfoGetter, proxyTransport http.RoundTripper, podDisruptionBudgetClient policyclient.PodDisruptionBudgetsGetter) PodStorage { store := &genericregistry.Store{ NewFunc: func() runtime.Object { return &api.Pod{} }, .... } } // pkg/api/types.go type Pod struct { metav1.TypeMeta // +optional metav1.ObjectMeta // Spec defines the behavior of a pod. // +optional Spec PodSpec // Status represents the current information about a pod. This data may not be up // to date. // +optional Status PodStatus }
etcd相关的, 在后面介绍
版权声明:自由转载-非商用-非衍生-保持署名 | Creative Commons BY-NC-ND 3.0