git subrepo clone (merge) https://github.com/kubernetes-incubator/metrics-server.git metrics-server
subrepo:
subdir: "metrics-server"
merged: "92d8412"
upstream:
origin: "https://github.com/kubernetes-incubator/metrics-server.git"
branch: "master"
commit: "92d8412"
git-subrepo:
version: "0.4.0"
origin: "???"
commit: "???"
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/server/handler.go b/metrics-server/vendor/k8s.io/apiserver/pkg/server/handler.go
new file mode 100644
index 0000000..e4e7d9a
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/server/handler.go
@@ -0,0 +1,190 @@
+/*
+Copyright 2017 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package server
+
+import (
+ "bytes"
+ "fmt"
+ "net/http"
+ rt "runtime"
+ "sort"
+ "strings"
+
+ "github.com/emicklei/go-restful"
+ "github.com/golang/glog"
+
+ apierrors "k8s.io/apimachinery/pkg/api/errors"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ "k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
+ "k8s.io/apiserver/pkg/server/mux"
+)
+
+// APIServerHandlers holds the different http.Handlers used by the API server.
+// This includes the full handler chain, the director (which chooses between gorestful and nonGoRestful,
+// the gorestful handler (used for the API) which falls through to the nonGoRestful handler on unregistered paths,
+// and the nonGoRestful handler (which can contain a fallthrough of its own)
+// FullHandlerChain -> Director -> {GoRestfulContainer,NonGoRestfulMux} based on inspection of registered web services
+type APIServerHandler struct {
+ // FullHandlerChain is the one that is eventually served with. It should include the full filter
+ // chain and then call the Director.
+ FullHandlerChain http.Handler
+ // The registered APIs. InstallAPIs uses this. Other servers probably shouldn't access this directly.
+ GoRestfulContainer *restful.Container
+ // NonGoRestfulMux is the final HTTP handler in the chain.
+ // It comes after all filters and the API handling
+ // This is where other servers can attach handler to various parts of the chain.
+ NonGoRestfulMux *mux.PathRecorderMux
+
+ // Director is here so that we can properly handle fall through and proxy cases.
+ // This looks a bit bonkers, but here's what's happening. We need to have /apis handling registered in gorestful in order to have
+ // swagger generated for compatibility. Doing that with `/apis` as a webservice, means that it forcibly 404s (no defaulting allowed)
+ // all requests which are not /apis or /apis/. We need those calls to fall through behind goresful for proper delegation. Trying to
+ // register for a pattern which includes everything behind it doesn't work because gorestful negotiates for verbs and content encoding
+ // and all those things go crazy when gorestful really just needs to pass through. In addition, openapi enforces unique verb constraints
+ // which we don't fit into and it still muddies up swagger. Trying to switch the webservices into a route doesn't work because the
+ // containing webservice faces all the same problems listed above.
+ // This leads to the crazy thing done here. Our mux does what we need, so we'll place it in front of gorestful. It will introspect to
+ // decide if the route is likely to be handled by goresful and route there if needed. Otherwise, it goes to PostGoRestful mux in
+ // order to handle "normal" paths and delegation. Hopefully no API consumers will ever have to deal with this level of detail. I think
+ // we should consider completely removing gorestful.
+ // Other servers should only use this opaquely to delegate to an API server.
+ Director http.Handler
+}
+
+// HandlerChainBuilderFn is used to wrap the GoRestfulContainer handler using the provided handler chain.
+// It is normally used to apply filtering like authentication and authorization
+type HandlerChainBuilderFn func(apiHandler http.Handler) http.Handler
+
+func NewAPIServerHandler(name string, s runtime.NegotiatedSerializer, handlerChainBuilder HandlerChainBuilderFn, notFoundHandler http.Handler) *APIServerHandler {
+ nonGoRestfulMux := mux.NewPathRecorderMux(name)
+ if notFoundHandler != nil {
+ nonGoRestfulMux.NotFoundHandler(notFoundHandler)
+ }
+
+ gorestfulContainer := restful.NewContainer()
+ gorestfulContainer.ServeMux = http.NewServeMux()
+ gorestfulContainer.Router(restful.CurlyRouter{}) // e.g. for proxy/{kind}/{name}/{*}
+ gorestfulContainer.RecoverHandler(func(panicReason interface{}, httpWriter http.ResponseWriter) {
+ logStackOnRecover(s, panicReason, httpWriter)
+ })
+ gorestfulContainer.ServiceErrorHandler(func(serviceErr restful.ServiceError, request *restful.Request, response *restful.Response) {
+ serviceErrorHandler(s, serviceErr, request, response)
+ })
+
+ director := director{
+ name: name,
+ goRestfulContainer: gorestfulContainer,
+ nonGoRestfulMux: nonGoRestfulMux,
+ }
+
+ return &APIServerHandler{
+ FullHandlerChain: handlerChainBuilder(director),
+ GoRestfulContainer: gorestfulContainer,
+ NonGoRestfulMux: nonGoRestfulMux,
+ Director: director,
+ }
+}
+
+// ListedPaths returns the paths that should be shown under /
+func (a *APIServerHandler) ListedPaths() []string {
+ var handledPaths []string
+ // Extract the paths handled using restful.WebService
+ for _, ws := range a.GoRestfulContainer.RegisteredWebServices() {
+ handledPaths = append(handledPaths, ws.RootPath())
+ }
+ handledPaths = append(handledPaths, a.NonGoRestfulMux.ListedPaths()...)
+ sort.Strings(handledPaths)
+
+ return handledPaths
+}
+
+type director struct {
+ name string
+ goRestfulContainer *restful.Container
+ nonGoRestfulMux *mux.PathRecorderMux
+}
+
+func (d director) ServeHTTP(w http.ResponseWriter, req *http.Request) {
+ path := req.URL.Path
+
+ // check to see if our webservices want to claim this path
+ for _, ws := range d.goRestfulContainer.RegisteredWebServices() {
+ switch {
+ case ws.RootPath() == "/apis":
+ // if we are exactly /apis or /apis/, then we need special handling in loop.
+ // normally these are passed to the nonGoRestfulMux, but if discovery is enabled, it will go directly.
+ // We can't rely on a prefix match since /apis matches everything (see the big comment on Director above)
+ if path == "/apis" || path == "/apis/" {
+ glog.V(5).Infof("%v: %v %q satisfied by gorestful with webservice %v", d.name, req.Method, path, ws.RootPath())
+ // don't use servemux here because gorestful servemuxes get messed up when removing webservices
+ // TODO fix gorestful, remove TPRs, or stop using gorestful
+ d.goRestfulContainer.Dispatch(w, req)
+ return
+ }
+
+ case strings.HasPrefix(path, ws.RootPath()):
+ // ensure an exact match or a path boundary match
+ if len(path) == len(ws.RootPath()) || path[len(ws.RootPath())] == '/' {
+ glog.V(5).Infof("%v: %v %q satisfied by gorestful with webservice %v", d.name, req.Method, path, ws.RootPath())
+ // don't use servemux here because gorestful servemuxes get messed up when removing webservices
+ // TODO fix gorestful, remove TPRs, or stop using gorestful
+ d.goRestfulContainer.Dispatch(w, req)
+ return
+ }
+ }
+ }
+
+ // if we didn't find a match, then we just skip gorestful altogether
+ glog.V(5).Infof("%v: %v %q satisfied by nonGoRestful", d.name, req.Method, path)
+ d.nonGoRestfulMux.ServeHTTP(w, req)
+}
+
+//TODO: Unify with RecoverPanics?
+func logStackOnRecover(s runtime.NegotiatedSerializer, panicReason interface{}, w http.ResponseWriter) {
+ var buffer bytes.Buffer
+ buffer.WriteString(fmt.Sprintf("recover from panic situation: - %v\r\n", panicReason))
+ for i := 2; ; i++ {
+ _, file, line, ok := rt.Caller(i)
+ if !ok {
+ break
+ }
+ buffer.WriteString(fmt.Sprintf(" %s:%d\r\n", file, line))
+ }
+ glog.Errorln(buffer.String())
+
+ headers := http.Header{}
+ if ct := w.Header().Get("Content-Type"); len(ct) > 0 {
+ headers.Set("Accept", ct)
+ }
+ responsewriters.ErrorNegotiated(apierrors.NewGenericServerResponse(http.StatusInternalServerError, "", schema.GroupResource{}, "", "", 0, false), s, schema.GroupVersion{}, w, &http.Request{Header: headers})
+}
+
+func serviceErrorHandler(s runtime.NegotiatedSerializer, serviceErr restful.ServiceError, request *restful.Request, resp *restful.Response) {
+ responsewriters.ErrorNegotiated(
+ apierrors.NewGenericServerResponse(serviceErr.Code, "", schema.GroupResource{}, "", serviceErr.Message, 0, false),
+ s,
+ schema.GroupVersion{},
+ resp,
+ request.Request,
+ )
+}
+
+// ServeHTTP makes it an http.Handler
+func (a *APIServerHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ a.FullHandlerChain.ServeHTTP(w, r)
+}