Merge pull request #87508 from jennybuckley/large-obj
Don't save managedFields if object is too large Kubernetes-commit: a019609d5dd07d923b3deed70335ccb60ffa9d15
This commit is contained in:
		
						commit
						94fd1da67f
					
				|  | @ -596,7 +596,7 @@ | |||
| 		}, | ||||
| 		{ | ||||
| 			"ImportPath": "k8s.io/client-go", | ||||
| 			"Rev": "ca7edf3d8a93" | ||||
| 			"Rev": "ec0a5f3fd2ba" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"ImportPath": "k8s.io/component-base", | ||||
|  |  | |||
							
								
								
									
										4
									
								
								go.mod
								
								
								
								
							
							
						
						
									
										4
									
								
								go.mod
								
								
								
								
							|  | @ -45,7 +45,7 @@ require ( | |||
| 	gotest.tools v2.2.0+incompatible // indirect | ||||
| 	k8s.io/api v0.0.0-20200302082247-8f54d34188b0 | ||||
| 	k8s.io/apimachinery v0.0.0-20200302045842-b9f0d37e94c6 | ||||
| 	k8s.io/client-go v0.0.0-20200302082525-ca7edf3d8a93 | ||||
| 	k8s.io/client-go v0.0.0-20200303002533-ec0a5f3fd2ba | ||||
| 	k8s.io/component-base v0.0.0-20200302162701-77f056ceea66 | ||||
| 	k8s.io/klog v1.0.0 | ||||
| 	k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c | ||||
|  | @ -60,6 +60,6 @@ replace ( | |||
| 	golang.org/x/tools => golang.org/x/tools v0.0.0-20190821162956-65e3620a7ae7 // pinned to release-branch.go1.13 | ||||
| 	k8s.io/api => k8s.io/api v0.0.0-20200302082247-8f54d34188b0 | ||||
| 	k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20200302045842-b9f0d37e94c6 | ||||
| 	k8s.io/client-go => k8s.io/client-go v0.0.0-20200302082525-ca7edf3d8a93 | ||||
| 	k8s.io/client-go => k8s.io/client-go v0.0.0-20200303002533-ec0a5f3fd2ba | ||||
| 	k8s.io/component-base => k8s.io/component-base v0.0.0-20200302162701-77f056ceea66 | ||||
| ) | ||||
|  |  | |||
							
								
								
									
										2
									
								
								go.sum
								
								
								
								
							
							
						
						
									
										2
									
								
								go.sum
								
								
								
								
							|  | @ -339,7 +339,7 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh | |||
| honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | ||||
| k8s.io/api v0.0.0-20200302082247-8f54d34188b0/go.mod h1:+2jnw1NMpdXlVDlcy5KjXl7Gh4M0HZ0AhKakQo+KiV8= | ||||
| k8s.io/apimachinery v0.0.0-20200302045842-b9f0d37e94c6/go.mod h1:5X8oEhnd931nEg6/Nkumo00nT6ZsCLp2h7Xwd7Ym6P4= | ||||
| k8s.io/client-go v0.0.0-20200302082525-ca7edf3d8a93/go.mod h1:2DzSdhxUXsi3Ln8q5B+GHLG6b2cQN64WNsTnBYZ8Y4Y= | ||||
| k8s.io/client-go v0.0.0-20200303002533-ec0a5f3fd2ba/go.mod h1:2DzSdhxUXsi3Ln8q5B+GHLG6b2cQN64WNsTnBYZ8Y4Y= | ||||
| k8s.io/component-base v0.0.0-20200302162701-77f056ceea66/go.mod h1:TFzve7JW8Nl3KjrLk5oj8UA+qvh04n0NuTOciggyn1s= | ||||
| k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= | ||||
| k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= | ||||
|  |  | |||
|  | @ -27,6 +27,7 @@ import ( | |||
| 	"unicode/utf8" | ||||
| 
 | ||||
| 	"k8s.io/apimachinery/pkg/api/errors" | ||||
| 	"k8s.io/apimachinery/pkg/api/meta" | ||||
| 	metainternalversionscheme "k8s.io/apimachinery/pkg/apis/meta/internalversion/scheme" | ||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||
| 	"k8s.io/apimachinery/pkg/apis/meta/v1/validation" | ||||
|  | @ -139,6 +140,16 @@ func createHandler(r rest.NamedCreater, scope *RequestScope, admit admission.Int | |||
| 		} | ||||
| 
 | ||||
| 		trace.Step("About to store object in database") | ||||
| 		admissionAttributes := admission.NewAttributesRecord(obj, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Create, options, dryrun.IsDryRun(options.DryRun), userInfo) | ||||
| 		requestFunc := func() (runtime.Object, error) { | ||||
| 			return r.Create( | ||||
| 				ctx, | ||||
| 				name, | ||||
| 				obj, | ||||
| 				rest.AdmissionToValidateObjectFunc(admit, admissionAttributes, scope), | ||||
| 				options, | ||||
| 			) | ||||
| 		} | ||||
| 		result, err := finishRequest(timeout, func() (runtime.Object, error) { | ||||
| 			if scope.FieldManager != nil { | ||||
| 				liveObj, err := scope.Creater.New(scope.Kind) | ||||
|  | @ -150,20 +161,21 @@ func createHandler(r rest.NamedCreater, scope *RequestScope, admit admission.Int | |||
| 					return nil, fmt.Errorf("failed to update object (Create for %v) managed fields: %v", scope.Kind, err) | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			admissionAttributes := admission.NewAttributesRecord(obj, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Create, options, dryrun.IsDryRun(options.DryRun), userInfo) | ||||
| 			if mutatingAdmission, ok := admit.(admission.MutationInterface); ok && mutatingAdmission.Handles(admission.Create) { | ||||
| 				if err := mutatingAdmission.Admit(ctx, admissionAttributes, scope); err != nil { | ||||
| 					return nil, err | ||||
| 				} | ||||
| 			} | ||||
| 			return r.Create( | ||||
| 				ctx, | ||||
| 				name, | ||||
| 				obj, | ||||
| 				rest.AdmissionToValidateObjectFunc(admit, admissionAttributes, scope), | ||||
| 				options, | ||||
| 			) | ||||
| 			result, err := requestFunc() | ||||
| 			// If the object wasn't committed to storage because it's serialized size was too large,
 | ||||
| 			// it is safe to remove managedFields (which can be large) and try again.
 | ||||
| 			if isTooLargeError(err) { | ||||
| 				if accessor, accessorErr := meta.Accessor(obj); accessorErr == nil { | ||||
| 					accessor.SetManagedFields(nil) | ||||
| 					result, err = requestFunc() | ||||
| 				} | ||||
| 			} | ||||
| 			return result, err | ||||
| 		}) | ||||
| 		if err != nil { | ||||
| 			scope.err(err, w, req) | ||||
|  |  | |||
|  | @ -581,12 +581,28 @@ func (p *patcher) patchResource(ctx context.Context, scope *RequestScope) (runti | |||
| 
 | ||||
| 	wasCreated := false | ||||
| 	p.updatedObjectInfo = rest.DefaultUpdatedObjectInfo(nil, p.applyPatch, p.applyAdmission) | ||||
| 	result, err := finishRequest(p.timeout, func() (runtime.Object, error) { | ||||
| 	requestFunc := func() (runtime.Object, error) { | ||||
| 		// Pass in UpdateOptions to override UpdateStrategy.AllowUpdateOnCreate
 | ||||
| 		options := patchToUpdateOptions(p.options) | ||||
| 		updateObject, created, updateErr := p.restPatcher.Update(ctx, p.name, p.updatedObjectInfo, p.createValidation, p.updateValidation, p.forceAllowCreate, options) | ||||
| 		wasCreated = created | ||||
| 		return updateObject, updateErr | ||||
| 	} | ||||
| 	result, err := finishRequest(p.timeout, func() (runtime.Object, error) { | ||||
| 		result, err := requestFunc() | ||||
| 		// If the object wasn't committed to storage because it's serialized size was too large,
 | ||||
| 		// it is safe to remove managedFields (which can be large) and try again.
 | ||||
| 		if isTooLargeError(err) && p.patchType != types.ApplyPatchType { | ||||
| 			if _, accessorErr := meta.Accessor(p.restPatcher.New()); accessorErr == nil { | ||||
| 				p.updatedObjectInfo = rest.DefaultUpdatedObjectInfo(nil, p.applyPatch, p.applyAdmission, func(_ context.Context, obj, _ runtime.Object) (runtime.Object, error) { | ||||
| 					accessor, _ := meta.Accessor(obj) | ||||
| 					accessor.SetManagedFields(nil) | ||||
| 					return obj, nil | ||||
| 				}) | ||||
| 				result, err = requestFunc() | ||||
| 			} | ||||
| 		} | ||||
| 		return result, err | ||||
| 	}) | ||||
| 	return result, wasCreated, err | ||||
| } | ||||
|  |  | |||
|  | @ -25,8 +25,12 @@ import ( | |||
| 	"net/http" | ||||
| 	"net/url" | ||||
| 	goruntime "runtime" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| 
 | ||||
| 	grpccodes "google.golang.org/grpc/codes" | ||||
| 	grpcstatus "google.golang.org/grpc/status" | ||||
| 
 | ||||
| 	"k8s.io/apimachinery/pkg/api/errors" | ||||
| 	"k8s.io/apimachinery/pkg/api/meta" | ||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||
|  | @ -416,3 +420,28 @@ func parseTimeout(str string) time.Duration { | |||
| func isDryRun(url *url.URL) bool { | ||||
| 	return len(url.Query()["dryRun"]) != 0 | ||||
| } | ||||
| 
 | ||||
| type etcdError interface { | ||||
| 	Code() grpccodes.Code | ||||
| 	Error() string | ||||
| } | ||||
| 
 | ||||
| type grpcError interface { | ||||
| 	GRPCStatus() *grpcstatus.Status | ||||
| } | ||||
| 
 | ||||
| func isTooLargeError(err error) bool { | ||||
| 	if err != nil { | ||||
| 		if etcdErr, ok := err.(etcdError); ok { | ||||
| 			if etcdErr.Code() == grpccodes.InvalidArgument && etcdErr.Error() == "etcdserver: request is too large" { | ||||
| 				return true | ||||
| 			} | ||||
| 		} | ||||
| 		if grpcErr, ok := err.(grpcError); ok { | ||||
| 			if grpcErr.GRPCStatus().Code() == grpccodes.ResourceExhausted && strings.Contains(grpcErr.GRPCStatus().Message(), "trying to send message larger than max") { | ||||
| 				return true | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|  |  | |||
|  | @ -24,6 +24,7 @@ import ( | |||
| 	"time" | ||||
| 
 | ||||
| 	"k8s.io/apimachinery/pkg/api/errors" | ||||
| 	"k8s.io/apimachinery/pkg/api/meta" | ||||
| 	metainternalversionscheme "k8s.io/apimachinery/pkg/apis/meta/internalversion/scheme" | ||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||
| 	"k8s.io/apimachinery/pkg/apis/meta/v1/validation" | ||||
|  | @ -124,15 +125,22 @@ func UpdateResource(r rest.Updater, scope *RequestScope, admit admission.Interfa | |||
| 
 | ||||
| 		userInfo, _ := request.UserFrom(ctx) | ||||
| 		transformers := []rest.TransformFunc{} | ||||
| 
 | ||||
| 		// allows skipping managedFields update if the resulting object is too big
 | ||||
| 		shouldUpdateManagedFields := true | ||||
| 		if scope.FieldManager != nil { | ||||
| 			transformers = append(transformers, func(_ context.Context, newObj, liveObj runtime.Object) (runtime.Object, error) { | ||||
| 				obj, err := scope.FieldManager.Update(liveObj, newObj, managerOrUserAgent(options.FieldManager, req.UserAgent())) | ||||
| 				if err != nil { | ||||
| 					return nil, fmt.Errorf("failed to update object (Update for %v) managed fields: %v", scope.Kind, err) | ||||
| 				if shouldUpdateManagedFields { | ||||
| 					obj, err := scope.FieldManager.Update(liveObj, newObj, managerOrUserAgent(options.FieldManager, req.UserAgent())) | ||||
| 					if err != nil { | ||||
| 						return nil, fmt.Errorf("failed to update object (Update for %v) managed fields: %v", scope.Kind, err) | ||||
| 					} | ||||
| 					return obj, nil | ||||
| 				} | ||||
| 				return obj, nil | ||||
| 				return newObj, nil | ||||
| 			}) | ||||
| 		} | ||||
| 
 | ||||
| 		if mutatingAdmission, ok := admit.(admission.MutationInterface); ok { | ||||
| 			transformers = append(transformers, func(ctx context.Context, newObj, oldObj runtime.Object) (runtime.Object, error) { | ||||
| 				isNotZeroObject, err := hasUID(oldObj) | ||||
|  | @ -149,7 +157,6 @@ func UpdateResource(r rest.Updater, scope *RequestScope, admit admission.Interfa | |||
| 				} | ||||
| 				return newObj, nil | ||||
| 			}) | ||||
| 
 | ||||
| 		} | ||||
| 
 | ||||
| 		createAuthorizerAttributes := authorizer.AttributesRecord{ | ||||
|  | @ -167,7 +174,7 @@ func UpdateResource(r rest.Updater, scope *RequestScope, admit admission.Interfa | |||
| 
 | ||||
| 		trace.Step("About to store object in database") | ||||
| 		wasCreated := false | ||||
| 		result, err := finishRequest(timeout, func() (runtime.Object, error) { | ||||
| 		requestFunc := func() (runtime.Object, error) { | ||||
| 			obj, created, err := r.Update( | ||||
| 				ctx, | ||||
| 				name, | ||||
|  | @ -184,6 +191,19 @@ func UpdateResource(r rest.Updater, scope *RequestScope, admit admission.Interfa | |||
| 			) | ||||
| 			wasCreated = created | ||||
| 			return obj, err | ||||
| 		} | ||||
| 		result, err := finishRequest(timeout, func() (runtime.Object, error) { | ||||
| 			result, err := requestFunc() | ||||
| 			// If the object wasn't committed to storage because it's serialized size was too large,
 | ||||
| 			// it is safe to remove managedFields (which can be large) and try again.
 | ||||
| 			if isTooLargeError(err) && scope.FieldManager != nil { | ||||
| 				if accessor, accessorErr := meta.Accessor(obj); accessorErr == nil { | ||||
| 					accessor.SetManagedFields(nil) | ||||
| 					shouldUpdateManagedFields = false | ||||
| 					result, err = requestFunc() | ||||
| 				} | ||||
| 			} | ||||
| 			return result, err | ||||
| 		}) | ||||
| 		if err != nil { | ||||
| 			scope.err(err, w, req) | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue