k8s.io/apiserver@v0.31.1/pkg/server/storage/resource_encoding_config.go (about) 1 /* 2 Copyright 2016 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package storage 18 19 import ( 20 "fmt" 21 22 "k8s.io/apimachinery/pkg/runtime" 23 "k8s.io/apimachinery/pkg/runtime/schema" 24 apimachineryversion "k8s.io/apimachinery/pkg/util/version" 25 "k8s.io/apiserver/pkg/util/version" 26 ) 27 28 type ResourceEncodingConfig interface { 29 // StorageEncoding returns the serialization format for the resource. 30 // TODO this should actually return a GroupVersionKind since you can logically have multiple "matching" Kinds 31 // For now, it returns just the GroupVersion for consistency with old behavior 32 StorageEncodingFor(schema.GroupResource) (schema.GroupVersion, error) 33 34 // InMemoryEncodingFor returns the groupVersion for the in memory representation the storage should convert to. 35 InMemoryEncodingFor(schema.GroupResource) (schema.GroupVersion, error) 36 } 37 38 type CompatibilityResourceEncodingConfig interface { 39 BackwardCompatibileStorageEncodingFor(schema.GroupResource, runtime.Object) (schema.GroupVersion, error) 40 } 41 42 type DefaultResourceEncodingConfig struct { 43 // resources records the overriding encoding configs for individual resources. 44 resources map[schema.GroupResource]*OverridingResourceEncoding 45 scheme *runtime.Scheme 46 effectiveVersion version.EffectiveVersion 47 } 48 49 type OverridingResourceEncoding struct { 50 ExternalResourceEncoding schema.GroupVersion 51 InternalResourceEncoding schema.GroupVersion 52 } 53 54 var _ ResourceEncodingConfig = &DefaultResourceEncodingConfig{} 55 56 func NewDefaultResourceEncodingConfig(scheme *runtime.Scheme) *DefaultResourceEncodingConfig { 57 return &DefaultResourceEncodingConfig{resources: map[schema.GroupResource]*OverridingResourceEncoding{}, scheme: scheme, effectiveVersion: version.DefaultKubeEffectiveVersion()} 58 } 59 60 func (o *DefaultResourceEncodingConfig) SetResourceEncoding(resourceBeingStored schema.GroupResource, externalEncodingVersion, internalVersion schema.GroupVersion) { 61 o.resources[resourceBeingStored] = &OverridingResourceEncoding{ 62 ExternalResourceEncoding: externalEncodingVersion, 63 InternalResourceEncoding: internalVersion, 64 } 65 } 66 67 func (o *DefaultResourceEncodingConfig) SetEffectiveVersion(effectiveVersion version.EffectiveVersion) { 68 o.effectiveVersion = effectiveVersion 69 } 70 71 func (o *DefaultResourceEncodingConfig) StorageEncodingFor(resource schema.GroupResource) (schema.GroupVersion, error) { 72 if !o.scheme.IsGroupRegistered(resource.Group) { 73 return schema.GroupVersion{}, fmt.Errorf("group %q is not registered in scheme", resource.Group) 74 } 75 76 resourceOverride, resourceExists := o.resources[resource] 77 if resourceExists { 78 return resourceOverride.ExternalResourceEncoding, nil 79 } 80 81 // return the most preferred external version for the group 82 return o.scheme.PrioritizedVersionsForGroup(resource.Group)[0], nil 83 } 84 85 func (o *DefaultResourceEncodingConfig) BackwardCompatibileStorageEncodingFor(resource schema.GroupResource, example runtime.Object) (schema.GroupVersion, error) { 86 if !o.scheme.IsGroupRegistered(resource.Group) { 87 return schema.GroupVersion{}, fmt.Errorf("group %q is not registered in scheme", resource.Group) 88 } 89 90 // Always respect overrides 91 resourceOverride, resourceExists := o.resources[resource] 92 if resourceExists { 93 return resourceOverride.ExternalResourceEncoding, nil 94 } 95 96 return emulatedStorageVersion( 97 o.scheme.PrioritizedVersionsForGroup(resource.Group)[0], 98 example, 99 o.effectiveVersion, 100 o.scheme) 101 } 102 103 func (o *DefaultResourceEncodingConfig) InMemoryEncodingFor(resource schema.GroupResource) (schema.GroupVersion, error) { 104 if !o.scheme.IsGroupRegistered(resource.Group) { 105 return schema.GroupVersion{}, fmt.Errorf("group %q is not registered in scheme", resource.Group) 106 } 107 108 resourceOverride, resourceExists := o.resources[resource] 109 if resourceExists { 110 return resourceOverride.InternalResourceEncoding, nil 111 } 112 return schema.GroupVersion{Group: resource.Group, Version: runtime.APIVersionInternal}, nil 113 } 114 115 // Object interface generated from "k8s:prerelease-lifecycle-gen:introduced" tags in types.go. 116 type introducedInterface interface { 117 APILifecycleIntroduced() (major, minor int) 118 } 119 120 func emulatedStorageVersion(binaryVersionOfResource schema.GroupVersion, example runtime.Object, effectiveVersion version.EffectiveVersion, scheme *runtime.Scheme) (schema.GroupVersion, error) { 121 if example == nil || effectiveVersion == nil { 122 return binaryVersionOfResource, nil 123 } 124 125 // Look up example in scheme to find all objects of the same Group-Kind 126 // Use the highest priority version for that group-kind whose lifecycle window 127 // includes the current emulation version. 128 // If no version is found, use the binary version 129 // (in this case the API should be disabled anyway) 130 gvks, _, err := scheme.ObjectKinds(example) 131 if err != nil { 132 return schema.GroupVersion{}, err 133 } else if len(gvks) == 0 { 134 // Probably shouldn't happen if err is non-nil 135 return schema.GroupVersion{}, fmt.Errorf("object %T has no GVKs registered in scheme", example) 136 } 137 138 // VersionsForGroupKind returns versions in priority order 139 versions := scheme.VersionsForGroupKind(schema.GroupKind{Group: gvks[0].Group, Kind: gvks[0].Kind}) 140 141 compatibilityVersion := effectiveVersion.MinCompatibilityVersion() 142 143 for _, gv := range versions { 144 if gv.Version == runtime.APIVersionInternal { 145 continue 146 } 147 148 gvk := schema.GroupVersionKind{ 149 Group: gv.Group, 150 Version: gv.Version, 151 Kind: gvks[0].Kind, 152 } 153 154 exampleOfGVK, err := scheme.New(gvk) 155 if err != nil { 156 return schema.GroupVersion{}, err 157 } 158 159 // If it was introduced after current compatibility version, don't use it 160 // skip the introduced check for test when currentVersion is 0.0 to test all apis 161 if introduced, hasIntroduced := exampleOfGVK.(introducedInterface); hasIntroduced && (compatibilityVersion.Major() > 0 || compatibilityVersion.Minor() > 0) { 162 // API resource lifecycles should be relative to k8s api version 163 majorIntroduced, minorIntroduced := introduced.APILifecycleIntroduced() 164 introducedVer := apimachineryversion.MajorMinor(uint(majorIntroduced), uint(minorIntroduced)) 165 if introducedVer.GreaterThan(compatibilityVersion) { 166 continue 167 } 168 } 169 170 // versions is returned in priority order, so just use first result 171 return gvk.GroupVersion(), nil 172 } 173 174 // Getting here means we're serving a version that is unknown to the 175 // min-compatibility-version server. 176 // 177 // This is only expected to happen when serving an alpha API type due 178 // to missing pre-release lifecycle information 179 // (which doesn't happen by default), or when emulation-version and 180 // min-compatibility-version are several versions apart so a beta or GA API 181 // was being served which didn't exist at all in min-compatibility-version. 182 // 183 // In the alpha case - we do not support compatibility versioning of 184 // alpha types and recommend users do not mix the two. 185 // In the skip-level case - The version of apiserver we are retaining 186 // compatibility with has no knowledge of the type, 187 // so storing it in another type is no issue. 188 return binaryVersionOfResource, nil 189 }