k8s.io/client-go@v0.31.1/discovery/aggregated_discovery_test.go (about)

     1  /*
     2  Copyright 2022 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package discovery
    18  
    19  import (
    20  	"testing"
    21  
    22  	"github.com/stretchr/testify/assert"
    23  	apidiscovery "k8s.io/api/apidiscovery/v2"
    24  	apidiscoveryv2beta1 "k8s.io/api/apidiscovery/v2beta1"
    25  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    26  	"k8s.io/apimachinery/pkg/runtime/schema"
    27  )
    28  
    29  func TestSplitGroupsAndResources(t *testing.T) {
    30  	tests := []struct {
    31  		name                string
    32  		agg                 apidiscovery.APIGroupDiscoveryList
    33  		expectedGroups      metav1.APIGroupList
    34  		expectedGVResources map[schema.GroupVersion]*metav1.APIResourceList
    35  		expectedFailedGVs   map[schema.GroupVersion]error
    36  	}{
    37  		{
    38  			name: "Aggregated discovery: core/v1 group and pod resource",
    39  			agg: apidiscovery.APIGroupDiscoveryList{
    40  				Items: []apidiscovery.APIGroupDiscovery{
    41  					{
    42  						ObjectMeta: metav1.ObjectMeta{
    43  							Name: "",
    44  						},
    45  						Versions: []apidiscovery.APIVersionDiscovery{
    46  							{
    47  								Version: "v1",
    48  								Resources: []apidiscovery.APIResourceDiscovery{
    49  									{
    50  										Resource: "pods",
    51  										ResponseKind: &metav1.GroupVersionKind{
    52  											Group:   "",
    53  											Version: "v1",
    54  											Kind:    "Pod",
    55  										},
    56  										Scope: apidiscovery.ScopeNamespace,
    57  									},
    58  								},
    59  							},
    60  						},
    61  					},
    62  				},
    63  			},
    64  			expectedGroups: metav1.APIGroupList{
    65  				Groups: []metav1.APIGroup{
    66  					{
    67  						Name: "",
    68  						Versions: []metav1.GroupVersionForDiscovery{
    69  							{
    70  								GroupVersion: "v1",
    71  								Version:      "v1",
    72  							},
    73  						},
    74  						PreferredVersion: metav1.GroupVersionForDiscovery{
    75  							GroupVersion: "v1",
    76  							Version:      "v1",
    77  						},
    78  					},
    79  				},
    80  			},
    81  			expectedGVResources: map[schema.GroupVersion]*metav1.APIResourceList{
    82  				{Group: "", Version: "v1"}: {
    83  					GroupVersion: "v1",
    84  					APIResources: []metav1.APIResource{
    85  						{
    86  							Name:       "pods",
    87  							Namespaced: true,
    88  							Group:      "",
    89  							Version:    "v1",
    90  							Kind:       "Pod",
    91  						},
    92  					},
    93  				},
    94  			},
    95  			expectedFailedGVs: map[schema.GroupVersion]error{},
    96  		},
    97  		{
    98  			name: "Aggregated discovery: 1 group/1 resources at /api, 1 group/2 versions/1 resources at /apis",
    99  			agg: apidiscovery.APIGroupDiscoveryList{
   100  				Items: []apidiscovery.APIGroupDiscovery{
   101  					{
   102  						ObjectMeta: metav1.ObjectMeta{
   103  							Name: "apps",
   104  						},
   105  						Versions: []apidiscovery.APIVersionDiscovery{
   106  							{
   107  								Version: "v2",
   108  								Resources: []apidiscovery.APIResourceDiscovery{
   109  									{
   110  										Resource: "deployments",
   111  										ResponseKind: &metav1.GroupVersionKind{
   112  											Group:   "apps",
   113  											Version: "v2",
   114  											Kind:    "Deployment",
   115  										},
   116  										Scope: apidiscovery.ScopeNamespace,
   117  									},
   118  								},
   119  							},
   120  							{
   121  								Version: "v1",
   122  								Resources: []apidiscovery.APIResourceDiscovery{
   123  									{
   124  										Resource: "deployments",
   125  										ResponseKind: &metav1.GroupVersionKind{
   126  											Group:   "apps",
   127  											Version: "v1",
   128  											Kind:    "Deployment",
   129  										},
   130  										Scope: apidiscovery.ScopeNamespace,
   131  									},
   132  								},
   133  							},
   134  						},
   135  					},
   136  				},
   137  			},
   138  			expectedGroups: metav1.APIGroupList{
   139  				Groups: []metav1.APIGroup{
   140  					{
   141  						Name: "apps",
   142  						Versions: []metav1.GroupVersionForDiscovery{
   143  							{
   144  								GroupVersion: "apps/v2",
   145  								Version:      "v2",
   146  							},
   147  							{
   148  								GroupVersion: "apps/v1",
   149  								Version:      "v1",
   150  							},
   151  						},
   152  						PreferredVersion: metav1.GroupVersionForDiscovery{
   153  							GroupVersion: "apps/v2",
   154  							Version:      "v2",
   155  						},
   156  					},
   157  				},
   158  			},
   159  			expectedGVResources: map[schema.GroupVersion]*metav1.APIResourceList{
   160  				{Group: "apps", Version: "v1"}: {
   161  					GroupVersion: "apps/v1",
   162  					APIResources: []metav1.APIResource{
   163  						{
   164  							Name:       "deployments",
   165  							Namespaced: true,
   166  							Group:      "apps",
   167  							Version:    "v1",
   168  							Kind:       "Deployment",
   169  						},
   170  					},
   171  				},
   172  				{Group: "apps", Version: "v2"}: {
   173  					GroupVersion: "apps/v2",
   174  					APIResources: []metav1.APIResource{
   175  						{
   176  							Name:       "deployments",
   177  							Namespaced: true,
   178  							Group:      "apps",
   179  							Version:    "v2",
   180  							Kind:       "Deployment",
   181  						},
   182  					},
   183  				},
   184  			},
   185  			expectedFailedGVs: map[schema.GroupVersion]error{},
   186  		},
   187  		{
   188  			name: "Aggregated discovery: 1 group/2 resources at /api, 1 group/2 resources at /apis",
   189  			agg: apidiscovery.APIGroupDiscoveryList{
   190  				Items: []apidiscovery.APIGroupDiscovery{
   191  					{
   192  						Versions: []apidiscovery.APIVersionDiscovery{
   193  							{
   194  								Version: "v1",
   195  								Resources: []apidiscovery.APIResourceDiscovery{
   196  									{
   197  										Resource: "pods",
   198  										ResponseKind: &metav1.GroupVersionKind{
   199  											Group:   "",
   200  											Version: "v1",
   201  											Kind:    "Pod",
   202  										},
   203  										Scope: apidiscovery.ScopeNamespace,
   204  									},
   205  									{
   206  										Resource: "services",
   207  										ResponseKind: &metav1.GroupVersionKind{
   208  											Group:   "",
   209  											Version: "v1",
   210  											Kind:    "Service",
   211  										},
   212  										Scope: apidiscovery.ScopeNamespace,
   213  									},
   214  								},
   215  							},
   216  						},
   217  					},
   218  					{
   219  						ObjectMeta: metav1.ObjectMeta{
   220  							Name: "apps",
   221  						},
   222  						Versions: []apidiscovery.APIVersionDiscovery{
   223  							{
   224  								Version: "v1",
   225  								Resources: []apidiscovery.APIResourceDiscovery{
   226  									{
   227  										Resource: "deployments",
   228  										ResponseKind: &metav1.GroupVersionKind{
   229  											Group:   "apps",
   230  											Version: "v1",
   231  											Kind:    "Deployment",
   232  										},
   233  										Scope: apidiscovery.ScopeNamespace,
   234  									},
   235  									{
   236  										Resource: "statefulsets",
   237  										ResponseKind: &metav1.GroupVersionKind{
   238  											Group:   "apps",
   239  											Version: "v1",
   240  											Kind:    "StatefulSet",
   241  										},
   242  										Scope: apidiscovery.ScopeNamespace,
   243  									},
   244  								},
   245  							},
   246  						},
   247  					},
   248  				},
   249  			},
   250  			expectedGroups: metav1.APIGroupList{
   251  				Groups: []metav1.APIGroup{
   252  					{
   253  						Name: "",
   254  						Versions: []metav1.GroupVersionForDiscovery{
   255  							{
   256  								GroupVersion: "v1",
   257  								Version:      "v1",
   258  							},
   259  						},
   260  						PreferredVersion: metav1.GroupVersionForDiscovery{
   261  							GroupVersion: "v1",
   262  							Version:      "v1",
   263  						},
   264  					},
   265  					{
   266  						Name: "apps",
   267  						Versions: []metav1.GroupVersionForDiscovery{
   268  							{
   269  								GroupVersion: "apps/v1",
   270  								Version:      "v1",
   271  							},
   272  						},
   273  						PreferredVersion: metav1.GroupVersionForDiscovery{
   274  							GroupVersion: "apps/v1",
   275  							Version:      "v1",
   276  						},
   277  					},
   278  				},
   279  			},
   280  			expectedGVResources: map[schema.GroupVersion]*metav1.APIResourceList{
   281  				{Group: "", Version: "v1"}: {
   282  					GroupVersion: "v1",
   283  					APIResources: []metav1.APIResource{
   284  						{
   285  							Name:       "pods",
   286  							Namespaced: true,
   287  							Group:      "",
   288  							Version:    "v1",
   289  							Kind:       "Pod",
   290  						},
   291  						{
   292  							Name:       "services",
   293  							Namespaced: true,
   294  							Group:      "",
   295  							Version:    "v1",
   296  							Kind:       "Service",
   297  						},
   298  					},
   299  				},
   300  				{Group: "apps", Version: "v1"}: {
   301  					GroupVersion: "apps/v1",
   302  					APIResources: []metav1.APIResource{
   303  						{
   304  							Name:       "deployments",
   305  							Namespaced: true,
   306  							Group:      "apps",
   307  							Version:    "v1",
   308  							Kind:       "Deployment",
   309  						},
   310  						{
   311  							Name:       "statefulsets",
   312  							Namespaced: true,
   313  							Group:      "apps",
   314  							Version:    "v1",
   315  							Kind:       "StatefulSet",
   316  						},
   317  					},
   318  				},
   319  			},
   320  			expectedFailedGVs: map[schema.GroupVersion]error{},
   321  		},
   322  		{
   323  			name: "Aggregated discovery: multiple groups with cluster-scoped resources",
   324  			agg: apidiscovery.APIGroupDiscoveryList{
   325  				Items: []apidiscovery.APIGroupDiscovery{
   326  					{
   327  						Versions: []apidiscovery.APIVersionDiscovery{
   328  							{
   329  								Version: "v1",
   330  								Resources: []apidiscovery.APIResourceDiscovery{
   331  									{
   332  										Resource: "pods",
   333  										ResponseKind: &metav1.GroupVersionKind{
   334  											Group:   "",
   335  											Version: "v1",
   336  											Kind:    "Pod",
   337  										},
   338  										Scope: apidiscovery.ScopeNamespace,
   339  									},
   340  									{
   341  										Resource: "namespaces",
   342  										ResponseKind: &metav1.GroupVersionKind{
   343  											Group:   "",
   344  											Version: "v1",
   345  											Kind:    "Namespace",
   346  										},
   347  										Scope: apidiscovery.ScopeCluster,
   348  									},
   349  								},
   350  							},
   351  						},
   352  					},
   353  					{
   354  						ObjectMeta: metav1.ObjectMeta{
   355  							Name: "rbac.authorization.k8s.io",
   356  						},
   357  						Versions: []apidiscovery.APIVersionDiscovery{
   358  							{
   359  								Version: "v1",
   360  								Resources: []apidiscovery.APIResourceDiscovery{
   361  									{
   362  										Resource: "roles",
   363  										ResponseKind: &metav1.GroupVersionKind{
   364  											Group:   "rbac.authorization.k8s.io",
   365  											Version: "v1",
   366  											Kind:    "Role",
   367  										},
   368  										Scope: apidiscovery.ScopeCluster,
   369  									},
   370  									{
   371  										Resource: "clusterroles",
   372  										ResponseKind: &metav1.GroupVersionKind{
   373  											Group:   "rbac.authorization.k8s.io",
   374  											Version: "v1",
   375  											Kind:    "ClusterRole",
   376  										},
   377  										Scope: apidiscovery.ScopeCluster,
   378  									},
   379  								},
   380  							},
   381  						},
   382  					},
   383  				},
   384  			},
   385  			expectedGroups: metav1.APIGroupList{
   386  				Groups: []metav1.APIGroup{
   387  					{
   388  						Name: "",
   389  						Versions: []metav1.GroupVersionForDiscovery{
   390  							{
   391  								GroupVersion: "v1",
   392  								Version:      "v1",
   393  							},
   394  						},
   395  						PreferredVersion: metav1.GroupVersionForDiscovery{
   396  							GroupVersion: "v1",
   397  							Version:      "v1",
   398  						},
   399  					},
   400  					{
   401  						Name: "rbac.authorization.k8s.io",
   402  						Versions: []metav1.GroupVersionForDiscovery{
   403  							{
   404  								GroupVersion: "rbac.authorization.k8s.io/v1",
   405  								Version:      "v1",
   406  							},
   407  						},
   408  						PreferredVersion: metav1.GroupVersionForDiscovery{
   409  							GroupVersion: "rbac.authorization.k8s.io/v1",
   410  							Version:      "v1",
   411  						},
   412  					},
   413  				},
   414  			},
   415  			expectedGVResources: map[schema.GroupVersion]*metav1.APIResourceList{
   416  				{Group: "", Version: "v1"}: {
   417  					GroupVersion: "v1",
   418  					APIResources: []metav1.APIResource{
   419  						{
   420  							Name:       "pods",
   421  							Namespaced: true,
   422  							Group:      "",
   423  							Version:    "v1",
   424  							Kind:       "Pod",
   425  						},
   426  						{
   427  							Name:       "namespaces",
   428  							Namespaced: false,
   429  							Group:      "",
   430  							Version:    "v1",
   431  							Kind:       "Namespace",
   432  						},
   433  					},
   434  				},
   435  				{Group: "rbac.authorization.k8s.io", Version: "v1"}: {
   436  					GroupVersion: "rbac.authorization.k8s.io/v1",
   437  					APIResources: []metav1.APIResource{
   438  						{
   439  							Name:       "roles",
   440  							Namespaced: false,
   441  							Group:      "rbac.authorization.k8s.io",
   442  							Version:    "v1",
   443  							Kind:       "Role",
   444  						},
   445  						{
   446  							Name:       "clusterroles",
   447  							Namespaced: false,
   448  							Group:      "rbac.authorization.k8s.io",
   449  							Version:    "v1",
   450  							Kind:       "ClusterRole",
   451  						},
   452  					},
   453  				},
   454  			},
   455  			expectedFailedGVs: map[schema.GroupVersion]error{},
   456  		},
   457  		{
   458  			name: "Aggregated discovery with single subresource",
   459  			agg: apidiscovery.APIGroupDiscoveryList{
   460  				Items: []apidiscovery.APIGroupDiscovery{
   461  					{
   462  						ObjectMeta: metav1.ObjectMeta{
   463  							Name: "apps",
   464  						},
   465  						Versions: []apidiscovery.APIVersionDiscovery{
   466  							{
   467  								Version: "v1",
   468  								Resources: []apidiscovery.APIResourceDiscovery{
   469  									{
   470  										Resource: "deployments",
   471  										ResponseKind: &metav1.GroupVersionKind{
   472  											Group:   "apps",
   473  											Version: "v1",
   474  											Kind:    "Deployment",
   475  										},
   476  										Scope:            apidiscovery.ScopeNamespace,
   477  										SingularResource: "deployment",
   478  										ShortNames:       []string{"deploy"},
   479  										Verbs:            []string{"parentverb1", "parentverb2", "parentverb3", "parentverb4"},
   480  										Categories:       []string{"all", "testcategory"},
   481  										Subresources: []apidiscovery.APISubresourceDiscovery{
   482  											{
   483  												Subresource: "scale",
   484  												ResponseKind: &metav1.GroupVersionKind{
   485  													Group:   "apps",
   486  													Version: "v1",
   487  													Kind:    "Deployment",
   488  												},
   489  												Verbs: []string{"get", "patch", "update"},
   490  											},
   491  										},
   492  									},
   493  								},
   494  							},
   495  						},
   496  					},
   497  				},
   498  			},
   499  			expectedGroups: metav1.APIGroupList{
   500  				Groups: []metav1.APIGroup{
   501  					{
   502  						Name: "apps",
   503  						Versions: []metav1.GroupVersionForDiscovery{
   504  							{
   505  								GroupVersion: "apps/v1",
   506  								Version:      "v1",
   507  							},
   508  						},
   509  						PreferredVersion: metav1.GroupVersionForDiscovery{
   510  							GroupVersion: "apps/v1",
   511  							Version:      "v1",
   512  						},
   513  					},
   514  				},
   515  			},
   516  			expectedGVResources: map[schema.GroupVersion]*metav1.APIResourceList{
   517  				{Group: "apps", Version: "v1"}: {
   518  					GroupVersion: "apps/v1",
   519  					APIResources: []metav1.APIResource{
   520  						{
   521  							Name:         "deployments",
   522  							SingularName: "deployment",
   523  							Namespaced:   true,
   524  							Group:        "apps",
   525  							Version:      "v1",
   526  							Kind:         "Deployment",
   527  							Verbs:        []string{"parentverb1", "parentverb2", "parentverb3", "parentverb4"},
   528  							ShortNames:   []string{"deploy"},
   529  							Categories:   []string{"all", "testcategory"},
   530  						},
   531  						{
   532  							Name:         "deployments/scale",
   533  							SingularName: "deployment",
   534  							Namespaced:   true,
   535  							Group:        "apps",
   536  							Version:      "v1",
   537  							Kind:         "Deployment",
   538  							Verbs:        []string{"get", "patch", "update"},
   539  						},
   540  					},
   541  				},
   542  			},
   543  			expectedFailedGVs: map[schema.GroupVersion]error{},
   544  		},
   545  		{
   546  			name: "Aggregated discovery with single subresource and parent missing GVK",
   547  			agg: apidiscovery.APIGroupDiscoveryList{
   548  				Items: []apidiscovery.APIGroupDiscovery{
   549  					{
   550  						ObjectMeta: metav1.ObjectMeta{
   551  							Name: "external.metrics.k8s.io",
   552  						},
   553  						Versions: []apidiscovery.APIVersionDiscovery{
   554  							{
   555  								Version: "v1beta1",
   556  								Resources: []apidiscovery.APIResourceDiscovery{
   557  									{
   558  										// resilient to nil GVK for parent
   559  										Resource:         "*",
   560  										Scope:            apidiscovery.ScopeNamespace,
   561  										SingularResource: "",
   562  										Subresources: []apidiscovery.APISubresourceDiscovery{
   563  											{
   564  												Subresource: "other-external-metric",
   565  												ResponseKind: &metav1.GroupVersionKind{
   566  													Kind: "MetricValueList",
   567  												},
   568  												Verbs: []string{"get"},
   569  											},
   570  										},
   571  									},
   572  								},
   573  							},
   574  						},
   575  					},
   576  				},
   577  			},
   578  			expectedGroups: metav1.APIGroupList{
   579  				Groups: []metav1.APIGroup{
   580  					{
   581  						Name: "external.metrics.k8s.io",
   582  						Versions: []metav1.GroupVersionForDiscovery{
   583  							{
   584  								GroupVersion: "external.metrics.k8s.io/v1beta1",
   585  								Version:      "v1beta1",
   586  							},
   587  						},
   588  						PreferredVersion: metav1.GroupVersionForDiscovery{
   589  							GroupVersion: "external.metrics.k8s.io/v1beta1",
   590  							Version:      "v1beta1",
   591  						},
   592  					},
   593  				},
   594  			},
   595  			expectedGVResources: map[schema.GroupVersion]*metav1.APIResourceList{
   596  				{Group: "external.metrics.k8s.io", Version: "v1beta1"}: {
   597  					GroupVersion: "external.metrics.k8s.io/v1beta1",
   598  					APIResources: []metav1.APIResource{
   599  						// Since parent GVK was nil, it is NOT returned--only the subresource.
   600  						{
   601  							Name:         "*/other-external-metric",
   602  							SingularName: "",
   603  							Namespaced:   true,
   604  							Group:        "",
   605  							Version:      "",
   606  							Kind:         "MetricValueList",
   607  							Verbs:        []string{"get"},
   608  						},
   609  					},
   610  				},
   611  			},
   612  			expectedFailedGVs: map[schema.GroupVersion]error{},
   613  		},
   614  		{
   615  			name: "Aggregated discovery with single subresource and parent empty GVK",
   616  			agg: apidiscovery.APIGroupDiscoveryList{
   617  				Items: []apidiscovery.APIGroupDiscovery{
   618  					{
   619  						ObjectMeta: metav1.ObjectMeta{
   620  							Name: "external.metrics.k8s.io",
   621  						},
   622  						Versions: []apidiscovery.APIVersionDiscovery{
   623  							{
   624  								Version: "v1beta1",
   625  								Resources: []apidiscovery.APIResourceDiscovery{
   626  									{
   627  										// resilient to empty GVK for parent
   628  										Resource:         "*",
   629  										Scope:            apidiscovery.ScopeNamespace,
   630  										SingularResource: "",
   631  										ResponseKind:     &metav1.GroupVersionKind{},
   632  										Subresources: []apidiscovery.APISubresourceDiscovery{
   633  											{
   634  												Subresource: "other-external-metric",
   635  												ResponseKind: &metav1.GroupVersionKind{
   636  													Kind: "MetricValueList",
   637  												},
   638  												Verbs: []string{"get"},
   639  											},
   640  										},
   641  									},
   642  								},
   643  							},
   644  						},
   645  					},
   646  				},
   647  			},
   648  			expectedGroups: metav1.APIGroupList{
   649  				Groups: []metav1.APIGroup{
   650  					{
   651  						Name: "external.metrics.k8s.io",
   652  						Versions: []metav1.GroupVersionForDiscovery{
   653  							{
   654  								GroupVersion: "external.metrics.k8s.io/v1beta1",
   655  								Version:      "v1beta1",
   656  							},
   657  						},
   658  						PreferredVersion: metav1.GroupVersionForDiscovery{
   659  							GroupVersion: "external.metrics.k8s.io/v1beta1",
   660  							Version:      "v1beta1",
   661  						},
   662  					},
   663  				},
   664  			},
   665  			expectedGVResources: map[schema.GroupVersion]*metav1.APIResourceList{
   666  				{Group: "external.metrics.k8s.io", Version: "v1beta1"}: {
   667  					GroupVersion: "external.metrics.k8s.io/v1beta1",
   668  					APIResources: []metav1.APIResource{
   669  						// Since parent GVK was nil, it is NOT returned--only the subresource.
   670  						{
   671  							Name:         "*/other-external-metric",
   672  							SingularName: "",
   673  							Namespaced:   true,
   674  							Group:        "",
   675  							Version:      "",
   676  							Kind:         "MetricValueList",
   677  							Verbs:        []string{"get"},
   678  						},
   679  					},
   680  				},
   681  			},
   682  			expectedFailedGVs: map[schema.GroupVersion]error{},
   683  		},
   684  		{
   685  			name: "Aggregated discovery with multiple subresources",
   686  			agg: apidiscovery.APIGroupDiscoveryList{
   687  				Items: []apidiscovery.APIGroupDiscovery{
   688  					{
   689  						ObjectMeta: metav1.ObjectMeta{
   690  							Name: "apps",
   691  						},
   692  						Versions: []apidiscovery.APIVersionDiscovery{
   693  							{
   694  								Version: "v1",
   695  								Resources: []apidiscovery.APIResourceDiscovery{
   696  									{
   697  										Resource: "deployments",
   698  										ResponseKind: &metav1.GroupVersionKind{
   699  											Group:   "apps",
   700  											Version: "v1",
   701  											Kind:    "Deployment",
   702  										},
   703  										Scope:            apidiscovery.ScopeNamespace,
   704  										SingularResource: "deployment",
   705  										Subresources: []apidiscovery.APISubresourceDiscovery{
   706  											{
   707  												Subresource: "scale",
   708  												ResponseKind: &metav1.GroupVersionKind{
   709  													Group:   "apps",
   710  													Version: "v1",
   711  													Kind:    "Deployment",
   712  												},
   713  												Verbs: []string{"get", "patch", "update"},
   714  											},
   715  											{
   716  												Subresource: "status",
   717  												ResponseKind: &metav1.GroupVersionKind{
   718  													Group:   "apps",
   719  													Version: "v1",
   720  													Kind:    "Deployment",
   721  												},
   722  												Verbs: []string{"get", "patch", "update"},
   723  											},
   724  										},
   725  									},
   726  								},
   727  							},
   728  						},
   729  					},
   730  				},
   731  			},
   732  			expectedGroups: metav1.APIGroupList{
   733  				Groups: []metav1.APIGroup{
   734  					{
   735  						Name: "apps",
   736  						Versions: []metav1.GroupVersionForDiscovery{
   737  							{
   738  								GroupVersion: "apps/v1",
   739  								Version:      "v1",
   740  							},
   741  						},
   742  						PreferredVersion: metav1.GroupVersionForDiscovery{
   743  							GroupVersion: "apps/v1",
   744  							Version:      "v1",
   745  						},
   746  					},
   747  				},
   748  			},
   749  			expectedGVResources: map[schema.GroupVersion]*metav1.APIResourceList{
   750  				{Group: "apps", Version: "v1"}: {
   751  					GroupVersion: "apps/v1",
   752  					APIResources: []metav1.APIResource{
   753  						{
   754  							Name:         "deployments",
   755  							SingularName: "deployment",
   756  							Namespaced:   true,
   757  							Group:        "apps",
   758  							Version:      "v1",
   759  							Kind:         "Deployment",
   760  						},
   761  						{
   762  							Name:         "deployments/scale",
   763  							SingularName: "deployment",
   764  							Namespaced:   true,
   765  							Group:        "apps",
   766  							Version:      "v1",
   767  							Kind:         "Deployment",
   768  							Verbs:        []string{"get", "patch", "update"},
   769  						},
   770  						{
   771  							Name:         "deployments/status",
   772  							SingularName: "deployment",
   773  							Namespaced:   true,
   774  							Group:        "apps",
   775  							Version:      "v1",
   776  							Kind:         "Deployment",
   777  							Verbs:        []string{"get", "patch", "update"},
   778  						},
   779  					},
   780  				},
   781  			},
   782  			expectedFailedGVs: map[schema.GroupVersion]error{},
   783  		},
   784  		{
   785  			name: "Aggregated discovery: single failed GV at /api",
   786  			agg: apidiscovery.APIGroupDiscoveryList{
   787  				Items: []apidiscovery.APIGroupDiscovery{
   788  					{
   789  						Versions: []apidiscovery.APIVersionDiscovery{
   790  							{
   791  								Version: "v1",
   792  								Resources: []apidiscovery.APIResourceDiscovery{
   793  									{
   794  										Resource: "pods",
   795  										ResponseKind: &metav1.GroupVersionKind{
   796  											Group:   "",
   797  											Version: "v1",
   798  											Kind:    "Pod",
   799  										},
   800  										Scope: apidiscovery.ScopeNamespace,
   801  									},
   802  									{
   803  										Resource: "services",
   804  										ResponseKind: &metav1.GroupVersionKind{
   805  											Group:   "",
   806  											Version: "v1",
   807  											Kind:    "Service",
   808  										},
   809  										Scope: apidiscovery.ScopeNamespace,
   810  									},
   811  								},
   812  								Freshness: apidiscovery.DiscoveryFreshnessStale,
   813  							},
   814  						},
   815  					},
   816  				},
   817  			},
   818  			// Single core Group/Version is stale, so no Version within Group.
   819  			expectedGroups: metav1.APIGroupList{
   820  				Groups: []metav1.APIGroup{{Name: ""}},
   821  			},
   822  			// Single core Group/Version is stale, so there are no expected resources.
   823  			expectedGVResources: map[schema.GroupVersion]*metav1.APIResourceList{},
   824  			expectedFailedGVs: map[schema.GroupVersion]error{
   825  				{Group: "", Version: "v1"}: StaleGroupVersionError{gv: schema.GroupVersion{Group: "", Version: "v1"}},
   826  			},
   827  		},
   828  		{
   829  			name: "Aggregated discovery: single failed GV at /apis",
   830  			agg: apidiscovery.APIGroupDiscoveryList{
   831  				Items: []apidiscovery.APIGroupDiscovery{
   832  					{
   833  						ObjectMeta: metav1.ObjectMeta{
   834  							Name: "apps",
   835  						},
   836  						Versions: []apidiscovery.APIVersionDiscovery{
   837  							{
   838  								Version: "v1",
   839  								Resources: []apidiscovery.APIResourceDiscovery{
   840  									{
   841  										Resource: "deployments",
   842  										ResponseKind: &metav1.GroupVersionKind{
   843  											Group:   "apps",
   844  											Version: "v1",
   845  											Kind:    "Deployment",
   846  										},
   847  										Scope: apidiscovery.ScopeNamespace,
   848  									},
   849  									{
   850  										Resource: "statefulsets",
   851  										ResponseKind: &metav1.GroupVersionKind{
   852  											Group:   "apps",
   853  											Version: "v1",
   854  											Kind:    "StatefulSets",
   855  										},
   856  										Scope: apidiscovery.ScopeNamespace,
   857  									},
   858  								},
   859  								Freshness: apidiscovery.DiscoveryFreshnessStale,
   860  							},
   861  						},
   862  					},
   863  				},
   864  			},
   865  			// Single apps/v1 Group/Version is stale, so no Version within Group.
   866  			expectedGroups: metav1.APIGroupList{
   867  				Groups: []metav1.APIGroup{{Name: "apps"}},
   868  			},
   869  			// Single apps/v1 Group/Version is stale, so there are no expected resources.
   870  			expectedGVResources: map[schema.GroupVersion]*metav1.APIResourceList{},
   871  			expectedFailedGVs: map[schema.GroupVersion]error{
   872  				{Group: "apps", Version: "v1"}: StaleGroupVersionError{gv: schema.GroupVersion{Group: "apps", Version: "v1"}},
   873  			},
   874  		},
   875  		{
   876  			name: "Aggregated discovery: 1 group/2 versions/1 failed GV at /apis",
   877  			agg: apidiscovery.APIGroupDiscoveryList{
   878  				Items: []apidiscovery.APIGroupDiscovery{
   879  					{
   880  						ObjectMeta: metav1.ObjectMeta{
   881  							Name: "apps",
   882  						},
   883  						Versions: []apidiscovery.APIVersionDiscovery{
   884  							// Stale v2 should report failed GV.
   885  							{
   886  								Version: "v2",
   887  								Resources: []apidiscovery.APIResourceDiscovery{
   888  									{
   889  										Resource: "daemonsets",
   890  										ResponseKind: &metav1.GroupVersionKind{
   891  											Group:   "apps",
   892  											Version: "v2",
   893  											Kind:    "DaemonSets",
   894  										},
   895  										Scope: apidiscovery.ScopeNamespace,
   896  									},
   897  								},
   898  								Freshness: apidiscovery.DiscoveryFreshnessStale,
   899  							},
   900  							{
   901  								Version: "v1",
   902  								Resources: []apidiscovery.APIResourceDiscovery{
   903  									{
   904  										Resource: "deployments",
   905  										ResponseKind: &metav1.GroupVersionKind{
   906  											Group:   "apps",
   907  											Version: "v1",
   908  											Kind:    "Deployment",
   909  										},
   910  										Scope: apidiscovery.ScopeNamespace,
   911  									},
   912  								},
   913  							},
   914  						},
   915  					},
   916  				},
   917  			},
   918  			// Only apps/v1 is non-stale expected Group/Version
   919  			expectedGroups: metav1.APIGroupList{
   920  				Groups: []metav1.APIGroup{
   921  					{
   922  						Name: "apps",
   923  						Versions: []metav1.GroupVersionForDiscovery{
   924  							{
   925  								GroupVersion: "apps/v1",
   926  								Version:      "v1",
   927  							},
   928  						},
   929  						// PreferredVersion must be apps/v1
   930  						PreferredVersion: metav1.GroupVersionForDiscovery{
   931  							GroupVersion: "apps/v1",
   932  							Version:      "v1",
   933  						},
   934  					},
   935  				},
   936  			},
   937  			// Only apps/v1 resources expected.
   938  			expectedGVResources: map[schema.GroupVersion]*metav1.APIResourceList{
   939  				{Group: "apps", Version: "v1"}: {
   940  					GroupVersion: "apps/v1",
   941  					APIResources: []metav1.APIResource{
   942  						{
   943  							Name:       "deployments",
   944  							Namespaced: true,
   945  							Group:      "apps",
   946  							Version:    "v1",
   947  							Kind:       "Deployment",
   948  						},
   949  					},
   950  				},
   951  			},
   952  			expectedFailedGVs: map[schema.GroupVersion]error{
   953  				{Group: "apps", Version: "v2"}: StaleGroupVersionError{gv: schema.GroupVersion{Group: "apps", Version: "v2"}},
   954  			},
   955  		},
   956  	}
   957  
   958  	for _, test := range tests {
   959  		apiGroups, resourcesByGV, failedGVs := SplitGroupsAndResources(test.agg)
   960  		assert.Equal(t, test.expectedFailedGVs, failedGVs)
   961  		assert.Equal(t, test.expectedGroups, *apiGroups)
   962  		assert.Equal(t, test.expectedGVResources, resourcesByGV)
   963  	}
   964  }
   965  
   966  // Duplicated from test above. Remove after 1.33
   967  func TestSplitGroupsAndResourcesV2Beta1(t *testing.T) {
   968  	tests := []struct {
   969  		name                string
   970  		agg                 apidiscoveryv2beta1.APIGroupDiscoveryList
   971  		expectedGroups      metav1.APIGroupList
   972  		expectedGVResources map[schema.GroupVersion]*metav1.APIResourceList
   973  		expectedFailedGVs   map[schema.GroupVersion]error
   974  	}{
   975  		{
   976  			name: "Aggregated discovery: core/v1 group and pod resource",
   977  			agg: apidiscoveryv2beta1.APIGroupDiscoveryList{
   978  				Items: []apidiscoveryv2beta1.APIGroupDiscovery{
   979  					{
   980  						ObjectMeta: metav1.ObjectMeta{
   981  							Name: "",
   982  						},
   983  						Versions: []apidiscoveryv2beta1.APIVersionDiscovery{
   984  							{
   985  								Version: "v1",
   986  								Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
   987  									{
   988  										Resource: "pods",
   989  										ResponseKind: &metav1.GroupVersionKind{
   990  											Group:   "",
   991  											Version: "v1",
   992  											Kind:    "Pod",
   993  										},
   994  										Scope: apidiscoveryv2beta1.ScopeNamespace,
   995  									},
   996  								},
   997  							},
   998  						},
   999  					},
  1000  				},
  1001  			},
  1002  			expectedGroups: metav1.APIGroupList{
  1003  				Groups: []metav1.APIGroup{
  1004  					{
  1005  						Name: "",
  1006  						Versions: []metav1.GroupVersionForDiscovery{
  1007  							{
  1008  								GroupVersion: "v1",
  1009  								Version:      "v1",
  1010  							},
  1011  						},
  1012  						PreferredVersion: metav1.GroupVersionForDiscovery{
  1013  							GroupVersion: "v1",
  1014  							Version:      "v1",
  1015  						},
  1016  					},
  1017  				},
  1018  			},
  1019  			expectedGVResources: map[schema.GroupVersion]*metav1.APIResourceList{
  1020  				{Group: "", Version: "v1"}: {
  1021  					GroupVersion: "v1",
  1022  					APIResources: []metav1.APIResource{
  1023  						{
  1024  							Name:       "pods",
  1025  							Namespaced: true,
  1026  							Group:      "",
  1027  							Version:    "v1",
  1028  							Kind:       "Pod",
  1029  						},
  1030  					},
  1031  				},
  1032  			},
  1033  			expectedFailedGVs: map[schema.GroupVersion]error{},
  1034  		},
  1035  		{
  1036  			name: "Aggregated discovery: 1 group/1 resources at /api, 1 group/2 versions/1 resources at /apis",
  1037  			agg: apidiscoveryv2beta1.APIGroupDiscoveryList{
  1038  				Items: []apidiscoveryv2beta1.APIGroupDiscovery{
  1039  					{
  1040  						ObjectMeta: metav1.ObjectMeta{
  1041  							Name: "apps",
  1042  						},
  1043  						Versions: []apidiscoveryv2beta1.APIVersionDiscovery{
  1044  							{
  1045  								Version: "v2",
  1046  								Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
  1047  									{
  1048  										Resource: "deployments",
  1049  										ResponseKind: &metav1.GroupVersionKind{
  1050  											Group:   "apps",
  1051  											Version: "v2",
  1052  											Kind:    "Deployment",
  1053  										},
  1054  										Scope: apidiscoveryv2beta1.ScopeNamespace,
  1055  									},
  1056  								},
  1057  							},
  1058  							{
  1059  								Version: "v1",
  1060  								Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
  1061  									{
  1062  										Resource: "deployments",
  1063  										ResponseKind: &metav1.GroupVersionKind{
  1064  											Group:   "apps",
  1065  											Version: "v1",
  1066  											Kind:    "Deployment",
  1067  										},
  1068  										Scope: apidiscoveryv2beta1.ScopeNamespace,
  1069  									},
  1070  								},
  1071  							},
  1072  						},
  1073  					},
  1074  				},
  1075  			},
  1076  			expectedGroups: metav1.APIGroupList{
  1077  				Groups: []metav1.APIGroup{
  1078  					{
  1079  						Name: "apps",
  1080  						Versions: []metav1.GroupVersionForDiscovery{
  1081  							{
  1082  								GroupVersion: "apps/v2",
  1083  								Version:      "v2",
  1084  							},
  1085  							{
  1086  								GroupVersion: "apps/v1",
  1087  								Version:      "v1",
  1088  							},
  1089  						},
  1090  						PreferredVersion: metav1.GroupVersionForDiscovery{
  1091  							GroupVersion: "apps/v2",
  1092  							Version:      "v2",
  1093  						},
  1094  					},
  1095  				},
  1096  			},
  1097  			expectedGVResources: map[schema.GroupVersion]*metav1.APIResourceList{
  1098  				{Group: "apps", Version: "v1"}: {
  1099  					GroupVersion: "apps/v1",
  1100  					APIResources: []metav1.APIResource{
  1101  						{
  1102  							Name:       "deployments",
  1103  							Namespaced: true,
  1104  							Group:      "apps",
  1105  							Version:    "v1",
  1106  							Kind:       "Deployment",
  1107  						},
  1108  					},
  1109  				},
  1110  				{Group: "apps", Version: "v2"}: {
  1111  					GroupVersion: "apps/v2",
  1112  					APIResources: []metav1.APIResource{
  1113  						{
  1114  							Name:       "deployments",
  1115  							Namespaced: true,
  1116  							Group:      "apps",
  1117  							Version:    "v2",
  1118  							Kind:       "Deployment",
  1119  						},
  1120  					},
  1121  				},
  1122  			},
  1123  			expectedFailedGVs: map[schema.GroupVersion]error{},
  1124  		},
  1125  		{
  1126  			name: "Aggregated discovery: 1 group/2 resources at /api, 1 group/2 resources at /apis",
  1127  			agg: apidiscoveryv2beta1.APIGroupDiscoveryList{
  1128  				Items: []apidiscoveryv2beta1.APIGroupDiscovery{
  1129  					{
  1130  						Versions: []apidiscoveryv2beta1.APIVersionDiscovery{
  1131  							{
  1132  								Version: "v1",
  1133  								Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
  1134  									{
  1135  										Resource: "pods",
  1136  										ResponseKind: &metav1.GroupVersionKind{
  1137  											Group:   "",
  1138  											Version: "v1",
  1139  											Kind:    "Pod",
  1140  										},
  1141  										Scope: apidiscoveryv2beta1.ScopeNamespace,
  1142  									},
  1143  									{
  1144  										Resource: "services",
  1145  										ResponseKind: &metav1.GroupVersionKind{
  1146  											Group:   "",
  1147  											Version: "v1",
  1148  											Kind:    "Service",
  1149  										},
  1150  										Scope: apidiscoveryv2beta1.ScopeNamespace,
  1151  									},
  1152  								},
  1153  							},
  1154  						},
  1155  					},
  1156  					{
  1157  						ObjectMeta: metav1.ObjectMeta{
  1158  							Name: "apps",
  1159  						},
  1160  						Versions: []apidiscoveryv2beta1.APIVersionDiscovery{
  1161  							{
  1162  								Version: "v1",
  1163  								Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
  1164  									{
  1165  										Resource: "deployments",
  1166  										ResponseKind: &metav1.GroupVersionKind{
  1167  											Group:   "apps",
  1168  											Version: "v1",
  1169  											Kind:    "Deployment",
  1170  										},
  1171  										Scope: apidiscoveryv2beta1.ScopeNamespace,
  1172  									},
  1173  									{
  1174  										Resource: "statefulsets",
  1175  										ResponseKind: &metav1.GroupVersionKind{
  1176  											Group:   "apps",
  1177  											Version: "v1",
  1178  											Kind:    "StatefulSet",
  1179  										},
  1180  										Scope: apidiscoveryv2beta1.ScopeNamespace,
  1181  									},
  1182  								},
  1183  							},
  1184  						},
  1185  					},
  1186  				},
  1187  			},
  1188  			expectedGroups: metav1.APIGroupList{
  1189  				Groups: []metav1.APIGroup{
  1190  					{
  1191  						Name: "",
  1192  						Versions: []metav1.GroupVersionForDiscovery{
  1193  							{
  1194  								GroupVersion: "v1",
  1195  								Version:      "v1",
  1196  							},
  1197  						},
  1198  						PreferredVersion: metav1.GroupVersionForDiscovery{
  1199  							GroupVersion: "v1",
  1200  							Version:      "v1",
  1201  						},
  1202  					},
  1203  					{
  1204  						Name: "apps",
  1205  						Versions: []metav1.GroupVersionForDiscovery{
  1206  							{
  1207  								GroupVersion: "apps/v1",
  1208  								Version:      "v1",
  1209  							},
  1210  						},
  1211  						PreferredVersion: metav1.GroupVersionForDiscovery{
  1212  							GroupVersion: "apps/v1",
  1213  							Version:      "v1",
  1214  						},
  1215  					},
  1216  				},
  1217  			},
  1218  			expectedGVResources: map[schema.GroupVersion]*metav1.APIResourceList{
  1219  				{Group: "", Version: "v1"}: {
  1220  					GroupVersion: "v1",
  1221  					APIResources: []metav1.APIResource{
  1222  						{
  1223  							Name:       "pods",
  1224  							Namespaced: true,
  1225  							Group:      "",
  1226  							Version:    "v1",
  1227  							Kind:       "Pod",
  1228  						},
  1229  						{
  1230  							Name:       "services",
  1231  							Namespaced: true,
  1232  							Group:      "",
  1233  							Version:    "v1",
  1234  							Kind:       "Service",
  1235  						},
  1236  					},
  1237  				},
  1238  				{Group: "apps", Version: "v1"}: {
  1239  					GroupVersion: "apps/v1",
  1240  					APIResources: []metav1.APIResource{
  1241  						{
  1242  							Name:       "deployments",
  1243  							Namespaced: true,
  1244  							Group:      "apps",
  1245  							Version:    "v1",
  1246  							Kind:       "Deployment",
  1247  						},
  1248  						{
  1249  							Name:       "statefulsets",
  1250  							Namespaced: true,
  1251  							Group:      "apps",
  1252  							Version:    "v1",
  1253  							Kind:       "StatefulSet",
  1254  						},
  1255  					},
  1256  				},
  1257  			},
  1258  			expectedFailedGVs: map[schema.GroupVersion]error{},
  1259  		},
  1260  		{
  1261  			name: "Aggregated discovery: multiple groups with cluster-scoped resources",
  1262  			agg: apidiscoveryv2beta1.APIGroupDiscoveryList{
  1263  				Items: []apidiscoveryv2beta1.APIGroupDiscovery{
  1264  					{
  1265  						Versions: []apidiscoveryv2beta1.APIVersionDiscovery{
  1266  							{
  1267  								Version: "v1",
  1268  								Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
  1269  									{
  1270  										Resource: "pods",
  1271  										ResponseKind: &metav1.GroupVersionKind{
  1272  											Group:   "",
  1273  											Version: "v1",
  1274  											Kind:    "Pod",
  1275  										},
  1276  										Scope: apidiscoveryv2beta1.ScopeNamespace,
  1277  									},
  1278  									{
  1279  										Resource: "namespaces",
  1280  										ResponseKind: &metav1.GroupVersionKind{
  1281  											Group:   "",
  1282  											Version: "v1",
  1283  											Kind:    "Namespace",
  1284  										},
  1285  										Scope: apidiscoveryv2beta1.ScopeCluster,
  1286  									},
  1287  								},
  1288  							},
  1289  						},
  1290  					},
  1291  					{
  1292  						ObjectMeta: metav1.ObjectMeta{
  1293  							Name: "rbac.authorization.k8s.io",
  1294  						},
  1295  						Versions: []apidiscoveryv2beta1.APIVersionDiscovery{
  1296  							{
  1297  								Version: "v1",
  1298  								Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
  1299  									{
  1300  										Resource: "roles",
  1301  										ResponseKind: &metav1.GroupVersionKind{
  1302  											Group:   "rbac.authorization.k8s.io",
  1303  											Version: "v1",
  1304  											Kind:    "Role",
  1305  										},
  1306  										Scope: apidiscoveryv2beta1.ScopeCluster,
  1307  									},
  1308  									{
  1309  										Resource: "clusterroles",
  1310  										ResponseKind: &metav1.GroupVersionKind{
  1311  											Group:   "rbac.authorization.k8s.io",
  1312  											Version: "v1",
  1313  											Kind:    "ClusterRole",
  1314  										},
  1315  										Scope: apidiscoveryv2beta1.ScopeCluster,
  1316  									},
  1317  								},
  1318  							},
  1319  						},
  1320  					},
  1321  				},
  1322  			},
  1323  			expectedGroups: metav1.APIGroupList{
  1324  				Groups: []metav1.APIGroup{
  1325  					{
  1326  						Name: "",
  1327  						Versions: []metav1.GroupVersionForDiscovery{
  1328  							{
  1329  								GroupVersion: "v1",
  1330  								Version:      "v1",
  1331  							},
  1332  						},
  1333  						PreferredVersion: metav1.GroupVersionForDiscovery{
  1334  							GroupVersion: "v1",
  1335  							Version:      "v1",
  1336  						},
  1337  					},
  1338  					{
  1339  						Name: "rbac.authorization.k8s.io",
  1340  						Versions: []metav1.GroupVersionForDiscovery{
  1341  							{
  1342  								GroupVersion: "rbac.authorization.k8s.io/v1",
  1343  								Version:      "v1",
  1344  							},
  1345  						},
  1346  						PreferredVersion: metav1.GroupVersionForDiscovery{
  1347  							GroupVersion: "rbac.authorization.k8s.io/v1",
  1348  							Version:      "v1",
  1349  						},
  1350  					},
  1351  				},
  1352  			},
  1353  			expectedGVResources: map[schema.GroupVersion]*metav1.APIResourceList{
  1354  				{Group: "", Version: "v1"}: {
  1355  					GroupVersion: "v1",
  1356  					APIResources: []metav1.APIResource{
  1357  						{
  1358  							Name:       "pods",
  1359  							Namespaced: true,
  1360  							Group:      "",
  1361  							Version:    "v1",
  1362  							Kind:       "Pod",
  1363  						},
  1364  						{
  1365  							Name:       "namespaces",
  1366  							Namespaced: false,
  1367  							Group:      "",
  1368  							Version:    "v1",
  1369  							Kind:       "Namespace",
  1370  						},
  1371  					},
  1372  				},
  1373  				{Group: "rbac.authorization.k8s.io", Version: "v1"}: {
  1374  					GroupVersion: "rbac.authorization.k8s.io/v1",
  1375  					APIResources: []metav1.APIResource{
  1376  						{
  1377  							Name:       "roles",
  1378  							Namespaced: false,
  1379  							Group:      "rbac.authorization.k8s.io",
  1380  							Version:    "v1",
  1381  							Kind:       "Role",
  1382  						},
  1383  						{
  1384  							Name:       "clusterroles",
  1385  							Namespaced: false,
  1386  							Group:      "rbac.authorization.k8s.io",
  1387  							Version:    "v1",
  1388  							Kind:       "ClusterRole",
  1389  						},
  1390  					},
  1391  				},
  1392  			},
  1393  			expectedFailedGVs: map[schema.GroupVersion]error{},
  1394  		},
  1395  		{
  1396  			name: "Aggregated discovery with single subresource",
  1397  			agg: apidiscoveryv2beta1.APIGroupDiscoveryList{
  1398  				Items: []apidiscoveryv2beta1.APIGroupDiscovery{
  1399  					{
  1400  						ObjectMeta: metav1.ObjectMeta{
  1401  							Name: "apps",
  1402  						},
  1403  						Versions: []apidiscoveryv2beta1.APIVersionDiscovery{
  1404  							{
  1405  								Version: "v1",
  1406  								Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
  1407  									{
  1408  										Resource: "deployments",
  1409  										ResponseKind: &metav1.GroupVersionKind{
  1410  											Group:   "apps",
  1411  											Version: "v1",
  1412  											Kind:    "Deployment",
  1413  										},
  1414  										Scope:            apidiscoveryv2beta1.ScopeNamespace,
  1415  										SingularResource: "deployment",
  1416  										ShortNames:       []string{"deploy"},
  1417  										Verbs:            []string{"parentverb1", "parentverb2", "parentverb3", "parentverb4"},
  1418  										Categories:       []string{"all", "testcategory"},
  1419  										Subresources: []apidiscoveryv2beta1.APISubresourceDiscovery{
  1420  											{
  1421  												Subresource: "scale",
  1422  												ResponseKind: &metav1.GroupVersionKind{
  1423  													Group:   "apps",
  1424  													Version: "v1",
  1425  													Kind:    "Deployment",
  1426  												},
  1427  												Verbs: []string{"get", "patch", "update"},
  1428  											},
  1429  										},
  1430  									},
  1431  								},
  1432  							},
  1433  						},
  1434  					},
  1435  				},
  1436  			},
  1437  			expectedGroups: metav1.APIGroupList{
  1438  				Groups: []metav1.APIGroup{
  1439  					{
  1440  						Name: "apps",
  1441  						Versions: []metav1.GroupVersionForDiscovery{
  1442  							{
  1443  								GroupVersion: "apps/v1",
  1444  								Version:      "v1",
  1445  							},
  1446  						},
  1447  						PreferredVersion: metav1.GroupVersionForDiscovery{
  1448  							GroupVersion: "apps/v1",
  1449  							Version:      "v1",
  1450  						},
  1451  					},
  1452  				},
  1453  			},
  1454  			expectedGVResources: map[schema.GroupVersion]*metav1.APIResourceList{
  1455  				{Group: "apps", Version: "v1"}: {
  1456  					GroupVersion: "apps/v1",
  1457  					APIResources: []metav1.APIResource{
  1458  						{
  1459  							Name:         "deployments",
  1460  							SingularName: "deployment",
  1461  							Namespaced:   true,
  1462  							Group:        "apps",
  1463  							Version:      "v1",
  1464  							Kind:         "Deployment",
  1465  							Verbs:        []string{"parentverb1", "parentverb2", "parentverb3", "parentverb4"},
  1466  							ShortNames:   []string{"deploy"},
  1467  							Categories:   []string{"all", "testcategory"},
  1468  						},
  1469  						{
  1470  							Name:         "deployments/scale",
  1471  							SingularName: "deployment",
  1472  							Namespaced:   true,
  1473  							Group:        "apps",
  1474  							Version:      "v1",
  1475  							Kind:         "Deployment",
  1476  							Verbs:        []string{"get", "patch", "update"},
  1477  						},
  1478  					},
  1479  				},
  1480  			},
  1481  			expectedFailedGVs: map[schema.GroupVersion]error{},
  1482  		},
  1483  		{
  1484  			name: "Aggregated discovery with single subresource and parent missing GVK",
  1485  			agg: apidiscoveryv2beta1.APIGroupDiscoveryList{
  1486  				Items: []apidiscoveryv2beta1.APIGroupDiscovery{
  1487  					{
  1488  						ObjectMeta: metav1.ObjectMeta{
  1489  							Name: "external.metrics.k8s.io",
  1490  						},
  1491  						Versions: []apidiscoveryv2beta1.APIVersionDiscovery{
  1492  							{
  1493  								Version: "v1beta1",
  1494  								Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
  1495  									{
  1496  										// resilient to nil GVK for parent
  1497  										Resource:         "*",
  1498  										Scope:            apidiscoveryv2beta1.ScopeNamespace,
  1499  										SingularResource: "",
  1500  										Subresources: []apidiscoveryv2beta1.APISubresourceDiscovery{
  1501  											{
  1502  												Subresource: "other-external-metric",
  1503  												ResponseKind: &metav1.GroupVersionKind{
  1504  													Kind: "MetricValueList",
  1505  												},
  1506  												Verbs: []string{"get"},
  1507  											},
  1508  										},
  1509  									},
  1510  								},
  1511  							},
  1512  						},
  1513  					},
  1514  				},
  1515  			},
  1516  			expectedGroups: metav1.APIGroupList{
  1517  				Groups: []metav1.APIGroup{
  1518  					{
  1519  						Name: "external.metrics.k8s.io",
  1520  						Versions: []metav1.GroupVersionForDiscovery{
  1521  							{
  1522  								GroupVersion: "external.metrics.k8s.io/v1beta1",
  1523  								Version:      "v1beta1",
  1524  							},
  1525  						},
  1526  						PreferredVersion: metav1.GroupVersionForDiscovery{
  1527  							GroupVersion: "external.metrics.k8s.io/v1beta1",
  1528  							Version:      "v1beta1",
  1529  						},
  1530  					},
  1531  				},
  1532  			},
  1533  			expectedGVResources: map[schema.GroupVersion]*metav1.APIResourceList{
  1534  				{Group: "external.metrics.k8s.io", Version: "v1beta1"}: {
  1535  					GroupVersion: "external.metrics.k8s.io/v1beta1",
  1536  					APIResources: []metav1.APIResource{
  1537  						// Since parent GVK was nil, it is NOT returned--only the subresource.
  1538  						{
  1539  							Name:         "*/other-external-metric",
  1540  							SingularName: "",
  1541  							Namespaced:   true,
  1542  							Group:        "",
  1543  							Version:      "",
  1544  							Kind:         "MetricValueList",
  1545  							Verbs:        []string{"get"},
  1546  						},
  1547  					},
  1548  				},
  1549  			},
  1550  			expectedFailedGVs: map[schema.GroupVersion]error{},
  1551  		},
  1552  		{
  1553  			name: "Aggregated discovery with single subresource and parent empty GVK",
  1554  			agg: apidiscoveryv2beta1.APIGroupDiscoveryList{
  1555  				Items: []apidiscoveryv2beta1.APIGroupDiscovery{
  1556  					{
  1557  						ObjectMeta: metav1.ObjectMeta{
  1558  							Name: "external.metrics.k8s.io",
  1559  						},
  1560  						Versions: []apidiscoveryv2beta1.APIVersionDiscovery{
  1561  							{
  1562  								Version: "v1beta1",
  1563  								Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
  1564  									{
  1565  										// resilient to empty GVK for parent
  1566  										Resource:         "*",
  1567  										Scope:            apidiscoveryv2beta1.ScopeNamespace,
  1568  										SingularResource: "",
  1569  										ResponseKind:     &metav1.GroupVersionKind{},
  1570  										Subresources: []apidiscoveryv2beta1.APISubresourceDiscovery{
  1571  											{
  1572  												Subresource: "other-external-metric",
  1573  												ResponseKind: &metav1.GroupVersionKind{
  1574  													Kind: "MetricValueList",
  1575  												},
  1576  												Verbs: []string{"get"},
  1577  											},
  1578  										},
  1579  									},
  1580  								},
  1581  							},
  1582  						},
  1583  					},
  1584  				},
  1585  			},
  1586  			expectedGroups: metav1.APIGroupList{
  1587  				Groups: []metav1.APIGroup{
  1588  					{
  1589  						Name: "external.metrics.k8s.io",
  1590  						Versions: []metav1.GroupVersionForDiscovery{
  1591  							{
  1592  								GroupVersion: "external.metrics.k8s.io/v1beta1",
  1593  								Version:      "v1beta1",
  1594  							},
  1595  						},
  1596  						PreferredVersion: metav1.GroupVersionForDiscovery{
  1597  							GroupVersion: "external.metrics.k8s.io/v1beta1",
  1598  							Version:      "v1beta1",
  1599  						},
  1600  					},
  1601  				},
  1602  			},
  1603  			expectedGVResources: map[schema.GroupVersion]*metav1.APIResourceList{
  1604  				{Group: "external.metrics.k8s.io", Version: "v1beta1"}: {
  1605  					GroupVersion: "external.metrics.k8s.io/v1beta1",
  1606  					APIResources: []metav1.APIResource{
  1607  						// Since parent GVK was nil, it is NOT returned--only the subresource.
  1608  						{
  1609  							Name:         "*/other-external-metric",
  1610  							SingularName: "",
  1611  							Namespaced:   true,
  1612  							Group:        "",
  1613  							Version:      "",
  1614  							Kind:         "MetricValueList",
  1615  							Verbs:        []string{"get"},
  1616  						},
  1617  					},
  1618  				},
  1619  			},
  1620  			expectedFailedGVs: map[schema.GroupVersion]error{},
  1621  		},
  1622  		{
  1623  			name: "Aggregated discovery with multiple subresources",
  1624  			agg: apidiscoveryv2beta1.APIGroupDiscoveryList{
  1625  				Items: []apidiscoveryv2beta1.APIGroupDiscovery{
  1626  					{
  1627  						ObjectMeta: metav1.ObjectMeta{
  1628  							Name: "apps",
  1629  						},
  1630  						Versions: []apidiscoveryv2beta1.APIVersionDiscovery{
  1631  							{
  1632  								Version: "v1",
  1633  								Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
  1634  									{
  1635  										Resource: "deployments",
  1636  										ResponseKind: &metav1.GroupVersionKind{
  1637  											Group:   "apps",
  1638  											Version: "v1",
  1639  											Kind:    "Deployment",
  1640  										},
  1641  										Scope:            apidiscoveryv2beta1.ScopeNamespace,
  1642  										SingularResource: "deployment",
  1643  										Subresources: []apidiscoveryv2beta1.APISubresourceDiscovery{
  1644  											{
  1645  												Subresource: "scale",
  1646  												ResponseKind: &metav1.GroupVersionKind{
  1647  													Group:   "apps",
  1648  													Version: "v1",
  1649  													Kind:    "Deployment",
  1650  												},
  1651  												Verbs: []string{"get", "patch", "update"},
  1652  											},
  1653  											{
  1654  												Subresource: "status",
  1655  												ResponseKind: &metav1.GroupVersionKind{
  1656  													Group:   "apps",
  1657  													Version: "v1",
  1658  													Kind:    "Deployment",
  1659  												},
  1660  												Verbs: []string{"get", "patch", "update"},
  1661  											},
  1662  										},
  1663  									},
  1664  								},
  1665  							},
  1666  						},
  1667  					},
  1668  				},
  1669  			},
  1670  			expectedGroups: metav1.APIGroupList{
  1671  				Groups: []metav1.APIGroup{
  1672  					{
  1673  						Name: "apps",
  1674  						Versions: []metav1.GroupVersionForDiscovery{
  1675  							{
  1676  								GroupVersion: "apps/v1",
  1677  								Version:      "v1",
  1678  							},
  1679  						},
  1680  						PreferredVersion: metav1.GroupVersionForDiscovery{
  1681  							GroupVersion: "apps/v1",
  1682  							Version:      "v1",
  1683  						},
  1684  					},
  1685  				},
  1686  			},
  1687  			expectedGVResources: map[schema.GroupVersion]*metav1.APIResourceList{
  1688  				{Group: "apps", Version: "v1"}: {
  1689  					GroupVersion: "apps/v1",
  1690  					APIResources: []metav1.APIResource{
  1691  						{
  1692  							Name:         "deployments",
  1693  							SingularName: "deployment",
  1694  							Namespaced:   true,
  1695  							Group:        "apps",
  1696  							Version:      "v1",
  1697  							Kind:         "Deployment",
  1698  						},
  1699  						{
  1700  							Name:         "deployments/scale",
  1701  							SingularName: "deployment",
  1702  							Namespaced:   true,
  1703  							Group:        "apps",
  1704  							Version:      "v1",
  1705  							Kind:         "Deployment",
  1706  							Verbs:        []string{"get", "patch", "update"},
  1707  						},
  1708  						{
  1709  							Name:         "deployments/status",
  1710  							SingularName: "deployment",
  1711  							Namespaced:   true,
  1712  							Group:        "apps",
  1713  							Version:      "v1",
  1714  							Kind:         "Deployment",
  1715  							Verbs:        []string{"get", "patch", "update"},
  1716  						},
  1717  					},
  1718  				},
  1719  			},
  1720  			expectedFailedGVs: map[schema.GroupVersion]error{},
  1721  		},
  1722  		{
  1723  			name: "Aggregated discovery: single failed GV at /api",
  1724  			agg: apidiscoveryv2beta1.APIGroupDiscoveryList{
  1725  				Items: []apidiscoveryv2beta1.APIGroupDiscovery{
  1726  					{
  1727  						Versions: []apidiscoveryv2beta1.APIVersionDiscovery{
  1728  							{
  1729  								Version: "v1",
  1730  								Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
  1731  									{
  1732  										Resource: "pods",
  1733  										ResponseKind: &metav1.GroupVersionKind{
  1734  											Group:   "",
  1735  											Version: "v1",
  1736  											Kind:    "Pod",
  1737  										},
  1738  										Scope: apidiscoveryv2beta1.ScopeNamespace,
  1739  									},
  1740  									{
  1741  										Resource: "services",
  1742  										ResponseKind: &metav1.GroupVersionKind{
  1743  											Group:   "",
  1744  											Version: "v1",
  1745  											Kind:    "Service",
  1746  										},
  1747  										Scope: apidiscoveryv2beta1.ScopeNamespace,
  1748  									},
  1749  								},
  1750  								Freshness: apidiscoveryv2beta1.DiscoveryFreshnessStale,
  1751  							},
  1752  						},
  1753  					},
  1754  				},
  1755  			},
  1756  			// Single core Group/Version is stale, so no Version within Group.
  1757  			expectedGroups: metav1.APIGroupList{
  1758  				Groups: []metav1.APIGroup{{Name: ""}},
  1759  			},
  1760  			// Single core Group/Version is stale, so there are no expected resources.
  1761  			expectedGVResources: map[schema.GroupVersion]*metav1.APIResourceList{},
  1762  			expectedFailedGVs: map[schema.GroupVersion]error{
  1763  				{Group: "", Version: "v1"}: StaleGroupVersionError{gv: schema.GroupVersion{Group: "", Version: "v1"}},
  1764  			},
  1765  		},
  1766  		{
  1767  			name: "Aggregated discovery: single failed GV at /apis",
  1768  			agg: apidiscoveryv2beta1.APIGroupDiscoveryList{
  1769  				Items: []apidiscoveryv2beta1.APIGroupDiscovery{
  1770  					{
  1771  						ObjectMeta: metav1.ObjectMeta{
  1772  							Name: "apps",
  1773  						},
  1774  						Versions: []apidiscoveryv2beta1.APIVersionDiscovery{
  1775  							{
  1776  								Version: "v1",
  1777  								Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
  1778  									{
  1779  										Resource: "deployments",
  1780  										ResponseKind: &metav1.GroupVersionKind{
  1781  											Group:   "apps",
  1782  											Version: "v1",
  1783  											Kind:    "Deployment",
  1784  										},
  1785  										Scope: apidiscoveryv2beta1.ScopeNamespace,
  1786  									},
  1787  									{
  1788  										Resource: "statefulsets",
  1789  										ResponseKind: &metav1.GroupVersionKind{
  1790  											Group:   "apps",
  1791  											Version: "v1",
  1792  											Kind:    "StatefulSets",
  1793  										},
  1794  										Scope: apidiscoveryv2beta1.ScopeNamespace,
  1795  									},
  1796  								},
  1797  								Freshness: apidiscoveryv2beta1.DiscoveryFreshnessStale,
  1798  							},
  1799  						},
  1800  					},
  1801  				},
  1802  			},
  1803  			// Single apps/v1 Group/Version is stale, so no Version within Group.
  1804  			expectedGroups: metav1.APIGroupList{
  1805  				Groups: []metav1.APIGroup{{Name: "apps"}},
  1806  			},
  1807  			// Single apps/v1 Group/Version is stale, so there are no expected resources.
  1808  			expectedGVResources: map[schema.GroupVersion]*metav1.APIResourceList{},
  1809  			expectedFailedGVs: map[schema.GroupVersion]error{
  1810  				{Group: "apps", Version: "v1"}: StaleGroupVersionError{gv: schema.GroupVersion{Group: "apps", Version: "v1"}},
  1811  			},
  1812  		},
  1813  		{
  1814  			name: "Aggregated discovery: 1 group/2 versions/1 failed GV at /apis",
  1815  			agg: apidiscoveryv2beta1.APIGroupDiscoveryList{
  1816  				Items: []apidiscoveryv2beta1.APIGroupDiscovery{
  1817  					{
  1818  						ObjectMeta: metav1.ObjectMeta{
  1819  							Name: "apps",
  1820  						},
  1821  						Versions: []apidiscoveryv2beta1.APIVersionDiscovery{
  1822  							// Stale v2 should report failed GV.
  1823  							{
  1824  								Version: "v2",
  1825  								Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
  1826  									{
  1827  										Resource: "daemonsets",
  1828  										ResponseKind: &metav1.GroupVersionKind{
  1829  											Group:   "apps",
  1830  											Version: "v2",
  1831  											Kind:    "DaemonSets",
  1832  										},
  1833  										Scope: apidiscoveryv2beta1.ScopeNamespace,
  1834  									},
  1835  								},
  1836  								Freshness: apidiscoveryv2beta1.DiscoveryFreshnessStale,
  1837  							},
  1838  							{
  1839  								Version: "v1",
  1840  								Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
  1841  									{
  1842  										Resource: "deployments",
  1843  										ResponseKind: &metav1.GroupVersionKind{
  1844  											Group:   "apps",
  1845  											Version: "v1",
  1846  											Kind:    "Deployment",
  1847  										},
  1848  										Scope: apidiscoveryv2beta1.ScopeNamespace,
  1849  									},
  1850  								},
  1851  							},
  1852  						},
  1853  					},
  1854  				},
  1855  			},
  1856  			// Only apps/v1 is non-stale expected Group/Version
  1857  			expectedGroups: metav1.APIGroupList{
  1858  				Groups: []metav1.APIGroup{
  1859  					{
  1860  						Name: "apps",
  1861  						Versions: []metav1.GroupVersionForDiscovery{
  1862  							{
  1863  								GroupVersion: "apps/v1",
  1864  								Version:      "v1",
  1865  							},
  1866  						},
  1867  						// PreferredVersion must be apps/v1
  1868  						PreferredVersion: metav1.GroupVersionForDiscovery{
  1869  							GroupVersion: "apps/v1",
  1870  							Version:      "v1",
  1871  						},
  1872  					},
  1873  				},
  1874  			},
  1875  			// Only apps/v1 resources expected.
  1876  			expectedGVResources: map[schema.GroupVersion]*metav1.APIResourceList{
  1877  				{Group: "apps", Version: "v1"}: {
  1878  					GroupVersion: "apps/v1",
  1879  					APIResources: []metav1.APIResource{
  1880  						{
  1881  							Name:       "deployments",
  1882  							Namespaced: true,
  1883  							Group:      "apps",
  1884  							Version:    "v1",
  1885  							Kind:       "Deployment",
  1886  						},
  1887  					},
  1888  				},
  1889  			},
  1890  			expectedFailedGVs: map[schema.GroupVersion]error{
  1891  				{Group: "apps", Version: "v2"}: StaleGroupVersionError{gv: schema.GroupVersion{Group: "apps", Version: "v2"}},
  1892  			},
  1893  		},
  1894  	}
  1895  
  1896  	for _, test := range tests {
  1897  		apiGroups, resourcesByGV, failedGVs := SplitGroupsAndResourcesV2Beta1(test.agg)
  1898  		assert.Equal(t, test.expectedFailedGVs, failedGVs)
  1899  		assert.Equal(t, test.expectedGroups, *apiGroups)
  1900  		assert.Equal(t, test.expectedGVResources, resourcesByGV)
  1901  	}
  1902  }