mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-14 14:23:37 +00:00
allow fallthrough handling from go-restful routes
This commit is contained in:
parent
0a6d82d8e7
commit
02efeeaf40
@ -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) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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 (
|
|||||||
// 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 (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// 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.
|
|||||||
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 {
|
|||||||
|
|
||||||
// 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
|
|||||||
// 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 {
|
|||||||
// 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 {
|
|||||||
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) {
|
|||||||
|
|
||||||
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) {
|
|||||||
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
|
|||||||
|
|
||||||
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)
|
||||||
|
@ -44,6 +44,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"
|
||||||
@ -125,6 +126,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.
|
||||||
@ -179,7 +183,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 (
|
|||||||
"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
|
|||||||
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) {
|
|||||||
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() {
|
|||||||
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 (
|
|||||||
// 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
|
|||||||
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.
|
|||||||
package mux
|
package mux
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"runtime/debug"
|
||||||
|
|
||||||
|
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Mux is an object that can register http handlers.
|
// PathRecorderMux wraps a mux object and records the registered exposedPaths. It is _not_ go routine safe.
|
||||||
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.
|
|
||||||
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 (
|
|||||||
"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 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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,
|
|||||||
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 (
|
|||||||
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) {
|
|||||||
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 (
|
|||||||
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) {
|
|||||||
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 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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 (
|
|||||||
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 (
|
|||||||
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))
|
||||||
}
|
}
|
||||||
|
@ -250,8 +250,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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
4
vendor/BUILD
vendored
4
vendor/BUILD
vendored
@ -10488,6 +10488,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",
|
||||||
],
|
],
|
||||||
@ -10627,7 +10628,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"],
|
||||||
@ -10647,6 +10648,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",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user