New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Surface "stale" GroupVersions from AggregatedDiscovery #116145
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,43 +24,69 @@ import ( | |
"k8s.io/apimachinery/pkg/runtime/schema" | ||
) | ||
|
||
// 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) | ||
} | ||
|
||
// SplitGroupsAndResources transforms "aggregated" discovery top-level structure into | ||
// the previous "unaggregated" discovery groups and resources. | ||
func SplitGroupsAndResources(aggregatedGroups apidiscovery.APIGroupDiscoveryList) (*metav1.APIGroupList, map[schema.GroupVersion]*metav1.APIResourceList) { | ||
func SplitGroupsAndResources(aggregatedGroups apidiscovery.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. | ||
// 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 := convertAPIGroup(aggGroup) | ||
group, resources, failed := convertAPIGroup(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 | ||
return groupList, resourcesByGV, failedGVs | ||
} | ||
|
||
// convertAPIGroup tranforms an "aggregated" APIGroupDiscovery to an "legacy" APIGroup, | ||
// also returning the map of APIResourceList for resources within GroupVersions. | ||
func convertAPIGroup(g apidiscovery.APIGroupDiscovery) (*metav1.APIGroup, map[schema.GroupVersion]*metav1.APIResourceList) { | ||
func convertAPIGroup(g apidiscovery.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 i, v := range g.Versions { | ||
version := metav1.GroupVersionForDiscovery{} | ||
for _, v := range g.Versions { | ||
gv := schema.GroupVersion{Group: g.Name, Version: v.Version} | ||
if v.Freshness == apidiscovery.DiscoveryFreshnessStale { | ||
failedGVs[gv] = StaleGroupVersionError{gv: gv} | ||
continue | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the legacy scenario, we return something in the error There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Addressed. Lots of plumbing complexity. Let me know what you think. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does it make sense to return the resources even if the gv is stale? I noticed that's what we do currently https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/client-go/discovery/discovery_client.go#L531, though not really sure about the use case for it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This question still stands but I don't know what the answer should be. Mostly pertains to aggregated apiservers being unavailable since local apiservers almost always will not encounter this. cc @deads2k There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think unaggregated discovery is returning resources from failed GV's. The resources are only added if they are non-nil here: https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/client-go/discovery/discovery_client.go#L529. But if the GV failed, then the resource list is always nil here: https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/client-go/discovery/discovery_client.go#L345. We can discount the 404, core/v1 toleration here: https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/client-go/discovery/discovery_client.go#L342. The comment here, I believe is just wrong now.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, the resource list will always be nil in https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/client-go/discovery/discovery_client.go#L529, thanks for confirming! Yeah I agree the comment is wrong, we shouldn't ever hit that case. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not having resources for stale discovery information seems valid since otherwise we will not converge with kube-apiservers that have restarted. If we eventually have a persisted set of resources so stale resources would be consistent among all kube-apiservers, I'd be in favor of doing so. |
||
} | ||
version := metav1.GroupVersionForDiscovery{} | ||
version.GroupVersion = gv.String() | ||
version.Version = v.Version | ||
group.Versions = append(group.Versions, version) | ||
if i == 0 { | ||
// PreferredVersion is first non-stale Version | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this true? I'd imagine the preferred gv wouldn't change based on stale and should be based on what was registered with the server? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a good question; I'd like to hear from others. I'm not sure it would be possible to have the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe the current aggregated functionality of not allowing a failed |
||
if group.PreferredVersion == (metav1.GroupVersionForDiscovery{}) { | ||
group.PreferredVersion = version | ||
} | ||
resourceList := &metav1.APIResourceList{} | ||
|
@@ -76,7 +102,7 @@ func convertAPIGroup(g apidiscovery.APIGroupDiscovery) (*metav1.APIGroup, map[sc | |
} | ||
gvResources[gv] = resourceList | ||
} | ||
return group, gvResources | ||
return group, gvResources, failedGVs | ||
} | ||
|
||
// convertAPIResource tranforms a APIResourceDiscovery to an APIResource. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not reuse the existing struct? https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/client-go/discovery/discovery_client.go#L358
I'd imagine we may have clients who already cast to that type already and introducing a new type might be a bit confusing? Logically, stale is equivalent to discovery failed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
ErrGroupDiscoveryFailed
error is supposed to encasulate all the Group/Versions which failed, which is why it stores amap[schema.GroupVersion]error
. So it would be awkward (especially printing the error) if there were a hierarchy of these errors. BTW, theErrGroupDiscoveryFailed
error is what gets returned to the discovery interface user storing the individual errors in the map (as an example, seeServerGroupsAndResources
).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!