mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-25 20:53:33 +00:00
use our own serve mux that directs how we want
This commit is contained in:
parent
fcd9b7f7ba
commit
c837c7fb1a
@ -2943,7 +2943,7 @@ runTests() {
|
|||||||
|
|
||||||
# Make sure the UI can be proxied
|
# Make sure the UI can be proxied
|
||||||
start-proxy
|
start-proxy
|
||||||
check-curl-proxy-code /ui 301
|
check-curl-proxy-code /ui 307
|
||||||
check-curl-proxy-code /api/ui 404
|
check-curl-proxy-code /api/ui 404
|
||||||
check-curl-proxy-code /api/v1/namespaces 200
|
check-curl-proxy-code /api/v1/namespaces 200
|
||||||
if kube::test::if_supports_resource "${metrics}" ; then
|
if kube::test::if_supports_resource "${metrics}" ; then
|
||||||
@ -2962,7 +2962,7 @@ runTests() {
|
|||||||
|
|
||||||
# Custom paths let you see everything.
|
# Custom paths let you see everything.
|
||||||
start-proxy /custom
|
start-proxy /custom
|
||||||
check-curl-proxy-code /custom/ui 301
|
check-curl-proxy-code /custom/ui 307
|
||||||
if kube::test::if_supports_resource "${metrics}" ; then
|
if kube::test::if_supports_resource "${metrics}" ; then
|
||||||
check-curl-proxy-code /custom/metrics 200
|
check-curl-proxy-code /custom/metrics 200
|
||||||
fi
|
fi
|
||||||
|
@ -28,7 +28,9 @@ const dashboardPath = "/api/v1/namespaces/kube-system/services/kubernetes-dashbo
|
|||||||
type UIRedirect struct{}
|
type UIRedirect struct{}
|
||||||
|
|
||||||
func (r UIRedirect) Install(c *mux.PathRecorderMux) {
|
func (r UIRedirect) Install(c *mux.PathRecorderMux) {
|
||||||
c.HandleFunc("/ui/", func(w http.ResponseWriter, r *http.Request) {
|
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
http.Redirect(w, r, dashboardPath, http.StatusTemporaryRedirect)
|
http.Redirect(w, r, dashboardPath, http.StatusTemporaryRedirect)
|
||||||
})
|
})
|
||||||
|
c.Handle("/ui", handler)
|
||||||
|
c.HandlePrefix("/ui/", handler)
|
||||||
}
|
}
|
||||||
|
@ -479,7 +479,15 @@ func (c completedConfig) buildHandlers(s *GenericAPIServer, delegate http.Handle
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
installAPI(s, c.Config, delegate)
|
installAPI(s, c.Config)
|
||||||
|
if delegate != nil {
|
||||||
|
s.FallThroughHandler.NotFoundHandler(delegate)
|
||||||
|
} else if c.EnableIndex {
|
||||||
|
s.FallThroughHandler.NotFoundHandler(routes.IndexLister{
|
||||||
|
StatusCode: http.StatusNotFound,
|
||||||
|
PathProvider: s.listedPathProvider,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
s.Handler = c.BuildHandlerChainFunc(s.HandlerContainer.ServeMux, c.Config)
|
s.Handler = c.BuildHandlerChainFunc(s.HandlerContainer.ServeMux, c.Config)
|
||||||
|
|
||||||
@ -500,15 +508,9 @@ func DefaultBuildHandlerChain(apiHandler http.Handler, c *Config) http.Handler {
|
|||||||
return handler
|
return handler
|
||||||
}
|
}
|
||||||
|
|
||||||
func installAPI(s *GenericAPIServer, c *Config, delegate http.Handler) {
|
func installAPI(s *GenericAPIServer, c *Config) {
|
||||||
switch {
|
if c.EnableIndex {
|
||||||
case c.EnableIndex:
|
routes.Index{}.Install(s.listedPathProvider, c.FallThroughHandler)
|
||||||
routes.Index{}.Install(s.listedPathProvider, c.FallThroughHandler, delegate)
|
|
||||||
|
|
||||||
case delegate != nil:
|
|
||||||
// if we have a delegate, allow it to handle everything that's unmatched even if
|
|
||||||
// the index is disabled.
|
|
||||||
s.FallThroughHandler.UnlistedHandleFunc("/", delegate.ServeHTTP)
|
|
||||||
}
|
}
|
||||||
if c.SwaggerConfig != nil && c.EnableSwaggerUI {
|
if c.SwaggerConfig != nil && c.EnableSwaggerUI {
|
||||||
routes.SwaggerUI{}.Install(s.FallThroughHandler)
|
routes.SwaggerUI{}.Install(s.FallThroughHandler)
|
||||||
|
@ -192,7 +192,7 @@ type emptyDelegate struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s emptyDelegate) UnprotectedHandler() http.Handler {
|
func (s emptyDelegate) UnprotectedHandler() http.Handler {
|
||||||
return http.NotFoundHandler()
|
return nil
|
||||||
}
|
}
|
||||||
func (s emptyDelegate) PostStartHooks() map[string]postStartHookEntry {
|
func (s emptyDelegate) PostStartHooks() map[string]postStartHookEntry {
|
||||||
return map[string]postStartHookEntry{}
|
return map[string]postStartHookEntry{}
|
||||||
|
@ -31,6 +31,7 @@ go_library(
|
|||||||
"//vendor/k8s.io/apimachinery/pkg/runtime: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/runtime/schema:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/util/runtime: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",
|
"//vendor/k8s.io/apiserver/pkg/endpoints/handlers/responsewriters:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
@ -21,18 +21,25 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PathRecorderMux wraps a mux object and records the registered exposedPaths.
|
// PathRecorderMux wraps a mux object and records the registered exposedPaths.
|
||||||
type PathRecorderMux struct {
|
type PathRecorderMux struct {
|
||||||
lock sync.Mutex
|
lock sync.Mutex
|
||||||
|
notFoundHandler http.Handler
|
||||||
pathToHandler map[string]http.Handler
|
pathToHandler map[string]http.Handler
|
||||||
|
prefixToHandler map[string]http.Handler
|
||||||
|
|
||||||
// mux stores an *http.ServeMux and is used to handle the actual serving
|
// mux stores a pathHandler and is used to handle the actual serving.
|
||||||
|
// Turns out, we want to accept trailing slashes, BUT we don't care about handling
|
||||||
|
// everything under them. This does exactly matches only unless its explicitly requested to
|
||||||
|
// do something different
|
||||||
mux atomic.Value
|
mux atomic.Value
|
||||||
|
|
||||||
// exposedPaths is the list of paths that should be shown at /
|
// exposedPaths is the list of paths that should be shown at /
|
||||||
@ -43,16 +50,38 @@ type PathRecorderMux struct {
|
|||||||
pathStacks map[string]string
|
pathStacks map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// pathHandler is an http.Handler that will satify requests first by exact match, then by prefix,
|
||||||
|
// then by notFoundHandler
|
||||||
|
type pathHandler struct {
|
||||||
|
// pathToHandler is a map of exactly matching request to its handler
|
||||||
|
pathToHandler map[string]http.Handler
|
||||||
|
|
||||||
|
// this has to be sorted by most slashes then by length
|
||||||
|
prefixHandlers []prefixHandler
|
||||||
|
|
||||||
|
// notFoundHandler is the handler to use for satisfying requests with no other match
|
||||||
|
notFoundHandler http.Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
// prefixHandler holds the prefix it should match and the handler to use
|
||||||
|
type prefixHandler struct {
|
||||||
|
// prefix is the prefix to test for a request match
|
||||||
|
prefix string
|
||||||
|
// handler is used to satisfy matching requests
|
||||||
|
handler http.Handler
|
||||||
|
}
|
||||||
|
|
||||||
// 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() *PathRecorderMux {
|
func NewPathRecorderMux() *PathRecorderMux {
|
||||||
ret := &PathRecorderMux{
|
ret := &PathRecorderMux{
|
||||||
pathToHandler: map[string]http.Handler{},
|
pathToHandler: map[string]http.Handler{},
|
||||||
|
prefixToHandler: map[string]http.Handler{},
|
||||||
mux: atomic.Value{},
|
mux: atomic.Value{},
|
||||||
exposedPaths: []string{},
|
exposedPaths: []string{},
|
||||||
pathStacks: map[string]string{},
|
pathStacks: map[string]string{},
|
||||||
}
|
}
|
||||||
|
|
||||||
ret.mux.Store(http.NewServeMux())
|
ret.mux.Store(&pathHandler{notFoundHandler: http.NotFoundHandler()})
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,12 +103,38 @@ func (m *PathRecorderMux) trackCallers(path string) {
|
|||||||
// refreshMuxLocked creates a new mux and must be called while locked. Otherwise the view of handlers may
|
// refreshMuxLocked creates a new mux and must be called while locked. Otherwise the view of handlers may
|
||||||
// not be consistent
|
// not be consistent
|
||||||
func (m *PathRecorderMux) refreshMuxLocked() {
|
func (m *PathRecorderMux) refreshMuxLocked() {
|
||||||
mux := http.NewServeMux()
|
newMux := &pathHandler{
|
||||||
|
pathToHandler: map[string]http.Handler{},
|
||||||
|
prefixHandlers: []prefixHandler{},
|
||||||
|
notFoundHandler: http.NotFoundHandler(),
|
||||||
|
}
|
||||||
|
if m.notFoundHandler != nil {
|
||||||
|
newMux.notFoundHandler = m.notFoundHandler
|
||||||
|
}
|
||||||
for path, handler := range m.pathToHandler {
|
for path, handler := range m.pathToHandler {
|
||||||
mux.Handle(path, handler)
|
newMux.pathToHandler[path] = handler
|
||||||
}
|
}
|
||||||
|
|
||||||
m.mux.Store(mux)
|
keys := sets.StringKeySet(m.prefixToHandler).List()
|
||||||
|
sort.Sort(sort.Reverse(byPrefixPriority(keys)))
|
||||||
|
for _, prefix := range keys {
|
||||||
|
newMux.prefixHandlers = append(newMux.prefixHandlers, prefixHandler{
|
||||||
|
prefix: prefix,
|
||||||
|
handler: m.prefixToHandler[prefix],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
m.mux.Store(newMux)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotFoundHandler sets the handler to use if there's no match for a give path
|
||||||
|
func (m *PathRecorderMux) NotFoundHandler(notFoundHandler http.Handler) {
|
||||||
|
m.lock.Lock()
|
||||||
|
defer m.lock.Unlock()
|
||||||
|
|
||||||
|
m.notFoundHandler = notFoundHandler
|
||||||
|
|
||||||
|
m.refreshMuxLocked()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unregister removes a path from the mux.
|
// Unregister removes a path from the mux.
|
||||||
@ -88,6 +143,7 @@ func (m *PathRecorderMux) Unregister(path string) {
|
|||||||
defer m.lock.Unlock()
|
defer m.lock.Unlock()
|
||||||
|
|
||||||
delete(m.pathToHandler, path)
|
delete(m.pathToHandler, path)
|
||||||
|
delete(m.prefixToHandler, path)
|
||||||
delete(m.pathStacks, path)
|
delete(m.pathStacks, path)
|
||||||
for i := range m.exposedPaths {
|
for i := range m.exposedPaths {
|
||||||
if m.exposedPaths[i] == path {
|
if m.exposedPaths[i] == path {
|
||||||
@ -114,13 +170,7 @@ func (m *PathRecorderMux) Handle(path string, handler http.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.
|
// 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.lock.Lock()
|
m.Handle(path, http.HandlerFunc(handler))
|
||||||
defer m.lock.Unlock()
|
|
||||||
m.trackCallers(path)
|
|
||||||
|
|
||||||
m.exposedPaths = append(m.exposedPaths, path)
|
|
||||||
m.pathToHandler[path] = http.HandlerFunc(handler)
|
|
||||||
m.refreshMuxLocked()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnlistedHandle registers the handler for the given pattern, but doesn't list it.
|
// UnlistedHandle registers the handler for the given pattern, but doesn't list it.
|
||||||
@ -137,15 +187,79 @@ func (m *PathRecorderMux) UnlistedHandle(path string, handler http.Handler) {
|
|||||||
// UnlistedHandleFunc registers the handler function for the given pattern, but doesn't list it.
|
// UnlistedHandleFunc registers the handler function for the given pattern, but doesn't list it.
|
||||||
// If a handler already exists for pattern, Handle panics.
|
// If a handler already exists for pattern, Handle panics.
|
||||||
func (m *PathRecorderMux) UnlistedHandleFunc(path string, handler func(http.ResponseWriter, *http.Request)) {
|
func (m *PathRecorderMux) UnlistedHandleFunc(path string, handler func(http.ResponseWriter, *http.Request)) {
|
||||||
|
m.UnlistedHandle(path, http.HandlerFunc(handler))
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandlePrefix is like Handle, but matches for anything under the path. Like a standard golang trailing slash.
|
||||||
|
func (m *PathRecorderMux) HandlePrefix(path string, handler http.Handler) {
|
||||||
|
if !strings.HasSuffix(path, "/") {
|
||||||
|
panic(fmt.Sprintf("%q must end in a trailing slash", path))
|
||||||
|
}
|
||||||
|
|
||||||
m.lock.Lock()
|
m.lock.Lock()
|
||||||
defer m.lock.Unlock()
|
defer m.lock.Unlock()
|
||||||
m.trackCallers(path)
|
m.trackCallers(path)
|
||||||
|
|
||||||
m.pathToHandler[path] = http.HandlerFunc(handler)
|
m.exposedPaths = append(m.exposedPaths, path)
|
||||||
|
m.prefixToHandler[path] = handler
|
||||||
|
m.refreshMuxLocked()
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnlistedHandlePrefix is like UnlistedHandle, but matches for anything under the path. Like a standard golang trailing slash.
|
||||||
|
func (m *PathRecorderMux) UnlistedHandlePrefix(path string, handler http.Handler) {
|
||||||
|
if !strings.HasSuffix(path, "/") {
|
||||||
|
panic(fmt.Sprintf("%q must end in a trailing slash", path))
|
||||||
|
}
|
||||||
|
|
||||||
|
m.lock.Lock()
|
||||||
|
defer m.lock.Unlock()
|
||||||
|
m.trackCallers(path)
|
||||||
|
|
||||||
|
m.prefixToHandler[path] = handler
|
||||||
m.refreshMuxLocked()
|
m.refreshMuxLocked()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServeHTTP makes it an http.Handler
|
// ServeHTTP makes it an http.Handler
|
||||||
func (m *PathRecorderMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (m *PathRecorderMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
m.mux.Load().(*http.ServeMux).ServeHTTP(w, r)
|
m.mux.Load().(*pathHandler).ServeHTTP(w, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServeHTTP makes it an http.Handler
|
||||||
|
func (h *pathHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if exactHandler, ok := h.pathToHandler[r.URL.Path]; ok {
|
||||||
|
exactHandler.ServeHTTP(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, prefixHandler := range h.prefixHandlers {
|
||||||
|
if strings.HasPrefix(r.URL.Path, prefixHandler.prefix) {
|
||||||
|
prefixHandler.handler.ServeHTTP(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h.notFoundHandler.ServeHTTP(w, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// byPrefixPriority sorts url prefixes by the order in which they should be tested by the mux
|
||||||
|
// this has to be sorted by most slashes then by length so that we can iterate straight
|
||||||
|
// through to match the "best" one first.
|
||||||
|
type byPrefixPriority []string
|
||||||
|
|
||||||
|
func (s byPrefixPriority) Len() int { return len(s) }
|
||||||
|
func (s byPrefixPriority) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||||
|
func (s byPrefixPriority) Less(i, j int) bool {
|
||||||
|
lhsNumParts := strings.Count(s[i], "/")
|
||||||
|
rhsNumParts := strings.Count(s[j], "/")
|
||||||
|
if lhsNumParts != rhsNumParts {
|
||||||
|
return lhsNumParts < rhsNumParts
|
||||||
|
}
|
||||||
|
|
||||||
|
lhsLen := len(s[i])
|
||||||
|
rhsLen := len(s[j])
|
||||||
|
if lhsLen != rhsLen {
|
||||||
|
return lhsLen < rhsLen
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Compare(s[i], s[j]) < 0
|
||||||
}
|
}
|
||||||
|
@ -67,3 +67,69 @@ func TestUnregisterHandlers(t *testing.T) {
|
|||||||
assert.Equal(t, second, 1)
|
assert.Equal(t, second, 1)
|
||||||
assert.Equal(t, resp.StatusCode, http.StatusOK)
|
assert.Equal(t, resp.StatusCode, http.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPrefixHandlers(t *testing.T) {
|
||||||
|
c := NewPathRecorderMux()
|
||||||
|
s := httptest.NewServer(c)
|
||||||
|
defer s.Close()
|
||||||
|
|
||||||
|
secretPrefixCount := 0
|
||||||
|
c.UnlistedHandlePrefix("/secretPrefix/", http.HandlerFunc(func(http.ResponseWriter, *http.Request) {
|
||||||
|
secretPrefixCount = secretPrefixCount + 1
|
||||||
|
}))
|
||||||
|
publicPrefixCount := 0
|
||||||
|
c.HandlePrefix("/publicPrefix/", http.HandlerFunc(func(http.ResponseWriter, *http.Request) {
|
||||||
|
publicPrefixCount = publicPrefixCount + 1
|
||||||
|
}))
|
||||||
|
precisePrefixCount := 0
|
||||||
|
c.HandlePrefix("/publicPrefix/but-more-precise/", http.HandlerFunc(func(http.ResponseWriter, *http.Request) {
|
||||||
|
precisePrefixCount = precisePrefixCount + 1
|
||||||
|
}))
|
||||||
|
exactMatchCount := 0
|
||||||
|
c.Handle("/publicPrefix/exactmatch", http.HandlerFunc(func(http.ResponseWriter, *http.Request) {
|
||||||
|
exactMatchCount = exactMatchCount + 1
|
||||||
|
}))
|
||||||
|
slashMatchCount := 0
|
||||||
|
c.Handle("/otherPublic/exactmatchslash/", http.HandlerFunc(func(http.ResponseWriter, *http.Request) {
|
||||||
|
slashMatchCount = slashMatchCount + 1
|
||||||
|
}))
|
||||||
|
fallThroughCount := 0
|
||||||
|
c.NotFoundHandler(http.HandlerFunc(func(http.ResponseWriter, *http.Request) {
|
||||||
|
fallThroughCount = fallThroughCount + 1
|
||||||
|
}))
|
||||||
|
|
||||||
|
assert.NotContains(t, c.ListedPaths(), "/secretPrefix/")
|
||||||
|
assert.Contains(t, c.ListedPaths(), "/publicPrefix/")
|
||||||
|
|
||||||
|
resp, _ := http.Get(s.URL + "/fallthrough")
|
||||||
|
assert.Equal(t, 1, fallThroughCount)
|
||||||
|
assert.Equal(t, resp.StatusCode, http.StatusOK)
|
||||||
|
resp, _ = http.Get(s.URL + "/publicPrefix")
|
||||||
|
assert.Equal(t, 2, fallThroughCount)
|
||||||
|
assert.Equal(t, resp.StatusCode, http.StatusOK)
|
||||||
|
|
||||||
|
http.Get(s.URL + "/publicPrefix/")
|
||||||
|
assert.Equal(t, 1, publicPrefixCount)
|
||||||
|
http.Get(s.URL + "/publicPrefix/something")
|
||||||
|
assert.Equal(t, 2, publicPrefixCount)
|
||||||
|
http.Get(s.URL + "/publicPrefix/but-more-precise")
|
||||||
|
assert.Equal(t, 3, publicPrefixCount)
|
||||||
|
http.Get(s.URL + "/publicPrefix/but-more-precise/")
|
||||||
|
assert.Equal(t, 1, precisePrefixCount)
|
||||||
|
http.Get(s.URL + "/publicPrefix/but-more-precise/more-stuff")
|
||||||
|
assert.Equal(t, 2, precisePrefixCount)
|
||||||
|
|
||||||
|
http.Get(s.URL + "/publicPrefix/exactmatch")
|
||||||
|
assert.Equal(t, 1, exactMatchCount)
|
||||||
|
http.Get(s.URL + "/publicPrefix/exactmatch/")
|
||||||
|
assert.Equal(t, 4, publicPrefixCount)
|
||||||
|
http.Get(s.URL + "/otherPublic/exactmatchslash")
|
||||||
|
assert.Equal(t, 3, fallThroughCount)
|
||||||
|
http.Get(s.URL + "/otherPublic/exactmatchslash/")
|
||||||
|
assert.Equal(t, 1, slashMatchCount)
|
||||||
|
|
||||||
|
http.Get(s.URL + "/secretPrefix/")
|
||||||
|
assert.Equal(t, 1, secretPrefixCount)
|
||||||
|
http.Get(s.URL + "/secretPrefix/something")
|
||||||
|
assert.Equal(t, 2, secretPrefixCount)
|
||||||
|
}
|
||||||
|
@ -50,20 +50,20 @@ func (p ListedPathProviders) ListedPaths() []string {
|
|||||||
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(pathProvider ListedPathProvider, mux *mux.PathRecorderMux, delegate http.Handler) {
|
func (i Index) Install(pathProvider ListedPathProvider, mux *mux.PathRecorderMux) {
|
||||||
mux.UnlistedHandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
handler := IndexLister{StatusCode: http.StatusOK, PathProvider: pathProvider}
|
||||||
status := http.StatusOK
|
|
||||||
if r.URL.Path != "/" && r.URL.Path != "/index.html" {
|
mux.UnlistedHandle("/", handler)
|
||||||
// Since "/" matches all paths, handleIndex is called for all paths for which there is no handler api.Registry.
|
mux.UnlistedHandle("/index.html", handler)
|
||||||
// if we have a delegate, we should call to it and simply return
|
|
||||||
if delegate != nil {
|
|
||||||
delegate.ServeHTTP(w, r)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we have no delegate, we want to return a 404 status with a list of all valid paths, incase of an invalid URL request.
|
// IndexLister lists the available indexes with the status code provided
|
||||||
status = http.StatusNotFound
|
type IndexLister struct {
|
||||||
|
StatusCode int
|
||||||
|
PathProvider ListedPathProvider
|
||||||
}
|
}
|
||||||
responsewriters.WriteRawJSON(status, metav1.RootPaths{Paths: pathProvider.ListedPaths()}, w)
|
|
||||||
})
|
// ServeHTTP serves the available paths.
|
||||||
|
func (i IndexLister) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
responsewriters.WriteRawJSON(i.StatusCode, metav1.RootPaths{Paths: i.PathProvider.ListedPaths()}, w)
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||||||
package routes
|
package routes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/http"
|
||||||
"net/http/pprof"
|
"net/http/pprof"
|
||||||
|
|
||||||
"k8s.io/apiserver/pkg/server/mux"
|
"k8s.io/apiserver/pkg/server/mux"
|
||||||
@ -27,7 +28,8 @@ 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.PathRecorderMux) {
|
func (d Profiling) Install(c *mux.PathRecorderMux) {
|
||||||
c.UnlistedHandleFunc("/debug/pprof/", pprof.Index)
|
c.UnlistedHandle("/debug/pprof", http.HandlerFunc(pprof.Index))
|
||||||
|
c.UnlistedHandlePrefix("/debug/pprof/", http.HandlerFunc(pprof.Index))
|
||||||
c.UnlistedHandleFunc("/debug/pprof/profile", pprof.Profile)
|
c.UnlistedHandleFunc("/debug/pprof/profile", pprof.Profile)
|
||||||
c.UnlistedHandleFunc("/debug/pprof/symbol", pprof.Symbol)
|
c.UnlistedHandleFunc("/debug/pprof/symbol", pprof.Symbol)
|
||||||
c.UnlistedHandleFunc("/debug/pprof/trace", pprof.Trace)
|
c.UnlistedHandleFunc("/debug/pprof/trace", pprof.Trace)
|
||||||
|
@ -36,5 +36,5 @@ func (l SwaggerUI) Install(c *mux.PathRecorderMux) {
|
|||||||
Prefix: "third_party/swagger-ui",
|
Prefix: "third_party/swagger-ui",
|
||||||
})
|
})
|
||||||
prefix := "/swagger-ui/"
|
prefix := "/swagger-ui/"
|
||||||
c.Handle(prefix, http.StripPrefix(prefix, fileServer))
|
c.HandlePrefix(prefix, http.StripPrefix(prefix, fileServer))
|
||||||
}
|
}
|
||||||
|
@ -173,12 +173,11 @@ func (c completedConfig) NewWithDelegate(delegationTarget genericapiserver.Deleg
|
|||||||
apisHandler := &apisHandler{
|
apisHandler := &apisHandler{
|
||||||
codecs: Codecs,
|
codecs: Codecs,
|
||||||
lister: s.lister,
|
lister: s.lister,
|
||||||
delegate: s.GenericAPIServer.FallThroughHandler,
|
|
||||||
serviceLister: s.serviceLister,
|
serviceLister: s.serviceLister,
|
||||||
endpointsLister: s.endpointsLister,
|
endpointsLister: s.endpointsLister,
|
||||||
}
|
}
|
||||||
s.GenericAPIServer.HandlerContainer.Handle("/apis", apisHandler)
|
s.GenericAPIServer.FallThroughHandler.Handle("/apis", apisHandler)
|
||||||
s.GenericAPIServer.HandlerContainer.Handle("/apis/", apisHandler)
|
s.GenericAPIServer.FallThroughHandler.UnlistedHandle("/apis/", apisHandler)
|
||||||
|
|
||||||
apiserviceRegistrationController := NewAPIServiceRegistrationController(informerFactory.Apiregistration().InternalVersion().APIServices(), kubeInformers.Core().V1().Services(), s)
|
apiserviceRegistrationController := NewAPIServiceRegistrationController(informerFactory.Apiregistration().InternalVersion().APIServices(), kubeInformers.Core().V1().Services(), s)
|
||||||
|
|
||||||
|
@ -40,8 +40,6 @@ type apisHandler struct {
|
|||||||
|
|
||||||
serviceLister v1listers.ServiceLister
|
serviceLister v1listers.ServiceLister
|
||||||
endpointsLister v1listers.EndpointsLister
|
endpointsLister v1listers.EndpointsLister
|
||||||
|
|
||||||
delegate http.Handler
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var discoveryGroup = metav1.APIGroup{
|
var discoveryGroup = metav1.APIGroup{
|
||||||
@ -59,12 +57,6 @@ var discoveryGroup = metav1.APIGroup{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *apisHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
func (r *apisHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
// don't handle URLs that aren't /apis
|
|
||||||
if req.URL.Path != "/apis" && req.URL.Path != "/apis/" {
|
|
||||||
r.delegate.ServeHTTP(w, req)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
discoveryGroupList := &metav1.APIGroupList{
|
discoveryGroupList := &metav1.APIGroupList{
|
||||||
// always add OUR api group to the list first. Since we'll never have a registered APIService for it
|
// always add OUR api group to the list first. Since we'll never have a registered APIService for it
|
||||||
// and since this is the crux of the API, having this first will give our names priority. It's good to be king.
|
// and since this is the crux of the API, having this first will give our names priority. It's good to be king.
|
||||||
@ -158,12 +150,6 @@ type apiGroupHandler struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *apiGroupHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
func (r *apiGroupHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
// don't handle URLs that aren't /apis/<groupName>
|
|
||||||
if req.URL.Path != "/apis/"+r.groupName && req.URL.Path != "/apis/"+r.groupName+"/" {
|
|
||||||
r.delegate.ServeHTTP(w, req)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
apiServices, err := r.lister.List(labels.Everything())
|
apiServices, err := r.lister.List(labels.Everything())
|
||||||
if statusErr, ok := err.(*apierrors.StatusError); ok && err != nil {
|
if statusErr, ok := err.(*apierrors.StatusError); ok && err != nil {
|
||||||
responsewriters.WriteRawJSON(int(statusErr.Status().Code), statusErr.Status(), w)
|
responsewriters.WriteRawJSON(int(statusErr.Status().Code), statusErr.Status(), w)
|
||||||
|
@ -35,64 +35,6 @@ import (
|
|||||||
listers "k8s.io/kube-aggregator/pkg/client/listers/apiregistration/internalversion"
|
listers "k8s.io/kube-aggregator/pkg/client/listers/apiregistration/internalversion"
|
||||||
)
|
)
|
||||||
|
|
||||||
type delegationHTTPHandler struct {
|
|
||||||
called bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *delegationHTTPHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
||||||
d.called = true
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAPIsDelegation(t *testing.T) {
|
|
||||||
indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})
|
|
||||||
delegate := &delegationHTTPHandler{}
|
|
||||||
handler := &apisHandler{
|
|
||||||
codecs: Codecs,
|
|
||||||
lister: listers.NewAPIServiceLister(indexer),
|
|
||||||
delegate: delegate,
|
|
||||||
}
|
|
||||||
|
|
||||||
server := httptest.NewServer(handler)
|
|
||||||
defer server.Close()
|
|
||||||
|
|
||||||
pathToDelegation := map[string]bool{
|
|
||||||
"/": true,
|
|
||||||
"/apis": false,
|
|
||||||
"/apis/": false,
|
|
||||||
"/apis/" + apiregistration.GroupName: true,
|
|
||||||
"/apis/" + apiregistration.GroupName + "/": true,
|
|
||||||
"/apis/" + apiregistration.GroupName + "/anything": true,
|
|
||||||
"/apis/" + apiregistration.GroupName + "/anything/again": true,
|
|
||||||
"/apis/something": true,
|
|
||||||
"/apis/something/nested": true,
|
|
||||||
"/apis/something/nested/deeper": true,
|
|
||||||
"/api": true,
|
|
||||||
"/api/v1": true,
|
|
||||||
"/version": true,
|
|
||||||
}
|
|
||||||
|
|
||||||
for path, expectedDelegation := range pathToDelegation {
|
|
||||||
delegate.called = false
|
|
||||||
|
|
||||||
resp, err := http.Get(server.URL + path)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("%s: %v", path, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
|
||||||
bytes, _ := httputil.DumpResponse(resp, true)
|
|
||||||
t.Log(string(bytes))
|
|
||||||
t.Errorf("%s: %v", path, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if e, a := expectedDelegation, delegate.called; e != a {
|
|
||||||
t.Errorf("%s: expected %v, got %v", path, e, a)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAPIs(t *testing.T) {
|
func TestAPIs(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
@ -269,13 +211,11 @@ func TestAPIs(t *testing.T) {
|
|||||||
indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})
|
indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})
|
||||||
serviceIndexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})
|
serviceIndexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})
|
||||||
endpointsIndexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})
|
endpointsIndexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})
|
||||||
delegate := &delegationHTTPHandler{}
|
|
||||||
handler := &apisHandler{
|
handler := &apisHandler{
|
||||||
codecs: Codecs,
|
codecs: Codecs,
|
||||||
serviceLister: v1listers.NewServiceLister(serviceIndexer),
|
serviceLister: v1listers.NewServiceLister(serviceIndexer),
|
||||||
endpointsLister: v1listers.NewEndpointsLister(endpointsIndexer),
|
endpointsLister: v1listers.NewEndpointsLister(endpointsIndexer),
|
||||||
lister: listers.NewAPIServiceLister(indexer),
|
lister: listers.NewAPIServiceLister(indexer),
|
||||||
delegate: delegate,
|
|
||||||
}
|
}
|
||||||
for _, o := range tc.apiservices {
|
for _, o := range tc.apiservices {
|
||||||
indexer.Add(o)
|
indexer.Add(o)
|
||||||
|
@ -73,6 +73,10 @@ func (r *proxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||||||
}
|
}
|
||||||
handlingInfo := value.(proxyHandlingInfo)
|
handlingInfo := value.(proxyHandlingInfo)
|
||||||
if handlingInfo.local {
|
if handlingInfo.local {
|
||||||
|
if r.localDelegate == nil {
|
||||||
|
http.Error(w, "", http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
r.localDelegate.ServeHTTP(w, req)
|
r.localDelegate.ServeHTTP(w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user