// 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 (c completedConfig) New() (*GenericAPIServer, error) { if c.Serializer == nil { return nil, fmt.Errorf("Genericapiserver.New() called with config.Serializer == nil") } s := &GenericAPIServer{ ServiceClusterIPRange: c.ServiceClusterIPRange, ServiceNodePortRange: c.ServiceNodePortRange, LoopbackClientConfig: c.LoopbackClientConfig, legacyAPIPrefix: c.APIPrefix, apiPrefix: c.APIGroupPrefix, admissionControl: c.AdmissionControl, requestContextMapper: c.RequestContextMapper, Serializer: c.Serializer, minRequestTimeout: time.Duration(c.MinRequestTimeout) * time.Second, enableSwaggerSupport: c.EnableSwaggerSupport, MasterCount: c.MasterCount, SecureServingInfo: c.SecureServingInfo, InsecureServingInfo: c.InsecureServingInfo, 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, openAPIDefinitions: c.OpenAPIDefinitions, } if c.RestfulContainer != nil { s.HandlerContainer = c.RestfulContainer } else { s.HandlerContainer = NewHandlerContainer(http.NewServeMux(), 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.Mux = apiserver.NewPathRecorderMux(s.HandlerContainer.ServeMux) apiserver.InstallServiceErrorHandler(s.Serializer, s.HandlerContainer) if c.ProxyDialer != nil || c.ProxyTLSClientConfig != nil { s.ProxyTransport = utilnet.SetTransportDefaults(&http.Transport{ Dial: c.ProxyDialer, TLSClientConfig: c.ProxyTLSClientConfig, }) } s.installAPI(c.Config) s.Handler, s.InsecureHandler = s.buildHandlerChains(c.Config, http.Handler(s.Mux.BaseMux().(*http.ServeMux))) return s, nil }
// TestHandleFuncWithAuth verifies HandleFuncWithAuth adds the path // to the MuxHelper.RegisteredPaths. func TestHandleFuncWithAuth(t *testing.T) { server, etcdserver, _, assert := setUp(t) defer etcdserver.Terminate(t) server.Mux = apiserver.NewPathRecorderMux(http.NewServeMux()) handler := func(r http.ResponseWriter, w *http.Request) { w.Write(nil) } server.HandleFuncWithAuth("/test", handler) assert.Contains(server.Mux.HandledPaths(), "/test", "Path not found in MuxHelper") }
// 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 (c Config) New() (*GenericAPIServer, error) { if c.Serializer == nil { return nil, fmt.Errorf("Genericapiserver.New() called with config.Serializer == nil") } c.setDefaults() s := &GenericAPIServer{ ServiceClusterIPRange: c.ServiceClusterIPRange, ServiceNodePortRange: c.ServiceNodePortRange, legacyAPIPrefix: c.APIPrefix, apiPrefix: c.APIGroupPrefix, admissionControl: c.AdmissionControl, requestContextMapper: c.RequestContextMapper, Serializer: c.Serializer, minRequestTimeout: time.Duration(c.MinRequestTimeout) * time.Second, enableSwaggerSupport: c.EnableSwaggerSupport, 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, } if c.EnableWatchCache { s.storageDecorator = registry.StorageWithCacher } else { s.storageDecorator = generic.UndecoratedStorage } if c.RestfulContainer != nil { s.HandlerContainer = c.RestfulContainer } else { s.HandlerContainer = NewHandlerContainer(http.NewServeMux(), 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.Mux = apiserver.NewPathRecorderMux(s.HandlerContainer.ServeMux) if c.ProxyDialer != nil || c.ProxyTLSClientConfig != nil { s.ProxyTransport = utilnet.SetTransportDefaults(&http.Transport{ Dial: c.ProxyDialer, TLSClientConfig: c.ProxyTLSClientConfig, }) } // Send correct mime type for .svg files. // TODO: remove when https://github.com/golang/go/commit/21e47d831bafb59f22b1ea8098f709677ec8ce33 // makes it into all of our supported go versions (only in v1.7.1 now). mime.AddExtensionType(".svg", "image/svg+xml") // Register root handler. // We do not register this using restful Webservice since we do not want to surface this in api docs. // Allow GenericAPIServer to be embedded in contexts which already have something registered at the root if c.EnableIndex { routes.Index{}.Install(s.Mux, s.HandlerContainer) } if c.EnableSwaggerSupport && c.EnableSwaggerUI { routes.SwaggerUI{}.Install(s.Mux, s.HandlerContainer) } if c.EnableProfiling { routes.Profiling{}.Install(s.Mux, s.HandlerContainer) } if c.EnableVersion { routes.Version{}.Install(s.Mux, s.HandlerContainer) } handler := http.Handler(s.Mux.BaseMux().(*http.ServeMux)) // TODO: handle CORS and auth using go-restful // See github.com/emicklei/go-restful/blob/master/examples/restful-CORS-filter.go, and // github.com/emicklei/go-restful/blob/master/examples/restful-basic-authentication.go if len(c.CorsAllowedOriginList) > 0 { allowedOriginRegexps, err := util.CompileRegexps(c.CorsAllowedOriginList) if err != nil { glog.Fatalf("Invalid CORS allowed origin, --cors-allowed-origins flag was set to %v - %v", strings.Join(c.CorsAllowedOriginList, ","), err) } handler = apiserver.CORS(handler, allowedOriginRegexps, nil, nil, "true") } s.InsecureHandler = handler attributeGetter := apiserver.NewRequestAttributeGetter(c.RequestContextMapper, s.NewRequestInfoResolver()) handler = apiserver.WithAuthorizationCheck(handler, attributeGetter, c.Authorizer) if len(c.AuditLogPath) != 0 { // audit handler must comes before the impersonationFilter to read the original user writer := &lumberjack.Logger{ Filename: c.AuditLogPath, MaxAge: c.AuditLogMaxAge, MaxBackups: c.AuditLogMaxBackups, MaxSize: c.AuditLogMaxSize, } handler = audit.WithAudit(handler, attributeGetter, writer) defer writer.Close() } handler = apiserver.WithImpersonation(handler, c.RequestContextMapper, c.Authorizer) // Install Authenticator if c.Authenticator != nil { authenticatedHandler, err := handlers.NewRequestAuthenticator(c.RequestContextMapper, c.Authenticator, handlers.Unauthorized(c.SupportsBasicAuth), handler) if err != nil { glog.Fatalf("Could not initialize authenticator: %v", err) } handler = authenticatedHandler } // TODO: Make this optional? Consumers of GenericAPIServer depend on this currently. s.Handler = handler // After all wrapping is done, put a context filter around both handlers var err error handler, err = api.NewRequestContextFilter(c.RequestContextMapper, s.Handler) if err != nil { glog.Fatalf("Could not initialize request context filter for s.Handler: %v", err) } s.Handler = handler handler, err = api.NewRequestContextFilter(c.RequestContextMapper, s.InsecureHandler) if err != nil { glog.Fatalf("Could not initialize request context filter for s.InsecureHandler: %v", err) } s.InsecureHandler = handler s.installGroupsDiscoveryHandler() return s, nil }