Merge pull request #45370 from deads2k/server-23-handlers

Automatic merge from submit-queue

refactor names for the apiserver handling chain

The names and structure around the handling chain got a bit confused.  This simplifies it back out into a single struct with three parts: overall handler, gorestful handler, pathrecording mux and makes the delegate wiring simpler
pull/6/head
Kubernetes Submit Queue 2017-05-08 07:06:27 -07:00 committed by GitHub
commit 843c40e32e
26 changed files with 230 additions and 285 deletions

View File

@ -82,7 +82,6 @@ go_library(
"//vendor/k8s.io/apiserver/pkg/server:go_default_library",
"//vendor/k8s.io/apiserver/pkg/server/filters:go_default_library",
"//vendor/k8s.io/apiserver/pkg/server/healthz:go_default_library",
"//vendor/k8s.io/apiserver/pkg/server/mux:go_default_library",
"//vendor/k8s.io/apiserver/pkg/server/options:go_default_library",
"//vendor/k8s.io/apiserver/pkg/server/storage:go_default_library",
"//vendor/k8s.io/client-go/kubernetes:go_default_library",

View File

@ -30,7 +30,6 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
genericapiserver "k8s.io/apiserver/pkg/server"
"k8s.io/apiserver/pkg/server/healthz"
"k8s.io/apiserver/pkg/server/mux"
genericoptions "k8s.io/apiserver/pkg/server/options"
kubeclientset "k8s.io/client-go/kubernetes"
"k8s.io/kube-aggregator/pkg/apis/apiregistration"
@ -46,7 +45,6 @@ func createAggregatorConfig(kubeAPIServerConfig genericapiserver.Config, command
// make a shallow copy to let us twiddle a few things
// most of the config actually remains the same. We only need to mess with a couple items related to the particulars of the aggregator
genericConfig := kubeAPIServerConfig
genericConfig.FallThroughHandler = mux.NewPathRecorderMux()
// the aggregator doesn't wire these up. It just delegates them to the kubeapiserver
genericConfig.EnableSwaggerUI = false

View File

@ -110,7 +110,7 @@ func Run(runOptions *options.ServerRunOptions, stopCh <-chan struct{}) error {
// run the insecure server now, don't block. It doesn't have any aggregator goodies since authentication wouldn't work
if insecureServingOptions != nil {
insecureHandlerChain := kubeserver.BuildInsecureHandlerChain(kubeAPIServer.GenericAPIServer.HandlerContainer.ServeMux, kubeAPIServerConfig.GenericConfig)
insecureHandlerChain := kubeserver.BuildInsecureHandlerChain(kubeAPIServer.GenericAPIServer.UnprotectedHandler(), kubeAPIServerConfig.GenericConfig)
if err := kubeserver.NonBlockingRun(insecureServingOptions, insecureHandlerChain, stopCh); err != nil {
return err
}
@ -139,7 +139,7 @@ func Run(runOptions *options.ServerRunOptions, stopCh <-chan struct{}) error {
// CreateKubeAPIServer creates and wires a workable kube-apiserver
func CreateKubeAPIServer(kubeAPIServerConfig *master.Config, sharedInformers informers.SharedInformerFactory, stopCh <-chan struct{}) (*master.Master, error) {
kubeAPIServer, err := kubeAPIServerConfig.Complete().New()
kubeAPIServer, err := kubeAPIServerConfig.Complete().New(genericapiserver.EmptyDelegate)
if err != nil {
return nil, err
}

View File

@ -224,13 +224,13 @@ func NonBlockingRun(s *options.ServerRunOptions, stopCh <-chan struct{}) error {
cachesize.SetWatchCacheSizes(s.GenericServerRunOptions.WatchCacheSizes)
}
m, err := genericConfig.Complete().New()
m, err := genericConfig.Complete().New(genericapiserver.EmptyDelegate)
if err != nil {
return err
}
routes.UIRedirect{}.Install(m.FallThroughHandler)
routes.Logs{}.Install(m.HandlerContainer)
routes.UIRedirect{}.Install(m.Handler.PostGoRestfulMux)
routes.Logs{}.Install(m.Handler.GoRestfulContainer)
apiResourceConfigSource := storageFactory.APIResourceConfigSource
installFederationAPIs(m, genericConfig.RESTOptionsGetter, apiResourceConfigSource)
@ -241,7 +241,7 @@ func NonBlockingRun(s *options.ServerRunOptions, stopCh <-chan struct{}) error {
// run the insecure server now
if insecureServingOptions != nil {
insecureHandlerChain := kubeserver.BuildInsecureHandlerChain(m.HandlerContainer.ServeMux, genericConfig)
insecureHandlerChain := kubeserver.BuildInsecureHandlerChain(m.UnprotectedHandler(), genericConfig)
if err := kubeserver.NonBlockingRun(insecureServingOptions, insecureHandlerChain, stopCh); err != nil {
return err
}

View File

@ -206,21 +206,21 @@ func (c *Config) SkipComplete() completedConfig {
// Certain config fields will be set to a default value if unset.
// Certain config fields must be specified, including:
// KubeletClientConfig
func (c completedConfig) New() (*Master, error) {
func (c completedConfig) New(delegationTarget genericapiserver.DelegationTarget) (*Master, error) {
if reflect.DeepEqual(c.KubeletClientConfig, kubeletclient.KubeletClientConfig{}) {
return nil, fmt.Errorf("Master.New() called with empty config.KubeletClientConfig")
}
s, err := c.Config.GenericConfig.SkipComplete().New() // completion is done in Complete, no need for a second time
s, err := c.Config.GenericConfig.SkipComplete().New(delegationTarget) // completion is done in Complete, no need for a second time
if err != nil {
return nil, err
}
if c.EnableUISupport {
routes.UIRedirect{}.Install(s.FallThroughHandler)
routes.UIRedirect{}.Install(s.Handler.PostGoRestfulMux)
}
if c.EnableLogsSupport {
routes.Logs{}.Install(s.HandlerContainer)
routes.Logs{}.Install(s.Handler.GoRestfulContainer)
}
m := &Master{

View File

@ -53,13 +53,13 @@ func TestValidOpenAPISpec(t *testing.T) {
}
config.GenericConfig.SwaggerConfig = genericapiserver.DefaultSwaggerConfig()
master, err := config.Complete().New()
master, err := config.Complete().New(genericapiserver.EmptyDelegate)
if err != nil {
t.Fatalf("Error in bringing up the master: %v", err)
}
// make sure swagger.json is not registered before calling PrepareRun.
server := httptest.NewServer(master.GenericAPIServer.HandlerContainer.ServeMux)
server := httptest.NewServer(master.GenericAPIServer.Handler.GoRestfulContainer.ServeMux)
defer server.Close()
resp, err := http.Get(server.URL + "/swagger.json")
if !assert.NoError(err) {

View File

@ -107,7 +107,7 @@ func setUp(t *testing.T) (*etcdtesting.EtcdTestServer, Config, *assert.Assertion
func newMaster(t *testing.T) (*Master, *etcdtesting.EtcdTestServer, Config, *assert.Assertions) {
etcdserver, config, assert := setUp(t)
master, err := config.Complete().New()
master, err := config.Complete().New(genericapiserver.EmptyDelegate)
if err != nil {
t.Fatalf("Error in bringing up the master: %v", err)
}
@ -133,7 +133,7 @@ func limitedAPIResourceConfigSource() *serverstorage.ResourceConfig {
func newLimitedMaster(t *testing.T) (*Master, *etcdtesting.EtcdTestServer, Config, *assert.Assertions) {
etcdserver, config, assert := setUp(t)
config.APIResourceConfigSource = limitedAPIResourceConfigSource()
master, err := config.Complete().New()
master, err := config.Complete().New(genericapiserver.EmptyDelegate)
if err != nil {
t.Fatalf("Error in bringing up the master: %v", err)
}
@ -226,7 +226,7 @@ func TestAPIVersionOfDiscoveryEndpoints(t *testing.T) {
master, etcdserver, _, assert := newMaster(t)
defer etcdserver.Terminate(t)
server := httptest.NewServer(master.GenericAPIServer.HandlerContainer.ServeMux)
server := httptest.NewServer(master.GenericAPIServer.Handler.GoRestfulContainer.ServeMux)
// /api exists in release-1.1
resp, err := http.Get(server.URL + "/api")

View File

@ -156,11 +156,11 @@ func (m *ThirdPartyResourceServer) RemoveThirdPartyResource(path string) error {
return err
}
services := m.genericAPIServer.HandlerContainer.RegisteredWebServices()
services := m.genericAPIServer.Handler.GoRestfulContainer.RegisteredWebServices()
for ix := range services {
root := services[ix].RootPath()
if root == path || strings.HasPrefix(root, path+"/") {
m.genericAPIServer.HandlerContainer.Remove(services[ix])
m.genericAPIServer.Handler.GoRestfulContainer.Remove(services[ix])
}
}
return nil
@ -281,13 +281,13 @@ func (m *ThirdPartyResourceServer) InstallThirdPartyResource(rsrc *extensions.Th
// the group with the new API
if m.hasThirdPartyGroupStorage(path) {
m.addThirdPartyResourceStorage(path, plural.Resource, thirdparty.Storage[plural.Resource].(*thirdpartyresourcedatastore.REST), apiGroup)
return thirdparty.UpdateREST(m.genericAPIServer.HandlerContainer.Container)
return thirdparty.UpdateREST(m.genericAPIServer.Handler.GoRestfulContainer)
}
if err := thirdparty.InstallREST(m.genericAPIServer.HandlerContainer.Container); err != nil {
if err := thirdparty.InstallREST(m.genericAPIServer.Handler.GoRestfulContainer); err != nil {
glog.Errorf("Unable to setup thirdparty api: %v", err)
}
m.genericAPIServer.HandlerContainer.Add(discovery.NewAPIGroupHandler(api.Codecs, apiGroup).WebService())
m.genericAPIServer.Handler.GoRestfulContainer.Add(discovery.NewAPIGroupHandler(api.Codecs, apiGroup).WebService())
m.addThirdPartyResourceStorage(path, plural.Resource, thirdparty.Storage[plural.Resource].(*thirdpartyresourcedatastore.REST), apiGroup)
api.Registry.AddThirdPartyAPIGroupVersions(schema.GroupVersion{Group: group, Version: rsrc.Versions[0].Name})

View File

@ -21,14 +21,12 @@ import (
"path"
"github.com/emicklei/go-restful"
"k8s.io/apiserver/pkg/server/mux"
)
// Logs adds handlers for the /logs path serving log files from /var/log.
type Logs struct{}
func (l Logs) Install(c *mux.APIContainer) {
func (l Logs) Install(c *restful.Container) {
// use restful: ws.Route(ws.GET("/logs/{logpath:*}").To(fileHandler))
// See github.com/emicklei/go-restful/blob/master/examples/restful-serve-static.go
ws := new(restful.WebService)

View File

@ -35,7 +35,6 @@ go_test(
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
"//vendor/k8s.io/apiserver/pkg/server/healthz:go_default_library",
"//vendor/k8s.io/apiserver/pkg/server/mux:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/etcd/testing:go_default_library",
"//vendor/k8s.io/client-go/rest:go_default_library",
],
@ -48,6 +47,7 @@ go_library(
"config_selfclient.go",
"doc.go",
"genericapiserver.go",
"handler.go",
"healthz.go",
"hooks.go",
"serve.go",
@ -55,11 +55,13 @@ go_library(
tags = ["automanaged"],
deps = [
"//vendor/github.com/coreos/go-systemd/daemon:go_default_library",
"//vendor/github.com/emicklei/go-restful:go_default_library",
"//vendor/github.com/emicklei/go-restful-swagger12:go_default_library",
"//vendor/github.com/go-openapi/spec:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/github.com/pborman/uuid:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apimachinery:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apimachinery/registered:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
@ -83,6 +85,7 @@ go_library(
"//vendor/k8s.io/apiserver/pkg/endpoints:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/discovery:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/filters:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/handlers/responsewriters:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/openapi:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library",

View File

@ -53,7 +53,6 @@ import (
genericregistry "k8s.io/apiserver/pkg/registry/generic"
genericfilters "k8s.io/apiserver/pkg/server/filters"
"k8s.io/apiserver/pkg/server/healthz"
"k8s.io/apiserver/pkg/server/mux"
"k8s.io/apiserver/pkg/server/routes"
restclient "k8s.io/client-go/rest"
certutil "k8s.io/client-go/util/cert"
@ -110,10 +109,6 @@ type Config struct {
// Will default to a value based on secure serving info and available ipv4 IPs.
ExternalAddress string
// FallThroughHandler is the final HTTP handler in the chain. If it is nil, one will be created for you.
// It comes after all filters and the API handling
FallThroughHandler *mux.PathRecorderMux
//===========================================================================
// Fields you probably don't care about changing
//===========================================================================
@ -312,6 +307,17 @@ func (c *Config) Complete() completedConfig {
},
}
}
if c.OpenAPIConfig.Info == nil {
c.OpenAPIConfig.Info = &spec.Info{}
}
if c.OpenAPIConfig.Info.Version == "" {
if c.Version != nil {
c.OpenAPIConfig.Info.Version = strings.Split(c.Version.String(), "-")[0]
} else {
c.OpenAPIConfig.Info.Version = "unversioned"
}
}
}
if c.SwaggerConfig != nil && len(c.SwaggerConfig.WebServicesUrl) == 0 {
if c.SecureServingInfo != nil {
@ -342,9 +348,6 @@ func (c *Config) Complete() completedConfig {
tokenAuthorizer := authorizerfactory.NewPrivilegedGroups(user.SystemPrivilegedGroup)
c.Authorizer = authorizerunion.New(tokenAuthorizer, c.Authorizer)
}
if c.FallThroughHandler == nil {
c.FallThroughHandler = mux.NewPathRecorderMux()
}
return completedConfig{c}
}
@ -354,38 +357,10 @@ func (c *Config) SkipComplete() completedConfig {
return completedConfig{c}
}
// 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) {
s, err := c.constructServer()
if err != nil {
return nil, err
}
// New creates a new server which logically combines the handling chain with the passed server.
func (c completedConfig) New(delegationTarget DelegationTarget) (*GenericAPIServer, error) {
// The delegationTarget and the config must agree on the RequestContextMapper
return c.buildHandlers(s, nil)
}
func (c completedConfig) constructServer() (*GenericAPIServer, error) {
if c.Serializer == nil {
return nil, fmt.Errorf("Genericapiserver.New() called with config.Serializer == nil")
}
@ -393,7 +368,10 @@ func (c completedConfig) constructServer() (*GenericAPIServer, error) {
return nil, fmt.Errorf("Genericapiserver.New() called with config.LoopbackClientConfig == nil")
}
handlerContainer := mux.NewAPIContainer(http.NewServeMux(), c.Serializer, c.FallThroughHandler)
handlerChainBuilder := func(handler http.Handler) http.Handler {
return c.BuildHandlerChainFunc(handler, c.Config)
}
apiServerHandler := NewAPIServerHandler(c.Serializer, handlerChainBuilder, delegationTarget.UnprotectedHandler())
s := &GenericAPIServer{
discoveryAddresses: c.DiscoveryAddresses,
@ -408,10 +386,9 @@ func (c completedConfig) constructServer() (*GenericAPIServer, error) {
SecureServingInfo: c.SecureServingInfo,
ExternalAddress: c.ExternalAddress,
HandlerContainer: handlerContainer,
FallThroughHandler: c.FallThroughHandler,
Handler: apiServerHandler,
listedPathProvider: routes.ListedPathProviders{handlerContainer, c.FallThroughHandler},
listedPathProvider: apiServerHandler,
swaggerConfig: c.SwaggerConfig,
openAPIConfig: c.OpenAPIConfig,
@ -424,20 +401,6 @@ func (c completedConfig) constructServer() (*GenericAPIServer, error) {
DiscoveryGroupManager: discovery.NewRootAPIsHandler(c.DiscoveryAddresses, c.Serializer),
}
return s, nil
}
// NewWithDelegate creates a new server which logically combines the handling chain with the passed server.
func (c completedConfig) NewWithDelegate(delegationTarget DelegationTarget) (*GenericAPIServer, error) {
// some pieces of the delegationTarget take precendence. Callers should already have ensured that these
// were wired correctly. Documenting them here.
// c.RequestContextMapper = delegationTarget.RequestContextMapper()
s, err := c.constructServer()
if err != nil {
return nil, err
}
for k, v := range delegationTarget.PostStartHooks() {
s.postStartHooks[k] = v
}
@ -459,38 +422,17 @@ func (c completedConfig) NewWithDelegate(delegationTarget DelegationTarget) (*Ge
s.listedPathProvider = routes.ListedPathProviders{s.listedPathProvider, delegationTarget}
installAPI(s, c.Config)
// use the UnprotectedHandler from the delegation target to ensure that we don't attempt to double authenticator, authorize,
// or some other part of the filter chain in delegation cases.
return c.buildHandlers(s, delegationTarget.UnprotectedHandler())
}
// buildHandlers builds our handling chain
func (c completedConfig) buildHandlers(s *GenericAPIServer, delegate http.Handler) (*GenericAPIServer, error) {
if s.openAPIConfig != nil {
if s.openAPIConfig.Info == nil {
s.openAPIConfig.Info = &spec.Info{}
}
if s.openAPIConfig.Info.Version == "" {
if c.Version != nil {
s.openAPIConfig.Info.Version = strings.Split(c.Version.String(), "-")[0]
} else {
s.openAPIConfig.Info.Version = "unversioned"
}
}
}
installAPI(s, c.Config)
if delegate != nil {
s.FallThroughHandler.NotFoundHandler(delegate)
} else if c.EnableIndex {
s.FallThroughHandler.NotFoundHandler(routes.IndexLister{
if delegationTarget.UnprotectedHandler() == nil && c.EnableIndex {
s.Handler.PostGoRestfulMux.NotFoundHandler(routes.IndexLister{
StatusCode: http.StatusNotFound,
PathProvider: s.listedPathProvider,
})
}
s.Handler = c.BuildHandlerChainFunc(s.HandlerContainer.ServeMux, c.Config)
return s, nil
}
@ -510,28 +452,28 @@ func DefaultBuildHandlerChain(apiHandler http.Handler, c *Config) http.Handler {
func installAPI(s *GenericAPIServer, c *Config) {
if c.EnableIndex {
routes.Index{}.Install(s.listedPathProvider, c.FallThroughHandler)
routes.Index{}.Install(s.listedPathProvider, s.Handler.PostGoRestfulMux)
}
if c.SwaggerConfig != nil && c.EnableSwaggerUI {
routes.SwaggerUI{}.Install(s.FallThroughHandler)
routes.SwaggerUI{}.Install(s.Handler.PostGoRestfulMux)
}
if c.EnableProfiling {
routes.Profiling{}.Install(s.FallThroughHandler)
routes.Profiling{}.Install(s.Handler.PostGoRestfulMux)
if c.EnableContentionProfiling {
goruntime.SetBlockProfileRate(1)
}
}
if c.EnableMetrics {
if c.EnableProfiling {
routes.MetricsWithReset{}.Install(s.FallThroughHandler)
routes.MetricsWithReset{}.Install(s.Handler.PostGoRestfulMux)
} else {
routes.DefaultMetrics{}.Install(s.FallThroughHandler)
routes.DefaultMetrics{}.Install(s.Handler.PostGoRestfulMux)
}
}
routes.Version{Version: c.Version}.Install(s.HandlerContainer)
routes.Version{Version: c.Version}.Install(s.Handler.GoRestfulContainer)
if c.EnableDiscovery {
s.HandlerContainer.Add(s.DiscoveryGroupManager.WebService())
s.Handler.GoRestfulContainer.Add(s.DiscoveryGroupManager.WebService())
}
}

View File

@ -28,7 +28,6 @@ import (
"k8s.io/apimachinery/pkg/util/sets"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/server/healthz"
"k8s.io/apiserver/pkg/server/mux"
"k8s.io/client-go/rest"
)
@ -38,7 +37,6 @@ func TestNewWithDelegate(t *testing.T) {
delegateConfig.RequestContextMapper = genericapirequest.NewRequestContextMapper()
delegateConfig.LegacyAPIGroupPrefixes = sets.NewString("/api")
delegateConfig.LoopbackClientConfig = &rest.Config{}
delegateConfig.FallThroughHandler = mux.NewPathRecorderMux()
delegateConfig.SwaggerConfig = DefaultSwaggerConfig()
delegateHealthzCalled := false
@ -47,14 +45,13 @@ func TestNewWithDelegate(t *testing.T) {
return fmt.Errorf("delegate failed healthcheck")
}))
delegateConfig.FallThroughHandler.HandleFunc("/foo", func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusForbidden)
})
delegateServer, err := delegateConfig.SkipComplete().New()
delegateServer, err := delegateConfig.SkipComplete().New(EmptyDelegate)
if err != nil {
t.Fatal(err)
}
delegateServer.Handler.PostGoRestfulMux.HandleFunc("/foo", func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusForbidden)
})
delegateServer.AddPostStartHook("delegate-post-start-hook", func(context PostStartHookContext) error {
return nil
@ -68,7 +65,6 @@ func TestNewWithDelegate(t *testing.T) {
wrappingConfig.RequestContextMapper = genericapirequest.NewRequestContextMapper()
wrappingConfig.LegacyAPIGroupPrefixes = sets.NewString("/api")
wrappingConfig.LoopbackClientConfig = &rest.Config{}
wrappingConfig.FallThroughHandler = mux.NewPathRecorderMux()
wrappingConfig.SwaggerConfig = DefaultSwaggerConfig()
wrappingHealthzCalled := false
@ -77,14 +73,13 @@ func TestNewWithDelegate(t *testing.T) {
return fmt.Errorf("wrapping failed healthcheck")
}))
wrappingConfig.FallThroughHandler.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusUnauthorized)
})
wrappingServer, err := wrappingConfig.Complete().NewWithDelegate(delegateServer)
wrappingServer, err := wrappingConfig.Complete().New(delegateServer)
if err != nil {
t.Fatal(err)
}
wrappingServer.Handler.PostGoRestfulMux.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusUnauthorized)
})
wrappingServer.AddPostStartHook("wrapping-post-start-hook", func(context PostStartHookContext) error {
return nil

View File

@ -42,8 +42,6 @@ import (
apirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/apiserver/pkg/server/healthz"
"k8s.io/apiserver/pkg/server/mux"
genericmux "k8s.io/apiserver/pkg/server/mux"
"k8s.io/apiserver/pkg/server/routes"
restclient "k8s.io/client-go/rest"
)
@ -101,9 +99,6 @@ type GenericAPIServer struct {
// requestContextMapper provides a way to get the context for a request. It may be nil.
requestContextMapper apirequest.RequestContextMapper
// The registered APIs
HandlerContainer *genericmux.APIContainer
SecureServingInfo *SecureServingInfo
// numerical ports, set after listening
@ -121,10 +116,8 @@ type GenericAPIServer struct {
Serializer runtime.NegotiatedSerializer
// "Outputs"
Handler http.Handler
// FallThroughHandler is the final HTTP handler in the chain.
// It comes after all filters and the API handling
FallThroughHandler *mux.PathRecorderMux
// Handler holdes the handlers being used by this API server
Handler *APIServerHandler
// listedPathProvider is a lister which provides the set of paths to show at /
listedPathProvider routes.ListedPathProvider
@ -171,7 +164,7 @@ type DelegationTarget interface {
}
func (s *GenericAPIServer) UnprotectedHandler() http.Handler {
return s.HandlerContainer.ServeMux
return s.Handler.GoRestfulContainer.ServeMux
}
func (s *GenericAPIServer) PostStartHooks() map[string]postStartHookEntry {
return s.postStartHooks
@ -233,12 +226,12 @@ type preparedGenericAPIServer struct {
// PrepareRun does post API installation setup steps.
func (s *GenericAPIServer) PrepareRun() preparedGenericAPIServer {
if s.swaggerConfig != nil {
routes.Swagger{Config: s.swaggerConfig}.Install(s.HandlerContainer)
routes.Swagger{Config: s.swaggerConfig}.Install(s.Handler.GoRestfulContainer)
}
if s.openAPIConfig != nil {
routes.OpenAPI{
Config: s.openAPIConfig,
}.Install(s.HandlerContainer, s.FallThroughHandler)
}.Install(s.Handler.GoRestfulContainer, s.Handler.PostGoRestfulMux)
}
s.installHealthz()
@ -306,7 +299,7 @@ func (s *GenericAPIServer) installAPIResources(apiPrefix string, apiGroupInfo *A
apiGroupVersion.OptionsExternalVersion = apiGroupInfo.OptionsExternalVersion
}
if err := apiGroupVersion.InstallREST(s.HandlerContainer.Container); err != nil {
if err := apiGroupVersion.InstallREST(s.Handler.GoRestfulContainer); err != nil {
return fmt.Errorf("Unable to setup API %v: %v", apiGroupInfo, err)
}
}
@ -329,7 +322,7 @@ func (s *GenericAPIServer) InstallLegacyAPIGroup(apiPrefix string, apiGroupInfo
}
// Install the version handler.
// Add a handler at /<apiPrefix> to enumerate the supported api versions.
s.HandlerContainer.Add(discovery.NewLegacyRootAPIHandler(s.discoveryAddresses, s.Serializer, apiPrefix, apiVersions).WebService())
s.Handler.GoRestfulContainer.Add(discovery.NewLegacyRootAPIHandler(s.discoveryAddresses, s.Serializer, apiPrefix, apiVersions).WebService())
return nil
}
@ -373,7 +366,7 @@ func (s *GenericAPIServer) InstallAPIGroup(apiGroupInfo *APIGroupInfo) error {
}
s.DiscoveryGroupManager.AddGroup(apiGroup)
s.HandlerContainer.Add(discovery.NewAPIGroupHandler(s.Serializer, apiGroup).WebService())
s.Handler.GoRestfulContainer.Add(discovery.NewAPIGroupHandler(s.Serializer, apiGroup).WebService())
return nil
}

View File

@ -47,7 +47,6 @@ import (
"k8s.io/apiserver/pkg/endpoints/discovery"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/apiserver/pkg/server/mux"
etcdtesting "k8s.io/apiserver/pkg/storage/etcd/testing"
restclient "k8s.io/client-go/rest"
)
@ -90,7 +89,6 @@ func setUp(t *testing.T) (*etcdtesting.EtcdTestServer, Config, *assert.Assertion
config.RequestContextMapper = genericapirequest.NewRequestContextMapper()
config.LegacyAPIGroupPrefixes = sets.NewString("/api")
config.LoopbackClientConfig = &restclient.Config{}
config.FallThroughHandler = mux.NewPathRecorderMux()
// TODO restore this test, but right now, eliminate our cycle
// config.OpenAPIConfig = DefaultOpenAPIConfig(testGetOpenAPIDefinitions, runtime.NewScheme())
@ -108,7 +106,7 @@ func setUp(t *testing.T) (*etcdtesting.EtcdTestServer, Config, *assert.Assertion
func newMaster(t *testing.T) (*GenericAPIServer, *etcdtesting.EtcdTestServer, Config, *assert.Assertions) {
etcdserver, config, assert := setUp(t)
s, err := config.Complete().New()
s, err := config.Complete().New(EmptyDelegate)
if err != nil {
t.Fatalf("Error in bringing up the server: %v", err)
}
@ -140,7 +138,7 @@ func TestInstallAPIGroups(t *testing.T) {
config.LegacyAPIGroupPrefixes = sets.NewString("/apiPrefix")
config.DiscoveryAddresses = discovery.DefaultAddresses{DefaultAddress: "ExternalAddress"}
s, err := config.SkipComplete().New()
s, err := config.SkipComplete().New(EmptyDelegate)
if err != nil {
t.Fatalf("Error in bringing up the server: %v", err)
}
@ -304,7 +302,7 @@ func TestPrepareRun(t *testing.T) {
assert.NotNil(config.SwaggerConfig)
// assert.NotNil(config.OpenAPIConfig)
server := httptest.NewServer(s.HandlerContainer.ServeMux)
server := httptest.NewServer(s.Handler.GoRestfulContainer.ServeMux)
defer server.Close()
s.PrepareRun()
@ -345,13 +343,13 @@ func TestCustomHandlerChain(t *testing.T) {
called = true
})
s, err := config.SkipComplete().New()
s, err := config.SkipComplete().New(EmptyDelegate)
if err != nil {
t.Fatalf("Error in bringing up the server: %v", err)
}
s.FallThroughHandler.Handle("/nonswagger", handler)
s.FallThroughHandler.Handle("/secret", handler)
s.Handler.PostGoRestfulMux.Handle("/nonswagger", handler)
s.Handler.PostGoRestfulMux.Handle("/secret", handler)
type Test struct {
handler http.Handler
@ -400,7 +398,7 @@ func TestNotRestRoutesHaveAuth(t *testing.T) {
kubeVersion := fakeVersion()
config.Version = &kubeVersion
s, err := config.SkipComplete().New()
s, err := config.SkipComplete().New(EmptyDelegate)
if err != nil {
t.Fatalf("Error in bringing up the server: %v", err)
}

View File

@ -0,0 +1,128 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package server
import (
"bytes"
"fmt"
"net/http"
rt "runtime"
"sort"
"github.com/emicklei/go-restful"
"github.com/golang/glog"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
"k8s.io/apiserver/pkg/server/mux"
genericmux "k8s.io/apiserver/pkg/server/mux"
)
// APIServerHandlers holds the different http.Handlers used by the API server.
// This includes the full handler chain, the gorestful handler (used for the API) which falls through to the postGoRestful handler
// and the postGoRestful handler (which can contain a fallthrough of its own)
type APIServerHandler struct {
// FullHandlerChain is the one that is eventually served with. It should include the full filter
// chain and then call the GoRestfulContainer.
FullHandlerChain http.Handler
// The registered APIs
GoRestfulContainer *restful.Container
// PostGoRestfulMux is the final HTTP handler in the chain.
// It comes after all filters and the API handling
PostGoRestfulMux *mux.PathRecorderMux
}
// HandlerChainBuilderFn is used to wrap the GoRestfulContainer handler using the provided handler chain.
// It is normally used to apply filtering like authentication and authorization
type HandlerChainBuilderFn func(apiHandler http.Handler) http.Handler
func NewAPIServerHandler(s runtime.NegotiatedSerializer, handlerChainBuilder HandlerChainBuilderFn, notFoundHandler http.Handler) *APIServerHandler {
postGoRestfulMux := genericmux.NewPathRecorderMux()
if notFoundHandler != nil {
postGoRestfulMux.NotFoundHandler(notFoundHandler)
}
gorestfulContainer := restful.NewContainer()
gorestfulContainer.ServeMux = http.NewServeMux()
gorestfulContainer.Router(restful.CurlyRouter{}) // e.g. for proxy/{kind}/{name}/{*}
gorestfulContainer.RecoverHandler(func(panicReason interface{}, httpWriter http.ResponseWriter) {
logStackOnRecover(s, panicReason, httpWriter)
})
gorestfulContainer.ServiceErrorHandler(func(serviceErr restful.ServiceError, request *restful.Request, response *restful.Response) {
serviceErrorHandler(s, serviceErr, request, response)
})
// register the defaultHandler for everything. This will allow an unhandled request to fall through to another handler instead of
// ending up with a forced 404
gorestfulContainer.Handle("/", postGoRestfulMux)
return &APIServerHandler{
FullHandlerChain: handlerChainBuilder(gorestfulContainer.ServeMux),
GoRestfulContainer: gorestfulContainer,
PostGoRestfulMux: postGoRestfulMux,
}
}
// ListedPaths returns the paths that should be shown under /
func (a *APIServerHandler) ListedPaths() []string {
var handledPaths []string
// Extract the paths handled using restful.WebService
for _, ws := range a.GoRestfulContainer.RegisteredWebServices() {
handledPaths = append(handledPaths, ws.RootPath())
}
handledPaths = append(handledPaths, a.PostGoRestfulMux.ListedPaths()...)
sort.Strings(handledPaths)
return handledPaths
}
//TODO: Unify with RecoverPanics?
func logStackOnRecover(s runtime.NegotiatedSerializer, panicReason interface{}, w http.ResponseWriter) {
var buffer bytes.Buffer
buffer.WriteString(fmt.Sprintf("recover from panic situation: - %v\r\n", panicReason))
for i := 2; ; i++ {
_, file, line, ok := rt.Caller(i)
if !ok {
break
}
buffer.WriteString(fmt.Sprintf(" %s:%d\r\n", file, line))
}
glog.Errorln(buffer.String())
headers := http.Header{}
if ct := w.Header().Get("Content-Type"); len(ct) > 0 {
headers.Set("Accept", ct)
}
responsewriters.ErrorNegotiated(apierrors.NewGenericServerResponse(http.StatusInternalServerError, "", schema.GroupResource{}, "", "", 0, false), s, schema.GroupVersion{}, w, &http.Request{Header: headers})
}
func serviceErrorHandler(s runtime.NegotiatedSerializer, serviceErr restful.ServiceError, request *restful.Request, resp *restful.Response) {
responsewriters.ErrorNegotiated(
apierrors.NewGenericServerResponse(serviceErr.Code, "", schema.GroupResource{}, "", serviceErr.Message, 0, false),
s,
schema.GroupVersion{},
resp,
request.Request,
)
}
// ServeHTTP makes it an http.Handler
func (a *APIServerHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
a.FullHandlerChain.ServeHTTP(w, r)
}

View File

@ -41,5 +41,5 @@ func (s *GenericAPIServer) installHealthz() {
defer s.healthzLock.Unlock()
s.healthzCreated = true
healthz.InstallHandler(s.FallThroughHandler, s.healthzChecks...)
healthz.InstallHandler(s.Handler.PostGoRestfulMux, s.healthzChecks...)
}

View File

@ -19,19 +19,12 @@ go_test(
go_library(
name = "go_default_library",
srcs = [
"container.go",
"doc.go",
"pathrecorder.go",
],
tags = ["automanaged"],
deps = [
"//vendor/github.com/emicklei/go-restful:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/handlers/responsewriters:go_default_library",
],
)

View File

@ -1,102 +0,0 @@
/*
Copyright 2016 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package mux
import (
"bytes"
"fmt"
"net/http"
rt "runtime"
"sort"
"github.com/emicklei/go-restful"
"github.com/golang/glog"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
)
// APIContainer is a restful container which in addition support registering
// handlers that do not show up in swagger or in /
type APIContainer struct {
*restful.Container
}
// NewAPIContainer constructs a new container for APIs
func NewAPIContainer(mux *http.ServeMux, s runtime.NegotiatedSerializer, defaultMux http.Handler) *APIContainer {
c := APIContainer{
Container: restful.NewContainer(),
}
c.Container.ServeMux = mux
c.Container.Router(restful.CurlyRouter{}) // e.g. for proxy/{kind}/{name}/{*}
c.Container.RecoverHandler(func(panicReason interface{}, httpWriter http.ResponseWriter) {
logStackOnRecover(s, panicReason, httpWriter)
})
c.Container.ServiceErrorHandler(func(serviceErr restful.ServiceError, request *restful.Request, response *restful.Response) {
serviceErrorHandler(s, serviceErr, request, response)
})
// register the defaultHandler for everything. This will allow an unhandled request to fall through to another handler instead of
// ending up with a forced 404
c.Container.Handle("/", defaultMux)
return &c
}
// ListedPaths returns the paths of the webservices for listing on /.
func (c *APIContainer) ListedPaths() []string {
var handledPaths []string
// Extract the paths handled using restful.WebService
for _, ws := range c.RegisteredWebServices() {
handledPaths = append(handledPaths, ws.RootPath())
}
sort.Strings(handledPaths)
return handledPaths
}
//TODO: Unify with RecoverPanics?
func logStackOnRecover(s runtime.NegotiatedSerializer, panicReason interface{}, w http.ResponseWriter) {
var buffer bytes.Buffer
buffer.WriteString(fmt.Sprintf("recover from panic situation: - %v\r\n", panicReason))
for i := 2; ; i++ {
_, file, line, ok := rt.Caller(i)
if !ok {
break
}
buffer.WriteString(fmt.Sprintf(" %s:%d\r\n", file, line))
}
glog.Errorln(buffer.String())
headers := http.Header{}
if ct := w.Header().Get("Content-Type"); len(ct) > 0 {
headers.Set("Accept", ct)
}
responsewriters.ErrorNegotiated(apierrors.NewGenericServerResponse(http.StatusInternalServerError, "", schema.GroupResource{}, "", "", 0, false), s, schema.GroupVersion{}, w, &http.Request{Header: headers})
}
func serviceErrorHandler(s runtime.NegotiatedSerializer, serviceErr restful.ServiceError, request *restful.Request, resp *restful.Response) {
responsewriters.ErrorNegotiated(
apierrors.NewGenericServerResponse(serviceErr.Code, "", schema.GroupResource{}, "", serviceErr.Message, 0, false),
s,
schema.GroupVersion{},
resp,
request.Request,
)
}

View File

@ -475,7 +475,7 @@ NextTest:
return
}
s, err := config.Complete().New()
s, err := config.Complete().New(server.EmptyDelegate)
if err != nil {
t.Errorf("%q - failed creating the server: %v", title, err)
return

View File

@ -17,6 +17,8 @@ limitations under the License.
package routes
import (
"github.com/emicklei/go-restful"
"k8s.io/apimachinery/pkg/openapi"
"k8s.io/apiserver/pkg/server/mux"
apiserveropenapi "k8s.io/apiserver/pkg/server/openapi"
@ -30,7 +32,7 @@ type OpenAPI struct {
}
// Install adds the SwaggerUI webservice to the given mux.
func (oa OpenAPI) Install(c *mux.APIContainer, mux *mux.PathRecorderMux) {
func (oa OpenAPI) Install(c *restful.Container, mux *mux.PathRecorderMux) {
err := apiserveropenapi.RegisterOpenAPIService("/swagger.json", c.RegisteredWebServices(), oa.Config, mux)
if err != nil {
glog.Fatalf("Failed to register open api spec for root: %v", err)

View File

@ -17,8 +17,7 @@ limitations under the License.
package routes
import (
"k8s.io/apiserver/pkg/server/mux"
"github.com/emicklei/go-restful"
"github.com/emicklei/go-restful-swagger12"
)
@ -31,7 +30,7 @@ type Swagger struct {
}
// Install adds the SwaggerUI webservice to the given mux.
func (s Swagger) Install(c *mux.APIContainer) {
func (s Swagger) Install(c *restful.Container) {
s.Config.WebServices = c.RegisteredWebServices()
swagger.RegisterSwaggerService(*s.Config, c.Container)
swagger.RegisterSwaggerService(*s.Config, c)
}

View File

@ -23,7 +23,6 @@ import (
"k8s.io/apimachinery/pkg/version"
"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
"k8s.io/apiserver/pkg/server/mux"
)
// Version provides a webservice with version information.
@ -32,7 +31,7 @@ type Version struct {
}
// Install registers the APIServer's `/version` handler.
func (v Version) Install(c *mux.APIContainer) {
func (v Version) Install(c *restful.Container) {
if v.Version == nil {
return
}

View File

@ -135,7 +135,7 @@ func (c *Config) SkipComplete() completedConfig {
// New returns a new instance of APIAggregator from the given config.
func (c completedConfig) NewWithDelegate(delegationTarget genericapiserver.DelegationTarget, stopCh <-chan struct{}) (*APIAggregator, error) {
genericServer, err := c.Config.GenericConfig.SkipComplete().NewWithDelegate(delegationTarget) // completion is done in Complete, no need for a second time
genericServer, err := c.Config.GenericConfig.SkipComplete().New(delegationTarget) // completion is done in Complete, no need for a second time
if err != nil {
return nil, err
}
@ -176,8 +176,8 @@ func (c completedConfig) NewWithDelegate(delegationTarget genericapiserver.Deleg
serviceLister: s.serviceLister,
endpointsLister: s.endpointsLister,
}
s.GenericAPIServer.FallThroughHandler.Handle("/apis", apisHandler)
s.GenericAPIServer.FallThroughHandler.UnlistedHandle("/apis/", apisHandler)
s.GenericAPIServer.Handler.PostGoRestfulMux.Handle("/apis", apisHandler)
s.GenericAPIServer.Handler.PostGoRestfulMux.UnlistedHandle("/apis/", apisHandler)
apiserviceRegistrationController := NewAPIServiceRegistrationController(informerFactory.Apiregistration().InternalVersion().APIServices(), kubeInformers.Core().V1().Services(), s)
@ -219,8 +219,8 @@ func (s *APIAggregator) AddAPIService(apiService *apiregistration.APIService, de
}
proxyHandler.updateAPIService(apiService, destinationHost)
s.proxyHandlers[apiService.Name] = proxyHandler
s.GenericAPIServer.FallThroughHandler.Handle(proxyPath, proxyHandler)
s.GenericAPIServer.FallThroughHandler.UnlistedHandle(proxyPath+"/", proxyHandler)
s.GenericAPIServer.Handler.PostGoRestfulMux.Handle(proxyPath, proxyHandler)
s.GenericAPIServer.Handler.PostGoRestfulMux.UnlistedHandlePrefix(proxyPath+"/", proxyHandler)
// if we're dealing with the legacy group, we're done here
if apiService.Name == legacyAPIServiceName {
@ -243,8 +243,8 @@ func (s *APIAggregator) AddAPIService(apiService *apiregistration.APIService, de
delegate: s.delegateHandler,
}
// aggregation is protected
s.GenericAPIServer.FallThroughHandler.Handle(groupPath, groupDiscoveryHandler)
s.GenericAPIServer.FallThroughHandler.UnlistedHandle(groupPath+"/", groupDiscoveryHandler)
s.GenericAPIServer.Handler.PostGoRestfulMux.Handle(groupPath, groupDiscoveryHandler)
s.GenericAPIServer.Handler.PostGoRestfulMux.UnlistedHandle(groupPath+"/", groupDiscoveryHandler)
s.handledGroups.Insert(apiService.Spec.Group)
}
@ -258,8 +258,8 @@ func (s *APIAggregator) RemoveAPIService(apiServiceName string) {
if apiServiceName == legacyAPIServiceName {
proxyPath = "/api"
}
s.GenericAPIServer.FallThroughHandler.Unregister(proxyPath)
s.GenericAPIServer.FallThroughHandler.Unregister(proxyPath + "/")
s.GenericAPIServer.Handler.PostGoRestfulMux.Unregister(proxyPath)
s.GenericAPIServer.Handler.PostGoRestfulMux.Unregister(proxyPath + "/")
delete(s.proxyHandlers, apiServiceName)
// TODO unregister group level discovery when there are no more versions for the group

View File

@ -102,7 +102,7 @@ func (c *Config) SkipComplete() completedConfig {
// New returns a new instance of CustomResources from the given config.
func (c completedConfig) New(delegationTarget genericapiserver.DelegationTarget, stopCh <-chan struct{}) (*CustomResources, error) {
genericServer, err := c.Config.GenericConfig.SkipComplete().New() // completion is done in Complete, no need for a second time
genericServer, err := c.Config.GenericConfig.SkipComplete().New(genericapiserver.EmptyDelegate) // completion is done in Complete, no need for a second time
if err != nil {
return nil, err
}
@ -149,8 +149,8 @@ func (c completedConfig) New(delegationTarget genericapiserver.DelegationTarget,
c.CustomResourceRESTOptionsGetter,
c.GenericConfig.AdmissionControl,
)
s.GenericAPIServer.FallThroughHandler.Handle("/apis", customResourceHandler)
s.GenericAPIServer.FallThroughHandler.HandlePrefix("/apis/", customResourceHandler)
s.GenericAPIServer.Handler.PostGoRestfulMux.Handle("/apis", customResourceHandler)
s.GenericAPIServer.Handler.PostGoRestfulMux.HandlePrefix("/apis/", customResourceHandler)
customResourceController := NewDiscoveryController(customResourceInformers.Apiextensions().InternalVersion().CustomResources(), versionDiscoveryHandler, groupDiscoveryHandler)

View File

@ -90,7 +90,7 @@ func (c *Config) SkipComplete() completedConfig {
// New returns a new instance of WardleServer from the given config.
func (c completedConfig) New() (*WardleServer, error) {
genericServer, err := c.Config.GenericConfig.SkipComplete().New() // completion is done in Complete, no need for a second time
genericServer, err := c.Config.GenericConfig.SkipComplete().New(genericapiserver.EmptyDelegate) // completion is done in Complete, no need for a second time
if err != nil {
return nil, err
}

View File

@ -236,7 +236,7 @@ func startMasterOrDie(masterConfig *master.Config, incomingServer *httptest.Serv
masterConfig.GenericConfig.LoopbackClientConfig.BearerToken = privilegedLoopbackToken
m, err := masterConfig.Complete().New()
m, err := masterConfig.Complete().New(genericapiserver.EmptyDelegate)
if err != nil {
glog.Fatalf("error in bringing up the master: %v", err)
}