基于版本 1.6.7

前面介绍了, api注册过程

问题: go-restful githubroute中, handlerpath是如何绑定在一起的? 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相关的, 在后面介绍