audit: wire through non-nil context everywhere

This commit is contained in:
Dr. Stefan Schimanski 2017-05-17 17:23:23 +02:00
parent 0b5bcb0219
commit ce942d19c3
19 changed files with 143 additions and 66 deletions

View File

@ -27,6 +27,7 @@ import (
"net/http/httptest" "net/http/httptest"
"testing" "testing"
apirequest "k8s.io/apiserver/pkg/endpoints/request"
genericapiserver "k8s.io/apiserver/pkg/server" genericapiserver "k8s.io/apiserver/pkg/server"
"k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api"
openapigen "k8s.io/kubernetes/pkg/generated/openapi" 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. // 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() defer server.Close()
resp, err := http.Get(server.URL + "/swagger.json") resp, err := http.Get(server.URL + "/swagger.json")
if !assert.NoError(err) { if !assert.NoError(err) {

View File

@ -234,7 +234,7 @@ func TestAPIVersionOfDiscoveryEndpoints(t *testing.T) {
master, etcdserver, _, assert := newMaster(t) master, etcdserver, _, assert := newMaster(t)
defer etcdserver.Terminate(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 // /api exists in release-1.1
resp, err := http.Get(server.URL + "/api") resp, err := http.Get(server.URL + "/api")

View File

@ -287,7 +287,7 @@ func (m *ThirdPartyResourceServer) InstallThirdPartyResource(rsrc *extensions.Th
if err := thirdparty.InstallREST(m.genericAPIServer.Handler.GoRestfulContainer); err != nil { if err := thirdparty.InstallREST(m.genericAPIServer.Handler.GoRestfulContainer); err != nil {
glog.Errorf("Unable to setup thirdparty api: %v", err) 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) m.addThirdPartyResourceStorage(path, plural.Resource, thirdparty.Storage[plural.Resource].(*thirdpartyresourcedatastore.REST), apiGroup)
api.Registry.AddThirdPartyAPIGroupVersions(schema.GroupVersion{Group: group, Version: rsrc.Versions[0].Name}) api.Registry.AddThirdPartyAPIGroupVersions(schema.GroupVersion{Group: group, Version: rsrc.Versions[0].Name})

View File

@ -3254,7 +3254,7 @@ func (obj *UnregisteredAPIObject) GetObjectKind() schema.ObjectKind {
func TestWriteJSONDecodeError(t *testing.T) { func TestWriteJSONDecodeError(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 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() defer server.Close()
// We send a 200 status code before we encode the object, so we expect OK, but there will // We send a 200 status code before we encode the object, so we expect OK, but there will

View File

@ -17,6 +17,7 @@ limitations under the License.
package discovery package discovery
import ( import (
"errors"
"net/http" "net/http"
"github.com/emicklei/go-restful" "github.com/emicklei/go-restful"
@ -26,17 +27,18 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/endpoints/handlers/negotiation" "k8s.io/apiserver/pkg/endpoints/handlers/negotiation"
"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters" "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 // 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. // of a group. E.g., such a web service will be registered at /apis/extensions.
type APIGroupHandler struct { type APIGroupHandler struct {
serializer runtime.NegotiatedSerializer serializer runtime.NegotiatedSerializer
contextMapper request.RequestContextMapper
group metav1.APIGroup 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) { if keepUnversioned(group.Name) {
// Because in release 1.1, /apis/extensions returns response with empty // Because in release 1.1, /apis/extensions returns response with empty
// APIVersion, we use stripVersionNegotiatedSerializer to keep the // APIVersion, we use stripVersionNegotiatedSerializer to keep the
@ -46,6 +48,7 @@ func NewAPIGroupHandler(serializer runtime.NegotiatedSerializer, group metav1.AP
return &APIGroupHandler{ return &APIGroupHandler{
serializer: serializer, serializer: serializer,
contextMapper: contextMapper,
group: group, 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) { 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)
} }

View File

@ -17,6 +17,7 @@ limitations under the License.
package discovery package discovery
import ( import (
"errors"
"net/http" "net/http"
"github.com/emicklei/go-restful" "github.com/emicklei/go-restful"
@ -27,6 +28,7 @@ import (
utilnet "k8s.io/apimachinery/pkg/util/net" utilnet "k8s.io/apimachinery/pkg/util/net"
"k8s.io/apiserver/pkg/endpoints/handlers/negotiation" "k8s.io/apiserver/pkg/endpoints/handlers/negotiation"
"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters" "k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
"k8s.io/apiserver/pkg/endpoints/request"
) )
// legacyRootAPIHandler creates a webservice serving api group discovery. // legacyRootAPIHandler creates a webservice serving api group discovery.
@ -36,9 +38,10 @@ type legacyRootAPIHandler struct {
apiPrefix string apiPrefix string
serializer runtime.NegotiatedSerializer serializer runtime.NegotiatedSerializer
apiVersions []string 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 // Because in release 1.1, /apis returns response with empty APIVersion, we
// use stripVersionNegotiatedSerializer to keep the response backwards // use stripVersionNegotiatedSerializer to keep the response backwards
// compatible. // compatible.
@ -49,6 +52,7 @@ func NewLegacyRootAPIHandler(addresses Addresses, serializer runtime.NegotiatedS
apiPrefix: apiPrefix, apiPrefix: apiPrefix,
serializer: serializer, serializer: serializer,
apiVersions: apiVersions, apiVersions: apiVersions,
contextMapper: contextMapper,
} }
} }
@ -68,11 +72,17 @@ func (s *legacyRootAPIHandler) WebService() *restful.WebService {
} }
func (s *legacyRootAPIHandler) handle(req *restful.Request, resp *restful.Response) { 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) clientIP := utilnet.GetClientIP(req.Request)
apiVersions := &metav1.APIVersions{ apiVersions := &metav1.APIVersions{
ServerAddressByClientCIDRs: s.addresses.ServerAddressByClientCIDRs(clientIP), ServerAddressByClientCIDRs: s.addresses.ServerAddressByClientCIDRs(clientIP),
Versions: s.apiVersions, 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)
} }

View File

@ -17,10 +17,11 @@ limitations under the License.
package discovery package discovery
import ( import (
"errors"
"net/http" "net/http"
"sync" "sync"
"github.com/emicklei/go-restful" restful "github.com/emicklei/go-restful"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
@ -28,6 +29,7 @@ import (
utilnet "k8s.io/apimachinery/pkg/util/net" utilnet "k8s.io/apimachinery/pkg/util/net"
"k8s.io/apiserver/pkg/endpoints/handlers/negotiation" "k8s.io/apiserver/pkg/endpoints/handlers/negotiation"
"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters" "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 // GroupManager is an interface that allows dynamic mutation of the existing webservice to handle
@ -47,6 +49,7 @@ type rootAPIsHandler struct {
addresses Addresses addresses Addresses
serializer runtime.NegotiatedSerializer serializer runtime.NegotiatedSerializer
contextMapper request.RequestContextMapper
// Map storing information about all groups to be exposed in discovery response. // Map storing information about all groups to be exposed in discovery response.
// The map is from name to the group. // The map is from name to the group.
@ -56,7 +59,7 @@ type rootAPIsHandler struct {
apiGroupNames []string 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 // Because in release 1.1, /apis returns response with empty APIVersion, we
// use stripVersionNegotiatedSerializer to keep the response backwards // use stripVersionNegotiatedSerializer to keep the response backwards
// compatible. // compatible.
@ -66,6 +69,7 @@ func NewRootAPIsHandler(addresses Addresses, serializer runtime.NegotiatedSerial
addresses: addresses, addresses: addresses,
serializer: serializer, serializer: serializer,
apiGroups: map[string]metav1.APIGroup{}, 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) { 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() s.lock.RLock()
defer s.lock.RUnlock() defer s.lock.RUnlock()
@ -111,7 +121,7 @@ func (s *rootAPIsHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request)
groups[i].ServerAddressByClientCIDRs = serverCIDR 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) { func (s *rootAPIsHandler) restfulHandle(req *restful.Request, resp *restful.Response) {

View File

@ -33,6 +33,7 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer" "k8s.io/apimachinery/pkg/runtime/serializer"
utilnet "k8s.io/apimachinery/pkg/util/net" utilnet "k8s.io/apimachinery/pkg/util/net"
"k8s.io/apiserver/pkg/endpoints/request"
) )
var ( var (
@ -83,9 +84,10 @@ func getGroupList(t *testing.T, server *httptest.Server) (*metav1.APIGroupList,
} }
func TestDiscoveryAtAPIS(t *testing.T) { 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) groupList, err := getGroupList(t, server)
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
@ -133,9 +135,10 @@ func TestDiscoveryAtAPIS(t *testing.T) {
} }
func TestDiscoveryOrdering(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) groupList, err := getGroupList(t, server)
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)

View File

@ -17,15 +17,17 @@ limitations under the License.
package discovery package discovery
import ( import (
"errors"
"net/http" "net/http"
"github.com/emicklei/go-restful" restful "github.com/emicklei/go-restful"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/endpoints/handlers/negotiation" "k8s.io/apiserver/pkg/endpoints/handlers/negotiation"
"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters" "k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
"k8s.io/apiserver/pkg/endpoints/request"
) )
type APIResourceLister interface { type APIResourceLister interface {
@ -42,12 +44,13 @@ func (f APIResourceListerFunc) ListAPIResources() []metav1.APIResource {
// E.g., such a web service will be registered at /apis/extensions/v1beta1. // E.g., such a web service will be registered at /apis/extensions/v1beta1.
type APIVersionHandler struct { type APIVersionHandler struct {
serializer runtime.NegotiatedSerializer serializer runtime.NegotiatedSerializer
contextMapper request.RequestContextMapper
groupVersion schema.GroupVersion groupVersion schema.GroupVersion
apiResourceLister APIResourceLister 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) { if keepUnversioned(groupVersion.Group) {
// Because in release 1.1, /apis/extensions returns response with empty // Because in release 1.1, /apis/extensions returns response with empty
// APIVersion, we use stripVersionNegotiatedSerializer to keep the // APIVersion, we use stripVersionNegotiatedSerializer to keep the
@ -59,6 +62,7 @@ func NewAPIVersionHandler(serializer runtime.NegotiatedSerializer, groupVersion
serializer: serializer, serializer: serializer,
groupVersion: groupVersion, groupVersion: groupVersion,
apiResourceLister: apiResourceLister, 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) { 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()}) &metav1.APIResourceList{GroupVersion: s.groupVersion.String(), APIResources: s.apiResourceLister.ListAPIResources()})
} }

View File

@ -100,7 +100,7 @@ func (g *APIGroupVersion) InstallREST(container *restful.Container) error {
if lister == nil { if lister == nil {
lister = staticLister{apiResources} lister = staticLister{apiResources}
} }
versionDiscoveryHandler := discovery.NewAPIVersionHandler(g.Serializer, g.GroupVersion, lister) versionDiscoveryHandler := discovery.NewAPIVersionHandler(g.Serializer, g.GroupVersion, lister, g.Context)
versionDiscoveryHandler.AddToWebService(ws) versionDiscoveryHandler.AddToWebService(ws)
container.Add(ws) container.Add(ws)
return utilerrors.NewAggregate(registrationErrors) return utilerrors.NewAggregate(registrationErrors)
@ -129,7 +129,7 @@ func (g *APIGroupVersion) UpdateREST(container *restful.Container) error {
if lister == nil { if lister == nil {
lister = staticLister{apiResources} lister = staticLister{apiResources}
} }
versionDiscoveryHandler := discovery.NewAPIVersionHandler(g.Serializer, g.GroupVersion, lister) versionDiscoveryHandler := discovery.NewAPIVersionHandler(g.Serializer, g.GroupVersion, lister, g.Context)
versionDiscoveryHandler.AddToWebService(ws) versionDiscoveryHandler.AddToWebService(ws)
return utilerrors.NewAggregate(registrationErrors) return utilerrors.NewAggregate(registrationErrors)
} }

View File

@ -171,7 +171,7 @@ func (r *ProxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// TODO convert this entire proxy to an UpgradeAwareProxy similar to // TODO convert this entire proxy to an UpgradeAwareProxy similar to
// https://github.com/openshift/origin/blob/master/pkg/util/httpproxy/upgradeawareproxy.go. // 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. // 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 return
} }
@ -220,7 +220,7 @@ func (r *ProxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
} }
// tryUpgrade returns true if the request was handled. // 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) { if !httpstream.IsUpgradeRequest(req) {
return false return false
} }

View File

@ -406,7 +406,7 @@ func (c completedConfig) New(delegationTarget DelegationTarget) (*GenericAPIServ
healthzChecks: c.HealthzChecks, 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() { for k, v := range delegationTarget.PostStartHooks() {

View File

@ -334,7 +334,7 @@ func (s *GenericAPIServer) InstallLegacyAPIGroup(apiPrefix string, apiGroupInfo
} }
// Install the version handler. // Install the version handler.
// Add a handler at /<apiPrefix> to enumerate the supported api versions. // Add a handler at /<apiPrefix> 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 return nil
} }
@ -378,7 +378,7 @@ func (s *GenericAPIServer) InstallAPIGroup(apiGroupInfo *APIGroupInfo) error {
} }
s.DiscoveryGroupManager.AddGroup(apiGroup) 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 return nil
} }

View File

@ -18,6 +18,7 @@ package server
import ( import (
"bytes" "bytes"
"errors"
"fmt" "fmt"
"net/http" "net/http"
rt "runtime" rt "runtime"
@ -30,6 +31,7 @@ import (
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters" "k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
"k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/server/mux" "k8s.io/apiserver/pkg/server/mux"
genericmux "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 // It is normally used to apply filtering like authentication and authorization
type HandlerChainBuilderFn func(apiHandler http.Handler) http.Handler 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() postGoRestfulMux := genericmux.NewPathRecorderMux()
if notFoundHandler != nil { if notFoundHandler != nil {
postGoRestfulMux.NotFoundHandler(notFoundHandler) postGoRestfulMux.NotFoundHandler(notFoundHandler)
@ -65,7 +67,11 @@ func NewAPIServerHandler(s runtime.NegotiatedSerializer, handlerChainBuilder Han
logStackOnRecover(s, panicReason, httpWriter) logStackOnRecover(s, panicReason, httpWriter)
}) })
gorestfulContainer.ServiceErrorHandler(func(serviceErr restful.ServiceError, request *restful.Request, response *restful.Response) { 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 // 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 { if ct := w.Header().Get("Content-Type"); len(ct) > 0 {
headers.Set("Accept", ct) 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( responsewriters.ErrorNegotiated(
nil, ctx,
apierrors.NewGenericServerResponse(serviceErr.Code, "", schema.GroupResource{}, "", serviceErr.Message, 0, false), apierrors.NewGenericServerResponse(serviceErr.Code, "", schema.GroupResource{}, "", serviceErr.Message, 0, false),
s, s,
schema.GroupVersion{}, schema.GroupVersion{},

View File

@ -172,6 +172,7 @@ func (c completedConfig) NewWithDelegate(delegationTarget genericapiserver.Deleg
apisHandler := &apisHandler{ apisHandler := &apisHandler{
codecs: Codecs, codecs: Codecs,
lister: s.lister, lister: s.lister,
mapper: s.contextMapper,
} }
s.GenericAPIServer.Handler.PostGoRestfulMux.Handle("/apis", apisHandler) s.GenericAPIServer.Handler.PostGoRestfulMux.Handle("/apis", apisHandler)
s.GenericAPIServer.Handler.PostGoRestfulMux.UnlistedHandle("/apis/", apisHandler) s.GenericAPIServer.Handler.PostGoRestfulMux.UnlistedHandle("/apis/", apisHandler)
@ -246,6 +247,7 @@ func (s *APIAggregator) AddAPIService(apiService *apiregistration.APIService, de
groupName: apiService.Spec.Group, groupName: apiService.Spec.Group,
lister: s.lister, lister: s.lister,
delegate: s.delegateHandler, delegate: s.delegateHandler,
contextMapper: s.contextMapper,
} }
// aggregation is protected // aggregation is protected
s.GenericAPIServer.Handler.PostGoRestfulMux.Handle(groupPath, groupDiscoveryHandler) s.GenericAPIServer.Handler.PostGoRestfulMux.Handle(groupPath, groupDiscoveryHandler)

View File

@ -17,6 +17,7 @@ limitations under the License.
package apiserver package apiserver
import ( import (
"errors"
"net/http" "net/http"
apierrors "k8s.io/apimachinery/pkg/api/errors" apierrors "k8s.io/apimachinery/pkg/api/errors"
@ -25,6 +26,7 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer" "k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters" "k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
"k8s.io/apiserver/pkg/endpoints/request"
apiregistrationapi "k8s.io/kube-aggregator/pkg/apis/apiregistration" apiregistrationapi "k8s.io/kube-aggregator/pkg/apis/apiregistration"
apiregistrationv1beta1api "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1beta1" apiregistrationv1beta1api "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1beta1"
@ -36,6 +38,7 @@ import (
type apisHandler struct { type apisHandler struct {
codecs serializer.CodecFactory codecs serializer.CodecFactory
lister listers.APIServiceLister lister listers.APIServiceLister
mapper request.RequestContextMapper
} }
var discoveryGroup = metav1.APIGroup{ var discoveryGroup = metav1.APIGroup{
@ -53,6 +56,12 @@ var discoveryGroup = metav1.APIGroup{
} }
func (r *apisHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { 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{ 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.
@ -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. // convertToDiscoveryAPIGroup takes apiservices in a single group and returns a discovery compatible object.
@ -117,6 +126,7 @@ func convertToDiscoveryAPIGroup(apiServices []*apiregistrationapi.APIService) *m
type apiGroupHandler struct { type apiGroupHandler struct {
codecs serializer.CodecFactory codecs serializer.CodecFactory
groupName string groupName string
contextMapper request.RequestContextMapper
lister listers.APIServiceLister lister listers.APIServiceLister
@ -124,6 +134,12 @@ type apiGroupHandler struct {
} }
func (r *apiGroupHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { 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()) 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)
@ -151,5 +167,5 @@ func (r *apiGroupHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
http.Error(w, "", http.StatusNotFound) http.Error(w, "", http.StatusNotFound)
return 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)
} }

View File

@ -27,6 +27,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/diff" "k8s.io/apimachinery/pkg/util/diff"
"k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/cache"
"k8s.io/kube-aggregator/pkg/apis/apiregistration" "k8s.io/kube-aggregator/pkg/apis/apiregistration"
@ -236,16 +237,18 @@ func TestAPIs(t *testing.T) {
} }
for _, tc := range tests { for _, tc := range tests {
mapper := request.NewRequestContextMapper()
indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}) indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})
handler := &apisHandler{ handler := &apisHandler{
codecs: Codecs, codecs: Codecs,
lister: listers.NewAPIServiceLister(indexer), lister: listers.NewAPIServiceLister(indexer),
mapper: mapper,
} }
for _, o := range tc.apiservices { for _, o := range tc.apiservices {
indexer.Add(o) indexer.Add(o)
} }
server := httptest.NewServer(handler) server := httptest.NewServer(request.WithRequestContext(handler, mapper))
defer server.Close() defer server.Close()
resp, err := http.Get(server.URL + "/apis") resp, err := http.Get(server.URL + "/apis")
@ -272,6 +275,7 @@ func TestAPIs(t *testing.T) {
} }
func TestAPIGroupMissing(t *testing.T) { func TestAPIGroupMissing(t *testing.T) {
mapper := request.NewRequestContextMapper()
indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}) indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})
handler := &apiGroupHandler{ handler := &apiGroupHandler{
codecs: Codecs, codecs: Codecs,
@ -280,9 +284,10 @@ func TestAPIGroupMissing(t *testing.T) {
delegate: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { delegate: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusForbidden) w.WriteHeader(http.StatusForbidden)
}), }),
contextMapper: mapper,
} }
server := httptest.NewServer(handler) server := httptest.NewServer(request.WithRequestContext(handler, mapper))
defer server.Close() defer server.Close()
// this call should delegate // this call should delegate
@ -415,17 +420,19 @@ func TestAPIGroup(t *testing.T) {
} }
for _, tc := range tests { for _, tc := range tests {
mapper := request.NewRequestContextMapper()
indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}) indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})
handler := &apiGroupHandler{ handler := &apiGroupHandler{
codecs: Codecs, codecs: Codecs,
lister: listers.NewAPIServiceLister(indexer), lister: listers.NewAPIServiceLister(indexer),
groupName: "foo", groupName: "foo",
contextMapper: mapper,
} }
for _, o := range tc.apiservices { for _, o := range tc.apiservices {
indexer.Add(o) indexer.Add(o)
} }
server := httptest.NewServer(handler) server := httptest.NewServer(request.WithRequestContext(handler, mapper))
defer server.Close() defer server.Close()
resp, err := http.Get(server.URL + "/apis/" + tc.group) resp, err := http.Get(server.URL + "/apis/" + tc.group)

View File

@ -160,7 +160,7 @@ func (c completedConfig) New(delegationTarget genericapiserver.DelegationTarget)
s.GenericAPIServer.Handler.PostGoRestfulMux.Handle("/apis", crdHandler) s.GenericAPIServer.Handler.PostGoRestfulMux.Handle("/apis", crdHandler)
s.GenericAPIServer.Handler.PostGoRestfulMux.HandlePrefix("/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) namingController := status.NewNamingConditionController(s.Informers.Apiextensions().InternalVersion().CustomResourceDefinitions(), crdClient)
finalizingController := finalizer.NewCRDFinalizer( finalizingController := finalizer.NewCRDFinalizer(
s.Informers.Apiextensions().InternalVersion().CustomResourceDefinitions(), s.Informers.Apiextensions().InternalVersion().CustomResourceDefinitions(),

View File

@ -28,6 +28,7 @@ import (
utilruntime "k8s.io/apimachinery/pkg/util/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apiserver/pkg/endpoints/discovery" "k8s.io/apiserver/pkg/endpoints/discovery"
"k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/cache"
"k8s.io/client-go/util/workqueue" "k8s.io/client-go/util/workqueue"
@ -39,6 +40,7 @@ import (
type DiscoveryController struct { type DiscoveryController struct {
versionHandler *versionDiscoveryHandler versionHandler *versionDiscoveryHandler
groupHandler *groupDiscoveryHandler groupHandler *groupDiscoveryHandler
contextMapper request.RequestContextMapper
crdLister listers.CustomResourceDefinitionLister crdLister listers.CustomResourceDefinitionLister
crdsSynced cache.InformerSynced crdsSynced cache.InformerSynced
@ -49,12 +51,13 @@ type DiscoveryController struct {
queue workqueue.RateLimitingInterface 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{ c := &DiscoveryController{
versionHandler: versionHandler, versionHandler: versionHandler,
groupHandler: groupHandler, groupHandler: groupHandler,
crdLister: crdInformer.Lister(), crdLister: crdInformer.Lister(),
crdsSynced: crdInformer.Informer().HasSynced, crdsSynced: crdInformer.Informer().HasSynced,
contextMapper: contextMapper,
queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "DiscoveryController"), 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 // the preferred versions for a group is arbitrary since there cannot be duplicate resources
PreferredVersion: apiVersionsForDiscovery[0], 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 { if !foundVersion {
c.versionHandler.unsetDiscovery(version) 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 { c.versionHandler.setDiscovery(version, discovery.NewAPIVersionHandler(Codecs, version, discovery.APIResourceListerFunc(func() []metav1.APIResource {
return apiResourcesForDiscovery return apiResourcesForDiscovery
}))) }), c.contextMapper))
return nil return nil
} }