Repository: kubernetes/kubernetes
File: staging/src/k8s.io/client-go/discovery/discovery_client.go (Go)

Code before:
const (
	// defaultRetries is the number of times a resource discovery is repeated if an api group disappears on the fly (e.g. CustomResourceDefinitions).
	defaultRetries = 2
	// protobuf mime type
	openAPIV2mimePb = "application/com.github.proto-openapi.spec.v2@v1.0+protobuf"

	// defaultTimeout is the maximum amount of time per request when no timeout has been set on a RESTClient.
	// Defaults to 32s in order to have a distinguishable length of time, relative to other timeouts that exist.
	defaultTimeout = 32 * time.Second

	// defaultBurst is the default burst to be used with the discovery client's token bucket rate limiter
	defaultBurst = 300

	AcceptV1 = runtime.ContentTypeJSON
	// Aggregated discovery content-type (v2beta1). NOTE: content-type parameters
	// 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 = AcceptV2 + "," + AcceptV2Beta1 + "," + AcceptV1
)

// Aggregated discovery content-type GVK.
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.
type DiscoveryInterface interface {
	RESTClient() restclient.Interface
	ServerGroupsInterface
	ServerResourcesInterface
	ServerVersionInterface
	OpenAPISchemaInterface
	OpenAPIV3SchemaInterface
	// Returns copy of current discovery client that will only
	// receive the legacy discovery format, or pointer to current
	// discovery client if it does not support legacy-only discovery.
	WithLegacy() DiscoveryInterface
}

// AggregatedDiscoveryInterface extends DiscoveryInterface to include a method to possibly
// return discovery resources along with the discovery groups, which is what the newer
// aggregated discovery format does (APIGroupDiscoveryList).
type AggregatedDiscoveryInterface interface {
	DiscoveryInterface

	GroupsAndMaybeResources() (*metav1.APIGroupList, map[schema.GroupVersion]*metav1.APIResourceList, map[schema.GroupVersion]error, error)
}

// CachedDiscoveryInterface is a DiscoveryInterface with cache invalidation and freshness.

Review comment (performance):
While I explicitly focused my previous comments on "what if old clients become incompatible with new servers and cost us a lot of CPU and latency", the same applies in reverse "what if new clients become incompatible with old servers and cost us a lot of CPU and latency".  This is especially true when we try to tell projects how to handle the incompatibility server-side.

Take the Gateway API project which guarantees a skew of the five latest kube releases (reference: https://gateway-api.sigs.k8s.io/concepts/versioning/#supported-versions ).  I see these choices

1. We make the server stop serving v2beta1.  We make the client support v2beta1.  We tell gateway API to update to use the latest client and they can keep their supported skew efficiently.
2. We make the server continue serving v2beta1 and gateway API (and other clients) can keep using the old client.
3. We eat a discovery cost for at least some portion of clients that use the old serialization.

I like option 1 best since it is fully under our control and prevents more clients from growing a dependency on v2beta1.

cc @jpbetz 

Code after:

const (
	// defaultRetries is the number of times a resource discovery is repeated if an api group disappears on the fly (e.g. CustomResourceDefinitions).
	defaultRetries = 2
	// protobuf mime type
	openAPIV2mimePb = "application/com.github.proto-openapi.spec.v2@v1.0+protobuf"

	// defaultTimeout is the maximum amount of time per request when no timeout has been set on a RESTClient.
	// Defaults to 32s in order to have a distinguishable length of time, relative to other timeouts that exist.
	defaultTimeout = 32 * time.Second

	// defaultBurst is the default burst to be used with the discovery client's token bucket rate limiter
	defaultBurst = 300

	AcceptV1 = runtime.ContentTypeJSON
	// Aggregated discovery content-type (v2beta1). NOTE: content-type parameters
	// 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 = 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.
type DiscoveryInterface interface {
	RESTClient() restclient.Interface
	ServerGroupsInterface
	ServerResourcesInterface
	ServerVersionInterface
	OpenAPISchemaInterface
	OpenAPIV3SchemaInterface
	// Returns copy of current discovery client that will only
	// receive the legacy discovery format, or pointer to current
	// discovery client if it does not support legacy-only discovery.
	WithLegacy() DiscoveryInterface
}

// AggregatedDiscoveryInterface extends DiscoveryInterface to include a method to possibly
// return discovery resources along with the discovery groups, which is what the newer
// aggregated discovery format does (APIGroupDiscoveryList).
type AggregatedDiscoveryInterface interface {
	DiscoveryInterface

	GroupsAndMaybeResources() (*metav1.APIGroupList, map[schema.GroupVersion]*metav1.APIResourceList, map[schema.GroupVersion]error, error)
}