@@ -24,6 +24,7 @@ import (
24
24
"os"
25
25
"strconv"
26
26
"strings"
27
+ "sync/atomic"
27
28
"time"
28
29
29
30
"github.com/munnerz/goautoneg"
@@ -89,7 +90,7 @@ type RESTClient struct {
89
90
versionedAPIPath string
90
91
91
92
// content describes how a RESTClient encodes and decodes responses.
92
- content ClientContentConfig
93
+ content requestClientContentConfigProvider
93
94
94
95
// creates BackoffManager that is passed to requests.
95
96
createBackoffMgr func () BackoffManager
@@ -119,11 +120,10 @@ func NewRESTClient(baseURL *url.URL, versionedAPIPath string, config ClientConte
119
120
return & RESTClient {
120
121
base : & base ,
121
122
versionedAPIPath : versionedAPIPath ,
122
- content : scrubCBORContentConfigIfDisabled (config ),
123
+ content : requestClientContentConfigProvider { base : scrubCBORContentConfigIfDisabled (config )} ,
123
124
createBackoffMgr : readExpBackoffConfig ,
124
125
rateLimiter : rateLimiter ,
125
-
126
- Client : client ,
126
+ Client : client ,
127
127
}, nil
128
128
}
129
129
@@ -237,5 +237,60 @@ func (c *RESTClient) Delete() *Request {
237
237
238
238
// APIVersion returns the APIVersion this RESTClient is expected to use.
239
239
func (c * RESTClient ) APIVersion () schema.GroupVersion {
240
- return c .content .GroupVersion
240
+ return c .content .GetClientContentConfig ().GroupVersion
241
+ }
242
+
243
+ // requestClientContentConfigProvider observes HTTP 415 (Unsupported Media Type) responses to detect
244
+ // that the server does not understand CBOR. Once this has happened, future requests are forced to
245
+ // use JSON so they can succeed. This is convenient for client users that want to prefer CBOR, but
246
+ // also need to interoperate with older servers so requests do not permanently fail. The clients
247
+ // will not default to using CBOR until at least all supported kube-apiservers have enable-CBOR
248
+ // locked to true, so this path will be rarely taken. Additionally, all generated clients accessing
249
+ // built-in kube resources are forced to protobuf, so those will not degrade to JSON.
250
+ type requestClientContentConfigProvider struct {
251
+ base ClientContentConfig
252
+
253
+ // Becomes permanently true if a server responds with HTTP 415 (Unsupported Media Type) to a
254
+ // request with "Content-Type" header containing the CBOR media type.
255
+ sawUnsupportedMediaTypeForCBOR atomic.Bool
256
+ }
257
+
258
+ // GetClientContentConfig returns the ClientContentConfig that should be used for new requests by
259
+ // this client.
260
+ func (p * requestClientContentConfigProvider ) GetClientContentConfig () ClientContentConfig {
261
+ if ! clientfeatures .TestOnlyFeatureGates .Enabled (clientfeatures .TestOnlyClientAllowsCBOR ) {
262
+ return p .base
263
+ }
264
+
265
+ if sawUnsupportedMediaTypeForCBOR := p .sawUnsupportedMediaTypeForCBOR .Load (); ! sawUnsupportedMediaTypeForCBOR {
266
+ return p .base
267
+ }
268
+
269
+ if mediaType , _ , _ := mime .ParseMediaType (p .base .ContentType ); mediaType != runtime .ContentTypeCBOR {
270
+ return p .base
271
+ }
272
+
273
+ config := p .base
274
+ // The default ClientContentConfig sets ContentType to CBOR and the client has previously
275
+ // received an HTTP 415 in response to a CBOR request. Override ContentType to JSON.
276
+ config .ContentType = runtime .ContentTypeJSON
277
+ return config
278
+ }
279
+
280
+ // UnsupportedMediaType reports that the server has responded to a request with HTTP 415 Unsupported
281
+ // Media Type.
282
+ func (p * requestClientContentConfigProvider ) UnsupportedMediaType (requestContentType string ) {
283
+ if ! clientfeatures .TestOnlyFeatureGates .Enabled (clientfeatures .TestOnlyClientAllowsCBOR ) {
284
+ return
285
+ }
286
+
287
+ // This could be extended to consider the Content-Encoding request header, the Accept and
288
+ // Accept-Encoding response headers, the request method, and URI (as mentioned in
289
+ // https://www.rfc-editor.org/rfc/rfc9110.html#section-15.5.16). The request Content-Type
290
+ // header is sufficient to implement a blanket CBOR fallback mechanism.
291
+ requestContentType , _ , _ = mime .ParseMediaType (requestContentType )
292
+ switch requestContentType {
293
+ case runtime .ContentTypeCBOR , string (types .ApplyCBORPatchType ):
294
+ p .sawUnsupportedMediaTypeForCBOR .Store (true )
295
+ }
241
296
}
0 commit comments