type APIGroupInfo struct { PrioritizedVersions []schema.GroupVersion
// Info about the resources in this group. It's a map from version to resource to the storage. VersionedResourcesStorageMap map[string]map[string]rest.Storage
type APIServerHandler struct { // FullHandlerChain is the one that is eventually served with. It should include the full filter // chain and then call the Director. FullHandlerChain http.Handler // The registered APIs. InstallAPIs uses this. Other servers probably shouldn't access this directly. GoRestfulContainer *restful.Container // NonGoRestfulMux is the final HTTP handler in the chain. // It comes after all filters and the API handling // This is where other servers can attach handler to various parts of the chain. NonGoRestfulMux *mux.PathRecorderMux
// Director is here so that we can properly handle fall through and proxy cases. // This looks a bit bonkers, but here's what's happening. We need to have /apis handling registered in gorestful in order to have // swagger generated for compatibility. Doing that with `/apis` as a webservice, means that it forcibly 404s (no defaulting allowed) // all requests which are not /apis or /apis/. We need those calls to fall through behind goresful for proper delegation. Trying to // register for a pattern which includes everything behind it doesn't work because gorestful negotiates for verbs and content encoding // and all those things go crazy when gorestful really just needs to pass through. In addition, openapi enforces unique verb constraints // which we don't fit into and it still muddies up swagger. Trying to switch the webservices into a route doesn't work because the // containing webservice faces all the same problems listed above. // This leads to the crazy thing done here. Our mux does what we need, so we'll place it in front of gorestful. It will introspect to // decide if the route is likely to be handled by goresful and route there if needed. Otherwise, it goes to PostGoRestful mux in // order to handle "normal" paths and delegation. Hopefully no API consumers will ever have to deal with this level of detail. I think // we should consider completely removing gorestful. // Other servers should only use this opaquely to delegate to an API server. Director http.Handler }
funcNewAPIServerHandler(name string, s runtime.NegotiatedSerializer, handlerChainBuilder HandlerChainBuilderFn, notFoundHandler http.Handler) *APIServerHandler { nonGoRestfulMux := mux.NewPathRecorderMux(name) if notFoundHandler != nil { nonGoRestfulMux.NotFoundHandler(notFoundHandler) }
func(s *GenericAPIServer) installAPIResources(apiPrefix string, apiGroupInfo *APIGroupInfo, openAPIModels openapiproto.Models) error { for _, groupVersion := range apiGroupInfo.PrioritizedVersions { iflen(apiGroupInfo.VersionedResourcesStorageMap[groupVersion.Version]) == 0 { klog.Warningf("Skipping API %v because it has no resources.", groupVersion) continue }
func(a *APIInstaller) Install() ([]metav1.APIResource, *restful.WebService, []error) { var apiResources []metav1.APIResource var errors []error ws := a.newWebService()
// 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) 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, ws, errors }
// check to see if our webservices want to claim this path for _, ws := range d.goRestfulContainer.RegisteredWebServices() { switch { case ws.RootPath() == "/apis": // if we are exactly /apis or /apis/, then we need special handling in loop. // normally these are passed to the nonGoRestfulMux, but if discovery is enabled, it will go directly. // We can't rely on a prefix match since /apis matches everything (see the big comment on Director above) if path == "/apis" || path == "/apis/" { klog.V(5).Infof("%v: %v %q satisfied by gorestful with webservice %v", d.name, req.Method, path, ws.RootPath()) // don't use servemux here because gorestful servemuxes get messed up when removing webservices // TODO fix gorestful, remove TPRs, or stop using gorestful d.goRestfulContainer.Dispatch(w, req) return }
case strings.HasPrefix(path, ws.RootPath()): // ensure an exact match or a path boundary match iflen(path) == len(ws.RootPath()) || path[len(ws.RootPath())] == '/' { klog.V(5).Infof("%v: %v %q satisfied by gorestful with webservice %v", d.name, req.Method, path, ws.RootPath()) // don't use servemux here because gorestful servemuxes get messed up when removing webservices // TODO fix gorestful, remove TPRs, or stop using gorestful d.goRestfulContainer.Dispatch(w, req) return } } }
// if we didn't find a match, then we just skip gorestful altogether klog.V(5).Infof("%v: %v %q satisfied by nonGoRestful", d.name, req.Method, path) d.nonGoRestfulMux.ServeHTTP(w, req) }
func(a *APIServerHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { a.FullHandlerChain.ServeHTTP(w, r) }
if s.postStartHooksCalled { return fmt.Errorf("unable to add %q because PostStartHooks have already been called", name) } if postStartHook, exists := s.postStartHooks[name]; exists { // this is programmer error, but it can be hard to debug return fmt.Errorf("unable to add %q because it was already registered by: %s", name, postStartHook.originatingStack) }
// done is closed when the poststarthook is finished. This is used by the health check to be able to indicate // that the poststarthook is finished done := make(chanstruct{}) if err := s.AddBootSequenceHealthChecks(postStartHookHealthz{name: "poststarthook/" + name, done: done}); err != nil { return err } s.postStartHooks[name] = postStartHookEntry{hook: hook, originatingStack: string(debug.Stack()), done: done}
err = aggregatorServer.GenericAPIServer.AddPostStartHook("kube-apiserver-autoregistration", func(context genericapiserver.PostStartHookContext)error { go crdRegistrationController.Run(5, context.StopCh) gofunc() { // let the CRD controller process the initial set of CRDs before starting the autoregistration controller. // this prevents the autoregistration controller's initial sync from deleting APIServices for CRDs that still exist. // we only need to do this if CRDs are enabled on this server. We can't use discovery because we are the source for discovery. if aggregatorConfig.GenericConfig.MergedResourceConfig.AnyVersionForGroupEnabled("apiextensions.k8s.io") { crdRegistrationController.WaitForInitialSync() } autoRegistrationController.Run(5, context.StopCh) }() returnnil })