mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 19:56:01 +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
|
||||
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/v1/namespaces 200
|
||||
if kube::test::if_supports_resource "${metrics}" ; then
|
||||
@ -2962,7 +2962,7 @@ runTests() {
|
||||
|
||||
# Custom paths let you see everything.
|
||||
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
|
||||
check-curl-proxy-code /custom/metrics 200
|
||||
fi
|
||||
|
@ -28,7 +28,9 @@ const dashboardPath = "/api/v1/namespaces/kube-system/services/kubernetes-dashbo
|
||||
type UIRedirect struct{}
|
||||
|
||||
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)
|
||||
})
|
||||
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)
|
||||
|
||||
@ -500,15 +508,9 @@ func DefaultBuildHandlerChain(apiHandler http.Handler, c *Config) http.Handler {
|
||||
return handler
|
||||
}
|
||||
|
||||
func installAPI(s *GenericAPIServer, c *Config, delegate http.Handler) {
|
||||
switch {
|
||||
case c.EnableIndex:
|
||||
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)
|
||||
func installAPI(s *GenericAPIServer, c *Config) {
|
||||
if c.EnableIndex {
|
||||
routes.Index{}.Install(s.listedPathProvider, c.FallThroughHandler)
|
||||
}
|
||||
if c.SwaggerConfig != nil && c.EnableSwaggerUI {
|
||||
routes.SwaggerUI{}.Install(s.FallThroughHandler)
|
||||
|
@ -192,7 +192,7 @@ type emptyDelegate struct {
|
||||
}
|
||||
|
||||
func (s emptyDelegate) UnprotectedHandler() http.Handler {
|
||||
return http.NotFoundHandler()
|
||||
return nil
|
||||
}
|
||||
func (s emptyDelegate) PostStartHooks() 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/schema:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/endpoints/handlers/responsewriters:go_default_library",
|
||||
],
|
||||
)
|
||||
|
@ -21,18 +21,25 @@ import (
|
||||
"net/http"
|
||||
"runtime/debug"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
)
|
||||
|
||||
// PathRecorderMux wraps a mux object and records the registered exposedPaths.
|
||||
type PathRecorderMux struct {
|
||||
lock sync.Mutex
|
||||
pathToHandler map[string]http.Handler
|
||||
lock sync.Mutex
|
||||
notFoundHandler 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
|
||||
|
||||
// exposedPaths is the list of paths that should be shown at /
|
||||
@ -43,16 +50,38 @@ type PathRecorderMux struct {
|
||||
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.
|
||||
func NewPathRecorderMux() *PathRecorderMux {
|
||||
ret := &PathRecorderMux{
|
||||
pathToHandler: map[string]http.Handler{},
|
||||
mux: atomic.Value{},
|
||||
exposedPaths: []string{},
|
||||
pathStacks: map[string]string{},
|
||||
pathToHandler: map[string]http.Handler{},
|
||||
prefixToHandler: map[string]http.Handler{},
|
||||
mux: atomic.Value{},
|
||||
exposedPaths: []string{},
|
||||
pathStacks: map[string]string{},
|
||||
}
|
||||
|
||||
ret.mux.Store(http.NewServeMux())
|
||||
ret.mux.Store(&pathHandler{notFoundHandler: http.NotFoundHandler()})
|
||||
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
|
||||
// not be consistent
|
||||
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 {
|
||||
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.
|
||||
@ -88,6 +143,7 @@ func (m *PathRecorderMux) Unregister(path string) {
|
||||
defer m.lock.Unlock()
|
||||
|
||||
delete(m.pathToHandler, path)
|
||||
delete(m.prefixToHandler, path)
|
||||
delete(m.pathStacks, path)
|
||||
for i := range m.exposedPaths {
|
||||
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.
|
||||
// If a handler already exists for pattern, Handle panics.
|
||||
func (m *PathRecorderMux) HandleFunc(path string, handler func(http.ResponseWriter, *http.Request)) {
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
m.trackCallers(path)
|
||||
|
||||
m.exposedPaths = append(m.exposedPaths, path)
|
||||
m.pathToHandler[path] = http.HandlerFunc(handler)
|
||||
m.refreshMuxLocked()
|
||||
m.Handle(path, http.HandlerFunc(handler))
|
||||
}
|
||||
|
||||
// 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.
|
||||
// If a handler already exists for pattern, Handle panics.
|
||||
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()
|
||||
defer m.lock.Unlock()
|
||||
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()
|
||||
}
|
||||
|
||||
// ServeHTTP makes it an http.Handler
|
||||
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, 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{}
|
||||
|
||||
// Install adds the Index webservice to the given mux.
|
||||
func (i Index) Install(pathProvider ListedPathProvider, mux *mux.PathRecorderMux, delegate http.Handler) {
|
||||
mux.UnlistedHandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
status := http.StatusOK
|
||||
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.
|
||||
// if we have a delegate, we should call to it and simply return
|
||||
if delegate != nil {
|
||||
delegate.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
func (i Index) Install(pathProvider ListedPathProvider, mux *mux.PathRecorderMux) {
|
||||
handler := IndexLister{StatusCode: http.StatusOK, PathProvider: pathProvider}
|
||||
|
||||
// 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.
|
||||
status = http.StatusNotFound
|
||||
}
|
||||
responsewriters.WriteRawJSON(status, metav1.RootPaths{Paths: pathProvider.ListedPaths()}, w)
|
||||
})
|
||||
mux.UnlistedHandle("/", handler)
|
||||
mux.UnlistedHandle("/index.html", handler)
|
||||
}
|
||||
|
||||
// IndexLister lists the available indexes with the status code provided
|
||||
type IndexLister struct {
|
||||
StatusCode int
|
||||
PathProvider ListedPathProvider
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/pprof"
|
||||
|
||||
"k8s.io/apiserver/pkg/server/mux"
|
||||
@ -27,7 +28,8 @@ type Profiling struct{}
|
||||
|
||||
// Install adds the Profiling webservice to the given mux.
|
||||
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/symbol", pprof.Symbol)
|
||||
c.UnlistedHandleFunc("/debug/pprof/trace", pprof.Trace)
|
||||
|
@ -36,5 +36,5 @@ func (l SwaggerUI) Install(c *mux.PathRecorderMux) {
|
||||
Prefix: "third_party/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{
|
||||
codecs: Codecs,
|
||||
lister: s.lister,
|
||||
delegate: s.GenericAPIServer.FallThroughHandler,
|
||||
serviceLister: s.serviceLister,
|
||||
endpointsLister: s.endpointsLister,
|
||||
}
|
||||
s.GenericAPIServer.HandlerContainer.Handle("/apis", apisHandler)
|
||||
s.GenericAPIServer.HandlerContainer.Handle("/apis/", apisHandler)
|
||||
s.GenericAPIServer.FallThroughHandler.Handle("/apis", apisHandler)
|
||||
s.GenericAPIServer.FallThroughHandler.UnlistedHandle("/apis/", apisHandler)
|
||||
|
||||
apiserviceRegistrationController := NewAPIServiceRegistrationController(informerFactory.Apiregistration().InternalVersion().APIServices(), kubeInformers.Core().V1().Services(), s)
|
||||
|
||||
|
@ -40,8 +40,6 @@ type apisHandler struct {
|
||||
|
||||
serviceLister v1listers.ServiceLister
|
||||
endpointsLister v1listers.EndpointsLister
|
||||
|
||||
delegate http.Handler
|
||||
}
|
||||
|
||||
var discoveryGroup = metav1.APIGroup{
|
||||
@ -59,12 +57,6 @@ var discoveryGroup = metav1.APIGroup{
|
||||
}
|
||||
|
||||
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{
|
||||
// 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.
|
||||
@ -158,12 +150,6 @@ type apiGroupHandler struct {
|
||||
}
|
||||
|
||||
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())
|
||||
if statusErr, ok := err.(*apierrors.StatusError); ok && err != nil {
|
||||
responsewriters.WriteRawJSON(int(statusErr.Status().Code), statusErr.Status(), w)
|
||||
|
@ -35,64 +35,6 @@ import (
|
||||
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) {
|
||||
tests := []struct {
|
||||
name string
|
||||
@ -269,13 +211,11 @@ func TestAPIs(t *testing.T) {
|
||||
indexer := 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})
|
||||
delegate := &delegationHTTPHandler{}
|
||||
handler := &apisHandler{
|
||||
codecs: Codecs,
|
||||
serviceLister: v1listers.NewServiceLister(serviceIndexer),
|
||||
endpointsLister: v1listers.NewEndpointsLister(endpointsIndexer),
|
||||
lister: listers.NewAPIServiceLister(indexer),
|
||||
delegate: delegate,
|
||||
}
|
||||
for _, o := range tc.apiservices {
|
||||
indexer.Add(o)
|
||||
|
@ -73,6 +73,10 @@ func (r *proxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
}
|
||||
handlingInfo := value.(proxyHandlingInfo)
|
||||
if handlingInfo.local {
|
||||
if r.localDelegate == nil {
|
||||
http.Error(w, "", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
r.localDelegate.ServeHTTP(w, req)
|
||||
return
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user