@@ -19,6 +19,7 @@ package server
19
19
import (
20
20
"fmt"
21
21
"os"
22
+ "regexp"
22
23
"strconv"
23
24
"strings"
24
25
@@ -31,10 +32,13 @@ import (
31
32
"k8s.io/klog/v2"
32
33
)
33
34
35
+ var alphaPattern = regexp .MustCompile (`^v\d+alpha\d+$` )
36
+
34
37
// resourceExpirationEvaluator holds info for deciding if a particular rest.Storage needs to excluded from the API
35
38
type resourceExpirationEvaluator struct {
36
- currentVersion * apimachineryversion.Version
37
- isAlpha bool
39
+ currentVersion * apimachineryversion.Version
40
+ emulationForwardCompatible bool
41
+ isAlpha bool
38
42
// Special flag checking for the existence of alpha.0
39
43
// alpha.0 is a special case where everything merged to master is auto propagated to the release-1.n branch
40
44
isAlphaZero bool
@@ -53,17 +57,21 @@ type ResourceExpirationEvaluator interface {
53
57
// RemoveDeletedKinds inspects the storage map and modifies it in place by removing storage for kinds that have been deleted.
54
58
// versionedResourcesStorageMap mirrors the field on APIGroupInfo, it's a map from version to resource to the storage.
55
59
RemoveDeletedKinds (groupName string , versioner runtime.ObjectVersioner , versionedResourcesStorageMap map [string ]map [string ]rest.Storage )
60
+ // RemoveUnIntroducedKinds inspects the storage map and modifies it in place by removing storage for kinds that are introduced after the current version.
61
+ // versionedResourcesStorageMap mirrors the field on APIGroupInfo, it's a map from version to resource to the storage.
62
+ RemoveUnIntroducedKinds (groupName string , versioner runtime.ObjectVersioner , versionedResourcesStorageMap map [string ]map [string ]rest.Storage )
56
63
// ShouldServeForVersion returns true if a particular version cut off is after the current version
57
64
ShouldServeForVersion (majorRemoved , minorRemoved int ) bool
58
65
}
59
66
60
- func NewResourceExpirationEvaluator (currentVersion * apimachineryversion.Version ) (ResourceExpirationEvaluator , error ) {
67
+ func NewResourceExpirationEvaluator (currentVersion * apimachineryversion.Version , emulationForwardCompatible bool ) (ResourceExpirationEvaluator , error ) {
61
68
if currentVersion == nil {
62
69
return nil , fmt .Errorf ("empty NewResourceExpirationEvaluator currentVersion" )
63
70
}
64
71
klog .V (1 ).Infof ("NewResourceExpirationEvaluator with currentVersion: %s." , currentVersion )
65
72
ret := & resourceExpirationEvaluator {
66
73
strictRemovedHandlingInAlpha : false ,
74
+ emulationForwardCompatible : emulationForwardCompatible ,
67
75
}
68
76
// Only keeps the major and minor versions from input version.
69
77
ret .currentVersion = apimachineryversion .MajorMinor (currentVersion .Major (), currentVersion .Minor ())
@@ -89,7 +97,7 @@ func NewResourceExpirationEvaluator(currentVersion *apimachineryversion.Version)
89
97
return ret , nil
90
98
}
91
99
92
- func (e * resourceExpirationEvaluator ) shouldServe (gv schema.GroupVersion , versioner runtime.ObjectVersioner , resourceServingInfo rest.Storage ) bool {
100
+ func (e * resourceExpirationEvaluator ) isNotRemoved (gv schema.GroupVersion , versioner runtime.ObjectVersioner , resourceServingInfo rest.Storage ) bool {
93
101
internalPtr := resourceServingInfo .New ()
94
102
95
103
target := gv
@@ -104,15 +112,6 @@ func (e *resourceExpirationEvaluator) shouldServe(gv schema.GroupVersion, versio
104
112
return false
105
113
}
106
114
107
- introduced , ok := versionedPtr .(introducedInterface )
108
- if ok {
109
- majorIntroduced , minorIntroduced := introduced .APILifecycleIntroduced ()
110
- verIntroduced := apimachineryversion .MajorMinor (uint (majorIntroduced ), uint (minorIntroduced ))
111
- if e .currentVersion .LessThan (verIntroduced ) {
112
- return false
113
- }
114
- }
115
-
116
115
removed , ok := versionedPtr .(removedInterface )
117
116
if ! ok {
118
117
return true
@@ -153,15 +152,15 @@ type introducedInterface interface {
153
152
APILifecycleIntroduced () (major , minor int )
154
153
}
155
154
156
- // removeDeletedKinds inspects the storage map and modifies it in place by removing storage for kinds that have been deleted.
155
+ // RemoveDeletedKinds inspects the storage map and modifies it in place by removing storage for kinds that have been deleted.
157
156
// versionedResourcesStorageMap mirrors the field on APIGroupInfo, it's a map from version to resource to the storage.
158
157
func (e * resourceExpirationEvaluator ) RemoveDeletedKinds (groupName string , versioner runtime.ObjectVersioner , versionedResourcesStorageMap map [string ]map [string ]rest.Storage ) {
159
158
versionsToRemove := sets .NewString ()
160
159
for apiVersion := range sets .StringKeySet (versionedResourcesStorageMap ) {
161
160
versionToResource := versionedResourcesStorageMap [apiVersion ]
162
161
resourcesToRemove := sets .NewString ()
163
162
for resourceName , resourceServingInfo := range versionToResource {
164
- if ! e .shouldServe (schema.GroupVersion {Group : groupName , Version : apiVersion }, versioner , resourceServingInfo ) {
163
+ if ! e .isNotRemoved (schema.GroupVersion {Group : groupName , Version : apiVersion }, versioner , resourceServingInfo ) {
165
164
resourcesToRemove .Insert (resourceName )
166
165
}
167
166
}
@@ -189,6 +188,82 @@ func (e *resourceExpirationEvaluator) RemoveDeletedKinds(groupName string, versi
189
188
}
190
189
}
191
190
191
+ // RemoveUnIntroducedKinds inspects the storage map and modifies it in place by removing storage for kinds that are introduced after the current version.
192
+ // versionedResourcesStorageMap mirrors the field on APIGroupInfo, it's a map from version to resource to the storage.
193
+ func (e * resourceExpirationEvaluator ) RemoveUnIntroducedKinds (groupName string , versioner runtime.ObjectVersioner , versionedResourcesStorageMap map [string ]map [string ]rest.Storage ) {
194
+ versionsToRemove := sets .NewString ()
195
+ prioritizedVersions := versioner .PrioritizedVersionsForGroup (groupName )
196
+ enabledResources := sets .NewString ()
197
+
198
+ // iterate from the end to the front, so that we remove the older versions first.
199
+ for i := len (prioritizedVersions ) - 1 ; i >= 0 ; i -- {
200
+ apiVersion := prioritizedVersions [i ].Version
201
+ versionToResource := versionedResourcesStorageMap [apiVersion ]
202
+ resourcesToRemove := sets .NewString ()
203
+ for resourceName , resourceServingInfo := range versionToResource {
204
+ // if an earlier version of the resource has been enabled, the same resource with higher priority
205
+ // should also be enabled if emulationForwardCompatible.
206
+ if e .emulationForwardCompatible && enabledResources .Has (resourceName ) {
207
+ continue
208
+ }
209
+ verIntroduced := versionIntroduced (schema.GroupVersion {Group : groupName , Version : apiVersion }, versioner , resourceServingInfo )
210
+ if e .currentVersion .LessThan (verIntroduced ) {
211
+ resourcesToRemove .Insert (resourceName )
212
+ } else {
213
+ // emulation forward compatibility is not applicable to alpha apis.
214
+ if ! alphaPattern .MatchString (apiVersion ) {
215
+ enabledResources .Insert (resourceName )
216
+ }
217
+ }
218
+ }
219
+
220
+ for resourceName := range versionedResourcesStorageMap [apiVersion ] {
221
+ if ! shouldRemoveResourceAndSubresources (resourcesToRemove , resourceName ) {
222
+ continue
223
+ }
224
+
225
+ klog .V (1 ).Infof ("Removing resource %v.%v.%v because it is introduced after the current version %s per APILifecycle." , resourceName , apiVersion , groupName , e .currentVersion .String ())
226
+ storage := versionToResource [resourceName ]
227
+ storage .Destroy ()
228
+ delete (versionToResource , resourceName )
229
+ }
230
+ versionedResourcesStorageMap [apiVersion ] = versionToResource
231
+
232
+ if len (versionedResourcesStorageMap [apiVersion ]) == 0 {
233
+ versionsToRemove .Insert (apiVersion )
234
+ }
235
+ }
236
+
237
+ for _ , apiVersion := range versionsToRemove .List () {
238
+ klog .V (1 ).Infof ("Removing version %v.%v because it is introduced after the current version %s and because it has no resources per APILifecycle." , apiVersion , groupName , e .currentVersion .String ())
239
+ delete (versionedResourcesStorageMap , apiVersion )
240
+ }
241
+ }
242
+
243
+ func versionIntroduced (gv schema.GroupVersion , versioner runtime.ObjectVersioner , resourceServingInfo rest.Storage ) * apimachineryversion.Version {
244
+ defaultVer := apimachineryversion .MajorMinor (0 , 0 )
245
+ internalPtr := resourceServingInfo .New ()
246
+
247
+ target := gv
248
+ // honor storage that overrides group version (used for things like scale subresources)
249
+ if versionProvider , ok := resourceServingInfo .(rest.GroupVersionKindProvider ); ok {
250
+ target = versionProvider .GroupVersionKind (target ).GroupVersion ()
251
+ }
252
+
253
+ versionedPtr , err := versioner .ConvertToVersion (internalPtr , target )
254
+ if err != nil {
255
+ utilruntime .HandleError (err )
256
+ return defaultVer
257
+ }
258
+
259
+ introduced , ok := versionedPtr .(introducedInterface )
260
+ if ok {
261
+ majorIntroduced , minorIntroduced := introduced .APILifecycleIntroduced ()
262
+ return apimachineryversion .MajorMinor (uint (majorIntroduced ), uint (minorIntroduced ))
263
+ }
264
+ return defaultVer
265
+ }
266
+
192
267
func shouldRemoveResourceAndSubresources (resourcesToRemove sets.String , resourceName string ) bool {
193
268
for _ , resourceToRemove := range resourcesToRemove .List () {
194
269
if resourceName == resourceToRemove {
0 commit comments