2023-02-01 17:06:36 +00:00
|
|
|
/*
|
|
|
|
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 discovery
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
apidiscovery "k8s.io/api/apidiscovery/v2beta1"
|
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
|
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
|
|
)
|
|
|
|
|
2023-06-01 16:58:10 +00:00
|
|
|
// StaleGroupVersionError encasulates failed GroupVersion marked "stale"
|
|
|
|
// in the returned AggregatedDiscovery format.
|
|
|
|
type StaleGroupVersionError struct {
|
|
|
|
gv schema.GroupVersion
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s StaleGroupVersionError) Error() string {
|
|
|
|
return fmt.Sprintf("stale GroupVersion discovery: %v", s.gv)
|
|
|
|
}
|
|
|
|
|
2023-02-01 17:06:36 +00:00
|
|
|
// SplitGroupsAndResources transforms "aggregated" discovery top-level structure into
|
|
|
|
// the previous "unaggregated" discovery groups and resources.
|
2023-06-01 16:58:10 +00:00
|
|
|
func SplitGroupsAndResources(aggregatedGroups apidiscovery.APIGroupDiscoveryList) (
|
|
|
|
*metav1.APIGroupList,
|
|
|
|
map[schema.GroupVersion]*metav1.APIResourceList,
|
|
|
|
map[schema.GroupVersion]error) {
|
2023-02-01 17:06:36 +00:00
|
|
|
// Aggregated group list will contain the entirety of discovery, including
|
2023-06-01 16:58:10 +00:00
|
|
|
// groups, versions, and resources. GroupVersions marked "stale" are failed.
|
2023-02-01 17:06:36 +00:00
|
|
|
groups := []*metav1.APIGroup{}
|
2023-06-01 16:58:10 +00:00
|
|
|
failedGVs := map[schema.GroupVersion]error{}
|
2023-02-01 17:06:36 +00:00
|
|
|
resourcesByGV := map[schema.GroupVersion]*metav1.APIResourceList{}
|
|
|
|
for _, aggGroup := range aggregatedGroups.Items {
|
2023-06-01 16:58:10 +00:00
|
|
|
group, resources, failed := convertAPIGroup(aggGroup)
|
2023-02-01 17:06:36 +00:00
|
|
|
groups = append(groups, group)
|
|
|
|
for gv, resourceList := range resources {
|
|
|
|
resourcesByGV[gv] = resourceList
|
|
|
|
}
|
2023-06-01 16:58:10 +00:00
|
|
|
for gv, err := range failed {
|
|
|
|
failedGVs[gv] = err
|
|
|
|
}
|
2023-02-01 17:06:36 +00:00
|
|
|
}
|
|
|
|
// 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)
|
|
|
|
}
|
2023-06-01 16:58:10 +00:00
|
|
|
return groupList, resourcesByGV, failedGVs
|
2023-02-01 17:06:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// convertAPIGroup tranforms an "aggregated" APIGroupDiscovery to an "legacy" APIGroup,
|
|
|
|
// also returning the map of APIResourceList for resources within GroupVersions.
|
2023-06-01 16:58:10 +00:00
|
|
|
func convertAPIGroup(g apidiscovery.APIGroupDiscovery) (
|
|
|
|
*metav1.APIGroup,
|
|
|
|
map[schema.GroupVersion]*metav1.APIResourceList,
|
|
|
|
map[schema.GroupVersion]error) {
|
2023-02-01 17:06:36 +00:00
|
|
|
// Iterate through versions to convert to group and resources.
|
|
|
|
group := &metav1.APIGroup{}
|
|
|
|
gvResources := map[schema.GroupVersion]*metav1.APIResourceList{}
|
2023-06-01 16:58:10 +00:00
|
|
|
failedGVs := map[schema.GroupVersion]error{}
|
2023-02-01 17:06:36 +00:00
|
|
|
group.Name = g.ObjectMeta.Name
|
2023-06-01 16:58:10 +00:00
|
|
|
for _, v := range g.Versions {
|
2023-02-01 17:06:36 +00:00
|
|
|
gv := schema.GroupVersion{Group: g.Name, Version: v.Version}
|
2023-06-01 16:58:10 +00:00
|
|
|
if v.Freshness == apidiscovery.DiscoveryFreshnessStale {
|
|
|
|
failedGVs[gv] = StaleGroupVersionError{gv: gv}
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
version := metav1.GroupVersionForDiscovery{}
|
2023-02-01 17:06:36 +00:00
|
|
|
version.GroupVersion = gv.String()
|
|
|
|
version.Version = v.Version
|
|
|
|
group.Versions = append(group.Versions, version)
|
2023-06-01 16:58:10 +00:00
|
|
|
// PreferredVersion is first non-stale Version
|
|
|
|
if group.PreferredVersion == (metav1.GroupVersionForDiscovery{}) {
|
2023-02-01 17:06:36 +00:00
|
|
|
group.PreferredVersion = version
|
|
|
|
}
|
|
|
|
resourceList := &metav1.APIResourceList{}
|
|
|
|
resourceList.GroupVersion = gv.String()
|
|
|
|
for _, r := range v.Resources {
|
2023-06-01 16:58:10 +00:00
|
|
|
resource, err := convertAPIResource(r)
|
|
|
|
if err == nil {
|
|
|
|
resourceList.APIResources = append(resourceList.APIResources, resource)
|
|
|
|
}
|
2023-02-01 17:06:36 +00:00
|
|
|
// Subresources field in new format get transformed into full APIResources.
|
2023-06-01 16:58:10 +00:00
|
|
|
// It is possible a partial result with an error was returned to be used
|
|
|
|
// as the parent resource for the subresource.
|
2023-02-01 17:06:36 +00:00
|
|
|
for _, subresource := range r.Subresources {
|
2023-06-01 16:58:10 +00:00
|
|
|
sr, err := convertAPISubresource(resource, subresource)
|
|
|
|
if err == nil {
|
|
|
|
resourceList.APIResources = append(resourceList.APIResources, sr)
|
|
|
|
}
|
2023-02-01 17:06:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
gvResources[gv] = resourceList
|
|
|
|
}
|
2023-06-01 16:58:10 +00:00
|
|
|
return group, gvResources, failedGVs
|
2023-02-01 17:06:36 +00:00
|
|
|
}
|
|
|
|
|
2023-08-17 05:15:28 +00:00
|
|
|
var emptyKind = metav1.GroupVersionKind{}
|
|
|
|
|
2023-06-01 16:58:10 +00:00
|
|
|
// 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 convertAPIResource(in apidiscovery.APIResourceDiscovery) (metav1.APIResource, error) {
|
|
|
|
result := metav1.APIResource{
|
2023-02-01 17:06:36 +00:00
|
|
|
Name: in.Resource,
|
|
|
|
SingularName: in.SingularResource,
|
|
|
|
Namespaced: in.Scope == apidiscovery.ScopeNamespace,
|
|
|
|
Verbs: in.Verbs,
|
|
|
|
ShortNames: in.ShortNames,
|
|
|
|
Categories: in.Categories,
|
|
|
|
}
|
2023-06-01 16:58:10 +00:00
|
|
|
var err error
|
2023-08-17 05:15:28 +00:00
|
|
|
if in.ResponseKind != nil && (*in.ResponseKind) != emptyKind {
|
2023-06-01 16:58:10 +00:00
|
|
|
result.Group = in.ResponseKind.Group
|
|
|
|
result.Version = in.ResponseKind.Version
|
|
|
|
result.Kind = in.ResponseKind.Kind
|
|
|
|
} else {
|
|
|
|
err = fmt.Errorf("discovery resource %s missing GVK", in.Resource)
|
|
|
|
}
|
|
|
|
// Can return partial result with error, which can be the parent for a
|
|
|
|
// subresource. Do not add this result to the returned discovery resources.
|
|
|
|
return result, err
|
2023-02-01 17:06:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// convertAPISubresource tranforms a APISubresourceDiscovery to an APIResource.
|
2023-06-01 16:58:10 +00:00
|
|
|
func convertAPISubresource(parent metav1.APIResource, in apidiscovery.APISubresourceDiscovery) (metav1.APIResource, error) {
|
|
|
|
result := metav1.APIResource{}
|
2023-08-17 05:15:28 +00:00
|
|
|
if in.ResponseKind == nil || (*in.ResponseKind) == emptyKind {
|
2023-06-01 16:58:10 +00:00
|
|
|
return result, fmt.Errorf("subresource %s/%s missing GVK", parent.Name, in.Subresource)
|
2023-02-01 17:06:36 +00:00
|
|
|
}
|
2023-06-01 16:58:10 +00:00
|
|
|
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
|
2023-02-01 17:06:36 +00:00
|
|
|
}
|