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)
|
|
}
|
|
})
|
|
}
|
|
}
|