kubernetes/test/integration/etcd/data.go
dom4ha 88948acc38 Remove v1alpha2 API definitions
Update client-go lister and informer imports to v1alpha3
2026-05-22 12:50:19 +00:00

1129 lines
64 KiB
Go

/*
Copyright 2018 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 etcd
import (
"fmt"
"strings"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apiextensions-apiserver/test/integration/fixtures"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/version"
"k8s.io/apiserver/pkg/util/compatibility"
utilversion "k8s.io/component-base/version"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/test/utils/image"
)
// GetSupportedEmulatedVersions provides the list of supported emulated versions in the etcd data.
// Tests aiming for full coverage of versions should test fixtures of all supported versions.
func GetSupportedEmulatedVersions() []string {
return []string{
compatibility.DefaultKubeEffectiveVersionForTest().BinaryVersion().SubtractMinor(3).String(),
compatibility.DefaultKubeEffectiveVersionForTest().BinaryVersion().SubtractMinor(2).String(),
compatibility.DefaultKubeEffectiveVersionForTest().BinaryVersion().SubtractMinor(1).String(),
compatibility.DefaultKubeEffectiveVersionForTest().BinaryVersion().String(),
}
}
// GetEtcdStorageData returns etcd data for all persisted objects at the latest release version.
// It is exported so that it can be reused across multiple tests.
// It returns a new map on every invocation to prevent different tests from mutating shared state.
func GetEtcdStorageData() map[schema.GroupVersionResource]StorageData {
return GetEtcdStorageDataServedAt(utilversion.DefaultKubeBinaryVersion, false)
}
// GetEtcdStorageDataServedAt returns etcd data for all persisted objects at a particular release version.
// It is exported so that it can be reused across multiple tests.
// It returns a new map on every invocation to prevent different tests from mutating shared state.
func GetEtcdStorageDataServedAt(version string, isEmulation bool) map[schema.GroupVersionResource]StorageData {
return GetEtcdStorageDataForNamespaceServedAt("etcdstoragepathtestnamespace", version, isEmulation)
}
// GetEtcdStorageDataForNamespace returns etcd data for all persisted objects at the latest release version.
// It is exported so that it can be reused across multiple tests.
// It returns a new map on every invocation to prevent different tests from mutating shared state.
// Namespaced objects keys are computed for the specified namespace.
func GetEtcdStorageDataForNamespace(namespace string) map[schema.GroupVersionResource]StorageData {
return GetEtcdStorageDataForNamespaceServedAt(namespace, utilversion.DefaultKubeBinaryVersion, false)
}
// GetEtcdStorageDataForNamespaceServedAt returns etcd data for all persisted objects at a particular release version.
// It is exported so that it can be reused across multiple tests.
// It returns a new map on every invocation to prevent different tests from mutating shared state.
// Namespaced objects keys are computed for the specified namespace.
func GetEtcdStorageDataForNamespaceServedAt(namespace string, v string, isEmulation bool) map[schema.GroupVersionResource]StorageData {
alternateImage := image.GetE2EImage(image.Etcd)
image := image.GetE2EImage(image.BusyBox)
etcdStorageData := map[schema.GroupVersionResource]StorageData{
// k8s.io/kubernetes/pkg/api/v1
gvr("", "v1", "configmaps"): {
Stub: `{"data": {"foo": "bar"}, "metadata": {"name": "cm1"}}`,
ExpectedEtcdPath: "/registry/configmaps/" + namespace + "/cm1",
IntroducedVersion: "1.2",
},
gvr("", "v1", "services"): {
Stub: `{"metadata": {"name": "service1"}, "spec": {"type": "LoadBalancer", "ports": [{"port": 10000, "targetPort": 11000}], "selector": {"test": "data"}}}`,
MutatedStub: `{"spec": {"type": "ClusterIP"}}`,
ExpectedEtcdPath: "/registry/services/specs/" + namespace + "/service1",
IntroducedVersion: "1.0",
StatusStub: `{"status": {"loadBalancer": {"ingress": [{"ip": "127.0.0.1", "ipMode": "VIP"}]}}}`,
MutatedStatusStub: `{"status": {"loadBalancer": {"ingress": [{"ip": "127.0.0.2", "ipMode": "VIP"}]}}}`,
},
gvr("", "v1", "podtemplates"): {
Stub: `{"metadata": {"name": "pt1name"}, "template": {"metadata": {"labels": {"pt": "01"}}, "spec": {"containers": [{"image": "` + image + `", "name": "container9"}]}}}`,
ExpectedEtcdPath: "/registry/podtemplates/" + namespace + "/pt1name",
IntroducedVersion: "1.0",
},
gvr("", "v1", "pods"): {
Stub: `{"metadata": {"name": "pod1"}, "spec": {"containers": [{"image": "` + image + `", "name": "container7", "resources": {"limits": {"cpu": "1M"}, "requests": {"cpu": "1M"}}}]}}`,
MutatedStub: `{"metadata": {"deletionTimestamp": "2020-01-01T00:00:00Z", "ownerReferences":[]}, "spec": {"containers": [{"image": "` + alternateImage + `", "name": "container7"}]}}`,
ExpectedEtcdPath: "/registry/pods/" + namespace + "/pod1",
IntroducedVersion: "1.0",
},
gvr("", "v1", "endpoints"): {
Stub: `{"metadata": {"name": "ep1name"}, "subsets": [{"addresses": [{"hostname": "bar-001", "ip": "192.168.3.1"}], "ports": [{"port": 8000}]}]}`,
ExpectedEtcdPath: "/registry/services/endpoints/" + namespace + "/ep1name",
IntroducedVersion: "1.0",
},
gvr("", "v1", "resourcequotas"): {
Stub: `{"metadata": {"name": "rq1name"}, "spec": {"hard": {"cpu": "5M"}}}`,
MutatedStub: `{"spec": {"hard": {"cpu": "25M"}}}`,
ExpectedEtcdPath: "/registry/resourcequotas/" + namespace + "/rq1name",
IntroducedVersion: "1.0",
StatusStub: `{"status": {"used": {"cpu": "5M"}}}`,
MutatedStatusStub: `{"status": {"used": {"cpu": "25M"}}}`,
},
gvr("", "v1", "limitranges"): {
Stub: `{"metadata": {"name": "lr1name"}, "spec": {"limits": [{"type": "Pod"}]}}`,
ExpectedEtcdPath: "/registry/limitranges/" + namespace + "/lr1name",
IntroducedVersion: "1.0",
},
gvr("", "v1", "namespaces"): {
Stub: `{"metadata": {"name": "namespace1"}, "spec": {"finalizers": ["kubernetes"]}}`,
MutatedStub: `{"spec": {"finalizers": ["kubernetes2"]}}`,
ExpectedEtcdPath: "/registry/namespaces/namespace1",
IntroducedVersion: "1.0",
},
gvr("", "v1", "nodes"): {
Stub: `{"metadata": {"name": "node1"}, "spec": {"unschedulable": true}}`,
MutatedStub: `{"spec": {"unschedulable": false}}`,
ExpectedEtcdPath: "/registry/minions/node1",
IntroducedVersion: "1.0",
},
gvr("", "v1", "persistentvolumes"): {
Stub: `{"metadata": {"name": "pv1name"}, "spec": {"accessModes": ["ReadWriteOnce"], "capacity": {"storage": "3M"}, "hostPath": {"path": "/tmp/test/"}}}`,
MutatedStub: `{"spec": {"capacity": {"storage": "23M"}}}`,
ExpectedEtcdPath: "/registry/persistentvolumes/pv1name",
IntroducedVersion: "1.0",
StatusStub: `{"status": {"message": "hello"}}`,
MutatedStatusStub: `{"status": {"message": "hello2"}}`,
},
gvr("", "v1", "events"): {
Stub: `{"involvedObject": {"namespace": "` + namespace + `"}, "message": "some data here", "metadata": {"name": "event1"}}`,
ExpectedEtcdPath: "/registry/events/" + namespace + "/event1",
IntroducedVersion: "1.0",
},
gvr("", "v1", "persistentvolumeclaims"): {
Stub: `{"metadata": {"name": "pvc1"}, "spec": {"accessModes": ["ReadWriteOnce"], "resources": {"limits": {"storage": "1M"}, "requests": {"storage": "2M"}}, "selector": {"matchLabels": {"pvc": "stuff"}}}}`,
MutatedStub: `{"spec": {"resources": {"requests": {"storage": "21M"}}}}`,
ExpectedEtcdPath: "/registry/persistentvolumeclaims/" + namespace + "/pvc1",
IntroducedVersion: "1.0",
},
gvr("", "v1", "serviceaccounts"): {
Stub: `{"metadata": {"name": "sa1name"}, "secrets": [{"name": "secret00"}]}`,
ExpectedEtcdPath: "/registry/serviceaccounts/" + namespace + "/sa1name",
IntroducedVersion: "1.0",
},
gvr("", "v1", "secrets"): {
Stub: `{"data": {"key": "ZGF0YSBmaWxl"}, "metadata": {"name": "secret1"}}`,
ExpectedEtcdPath: "/registry/secrets/" + namespace + "/secret1",
IntroducedVersion: "1.0",
},
gvr("", "v1", "replicationcontrollers"): {
Stub: `{"metadata": {"name": "rc1"}, "spec": {"selector": {"new": "stuff"}, "template": {"metadata": {"labels": {"new": "stuff"}}, "spec": {"containers": [{"image": "` + image + `", "name": "container8"}]}}}}`,
MutatedStub: `{"spec": {"selector": {"new": "stuff2"}}}`,
ExpectedEtcdPath: "/registry/controllers/" + namespace + "/rc1",
IntroducedVersion: "1.0",
},
// --
// k8s.io/kubernetes/pkg/apis/apps/v1
gvr("apps", "v1", "daemonsets"): {
Stub: `{"metadata": {"name": "ds6"}, "spec": {"selector": {"matchLabels": {"a": "b"}}, "template": {"metadata": {"labels": {"a": "b"}}, "spec": {"containers": [{"image": "` + image + `", "name": "container6"}]}}}}`,
MutatedStub: `{"spec": {"template": {"spec": {"containers": [{"image": "` + alternateImage + `", "name": "container6"}]}}}}`,
ExpectedEtcdPath: "/registry/daemonsets/" + namespace + "/ds6",
IntroducedVersion: "1.9",
},
gvr("apps", "v1", "deployments"): {
Stub: `{"metadata": {"name": "deployment4"}, "spec": {"selector": {"matchLabels": {"f": "z"}}, "template": {"metadata": {"labels": {"f": "z"}}, "spec": {"containers": [{"image": "` + image + `", "name": "container6"}]}}}}`,
MutatedStub: `{"metadata": {"labels": {"a":"c"}}, "spec": {"template": {"spec": {"containers": [{"image": "` + alternateImage + `", "name": "container6"}]}}}}`,
ExpectedEtcdPath: "/registry/deployments/" + namespace + "/deployment4",
IntroducedVersion: "1.9",
},
gvr("apps", "v1", "statefulsets"): {
Stub: `{"metadata": {"name": "ss3"}, "spec": {"selector": {"matchLabels": {"a": "b"}}, "template": {"metadata": {"labels": {"a": "b"}}, "spec": {"restartPolicy": "Always", "terminationGracePeriodSeconds": 30, "containers": [{"image": "` + image + `", "name": "container6", "terminationMessagePolicy": "File"}]}}}}`,
MutatedStub: `{"spec": {"selector": {"matchLabels": {"a2": "b2"}}}}`,
ExpectedEtcdPath: "/registry/statefulsets/" + namespace + "/ss3",
IntroducedVersion: "1.9",
},
gvr("apps", "v1", "replicasets"): {
Stub: `{"metadata": {"name": "rs3"}, "spec": {"selector": {"matchLabels": {"g": "h"}}, "template": {"metadata": {"labels": {"g": "h"}}, "spec": {"containers": [{"image": "` + image + `", "name": "container4"}]}}}}`,
MutatedStub: `{"spec": {"template": {"spec": {"containers": [{"image": "` + alternateImage + `", "name": "container4"}]}}}}`,
ExpectedEtcdPath: "/registry/replicasets/" + namespace + "/rs3",
IntroducedVersion: "1.9",
},
gvr("apps", "v1", "controllerrevisions"): {
Stub: `{"metadata":{"name":"crs3"},"data":{"name":"abc","namespace":"default","Spec":{"Replicas":0,"Selector":{"matchLabels":{"foo":"bar"}},"Template":{"labels":{"foo":"bar"},"Spec":{"Volumes":null,"InitContainers":null,"Containers":null,"RestartPolicy":"Always","TerminationGracePeriodSeconds":null,"ActiveDeadlineSeconds":null,"DNSPolicy":"ClusterFirst","NodeSelector":null,"ServiceAccountName":"","AutomountServiceAccountToken":null,"NodeName":"","SecurityContext":null,"ImagePullSecrets":null,"Hostname":"","Subdomain":"","Affinity":null,"SchedulerName":"","Tolerations":null,"HostAliases":null}},"VolumeClaimTemplates":null,"ServiceName":""},"Status":{"ObservedGeneration":null,"Replicas":0}},"revision":0}`,
ExpectedEtcdPath: "/registry/controllerrevisions/" + namespace + "/crs3",
IntroducedVersion: "1.9",
},
// --
// k8s.io/kubernetes/pkg/apis/autoscaling/v1
gvr("autoscaling", "v1", "horizontalpodautoscalers"): {
Stub: `{"metadata": {"name": "hpa2"}, "spec": {"maxReplicas": 3, "scaleTargetRef": {"kind": "something", "name": "cross", "apiVersion": "apps/v1"}}}`,
MutatedStub: `{"spec": {"maxReplicas": 23}}`,
ExpectedEtcdPath: "/registry/horizontalpodautoscalers/" + namespace + "/hpa2",
ExpectedGVK: gvkP("autoscaling", "v2", "HorizontalPodAutoscaler"),
IntroducedVersion: "1.2",
StatusStub: `{"status": {"currentReplicas": 5}}`,
MutatedStatusStub: `{"status": {"currentReplicas": 25}}`,
},
// --
// k8s.io/kubernetes/pkg/apis/autoscaling/v2
gvr("autoscaling", "v2", "horizontalpodautoscalers"): {
Stub: `{"metadata": {"name": "hpa4"}, "spec": {"maxReplicas": 3, "scaleTargetRef": {"kind": "something", "name": "cross", "apiVersion": "apps/v1"}}}`,
MutatedStub: `{"spec": {"maxReplicas": 23}}`,
ExpectedEtcdPath: "/registry/horizontalpodautoscalers/" + namespace + "/hpa4",
IntroducedVersion: "1.23",
StatusStub: `{"status": {"currentReplicas": 5}}`,
MutatedStatusStub: `{"status": {"currentReplicas": 25}}`,
},
// --
// k8s.io/kubernetes/pkg/apis/batch/v1
gvr("batch", "v1", "jobs"): {
Stub: `{"metadata": {"name": "job1"}, "spec": {"manualSelector": true, "selector": {"matchLabels": {"controller-uid": "uid1"}}, "template": {"metadata": {"labels": {"controller-uid": "uid1"}}, "spec": {"containers": [{"image": "` + image + `", "name": "container1"}], "dnsPolicy": "ClusterFirst", "restartPolicy": "Never"}}}}`,
MutatedStub: `{"spec": {"template": {"spec": {"containers": [{"image": "` + alternateImage + `", "name": "container1"}]}}}}`,
ExpectedEtcdPath: "/registry/jobs/" + namespace + "/job1",
IntroducedVersion: "1.2",
},
gvr("batch", "v1", "cronjobs"): {
Stub: `{"metadata": {"name": "cjv1"}, "spec": {"jobTemplate": {"spec": {"template": {"metadata": {"labels": {"controller-uid": "uid0"}}, "spec": {"containers": [{"image": "` + image + `", "name": "container0"}], "dnsPolicy": "ClusterFirst", "restartPolicy": "Never"}}}}, "schedule": "* * * * *"}}`,
MutatedStub: `{"spec": {"jobTemplate": {"spec": {"template": {"spec": {"containers": [{"image": "` + alternateImage + `", "name": "container0"}]}}}}}}`,
ExpectedEtcdPath: "/registry/cronjobs/" + namespace + "/cjv1",
IntroducedVersion: "1.21",
StatusStub: `{"status": {"lastScheduleTime": null}}`,
MutatedStatusStub: `{"status": {"lastScheduleTime": "2020-01-01T00:00:00Z"}}`,
},
// --
// k8s.io/kubernetes/pkg/apis/certificates/v1
gvr("certificates.k8s.io", "v1", "certificatesigningrequests"): {
Stub: `{"metadata": {"name": "csr2"}, "spec": {"signerName":"example.com/signer", "usages":["any"], "request": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0KTUlJQnlqQ0NBVE1DQVFBd2dZa3hDekFKQmdOVkJBWVRBbFZUTVJNd0VRWURWUVFJRXdwRFlXeHBabTl5Ym1saApNUll3RkFZRFZRUUhFdzFOYjNWdWRHRnBiaUJXYVdWM01STXdFUVlEVlFRS0V3cEhiMjluYkdVZ1NXNWpNUjh3CkhRWURWUVFMRXhaSmJtWnZjbTFoZEdsdmJpQlVaV05vYm05c2IyZDVNUmN3RlFZRFZRUURFdzUzZDNjdVoyOXYKWjJ4bExtTnZiVENCbnpBTkJna3Foa2lHOXcwQkFRRUZBQU9CalFBd2dZa0NnWUVBcFp0WUpDSEo0VnBWWEhmVgpJbHN0UVRsTzRxQzAzaGpYK1prUHl2ZFlkMVE0K3FiQWVUd1htQ1VLWUhUaFZSZDVhWFNxbFB6eUlCd2llTVpyCldGbFJRZGRaMUl6WEFsVlJEV3dBbzYwS2VjcWVBWG5uVUsrNWZYb1RJL1VnV3NocmU4dEoreC9UTUhhUUtSL0oKY0lXUGhxYVFoc0p1elpidkFkR0E4MEJMeGRNQ0F3RUFBYUFBTUEwR0NTcUdTSWIzRFFFQkJRVUFBNEdCQUlobAo0UHZGcStlN2lwQVJnSTVaTStHWng2bXBDejQ0RFRvMEprd2ZSRGYrQnRyc2FDMHE2OGVUZjJYaFlPc3E0ZmtIClEwdUEwYVZvZzNmNWlKeENhM0hwNWd4YkpRNnpWNmtKMFRFc3VhYU9oRWtvOXNkcENvUE9uUkJtMmkvWFJEMkQKNmlOaDhmOHowU2hHc0ZxakRnRkh5RjNvK2xVeWorVUM2SDFRVzdibgotLS0tLUVORCBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0="}}`,
MutatedStub: `{}`,
ExpectedEtcdPath: "/registry/certificatesigningrequests/csr2",
IntroducedVersion: "1.19",
},
// --
// k8s.io/kubernetes/pkg/apis/certificates/v1alpha1
gvr("certificates.k8s.io", "v1alpha1", "clustertrustbundles"): {
Stub: `{"metadata": {"name": "example.com:signer:abcd"}, "spec": {"signerName":"example.com/signer", "trustBundle": "-----BEGIN CERTIFICATE-----\nMIIBBDCBt6ADAgECAgEAMAUGAytlcDAQMQ4wDAYDVQQDEwVyb290MTAiGA8wMDAx\nMDEwMTAwMDAwMFoYDzAwMDEwMTAxMDAwMDAwWjAQMQ4wDAYDVQQDEwVyb290MTAq\nMAUGAytlcAMhAF2MoFeGa97gK2NGT1h6p1/a1GlMXAXbcjI/OShyIobPozIwMDAP\nBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTWDdK2CNQiHqRjPaAWYPPtIykQgjAF\nBgMrZXADQQCtom9WGl7m2SAa4tXM9Soo/mbInBsRhn187BMoqTAHInHchKup5/3y\nl1tYJSZZsEXnXrCvw2qLCBNif6+2YYgE\n-----END CERTIFICATE-----\n"}}`,
ExpectedEtcdPath: "/registry/clustertrustbundles/example.com:signer:abcd",
ExpectedGVK: gvkP("certificates.k8s.io", "v1beta1", "ClusterTrustBundle"),
IntroducedVersion: "1.26",
RemovedVersion: "1.37",
},
gvr("certificates.k8s.io", "v1alpha1", "podcertificaterequests"): {
Stub: `{"metadata": {"name": "req-1"}, "spec": {"signerName":"example.com/signer", "podName":"pod-1", "podUID":"pod-uid-1", "serviceAccountName":"sa-1", "serviceAccountUID":"sa-uid-1", "nodeName":"node-1", "nodeUID":"node-uid-1", "maxExpirationSeconds":86400, "pkixPublicKey":"MCowBQYDK2VwAyEA5g+rk9q/hjojtc2nwHJ660RdX5w1f4AK0/kP391QyLY=", "proofOfPossession":"SuGHX7SMyPHuN5cD5wjKLXGNbhdlCYUnTH65JkTx17iWlLynQ/g9GiTYObftSHNzqRh0ofdgAGqK6a379O7RBw=="}}`,
ExpectedEtcdPath: "/registry/podcertificaterequests/" + namespace + "/req-1",
ExpectedGVK: gvkP("certificates.k8s.io", "v1alpha1", "PodCertificateRequest"),
IntroducedVersion: "1.34",
RemovedVersion: "1.35",
},
// --
// k8s.io/kubernetes/pkg/apis/certificates/v1beta1
gvr("certificates.k8s.io", "v1beta1", "clustertrustbundles"): {
Stub: `{"metadata": {"name": "example.com:signer:abc"}, "spec": {"signerName":"example.com/signer", "trustBundle": "-----BEGIN CERTIFICATE-----\nMIIBBDCBt6ADAgECAgEAMAUGAytlcDAQMQ4wDAYDVQQDEwVyb290MTAiGA8wMDAx\nMDEwMTAwMDAwMFoYDzAwMDEwMTAxMDAwMDAwWjAQMQ4wDAYDVQQDEwVyb290MTAq\nMAUGAytlcAMhAF2MoFeGa97gK2NGT1h6p1/a1GlMXAXbcjI/OShyIobPozIwMDAP\nBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTWDdK2CNQiHqRjPaAWYPPtIykQgjAF\nBgMrZXADQQCtom9WGl7m2SAa4tXM9Soo/mbInBsRhn187BMoqTAHInHchKup5/3y\nl1tYJSZZsEXnXrCvw2qLCBNif6+2YYgE\n-----END CERTIFICATE-----\n"}}`,
ExpectedEtcdPath: "/registry/clustertrustbundles/example.com:signer:abc",
IntroducedVersion: "1.33",
RemovedVersion: "1.39",
},
gvr("certificates.k8s.io", "v1beta1", "podcertificaterequests"): {
Stub: `{"metadata": {"name": "req-1"}, "spec": {"signerName":"example.com/signer", "podName":"pod-1", "podUID":"pod-uid-1", "serviceAccountName":"sa-1", "serviceAccountUID":"sa-uid-1", "nodeName":"node-1", "nodeUID":"node-uid-1", "maxExpirationSeconds":86400, "pkixPublicKey":"MCowBQYDK2VwAyEA5g+rk9q/hjojtc2nwHJ660RdX5w1f4AK0/kP391QyLY=", "proofOfPossession":"SuGHX7SMyPHuN5cD5wjKLXGNbhdlCYUnTH65JkTx17iWlLynQ/g9GiTYObftSHNzqRh0ofdgAGqK6a379O7RBw=="}}`,
ExpectedEtcdPath: "/registry/podcertificaterequests/" + namespace + "/req-1",
ExpectedGVK: gvkP("certificates.k8s.io", "v1beta1", "PodCertificateRequest"),
StatusStub: `{"status": {"conditions": [{"type": "Denied", "status":"True", "lastTransitionTime": "2020-01-01T00:00:00Z", "reason": "TestReason", "message": "test message"}]}}`,
MutatedStatusStub: `{"status": {"conditions": [{"type": "Denied", "status":"False", "lastTransitionTime": "2020-01-01T00:00:00Z", "reason": "TestReason", "message": "test message"}]}}`,
IntroducedVersion: "1.35",
RemovedVersion: "1.39",
},
// --
// k8s.io/kubernetes/pkg/apis/coordination/v1
gvr("coordination.k8s.io", "v1", "leases"): {
Stub: `{"metadata": {"name": "leasev1"}, "spec": {"holderIdentity": "holder", "leaseDurationSeconds": 5}}`,
ExpectedEtcdPath: "/registry/leases/" + namespace + "/leasev1",
IntroducedVersion: "1.14",
},
// --
// k8s.io/kubernetes/pkg/apis/coordination/v1beta1
gvr("coordination.k8s.io", "v1beta1", "leasecandidates"): {
Stub: `{"metadata": {"name": "leasecandidatev1beta1"}, "spec": {"leaseName": "lease", "binaryVersion": "0.1.0", "emulationVersion": "0.1.0", "strategy": "OldestEmulationVersion"}}`,
ExpectedEtcdPath: "/registry/leasecandidates/" + namespace + "/leasecandidatev1beta1",
IntroducedVersion: "1.33",
RemovedVersion: "1.39",
},
// --
// k8s.io/kubernetes/pkg/apis/coordination/v1alpha2
gvr("coordination.k8s.io", "v1alpha2", "leasecandidates"): {
Stub: `{"metadata": {"name": "leasecandidatev1alpha2"}, "spec": {"leaseName": "lease", "binaryVersion": "0.1.0", "emulationVersion": "0.1.0", "strategy": "OldestEmulationVersion"}}`,
ExpectedEtcdPath: "/registry/leasecandidates/" + namespace + "/leasecandidatev1alpha2",
ExpectedGVK: gvkP("coordination.k8s.io", "v1beta1", "LeaseCandidate"),
IntroducedVersion: "1.32",
RemovedVersion: "1.38",
},
// --
// k8s.io/kubernetes/pkg/apis/discovery/v1
gvr("discovery.k8s.io", "v1", "endpointslices"): {
Stub: `{"metadata": {"name": "slicev1"}, "addressType": "IPv4", "protocol": "TCP", "ports": [], "endpoints": []}`,
ExpectedEtcdPath: "/registry/endpointslices/" + namespace + "/slicev1",
IntroducedVersion: "1.21",
},
// --
// k8s.io/kubernetes/pkg/apis/events/v1
gvr("events.k8s.io", "v1", "events"): {
Stub: `{"metadata": {"name": "event3"}, "regarding": {"namespace": "` + namespace + `"}, "note": "some data here", "eventTime": "2017-08-09T15:04:05.000000Z", "reportingInstance": "node-xyz", "reportingController": "k8s.io/my-controller", "action": "DidNothing", "reason": "Laziness", "type": "Normal"}`,
ExpectedEtcdPath: "/registry/events/" + namespace + "/event3",
ExpectedGVK: gvkP("", "v1", "Event"),
IntroducedVersion: "1.19",
},
// --
// k8s.io/kubernetes/pkg/apis/networking/v1
gvr("networking.k8s.io", "v1", "ingresses"): {
Stub: `{"metadata": {"name": "ingress3"}, "spec": {"defaultBackend": {"service":{"name":"service", "port":{"number": 5000}}}}}`,
MutatedStub: `{"spec": {"defaultBackend": {"service": {"name": "service2"}}}}`,
ExpectedEtcdPath: "/registry/ingress/" + namespace + "/ingress3",
IntroducedVersion: "1.19",
StatusStub: `{"status": {"loadBalancer": {"ingress": [{"ip": "127.0.0.1"}]}}}`,
MutatedStatusStub: `{"status": {"loadBalancer": {"ingress": [{"ip": "127.0.0.2"}]}}}`,
},
gvr("networking.k8s.io", "v1", "ingressclasses"): {
Stub: `{"metadata": {"name": "ingressclass3"}, "spec": {"controller": "example.com/controller"}}`,
ExpectedEtcdPath: "/registry/ingressclasses/ingressclass3",
IntroducedVersion: "1.19",
},
gvr("networking.k8s.io", "v1", "networkpolicies"): {
Stub: `{"metadata": {"name": "np2"}, "spec": {"podSelector": {"matchLabels": {"e": "f"}}}}`,
ExpectedEtcdPath: "/registry/networkpolicies/" + namespace + "/np2",
IntroducedVersion: "1.7",
},
gvr("networking.k8s.io", "v1", "ipaddresses"): {
Stub: `{"metadata": {"name": "192.168.2.3"}, "spec": {"parentRef": {"resource": "services","name": "test", "namespace": "ns"}}}`,
ExpectedEtcdPath: "/registry/ipaddresses/192.168.2.3",
ExpectedGVK: gvkP("networking.k8s.io", "v1", "IPAddress"),
IntroducedVersion: "1.33",
},
gvr("networking.k8s.io", "v1", "servicecidrs"): {
Stub: `{"metadata": {"name": "range-b2"}, "spec": {"cidrs": ["192.168.0.0/16","fd00:1::/120"]}}`,
MutatedStub: `{}`,
ExpectedEtcdPath: "/registry/servicecidrs/range-b2",
ExpectedGVK: gvkP("networking.k8s.io", "v1", "ServiceCIDR"),
IntroducedVersion: "1.33",
MutatedStatusStub: `{"status": {"conditions":[{"type":"Accepted","status":"True","lastTransitionTime":"2020-01-01T00:00:00Z","reason":"RuleApplied","message":"Rule was applied"}]}}`,
},
// --
// k8s.io/kubernetes/pkg/apis/networking/v1beta1
gvr("networking.k8s.io", "v1beta1", "ipaddresses"): {
Stub: `{"metadata": {"name": "192.168.1.3"}, "spec": {"parentRef": {"resource": "services","name": "test", "namespace": "ns"}}}`,
ExpectedEtcdPath: "/registry/ipaddresses/192.168.1.3",
ExpectedGVK: gvkP("networking.k8s.io", "v1", "IPAddress"),
IntroducedVersion: "1.31",
RemovedVersion: "1.37",
},
// --
// k8s.io/kubernetes/pkg/apis/networking/v1beta1
gvr("networking.k8s.io", "v1beta1", "servicecidrs"): {
Stub: `{"metadata": {"name": "range-b1"}, "spec": {"cidrs": ["192.168.0.0/16","fd00:1::/120"]}}`,
MutatedStub: `{}`,
ExpectedEtcdPath: "/registry/servicecidrs/range-b1",
ExpectedGVK: gvkP("networking.k8s.io", "v1", "ServiceCIDR"),
IntroducedVersion: "1.31",
RemovedVersion: "1.37",
MutatedStatusStub: `{"status": {"conditions":[{"type":"Accepted","status":"True","lastTransitionTime":"2020-01-01T00:00:00Z","reason":"RuleApplied","message":"Rule was applied"}]}}`,
},
// --
// k8s.io/kubernetes/pkg/apis/policy/v1
gvr("policy", "v1", "poddisruptionbudgets"): {
Stub: `{"metadata": {"name": "pdbv1"}, "spec": {"selector": {"matchLabels": {"anokkey": "anokvalue"}}}}`,
MutatedStub: `{"spec": {"selector": {"matchLabels": {"anokkey2": "anokvalue"}}}}`,
ExpectedEtcdPath: "/registry/poddisruptionbudgets/" + namespace + "/pdbv1",
IntroducedVersion: "1.21",
StatusStub: `{"status": {"currentHealthy": 5}}`,
MutatedStatusStub: `{"status": {"currentHealthy": 25}}`,
},
// --
// k8s.io/kubernetes/pkg/apis/storagemigration/v1beta1
gvr("storagemigration.k8s.io", "v1beta1", "storageversionmigrations"): {
Stub: `{"metadata": {"name": "test-migration"}, "spec":{"resource": {"group": "test-group", "resource": "test-resource"}}}`,
ExpectedEtcdPath: "/registry/storageversionmigrations/test-migration",
IntroducedVersion: "1.35",
RemovedVersion: "1.41",
},
// --
// k8s.io/kubernetes/pkg/apis/flowcontrol/v1
gvr("flowcontrol.apiserver.k8s.io", "v1", "flowschemas"): {
Stub: `{"metadata": {"name": "fs-3"}, "spec": {"priorityLevelConfiguration": {"name": "name1"}}}`,
MutatedStub: `{"metadata": {"labels":{"a":"c"}}, "spec": {"priorityLevelConfiguration": {"name": "name2"}}}`,
ExpectedEtcdPath: "/registry/flowschemas/fs-3",
IntroducedVersion: "1.29",
},
// --
// k8s.io/kubernetes/pkg/apis/flowcontrol/v1
gvr("flowcontrol.apiserver.k8s.io", "v1", "prioritylevelconfigurations"): {
Stub: `{"metadata": {"name": "conf5"}, "spec": {"type": "Limited", "limited": {"nominalConcurrencyShares":3, "limitResponse": {"type": "Reject"}}}}`,
MutatedStub: `{"metadata": {"labels":{"a":"c"}}, "spec": {"limited": {"nominalConcurrencyShares": 23}}}`,
ExpectedEtcdPath: "/registry/prioritylevelconfigurations/conf5",
IntroducedVersion: "1.29",
},
// --
// k8s.io/kubernetes/pkg/apis/storage/v1
gvr("storage.k8s.io", "v1", "volumeattachments"): {
Stub: `{"metadata": {"name": "va3"}, "spec": {"attacher": "gce", "nodeName": "localhost", "source": {"persistentVolumeName": "pv3"}}}`,
MutatedStub: `{"metadata": {"name": "va3"}, "spec": {"nodeName": "localhost2"}}`,
ExpectedEtcdPath: "/registry/volumeattachments/va3",
IntroducedVersion: "1.13",
StatusStub: `{"status": {"attached": true}}`,
MutatedStatusStub: `{"status": {"attached": false}}`,
},
// --
// k8s.io/kubernetes/pkg/apis/storage/v1beta1
gvr("storage.k8s.io", "v1beta1", "volumeattributesclasses"): {
Stub: `{"metadata": {"name": "vac2"}, "driverName": "example.com/driver", "parameters": {"foo": "bar"}}`,
ExpectedEtcdPath: "/registry/volumeattributesclasses/vac2",
ExpectedGVK: gvkP("storage.k8s.io", "v1", "VolumeAttributesClass"),
IntroducedVersion: "1.31",
RemovedVersion: "1.37",
},
// --
// k8s.io/kubernetes/pkg/apis/storage/v1
gvr("storage.k8s.io", "v1", "volumeattributesclasses"): {
Stub: `{"metadata": {"name": "vac3"}, "driverName": "example.com/driver", "parameters": {"foo": "bar"}}`,
ExpectedEtcdPath: "/registry/volumeattributesclasses/vac3",
ExpectedGVK: gvkP("storage.k8s.io", "v1", "VolumeAttributesClass"),
IntroducedVersion: "1.34",
},
// --
// k8s.io/kubernetes/pkg/apis/storage/v1
gvr("storage.k8s.io", "v1", "storageclasses"): {
Stub: `{"metadata": {"name": "sc2"}, "provisioner": "aws"}`,
ExpectedEtcdPath: "/registry/storageclasses/sc2",
IntroducedVersion: "1.6",
},
gvr("storage.k8s.io", "v1", "csistoragecapacities"): {
Stub: `{"metadata": {"name": "csc-12345-3"}, "storageClassName": "sc1"}`,
ExpectedEtcdPath: "/registry/csistoragecapacities/" + namespace + "/csc-12345-3",
IntroducedVersion: "1.24",
},
// --
// k8s.io/kubernetes/pkg/apis/rbac/v1
gvr("rbac.authorization.k8s.io", "v1", "roles"): {
Stub: `{"metadata": {"name": "role3"}, "rules": [{"apiGroups": ["v1"], "resources": ["events"], "verbs": ["watch"]}]}`,
ExpectedEtcdPath: "/registry/roles/" + namespace + "/role3",
IntroducedVersion: "1.8",
},
gvr("rbac.authorization.k8s.io", "v1", "clusterroles"): {
Stub: `{"metadata": {"name": "crole3"}, "rules": [{"nonResourceURLs": ["/version"], "verbs": ["get"]}]}`,
ExpectedEtcdPath: "/registry/clusterroles/crole3",
IntroducedVersion: "1.8",
},
gvr("rbac.authorization.k8s.io", "v1", "rolebindings"): {
Stub: `{"metadata": {"name": "roleb3"}, "roleRef": {"apiGroup": "rbac.authorization.k8s.io", "kind": "ClusterRole", "name": "somecr"}, "subjects": [{"apiVersion": "rbac.authorization.k8s.io/v1alpha1", "kind": "Group", "name": "system:authenticated"}]}`,
ExpectedEtcdPath: "/registry/rolebindings/" + namespace + "/roleb3",
IntroducedVersion: "1.8",
},
gvr("rbac.authorization.k8s.io", "v1", "clusterrolebindings"): {
Stub: `{"metadata": {"name": "croleb3"}, "roleRef": {"apiGroup": "rbac.authorization.k8s.io", "kind": "ClusterRole", "name": "somecr"}, "subjects": [{"apiVersion": "rbac.authorization.k8s.io/v1alpha1", "kind": "Group", "name": "system:authenticated"}]}`,
ExpectedEtcdPath: "/registry/clusterrolebindings/croleb3",
IntroducedVersion: "1.8",
},
// --
// k8s.io/kubernetes/pkg/apis/admissionregistration/v1
gvr("admissionregistration.k8s.io", "v1", "validatingwebhookconfigurations"): {
Stub: `{"metadata":{"name":"hook2"},"webhooks":[{"name":"externaladmissionhook.k8s.io","clientConfig":{"service":{"namespace":"ns","name":"n"},"caBundle":null},"rules":[{"operations":["CREATE"],"apiGroups":["group"],"apiVersions":["version"],"resources":["resource"]}],"failurePolicy":"Ignore","sideEffects":"None","admissionReviewVersions":["v1beta1"]}]}`,
ExpectedEtcdPath: "/registry/validatingwebhookconfigurations/hook2",
IntroducedVersion: "1.16",
},
gvr("admissionregistration.k8s.io", "v1", "mutatingwebhookconfigurations"): {
Stub: `{"metadata":{"name":"hook2"},"webhooks":[{"name":"externaladmissionhook.k8s.io","clientConfig":{"service":{"namespace":"ns","name":"n"},"caBundle":null},"rules":[{"operations":["CREATE"],"apiGroups":["group"],"apiVersions":["version"],"resources":["resource"]}],"failurePolicy":"Ignore","sideEffects":"None","admissionReviewVersions":["v1beta1"]}]}`,
ExpectedEtcdPath: "/registry/mutatingwebhookconfigurations/hook2",
IntroducedVersion: "1.16",
},
gvr("admissionregistration.k8s.io", "v1", "validatingadmissionpolicies"): {
Stub: `{"metadata":{"name":"vap1"},"spec":{"paramKind":{"apiVersion":"test.example.com/v1","kind":"Example"},"matchConstraints":{"resourceRules": [{"resourceNames": ["fakeName"], "apiGroups":["apps"],"apiVersions":["v1"],"operations":["CREATE", "UPDATE"], "resources":["deployments"]}]},"validations":[{"expression":"object.spec.replicas <= params.maxReplicas","message":"Too many replicas"}]}}`,
MutatedStub: `{"metadata": {"labels": {"a":"c"}}, "spec": {"paramKind": {"apiVersion": "apps/v1", "kind": "Deployment"}}}`,
ExpectedEtcdPath: "/registry/validatingadmissionpolicies/vap1",
IntroducedVersion: "1.30",
},
gvr("admissionregistration.k8s.io", "v1", "validatingadmissionpolicybindings"): {
Stub: `{"metadata":{"name":"pb1"},"spec":{"policyName":"replicalimit-policy.example.com","paramRef":{"name":"replica-limit-test.example.com","parameterNotFoundAction":"Deny"},"validationActions":["Deny"]}}`,
ExpectedEtcdPath: "/registry/validatingadmissionpolicybindings/pb1",
IntroducedVersion: "1.30",
},
gvr("admissionregistration.k8s.io", "v1", "mutatingadmissionpolicies"): {
Stub: `{"metadata":{"name":"map1v1"},"spec":{"paramKind":{"apiVersion":"test.example.com/v1","kind":"Example"},"matchConstraints":{"resourceRules": [{"resourceNames": ["fakeName"], "apiGroups":["apps"],"apiVersions":["v1"],"operations":["CREATE", "UPDATE"], "resources":["deployments"]}]},"reinvocationPolicy": "IfNeeded","mutations":[{"applyConfiguration": {"expression":"Object{metadata: Object.metadata{labels: {'example':'true'}}}"}, "patchType":"ApplyConfiguration"}]}}`,
ExpectedEtcdPath: "/registry/mutatingadmissionpolicies/map1v1",
ExpectedGVK: gvkP("admissionregistration.k8s.io", "v1beta1", "MutatingAdmissionPolicy"),
IntroducedVersion: "1.36",
},
gvr("admissionregistration.k8s.io", "v1", "mutatingadmissionpolicybindings"): {
Stub: `{"metadata":{"name":"mpb1v1"},"spec":{"policyName":"replicalimit-policy.example.com","paramRef":{"name":"replica-limit-test.example.com", "parameterNotFoundAction": "Allow"}}}`,
ExpectedEtcdPath: "/registry/mutatingadmissionpolicybindings/mpb1v1",
ExpectedGVK: gvkP("admissionregistration.k8s.io", "v1beta1", "MutatingAdmissionPolicyBinding"),
IntroducedVersion: "1.36",
},
// --
// k8s.io/kubernetes/pkg/apis/admissionregistration/v1beta1
gvr("admissionregistration.k8s.io", "v1beta1", "validatingadmissionpolicies"): {
Stub: `{"metadata":{"name":"vap1b1"},"spec":{"paramKind":{"apiVersion":"test.example.com/v1","kind":"Example"},"matchConstraints":{"resourceRules": [{"resourceNames": ["fakeName"], "apiGroups":["apps"],"apiVersions":["v1"],"operations":["CREATE", "UPDATE"], "resources":["deployments"]}]},"validations":[{"expression":"object.spec.replicas <= params.maxReplicas","message":"Too many replicas"}]}}`,
ExpectedEtcdPath: "/registry/validatingadmissionpolicies/vap1b1",
ExpectedGVK: gvkP("admissionregistration.k8s.io", "v1", "ValidatingAdmissionPolicy"),
IntroducedVersion: "1.28",
RemovedVersion: "1.34",
},
gvr("admissionregistration.k8s.io", "v1beta1", "validatingadmissionpolicybindings"): {
Stub: `{"metadata":{"name":"pb1b1"},"spec":{"policyName":"replicalimit-policy.example.com","paramRef":{"name":"replica-limit-test.example.com","parameterNotFoundAction":"Deny"},"validationActions":["Deny"]}}`,
ExpectedEtcdPath: "/registry/validatingadmissionpolicybindings/pb1b1",
ExpectedGVK: gvkP("admissionregistration.k8s.io", "v1", "ValidatingAdmissionPolicyBinding"),
IntroducedVersion: "1.28",
RemovedVersion: "1.34",
},
gvr("admissionregistration.k8s.io", "v1beta1", "mutatingadmissionpolicies"): {
Stub: `{"metadata":{"name":"map1b1"},"spec":{"paramKind":{"apiVersion":"test.example.com/v1","kind":"Example"},"matchConstraints":{"resourceRules": [{"resourceNames": ["fakeName"], "apiGroups":["apps"],"apiVersions":["v1"],"operations":["CREATE", "UPDATE"], "resources":["deployments"]}]},"reinvocationPolicy": "IfNeeded","mutations":[{"applyConfiguration": {"expression":"Object{metadata: Object.metadata{labels: {'example':'true'}}}"}, "patchType":"ApplyConfiguration"}]}}`,
ExpectedEtcdPath: "/registry/mutatingadmissionpolicies/map1b1",
IntroducedVersion: "1.34",
RemovedVersion: "1.40",
},
gvr("admissionregistration.k8s.io", "v1beta1", "mutatingadmissionpolicybindings"): {
Stub: `{"metadata":{"name":"mpb1b1"},"spec":{"policyName":"replicalimit-policy.example.com","paramRef":{"name":"replica-limit-test.example.com", "parameterNotFoundAction": "Allow"}}}`,
ExpectedEtcdPath: "/registry/mutatingadmissionpolicybindings/mpb1b1",
IntroducedVersion: "1.34",
RemovedVersion: "1.40",
},
// --
// k8s.io/kubernetes/pkg/apis/admissionregistration/v1alpha1
gvr("admissionregistration.k8s.io", "v1alpha1", "mutatingadmissionpolicies"): {
Stub: `{"metadata":{"name":"map1"},"spec":{"paramKind":{"apiVersion":"test.example.com/v1","kind":"Example"},"matchConstraints":{"resourceRules": [{"resourceNames": ["fakeName"], "apiGroups":["apps"],"apiVersions":["v1"],"operations":["CREATE", "UPDATE"], "resources":["deployments"]}]},"reinvocationPolicy": "IfNeeded","mutations":[{"applyConfiguration": {"expression":"Object{metadata: Object.metadata{labels: {'example':'true'}}}"}, "patchType":"ApplyConfiguration"}]}}`,
ExpectedEtcdPath: "/registry/mutatingadmissionpolicies/map1",
ExpectedGVK: gvkP("admissionregistration.k8s.io", "v1beta1", "MutatingAdmissionPolicy"),
IntroducedVersion: "1.32",
RemovedVersion: "1.38",
},
gvr("admissionregistration.k8s.io", "v1alpha1", "mutatingadmissionpolicybindings"): {
Stub: `{"metadata":{"name":"mpb1"},"spec":{"policyName":"replicalimit-policy.example.com","paramRef":{"name":"replica-limit-test.example.com", "parameterNotFoundAction": "Allow"}}}`,
ExpectedEtcdPath: "/registry/mutatingadmissionpolicybindings/mpb1",
ExpectedGVK: gvkP("admissionregistration.k8s.io", "v1beta1", "MutatingAdmissionPolicyBinding"),
IntroducedVersion: "1.32",
RemovedVersion: "1.38",
},
// --
// k8s.io/kubernetes/pkg/apis/scheduling/v1
gvr("scheduling.k8s.io", "v1", "priorityclasses"): {
Stub: `{"metadata":{"name":"pc3"},"Value":1000}`,
ExpectedEtcdPath: "/registry/priorityclasses/pc3",
IntroducedVersion: "1.14",
},
// --
// k8s.io/kubernetes/pkg/apis/scheduling/v1alpha3
gvr("scheduling.k8s.io", "v1alpha3", "workloads"): {
Stub: `{"metadata": {"name": "w1"}, "spec": {"podGroupTemplates": [{"name": "group1", "schedulingPolicy": {"basic": {}}}]}}`,
ExpectedEtcdPath: "/registry/workloads/" + namespace + "/w1",
IntroducedVersion: "1.36",
RemovedVersion: "1.42",
},
gvr("scheduling.k8s.io", "v1alpha3", "podgroups"): {
Stub: `{"metadata": {"name": "pg1"}, "spec": {"podGroupTemplateRef": {"workload": {"podGroupTemplateName": "t", "workloadName": "w"}}, "schedulingPolicy": {"basic": {}}}}`,
ExpectedEtcdPath: "/registry/podgroups/" + namespace + "/pg1",
IntroducedVersion: "1.36",
RemovedVersion: "1.42",
},
// --
// k8s.io/kube-aggregator/pkg/apis/apiregistration/v1
// depends on aggregator using the same ungrouped RESTOptionsGetter as the kube apiserver, not SimpleRestOptionsFactory in aggregator.go
gvr("apiregistration.k8s.io", "v1", "apiservices"): {
Stub: `{"metadata": {"name": "as2.foo.com"}, "spec": {"group": "foo.com", "version": "as2", "groupPriorityMinimum":100, "versionPriority":10}}`,
MutatedStub: `{"metadata": {"labels": {"a":"c"}}, "spec": {"versionPriority": 100}}`,
ExpectedEtcdPath: "/registry/apiregistration.k8s.io/apiservices/as2.foo.com",
IntroducedVersion: "1.10",
},
// --
// k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1
gvr("apiextensions.k8s.io", "v1", "customresourcedefinitions"): {
Stub: `{"metadata": {"name": "openshiftwebconsoleconfigs.webconsole2.operator.openshift.io"},"spec": {` +
`"scope": "Cluster","group": "webconsole2.operator.openshift.io",` +
`"versions": [{"name":"v1alpha1","storage":true,"served":true,"schema":{"openAPIV3Schema":{"type":"object"}}}],` +
`"names": {"kind": "OpenShiftWebConsoleConfig","plural": "openshiftwebconsoleconfigs","singular": "openshiftwebconsoleconfig"}}}`,
MutatedStub: `{"metadata": {"labels":{"a":"c"}}, "spec": {"group": "webconsole22.operator.openshift.io"}}`,
ExpectedEtcdPath: "/registry/apiextensions.k8s.io/customresourcedefinitions/openshiftwebconsoleconfigs.webconsole2.operator.openshift.io",
ExpectedGVK: gvkP("apiextensions.k8s.io", "v1beta1", "CustomResourceDefinition"),
IntroducedVersion: "1.16",
},
gvr("cr.bar.com", "v1", "foos"): {
Stub: `{"kind": "Foo", "apiVersion": "cr.bar.com/v1", "metadata": {"name": "cr1foo"}, "color": "blue"}`, // requires TypeMeta due to CRD scheme's UnstructuredObjectTyper
ExpectedEtcdPath: "/registry/cr.bar.com/foos/" + namespace + "/cr1foo",
IntroducedVersion: "1.0",
},
gvr("custom.fancy.com", "v2", "pants"): {
Stub: `{"kind": "Pant", "apiVersion": "custom.fancy.com/v2", "metadata": {"name": "cr2pant"}, "isFancy": true}`, // requires TypeMeta due to CRD scheme's UnstructuredObjectTyper
ExpectedEtcdPath: "/registry/custom.fancy.com/pants/cr2pant",
IntroducedVersion: "1.0",
},
gvr("awesome.bears.com", "v1", "pandas"): {
Stub: `{"kind": "Panda", "apiVersion": "awesome.bears.com/v1", "metadata": {"name": "cr3panda"}, "spec":{"replicas": 100}}`, // requires TypeMeta due to CRD scheme's UnstructuredObjectTyper
MutatedStub: `{"spec": {"replicas": 102}}`,
ExpectedEtcdPath: "/registry/awesome.bears.com/pandas/cr3panda",
IntroducedVersion: "1.0",
},
gvr("awesome.bears.com", "v3", "pandas"): {
Stub: `{"kind": "Panda", "apiVersion": "awesome.bears.com/v3", "metadata": {"name": "cr4panda"}, "spec":{"replicas": 300}}`, // requires TypeMeta due to CRD scheme's UnstructuredObjectTyper
MutatedStub: `{"spec": {"replicas": 302}}`,
ExpectedEtcdPath: "/registry/awesome.bears.com/pandas/cr4panda",
ExpectedGVK: gvkP("awesome.bears.com", "v1", "Panda"),
IntroducedVersion: "1.0",
},
gvr("random.numbers.com", "v1", "integers"): {
Stub: `{"kind": "Integer", "apiVersion": "random.numbers.com/v1", "metadata": {"name": "fortytwo"}, "value": 42, "garbage": "oiujnasdf"}`, // requires TypeMeta due to CRD scheme's UnstructuredObjectTyper
ExpectedEtcdPath: "/registry/random.numbers.com/integers/fortytwo",
IntroducedVersion: "1.0",
},
// --
// k8s.io/kubernetes/pkg/apis/node/v1
gvr("node.k8s.io", "v1", "runtimeclasses"): {
Stub: `{"metadata": {"name": "rc3"}, "handler": "h3"}`,
ExpectedEtcdPath: "/registry/runtimeclasses/rc3",
IntroducedVersion: "1.20",
},
// --
// k8s.io/kubernetes/pkg/apis/resource/v1alpha3
gvr("resource.k8s.io", "v1alpha3", "devicetaintrules"): {
Stub: `{"metadata": {"name": "taint1name"}, "spec": {"taint": {"key": "example.com/taintkey", "value": "taintvalue", "effect": "NoSchedule"}}}`,
MutatedStub: `{"spec": {"taint": {"effect": "NoExecute"}}}`,
ExpectedEtcdPath: "/registry/devicetaintrules/taint1name",
IntroducedVersion: "1.33",
RemovedVersion: "1.39",
},
gvr("resource.k8s.io", "v1alpha3", "resourcepoolstatusrequests"): {
Stub: `{"metadata": {"name": "rpsr1name"}, "spec": {"driver": "test-driver.example.com"}}`,
ExpectedEtcdPath: "/registry/resourcepoolstatusrequests/rpsr1name",
StatusStub: `{"status": {"poolCount": 0}}`,
MutatedStatusStub: `{"status": {"poolCount": 1}}`,
IntroducedVersion: "1.36",
RemovedVersion: "1.42",
},
// --
// k8s.io/kubernetes/pkg/apis/resource/v1beta1
gvr("resource.k8s.io", "v1beta1", "deviceclasses"): {
Stub: `{"metadata": {"name": "class2name"}}`,
MutatedStub: `{"metadata": {"labels":{"a":"c"}}}`,
ExpectedEtcdPath: "/registry/deviceclasses/class2name",
ExpectedGVK: gvkP("resource.k8s.io", "v1", "DeviceClass"),
IntroducedVersion: "1.32",
RemovedVersion: "1.38",
},
gvr("resource.k8s.io", "v1beta1", "resourceclaims"): {
Stub: `{"metadata": {"name": "claim2name"}, "spec": {"devices": {"requests": [{"name": "req-0", "deviceClassName": "example-class", "allocationMode": "ExactCount", "count": 1}]}}}`,
MutatedStub: `{"spec": {"devices": {"requests": [{"name": "req-0", "deviceClassName": "other-class"}]}}}`,
ExpectedEtcdPath: "/registry/resourceclaims/" + namespace + "/claim2name",
ExpectedGVK: gvkP("resource.k8s.io", "v1", "ResourceClaim"),
IntroducedVersion: "1.32",
RemovedVersion: "1.38",
StatusStub: `{"status": {"allocation": {"nodeSelector": {"nodeSelectorTerms": [{"matchExpressions": [{"key": "some-label", "operator": "In", "values": ["some-value"]}] }]}}}}`,
MutatedStatusStub: `{"status": {"allocation": {"nodeSelector": {"nodeSelectorTerms": [{"matchExpressions": [{"key": "some-label", "operator": "In", "values": ["some-other-value"]}] }]}}}}`,
},
gvr("resource.k8s.io", "v1beta1", "resourceclaimtemplates"): {
Stub: `{"metadata": {"name": "claimtemplate2name"}, "spec": {"spec": {"devices": {"requests": [{"name": "req-0", "deviceClassName": "example-class", "allocationMode": "ExactCount", "count": 1}]}}}}`,
MutatedStub: `{"spec": {"spec": {"resourceClassName": "class2name"}}}`,
ExpectedEtcdPath: "/registry/resourceclaimtemplates/" + namespace + "/claimtemplate2name",
ExpectedGVK: gvkP("resource.k8s.io", "v1", "ResourceClaimTemplate"),
IntroducedVersion: "1.32",
RemovedVersion: "1.38",
},
gvr("resource.k8s.io", "v1beta1", "resourceslices"): {
Stub: `{"metadata": {"name": "node2slice"}, "spec": {"nodeName": "worker1", "driver": "dra.example.com", "pool": {"name": "worker1", "resourceSliceCount": 1}}}`,
ExpectedEtcdPath: "/registry/resourceslices/node2slice",
ExpectedGVK: gvkP("resource.k8s.io", "v1", "ResourceSlice"),
IntroducedVersion: "1.32",
RemovedVersion: "1.38",
},
// --
// k8s.io/kubernetes/pkg/apis/resource/v1beta2
//
// The expected GVK must be set explicitly because when emulating 1.33,
// v1beta1 is the default although the actual storage version is v1beta2.
gvr("resource.k8s.io", "v1beta2", "deviceclasses"): {
Stub: `{"metadata": {"name": "class3name"}}`,
MutatedStub: `{"metadata": {"labels":{"a":"c"}}}`,
ExpectedEtcdPath: "/registry/deviceclasses/class3name",
ExpectedGVK: gvkP("resource.k8s.io", "v1", "DeviceClass"),
IntroducedVersion: "1.33",
RemovedVersion: "1.39",
},
gvr("resource.k8s.io", "v1beta2", "devicetaintrules"): {
Stub: `{"metadata": {"name": "taint2name"}, "spec": {"taint": {"key": "example.com/taintkey", "value": "taintvalue", "effect": "NoSchedule"}}}`,
MutatedStub: `{"spec": {"taint": {"effect": "NoExecute"}}}`,
ExpectedEtcdPath: "/registry/devicetaintrules/taint2name",
ExpectedGVK: gvkP("resource.k8s.io", "v1alpha3", "DeviceTaintRule"), // v1beta2 has higher priority, but to support downgrades v1alpha3 is picked automatically.
IntroducedVersion: "1.36",
RemovedVersion: "1.42",
},
gvr("resource.k8s.io", "v1beta2", "resourceclaims"): {
Stub: `{"metadata": {"name": "claim3name"}, "spec": {"devices": {"requests": [{"name": "req-0", "exactly": {"deviceClassName": "example-class", "allocationMode": "ExactCount", "count": 1}}]}}}`,
MutatedStub: `{"spec": {"devices": {"requests": [{"name": "req-0", "exactly": {"deviceClassName": "other-class"}}]}}}`,
ExpectedEtcdPath: "/registry/resourceclaims/" + namespace + "/claim3name",
ExpectedGVK: gvkP("resource.k8s.io", "v1", "ResourceClaim"),
IntroducedVersion: "1.33",
RemovedVersion: "1.39",
StatusStub: `{"status": {"allocation": {"nodeSelector": {"nodeSelectorTerms": [{"matchExpressions": [{"key": "some-label", "operator": "In", "values": ["some-value"]}] }]}}}}`,
MutatedStatusStub: `{"status": {"allocation": {"nodeSelector": {"nodeSelectorTerms": [{"matchExpressions": [{"key": "some-label", "operator": "In", "values": ["some-other-value"]}] }]}}}}`,
},
gvr("resource.k8s.io", "v1beta2", "resourceclaimtemplates"): {
Stub: `{"metadata": {"name": "claimtemplate3name"}, "spec": {"spec": {"devices": {"requests": [{"name": "req-0", "exactly": {"deviceClassName": "example-class", "allocationMode": "ExactCount", "count": 1}}]}}}}`,
MutatedStub: `{"spec": {"spec": {"resourceClassName": "class2name"}}}`,
ExpectedEtcdPath: "/registry/resourceclaimtemplates/" + namespace + "/claimtemplate3name",
ExpectedGVK: gvkP("resource.k8s.io", "v1", "ResourceClaimTemplate"),
IntroducedVersion: "1.33",
RemovedVersion: "1.39",
},
gvr("resource.k8s.io", "v1beta2", "resourceslices"): {
Stub: `{"metadata": {"name": "node3slice"}, "spec": {"nodeName": "worker1", "driver": "dra.example.com", "pool": {"name": "worker1", "resourceSliceCount": 1}}}`,
ExpectedEtcdPath: "/registry/resourceslices/node3slice",
ExpectedGVK: gvkP("resource.k8s.io", "v1", "ResourceSlice"),
IntroducedVersion: "1.33",
RemovedVersion: "1.39",
},
// --
// k8s.io/kubernetes/pkg/apis/resource/v1
gvr("resource.k8s.io", "v1", "deviceclasses"): {
Stub: `{"metadata": {"name": "class4name"}}`,
MutatedStub: `{"metadata": {"labels":{"a":"c"}}}`,
ExpectedEtcdPath: "/registry/deviceclasses/class4name",
ExpectedGVK: gvkP("resource.k8s.io", "v1", "DeviceClass"),
IntroducedVersion: "1.34",
},
gvr("resource.k8s.io", "v1", "resourceclaims"): {
Stub: `{"metadata": {"name": "claim4name"}, "spec": {"devices": {"requests": [{"name": "req-0", "exactly": {"deviceClassName": "example-class", "allocationMode": "ExactCount", "count": 1}}]}}}`,
MutatedStub: `{"spec": {"devices": {"requests": [{"name": "req-0", "exactly": {"deviceClassName": "other-class"}}]}}}`,
ExpectedEtcdPath: "/registry/resourceclaims/" + namespace + "/claim4name",
ExpectedGVK: gvkP("resource.k8s.io", "v1", "ResourceClaim"),
IntroducedVersion: "1.34",
StatusStub: `{"status": {"allocation": {"nodeSelector": {"nodeSelectorTerms": [{"matchExpressions": [{"key": "some-label", "operator": "In", "values": ["some-value"]}] }]}}}}`,
MutatedStatusStub: `{"status": {"allocation": {"nodeSelector": {"nodeSelectorTerms": [{"matchExpressions": [{"key": "some-label", "operator": "In", "values": ["some-other-value"]}] }]}}}}`,
},
gvr("resource.k8s.io", "v1", "resourceclaimtemplates"): {
Stub: `{"metadata": {"name": "claimtemplate4name"}, "spec": {"spec": {"devices": {"requests": [{"name": "req-0", "exactly": {"deviceClassName": "example-class", "allocationMode": "ExactCount", "count": 1}}]}}}}`,
MutatedStub: `{"spec": {"spec": {"resourceClassName": "class2name"}}}`,
ExpectedEtcdPath: "/registry/resourceclaimtemplates/" + namespace + "/claimtemplate4name",
ExpectedGVK: gvkP("resource.k8s.io", "v1", "ResourceClaimTemplate"),
IntroducedVersion: "1.34",
},
gvr("resource.k8s.io", "v1", "resourceslices"): {
Stub: `{"metadata": {"name": "node4slice"}, "spec": {"nodeName": "worker1", "driver": "dra.example.com", "pool": {"name": "worker1", "resourceSliceCount": 1}}}`,
ExpectedEtcdPath: "/registry/resourceslices/node4slice",
ExpectedGVK: gvkP("resource.k8s.io", "v1", "ResourceSlice"),
IntroducedVersion: "1.34",
},
// --
// k8s.io/apiserver/pkg/apis/apiserverinternal/v1alpha1
gvr("internal.apiserver.k8s.io", "v1alpha1", "storageversions"): {
Stub: `{"metadata":{"name":"sv1.test"},"spec":{}}`,
MutatedStub: `{}`,
ExpectedEtcdPath: "/registry/storageversions/sv1.test",
StatusStub: `{"status": {"commonEncodingVersion":"v1","storageVersions":[{"apiServerID":"1","decodableVersions":["v1","v2"],"encodingVersion":"v1"}],"conditions":[{"type":"AllEncodingVersionsEqual","status":"True","lastTransitionTime":"2020-01-01T00:00:00Z","reason":"allEncodingVersionsEqual","message":"all encoding versions are set to v1"}]}}`,
MutatedStatusStub: `{"status": {"commonEncodingVersion":"v1","storageVersions":[{"apiServerID":"1","decodableVersions":["v1","v2"],"encodingVersion":"v1"}],"conditions":[{"type":"AllEncodingVersionsEqual","status":"False","lastTransitionTime":"2020-01-01T00:00:00Z","reason":"allEncodingVersionsEqual","message":"all encoding versions are set to v1"}]}}`,
},
// --
// k8s.io/kubernetes/pkg/apis/storage/v1
gvr("storage.k8s.io", "v1", "csinodes"): {
Stub: `{"metadata": {"name": "csini2"}, "spec": {"drivers": [{"name": "test-driver", "nodeID": "localhost", "topologyKeys": ["company.com/zone1", "company.com/zone2"]}]}}`,
ExpectedEtcdPath: "/registry/csinodes/csini2",
IntroducedVersion: "1.17",
},
// --
// k8s.io/kubernetes/pkg/apis/storage/v1
gvr("storage.k8s.io", "v1", "csidrivers"): {
Stub: `{"metadata": {"name": "csid2"}, "spec": {"attachRequired": true, "podInfoOnMount": true}}`,
ExpectedEtcdPath: "/registry/csidrivers/csid2",
IntroducedVersion: "1.18",
},
// --
}
// get the min version the Beta api of a group is introduced, and it would be used to determine emulation forward compatibility.
minBetaVersions := map[schema.GroupResource]*version.Version{}
for key, data := range etcdStorageData {
if !strings.Contains(key.Version, "beta") {
continue
}
introduced := version.MustParse(data.IntroducedVersion)
if ver, ok := minBetaVersions[key.GroupResource()]; ok {
if introduced.LessThan(ver) {
minBetaVersions[key.GroupResource()] = introduced
}
} else {
minBetaVersions[key.GroupResource()] = introduced
}
}
// Delete types no longer served or not yet added at a particular emulated version.
for key, data := range etcdStorageData {
if data.RemovedVersion != "" && version.MustParse(v).AtLeast(version.MustParse(data.RemovedVersion)) {
delete(etcdStorageData, key)
}
if data.IntroducedVersion == "" || version.MustParse(v).AtLeast(version.MustParse(data.IntroducedVersion)) {
continue
}
minBetaVersion, ok := minBetaVersions[key.GroupResource()]
if ok && version.MustParse(v).AtLeast(minBetaVersion) {
continue
}
delete(etcdStorageData, key)
}
if isEmulation {
for key := range etcdStorageData {
if strings.Contains(key.Version, "alpha") {
delete(etcdStorageData, key)
}
}
}
// match the resource to the correct storage version for emulated version
if isEmulation {
for key, data := range etcdStorageData {
storageVersion := storageVersionAtEmulationVersion(key, data.ExpectedGVK, v, etcdStorageData)
if storageVersion == "" {
continue
}
data.ExpectedGVK.Version = storageVersion
}
}
validateStorageData(etcdStorageData)
return etcdStorageData
}
func validateStorageData(etcdStorageData map[schema.GroupVersionResource]StorageData) {
exceptions := map[schema.GroupVersionResource]bool{
gvr("internal.apiserver.k8s.io", "v1alpha1", "storageversions"): true,
}
for key, storageData := range etcdStorageData {
if _, ok := exceptions[key]; ok {
continue
}
version := key.Version
if strings.Contains(version, "alpha") || strings.Contains(version, "beta") {
if storageData.RemovedVersion == "" {
panic(fmt.Sprintf("Error. Non-GA resource %s must have a removed version", key.String()))
}
}
if storageData.IntroducedVersion == "" {
panic(fmt.Sprintf("Error. Non-GA resource %s must have an introduced version", key.String()))
}
}
}
// storageVersionAtEmulationVersion tries to find the correct storage version at an emulation version.
// If a GVK is introduced after the min compatibility version, we need to use an earlier version in storage.
func storageVersionAtEmulationVersion(key schema.GroupVersionResource, expectedGVK *schema.GroupVersionKind, emuVer string, etcdStorageData map[schema.GroupVersionResource]StorageData) string {
// expectedGVK is needed to find the correct GVK with the correct storage version.
if expectedGVK == nil {
return ""
}
minCompatVer := version.MustParse(emuVer).SubtractMinor(1)
expectedGVR := gvr(expectedGVK.Group, expectedGVK.Version, key.Resource)
expectedGVRData, ok := etcdStorageData[expectedGVR]
// expectedGVK is introduced before the emulation version, no need to change.
if !ok || minCompatVer.AtLeast(version.MustParse(expectedGVRData.IntroducedVersion)) {
return ""
}
// go through the prioritized version list to find the first version introduced before the emulation version.
gvs := legacyscheme.Scheme.PrioritizedVersionsForGroup(key.Group)
for _, gv := range gvs {
expectedGVR := gv.WithResource(key.Resource)
if expectedGVRData, ok := etcdStorageData[expectedGVR]; ok {
if minCompatVer.AtLeast(version.MustParse(expectedGVRData.IntroducedVersion)) {
return gv.Version
}
}
}
return ""
}
// StorageData contains information required to create an object and verify its storage in etcd
// It must be paired with a specific resource
type StorageData struct {
// Stub is a valid JSON object used to create the resource.
Stub string
// MutatedStub is partial update of Sub as a JSON object, with at least one field
// changed to a different value. For resources with a status subresource, MutatedStub
// MUST mutate a spec field. If a resource is immutable, do not set MutatedStub.
// For resources without a status subresource, MutatedStub may mutate any field
// or be left empty ("", do not set this field to "{}" as a noop apply will confuse tests).
// The mutation is applied as a patch.
// Example: if Stub has `"replicas": 1`, MutatedStub might have `"replicas": 2`.
MutatedStub string
// StatusStub is a valid JSON status payload used to write to the /status subresource.
// If empty, a metav1.Condition style status condition is used. Required only
// for resources without status conditions.
StatusStub string
// MutatedStatusStub is StatusStub with at least one status field changed to a different value.
// If empty, the default status payload with a flipped condition value is used.
// This mutation is applied using a server-side apply patch.
// Must be set whenever StatusStub is set.
MutatedStatusStub string
Prerequisites []Prerequisite // Optional, ordered list of JSON objects to create before stub
ExpectedEtcdPath string // Expected location of object in etcd, do not use any variables, constants, etc to derive this value - always supply the full raw string
ExpectedGVK *schema.GroupVersionKind // The GVK that we expect this object to be stored as - leave this nil to use the default
IntroducedVersion string // The version that this type is introduced
RemovedVersion string // The version that this type is removed. May be empty for stable resources
}
const defaultStatusStub = `{"status": {"conditions": [{"type": "MyStatus", "status":"True", "lastTransitionTime": "2020-01-01T00:00:00Z", "reason": "MyReason", "message": "some message"}]}}`
const defaultMutatedStatusStub = `{"status": {"conditions": [{"type": "MyStatus", "status":"False", "lastTransitionTime": "2020-01-01T00:00:00Z", "reason": "MyReason", "message": "some message"}]}}`
// GetStatusStub returns the StatusStub, or a default conditions-based status if empty.
func (s StorageData) GetStatusStub() string {
if s.StatusStub != "" {
return s.StatusStub
}
return defaultStatusStub
}
// GetMutatedStatusStub returns the MutatedStatusStub, or a default conditions-based status
// with a flipped condition value if empty.
func (s StorageData) GetMutatedStatusStub() string {
if s.MutatedStatusStub != "" {
return s.MutatedStatusStub
}
return defaultMutatedStatusStub
}
// Prerequisite contains information required to create a resource (but not verify it)
type Prerequisite struct {
GvrData schema.GroupVersionResource
Stub string
}
// GetCustomResourceDefinitionData returns the resource definitions that back the custom resources
// included in GetEtcdStorageData. They should be created using CreateTestCRDs before running any tests.
// We can switch this to v1 CRDs based on transitive call site analysis.
// Call sites:
// 1. TestDedupOwnerReferences - beta doesn't matter
// 2. TestWebhookAdmissionWithWatchCache/TestWebhookAdmissionWithoutWatchCache - beta doesn't matter
// 3. TestApplyStatus - the version fields don't matter. Pruning isn't checked, just ownership.
// 4. TestDryRun - versions and pruning don't matter
// 5. TestStorageVersionBootstrap - versions and pruning don't matter.
// 6. TestEtcdStoragePath - beta doesn't matter
// 7. TestCrossGroupStorage - beta doesn't matter
// 8. TestOverlappingCustomResourceCustomResourceDefinition - beta doesn't matter
// 9. TestOverlappingCustomResourceAPIService - beta doesn't matter
func GetCustomResourceDefinitionData() []*apiextensionsv1.CustomResourceDefinition {
return []*apiextensionsv1.CustomResourceDefinition{
// namespaced
{
ObjectMeta: metav1.ObjectMeta{
Name: "foos.cr.bar.com",
},
Spec: apiextensionsv1.CustomResourceDefinitionSpec{
Group: "cr.bar.com",
Scope: apiextensionsv1.NamespaceScoped,
Names: apiextensionsv1.CustomResourceDefinitionNames{
Plural: "foos",
Kind: "Foo",
},
Versions: []apiextensionsv1.CustomResourceDefinitionVersion{
{
Name: "v1",
Served: true,
Storage: true,
Schema: fixtures.AllowAllSchema(),
},
},
},
},
// cluster scoped
{
ObjectMeta: metav1.ObjectMeta{
Name: "pants.custom.fancy.com",
},
Spec: apiextensionsv1.CustomResourceDefinitionSpec{
Group: "custom.fancy.com",
Scope: apiextensionsv1.ClusterScoped,
Names: apiextensionsv1.CustomResourceDefinitionNames{
Plural: "pants",
Kind: "Pant",
},
Versions: []apiextensionsv1.CustomResourceDefinitionVersion{
{
Name: "v2",
Served: true,
Storage: true,
Schema: fixtures.AllowAllSchema(),
},
},
},
},
// cluster scoped with legacy version field and pruning.
{
ObjectMeta: metav1.ObjectMeta{
Name: "integers.random.numbers.com",
},
Spec: apiextensionsv1.CustomResourceDefinitionSpec{
Group: "random.numbers.com",
Scope: apiextensionsv1.ClusterScoped,
Names: apiextensionsv1.CustomResourceDefinitionNames{
Plural: "integers",
Kind: "Integer",
},
Versions: []apiextensionsv1.CustomResourceDefinitionVersion{
{
Name: "v1",
Served: true,
Storage: true,
Schema: &apiextensionsv1.CustomResourceValidation{
OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{
Type: "object",
Properties: map[string]apiextensionsv1.JSONSchemaProps{
"value": {
Type: "number",
},
},
}},
},
},
},
},
// cluster scoped with versions field
{
ObjectMeta: metav1.ObjectMeta{
Name: "pandas.awesome.bears.com",
},
Spec: apiextensionsv1.CustomResourceDefinitionSpec{
Group: "awesome.bears.com",
Versions: []apiextensionsv1.CustomResourceDefinitionVersion{
{
Name: "v1",
Served: true,
Storage: true,
Schema: fixtures.AllowAllSchema(),
Subresources: &apiextensionsv1.CustomResourceSubresources{
Status: &apiextensionsv1.CustomResourceSubresourceStatus{},
Scale: &apiextensionsv1.CustomResourceSubresourceScale{
SpecReplicasPath: ".spec.replicas",
StatusReplicasPath: ".status.replicas",
LabelSelectorPath: func() *string { path := ".status.selector"; return &path }(),
},
},
},
{
Name: "v2",
Served: false,
Storage: false,
Schema: fixtures.AllowAllSchema(),
Subresources: &apiextensionsv1.CustomResourceSubresources{
Status: &apiextensionsv1.CustomResourceSubresourceStatus{},
Scale: &apiextensionsv1.CustomResourceSubresourceScale{
SpecReplicasPath: ".spec.replicas",
StatusReplicasPath: ".status.replicas",
LabelSelectorPath: func() *string { path := ".status.selector"; return &path }(),
},
},
},
{
Name: "v3",
Served: true,
Storage: false,
Schema: fixtures.AllowAllSchema(),
Subresources: &apiextensionsv1.CustomResourceSubresources{
Status: &apiextensionsv1.CustomResourceSubresourceStatus{},
Scale: &apiextensionsv1.CustomResourceSubresourceScale{
SpecReplicasPath: ".spec.replicas",
StatusReplicasPath: ".status.replicas",
LabelSelectorPath: func() *string { path := ".status.selector"; return &path }(),
},
},
},
},
Scope: apiextensionsv1.ClusterScoped,
Names: apiextensionsv1.CustomResourceDefinitionNames{
Plural: "pandas",
Kind: "Panda",
},
},
},
}
}
func gvr(g, v, r string) schema.GroupVersionResource {
return schema.GroupVersionResource{Group: g, Version: v, Resource: r}
}
func gvkP(g, v, k string) *schema.GroupVersionKind {
return &schema.GroupVersionKind{Group: g, Version: v, Kind: k}
}
func gvk(g, v, k string) schema.GroupVersionKind {
return schema.GroupVersionKind{Group: g, Version: v, Kind: k}
}