1586 lines
		
	
	
		
			48 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			1586 lines
		
	
	
		
			48 KiB
		
	
	
	
		
			Go
		
	
	
	
| /*
 | |
| Copyright 2020 The Kubernetes Authors.
 | |
| 
 | |
| Licensed under the Apache License, Version 2.0 (the "License");
 | |
| you may not use this file except in compliance with the License.
 | |
| You may obtain a copy of the License at
 | |
| 
 | |
|     http://www.apache.org/licenses/LICENSE-2.0
 | |
| 
 | |
| Unless required by applicable law or agreed to in writing, software
 | |
| distributed under the License is distributed on an "AS IS" BASIS,
 | |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
| See the License for the specific language governing permissions and
 | |
| limitations under the License.
 | |
| */
 | |
| 
 | |
| package server
 | |
| 
 | |
| import (
 | |
| 	"reflect"
 | |
| 	"strings"
 | |
| 	"testing"
 | |
| 
 | |
| 	"k8s.io/apimachinery/pkg/runtime"
 | |
| 	"k8s.io/apimachinery/pkg/runtime/schema"
 | |
| 	"k8s.io/apimachinery/pkg/util/dump"
 | |
| 	"k8s.io/apimachinery/pkg/util/sets"
 | |
| 	apimachineryversion "k8s.io/apimachinery/pkg/util/version"
 | |
| 	"k8s.io/apiserver/pkg/registry/rest"
 | |
| 	"k8s.io/apiserver/pkg/server/resourceconfig"
 | |
| 	serverstorage "k8s.io/apiserver/pkg/server/storage"
 | |
| 
 | |
| 	"github.com/stretchr/testify/require"
 | |
| )
 | |
| 
 | |
| func Test_newResourceExpirationEvaluator(t *testing.T) {
 | |
| 	tests := []struct {
 | |
| 		name           string
 | |
| 		currentVersion string
 | |
| 		expected       resourceExpirationEvaluator
 | |
| 		expectedErr    string
 | |
| 	}{
 | |
| 		{
 | |
| 			name:           "beta",
 | |
| 			currentVersion: "v1.20.0-beta.0.62+a5d22854a2ac21",
 | |
| 			expected:       resourceExpirationEvaluator{currentVersion: apimachineryversion.MajorMinor(1, 20)},
 | |
| 		},
 | |
| 		{
 | |
| 			name:           "alpha .0",
 | |
| 			currentVersion: "v1.20.0-alpha.0.62+a5d22854a2ac21",
 | |
| 			expected:       resourceExpirationEvaluator{currentVersion: apimachineryversion.MajorMinor(1, 20), isAlpha: true, isAlphaZero: true},
 | |
| 		},
 | |
| 		{
 | |
| 			name:           "alpha not .0",
 | |
| 			currentVersion: "v1.20.0-alpha.1.62+a5d22854a2ac21",
 | |
| 			expected:       resourceExpirationEvaluator{currentVersion: apimachineryversion.MajorMinor(1, 20), isAlpha: true, isAlphaZero: false},
 | |
| 		},
 | |
| 
 | |
| 		{
 | |
| 			name:           "maintenance",
 | |
| 			currentVersion: "v1.20.1",
 | |
| 			expected:       resourceExpirationEvaluator{currentVersion: apimachineryversion.MajorMinor(1, 20)},
 | |
| 		},
 | |
| 		{
 | |
| 			name:           "no v prefix",
 | |
| 			currentVersion: "1.20.1",
 | |
| 			expected:       resourceExpirationEvaluator{currentVersion: apimachineryversion.MajorMinor(1, 20)},
 | |
| 		},
 | |
| 	}
 | |
| 	for _, tt := range tests {
 | |
| 		t.Run(tt.name, func(t *testing.T) {
 | |
| 			actual, actualErr := NewResourceExpirationEvaluator(apimachineryversion.MustParse(tt.currentVersion))
 | |
| 
 | |
| 			checkErr(t, actualErr, tt.expectedErr)
 | |
| 			if actualErr != nil {
 | |
| 				return
 | |
| 			}
 | |
| 
 | |
| 			actual.(*resourceExpirationEvaluator).strictRemovedHandlingInAlpha = false
 | |
| 			if !reflect.DeepEqual(tt.expected, *actual.(*resourceExpirationEvaluator)) {
 | |
| 				t.Fatal(actual)
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| type defaultObj struct {
 | |
| }
 | |
| 
 | |
| func (r *defaultObj) GetObjectKind() schema.ObjectKind {
 | |
| 	panic("don't do this")
 | |
| }
 | |
| func (r *defaultObj) DeepCopyObject() runtime.Object {
 | |
| 	panic("don't do this either")
 | |
| }
 | |
| 
 | |
| type removedInObj struct {
 | |
| 	major, minor int
 | |
| }
 | |
| 
 | |
| func (r *removedInObj) GetObjectKind() schema.ObjectKind {
 | |
| 	panic("don't do this")
 | |
| }
 | |
| func (r *removedInObj) DeepCopyObject() runtime.Object {
 | |
| 	panic("don't do this either")
 | |
| }
 | |
| func (r *removedInObj) APILifecycleRemoved() (major, minor int) {
 | |
| 	return r.major, r.minor
 | |
| }
 | |
| 
 | |
| type IntroducedInObj struct {
 | |
| 	major, minor int
 | |
| }
 | |
| 
 | |
| func (r *IntroducedInObj) GetObjectKind() schema.ObjectKind {
 | |
| 	panic("don't do this")
 | |
| }
 | |
| func (r *IntroducedInObj) DeepCopyObject() runtime.Object {
 | |
| 	panic("don't do this either")
 | |
| }
 | |
| func (r *IntroducedInObj) APILifecycleIntroduced() (major, minor int) {
 | |
| 	return r.major, r.minor
 | |
| }
 | |
| 
 | |
| type introducedAndRemovedInObj struct {
 | |
| 	majorIntroduced, minorIntroduced int
 | |
| 	majorRemoved, minorRemoved       int
 | |
| }
 | |
| 
 | |
| func (r *introducedAndRemovedInObj) GetObjectKind() schema.ObjectKind {
 | |
| 	panic("don't do this")
 | |
| }
 | |
| func (r *introducedAndRemovedInObj) DeepCopyObject() runtime.Object {
 | |
| 	panic("don't do this either")
 | |
| }
 | |
| func (r *introducedAndRemovedInObj) APILifecycleIntroduced() (major, minor int) {
 | |
| 	return r.majorIntroduced, r.minorIntroduced
 | |
| }
 | |
| func (r *introducedAndRemovedInObj) APILifecycleRemoved() (major, minor int) {
 | |
| 	return r.majorRemoved, r.minorRemoved
 | |
| }
 | |
| 
 | |
| func storageRemovedIn(major, minor int) *introducedAndRemovedInStorage {
 | |
| 	return &introducedAndRemovedInStorage{majorRemoved: major, minorRemoved: minor}
 | |
| }
 | |
| 
 | |
| func storageNeverRemoved() *introducedAndRemovedInStorage {
 | |
| 	return &introducedAndRemovedInStorage{}
 | |
| }
 | |
| 
 | |
| func storageIntroducedIn(major, minor int) *introducedAndRemovedInStorage {
 | |
| 	return &introducedAndRemovedInStorage{majorIntroduced: major, minorIntroduced: minor}
 | |
| }
 | |
| 
 | |
| func storageIntroducedAndRemovedIn(majorIntroduced, minorIntroduced, majorRemoved, minorRemoved int) *introducedAndRemovedInStorage {
 | |
| 	return &introducedAndRemovedInStorage{majorIntroduced: majorIntroduced, minorIntroduced: minorIntroduced, majorRemoved: majorRemoved, minorRemoved: minorRemoved}
 | |
| }
 | |
| 
 | |
| type introducedAndRemovedInStorage struct {
 | |
| 	majorIntroduced, minorIntroduced int
 | |
| 	majorRemoved, minorRemoved       int
 | |
| }
 | |
| 
 | |
| func (r *introducedAndRemovedInStorage) New() runtime.Object {
 | |
| 	if r.majorIntroduced == 0 && r.minorIntroduced == 0 && r.majorRemoved == 0 && r.minorRemoved == 0 {
 | |
| 		return &defaultObj{}
 | |
| 	}
 | |
| 	if r.majorIntroduced == 0 && r.minorIntroduced == 0 {
 | |
| 		return &removedInObj{major: r.majorRemoved, minor: r.minorRemoved}
 | |
| 	}
 | |
| 	if r.majorRemoved == 0 && r.minorRemoved == 0 {
 | |
| 		return &IntroducedInObj{major: r.majorIntroduced, minor: r.minorIntroduced}
 | |
| 	}
 | |
| 	return &introducedAndRemovedInObj{majorIntroduced: r.majorIntroduced, minorIntroduced: r.minorIntroduced, majorRemoved: r.majorRemoved, minorRemoved: r.minorRemoved}
 | |
| }
 | |
| 
 | |
| func (r *introducedAndRemovedInStorage) Destroy() {}
 | |
| 
 | |
| func Test_resourceExpirationEvaluator_isNotRemoved(t *testing.T) {
 | |
| 	tests := []struct {
 | |
| 		name                        string
 | |
| 		resourceExpirationEvaluator resourceExpirationEvaluator
 | |
| 		restStorage                 rest.Storage
 | |
| 		expected                    bool
 | |
| 	}{
 | |
| 		{
 | |
| 			name: "removed-in-curr",
 | |
| 			resourceExpirationEvaluator: resourceExpirationEvaluator{
 | |
| 				currentVersion: apimachineryversion.MajorMinor(1, 20),
 | |
| 			},
 | |
| 			restStorage: storageRemovedIn(1, 20),
 | |
| 			expected:    false,
 | |
| 		},
 | |
| 		{
 | |
| 			name: "removed-in-curr-but-deferred",
 | |
| 			resourceExpirationEvaluator: resourceExpirationEvaluator{
 | |
| 				currentVersion:                 apimachineryversion.MajorMinor(1, 20),
 | |
| 				serveRemovedAPIsOneMoreRelease: true,
 | |
| 			},
 | |
| 			restStorage: storageRemovedIn(1, 20),
 | |
| 			expected:    true,
 | |
| 		},
 | |
| 		{
 | |
| 			name: "removed-in-curr-but-alpha",
 | |
| 			resourceExpirationEvaluator: resourceExpirationEvaluator{
 | |
| 				currentVersion: apimachineryversion.MajorMinor(1, 20),
 | |
| 				isAlpha:        true,
 | |
| 			},
 | |
| 			restStorage: storageRemovedIn(1, 20),
 | |
| 			expected:    true,
 | |
| 		},
 | |
| 		{
 | |
| 			name: "removed-in-curr-but-alpha-but-strict",
 | |
| 			resourceExpirationEvaluator: resourceExpirationEvaluator{
 | |
| 				currentVersion:               apimachineryversion.MajorMinor(1, 20),
 | |
| 				isAlpha:                      true,
 | |
| 				strictRemovedHandlingInAlpha: true,
 | |
| 			},
 | |
| 			restStorage: storageRemovedIn(1, 20),
 | |
| 			expected:    false,
 | |
| 		},
 | |
| 		{
 | |
| 			name: "removed-in-curr-but-alpha-but-strict-and-alpha-zero",
 | |
| 			resourceExpirationEvaluator: resourceExpirationEvaluator{
 | |
| 				currentVersion:               apimachineryversion.MajorMinor(1, 20),
 | |
| 				isAlpha:                      true,
 | |
| 				isAlphaZero:                  true,
 | |
| 				strictRemovedHandlingInAlpha: true,
 | |
| 			},
 | |
| 			restStorage: storageRemovedIn(1, 20),
 | |
| 			expected:    true,
 | |
| 		},
 | |
| 		{
 | |
| 			name: "removed-in-prev-deferral-does-not-help",
 | |
| 			resourceExpirationEvaluator: resourceExpirationEvaluator{
 | |
| 				currentVersion:                 apimachineryversion.MajorMinor(1, 21),
 | |
| 				serveRemovedAPIsOneMoreRelease: true,
 | |
| 			},
 | |
| 			restStorage: storageRemovedIn(1, 20),
 | |
| 			expected:    false,
 | |
| 		},
 | |
| 		{
 | |
| 			name: "removed-in-prev-major",
 | |
| 			resourceExpirationEvaluator: resourceExpirationEvaluator{
 | |
| 				currentVersion:                 apimachineryversion.MajorMinor(2, 20),
 | |
| 				serveRemovedAPIsOneMoreRelease: true,
 | |
| 			},
 | |
| 			restStorage: storageRemovedIn(1, 20),
 | |
| 			expected:    false,
 | |
| 		},
 | |
| 		{
 | |
| 			name: "removed-in-future",
 | |
| 			resourceExpirationEvaluator: resourceExpirationEvaluator{
 | |
| 				currentVersion: apimachineryversion.MajorMinor(1, 20),
 | |
| 			},
 | |
| 			restStorage: storageRemovedIn(1, 21),
 | |
| 			expected:    true,
 | |
| 		},
 | |
| 		{
 | |
| 			name: "never-removed",
 | |
| 			resourceExpirationEvaluator: resourceExpirationEvaluator{
 | |
| 				currentVersion: apimachineryversion.MajorMinor(1, 20),
 | |
| 			},
 | |
| 			restStorage: storageNeverRemoved(),
 | |
| 			expected:    true,
 | |
| 		},
 | |
| 	}
 | |
| 	for _, tt := range tests {
 | |
| 		t.Run(tt.name, func(t *testing.T) {
 | |
| 			gv := schema.GroupVersion{Group: "mygroup", Version: "myversion"}
 | |
| 			convertor := &dummyConvertor{prioritizedVersions: []schema.GroupVersion{gv}}
 | |
| 			if actual := tt.resourceExpirationEvaluator.isNotRemoved(gv, convertor, tt.restStorage); actual != tt.expected {
 | |
| 				t.Errorf("isRemoved() = %v, want %v", actual, tt.expected)
 | |
| 			}
 | |
| 			if !reflect.DeepEqual(convertor.called, gv) {
 | |
| 				t.Errorf("expected converter to be called with %#v, got %#v", gv, convertor.called)
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| type dummyConvertor struct {
 | |
| 	called              runtime.GroupVersioner
 | |
| 	prioritizedVersions []schema.GroupVersion
 | |
| }
 | |
| 
 | |
| func (d *dummyConvertor) ConvertToVersion(in runtime.Object, gv runtime.GroupVersioner) (runtime.Object, error) {
 | |
| 	d.called = gv
 | |
| 	return in, nil
 | |
| }
 | |
| 
 | |
| func (d *dummyConvertor) PrioritizedVersionsForGroup(group string) []schema.GroupVersion {
 | |
| 	return d.prioritizedVersions
 | |
| }
 | |
| 
 | |
| func (d *dummyConvertor) IsGroupRegistered(group string) bool {
 | |
| 	return true
 | |
| }
 | |
| 
 | |
| func (d *dummyConvertor) IsVersionRegistered(v schema.GroupVersion) bool {
 | |
| 	return true
 | |
| }
 | |
| 
 | |
| func (d *dummyConvertor) PrioritizedVersionsAllGroups() []schema.GroupVersion {
 | |
| 	return d.prioritizedVersions
 | |
| }
 | |
| 
 | |
| func checkErr(t *testing.T, actual error, expected string) {
 | |
| 	t.Helper()
 | |
| 	switch {
 | |
| 	case len(expected) == 0 && actual == nil:
 | |
| 	case len(expected) == 0 && actual != nil:
 | |
| 		t.Fatal(actual)
 | |
| 	case len(expected) != 0 && actual == nil:
 | |
| 		t.Fatalf("missing %q, <nil>", expected)
 | |
| 	case len(expected) != 0 && actual != nil && !strings.Contains(actual.Error(), expected):
 | |
| 		t.Fatalf("missing %q, %v", expected, actual)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func Test_removeDeletedKinds(t *testing.T) {
 | |
| 	groupName := "group.name"
 | |
| 	tests := []struct {
 | |
| 		name                         string
 | |
| 		resourceExpirationEvaluator  resourceExpirationEvaluator
 | |
| 		versionedResourcesStorageMap map[string]map[string]rest.Storage
 | |
| 		expectedStorage              map[string]map[string]rest.Storage
 | |
| 	}{
 | |
| 		{
 | |
| 			name: "remove-one-of-two",
 | |
| 			resourceExpirationEvaluator: resourceExpirationEvaluator{
 | |
| 				currentVersion: apimachineryversion.MajorMinor(1, 20),
 | |
| 			},
 | |
| 			versionedResourcesStorageMap: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					"twenty":    storageRemovedIn(1, 20),
 | |
| 					"twentyone": storageRemovedIn(1, 21),
 | |
| 				},
 | |
| 			},
 | |
| 			expectedStorage: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					"twentyone": storageRemovedIn(1, 21),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name: "remove-nested-not-expired",
 | |
| 			resourceExpirationEvaluator: resourceExpirationEvaluator{
 | |
| 				currentVersion: apimachineryversion.MajorMinor(1, 20),
 | |
| 			},
 | |
| 			versionedResourcesStorageMap: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					"twenty":       storageRemovedIn(1, 20),
 | |
| 					"twenty/scale": storageRemovedIn(1, 21),
 | |
| 					"twentyone":    storageRemovedIn(1, 21),
 | |
| 				},
 | |
| 			},
 | |
| 			expectedStorage: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					"twentyone": storageRemovedIn(1, 21),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name: "remove-all-of-version",
 | |
| 			resourceExpirationEvaluator: resourceExpirationEvaluator{
 | |
| 				currentVersion: apimachineryversion.MajorMinor(1, 20),
 | |
| 			},
 | |
| 			versionedResourcesStorageMap: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					"twenty": storageRemovedIn(1, 20),
 | |
| 				},
 | |
| 				"v2": {
 | |
| 					"twenty":    storageRemovedIn(1, 20),
 | |
| 					"twentyone": storageRemovedIn(1, 21),
 | |
| 				},
 | |
| 			},
 | |
| 			expectedStorage: map[string]map[string]rest.Storage{
 | |
| 				"v2": {
 | |
| 					"twentyone": storageRemovedIn(1, 21),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 	}
 | |
| 	for _, tt := range tests {
 | |
| 		t.Run(tt.name, func(t *testing.T) {
 | |
| 			convertor := &dummyConvertor{prioritizedVersions: []schema.GroupVersion{
 | |
| 				{Group: groupName, Version: "v2"}, {Group: groupName, Version: "v1"}}}
 | |
| 			tt.resourceExpirationEvaluator.removeDeletedKinds(groupName, convertor, tt.versionedResourcesStorageMap)
 | |
| 			if !reflect.DeepEqual(tt.expectedStorage, tt.versionedResourcesStorageMap) {
 | |
| 				t.Fatal(dump.Pretty(tt.versionedResourcesStorageMap))
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func Test_removeUnIntroducedKinds(t *testing.T) {
 | |
| 	groupName := "group.name"
 | |
| 	resource1 := "resource1"
 | |
| 	resource2 := "resource2"
 | |
| 	tests := []struct {
 | |
| 		name                         string
 | |
| 		resourceExpirationEvaluator  resourceExpirationEvaluator
 | |
| 		runtimeConfig                map[string]string
 | |
| 		expectErr                    bool
 | |
| 		versionedResourcesStorageMap map[string]map[string]rest.Storage
 | |
| 		expectedStorage              map[string]map[string]rest.Storage
 | |
| 	}{
 | |
| 		{
 | |
| 			name: "remove-future-version",
 | |
| 			resourceExpirationEvaluator: resourceExpirationEvaluator{
 | |
| 				currentVersion: apimachineryversion.MajorMinor(1, 21),
 | |
| 			},
 | |
| 			runtimeConfig: map[string]string{
 | |
| 				"api/beta":             "true",
 | |
| 				groupName + "/v2beta1": "true",
 | |
| 			},
 | |
| 			versionedResourcesStorageMap: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageIntroducedIn(1, 18),
 | |
| 				},
 | |
| 				"v2alpha1": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 20, 1, 21),
 | |
| 				},
 | |
| 				"v2beta1": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 21, 1, 22),
 | |
| 				},
 | |
| 				"v2beta2": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 				},
 | |
| 				"v2": {
 | |
| 					resource1: storageIntroducedIn(1, 23),
 | |
| 					resource2: storageIntroducedIn(1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 			expectedStorage: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageIntroducedIn(1, 18),
 | |
| 				},
 | |
| 				"v2alpha1": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 20, 1, 21),
 | |
| 				},
 | |
| 				"v2beta1": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 21, 1, 22),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name: "missing-introduced-version",
 | |
| 			resourceExpirationEvaluator: resourceExpirationEvaluator{
 | |
| 				currentVersion: apimachineryversion.MajorMinor(1, 20),
 | |
| 			},
 | |
| 			versionedResourcesStorageMap: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageRemovedIn(1, 23),
 | |
| 				},
 | |
| 				"v2": {
 | |
| 					resource1: storageIntroducedIn(1, 23),
 | |
| 					resource2: storageIntroducedIn(1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 			expectedStorage: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageRemovedIn(1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name: "emulation-forward-compatible-ga-api",
 | |
| 			resourceExpirationEvaluator: resourceExpirationEvaluator{
 | |
| 				currentVersion:             apimachineryversion.MajorMinor(1, 19),
 | |
| 				emulationForwardCompatible: true,
 | |
| 			},
 | |
| 			versionedResourcesStorageMap: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageIntroducedIn(1, 18),
 | |
| 				},
 | |
| 				"v2alpha1": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 20, 1, 21),
 | |
| 				},
 | |
| 				"v2beta1": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 21, 1, 22),
 | |
| 				},
 | |
| 				"v2beta2": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 				},
 | |
| 				"v2": {
 | |
| 					resource1: storageIntroducedIn(1, 23),
 | |
| 					resource2: storageIntroducedIn(1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 			expectedStorage: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageIntroducedIn(1, 18),
 | |
| 				},
 | |
| 				"v2": {
 | |
| 					resource1: storageIntroducedIn(1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name: "emulation-forward-compatible-alpha-api",
 | |
| 			resourceExpirationEvaluator: resourceExpirationEvaluator{
 | |
| 				currentVersion:             apimachineryversion.MajorMinor(1, 20),
 | |
| 				emulationForwardCompatible: true,
 | |
| 			},
 | |
| 			versionedResourcesStorageMap: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageIntroducedIn(1, 18),
 | |
| 				},
 | |
| 				"v2alpha1": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 20, 1, 21),
 | |
| 				},
 | |
| 				"v2beta1": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 21, 1, 22),
 | |
| 				},
 | |
| 				"v2beta2": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 				},
 | |
| 				"v2": {
 | |
| 					resource1: storageIntroducedIn(1, 23),
 | |
| 					resource2: storageIntroducedIn(1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 			expectedStorage: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageIntroducedIn(1, 18),
 | |
| 				},
 | |
| 				"v2alpha1": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 20, 1, 21),
 | |
| 				},
 | |
| 				"v2": {
 | |
| 					resource1: storageIntroducedIn(1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name: "emulation-forward-compatible-beta1-api",
 | |
| 			resourceExpirationEvaluator: resourceExpirationEvaluator{
 | |
| 				currentVersion:             apimachineryversion.MajorMinor(1, 21),
 | |
| 				emulationForwardCompatible: true,
 | |
| 			},
 | |
| 			versionedResourcesStorageMap: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageIntroducedIn(1, 20),
 | |
| 				},
 | |
| 				"v2beta1": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 21, 1, 22),
 | |
| 				},
 | |
| 				"v2beta2": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 				},
 | |
| 				"v2": {
 | |
| 					resource1: storageIntroducedIn(1, 23),
 | |
| 					resource2: storageIntroducedIn(1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 			expectedStorage: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageIntroducedIn(1, 20),
 | |
| 				},
 | |
| 				"v2beta1": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 21, 1, 22),
 | |
| 				},
 | |
| 				"v2beta2": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 				},
 | |
| 				"v2": {
 | |
| 					resource1: storageIntroducedIn(1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name: "emulation-forward-compatible-new-resource",
 | |
| 			resourceExpirationEvaluator: resourceExpirationEvaluator{
 | |
| 				currentVersion:             apimachineryversion.MajorMinor(1, 22),
 | |
| 				emulationForwardCompatible: true,
 | |
| 			},
 | |
| 			versionedResourcesStorageMap: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageIntroducedIn(1, 20),
 | |
| 				},
 | |
| 				"v2beta1": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 21, 1, 22),
 | |
| 				},
 | |
| 				"v2beta2": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 				},
 | |
| 				"v2": {
 | |
| 					resource1: storageIntroducedIn(1, 23),
 | |
| 					resource2: storageIntroducedIn(1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 			expectedStorage: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageIntroducedIn(1, 20),
 | |
| 				},
 | |
| 				"v2beta1": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 21, 1, 22),
 | |
| 				},
 | |
| 				"v2beta2": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 				},
 | |
| 				"v2": {
 | |
| 					resource1: storageIntroducedIn(1, 23),
 | |
| 					resource2: storageIntroducedIn(1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name: "runtime-config-enable-future-version-err",
 | |
| 			resourceExpirationEvaluator: resourceExpirationEvaluator{
 | |
| 				currentVersion: apimachineryversion.MajorMinor(1, 20),
 | |
| 			},
 | |
| 			runtimeConfig: map[string]string{
 | |
| 				groupName + "/v2beta2": "true",
 | |
| 			},
 | |
| 			expectErr: true,
 | |
| 			versionedResourcesStorageMap: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageIntroducedIn(1, 20),
 | |
| 				},
 | |
| 				"v2beta1": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 21, 1, 22),
 | |
| 				},
 | |
| 				"v2beta2": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 				},
 | |
| 				"v2": {
 | |
| 					resource1: storageIntroducedIn(1, 23),
 | |
| 					resource2: storageIntroducedIn(1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name: "runtime-config-enable-future-resource-err",
 | |
| 			resourceExpirationEvaluator: resourceExpirationEvaluator{
 | |
| 				currentVersion: apimachineryversion.MajorMinor(1, 20),
 | |
| 			},
 | |
| 			runtimeConfig: map[string]string{
 | |
| 				groupName + "/v2beta2/resource2": "true",
 | |
| 			},
 | |
| 			expectErr: true,
 | |
| 			versionedResourcesStorageMap: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageIntroducedIn(1, 20),
 | |
| 				},
 | |
| 				"v2beta1": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 21, 1, 22),
 | |
| 				},
 | |
| 				"v2beta2": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 				},
 | |
| 				"v2": {
 | |
| 					resource1: storageIntroducedIn(1, 23),
 | |
| 					resource2: storageIntroducedIn(1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name: "runtime-config-emulation-forward-compatible-beta2-api",
 | |
| 			resourceExpirationEvaluator: resourceExpirationEvaluator{
 | |
| 				currentVersion:                          apimachineryversion.MajorMinor(1, 20),
 | |
| 				runtimeConfigEmulationForwardCompatible: true,
 | |
| 			},
 | |
| 			runtimeConfig: map[string]string{
 | |
| 				groupName + "/v2beta2": "true",
 | |
| 			},
 | |
| 			versionedResourcesStorageMap: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageIntroducedIn(1, 20),
 | |
| 				},
 | |
| 				"v2beta1": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 21, 1, 22),
 | |
| 				},
 | |
| 				"v2beta2": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 				},
 | |
| 				"v2": {
 | |
| 					resource1: storageIntroducedIn(1, 23),
 | |
| 					resource2: storageIntroducedIn(1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 			expectedStorage: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageIntroducedIn(1, 20),
 | |
| 				},
 | |
| 				"v2beta2": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name: "runtime-config-emulation-forward-compatible-beta2-resource",
 | |
| 			resourceExpirationEvaluator: resourceExpirationEvaluator{
 | |
| 				currentVersion:                          apimachineryversion.MajorMinor(1, 20),
 | |
| 				runtimeConfigEmulationForwardCompatible: true,
 | |
| 			},
 | |
| 			runtimeConfig: map[string]string{
 | |
| 				groupName + "/v2beta2/resource2": "true",
 | |
| 			},
 | |
| 			versionedResourcesStorageMap: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageIntroducedIn(1, 20),
 | |
| 				},
 | |
| 				"v2beta1": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 21, 1, 22),
 | |
| 				},
 | |
| 				"v2beta2": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 				},
 | |
| 				"v2": {
 | |
| 					resource1: storageIntroducedIn(1, 23),
 | |
| 					resource2: storageIntroducedIn(1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 			expectedStorage: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageIntroducedIn(1, 20),
 | |
| 				},
 | |
| 				"v2beta2": {
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name: "emulation-forward-compatible-runtime-config-beta2-api",
 | |
| 			resourceExpirationEvaluator: resourceExpirationEvaluator{
 | |
| 				currentVersion:             apimachineryversion.MajorMinor(1, 20),
 | |
| 				emulationForwardCompatible: true,
 | |
| 			},
 | |
| 			runtimeConfig: map[string]string{
 | |
| 				groupName + "/v2beta2": "true",
 | |
| 			},
 | |
| 			versionedResourcesStorageMap: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageIntroducedIn(1, 20),
 | |
| 				},
 | |
| 				"v2beta1": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 21, 1, 22),
 | |
| 				},
 | |
| 				"v2beta2": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 				},
 | |
| 				"v2": {
 | |
| 					resource1: storageIntroducedIn(1, 23),
 | |
| 					resource2: storageIntroducedIn(1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 			expectErr: true,
 | |
| 		},
 | |
| 		{
 | |
| 			name: "both-runtime-config-and-emulation-forward-compatible-runtime-config-beta2-api",
 | |
| 			resourceExpirationEvaluator: resourceExpirationEvaluator{
 | |
| 				currentVersion:                          apimachineryversion.MajorMinor(1, 20),
 | |
| 				emulationForwardCompatible:              true,
 | |
| 				runtimeConfigEmulationForwardCompatible: true,
 | |
| 			},
 | |
| 			runtimeConfig: map[string]string{
 | |
| 				groupName + "/v2beta2": "true",
 | |
| 			},
 | |
| 			versionedResourcesStorageMap: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageIntroducedIn(1, 20),
 | |
| 				},
 | |
| 				"v2beta1": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 21, 1, 22),
 | |
| 				},
 | |
| 				"v2beta2": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 				},
 | |
| 				"v2": {
 | |
| 					resource1: storageIntroducedIn(1, 23),
 | |
| 					resource2: storageIntroducedIn(1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 			expectedStorage: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageIntroducedIn(1, 20),
 | |
| 				},
 | |
| 				"v2beta2": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 				},
 | |
| 				"v2": {
 | |
| 					resource1: storageIntroducedIn(1, 23),
 | |
| 					resource2: storageIntroducedIn(1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name: "both-runtime-config-and-emulation-forward-compatible-runtime-config-alpha-api",
 | |
| 			resourceExpirationEvaluator: resourceExpirationEvaluator{
 | |
| 				currentVersion:                          apimachineryversion.MajorMinor(1, 19),
 | |
| 				runtimeConfigEmulationForwardCompatible: true,
 | |
| 				emulationForwardCompatible:              true,
 | |
| 			},
 | |
| 			runtimeConfig: map[string]string{
 | |
| 				groupName + "/v2alpha1": "true",
 | |
| 			},
 | |
| 			versionedResourcesStorageMap: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageIntroducedIn(1, 19),
 | |
| 				},
 | |
| 				"v2alpha1": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 20, 1, 21),
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 20, 1, 21),
 | |
| 				},
 | |
| 				"v2beta1": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 21, 1, 22),
 | |
| 				},
 | |
| 				"v2beta2": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 				},
 | |
| 				"v2": {
 | |
| 					resource1: storageIntroducedIn(1, 23),
 | |
| 					resource2: storageIntroducedIn(1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 			expectedStorage: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageIntroducedIn(1, 19),
 | |
| 				},
 | |
| 				"v2alpha1": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 20, 1, 21),
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 20, 1, 21),
 | |
| 				},
 | |
| 				"v2": {
 | |
| 					resource1: storageIntroducedIn(1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name: "both-runtime-config-and-emulation-forward-compatible-runtime-config-alpha-resource",
 | |
| 			resourceExpirationEvaluator: resourceExpirationEvaluator{
 | |
| 				currentVersion:                          apimachineryversion.MajorMinor(1, 19),
 | |
| 				runtimeConfigEmulationForwardCompatible: true,
 | |
| 				emulationForwardCompatible:              true,
 | |
| 			},
 | |
| 			runtimeConfig: map[string]string{
 | |
| 				groupName + "/v2alpha1/resource2": "true",
 | |
| 			},
 | |
| 			versionedResourcesStorageMap: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageIntroducedIn(1, 19),
 | |
| 				},
 | |
| 				"v2alpha1": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 20, 1, 21),
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 20, 1, 21),
 | |
| 				},
 | |
| 				"v2beta1": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 21, 1, 22),
 | |
| 				},
 | |
| 				"v2beta2": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 				},
 | |
| 				"v2": {
 | |
| 					resource1: storageIntroducedIn(1, 23),
 | |
| 					resource2: storageIntroducedIn(1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 			expectedStorage: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageIntroducedIn(1, 19),
 | |
| 				},
 | |
| 				"v2alpha1": {
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 20, 1, 21),
 | |
| 				},
 | |
| 				"v2": {
 | |
| 					resource1: storageIntroducedIn(1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name: "emulation-forward-compatible-beta1-api",
 | |
| 			resourceExpirationEvaluator: resourceExpirationEvaluator{
 | |
| 				currentVersion:             apimachineryversion.MajorMinor(1, 21),
 | |
| 				emulationForwardCompatible: true,
 | |
| 			},
 | |
| 			runtimeConfig: map[string]string{
 | |
| 				groupName + "/v2beta1": "true",
 | |
| 			},
 | |
| 			versionedResourcesStorageMap: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageIntroducedIn(1, 20),
 | |
| 				},
 | |
| 				"v2beta1": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 21, 1, 22),
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 21, 1, 22),
 | |
| 				},
 | |
| 				"v2beta2": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 				},
 | |
| 				"v2": {
 | |
| 					resource1: storageIntroducedIn(1, 23),
 | |
| 					resource2: storageIntroducedIn(1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 			expectedStorage: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageIntroducedIn(1, 20),
 | |
| 				},
 | |
| 				"v2beta1": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 21, 1, 22),
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 21, 1, 22),
 | |
| 				},
 | |
| 				"v2beta2": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 				},
 | |
| 				"v2": {
 | |
| 					resource1: storageIntroducedIn(1, 23),
 | |
| 					resource2: storageIntroducedIn(1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name: "emulation-forward-compatible-beta1-resource",
 | |
| 			resourceExpirationEvaluator: resourceExpirationEvaluator{
 | |
| 				currentVersion:             apimachineryversion.MajorMinor(1, 21),
 | |
| 				emulationForwardCompatible: true,
 | |
| 			},
 | |
| 			runtimeConfig: map[string]string{
 | |
| 				groupName + "/v2beta1/resource2": "true",
 | |
| 			},
 | |
| 			versionedResourcesStorageMap: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageIntroducedIn(1, 20),
 | |
| 				},
 | |
| 				"v2beta1": {
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 21, 1, 22),
 | |
| 				},
 | |
| 				"v2beta2": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 				},
 | |
| 				"v2": {
 | |
| 					resource1: storageIntroducedIn(1, 23),
 | |
| 					resource2: storageIntroducedIn(1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 			expectedStorage: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageIntroducedIn(1, 20),
 | |
| 				},
 | |
| 				"v2beta1": {
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 21, 1, 22),
 | |
| 				},
 | |
| 				"v2beta2": {
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 				},
 | |
| 				"v2": {
 | |
| 					resource1: storageIntroducedIn(1, 23),
 | |
| 					resource2: storageIntroducedIn(1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name: "both-runtime-config-and-emulation-forward-compatible-runtime-config-beta1-api",
 | |
| 			resourceExpirationEvaluator: resourceExpirationEvaluator{
 | |
| 				currentVersion:                          apimachineryversion.MajorMinor(1, 20),
 | |
| 				emulationForwardCompatible:              true,
 | |
| 				runtimeConfigEmulationForwardCompatible: true,
 | |
| 			},
 | |
| 			runtimeConfig: map[string]string{
 | |
| 				groupName + "/v2beta1": "true",
 | |
| 			},
 | |
| 			versionedResourcesStorageMap: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageIntroducedIn(1, 20),
 | |
| 				},
 | |
| 				"v2beta1": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 21, 1, 22),
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 21, 1, 22),
 | |
| 				},
 | |
| 				"v2beta2": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 				},
 | |
| 				"v2": {
 | |
| 					resource1: storageIntroducedIn(1, 23),
 | |
| 					resource2: storageIntroducedIn(1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 			expectedStorage: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageIntroducedIn(1, 20),
 | |
| 				},
 | |
| 				"v2beta1": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 21, 1, 22),
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 21, 1, 22),
 | |
| 				},
 | |
| 				"v2beta2": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 				},
 | |
| 				"v2": {
 | |
| 					resource1: storageIntroducedIn(1, 23),
 | |
| 					resource2: storageIntroducedIn(1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name: "both-runtime-config-and-emulation-forward-compatible-runtime-config-beta1-resource",
 | |
| 			resourceExpirationEvaluator: resourceExpirationEvaluator{
 | |
| 				currentVersion:                          apimachineryversion.MajorMinor(1, 20),
 | |
| 				emulationForwardCompatible:              true,
 | |
| 				runtimeConfigEmulationForwardCompatible: true,
 | |
| 			},
 | |
| 			runtimeConfig: map[string]string{
 | |
| 				groupName + "/v2beta1/resource2": "true",
 | |
| 			},
 | |
| 			versionedResourcesStorageMap: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageIntroducedIn(1, 20),
 | |
| 				},
 | |
| 				"v2beta1": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 21, 1, 22),
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 21, 1, 22),
 | |
| 				},
 | |
| 				"v2beta2": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 				},
 | |
| 				"v2": {
 | |
| 					resource1: storageIntroducedIn(1, 23),
 | |
| 					resource2: storageIntroducedIn(1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 			expectedStorage: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageIntroducedIn(1, 20),
 | |
| 				},
 | |
| 				"v2beta1": {
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 21, 1, 22),
 | |
| 				},
 | |
| 				"v2beta2": {
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 				},
 | |
| 				"v2": {
 | |
| 					resource1: storageIntroducedIn(1, 23),
 | |
| 					resource2: storageIntroducedIn(1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 	}
 | |
| 	for _, tt := range tests {
 | |
| 		t.Run(tt.name, func(t *testing.T) {
 | |
| 			resourceConfig := serverstorage.NewResourceConfig()
 | |
| 			convertor := &dummyConvertor{prioritizedVersions: []schema.GroupVersion{
 | |
| 				{Group: groupName, Version: "v2"}, {Group: groupName, Version: "v1"},
 | |
| 				{Group: groupName, Version: "v2beta1"},
 | |
| 				{Group: groupName, Version: "v2beta2"},
 | |
| 				{Group: groupName, Version: "v2alpha1"}}}
 | |
| 			resourceConfig.EnableVersions(convertor.PrioritizedVersionsForGroup(groupName)...)
 | |
| 			resourceConfig, err := resourceconfig.MergeAPIResourceConfigs(resourceConfig, tt.runtimeConfig, convertor)
 | |
| 			require.NoError(t, err)
 | |
| 			err = tt.resourceExpirationEvaluator.removeUnintroducedKinds(groupName, convertor, tt.versionedResourcesStorageMap, resourceConfig)
 | |
| 			if tt.expectErr {
 | |
| 				require.Error(t, err)
 | |
| 				return
 | |
| 			}
 | |
| 			require.NoError(t, err)
 | |
| 			if !reflect.DeepEqual(tt.expectedStorage, tt.versionedResourcesStorageMap) {
 | |
| 				t.Fatal(dump.Pretty(tt.versionedResourcesStorageMap))
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func Test_RemoveUnavailableKinds(t *testing.T) {
 | |
| 	groupName := "group.name"
 | |
| 	resource1 := "resource1"
 | |
| 	resource2 := "resource2"
 | |
| 	tests := []struct {
 | |
| 		name                         string
 | |
| 		resourceExpirationEvaluator  resourceExpirationEvaluator
 | |
| 		runtimeConfig                map[string]string
 | |
| 		expectErr                    bool
 | |
| 		versionedResourcesStorageMap map[string]map[string]rest.Storage
 | |
| 		expectedStorage              map[string]map[string]rest.Storage
 | |
| 	}{
 | |
| 		{
 | |
| 			name: "remove-future-version",
 | |
| 			resourceExpirationEvaluator: resourceExpirationEvaluator{
 | |
| 				currentVersion: apimachineryversion.MajorMinor(1, 21),
 | |
| 			},
 | |
| 			runtimeConfig: map[string]string{
 | |
| 				"api/beta":             "true",
 | |
| 				groupName + "/v2beta1": "true",
 | |
| 			},
 | |
| 			versionedResourcesStorageMap: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageIntroducedIn(1, 18),
 | |
| 				},
 | |
| 				"v2alpha1": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 20, 1, 21),
 | |
| 				},
 | |
| 				"v2beta1": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 21, 1, 22),
 | |
| 				},
 | |
| 				"v2beta2": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 				},
 | |
| 				"v2": {
 | |
| 					resource1: storageIntroducedIn(1, 23),
 | |
| 					resource2: storageIntroducedIn(1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 			expectedStorage: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageIntroducedIn(1, 18),
 | |
| 				},
 | |
| 				"v2beta1": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 21, 1, 22),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name: "missing-introduced-version",
 | |
| 			resourceExpirationEvaluator: resourceExpirationEvaluator{
 | |
| 				currentVersion: apimachineryversion.MajorMinor(1, 20),
 | |
| 			},
 | |
| 			versionedResourcesStorageMap: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageRemovedIn(1, 23),
 | |
| 				},
 | |
| 				"v2": {
 | |
| 					resource1: storageIntroducedIn(1, 23),
 | |
| 					resource2: storageIntroducedIn(1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 			expectedStorage: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageRemovedIn(1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name: "emulation-forward-compatible-ga-api",
 | |
| 			resourceExpirationEvaluator: resourceExpirationEvaluator{
 | |
| 				currentVersion:             apimachineryversion.MajorMinor(1, 19),
 | |
| 				emulationForwardCompatible: true,
 | |
| 			},
 | |
| 			versionedResourcesStorageMap: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageIntroducedIn(1, 18),
 | |
| 				},
 | |
| 				"v2alpha1": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 20, 1, 21),
 | |
| 				},
 | |
| 				"v2beta1": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 21, 1, 22),
 | |
| 				},
 | |
| 				"v2beta2": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 				},
 | |
| 				"v2": {
 | |
| 					resource1: storageIntroducedIn(1, 23),
 | |
| 					resource2: storageIntroducedIn(1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 			expectedStorage: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageIntroducedIn(1, 18),
 | |
| 				},
 | |
| 				"v2": {
 | |
| 					resource1: storageIntroducedIn(1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name: "emulation-forward-compatible-alpha-api",
 | |
| 			resourceExpirationEvaluator: resourceExpirationEvaluator{
 | |
| 				currentVersion:             apimachineryversion.MajorMinor(1, 20),
 | |
| 				emulationForwardCompatible: true,
 | |
| 			},
 | |
| 			versionedResourcesStorageMap: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageIntroducedIn(1, 18),
 | |
| 				},
 | |
| 				"v2alpha1": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 20, 1, 21),
 | |
| 				},
 | |
| 				"v2beta1": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 21, 1, 22),
 | |
| 				},
 | |
| 				"v2beta2": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 				},
 | |
| 				"v2": {
 | |
| 					resource1: storageIntroducedIn(1, 23),
 | |
| 					resource2: storageIntroducedIn(1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 			expectedStorage: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageIntroducedIn(1, 18),
 | |
| 				},
 | |
| 				"v2alpha1": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 20, 1, 21),
 | |
| 				},
 | |
| 				"v2": {
 | |
| 					resource1: storageIntroducedIn(1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name: "emulation-forward-compatible-beta1-api",
 | |
| 			resourceExpirationEvaluator: resourceExpirationEvaluator{
 | |
| 				currentVersion:             apimachineryversion.MajorMinor(1, 21),
 | |
| 				emulationForwardCompatible: true,
 | |
| 			},
 | |
| 			versionedResourcesStorageMap: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageIntroducedIn(1, 20),
 | |
| 				},
 | |
| 				"v2beta1": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 21, 1, 22),
 | |
| 				},
 | |
| 				"v2beta2": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 				},
 | |
| 				"v2": {
 | |
| 					resource1: storageIntroducedIn(1, 23),
 | |
| 					resource2: storageIntroducedIn(1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 			expectedStorage: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageIntroducedIn(1, 20),
 | |
| 				},
 | |
| 				"v2beta1": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 21, 1, 22),
 | |
| 				},
 | |
| 				"v2beta2": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 				},
 | |
| 				"v2": {
 | |
| 					resource1: storageIntroducedIn(1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name: "emulation-forward-compatible-new-resource",
 | |
| 			resourceExpirationEvaluator: resourceExpirationEvaluator{
 | |
| 				currentVersion:             apimachineryversion.MajorMinor(1, 22),
 | |
| 				emulationForwardCompatible: true,
 | |
| 			},
 | |
| 			versionedResourcesStorageMap: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageIntroducedIn(1, 20),
 | |
| 				},
 | |
| 				"v2beta1": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 21, 1, 22),
 | |
| 				},
 | |
| 				"v2beta2": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 				},
 | |
| 				"v2": {
 | |
| 					resource1: storageIntroducedIn(1, 23),
 | |
| 					resource2: storageIntroducedIn(1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 			expectedStorage: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageIntroducedIn(1, 20),
 | |
| 				},
 | |
| 				"v2beta2": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 				},
 | |
| 				"v2": {
 | |
| 					resource1: storageIntroducedIn(1, 23),
 | |
| 					resource2: storageIntroducedIn(1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name: "runtime-config-enable-future-version-err",
 | |
| 			resourceExpirationEvaluator: resourceExpirationEvaluator{
 | |
| 				currentVersion: apimachineryversion.MajorMinor(1, 20),
 | |
| 			},
 | |
| 			runtimeConfig: map[string]string{
 | |
| 				groupName + "/v2beta2": "true",
 | |
| 			},
 | |
| 			expectErr: true,
 | |
| 			versionedResourcesStorageMap: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageIntroducedIn(1, 20),
 | |
| 				},
 | |
| 				"v2beta1": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 21, 1, 22),
 | |
| 				},
 | |
| 				"v2beta2": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 				},
 | |
| 				"v2": {
 | |
| 					resource1: storageIntroducedIn(1, 23),
 | |
| 					resource2: storageIntroducedIn(1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name: "runtime-config-enable-future-resource-err",
 | |
| 			resourceExpirationEvaluator: resourceExpirationEvaluator{
 | |
| 				currentVersion: apimachineryversion.MajorMinor(1, 20),
 | |
| 			},
 | |
| 			runtimeConfig: map[string]string{
 | |
| 				groupName + "/v2beta2/resource2": "true",
 | |
| 			},
 | |
| 			expectErr: true,
 | |
| 			versionedResourcesStorageMap: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageIntroducedIn(1, 20),
 | |
| 				},
 | |
| 				"v2beta1": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 21, 1, 22),
 | |
| 				},
 | |
| 				"v2beta2": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 				},
 | |
| 				"v2": {
 | |
| 					resource1: storageIntroducedIn(1, 23),
 | |
| 					resource2: storageIntroducedIn(1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name: "runtime-config-emulation-forward-compatible-beta2-api",
 | |
| 			resourceExpirationEvaluator: resourceExpirationEvaluator{
 | |
| 				currentVersion:                          apimachineryversion.MajorMinor(1, 20),
 | |
| 				runtimeConfigEmulationForwardCompatible: true,
 | |
| 			},
 | |
| 			runtimeConfig: map[string]string{
 | |
| 				groupName + "/v2beta2": "true",
 | |
| 			},
 | |
| 			versionedResourcesStorageMap: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageIntroducedIn(1, 20),
 | |
| 				},
 | |
| 				"v2beta1": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 21, 1, 22),
 | |
| 				},
 | |
| 				"v2beta2": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 				},
 | |
| 				"v2": {
 | |
| 					resource1: storageIntroducedIn(1, 23),
 | |
| 					resource2: storageIntroducedIn(1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 			expectedStorage: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageIntroducedIn(1, 20),
 | |
| 				},
 | |
| 				"v2beta2": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name: "runtime-config-emulation-forward-compatible-beta2-resource",
 | |
| 			resourceExpirationEvaluator: resourceExpirationEvaluator{
 | |
| 				currentVersion:                          apimachineryversion.MajorMinor(1, 20),
 | |
| 				runtimeConfigEmulationForwardCompatible: true,
 | |
| 			},
 | |
| 			runtimeConfig: map[string]string{
 | |
| 				groupName + "/v2beta2/resource2": "true",
 | |
| 			},
 | |
| 			versionedResourcesStorageMap: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageIntroducedIn(1, 20),
 | |
| 				},
 | |
| 				"v2beta1": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 21, 1, 22),
 | |
| 				},
 | |
| 				"v2beta2": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 				},
 | |
| 				"v2": {
 | |
| 					resource1: storageIntroducedIn(1, 23),
 | |
| 					resource2: storageIntroducedIn(1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 			expectedStorage: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageIntroducedIn(1, 20),
 | |
| 				},
 | |
| 				"v2beta2": {
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name: "emulation-forward-compatible-runtime-config-beta2-api",
 | |
| 			resourceExpirationEvaluator: resourceExpirationEvaluator{
 | |
| 				currentVersion:             apimachineryversion.MajorMinor(1, 20),
 | |
| 				emulationForwardCompatible: true,
 | |
| 			},
 | |
| 			runtimeConfig: map[string]string{
 | |
| 				groupName + "/v2beta2": "true",
 | |
| 			},
 | |
| 			versionedResourcesStorageMap: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageIntroducedIn(1, 20),
 | |
| 				},
 | |
| 				"v2beta1": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 21, 1, 22),
 | |
| 				},
 | |
| 				"v2beta2": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 				},
 | |
| 				"v2": {
 | |
| 					resource1: storageIntroducedIn(1, 23),
 | |
| 					resource2: storageIntroducedIn(1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 			expectErr: true,
 | |
| 		},
 | |
| 		{
 | |
| 			name: "both-runtime-config-and-emulation-forward-compatible-runtime-config-beta2-api",
 | |
| 			resourceExpirationEvaluator: resourceExpirationEvaluator{
 | |
| 				currentVersion:                          apimachineryversion.MajorMinor(1, 20),
 | |
| 				emulationForwardCompatible:              true,
 | |
| 				runtimeConfigEmulationForwardCompatible: true,
 | |
| 			},
 | |
| 			runtimeConfig: map[string]string{
 | |
| 				groupName + "/v2beta2": "true",
 | |
| 			},
 | |
| 			versionedResourcesStorageMap: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageIntroducedIn(1, 20),
 | |
| 				},
 | |
| 				"v2beta1": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 21, 1, 22),
 | |
| 				},
 | |
| 				"v2beta2": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 				},
 | |
| 				"v2": {
 | |
| 					resource1: storageIntroducedIn(1, 23),
 | |
| 					resource2: storageIntroducedIn(1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 			expectedStorage: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageIntroducedIn(1, 20),
 | |
| 				},
 | |
| 				"v2beta2": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 				},
 | |
| 				"v2": {
 | |
| 					resource1: storageIntroducedIn(1, 23),
 | |
| 					resource2: storageIntroducedIn(1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name: "emulation-forward-compatible-runtime-config-beta2-api-resource1-ok",
 | |
| 			resourceExpirationEvaluator: resourceExpirationEvaluator{
 | |
| 				currentVersion:             apimachineryversion.MajorMinor(1, 21),
 | |
| 				emulationForwardCompatible: true,
 | |
| 			},
 | |
| 			runtimeConfig: map[string]string{
 | |
| 				groupName + "/v2beta2": "true",
 | |
| 			},
 | |
| 			versionedResourcesStorageMap: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageIntroducedIn(1, 20),
 | |
| 				},
 | |
| 				"v2beta1": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 21, 1, 22),
 | |
| 				},
 | |
| 				"v2beta2": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 					resource2: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 				},
 | |
| 				"v2": {
 | |
| 					resource1: storageIntroducedIn(1, 23),
 | |
| 					resource2: storageIntroducedIn(1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 			expectedStorage: map[string]map[string]rest.Storage{
 | |
| 				"v1": {
 | |
| 					resource1: storageIntroducedIn(1, 20),
 | |
| 				},
 | |
| 				"v2beta1": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 21, 1, 22),
 | |
| 				},
 | |
| 				"v2beta2": {
 | |
| 					resource1: storageIntroducedAndRemovedIn(1, 22, 1, 23),
 | |
| 				},
 | |
| 				"v2": {
 | |
| 					resource1: storageIntroducedIn(1, 23),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 	}
 | |
| 	for _, tt := range tests {
 | |
| 		t.Run(tt.name, func(t *testing.T) {
 | |
| 			resourceConfig := serverstorage.NewResourceConfig()
 | |
| 			convertor := &dummyConvertor{prioritizedVersions: []schema.GroupVersion{
 | |
| 				{Group: groupName, Version: "v2"}, {Group: groupName, Version: "v1"},
 | |
| 				{Group: groupName, Version: "v2beta2"}, {Group: groupName, Version: "v2beta1"},
 | |
| 				{Group: groupName, Version: "v2alpha1"}}}
 | |
| 			resourceConfig.EnableVersions(convertor.PrioritizedVersionsForGroup(groupName)...)
 | |
| 			resourceConfig, err := resourceconfig.MergeAPIResourceConfigs(resourceConfig, tt.runtimeConfig, convertor)
 | |
| 			require.NoError(t, err)
 | |
| 			err = tt.resourceExpirationEvaluator.RemoveUnavailableKinds(groupName, convertor, tt.versionedResourcesStorageMap, resourceConfig)
 | |
| 			if tt.expectErr {
 | |
| 				require.Error(t, err)
 | |
| 				return
 | |
| 			}
 | |
| 			require.NoError(t, err)
 | |
| 			if !reflect.DeepEqual(tt.expectedStorage, tt.versionedResourcesStorageMap) {
 | |
| 				t.Fatal(dump.Pretty(tt.versionedResourcesStorageMap))
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func Test_shouldRemoveResource(t *testing.T) {
 | |
| 	tests := []struct {
 | |
| 		name              string
 | |
| 		resourcesToRemove sets.String
 | |
| 		resourceName      string
 | |
| 		want              bool
 | |
| 	}{
 | |
| 		{
 | |
| 			name:              "prefix-matches",
 | |
| 			resourcesToRemove: sets.NewString("foo"),
 | |
| 			resourceName:      "foo/scale",
 | |
| 			want:              true,
 | |
| 		},
 | |
| 		{
 | |
| 			name:              "exact-matches",
 | |
| 			resourcesToRemove: sets.NewString("foo"),
 | |
| 			resourceName:      "foo",
 | |
| 			want:              true,
 | |
| 		},
 | |
| 		{
 | |
| 			name:              "no-match",
 | |
| 			resourcesToRemove: sets.NewString("foo"),
 | |
| 			resourceName:      "bar",
 | |
| 			want:              false,
 | |
| 		},
 | |
| 	}
 | |
| 	for _, tt := range tests {
 | |
| 		t.Run(tt.name, func(t *testing.T) {
 | |
| 			if actual := shouldRemoveResourceAndSubresources(tt.resourcesToRemove, tt.resourceName); actual != tt.want {
 | |
| 				t.Errorf("shouldRemoveResourceAndSubresources() = %v, want %v", actual, tt.want)
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 |