@@ -35,9 +35,11 @@ import (
35
35
"k8s.io/apimachinery/pkg/apis/meta/v1/validation"
36
36
"k8s.io/apimachinery/pkg/runtime"
37
37
"k8s.io/apimachinery/pkg/runtime/schema"
38
+ cbor "k8s.io/apimachinery/pkg/runtime/serializer/cbor/direct"
38
39
"k8s.io/apimachinery/pkg/types"
39
40
"k8s.io/apimachinery/pkg/util/managedfields"
40
41
"k8s.io/apimachinery/pkg/util/mergepatch"
42
+ utilruntime "k8s.io/apimachinery/pkg/util/runtime"
41
43
"k8s.io/apimachinery/pkg/util/sets"
42
44
"k8s.io/apimachinery/pkg/util/strategicpatch"
43
45
"k8s.io/apimachinery/pkg/util/validation/field"
@@ -50,8 +52,10 @@ import (
50
52
requestmetrics "k8s.io/apiserver/pkg/endpoints/handlers/metrics"
51
53
"k8s.io/apiserver/pkg/endpoints/handlers/negotiation"
52
54
"k8s.io/apiserver/pkg/endpoints/request"
55
+ "k8s.io/apiserver/pkg/features"
53
56
"k8s.io/apiserver/pkg/registry/rest"
54
57
"k8s.io/apiserver/pkg/util/dryrun"
58
+ utilfeature "k8s.io/apiserver/pkg/util/feature"
55
59
"k8s.io/component-base/tracing"
56
60
)
57
61
@@ -129,10 +133,25 @@ func PatchResource(r rest.Patcher, scope *RequestScope, admit admission.Interfac
129
133
audit .LogRequestPatch (req .Context (), patchBytes )
130
134
span .AddEvent ("Recorded the audit event" )
131
135
132
- baseContentType := runtime .ContentTypeJSON
133
- if patchType == types .ApplyPatchType {
136
+ var baseContentType string
137
+ switch patchType {
138
+ case types .ApplyYAMLPatchType :
134
139
baseContentType = runtime .ContentTypeYAML
140
+ case types .ApplyCBORPatchType :
141
+ if ! utilfeature .TestOnlyFeatureGate .Enabled (features .TestOnlyCBORServingAndStorage ) {
142
+ // This request should have already been rejected by the
143
+ // Content-Type allowlist check. Return 500 because assumptions are
144
+ // already broken and the feature is not GA.
145
+ utilruntime .HandleErrorWithContext (req .Context (), nil , "The patch content-type allowlist check should have made this unreachable." )
146
+ scope .err (errors .NewInternalError (errors .NewInternalError (fmt .Errorf ("unexpected patch type: %v" , patchType ))), w , req )
147
+ return
148
+ }
149
+
150
+ baseContentType = runtime .ContentTypeCBOR
151
+ default :
152
+ baseContentType = runtime .ContentTypeJSON
135
153
}
154
+
136
155
s , ok := runtime .SerializerInfoForMediaType (scope .Serializer .SupportedMediaTypes (), baseContentType )
137
156
if ! ok {
138
157
scope .err (fmt .Errorf ("no serializer defined for %v" , baseContentType ), w , req )
@@ -452,6 +471,20 @@ func (p *smpPatcher) createNewObject(_ context.Context) (runtime.Object, error)
452
471
return nil , errors .NewNotFound (p .resource .GroupResource (), p .name )
453
472
}
454
473
474
+ func newApplyPatcher (p * patcher , fieldManager * managedfields.FieldManager , unmarshalFn , unmarshalStrictFn func ([]byte , interface {}) error ) * applyPatcher {
475
+ return & applyPatcher {
476
+ fieldManager : fieldManager ,
477
+ patch : p .patchBytes ,
478
+ options : p .options ,
479
+ creater : p .creater ,
480
+ kind : p .kind ,
481
+ userAgent : p .userAgent ,
482
+ validationDirective : p .validationDirective ,
483
+ unmarshalFn : unmarshalFn ,
484
+ unmarshalStrictFn : unmarshalStrictFn ,
485
+ }
486
+ }
487
+
455
488
type applyPatcher struct {
456
489
patch []byte
457
490
options * metav1.PatchOptions
@@ -460,6 +493,8 @@ type applyPatcher struct {
460
493
fieldManager * managedfields.FieldManager
461
494
userAgent string
462
495
validationDirective string
496
+ unmarshalFn func (data []byte , v interface {}) error
497
+ unmarshalStrictFn func (data []byte , v interface {}) error
463
498
}
464
499
465
500
func (p * applyPatcher ) applyPatchToCurrentObject (requestContext context.Context , obj runtime.Object ) (runtime.Object , error ) {
@@ -472,7 +507,7 @@ func (p *applyPatcher) applyPatchToCurrentObject(requestContext context.Context,
472
507
}
473
508
474
509
patchObj := & unstructured.Unstructured {Object : map [string ]interface {}{}}
475
- if err := yaml . Unmarshal (p .patch , & patchObj .Object ); err != nil {
510
+ if err := p . unmarshalFn (p .patch , & patchObj .Object ); err != nil {
476
511
return nil , errors .NewBadRequest (fmt .Sprintf ("error decoding YAML: %v" , err ))
477
512
}
478
513
@@ -484,7 +519,7 @@ func (p *applyPatcher) applyPatchToCurrentObject(requestContext context.Context,
484
519
// TODO: spawn something to track deciding whether a fieldValidation=Strict
485
520
// fatal error should return before an error from the apply operation
486
521
if p .validationDirective == metav1 .FieldValidationStrict || p .validationDirective == metav1 .FieldValidationWarn {
487
- if err := yaml . UnmarshalStrict (p .patch , & map [string ]interface {}{}); err != nil {
522
+ if err := p . unmarshalStrictFn (p .patch , & map [string ]interface {}{}); err != nil {
488
523
if p .validationDirective == metav1 .FieldValidationStrict {
489
524
return nil , errors .NewBadRequest (fmt .Sprintf ("error strict decoding YAML: %v" , err ))
490
525
}
@@ -634,16 +669,21 @@ func (p *patcher) patchResource(ctx context.Context, scope *RequestScope) (runti
634
669
fieldManager : scope .FieldManager ,
635
670
}
636
671
// this case is unreachable if ServerSideApply is not enabled because we will have already rejected the content type
637
- case types .ApplyPatchType :
638
- p .mechanism = & applyPatcher {
639
- fieldManager : scope .FieldManager ,
640
- patch : p .patchBytes ,
641
- options : p .options ,
642
- creater : p .creater ,
643
- kind : p .kind ,
644
- userAgent : p .userAgent ,
645
- validationDirective : p .validationDirective ,
646
- }
672
+ case types .ApplyYAMLPatchType :
673
+ p .mechanism = newApplyPatcher (p , scope .FieldManager , yaml .Unmarshal , yaml .UnmarshalStrict )
674
+ p .forceAllowCreate = true
675
+ case types .ApplyCBORPatchType :
676
+ if ! utilfeature .TestOnlyFeatureGate .Enabled (features .TestOnlyCBORServingAndStorage ) {
677
+ utilruntime .HandleErrorWithContext (context .TODO (), nil , "CBOR apply requests should be rejected before reaching this point unless the feature gate is enabled." )
678
+ return nil , false , fmt .Errorf ("%v: unimplemented patch type" , p .patchType )
679
+ }
680
+
681
+ // The strict and non-strict funcs are the same here because any CBOR map with
682
+ // duplicate keys is invalid and always rejected outright regardless of strictness
683
+ // mode, and unknown field errors can't occur in practice because the type of the
684
+ // destination value for unmarshaling an apply configuration is always
685
+ // "unstructured".
686
+ p .mechanism = newApplyPatcher (p , scope .FieldManager , cbor .Unmarshal , cbor .Unmarshal )
647
687
p .forceAllowCreate = true
648
688
default :
649
689
return nil , false , fmt .Errorf ("%v: unimplemented patch type" , p .patchType )
@@ -670,7 +710,7 @@ func (p *patcher) patchResource(ctx context.Context, scope *RequestScope) (runti
670
710
result , err := requestFunc ()
671
711
// If the object wasn't committed to storage because it's serialized size was too large,
672
712
// it is safe to remove managedFields (which can be large) and try again.
673
- if isTooLargeError (err ) && p .patchType != types .ApplyPatchType {
713
+ if isTooLargeError (err ) && p .patchType != types .ApplyYAMLPatchType && p . patchType != types . ApplyCBORPatchType {
674
714
if _ , accessorErr := meta .Accessor (p .restPatcher .New ()); accessorErr == nil {
675
715
p .updatedObjectInfo = rest .DefaultUpdatedObjectInfo (nil ,
676
716
p .applyPatch ,
0 commit comments