Use v2 types with agg discovery

This commit is contained in:
Jefftree
2024-01-19 16:13:47 -05:00
parent 301e804c3f
commit 462dd326c2
30 changed files with 2664 additions and 333 deletions

View File

@@ -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)

View File

@@ -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"},

View File

@@ -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)
}

View 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"

View File

@@ -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))
}
}
}

View File

@@ -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
)

View File

@@ -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"

View File

@@ -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
)

View File

@@ -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,

View File

@@ -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()

View File

@@ -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,

View File

@@ -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)

View File

@@ -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
}

View File

@@ -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}
}

View File

@@ -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",

View File

@@ -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,

View File

@@ -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

View File

@@ -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",
},

View File

@@ -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,
},

View File

@@ -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
}

View File

@@ -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)
}
}

View File

@@ -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)
}))

View File

@@ -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)...)

View File

@@ -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)

View File

@@ -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{
@@ -1780,8 +2171,9 @@ func TestAggregatedServerGroupsAndResources(t *testing.T) {
expectedFailedGVs: []string{"batch/v1"},
},
{
name: "Aggregated discovery: /api returns nothing, 2 groups/2 resources at /apis",
corev1: &apidiscovery.APIGroupDiscoveryList{},
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,61 +2364,84 @@ 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 {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
var agg *apidiscovery.APIGroupDiscoveryList
switch req.URL.Path {
case "/api":
agg = test.corev1
case "/apis":
agg = test.apis
default:
w.WriteHeader(http.StatusNotFound)
return
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":
agg = test.corev1
case "/apis":
agg = test.apis
default:
w.WriteHeader(http.StatusNotFound)
return
}
output, err = json.Marshal(agg)
require.NoError(t, err)
} 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", accept+"; charset=utf-8")
w.WriteHeader(http.StatusOK)
_, err = w.Write(output)
require.NoError(t, err)
}))
defer server.Close()
client := NewDiscoveryClientForConfigOrDie(&restclient.Config{Host: server.URL})
apiGroups, resources, err := client.ServerGroupsAndResources()
if len(test.expectedFailedGVs) > 0 {
require.Error(t, err)
expectedFailedGVs := sets.NewString(test.expectedFailedGVs...)
actualFailedGVs := sets.NewString(failedGroupVersions(err)...)
assert.True(t, expectedFailedGVs.Equal(actualFailedGVs),
"%s: Expected Failed GVs (%s), got (%s)", test.name, expectedFailedGVs, actualFailedGVs)
} else {
require.NoError(t, err)
}
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.WriteHeader(http.StatusOK)
w.Write(output)
}))
defer server.Close()
client := NewDiscoveryClientForConfigOrDie(&restclient.Config{Host: server.URL})
apiGroups, resources, err := client.ServerGroupsAndResources()
if len(test.expectedFailedGVs) > 0 {
require.Error(t, err)
expectedFailedGVs := sets.NewString(test.expectedFailedGVs...)
actualFailedGVs := sets.NewString(failedGroupVersions(err)...)
assert.True(t, expectedFailedGVs.Equal(actualFailedGVs),
"%s: Expected Failed GVs (%s), got (%s)", test.name, expectedFailedGVs, actualFailedGVs)
} else {
require.NoError(t, err)
// Test the expected groups are returned for the aggregated format.
expectedGroupNames := sets.NewString(test.expectedGroupNames...)
actualGroupNames := sets.NewString(groupNames(apiGroups)...)
assert.True(t, expectedGroupNames.Equal(actualGroupNames),
"%s: Expected GVKs (%s), got (%s)", test.name, expectedGroupNames.List(), actualGroupNames.List())
// If the core V1 group is returned from /api, it should be the first group.
if expectedGroupNames.Has("") {
assert.True(t, len(apiGroups) > 0)
actualFirstGroup := apiGroups[0]
assert.True(t, len(actualFirstGroup.Versions) > 0)
actualFirstGroupVersion := actualFirstGroup.Versions[0].GroupVersion
assert.Equal(t, "v1", actualFirstGroupVersion)
}
// Test the expected group/versions are returned from the aggregated discovery.
expectedGroupVersions := sets.NewString(test.expectedGroupVersions...)
actualGroupVersions := sets.NewString(groupVersions(resources)...)
assert.True(t, expectedGroupVersions.Equal(actualGroupVersions),
"%s: Expected GroupVersions(%s), got (%s)", test.name, expectedGroupVersions.List(), actualGroupVersions.List())
// Test the expected GVKs are returned from the aggregated discovery.
expectedGVKs := sets.NewString(test.expectedGVKs...)
actualGVKs := sets.NewString(groupVersionKinds(resources)...)
assert.True(t, expectedGVKs.Equal(actualGVKs),
"%s: Expected GVKs (%s), got (%s)", test.name, expectedGVKs.List(), actualGVKs.List())
}
// Test the expected groups are returned for the aggregated format.
expectedGroupNames := sets.NewString(test.expectedGroupNames...)
actualGroupNames := sets.NewString(groupNames(apiGroups)...)
assert.True(t, expectedGroupNames.Equal(actualGroupNames),
"%s: Expected GVKs (%s), got (%s)", test.name, expectedGroupNames.List(), actualGroupNames.List())
// If the core V1 group is returned from /api, it should be the first group.
if expectedGroupNames.Has("") {
assert.True(t, len(apiGroups) > 0)
actualFirstGroup := apiGroups[0]
assert.True(t, len(actualFirstGroup.Versions) > 0)
actualFirstGroupVersion := actualFirstGroup.Versions[0].GroupVersion
assert.Equal(t, "v1", actualFirstGroupVersion)
}
// Test the expected group/versions are returned from the aggregated discovery.
expectedGroupVersions := sets.NewString(test.expectedGroupVersions...)
actualGroupVersions := sets.NewString(groupVersions(resources)...)
assert.True(t, expectedGroupVersions.Equal(actualGroupVersions),
"%s: Expected GroupVersions(%s), got (%s)", test.name, expectedGroupVersions.List(), actualGroupVersions.List())
// Test the expected GVKs are returned from the aggregated discovery.
expectedGVKs := sets.NewString(test.expectedGVKs...)
actualGVKs := sets.NewString(groupVersionKinds(resources)...)
assert.True(t, expectedGVKs.Equal(actualGVKs),
"%s: Expected GVKs (%s), got (%s)", test.name, expectedGVKs.List(), actualGVKs.List())
}
}
@@ -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,
},

View File

@@ -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)

View File

@@ -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)
}

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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