Commit 8aeb6012 authored by Kubernetes Submit Queue's avatar Kubernetes Submit Queue Committed by GitHub

Merge pull request #42886 from deads2k/server-02-fallthrough

Automatic merge from submit-queue allow fallthrough handling from go-restful routes This sets up the gorestful routes to fall through to a default handler and reorders the API to be ahead of the other endpoints. This makes it possible to cleanly support cases of "match, fail, try this other handler" which we'll need for API server composition. @kubernetes/sig-api-machinery-pr-reviews @ncdc
parents a9c8d977 02efeeaf
...@@ -223,7 +223,7 @@ func NonBlockingRun(s *options.ServerRunOptions, stopCh <-chan struct{}) error { ...@@ -223,7 +223,7 @@ func NonBlockingRun(s *options.ServerRunOptions, stopCh <-chan struct{}) error {
return err return err
} }
routes.UIRedirect{}.Install(m.HandlerContainer) routes.UIRedirect{}.Install(m.FallThroughHandler)
routes.Logs{}.Install(m.HandlerContainer) routes.Logs{}.Install(m.HandlerContainer)
installFederationAPIs(m, genericConfig.RESTOptionsGetter) installFederationAPIs(m, genericConfig.RESTOptionsGetter)
......
...@@ -216,7 +216,7 @@ func (c completedConfig) New() (*Master, error) { ...@@ -216,7 +216,7 @@ func (c completedConfig) New() (*Master, error) {
} }
if c.EnableUISupport { if c.EnableUISupport {
routes.UIRedirect{}.Install(s.HandlerContainer) routes.UIRedirect{}.Install(s.FallThroughHandler)
} }
if c.EnableLogsSupport { if c.EnableLogsSupport {
routes.Logs{}.Install(s.HandlerContainer) routes.Logs{}.Install(s.HandlerContainer)
......
...@@ -40,7 +40,7 @@ import ( ...@@ -40,7 +40,7 @@ import (
// TestValidOpenAPISpec verifies that the open api is added // TestValidOpenAPISpec verifies that the open api is added
// at the proper endpoint and the spec is valid. // at the proper endpoint and the spec is valid.
func TestValidOpenAPISpec(t *testing.T) { func TestValidOpenAPISpec(t *testing.T) {
_, etcdserver, config, assert := setUp(t) etcdserver, config, assert := setUp(t)
defer etcdserver.Terminate(t) defer etcdserver.Terminate(t)
config.GenericConfig.EnableIndex = true config.GenericConfig.EnableIndex = true
......
...@@ -60,7 +60,7 @@ import ( ...@@ -60,7 +60,7 @@ import (
) )
// setUp is a convience function for setting up for (most) tests. // setUp is a convience function for setting up for (most) tests.
func setUp(t *testing.T) (*Master, *etcdtesting.EtcdTestServer, Config, *assert.Assertions) { func setUp(t *testing.T) (*etcdtesting.EtcdTestServer, Config, *assert.Assertions) {
server, storageConfig := etcdtesting.NewUnsecuredEtcd3TestClientServer(t, api.Scheme) server, storageConfig := etcdtesting.NewUnsecuredEtcd3TestClientServer(t, api.Scheme)
config := &Config{ config := &Config{
...@@ -101,16 +101,11 @@ func setUp(t *testing.T) (*Master, *etcdtesting.EtcdTestServer, Config, *assert. ...@@ -101,16 +101,11 @@ func setUp(t *testing.T) (*Master, *etcdtesting.EtcdTestServer, Config, *assert.
TLSClientConfig: &tls.Config{}, TLSClientConfig: &tls.Config{},
}) })
master, err := config.Complete().New() return server, *config, assert.New(t)
if err != nil {
t.Fatal(err)
}
return master, server, *config, assert.New(t)
} }
func newMaster(t *testing.T) (*Master, *etcdtesting.EtcdTestServer, Config, *assert.Assertions) { func newMaster(t *testing.T) (*Master, *etcdtesting.EtcdTestServer, Config, *assert.Assertions) {
_, etcdserver, config, assert := setUp(t) etcdserver, config, assert := setUp(t)
master, err := config.Complete().New() master, err := config.Complete().New()
if err != nil { if err != nil {
...@@ -136,7 +131,7 @@ func limitedAPIResourceConfigSource() *serverstorage.ResourceConfig { ...@@ -136,7 +131,7 @@ func limitedAPIResourceConfigSource() *serverstorage.ResourceConfig {
// newLimitedMaster only enables the core group, the extensions group, the batch group, and the autoscaling group. // newLimitedMaster only enables the core group, the extensions group, the batch group, and the autoscaling group.
func newLimitedMaster(t *testing.T) (*Master, *etcdtesting.EtcdTestServer, Config, *assert.Assertions) { func newLimitedMaster(t *testing.T) (*Master, *etcdtesting.EtcdTestServer, Config, *assert.Assertions) {
_, etcdserver, config, assert := setUp(t) etcdserver, config, assert := setUp(t)
config.APIResourceConfigSource = limitedAPIResourceConfigSource() config.APIResourceConfigSource = limitedAPIResourceConfigSource()
master, err := config.Complete().New() master, err := config.Complete().New()
if err != nil { if err != nil {
......
...@@ -27,8 +27,8 @@ const dashboardPath = "/api/v1/namespaces/kube-system/services/kubernetes-dashbo ...@@ -27,8 +27,8 @@ const dashboardPath = "/api/v1/namespaces/kube-system/services/kubernetes-dashbo
// UIRediect redirects /ui to the kube-ui proxy path. // UIRediect redirects /ui to the kube-ui proxy path.
type UIRedirect struct{} type UIRedirect struct{}
func (r UIRedirect) Install(c *mux.APIContainer) { func (r UIRedirect) Install(c *mux.PathRecorderMux) {
c.NonSwaggerRoutes.HandleFunc("/ui/", func(w http.ResponseWriter, r *http.Request) { c.HandleFunc("/ui/", func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, dashboardPath, http.StatusTemporaryRedirect) http.Redirect(w, r, dashboardPath, http.StatusTemporaryRedirect)
}) })
} }
...@@ -107,6 +107,10 @@ type Config struct { ...@@ -107,6 +107,10 @@ type Config struct {
// Will default to a value based on secure serving info and available ipv4 IPs. // Will default to a value based on secure serving info and available ipv4 IPs.
ExternalAddress string 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 // Fields you probably don't care about changing
//=========================================================================== //===========================================================================
...@@ -337,6 +341,9 @@ func (c *Config) Complete() completedConfig { ...@@ -337,6 +341,9 @@ func (c *Config) Complete() completedConfig {
tokenAuthorizer := authorizerfactory.NewPrivilegedGroups(user.SystemPrivilegedGroup) tokenAuthorizer := authorizerfactory.NewPrivilegedGroups(user.SystemPrivilegedGroup)
c.Authorizer = authorizerunion.New(tokenAuthorizer, c.Authorizer) c.Authorizer = authorizerunion.New(tokenAuthorizer, c.Authorizer)
} }
if c.FallThroughHandler == nil {
c.FallThroughHandler = mux.NewPathRecorderMux()
}
return completedConfig{c} return completedConfig{c}
} }
...@@ -392,6 +399,8 @@ func (c completedConfig) New() (*GenericAPIServer, error) { ...@@ -392,6 +399,8 @@ func (c completedConfig) New() (*GenericAPIServer, error) {
apiGroupsForDiscovery: map[string]metav1.APIGroup{}, apiGroupsForDiscovery: map[string]metav1.APIGroup{},
FallThroughHandler: c.FallThroughHandler,
swaggerConfig: c.SwaggerConfig, swaggerConfig: c.SwaggerConfig,
openAPIConfig: c.OpenAPIConfig, openAPIConfig: c.OpenAPIConfig,
...@@ -399,7 +408,7 @@ func (c completedConfig) New() (*GenericAPIServer, error) { ...@@ -399,7 +408,7 @@ func (c completedConfig) New() (*GenericAPIServer, error) {
healthzChecks: c.HealthzChecks, healthzChecks: c.HealthzChecks,
} }
s.HandlerContainer = mux.NewAPIContainer(http.NewServeMux(), c.Serializer) s.HandlerContainer = mux.NewAPIContainer(http.NewServeMux(), c.Serializer, s.FallThroughHandler)
if s.openAPIConfig != nil { if s.openAPIConfig != nil {
if s.openAPIConfig.Info == nil { if s.openAPIConfig.Info == nil {
...@@ -447,22 +456,22 @@ func DefaultBuildHandlerChain(apiHandler http.Handler, c *Config) (secure, insec ...@@ -447,22 +456,22 @@ func DefaultBuildHandlerChain(apiHandler http.Handler, c *Config) (secure, insec
func (s *GenericAPIServer) installAPI(c *Config) { func (s *GenericAPIServer) installAPI(c *Config) {
if c.EnableIndex { if c.EnableIndex {
routes.Index{}.Install(s.HandlerContainer) routes.Index{}.Install(s.HandlerContainer, s.FallThroughHandler)
} }
if c.SwaggerConfig != nil && c.EnableSwaggerUI { if c.SwaggerConfig != nil && c.EnableSwaggerUI {
routes.SwaggerUI{}.Install(s.HandlerContainer) routes.SwaggerUI{}.Install(s.FallThroughHandler)
} }
if c.EnableProfiling { if c.EnableProfiling {
routes.Profiling{}.Install(s.HandlerContainer) routes.Profiling{}.Install(s.FallThroughHandler)
if c.EnableContentionProfiling { if c.EnableContentionProfiling {
goruntime.SetBlockProfileRate(1) goruntime.SetBlockProfileRate(1)
} }
} }
if c.EnableMetrics { if c.EnableMetrics {
if c.EnableProfiling { if c.EnableProfiling {
routes.MetricsWithReset{}.Install(s.HandlerContainer) routes.MetricsWithReset{}.Install(s.FallThroughHandler)
} else { } else {
routes.DefaultMetrics{}.Install(s.HandlerContainer) routes.DefaultMetrics{}.Install(s.FallThroughHandler)
} }
} }
routes.Version{Version: c.Version}.Install(s.HandlerContainer) routes.Version{Version: c.Version}.Install(s.HandlerContainer)
......
...@@ -43,6 +43,7 @@ import ( ...@@ -43,6 +43,7 @@ import (
apirequest "k8s.io/apiserver/pkg/endpoints/request" apirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest" "k8s.io/apiserver/pkg/registry/rest"
"k8s.io/apiserver/pkg/server/healthz" "k8s.io/apiserver/pkg/server/healthz"
"k8s.io/apiserver/pkg/server/mux"
genericmux "k8s.io/apiserver/pkg/server/mux" genericmux "k8s.io/apiserver/pkg/server/mux"
"k8s.io/apiserver/pkg/server/routes" "k8s.io/apiserver/pkg/server/routes"
restclient "k8s.io/client-go/rest" restclient "k8s.io/client-go/rest"
...@@ -124,6 +125,9 @@ type GenericAPIServer struct { ...@@ -124,6 +125,9 @@ type GenericAPIServer struct {
// "Outputs" // "Outputs"
Handler http.Handler Handler http.Handler
InsecureHandler http.Handler InsecureHandler http.Handler
// FallThroughHandler is the final HTTP handler in the chain.
// It comes after all filters and the API handling
FallThroughHandler *mux.PathRecorderMux
// Map storing information about all groups to be exposed in discovery response. // Map storing information about all groups to be exposed in discovery response.
// The map is from name to the group. // The map is from name to the group.
...@@ -180,7 +184,7 @@ func (s *GenericAPIServer) PrepareRun() preparedGenericAPIServer { ...@@ -180,7 +184,7 @@ func (s *GenericAPIServer) PrepareRun() preparedGenericAPIServer {
if s.openAPIConfig != nil { if s.openAPIConfig != nil {
routes.OpenAPI{ routes.OpenAPI{
Config: s.openAPIConfig, Config: s.openAPIConfig,
}.Install(s.HandlerContainer) }.Install(s.HandlerContainer, s.FallThroughHandler)
} }
s.installHealthz() s.installHealthz()
......
...@@ -48,6 +48,7 @@ import ( ...@@ -48,6 +48,7 @@ import (
"k8s.io/apiserver/pkg/authorization/authorizer" "k8s.io/apiserver/pkg/authorization/authorizer"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request" genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest" "k8s.io/apiserver/pkg/registry/rest"
"k8s.io/apiserver/pkg/server/mux"
etcdtesting "k8s.io/apiserver/pkg/storage/etcd/testing" etcdtesting "k8s.io/apiserver/pkg/storage/etcd/testing"
restclient "k8s.io/client-go/rest" restclient "k8s.io/client-go/rest"
) )
...@@ -90,6 +91,7 @@ func setUp(t *testing.T) (*etcdtesting.EtcdTestServer, Config, *assert.Assertion ...@@ -90,6 +91,7 @@ func setUp(t *testing.T) (*etcdtesting.EtcdTestServer, Config, *assert.Assertion
config.RequestContextMapper = genericapirequest.NewRequestContextMapper() config.RequestContextMapper = genericapirequest.NewRequestContextMapper()
config.LegacyAPIGroupPrefixes = sets.NewString("/api") config.LegacyAPIGroupPrefixes = sets.NewString("/api")
config.LoopbackClientConfig = &restclient.Config{} config.LoopbackClientConfig = &restclient.Config{}
config.FallThroughHandler = mux.NewPathRecorderMux()
// TODO restore this test, but right now, eliminate our cycle // TODO restore this test, but right now, eliminate our cycle
// config.OpenAPIConfig = DefaultOpenAPIConfig(testGetOpenAPIDefinitions, runtime.NewScheme()) // config.OpenAPIConfig = DefaultOpenAPIConfig(testGetOpenAPIDefinitions, runtime.NewScheme())
...@@ -352,8 +354,8 @@ func TestCustomHandlerChain(t *testing.T) { ...@@ -352,8 +354,8 @@ func TestCustomHandlerChain(t *testing.T) {
t.Fatalf("Error in bringing up the server: %v", err) t.Fatalf("Error in bringing up the server: %v", err)
} }
s.HandlerContainer.NonSwaggerRoutes.Handle("/nonswagger", handler) s.FallThroughHandler.Handle("/nonswagger", handler)
s.HandlerContainer.UnlistedRoutes.Handle("/secret", handler) s.FallThroughHandler.Handle("/secret", handler)
type Test struct { type Test struct {
handler http.Handler handler http.Handler
......
...@@ -41,5 +41,5 @@ func (s *GenericAPIServer) installHealthz() { ...@@ -41,5 +41,5 @@ func (s *GenericAPIServer) installHealthz() {
defer s.healthzLock.Unlock() defer s.healthzLock.Unlock()
s.healthzCreated = true s.healthzCreated = true
healthz.InstallHandler(&s.HandlerContainer.NonSwaggerRoutes, s.healthzChecks...) healthz.InstallHandler(s.FallThroughHandler, s.healthzChecks...)
} }
...@@ -35,22 +35,12 @@ import ( ...@@ -35,22 +35,12 @@ import (
// handlers that do not show up in swagger or in / // handlers that do not show up in swagger or in /
type APIContainer struct { type APIContainer struct {
*restful.Container *restful.Container
// NonSwaggerRoutes are recorded and are visible at /, but do not show up in Swagger.
NonSwaggerRoutes PathRecorderMux
// UnlistedRoutes are not recorded, therefore not visible at / and do not show up in Swagger.
UnlistedRoutes *http.ServeMux
} }
// NewAPIContainer constructs a new container for APIs // NewAPIContainer constructs a new container for APIs
func NewAPIContainer(mux *http.ServeMux, s runtime.NegotiatedSerializer) *APIContainer { func NewAPIContainer(mux *http.ServeMux, s runtime.NegotiatedSerializer, defaultMux http.Handler) *APIContainer {
c := APIContainer{ c := APIContainer{
Container: restful.NewContainer(), Container: restful.NewContainer(),
NonSwaggerRoutes: PathRecorderMux{
mux: mux,
},
UnlistedRoutes: mux,
} }
c.Container.ServeMux = mux c.Container.ServeMux = mux
c.Container.Router(restful.CurlyRouter{}) // e.g. for proxy/{kind}/{name}/{*} c.Container.Router(restful.CurlyRouter{}) // e.g. for proxy/{kind}/{name}/{*}
...@@ -61,6 +51,10 @@ func NewAPIContainer(mux *http.ServeMux, s runtime.NegotiatedSerializer) *APICon ...@@ -61,6 +51,10 @@ func NewAPIContainer(mux *http.ServeMux, s runtime.NegotiatedSerializer) *APICon
serviceErrorHandler(s, serviceErr, request, 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 return &c
} }
......
...@@ -17,47 +17,83 @@ limitations under the License. ...@@ -17,47 +17,83 @@ limitations under the License.
package mux package mux
import ( import (
"fmt"
"net/http" "net/http"
) "runtime/debug"
// Mux is an object that can register http handlers. utilruntime "k8s.io/apimachinery/pkg/util/runtime"
type Mux interface { )
Handle(pattern string, handler http.Handler)
HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request))
}
// PathRecorderMux wraps a mux object and records the registered paths. It is _not_ go routine safe. // PathRecorderMux wraps a mux object and records the registered exposedPaths. It is _not_ go routine safe.
type PathRecorderMux struct { type PathRecorderMux struct {
mux Mux mux *http.ServeMux
paths []string exposedPaths []string
// pathStacks holds the stacks of all registered paths. This allows us to show a more helpful message
// before the "http: multiple registrations for %s" panic.
pathStacks map[string]string
} }
// NewPathRecorderMux creates a new PathRecorderMux with the given mux as the base mux. // NewPathRecorderMux creates a new PathRecorderMux with the given mux as the base mux.
func NewPathRecorderMux(mux Mux) *PathRecorderMux { func NewPathRecorderMux() *PathRecorderMux {
return &PathRecorderMux{ return &PathRecorderMux{
mux: mux, mux: http.NewServeMux(),
pathStacks: map[string]string{},
} }
} }
// BaseMux returns the underlying mux. // HandledPaths returns the registered handler exposedPaths.
func (m *PathRecorderMux) BaseMux() Mux {
return m.mux
}
// HandledPaths returns the registered handler paths.
func (m *PathRecorderMux) HandledPaths() []string { func (m *PathRecorderMux) HandledPaths() []string {
return append([]string{}, m.paths...) return append([]string{}, m.exposedPaths...)
} }
// Handle registers the handler for the given pattern. // Handle registers the handler for the given pattern.
// If a handler already exists for pattern, Handle panics. // If a handler already exists for pattern, Handle panics.
func (m *PathRecorderMux) Handle(path string, handler http.Handler) { func (m *PathRecorderMux) Handle(path string, handler http.Handler) {
m.paths = append(m.paths, path) if existingStack, ok := m.pathStacks[path]; ok {
utilruntime.HandleError(fmt.Errorf("registered %q from %v", path, existingStack))
}
m.pathStacks[path] = string(debug.Stack())
m.exposedPaths = append(m.exposedPaths, path)
m.mux.Handle(path, handler) m.mux.Handle(path, handler)
} }
// HandleFunc registers the handler function for the given pattern. // HandleFunc registers the handler function for the given pattern.
// If a handler already exists for pattern, Handle panics.
func (m *PathRecorderMux) HandleFunc(path string, handler func(http.ResponseWriter, *http.Request)) { func (m *PathRecorderMux) HandleFunc(path string, handler func(http.ResponseWriter, *http.Request)) {
m.paths = append(m.paths, path) if existingStack, ok := m.pathStacks[path]; ok {
utilruntime.HandleError(fmt.Errorf("registered %q from %v", path, existingStack))
}
m.pathStacks[path] = string(debug.Stack())
m.exposedPaths = append(m.exposedPaths, path)
m.mux.HandleFunc(path, handler) m.mux.HandleFunc(path, handler)
} }
// UnlistedHandle registers the handler for the given pattern, but doesn't list it.
// If a handler already exists for pattern, Handle panics.
func (m *PathRecorderMux) UnlistedHandle(path string, handler http.Handler) {
if existingStack, ok := m.pathStacks[path]; ok {
utilruntime.HandleError(fmt.Errorf("registered %q from %v", path, existingStack))
}
m.pathStacks[path] = string(debug.Stack())
m.mux.Handle(path, handler)
}
// UnlistedHandleFunc registers the handler function for the given pattern, but doesn't list it.
// If a handler already exists for pattern, Handle panics.
func (m *PathRecorderMux) UnlistedHandleFunc(path string, handler func(http.ResponseWriter, *http.Request)) {
if existingStack, ok := m.pathStacks[path]; ok {
utilruntime.HandleError(fmt.Errorf("registered %q from %v", path, existingStack))
}
m.pathStacks[path] = string(debug.Stack())
m.mux.HandleFunc(path, handler)
}
// ServeHTTP makes it an http.Handler
func (m *PathRecorderMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
m.mux.ServeHTTP(w, r)
}
...@@ -23,18 +23,10 @@ import ( ...@@ -23,18 +23,10 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func TestNewAPIContainer(t *testing.T) {
mux := http.NewServeMux()
c := NewAPIContainer(mux, nil)
assert.Equal(t, mux, c.UnlistedRoutes, "UnlistedRoutes ServeMux's do not match")
assert.Equal(t, mux, c.Container.ServeMux, "Container ServeMux's do not match")
}
func TestSecretHandlers(t *testing.T) { func TestSecretHandlers(t *testing.T) {
mux := http.NewServeMux() c := NewPathRecorderMux()
c := NewAPIContainer(mux, nil) c.UnlistedHandleFunc("/secret", func(http.ResponseWriter, *http.Request) {})
c.UnlistedRoutes.HandleFunc("/secret", func(http.ResponseWriter, *http.Request) {}) c.HandleFunc("/nonswagger", func(http.ResponseWriter, *http.Request) {})
c.NonSwaggerRoutes.HandleFunc("/nonswagger", func(http.ResponseWriter, *http.Request) {}) assert.NotContains(t, c.HandledPaths(), "/secret")
assert.NotContains(t, c.NonSwaggerRoutes.HandledPaths(), "/secret") assert.Contains(t, c.HandledPaths(), "/nonswagger")
assert.Contains(t, c.NonSwaggerRoutes.HandledPaths(), "/nonswagger")
} }
...@@ -44,7 +44,7 @@ type openAPI struct { ...@@ -44,7 +44,7 @@ type openAPI struct {
} }
// RegisterOpenAPIService registers a handler to provides standard OpenAPI specification. // RegisterOpenAPIService registers a handler to provides standard OpenAPI specification.
func RegisterOpenAPIService(servePath string, webServices []*restful.WebService, config *openapi.Config, container *genericmux.APIContainer) (err error) { func RegisterOpenAPIService(servePath string, webServices []*restful.WebService, config *openapi.Config, mux *genericmux.PathRecorderMux) (err error) {
o := openAPI{ o := openAPI{
config: config, config: config,
servePath: servePath, servePath: servePath,
...@@ -63,7 +63,7 @@ func RegisterOpenAPIService(servePath string, webServices []*restful.WebService, ...@@ -63,7 +63,7 @@ func RegisterOpenAPIService(servePath string, webServices []*restful.WebService,
return err return err
} }
container.UnlistedRoutes.HandleFunc(servePath, func(w http.ResponseWriter, r *http.Request) { mux.UnlistedHandleFunc(servePath, func(w http.ResponseWriter, r *http.Request) {
resp := restful.NewResponse(w) resp := restful.NewResponse(w)
if r.URL.Path != servePath { if r.URL.Path != servePath {
resp.WriteErrorString(http.StatusNotFound, "Path not found!") resp.WriteErrorString(http.StatusNotFound, "Path not found!")
......
...@@ -29,8 +29,8 @@ import ( ...@@ -29,8 +29,8 @@ import (
type Index struct{} type Index struct{}
// Install adds the Index webservice to the given mux. // Install adds the Index webservice to the given mux.
func (i Index) Install(c *mux.APIContainer) { func (i Index) Install(c *mux.APIContainer, mux *mux.PathRecorderMux) {
c.UnlistedRoutes.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { mux.UnlistedHandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
status := http.StatusOK status := http.StatusOK
if r.URL.Path != "/" && r.URL.Path != "/index.html" { if r.URL.Path != "/" && r.URL.Path != "/index.html" {
// Since "/" matches all paths, handleIndex is called for all paths for which there is no handler api.Registry. // Since "/" matches all paths, handleIndex is called for all paths for which there is no handler api.Registry.
...@@ -43,7 +43,7 @@ func (i Index) Install(c *mux.APIContainer) { ...@@ -43,7 +43,7 @@ func (i Index) Install(c *mux.APIContainer) {
handledPaths = append(handledPaths, ws.RootPath()) handledPaths = append(handledPaths, ws.RootPath())
} }
// Extract the paths handled using mux handler. // Extract the paths handled using mux handler.
handledPaths = append(handledPaths, c.NonSwaggerRoutes.HandledPaths()...) handledPaths = append(handledPaths, mux.HandledPaths()...)
sort.Strings(handledPaths) sort.Strings(handledPaths)
responsewriters.WriteRawJSON(status, metav1.RootPaths{Paths: handledPaths}, w) responsewriters.WriteRawJSON(status, metav1.RootPaths{Paths: handledPaths}, w)
}) })
......
...@@ -31,8 +31,8 @@ import ( ...@@ -31,8 +31,8 @@ import (
type DefaultMetrics struct{} type DefaultMetrics struct{}
// Install adds the DefaultMetrics handler // Install adds the DefaultMetrics handler
func (m DefaultMetrics) Install(c *mux.APIContainer) { func (m DefaultMetrics) Install(c *mux.PathRecorderMux) {
c.NonSwaggerRoutes.Handle("/metrics", prometheus.Handler()) c.Handle("/metrics", prometheus.Handler())
} }
// MetricsWithReset install the prometheus metrics handler extended with support for the DELETE method // MetricsWithReset install the prometheus metrics handler extended with support for the DELETE method
...@@ -40,9 +40,9 @@ func (m DefaultMetrics) Install(c *mux.APIContainer) { ...@@ -40,9 +40,9 @@ func (m DefaultMetrics) Install(c *mux.APIContainer) {
type MetricsWithReset struct{} type MetricsWithReset struct{}
// Install adds the MetricsWithReset handler // Install adds the MetricsWithReset handler
func (m MetricsWithReset) Install(c *mux.APIContainer) { func (m MetricsWithReset) Install(c *mux.PathRecorderMux) {
defaultMetricsHandler := prometheus.Handler().ServeHTTP defaultMetricsHandler := prometheus.Handler().ServeHTTP
c.NonSwaggerRoutes.HandleFunc("/metrics", func(w http.ResponseWriter, req *http.Request) { c.HandleFunc("/metrics", func(w http.ResponseWriter, req *http.Request) {
if req.Method == "DELETE" { if req.Method == "DELETE" {
apimetrics.Reset() apimetrics.Reset()
etcdmetrics.Reset() etcdmetrics.Reset()
......
...@@ -30,8 +30,8 @@ type OpenAPI struct { ...@@ -30,8 +30,8 @@ type OpenAPI struct {
} }
// Install adds the SwaggerUI webservice to the given mux. // Install adds the SwaggerUI webservice to the given mux.
func (oa OpenAPI) Install(c *mux.APIContainer) { func (oa OpenAPI) Install(c *mux.APIContainer, mux *mux.PathRecorderMux) {
err := apiserveropenapi.RegisterOpenAPIService("/swagger.json", c.RegisteredWebServices(), oa.Config, c) err := apiserveropenapi.RegisterOpenAPIService("/swagger.json", c.RegisteredWebServices(), oa.Config, mux)
if err != nil { if err != nil {
glog.Fatalf("Failed to register open api spec for root: %v", err) glog.Fatalf("Failed to register open api spec for root: %v", err)
} }
......
...@@ -26,9 +26,9 @@ import ( ...@@ -26,9 +26,9 @@ import (
type Profiling struct{} type Profiling struct{}
// Install adds the Profiling webservice to the given mux. // Install adds the Profiling webservice to the given mux.
func (d Profiling) Install(c *mux.APIContainer) { func (d Profiling) Install(c *mux.PathRecorderMux) {
c.UnlistedRoutes.HandleFunc("/debug/pprof/", pprof.Index) c.UnlistedHandleFunc("/debug/pprof/", pprof.Index)
c.UnlistedRoutes.HandleFunc("/debug/pprof/profile", pprof.Profile) c.UnlistedHandleFunc("/debug/pprof/profile", pprof.Profile)
c.UnlistedRoutes.HandleFunc("/debug/pprof/symbol", pprof.Symbol) c.UnlistedHandleFunc("/debug/pprof/symbol", pprof.Symbol)
c.UnlistedRoutes.HandleFunc("/debug/pprof/trace", pprof.Trace) c.UnlistedHandleFunc("/debug/pprof/trace", pprof.Trace)
} }
...@@ -29,12 +29,12 @@ import ( ...@@ -29,12 +29,12 @@ import (
type SwaggerUI struct{} type SwaggerUI struct{}
// Install adds the SwaggerUI webservice to the given mux. // Install adds the SwaggerUI webservice to the given mux.
func (l SwaggerUI) Install(c *mux.APIContainer) { func (l SwaggerUI) Install(c *mux.PathRecorderMux) {
fileServer := http.FileServer(&assetfs.AssetFS{ fileServer := http.FileServer(&assetfs.AssetFS{
Asset: swagger.Asset, Asset: swagger.Asset,
AssetDir: swagger.AssetDir, AssetDir: swagger.AssetDir,
Prefix: "third_party/swagger-ui", Prefix: "third_party/swagger-ui",
}) })
prefix := "/swagger-ui/" prefix := "/swagger-ui/"
c.NonSwaggerRoutes.Handle(prefix, http.StripPrefix(prefix, fileServer)) c.Handle(prefix, http.StripPrefix(prefix, fileServer))
} }
...@@ -287,8 +287,8 @@ func (s *APIAggregator) AddAPIService(apiService *apiregistration.APIService) { ...@@ -287,8 +287,8 @@ func (s *APIAggregator) AddAPIService(apiService *apiregistration.APIService) {
endpointsLister: s.endpointsLister, endpointsLister: s.endpointsLister,
} }
// aggregation is protected // aggregation is protected
s.GenericAPIServer.HandlerContainer.UnlistedRoutes.Handle(groupPath, groupDiscoveryHandler) s.GenericAPIServer.FallThroughHandler.UnlistedHandle(groupPath, groupDiscoveryHandler)
s.GenericAPIServer.HandlerContainer.UnlistedRoutes.Handle(groupPath+"/", groupDiscoveryHandler) s.GenericAPIServer.FallThroughHandler.UnlistedHandle(groupPath+"/", groupDiscoveryHandler)
s.handledGroups.Insert(apiService.Spec.Group) s.handledGroups.Insert(apiService.Spec.Group)
} }
......
...@@ -10501,6 +10501,7 @@ go_test( ...@@ -10501,6 +10501,7 @@ go_test(
"//vendor:k8s.io/apiserver/pkg/authorization/authorizer", "//vendor:k8s.io/apiserver/pkg/authorization/authorizer",
"//vendor:k8s.io/apiserver/pkg/endpoints/request", "//vendor:k8s.io/apiserver/pkg/endpoints/request",
"//vendor:k8s.io/apiserver/pkg/registry/rest", "//vendor:k8s.io/apiserver/pkg/registry/rest",
"//vendor:k8s.io/apiserver/pkg/server/mux",
"//vendor:k8s.io/apiserver/pkg/storage/etcd/testing", "//vendor:k8s.io/apiserver/pkg/storage/etcd/testing",
"//vendor:k8s.io/client-go/rest", "//vendor:k8s.io/client-go/rest",
], ],
...@@ -10640,7 +10641,7 @@ go_library( ...@@ -10640,7 +10641,7 @@ go_library(
go_test( go_test(
name = "k8s.io/apiserver/pkg/server/mux_test", name = "k8s.io/apiserver/pkg/server/mux_test",
srcs = ["k8s.io/apiserver/pkg/server/mux/container_test.go"], srcs = ["k8s.io/apiserver/pkg/server/mux/pathrecorder_test.go"],
library = ":k8s.io/apiserver/pkg/server/mux", library = ":k8s.io/apiserver/pkg/server/mux",
tags = ["automanaged"], tags = ["automanaged"],
deps = ["//vendor:github.com/stretchr/testify/assert"], deps = ["//vendor:github.com/stretchr/testify/assert"],
...@@ -10660,6 +10661,7 @@ go_library( ...@@ -10660,6 +10661,7 @@ go_library(
"//vendor:k8s.io/apimachinery/pkg/api/errors", "//vendor:k8s.io/apimachinery/pkg/api/errors",
"//vendor:k8s.io/apimachinery/pkg/runtime", "//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apimachinery/pkg/runtime/schema", "//vendor:k8s.io/apimachinery/pkg/runtime/schema",
"//vendor:k8s.io/apimachinery/pkg/util/runtime",
"//vendor:k8s.io/apiserver/pkg/endpoints/handlers/responsewriters", "//vendor:k8s.io/apiserver/pkg/endpoints/handlers/responsewriters",
], ],
) )
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment