kubernetes源码解析---- apiserver路由构建解析(2)
kubernetes源码解析---- apiserver路由构建解析(2)
上文主要对go-restful这个包进行了简单的介绍,下面我们通过阅读代码来理解apiserver路由的详细构建过程。
(kubernetes代码版本:1.3.6 Commit id:ed3a29bd6aeb)
从启动位置main函数开始(kubernetes\cmd\kube-apiserver\apiserver.go):
func main() {
rand.Seed(time.Now().UTC().UnixNano())
// New APIServer
s := options.NewAPIServer()
s.AddFlags(pflag.CommandLine)
// 解析命令行参数
flag.InitFlags()
logs.InitLogs()
defer logs.FlushLogs()
verflag.PrintAndExitIfRequested()
// 启动APIServer
if err := app.Run(s); err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(1)
}
}
main函数里做的事情比较简单,主要是生成默认的ApiServer运行参数,解析命令行,设置log,然后调用 app.Run()方法启动服务。继续跟进这个Run()方法:
func Run(s *options.APIServer) error {
genericvalidation.VerifyEtcdServersList(s.ServerRunOptions)
genericapiserver.DefaultAndValidateRunOptions(s.ServerRunOptions)
// master config组装
...
// New master
m, err := master.New(config)
if err != nil {
return err
}
sharedInformers.Start(wait.NeverStop)
// 启动master
m.Run(s.ServerRunOptions)
return nil
Run()方法代码较长,这里只贴出了其中一部分。代码上,首先组装master config,其中包括ssh tunneler配置,storageFactory配置,authenticator配置和authorizer配置等,然后根据config创建并启动master。
这里有两个与路由构建相关的方法:master.New(config)和m.Run(s.ServerRunOptions)。我们先来跟进New()方法:
// New returns a new instance of Master from the given config.
// Certain config fields will be set to a default value if unset.
// Certain config fields must be specified, including:
// KubeletClient
func New(c *Config) (*Master, error) {
if c.KubeletClient == nil {
return nil, fmt.Errorf("Master.New() called with config.KubeletClient == nil")
}
// 创建并初始化GenericAPIServer
s, err := genericapiserver.New(c.Config)
if err != nil {
return nil, err
}
// 构造Master
m := &Master{
GenericAPIServer: s,
enableCoreControllers: c.EnableCoreControllers,
deleteCollectionWorkers: c.DeleteCollectionWorkers,
tunneler: c.Tunneler,
disableThirdPartyControllerForTesting: c.disableThirdPartyControllerForTesting,
}
// Add some hardcoded storage for now. Append to the map.
if c.RESTStorageProviders == nil {
c.RESTStorageProviders = map[string]RESTStorageProvider{}
}
c.RESTStorageProviders[appsapi.GroupName] = AppsRESTStorageProvider{}
c.RESTStorageProviders[autoscaling.GroupName] = AutoscalingRESTStorageProvider{}
c.RESTStorageProviders[batch.GroupName] = BatchRESTStorageProvider{}
c.RESTStorageProviders[certificates.GroupName] = CertificatesRESTStorageProvider{}
c.RESTStorageProviders[extensions.GroupName] = ExtensionsRESTStorageProvider{
ResourceInterface: m,
DisableThirdPartyControllerForTesting: m.disableThirdPartyControllerForTesting,
}
c.RESTStorageProviders[policy.GroupName] = PolicyRESTStorageProvider{}
c.RESTStorageProviders[rbac.GroupName] = RBACRESTStorageProvider{AuthorizerRBACSuperUser: c.AuthorizerRBACSuperUser}
c.RESTStorageProviders[authenticationv1beta1.GroupName] = AuthenticationRESTStorageProvider{Authenticator: c.Authenticator}
c.RESTStorageProviders[authorization.GroupName] = AuthorizationRESTStorageProvider{Authorizer: c.Authorizer}
// 安装APIs
m.InstallAPIs(c)
// TODO: Attempt clean shutdown?
if m.enableCoreControllers {
m.NewBootstrapController(c.EndpointReconcilerConfig).Start()
}
return m, nil
}
首先,调用genericapiserver.New(c.Config),创建一个genericapiserver对象。然后往master config中添加一些“hardcoded storage”,再调用m.InstallAPIs(c)安装APIs。
我们先跟进这个genericapiserver.New()方法:
// New returns a new instance of GenericAPIServer from the given config.
// Certain config fields will be set to a default value if unset,
// including:
// ServiceClusterIPRange
// ServiceNodePortRange
// MasterCount
// ReadWritePort
// PublicAddress
// Public fields:
// Handler -- The returned GenericAPIServer has a field TopHandler which is an
// http.Handler which handles all the endpoints provided by the GenericAPIServer,
// including the API, the UI, and miscellaneous debugging endpoints. All
// these are subject to authorization and authentication.
// InsecureHandler -- an http.Handler which handles all the same
// endpoints as Handler, but no authorization and authentication is done.
// Public methods:
// HandleWithAuth -- Allows caller to add an http.Handler for an endpoint
// that uses the same authentication and authorization (if any is configured)
// as the GenericAPIServer's built-in endpoints.
// If the caller wants to add additional endpoints not using the GenericAPIServer's
// auth, then the caller should create a handler for those endpoints, which delegates the
// any unhandled paths to "Handler".
func New(c *Config) (*GenericAPIServer, error) {
if c.Serializer == nil {
return nil, fmt.Errorf("Genericapiserver.New() called with config.Serializer == nil")
}
setDefaults(c)
s := &GenericAPIServer{
ServiceClusterIPRange: c.ServiceClusterIPRange,
ServiceNodePortRange: c.ServiceNodePortRange,
RootWebService: new(restful.WebService),
enableLogsSupport: c.EnableLogsSupport,
enableUISupport: c.EnableUISupport,
enableSwaggerSupport: c.EnableSwaggerSupport,
enableSwaggerUI: c.EnableSwaggerUI,
enableProfiling: c.EnableProfiling,
enableWatchCache: c.EnableWatchCache,
APIPrefix: c.APIPrefix,
APIGroupPrefix: c.APIGroupPrefix,
corsAllowedOriginList: c.CorsAllowedOriginList,
authenticator: c.Authenticator,
authorizer: c.Authorizer,
AdmissionControl: c.AdmissionControl,
RequestContextMapper: c.RequestContextMapper,
Serializer: c.Serializer,
cacheTimeout: c.CacheTimeout,
MinRequestTimeout: time.Duration(c.MinRequestTimeout) * time.Second,
MasterCount: c.MasterCount,
ExternalAddress: c.ExternalHost,
ClusterIP: c.PublicAddress,
PublicReadWritePort: c.ReadWritePort,
ServiceReadWriteIP: c.ServiceReadWriteIP,
ServiceReadWritePort: c.ServiceReadWritePort,
ExtraServicePorts: c.ExtraServicePorts,
ExtraEndpointPorts: c.ExtraEndpointPorts,
KubernetesServiceNodePort: c.KubernetesServiceNodePort,
apiGroupsForDiscovery: map[string]unversioned.APIGroup{},
enableOpenAPISupport: c.EnableOpenAPISupport,
openAPIInfo: c.OpenAPIInfo,
openAPIDefaultResponse: c.OpenAPIDefaultResponse,
}
// 初始化GenericAPIServer成员变量HandlerContainer与mux
if c.RestfulContainer != nil {
s.mux = c.RestfulContainer.ServeMux
s.HandlerContainer = c.RestfulContainer
} else {
mux := http.NewServeMux()
s.mux = mux
s.HandlerContainer = NewHandlerContainer(mux, c.Serializer)
}
// Use CurlyRouter to be able to use regular expressions in paths. Regular expressions are required in paths for example for proxy (where the path is proxy/{kind}/{name}/{*})
s.HandlerContainer.Router(restful.CurlyRouter{})
s.MuxHelper = &apiserver.MuxHelper{Mux: s.mux, RegisteredPaths: []string{}}
s.init(c)
return s, nil
}
New()方法创建一个GenericAPIServer对象并返回,这里初始化了GenericAPIServer结构用于构建Restful路由的两个成员变量:
mux : net/http包中原生的路由构造器。
> ServeMux is an HTTP request multiplexer.
> It matches the URL of each incoming request against a list of registered
> patterns and calls the handler for the pattern that most closely matches the URL.
HandlerContainer: 既是go-restful包中的Container类型对象(详细解释参见此系列文章1)。设置路由选择器为CurlyRouter。后面创建的Webservice都要加入到这个Container中。
退出这个New()方法,继续跟进Master结构的m.InstallAPIs(c)方法:
func (m *Master) InstallAPIs(c *Config) {
apiGroupsInfo := []genericapiserver.APIGroupInfo{}
// Install v1 unless disabled.
if c.APIResourceConfigSource.AnyResourcesForVersionEnabled(apiv1.SchemeGroupVersion) {
// Install v1 API.
m.initV1ResourcesStorage(c)
apiGroupInfo := genericapiserver.APIGroupInfo{
GroupMeta: *registered.GroupOrDie(api.GroupName),
VersionedResourcesStorageMap: map[string]map[string]rest.Storage{
"v1": m.v1ResourcesStorage,
},
IsLegacyGroup: true,
Scheme: api.Scheme,
ParameterCodec: api.ParameterCodec,
NegotiatedSerializer: api.Codecs,
SubresourceGroupVersionKind: map[string]unversioned.GroupVersionKind{},
}
if autoscalingGroupVersion := (unversioned.GroupVersion{Group: "autoscaling", Version: "v1"}); registered.IsEnabledVersion(autoscalingGroupVersion) {
apiGroupInfo.SubresourceGroupVersionKind["replicationcontrollers/scale"] = autoscalingGroupVersion.WithKind("Scale")
}
if policyGroupVersion := (unversioned.GroupVersion{Group: "policy", Version: "v1alpha1"}); registered.IsEnabledVersion(policyGroupVersion) {
apiGroupInfo.SubresourceGroupVersionKind["pods/eviction"] = policyGroupVersion.WithKind("Eviction")
}
apiGroupsInfo = append(apiGroupsInfo, apiGroupInfo)
}
...
// 安装APIs
if err := m.InstallAPIGroups(apiGroupsInfo); err != nil {
glog.Fatalf("Error in registering group versions: %v", err)
}
}
InstallAPIs()方法进行实际的API安装工作,这里贴出部分代码。主要关注m.initV1ResourcesStorage(c)和m.InstallAPIGroups(apiGroupsInfo)这两个方法。
m.initV1ResourcesStorage(c)初始化各种资源后端的存储配置,并存放到Master的成员变量v1ResourcesStorage中。这些后端存储其实也就是对应的Restful路由,只是这些路由还没有实装。
func (m *Master) initV1ResourcesStorage(c *Config) {
restOptions := func(resource string) generic.RESTOptions {
return m.GetRESTOptionsOrDie(c, api.Resource(resource))
}
// 生成资源对应的后端存储
podTemplateStorage := podtemplateetcd.NewREST(restOptions("podTemplates"))
eventStorage := eventetcd.NewREST(restOptions("events"), uint64(c.EventTTL.Seconds()))
limitRangeStorage := limitrangeetcd.NewREST(restOptions("limitRanges"))
resourceQuotaStorage, resourceQuotaStatusStorage := resourcequotaetcd.NewREST(restOptions("resourceQuotas"))
secretStorage := secretetcd.NewREST(restOptions("secrets"))
serviceAccountStorage := serviceaccountetcd.NewREST(restOptions("serviceAccounts"))
persistentVolumeStorage, persistentVolumeStatusStorage := pvetcd.NewREST(restOptions("persistentVolumes"))
persistentVolumeClaimStorage, persistentVolumeClaimStatusStorage := pvcetcd.NewREST(restOptions("persistentVolumeClaims"))
configMapStorage := configmapetcd.NewREST(restOptions("configMaps"))
namespaceStorage, namespaceStatusStorage, namespaceFinalizeStorage := namespaceetcd.NewREST(restOptions("namespaces"))
m.namespaceRegistry = namespace.NewRegistry(namespaceStorage)
endpointsStorage := endpointsetcd.NewREST(restOptions("endpoints"))
m.endpointRegistry = endpoint.NewRegistry(endpointsStorage)
nodeStorage := nodeetcd.NewStorage(restOptions("nodes"), c.KubeletClient, m.ProxyTransport)
m.nodeRegistry = node.NewRegistry(nodeStorage.Node)
podStorage := podetcd.NewStorage(
restOptions("pods"),
kubeletclient.ConnectionInfoGetter(nodeStorage.Node),
m.ProxyTransport,
)
serviceRESTStorage, serviceStatusStorage := serviceetcd.NewREST(restOptions("services"))
m.serviceRegistry = service.NewRegistry(serviceRESTStorage)
var serviceClusterIPRegistry rangeallocation.RangeRegistry
serviceClusterIPRange := m.ServiceClusterIPRange
if serviceClusterIPRange == nil {
glog.Fatalf("service clusterIPRange is nil")
return
}
serviceStorageConfig, err := c.StorageFactory.NewConfig(api.Resource("services"))
if err != nil {
glog.Fatal(err.Error())
}
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
})
m.serviceClusterIPAllocator = serviceClusterIPRegistry
var serviceNodePortRegistry rangeallocation.RangeRegistry
serviceNodePortAllocator := portallocator.NewPortAllocatorCustom(m.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
})
m.serviceNodePortAllocator = serviceNodePortRegistry
controllerStorage := controlleretcd.NewStorage(restOptions("replicationControllers"))
serviceRest := service.NewStorage(m.serviceRegistry, m.endpointRegistry, serviceClusterIPAllocator, serviceNodePortAllocator, m.ProxyTransport)
// 将(资源:存储)的匹配关系存放到v1ResourcesStorage中
// TODO: Factor out the core API registration
m.v1ResourcesStorage = 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(func() map[string]apiserver.Server { return m.getServersToValidate(c) }),
}
if registered.IsEnabledVersion(unversioned.GroupVersion{Group: "autoscaling", Version: "v1"}) {
m.v1ResourcesStorage["replicationControllers/scale"] = controllerStorage.Scale
}
if registered.IsEnabledVersion(unversioned.GroupVersion{Group: "policy", Version: "v1alpha1"}) {
m.v1ResourcesStorage["pods/eviction"] = podStorage.Eviction
}
}
以pod为例,首先构造pod的后端存储podStorage:
podStorage := podetcd.NewStorage(
restOptions("pods"),
kubeletclient.ConnectionInfoGetter(nodeStorage.Node),
m.ProxyTransport,
)
然后注册pod及其子资源的后端存储到成员变量v1ResourcesStorage中:
m.v1ResourcesStorage = 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,
...
}
那么APIs的实装在哪里,我们来看下m.InstallAPIGroups(apiGroupsInfo)这个方法:
// Exposes the given group versions in API. Helper method to install multiple group versions at once.
func (s *GenericAPIServer) InstallAPIGroups(groupsInfo []APIGroupInfo) error {
for _, apiGroupInfo := range groupsInfo {
if err := s.InstallAPIGroup(&apiGroupInfo); err != nil {
return err
}
}
return nil
}
InstallAPIGroups()实际上是批量运行InstallAPIGroup()方法的封装,继续跟进到InstallAPIGroup()这个方法:
// Exposes the given group version in API.
func (s *GenericAPIServer) InstallAPIGroup(apiGroupInfo *APIGroupInfo) error {
apiPrefix := s.APIGroupPrefix
if apiGroupInfo.IsLegacyGroup {
apiPrefix = s.APIPrefix
}
// Install REST handlers for all the versions in this group.
apiVersions := []string{}
for _, groupVersion := range apiGroupInfo.GroupMeta.GroupVersions {
apiVersions = append(apiVersions, groupVersion.Version)
apiGroupVersion, err := s.getAPIGroupVersion(apiGroupInfo, groupVersion, apiPrefix)
if err != nil {
return err
}
if apiGroupInfo.OptionsExternalVersion != nil {
apiGroupVersion.OptionsExternalVersion = apiGroupInfo.OptionsExternalVersion
}
if err := apiGroupVersion.InstallREST(s.HandlerContainer); err != nil {
return fmt.Errorf("Unable to setup API %v: %v", apiGroupInfo, err)
}
}
// Install the version handler.
...
apiserver.InstallServiceErrorHandler(s.Serializer, s.HandlerContainer, s.NewRequestInfoResolver(), apiVersions)
return nil
}
调用getAPIGroupVersion()方法组装APIGroupVersion类型对象,APIGroupVersion用于转换存储配置(rest.Storage)到Restful后端处理(Restful Handlers)。
APIGroupVersion is a helper for exposing rest.Storage objects as http.Handlers via go-restful
然后调用APIGroupVersion中的InstallREST()方法实装APIs:
// InstallREST registers the REST handlers (storage, watch, proxy and redirect) into a restful Container.
// It is expected that the provided path root prefix will serve all operations. Root MUST NOT end
// in a slash.
func (g *APIGroupVersion) InstallREST(container *restful.Container) error {
installer := g.newInstaller()
// 为每个资源新建WebService
ws := installer.NewWebService()
// 安装Routes
apiResources, registrationErrors := installer.Install(ws)
lister := g.ResourceLister
if lister == nil {
lister = staticLister{apiResources}
}
AddSupportedResourcesWebService(g.Serializer, ws, g.GroupVersion, lister)
// 将WebService加入到Container中
container.Add(ws)
return utilerrors.NewAggregate(registrationErrors)
}
InstallREST()代码逻辑很简单,就是为资源新建一个WebService,然后调用installer.Install(ws)向WebService中添加资源对应的路由,最后将WebService加入到Container中。
installer.Install(ws)方法中调用APIInstaller.registerResourceHandlers()来进行Routes的安装,registerResourceHandlers()方法代码有500行左右,这里只贴出关键逻辑:
func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storage, ws *restful.WebService, proxyHandler http.Handler) (*unversioned.APIResource, error) {
...
// what verbs are supported by the storage, used to know what verbs we support per path
creater, isCreater := storage.(rest.Creater)
namedCreater, isNamedCreater := storage.(rest.NamedCreater)
lister, isLister := storage.(rest.Lister)
getter, isGetter := storage.(rest.Getter)
getterWithOptions, isGetterWithOptions := storage.(rest.GetterWithOptions)
deleter, isDeleter := storage.(rest.Deleter)
gracefulDeleter, isGracefulDeleter := storage.(rest.GracefulDeleter)
collectionDeleter, isCollectionDeleter := storage.(rest.CollectionDeleter)
updater, isUpdater := storage.(rest.Updater)
patcher, isPatcher := storage.(rest.Patcher)
watcher, isWatcher := storage.(rest.Watcher)
_, isRedirector := storage.(rest.Redirector)
connecter, isConnecter := storage.(rest.Connecter)
storageMeta, isMetadata := storage.(rest.StorageMetadata)
if !isMetadata {
storageMeta = defaultStorageMetadata{}
}
exporter, isExporter := storage.(rest.Exporter)
if !isExporter {
exporter = nil
}
versionedExportOptions, err := a.group.Creater.New(optionsExternalVersion.WithKind("ExportOptions"))
if err != nil {
return nil, err
}
if isNamedCreater {
isCreater = true
}
...
var apiResource unversioned.APIResource
// Get the list of actions for the given scope.
switch scope.Name() {
case meta.RESTScopeNameRoot:
// 组装actions
...
break
case meta.RESTScopeNameNamespace:
// Handler for standard REST verbs (GET, PUT, POST and DELETE).
// 组装actions
...
break
default:
return nil, fmt.Errorf("unsupported restscope: %s", scope.Name())
}
// Create Routes for the actions.
// TODO: Add status documentation using Returns()
// Errors (see api/errors/errors.go as well as go-restful router):
// http.StatusNotFound, http.StatusMethodNotAllowed,
// http.StatusUnsupportedMediaType, http.StatusNotAcceptable,
// http.StatusBadRequest, http.StatusUnauthorized, http.StatusForbidden,
// http.StatusRequestTimeout, http.StatusConflict, http.StatusPreconditionFailed,
// 422 (StatusUnprocessableEntity), http.StatusInternalServerError,
// http.StatusServiceUnavailable
// and api error codes
// Note that if we specify a versioned Status object here, we may need to
// create one for the tests, also
// Success:
// http.StatusOK, http.StatusCreated, http.StatusAccepted, http.StatusNoContent
//
// test/integration/auth_test.go is currently the most comprehensive status code test
reqScope := RequestScope{
ContextFunc: ctxFn,
Serializer: a.group.Serializer,
ParameterCodec: a.group.ParameterCodec,
Creater: a.group.Creater,
Convertor: a.group.Convertor,
Copier: a.group.Copier,
// TODO: This seems wrong for cross-group subresources. It makes an assumption that a subresource and its parent are in the same group version. Revisit this.
Resource: a.group.GroupVersion.WithResource(resource),
Subresource: subresource,
Kind: fqKindToRegister,
}
for _, action := range actions {
reqScope.Namer = action.Namer
namespaced := ""
if apiResource.Namespaced {
namespaced = "Namespaced"
}
switch action.Verb {
case "GET": // Get a resource.
var handler restful.RouteFunction
// 创建handler
if isGetterWithOptions {
handler = GetResourceWithOptions(getterWithOptions, reqScope)
} else {
handler = GetResource(getter, exporter, reqScope)
}
handler = metrics.InstrumentRouteFunc(action.Verb, resource, handler)
doc := "read the specified " + kind
if hasSubresource {
doc = "read " + subresource + " of the specified " + kind
}
// 设置路由属性,包括Path,Handler,Doc,Param,MIME等
route := ws.GET(action.Path).To(handler).
Doc(doc).
Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")).
Operation("read"+namespaced+kind+strings.Title(subresource)).
Produces(append(storageMeta.ProducesMIMETypes(action.Verb), a.group.Serializer.SupportedMediaTypes()...)...).
Returns(http.StatusOK, "OK", versionedObject).
Writes(versionedObject)
...
// 设置路由的参数
addParams(route, action.Params)
// 将路由添加到WebService中
ws.Route(route)
case "LIST": // List all resources of a kind.
...
ws.Route(route)
case "PUT": // Update a resource.
...
ws.Route(route)
case "POST": // Create a resource.
...
ws.Route(route)
case "DELETE": // Delete a resource.
...
ws.Route(route)
// TODO: deprecated
case "WATCHLIST": // Watch all resources of a kind.
...
ws.Route(route)
// We add "proxy" subresource to remove the need for the generic top level prefix proxy.
// The generic top level prefix proxy is deprecated in v1.2, and will be removed in 1.3, or 1.4 at the latest.
// TODO: DEPRECATED in v1.2.
case "PROXY": // Proxy requests to a resource.
// Accept all methods as per http://issue.k8s.io/3996
addProxyRoute(ws, "GET", a.prefix, action.Path, proxyHandler, namespaced, kind, resource, subresource, hasSubresource, action.Params)
addProxyRoute(ws, "PUT", a.prefix, action.Path, proxyHandler, namespaced, kind, resource, subresource, hasSubresource, action.Params)
addProxyRoute(ws, "POST", a.prefix, action.Path, proxyHandler, namespaced, kind, resource, subresource, hasSubresource, action.Params)
addProxyRoute(ws, "DELETE", a.prefix, action.Path, proxyHandler, namespaced, kind, resource, subresource, hasSubresource, action.Params)
addProxyRoute(ws, "HEAD", a.prefix, action.Path, proxyHandler, namespaced, kind, resource, subresource, hasSubresource, action.Params)
addProxyRoute(ws, "OPTIONS", a.prefix, action.Path, proxyHandler, namespaced, kind, resource, subresource, hasSubresource, action.Params)
case "CONNECT":
for _, method := range connecter.ConnectMethods() {
...
ws.Route(route)
}
default:
return nil, fmt.Errorf("unrecognized action verb: %s", action.Verb)
}
// Note: update GetAttribs() when adding a custom handler.
}
return &apiResource, nil
}
代码首先对资源的后端存储(rest.Storage)进行验证,根据存储支持的方法进行actions的初始化,然后根据actions来进行Route的构建,以Get方法为例,参见代码注释。
至此,所有路由构建的相关组件Container,WebService, Route等已经初始化完毕。
我们再回到Master.Run()方法,其实也就是GenericAPIServer.Run()方法:
func (s *GenericAPIServer) Run(options *options.ServerRunOptions) {
...
if secureLocation != "" {
handler := apiserver.TimeoutHandler(apiserver.RecoverPanics(s.Handler), longRunningTimeout)
secureServer := &http.Server{
Addr: secureLocation,
Handler: apiserver.MaxInFlightLimit(sem, longRunningRequestCheck, handler),
MaxHeaderBytes: 1 << 20,
TLSConfig: &tls.Config{
// Can't use SSLv3 because of POODLE and BEAST
// Can't use TLSv1.0 because of POODLE and BEAST using CBC cipher
// Can't use TLSv1.1 because of RC4 cipher usage
MinVersion: tls.VersionTLS12,
},
}
...
go func() {
defer utilruntime.HandleCrash()
for {
// err == systemd.SdNotifyNoSocket when not running on a systemd system
if err := systemd.SdNotify("READY=1\n"); err != nil && err != systemd.SdNotifyNoSocket {
glog.Errorf("Unable to send systemd daemon successful start message: %v\n", err)
}
if err := secureServer.ListenAndServeTLS(options.TLSCertFile, options.TLSPrivateKeyFile); err != nil {
glog.Errorf("Unable to listen for secure (%v); will try again.", err)
}
time.Sleep(15 * time.Second)
}
}()
} else {
// err == systemd.SdNotifyNoSocket when not running on a systemd system
if err := systemd.SdNotify("READY=1\n"); err != nil && err != systemd.SdNotifyNoSocket {
glog.Errorf("Unable to send systemd daemon successful start message: %v\n", err)
}
}
handler := apiserver.TimeoutHandler(apiserver.RecoverPanics(s.InsecureHandler), longRunningTimeout)
http := &http.Server{
Addr: insecureLocation,
Handler: handler,
MaxHeaderBytes: 1 << 20,
}
glog.Infof("Serving insecurely on %s", insecureLocation)
go func() {
defer utilruntime.HandleCrash()
for {
if err := http.ListenAndServe(); err != nil {
glog.Errorf("Unable to listen for insecure (%v); will try again.", err)
}
time.Sleep(15 * time.Second)
}
}()
select {}
}
可以看到Run()中通过http.ListenAndServe()方法启动了两个端口来监听请求: Localhost Port和Secure Port。
至此,整个apiserver的Restful服务已经构建完成并运行。
kubernetes源码解析---- apiserver路由构建解析(2)的更多相关文章
- kubernetes源码解析---- apiserver路由构建解析(1)
kubernetes源码解析---- apiserver路由构建解析(1) apiserver作为k8s集群的唯一入口,内部主要实现了两个功能,一个是请求的路由和处理,简单说就是监听一个端口,把接收到 ...
- Spring源码情操陶冶#task:scheduled-tasks解析器
承接前文Spring源码情操陶冶#task:executor解析器,在前文基础上解析我们常用的spring中的定时任务的节点配置.备注:此文建立在spring的4.2.3.RELEASE版本 附例 S ...
- springMVC源码分析--HandlerMethodReturnValueHandlerComposite返回值解析器集合(二)
在上一篇博客springMVC源码分析--HandlerMethodReturnValueHandler返回值解析器(一)我们介绍了返回值解析器HandlerMethodReturnValueHand ...
- 曹工说Spring Boot源码(8)-- Spring解析xml文件,到底从中得到了什么(util命名空间)
写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...
- 曹工说Spring Boot源码(9)-- Spring解析xml文件,到底从中得到了什么(context命名空间上)
写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...
- # 曹工说Spring Boot源码(10)-- Spring解析xml文件,到底从中得到了什么(context:annotation-config 解析)
写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...
- 曹工说Spring Boot源码(12)-- Spring解析xml文件,到底从中得到了什么(context:component-scan完整解析)
写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...
- Spring源码分析之AOP从解析到调用
正文: 在上一篇,我们对IOC核心部分流程已经分析完毕,相信小伙伴们有所收获,从这一篇开始,我们将会踏上新的旅程,即Spring的另一核心:AOP! 首先,为了让大家能更有效的理解AOP,先带大家过一 ...
- Spring源码-IOC部分-Xml Bean解析注册过程【3】
实验环境:spring-framework-5.0.2.jdk8.gradle4.3.1 Spring源码-IOC部分-容器简介[1] Spring源码-IOC部分-容器初始化过程[2] Spring ...
随机推荐
- 在Visual Studio 中使用git——文件管理-中(五)
在Visual Studio 中使用git--什么是Git(一) 在Visual Studio 中使用git--给Visual Studio安装 git插件(二) 在Visual Studio 中使用 ...
- OPC使用思路
- OO_Unit1_表达式求导
CSDN链接 一.第一次作业 1.需求分析 简单多项式导函数 带符号整数 支持前导0的带符号整数,符号可省略,如: +02.-16.19260817等. 幂函数 一般形式 由自变量x和指数组成,指数为 ...
- [Python] RPC实现
单线程同步 使用socket传输数据 使用json序列化消息体 struct将消息编码为二进制字节串,进行网络传输 消息协议 1 // 输入 2 { 3 in: "ping", 4 ...
- [c++] 二级指针的原理
示例 将值(实参)传递给值(形参),无法更改val 1 #include <iostream> 2 using namespace std; 3 4 void change(int mem ...
- 【转载】kvm迁移
https://www.jianshu.com/p/60132085a3c9 kvm分静态和动态迁移,静态就是关机迁移,比较简单,动态迁移就是不关闭服务器进行迁移.静态迁移:确定虚拟机关闭 https ...
- Linux Centos7设置UTF-8编码,防止中文乱码
Linux Centos7设置UTF-8编码,防止中文乱码 # localeLANG=zh_CN.gb2312LC_CTYPE="zh_CN.gb2312"LC_NUMERIC=& ...
- Linux占用swap分区过高,物理内存还有剩余
Linux占用swap分区过高,物理内存还有剩余 问题分析 Swap配置对性能的影响 分配太多的Swap空间会浪费磁盘空间,而Swap空间太少,则系统会发生错误.如果系统的物理内存用光了,系统就会跑得 ...
- dmidecode -t1 | egrep "Manufacturer|Product Name"
# dmidecode -t1 | egrep "Manufacturer|Product Name" Manufacturer: Loongson Product Name: L ...
- Centos7 离线安装python3 Django
安装python 1..下载Python3源码包 下载地址:www.python.org/ftp/python/ 2.安装python前的库环境 yum install gcc patch libff ...