Merge pull request #122882 from Jefftree/agg-discovery-v2-usage
Use Aggregated Discovery v2 types and promote to GA
This commit is contained in:
		@@ -1176,7 +1176,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
 | 
			
		||||
 | 
			
		||||
	genericfeatures.AdmissionWebhookMatchConditions: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.33
 | 
			
		||||
 | 
			
		||||
	genericfeatures.AggregatedDiscoveryEndpoint: {Default: true, PreRelease: featuregate.Beta},
 | 
			
		||||
	genericfeatures.AggregatedDiscoveryEndpoint: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.33
 | 
			
		||||
 | 
			
		||||
	genericfeatures.APIListChunking: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.32
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -23,7 +23,7 @@ import (
 | 
			
		||||
 | 
			
		||||
	"k8s.io/klog/v2"
 | 
			
		||||
 | 
			
		||||
	apidiscoveryv2beta1 "k8s.io/api/apidiscovery/v2beta1"
 | 
			
		||||
	apidiscoveryv2 "k8s.io/api/apidiscovery/v2"
 | 
			
		||||
	autoscaling "k8s.io/api/autoscaling/v1"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/labels"
 | 
			
		||||
@@ -87,7 +87,7 @@ func (c *DiscoveryController) sync(version schema.GroupVersion) error {
 | 
			
		||||
 | 
			
		||||
	apiVersionsForDiscovery := []metav1.GroupVersionForDiscovery{}
 | 
			
		||||
	apiResourcesForDiscovery := []metav1.APIResource{}
 | 
			
		||||
	aggregatedApiResourcesForDiscovery := []apidiscoveryv2beta1.APIResourceDiscovery{}
 | 
			
		||||
	aggregatedAPIResourcesForDiscovery := []apidiscoveryv2.APIResourceDiscovery{}
 | 
			
		||||
	versionsForDiscoveryMap := map[metav1.GroupVersion]bool{}
 | 
			
		||||
 | 
			
		||||
	crds, err := c.crdLister.List(labels.Everything())
 | 
			
		||||
@@ -158,13 +158,13 @@ func (c *DiscoveryController) sync(version schema.GroupVersion) error {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if c.resourceManager != nil {
 | 
			
		||||
			var scope apidiscoveryv2beta1.ResourceScope
 | 
			
		||||
			var scope apidiscoveryv2.ResourceScope
 | 
			
		||||
			if crd.Spec.Scope == apiextensionsv1.NamespaceScoped {
 | 
			
		||||
				scope = apidiscoveryv2beta1.ScopeNamespace
 | 
			
		||||
				scope = apidiscoveryv2.ScopeNamespace
 | 
			
		||||
			} else {
 | 
			
		||||
				scope = apidiscoveryv2beta1.ScopeCluster
 | 
			
		||||
				scope = apidiscoveryv2.ScopeCluster
 | 
			
		||||
			}
 | 
			
		||||
			apiResourceDiscovery := apidiscoveryv2beta1.APIResourceDiscovery{
 | 
			
		||||
			apiResourceDiscovery := apidiscoveryv2.APIResourceDiscovery{
 | 
			
		||||
				Resource:         crd.Status.AcceptedNames.Plural,
 | 
			
		||||
				SingularResource: crd.Status.AcceptedNames.Singular,
 | 
			
		||||
				Scope:            scope,
 | 
			
		||||
@@ -178,7 +178,7 @@ func (c *DiscoveryController) sync(version schema.GroupVersion) error {
 | 
			
		||||
				Categories: crd.Status.AcceptedNames.Categories,
 | 
			
		||||
			}
 | 
			
		||||
			if subresources != nil && subresources.Status != nil {
 | 
			
		||||
				apiResourceDiscovery.Subresources = append(apiResourceDiscovery.Subresources, apidiscoveryv2beta1.APISubresourceDiscovery{
 | 
			
		||||
				apiResourceDiscovery.Subresources = append(apiResourceDiscovery.Subresources, apidiscoveryv2.APISubresourceDiscovery{
 | 
			
		||||
					Subresource: "status",
 | 
			
		||||
					ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
						Group:   version.Group,
 | 
			
		||||
@@ -189,7 +189,7 @@ func (c *DiscoveryController) sync(version schema.GroupVersion) error {
 | 
			
		||||
				})
 | 
			
		||||
			}
 | 
			
		||||
			if subresources != nil && subresources.Scale != nil {
 | 
			
		||||
				apiResourceDiscovery.Subresources = append(apiResourceDiscovery.Subresources, apidiscoveryv2beta1.APISubresourceDiscovery{
 | 
			
		||||
				apiResourceDiscovery.Subresources = append(apiResourceDiscovery.Subresources, apidiscoveryv2.APISubresourceDiscovery{
 | 
			
		||||
					Subresource: "scale",
 | 
			
		||||
					ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
						Group:   autoscaling.GroupName,
 | 
			
		||||
@@ -200,7 +200,7 @@ func (c *DiscoveryController) sync(version schema.GroupVersion) error {
 | 
			
		||||
				})
 | 
			
		||||
 | 
			
		||||
			}
 | 
			
		||||
			aggregatedApiResourcesForDiscovery = append(aggregatedApiResourcesForDiscovery, apiResourceDiscovery)
 | 
			
		||||
			aggregatedAPIResourcesForDiscovery = append(aggregatedAPIResourcesForDiscovery, apiResourceDiscovery)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if subresources != nil && subresources.Status != nil {
 | 
			
		||||
@@ -260,14 +260,14 @@ func (c *DiscoveryController) sync(version schema.GroupVersion) error {
 | 
			
		||||
		return apiResourcesForDiscovery
 | 
			
		||||
	})))
 | 
			
		||||
 | 
			
		||||
	sort.Slice(aggregatedApiResourcesForDiscovery[:], func(i, j int) bool {
 | 
			
		||||
		return aggregatedApiResourcesForDiscovery[i].Resource < aggregatedApiResourcesForDiscovery[j].Resource
 | 
			
		||||
	sort.Slice(aggregatedAPIResourcesForDiscovery, func(i, j int) bool {
 | 
			
		||||
		return aggregatedAPIResourcesForDiscovery[i].Resource < aggregatedAPIResourcesForDiscovery[j].Resource
 | 
			
		||||
	})
 | 
			
		||||
	if c.resourceManager != nil {
 | 
			
		||||
		c.resourceManager.AddGroupVersion(version.Group, apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
			Freshness: apidiscoveryv2beta1.DiscoveryFreshnessCurrent,
 | 
			
		||||
		c.resourceManager.AddGroupVersion(version.Group, apidiscoveryv2.APIVersionDiscovery{
 | 
			
		||||
			Freshness: apidiscoveryv2.DiscoveryFreshnessCurrent,
 | 
			
		||||
			Version:   version.Version,
 | 
			
		||||
			Resources: aggregatedApiResourcesForDiscovery,
 | 
			
		||||
			Resources: aggregatedAPIResourcesForDiscovery,
 | 
			
		||||
		})
 | 
			
		||||
		// Default priority for CRDs
 | 
			
		||||
		c.resourceManager.SetGroupVersionPriority(metav1.GroupVersion(version), 1000, 100)
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,7 @@ import (
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/stretchr/testify/require"
 | 
			
		||||
	apidiscoveryv2beta1 "k8s.io/api/apidiscovery/v2beta1"
 | 
			
		||||
	apidiscoveryv2 "k8s.io/api/apidiscovery/v2"
 | 
			
		||||
	v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
 | 
			
		||||
	"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
 | 
			
		||||
	"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/fake"
 | 
			
		||||
@@ -125,13 +125,13 @@ var coolBarCRD = &v1.CustomResourceDefinition{
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var coolFooDiscovery apidiscoveryv2beta1.APIVersionDiscovery = apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
var coolFooDiscovery apidiscoveryv2.APIVersionDiscovery = apidiscoveryv2.APIVersionDiscovery{
 | 
			
		||||
	Version:   "v1",
 | 
			
		||||
	Freshness: apidiscoveryv2beta1.DiscoveryFreshnessCurrent,
 | 
			
		||||
	Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
 | 
			
		||||
	Freshness: apidiscoveryv2.DiscoveryFreshnessCurrent,
 | 
			
		||||
	Resources: []apidiscoveryv2.APIResourceDiscovery{
 | 
			
		||||
		{
 | 
			
		||||
			Resource:         "coolfoos",
 | 
			
		||||
			Scope:            apidiscoveryv2beta1.ScopeCluster,
 | 
			
		||||
			Scope:            apidiscoveryv2.ScopeCluster,
 | 
			
		||||
			SingularResource: "coolfoo",
 | 
			
		||||
			Verbs:            []string{"delete", "deletecollection", "get", "list", "patch", "create", "update", "watch"},
 | 
			
		||||
			ShortNames:       []string{"foo"},
 | 
			
		||||
@@ -141,7 +141,7 @@ var coolFooDiscovery apidiscoveryv2beta1.APIVersionDiscovery = apidiscoveryv2bet
 | 
			
		||||
				Version: "v1",
 | 
			
		||||
				Kind:    "CoolFoo",
 | 
			
		||||
			},
 | 
			
		||||
			Subresources: []apidiscoveryv2beta1.APISubresourceDiscovery{
 | 
			
		||||
			Subresources: []apidiscoveryv2.APISubresourceDiscovery{
 | 
			
		||||
				{
 | 
			
		||||
					Subresource:   "status",
 | 
			
		||||
					Verbs:         []string{"get", "patch", "update"},
 | 
			
		||||
@@ -157,13 +157,13 @@ var coolFooDiscovery apidiscoveryv2beta1.APIVersionDiscovery = apidiscoveryv2bet
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var mergedDiscovery apidiscoveryv2beta1.APIVersionDiscovery = apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
var mergedDiscovery apidiscoveryv2.APIVersionDiscovery = apidiscoveryv2.APIVersionDiscovery{
 | 
			
		||||
	Version:   "v1",
 | 
			
		||||
	Freshness: apidiscoveryv2beta1.DiscoveryFreshnessCurrent,
 | 
			
		||||
	Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
 | 
			
		||||
	Freshness: apidiscoveryv2.DiscoveryFreshnessCurrent,
 | 
			
		||||
	Resources: []apidiscoveryv2.APIResourceDiscovery{
 | 
			
		||||
		{
 | 
			
		||||
			Resource:         "coolbars",
 | 
			
		||||
			Scope:            apidiscoveryv2beta1.ScopeCluster,
 | 
			
		||||
			Scope:            apidiscoveryv2.ScopeCluster,
 | 
			
		||||
			SingularResource: "coolbar",
 | 
			
		||||
			Verbs:            []string{"delete", "deletecollection", "get", "list", "patch", "create", "update", "watch"},
 | 
			
		||||
			ShortNames:       []string{"bar"},
 | 
			
		||||
@@ -175,7 +175,7 @@ var mergedDiscovery apidiscoveryv2beta1.APIVersionDiscovery = apidiscoveryv2beta
 | 
			
		||||
			},
 | 
			
		||||
		}, {
 | 
			
		||||
			Resource:         "coolfoos",
 | 
			
		||||
			Scope:            apidiscoveryv2beta1.ScopeCluster,
 | 
			
		||||
			Scope:            apidiscoveryv2.ScopeCluster,
 | 
			
		||||
			SingularResource: "coolfoo",
 | 
			
		||||
			Verbs:            []string{"delete", "deletecollection", "get", "list", "patch", "create", "update", "watch"},
 | 
			
		||||
			ShortNames:       []string{"foo"},
 | 
			
		||||
@@ -185,7 +185,7 @@ var mergedDiscovery apidiscoveryv2beta1.APIVersionDiscovery = apidiscoveryv2beta
 | 
			
		||||
				Version: "v1",
 | 
			
		||||
				Kind:    "CoolFoo",
 | 
			
		||||
			},
 | 
			
		||||
			Subresources: []apidiscoveryv2beta1.APISubresourceDiscovery{
 | 
			
		||||
			Subresources: []apidiscoveryv2.APISubresourceDiscovery{
 | 
			
		||||
				{
 | 
			
		||||
					Subresource:   "status",
 | 
			
		||||
					Verbs:         []string{"get", "patch", "update"},
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,226 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2024 The Kubernetes Authors.
 | 
			
		||||
 | 
			
		||||
Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
you may not use this file except in compliance with the License.
 | 
			
		||||
You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
See the License for the specific language governing permissions and
 | 
			
		||||
limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
// This file was duplicated from the auto-generated file by conversion-gen in
 | 
			
		||||
// k8s.io/kubernetes/pkg/apis/apidiscovery Unlike most k8s types discovery is
 | 
			
		||||
// served by all apiservers and conversion is needed by all apiservers. The
 | 
			
		||||
// concept of internal/hub type does not exist for discovery as we work directly
 | 
			
		||||
// with the versioned types.
 | 
			
		||||
 | 
			
		||||
// The conversion code here facilities conversion strictly between v2beta1 and
 | 
			
		||||
// v2 types. It is only necessary in k8s versions where mixed state could be
 | 
			
		||||
// possible before the full removal of the v2beta1 types. It is placed in this
 | 
			
		||||
// directory such that all apiservers can benefit from the conversion without
 | 
			
		||||
// having to implement their own if the client/server they're communicating with
 | 
			
		||||
// only supports one version.
 | 
			
		||||
 | 
			
		||||
// Once the v2beta1 types are removed (intended for Kubernetes v1.33), this file
 | 
			
		||||
// will be removed.
 | 
			
		||||
package v2
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	unsafe "unsafe"
 | 
			
		||||
 | 
			
		||||
	v2 "k8s.io/api/apidiscovery/v2"
 | 
			
		||||
	v2beta1 "k8s.io/api/apidiscovery/v2beta1"
 | 
			
		||||
	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	conversion "k8s.io/apimachinery/pkg/conversion"
 | 
			
		||||
	runtime "k8s.io/apimachinery/pkg/runtime"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// RegisterConversions adds conversion functions to the given scheme.
 | 
			
		||||
// Public to allow building arbitrary schemes.
 | 
			
		||||
func RegisterConversions(s *runtime.Scheme) error {
 | 
			
		||||
	if err := s.AddGeneratedConversionFunc((*v2beta1.APIGroupDiscovery)(nil), (*v2.APIGroupDiscovery)(nil), func(a, b interface{}, scope conversion.Scope) error {
 | 
			
		||||
		return Convertv2beta1APIGroupDiscoveryTov2APIGroupDiscovery(a.(*v2beta1.APIGroupDiscovery), b.(*v2.APIGroupDiscovery), scope)
 | 
			
		||||
	}); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if err := s.AddGeneratedConversionFunc((*v2.APIGroupDiscovery)(nil), (*v2beta1.APIGroupDiscovery)(nil), func(a, b interface{}, scope conversion.Scope) error {
 | 
			
		||||
		return Convertv2APIGroupDiscoveryTov2beta1APIGroupDiscovery(a.(*v2.APIGroupDiscovery), b.(*v2beta1.APIGroupDiscovery), scope)
 | 
			
		||||
	}); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if err := s.AddGeneratedConversionFunc((*v2beta1.APIGroupDiscoveryList)(nil), (*v2.APIGroupDiscoveryList)(nil), func(a, b interface{}, scope conversion.Scope) error {
 | 
			
		||||
		return Convertv2beta1APIGroupDiscoveryListTov2APIGroupDiscoveryList(a.(*v2beta1.APIGroupDiscoveryList), b.(*v2.APIGroupDiscoveryList), scope)
 | 
			
		||||
	}); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if err := s.AddGeneratedConversionFunc((*v2.APIGroupDiscoveryList)(nil), (*v2beta1.APIGroupDiscoveryList)(nil), func(a, b interface{}, scope conversion.Scope) error {
 | 
			
		||||
		return Convertv2APIGroupDiscoveryListTov2beta1APIGroupDiscoveryList(a.(*v2.APIGroupDiscoveryList), b.(*v2beta1.APIGroupDiscoveryList), scope)
 | 
			
		||||
	}); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if err := s.AddGeneratedConversionFunc((*v2beta1.APIResourceDiscovery)(nil), (*v2.APIResourceDiscovery)(nil), func(a, b interface{}, scope conversion.Scope) error {
 | 
			
		||||
		return Convertv2beta1APIResourceDiscoveryTov2APIResourceDiscovery(a.(*v2beta1.APIResourceDiscovery), b.(*v2.APIResourceDiscovery), scope)
 | 
			
		||||
	}); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if err := s.AddGeneratedConversionFunc((*v2.APIResourceDiscovery)(nil), (*v2beta1.APIResourceDiscovery)(nil), func(a, b interface{}, scope conversion.Scope) error {
 | 
			
		||||
		return Convertv2APIResourceDiscoveryTov2beta1APIResourceDiscovery(a.(*v2.APIResourceDiscovery), b.(*v2beta1.APIResourceDiscovery), scope)
 | 
			
		||||
	}); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if err := s.AddGeneratedConversionFunc((*v2beta1.APISubresourceDiscovery)(nil), (*v2.APISubresourceDiscovery)(nil), func(a, b interface{}, scope conversion.Scope) error {
 | 
			
		||||
		return Convertv2beta1APISubresourceDiscoveryTov2APISubresourceDiscovery(a.(*v2beta1.APISubresourceDiscovery), b.(*v2.APISubresourceDiscovery), scope)
 | 
			
		||||
	}); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if err := s.AddGeneratedConversionFunc((*v2.APISubresourceDiscovery)(nil), (*v2beta1.APISubresourceDiscovery)(nil), func(a, b interface{}, scope conversion.Scope) error {
 | 
			
		||||
		return Convertv2APISubresourceDiscoveryTov2beta1APISubresourceDiscovery(a.(*v2.APISubresourceDiscovery), b.(*v2beta1.APISubresourceDiscovery), scope)
 | 
			
		||||
	}); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if err := s.AddGeneratedConversionFunc((*v2beta1.APIVersionDiscovery)(nil), (*v2.APIVersionDiscovery)(nil), func(a, b interface{}, scope conversion.Scope) error {
 | 
			
		||||
		return Convertv2beta1APIVersionDiscoveryTov2APIVersionDiscovery(a.(*v2beta1.APIVersionDiscovery), b.(*v2.APIVersionDiscovery), scope)
 | 
			
		||||
	}); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if err := s.AddGeneratedConversionFunc((*v2.APIVersionDiscovery)(nil), (*v2beta1.APIVersionDiscovery)(nil), func(a, b interface{}, scope conversion.Scope) error {
 | 
			
		||||
		return Convertv2APIVersionDiscoveryTov2beta1APIVersionDiscovery(a.(*v2.APIVersionDiscovery), b.(*v2beta1.APIVersionDiscovery), scope)
 | 
			
		||||
	}); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func autoConvertv2beta1APIGroupDiscoveryTov2APIGroupDiscovery(in *v2beta1.APIGroupDiscovery, out *v2.APIGroupDiscovery, s conversion.Scope) error {
 | 
			
		||||
	out.ObjectMeta = in.ObjectMeta
 | 
			
		||||
	out.Versions = *(*[]v2.APIVersionDiscovery)(unsafe.Pointer(&in.Versions))
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Convertv2beta1APIGroupDiscoveryTov2APIGroupDiscovery is an autogenerated conversion function.
 | 
			
		||||
func Convertv2beta1APIGroupDiscoveryTov2APIGroupDiscovery(in *v2beta1.APIGroupDiscovery, out *v2.APIGroupDiscovery, s conversion.Scope) error {
 | 
			
		||||
	return autoConvertv2beta1APIGroupDiscoveryTov2APIGroupDiscovery(in, out, s)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func autoConvertv2APIGroupDiscoveryTov2beta1APIGroupDiscovery(in *v2.APIGroupDiscovery, out *v2beta1.APIGroupDiscovery, s conversion.Scope) error {
 | 
			
		||||
	out.ObjectMeta = in.ObjectMeta
 | 
			
		||||
	out.Versions = *(*[]v2beta1.APIVersionDiscovery)(unsafe.Pointer(&in.Versions))
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Convertv2APIGroupDiscoveryTov2beta1APIGroupDiscovery is an autogenerated conversion function.
 | 
			
		||||
func Convertv2APIGroupDiscoveryTov2beta1APIGroupDiscovery(in *v2.APIGroupDiscovery, out *v2beta1.APIGroupDiscovery, s conversion.Scope) error {
 | 
			
		||||
	return autoConvertv2APIGroupDiscoveryTov2beta1APIGroupDiscovery(in, out, s)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func autoConvertv2beta1APIGroupDiscoveryListTov2APIGroupDiscoveryList(in *v2beta1.APIGroupDiscoveryList, out *v2.APIGroupDiscoveryList, s conversion.Scope) error {
 | 
			
		||||
	out.ListMeta = in.ListMeta
 | 
			
		||||
	out.Items = *(*[]v2.APIGroupDiscovery)(unsafe.Pointer(&in.Items))
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Convertv2beta1APIGroupDiscoveryListTov2APIGroupDiscoveryList is an autogenerated conversion function.
 | 
			
		||||
func Convertv2beta1APIGroupDiscoveryListTov2APIGroupDiscoveryList(in *v2beta1.APIGroupDiscoveryList, out *v2.APIGroupDiscoveryList, s conversion.Scope) error {
 | 
			
		||||
	return autoConvertv2beta1APIGroupDiscoveryListTov2APIGroupDiscoveryList(in, out, s)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func autoConvertv2APIGroupDiscoveryListTov2beta1APIGroupDiscoveryList(in *v2.APIGroupDiscoveryList, out *v2beta1.APIGroupDiscoveryList, s conversion.Scope) error {
 | 
			
		||||
	out.ListMeta = in.ListMeta
 | 
			
		||||
	out.Items = *(*[]v2beta1.APIGroupDiscovery)(unsafe.Pointer(&in.Items))
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Convertv2APIGroupDiscoveryListTov2beta1APIGroupDiscoveryList is an autogenerated conversion function.
 | 
			
		||||
func Convertv2APIGroupDiscoveryListTov2beta1APIGroupDiscoveryList(in *v2.APIGroupDiscoveryList, out *v2beta1.APIGroupDiscoveryList, s conversion.Scope) error {
 | 
			
		||||
	return autoConvertv2APIGroupDiscoveryListTov2beta1APIGroupDiscoveryList(in, out, s)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func autoConvertv2beta1APIResourceDiscoveryTov2APIResourceDiscovery(in *v2beta1.APIResourceDiscovery, out *v2.APIResourceDiscovery, s conversion.Scope) error {
 | 
			
		||||
	out.Resource = in.Resource
 | 
			
		||||
	out.ResponseKind = (*v1.GroupVersionKind)(unsafe.Pointer(in.ResponseKind))
 | 
			
		||||
	out.Scope = v2.ResourceScope(in.Scope)
 | 
			
		||||
	out.SingularResource = in.SingularResource
 | 
			
		||||
	out.Verbs = *(*[]string)(unsafe.Pointer(&in.Verbs))
 | 
			
		||||
	out.ShortNames = *(*[]string)(unsafe.Pointer(&in.ShortNames))
 | 
			
		||||
	out.Categories = *(*[]string)(unsafe.Pointer(&in.Categories))
 | 
			
		||||
	out.Subresources = *(*[]v2.APISubresourceDiscovery)(unsafe.Pointer(&in.Subresources))
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Convertv2beta1APIResourceDiscoveryTov2APIResourceDiscovery is an autogenerated conversion function.
 | 
			
		||||
func Convertv2beta1APIResourceDiscoveryTov2APIResourceDiscovery(in *v2beta1.APIResourceDiscovery, out *v2.APIResourceDiscovery, s conversion.Scope) error {
 | 
			
		||||
	return autoConvertv2beta1APIResourceDiscoveryTov2APIResourceDiscovery(in, out, s)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func autoConvertv2APIResourceDiscoveryTov2beta1APIResourceDiscovery(in *v2.APIResourceDiscovery, out *v2beta1.APIResourceDiscovery, s conversion.Scope) error {
 | 
			
		||||
	out.Resource = in.Resource
 | 
			
		||||
	out.ResponseKind = (*v1.GroupVersionKind)(unsafe.Pointer(in.ResponseKind))
 | 
			
		||||
	out.Scope = v2beta1.ResourceScope(in.Scope)
 | 
			
		||||
	out.SingularResource = in.SingularResource
 | 
			
		||||
	out.Verbs = *(*[]string)(unsafe.Pointer(&in.Verbs))
 | 
			
		||||
	out.ShortNames = *(*[]string)(unsafe.Pointer(&in.ShortNames))
 | 
			
		||||
	out.Categories = *(*[]string)(unsafe.Pointer(&in.Categories))
 | 
			
		||||
	out.Subresources = *(*[]v2beta1.APISubresourceDiscovery)(unsafe.Pointer(&in.Subresources))
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Convertv2APIResourceDiscoveryTov2beta1APIResourceDiscovery is an autogenerated conversion function.
 | 
			
		||||
func Convertv2APIResourceDiscoveryTov2beta1APIResourceDiscovery(in *v2.APIResourceDiscovery, out *v2beta1.APIResourceDiscovery, s conversion.Scope) error {
 | 
			
		||||
	return autoConvertv2APIResourceDiscoveryTov2beta1APIResourceDiscovery(in, out, s)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func autoConvertv2beta1APISubresourceDiscoveryTov2APISubresourceDiscovery(in *v2beta1.APISubresourceDiscovery, out *v2.APISubresourceDiscovery, s conversion.Scope) error {
 | 
			
		||||
	out.Subresource = in.Subresource
 | 
			
		||||
	out.ResponseKind = (*v1.GroupVersionKind)(unsafe.Pointer(in.ResponseKind))
 | 
			
		||||
	out.AcceptedTypes = *(*[]v1.GroupVersionKind)(unsafe.Pointer(&in.AcceptedTypes))
 | 
			
		||||
	out.Verbs = *(*[]string)(unsafe.Pointer(&in.Verbs))
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Convertv2beta1APISubresourceDiscoveryTov2APISubresourceDiscovery is an autogenerated conversion function.
 | 
			
		||||
func Convertv2beta1APISubresourceDiscoveryTov2APISubresourceDiscovery(in *v2beta1.APISubresourceDiscovery, out *v2.APISubresourceDiscovery, s conversion.Scope) error {
 | 
			
		||||
	return autoConvertv2beta1APISubresourceDiscoveryTov2APISubresourceDiscovery(in, out, s)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func autoConvertv2APISubresourceDiscoveryTov2beta1APISubresourceDiscovery(in *v2.APISubresourceDiscovery, out *v2beta1.APISubresourceDiscovery, s conversion.Scope) error {
 | 
			
		||||
	out.Subresource = in.Subresource
 | 
			
		||||
	out.ResponseKind = (*v1.GroupVersionKind)(unsafe.Pointer(in.ResponseKind))
 | 
			
		||||
	out.AcceptedTypes = *(*[]v1.GroupVersionKind)(unsafe.Pointer(&in.AcceptedTypes))
 | 
			
		||||
	out.Verbs = *(*[]string)(unsafe.Pointer(&in.Verbs))
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Convertv2APISubresourceDiscoveryTov2beta1APISubresourceDiscovery is an autogenerated conversion function.
 | 
			
		||||
func Convertv2APISubresourceDiscoveryTov2beta1APISubresourceDiscovery(in *v2.APISubresourceDiscovery, out *v2beta1.APISubresourceDiscovery, s conversion.Scope) error {
 | 
			
		||||
	return autoConvertv2APISubresourceDiscoveryTov2beta1APISubresourceDiscovery(in, out, s)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func autoConvertv2beta1APIVersionDiscoveryTov2APIVersionDiscovery(in *v2beta1.APIVersionDiscovery, out *v2.APIVersionDiscovery, s conversion.Scope) error {
 | 
			
		||||
	out.Version = in.Version
 | 
			
		||||
	out.Resources = *(*[]v2.APIResourceDiscovery)(unsafe.Pointer(&in.Resources))
 | 
			
		||||
	out.Freshness = v2.DiscoveryFreshness(in.Freshness)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Convertv2beta1APIVersionDiscoveryTov2APIVersionDiscovery is an autogenerated conversion function.
 | 
			
		||||
func Convertv2beta1APIVersionDiscoveryTov2APIVersionDiscovery(in *v2beta1.APIVersionDiscovery, out *v2.APIVersionDiscovery, s conversion.Scope) error {
 | 
			
		||||
	return autoConvertv2beta1APIVersionDiscoveryTov2APIVersionDiscovery(in, out, s)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func autoConvertv2APIVersionDiscoveryTov2beta1APIVersionDiscovery(in *v2.APIVersionDiscovery, out *v2beta1.APIVersionDiscovery, s conversion.Scope) error {
 | 
			
		||||
	out.Version = in.Version
 | 
			
		||||
	out.Resources = *(*[]v2beta1.APIResourceDiscovery)(unsafe.Pointer(&in.Resources))
 | 
			
		||||
	out.Freshness = v2beta1.DiscoveryFreshness(in.Freshness)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Convertv2APIVersionDiscoveryTov2beta1APIVersionDiscovery is an autogenerated conversion function.
 | 
			
		||||
func Convertv2APIVersionDiscoveryTov2beta1APIVersionDiscovery(in *v2.APIVersionDiscovery, out *v2beta1.APIVersionDiscovery, s conversion.Scope) error {
 | 
			
		||||
	return autoConvertv2APIVersionDiscoveryTov2beta1APIVersionDiscovery(in, out, s)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										19
									
								
								staging/src/k8s.io/apiserver/pkg/apis/apidiscovery/v2/doc.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								staging/src/k8s.io/apiserver/pkg/apis/apidiscovery/v2/doc.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2024 The Kubernetes Authors.
 | 
			
		||||
 | 
			
		||||
Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
you may not use this file except in compliance with the License.
 | 
			
		||||
You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
See the License for the specific language governing permissions and
 | 
			
		||||
limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
// +groupName=apidiscovery.k8s.io
 | 
			
		||||
 | 
			
		||||
package v2 // import "k8s.io/apiserver/pkg/apis/apidiscovery/v2"
 | 
			
		||||
@@ -0,0 +1,87 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2024 The Kubernetes Authors.
 | 
			
		||||
 | 
			
		||||
Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
you may not use this file except in compliance with the License.
 | 
			
		||||
You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
See the License for the specific language governing permissions and
 | 
			
		||||
limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package v2_test
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	v2 "k8s.io/api/apidiscovery/v2"
 | 
			
		||||
	v2beta1 "k8s.io/api/apidiscovery/v2beta1"
 | 
			
		||||
	v2scheme "k8s.io/apiserver/pkg/apis/apidiscovery/v2"
 | 
			
		||||
	v2beta1scheme "k8s.io/apiserver/pkg/apis/apidiscovery/v2beta1"
 | 
			
		||||
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	runtime "k8s.io/apimachinery/pkg/runtime"
 | 
			
		||||
 | 
			
		||||
	"github.com/google/go-cmp/cmp"
 | 
			
		||||
	fuzz "github.com/google/gofuzz"
 | 
			
		||||
	"github.com/stretchr/testify/require"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestConversionRoundTrip(t *testing.T) {
 | 
			
		||||
	scheme := runtime.NewScheme()
 | 
			
		||||
	err := v2beta1scheme.AddToScheme(scheme)
 | 
			
		||||
	require.NoError(t, err)
 | 
			
		||||
	v2scheme.SchemeBuilder.Register(v2scheme.RegisterConversions)
 | 
			
		||||
	err = v2scheme.AddToScheme(scheme)
 | 
			
		||||
	require.NoError(t, err)
 | 
			
		||||
 | 
			
		||||
	fuzzer := fuzz.NewWithSeed(2374375)
 | 
			
		||||
 | 
			
		||||
	// v2 -> v2beta1 -> v2
 | 
			
		||||
	for i := 0; i < 100; i++ {
 | 
			
		||||
		expected := &v2.APIGroupDiscoveryList{}
 | 
			
		||||
		fuzzer.Fuzz(expected)
 | 
			
		||||
		expected.TypeMeta = metav1.TypeMeta{
 | 
			
		||||
			Kind:       "APIGroupDiscoveryList",
 | 
			
		||||
			APIVersion: "apidiscovery.k8s.io/v2",
 | 
			
		||||
		}
 | 
			
		||||
		o, err := scheme.ConvertToVersion(expected, v2beta1.SchemeGroupVersion)
 | 
			
		||||
		require.NoError(t, err)
 | 
			
		||||
		v2beta1Type := o.(*v2beta1.APIGroupDiscoveryList)
 | 
			
		||||
 | 
			
		||||
		o2, err := scheme.ConvertToVersion(v2beta1Type, v2.SchemeGroupVersion)
 | 
			
		||||
		require.NoError(t, err)
 | 
			
		||||
		actual := o2.(*v2.APIGroupDiscoveryList)
 | 
			
		||||
 | 
			
		||||
		if !reflect.DeepEqual(expected, actual) {
 | 
			
		||||
			t.Error(cmp.Diff(expected, actual))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// v2beta1 -> v2 -> v2beta1
 | 
			
		||||
	for i := 0; i < 100; i++ {
 | 
			
		||||
		expected := &v2beta1.APIGroupDiscoveryList{}
 | 
			
		||||
		fuzzer.Fuzz(expected)
 | 
			
		||||
		expected.TypeMeta = metav1.TypeMeta{
 | 
			
		||||
			Kind:       "APIGroupDiscoveryList",
 | 
			
		||||
			APIVersion: "apidiscovery.k8s.io/v2beta1",
 | 
			
		||||
		}
 | 
			
		||||
		o, err := scheme.ConvertToVersion(expected, v2.SchemeGroupVersion)
 | 
			
		||||
		require.NoError(t, err)
 | 
			
		||||
		v2Type := o.(*v2.APIGroupDiscoveryList)
 | 
			
		||||
 | 
			
		||||
		o2, err := scheme.ConvertToVersion(v2Type, v2beta1.SchemeGroupVersion)
 | 
			
		||||
		require.NoError(t, err)
 | 
			
		||||
		actual := o2.(*v2beta1.APIGroupDiscoveryList)
 | 
			
		||||
 | 
			
		||||
		if !reflect.DeepEqual(expected, actual) {
 | 
			
		||||
			t.Error(cmp.Diff(expected, actual))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,39 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2024 The Kubernetes Authors.
 | 
			
		||||
 | 
			
		||||
Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
you may not use this file except in compliance with the License.
 | 
			
		||||
You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
See the License for the specific language governing permissions and
 | 
			
		||||
limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package v2
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	apidiscoveryv2 "k8s.io/api/apidiscovery/v2"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime/schema"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// GroupName is the group name use in this package
 | 
			
		||||
const GroupName = "apidiscovery.k8s.io"
 | 
			
		||||
 | 
			
		||||
// SchemeGroupVersion is group version used to register these objects
 | 
			
		||||
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v2"}
 | 
			
		||||
 | 
			
		||||
// Resource takes an unqualified resource and returns a Group qualified GroupResource
 | 
			
		||||
func Resource(resource string) schema.GroupResource {
 | 
			
		||||
	return SchemeGroupVersion.WithResource(resource).GroupResource()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	SchemeBuilder = &apidiscoveryv2.SchemeBuilder
 | 
			
		||||
	// AddToScheme adds api to a scheme
 | 
			
		||||
	AddToScheme = SchemeBuilder.AddToScheme
 | 
			
		||||
)
 | 
			
		||||
@@ -0,0 +1,19 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2022 The Kubernetes Authors.
 | 
			
		||||
 | 
			
		||||
Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
you may not use this file except in compliance with the License.
 | 
			
		||||
You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
See the License for the specific language governing permissions and
 | 
			
		||||
limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
// +groupName=apidiscovery.k8s.io
 | 
			
		||||
 | 
			
		||||
package v2beta1 // import "k8s.io/apiserver/pkg/apis/apidiscovery/v2beta1"
 | 
			
		||||
@@ -0,0 +1,39 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2022 The Kubernetes Authors.
 | 
			
		||||
 | 
			
		||||
Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
you may not use this file except in compliance with the License.
 | 
			
		||||
You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
See the License for the specific language governing permissions and
 | 
			
		||||
limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package v2beta1
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	apidiscoveryv2beta1 "k8s.io/api/apidiscovery/v2beta1"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime/schema"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// GroupName is the group name use in this package
 | 
			
		||||
const GroupName = "apidiscovery.k8s.io"
 | 
			
		||||
 | 
			
		||||
// SchemeGroupVersion is group version used to register these objects
 | 
			
		||||
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v2beta1"}
 | 
			
		||||
 | 
			
		||||
// Resource takes an unqualified resource and returns a Group qualified GroupResource
 | 
			
		||||
func Resource(resource string) schema.GroupResource {
 | 
			
		||||
	return SchemeGroupVersion.WithResource(resource).GroupResource()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	localSchemeBuilder = &apidiscoveryv2beta1.SchemeBuilder
 | 
			
		||||
	// AddToScheme adds api to a scheme
 | 
			
		||||
	AddToScheme = localSchemeBuilder.AddToScheme
 | 
			
		||||
)
 | 
			
		||||
@@ -24,6 +24,7 @@ import (
 | 
			
		||||
	"strconv"
 | 
			
		||||
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime/schema"
 | 
			
		||||
	"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -39,6 +40,7 @@ import (
 | 
			
		||||
func ServeHTTPWithETag(
 | 
			
		||||
	object runtime.Object,
 | 
			
		||||
	hash string,
 | 
			
		||||
	targetGV schema.GroupVersion,
 | 
			
		||||
	serializer runtime.NegotiatedSerializer,
 | 
			
		||||
	w http.ResponseWriter,
 | 
			
		||||
	req *http.Request,
 | 
			
		||||
@@ -64,7 +66,7 @@ func ServeHTTPWithETag(
 | 
			
		||||
	responsewriters.WriteObjectNegotiated(
 | 
			
		||||
		serializer,
 | 
			
		||||
		DiscoveryEndpointRestrictions,
 | 
			
		||||
		AggregatedDiscoveryGV,
 | 
			
		||||
		targetGV,
 | 
			
		||||
		w,
 | 
			
		||||
		req,
 | 
			
		||||
		http.StatusOK,
 | 
			
		||||
 
 | 
			
		||||
@@ -26,7 +26,7 @@ import (
 | 
			
		||||
 | 
			
		||||
	"github.com/emicklei/go-restful/v3"
 | 
			
		||||
	"github.com/google/go-cmp/cmp"
 | 
			
		||||
	apidiscoveryv2beta1 "k8s.io/api/apidiscovery/v2beta1"
 | 
			
		||||
	apidiscoveryv2 "k8s.io/api/apidiscovery/v2"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/util/wait"
 | 
			
		||||
)
 | 
			
		||||
@@ -122,7 +122,7 @@ func (f *recorderResourceManager) SetGroupVersionPriority(gv metav1.GroupVersion
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *recorderResourceManager) AddGroupVersion(groupName string, value apidiscoveryv2beta1.APIVersionDiscovery) {
 | 
			
		||||
func (f *recorderResourceManager) AddGroupVersion(groupName string, value apidiscoveryv2.APIVersionDiscovery) {
 | 
			
		||||
	f.lock.Lock()
 | 
			
		||||
	defer f.lock.Unlock()
 | 
			
		||||
 | 
			
		||||
@@ -153,7 +153,7 @@ func (f *recorderResourceManager) RemoveGroupVersion(gv metav1.GroupVersion) {
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
func (f *recorderResourceManager) SetGroups(values []apidiscoveryv2beta1.APIGroupDiscovery) {
 | 
			
		||||
func (f *recorderResourceManager) SetGroups(values []apidiscoveryv2.APIGroupDiscovery) {
 | 
			
		||||
	f.lock.Lock()
 | 
			
		||||
	defer f.lock.Unlock()
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -17,15 +17,22 @@ limitations under the License.
 | 
			
		||||
package aggregated
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"sync"
 | 
			
		||||
 | 
			
		||||
	apidiscoveryv2 "k8s.io/api/apidiscovery/v2"
 | 
			
		||||
	apidiscoveryv2beta1 "k8s.io/api/apidiscovery/v2beta1"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime/schema"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime/serializer"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/version"
 | 
			
		||||
	apidiscoveryv2conversion "k8s.io/apiserver/pkg/apis/apidiscovery/v2"
 | 
			
		||||
 | 
			
		||||
	"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
 | 
			
		||||
 | 
			
		||||
	"k8s.io/apiserver/pkg/endpoints/handlers/negotiation"
 | 
			
		||||
	"k8s.io/apiserver/pkg/endpoints/metrics"
 | 
			
		||||
 | 
			
		||||
	"sync/atomic"
 | 
			
		||||
@@ -51,7 +58,7 @@ type ResourceManager interface {
 | 
			
		||||
	// Adds knowledge of the given groupversion to the discovery document
 | 
			
		||||
	// If it was already being tracked, updates the stored APIVersionDiscovery
 | 
			
		||||
	// Thread-safe
 | 
			
		||||
	AddGroupVersion(groupName string, value apidiscoveryv2beta1.APIVersionDiscovery)
 | 
			
		||||
	AddGroupVersion(groupName string, value apidiscoveryv2.APIVersionDiscovery)
 | 
			
		||||
 | 
			
		||||
	// Sets a priority to be used while sorting a specific group and
 | 
			
		||||
	// group-version. If two versions report different priorities for
 | 
			
		||||
@@ -72,7 +79,7 @@ type ResourceManager interface {
 | 
			
		||||
	// Resets the manager's known list of group-versions and replaces them
 | 
			
		||||
	// with the given groups
 | 
			
		||||
	// Thread-Safe
 | 
			
		||||
	SetGroups([]apidiscoveryv2beta1.APIGroupDiscovery)
 | 
			
		||||
	SetGroups([]apidiscoveryv2.APIGroupDiscovery)
 | 
			
		||||
 | 
			
		||||
	// Returns the same resource manager using a different source
 | 
			
		||||
	// The source is used to decide how to de-duplicate groups.
 | 
			
		||||
@@ -87,7 +94,7 @@ type resourceManager struct {
 | 
			
		||||
	*resourceDiscoveryManager
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (rm resourceManager) AddGroupVersion(groupName string, value apidiscoveryv2beta1.APIVersionDiscovery) {
 | 
			
		||||
func (rm resourceManager) AddGroupVersion(groupName string, value apidiscoveryv2.APIVersionDiscovery) {
 | 
			
		||||
	rm.resourceDiscoveryManager.AddGroupVersion(rm.source, groupName, value)
 | 
			
		||||
}
 | 
			
		||||
func (rm resourceManager) SetGroupVersionPriority(gv metav1.GroupVersion, grouppriority, versionpriority int) {
 | 
			
		||||
@@ -99,7 +106,7 @@ func (rm resourceManager) RemoveGroup(groupName string) {
 | 
			
		||||
func (rm resourceManager) RemoveGroupVersion(gv metav1.GroupVersion) {
 | 
			
		||||
	rm.resourceDiscoveryManager.RemoveGroupVersion(rm.source, gv)
 | 
			
		||||
}
 | 
			
		||||
func (rm resourceManager) SetGroups(groups []apidiscoveryv2beta1.APIGroupDiscovery) {
 | 
			
		||||
func (rm resourceManager) SetGroups(groups []apidiscoveryv2.APIGroupDiscovery) {
 | 
			
		||||
	rm.resourceDiscoveryManager.SetGroups(rm.source, groups)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -133,7 +140,7 @@ type resourceDiscoveryManager struct {
 | 
			
		||||
	// Writes protected by the lock.
 | 
			
		||||
	// List of all apigroups & resources indexed by the resource manager
 | 
			
		||||
	lock              sync.RWMutex
 | 
			
		||||
	apiGroups         map[groupKey]*apidiscoveryv2beta1.APIGroupDiscovery
 | 
			
		||||
	apiGroups         map[groupKey]*apidiscoveryv2.APIGroupDiscovery
 | 
			
		||||
	versionPriorities map[groupVersionKey]priorityInfo
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -144,8 +151,12 @@ type priorityInfo struct {
 | 
			
		||||
 | 
			
		||||
func NewResourceManager(path string) ResourceManager {
 | 
			
		||||
	scheme := runtime.NewScheme()
 | 
			
		||||
	codecs := serializer.NewCodecFactory(scheme)
 | 
			
		||||
	// Register conversion for apidiscovery
 | 
			
		||||
	apidiscoveryv2.SchemeBuilder.Register(apidiscoveryv2conversion.RegisterConversions)
 | 
			
		||||
	utilruntime.Must(apidiscoveryv2.AddToScheme(scheme))
 | 
			
		||||
	utilruntime.Must(apidiscoveryv2beta1.AddToScheme(scheme))
 | 
			
		||||
 | 
			
		||||
	codecs := serializer.NewCodecFactory(scheme)
 | 
			
		||||
	rdm := &resourceDiscoveryManager{
 | 
			
		||||
		serializer:        codecs,
 | 
			
		||||
		versionPriorities: make(map[groupVersionKey]priorityInfo),
 | 
			
		||||
@@ -181,7 +192,7 @@ func (rdm *resourceDiscoveryManager) SetGroupVersionPriority(source Source, gv m
 | 
			
		||||
	rdm.cache.Store(nil)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (rdm *resourceDiscoveryManager) SetGroups(source Source, groups []apidiscoveryv2beta1.APIGroupDiscovery) {
 | 
			
		||||
func (rdm *resourceDiscoveryManager) SetGroups(source Source, groups []apidiscoveryv2.APIGroupDiscovery) {
 | 
			
		||||
	rdm.lock.Lock()
 | 
			
		||||
	defer rdm.lock.Unlock()
 | 
			
		||||
 | 
			
		||||
@@ -221,17 +232,17 @@ func (rdm *resourceDiscoveryManager) SetGroups(source Source, groups []apidiscov
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (rdm *resourceDiscoveryManager) AddGroupVersion(source Source, groupName string, value apidiscoveryv2beta1.APIVersionDiscovery) {
 | 
			
		||||
func (rdm *resourceDiscoveryManager) AddGroupVersion(source Source, groupName string, value apidiscoveryv2.APIVersionDiscovery) {
 | 
			
		||||
	rdm.lock.Lock()
 | 
			
		||||
	defer rdm.lock.Unlock()
 | 
			
		||||
 | 
			
		||||
	rdm.addGroupVersionLocked(source, groupName, value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (rdm *resourceDiscoveryManager) addGroupVersionLocked(source Source, groupName string, value apidiscoveryv2beta1.APIVersionDiscovery) {
 | 
			
		||||
func (rdm *resourceDiscoveryManager) addGroupVersionLocked(source Source, groupName string, value apidiscoveryv2.APIVersionDiscovery) {
 | 
			
		||||
 | 
			
		||||
	if rdm.apiGroups == nil {
 | 
			
		||||
		rdm.apiGroups = make(map[groupKey]*apidiscoveryv2beta1.APIGroupDiscovery)
 | 
			
		||||
		rdm.apiGroups = make(map[groupKey]*apidiscoveryv2.APIGroupDiscovery)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	key := groupKey{
 | 
			
		||||
@@ -264,11 +275,11 @@ func (rdm *resourceDiscoveryManager) addGroupVersionLocked(source Source, groupN
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	} else {
 | 
			
		||||
		group := &apidiscoveryv2beta1.APIGroupDiscovery{
 | 
			
		||||
		group := &apidiscoveryv2.APIGroupDiscovery{
 | 
			
		||||
			ObjectMeta: metav1.ObjectMeta{
 | 
			
		||||
				Name: groupName,
 | 
			
		||||
			},
 | 
			
		||||
			Versions: []apidiscoveryv2beta1.APIVersionDiscovery{value},
 | 
			
		||||
			Versions: []apidiscoveryv2.APIVersionDiscovery{value},
 | 
			
		||||
		}
 | 
			
		||||
		rdm.apiGroups[key] = group
 | 
			
		||||
	}
 | 
			
		||||
@@ -354,12 +365,12 @@ func (rdm *resourceDiscoveryManager) RemoveGroup(source Source, groupName string
 | 
			
		||||
 | 
			
		||||
// Prepares the api group list for serving by converting them from map into
 | 
			
		||||
// list and sorting them according to insertion order
 | 
			
		||||
func (rdm *resourceDiscoveryManager) calculateAPIGroupsLocked() []apidiscoveryv2beta1.APIGroupDiscovery {
 | 
			
		||||
func (rdm *resourceDiscoveryManager) calculateAPIGroupsLocked() []apidiscoveryv2.APIGroupDiscovery {
 | 
			
		||||
	regenerationCounter.Inc()
 | 
			
		||||
	// Re-order the apiGroups by their priority.
 | 
			
		||||
	groups := []apidiscoveryv2beta1.APIGroupDiscovery{}
 | 
			
		||||
	groups := []apidiscoveryv2.APIGroupDiscovery{}
 | 
			
		||||
 | 
			
		||||
	groupsToUse := map[string]apidiscoveryv2beta1.APIGroupDiscovery{}
 | 
			
		||||
	groupsToUse := map[string]apidiscoveryv2.APIGroupDiscovery{}
 | 
			
		||||
	sourcesUsed := map[metav1.GroupVersion]Source{}
 | 
			
		||||
 | 
			
		||||
	for key, group := range rdm.apiGroups {
 | 
			
		||||
@@ -475,7 +486,7 @@ func (rdm *resourceDiscoveryManager) fetchFromCache() *cachedGroupList {
 | 
			
		||||
	if cacheLoad != nil {
 | 
			
		||||
		return cacheLoad
 | 
			
		||||
	}
 | 
			
		||||
	response := apidiscoveryv2beta1.APIGroupDiscoveryList{
 | 
			
		||||
	response := apidiscoveryv2.APIGroupDiscoveryList{
 | 
			
		||||
		Items: rdm.calculateAPIGroupsLocked(),
 | 
			
		||||
	}
 | 
			
		||||
	etag, err := calculateETag(response)
 | 
			
		||||
@@ -492,7 +503,13 @@ func (rdm *resourceDiscoveryManager) fetchFromCache() *cachedGroupList {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type cachedGroupList struct {
 | 
			
		||||
	cachedResponse     apidiscoveryv2beta1.APIGroupDiscoveryList
 | 
			
		||||
	cachedResponse apidiscoveryv2.APIGroupDiscoveryList
 | 
			
		||||
	// etag is calculated based on a SHA hash of only the JSON object.
 | 
			
		||||
	// A response via different Accept encodings (eg: protobuf, json) will
 | 
			
		||||
	// yield the same etag. This is okay because Accept is part of the Vary header.
 | 
			
		||||
	// Per RFC7231 a client must only cache a response etag pair if the header field
 | 
			
		||||
	// matches as indicated by the Vary field. Thus, protobuf and json and other Accept
 | 
			
		||||
	// encodings will not be cached as the same response despite having the same etag.
 | 
			
		||||
	cachedResponseETag string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -505,11 +522,30 @@ func (rdm *resourceDiscoveryManager) serveHTTP(resp http.ResponseWriter, req *ht
 | 
			
		||||
	response := cache.cachedResponse
 | 
			
		||||
	etag := cache.cachedResponseETag
 | 
			
		||||
 | 
			
		||||
	mediaType, _, err := negotiation.NegotiateOutputMediaType(req, rdm.serializer, DiscoveryEndpointRestrictions)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		// Should never happen. wrapper.go will only proxy requests to this
 | 
			
		||||
		// handler if the media type passes DiscoveryEndpointRestrictions
 | 
			
		||||
		utilruntime.HandleError(err)
 | 
			
		||||
		resp.WriteHeader(http.StatusInternalServerError)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	var targetGV schema.GroupVersion
 | 
			
		||||
	if mediaType.Convert == nil ||
 | 
			
		||||
		(mediaType.Convert.GroupVersion() != apidiscoveryv2.SchemeGroupVersion &&
 | 
			
		||||
			mediaType.Convert.GroupVersion() != apidiscoveryv2beta1.SchemeGroupVersion) {
 | 
			
		||||
		utilruntime.HandleError(fmt.Errorf("expected aggregated discovery group version, got group: %s, version %s", mediaType.Convert.Group, mediaType.Convert.Version))
 | 
			
		||||
		resp.WriteHeader(http.StatusInternalServerError)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	targetGV = mediaType.Convert.GroupVersion()
 | 
			
		||||
 | 
			
		||||
	if len(etag) > 0 {
 | 
			
		||||
		// Use proper e-tag headers if one is available
 | 
			
		||||
		ServeHTTPWithETag(
 | 
			
		||||
			&response,
 | 
			
		||||
			etag,
 | 
			
		||||
			targetGV,
 | 
			
		||||
			rdm.serializer,
 | 
			
		||||
			resp,
 | 
			
		||||
			req,
 | 
			
		||||
@@ -520,7 +556,7 @@ func (rdm *resourceDiscoveryManager) serveHTTP(resp http.ResponseWriter, req *ht
 | 
			
		||||
		responsewriters.WriteObjectNegotiated(
 | 
			
		||||
			rdm.serializer,
 | 
			
		||||
			DiscoveryEndpointRestrictions,
 | 
			
		||||
			AggregatedDiscoveryGV,
 | 
			
		||||
			targetGV,
 | 
			
		||||
			resp,
 | 
			
		||||
			req,
 | 
			
		||||
			http.StatusOK,
 | 
			
		||||
 
 | 
			
		||||
@@ -32,11 +32,15 @@ import (
 | 
			
		||||
	fuzz "github.com/google/gofuzz"
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
	"github.com/stretchr/testify/require"
 | 
			
		||||
	apidiscoveryv2 "k8s.io/api/apidiscovery/v2"
 | 
			
		||||
	apidiscoveryv2beta1 "k8s.io/api/apidiscovery/v2beta1"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime/schema"
 | 
			
		||||
	runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer"
 | 
			
		||||
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/version"
 | 
			
		||||
	apidiscoveryv2conversion "k8s.io/apiserver/pkg/apis/apidiscovery/v2"
 | 
			
		||||
	discoveryendpoint "k8s.io/apiserver/pkg/endpoints/discovery/aggregated"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -46,21 +50,23 @@ var codecs = runtimeserializer.NewCodecFactory(scheme)
 | 
			
		||||
const discoveryPath = "/apis"
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	// Add all builtin types to scheme
 | 
			
		||||
	apidiscoveryv2beta1.AddToScheme(scheme)
 | 
			
		||||
	// Register conversion for apidiscovery
 | 
			
		||||
	apidiscoveryv2.SchemeBuilder.Register(apidiscoveryv2conversion.RegisterConversions)
 | 
			
		||||
	utilruntime.Must(apidiscoveryv2.AddToScheme(scheme))
 | 
			
		||||
	utilruntime.Must(apidiscoveryv2beta1.AddToScheme(scheme))
 | 
			
		||||
	codecs = runtimeserializer.NewCodecFactory(scheme)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func fuzzAPIGroups(atLeastNumGroups, maxNumGroups int, seed int64) apidiscoveryv2beta1.APIGroupDiscoveryList {
 | 
			
		||||
func fuzzAPIGroups(atLeastNumGroups, maxNumGroups int, seed int64) apidiscoveryv2.APIGroupDiscoveryList {
 | 
			
		||||
	fuzzer := fuzz.NewWithSeed(seed)
 | 
			
		||||
	fuzzer.NumElements(atLeastNumGroups, maxNumGroups)
 | 
			
		||||
	fuzzer.NilChance(0)
 | 
			
		||||
	fuzzer.Funcs(func(o *apidiscoveryv2beta1.APIGroupDiscovery, c fuzz.Continue) {
 | 
			
		||||
	fuzzer.Funcs(func(o *apidiscoveryv2.APIGroupDiscovery, c fuzz.Continue) {
 | 
			
		||||
		c.FuzzNoCustom(o)
 | 
			
		||||
 | 
			
		||||
		// The ResourceManager will just not serve the group if its versions
 | 
			
		||||
		// list is empty
 | 
			
		||||
		atLeastOne := apidiscoveryv2beta1.APIVersionDiscovery{}
 | 
			
		||||
		atLeastOne := apidiscoveryv2.APIVersionDiscovery{}
 | 
			
		||||
		c.Fuzz(&atLeastOne)
 | 
			
		||||
		o.Versions = append(o.Versions, atLeastOne)
 | 
			
		||||
		sort.Slice(o.Versions[:], func(i, j int) bool {
 | 
			
		||||
@@ -75,28 +81,58 @@ func fuzzAPIGroups(atLeastNumGroups, maxNumGroups int, seed int64) apidiscoveryv
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	var apis []apidiscoveryv2beta1.APIGroupDiscovery
 | 
			
		||||
	var apis []apidiscoveryv2.APIGroupDiscovery
 | 
			
		||||
	fuzzer.Fuzz(&apis)
 | 
			
		||||
	sort.Slice(apis[:], func(i, j int) bool {
 | 
			
		||||
		return apis[i].Name < apis[j].Name
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	return apidiscoveryv2beta1.APIGroupDiscoveryList{
 | 
			
		||||
	return apidiscoveryv2.APIGroupDiscoveryList{
 | 
			
		||||
		TypeMeta: metav1.TypeMeta{
 | 
			
		||||
			Kind:       "APIGroupDiscoveryList",
 | 
			
		||||
			APIVersion: "apidiscovery.k8s.io/v2beta1",
 | 
			
		||||
			APIVersion: "apidiscovery.k8s.io/v2",
 | 
			
		||||
		},
 | 
			
		||||
		Items: apis,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func fetchPath(handler http.Handler, acceptPrefix string, path string, etag string) (*http.Response, []byte, *apidiscoveryv2beta1.APIGroupDiscoveryList) {
 | 
			
		||||
func fetchPathV2Beta1(handler http.Handler, acceptPrefix string, path string, etag string) (*http.Response, []byte, *apidiscoveryv2beta1.APIGroupDiscoveryList) {
 | 
			
		||||
	acceptSuffix := ";g=apidiscovery.k8s.io;v=v2beta1;as=APIGroupDiscoveryList"
 | 
			
		||||
	r, bytes := fetchPathHelper(handler, acceptPrefix+acceptSuffix, path, etag)
 | 
			
		||||
	var decoded *apidiscoveryv2beta1.APIGroupDiscoveryList
 | 
			
		||||
	if len(bytes) > 0 {
 | 
			
		||||
		decoded = &apidiscoveryv2beta1.APIGroupDiscoveryList{}
 | 
			
		||||
		err := runtime.DecodeInto(codecs.UniversalDecoder(), bytes, decoded)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			panic(fmt.Sprintf("failed to decode response: %v", err))
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
	return r, bytes, decoded
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func fetchPath(handler http.Handler, acceptPrefix string, path string, etag string) (*http.Response, []byte, *apidiscoveryv2.APIGroupDiscoveryList) {
 | 
			
		||||
	acceptSuffix := ";g=apidiscovery.k8s.io;v=v2;as=APIGroupDiscoveryList,"
 | 
			
		||||
	acceptSuffixV2Beta1 := ";g=apidiscovery.k8s.io;v=v2beta1;as=APIGroupDiscoveryList,"
 | 
			
		||||
	r, bytes := fetchPathHelper(handler, acceptPrefix+acceptSuffix+","+acceptPrefix+acceptSuffixV2Beta1, path, etag)
 | 
			
		||||
	var decoded *apidiscoveryv2.APIGroupDiscoveryList
 | 
			
		||||
	if len(bytes) > 0 {
 | 
			
		||||
		decoded = &apidiscoveryv2.APIGroupDiscoveryList{}
 | 
			
		||||
		err := runtime.DecodeInto(codecs.UniversalDecoder(), bytes, decoded)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			panic(fmt.Sprintf("failed to decode response: %v", err))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return r, bytes, decoded
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func fetchPathHelper(handler http.Handler, accept string, path string, etag string) (*http.Response, []byte) {
 | 
			
		||||
	// Expect json-formatted apis group list
 | 
			
		||||
	w := httptest.NewRecorder()
 | 
			
		||||
	req := httptest.NewRequest("GET", discoveryPath, nil)
 | 
			
		||||
 | 
			
		||||
	// Ask for JSON response
 | 
			
		||||
	req.Header.Set("Accept", acceptPrefix+";g=apidiscovery.k8s.io;v=v2beta1;as=APIGroupDiscoveryList")
 | 
			
		||||
	req.Header.Set("Accept", accept)
 | 
			
		||||
 | 
			
		||||
	if etag != "" {
 | 
			
		||||
		// Quote provided etag if unquoted
 | 
			
		||||
@@ -110,13 +146,7 @@ func fetchPath(handler http.Handler, acceptPrefix string, path string, etag stri
 | 
			
		||||
	handler.ServeHTTP(w, req)
 | 
			
		||||
 | 
			
		||||
	bytes := w.Body.Bytes()
 | 
			
		||||
	var decoded *apidiscoveryv2beta1.APIGroupDiscoveryList
 | 
			
		||||
	if len(bytes) > 0 {
 | 
			
		||||
		decoded = &apidiscoveryv2beta1.APIGroupDiscoveryList{}
 | 
			
		||||
		runtime.DecodeInto(codecs.UniversalDecoder(), bytes, decoded)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return w.Result(), bytes, decoded
 | 
			
		||||
	return w.Result(), bytes
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Add all builtin APIServices to the manager and check the output
 | 
			
		||||
@@ -132,7 +162,7 @@ func TestBasicResponse(t *testing.T) {
 | 
			
		||||
	require.NoError(t, err, "json marshal should always succeed")
 | 
			
		||||
 | 
			
		||||
	assert.Equal(t, http.StatusOK, response.StatusCode, "response should be 200 OK")
 | 
			
		||||
	assert.Equal(t, "application/json;g=apidiscovery.k8s.io;v=v2beta1;as=APIGroupDiscoveryList", response.Header.Get("Content-Type"), "Content-Type response header should be as requested in Accept header if supported")
 | 
			
		||||
	assert.Equal(t, "application/json;g=apidiscovery.k8s.io;v=v2;as=APIGroupDiscoveryList", response.Header.Get("Content-Type"), "Content-Type response header should be as requested in Accept header if supported")
 | 
			
		||||
	assert.NotEmpty(t, response.Header.Get("ETag"), "E-Tag should be set")
 | 
			
		||||
 | 
			
		||||
	assert.NoError(t, err, "decode should always succeed")
 | 
			
		||||
@@ -149,11 +179,39 @@ func TestBasicResponseProtobuf(t *testing.T) {
 | 
			
		||||
 | 
			
		||||
	response, _, decoded := fetchPath(manager, "application/vnd.kubernetes.protobuf", discoveryPath, "")
 | 
			
		||||
	assert.Equal(t, http.StatusOK, response.StatusCode, "response should be 200 OK")
 | 
			
		||||
	assert.Equal(t, "application/vnd.kubernetes.protobuf;g=apidiscovery.k8s.io;v=v2beta1;as=APIGroupDiscoveryList", response.Header.Get("Content-Type"), "Content-Type response header should be as requested in Accept header if supported")
 | 
			
		||||
	assert.Equal(t, "application/vnd.kubernetes.protobuf;g=apidiscovery.k8s.io;v=v2;as=APIGroupDiscoveryList", response.Header.Get("Content-Type"), "Content-Type response header should be as requested in Accept header if supported")
 | 
			
		||||
	assert.NotEmpty(t, response.Header.Get("ETag"), "E-Tag should be set")
 | 
			
		||||
	assert.EqualValues(t, &apis, decoded, "decoded value should equal input")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// V2Beta1 should still be served
 | 
			
		||||
func TestV2Beta1SkewSupport(t *testing.T) {
 | 
			
		||||
	manager := discoveryendpoint.NewResourceManager("apis")
 | 
			
		||||
 | 
			
		||||
	apis := fuzzAPIGroups(1, 3, 10)
 | 
			
		||||
	manager.SetGroups(apis.Items)
 | 
			
		||||
 | 
			
		||||
	converted, err := scheme.ConvertToVersion(&apis, &schema.GroupVersion{Group: "apidiscovery.k8s.io", Version: "v2beta1"})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	v2beta1apis := converted.(*apidiscoveryv2beta1.APIGroupDiscoveryList)
 | 
			
		||||
 | 
			
		||||
	response, body, decoded := fetchPathV2Beta1(manager, "application/json", discoveryPath, "")
 | 
			
		||||
 | 
			
		||||
	jsonFormatted, err := json.Marshal(v2beta1apis)
 | 
			
		||||
	require.NoError(t, err, "json marshal should always succeed")
 | 
			
		||||
 | 
			
		||||
	assert.Equal(t, http.StatusOK, response.StatusCode, "response should be 200 OK")
 | 
			
		||||
	assert.Equal(t, "application/json;g=apidiscovery.k8s.io;v=v2beta1;as=APIGroupDiscoveryList", response.Header.Get("Content-Type"), "Content-Type response header should be as requested in Accept header if supported")
 | 
			
		||||
	assert.NotEmpty(t, response.Header.Get("ETag"), "E-Tag should be set")
 | 
			
		||||
 | 
			
		||||
	assert.NoError(t, err, "decode should always succeed")
 | 
			
		||||
	assert.EqualValues(t, v2beta1apis, decoded, "decoded value should equal input")
 | 
			
		||||
	assert.Equal(t, string(jsonFormatted)+"\n", string(body), "response should be the api group list")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Test that an etag associated with the service only depends on the apiresources
 | 
			
		||||
// e.g.: Multiple services with the same contents should have the same etag.
 | 
			
		||||
func TestEtagConsistent(t *testing.T) {
 | 
			
		||||
@@ -348,7 +406,7 @@ func TestUpdateService(t *testing.T) {
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Error(err)
 | 
			
		||||
	}
 | 
			
		||||
	var newapis apidiscoveryv2beta1.APIGroupDiscoveryList
 | 
			
		||||
	var newapis apidiscoveryv2.APIGroupDiscoveryList
 | 
			
		||||
	err = json.Unmarshal(b, &newapis)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Error(err)
 | 
			
		||||
@@ -369,7 +427,7 @@ func TestUpdateService(t *testing.T) {
 | 
			
		||||
func TestMultipleSources(t *testing.T) {
 | 
			
		||||
	type pair struct {
 | 
			
		||||
		manager discoveryendpoint.ResourceManager
 | 
			
		||||
		apis    apidiscoveryv2beta1.APIGroupDiscoveryList
 | 
			
		||||
		apis    apidiscoveryv2.APIGroupDiscoveryList
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pairs := []pair{}
 | 
			
		||||
@@ -388,7 +446,7 @@ func TestMultipleSources(t *testing.T) {
 | 
			
		||||
		pairs = append(pairs, pair{manager, apis})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	expectedResult := []apidiscoveryv2beta1.APIGroupDiscovery{}
 | 
			
		||||
	expectedResult := []apidiscoveryv2.APIGroupDiscovery{}
 | 
			
		||||
 | 
			
		||||
	groupCounter := 0
 | 
			
		||||
	for _, p := range pairs {
 | 
			
		||||
@@ -422,7 +480,7 @@ func TestSourcePrecedence(t *testing.T) {
 | 
			
		||||
	apis := fuzzAPIGroups(1, 3, int64(15))
 | 
			
		||||
	for _, g := range apis.Items {
 | 
			
		||||
		for i, v := range g.Versions {
 | 
			
		||||
			v.Freshness = apidiscoveryv2beta1.DiscoveryFreshnessCurrent
 | 
			
		||||
			v.Freshness = apidiscoveryv2.DiscoveryFreshnessCurrent
 | 
			
		||||
			g.Versions[i] = v
 | 
			
		||||
			otherManager.AddGroupVersion(g.Name, v)
 | 
			
		||||
		}
 | 
			
		||||
@@ -434,12 +492,12 @@ func TestSourcePrecedence(t *testing.T) {
 | 
			
		||||
	// Add the first groupversion under default.
 | 
			
		||||
	// No versions should appear in discovery document except this one
 | 
			
		||||
	overrideVersion := initialDocument.Items[0].Versions[0]
 | 
			
		||||
	overrideVersion.Freshness = apidiscoveryv2beta1.DiscoveryFreshnessStale
 | 
			
		||||
	overrideVersion.Freshness = apidiscoveryv2.DiscoveryFreshnessStale
 | 
			
		||||
	defaultManager.AddGroupVersion(initialDocument.Items[0].Name, overrideVersion)
 | 
			
		||||
 | 
			
		||||
	_, _, maskedDocument := fetchPath(defaultManager, "application/json", discoveryPath, "")
 | 
			
		||||
	masked := initialDocument.DeepCopy()
 | 
			
		||||
	masked.Items[0].Versions[0].Freshness = apidiscoveryv2beta1.DiscoveryFreshnessStale
 | 
			
		||||
	masked.Items[0].Versions[0].Freshness = apidiscoveryv2.DiscoveryFreshnessStale
 | 
			
		||||
 | 
			
		||||
	require.Equal(t, masked.Items, maskedDocument.Items)
 | 
			
		||||
 | 
			
		||||
@@ -593,19 +651,19 @@ func TestAbuse(t *testing.T) {
 | 
			
		||||
func TestVersionSortingNoPriority(t *testing.T) {
 | 
			
		||||
	manager := discoveryendpoint.NewResourceManager("apis")
 | 
			
		||||
 | 
			
		||||
	manager.AddGroupVersion("default", apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
	manager.AddGroupVersion("default", apidiscoveryv2.APIVersionDiscovery{
 | 
			
		||||
		Version: "v1alpha1",
 | 
			
		||||
	})
 | 
			
		||||
	manager.AddGroupVersion("default", apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
	manager.AddGroupVersion("default", apidiscoveryv2.APIVersionDiscovery{
 | 
			
		||||
		Version: "v2beta1",
 | 
			
		||||
	})
 | 
			
		||||
	manager.AddGroupVersion("default", apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
	manager.AddGroupVersion("default", apidiscoveryv2.APIVersionDiscovery{
 | 
			
		||||
		Version: "v1",
 | 
			
		||||
	})
 | 
			
		||||
	manager.AddGroupVersion("default", apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
	manager.AddGroupVersion("default", apidiscoveryv2.APIVersionDiscovery{
 | 
			
		||||
		Version: "v1beta1",
 | 
			
		||||
	})
 | 
			
		||||
	manager.AddGroupVersion("default", apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
	manager.AddGroupVersion("default", apidiscoveryv2.APIVersionDiscovery{
 | 
			
		||||
		Version: "v2",
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
@@ -625,11 +683,11 @@ func TestVersionSortingNoPriority(t *testing.T) {
 | 
			
		||||
func TestVersionSortingWithPriority(t *testing.T) {
 | 
			
		||||
	manager := discoveryendpoint.NewResourceManager("apis")
 | 
			
		||||
 | 
			
		||||
	manager.AddGroupVersion("default", apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
	manager.AddGroupVersion("default", apidiscoveryv2.APIVersionDiscovery{
 | 
			
		||||
		Version: "v1",
 | 
			
		||||
	})
 | 
			
		||||
	manager.SetGroupVersionPriority(metav1.GroupVersion{Group: "default", Version: "v1"}, 1000, 100)
 | 
			
		||||
	manager.AddGroupVersion("default", apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
	manager.AddGroupVersion("default", apidiscoveryv2.APIVersionDiscovery{
 | 
			
		||||
		Version: "v1alpha1",
 | 
			
		||||
	})
 | 
			
		||||
	manager.SetGroupVersionPriority(metav1.GroupVersion{Group: "default", Version: "v1alpha1"}, 1000, 200)
 | 
			
		||||
@@ -648,15 +706,15 @@ func TestVersionSortingWithPriority(t *testing.T) {
 | 
			
		||||
func TestGroupVersionSortingConflictingPriority(t *testing.T) {
 | 
			
		||||
	manager := discoveryendpoint.NewResourceManager("apis")
 | 
			
		||||
 | 
			
		||||
	manager.AddGroupVersion("default", apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
	manager.AddGroupVersion("default", apidiscoveryv2.APIVersionDiscovery{
 | 
			
		||||
		Version: "v1",
 | 
			
		||||
	})
 | 
			
		||||
	manager.SetGroupVersionPriority(metav1.GroupVersion{Group: "default", Version: "v1"}, 1000, 100)
 | 
			
		||||
	manager.AddGroupVersion("test", apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
	manager.AddGroupVersion("test", apidiscoveryv2.APIVersionDiscovery{
 | 
			
		||||
		Version: "v1alpha1",
 | 
			
		||||
	})
 | 
			
		||||
	manager.SetGroupVersionPriority(metav1.GroupVersion{Group: "test", Version: "v1alpha1"}, 500, 100)
 | 
			
		||||
	manager.AddGroupVersion("test", apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
	manager.AddGroupVersion("test", apidiscoveryv2.APIVersionDiscovery{
 | 
			
		||||
		Version: "v1alpha2",
 | 
			
		||||
	})
 | 
			
		||||
	manager.SetGroupVersionPriority(metav1.GroupVersion{Group: "test", Version: "v1alpha1"}, 2000, 100)
 | 
			
		||||
@@ -679,17 +737,17 @@ func TestStatelessGroupPriorityMinimum(t *testing.T) {
 | 
			
		||||
	stableGroup := "stable.example.com"
 | 
			
		||||
	experimentalGroup := "experimental.example.com"
 | 
			
		||||
 | 
			
		||||
	manager.AddGroupVersion(stableGroup, apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
	manager.AddGroupVersion(stableGroup, apidiscoveryv2.APIVersionDiscovery{
 | 
			
		||||
		Version: "v1",
 | 
			
		||||
	})
 | 
			
		||||
	manager.SetGroupVersionPriority(metav1.GroupVersion{Group: stableGroup, Version: "v1"}, 1000, 100)
 | 
			
		||||
 | 
			
		||||
	manager.AddGroupVersion(experimentalGroup, apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
	manager.AddGroupVersion(experimentalGroup, apidiscoveryv2.APIVersionDiscovery{
 | 
			
		||||
		Version: "v1",
 | 
			
		||||
	})
 | 
			
		||||
	manager.SetGroupVersionPriority(metav1.GroupVersion{Group: experimentalGroup, Version: "v1"}, 100, 100)
 | 
			
		||||
 | 
			
		||||
	manager.AddGroupVersion(experimentalGroup, apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
	manager.AddGroupVersion(experimentalGroup, apidiscoveryv2.APIVersionDiscovery{
 | 
			
		||||
		Version: "v1alpha1",
 | 
			
		||||
	})
 | 
			
		||||
	manager.SetGroupVersionPriority(metav1.GroupVersion{Group: experimentalGroup, Version: "v1alpha1"}, 10000, 100)
 | 
			
		||||
 
 | 
			
		||||
@@ -20,8 +20,6 @@ import (
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime/schema"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var AggregatedDiscoveryGV = schema.GroupVersion{Group: "apidiscovery.k8s.io", Version: "v2beta1"}
 | 
			
		||||
 | 
			
		||||
// Interface is from "k8s.io/apiserver/pkg/endpoints/handlers/negotiation"
 | 
			
		||||
 | 
			
		||||
// DiscoveryEndpointRestrictions allows requests to /apis to provide a Content Negotiation GVK for aggregated discovery.
 | 
			
		||||
@@ -39,7 +37,7 @@ func (discoveryEndpointRestrictions) AllowsStreamSchema(s string) bool { return
 | 
			
		||||
// IsAggregatedDiscoveryGVK checks if a provided GVK is the GVK for serving aggregated discovery.
 | 
			
		||||
func IsAggregatedDiscoveryGVK(gvk *schema.GroupVersionKind) bool {
 | 
			
		||||
	if gvk != nil {
 | 
			
		||||
		return gvk.Group == "apidiscovery.k8s.io" && gvk.Version == "v2beta1" && gvk.Kind == "APIGroupDiscoveryList"
 | 
			
		||||
		return gvk.Group == "apidiscovery.k8s.io" && (gvk.Version == "v2beta1" || gvk.Version == "v2") && gvk.Kind == "APIGroupDiscoveryList"
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -19,8 +19,10 @@ package aggregated
 | 
			
		||||
import (
 | 
			
		||||
	"net/http"
 | 
			
		||||
 | 
			
		||||
	apidiscoveryv2 "k8s.io/api/apidiscovery/v2"
 | 
			
		||||
	apidiscoveryv2beta1 "k8s.io/api/apidiscovery/v2beta1"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime/serializer"
 | 
			
		||||
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
 | 
			
		||||
 | 
			
		||||
	"github.com/emicklei/go-restful/v3"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime"
 | 
			
		||||
@@ -69,10 +71,11 @@ func (wrapped *WrappedHandler) GenerateWebService(prefix string, returnType inte
 | 
			
		||||
// WrapAggregatedDiscoveryToHandler wraps a handler with an option to
 | 
			
		||||
// emit the aggregated discovery by passing in the aggregated
 | 
			
		||||
// discovery type in content negotiation headers: eg: (Accept:
 | 
			
		||||
// application/json;v=v2beta1;g=apidiscovery.k8s.io;as=APIGroupDiscoveryList)
 | 
			
		||||
// application/json;v=v2;g=apidiscovery.k8s.io;as=APIGroupDiscoveryList)
 | 
			
		||||
func WrapAggregatedDiscoveryToHandler(handler http.Handler, aggHandler http.Handler) *WrappedHandler {
 | 
			
		||||
	scheme := runtime.NewScheme()
 | 
			
		||||
	apidiscoveryv2beta1.AddToScheme(scheme)
 | 
			
		||||
	utilruntime.Must(apidiscoveryv2.AddToScheme(scheme))
 | 
			
		||||
	utilruntime.Must(apidiscoveryv2beta1.AddToScheme(scheme))
 | 
			
		||||
	codecs := serializer.NewCodecFactory(scheme)
 | 
			
		||||
	return &WrappedHandler{codecs, handler, aggHandler}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -32,8 +32,11 @@ import (
 | 
			
		||||
const discoveryPath = "/apis"
 | 
			
		||||
const jsonAccept = "application/json"
 | 
			
		||||
const protobufAccept = "application/vnd.kubernetes.protobuf"
 | 
			
		||||
const aggregatedAcceptSuffix = ";g=apidiscovery.k8s.io;v=v2beta1;as=APIGroupDiscoveryList"
 | 
			
		||||
const aggregatedV2Beta1AcceptSuffix = ";g=apidiscovery.k8s.io;v=v2beta1;as=APIGroupDiscoveryList"
 | 
			
		||||
const aggregatedAcceptSuffix = ";g=apidiscovery.k8s.io;v=v2;as=APIGroupDiscoveryList"
 | 
			
		||||
 | 
			
		||||
const aggregatedV2Beta1JSONAccept = jsonAccept + aggregatedV2Beta1AcceptSuffix
 | 
			
		||||
const aggregatedV2Beta1ProtoAccept = protobufAccept + aggregatedV2Beta1AcceptSuffix
 | 
			
		||||
const aggregatedJSONAccept = jsonAccept + aggregatedAcceptSuffix
 | 
			
		||||
const aggregatedProtoAccept = protobufAccept + aggregatedAcceptSuffix
 | 
			
		||||
 | 
			
		||||
@@ -75,6 +78,12 @@ func TestAggregationEnabled(t *testing.T) {
 | 
			
		||||
			// Empty accept headers are valid and should be handled by the unaggregated handler
 | 
			
		||||
			accept:   "",
 | 
			
		||||
			expected: "unaggregated",
 | 
			
		||||
		}, {
 | 
			
		||||
			accept:   aggregatedV2Beta1JSONAccept,
 | 
			
		||||
			expected: "aggregated",
 | 
			
		||||
		}, {
 | 
			
		||||
			accept:   aggregatedV2Beta1ProtoAccept,
 | 
			
		||||
			expected: "aggregated",
 | 
			
		||||
		}, {
 | 
			
		||||
			accept:   aggregatedJSONAccept,
 | 
			
		||||
			expected: "aggregated",
 | 
			
		||||
@@ -103,54 +112,3 @@ func TestAggregationEnabled(t *testing.T) {
 | 
			
		||||
		assert.Equal(t, tc.expected, body)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestAggregationDisabled(t *testing.T) {
 | 
			
		||||
	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.AggregatedDiscoveryEndpoint, false)()
 | 
			
		||||
 | 
			
		||||
	unaggregated := fakeHTTPHandler{data: "unaggregated"}
 | 
			
		||||
	aggregated := fakeHTTPHandler{data: "aggregated"}
 | 
			
		||||
	wrapped := WrapAggregatedDiscoveryToHandler(unaggregated, aggregated)
 | 
			
		||||
 | 
			
		||||
	testCases := []struct {
 | 
			
		||||
		accept   string
 | 
			
		||||
		expected string
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			// Misconstructed/incorrect accept headers should be passed to the unaggregated handler to return an error
 | 
			
		||||
			accept:   "application/json;foo=bar",
 | 
			
		||||
			expected: "unaggregated",
 | 
			
		||||
		}, {
 | 
			
		||||
			// Empty accept headers are valid and should be handled by the unaggregated handler
 | 
			
		||||
			accept:   "",
 | 
			
		||||
			expected: "unaggregated",
 | 
			
		||||
		}, {
 | 
			
		||||
 | 
			
		||||
			accept:   aggregatedJSONAccept,
 | 
			
		||||
			expected: "unaggregated",
 | 
			
		||||
		}, {
 | 
			
		||||
			accept:   aggregatedProtoAccept,
 | 
			
		||||
			expected: "unaggregated",
 | 
			
		||||
		}, {
 | 
			
		||||
			accept:   jsonAccept,
 | 
			
		||||
			expected: "unaggregated",
 | 
			
		||||
		}, {
 | 
			
		||||
			accept:   protobufAccept,
 | 
			
		||||
			expected: "unaggregated",
 | 
			
		||||
		}, {
 | 
			
		||||
			// Server should return the first accepted type.
 | 
			
		||||
			// If aggregation is disabled, the unaggregated type should be returned.
 | 
			
		||||
			accept:   aggregatedJSONAccept + "," + jsonAccept,
 | 
			
		||||
			expected: "unaggregated",
 | 
			
		||||
		}, {
 | 
			
		||||
			// Server should return the first accepted type.
 | 
			
		||||
			// If aggregation is disabled, the unaggregated type should be returned.
 | 
			
		||||
			accept:   aggregatedProtoAccept + "," + protobufAccept,
 | 
			
		||||
			expected: "unaggregated",
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, tc := range testCases {
 | 
			
		||||
		body := fetchPath(wrapped, discoveryPath, tc.accept)
 | 
			
		||||
		assert.Equal(t, tc.expected, body)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,7 @@ import (
 | 
			
		||||
 | 
			
		||||
	restful "github.com/emicklei/go-restful/v3"
 | 
			
		||||
 | 
			
		||||
	apidiscoveryv2beta1 "k8s.io/api/apidiscovery/v2beta1"
 | 
			
		||||
	apidiscoveryv2 "k8s.io/api/apidiscovery/v2"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime/schema"
 | 
			
		||||
@@ -107,7 +107,7 @@ type APIGroupVersion struct {
 | 
			
		||||
// InstallREST registers the REST handlers (storage, watch, proxy and redirect) into a restful Container.
 | 
			
		||||
// It is expected that the provided path root prefix will serve all operations. Root MUST NOT end
 | 
			
		||||
// in a slash.
 | 
			
		||||
func (g *APIGroupVersion) InstallREST(container *restful.Container) ([]apidiscoveryv2beta1.APIResourceDiscovery, []*storageversion.ResourceInfo, error) {
 | 
			
		||||
func (g *APIGroupVersion) InstallREST(container *restful.Container) ([]apidiscoveryv2.APIResourceDiscovery, []*storageversion.ResourceInfo, error) {
 | 
			
		||||
	prefix := path.Join(g.Root, g.GroupVersion.Group, g.GroupVersion.Version)
 | 
			
		||||
	installer := &APIInstaller{
 | 
			
		||||
		group:             g,
 | 
			
		||||
 
 | 
			
		||||
@@ -28,7 +28,7 @@ import (
 | 
			
		||||
	restful "github.com/emicklei/go-restful/v3"
 | 
			
		||||
	"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
 | 
			
		||||
 | 
			
		||||
	apidiscoveryv2beta1 "k8s.io/api/apidiscovery/v2beta1"
 | 
			
		||||
	apidiscoveryv2 "k8s.io/api/apidiscovery/v2"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/conversion"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime"
 | 
			
		||||
@@ -71,8 +71,8 @@ type action struct {
 | 
			
		||||
	AllNamespaces bool // true iff the action is namespaced but works on aggregate result for all namespaces
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ConvertGroupVersionIntoToDiscovery(list []metav1.APIResource) ([]apidiscoveryv2beta1.APIResourceDiscovery, error) {
 | 
			
		||||
	var apiResourceList []apidiscoveryv2beta1.APIResourceDiscovery
 | 
			
		||||
func ConvertGroupVersionIntoToDiscovery(list []metav1.APIResource) ([]apidiscoveryv2.APIResourceDiscovery, error) {
 | 
			
		||||
	var apiResourceList []apidiscoveryv2.APIResourceDiscovery
 | 
			
		||||
	parentResources := make(map[string]int)
 | 
			
		||||
 | 
			
		||||
	// Loop through all top-level resources
 | 
			
		||||
@@ -82,14 +82,14 @@ func ConvertGroupVersionIntoToDiscovery(list []metav1.APIResource) ([]apidiscove
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		var scope apidiscoveryv2beta1.ResourceScope
 | 
			
		||||
		var scope apidiscoveryv2.ResourceScope
 | 
			
		||||
		if r.Namespaced {
 | 
			
		||||
			scope = apidiscoveryv2beta1.ScopeNamespace
 | 
			
		||||
			scope = apidiscoveryv2.ScopeNamespace
 | 
			
		||||
		} else {
 | 
			
		||||
			scope = apidiscoveryv2beta1.ScopeCluster
 | 
			
		||||
			scope = apidiscoveryv2.ScopeCluster
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		resource := apidiscoveryv2beta1.APIResourceDiscovery{
 | 
			
		||||
		resource := apidiscoveryv2.APIResourceDiscovery{
 | 
			
		||||
			Resource: r.Name,
 | 
			
		||||
			Scope:    scope,
 | 
			
		||||
			ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
@@ -116,17 +116,17 @@ func ConvertGroupVersionIntoToDiscovery(list []metav1.APIResource) ([]apidiscove
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		var scope apidiscoveryv2beta1.ResourceScope
 | 
			
		||||
		var scope apidiscoveryv2.ResourceScope
 | 
			
		||||
		if r.Namespaced {
 | 
			
		||||
			scope = apidiscoveryv2beta1.ScopeNamespace
 | 
			
		||||
			scope = apidiscoveryv2.ScopeNamespace
 | 
			
		||||
		} else {
 | 
			
		||||
			scope = apidiscoveryv2beta1.ScopeCluster
 | 
			
		||||
			scope = apidiscoveryv2.ScopeCluster
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		parentidx, exists := parentResources[split[0]]
 | 
			
		||||
		if !exists {
 | 
			
		||||
			// If a subresource exists without a parent, create a parent
 | 
			
		||||
			apiResourceList = append(apiResourceList, apidiscoveryv2beta1.APIResourceDiscovery{
 | 
			
		||||
			apiResourceList = append(apiResourceList, apidiscoveryv2.APIResourceDiscovery{
 | 
			
		||||
				Resource: split[0],
 | 
			
		||||
				Scope:    scope,
 | 
			
		||||
				// avoid nil panics in v0.26.0-v0.26.3 client-go clients
 | 
			
		||||
@@ -142,7 +142,7 @@ func ConvertGroupVersionIntoToDiscovery(list []metav1.APIResource) ([]apidiscove
 | 
			
		||||
			//
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		subresource := apidiscoveryv2beta1.APISubresourceDiscovery{
 | 
			
		||||
		subresource := apidiscoveryv2.APISubresourceDiscovery{
 | 
			
		||||
			Subresource: split[1],
 | 
			
		||||
			Verbs:       r.Verbs,
 | 
			
		||||
			// avoid nil panics in v0.26.0-v0.26.3 client-go clients
 | 
			
		||||
 
 | 
			
		||||
@@ -20,7 +20,7 @@ import (
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/stretchr/testify/require"
 | 
			
		||||
	apidiscoveryv2beta1 "k8s.io/api/apidiscovery/v2beta1"
 | 
			
		||||
	apidiscoveryv2 "k8s.io/api/apidiscovery/v2"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -106,7 +106,7 @@ func TestConvertAPIResourceToDiscovery(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name                     string
 | 
			
		||||
		resources                []metav1.APIResource
 | 
			
		||||
		wantAPIResourceDiscovery []apidiscoveryv2beta1.APIResourceDiscovery
 | 
			
		||||
		wantAPIResourceDiscovery []apidiscoveryv2.APIResourceDiscovery
 | 
			
		||||
		wantErr                  bool
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
@@ -121,10 +121,10 @@ func TestConvertAPIResourceToDiscovery(t *testing.T) {
 | 
			
		||||
					Verbs:      []string{"create", "delete", "deletecollection", "get", "list", "patch", "update", "watch"},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			wantAPIResourceDiscovery: []apidiscoveryv2beta1.APIResourceDiscovery{
 | 
			
		||||
			wantAPIResourceDiscovery: []apidiscoveryv2.APIResourceDiscovery{
 | 
			
		||||
				{
 | 
			
		||||
					Resource: "pods",
 | 
			
		||||
					Scope:    apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
					Scope:    apidiscoveryv2.ScopeNamespace,
 | 
			
		||||
					ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
						Kind: "Pod",
 | 
			
		||||
					},
 | 
			
		||||
@@ -146,10 +146,10 @@ func TestConvertAPIResourceToDiscovery(t *testing.T) {
 | 
			
		||||
					Verbs:      []string{"create", "delete", "deletecollection", "get", "list", "patch", "update", "watch"},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			wantAPIResourceDiscovery: []apidiscoveryv2beta1.APIResourceDiscovery{
 | 
			
		||||
			wantAPIResourceDiscovery: []apidiscoveryv2.APIResourceDiscovery{
 | 
			
		||||
				{
 | 
			
		||||
					Resource: "cronjobs",
 | 
			
		||||
					Scope:    apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
					Scope:    apidiscoveryv2.ScopeNamespace,
 | 
			
		||||
					ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
						Group:   "batch",
 | 
			
		||||
						Version: "v1",
 | 
			
		||||
@@ -182,10 +182,10 @@ func TestConvertAPIResourceToDiscovery(t *testing.T) {
 | 
			
		||||
					Verbs:      []string{"create", "delete", "deletecollection", "get", "list", "patch", "update", "watch"},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			wantAPIResourceDiscovery: []apidiscoveryv2beta1.APIResourceDiscovery{
 | 
			
		||||
			wantAPIResourceDiscovery: []apidiscoveryv2.APIResourceDiscovery{
 | 
			
		||||
				{
 | 
			
		||||
					Resource: "cronjobs",
 | 
			
		||||
					Scope:    apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
					Scope:    apidiscoveryv2.ScopeNamespace,
 | 
			
		||||
					ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
						Group:   "batch",
 | 
			
		||||
						Version: "v1",
 | 
			
		||||
@@ -193,7 +193,7 @@ func TestConvertAPIResourceToDiscovery(t *testing.T) {
 | 
			
		||||
					},
 | 
			
		||||
					ShortNames: []string{"cj"},
 | 
			
		||||
					Verbs:      []string{"create", "delete", "deletecollection", "get", "list", "patch", "update", "watch"},
 | 
			
		||||
					Subresources: []apidiscoveryv2beta1.APISubresourceDiscovery{{
 | 
			
		||||
					Subresources: []apidiscoveryv2.APISubresourceDiscovery{{
 | 
			
		||||
						Subresource: "status",
 | 
			
		||||
						ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
							Group:   "batch",
 | 
			
		||||
@@ -245,10 +245,10 @@ func TestConvertAPIResourceToDiscovery(t *testing.T) {
 | 
			
		||||
					Verbs:      []string{"create", "delete", "deletecollection", "get", "list", "patch", "update", "watch"},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			wantAPIResourceDiscovery: []apidiscoveryv2beta1.APIResourceDiscovery{
 | 
			
		||||
			wantAPIResourceDiscovery: []apidiscoveryv2.APIResourceDiscovery{
 | 
			
		||||
				{
 | 
			
		||||
					Resource: "cronjobs",
 | 
			
		||||
					Scope:    apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
					Scope:    apidiscoveryv2.ScopeNamespace,
 | 
			
		||||
					ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
						Group:   "batch",
 | 
			
		||||
						Version: "v1",
 | 
			
		||||
@@ -256,7 +256,7 @@ func TestConvertAPIResourceToDiscovery(t *testing.T) {
 | 
			
		||||
					},
 | 
			
		||||
					ShortNames: []string{"cj"},
 | 
			
		||||
					Verbs:      []string{"create", "delete", "deletecollection", "get", "list", "patch", "update", "watch"},
 | 
			
		||||
					Subresources: []apidiscoveryv2beta1.APISubresourceDiscovery{{
 | 
			
		||||
					Subresources: []apidiscoveryv2.APISubresourceDiscovery{{
 | 
			
		||||
						Subresource: "status",
 | 
			
		||||
						ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
							Group:   "batch",
 | 
			
		||||
@@ -267,7 +267,7 @@ func TestConvertAPIResourceToDiscovery(t *testing.T) {
 | 
			
		||||
					}},
 | 
			
		||||
				}, {
 | 
			
		||||
					Resource: "deployments",
 | 
			
		||||
					Scope:    apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
					Scope:    apidiscoveryv2.ScopeNamespace,
 | 
			
		||||
					ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
						Group:   "apps",
 | 
			
		||||
						Version: "v1",
 | 
			
		||||
@@ -275,7 +275,7 @@ func TestConvertAPIResourceToDiscovery(t *testing.T) {
 | 
			
		||||
					},
 | 
			
		||||
					ShortNames: []string{"deploy"},
 | 
			
		||||
					Verbs:      []string{"create", "delete", "deletecollection", "get", "list", "patch", "update", "watch"},
 | 
			
		||||
					Subresources: []apidiscoveryv2beta1.APISubresourceDiscovery{{
 | 
			
		||||
					Subresources: []apidiscoveryv2.APISubresourceDiscovery{{
 | 
			
		||||
						Subresource: "status",
 | 
			
		||||
						ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
							Group:   "apps",
 | 
			
		||||
@@ -298,13 +298,13 @@ func TestConvertAPIResourceToDiscovery(t *testing.T) {
 | 
			
		||||
					Verbs:      []string{"create", "delete", "deletecollection", "get", "list", "patch", "update", "watch"},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			wantAPIResourceDiscovery: []apidiscoveryv2beta1.APIResourceDiscovery{
 | 
			
		||||
			wantAPIResourceDiscovery: []apidiscoveryv2.APIResourceDiscovery{
 | 
			
		||||
				{
 | 
			
		||||
					Resource: "cronjobs",
 | 
			
		||||
					Scope:    apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
					Scope:    apidiscoveryv2.ScopeNamespace,
 | 
			
		||||
					// populated to avoid nil panics
 | 
			
		||||
					ResponseKind: &metav1.GroupVersionKind{},
 | 
			
		||||
					Subresources: []apidiscoveryv2beta1.APISubresourceDiscovery{{
 | 
			
		||||
					Subresources: []apidiscoveryv2.APISubresourceDiscovery{{
 | 
			
		||||
						Subresource: "status",
 | 
			
		||||
						ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
							Group:   "batch",
 | 
			
		||||
@@ -327,13 +327,13 @@ func TestConvertAPIResourceToDiscovery(t *testing.T) {
 | 
			
		||||
					Verbs:      []string{"create", "delete", "deletecollection", "get", "list", "patch", "update", "watch"},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			wantAPIResourceDiscovery: []apidiscoveryv2beta1.APIResourceDiscovery{
 | 
			
		||||
			wantAPIResourceDiscovery: []apidiscoveryv2.APIResourceDiscovery{
 | 
			
		||||
				{
 | 
			
		||||
					Resource: "cronjobs",
 | 
			
		||||
					Scope:    apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
					Scope:    apidiscoveryv2.ScopeNamespace,
 | 
			
		||||
					// populated to avoid nil panics
 | 
			
		||||
					ResponseKind: &metav1.GroupVersionKind{},
 | 
			
		||||
					Subresources: []apidiscoveryv2beta1.APISubresourceDiscovery{{
 | 
			
		||||
					Subresources: []apidiscoveryv2.APISubresourceDiscovery{{
 | 
			
		||||
						Subresource: "status",
 | 
			
		||||
						// populated to avoid nil panics
 | 
			
		||||
						ResponseKind: &metav1.GroupVersionKind{},
 | 
			
		||||
@@ -364,7 +364,7 @@ func TestConvertAPIResourceToDiscovery(t *testing.T) {
 | 
			
		||||
					Verbs:      []string{"create", "delete", "deletecollection", "get", "list", "patch", "update", "watch"},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			wantAPIResourceDiscovery: []apidiscoveryv2beta1.APIResourceDiscovery{},
 | 
			
		||||
			wantAPIResourceDiscovery: []apidiscoveryv2.APIResourceDiscovery{},
 | 
			
		||||
			wantErr:                  true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
@@ -378,10 +378,10 @@ func TestConvertAPIResourceToDiscovery(t *testing.T) {
 | 
			
		||||
					Verbs:      []string{"create", "delete", "deletecollection", "get", "list", "patch", "update", "watch"},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			wantAPIResourceDiscovery: []apidiscoveryv2beta1.APIResourceDiscovery{
 | 
			
		||||
			wantAPIResourceDiscovery: []apidiscoveryv2.APIResourceDiscovery{
 | 
			
		||||
				{
 | 
			
		||||
					Resource: "nodes",
 | 
			
		||||
					Scope:    apidiscoveryv2beta1.ScopeCluster,
 | 
			
		||||
					Scope:    apidiscoveryv2.ScopeCluster,
 | 
			
		||||
					ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
						Kind: "Node",
 | 
			
		||||
					},
 | 
			
		||||
@@ -401,10 +401,10 @@ func TestConvertAPIResourceToDiscovery(t *testing.T) {
 | 
			
		||||
					Verbs:      []string{"create", "delete", "deletecollection", "get", "list", "patch", "update", "watch"},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			wantAPIResourceDiscovery: []apidiscoveryv2beta1.APIResourceDiscovery{
 | 
			
		||||
			wantAPIResourceDiscovery: []apidiscoveryv2.APIResourceDiscovery{
 | 
			
		||||
				{
 | 
			
		||||
					Resource: "nodes",
 | 
			
		||||
					Scope:    apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
					Scope:    apidiscoveryv2.ScopeNamespace,
 | 
			
		||||
					ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
						Kind: "Node",
 | 
			
		||||
					},
 | 
			
		||||
@@ -424,11 +424,11 @@ func TestConvertAPIResourceToDiscovery(t *testing.T) {
 | 
			
		||||
					Verbs:        []string{"create", "delete", "deletecollection", "get", "list", "patch", "update", "watch"},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			wantAPIResourceDiscovery: []apidiscoveryv2beta1.APIResourceDiscovery{
 | 
			
		||||
			wantAPIResourceDiscovery: []apidiscoveryv2.APIResourceDiscovery{
 | 
			
		||||
				{
 | 
			
		||||
					Resource:         "nodes",
 | 
			
		||||
					SingularResource: "node",
 | 
			
		||||
					Scope:            apidiscoveryv2beta1.ScopeCluster,
 | 
			
		||||
					Scope:            apidiscoveryv2.ScopeCluster,
 | 
			
		||||
					ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
						Kind: "Node",
 | 
			
		||||
					},
 | 
			
		||||
 
 | 
			
		||||
@@ -47,6 +47,7 @@ const (
 | 
			
		||||
	// owner: @jefftree @alexzielenski
 | 
			
		||||
	// alpha: v1.26
 | 
			
		||||
	// beta: v1.27
 | 
			
		||||
	// stable: v1.30
 | 
			
		||||
	//
 | 
			
		||||
	// Enables an single HTTP endpoint /discovery/<version> which supports native HTTP
 | 
			
		||||
	// caching with ETags containing all APIResources known to the apiserver.
 | 
			
		||||
@@ -284,7 +285,7 @@ func init() {
 | 
			
		||||
// available throughout Kubernetes binaries.
 | 
			
		||||
var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{
 | 
			
		||||
 | 
			
		||||
	AggregatedDiscoveryEndpoint: {Default: true, PreRelease: featuregate.Beta},
 | 
			
		||||
	AggregatedDiscoveryEndpoint: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.33
 | 
			
		||||
 | 
			
		||||
	AdmissionWebhookMatchConditions: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.33
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -29,7 +29,7 @@ import (
 | 
			
		||||
	systemd "github.com/coreos/go-systemd/v22/daemon"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/time/rate"
 | 
			
		||||
	apidiscoveryv2beta1 "k8s.io/api/apidiscovery/v2beta1"
 | 
			
		||||
	apidiscoveryv2 "k8s.io/api/apidiscovery/v2"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/api/meta"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime"
 | 
			
		||||
@@ -756,8 +756,8 @@ func (s *GenericAPIServer) installAPIResources(apiPrefix string, apiGroupInfo *A
 | 
			
		||||
			if apiPrefix == APIGroupPrefix {
 | 
			
		||||
				s.AggregatedDiscoveryGroupManager.AddGroupVersion(
 | 
			
		||||
					groupVersion.Group,
 | 
			
		||||
					apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
						Freshness: apidiscoveryv2beta1.DiscoveryFreshnessCurrent,
 | 
			
		||||
					apidiscoveryv2.APIVersionDiscovery{
 | 
			
		||||
						Freshness: apidiscoveryv2.DiscoveryFreshnessCurrent,
 | 
			
		||||
						Version:   groupVersion.Version,
 | 
			
		||||
						Resources: discoveryAPIResources,
 | 
			
		||||
					},
 | 
			
		||||
@@ -766,8 +766,8 @@ func (s *GenericAPIServer) installAPIResources(apiPrefix string, apiGroupInfo *A
 | 
			
		||||
				// There is only one group version for legacy resources, priority can be defaulted to 0.
 | 
			
		||||
				s.AggregatedLegacyDiscoveryGroupManager.AddGroupVersion(
 | 
			
		||||
					groupVersion.Group,
 | 
			
		||||
					apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
						Freshness: apidiscoveryv2beta1.DiscoveryFreshnessCurrent,
 | 
			
		||||
					apidiscoveryv2.APIVersionDiscovery{
 | 
			
		||||
						Freshness: apidiscoveryv2.DiscoveryFreshnessCurrent,
 | 
			
		||||
						Version:   groupVersion.Version,
 | 
			
		||||
						Resources: discoveryAPIResources,
 | 
			
		||||
					},
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,8 @@ package discovery
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	apidiscovery "k8s.io/api/apidiscovery/v2beta1"
 | 
			
		||||
	apidiscovery "k8s.io/api/apidiscovery/v2"
 | 
			
		||||
	apidiscoveryv2beta1 "k8s.io/api/apidiscovery/v2beta1"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime/schema"
 | 
			
		||||
)
 | 
			
		||||
@@ -154,3 +155,124 @@ func convertAPISubresource(parent metav1.APIResource, in apidiscovery.APISubreso
 | 
			
		||||
	result.Verbs = in.Verbs
 | 
			
		||||
	return result, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Please note the functions below will be removed in v1.33. They facilitate conversion
 | 
			
		||||
// between the deprecated type apidiscoveryv2beta1.APIGroupDiscoveryList.
 | 
			
		||||
 | 
			
		||||
// SplitGroupsAndResourcesV2Beta1 transforms "aggregated" discovery top-level structure into
 | 
			
		||||
// the previous "unaggregated" discovery groups and resources.
 | 
			
		||||
// Deprecated: Please use SplitGroupsAndResources
 | 
			
		||||
func SplitGroupsAndResourcesV2Beta1(aggregatedGroups apidiscoveryv2beta1.APIGroupDiscoveryList) (
 | 
			
		||||
	*metav1.APIGroupList,
 | 
			
		||||
	map[schema.GroupVersion]*metav1.APIResourceList,
 | 
			
		||||
	map[schema.GroupVersion]error) {
 | 
			
		||||
	// Aggregated group list will contain the entirety of discovery, including
 | 
			
		||||
	// groups, versions, and resources. GroupVersions marked "stale" are failed.
 | 
			
		||||
	groups := []*metav1.APIGroup{}
 | 
			
		||||
	failedGVs := map[schema.GroupVersion]error{}
 | 
			
		||||
	resourcesByGV := map[schema.GroupVersion]*metav1.APIResourceList{}
 | 
			
		||||
	for _, aggGroup := range aggregatedGroups.Items {
 | 
			
		||||
		group, resources, failed := convertAPIGroupv2beta1(aggGroup)
 | 
			
		||||
		groups = append(groups, group)
 | 
			
		||||
		for gv, resourceList := range resources {
 | 
			
		||||
			resourcesByGV[gv] = resourceList
 | 
			
		||||
		}
 | 
			
		||||
		for gv, err := range failed {
 | 
			
		||||
			failedGVs[gv] = err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	// Transform slice of groups to group list before returning.
 | 
			
		||||
	groupList := &metav1.APIGroupList{}
 | 
			
		||||
	groupList.Groups = make([]metav1.APIGroup, 0, len(groups))
 | 
			
		||||
	for _, group := range groups {
 | 
			
		||||
		groupList.Groups = append(groupList.Groups, *group)
 | 
			
		||||
	}
 | 
			
		||||
	return groupList, resourcesByGV, failedGVs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// convertAPIGroupv2beta1 tranforms an "aggregated" APIGroupDiscovery to an "legacy" APIGroup,
 | 
			
		||||
// also returning the map of APIResourceList for resources within GroupVersions.
 | 
			
		||||
func convertAPIGroupv2beta1(g apidiscoveryv2beta1.APIGroupDiscovery) (
 | 
			
		||||
	*metav1.APIGroup,
 | 
			
		||||
	map[schema.GroupVersion]*metav1.APIResourceList,
 | 
			
		||||
	map[schema.GroupVersion]error) {
 | 
			
		||||
	// Iterate through versions to convert to group and resources.
 | 
			
		||||
	group := &metav1.APIGroup{}
 | 
			
		||||
	gvResources := map[schema.GroupVersion]*metav1.APIResourceList{}
 | 
			
		||||
	failedGVs := map[schema.GroupVersion]error{}
 | 
			
		||||
	group.Name = g.ObjectMeta.Name
 | 
			
		||||
	for _, v := range g.Versions {
 | 
			
		||||
		gv := schema.GroupVersion{Group: g.Name, Version: v.Version}
 | 
			
		||||
		if v.Freshness == apidiscoveryv2beta1.DiscoveryFreshnessStale {
 | 
			
		||||
			failedGVs[gv] = StaleGroupVersionError{gv: gv}
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		version := metav1.GroupVersionForDiscovery{}
 | 
			
		||||
		version.GroupVersion = gv.String()
 | 
			
		||||
		version.Version = v.Version
 | 
			
		||||
		group.Versions = append(group.Versions, version)
 | 
			
		||||
		// PreferredVersion is first non-stale Version
 | 
			
		||||
		if group.PreferredVersion == (metav1.GroupVersionForDiscovery{}) {
 | 
			
		||||
			group.PreferredVersion = version
 | 
			
		||||
		}
 | 
			
		||||
		resourceList := &metav1.APIResourceList{}
 | 
			
		||||
		resourceList.GroupVersion = gv.String()
 | 
			
		||||
		for _, r := range v.Resources {
 | 
			
		||||
			resource, err := convertAPIResourcev2beta1(r)
 | 
			
		||||
			if err == nil {
 | 
			
		||||
				resourceList.APIResources = append(resourceList.APIResources, resource)
 | 
			
		||||
			}
 | 
			
		||||
			// Subresources field in new format get transformed into full APIResources.
 | 
			
		||||
			// It is possible a partial result with an error was returned to be used
 | 
			
		||||
			// as the parent resource for the subresource.
 | 
			
		||||
			for _, subresource := range r.Subresources {
 | 
			
		||||
				sr, err := convertAPISubresourcev2beta1(resource, subresource)
 | 
			
		||||
				if err == nil {
 | 
			
		||||
					resourceList.APIResources = append(resourceList.APIResources, sr)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		gvResources[gv] = resourceList
 | 
			
		||||
	}
 | 
			
		||||
	return group, gvResources, failedGVs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// convertAPIResource tranforms a APIResourceDiscovery to an APIResource. We are
 | 
			
		||||
// resilient to missing GVK, since this resource might be the parent resource
 | 
			
		||||
// for a subresource. If the parent is missing a GVK, it is not returned in
 | 
			
		||||
// discovery, and the subresource MUST have the GVK.
 | 
			
		||||
func convertAPIResourcev2beta1(in apidiscoveryv2beta1.APIResourceDiscovery) (metav1.APIResource, error) {
 | 
			
		||||
	result := metav1.APIResource{
 | 
			
		||||
		Name:         in.Resource,
 | 
			
		||||
		SingularName: in.SingularResource,
 | 
			
		||||
		Namespaced:   in.Scope == apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
		Verbs:        in.Verbs,
 | 
			
		||||
		ShortNames:   in.ShortNames,
 | 
			
		||||
		Categories:   in.Categories,
 | 
			
		||||
	}
 | 
			
		||||
	// Can return partial result with error, which can be the parent for a
 | 
			
		||||
	// subresource. Do not add this result to the returned discovery resources.
 | 
			
		||||
	if in.ResponseKind == nil || (*in.ResponseKind) == emptyKind {
 | 
			
		||||
		return result, fmt.Errorf("discovery resource %s missing GVK", in.Resource)
 | 
			
		||||
	}
 | 
			
		||||
	result.Group = in.ResponseKind.Group
 | 
			
		||||
	result.Version = in.ResponseKind.Version
 | 
			
		||||
	result.Kind = in.ResponseKind.Kind
 | 
			
		||||
	return result, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// convertAPISubresource tranforms a APISubresourceDiscovery to an APIResource.
 | 
			
		||||
func convertAPISubresourcev2beta1(parent metav1.APIResource, in apidiscoveryv2beta1.APISubresourceDiscovery) (metav1.APIResource, error) {
 | 
			
		||||
	result := metav1.APIResource{}
 | 
			
		||||
	if in.ResponseKind == nil || (*in.ResponseKind) == emptyKind {
 | 
			
		||||
		return result, fmt.Errorf("subresource %s/%s missing GVK", parent.Name, in.Subresource)
 | 
			
		||||
	}
 | 
			
		||||
	result.Name = fmt.Sprintf("%s/%s", parent.Name, in.Subresource)
 | 
			
		||||
	result.SingularName = parent.SingularName
 | 
			
		||||
	result.Namespaced = parent.Namespaced
 | 
			
		||||
	result.Group = in.ResponseKind.Group
 | 
			
		||||
	result.Version = in.ResponseKind.Version
 | 
			
		||||
	result.Kind = in.ResponseKind.Kind
 | 
			
		||||
	result.Verbs = in.Verbs
 | 
			
		||||
	return result, nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -20,7 +20,8 @@ import (
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
	apidiscovery "k8s.io/api/apidiscovery/v2beta1"
 | 
			
		||||
	apidiscovery "k8s.io/api/apidiscovery/v2"
 | 
			
		||||
	apidiscoveryv2beta1 "k8s.io/api/apidiscovery/v2beta1"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime/schema"
 | 
			
		||||
)
 | 
			
		||||
@@ -961,3 +962,941 @@ func TestSplitGroupsAndResources(t *testing.T) {
 | 
			
		||||
		assert.Equal(t, test.expectedGVResources, resourcesByGV)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Duplicated from test above. Remove after 1.33
 | 
			
		||||
func TestSplitGroupsAndResourcesV2Beta1(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name                string
 | 
			
		||||
		agg                 apidiscoveryv2beta1.APIGroupDiscoveryList
 | 
			
		||||
		expectedGroups      metav1.APIGroupList
 | 
			
		||||
		expectedGVResources map[schema.GroupVersion]*metav1.APIResourceList
 | 
			
		||||
		expectedFailedGVs   map[schema.GroupVersion]error
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "Aggregated discovery: core/v1 group and pod resource",
 | 
			
		||||
			agg: apidiscoveryv2beta1.APIGroupDiscoveryList{
 | 
			
		||||
				Items: []apidiscoveryv2beta1.APIGroupDiscovery{
 | 
			
		||||
					{
 | 
			
		||||
						ObjectMeta: metav1.ObjectMeta{
 | 
			
		||||
							Name: "",
 | 
			
		||||
						},
 | 
			
		||||
						Versions: []apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
							{
 | 
			
		||||
								Version: "v1",
 | 
			
		||||
								Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
 | 
			
		||||
									{
 | 
			
		||||
										Resource: "pods",
 | 
			
		||||
										ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
											Group:   "",
 | 
			
		||||
											Version: "v1",
 | 
			
		||||
											Kind:    "Pod",
 | 
			
		||||
										},
 | 
			
		||||
										Scope: apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expectedGroups: metav1.APIGroupList{
 | 
			
		||||
				Groups: []metav1.APIGroup{
 | 
			
		||||
					{
 | 
			
		||||
						Name: "",
 | 
			
		||||
						Versions: []metav1.GroupVersionForDiscovery{
 | 
			
		||||
							{
 | 
			
		||||
								GroupVersion: "v1",
 | 
			
		||||
								Version:      "v1",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						PreferredVersion: metav1.GroupVersionForDiscovery{
 | 
			
		||||
							GroupVersion: "v1",
 | 
			
		||||
							Version:      "v1",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expectedGVResources: map[schema.GroupVersion]*metav1.APIResourceList{
 | 
			
		||||
				{Group: "", Version: "v1"}: {
 | 
			
		||||
					GroupVersion: "v1",
 | 
			
		||||
					APIResources: []metav1.APIResource{
 | 
			
		||||
						{
 | 
			
		||||
							Name:       "pods",
 | 
			
		||||
							Namespaced: true,
 | 
			
		||||
							Group:      "",
 | 
			
		||||
							Version:    "v1",
 | 
			
		||||
							Kind:       "Pod",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expectedFailedGVs: map[schema.GroupVersion]error{},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Aggregated discovery: 1 group/1 resources at /api, 1 group/2 versions/1 resources at /apis",
 | 
			
		||||
			agg: apidiscoveryv2beta1.APIGroupDiscoveryList{
 | 
			
		||||
				Items: []apidiscoveryv2beta1.APIGroupDiscovery{
 | 
			
		||||
					{
 | 
			
		||||
						ObjectMeta: metav1.ObjectMeta{
 | 
			
		||||
							Name: "apps",
 | 
			
		||||
						},
 | 
			
		||||
						Versions: []apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
							{
 | 
			
		||||
								Version: "v2",
 | 
			
		||||
								Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
 | 
			
		||||
									{
 | 
			
		||||
										Resource: "deployments",
 | 
			
		||||
										ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
											Group:   "apps",
 | 
			
		||||
											Version: "v2",
 | 
			
		||||
											Kind:    "Deployment",
 | 
			
		||||
										},
 | 
			
		||||
										Scope: apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
							{
 | 
			
		||||
								Version: "v1",
 | 
			
		||||
								Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
 | 
			
		||||
									{
 | 
			
		||||
										Resource: "deployments",
 | 
			
		||||
										ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
											Group:   "apps",
 | 
			
		||||
											Version: "v1",
 | 
			
		||||
											Kind:    "Deployment",
 | 
			
		||||
										},
 | 
			
		||||
										Scope: apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expectedGroups: metav1.APIGroupList{
 | 
			
		||||
				Groups: []metav1.APIGroup{
 | 
			
		||||
					{
 | 
			
		||||
						Name: "apps",
 | 
			
		||||
						Versions: []metav1.GroupVersionForDiscovery{
 | 
			
		||||
							{
 | 
			
		||||
								GroupVersion: "apps/v2",
 | 
			
		||||
								Version:      "v2",
 | 
			
		||||
							},
 | 
			
		||||
							{
 | 
			
		||||
								GroupVersion: "apps/v1",
 | 
			
		||||
								Version:      "v1",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						PreferredVersion: metav1.GroupVersionForDiscovery{
 | 
			
		||||
							GroupVersion: "apps/v2",
 | 
			
		||||
							Version:      "v2",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expectedGVResources: map[schema.GroupVersion]*metav1.APIResourceList{
 | 
			
		||||
				{Group: "apps", Version: "v1"}: {
 | 
			
		||||
					GroupVersion: "apps/v1",
 | 
			
		||||
					APIResources: []metav1.APIResource{
 | 
			
		||||
						{
 | 
			
		||||
							Name:       "deployments",
 | 
			
		||||
							Namespaced: true,
 | 
			
		||||
							Group:      "apps",
 | 
			
		||||
							Version:    "v1",
 | 
			
		||||
							Kind:       "Deployment",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				{Group: "apps", Version: "v2"}: {
 | 
			
		||||
					GroupVersion: "apps/v2",
 | 
			
		||||
					APIResources: []metav1.APIResource{
 | 
			
		||||
						{
 | 
			
		||||
							Name:       "deployments",
 | 
			
		||||
							Namespaced: true,
 | 
			
		||||
							Group:      "apps",
 | 
			
		||||
							Version:    "v2",
 | 
			
		||||
							Kind:       "Deployment",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expectedFailedGVs: map[schema.GroupVersion]error{},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Aggregated discovery: 1 group/2 resources at /api, 1 group/2 resources at /apis",
 | 
			
		||||
			agg: apidiscoveryv2beta1.APIGroupDiscoveryList{
 | 
			
		||||
				Items: []apidiscoveryv2beta1.APIGroupDiscovery{
 | 
			
		||||
					{
 | 
			
		||||
						Versions: []apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
							{
 | 
			
		||||
								Version: "v1",
 | 
			
		||||
								Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
 | 
			
		||||
									{
 | 
			
		||||
										Resource: "pods",
 | 
			
		||||
										ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
											Group:   "",
 | 
			
		||||
											Version: "v1",
 | 
			
		||||
											Kind:    "Pod",
 | 
			
		||||
										},
 | 
			
		||||
										Scope: apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
									},
 | 
			
		||||
									{
 | 
			
		||||
										Resource: "services",
 | 
			
		||||
										ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
											Group:   "",
 | 
			
		||||
											Version: "v1",
 | 
			
		||||
											Kind:    "Service",
 | 
			
		||||
										},
 | 
			
		||||
										Scope: apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						ObjectMeta: metav1.ObjectMeta{
 | 
			
		||||
							Name: "apps",
 | 
			
		||||
						},
 | 
			
		||||
						Versions: []apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
							{
 | 
			
		||||
								Version: "v1",
 | 
			
		||||
								Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
 | 
			
		||||
									{
 | 
			
		||||
										Resource: "deployments",
 | 
			
		||||
										ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
											Group:   "apps",
 | 
			
		||||
											Version: "v1",
 | 
			
		||||
											Kind:    "Deployment",
 | 
			
		||||
										},
 | 
			
		||||
										Scope: apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
									},
 | 
			
		||||
									{
 | 
			
		||||
										Resource: "statefulsets",
 | 
			
		||||
										ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
											Group:   "apps",
 | 
			
		||||
											Version: "v1",
 | 
			
		||||
											Kind:    "StatefulSet",
 | 
			
		||||
										},
 | 
			
		||||
										Scope: apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expectedGroups: metav1.APIGroupList{
 | 
			
		||||
				Groups: []metav1.APIGroup{
 | 
			
		||||
					{
 | 
			
		||||
						Name: "",
 | 
			
		||||
						Versions: []metav1.GroupVersionForDiscovery{
 | 
			
		||||
							{
 | 
			
		||||
								GroupVersion: "v1",
 | 
			
		||||
								Version:      "v1",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						PreferredVersion: metav1.GroupVersionForDiscovery{
 | 
			
		||||
							GroupVersion: "v1",
 | 
			
		||||
							Version:      "v1",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						Name: "apps",
 | 
			
		||||
						Versions: []metav1.GroupVersionForDiscovery{
 | 
			
		||||
							{
 | 
			
		||||
								GroupVersion: "apps/v1",
 | 
			
		||||
								Version:      "v1",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						PreferredVersion: metav1.GroupVersionForDiscovery{
 | 
			
		||||
							GroupVersion: "apps/v1",
 | 
			
		||||
							Version:      "v1",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expectedGVResources: map[schema.GroupVersion]*metav1.APIResourceList{
 | 
			
		||||
				{Group: "", Version: "v1"}: {
 | 
			
		||||
					GroupVersion: "v1",
 | 
			
		||||
					APIResources: []metav1.APIResource{
 | 
			
		||||
						{
 | 
			
		||||
							Name:       "pods",
 | 
			
		||||
							Namespaced: true,
 | 
			
		||||
							Group:      "",
 | 
			
		||||
							Version:    "v1",
 | 
			
		||||
							Kind:       "Pod",
 | 
			
		||||
						},
 | 
			
		||||
						{
 | 
			
		||||
							Name:       "services",
 | 
			
		||||
							Namespaced: true,
 | 
			
		||||
							Group:      "",
 | 
			
		||||
							Version:    "v1",
 | 
			
		||||
							Kind:       "Service",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				{Group: "apps", Version: "v1"}: {
 | 
			
		||||
					GroupVersion: "apps/v1",
 | 
			
		||||
					APIResources: []metav1.APIResource{
 | 
			
		||||
						{
 | 
			
		||||
							Name:       "deployments",
 | 
			
		||||
							Namespaced: true,
 | 
			
		||||
							Group:      "apps",
 | 
			
		||||
							Version:    "v1",
 | 
			
		||||
							Kind:       "Deployment",
 | 
			
		||||
						},
 | 
			
		||||
						{
 | 
			
		||||
							Name:       "statefulsets",
 | 
			
		||||
							Namespaced: true,
 | 
			
		||||
							Group:      "apps",
 | 
			
		||||
							Version:    "v1",
 | 
			
		||||
							Kind:       "StatefulSet",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expectedFailedGVs: map[schema.GroupVersion]error{},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Aggregated discovery: multiple groups with cluster-scoped resources",
 | 
			
		||||
			agg: apidiscoveryv2beta1.APIGroupDiscoveryList{
 | 
			
		||||
				Items: []apidiscoveryv2beta1.APIGroupDiscovery{
 | 
			
		||||
					{
 | 
			
		||||
						Versions: []apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
							{
 | 
			
		||||
								Version: "v1",
 | 
			
		||||
								Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
 | 
			
		||||
									{
 | 
			
		||||
										Resource: "pods",
 | 
			
		||||
										ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
											Group:   "",
 | 
			
		||||
											Version: "v1",
 | 
			
		||||
											Kind:    "Pod",
 | 
			
		||||
										},
 | 
			
		||||
										Scope: apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
									},
 | 
			
		||||
									{
 | 
			
		||||
										Resource: "namespaces",
 | 
			
		||||
										ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
											Group:   "",
 | 
			
		||||
											Version: "v1",
 | 
			
		||||
											Kind:    "Namespace",
 | 
			
		||||
										},
 | 
			
		||||
										Scope: apidiscoveryv2beta1.ScopeCluster,
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						ObjectMeta: metav1.ObjectMeta{
 | 
			
		||||
							Name: "rbac.authorization.k8s.io",
 | 
			
		||||
						},
 | 
			
		||||
						Versions: []apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
							{
 | 
			
		||||
								Version: "v1",
 | 
			
		||||
								Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
 | 
			
		||||
									{
 | 
			
		||||
										Resource: "roles",
 | 
			
		||||
										ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
											Group:   "rbac.authorization.k8s.io",
 | 
			
		||||
											Version: "v1",
 | 
			
		||||
											Kind:    "Role",
 | 
			
		||||
										},
 | 
			
		||||
										Scope: apidiscoveryv2beta1.ScopeCluster,
 | 
			
		||||
									},
 | 
			
		||||
									{
 | 
			
		||||
										Resource: "clusterroles",
 | 
			
		||||
										ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
											Group:   "rbac.authorization.k8s.io",
 | 
			
		||||
											Version: "v1",
 | 
			
		||||
											Kind:    "ClusterRole",
 | 
			
		||||
										},
 | 
			
		||||
										Scope: apidiscoveryv2beta1.ScopeCluster,
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expectedGroups: metav1.APIGroupList{
 | 
			
		||||
				Groups: []metav1.APIGroup{
 | 
			
		||||
					{
 | 
			
		||||
						Name: "",
 | 
			
		||||
						Versions: []metav1.GroupVersionForDiscovery{
 | 
			
		||||
							{
 | 
			
		||||
								GroupVersion: "v1",
 | 
			
		||||
								Version:      "v1",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						PreferredVersion: metav1.GroupVersionForDiscovery{
 | 
			
		||||
							GroupVersion: "v1",
 | 
			
		||||
							Version:      "v1",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						Name: "rbac.authorization.k8s.io",
 | 
			
		||||
						Versions: []metav1.GroupVersionForDiscovery{
 | 
			
		||||
							{
 | 
			
		||||
								GroupVersion: "rbac.authorization.k8s.io/v1",
 | 
			
		||||
								Version:      "v1",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						PreferredVersion: metav1.GroupVersionForDiscovery{
 | 
			
		||||
							GroupVersion: "rbac.authorization.k8s.io/v1",
 | 
			
		||||
							Version:      "v1",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expectedGVResources: map[schema.GroupVersion]*metav1.APIResourceList{
 | 
			
		||||
				{Group: "", Version: "v1"}: {
 | 
			
		||||
					GroupVersion: "v1",
 | 
			
		||||
					APIResources: []metav1.APIResource{
 | 
			
		||||
						{
 | 
			
		||||
							Name:       "pods",
 | 
			
		||||
							Namespaced: true,
 | 
			
		||||
							Group:      "",
 | 
			
		||||
							Version:    "v1",
 | 
			
		||||
							Kind:       "Pod",
 | 
			
		||||
						},
 | 
			
		||||
						{
 | 
			
		||||
							Name:       "namespaces",
 | 
			
		||||
							Namespaced: false,
 | 
			
		||||
							Group:      "",
 | 
			
		||||
							Version:    "v1",
 | 
			
		||||
							Kind:       "Namespace",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				{Group: "rbac.authorization.k8s.io", Version: "v1"}: {
 | 
			
		||||
					GroupVersion: "rbac.authorization.k8s.io/v1",
 | 
			
		||||
					APIResources: []metav1.APIResource{
 | 
			
		||||
						{
 | 
			
		||||
							Name:       "roles",
 | 
			
		||||
							Namespaced: false,
 | 
			
		||||
							Group:      "rbac.authorization.k8s.io",
 | 
			
		||||
							Version:    "v1",
 | 
			
		||||
							Kind:       "Role",
 | 
			
		||||
						},
 | 
			
		||||
						{
 | 
			
		||||
							Name:       "clusterroles",
 | 
			
		||||
							Namespaced: false,
 | 
			
		||||
							Group:      "rbac.authorization.k8s.io",
 | 
			
		||||
							Version:    "v1",
 | 
			
		||||
							Kind:       "ClusterRole",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expectedFailedGVs: map[schema.GroupVersion]error{},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Aggregated discovery with single subresource",
 | 
			
		||||
			agg: apidiscoveryv2beta1.APIGroupDiscoveryList{
 | 
			
		||||
				Items: []apidiscoveryv2beta1.APIGroupDiscovery{
 | 
			
		||||
					{
 | 
			
		||||
						ObjectMeta: metav1.ObjectMeta{
 | 
			
		||||
							Name: "apps",
 | 
			
		||||
						},
 | 
			
		||||
						Versions: []apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
							{
 | 
			
		||||
								Version: "v1",
 | 
			
		||||
								Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
 | 
			
		||||
									{
 | 
			
		||||
										Resource: "deployments",
 | 
			
		||||
										ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
											Group:   "apps",
 | 
			
		||||
											Version: "v1",
 | 
			
		||||
											Kind:    "Deployment",
 | 
			
		||||
										},
 | 
			
		||||
										Scope:            apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
										SingularResource: "deployment",
 | 
			
		||||
										ShortNames:       []string{"deploy"},
 | 
			
		||||
										Verbs:            []string{"parentverb1", "parentverb2", "parentverb3", "parentverb4"},
 | 
			
		||||
										Categories:       []string{"all", "testcategory"},
 | 
			
		||||
										Subresources: []apidiscoveryv2beta1.APISubresourceDiscovery{
 | 
			
		||||
											{
 | 
			
		||||
												Subresource: "scale",
 | 
			
		||||
												ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
													Group:   "apps",
 | 
			
		||||
													Version: "v1",
 | 
			
		||||
													Kind:    "Deployment",
 | 
			
		||||
												},
 | 
			
		||||
												Verbs: []string{"get", "patch", "update"},
 | 
			
		||||
											},
 | 
			
		||||
										},
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expectedGroups: metav1.APIGroupList{
 | 
			
		||||
				Groups: []metav1.APIGroup{
 | 
			
		||||
					{
 | 
			
		||||
						Name: "apps",
 | 
			
		||||
						Versions: []metav1.GroupVersionForDiscovery{
 | 
			
		||||
							{
 | 
			
		||||
								GroupVersion: "apps/v1",
 | 
			
		||||
								Version:      "v1",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						PreferredVersion: metav1.GroupVersionForDiscovery{
 | 
			
		||||
							GroupVersion: "apps/v1",
 | 
			
		||||
							Version:      "v1",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expectedGVResources: map[schema.GroupVersion]*metav1.APIResourceList{
 | 
			
		||||
				{Group: "apps", Version: "v1"}: {
 | 
			
		||||
					GroupVersion: "apps/v1",
 | 
			
		||||
					APIResources: []metav1.APIResource{
 | 
			
		||||
						{
 | 
			
		||||
							Name:         "deployments",
 | 
			
		||||
							SingularName: "deployment",
 | 
			
		||||
							Namespaced:   true,
 | 
			
		||||
							Group:        "apps",
 | 
			
		||||
							Version:      "v1",
 | 
			
		||||
							Kind:         "Deployment",
 | 
			
		||||
							Verbs:        []string{"parentverb1", "parentverb2", "parentverb3", "parentverb4"},
 | 
			
		||||
							ShortNames:   []string{"deploy"},
 | 
			
		||||
							Categories:   []string{"all", "testcategory"},
 | 
			
		||||
						},
 | 
			
		||||
						{
 | 
			
		||||
							Name:         "deployments/scale",
 | 
			
		||||
							SingularName: "deployment",
 | 
			
		||||
							Namespaced:   true,
 | 
			
		||||
							Group:        "apps",
 | 
			
		||||
							Version:      "v1",
 | 
			
		||||
							Kind:         "Deployment",
 | 
			
		||||
							Verbs:        []string{"get", "patch", "update"},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expectedFailedGVs: map[schema.GroupVersion]error{},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Aggregated discovery with single subresource and parent missing GVK",
 | 
			
		||||
			agg: apidiscoveryv2beta1.APIGroupDiscoveryList{
 | 
			
		||||
				Items: []apidiscoveryv2beta1.APIGroupDiscovery{
 | 
			
		||||
					{
 | 
			
		||||
						ObjectMeta: metav1.ObjectMeta{
 | 
			
		||||
							Name: "external.metrics.k8s.io",
 | 
			
		||||
						},
 | 
			
		||||
						Versions: []apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
							{
 | 
			
		||||
								Version: "v1beta1",
 | 
			
		||||
								Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
 | 
			
		||||
									{
 | 
			
		||||
										// resilient to nil GVK for parent
 | 
			
		||||
										Resource:         "*",
 | 
			
		||||
										Scope:            apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
										SingularResource: "",
 | 
			
		||||
										Subresources: []apidiscoveryv2beta1.APISubresourceDiscovery{
 | 
			
		||||
											{
 | 
			
		||||
												Subresource: "other-external-metric",
 | 
			
		||||
												ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
													Kind: "MetricValueList",
 | 
			
		||||
												},
 | 
			
		||||
												Verbs: []string{"get"},
 | 
			
		||||
											},
 | 
			
		||||
										},
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expectedGroups: metav1.APIGroupList{
 | 
			
		||||
				Groups: []metav1.APIGroup{
 | 
			
		||||
					{
 | 
			
		||||
						Name: "external.metrics.k8s.io",
 | 
			
		||||
						Versions: []metav1.GroupVersionForDiscovery{
 | 
			
		||||
							{
 | 
			
		||||
								GroupVersion: "external.metrics.k8s.io/v1beta1",
 | 
			
		||||
								Version:      "v1beta1",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						PreferredVersion: metav1.GroupVersionForDiscovery{
 | 
			
		||||
							GroupVersion: "external.metrics.k8s.io/v1beta1",
 | 
			
		||||
							Version:      "v1beta1",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expectedGVResources: map[schema.GroupVersion]*metav1.APIResourceList{
 | 
			
		||||
				{Group: "external.metrics.k8s.io", Version: "v1beta1"}: {
 | 
			
		||||
					GroupVersion: "external.metrics.k8s.io/v1beta1",
 | 
			
		||||
					APIResources: []metav1.APIResource{
 | 
			
		||||
						// Since parent GVK was nil, it is NOT returned--only the subresource.
 | 
			
		||||
						{
 | 
			
		||||
							Name:         "*/other-external-metric",
 | 
			
		||||
							SingularName: "",
 | 
			
		||||
							Namespaced:   true,
 | 
			
		||||
							Group:        "",
 | 
			
		||||
							Version:      "",
 | 
			
		||||
							Kind:         "MetricValueList",
 | 
			
		||||
							Verbs:        []string{"get"},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expectedFailedGVs: map[schema.GroupVersion]error{},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Aggregated discovery with single subresource and parent empty GVK",
 | 
			
		||||
			agg: apidiscoveryv2beta1.APIGroupDiscoveryList{
 | 
			
		||||
				Items: []apidiscoveryv2beta1.APIGroupDiscovery{
 | 
			
		||||
					{
 | 
			
		||||
						ObjectMeta: metav1.ObjectMeta{
 | 
			
		||||
							Name: "external.metrics.k8s.io",
 | 
			
		||||
						},
 | 
			
		||||
						Versions: []apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
							{
 | 
			
		||||
								Version: "v1beta1",
 | 
			
		||||
								Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
 | 
			
		||||
									{
 | 
			
		||||
										// resilient to empty GVK for parent
 | 
			
		||||
										Resource:         "*",
 | 
			
		||||
										Scope:            apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
										SingularResource: "",
 | 
			
		||||
										ResponseKind:     &metav1.GroupVersionKind{},
 | 
			
		||||
										Subresources: []apidiscoveryv2beta1.APISubresourceDiscovery{
 | 
			
		||||
											{
 | 
			
		||||
												Subresource: "other-external-metric",
 | 
			
		||||
												ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
													Kind: "MetricValueList",
 | 
			
		||||
												},
 | 
			
		||||
												Verbs: []string{"get"},
 | 
			
		||||
											},
 | 
			
		||||
										},
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expectedGroups: metav1.APIGroupList{
 | 
			
		||||
				Groups: []metav1.APIGroup{
 | 
			
		||||
					{
 | 
			
		||||
						Name: "external.metrics.k8s.io",
 | 
			
		||||
						Versions: []metav1.GroupVersionForDiscovery{
 | 
			
		||||
							{
 | 
			
		||||
								GroupVersion: "external.metrics.k8s.io/v1beta1",
 | 
			
		||||
								Version:      "v1beta1",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						PreferredVersion: metav1.GroupVersionForDiscovery{
 | 
			
		||||
							GroupVersion: "external.metrics.k8s.io/v1beta1",
 | 
			
		||||
							Version:      "v1beta1",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expectedGVResources: map[schema.GroupVersion]*metav1.APIResourceList{
 | 
			
		||||
				{Group: "external.metrics.k8s.io", Version: "v1beta1"}: {
 | 
			
		||||
					GroupVersion: "external.metrics.k8s.io/v1beta1",
 | 
			
		||||
					APIResources: []metav1.APIResource{
 | 
			
		||||
						// Since parent GVK was nil, it is NOT returned--only the subresource.
 | 
			
		||||
						{
 | 
			
		||||
							Name:         "*/other-external-metric",
 | 
			
		||||
							SingularName: "",
 | 
			
		||||
							Namespaced:   true,
 | 
			
		||||
							Group:        "",
 | 
			
		||||
							Version:      "",
 | 
			
		||||
							Kind:         "MetricValueList",
 | 
			
		||||
							Verbs:        []string{"get"},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expectedFailedGVs: map[schema.GroupVersion]error{},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Aggregated discovery with multiple subresources",
 | 
			
		||||
			agg: apidiscoveryv2beta1.APIGroupDiscoveryList{
 | 
			
		||||
				Items: []apidiscoveryv2beta1.APIGroupDiscovery{
 | 
			
		||||
					{
 | 
			
		||||
						ObjectMeta: metav1.ObjectMeta{
 | 
			
		||||
							Name: "apps",
 | 
			
		||||
						},
 | 
			
		||||
						Versions: []apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
							{
 | 
			
		||||
								Version: "v1",
 | 
			
		||||
								Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
 | 
			
		||||
									{
 | 
			
		||||
										Resource: "deployments",
 | 
			
		||||
										ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
											Group:   "apps",
 | 
			
		||||
											Version: "v1",
 | 
			
		||||
											Kind:    "Deployment",
 | 
			
		||||
										},
 | 
			
		||||
										Scope:            apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
										SingularResource: "deployment",
 | 
			
		||||
										Subresources: []apidiscoveryv2beta1.APISubresourceDiscovery{
 | 
			
		||||
											{
 | 
			
		||||
												Subresource: "scale",
 | 
			
		||||
												ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
													Group:   "apps",
 | 
			
		||||
													Version: "v1",
 | 
			
		||||
													Kind:    "Deployment",
 | 
			
		||||
												},
 | 
			
		||||
												Verbs: []string{"get", "patch", "update"},
 | 
			
		||||
											},
 | 
			
		||||
											{
 | 
			
		||||
												Subresource: "status",
 | 
			
		||||
												ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
													Group:   "apps",
 | 
			
		||||
													Version: "v1",
 | 
			
		||||
													Kind:    "Deployment",
 | 
			
		||||
												},
 | 
			
		||||
												Verbs: []string{"get", "patch", "update"},
 | 
			
		||||
											},
 | 
			
		||||
										},
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expectedGroups: metav1.APIGroupList{
 | 
			
		||||
				Groups: []metav1.APIGroup{
 | 
			
		||||
					{
 | 
			
		||||
						Name: "apps",
 | 
			
		||||
						Versions: []metav1.GroupVersionForDiscovery{
 | 
			
		||||
							{
 | 
			
		||||
								GroupVersion: "apps/v1",
 | 
			
		||||
								Version:      "v1",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						PreferredVersion: metav1.GroupVersionForDiscovery{
 | 
			
		||||
							GroupVersion: "apps/v1",
 | 
			
		||||
							Version:      "v1",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expectedGVResources: map[schema.GroupVersion]*metav1.APIResourceList{
 | 
			
		||||
				{Group: "apps", Version: "v1"}: {
 | 
			
		||||
					GroupVersion: "apps/v1",
 | 
			
		||||
					APIResources: []metav1.APIResource{
 | 
			
		||||
						{
 | 
			
		||||
							Name:         "deployments",
 | 
			
		||||
							SingularName: "deployment",
 | 
			
		||||
							Namespaced:   true,
 | 
			
		||||
							Group:        "apps",
 | 
			
		||||
							Version:      "v1",
 | 
			
		||||
							Kind:         "Deployment",
 | 
			
		||||
						},
 | 
			
		||||
						{
 | 
			
		||||
							Name:         "deployments/scale",
 | 
			
		||||
							SingularName: "deployment",
 | 
			
		||||
							Namespaced:   true,
 | 
			
		||||
							Group:        "apps",
 | 
			
		||||
							Version:      "v1",
 | 
			
		||||
							Kind:         "Deployment",
 | 
			
		||||
							Verbs:        []string{"get", "patch", "update"},
 | 
			
		||||
						},
 | 
			
		||||
						{
 | 
			
		||||
							Name:         "deployments/status",
 | 
			
		||||
							SingularName: "deployment",
 | 
			
		||||
							Namespaced:   true,
 | 
			
		||||
							Group:        "apps",
 | 
			
		||||
							Version:      "v1",
 | 
			
		||||
							Kind:         "Deployment",
 | 
			
		||||
							Verbs:        []string{"get", "patch", "update"},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expectedFailedGVs: map[schema.GroupVersion]error{},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Aggregated discovery: single failed GV at /api",
 | 
			
		||||
			agg: apidiscoveryv2beta1.APIGroupDiscoveryList{
 | 
			
		||||
				Items: []apidiscoveryv2beta1.APIGroupDiscovery{
 | 
			
		||||
					{
 | 
			
		||||
						Versions: []apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
							{
 | 
			
		||||
								Version: "v1",
 | 
			
		||||
								Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
 | 
			
		||||
									{
 | 
			
		||||
										Resource: "pods",
 | 
			
		||||
										ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
											Group:   "",
 | 
			
		||||
											Version: "v1",
 | 
			
		||||
											Kind:    "Pod",
 | 
			
		||||
										},
 | 
			
		||||
										Scope: apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
									},
 | 
			
		||||
									{
 | 
			
		||||
										Resource: "services",
 | 
			
		||||
										ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
											Group:   "",
 | 
			
		||||
											Version: "v1",
 | 
			
		||||
											Kind:    "Service",
 | 
			
		||||
										},
 | 
			
		||||
										Scope: apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
								Freshness: apidiscoveryv2beta1.DiscoveryFreshnessStale,
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			// Single core Group/Version is stale, so no Version within Group.
 | 
			
		||||
			expectedGroups: metav1.APIGroupList{
 | 
			
		||||
				Groups: []metav1.APIGroup{{Name: ""}},
 | 
			
		||||
			},
 | 
			
		||||
			// Single core Group/Version is stale, so there are no expected resources.
 | 
			
		||||
			expectedGVResources: map[schema.GroupVersion]*metav1.APIResourceList{},
 | 
			
		||||
			expectedFailedGVs: map[schema.GroupVersion]error{
 | 
			
		||||
				{Group: "", Version: "v1"}: StaleGroupVersionError{gv: schema.GroupVersion{Group: "", Version: "v1"}},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Aggregated discovery: single failed GV at /apis",
 | 
			
		||||
			agg: apidiscoveryv2beta1.APIGroupDiscoveryList{
 | 
			
		||||
				Items: []apidiscoveryv2beta1.APIGroupDiscovery{
 | 
			
		||||
					{
 | 
			
		||||
						ObjectMeta: metav1.ObjectMeta{
 | 
			
		||||
							Name: "apps",
 | 
			
		||||
						},
 | 
			
		||||
						Versions: []apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
							{
 | 
			
		||||
								Version: "v1",
 | 
			
		||||
								Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
 | 
			
		||||
									{
 | 
			
		||||
										Resource: "deployments",
 | 
			
		||||
										ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
											Group:   "apps",
 | 
			
		||||
											Version: "v1",
 | 
			
		||||
											Kind:    "Deployment",
 | 
			
		||||
										},
 | 
			
		||||
										Scope: apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
									},
 | 
			
		||||
									{
 | 
			
		||||
										Resource: "statefulsets",
 | 
			
		||||
										ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
											Group:   "apps",
 | 
			
		||||
											Version: "v1",
 | 
			
		||||
											Kind:    "StatefulSets",
 | 
			
		||||
										},
 | 
			
		||||
										Scope: apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
								Freshness: apidiscoveryv2beta1.DiscoveryFreshnessStale,
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			// Single apps/v1 Group/Version is stale, so no Version within Group.
 | 
			
		||||
			expectedGroups: metav1.APIGroupList{
 | 
			
		||||
				Groups: []metav1.APIGroup{{Name: "apps"}},
 | 
			
		||||
			},
 | 
			
		||||
			// Single apps/v1 Group/Version is stale, so there are no expected resources.
 | 
			
		||||
			expectedGVResources: map[schema.GroupVersion]*metav1.APIResourceList{},
 | 
			
		||||
			expectedFailedGVs: map[schema.GroupVersion]error{
 | 
			
		||||
				{Group: "apps", Version: "v1"}: StaleGroupVersionError{gv: schema.GroupVersion{Group: "apps", Version: "v1"}},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Aggregated discovery: 1 group/2 versions/1 failed GV at /apis",
 | 
			
		||||
			agg: apidiscoveryv2beta1.APIGroupDiscoveryList{
 | 
			
		||||
				Items: []apidiscoveryv2beta1.APIGroupDiscovery{
 | 
			
		||||
					{
 | 
			
		||||
						ObjectMeta: metav1.ObjectMeta{
 | 
			
		||||
							Name: "apps",
 | 
			
		||||
						},
 | 
			
		||||
						Versions: []apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
							// Stale v2 should report failed GV.
 | 
			
		||||
							{
 | 
			
		||||
								Version: "v2",
 | 
			
		||||
								Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
 | 
			
		||||
									{
 | 
			
		||||
										Resource: "daemonsets",
 | 
			
		||||
										ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
											Group:   "apps",
 | 
			
		||||
											Version: "v2",
 | 
			
		||||
											Kind:    "DaemonSets",
 | 
			
		||||
										},
 | 
			
		||||
										Scope: apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
								Freshness: apidiscoveryv2beta1.DiscoveryFreshnessStale,
 | 
			
		||||
							},
 | 
			
		||||
							{
 | 
			
		||||
								Version: "v1",
 | 
			
		||||
								Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
 | 
			
		||||
									{
 | 
			
		||||
										Resource: "deployments",
 | 
			
		||||
										ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
											Group:   "apps",
 | 
			
		||||
											Version: "v1",
 | 
			
		||||
											Kind:    "Deployment",
 | 
			
		||||
										},
 | 
			
		||||
										Scope: apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			// Only apps/v1 is non-stale expected Group/Version
 | 
			
		||||
			expectedGroups: metav1.APIGroupList{
 | 
			
		||||
				Groups: []metav1.APIGroup{
 | 
			
		||||
					{
 | 
			
		||||
						Name: "apps",
 | 
			
		||||
						Versions: []metav1.GroupVersionForDiscovery{
 | 
			
		||||
							{
 | 
			
		||||
								GroupVersion: "apps/v1",
 | 
			
		||||
								Version:      "v1",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						// PreferredVersion must be apps/v1
 | 
			
		||||
						PreferredVersion: metav1.GroupVersionForDiscovery{
 | 
			
		||||
							GroupVersion: "apps/v1",
 | 
			
		||||
							Version:      "v1",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			// Only apps/v1 resources expected.
 | 
			
		||||
			expectedGVResources: map[schema.GroupVersion]*metav1.APIResourceList{
 | 
			
		||||
				{Group: "apps", Version: "v1"}: {
 | 
			
		||||
					GroupVersion: "apps/v1",
 | 
			
		||||
					APIResources: []metav1.APIResource{
 | 
			
		||||
						{
 | 
			
		||||
							Name:       "deployments",
 | 
			
		||||
							Namespaced: true,
 | 
			
		||||
							Group:      "apps",
 | 
			
		||||
							Version:    "v1",
 | 
			
		||||
							Kind:       "Deployment",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expectedFailedGVs: map[schema.GroupVersion]error{
 | 
			
		||||
				{Group: "apps", Version: "v2"}: StaleGroupVersionError{gv: schema.GroupVersion{Group: "apps", Version: "v2"}},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, test := range tests {
 | 
			
		||||
		apiGroups, resourcesByGV, failedGVs := SplitGroupsAndResourcesV2Beta1(test.agg)
 | 
			
		||||
		assert.Equal(t, test.expectedFailedGVs, failedGVs)
 | 
			
		||||
		assert.Equal(t, test.expectedGroups, *apiGroups)
 | 
			
		||||
		assert.Equal(t, test.expectedGVResources, resourcesByGV)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,7 @@ import (
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
	"github.com/stretchr/testify/require"
 | 
			
		||||
 | 
			
		||||
	apidiscovery "k8s.io/api/apidiscovery/v2beta1"
 | 
			
		||||
	apidiscovery "k8s.io/api/apidiscovery/v2"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/api/errors"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime"
 | 
			
		||||
@@ -643,7 +643,7 @@ func TestCachedDiscoveryClientAggregatedServerGroups(t *testing.T) {
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			// Content-type is "aggregated" discovery format.
 | 
			
		||||
			w.Header().Set("Content-Type", discovery.AcceptV2Beta1)
 | 
			
		||||
			w.Header().Set("Content-Type", discovery.AcceptV2)
 | 
			
		||||
			w.WriteHeader(http.StatusOK)
 | 
			
		||||
			w.Write(output)
 | 
			
		||||
		}))
 | 
			
		||||
 
 | 
			
		||||
@@ -28,7 +28,7 @@ import (
 | 
			
		||||
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
	"github.com/stretchr/testify/require"
 | 
			
		||||
	apidiscovery "k8s.io/api/apidiscovery/v2beta1"
 | 
			
		||||
	apidiscovery "k8s.io/api/apidiscovery/v2"
 | 
			
		||||
	errorsutil "k8s.io/apimachinery/pkg/api/errors"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime"
 | 
			
		||||
@@ -1118,7 +1118,7 @@ func TestAggregatedMemCacheGroupsAndMaybeResources(t *testing.T) {
 | 
			
		||||
			output, err := json.Marshal(agg)
 | 
			
		||||
			require.NoError(t, err)
 | 
			
		||||
			// Content-type is "aggregated" discovery format.
 | 
			
		||||
			w.Header().Set("Content-Type", discovery.AcceptV2Beta1)
 | 
			
		||||
			w.Header().Set("Content-Type", discovery.AcceptV2)
 | 
			
		||||
			w.WriteHeader(http.StatusOK)
 | 
			
		||||
			w.Write(output)
 | 
			
		||||
		}))
 | 
			
		||||
@@ -1161,6 +1161,7 @@ func TestAggregatedMemCacheGroupsAndMaybeResources(t *testing.T) {
 | 
			
		||||
		memClient.Invalidate()
 | 
			
		||||
		assert.False(t, memClient.Fresh())
 | 
			
		||||
		apiGroupList, _, _, err = memClient.GroupsAndMaybeResources()
 | 
			
		||||
 | 
			
		||||
		require.NoError(t, err)
 | 
			
		||||
		// Test the expected groups are returned for the aggregated format.
 | 
			
		||||
		actualGroupNames = sets.NewString(groupNamesFromList(apiGroupList)...)
 | 
			
		||||
 
 | 
			
		||||
@@ -33,7 +33,8 @@ import (
 | 
			
		||||
	"github.com/golang/protobuf/proto"
 | 
			
		||||
	openapi_v2 "github.com/google/gnostic-models/openapiv2"
 | 
			
		||||
 | 
			
		||||
	apidiscovery "k8s.io/api/apidiscovery/v2beta1"
 | 
			
		||||
	apidiscoveryv2 "k8s.io/api/apidiscovery/v2"
 | 
			
		||||
	apidiscoveryv2beta1 "k8s.io/api/apidiscovery/v2beta1"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/api/errors"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime"
 | 
			
		||||
@@ -64,12 +65,14 @@ const (
 | 
			
		||||
	// MUST be ordered (g, v, as) for server in "Accept" header (BUT we are resilient
 | 
			
		||||
	// to ordering when comparing returned values in "Content-Type" header).
 | 
			
		||||
	AcceptV2Beta1 = runtime.ContentTypeJSON + ";" + "g=apidiscovery.k8s.io;v=v2beta1;as=APIGroupDiscoveryList"
 | 
			
		||||
	AcceptV2      = runtime.ContentTypeJSON + ";" + "g=apidiscovery.k8s.io;v=v2;as=APIGroupDiscoveryList"
 | 
			
		||||
	// Prioritize aggregated discovery by placing first in the order of discovery accept types.
 | 
			
		||||
	acceptDiscoveryFormats = AcceptV2Beta1 + "," + AcceptV1
 | 
			
		||||
	acceptDiscoveryFormats = AcceptV2 + "," + AcceptV2Beta1 + "," + AcceptV1
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Aggregated discovery content-type GVK.
 | 
			
		||||
var v2Beta1GVK = schema.GroupVersionKind{Group: "apidiscovery.k8s.io", Version: "v2beta1", Kind: "APIGroupDiscoveryList"}
 | 
			
		||||
var v2GVK = schema.GroupVersionKind{Group: "apidiscovery.k8s.io", Version: "v2", Kind: "APIGroupDiscoveryList"}
 | 
			
		||||
 | 
			
		||||
// DiscoveryInterface holds the methods that discover server-supported API groups,
 | 
			
		||||
// versions and resources.
 | 
			
		||||
@@ -265,13 +268,20 @@ func (d *DiscoveryClient) downloadLegacy() (
 | 
			
		||||
 | 
			
		||||
	var resourcesByGV map[schema.GroupVersion]*metav1.APIResourceList
 | 
			
		||||
	// Based on the content-type server responded with: aggregated or unaggregated.
 | 
			
		||||
	if isGVK, _ := ContentTypeIsGVK(responseContentType, v2Beta1GVK); isGVK {
 | 
			
		||||
		var aggregatedDiscovery apidiscovery.APIGroupDiscoveryList
 | 
			
		||||
	if isGVK, _ := ContentTypeIsGVK(responseContentType, v2GVK); isGVK {
 | 
			
		||||
		var aggregatedDiscovery apidiscoveryv2.APIGroupDiscoveryList
 | 
			
		||||
		err = json.Unmarshal(body, &aggregatedDiscovery)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, nil, nil, err
 | 
			
		||||
		}
 | 
			
		||||
		apiGroupList, resourcesByGV, failedGVs = SplitGroupsAndResources(aggregatedDiscovery)
 | 
			
		||||
	} else if isGVK, _ := ContentTypeIsGVK(responseContentType, v2Beta1GVK); isGVK {
 | 
			
		||||
		var aggregatedDiscovery apidiscoveryv2beta1.APIGroupDiscoveryList
 | 
			
		||||
		err = json.Unmarshal(body, &aggregatedDiscovery)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, nil, nil, err
 | 
			
		||||
		}
 | 
			
		||||
		apiGroupList, resourcesByGV, failedGVs = SplitGroupsAndResourcesV2Beta1(aggregatedDiscovery)
 | 
			
		||||
	} else {
 | 
			
		||||
		// Default is unaggregated discovery v1.
 | 
			
		||||
		var v metav1.APIVersions
 | 
			
		||||
@@ -317,13 +327,20 @@ func (d *DiscoveryClient) downloadAPIs() (
 | 
			
		||||
	failedGVs := map[schema.GroupVersion]error{}
 | 
			
		||||
	var resourcesByGV map[schema.GroupVersion]*metav1.APIResourceList
 | 
			
		||||
	// Based on the content-type server responded with: aggregated or unaggregated.
 | 
			
		||||
	if isGVK, _ := ContentTypeIsGVK(responseContentType, v2Beta1GVK); isGVK {
 | 
			
		||||
		var aggregatedDiscovery apidiscovery.APIGroupDiscoveryList
 | 
			
		||||
	if isGVK, _ := ContentTypeIsGVK(responseContentType, v2GVK); isGVK {
 | 
			
		||||
		var aggregatedDiscovery apidiscoveryv2.APIGroupDiscoveryList
 | 
			
		||||
		err = json.Unmarshal(body, &aggregatedDiscovery)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, nil, nil, err
 | 
			
		||||
		}
 | 
			
		||||
		apiGroupList, resourcesByGV, failedGVs = SplitGroupsAndResources(aggregatedDiscovery)
 | 
			
		||||
	} else if isGVK, _ := ContentTypeIsGVK(responseContentType, v2Beta1GVK); isGVK {
 | 
			
		||||
		var aggregatedDiscovery apidiscoveryv2beta1.APIGroupDiscoveryList
 | 
			
		||||
		err = json.Unmarshal(body, &aggregatedDiscovery)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, nil, nil, err
 | 
			
		||||
		}
 | 
			
		||||
		apiGroupList, resourcesByGV, failedGVs = SplitGroupsAndResourcesV2Beta1(aggregatedDiscovery)
 | 
			
		||||
	} else {
 | 
			
		||||
		// Default is unaggregated discovery v1.
 | 
			
		||||
		err = json.Unmarshal(body, apiGroupList)
 | 
			
		||||
 
 | 
			
		||||
@@ -32,7 +32,8 @@ import (
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
	"github.com/stretchr/testify/require"
 | 
			
		||||
	golangproto "google.golang.org/protobuf/proto"
 | 
			
		||||
	apidiscovery "k8s.io/api/apidiscovery/v2beta1"
 | 
			
		||||
	apidiscovery "k8s.io/api/apidiscovery/v2"
 | 
			
		||||
	apidiscoveryv2beta1 "k8s.io/api/apidiscovery/v2beta1"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime/schema"
 | 
			
		||||
@@ -58,7 +59,8 @@ func TestGetServerVersion(t *testing.T) {
 | 
			
		||||
		}
 | 
			
		||||
		w.Header().Set("Content-Type", "application/json")
 | 
			
		||||
		w.WriteHeader(http.StatusOK)
 | 
			
		||||
		w.Write(output)
 | 
			
		||||
		_, err = w.Write(output)
 | 
			
		||||
		require.NoError(t, err)
 | 
			
		||||
	}))
 | 
			
		||||
	defer server.Close()
 | 
			
		||||
	client := NewDiscoveryClientForConfigOrDie(&restclient.Config{Host: server.URL})
 | 
			
		||||
@@ -104,7 +106,8 @@ func TestGetServerGroupsWithV1Server(t *testing.T) {
 | 
			
		||||
		}
 | 
			
		||||
		w.Header().Set("Content-Type", "application/json")
 | 
			
		||||
		w.WriteHeader(http.StatusOK)
 | 
			
		||||
		w.Write(output)
 | 
			
		||||
		_, err = w.Write(output)
 | 
			
		||||
		require.NoError(t, err)
 | 
			
		||||
	}))
 | 
			
		||||
	defer server.Close()
 | 
			
		||||
	client := NewDiscoveryClientForConfigOrDie(&restclient.Config{Host: server.URL})
 | 
			
		||||
@@ -144,7 +147,8 @@ func TestDiscoveryToleratesMissingCoreGroup(t *testing.T) {
 | 
			
		||||
		}
 | 
			
		||||
		w.Header().Set("Content-Type", "application/json")
 | 
			
		||||
		w.WriteHeader(http.StatusOK)
 | 
			
		||||
		w.Write(output)
 | 
			
		||||
		_, err = w.Write(output)
 | 
			
		||||
		require.NoError(t, err)
 | 
			
		||||
	}))
 | 
			
		||||
	defer server.Close()
 | 
			
		||||
	client := NewDiscoveryClientForConfigOrDie(&restclient.Config{Host: server.URL})
 | 
			
		||||
@@ -180,7 +184,8 @@ func TestDiscoveryFailsWhenNonCoreGroupsMissing(t *testing.T) {
 | 
			
		||||
		}
 | 
			
		||||
		w.Header().Set("Content-Type", "application/json")
 | 
			
		||||
		w.WriteHeader(http.StatusOK)
 | 
			
		||||
		w.Write(output)
 | 
			
		||||
		_, err = w.Write(output)
 | 
			
		||||
		require.NoError(t, err)
 | 
			
		||||
	}))
 | 
			
		||||
	defer server.Close()
 | 
			
		||||
	client := NewDiscoveryClientForConfigOrDie(&restclient.Config{Host: server.URL})
 | 
			
		||||
@@ -380,7 +385,8 @@ func TestGetServerResourcesForGroupVersion(t *testing.T) {
 | 
			
		||||
		}
 | 
			
		||||
		w.Header().Set("Content-Type", "application/json")
 | 
			
		||||
		w.WriteHeader(http.StatusOK)
 | 
			
		||||
		w.Write(output)
 | 
			
		||||
		_, err = w.Write(output)
 | 
			
		||||
		require.NoError(t, err)
 | 
			
		||||
	}))
 | 
			
		||||
	defer server.Close()
 | 
			
		||||
	for _, test := range tests {
 | 
			
		||||
@@ -1294,6 +1300,8 @@ func TestAggregatedServerGroups(t *testing.T) {
 | 
			
		||||
 | 
			
		||||
	for _, test := range tests {
 | 
			
		||||
		server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
 | 
			
		||||
			var output []byte
 | 
			
		||||
			var err error
 | 
			
		||||
			var agg *apidiscovery.APIGroupDiscoveryList
 | 
			
		||||
			switch req.URL.Path {
 | 
			
		||||
			case "/api":
 | 
			
		||||
@@ -1304,13 +1312,14 @@ func TestAggregatedServerGroups(t *testing.T) {
 | 
			
		||||
				w.WriteHeader(http.StatusNotFound)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			output, err := json.Marshal(agg)
 | 
			
		||||
			output, err = json.Marshal(agg)
 | 
			
		||||
			require.NoError(t, err)
 | 
			
		||||
			// Content-Type is "aggregated" discovery format. Add extra parameter
 | 
			
		||||
			// to ensure we are resilient to these extra parameters.
 | 
			
		||||
			w.Header().Set("Content-Type", AcceptV2Beta1+"; charset=utf-8")
 | 
			
		||||
			w.Header().Set("Content-Type", AcceptV2+"; charset=utf-8")
 | 
			
		||||
			w.WriteHeader(http.StatusOK)
 | 
			
		||||
			w.Write(output)
 | 
			
		||||
			_, err = w.Write(output)
 | 
			
		||||
			require.NoError(t, err)
 | 
			
		||||
		}))
 | 
			
		||||
		defer server.Close()
 | 
			
		||||
		client := NewDiscoveryClientForConfigOrDie(&restclient.Config{Host: server.URL})
 | 
			
		||||
@@ -1338,7 +1347,9 @@ func TestAggregatedServerGroupsAndResources(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name                  string
 | 
			
		||||
		corev1                *apidiscovery.APIGroupDiscoveryList
 | 
			
		||||
		corev1DiscoveryBeta   *apidiscoveryv2beta1.APIGroupDiscoveryList
 | 
			
		||||
		apis                  *apidiscovery.APIGroupDiscoveryList
 | 
			
		||||
		apisDiscoveryBeta     *apidiscoveryv2beta1.APIGroupDiscoveryList
 | 
			
		||||
		expectedGroupNames    []string
 | 
			
		||||
		expectedGroupVersions []string
 | 
			
		||||
		expectedGVKs          []string
 | 
			
		||||
@@ -1368,6 +1379,28 @@ func TestAggregatedServerGroupsAndResources(t *testing.T) {
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			corev1DiscoveryBeta: &apidiscoveryv2beta1.APIGroupDiscoveryList{
 | 
			
		||||
				Items: []apidiscoveryv2beta1.APIGroupDiscovery{
 | 
			
		||||
					{
 | 
			
		||||
						Versions: []apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
							{
 | 
			
		||||
								Version: "v1",
 | 
			
		||||
								Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
 | 
			
		||||
									{
 | 
			
		||||
										Resource: "pods",
 | 
			
		||||
										ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
											Group:   "",
 | 
			
		||||
											Version: "v1",
 | 
			
		||||
											Kind:    "Pod",
 | 
			
		||||
										},
 | 
			
		||||
										Scope: apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			apis: &apidiscovery.APIGroupDiscoveryList{
 | 
			
		||||
				Items: []apidiscovery.APIGroupDiscovery{
 | 
			
		||||
					{
 | 
			
		||||
@@ -1393,6 +1426,31 @@ func TestAggregatedServerGroupsAndResources(t *testing.T) {
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			apisDiscoveryBeta: &apidiscoveryv2beta1.APIGroupDiscoveryList{
 | 
			
		||||
				Items: []apidiscoveryv2beta1.APIGroupDiscovery{
 | 
			
		||||
					{
 | 
			
		||||
						ObjectMeta: metav1.ObjectMeta{
 | 
			
		||||
							Name: "apps",
 | 
			
		||||
						},
 | 
			
		||||
						Versions: []apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
							{
 | 
			
		||||
								Version: "v1",
 | 
			
		||||
								Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
 | 
			
		||||
									{
 | 
			
		||||
										Resource: "deployments",
 | 
			
		||||
										ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
											Group:   "apps",
 | 
			
		||||
											Version: "v1",
 | 
			
		||||
											Kind:    "Deployment",
 | 
			
		||||
										},
 | 
			
		||||
										Scope: apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expectedGroupNames:    []string{"", "apps"},
 | 
			
		||||
			expectedGroupVersions: []string{"v1", "apps/v1"},
 | 
			
		||||
			expectedGVKs: []string{
 | 
			
		||||
@@ -1424,6 +1482,28 @@ func TestAggregatedServerGroupsAndResources(t *testing.T) {
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			corev1DiscoveryBeta: &apidiscoveryv2beta1.APIGroupDiscoveryList{
 | 
			
		||||
				Items: []apidiscoveryv2beta1.APIGroupDiscovery{
 | 
			
		||||
					{
 | 
			
		||||
						Versions: []apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
							{
 | 
			
		||||
								Version: "v1",
 | 
			
		||||
								Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
 | 
			
		||||
									{
 | 
			
		||||
										Resource: "pods",
 | 
			
		||||
										ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
											Group:   "",
 | 
			
		||||
											Version: "v1",
 | 
			
		||||
											Kind:    "Pod",
 | 
			
		||||
										},
 | 
			
		||||
										Scope: apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			apis: &apidiscovery.APIGroupDiscoveryList{
 | 
			
		||||
				Items: []apidiscovery.APIGroupDiscovery{
 | 
			
		||||
					{
 | 
			
		||||
@@ -1463,6 +1543,45 @@ func TestAggregatedServerGroupsAndResources(t *testing.T) {
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			apisDiscoveryBeta: &apidiscoveryv2beta1.APIGroupDiscoveryList{
 | 
			
		||||
				Items: []apidiscoveryv2beta1.APIGroupDiscovery{
 | 
			
		||||
					{
 | 
			
		||||
						ObjectMeta: metav1.ObjectMeta{
 | 
			
		||||
							Name: "apps",
 | 
			
		||||
						},
 | 
			
		||||
						Versions: []apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
							{
 | 
			
		||||
								Version: "v1",
 | 
			
		||||
								Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
 | 
			
		||||
									{
 | 
			
		||||
										Resource: "deployments",
 | 
			
		||||
										ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
											Group:   "apps",
 | 
			
		||||
											Version: "v1",
 | 
			
		||||
											Kind:    "Deployment",
 | 
			
		||||
										},
 | 
			
		||||
										Scope: apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
							{
 | 
			
		||||
								Version: "v2",
 | 
			
		||||
								Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
 | 
			
		||||
									{
 | 
			
		||||
										Resource: "deployments",
 | 
			
		||||
										ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
											Group:   "apps",
 | 
			
		||||
											Version: "v2",
 | 
			
		||||
											Kind:    "Deployment",
 | 
			
		||||
										},
 | 
			
		||||
										Scope: apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expectedGroupNames:    []string{"", "apps"},
 | 
			
		||||
			expectedGroupVersions: []string{"v1", "apps/v1", "apps/v2"},
 | 
			
		||||
			expectedGVKs: []string{
 | 
			
		||||
@@ -1495,6 +1614,28 @@ func TestAggregatedServerGroupsAndResources(t *testing.T) {
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			corev1DiscoveryBeta: &apidiscoveryv2beta1.APIGroupDiscoveryList{
 | 
			
		||||
				Items: []apidiscoveryv2beta1.APIGroupDiscovery{
 | 
			
		||||
					{
 | 
			
		||||
						Versions: []apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
							{
 | 
			
		||||
								Version: "v1",
 | 
			
		||||
								Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
 | 
			
		||||
									{
 | 
			
		||||
										Resource: "pods",
 | 
			
		||||
										ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
											Group:   "",
 | 
			
		||||
											Version: "v1",
 | 
			
		||||
											Kind:    "Pod",
 | 
			
		||||
										},
 | 
			
		||||
										Scope: apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			apis: &apidiscovery.APIGroupDiscoveryList{
 | 
			
		||||
				Items: []apidiscovery.APIGroupDiscovery{
 | 
			
		||||
					{
 | 
			
		||||
@@ -1535,6 +1676,46 @@ func TestAggregatedServerGroupsAndResources(t *testing.T) {
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			apisDiscoveryBeta: &apidiscoveryv2beta1.APIGroupDiscoveryList{
 | 
			
		||||
				Items: []apidiscoveryv2beta1.APIGroupDiscovery{
 | 
			
		||||
					{
 | 
			
		||||
						ObjectMeta: metav1.ObjectMeta{
 | 
			
		||||
							Name: "apps",
 | 
			
		||||
						},
 | 
			
		||||
						Versions: []apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
							{
 | 
			
		||||
								Version: "v1",
 | 
			
		||||
								Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
 | 
			
		||||
									{
 | 
			
		||||
										Resource: "deployments",
 | 
			
		||||
										ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
											Group:   "apps",
 | 
			
		||||
											Version: "v1",
 | 
			
		||||
											Kind:    "Deployment",
 | 
			
		||||
										},
 | 
			
		||||
										Scope: apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
							{
 | 
			
		||||
								Version: "v2",
 | 
			
		||||
								Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
 | 
			
		||||
									{
 | 
			
		||||
										Resource: "deployments",
 | 
			
		||||
										ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
											Group:   "apps",
 | 
			
		||||
											Version: "v2",
 | 
			
		||||
											Kind:    "Deployment",
 | 
			
		||||
										},
 | 
			
		||||
										Scope: apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
								Freshness: apidiscoveryv2beta1.DiscoveryFreshnessStale,
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expectedGroupNames:    []string{"", "apps"},
 | 
			
		||||
			expectedGroupVersions: []string{"v1", "apps/v1"},
 | 
			
		||||
			expectedGVKs: []string{
 | 
			
		||||
@@ -1576,6 +1757,37 @@ func TestAggregatedServerGroupsAndResources(t *testing.T) {
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			corev1DiscoveryBeta: &apidiscoveryv2beta1.APIGroupDiscoveryList{
 | 
			
		||||
				Items: []apidiscoveryv2beta1.APIGroupDiscovery{
 | 
			
		||||
					{
 | 
			
		||||
						Versions: []apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
							{
 | 
			
		||||
								Version: "v1",
 | 
			
		||||
								Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
 | 
			
		||||
									{
 | 
			
		||||
										Resource: "pods",
 | 
			
		||||
										ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
											Group:   "",
 | 
			
		||||
											Version: "v1",
 | 
			
		||||
											Kind:    "Pod",
 | 
			
		||||
										},
 | 
			
		||||
										Scope: apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
									},
 | 
			
		||||
									{
 | 
			
		||||
										Resource: "services",
 | 
			
		||||
										ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
											Group:   "",
 | 
			
		||||
											Version: "v1",
 | 
			
		||||
											Kind:    "Service",
 | 
			
		||||
										},
 | 
			
		||||
										Scope: apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			apis: &apidiscovery.APIGroupDiscoveryList{
 | 
			
		||||
				Items: []apidiscovery.APIGroupDiscovery{
 | 
			
		||||
					{
 | 
			
		||||
@@ -1635,6 +1847,65 @@ func TestAggregatedServerGroupsAndResources(t *testing.T) {
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			apisDiscoveryBeta: &apidiscoveryv2beta1.APIGroupDiscoveryList{
 | 
			
		||||
				Items: []apidiscoveryv2beta1.APIGroupDiscovery{
 | 
			
		||||
					{
 | 
			
		||||
						ObjectMeta: metav1.ObjectMeta{
 | 
			
		||||
							Name: "apps",
 | 
			
		||||
						},
 | 
			
		||||
						Versions: []apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
							// Stale "v2" version not included.
 | 
			
		||||
							{
 | 
			
		||||
								Version: "v2",
 | 
			
		||||
								Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
 | 
			
		||||
									{
 | 
			
		||||
										Resource: "deployments",
 | 
			
		||||
										ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
											Group:   "apps",
 | 
			
		||||
											Version: "v2",
 | 
			
		||||
											Kind:    "Deployment",
 | 
			
		||||
										},
 | 
			
		||||
										Scope: apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
									},
 | 
			
		||||
									{
 | 
			
		||||
										Resource: "statefulsets",
 | 
			
		||||
										ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
											Group:   "apps",
 | 
			
		||||
											Version: "v2",
 | 
			
		||||
											Kind:    "StatefulSet",
 | 
			
		||||
										},
 | 
			
		||||
										Scope: apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
								Freshness: apidiscoveryv2beta1.DiscoveryFreshnessStale,
 | 
			
		||||
							},
 | 
			
		||||
							{
 | 
			
		||||
								Version: "v1",
 | 
			
		||||
								Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
 | 
			
		||||
									{
 | 
			
		||||
										Resource: "deployments",
 | 
			
		||||
										ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
											Group:   "apps",
 | 
			
		||||
											Version: "v1",
 | 
			
		||||
											Kind:    "Deployment",
 | 
			
		||||
										},
 | 
			
		||||
										Scope: apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
									},
 | 
			
		||||
									{
 | 
			
		||||
										Resource: "statefulsets",
 | 
			
		||||
										ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
											Group:   "apps",
 | 
			
		||||
											Version: "v1",
 | 
			
		||||
											Kind:    "StatefulSet",
 | 
			
		||||
										},
 | 
			
		||||
										Scope: apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expectedGroupNames:    []string{"", "apps"},
 | 
			
		||||
			expectedGroupVersions: []string{"v1", "apps/v1"},
 | 
			
		||||
			expectedGVKs: []string{
 | 
			
		||||
@@ -1678,6 +1949,37 @@ func TestAggregatedServerGroupsAndResources(t *testing.T) {
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			corev1DiscoveryBeta: &apidiscoveryv2beta1.APIGroupDiscoveryList{
 | 
			
		||||
				Items: []apidiscoveryv2beta1.APIGroupDiscovery{
 | 
			
		||||
					{
 | 
			
		||||
						Versions: []apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
							{
 | 
			
		||||
								Version: "v1",
 | 
			
		||||
								Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
 | 
			
		||||
									{
 | 
			
		||||
										Resource: "pods",
 | 
			
		||||
										ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
											Group:   "",
 | 
			
		||||
											Version: "v1",
 | 
			
		||||
											Kind:    "Pod",
 | 
			
		||||
										},
 | 
			
		||||
										Scope: apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
									},
 | 
			
		||||
									{
 | 
			
		||||
										Resource: "services",
 | 
			
		||||
										ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
											Group:   "",
 | 
			
		||||
											Version: "v1",
 | 
			
		||||
											Kind:    "Service",
 | 
			
		||||
										},
 | 
			
		||||
										Scope: apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			apis: &apidiscovery.APIGroupDiscoveryList{
 | 
			
		||||
				Items: []apidiscovery.APIGroupDiscovery{
 | 
			
		||||
					{
 | 
			
		||||
@@ -1767,6 +2069,95 @@ func TestAggregatedServerGroupsAndResources(t *testing.T) {
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			apisDiscoveryBeta: &apidiscoveryv2beta1.APIGroupDiscoveryList{
 | 
			
		||||
				Items: []apidiscoveryv2beta1.APIGroupDiscovery{
 | 
			
		||||
					{
 | 
			
		||||
						ObjectMeta: metav1.ObjectMeta{
 | 
			
		||||
							Name: "apps",
 | 
			
		||||
						},
 | 
			
		||||
						Versions: []apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
							{
 | 
			
		||||
								Version: "v1",
 | 
			
		||||
								Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
 | 
			
		||||
									{
 | 
			
		||||
										Resource: "deployments",
 | 
			
		||||
										ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
											Group:   "apps",
 | 
			
		||||
											Version: "v1",
 | 
			
		||||
											Kind:    "Deployment",
 | 
			
		||||
										},
 | 
			
		||||
										Scope: apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
									},
 | 
			
		||||
									{
 | 
			
		||||
										Resource: "statefulsets",
 | 
			
		||||
										ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
											Group:   "apps",
 | 
			
		||||
											Version: "v1",
 | 
			
		||||
											Kind:    "StatefulSet",
 | 
			
		||||
										},
 | 
			
		||||
										Scope: apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						ObjectMeta: metav1.ObjectMeta{
 | 
			
		||||
							Name: "batch",
 | 
			
		||||
						},
 | 
			
		||||
						Versions: []apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
							// Stale Group/Version is not included
 | 
			
		||||
							{
 | 
			
		||||
								Version: "v1",
 | 
			
		||||
								Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
 | 
			
		||||
									{
 | 
			
		||||
										Resource: "jobs",
 | 
			
		||||
										ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
											Group:   "batch",
 | 
			
		||||
											Version: "v1",
 | 
			
		||||
											Kind:    "Job",
 | 
			
		||||
										},
 | 
			
		||||
										Scope: apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
									},
 | 
			
		||||
									{
 | 
			
		||||
										Resource: "cronjobs",
 | 
			
		||||
										ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
											Group:   "batch",
 | 
			
		||||
											Version: "v1",
 | 
			
		||||
											Kind:    "CronJob",
 | 
			
		||||
										},
 | 
			
		||||
										Scope: apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
								Freshness: apidiscoveryv2beta1.DiscoveryFreshnessStale,
 | 
			
		||||
							},
 | 
			
		||||
							{
 | 
			
		||||
								Version: "v1beta1",
 | 
			
		||||
								Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
 | 
			
		||||
									{
 | 
			
		||||
										Resource: "jobs",
 | 
			
		||||
										ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
											Group:   "batch",
 | 
			
		||||
											Version: "v1beta1",
 | 
			
		||||
											Kind:    "Job",
 | 
			
		||||
										},
 | 
			
		||||
										Scope: apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
									},
 | 
			
		||||
									{
 | 
			
		||||
										Resource: "cronjobs",
 | 
			
		||||
										ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
											Group:   "batch",
 | 
			
		||||
											Version: "v1beta1",
 | 
			
		||||
											Kind:    "CronJob",
 | 
			
		||||
										},
 | 
			
		||||
										Scope: apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expectedGroupNames:    []string{"", "apps", "batch"},
 | 
			
		||||
			expectedGroupVersions: []string{"v1", "apps/v1", "batch/v1beta1"},
 | 
			
		||||
			expectedGVKs: []string{
 | 
			
		||||
@@ -1782,6 +2173,7 @@ func TestAggregatedServerGroupsAndResources(t *testing.T) {
 | 
			
		||||
		{
 | 
			
		||||
			name:                "Aggregated discovery: /api returns nothing, 2 groups/2 resources at /apis",
 | 
			
		||||
			corev1:              &apidiscovery.APIGroupDiscoveryList{},
 | 
			
		||||
			corev1DiscoveryBeta: &apidiscoveryv2beta1.APIGroupDiscoveryList{},
 | 
			
		||||
			apis: &apidiscovery.APIGroupDiscoveryList{
 | 
			
		||||
				Items: []apidiscovery.APIGroupDiscovery{
 | 
			
		||||
					{
 | 
			
		||||
@@ -1871,6 +2263,95 @@ func TestAggregatedServerGroupsAndResources(t *testing.T) {
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			apisDiscoveryBeta: &apidiscoveryv2beta1.APIGroupDiscoveryList{
 | 
			
		||||
				Items: []apidiscoveryv2beta1.APIGroupDiscovery{
 | 
			
		||||
					{
 | 
			
		||||
						ObjectMeta: metav1.ObjectMeta{
 | 
			
		||||
							Name: "apps",
 | 
			
		||||
						},
 | 
			
		||||
						Versions: []apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
							{
 | 
			
		||||
								Version: "v1",
 | 
			
		||||
								Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
 | 
			
		||||
									{
 | 
			
		||||
										Resource: "deployments",
 | 
			
		||||
										ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
											Group:   "apps",
 | 
			
		||||
											Version: "v1",
 | 
			
		||||
											Kind:    "Deployment",
 | 
			
		||||
										},
 | 
			
		||||
										Scope: apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
									},
 | 
			
		||||
									{
 | 
			
		||||
										Resource: "statefulsets",
 | 
			
		||||
										ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
											Group:   "apps",
 | 
			
		||||
											Version: "v1",
 | 
			
		||||
											Kind:    "StatefulSet",
 | 
			
		||||
										},
 | 
			
		||||
										Scope: apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						ObjectMeta: metav1.ObjectMeta{
 | 
			
		||||
							Name: "batch",
 | 
			
		||||
						},
 | 
			
		||||
						Versions: []apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
							{
 | 
			
		||||
								Version: "v1",
 | 
			
		||||
								Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
 | 
			
		||||
									{
 | 
			
		||||
										Resource: "jobs",
 | 
			
		||||
										ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
											Group:   "batch",
 | 
			
		||||
											Version: "v1",
 | 
			
		||||
											Kind:    "Job",
 | 
			
		||||
										},
 | 
			
		||||
										Scope: apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
									},
 | 
			
		||||
									{
 | 
			
		||||
										Resource: "cronjobs",
 | 
			
		||||
										ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
											Group:   "batch",
 | 
			
		||||
											Version: "v1",
 | 
			
		||||
											Kind:    "CronJob",
 | 
			
		||||
										},
 | 
			
		||||
										Scope: apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
							{
 | 
			
		||||
								// Stale "v1beta1" not included.
 | 
			
		||||
								Version: "v1beta1",
 | 
			
		||||
								Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
 | 
			
		||||
									{
 | 
			
		||||
										Resource: "jobs",
 | 
			
		||||
										ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
											Group:   "batch",
 | 
			
		||||
											Version: "v1beta1",
 | 
			
		||||
											Kind:    "Job",
 | 
			
		||||
										},
 | 
			
		||||
										Scope: apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
									},
 | 
			
		||||
									{
 | 
			
		||||
										Resource: "cronjobs",
 | 
			
		||||
										ResponseKind: &metav1.GroupVersionKind{
 | 
			
		||||
											Group:   "batch",
 | 
			
		||||
											Version: "v1beta1",
 | 
			
		||||
											Kind:    "CronJob",
 | 
			
		||||
										},
 | 
			
		||||
										Scope: apidiscoveryv2beta1.ScopeNamespace,
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
								Freshness: apidiscoveryv2beta1.DiscoveryFreshnessStale,
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expectedGroupNames:    []string{"apps", "batch"},
 | 
			
		||||
			expectedGroupVersions: []string{"apps/v1", "batch/v1"},
 | 
			
		||||
			expectedGVKs: []string{
 | 
			
		||||
@@ -1883,8 +2364,14 @@ func TestAggregatedServerGroupsAndResources(t *testing.T) {
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Ensure that client can parse both V2Beta1 and V2 types from server
 | 
			
		||||
	serverAccepts := []string{AcceptV2Beta1, AcceptV2}
 | 
			
		||||
	for _, test := range tests {
 | 
			
		||||
		for _, accept := range serverAccepts {
 | 
			
		||||
			server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
 | 
			
		||||
				var output []byte
 | 
			
		||||
				var err error
 | 
			
		||||
				if accept == AcceptV2 {
 | 
			
		||||
					var agg *apidiscovery.APIGroupDiscoveryList
 | 
			
		||||
					switch req.URL.Path {
 | 
			
		||||
					case "/api":
 | 
			
		||||
@@ -1895,13 +2382,29 @@ func TestAggregatedServerGroupsAndResources(t *testing.T) {
 | 
			
		||||
						w.WriteHeader(http.StatusNotFound)
 | 
			
		||||
						return
 | 
			
		||||
					}
 | 
			
		||||
			output, err := json.Marshal(agg)
 | 
			
		||||
					output, err = json.Marshal(agg)
 | 
			
		||||
					require.NoError(t, err)
 | 
			
		||||
			// Content-type is "aggregated" discovery format. Add extra parameter
 | 
			
		||||
				} else {
 | 
			
		||||
					var agg *apidiscoveryv2beta1.APIGroupDiscoveryList
 | 
			
		||||
					switch req.URL.Path {
 | 
			
		||||
					case "/api":
 | 
			
		||||
						agg = test.corev1DiscoveryBeta
 | 
			
		||||
					case "/apis":
 | 
			
		||||
						agg = test.apisDiscoveryBeta
 | 
			
		||||
					default:
 | 
			
		||||
						w.WriteHeader(http.StatusNotFound)
 | 
			
		||||
						return
 | 
			
		||||
					}
 | 
			
		||||
					output, err = json.Marshal(&agg)
 | 
			
		||||
					require.NoError(t, err)
 | 
			
		||||
				}
 | 
			
		||||
				// Content-Type is "aggregated" discovery format. Add extra parameter
 | 
			
		||||
				// to ensure we are resilient to these extra parameters.
 | 
			
		||||
			w.Header().Set("Content-Type", AcceptV2Beta1+"; charset=utf-8")
 | 
			
		||||
				w.Header().Set("Content-Type", accept+"; charset=utf-8")
 | 
			
		||||
				w.WriteHeader(http.StatusOK)
 | 
			
		||||
			w.Write(output)
 | 
			
		||||
				_, err = w.Write(output)
 | 
			
		||||
				require.NoError(t, err)
 | 
			
		||||
 | 
			
		||||
			}))
 | 
			
		||||
			defer server.Close()
 | 
			
		||||
			client := NewDiscoveryClientForConfigOrDie(&restclient.Config{Host: server.URL})
 | 
			
		||||
@@ -1940,6 +2443,7 @@ func TestAggregatedServerGroupsAndResources(t *testing.T) {
 | 
			
		||||
				"%s: Expected GVKs (%s), got (%s)", test.name, expectedGVKs.List(), actualGVKs.List())
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestAggregatedServerGroupsAndResourcesWithErrors(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
@@ -2023,8 +2527,10 @@ func TestAggregatedServerGroupsAndResourcesWithErrors(t *testing.T) {
 | 
			
		||||
 | 
			
		||||
	for _, test := range tests {
 | 
			
		||||
		server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
 | 
			
		||||
			var agg *apidiscovery.APIGroupDiscoveryList
 | 
			
		||||
			var output []byte
 | 
			
		||||
			var err error
 | 
			
		||||
			var status int
 | 
			
		||||
			var agg *apidiscovery.APIGroupDiscoveryList
 | 
			
		||||
			switch req.URL.Path {
 | 
			
		||||
			case "/api":
 | 
			
		||||
				agg = test.corev1
 | 
			
		||||
@@ -2036,15 +2542,17 @@ func TestAggregatedServerGroupsAndResourcesWithErrors(t *testing.T) {
 | 
			
		||||
				w.WriteHeader(http.StatusNotFound)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			output, err := json.Marshal(agg)
 | 
			
		||||
			output, err = json.Marshal(agg)
 | 
			
		||||
			require.NoError(t, err)
 | 
			
		||||
			// Content-type is "aggregated" discovery format. Add extra parameter
 | 
			
		||||
			// Content-Type is "aggregated" discovery format. Add extra parameter
 | 
			
		||||
			// to ensure we are resilient to these extra parameters.
 | 
			
		||||
			w.Header().Set("Content-Type", AcceptV2Beta1+"; charset=utf-8")
 | 
			
		||||
			w.Header().Set("Content-Type", AcceptV2+"; charset=utf-8")
 | 
			
		||||
			w.WriteHeader(status)
 | 
			
		||||
			w.Write(output)
 | 
			
		||||
			_, err = w.Write(output)
 | 
			
		||||
			require.NoError(t, err)
 | 
			
		||||
		}))
 | 
			
		||||
		defer server.Close()
 | 
			
		||||
 | 
			
		||||
		client := NewDiscoveryClientForConfigOrDie(&restclient.Config{Host: server.URL})
 | 
			
		||||
		apiGroups, resources, err := client.ServerGroupsAndResources()
 | 
			
		||||
		if test.expectedErr {
 | 
			
		||||
@@ -2635,6 +3143,8 @@ func TestAggregatedServerPreferredResources(t *testing.T) {
 | 
			
		||||
 | 
			
		||||
	for _, test := range tests {
 | 
			
		||||
		server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
 | 
			
		||||
			var output []byte
 | 
			
		||||
			var err error
 | 
			
		||||
			var agg *apidiscovery.APIGroupDiscoveryList
 | 
			
		||||
			switch req.URL.Path {
 | 
			
		||||
			case "/api":
 | 
			
		||||
@@ -2645,13 +3155,14 @@ func TestAggregatedServerPreferredResources(t *testing.T) {
 | 
			
		||||
				w.WriteHeader(http.StatusNotFound)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			output, err := json.Marshal(agg)
 | 
			
		||||
			output, err = json.Marshal(agg)
 | 
			
		||||
			require.NoError(t, err)
 | 
			
		||||
			// Content-type is "aggregated" discovery format. Add extra parameter
 | 
			
		||||
			// Content-Type is "aggregated" discovery format. Add extra parameter
 | 
			
		||||
			// to ensure we are resilient to these extra parameters.
 | 
			
		||||
			w.Header().Set("Content-Type", AcceptV2Beta1+"; charset=utf-8")
 | 
			
		||||
			w.Header().Set("Content-Type", AcceptV2+"; charset=utf-8")
 | 
			
		||||
			w.WriteHeader(http.StatusOK)
 | 
			
		||||
			w.Write(output)
 | 
			
		||||
			_, err = w.Write(output)
 | 
			
		||||
			require.NoError(t, err)
 | 
			
		||||
		}))
 | 
			
		||||
		defer server.Close()
 | 
			
		||||
		client := NewDiscoveryClientForConfigOrDie(&restclient.Config{Host: server.URL})
 | 
			
		||||
@@ -2674,7 +3185,7 @@ func TestAggregatedServerPreferredResources(t *testing.T) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestDiscoveryContentTypeVersion(t *testing.T) {
 | 
			
		||||
	v2beta1 := schema.GroupVersionKind{Group: "apidiscovery.k8s.io", Version: "v2beta1", Kind: "APIGroupDiscoveryList"}
 | 
			
		||||
	v2 := schema.GroupVersionKind{Group: "apidiscovery.k8s.io", Version: "v2", Kind: "APIGroupDiscoveryList"}
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		contentType string
 | 
			
		||||
		gvk         schema.GroupVersionKind
 | 
			
		||||
@@ -2682,59 +3193,59 @@ func TestDiscoveryContentTypeVersion(t *testing.T) {
 | 
			
		||||
		expectErr   bool
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			contentType: "application/json; g=apidiscovery.k8s.io;v=v2beta1;as=APIGroupDiscoveryList",
 | 
			
		||||
			gvk:         v2beta1,
 | 
			
		||||
			contentType: "application/json; g=apidiscovery.k8s.io;v=v2;as=APIGroupDiscoveryList",
 | 
			
		||||
			gvk:         v2,
 | 
			
		||||
			match:       true,
 | 
			
		||||
			expectErr:   false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			// content-type parameters are not in correct order, but comparison ignores order.
 | 
			
		||||
			contentType: "application/json; v=v2beta1;as=APIGroupDiscoveryList;g=apidiscovery.k8s.io",
 | 
			
		||||
			gvk:         v2beta1,
 | 
			
		||||
			contentType: "application/json; v=v2;as=APIGroupDiscoveryList;g=apidiscovery.k8s.io",
 | 
			
		||||
			gvk:         v2,
 | 
			
		||||
			match:       true,
 | 
			
		||||
			expectErr:   false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			// content-type parameters are not in correct order, but comparison ignores order.
 | 
			
		||||
			contentType: "application/json; as=APIGroupDiscoveryList;g=apidiscovery.k8s.io;v=v2beta1",
 | 
			
		||||
			gvk:         v2beta1,
 | 
			
		||||
			contentType: "application/json; as=APIGroupDiscoveryList;g=apidiscovery.k8s.io;v=v2",
 | 
			
		||||
			gvk:         v2,
 | 
			
		||||
			match:       true,
 | 
			
		||||
			expectErr:   false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			// Ignores extra parameter "charset=utf-8"
 | 
			
		||||
			contentType: "application/json; g=apidiscovery.k8s.io;v=v2beta1;as=APIGroupDiscoveryList;charset=utf-8",
 | 
			
		||||
			gvk:         v2beta1,
 | 
			
		||||
			contentType: "application/json; g=apidiscovery.k8s.io;v=v2;as=APIGroupDiscoveryList;charset=utf-8",
 | 
			
		||||
			gvk:         v2,
 | 
			
		||||
			match:       true,
 | 
			
		||||
			expectErr:   false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			contentType: "application/json",
 | 
			
		||||
			gvk:         v2beta1,
 | 
			
		||||
			gvk:         v2,
 | 
			
		||||
			match:       false,
 | 
			
		||||
			expectErr:   false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			contentType: "application/json; charset=UTF-8",
 | 
			
		||||
			gvk:         v2beta1,
 | 
			
		||||
			gvk:         v2,
 | 
			
		||||
			match:       false,
 | 
			
		||||
			expectErr:   false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			contentType: "text/json",
 | 
			
		||||
			gvk:         v2beta1,
 | 
			
		||||
			gvk:         v2,
 | 
			
		||||
			match:       false,
 | 
			
		||||
			expectErr:   false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			contentType: "text/html",
 | 
			
		||||
			gvk:         v2beta1,
 | 
			
		||||
			gvk:         v2,
 | 
			
		||||
			match:       false,
 | 
			
		||||
			expectErr:   false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			contentType: "",
 | 
			
		||||
			gvk:         v2beta1,
 | 
			
		||||
			gvk:         v2,
 | 
			
		||||
			match:       false,
 | 
			
		||||
			expectErr:   true,
 | 
			
		||||
		},
 | 
			
		||||
 
 | 
			
		||||
@@ -23,10 +23,15 @@ import (
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	apidiscoveryv2 "k8s.io/api/apidiscovery/v2"
 | 
			
		||||
	apidiscoveryv2beta1 "k8s.io/api/apidiscovery/v2beta1"
 | 
			
		||||
	apidiscoveryv2conversion "k8s.io/apiserver/pkg/apis/apidiscovery/v2"
 | 
			
		||||
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime/schema"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime/serializer"
 | 
			
		||||
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/util/sets"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/util/wait"
 | 
			
		||||
	"k8s.io/apiserver/pkg/authentication/user"
 | 
			
		||||
@@ -34,11 +39,11 @@ import (
 | 
			
		||||
	discoveryendpoint "k8s.io/apiserver/pkg/endpoints/discovery/aggregated"
 | 
			
		||||
	"k8s.io/apiserver/pkg/endpoints/request"
 | 
			
		||||
	"k8s.io/client-go/discovery"
 | 
			
		||||
	"k8s.io/client-go/kubernetes/scheme"
 | 
			
		||||
	"k8s.io/client-go/util/workqueue"
 | 
			
		||||
	"k8s.io/klog/v2"
 | 
			
		||||
	apiregistrationv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1"
 | 
			
		||||
	"k8s.io/kube-aggregator/pkg/apis/apiregistration/v1/helper"
 | 
			
		||||
	"k8s.io/kube-aggregator/pkg/apiserver/scheme"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var APIRegistrationGroupVersion metav1.GroupVersion = metav1.GroupVersion{Group: "apiregistration.k8s.io", Version: "v1"}
 | 
			
		||||
@@ -54,6 +59,12 @@ var v2Beta1GVK = schema.GroupVersionKind{
 | 
			
		||||
	Kind:    "APIGroupDiscoveryList",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var v2GVK = schema.GroupVersionKind{
 | 
			
		||||
	Group:   "apidiscovery.k8s.io",
 | 
			
		||||
	Version: "v2",
 | 
			
		||||
	Kind:    "APIGroupDiscoveryList",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Given a list of APIServices and proxyHandlers for contacting them,
 | 
			
		||||
// DiscoveryManager caches a list of discovery documents for each server
 | 
			
		||||
 | 
			
		||||
@@ -96,6 +107,9 @@ type discoveryManager struct {
 | 
			
		||||
 | 
			
		||||
	// Merged handler which stores all known groupversions
 | 
			
		||||
	mergedDiscoveryHandler discoveryendpoint.ResourceManager
 | 
			
		||||
 | 
			
		||||
	// Codecs is the serializer used for decoding aggregated apiserver responses
 | 
			
		||||
	codecs serializer.CodecFactory
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Version of Service/Spec with relevant fields for use as a cache key
 | 
			
		||||
@@ -129,7 +143,7 @@ func newServiceKey(service apiregistrationv1.ServiceReference) serviceKey {
 | 
			
		||||
type cachedResult struct {
 | 
			
		||||
	// Currently cached discovery document for this service
 | 
			
		||||
	// Map from group name to version name to
 | 
			
		||||
	discovery map[metav1.GroupVersion]apidiscoveryv2beta1.APIVersionDiscovery
 | 
			
		||||
	discovery map[metav1.GroupVersion]apidiscoveryv2.APIVersionDiscovery
 | 
			
		||||
 | 
			
		||||
	// ETag hash of the cached discoveryDocument
 | 
			
		||||
	etag string
 | 
			
		||||
@@ -172,11 +186,19 @@ var _ DiscoveryAggregationController = &discoveryManager{}
 | 
			
		||||
func NewDiscoveryManager(
 | 
			
		||||
	target discoveryendpoint.ResourceManager,
 | 
			
		||||
) DiscoveryAggregationController {
 | 
			
		||||
	discoveryScheme := runtime.NewScheme()
 | 
			
		||||
	// Register conversion for apidiscovery
 | 
			
		||||
	apidiscoveryv2.SchemeBuilder.Register(apidiscoveryv2conversion.RegisterConversions)
 | 
			
		||||
	utilruntime.Must(apidiscoveryv2.AddToScheme(discoveryScheme))
 | 
			
		||||
	utilruntime.Must(apidiscoveryv2beta1.AddToScheme(discoveryScheme))
 | 
			
		||||
	codecs := serializer.NewCodecFactory(discoveryScheme)
 | 
			
		||||
 | 
			
		||||
	return &discoveryManager{
 | 
			
		||||
		mergedDiscoveryHandler: target,
 | 
			
		||||
		apiServices:            make(map[string]groupVersionInfo),
 | 
			
		||||
		cachedResults:          make(map[serviceKey]cachedResult),
 | 
			
		||||
		dirtyAPIServiceQueue:   workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "discovery-manager"),
 | 
			
		||||
		codecs:                 codecs,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -214,7 +236,7 @@ func (dm *discoveryManager) fetchFreshDiscoveryForService(gv metav1.GroupVersion
 | 
			
		||||
		Path:              req.URL.Path,
 | 
			
		||||
		IsResourceRequest: false,
 | 
			
		||||
	}))
 | 
			
		||||
	req.Header.Add("Accept", discovery.AcceptV2Beta1)
 | 
			
		||||
	req.Header.Add("Accept", discovery.AcceptV2+","+discovery.AcceptV2Beta1)
 | 
			
		||||
 | 
			
		||||
	if exists && len(cached.etag) > 0 {
 | 
			
		||||
		req.Header.Add("If-None-Match", cached.etag)
 | 
			
		||||
@@ -228,6 +250,8 @@ func (dm *discoveryManager) fetchFreshDiscoveryForService(gv metav1.GroupVersion
 | 
			
		||||
	handler.ServeHTTP(writer, req)
 | 
			
		||||
 | 
			
		||||
	isV2Beta1GVK, _ := discovery.ContentTypeIsGVK(writer.Header().Get("Content-Type"), v2Beta1GVK)
 | 
			
		||||
	isV2GVK, _ := discovery.ContentTypeIsGVK(writer.Header().Get("Content-Type"), v2GVK)
 | 
			
		||||
 | 
			
		||||
	switch {
 | 
			
		||||
	case writer.respCode == http.StatusNotModified:
 | 
			
		||||
		// Keep old entry, update timestamp
 | 
			
		||||
@@ -242,16 +266,16 @@ func (dm *discoveryManager) fetchFreshDiscoveryForService(gv metav1.GroupVersion
 | 
			
		||||
	case writer.respCode == http.StatusServiceUnavailable:
 | 
			
		||||
		return nil, fmt.Errorf("service %s returned non-success response code: %v",
 | 
			
		||||
			info.service.String(), writer.respCode)
 | 
			
		||||
	case writer.respCode == http.StatusOK && isV2Beta1GVK:
 | 
			
		||||
		parsed := &apidiscoveryv2beta1.APIGroupDiscoveryList{}
 | 
			
		||||
		if err := runtime.DecodeInto(scheme.Codecs.UniversalDecoder(), writer.data, parsed); err != nil {
 | 
			
		||||
	case writer.respCode == http.StatusOK && (isV2GVK || isV2Beta1GVK):
 | 
			
		||||
		parsed := &apidiscoveryv2.APIGroupDiscoveryList{}
 | 
			
		||||
		if err := runtime.DecodeInto(dm.codecs.UniversalDecoder(), writer.data, parsed); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		klog.V(3).Infof("DiscoveryManager: Successfully downloaded discovery for %s", info.service.String())
 | 
			
		||||
 | 
			
		||||
		// Convert discovery info into a map for convenient lookup later
 | 
			
		||||
		discoMap := map[metav1.GroupVersion]apidiscoveryv2beta1.APIVersionDiscovery{}
 | 
			
		||||
		discoMap := map[metav1.GroupVersion]apidiscoveryv2.APIVersionDiscovery{}
 | 
			
		||||
		for _, g := range parsed.Items {
 | 
			
		||||
			for _, v := range g.Versions {
 | 
			
		||||
				discoMap[metav1.GroupVersion{Group: g.Name, Version: v.Version}] = v
 | 
			
		||||
@@ -330,7 +354,7 @@ func (dm *discoveryManager) fetchFreshDiscoveryForService(gv metav1.GroupVersion
 | 
			
		||||
		}
 | 
			
		||||
		klog.V(3).Infof("DiscoveryManager: Successfully downloaded legacy discovery for %s", info.service.String())
 | 
			
		||||
 | 
			
		||||
		discoMap := map[metav1.GroupVersion]apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
		discoMap := map[metav1.GroupVersion]apidiscoveryv2.APIVersionDiscovery{
 | 
			
		||||
			// Convert old-style APIGroupList to new information
 | 
			
		||||
			gv: {
 | 
			
		||||
				Version:   gv.Version,
 | 
			
		||||
@@ -366,7 +390,7 @@ func (dm *discoveryManager) syncAPIService(apiServiceName string) error {
 | 
			
		||||
	// Lookup last cached result for this apiservice's service.
 | 
			
		||||
	cached, err := dm.fetchFreshDiscoveryForService(mgv, info)
 | 
			
		||||
 | 
			
		||||
	var entry apidiscoveryv2beta1.APIVersionDiscovery
 | 
			
		||||
	var entry apidiscoveryv2.APIVersionDiscovery
 | 
			
		||||
 | 
			
		||||
	// Extract the APIService's specific resource information from the
 | 
			
		||||
	// groupversion
 | 
			
		||||
@@ -377,7 +401,7 @@ func (dm *discoveryManager) syncAPIService(apiServiceName string) error {
 | 
			
		||||
		// Just use empty GV to mark that GV exists, but no resources.
 | 
			
		||||
		// Also mark that it is stale to indicate the fetch failed
 | 
			
		||||
		// TODO: Maybe also stick in a status for the version the error?
 | 
			
		||||
		entry = apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
		entry = apidiscoveryv2.APIVersionDiscovery{
 | 
			
		||||
			Version: gv.Version,
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
@@ -388,7 +412,7 @@ func (dm *discoveryManager) syncAPIService(apiServiceName string) error {
 | 
			
		||||
		} else {
 | 
			
		||||
			// Successfully fetched discovery information from the server, but
 | 
			
		||||
			// the server did not include this groupversion?
 | 
			
		||||
			entry = apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
			entry = apidiscoveryv2.APIVersionDiscovery{
 | 
			
		||||
				Version: gv.Version,
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
@@ -397,9 +421,9 @@ func (dm *discoveryManager) syncAPIService(apiServiceName string) error {
 | 
			
		||||
	// The entry's staleness depends upon if `fetchFreshDiscoveryForService`
 | 
			
		||||
	// returned an error or not.
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		entry.Freshness = apidiscoveryv2beta1.DiscoveryFreshnessCurrent
 | 
			
		||||
		entry.Freshness = apidiscoveryv2.DiscoveryFreshnessCurrent
 | 
			
		||||
	} else {
 | 
			
		||||
		entry.Freshness = apidiscoveryv2beta1.DiscoveryFreshnessStale
 | 
			
		||||
		entry.Freshness = apidiscoveryv2.DiscoveryFreshnessStale
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dm.mergedDiscoveryHandler.AddGroupVersion(gv.Group, entry)
 | 
			
		||||
 
 | 
			
		||||
@@ -33,10 +33,12 @@ import (
 | 
			
		||||
	fuzz "github.com/google/gofuzz"
 | 
			
		||||
	"github.com/stretchr/testify/require"
 | 
			
		||||
 | 
			
		||||
	apidiscoveryv2 "k8s.io/api/apidiscovery/v2"
 | 
			
		||||
	apidiscoveryv2beta1 "k8s.io/api/apidiscovery/v2beta1"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime/schema"
 | 
			
		||||
	apidiscoveryv2scheme "k8s.io/apiserver/pkg/apis/apidiscovery/v2"
 | 
			
		||||
	"k8s.io/apiserver/pkg/endpoints"
 | 
			
		||||
	"k8s.io/apiserver/pkg/endpoints/discovery"
 | 
			
		||||
	discoveryendpoint "k8s.io/apiserver/pkg/endpoints/discovery/aggregated"
 | 
			
		||||
@@ -68,31 +70,31 @@ func TestBasic(t *testing.T) {
 | 
			
		||||
	service3 := discoveryendpoint.NewResourceManager("apis")
 | 
			
		||||
	apiGroup1 := fuzzAPIGroups(2, 5, 25)
 | 
			
		||||
	apiGroup2 := fuzzAPIGroups(2, 5, 50)
 | 
			
		||||
	apiGroup3 := apidiscoveryv2beta1.APIGroupDiscoveryList{Items: []apidiscoveryv2beta1.APIGroupDiscovery{
 | 
			
		||||
	apiGroup3 := apidiscoveryv2.APIGroupDiscoveryList{Items: []apidiscoveryv2.APIGroupDiscovery{
 | 
			
		||||
		{
 | 
			
		||||
			ObjectMeta: metav1.ObjectMeta{Name: "weird.example.com"},
 | 
			
		||||
			Versions: []apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
			Versions: []apidiscoveryv2.APIVersionDiscovery{
 | 
			
		||||
				{
 | 
			
		||||
					Version:   "v1",
 | 
			
		||||
					Freshness: "Current",
 | 
			
		||||
					Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
 | 
			
		||||
					Resources: []apidiscoveryv2.APIResourceDiscovery{
 | 
			
		||||
						{
 | 
			
		||||
							Resource: "parent-missing-kind",
 | 
			
		||||
							Subresources: []apidiscoveryv2beta1.APISubresourceDiscovery{
 | 
			
		||||
							Subresources: []apidiscoveryv2.APISubresourceDiscovery{
 | 
			
		||||
								{Subresource: "subresource-missing-kind"},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						{
 | 
			
		||||
							Resource:     "parent-empty-kind",
 | 
			
		||||
							ResponseKind: &metav1.GroupVersionKind{},
 | 
			
		||||
							Subresources: []apidiscoveryv2beta1.APISubresourceDiscovery{
 | 
			
		||||
							Subresources: []apidiscoveryv2.APISubresourceDiscovery{
 | 
			
		||||
								{Subresource: "subresource-empty-kind", ResponseKind: &metav1.GroupVersionKind{}},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						{
 | 
			
		||||
							Resource:     "parent-with-kind",
 | 
			
		||||
							ResponseKind: &metav1.GroupVersionKind{Kind: "ParentWithKind"},
 | 
			
		||||
							Subresources: []apidiscoveryv2beta1.APISubresourceDiscovery{
 | 
			
		||||
							Subresources: []apidiscoveryv2.APISubresourceDiscovery{
 | 
			
		||||
								{Subresource: "subresource-with-kind", ResponseKind: &metav1.GroupVersionKind{Kind: "SubresourceWithKind"}},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
@@ -101,32 +103,32 @@ func TestBasic(t *testing.T) {
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}}
 | 
			
		||||
	apiGroup3WithFixup := apidiscoveryv2beta1.APIGroupDiscoveryList{Items: []apidiscoveryv2beta1.APIGroupDiscovery{
 | 
			
		||||
	apiGroup3WithFixup := apidiscoveryv2.APIGroupDiscoveryList{Items: []apidiscoveryv2.APIGroupDiscovery{
 | 
			
		||||
		{
 | 
			
		||||
			ObjectMeta: metav1.ObjectMeta{Name: "weird.example.com"},
 | 
			
		||||
			Versions: []apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
			Versions: []apidiscoveryv2.APIVersionDiscovery{
 | 
			
		||||
				{
 | 
			
		||||
					Version:   "v1",
 | 
			
		||||
					Freshness: "Current",
 | 
			
		||||
					Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
 | 
			
		||||
					Resources: []apidiscoveryv2.APIResourceDiscovery{
 | 
			
		||||
						{
 | 
			
		||||
							Resource:     "parent-missing-kind",
 | 
			
		||||
							ResponseKind: &metav1.GroupVersionKind{}, // defaulted by aggregator
 | 
			
		||||
							Subresources: []apidiscoveryv2beta1.APISubresourceDiscovery{
 | 
			
		||||
							Subresources: []apidiscoveryv2.APISubresourceDiscovery{
 | 
			
		||||
								{Subresource: "subresource-missing-kind", ResponseKind: &metav1.GroupVersionKind{}}, // defaulted by aggregator
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						{
 | 
			
		||||
							Resource:     "parent-empty-kind",
 | 
			
		||||
							ResponseKind: &metav1.GroupVersionKind{},
 | 
			
		||||
							Subresources: []apidiscoveryv2beta1.APISubresourceDiscovery{
 | 
			
		||||
							Subresources: []apidiscoveryv2.APISubresourceDiscovery{
 | 
			
		||||
								{Subresource: "subresource-empty-kind", ResponseKind: &metav1.GroupVersionKind{}},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						{
 | 
			
		||||
							Resource:     "parent-with-kind",
 | 
			
		||||
							ResponseKind: &metav1.GroupVersionKind{Kind: "ParentWithKind"},
 | 
			
		||||
							Subresources: []apidiscoveryv2beta1.APISubresourceDiscovery{
 | 
			
		||||
							Subresources: []apidiscoveryv2.APISubresourceDiscovery{
 | 
			
		||||
								{Subresource: "subresource-with-kind", ResponseKind: &metav1.GroupVersionKind{Kind: "SubresourceWithKind"}},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
@@ -217,7 +219,7 @@ func TestBasic(t *testing.T) {
 | 
			
		||||
	checkAPIGroups(t, apiGroup3WithFixup, parsed)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func checkAPIGroups(t *testing.T, api apidiscoveryv2beta1.APIGroupDiscoveryList, response *apidiscoveryv2beta1.APIGroupDiscoveryList) {
 | 
			
		||||
func checkAPIGroups(t *testing.T, api apidiscoveryv2.APIGroupDiscoveryList, response *apidiscoveryv2.APIGroupDiscoveryList) {
 | 
			
		||||
	t.Helper()
 | 
			
		||||
	if len(response.Items) < len(api.Items) {
 | 
			
		||||
		t.Errorf("expected to check for at least %d groups, only have %d groups in response", len(api.Items), len(response.Items))
 | 
			
		||||
@@ -281,10 +283,10 @@ func TestInitialRunHasAllAPIServices(t *testing.T) {
 | 
			
		||||
		t.Fatalf("unexpected status code %d", response.StatusCode)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	apiGroup := apidiscoveryv2beta1.APIGroupDiscoveryList{Items: []apidiscoveryv2beta1.APIGroupDiscovery{
 | 
			
		||||
	apiGroup := apidiscoveryv2.APIGroupDiscoveryList{Items: []apidiscoveryv2.APIGroupDiscovery{
 | 
			
		||||
		{
 | 
			
		||||
			ObjectMeta: metav1.ObjectMeta{Name: "stable.example.com"},
 | 
			
		||||
			Versions: []apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
			Versions: []apidiscoveryv2.APIVersionDiscovery{
 | 
			
		||||
				{
 | 
			
		||||
					Version:   "v1",
 | 
			
		||||
					Freshness: "Stale",
 | 
			
		||||
@@ -348,6 +350,71 @@ func TestServiceGC(t *testing.T) {
 | 
			
		||||
	require.Equal(t, 1, getCacheLen())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TestV2Beta1Skew tests that aggregated apiservers that only serve V2Beta1
 | 
			
		||||
// are still supported
 | 
			
		||||
func TestV2Beta1Skew(t *testing.T) {
 | 
			
		||||
	apiGroup := apidiscoveryv2.APIGroupDiscoveryList{Items: []apidiscoveryv2.APIGroupDiscovery{
 | 
			
		||||
		{
 | 
			
		||||
			ObjectMeta: metav1.ObjectMeta{Name: "stable.example.com"},
 | 
			
		||||
			Versions: []apidiscoveryv2.APIVersionDiscovery{
 | 
			
		||||
				{
 | 
			
		||||
					Version:   "v1",
 | 
			
		||||
					Freshness: "Current",
 | 
			
		||||
					Resources: []apidiscoveryv2.APIResourceDiscovery{
 | 
			
		||||
						{
 | 
			
		||||
							Resource:     "parent-with-kind",
 | 
			
		||||
							ResponseKind: &metav1.GroupVersionKind{Kind: "ParentWithKind"},
 | 
			
		||||
							Subresources: []apidiscoveryv2.APISubresourceDiscovery{
 | 
			
		||||
								{Subresource: "subresource-with-kind", ResponseKind: &metav1.GroupVersionKind{Kind: "SubresourceWithKind"}},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}}
 | 
			
		||||
 | 
			
		||||
	aggregatedResourceManager := discoveryendpoint.NewResourceManager("apis")
 | 
			
		||||
 | 
			
		||||
	aggregatedManager := newDiscoveryManager(aggregatedResourceManager)
 | 
			
		||||
 | 
			
		||||
	aggregatedManager.AddAPIService(&apiregistrationv1.APIService{
 | 
			
		||||
		ObjectMeta: metav1.ObjectMeta{
 | 
			
		||||
			Name: "v1.stable.example.com",
 | 
			
		||||
		},
 | 
			
		||||
		Spec: apiregistrationv1.APIServiceSpec{
 | 
			
		||||
			Group:   "stable.example.com",
 | 
			
		||||
			Version: "v1",
 | 
			
		||||
			Service: &apiregistrationv1.ServiceReference{
 | 
			
		||||
				Name: "test-service",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		// Force a v2beta1 response from the aggregated apiserver
 | 
			
		||||
		v2b := apidiscoveryv2beta1.APIGroupDiscoveryList{}
 | 
			
		||||
		err := apidiscoveryv2scheme.Convertv2APIGroupDiscoveryListTov2beta1APIGroupDiscoveryList(&apiGroup, &v2b, nil)
 | 
			
		||||
		require.NoError(t, err)
 | 
			
		||||
		converted, err := json.Marshal(v2b)
 | 
			
		||||
		require.NoError(t, err)
 | 
			
		||||
		w.Header().Set("Content-Type", "application/json;"+"g=apidiscovery.k8s.io;v=v2beta1;as=APIGroupDiscoveryList")
 | 
			
		||||
		w.WriteHeader(200)
 | 
			
		||||
		_, err = w.Write(converted)
 | 
			
		||||
		require.NoError(t, err)
 | 
			
		||||
	}))
 | 
			
		||||
	testCtx, cancel := context.WithCancel(context.Background())
 | 
			
		||||
	defer cancel()
 | 
			
		||||
 | 
			
		||||
	go aggregatedManager.Run(testCtx.Done(), nil)
 | 
			
		||||
	require.True(t, waitForQueueComplete(testCtx.Done(), aggregatedManager))
 | 
			
		||||
 | 
			
		||||
	response, _, parsed := fetchPath(aggregatedResourceManager, "")
 | 
			
		||||
	if response.StatusCode != 200 {
 | 
			
		||||
		t.Fatalf("unexpected status code %d", response.StatusCode)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	checkAPIGroups(t, apiGroup, parsed)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Test that a handler associated with an APIService gets pinged after the
 | 
			
		||||
// APIService has been marked as dirty
 | 
			
		||||
func TestDirty(t *testing.T) {
 | 
			
		||||
@@ -642,34 +709,34 @@ func TestLegacyFallbackNoCache(t *testing.T) {
 | 
			
		||||
	// includes the legacy resources
 | 
			
		||||
	_, _, doc := fetchPath(aggregatedResourceManager, "")
 | 
			
		||||
 | 
			
		||||
	mustConvert := func(r []metav1.APIResource) []apidiscoveryv2beta1.APIResourceDiscovery {
 | 
			
		||||
	mustConvert := func(r []metav1.APIResource) []apidiscoveryv2.APIResourceDiscovery {
 | 
			
		||||
		converted, err := endpoints.ConvertGroupVersionIntoToDiscovery(r)
 | 
			
		||||
		require.NoError(t, err)
 | 
			
		||||
		return converted
 | 
			
		||||
	}
 | 
			
		||||
	expectAggregatedDiscovery := []apidiscoveryv2beta1.APIGroupDiscovery{{
 | 
			
		||||
	expectAggregatedDiscovery := []apidiscoveryv2.APIGroupDiscovery{{
 | 
			
		||||
		ObjectMeta: metav1.ObjectMeta{
 | 
			
		||||
			Name: "stable.example.com",
 | 
			
		||||
		},
 | 
			
		||||
		Versions: []apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
		Versions: []apidiscoveryv2.APIVersionDiscovery{
 | 
			
		||||
			{
 | 
			
		||||
				Version:   "v1",
 | 
			
		||||
				Resources: mustConvert([]metav1.APIResource{resources["v1"]}),
 | 
			
		||||
				Freshness: apidiscoveryv2beta1.DiscoveryFreshnessCurrent,
 | 
			
		||||
				Freshness: apidiscoveryv2.DiscoveryFreshnessCurrent,
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				Version:   "v1beta1",
 | 
			
		||||
				Resources: mustConvert([]metav1.APIResource{resources["v1beta1"]}),
 | 
			
		||||
				Freshness: apidiscoveryv2beta1.DiscoveryFreshnessCurrent,
 | 
			
		||||
				Freshness: apidiscoveryv2.DiscoveryFreshnessCurrent,
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				Version: "v2alpha1",
 | 
			
		||||
				Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
 | 
			
		||||
				Resources: []apidiscoveryv2.APIResourceDiscovery{
 | 
			
		||||
					{
 | 
			
		||||
						Resource:     "parent-without-kind",
 | 
			
		||||
						ResponseKind: &metav1.GroupVersionKind{}, // defaulted
 | 
			
		||||
						Scope:        "Cluster",
 | 
			
		||||
						Subresources: []apidiscoveryv2beta1.APISubresourceDiscovery{
 | 
			
		||||
						Subresources: []apidiscoveryv2.APISubresourceDiscovery{
 | 
			
		||||
							{
 | 
			
		||||
								Subresource:  "subresource",
 | 
			
		||||
								ResponseKind: &metav1.GroupVersionKind{Kind: "Subresource"},
 | 
			
		||||
@@ -684,7 +751,7 @@ func TestLegacyFallbackNoCache(t *testing.T) {
 | 
			
		||||
						Resource:     "missing-parent",
 | 
			
		||||
						ResponseKind: &metav1.GroupVersionKind{}, // defaulted
 | 
			
		||||
						Scope:        "Cluster",
 | 
			
		||||
						Subresources: []apidiscoveryv2beta1.APISubresourceDiscovery{
 | 
			
		||||
						Subresources: []apidiscoveryv2.APISubresourceDiscovery{
 | 
			
		||||
							{
 | 
			
		||||
								Subresource:  "subresource-without-parent",
 | 
			
		||||
								ResponseKind: &metav1.GroupVersionKind{Kind: "SubresourceWithoutParent"},
 | 
			
		||||
@@ -692,12 +759,12 @@ func TestLegacyFallbackNoCache(t *testing.T) {
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				Freshness: apidiscoveryv2beta1.DiscoveryFreshnessCurrent,
 | 
			
		||||
				Freshness: apidiscoveryv2.DiscoveryFreshnessCurrent,
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				Version:   "v1alpha1",
 | 
			
		||||
				Resources: mustConvert([]metav1.APIResource{resources["v1alpha1"]}),
 | 
			
		||||
				Freshness: apidiscoveryv2beta1.DiscoveryFreshnessCurrent,
 | 
			
		||||
				Freshness: apidiscoveryv2.DiscoveryFreshnessCurrent,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}}
 | 
			
		||||
@@ -782,16 +849,16 @@ func testLegacyFallbackWithCustomRootHandler(t *testing.T, rootHandlerFn func(ht
 | 
			
		||||
 | 
			
		||||
	converted, err := endpoints.ConvertGroupVersionIntoToDiscovery([]metav1.APIResource{resource})
 | 
			
		||||
	require.NoError(t, err)
 | 
			
		||||
	require.Equal(t, []apidiscoveryv2beta1.APIGroupDiscovery{
 | 
			
		||||
	require.Equal(t, []apidiscoveryv2.APIGroupDiscovery{
 | 
			
		||||
		{
 | 
			
		||||
			ObjectMeta: metav1.ObjectMeta{
 | 
			
		||||
				Name: resource.Group,
 | 
			
		||||
			},
 | 
			
		||||
			Versions: []apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
			Versions: []apidiscoveryv2.APIVersionDiscovery{
 | 
			
		||||
				{
 | 
			
		||||
					Version:   resource.Version,
 | 
			
		||||
					Resources: converted,
 | 
			
		||||
					Freshness: apidiscoveryv2beta1.DiscoveryFreshnessCurrent,
 | 
			
		||||
					Freshness: apidiscoveryv2.DiscoveryFreshnessCurrent,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
@@ -859,15 +926,15 @@ func TestAPIServiceStale(t *testing.T) {
 | 
			
		||||
	// At this point external services have synced. Check if discovery document
 | 
			
		||||
	// lists the APIService group version as Stale.
 | 
			
		||||
	_, _, doc := fetchPath(aggregatedResourceManager, "")
 | 
			
		||||
	require.Equal(t, []apidiscoveryv2beta1.APIGroupDiscovery{
 | 
			
		||||
	require.Equal(t, []apidiscoveryv2.APIGroupDiscovery{
 | 
			
		||||
		{
 | 
			
		||||
			ObjectMeta: metav1.ObjectMeta{
 | 
			
		||||
				Name: "stable.example.com",
 | 
			
		||||
			},
 | 
			
		||||
			Versions: []apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
			Versions: []apidiscoveryv2.APIVersionDiscovery{
 | 
			
		||||
				{
 | 
			
		||||
					Version:   "v1",
 | 
			
		||||
					Freshness: apidiscoveryv2beta1.DiscoveryFreshnessStale,
 | 
			
		||||
					Freshness: apidiscoveryv2.DiscoveryFreshnessStale,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
@@ -934,16 +1001,16 @@ func TestNotModified(t *testing.T) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// copied from staging/src/k8s.io/apiserver/pkg/endpoints/discovery/v2/handler_test.go
 | 
			
		||||
func fuzzAPIGroups(atLeastNumGroups, maxNumGroups int, seed int64) apidiscoveryv2beta1.APIGroupDiscoveryList {
 | 
			
		||||
func fuzzAPIGroups(atLeastNumGroups, maxNumGroups int, seed int64) apidiscoveryv2.APIGroupDiscoveryList {
 | 
			
		||||
	fuzzer := fuzz.NewWithSeed(seed)
 | 
			
		||||
	fuzzer.NumElements(atLeastNumGroups, maxNumGroups)
 | 
			
		||||
	fuzzer.NilChance(0)
 | 
			
		||||
	fuzzer.Funcs(func(o *apidiscoveryv2beta1.APIGroupDiscovery, c fuzz.Continue) {
 | 
			
		||||
	fuzzer.Funcs(func(o *apidiscoveryv2.APIGroupDiscovery, c fuzz.Continue) {
 | 
			
		||||
		c.FuzzNoCustom(o)
 | 
			
		||||
 | 
			
		||||
		// The ResourceManager will just not serve the group if its versions
 | 
			
		||||
		// list is empty
 | 
			
		||||
		atLeastOne := apidiscoveryv2beta1.APIVersionDiscovery{}
 | 
			
		||||
		atLeastOne := apidiscoveryv2.APIVersionDiscovery{}
 | 
			
		||||
		c.Fuzz(&atLeastOne)
 | 
			
		||||
		o.Versions = append(o.Versions, atLeastOne)
 | 
			
		||||
 | 
			
		||||
@@ -958,10 +1025,10 @@ func fuzzAPIGroups(atLeastNumGroups, maxNumGroups int, seed int64) apidiscoveryv
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	var apis []apidiscoveryv2beta1.APIGroupDiscovery
 | 
			
		||||
	var apis []apidiscoveryv2.APIGroupDiscovery
 | 
			
		||||
	fuzzer.Fuzz(&apis)
 | 
			
		||||
 | 
			
		||||
	return apidiscoveryv2beta1.APIGroupDiscoveryList{
 | 
			
		||||
	return apidiscoveryv2.APIGroupDiscoveryList{
 | 
			
		||||
		TypeMeta: metav1.TypeMeta{
 | 
			
		||||
			Kind:       "APIGroupDiscoveryList",
 | 
			
		||||
			APIVersion: "v1",
 | 
			
		||||
@@ -972,13 +1039,13 @@ func fuzzAPIGroups(atLeastNumGroups, maxNumGroups int, seed int64) apidiscoveryv
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// copied from staging/src/k8s.io/apiserver/pkg/endpoints/discovery/v2/handler_test.go
 | 
			
		||||
func fetchPath(handler http.Handler, etag string) (*http.Response, []byte, *apidiscoveryv2beta1.APIGroupDiscoveryList) {
 | 
			
		||||
func fetchPath(handler http.Handler, etag string) (*http.Response, []byte, *apidiscoveryv2.APIGroupDiscoveryList) {
 | 
			
		||||
	// Expect json-formatted apis group list
 | 
			
		||||
	w := httptest.NewRecorder()
 | 
			
		||||
	req := httptest.NewRequest("GET", "/apis", nil)
 | 
			
		||||
 | 
			
		||||
	// Ask for JSON response
 | 
			
		||||
	req.Header.Set("Accept", runtime.ContentTypeJSON+";g=apidiscovery.k8s.io;v=v2beta1;as=APIGroupDiscoveryList")
 | 
			
		||||
	req.Header.Set("Accept", runtime.ContentTypeJSON+";g=apidiscovery.k8s.io;v=v2;as=APIGroupDiscoveryList,"+runtime.ContentTypeJSON+";g=apidiscovery.k8s.io;v=v2beta1;as=APIGroupDiscoveryList")
 | 
			
		||||
 | 
			
		||||
	if etag != "" {
 | 
			
		||||
		// Quote provided etag if unquoted
 | 
			
		||||
@@ -992,9 +1059,9 @@ func fetchPath(handler http.Handler, etag string) (*http.Response, []byte, *apid
 | 
			
		||||
	handler.ServeHTTP(w, req)
 | 
			
		||||
 | 
			
		||||
	bytes := w.Body.Bytes()
 | 
			
		||||
	var decoded *apidiscoveryv2beta1.APIGroupDiscoveryList
 | 
			
		||||
	var decoded *apidiscoveryv2.APIGroupDiscoveryList
 | 
			
		||||
	if len(bytes) > 0 {
 | 
			
		||||
		decoded = &apidiscoveryv2beta1.APIGroupDiscoveryList{}
 | 
			
		||||
		decoded = &apidiscoveryv2.APIGroupDiscoveryList{}
 | 
			
		||||
		runtime.DecodeInto(scheme.Codecs.UniversalDecoder(), bytes, decoded)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,7 @@ import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	apidiscoveryv2beta1 "k8s.io/api/apidiscovery/v2beta1"
 | 
			
		||||
	apidiscoveryv2 "k8s.io/api/apidiscovery/v2"
 | 
			
		||||
	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
 | 
			
		||||
	apiextensionclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
 | 
			
		||||
	"k8s.io/apiextensions-apiserver/test/integration/fixtures"
 | 
			
		||||
@@ -143,7 +143,7 @@ var _ = SIGDescribe("AggregatedDiscovery", func() {
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const aggregatedAccept = "application/json;g=apidiscovery.k8s.io;v=v2beta1;as=APIGroupDiscoveryList"
 | 
			
		||||
	const aggregatedAccept = "application/json;g=apidiscovery.k8s.io;v=v2;as=APIGroupDiscoveryList"
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
		Release : v1.30
 | 
			
		||||
@@ -156,7 +156,7 @@ var _ = SIGDescribe("AggregatedDiscovery", func() {
 | 
			
		||||
			framework.Failf("Failed to get raw aggregated discovery document")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		groupList := apidiscoveryv2beta1.APIGroupDiscoveryList{}
 | 
			
		||||
		groupList := apidiscoveryv2.APIGroupDiscoveryList{}
 | 
			
		||||
		err = json.Unmarshal(d, &groupList)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			framework.Failf("Failed to parse discovery: %v", err)
 | 
			
		||||
@@ -174,7 +174,7 @@ var _ = SIGDescribe("AggregatedDiscovery", func() {
 | 
			
		||||
			framework.Failf("Failed to get raw aggregated discovery document")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		groupListLegacy := apidiscoveryv2beta1.APIGroupDiscoveryList{}
 | 
			
		||||
		groupListLegacy := apidiscoveryv2.APIGroupDiscoveryList{}
 | 
			
		||||
		err = json.Unmarshal(d2, &groupListLegacy)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			framework.Failf("Failed to parse discovery: %v", err)
 | 
			
		||||
@@ -238,7 +238,7 @@ var _ = SIGDescribe("AggregatedDiscovery", func() {
 | 
			
		||||
				framework.Failf("Failed to get raw aggregated discovery document")
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			groupList := apidiscoveryv2beta1.APIGroupDiscoveryList{}
 | 
			
		||||
			groupList := apidiscoveryv2.APIGroupDiscoveryList{}
 | 
			
		||||
			err = json.Unmarshal(d, &groupList)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				framework.Failf("Failed to parse discovery: %v", err)
 | 
			
		||||
@@ -392,7 +392,7 @@ func isGVPresent(gvs *metav1.APIGroupList, gv schema.GroupVersion) bool {
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isGVRPresentAPIDiscovery(apidiscovery apidiscoveryv2beta1.APIGroupDiscoveryList, gvr schema.GroupVersionResource) bool {
 | 
			
		||||
func isGVRPresentAPIDiscovery(apidiscovery apidiscoveryv2.APIGroupDiscoveryList, gvr schema.GroupVersionResource) bool {
 | 
			
		||||
	for _, group := range apidiscovery.Items {
 | 
			
		||||
		if gvr.Group == group.Name {
 | 
			
		||||
			for _, version := range group.Versions {
 | 
			
		||||
 
 | 
			
		||||
@@ -28,7 +28,7 @@ import (
 | 
			
		||||
	"github.com/google/go-cmp/cmp"
 | 
			
		||||
	"github.com/stretchr/testify/require"
 | 
			
		||||
 | 
			
		||||
	apidiscoveryv2beta1 "k8s.io/api/apidiscovery/v2beta1"
 | 
			
		||||
	apidiscoveryv2 "k8s.io/api/apidiscovery/v2"
 | 
			
		||||
	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
 | 
			
		||||
	apiextensions "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/api/meta"
 | 
			
		||||
@@ -79,14 +79,14 @@ var (
 | 
			
		||||
	codecs    = runtimeserializer.NewCodecFactory(scheme)
 | 
			
		||||
	serialize runtime.NegotiatedSerializer
 | 
			
		||||
 | 
			
		||||
	basicTestGroup = apidiscoveryv2beta1.APIGroupDiscovery{
 | 
			
		||||
	basicTestGroup = apidiscoveryv2.APIGroupDiscovery{
 | 
			
		||||
		ObjectMeta: metav1.ObjectMeta{
 | 
			
		||||
			Name: "stable.example.com",
 | 
			
		||||
		},
 | 
			
		||||
		Versions: []apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
		Versions: []apidiscoveryv2.APIVersionDiscovery{
 | 
			
		||||
			{
 | 
			
		||||
				Version: "v1",
 | 
			
		||||
				Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
 | 
			
		||||
				Resources: []apidiscoveryv2.APIResourceDiscovery{
 | 
			
		||||
					{
 | 
			
		||||
						Resource:   "jobs",
 | 
			
		||||
						Verbs:      []string{"create", "list", "watch", "delete"},
 | 
			
		||||
@@ -94,19 +94,19 @@ var (
 | 
			
		||||
						Categories: []string{"all"},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				Freshness: apidiscoveryv2beta1.DiscoveryFreshnessCurrent,
 | 
			
		||||
				Freshness: apidiscoveryv2.DiscoveryFreshnessCurrent,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	basicTestGroupWithFixup = apidiscoveryv2beta1.APIGroupDiscovery{
 | 
			
		||||
	basicTestGroupWithFixup = apidiscoveryv2.APIGroupDiscovery{
 | 
			
		||||
		ObjectMeta: metav1.ObjectMeta{
 | 
			
		||||
			Name: "stable.example.com",
 | 
			
		||||
		},
 | 
			
		||||
		Versions: []apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
		Versions: []apidiscoveryv2.APIVersionDiscovery{
 | 
			
		||||
			{
 | 
			
		||||
				Version: "v1",
 | 
			
		||||
				Resources: []apidiscoveryv2beta1.APIResourceDiscovery{
 | 
			
		||||
				Resources: []apidiscoveryv2.APIResourceDiscovery{
 | 
			
		||||
					{
 | 
			
		||||
						Resource:   "jobs",
 | 
			
		||||
						Verbs:      []string{"create", "list", "watch", "delete"},
 | 
			
		||||
@@ -116,19 +116,19 @@ var (
 | 
			
		||||
						ResponseKind: &metav1.GroupVersionKind{},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				Freshness: apidiscoveryv2beta1.DiscoveryFreshnessCurrent,
 | 
			
		||||
				Freshness: apidiscoveryv2.DiscoveryFreshnessCurrent,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	basicTestGroupStale = apidiscoveryv2beta1.APIGroupDiscovery{
 | 
			
		||||
	basicTestGroupStale = apidiscoveryv2.APIGroupDiscovery{
 | 
			
		||||
		ObjectMeta: metav1.ObjectMeta{
 | 
			
		||||
			Name: "stable.example.com",
 | 
			
		||||
		},
 | 
			
		||||
		Versions: []apidiscoveryv2beta1.APIVersionDiscovery{
 | 
			
		||||
		Versions: []apidiscoveryv2.APIVersionDiscovery{
 | 
			
		||||
			{
 | 
			
		||||
				Version:   "v1",
 | 
			
		||||
				Freshness: apidiscoveryv2beta1.DiscoveryFreshnessStale,
 | 
			
		||||
				Freshness: apidiscoveryv2.DiscoveryFreshnessStale,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
@@ -193,7 +193,7 @@ func TestReadinessAggregatedAPIServiceDiscovery(t *testing.T) {
 | 
			
		||||
 | 
			
		||||
	// Create a resource manager whichs serves our GroupVersion
 | 
			
		||||
	resourceManager := discoveryendpoint.NewResourceManager("apis")
 | 
			
		||||
	resourceManager.SetGroups([]apidiscoveryv2beta1.APIGroupDiscovery{basicTestGroup})
 | 
			
		||||
	resourceManager.SetGroups([]apidiscoveryv2.APIGroupDiscovery{basicTestGroup})
 | 
			
		||||
 | 
			
		||||
	apiServiceWaitCh := make(chan struct{})
 | 
			
		||||
 | 
			
		||||
@@ -290,7 +290,7 @@ func TestAggregatedAPIServiceDiscovery(t *testing.T) {
 | 
			
		||||
 | 
			
		||||
	// Create a resource manager whichs serves our GroupVersion
 | 
			
		||||
	resourceManager := discoveryendpoint.NewResourceManager("apis")
 | 
			
		||||
	resourceManager.SetGroups([]apidiscoveryv2beta1.APIGroupDiscovery{basicTestGroup})
 | 
			
		||||
	resourceManager.SetGroups([]apidiscoveryv2.APIGroupDiscovery{basicTestGroup})
 | 
			
		||||
 | 
			
		||||
	// Install our ResourceManager as an Aggregated APIService to the
 | 
			
		||||
	// test server
 | 
			
		||||
@@ -371,7 +371,7 @@ func runTestCases(t *testing.T, cases []testCase) {
 | 
			
		||||
			})
 | 
			
		||||
			require.NoError(t, err, "v1 discovery must reset between tests: "+diff)
 | 
			
		||||
 | 
			
		||||
			err = WaitForResultWithCondition(ctx, client, func(result apidiscoveryv2beta1.APIGroupDiscoveryList) bool {
 | 
			
		||||
			err = WaitForResultWithCondition(ctx, client, func(result apidiscoveryv2.APIGroupDiscoveryList) bool {
 | 
			
		||||
				diff = cmp.Diff(originalV2, result)
 | 
			
		||||
				return reflect.DeepEqual(result, originalV2)
 | 
			
		||||
			})
 | 
			
		||||
@@ -393,6 +393,7 @@ func TestCRD(t *testing.T) {
 | 
			
		||||
				applyCRD(makeCRDSpec(stableGroup, "Foo", false, []string{"v1", "v1alpha1", "v1beta1", "v2"})),
 | 
			
		||||
				waitForGroupVersionsV1([]metav1.GroupVersion{stableV1, stableV1alpha1, stableV1beta1, stableV2}),
 | 
			
		||||
				waitForGroupVersionsV2([]metav1.GroupVersion{stableV1, stableV1alpha1, stableV1beta1, stableV2}),
 | 
			
		||||
				waitForGroupVersionsV2Beta1([]metav1.GroupVersion{stableV1, stableV1alpha1, stableV1beta1, stableV2}),
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
@@ -402,6 +403,7 @@ func TestCRD(t *testing.T) {
 | 
			
		||||
				applyCRD(makeCRDSpec(stableGroup, "Foo", false, []string{"v1", "v1alpha1", "v1beta1", "v2"})),
 | 
			
		||||
				waitForGroupVersionsV1([]metav1.GroupVersion{stableV1, stableV1alpha1, stableV1beta1, stableV2}),
 | 
			
		||||
				waitForGroupVersionsV2([]metav1.GroupVersion{stableV1, stableV1alpha1, stableV1beta1, stableV2}),
 | 
			
		||||
				waitForGroupVersionsV2Beta1([]metav1.GroupVersion{stableV1, stableV1alpha1, stableV1beta1, stableV2}),
 | 
			
		||||
				deleteObject{
 | 
			
		||||
					GroupVersionResource: metav1.GroupVersionResource(apiextensionsv1.SchemeGroupVersion.WithResource("customresourcedefinitions")),
 | 
			
		||||
					Name:                 "foos.stable.example.com",
 | 
			
		||||
@@ -469,6 +471,7 @@ func TestCRD(t *testing.T) {
 | 
			
		||||
				// Wait for GV to appear in both discovery documents
 | 
			
		||||
				waitForGroupVersionsV1([]metav1.GroupVersion{stableV2, stableV1alpha1}),
 | 
			
		||||
				waitForGroupVersionsV2([]metav1.GroupVersion{stableV2, stableV1alpha1}),
 | 
			
		||||
				waitForGroupVersionsV2Beta1([]metav1.GroupVersion{stableV2, stableV1alpha1}),
 | 
			
		||||
 | 
			
		||||
				applyAPIService(
 | 
			
		||||
					apiregistrationv1.APIServiceSpec{
 | 
			
		||||
@@ -487,11 +490,13 @@ func TestCRD(t *testing.T) {
 | 
			
		||||
				// We should now have stable v1 available
 | 
			
		||||
				waitForGroupVersionsV1([]metav1.GroupVersion{stableV1}),
 | 
			
		||||
				waitForGroupVersionsV2([]metav1.GroupVersion{stableV1}),
 | 
			
		||||
				waitForGroupVersionsV2Beta1([]metav1.GroupVersion{stableV1}),
 | 
			
		||||
 | 
			
		||||
				// The CRD group-versions not served by the aggregated
 | 
			
		||||
				// apiservice should still be availablee
 | 
			
		||||
				waitForGroupVersionsV1([]metav1.GroupVersion{stableV2, stableV1alpha1}),
 | 
			
		||||
				waitForGroupVersionsV2([]metav1.GroupVersion{stableV2, stableV1alpha1}),
 | 
			
		||||
				waitForGroupVersionsV2Beta1([]metav1.GroupVersion{stableV2, stableV1alpha1}),
 | 
			
		||||
 | 
			
		||||
				// Remove API service. Show we have switched to CRD
 | 
			
		||||
				deleteObject{
 | 
			
		||||
@@ -502,9 +507,11 @@ func TestCRD(t *testing.T) {
 | 
			
		||||
				// Show that we still have stable v1 since it is in the CRD
 | 
			
		||||
				waitForGroupVersionsV1([]metav1.GroupVersion{stableV2, stableV1alpha1}),
 | 
			
		||||
				waitForGroupVersionsV2([]metav1.GroupVersion{stableV2, stableV1alpha1}),
 | 
			
		||||
				waitForGroupVersionsV2Beta1([]metav1.GroupVersion{stableV2, stableV1alpha1}),
 | 
			
		||||
 | 
			
		||||
				waitForAbsentGroupVersionsV1([]metav1.GroupVersion{stableV1}),
 | 
			
		||||
				waitForAbsentGroupVersionsV2([]metav1.GroupVersion{stableV1}),
 | 
			
		||||
				waitForAbsentGroupVersionsV2Beta1([]metav1.GroupVersion{stableV1}),
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
@@ -635,9 +642,9 @@ func TestFreshness(t *testing.T) {
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				switch entry.Freshness {
 | 
			
		||||
				case apidiscoveryv2beta1.DiscoveryFreshnessCurrent:
 | 
			
		||||
				case apidiscoveryv2.DiscoveryFreshnessCurrent:
 | 
			
		||||
					// Skip
 | 
			
		||||
				case apidiscoveryv2beta1.DiscoveryFreshnessStale:
 | 
			
		||||
				case apidiscoveryv2.DiscoveryFreshnessStale:
 | 
			
		||||
					staleGVs = append(staleGVs, targetGv)
 | 
			
		||||
				default:
 | 
			
		||||
					return fmt.Errorf("unrecognized freshness '%v' on gv '%v'", entry.Freshness, targetGv)
 | 
			
		||||
@@ -704,7 +711,7 @@ func TestFreshness(t *testing.T) {
 | 
			
		||||
 | 
			
		||||
// Shows a group for which multiple APIServices specify a GroupPriorityMinimum,
 | 
			
		||||
// it is sorted the same in both versions of discovery
 | 
			
		||||
func TestGroupPriorty(t *testing.T) {
 | 
			
		||||
func TestGroupPriority(t *testing.T) {
 | 
			
		||||
	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.AggregatedDiscoveryEndpoint, true)()
 | 
			
		||||
 | 
			
		||||
	makeApiServiceSpec := func(gv metav1.GroupVersion, groupPriorityMin, versionPriority int) apiregistrationv1.APIServiceSpec {
 | 
			
		||||
 
 | 
			
		||||
@@ -36,6 +36,7 @@ import (
 | 
			
		||||
	"k8s.io/client-go/kubernetes"
 | 
			
		||||
	aggregator "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset"
 | 
			
		||||
 | 
			
		||||
	apidiscoveryv2 "k8s.io/api/apidiscovery/v2"
 | 
			
		||||
	apidiscoveryv2beta1 "k8s.io/api/apidiscovery/v2beta1"
 | 
			
		||||
	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
@@ -43,7 +44,8 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const acceptV1JSON = "application/json"
 | 
			
		||||
const acceptV2JSON = "application/json;g=apidiscovery.k8s.io;v=v2beta1;as=APIGroupDiscoveryList"
 | 
			
		||||
const acceptV2Beta1JSON = "application/json;g=apidiscovery.k8s.io;v=v2beta1;as=APIGroupDiscoveryList"
 | 
			
		||||
const acceptV2JSON = "application/json;g=apidiscovery.k8s.io;v=v2;as=APIGroupDiscoveryList"
 | 
			
		||||
 | 
			
		||||
const maxTimeout = 10 * time.Second
 | 
			
		||||
 | 
			
		||||
@@ -93,9 +95,15 @@ type waitForAbsentGroupVersionsV1 []metav1.GroupVersion
 | 
			
		||||
// Wait for groupversions to appear in v2 discovery
 | 
			
		||||
type waitForGroupVersionsV2 []metav1.GroupVersion
 | 
			
		||||
 | 
			
		||||
// Wait for groupversions to appear in v2beta1 discovery
 | 
			
		||||
type waitForGroupVersionsV2Beta1 []metav1.GroupVersion
 | 
			
		||||
 | 
			
		||||
// Wait for groupversions to disappear from v2 discovery
 | 
			
		||||
type waitForAbsentGroupVersionsV2 []metav1.GroupVersion
 | 
			
		||||
 | 
			
		||||
// Wait for groupversions to disappear from v2beta1 discovery
 | 
			
		||||
type waitForAbsentGroupVersionsV2Beta1 []metav1.GroupVersion
 | 
			
		||||
 | 
			
		||||
type waitForStaleGroupVersionsV2 []metav1.GroupVersion
 | 
			
		||||
type waitForFreshGroupVersionsV2 []metav1.GroupVersion
 | 
			
		||||
 | 
			
		||||
@@ -144,10 +152,11 @@ func (a applyAPIService) Cleanup(ctx context.Context, client testClient) error {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = wait.PollWithContext(
 | 
			
		||||
	err = wait.PollUntilContextTimeout(
 | 
			
		||||
		ctx,
 | 
			
		||||
		250*time.Millisecond,
 | 
			
		||||
		maxTimeout,
 | 
			
		||||
		true,
 | 
			
		||||
		func(ctx context.Context) (done bool, err error) {
 | 
			
		||||
			_, err = client.ApiregistrationV1().APIServices().Get(ctx, name, metav1.GetOptions{})
 | 
			
		||||
			if err == nil {
 | 
			
		||||
@@ -212,10 +221,11 @@ func (a applyCRD) Cleanup(ctx context.Context, client testClient) error {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = wait.PollWithContext(
 | 
			
		||||
	err = wait.PollUntilContextTimeout(
 | 
			
		||||
		ctx,
 | 
			
		||||
		250*time.Millisecond,
 | 
			
		||||
		maxTimeout,
 | 
			
		||||
		true,
 | 
			
		||||
		func(ctx context.Context) (done bool, err error) {
 | 
			
		||||
			_, err = client.ApiextensionsV1().CustomResourceDefinitions().Get(ctx, name, metav1.GetOptions{})
 | 
			
		||||
			if err == nil {
 | 
			
		||||
@@ -248,9 +258,9 @@ func (d deleteObject) Do(ctx context.Context, client testClient) error {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w waitForStaleGroupVersionsV2) Do(ctx context.Context, client testClient) error {
 | 
			
		||||
	err := WaitForResultWithCondition(ctx, client, func(result apidiscoveryv2beta1.APIGroupDiscoveryList) bool {
 | 
			
		||||
	err := WaitForResultWithCondition(ctx, client, func(result apidiscoveryv2.APIGroupDiscoveryList) bool {
 | 
			
		||||
		for _, gv := range w {
 | 
			
		||||
			if info := FindGroupVersionV2(result, gv); info == nil || info.Freshness != apidiscoveryv2beta1.DiscoveryFreshnessStale {
 | 
			
		||||
			if info := FindGroupVersionV2(result, gv); info == nil || info.Freshness != apidiscoveryv2.DiscoveryFreshnessStale {
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
@@ -265,9 +275,9 @@ func (w waitForStaleGroupVersionsV2) Do(ctx context.Context, client testClient)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w waitForFreshGroupVersionsV2) Do(ctx context.Context, client testClient) error {
 | 
			
		||||
	err := WaitForResultWithCondition(ctx, client, func(result apidiscoveryv2beta1.APIGroupDiscoveryList) bool {
 | 
			
		||||
	err := WaitForResultWithCondition(ctx, client, func(result apidiscoveryv2.APIGroupDiscoveryList) bool {
 | 
			
		||||
		for _, gv := range w {
 | 
			
		||||
			if info := FindGroupVersionV2(result, gv); info == nil || info.Freshness != apidiscoveryv2beta1.DiscoveryFreshnessCurrent {
 | 
			
		||||
			if info := FindGroupVersionV2(result, gv); info == nil || info.Freshness != apidiscoveryv2.DiscoveryFreshnessCurrent {
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
@@ -282,7 +292,7 @@ func (w waitForFreshGroupVersionsV2) Do(ctx context.Context, client testClient)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w waitForGroupVersionsV2) Do(ctx context.Context, client testClient) error {
 | 
			
		||||
	err := WaitForResultWithCondition(ctx, client, func(result apidiscoveryv2beta1.APIGroupDiscoveryList) bool {
 | 
			
		||||
	err := WaitForResultWithCondition(ctx, client, func(result apidiscoveryv2.APIGroupDiscoveryList) bool {
 | 
			
		||||
		for _, gv := range w {
 | 
			
		||||
			if FindGroupVersionV2(result, gv) == nil {
 | 
			
		||||
				return false
 | 
			
		||||
@@ -298,8 +308,25 @@ func (w waitForGroupVersionsV2) Do(ctx context.Context, client testClient) error
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w waitForGroupVersionsV2Beta1) Do(ctx context.Context, client testClient) error {
 | 
			
		||||
	err := WaitForV2Beta1ResultWithCondition(ctx, client, func(result apidiscoveryv2beta1.APIGroupDiscoveryList) bool {
 | 
			
		||||
		for _, gv := range w {
 | 
			
		||||
			if FindGroupVersionV2Beta1(result, gv) == nil {
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return true
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("waiting for groupversions v2 (%v): %w", w, err)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w waitForAbsentGroupVersionsV2) Do(ctx context.Context, client testClient) error {
 | 
			
		||||
	err := WaitForResultWithCondition(ctx, client, func(result apidiscoveryv2beta1.APIGroupDiscoveryList) bool {
 | 
			
		||||
	err := WaitForResultWithCondition(ctx, client, func(result apidiscoveryv2.APIGroupDiscoveryList) bool {
 | 
			
		||||
		for _, gv := range w {
 | 
			
		||||
			if FindGroupVersionV2(result, gv) != nil {
 | 
			
		||||
				return false
 | 
			
		||||
@@ -315,6 +342,23 @@ func (w waitForAbsentGroupVersionsV2) Do(ctx context.Context, client testClient)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w waitForAbsentGroupVersionsV2Beta1) Do(ctx context.Context, client testClient) error {
 | 
			
		||||
	err := WaitForV2Beta1ResultWithCondition(ctx, client, func(result apidiscoveryv2beta1.APIGroupDiscoveryList) bool {
 | 
			
		||||
		for _, gv := range w {
 | 
			
		||||
			if FindGroupVersionV2Beta1(result, gv) != nil {
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return true
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("waiting for absent groupversions v2 (%v): %w", w, err)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w waitForGroupVersionsV1) Do(ctx context.Context, client testClient) error {
 | 
			
		||||
	err := WaitForV1GroupsWithCondition(ctx, client, func(result metav1.APIGroupList) bool {
 | 
			
		||||
		for _, gv := range w {
 | 
			
		||||
@@ -429,7 +473,7 @@ func (w waitForResourcesAbsentV1) Do(ctx context.Context, client testClient) err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w waitForResourcesV2) Do(ctx context.Context, client testClient) error {
 | 
			
		||||
	err := WaitForResultWithCondition(ctx, client, func(result apidiscoveryv2beta1.APIGroupDiscoveryList) bool {
 | 
			
		||||
	err := WaitForResultWithCondition(ctx, client, func(result apidiscoveryv2.APIGroupDiscoveryList) bool {
 | 
			
		||||
		for _, gvr := range w {
 | 
			
		||||
			if info := FindGroupVersionV2(result, metav1.GroupVersion{Group: gvr.Group, Version: gvr.Version}); info == nil {
 | 
			
		||||
				return false
 | 
			
		||||
@@ -458,7 +502,7 @@ func (w waitForResourcesV2) Do(ctx context.Context, client testClient) error {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w waitForResourcesAbsentV2) Do(ctx context.Context, client testClient) error {
 | 
			
		||||
	err := WaitForResultWithCondition(ctx, client, func(result apidiscoveryv2beta1.APIGroupDiscoveryList) bool {
 | 
			
		||||
	err := WaitForResultWithCondition(ctx, client, func(result apidiscoveryv2.APIGroupDiscoveryList) bool {
 | 
			
		||||
		for _, gvr := range w {
 | 
			
		||||
			if info := FindGroupVersionV2(result, metav1.GroupVersion{Group: gvr.Group, Version: gvr.Version}); info == nil {
 | 
			
		||||
				return false
 | 
			
		||||
@@ -484,7 +528,7 @@ func (i inlineAction) Do(ctx context.Context, client testClient) error {
 | 
			
		||||
	return i(ctx, client)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func FetchV2Discovery(ctx context.Context, client testClient) (apidiscoveryv2beta1.APIGroupDiscoveryList, error) {
 | 
			
		||||
func FetchV2Discovery(ctx context.Context, client testClient) (apidiscoveryv2.APIGroupDiscoveryList, error) {
 | 
			
		||||
	result, err := client.
 | 
			
		||||
		Discovery().
 | 
			
		||||
		RESTClient().
 | 
			
		||||
@@ -494,6 +538,29 @@ func FetchV2Discovery(ctx context.Context, client testClient) (apidiscoveryv2bet
 | 
			
		||||
		Do(ctx).
 | 
			
		||||
		Raw()
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return apidiscoveryv2.APIGroupDiscoveryList{}, fmt.Errorf("failed to fetch v2 discovery: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	groupList := apidiscoveryv2.APIGroupDiscoveryList{}
 | 
			
		||||
	err = json.Unmarshal(result, &groupList)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return apidiscoveryv2.APIGroupDiscoveryList{}, fmt.Errorf("failed to parse v2 discovery: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return groupList, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func FetchV2Beta1Discovery(ctx context.Context, client testClient) (apidiscoveryv2beta1.APIGroupDiscoveryList, error) {
 | 
			
		||||
	result, err := client.
 | 
			
		||||
		Discovery().
 | 
			
		||||
		RESTClient().
 | 
			
		||||
		Get().
 | 
			
		||||
		AbsPath("/apis").
 | 
			
		||||
		SetHeader("Accept", acceptV2Beta1JSON).
 | 
			
		||||
		Do(ctx).
 | 
			
		||||
		Raw()
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return apidiscoveryv2beta1.APIGroupDiscoveryList{}, fmt.Errorf("failed to fetch v2 discovery: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
@@ -562,7 +629,7 @@ func FetchV1DiscoveryResource(ctx context.Context, client testClient, gv metav1.
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func WaitForGroupsAbsent(ctx context.Context, client testClient, groups ...string) error {
 | 
			
		||||
	return WaitForResultWithCondition(ctx, client, func(groupList apidiscoveryv2beta1.APIGroupDiscoveryList) bool {
 | 
			
		||||
	return WaitForResultWithCondition(ctx, client, func(groupList apidiscoveryv2.APIGroupDiscoveryList) bool {
 | 
			
		||||
		for _, searchGroup := range groups {
 | 
			
		||||
			for _, docGroup := range groupList.Items {
 | 
			
		||||
				if docGroup.Name == searchGroup {
 | 
			
		||||
@@ -598,8 +665,8 @@ func WaitForRootPaths(t *testing.T, ctx context.Context, client testClient, requ
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func WaitForGroups(ctx context.Context, client testClient, groups ...apidiscoveryv2beta1.APIGroupDiscovery) error {
 | 
			
		||||
	return WaitForResultWithCondition(ctx, client, func(groupList apidiscoveryv2beta1.APIGroupDiscoveryList) bool {
 | 
			
		||||
func WaitForGroups(ctx context.Context, client testClient, groups ...apidiscoveryv2.APIGroupDiscovery) error {
 | 
			
		||||
	return WaitForResultWithCondition(ctx, client, func(groupList apidiscoveryv2.APIGroupDiscoveryList) bool {
 | 
			
		||||
		for _, searchGroup := range groups {
 | 
			
		||||
			found := false
 | 
			
		||||
			for _, docGroup := range groupList.Items {
 | 
			
		||||
@@ -616,13 +683,14 @@ func WaitForGroups(ctx context.Context, client testClient, groups ...apidiscover
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func WaitForResultWithCondition(ctx context.Context, client testClient, condition func(result apidiscoveryv2beta1.APIGroupDiscoveryList) bool) error {
 | 
			
		||||
func WaitForResultWithCondition(ctx context.Context, client testClient, condition func(result apidiscoveryv2.APIGroupDiscoveryList) bool) error {
 | 
			
		||||
	// Keep repeatedly fetching document from aggregator.
 | 
			
		||||
	// Check to see if it contains our service within a reasonable amount of time
 | 
			
		||||
	return wait.PollWithContext(
 | 
			
		||||
	return wait.PollUntilContextTimeout(
 | 
			
		||||
		ctx,
 | 
			
		||||
		250*time.Millisecond,
 | 
			
		||||
		maxTimeout,
 | 
			
		||||
		true,
 | 
			
		||||
		func(ctx context.Context) (done bool, err error) {
 | 
			
		||||
			groupList, err := FetchV2Discovery(ctx, client)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
@@ -637,13 +705,36 @@ func WaitForResultWithCondition(ctx context.Context, client testClient, conditio
 | 
			
		||||
		})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func WaitForV2Beta1ResultWithCondition(ctx context.Context, client testClient, condition func(result apidiscoveryv2beta1.APIGroupDiscoveryList) bool) error {
 | 
			
		||||
	// Keep repeatedly fetching document from aggregator.
 | 
			
		||||
	// Check to see if it contains our service within a reasonable amount of time
 | 
			
		||||
	return wait.PollUntilContextTimeout(
 | 
			
		||||
		ctx,
 | 
			
		||||
		250*time.Millisecond,
 | 
			
		||||
		maxTimeout,
 | 
			
		||||
		true,
 | 
			
		||||
		func(ctx context.Context) (done bool, err error) {
 | 
			
		||||
			groupList, err := FetchV2Beta1Discovery(ctx, client)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return false, err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if condition(groupList) {
 | 
			
		||||
				return true, nil
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return false, nil
 | 
			
		||||
		})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func WaitForV1GroupsWithCondition(ctx context.Context, client testClient, condition func(result metav1.APIGroupList) bool) error {
 | 
			
		||||
	// Keep repeatedly fetching document from aggregator.
 | 
			
		||||
	// Check to see if it contains our service within a reasonable amount of time
 | 
			
		||||
	return wait.PollWithContext(
 | 
			
		||||
	return wait.PollUntilContextTimeout(
 | 
			
		||||
		ctx,
 | 
			
		||||
		250*time.Millisecond,
 | 
			
		||||
		maxTimeout,
 | 
			
		||||
		true,
 | 
			
		||||
		func(ctx context.Context) (done bool, err error) {
 | 
			
		||||
			groupList, err := FetchV1DiscoveryGroups(ctx, client)
 | 
			
		||||
 | 
			
		||||
@@ -662,10 +753,11 @@ func WaitForV1GroupsWithCondition(ctx context.Context, client testClient, condit
 | 
			
		||||
func WaitForV1ResourcesWithCondition(ctx context.Context, client testClient, gv metav1.GroupVersion, condition func(result metav1.APIResourceList) bool) error {
 | 
			
		||||
	// Keep repeatedly fetching document from aggregator.
 | 
			
		||||
	// Check to see if it contains our service within a reasonable amount of time
 | 
			
		||||
	return wait.PollWithContext(
 | 
			
		||||
	return wait.PollUntilContextTimeout(
 | 
			
		||||
		ctx,
 | 
			
		||||
		250*time.Millisecond,
 | 
			
		||||
		maxTimeout,
 | 
			
		||||
		true,
 | 
			
		||||
		func(ctx context.Context) (done bool, err error) {
 | 
			
		||||
			resourceList, err := FetchV1DiscoveryResource(ctx, client, gv)
 | 
			
		||||
 | 
			
		||||
@@ -697,7 +789,23 @@ func FindGroupVersionV1(discovery metav1.APIGroupList, gv metav1.GroupVersion) b
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func FindGroupVersionV2(discovery apidiscoveryv2beta1.APIGroupDiscoveryList, gv metav1.GroupVersion) *apidiscoveryv2beta1.APIVersionDiscovery {
 | 
			
		||||
func FindGroupVersionV2(discovery apidiscoveryv2.APIGroupDiscoveryList, gv metav1.GroupVersion) *apidiscoveryv2.APIVersionDiscovery {
 | 
			
		||||
	for _, documentGroup := range discovery.Items {
 | 
			
		||||
		if documentGroup.Name != gv.Group {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, documentVersion := range documentGroup.Versions {
 | 
			
		||||
			if documentVersion.Version == gv.Version {
 | 
			
		||||
				return &documentVersion
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func FindGroupVersionV2Beta1(discovery apidiscoveryv2beta1.APIGroupDiscoveryList, gv metav1.GroupVersion) *apidiscoveryv2beta1.APIVersionDiscovery {
 | 
			
		||||
	for _, documentGroup := range discovery.Items {
 | 
			
		||||
		if documentGroup.Name != gv.Group {
 | 
			
		||||
			continue
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user