From ec8d130fa7fcbd6780644f60380b4a62b35500ed 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 Kubernetes-commit: ce942d19c378ecd335e7e158e30cdc184f9d6184 --- pkg/endpoints/apiserver_test.go | 2 +- pkg/endpoints/discovery/group.go | 22 +++++++++++++------- pkg/endpoints/discovery/legacy.go | 30 ++++++++++++++++++---------- pkg/endpoints/discovery/root.go | 24 +++++++++++++++------- pkg/endpoints/discovery/root_test.go | 11 ++++++---- pkg/endpoints/discovery/version.go | 18 +++++++++++++---- pkg/endpoints/groupversion.go | 4 ++-- pkg/endpoints/handlers/proxy.go | 4 ++-- pkg/server/config.go | 2 +- pkg/server/genericapiserver.go | 4 ++-- pkg/server/handler.go | 17 +++++++++++----- 11 files changed, 93 insertions(+), 45 deletions(-) diff --git a/pkg/endpoints/apiserver_test.go b/pkg/endpoints/apiserver_test.go index a37f5f03b..eb2abc3b0 100644 --- a/pkg/endpoints/apiserver_test.go +++ b/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/pkg/endpoints/discovery/group.go b/pkg/endpoints/discovery/group.go index 6ceda0131..ea47a1046 100644 --- a/pkg/endpoints/discovery/group.go +++ b/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/pkg/endpoints/discovery/legacy.go b/pkg/endpoints/discovery/legacy.go index 65747cd04..3a98e6320 100644 --- a/pkg/endpoints/discovery/legacy.go +++ b/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/pkg/endpoints/discovery/root.go b/pkg/endpoints/discovery/root.go index a436b3b36..c1a8ecf65 100644 --- a/pkg/endpoints/discovery/root.go +++ b/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/pkg/endpoints/discovery/root_test.go b/pkg/endpoints/discovery/root_test.go index d52971634..c8aeed93b 100644 --- a/pkg/endpoints/discovery/root_test.go +++ b/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/pkg/endpoints/discovery/version.go b/pkg/endpoints/discovery/version.go index eac3f4f86..a6bd99998 100644 --- a/pkg/endpoints/discovery/version.go +++ b/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/pkg/endpoints/groupversion.go b/pkg/endpoints/groupversion.go index f2a072edf..d380bdd90 100644 --- a/pkg/endpoints/groupversion.go +++ b/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/pkg/endpoints/handlers/proxy.go b/pkg/endpoints/handlers/proxy.go index 586ab494c..d4bbc4e05 100644 --- a/pkg/endpoints/handlers/proxy.go +++ b/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/pkg/server/config.go b/pkg/server/config.go index 24978a2ff..2475e50be 100644 --- a/pkg/server/config.go +++ b/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/pkg/server/genericapiserver.go b/pkg/server/genericapiserver.go index 27aac3c97..192180fe0 100644 --- a/pkg/server/genericapiserver.go +++ b/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/pkg/server/handler.go b/pkg/server/handler.go index 89da8e314..148f8a020 100644 --- a/pkg/server/handler.go +++ b/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{},