provide directly decodable versions for storageversion API

This commit is contained in:
David Eads 2021-03-08 09:33:46 -05:00
parent 4fccba9e06
commit fa03dee68c
6 changed files with 77 additions and 18 deletions

View File

@ -67,6 +67,7 @@ func createAPIExtensionsConfig(
// copy the etcd options so we don't mutate originals. // copy the etcd options so we don't mutate originals.
etcdOptions := *commandOptions.Etcd etcdOptions := *commandOptions.Etcd
etcdOptions.StorageConfig.Paging = utilfeature.DefaultFeatureGate.Enabled(features.APIListChunking) etcdOptions.StorageConfig.Paging = utilfeature.DefaultFeatureGate.Enabled(features.APIListChunking)
// this is where the true decodable levels come from.
etcdOptions.StorageConfig.Codec = apiextensionsapiserver.Codecs.LegacyCodec(v1beta1.SchemeGroupVersion, v1.SchemeGroupVersion) etcdOptions.StorageConfig.Codec = apiextensionsapiserver.Codecs.LegacyCodec(v1beta1.SchemeGroupVersion, v1.SchemeGroupVersion)
// prefer the more compact serialization (v1beta1) for storage until http://issue.k8s.io/82292 is resolved for objects whose v1 serialization is too big but whose v1beta1 serialization can be stored // prefer the more compact serialization (v1beta1) for storage until http://issue.k8s.io/82292 is resolved for objects whose v1 serialization is too big but whose v1beta1 serialization can be stored
etcdOptions.StorageConfig.EncodeVersioner = runtime.NewMultiGroupVersioner(v1beta1.SchemeGroupVersion, schema.GroupKind{Group: v1beta1.GroupName}) etcdOptions.StorageConfig.EncodeVersioner = runtime.NewMultiGroupVersioner(v1beta1.SchemeGroupVersion, schema.GroupKind{Group: v1beta1.GroupName})

View File

@ -229,6 +229,32 @@ func (s *Scheme) KnownTypes(gv schema.GroupVersion) map[string]reflect.Type {
return types return types
} }
// VersionsForGroupKind returns the versions that a particular GroupKind can be converted to within the given group.
// A GroupKind might be converted to a different group. That information is available in EquivalentResourceMapper.
func (s *Scheme) VersionsForGroupKind(gk schema.GroupKind) []schema.GroupVersion {
availableVersions := []schema.GroupVersion{}
for gvk := range s.gvkToType {
if gk != gvk.GroupKind() {
continue
}
availableVersions = append(availableVersions, gvk.GroupVersion())
}
// order the return for stability
ret := []schema.GroupVersion{}
for _, version := range s.PrioritizedVersionsForGroup(gk.Group) {
for _, availableVersion := range availableVersions {
if version != availableVersion {
continue
}
ret = append(ret, availableVersion)
}
}
return ret
}
// AllKnownTypes returns the all known types. // AllKnownTypes returns the all known types.
func (s *Scheme) AllKnownTypes() map[schema.GroupVersionKind]reflect.Type { func (s *Scheme) AllKnownTypes() map[schema.GroupVersionKind]reflect.Type {
return s.gvkToType return s.gvkToType

View File

@ -36,6 +36,13 @@ import (
openapiproto "k8s.io/kube-openapi/pkg/util/proto" openapiproto "k8s.io/kube-openapi/pkg/util/proto"
) )
// ConvertabilityChecker indicates what versions a GroupKind is available in.
type ConvertabilityChecker interface {
// VersionsForGroupKind indicates what versions are available to convert a group kind. This determines
// what our decoding abilities are.
VersionsForGroupKind(gk schema.GroupKind) []schema.GroupVersion
}
// APIGroupVersion is a helper for exposing rest.Storage objects as http.Handlers via go-restful // APIGroupVersion is a helper for exposing rest.Storage objects as http.Handlers via go-restful
// It handles URLs of the form: // It handles URLs of the form:
// /${storage_key}[/${object_name}] // /${storage_key}[/${object_name}]
@ -67,13 +74,14 @@ type APIGroupVersion struct {
Serializer runtime.NegotiatedSerializer Serializer runtime.NegotiatedSerializer
ParameterCodec runtime.ParameterCodec ParameterCodec runtime.ParameterCodec
Typer runtime.ObjectTyper Typer runtime.ObjectTyper
Creater runtime.ObjectCreater Creater runtime.ObjectCreater
Convertor runtime.ObjectConvertor Convertor runtime.ObjectConvertor
Defaulter runtime.ObjectDefaulter ConvertabilityChecker ConvertabilityChecker
Linker runtime.SelfLinker Defaulter runtime.ObjectDefaulter
UnsafeConvertor runtime.ObjectConvertor Linker runtime.SelfLinker
TypeConverter fieldmanager.TypeConverter UnsafeConvertor runtime.ObjectConvertor
TypeConverter fieldmanager.TypeConverter
EquivalentResourceRegistry runtime.EquivalentResourceRegistry EquivalentResourceRegistry runtime.EquivalentResourceRegistry

View File

@ -513,6 +513,10 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
decodableVersions := []schema.GroupVersion{}
if a.group.ConvertabilityChecker != nil {
decodableVersions = a.group.ConvertabilityChecker.VersionsForGroupKind(fqKindToRegister.GroupKind())
}
resourceInfo = &storageversion.ResourceInfo{ resourceInfo = &storageversion.ResourceInfo{
GroupResource: schema.GroupResource{ GroupResource: schema.GroupResource{
Group: a.group.GroupVersion.Group, Group: a.group.GroupVersion.Group,
@ -523,6 +527,8 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
// DecodableVersions immediately because API installation must // DecodableVersions immediately because API installation must
// be completed first for us to know equivalent APIs // be completed first for us to know equivalent APIs
EquivalentResourceMapper: a.group.EquivalentResourceRegistry, EquivalentResourceMapper: a.group.EquivalentResourceRegistry,
DirectlyDecodableVersions: decodableVersions,
} }
} }

View File

@ -549,14 +549,15 @@ func (s *GenericAPIServer) newAPIGroupVersion(apiGroupInfo *APIGroupInfo, groupV
GroupVersion: groupVersion, GroupVersion: groupVersion,
MetaGroupVersion: apiGroupInfo.MetaGroupVersion, MetaGroupVersion: apiGroupInfo.MetaGroupVersion,
ParameterCodec: apiGroupInfo.ParameterCodec, ParameterCodec: apiGroupInfo.ParameterCodec,
Serializer: apiGroupInfo.NegotiatedSerializer, Serializer: apiGroupInfo.NegotiatedSerializer,
Creater: apiGroupInfo.Scheme, Creater: apiGroupInfo.Scheme,
Convertor: apiGroupInfo.Scheme, Convertor: apiGroupInfo.Scheme,
UnsafeConvertor: runtime.UnsafeObjectConvertor(apiGroupInfo.Scheme), ConvertabilityChecker: apiGroupInfo.Scheme,
Defaulter: apiGroupInfo.Scheme, UnsafeConvertor: runtime.UnsafeObjectConvertor(apiGroupInfo.Scheme),
Typer: apiGroupInfo.Scheme, Defaulter: apiGroupInfo.Scheme,
Linker: runtime.SelfLinker(meta.NewAccessor()), Typer: apiGroupInfo.Scheme,
Linker: runtime.SelfLinker(meta.NewAccessor()),
EquivalentResourceRegistry: s.EquivalentResourceRegistry, EquivalentResourceRegistry: s.EquivalentResourceRegistry,

View File

@ -40,6 +40,10 @@ type ResourceInfo struct {
// Used to calculate decodable versions. Can only be used after all // Used to calculate decodable versions. Can only be used after all
// equivalent versions are registered by InstallREST. // equivalent versions are registered by InstallREST.
EquivalentResourceMapper runtime.EquivalentResourceRegistry EquivalentResourceMapper runtime.EquivalentResourceRegistry
// DirectlyDecodableVersions is a list of versions that the converter for REST storage knows how to convert. This
// contains items like apiextensions.k8s.io/v1beta1 even if we don't serve that version.
DirectlyDecodableVersions []schema.GroupVersion
} }
// Manager records the resources whose StorageVersions need updates, and provides a method to update those StorageVersions. // Manager records the resources whose StorageVersions need updates, and provides a method to update those StorageVersions.
@ -133,13 +137,13 @@ func (s *defaultManager) UpdateStorageVersions(kubeAPIServerClientConfig *rest.C
// StorageVersion objects have CommonEncodingVersion (each with one server registered). // StorageVersion objects have CommonEncodingVersion (each with one server registered).
sortResourceInfosByGroupResource(resources) sortResourceInfosByGroupResource(resources)
for _, r := range dedupResourceInfos(resources) { for _, r := range dedupResourceInfos(resources) {
dv := decodableVersions(r.EquivalentResourceMapper, r.GroupResource) decodableVersions := decodableVersions(r.DirectlyDecodableVersions, r.EquivalentResourceMapper, r.GroupResource)
gr := r.GroupResource gr := r.GroupResource
// Group must be a valid subdomain in DNS (RFC 1123) // Group must be a valid subdomain in DNS (RFC 1123)
if len(gr.Group) == 0 { if len(gr.Group) == 0 {
gr.Group = "core" gr.Group = "core"
} }
if err := updateStorageVersionFor(sc, serverID, gr, r.EncodingVersion, dv); err != nil { if err := updateStorageVersionFor(sc, serverID, gr, r.EncodingVersion, decodableVersions); err != nil {
utilruntime.HandleError(fmt.Errorf("failed to update storage version for %v: %v", r.GroupResource, err)) utilruntime.HandleError(fmt.Errorf("failed to update storage version for %v: %v", r.GroupResource, err))
s.recordStatusFailure(&r, err) s.recordStatusFailure(&r, err)
hasFailure = true hasFailure = true
@ -267,10 +271,23 @@ func (s *defaultManager) Completed() bool {
return s.completed.Load().(bool) return s.completed.Load().(bool)
} }
func decodableVersions(e runtime.EquivalentResourceRegistry, gr schema.GroupResource) []string { func decodableVersions(directlyDecodableVersions []schema.GroupVersion, e runtime.EquivalentResourceRegistry, gr schema.GroupResource) []string {
var versions []string var versions []string
for _, decodableVersions := range directlyDecodableVersions {
versions = append(versions, decodableVersions.String())
}
decodingGVRs := e.EquivalentResourcesFor(gr.WithVersion(""), "") decodingGVRs := e.EquivalentResourcesFor(gr.WithVersion(""), "")
for _, v := range decodingGVRs { for _, v := range decodingGVRs {
found := false
for _, existingVersion := range versions {
if existingVersion == v.GroupVersion().String() {
found = true
}
}
if found {
continue
}
versions = append(versions, v.GroupVersion().String()) versions = append(versions, v.GroupVersion().String())
} }
return versions return versions