From ce942d19c378ecd335e7e158e30cdc184f9d6184 Mon Sep 17 00:00:00 2001 From: "Dr. Stefan Schimanski" Date: Wed, 17 May 2017 17:23:23 +0200 Subject: [PATCH] audit: wire through non-nil context everywhere --- pkg/master/master_openapi_test.go | 3 +- pkg/master/master_test.go | 2 +- pkg/master/thirdparty/thirdparty.go | 2 +- .../apiserver/pkg/endpoints/apiserver_test.go | 2 +- .../pkg/endpoints/discovery/group.go | 22 +++++++++----- .../pkg/endpoints/discovery/legacy.go | 30 ++++++++++++------- .../apiserver/pkg/endpoints/discovery/root.go | 24 ++++++++++----- .../pkg/endpoints/discovery/root_test.go | 11 ++++--- .../pkg/endpoints/discovery/version.go | 18 ++++++++--- .../apiserver/pkg/endpoints/groupversion.go | 4 +-- .../apiserver/pkg/endpoints/handlers/proxy.go | 4 +-- .../src/k8s.io/apiserver/pkg/server/config.go | 2 +- .../apiserver/pkg/server/genericapiserver.go | 4 +-- .../k8s.io/apiserver/pkg/server/handler.go | 17 +++++++---- .../pkg/apiserver/apiserver.go | 10 ++++--- .../pkg/apiserver/handler_apis.go | 24 ++++++++++++--- .../pkg/apiserver/handler_apis_test.go | 19 ++++++++---- .../pkg/apiserver/apiserver.go | 2 +- .../customresource_discovery_controller.go | 9 ++++-- 19 files changed, 143 insertions(+), 66 deletions(-) diff --git a/pkg/master/master_openapi_test.go b/pkg/master/master_openapi_test.go index 68ca925559e..39ddb686c53 100644 --- a/pkg/master/master_openapi_test.go +++ b/pkg/master/master_openapi_test.go @@ -27,6 +27,7 @@ import ( "net/http/httptest" "testing" + apirequest "k8s.io/apiserver/pkg/endpoints/request" genericapiserver "k8s.io/apiserver/pkg/server" "k8s.io/kubernetes/pkg/api" openapigen "k8s.io/kubernetes/pkg/generated/openapi" @@ -59,7 +60,7 @@ func TestValidOpenAPISpec(t *testing.T) { } // make sure swagger.json is not registered before calling PrepareRun. - server := httptest.NewServer(master.GenericAPIServer.Handler.GoRestfulContainer.ServeMux) + server := httptest.NewServer(apirequest.WithRequestContext(master.GenericAPIServer.Handler.GoRestfulContainer.ServeMux, master.GenericAPIServer.RequestContextMapper())) defer server.Close() resp, err := http.Get(server.URL + "/swagger.json") if !assert.NoError(err) { diff --git a/pkg/master/master_test.go b/pkg/master/master_test.go index b9785890190..25d7ba010b2 100644 --- a/pkg/master/master_test.go +++ b/pkg/master/master_test.go @@ -234,7 +234,7 @@ func TestAPIVersionOfDiscoveryEndpoints(t *testing.T) { master, etcdserver, _, assert := newMaster(t) defer etcdserver.Terminate(t) - server := httptest.NewServer(master.GenericAPIServer.Handler.GoRestfulContainer.ServeMux) + server := httptest.NewServer(genericapirequest.WithRequestContext(master.GenericAPIServer.Handler.GoRestfulContainer.ServeMux, master.GenericAPIServer.RequestContextMapper())) // /api exists in release-1.1 resp, err := http.Get(server.URL + "/api") diff --git a/pkg/master/thirdparty/thirdparty.go b/pkg/master/thirdparty/thirdparty.go index c4d91a9d5bf..9a70bf81541 100644 --- a/pkg/master/thirdparty/thirdparty.go +++ b/pkg/master/thirdparty/thirdparty.go @@ -287,7 +287,7 @@ func (m *ThirdPartyResourceServer) InstallThirdPartyResource(rsrc *extensions.Th if err := thirdparty.InstallREST(m.genericAPIServer.Handler.GoRestfulContainer); err != nil { glog.Errorf("Unable to setup thirdparty api: %v", err) } - m.genericAPIServer.Handler.GoRestfulContainer.Add(discovery.NewAPIGroupHandler(api.Codecs, apiGroup).WebService()) + m.genericAPIServer.Handler.GoRestfulContainer.Add(discovery.NewAPIGroupHandler(api.Codecs, apiGroup, m.genericAPIServer.RequestContextMapper()).WebService()) m.addThirdPartyResourceStorage(path, plural.Resource, thirdparty.Storage[plural.Resource].(*thirdpartyresourcedatastore.REST), apiGroup) api.Registry.AddThirdPartyAPIGroupVersions(schema.GroupVersion{Group: group, Version: rsrc.Versions[0].Name}) diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/apiserver_test.go b/staging/src/k8s.io/apiserver/pkg/endpoints/apiserver_test.go index a37f5f03bc7..eb2abc3b035 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/apiserver_test.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/apiserver_test.go @@ -3254,7 +3254,7 @@ func (obj *UnregisteredAPIObject) GetObjectKind() schema.ObjectKind { func TestWriteJSONDecodeError(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - responsewriters.WriteObjectNegotiated(nil, codecs, newGroupVersion, w, req, http.StatusOK, &UnregisteredAPIObject{"Undecodable"}) + responsewriters.WriteObjectNegotiated(request.NewContext(), codecs, newGroupVersion, w, req, http.StatusOK, &UnregisteredAPIObject{"Undecodable"}) })) defer server.Close() // We send a 200 status code before we encode the object, so we expect OK, but there will diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/discovery/group.go b/staging/src/k8s.io/apiserver/pkg/endpoints/discovery/group.go index 6ceda0131dc..ea47a104691 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/discovery/group.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/discovery/group.go @@ -17,6 +17,7 @@ limitations under the License. package discovery import ( + "errors" "net/http" "github.com/emicklei/go-restful" @@ -26,17 +27,18 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apiserver/pkg/endpoints/handlers/negotiation" "k8s.io/apiserver/pkg/endpoints/handlers/responsewriters" + "k8s.io/apiserver/pkg/endpoints/request" ) // APIGroupHandler creates a webservice serving the supported versions, preferred version, and name // of a group. E.g., such a web service will be registered at /apis/extensions. type APIGroupHandler struct { - serializer runtime.NegotiatedSerializer - - group metav1.APIGroup + serializer runtime.NegotiatedSerializer + contextMapper request.RequestContextMapper + group metav1.APIGroup } -func NewAPIGroupHandler(serializer runtime.NegotiatedSerializer, group metav1.APIGroup) *APIGroupHandler { +func NewAPIGroupHandler(serializer runtime.NegotiatedSerializer, group metav1.APIGroup, contextMapper request.RequestContextMapper) *APIGroupHandler { if keepUnversioned(group.Name) { // Because in release 1.1, /apis/extensions returns response with empty // APIVersion, we use stripVersionNegotiatedSerializer to keep the @@ -45,8 +47,9 @@ func NewAPIGroupHandler(serializer runtime.NegotiatedSerializer, group metav1.AP } return &APIGroupHandler{ - serializer: serializer, - group: group, + serializer: serializer, + contextMapper: contextMapper, + group: group, } } @@ -70,5 +73,10 @@ func (s *APIGroupHandler) handle(req *restful.Request, resp *restful.Response) { } func (s *APIGroupHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { - responsewriters.WriteObjectNegotiated(nil, s.serializer, schema.GroupVersion{}, w, req, http.StatusOK, &s.group) + ctx, ok := s.contextMapper.Get(req) + if !ok { + responsewriters.InternalError(w, req, errors.New("no context found for request")) + return + } + responsewriters.WriteObjectNegotiated(ctx, s.serializer, schema.GroupVersion{}, w, req, http.StatusOK, &s.group) } diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/discovery/legacy.go b/staging/src/k8s.io/apiserver/pkg/endpoints/discovery/legacy.go index 65747cd047d..3a98e6320e4 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/discovery/legacy.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/discovery/legacy.go @@ -17,6 +17,7 @@ limitations under the License. package discovery import ( + "errors" "net/http" "github.com/emicklei/go-restful" @@ -27,28 +28,31 @@ import ( utilnet "k8s.io/apimachinery/pkg/util/net" "k8s.io/apiserver/pkg/endpoints/handlers/negotiation" "k8s.io/apiserver/pkg/endpoints/handlers/responsewriters" + "k8s.io/apiserver/pkg/endpoints/request" ) // legacyRootAPIHandler creates a webservice serving api group discovery. type legacyRootAPIHandler struct { // addresses is used to build cluster IPs for discovery. - addresses Addresses - apiPrefix string - serializer runtime.NegotiatedSerializer - apiVersions []string + addresses Addresses + apiPrefix string + serializer runtime.NegotiatedSerializer + apiVersions []string + contextMapper request.RequestContextMapper } -func NewLegacyRootAPIHandler(addresses Addresses, serializer runtime.NegotiatedSerializer, apiPrefix string, apiVersions []string) *legacyRootAPIHandler { +func NewLegacyRootAPIHandler(addresses Addresses, serializer runtime.NegotiatedSerializer, apiPrefix string, apiVersions []string, contextMapper request.RequestContextMapper) *legacyRootAPIHandler { // Because in release 1.1, /apis returns response with empty APIVersion, we // use stripVersionNegotiatedSerializer to keep the response backwards // compatible. serializer = stripVersionNegotiatedSerializer{serializer} return &legacyRootAPIHandler{ - addresses: addresses, - apiPrefix: apiPrefix, - serializer: serializer, - apiVersions: apiVersions, + addresses: addresses, + apiPrefix: apiPrefix, + serializer: serializer, + apiVersions: apiVersions, + contextMapper: contextMapper, } } @@ -68,11 +72,17 @@ func (s *legacyRootAPIHandler) WebService() *restful.WebService { } func (s *legacyRootAPIHandler) handle(req *restful.Request, resp *restful.Response) { + ctx, ok := s.contextMapper.Get(req.Request) + if !ok { + responsewriters.InternalError(resp.ResponseWriter, req.Request, errors.New("no context found for request")) + return + } + clientIP := utilnet.GetClientIP(req.Request) apiVersions := &metav1.APIVersions{ ServerAddressByClientCIDRs: s.addresses.ServerAddressByClientCIDRs(clientIP), Versions: s.apiVersions, } - responsewriters.WriteObjectNegotiated(nil, s.serializer, schema.GroupVersion{}, resp.ResponseWriter, req.Request, http.StatusOK, apiVersions) + responsewriters.WriteObjectNegotiated(ctx, s.serializer, schema.GroupVersion{}, resp.ResponseWriter, req.Request, http.StatusOK, apiVersions) } diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/discovery/root.go b/staging/src/k8s.io/apiserver/pkg/endpoints/discovery/root.go index a436b3b368b..c1a8ecf6504 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/discovery/root.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/discovery/root.go @@ -17,10 +17,11 @@ limitations under the License. package discovery import ( + "errors" "net/http" "sync" - "github.com/emicklei/go-restful" + restful "github.com/emicklei/go-restful" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -28,6 +29,7 @@ import ( utilnet "k8s.io/apimachinery/pkg/util/net" "k8s.io/apiserver/pkg/endpoints/handlers/negotiation" "k8s.io/apiserver/pkg/endpoints/handlers/responsewriters" + "k8s.io/apiserver/pkg/endpoints/request" ) // GroupManager is an interface that allows dynamic mutation of the existing webservice to handle @@ -46,7 +48,8 @@ type rootAPIsHandler struct { // addresses is used to build cluster IPs for discovery. addresses Addresses - serializer runtime.NegotiatedSerializer + serializer runtime.NegotiatedSerializer + contextMapper request.RequestContextMapper // Map storing information about all groups to be exposed in discovery response. // The map is from name to the group. @@ -56,16 +59,17 @@ type rootAPIsHandler struct { apiGroupNames []string } -func NewRootAPIsHandler(addresses Addresses, serializer runtime.NegotiatedSerializer) *rootAPIsHandler { +func NewRootAPIsHandler(addresses Addresses, serializer runtime.NegotiatedSerializer, contextMapper request.RequestContextMapper) *rootAPIsHandler { // Because in release 1.1, /apis returns response with empty APIVersion, we // use stripVersionNegotiatedSerializer to keep the response backwards // compatible. serializer = stripVersionNegotiatedSerializer{serializer} return &rootAPIsHandler{ - addresses: addresses, - serializer: serializer, - apiGroups: map[string]metav1.APIGroup{}, + addresses: addresses, + serializer: serializer, + apiGroups: map[string]metav1.APIGroup{}, + contextMapper: contextMapper, } } @@ -95,6 +99,12 @@ func (s *rootAPIsHandler) RemoveGroup(groupName string) { } func (s *rootAPIsHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) { + ctx, ok := s.contextMapper.Get(req) + if !ok { + responsewriters.InternalError(resp, req, errors.New("no context found for request")) + return + } + s.lock.RLock() defer s.lock.RUnlock() @@ -111,7 +121,7 @@ func (s *rootAPIsHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) groups[i].ServerAddressByClientCIDRs = serverCIDR } - responsewriters.WriteObjectNegotiated(nil, s.serializer, schema.GroupVersion{}, resp, req, http.StatusOK, &metav1.APIGroupList{Groups: groups}) + responsewriters.WriteObjectNegotiated(ctx, s.serializer, schema.GroupVersion{}, resp, req, http.StatusOK, &metav1.APIGroupList{Groups: groups}) } func (s *rootAPIsHandler) restfulHandle(req *restful.Request, resp *restful.Response) { diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/discovery/root_test.go b/staging/src/k8s.io/apiserver/pkg/endpoints/discovery/root_test.go index d5297163453..c8aeed93b27 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/discovery/root_test.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/discovery/root_test.go @@ -33,6 +33,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/serializer" utilnet "k8s.io/apimachinery/pkg/util/net" + "k8s.io/apiserver/pkg/endpoints/request" ) var ( @@ -83,9 +84,10 @@ func getGroupList(t *testing.T, server *httptest.Server) (*metav1.APIGroupList, } func TestDiscoveryAtAPIS(t *testing.T) { - handler := NewRootAPIsHandler(DefaultAddresses{DefaultAddress: "192.168.1.1"}, codecs) + mapper := request.NewRequestContextMapper() + handler := NewRootAPIsHandler(DefaultAddresses{DefaultAddress: "192.168.1.1"}, codecs, mapper) - server := httptest.NewServer(handler) + server := httptest.NewServer(request.WithRequestContext(handler, mapper)) groupList, err := getGroupList(t, server) if err != nil { t.Fatalf("unexpected error: %v", err) @@ -133,9 +135,10 @@ func TestDiscoveryAtAPIS(t *testing.T) { } func TestDiscoveryOrdering(t *testing.T) { - handler := NewRootAPIsHandler(DefaultAddresses{DefaultAddress: "192.168.1.1"}, codecs) + mapper := request.NewRequestContextMapper() + handler := NewRootAPIsHandler(DefaultAddresses{DefaultAddress: "192.168.1.1"}, codecs, mapper) - server := httptest.NewServer(handler) + server := httptest.NewServer(request.WithRequestContext(handler, mapper)) groupList, err := getGroupList(t, server) if err != nil { t.Fatalf("unexpected error: %v", err) diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/discovery/version.go b/staging/src/k8s.io/apiserver/pkg/endpoints/discovery/version.go index eac3f4f867b..a6bd99998dc 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/discovery/version.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/discovery/version.go @@ -17,15 +17,17 @@ limitations under the License. package discovery import ( + "errors" "net/http" - "github.com/emicklei/go-restful" + restful "github.com/emicklei/go-restful" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apiserver/pkg/endpoints/handlers/negotiation" "k8s.io/apiserver/pkg/endpoints/handlers/responsewriters" + "k8s.io/apiserver/pkg/endpoints/request" ) type APIResourceLister interface { @@ -41,13 +43,14 @@ func (f APIResourceListerFunc) ListAPIResources() []metav1.APIResource { // APIVersionHandler creates a webservice serving the supported resources for the version // E.g., such a web service will be registered at /apis/extensions/v1beta1. type APIVersionHandler struct { - serializer runtime.NegotiatedSerializer + serializer runtime.NegotiatedSerializer + contextMapper request.RequestContextMapper groupVersion schema.GroupVersion apiResourceLister APIResourceLister } -func NewAPIVersionHandler(serializer runtime.NegotiatedSerializer, groupVersion schema.GroupVersion, apiResourceLister APIResourceLister) *APIVersionHandler { +func NewAPIVersionHandler(serializer runtime.NegotiatedSerializer, groupVersion schema.GroupVersion, apiResourceLister APIResourceLister, contextMapper request.RequestContextMapper) *APIVersionHandler { if keepUnversioned(groupVersion.Group) { // Because in release 1.1, /apis/extensions returns response with empty // APIVersion, we use stripVersionNegotiatedSerializer to keep the @@ -59,6 +62,7 @@ func NewAPIVersionHandler(serializer runtime.NegotiatedSerializer, groupVersion serializer: serializer, groupVersion: groupVersion, apiResourceLister: apiResourceLister, + contextMapper: contextMapper, } } @@ -78,6 +82,12 @@ func (s *APIVersionHandler) handle(req *restful.Request, resp *restful.Response) } func (s *APIVersionHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { - responsewriters.WriteObjectNegotiated(nil, s.serializer, schema.GroupVersion{}, w, req, http.StatusOK, + ctx, ok := s.contextMapper.Get(req) + if !ok { + responsewriters.InternalError(w, req, errors.New("no context found for request")) + return + } + + responsewriters.WriteObjectNegotiated(ctx, s.serializer, schema.GroupVersion{}, w, req, http.StatusOK, &metav1.APIResourceList{GroupVersion: s.groupVersion.String(), APIResources: s.apiResourceLister.ListAPIResources()}) } diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/groupversion.go b/staging/src/k8s.io/apiserver/pkg/endpoints/groupversion.go index f2a072edff7..d380bdd905d 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/groupversion.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/groupversion.go @@ -100,7 +100,7 @@ func (g *APIGroupVersion) InstallREST(container *restful.Container) error { if lister == nil { lister = staticLister{apiResources} } - versionDiscoveryHandler := discovery.NewAPIVersionHandler(g.Serializer, g.GroupVersion, lister) + versionDiscoveryHandler := discovery.NewAPIVersionHandler(g.Serializer, g.GroupVersion, lister, g.Context) versionDiscoveryHandler.AddToWebService(ws) container.Add(ws) return utilerrors.NewAggregate(registrationErrors) @@ -129,7 +129,7 @@ func (g *APIGroupVersion) UpdateREST(container *restful.Container) error { if lister == nil { lister = staticLister{apiResources} } - versionDiscoveryHandler := discovery.NewAPIVersionHandler(g.Serializer, g.GroupVersion, lister) + versionDiscoveryHandler := discovery.NewAPIVersionHandler(g.Serializer, g.GroupVersion, lister, g.Context) versionDiscoveryHandler.AddToWebService(ws) return utilerrors.NewAggregate(registrationErrors) } diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/proxy.go b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/proxy.go index 586ab494c2c..d4bbc4e0537 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/proxy.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/proxy.go @@ -171,7 +171,7 @@ func (r *ProxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { // TODO convert this entire proxy to an UpgradeAwareProxy similar to // https://github.com/openshift/origin/blob/master/pkg/util/httpproxy/upgradeawareproxy.go. // That proxy needs to be modified to support multiple backends, not just 1. - if r.tryUpgrade(w, req, newReq, location, roundTripper, gv, ctx) { + if r.tryUpgrade(ctx, w, req, newReq, location, roundTripper, gv) { return } @@ -220,7 +220,7 @@ func (r *ProxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { } // tryUpgrade returns true if the request was handled. -func (r *ProxyHandler) tryUpgrade(w http.ResponseWriter, req, newReq *http.Request, location *url.URL, transport http.RoundTripper, gv schema.GroupVersion, ctx request.Context) bool { +func (r *ProxyHandler) tryUpgrade(ctx request.Context, w http.ResponseWriter, req, newReq *http.Request, location *url.URL, transport http.RoundTripper, gv schema.GroupVersion) bool { if !httpstream.IsUpgradeRequest(req) { return false } diff --git a/staging/src/k8s.io/apiserver/pkg/server/config.go b/staging/src/k8s.io/apiserver/pkg/server/config.go index 24978a2ff46..2475e50be93 100644 --- a/staging/src/k8s.io/apiserver/pkg/server/config.go +++ b/staging/src/k8s.io/apiserver/pkg/server/config.go @@ -406,7 +406,7 @@ func (c completedConfig) New(delegationTarget DelegationTarget) (*GenericAPIServ healthzChecks: c.HealthzChecks, - DiscoveryGroupManager: discovery.NewRootAPIsHandler(c.DiscoveryAddresses, c.Serializer), + DiscoveryGroupManager: discovery.NewRootAPIsHandler(c.DiscoveryAddresses, c.Serializer, c.RequestContextMapper), } for k, v := range delegationTarget.PostStartHooks() { diff --git a/staging/src/k8s.io/apiserver/pkg/server/genericapiserver.go b/staging/src/k8s.io/apiserver/pkg/server/genericapiserver.go index 27aac3c978a..192180fe0f3 100644 --- a/staging/src/k8s.io/apiserver/pkg/server/genericapiserver.go +++ b/staging/src/k8s.io/apiserver/pkg/server/genericapiserver.go @@ -334,7 +334,7 @@ func (s *GenericAPIServer) InstallLegacyAPIGroup(apiPrefix string, apiGroupInfo } // Install the version handler. // Add a handler at / to enumerate the supported api versions. - s.Handler.GoRestfulContainer.Add(discovery.NewLegacyRootAPIHandler(s.discoveryAddresses, s.Serializer, apiPrefix, apiVersions).WebService()) + s.Handler.GoRestfulContainer.Add(discovery.NewLegacyRootAPIHandler(s.discoveryAddresses, s.Serializer, apiPrefix, apiVersions, s.requestContextMapper).WebService()) return nil } @@ -378,7 +378,7 @@ func (s *GenericAPIServer) InstallAPIGroup(apiGroupInfo *APIGroupInfo) error { } s.DiscoveryGroupManager.AddGroup(apiGroup) - s.Handler.GoRestfulContainer.Add(discovery.NewAPIGroupHandler(s.Serializer, apiGroup).WebService()) + s.Handler.GoRestfulContainer.Add(discovery.NewAPIGroupHandler(s.Serializer, apiGroup, s.requestContextMapper).WebService()) return nil } diff --git a/staging/src/k8s.io/apiserver/pkg/server/handler.go b/staging/src/k8s.io/apiserver/pkg/server/handler.go index 89da8e3148b..148f8a020a9 100644 --- a/staging/src/k8s.io/apiserver/pkg/server/handler.go +++ b/staging/src/k8s.io/apiserver/pkg/server/handler.go @@ -18,6 +18,7 @@ package server import ( "bytes" + "errors" "fmt" "net/http" rt "runtime" @@ -30,6 +31,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apiserver/pkg/endpoints/handlers/responsewriters" + "k8s.io/apiserver/pkg/endpoints/request" "k8s.io/apiserver/pkg/server/mux" genericmux "k8s.io/apiserver/pkg/server/mux" ) @@ -52,7 +54,7 @@ type APIServerHandler struct { // It is normally used to apply filtering like authentication and authorization type HandlerChainBuilderFn func(apiHandler http.Handler) http.Handler -func NewAPIServerHandler(s runtime.NegotiatedSerializer, handlerChainBuilder HandlerChainBuilderFn, notFoundHandler http.Handler) *APIServerHandler { +func NewAPIServerHandler(contextMapper request.RequestContextMapper, s runtime.NegotiatedSerializer, handlerChainBuilder HandlerChainBuilderFn, notFoundHandler http.Handler) *APIServerHandler { postGoRestfulMux := genericmux.NewPathRecorderMux() if notFoundHandler != nil { postGoRestfulMux.NotFoundHandler(notFoundHandler) @@ -65,7 +67,11 @@ func NewAPIServerHandler(s runtime.NegotiatedSerializer, handlerChainBuilder Han logStackOnRecover(s, panicReason, httpWriter) }) gorestfulContainer.ServiceErrorHandler(func(serviceErr restful.ServiceError, request *restful.Request, response *restful.Response) { - serviceErrorHandler(s, serviceErr, request, response) + ctx, ok := contextMapper.Get(request.Request) + if !ok { + responsewriters.InternalError(response.ResponseWriter, request.Request, errors.New("no context found for request")) + } + serviceErrorHandler(ctx, s, serviceErr, request, response) }) // register the defaultHandler for everything. This will allow an unhandled request to fall through to another handler instead of @@ -109,12 +115,13 @@ func logStackOnRecover(s runtime.NegotiatedSerializer, panicReason interface{}, if ct := w.Header().Get("Content-Type"); len(ct) > 0 { headers.Set("Accept", ct) } - responsewriters.ErrorNegotiated(nil, apierrors.NewGenericServerResponse(http.StatusInternalServerError, "", schema.GroupResource{}, "", "", 0, false), s, schema.GroupVersion{}, w, &http.Request{Header: headers}) + emptyContext := request.NewContext() // best we can do here: we don't know the request + responsewriters.ErrorNegotiated(emptyContext, 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) { +func serviceErrorHandler(ctx request.Context, s runtime.NegotiatedSerializer, serviceErr restful.ServiceError, request *restful.Request, resp *restful.Response) { responsewriters.ErrorNegotiated( - nil, + ctx, apierrors.NewGenericServerResponse(serviceErr.Code, "", schema.GroupResource{}, "", serviceErr.Message, 0, false), s, schema.GroupVersion{}, diff --git a/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go b/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go index dbb912ec065..87e0d3e695a 100644 --- a/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go +++ b/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go @@ -172,6 +172,7 @@ func (c completedConfig) NewWithDelegate(delegationTarget genericapiserver.Deleg apisHandler := &apisHandler{ codecs: Codecs, lister: s.lister, + mapper: s.contextMapper, } s.GenericAPIServer.Handler.PostGoRestfulMux.Handle("/apis", apisHandler) s.GenericAPIServer.Handler.PostGoRestfulMux.UnlistedHandle("/apis/", apisHandler) @@ -242,10 +243,11 @@ func (s *APIAggregator) AddAPIService(apiService *apiregistration.APIService, de // it's time to register the group aggregation endpoint groupPath := "/apis/" + apiService.Spec.Group groupDiscoveryHandler := &apiGroupHandler{ - codecs: Codecs, - groupName: apiService.Spec.Group, - lister: s.lister, - delegate: s.delegateHandler, + codecs: Codecs, + groupName: apiService.Spec.Group, + lister: s.lister, + delegate: s.delegateHandler, + contextMapper: s.contextMapper, } // aggregation is protected s.GenericAPIServer.Handler.PostGoRestfulMux.Handle(groupPath, groupDiscoveryHandler) diff --git a/staging/src/k8s.io/kube-aggregator/pkg/apiserver/handler_apis.go b/staging/src/k8s.io/kube-aggregator/pkg/apiserver/handler_apis.go index b1e99e24cfe..352d43cc698 100644 --- a/staging/src/k8s.io/kube-aggregator/pkg/apiserver/handler_apis.go +++ b/staging/src/k8s.io/kube-aggregator/pkg/apiserver/handler_apis.go @@ -17,6 +17,7 @@ limitations under the License. package apiserver import ( + "errors" "net/http" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -25,6 +26,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/serializer" "k8s.io/apiserver/pkg/endpoints/handlers/responsewriters" + "k8s.io/apiserver/pkg/endpoints/request" apiregistrationapi "k8s.io/kube-aggregator/pkg/apis/apiregistration" apiregistrationv1beta1api "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1beta1" @@ -36,6 +38,7 @@ import ( type apisHandler struct { codecs serializer.CodecFactory lister listers.APIServiceLister + mapper request.RequestContextMapper } var discoveryGroup = metav1.APIGroup{ @@ -53,6 +56,12 @@ var discoveryGroup = metav1.APIGroup{ } func (r *apisHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { + ctx, ok := r.mapper.Get(req) + if !ok { + responsewriters.InternalError(w, req, errors.New("no context found for request")) + 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. @@ -76,7 +85,7 @@ func (r *apisHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { } } - responsewriters.WriteObjectNegotiated(nil, r.codecs, schema.GroupVersion{}, w, req, http.StatusOK, discoveryGroupList) + responsewriters.WriteObjectNegotiated(ctx, r.codecs, schema.GroupVersion{}, w, req, http.StatusOK, discoveryGroupList) } // convertToDiscoveryAPIGroup takes apiservices in a single group and returns a discovery compatible object. @@ -115,8 +124,9 @@ func convertToDiscoveryAPIGroup(apiServices []*apiregistrationapi.APIService) *m // apiGroupHandler serves the `/apis/` endpoint. type apiGroupHandler struct { - codecs serializer.CodecFactory - groupName string + codecs serializer.CodecFactory + groupName string + contextMapper request.RequestContextMapper lister listers.APIServiceLister @@ -124,6 +134,12 @@ type apiGroupHandler struct { } func (r *apiGroupHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { + ctx, ok := r.contextMapper.Get(req) + if !ok { + responsewriters.InternalError(w, req, errors.New("no context found for request")) + 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) @@ -151,5 +167,5 @@ func (r *apiGroupHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { http.Error(w, "", http.StatusNotFound) return } - responsewriters.WriteObjectNegotiated(nil, r.codecs, schema.GroupVersion{}, w, req, http.StatusOK, discoveryGroup) + responsewriters.WriteObjectNegotiated(ctx, r.codecs, schema.GroupVersion{}, w, req, http.StatusOK, discoveryGroup) } diff --git a/staging/src/k8s.io/kube-aggregator/pkg/apiserver/handler_apis_test.go b/staging/src/k8s.io/kube-aggregator/pkg/apiserver/handler_apis_test.go index c178598652c..55a9ebf9c87 100644 --- a/staging/src/k8s.io/kube-aggregator/pkg/apiserver/handler_apis_test.go +++ b/staging/src/k8s.io/kube-aggregator/pkg/apiserver/handler_apis_test.go @@ -27,6 +27,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/diff" + "k8s.io/apiserver/pkg/endpoints/request" "k8s.io/client-go/tools/cache" "k8s.io/kube-aggregator/pkg/apis/apiregistration" @@ -236,16 +237,18 @@ func TestAPIs(t *testing.T) { } for _, tc := range tests { + mapper := request.NewRequestContextMapper() indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}) handler := &apisHandler{ codecs: Codecs, lister: listers.NewAPIServiceLister(indexer), + mapper: mapper, } for _, o := range tc.apiservices { indexer.Add(o) } - server := httptest.NewServer(handler) + server := httptest.NewServer(request.WithRequestContext(handler, mapper)) defer server.Close() resp, err := http.Get(server.URL + "/apis") @@ -272,6 +275,7 @@ func TestAPIs(t *testing.T) { } func TestAPIGroupMissing(t *testing.T) { + mapper := request.NewRequestContextMapper() indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}) handler := &apiGroupHandler{ codecs: Codecs, @@ -280,9 +284,10 @@ func TestAPIGroupMissing(t *testing.T) { delegate: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusForbidden) }), + contextMapper: mapper, } - server := httptest.NewServer(handler) + server := httptest.NewServer(request.WithRequestContext(handler, mapper)) defer server.Close() // this call should delegate @@ -415,17 +420,19 @@ func TestAPIGroup(t *testing.T) { } for _, tc := range tests { + mapper := request.NewRequestContextMapper() indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}) handler := &apiGroupHandler{ - codecs: Codecs, - lister: listers.NewAPIServiceLister(indexer), - groupName: "foo", + codecs: Codecs, + lister: listers.NewAPIServiceLister(indexer), + groupName: "foo", + contextMapper: mapper, } for _, o := range tc.apiservices { indexer.Add(o) } - server := httptest.NewServer(handler) + server := httptest.NewServer(request.WithRequestContext(handler, mapper)) defer server.Close() resp, err := http.Get(server.URL + "/apis/" + tc.group) diff --git a/staging/src/k8s.io/kube-apiextensions-server/pkg/apiserver/apiserver.go b/staging/src/k8s.io/kube-apiextensions-server/pkg/apiserver/apiserver.go index 7ce8bb58b3e..819e53dae58 100644 --- a/staging/src/k8s.io/kube-apiextensions-server/pkg/apiserver/apiserver.go +++ b/staging/src/k8s.io/kube-apiextensions-server/pkg/apiserver/apiserver.go @@ -160,7 +160,7 @@ func (c completedConfig) New(delegationTarget genericapiserver.DelegationTarget) s.GenericAPIServer.Handler.PostGoRestfulMux.Handle("/apis", crdHandler) s.GenericAPIServer.Handler.PostGoRestfulMux.HandlePrefix("/apis/", crdHandler) - crdController := NewDiscoveryController(s.Informers.Apiextensions().InternalVersion().CustomResourceDefinitions(), versionDiscoveryHandler, groupDiscoveryHandler) + crdController := NewDiscoveryController(s.Informers.Apiextensions().InternalVersion().CustomResourceDefinitions(), versionDiscoveryHandler, groupDiscoveryHandler, c.GenericConfig.RequestContextMapper) namingController := status.NewNamingConditionController(s.Informers.Apiextensions().InternalVersion().CustomResourceDefinitions(), crdClient) finalizingController := finalizer.NewCRDFinalizer( s.Informers.Apiextensions().InternalVersion().CustomResourceDefinitions(), diff --git a/staging/src/k8s.io/kube-apiextensions-server/pkg/apiserver/customresource_discovery_controller.go b/staging/src/k8s.io/kube-apiextensions-server/pkg/apiserver/customresource_discovery_controller.go index 3ba89146b29..b385d69310b 100644 --- a/staging/src/k8s.io/kube-apiextensions-server/pkg/apiserver/customresource_discovery_controller.go +++ b/staging/src/k8s.io/kube-apiextensions-server/pkg/apiserver/customresource_discovery_controller.go @@ -28,6 +28,7 @@ import ( utilruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/apiserver/pkg/endpoints/discovery" + "k8s.io/apiserver/pkg/endpoints/request" "k8s.io/client-go/tools/cache" "k8s.io/client-go/util/workqueue" @@ -39,6 +40,7 @@ import ( type DiscoveryController struct { versionHandler *versionDiscoveryHandler groupHandler *groupDiscoveryHandler + contextMapper request.RequestContextMapper crdLister listers.CustomResourceDefinitionLister crdsSynced cache.InformerSynced @@ -49,12 +51,13 @@ type DiscoveryController struct { queue workqueue.RateLimitingInterface } -func NewDiscoveryController(crdInformer informers.CustomResourceDefinitionInformer, versionHandler *versionDiscoveryHandler, groupHandler *groupDiscoveryHandler) *DiscoveryController { +func NewDiscoveryController(crdInformer informers.CustomResourceDefinitionInformer, versionHandler *versionDiscoveryHandler, groupHandler *groupDiscoveryHandler, contextMapper request.RequestContextMapper) *DiscoveryController { c := &DiscoveryController{ versionHandler: versionHandler, groupHandler: groupHandler, crdLister: crdInformer.Lister(), crdsSynced: crdInformer.Informer().HasSynced, + contextMapper: contextMapper, queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "DiscoveryController"), } @@ -129,7 +132,7 @@ func (c *DiscoveryController) sync(version schema.GroupVersion) error { // the preferred versions for a group is arbitrary since there cannot be duplicate resources PreferredVersion: apiVersionsForDiscovery[0], } - c.groupHandler.setDiscovery(version.Group, discovery.NewAPIGroupHandler(Codecs, apiGroup)) + c.groupHandler.setDiscovery(version.Group, discovery.NewAPIGroupHandler(Codecs, apiGroup, c.contextMapper)) if !foundVersion { c.versionHandler.unsetDiscovery(version) @@ -137,7 +140,7 @@ func (c *DiscoveryController) sync(version schema.GroupVersion) error { } c.versionHandler.setDiscovery(version, discovery.NewAPIVersionHandler(Codecs, version, discovery.APIResourceListerFunc(func() []metav1.APIResource { return apiResourcesForDiscovery - }))) + }), c.contextMapper)) return nil }