@@ -20,6 +20,7 @@ import (
2020 "context"
2121 "encoding/json"
2222 "fmt"
23+ "mime"
2324 "net/http"
2425 "net/url"
2526 "sort"
@@ -58,8 +59,9 @@ const (
5859 defaultBurst = 300
5960
6061 AcceptV1 = runtime .ContentTypeJSON
61- // Aggregated discovery content-type (currently v2beta1). NOTE: Currently, we are assuming the order
62- // for "g", "v", and "as" from the server. We can only compare this string if we can make that assumption.
62+ // Aggregated discovery content-type (v2beta1). NOTE: content-type parameters
63+ // MUST be ordered (g, v, as) for server in "Accept" header (BUT we are resilient
64+ // to ordering when comparing returned values in "Content-Type" header).
6365 AcceptV2Beta1 = runtime .ContentTypeJSON + ";" + "g=apidiscovery.k8s.io;v=v2beta1;as=APIGroupDiscoveryList"
6466 // Prioritize aggregated discovery by placing first in the order of discovery accept types.
6567 acceptDiscoveryFormats = AcceptV2Beta1 + "," + AcceptV1
@@ -259,8 +261,16 @@ func (d *DiscoveryClient) downloadLegacy() (
259261
260262 var resourcesByGV map [schema.GroupVersion ]* metav1.APIResourceList
261263 // Switch on content-type server responded with: aggregated or unaggregated.
262- switch responseContentType {
263- case AcceptV1 :
264+ switch {
265+ case isV2Beta1ContentType (responseContentType ):
266+ var aggregatedDiscovery apidiscovery.APIGroupDiscoveryList
267+ err = json .Unmarshal (body , & aggregatedDiscovery )
268+ if err != nil {
269+ return nil , nil , nil , err
270+ }
271+ apiGroupList , resourcesByGV , failedGVs = SplitGroupsAndResources (aggregatedDiscovery )
272+ default :
273+ // Default is unaggregated discovery v1.
264274 var v metav1.APIVersions
265275 err = json .Unmarshal (body , & v )
266276 if err != nil {
@@ -271,15 +281,6 @@ func (d *DiscoveryClient) downloadLegacy() (
271281 apiGroup = apiVersionsToAPIGroup (& v )
272282 }
273283 apiGroupList .Groups = []metav1.APIGroup {apiGroup }
274- case AcceptV2Beta1 :
275- var aggregatedDiscovery apidiscovery.APIGroupDiscoveryList
276- err = json .Unmarshal (body , & aggregatedDiscovery )
277- if err != nil {
278- return nil , nil , nil , err
279- }
280- apiGroupList , resourcesByGV , failedGVs = SplitGroupsAndResources (aggregatedDiscovery )
281- default :
282- return nil , nil , nil , fmt .Errorf ("Unknown discovery response content-type: %s" , responseContentType )
283284 }
284285
285286 return apiGroupList , resourcesByGV , failedGVs , nil
@@ -313,26 +314,47 @@ func (d *DiscoveryClient) downloadAPIs() (
313314 failedGVs := map [schema.GroupVersion ]error {}
314315 var resourcesByGV map [schema.GroupVersion ]* metav1.APIResourceList
315316 // Switch on content-type server responded with: aggregated or unaggregated.
316- switch responseContentType {
317- case AcceptV1 :
318- err = json .Unmarshal (body , apiGroupList )
319- if err != nil {
320- return nil , nil , nil , err
321- }
322- case AcceptV2Beta1 :
317+ switch {
318+ case isV2Beta1ContentType (responseContentType ):
323319 var aggregatedDiscovery apidiscovery.APIGroupDiscoveryList
324320 err = json .Unmarshal (body , & aggregatedDiscovery )
325321 if err != nil {
326322 return nil , nil , nil , err
327323 }
328324 apiGroupList , resourcesByGV , failedGVs = SplitGroupsAndResources (aggregatedDiscovery )
329325 default :
330- return nil , nil , nil , fmt .Errorf ("Unknown discovery response content-type: %s" , responseContentType )
326+ // Default is unaggregated discovery v1.
327+ err = json .Unmarshal (body , apiGroupList )
328+ if err != nil {
329+ return nil , nil , nil , err
330+ }
331331 }
332332
333333 return apiGroupList , resourcesByGV , failedGVs , nil
334334}
335335
336+ // isV2Beta1ContentType checks of the content-type string is both
337+ // "application/json" and contains the v2beta1 content-type params.
338+ // NOTE: This function is resilient to the ordering of the
339+ // content-type parameters, as well as parameters added by
340+ // intermediaries such as proxies or gateways. Examples:
341+ //
342+ // "application/json; g=apidiscovery.k8s.io;v=v2beta1;as=APIGroupDiscoveryList" = true
343+ // "application/json; as=APIGroupDiscoveryList;v=v2beta1;g=apidiscovery.k8s.io" = true
344+ // "application/json; as=APIGroupDiscoveryList;v=v2beta1;g=apidiscovery.k8s.io;charset=utf-8" = true
345+ // "application/json" = false
346+ // "application/json; charset=UTF-8" = false
347+ func isV2Beta1ContentType (contentType string ) bool {
348+ base , params , err := mime .ParseMediaType (contentType )
349+ if err != nil {
350+ return false
351+ }
352+ return runtime .ContentTypeJSON == base &&
353+ params ["g" ] == "apidiscovery.k8s.io" &&
354+ params ["v" ] == "v2beta1" &&
355+ params ["as" ] == "APIGroupDiscoveryList"
356+ }
357+
336358// ServerGroups returns the supported groups, with information like supported versions and the
337359// preferred version.
338360func (d * DiscoveryClient ) ServerGroups () (* metav1.APIGroupList , error ) {
0 commit comments