Make RequestScope be a pointer consistently for better memory use

RequestScope is a large struct and causes stack growth when we pass
it by value into multiple stack levels. Avoid the allocations for
this read only struct by passing a pointer.
This commit is contained in:
Clayton Coleman 2019-03-25 23:33:48 -04:00
parent 1514bb2141
commit 8fede0b18a
No known key found for this signature in database
GPG Key ID: 3D16906B4F1C5CB3
12 changed files with 82 additions and 87 deletions

View File

@ -113,13 +113,13 @@ type crdInfo struct {
storages map[string]customresource.CustomResourceStorage storages map[string]customresource.CustomResourceStorage
// Request scope per version // Request scope per version
requestScopes map[string]handlers.RequestScope requestScopes map[string]*handlers.RequestScope
// Scale scope per version // Scale scope per version
scaleRequestScopes map[string]handlers.RequestScope scaleRequestScopes map[string]*handlers.RequestScope
// Status scope per version // Status scope per version
statusRequestScopes map[string]handlers.RequestScope statusRequestScopes map[string]*handlers.RequestScope
// storageVersion is the CRD version used when storing the object in etcd. // storageVersion is the CRD version used when storing the object in etcd.
storageVersion string storageVersion string
@ -444,10 +444,10 @@ func (r *crdHandler) getOrCreateServingInfoFor(crd *apiextensions.CustomResource
} }
// Scope/Storages per version. // Scope/Storages per version.
requestScopes := map[string]handlers.RequestScope{} requestScopes := map[string]*handlers.RequestScope{}
storages := map[string]customresource.CustomResourceStorage{} storages := map[string]customresource.CustomResourceStorage{}
statusScopes := map[string]handlers.RequestScope{} statusScopes := map[string]*handlers.RequestScope{}
scaleScopes := map[string]handlers.RequestScope{} scaleScopes := map[string]*handlers.RequestScope{}
for _, v := range crd.Spec.Versions { for _, v := range crd.Spec.Versions {
safeConverter, unsafeConverter, err := r.converterFactory.NewConverter(crd) safeConverter, unsafeConverter, err := r.converterFactory.NewConverter(crd)
@ -548,7 +548,7 @@ func (r *crdHandler) getOrCreateServingInfoFor(crd *apiextensions.CustomResource
clusterScoped := crd.Spec.Scope == apiextensions.ClusterScoped clusterScoped := crd.Spec.Scope == apiextensions.ClusterScoped
requestScopes[v.Name] = handlers.RequestScope{ requestScopes[v.Name] = &handlers.RequestScope{
Namer: handlers.ContextBasedNaming{ Namer: handlers.ContextBasedNaming{
SelfLinker: meta.NewAccessor(), SelfLinker: meta.NewAccessor(),
ClusterScoped: clusterScoped, ClusterScoped: clusterScoped,
@ -576,19 +576,19 @@ func (r *crdHandler) getOrCreateServingInfoFor(crd *apiextensions.CustomResource
Authorizer: r.authorizer, Authorizer: r.authorizer,
} }
if utilfeature.DefaultFeatureGate.Enabled(features.ServerSideApply) { if utilfeature.DefaultFeatureGate.Enabled(features.ServerSideApply) {
reqScope := requestScopes[v.Name] reqScope := *requestScopes[v.Name]
reqScope.FieldManager = fieldmanager.NewCRDFieldManager( reqScope.FieldManager = fieldmanager.NewCRDFieldManager(
reqScope.Convertor, reqScope.Convertor,
reqScope.Defaulter, reqScope.Defaulter,
reqScope.Kind.GroupVersion(), reqScope.Kind.GroupVersion(),
reqScope.HubGroupVersion, reqScope.HubGroupVersion,
) )
requestScopes[v.Name] = reqScope requestScopes[v.Name] = &reqScope
} }
// override scaleSpec subresource values // override scaleSpec subresource values
// shallow copy // shallow copy
scaleScope := requestScopes[v.Name] scaleScope := *requestScopes[v.Name]
scaleConverter := scale.NewScaleConverter() scaleConverter := scale.NewScaleConverter()
scaleScope.Subresource = "scale" scaleScope.Subresource = "scale"
scaleScope.Serializer = serializer.NewCodecFactory(scaleConverter.Scheme()) scaleScope.Serializer = serializer.NewCodecFactory(scaleConverter.Scheme())
@ -599,11 +599,11 @@ func (r *crdHandler) getOrCreateServingInfoFor(crd *apiextensions.CustomResource
SelfLinkPathPrefix: selfLinkPrefix, SelfLinkPathPrefix: selfLinkPrefix,
SelfLinkPathSuffix: "/scale", SelfLinkPathSuffix: "/scale",
} }
scaleScopes[v.Name] = scaleScope scaleScopes[v.Name] = &scaleScope
// override status subresource values // override status subresource values
// shallow copy // shallow copy
statusScope := requestScopes[v.Name] statusScope := *requestScopes[v.Name]
statusScope.Subresource = "status" statusScope.Subresource = "status"
statusScope.Namer = handlers.ContextBasedNaming{ statusScope.Namer = handlers.ContextBasedNaming{
SelfLinker: meta.NewAccessor(), SelfLinker: meta.NewAccessor(),
@ -611,7 +611,7 @@ func (r *crdHandler) getOrCreateServingInfoFor(crd *apiextensions.CustomResource
SelfLinkPathPrefix: selfLinkPrefix, SelfLinkPathPrefix: selfLinkPrefix,
SelfLinkPathSuffix: "/status", SelfLinkPathSuffix: "/status",
} }
statusScopes[v.Name] = statusScope statusScopes[v.Name] = &statusScope
} }
ret := &crdInfo{ ret := &crdInfo{

View File

@ -43,7 +43,7 @@ import (
utiltrace "k8s.io/utils/trace" utiltrace "k8s.io/utils/trace"
) )
func createHandler(r rest.NamedCreater, scope RequestScope, admit admission.Interface, includeName bool) http.HandlerFunc { func createHandler(r rest.NamedCreater, scope *RequestScope, admit admission.Interface, includeName bool) http.HandlerFunc {
return func(w http.ResponseWriter, req *http.Request) { return func(w http.ResponseWriter, req *http.Request) {
// For performance tracking purposes. // For performance tracking purposes.
trace := utiltrace.New("Create " + req.URL.Path) trace := utiltrace.New("Create " + req.URL.Path)
@ -73,7 +73,7 @@ func createHandler(r rest.NamedCreater, scope RequestScope, admit admission.Inte
ctx := req.Context() ctx := req.Context()
ctx = request.WithNamespace(ctx, namespace) ctx = request.WithNamespace(ctx, namespace)
outputMediaType, _, err := negotiation.NegotiateOutputMediaType(req, scope.Serializer, &scope) outputMediaType, _, err := negotiation.NegotiateOutputMediaType(req, scope.Serializer, scope)
if err != nil { if err != nil {
scope.err(err, w, req) scope.err(err, w, req)
return return
@ -130,7 +130,7 @@ func createHandler(r rest.NamedCreater, scope RequestScope, admit admission.Inte
userInfo, _ := request.UserFrom(ctx) userInfo, _ := request.UserFrom(ctx)
admissionAttributes := admission.NewAttributesRecord(obj, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Create, dryrun.IsDryRun(options.DryRun), userInfo) admissionAttributes := admission.NewAttributesRecord(obj, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Create, dryrun.IsDryRun(options.DryRun), userInfo)
if mutatingAdmission, ok := admit.(admission.MutationInterface); ok && mutatingAdmission.Handles(admission.Create) { if mutatingAdmission, ok := admit.(admission.MutationInterface); ok && mutatingAdmission.Handles(admission.Create) {
err = mutatingAdmission.Admit(admissionAttributes, &scope) err = mutatingAdmission.Admit(admissionAttributes, scope)
if err != nil { if err != nil {
scope.err(err, w, req) scope.err(err, w, req)
return return
@ -157,7 +157,7 @@ func createHandler(r rest.NamedCreater, scope RequestScope, admit admission.Inte
ctx, ctx,
name, name,
obj, obj,
rest.AdmissionToValidateObjectFunc(admit, admissionAttributes, &scope), rest.AdmissionToValidateObjectFunc(admit, admissionAttributes, scope),
options, options,
) )
}) })
@ -173,18 +173,17 @@ func createHandler(r rest.NamedCreater, scope RequestScope, admit admission.Inte
status.Code = int32(code) status.Code = int32(code)
} }
scope.Trace = trace transformResponseObject(ctx, scope, trace, req, w, code, outputMediaType, result)
transformResponseObject(ctx, scope, req, w, code, outputMediaType, result)
} }
} }
// CreateNamedResource returns a function that will handle a resource creation with name. // CreateNamedResource returns a function that will handle a resource creation with name.
func CreateNamedResource(r rest.NamedCreater, scope RequestScope, admission admission.Interface) http.HandlerFunc { func CreateNamedResource(r rest.NamedCreater, scope *RequestScope, admission admission.Interface) http.HandlerFunc {
return createHandler(r, scope, admission, true) return createHandler(r, scope, admission, true)
} }
// CreateResource returns a function that will handle a resource creation. // CreateResource returns a function that will handle a resource creation.
func CreateResource(r rest.Creater, scope RequestScope, admission admission.Interface) http.HandlerFunc { func CreateResource(r rest.Creater, scope *RequestScope, admission admission.Interface) http.HandlerFunc {
return createHandler(&namedCreaterAdapter{r}, scope, admission, false) return createHandler(&namedCreaterAdapter{r}, scope, admission, false)
} }

View File

@ -40,7 +40,7 @@ import (
// DeleteResource returns a function that will handle a resource deletion // DeleteResource returns a function that will handle a resource deletion
// TODO admission here becomes solely validating admission // TODO admission here becomes solely validating admission
func DeleteResource(r rest.GracefulDeleter, allowsOptions bool, scope RequestScope, admit admission.Interface) http.HandlerFunc { func DeleteResource(r rest.GracefulDeleter, allowsOptions bool, scope *RequestScope, admit admission.Interface) http.HandlerFunc {
return func(w http.ResponseWriter, req *http.Request) { return func(w http.ResponseWriter, req *http.Request) {
// For performance tracking purposes. // For performance tracking purposes.
trace := utiltrace.New("Delete " + req.URL.Path) trace := utiltrace.New("Delete " + req.URL.Path)
@ -64,7 +64,7 @@ func DeleteResource(r rest.GracefulDeleter, allowsOptions bool, scope RequestSco
ae := request.AuditEventFrom(ctx) ae := request.AuditEventFrom(ctx)
admit = admission.WithAudit(admit, ae) admit = admission.WithAudit(admit, ae)
outputMediaType, _, err := negotiation.NegotiateOutputMediaType(req, scope.Serializer, &scope) outputMediaType, _, err := negotiation.NegotiateOutputMediaType(req, scope.Serializer, scope)
if err != nil { if err != nil {
scope.err(err, w, req) scope.err(err, w, req)
return return
@ -119,13 +119,13 @@ func DeleteResource(r rest.GracefulDeleter, allowsOptions bool, scope RequestSco
userInfo, _ := request.UserFrom(ctx) userInfo, _ := request.UserFrom(ctx)
attrs := admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Delete, dryrun.IsDryRun(options.DryRun), userInfo) attrs := admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Delete, dryrun.IsDryRun(options.DryRun), userInfo)
if mutatingAdmission, ok := admit.(admission.MutationInterface); ok { if mutatingAdmission, ok := admit.(admission.MutationInterface); ok {
if err := mutatingAdmission.Admit(attrs, &scope); err != nil { if err := mutatingAdmission.Admit(attrs, scope); err != nil {
scope.err(err, w, req) scope.err(err, w, req)
return return
} }
} }
if validatingAdmission, ok := admit.(admission.ValidationInterface); ok { if validatingAdmission, ok := admit.(admission.ValidationInterface); ok {
if err := validatingAdmission.Validate(attrs, &scope); err != nil { if err := validatingAdmission.Validate(attrs, scope); err != nil {
scope.err(err, w, req) scope.err(err, w, req)
return return
} }
@ -168,13 +168,12 @@ func DeleteResource(r rest.GracefulDeleter, allowsOptions bool, scope RequestSco
} }
} }
scope.Trace = trace transformResponseObject(ctx, scope, trace, req, w, status, outputMediaType, result)
transformResponseObject(ctx, scope, req, w, status, outputMediaType, result)
} }
} }
// DeleteCollection returns a function that will handle a collection deletion // DeleteCollection returns a function that will handle a collection deletion
func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope RequestScope, admit admission.Interface) http.HandlerFunc { func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope *RequestScope, admit admission.Interface) http.HandlerFunc {
return func(w http.ResponseWriter, req *http.Request) { return func(w http.ResponseWriter, req *http.Request) {
trace := utiltrace.New("Delete " + req.URL.Path) trace := utiltrace.New("Delete " + req.URL.Path)
defer trace.LogIfLong(500 * time.Millisecond) defer trace.LogIfLong(500 * time.Millisecond)
@ -197,7 +196,7 @@ func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope RequestSco
ctx = request.WithNamespace(ctx, namespace) ctx = request.WithNamespace(ctx, namespace)
ae := request.AuditEventFrom(ctx) ae := request.AuditEventFrom(ctx)
outputMediaType, _, err := negotiation.NegotiateOutputMediaType(req, scope.Serializer, &scope) outputMediaType, _, err := negotiation.NegotiateOutputMediaType(req, scope.Serializer, scope)
if err != nil { if err != nil {
scope.err(err, w, req) scope.err(err, w, req)
return return
@ -269,7 +268,7 @@ func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope RequestSco
userInfo, _ := request.UserFrom(ctx) userInfo, _ := request.UserFrom(ctx)
attrs := admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, "", scope.Resource, scope.Subresource, admission.Delete, dryrun.IsDryRun(options.DryRun), userInfo) attrs := admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, "", scope.Resource, scope.Subresource, admission.Delete, dryrun.IsDryRun(options.DryRun), userInfo)
if mutatingAdmission, ok := admit.(admission.MutationInterface); ok { if mutatingAdmission, ok := admit.(admission.MutationInterface); ok {
err = mutatingAdmission.Admit(attrs, &scope) err = mutatingAdmission.Admit(attrs, scope)
if err != nil { if err != nil {
scope.err(err, w, req) scope.err(err, w, req)
return return
@ -277,7 +276,7 @@ func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope RequestSco
} }
if validatingAdmission, ok := admit.(admission.ValidationInterface); ok { if validatingAdmission, ok := admit.(admission.ValidationInterface); ok {
err = validatingAdmission.Validate(attrs, &scope) err = validatingAdmission.Validate(attrs, scope)
if err != nil { if err != nil {
scope.err(err, w, req) scope.err(err, w, req)
return return
@ -305,7 +304,6 @@ func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope RequestSco
} }
} }
scope.Trace = trace transformResponseObject(ctx, scope, trace, req, w, http.StatusOK, outputMediaType, result)
transformResponseObject(ctx, scope, req, w, http.StatusOK, outputMediaType, result)
} }
} }

View File

@ -46,7 +46,7 @@ type getterFunc func(ctx context.Context, name string, req *http.Request, trace
// getResourceHandler is an HTTP handler function for get requests. It delegates to the // getResourceHandler is an HTTP handler function for get requests. It delegates to the
// passed-in getterFunc to perform the actual get. // passed-in getterFunc to perform the actual get.
func getResourceHandler(scope RequestScope, getter getterFunc) http.HandlerFunc { func getResourceHandler(scope *RequestScope, getter getterFunc) http.HandlerFunc {
return func(w http.ResponseWriter, req *http.Request) { return func(w http.ResponseWriter, req *http.Request) {
trace := utiltrace.New("Get " + req.URL.Path) trace := utiltrace.New("Get " + req.URL.Path)
defer trace.LogIfLong(500 * time.Millisecond) defer trace.LogIfLong(500 * time.Millisecond)
@ -59,7 +59,7 @@ func getResourceHandler(scope RequestScope, getter getterFunc) http.HandlerFunc
ctx := req.Context() ctx := req.Context()
ctx = request.WithNamespace(ctx, namespace) ctx = request.WithNamespace(ctx, namespace)
outputMediaType, _, err := negotiation.NegotiateOutputMediaType(req, scope.Serializer, &scope) outputMediaType, _, err := negotiation.NegotiateOutputMediaType(req, scope.Serializer, scope)
if err != nil { if err != nil {
scope.err(err, w, req) scope.err(err, w, req)
return return
@ -72,14 +72,13 @@ func getResourceHandler(scope RequestScope, getter getterFunc) http.HandlerFunc
} }
trace.Step("About to write a response") trace.Step("About to write a response")
scope.Trace = trace transformResponseObject(ctx, scope, trace, req, w, http.StatusOK, outputMediaType, result)
transformResponseObject(ctx, scope, req, w, http.StatusOK, outputMediaType, result)
trace.Step("Transformed response object") trace.Step("Transformed response object")
} }
} }
// GetResource returns a function that handles retrieving a single resource from a rest.Storage object. // GetResource returns a function that handles retrieving a single resource from a rest.Storage object.
func GetResource(r rest.Getter, e rest.Exporter, scope RequestScope) http.HandlerFunc { func GetResource(r rest.Getter, e rest.Exporter, scope *RequestScope) http.HandlerFunc {
return getResourceHandler(scope, return getResourceHandler(scope,
func(ctx context.Context, name string, req *http.Request, trace *utiltrace.Trace) (runtime.Object, error) { func(ctx context.Context, name string, req *http.Request, trace *utiltrace.Trace) (runtime.Object, error) {
// check for export // check for export
@ -109,7 +108,7 @@ func GetResource(r rest.Getter, e rest.Exporter, scope RequestScope) http.Handle
} }
// GetResourceWithOptions returns a function that handles retrieving a single resource from a rest.Storage object. // GetResourceWithOptions returns a function that handles retrieving a single resource from a rest.Storage object.
func GetResourceWithOptions(r rest.GetterWithOptions, scope RequestScope, isSubresource bool) http.HandlerFunc { func GetResourceWithOptions(r rest.GetterWithOptions, scope *RequestScope, isSubresource bool) http.HandlerFunc {
return getResourceHandler(scope, return getResourceHandler(scope,
func(ctx context.Context, name string, req *http.Request, trace *utiltrace.Trace) (runtime.Object, error) { func(ctx context.Context, name string, req *http.Request, trace *utiltrace.Trace) (runtime.Object, error) {
opts, subpath, subpathKey := r.NewGetOptions() opts, subpath, subpathKey := r.NewGetOptions()
@ -126,7 +125,7 @@ func GetResourceWithOptions(r rest.GetterWithOptions, scope RequestScope, isSubr
} }
// getRequestOptions parses out options and can include path information. The path information shouldn't include the subresource. // getRequestOptions parses out options and can include path information. The path information shouldn't include the subresource.
func getRequestOptions(req *http.Request, scope RequestScope, into runtime.Object, subpath bool, subpathKey string, isSubresource bool) error { func getRequestOptions(req *http.Request, scope *RequestScope, into runtime.Object, subpath bool, subpathKey string, isSubresource bool) error {
if into == nil { if into == nil {
return nil return nil
} }
@ -163,7 +162,7 @@ func getRequestOptions(req *http.Request, scope RequestScope, into runtime.Objec
return scope.ParameterCodec.DecodeParameters(query, scope.Kind.GroupVersion(), into) return scope.ParameterCodec.DecodeParameters(query, scope.Kind.GroupVersion(), into)
} }
func ListResource(r rest.Lister, rw rest.Watcher, scope RequestScope, forceWatch bool, minRequestTimeout time.Duration) http.HandlerFunc { func ListResource(r rest.Lister, rw rest.Watcher, scope *RequestScope, forceWatch bool, minRequestTimeout time.Duration) http.HandlerFunc {
return func(w http.ResponseWriter, req *http.Request) { return func(w http.ResponseWriter, req *http.Request) {
// For performance tracking purposes. // For performance tracking purposes.
trace := utiltrace.New("List " + req.URL.Path) trace := utiltrace.New("List " + req.URL.Path)
@ -185,7 +184,7 @@ func ListResource(r rest.Lister, rw rest.Watcher, scope RequestScope, forceWatch
ctx := req.Context() ctx := req.Context()
ctx = request.WithNamespace(ctx, namespace) ctx = request.WithNamespace(ctx, namespace)
outputMediaType, _, err := negotiation.NegotiateOutputMediaType(req, scope.Serializer, &scope) outputMediaType, _, err := negotiation.NegotiateOutputMediaType(req, scope.Serializer, scope)
if err != nil { if err != nil {
scope.err(err, w, req) scope.err(err, w, req)
return return
@ -272,8 +271,7 @@ func ListResource(r rest.Lister, rw rest.Watcher, scope RequestScope, forceWatch
} }
trace.Step("Listing from storage done") trace.Step("Listing from storage done")
scope.Trace = trace transformResponseObject(ctx, scope, trace, req, w, http.StatusOK, outputMediaType, result)
transformResponseObject(ctx, scope, req, w, http.StatusOK, outputMediaType, result)
trace.Step(fmt.Sprintf("Writing http response done (%d items)", meta.LenList(result))) trace.Step(fmt.Sprintf("Writing http response done (%d items)", meta.LenList(result)))
} }
} }

View File

@ -55,7 +55,7 @@ const (
) )
// PatchResource returns a function that will handle a resource patch. // PatchResource returns a function that will handle a resource patch.
func PatchResource(r rest.Patcher, scope RequestScope, admit admission.Interface, patchTypes []string) http.HandlerFunc { func PatchResource(r rest.Patcher, scope *RequestScope, admit admission.Interface, patchTypes []string) http.HandlerFunc {
return func(w http.ResponseWriter, req *http.Request) { return func(w http.ResponseWriter, req *http.Request) {
// For performance tracking purposes. // For performance tracking purposes.
trace := utiltrace.New("Patch " + req.URL.Path) trace := utiltrace.New("Patch " + req.URL.Path)
@ -95,7 +95,7 @@ func PatchResource(r rest.Patcher, scope RequestScope, admit admission.Interface
ctx := req.Context() ctx := req.Context()
ctx = request.WithNamespace(ctx, namespace) ctx = request.WithNamespace(ctx, namespace)
outputMediaType, _, err := negotiation.NegotiateOutputMediaType(req, scope.Serializer, &scope) outputMediaType, _, err := negotiation.NegotiateOutputMediaType(req, scope.Serializer, scope)
if err != nil { if err != nil {
scope.err(err, w, req) scope.err(err, w, req)
return return
@ -191,12 +191,12 @@ func PatchResource(r rest.Patcher, scope RequestScope, admit admission.Interface
subresource: scope.Subresource, subresource: scope.Subresource,
dryRun: dryrun.IsDryRun(options.DryRun), dryRun: dryrun.IsDryRun(options.DryRun),
objectInterfaces: &scope, objectInterfaces: scope,
hubGroupVersion: scope.HubGroupVersion, hubGroupVersion: scope.HubGroupVersion,
createValidation: withAuthorization(rest.AdmissionToValidateObjectFunc(admit, staticCreateAttributes, &scope), scope.Authorizer, createAuthorizerAttributes), createValidation: withAuthorization(rest.AdmissionToValidateObjectFunc(admit, staticCreateAttributes, scope), scope.Authorizer, createAuthorizerAttributes),
updateValidation: rest.AdmissionToValidateObjectUpdateFunc(admit, staticUpdateAttributes, &scope), updateValidation: rest.AdmissionToValidateObjectUpdateFunc(admit, staticUpdateAttributes, scope),
admissionCheck: mutatingAdmission, admissionCheck: mutatingAdmission,
codec: codec, codec: codec,
@ -235,8 +235,7 @@ func PatchResource(r rest.Patcher, scope RequestScope, admit admission.Interface
if wasCreated { if wasCreated {
status = http.StatusCreated status = http.StatusCreated
} }
scope.Trace = trace transformResponseObject(ctx, scope, trace, req, w, status, outputMediaType, result)
transformResponseObject(ctx, scope, req, w, status, outputMediaType, result)
} }
} }
@ -517,7 +516,7 @@ func (p *patcher) applyAdmission(ctx context.Context, patchedObject runtime.Obje
} }
// patchResource divides PatchResource for easier unit testing // patchResource divides PatchResource for easier unit testing
func (p *patcher) patchResource(ctx context.Context, scope RequestScope) (runtime.Object, bool, error) { func (p *patcher) patchResource(ctx context.Context, scope *RequestScope) (runtime.Object, bool, error) {
p.namespace = request.NamespaceValue(ctx) p.namespace = request.NamespaceValue(ctx)
switch p.patchType { switch p.patchType {
case types.JSONPatchType, types.MergePatchType: case types.JSONPatchType, types.MergePatchType:

View File

@ -31,12 +31,13 @@ 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"
utiltrace "k8s.io/utils/trace"
) )
// transformObject takes the object as returned by storage and ensures it is in // transformObject takes the object as returned by storage and ensures it is in
// the client's desired form, as well as ensuring any API level fields like self-link // the client's desired form, as well as ensuring any API level fields like self-link
// are properly set. // are properly set.
func transformObject(ctx context.Context, obj runtime.Object, opts interface{}, mediaType negotiation.MediaTypeOptions, scope RequestScope, req *http.Request) (runtime.Object, error) { func transformObject(ctx context.Context, obj runtime.Object, opts interface{}, mediaType negotiation.MediaTypeOptions, scope *RequestScope, req *http.Request) (runtime.Object, error) {
if _, ok := obj.(*metav1.Status); ok { if _, ok := obj.(*metav1.Status); ok {
return obj, nil return obj, nil
} }
@ -104,7 +105,7 @@ func targetEncodingForTransform(scope *RequestScope, mediaType negotiation.Media
// transformResponseObject takes an object loaded from storage and performs any necessary transformations. // transformResponseObject takes an object loaded from storage and performs any necessary transformations.
// Will write the complete response object. // Will write the complete response object.
func transformResponseObject(ctx context.Context, scope RequestScope, req *http.Request, w http.ResponseWriter, statusCode int, mediaType negotiation.MediaTypeOptions, result runtime.Object) { func transformResponseObject(ctx context.Context, scope *RequestScope, trace *utiltrace.Trace, req *http.Request, w http.ResponseWriter, statusCode int, mediaType negotiation.MediaTypeOptions, result runtime.Object) {
options, err := optionsForTransform(mediaType, req) options, err := optionsForTransform(mediaType, req)
if err != nil { if err != nil {
scope.err(err, w, req) scope.err(err, w, req)
@ -115,8 +116,8 @@ func transformResponseObject(ctx context.Context, scope RequestScope, req *http.
scope.err(err, w, req) scope.err(err, w, req)
return return
} }
kind, serializer, _ := targetEncodingForTransform(&scope, mediaType, req) kind, serializer, _ := targetEncodingForTransform(scope, mediaType, req)
responsewriters.WriteObjectNegotiated(serializer, &scope, kind.GroupVersion(), w, req, statusCode, obj) responsewriters.WriteObjectNegotiated(serializer, scope, kind.GroupVersion(), w, req, statusCode, obj)
} }
// errNotAcceptable indicates Accept negotiation has failed // errNotAcceptable indicates Accept negotiation has failed
@ -141,7 +142,7 @@ func (e errNotAcceptable) Status() metav1.Status {
} }
} }
func asV1Beta1Table(ctx context.Context, result runtime.Object, opts *metav1beta1.TableOptions, scope RequestScope) (runtime.Object, error) { func asV1Beta1Table(ctx context.Context, result runtime.Object, opts *metav1beta1.TableOptions, scope *RequestScope) (runtime.Object, error) {
table, err := scope.TableConvertor.ConvertToTable(ctx, result, opts) table, err := scope.TableConvertor.ConvertToTable(ctx, result, opts)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -42,7 +42,6 @@ import (
"k8s.io/apiserver/pkg/endpoints/request" "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest" "k8s.io/apiserver/pkg/registry/rest"
"k8s.io/klog" "k8s.io/klog"
utiltrace "k8s.io/utils/trace"
) )
// RequestScope encapsulates common fields across all RESTful handler methods. // RequestScope encapsulates common fields across all RESTful handler methods.
@ -58,7 +57,6 @@ type RequestScope struct {
Typer runtime.ObjectTyper Typer runtime.ObjectTyper
UnsafeConvertor runtime.ObjectConvertor UnsafeConvertor runtime.ObjectConvertor
Authorizer authorizer.Authorizer Authorizer authorizer.Authorizer
Trace *utiltrace.Trace
TableConvertor rest.TableConvertor TableConvertor rest.TableConvertor
FieldManager *fieldmanager.FieldManager FieldManager *fieldmanager.FieldManager
@ -113,7 +111,7 @@ func (r *RequestScope) GetObjectDefaulter() runtime.ObjectDefaulter { return r.D
func (r *RequestScope) GetObjectConvertor() runtime.ObjectConvertor { return r.Convertor } func (r *RequestScope) GetObjectConvertor() runtime.ObjectConvertor { return r.Convertor }
// ConnectResource returns a function that handles a connect request on a rest.Storage object. // ConnectResource returns a function that handles a connect request on a rest.Storage object.
func ConnectResource(connecter rest.Connecter, scope RequestScope, admit admission.Interface, restPath string, isSubresource bool) http.HandlerFunc { func ConnectResource(connecter rest.Connecter, scope *RequestScope, admit admission.Interface, restPath string, isSubresource bool) http.HandlerFunc {
return func(w http.ResponseWriter, req *http.Request) { return func(w http.ResponseWriter, req *http.Request) {
if isDryRun(req.URL) { if isDryRun(req.URL) {
scope.err(errors.NewBadRequest("dryRun is not supported"), w, req) scope.err(errors.NewBadRequest("dryRun is not supported"), w, req)
@ -140,14 +138,14 @@ func ConnectResource(connecter rest.Connecter, scope RequestScope, admit admissi
userInfo, _ := request.UserFrom(ctx) userInfo, _ := request.UserFrom(ctx)
// TODO: remove the mutating admission here as soon as we have ported all plugin that handle CONNECT // TODO: remove the mutating admission here as soon as we have ported all plugin that handle CONNECT
if mutatingAdmission, ok := admit.(admission.MutationInterface); ok { if mutatingAdmission, ok := admit.(admission.MutationInterface); ok {
err = mutatingAdmission.Admit(admission.NewAttributesRecord(opts, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Connect, false, userInfo), &scope) err = mutatingAdmission.Admit(admission.NewAttributesRecord(opts, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Connect, false, userInfo), scope)
if err != nil { if err != nil {
scope.err(err, w, req) scope.err(err, w, req)
return return
} }
} }
if validatingAdmission, ok := admit.(admission.ValidationInterface); ok { if validatingAdmission, ok := admit.(admission.ValidationInterface); ok {
err = validatingAdmission.Validate(admission.NewAttributesRecord(opts, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Connect, false, userInfo), &scope) err = validatingAdmission.Validate(admission.NewAttributesRecord(opts, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Connect, false, userInfo), scope)
if err != nil { if err != nil {
scope.err(err, w, req) scope.err(err, w, req)
return return
@ -168,13 +166,13 @@ func ConnectResource(connecter rest.Connecter, scope RequestScope, admit admissi
// responder implements rest.Responder for assisting a connector in writing objects or errors. // responder implements rest.Responder for assisting a connector in writing objects or errors.
type responder struct { type responder struct {
scope RequestScope scope *RequestScope
req *http.Request req *http.Request
w http.ResponseWriter w http.ResponseWriter
} }
func (r *responder) Object(statusCode int, obj runtime.Object) { func (r *responder) Object(statusCode int, obj runtime.Object) {
responsewriters.WriteObjectNegotiated(r.scope.Serializer, &r.scope, r.scope.Kind.GroupVersion(), r.w, r.req, statusCode, obj) responsewriters.WriteObjectNegotiated(r.scope.Serializer, r.scope, r.scope.Kind.GroupVersion(), r.w, r.req, statusCode, obj)
} }
func (r *responder) Error(err error) { func (r *responder) Error(err error) {

View File

@ -462,7 +462,7 @@ func (tc *patchTestCase) Run(t *testing.T) {
trace: utiltrace.New("Patch" + name), trace: utiltrace.New("Patch" + name),
} }
resultObj, _, err := p.patchResource(ctx, RequestScope{}) resultObj, _, err := p.patchResource(ctx, &RequestScope{})
if len(tc.expectedError) != 0 { if len(tc.expectedError) != 0 {
if err == nil || err.Error() != tc.expectedError { if err == nil || err.Error() != tc.expectedError {
t.Errorf("%s: expected error %v, but got %v", tc.name, tc.expectedError, err) t.Errorf("%s: expected error %v, but got %v", tc.name, tc.expectedError, err)

View File

@ -42,7 +42,7 @@ import (
) )
// UpdateResource returns a function that will handle a resource update // UpdateResource returns a function that will handle a resource update
func UpdateResource(r rest.Updater, scope RequestScope, admit admission.Interface) http.HandlerFunc { func UpdateResource(r rest.Updater, scope *RequestScope, admit admission.Interface) http.HandlerFunc {
return func(w http.ResponseWriter, req *http.Request) { return func(w http.ResponseWriter, req *http.Request) {
// For performance tracking purposes. // For performance tracking purposes.
trace := utiltrace.New("Update " + req.URL.Path) trace := utiltrace.New("Update " + req.URL.Path)
@ -64,7 +64,7 @@ func UpdateResource(r rest.Updater, scope RequestScope, admit admission.Interfac
ctx := req.Context() ctx := req.Context()
ctx = request.WithNamespace(ctx, namespace) ctx = request.WithNamespace(ctx, namespace)
outputMediaType, _, err := negotiation.NegotiateOutputMediaType(req, scope.Serializer, &scope) outputMediaType, _, err := negotiation.NegotiateOutputMediaType(req, scope.Serializer, scope)
if err != nil { if err != nil {
scope.err(err, w, req) scope.err(err, w, req)
return return
@ -138,11 +138,11 @@ func UpdateResource(r rest.Updater, scope RequestScope, admit admission.Interfac
return nil, fmt.Errorf("unexpected error when extracting UID from oldObj: %v", err.Error()) return nil, fmt.Errorf("unexpected error when extracting UID from oldObj: %v", err.Error())
} else if !isNotZeroObject { } else if !isNotZeroObject {
if mutatingAdmission.Handles(admission.Create) { if mutatingAdmission.Handles(admission.Create) {
return newObj, mutatingAdmission.Admit(admission.NewAttributesRecord(newObj, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Create, dryrun.IsDryRun(options.DryRun), userInfo), &scope) return newObj, mutatingAdmission.Admit(admission.NewAttributesRecord(newObj, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Create, dryrun.IsDryRun(options.DryRun), userInfo), scope)
} }
} else { } else {
if mutatingAdmission.Handles(admission.Update) { if mutatingAdmission.Handles(admission.Update) {
return newObj, mutatingAdmission.Admit(admission.NewAttributesRecord(newObj, oldObj, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Update, dryrun.IsDryRun(options.DryRun), userInfo), &scope) return newObj, mutatingAdmission.Admit(admission.NewAttributesRecord(newObj, oldObj, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Update, dryrun.IsDryRun(options.DryRun), userInfo), scope)
} }
} }
return newObj, nil return newObj, nil
@ -172,11 +172,11 @@ func UpdateResource(r rest.Updater, scope RequestScope, admit admission.Interfac
rest.DefaultUpdatedObjectInfo(obj, transformers...), rest.DefaultUpdatedObjectInfo(obj, transformers...),
withAuthorization(rest.AdmissionToValidateObjectFunc( withAuthorization(rest.AdmissionToValidateObjectFunc(
admit, admit,
admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Create, dryrun.IsDryRun(options.DryRun), userInfo), &scope), admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Create, dryrun.IsDryRun(options.DryRun), userInfo), scope),
scope.Authorizer, createAuthorizerAttributes), scope.Authorizer, createAuthorizerAttributes),
rest.AdmissionToValidateObjectUpdateFunc( rest.AdmissionToValidateObjectUpdateFunc(
admit, admit,
admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Update, dryrun.IsDryRun(options.DryRun), userInfo), &scope), admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Update, dryrun.IsDryRun(options.DryRun), userInfo), scope),
false, false,
options, options,
) )
@ -194,8 +194,7 @@ func UpdateResource(r rest.Updater, scope RequestScope, admit admission.Interfac
status = http.StatusCreated status = http.StatusCreated
} }
scope.Trace = trace transformResponseObject(ctx, scope, trace, req, w, status, outputMediaType, result)
transformResponseObject(ctx, scope, req, w, status, outputMediaType, result)
} }
} }

View File

@ -63,7 +63,7 @@ func (w *realTimeoutFactory) TimeoutCh() (<-chan time.Time, func() bool) {
// serveWatch will serve a watch response. // serveWatch will serve a watch response.
// TODO: the functionality in this method and in WatchServer.Serve is not cleanly decoupled. // TODO: the functionality in this method and in WatchServer.Serve is not cleanly decoupled.
func serveWatch(watcher watch.Interface, scope RequestScope, mediaTypeOptions negotiation.MediaTypeOptions, req *http.Request, w http.ResponseWriter, timeout time.Duration) { func serveWatch(watcher watch.Interface, scope *RequestScope, mediaTypeOptions negotiation.MediaTypeOptions, req *http.Request, w http.ResponseWriter, timeout time.Duration) {
options, err := optionsForTransform(mediaTypeOptions, req) options, err := optionsForTransform(mediaTypeOptions, req)
if err != nil { if err != nil {
scope.err(err, w, req) scope.err(err, w, req)
@ -71,7 +71,7 @@ func serveWatch(watcher watch.Interface, scope RequestScope, mediaTypeOptions ne
} }
// negotiate for the stream serializer from the scope's serializer // negotiate for the stream serializer from the scope's serializer
serializer, err := negotiation.NegotiateOutputMediaTypeStream(req, scope.Serializer, &scope) serializer, err := negotiation.NegotiateOutputMediaTypeStream(req, scope.Serializer, scope)
if err != nil { if err != nil {
scope.err(err, w, req) scope.err(err, w, req)
return return
@ -92,7 +92,7 @@ func serveWatch(watcher watch.Interface, scope RequestScope, mediaTypeOptions ne
// locate the appropriate embedded encoder based on the transform // locate the appropriate embedded encoder based on the transform
var embeddedEncoder runtime.Encoder var embeddedEncoder runtime.Encoder
contentKind, contentSerializer, transform := targetEncodingForTransform(&scope, mediaTypeOptions, req) contentKind, contentSerializer, transform := targetEncodingForTransform(scope, mediaTypeOptions, req)
if transform { if transform {
var embedded runtime.Serializer var embedded runtime.Serializer
for _, supported := range contentSerializer.SupportedMediaTypes() { for _, supported := range contentSerializer.SupportedMediaTypes() {
@ -145,7 +145,7 @@ func serveWatch(watcher watch.Interface, scope RequestScope, mediaTypeOptions ne
// WatchServer serves a watch.Interface over a websocket or vanilla HTTP. // WatchServer serves a watch.Interface over a websocket or vanilla HTTP.
type WatchServer struct { type WatchServer struct {
Watching watch.Interface Watching watch.Interface
Scope RequestScope Scope *RequestScope
// true if websocket messages should use text framing (as opposed to binary framing) // true if websocket messages should use text framing (as opposed to binary framing)
UseTextFraming bool UseTextFraming bool

View File

@ -1076,60 +1076,60 @@ func isVowel(c rune) bool {
func restfulListResource(r rest.Lister, rw rest.Watcher, scope handlers.RequestScope, forceWatch bool, minRequestTimeout time.Duration) restful.RouteFunction { func restfulListResource(r rest.Lister, rw rest.Watcher, scope handlers.RequestScope, forceWatch bool, minRequestTimeout time.Duration) restful.RouteFunction {
return func(req *restful.Request, res *restful.Response) { return func(req *restful.Request, res *restful.Response) {
handlers.ListResource(r, rw, scope, forceWatch, minRequestTimeout)(res.ResponseWriter, req.Request) handlers.ListResource(r, rw, &scope, forceWatch, minRequestTimeout)(res.ResponseWriter, req.Request)
} }
} }
func restfulCreateNamedResource(r rest.NamedCreater, scope handlers.RequestScope, admit admission.Interface) restful.RouteFunction { func restfulCreateNamedResource(r rest.NamedCreater, scope handlers.RequestScope, admit admission.Interface) restful.RouteFunction {
return func(req *restful.Request, res *restful.Response) { return func(req *restful.Request, res *restful.Response) {
handlers.CreateNamedResource(r, scope, admit)(res.ResponseWriter, req.Request) handlers.CreateNamedResource(r, &scope, admit)(res.ResponseWriter, req.Request)
} }
} }
func restfulCreateResource(r rest.Creater, scope handlers.RequestScope, admit admission.Interface) restful.RouteFunction { func restfulCreateResource(r rest.Creater, scope handlers.RequestScope, admit admission.Interface) restful.RouteFunction {
return func(req *restful.Request, res *restful.Response) { return func(req *restful.Request, res *restful.Response) {
handlers.CreateResource(r, scope, admit)(res.ResponseWriter, req.Request) handlers.CreateResource(r, &scope, admit)(res.ResponseWriter, req.Request)
} }
} }
func restfulDeleteResource(r rest.GracefulDeleter, allowsOptions bool, scope handlers.RequestScope, admit admission.Interface) restful.RouteFunction { func restfulDeleteResource(r rest.GracefulDeleter, allowsOptions bool, scope handlers.RequestScope, admit admission.Interface) restful.RouteFunction {
return func(req *restful.Request, res *restful.Response) { return func(req *restful.Request, res *restful.Response) {
handlers.DeleteResource(r, allowsOptions, scope, admit)(res.ResponseWriter, req.Request) handlers.DeleteResource(r, allowsOptions, &scope, admit)(res.ResponseWriter, req.Request)
} }
} }
func restfulDeleteCollection(r rest.CollectionDeleter, checkBody bool, scope handlers.RequestScope, admit admission.Interface) restful.RouteFunction { func restfulDeleteCollection(r rest.CollectionDeleter, checkBody bool, scope handlers.RequestScope, admit admission.Interface) restful.RouteFunction {
return func(req *restful.Request, res *restful.Response) { return func(req *restful.Request, res *restful.Response) {
handlers.DeleteCollection(r, checkBody, scope, admit)(res.ResponseWriter, req.Request) handlers.DeleteCollection(r, checkBody, &scope, admit)(res.ResponseWriter, req.Request)
} }
} }
func restfulUpdateResource(r rest.Updater, scope handlers.RequestScope, admit admission.Interface) restful.RouteFunction { func restfulUpdateResource(r rest.Updater, scope handlers.RequestScope, admit admission.Interface) restful.RouteFunction {
return func(req *restful.Request, res *restful.Response) { return func(req *restful.Request, res *restful.Response) {
handlers.UpdateResource(r, scope, admit)(res.ResponseWriter, req.Request) handlers.UpdateResource(r, &scope, admit)(res.ResponseWriter, req.Request)
} }
} }
func restfulPatchResource(r rest.Patcher, scope handlers.RequestScope, admit admission.Interface, supportedTypes []string) restful.RouteFunction { func restfulPatchResource(r rest.Patcher, scope handlers.RequestScope, admit admission.Interface, supportedTypes []string) restful.RouteFunction {
return func(req *restful.Request, res *restful.Response) { return func(req *restful.Request, res *restful.Response) {
handlers.PatchResource(r, scope, admit, supportedTypes)(res.ResponseWriter, req.Request) handlers.PatchResource(r, &scope, admit, supportedTypes)(res.ResponseWriter, req.Request)
} }
} }
func restfulGetResource(r rest.Getter, e rest.Exporter, scope handlers.RequestScope) restful.RouteFunction { func restfulGetResource(r rest.Getter, e rest.Exporter, scope handlers.RequestScope) restful.RouteFunction {
return func(req *restful.Request, res *restful.Response) { return func(req *restful.Request, res *restful.Response) {
handlers.GetResource(r, e, scope)(res.ResponseWriter, req.Request) handlers.GetResource(r, e, &scope)(res.ResponseWriter, req.Request)
} }
} }
func restfulGetResourceWithOptions(r rest.GetterWithOptions, scope handlers.RequestScope, isSubresource bool) restful.RouteFunction { func restfulGetResourceWithOptions(r rest.GetterWithOptions, scope handlers.RequestScope, isSubresource bool) restful.RouteFunction {
return func(req *restful.Request, res *restful.Response) { return func(req *restful.Request, res *restful.Response) {
handlers.GetResourceWithOptions(r, scope, isSubresource)(res.ResponseWriter, req.Request) handlers.GetResourceWithOptions(r, &scope, isSubresource)(res.ResponseWriter, req.Request)
} }
} }
func restfulConnectResource(connecter rest.Connecter, scope handlers.RequestScope, admit admission.Interface, restPath string, isSubresource bool) restful.RouteFunction { func restfulConnectResource(connecter rest.Connecter, scope handlers.RequestScope, admit admission.Interface, restPath string, isSubresource bool) restful.RouteFunction {
return func(req *restful.Request, res *restful.Response) { return func(req *restful.Request, res *restful.Response) {
handlers.ConnectResource(connecter, scope, admit, restPath, isSubresource)(res.ResponseWriter, req.Request) handlers.ConnectResource(connecter, &scope, admit, restPath, isSubresource)(res.ResponseWriter, req.Request)
} }
} }

View File

@ -579,6 +579,7 @@ func TestWatchHTTPErrors(t *testing.T) {
// Setup a new watchserver // Setup a new watchserver
watchServer := &handlers.WatchServer{ watchServer := &handlers.WatchServer{
Scope: &handlers.RequestScope{},
Watching: watcher, Watching: watcher,
MediaType: "testcase/json", MediaType: "testcase/json",
@ -639,6 +640,7 @@ func TestWatchHTTPDynamicClientErrors(t *testing.T) {
// Setup a new watchserver // Setup a new watchserver
watchServer := &handlers.WatchServer{ watchServer := &handlers.WatchServer{
Scope: &handlers.RequestScope{},
Watching: watcher, Watching: watcher,
MediaType: "testcase/json", MediaType: "testcase/json",
@ -701,6 +703,7 @@ func TestWatchHTTPTimeout(t *testing.T) {
// Setup a new watchserver // Setup a new watchserver
watchServer := &handlers.WatchServer{ watchServer := &handlers.WatchServer{
Scope: &handlers.RequestScope{},
Watching: watcher, Watching: watcher,
MediaType: "testcase/json", MediaType: "testcase/json",