diff --git a/test/integration/etcd/data.go b/test/integration/etcd/data.go index 2d8618f01b3..90081ffca46 100644 --- a/test/integration/etcd/data.go +++ b/test/integration/etcd/data.go @@ -17,12 +17,14 @@ 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" utilversion "k8s.io/component-base/version" "k8s.io/kubernetes/test/utils/image" @@ -64,344 +66,417 @@ func GetEtcdStorageDataForNamespace(namespace string) map[schema.GroupVersionRes // 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, version string, removeAlphas bool) map[schema.GroupVersionResource]StorageData { +func GetEtcdStorageDataForNamespaceServedAt(namespace string, v string, removeAlphas bool) map[schema.GroupVersionResource]StorageData { 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", + 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"}}}`, - ExpectedEtcdPath: "/registry/services/specs/" + namespace + "/service1", + Stub: `{"metadata": {"name": "service1"}, "spec": {"type": "LoadBalancer", "ports": [{"port": 10000, "targetPort": 11000}], "selector": {"test": "data"}}}`, + ExpectedEtcdPath: "/registry/services/specs/" + namespace + "/service1", + IntroducedVersion: "1.0", }, gvr("", "v1", "podtemplates"): { - Stub: `{"metadata": {"name": "pt1name"}, "template": {"metadata": {"labels": {"pt": "01"}}, "spec": {"containers": [{"image": "` + image + `", "name": "container9"}]}}}`, - ExpectedEtcdPath: "/registry/podtemplates/" + namespace + "/pt1name", + 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"}}}]}}`, - ExpectedEtcdPath: "/registry/pods/" + namespace + "/pod1", + Stub: `{"metadata": {"name": "pod1"}, "spec": {"containers": [{"image": "` + image + `", "name": "container7", "resources": {"limits": {"cpu": "1M"}, "requests": {"cpu": "1M"}}}]}}`, + 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", + 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"}}}`, - ExpectedEtcdPath: "/registry/resourcequotas/" + namespace + "/rq1name", + Stub: `{"metadata": {"name": "rq1name"}, "spec": {"hard": {"cpu": "5M"}}}`, + ExpectedEtcdPath: "/registry/resourcequotas/" + namespace + "/rq1name", + IntroducedVersion: "1.0", }, gvr("", "v1", "limitranges"): { - Stub: `{"metadata": {"name": "lr1name"}, "spec": {"limits": [{"type": "Pod"}]}}`, - ExpectedEtcdPath: "/registry/limitranges/" + namespace + "/lr1name", + 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"]}}`, - ExpectedEtcdPath: "/registry/namespaces/namespace1", + Stub: `{"metadata": {"name": "namespace1"}, "spec": {"finalizers": ["kubernetes"]}}`, + ExpectedEtcdPath: "/registry/namespaces/namespace1", + IntroducedVersion: "1.0", }, gvr("", "v1", "nodes"): { - Stub: `{"metadata": {"name": "node1"}, "spec": {"unschedulable": true}}`, - ExpectedEtcdPath: "/registry/minions/node1", + Stub: `{"metadata": {"name": "node1"}, "spec": {"unschedulable": true}}`, + ExpectedEtcdPath: "/registry/minions/node1", + IntroducedVersion: "1.0", }, gvr("", "v1", "persistentvolumes"): { - Stub: `{"metadata": {"name": "pv1name"}, "spec": {"accessModes": ["ReadWriteOnce"], "capacity": {"storage": "3M"}, "hostPath": {"path": "/tmp/test/"}}}`, - ExpectedEtcdPath: "/registry/persistentvolumes/pv1name", + Stub: `{"metadata": {"name": "pv1name"}, "spec": {"accessModes": ["ReadWriteOnce"], "capacity": {"storage": "3M"}, "hostPath": {"path": "/tmp/test/"}}}`, + ExpectedEtcdPath: "/registry/persistentvolumes/pv1name", + IntroducedVersion: "1.0", }, gvr("", "v1", "events"): { - Stub: `{"involvedObject": {"namespace": "` + namespace + `"}, "message": "some data here", "metadata": {"name": "event1"}}`, - ExpectedEtcdPath: "/registry/events/" + namespace + "/event1", + 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"}}}}`, - ExpectedEtcdPath: "/registry/persistentvolumeclaims/" + namespace + "/pvc1", + Stub: `{"metadata": {"name": "pvc1"}, "spec": {"accessModes": ["ReadWriteOnce"], "resources": {"limits": {"storage": "1M"}, "requests": {"storage": "2M"}}, "selector": {"matchLabels": {"pvc": "stuff"}}}}`, + ExpectedEtcdPath: "/registry/persistentvolumeclaims/" + namespace + "/pvc1", + IntroducedVersion: "1.0", }, gvr("", "v1", "serviceaccounts"): { - Stub: `{"metadata": {"name": "sa1name"}, "secrets": [{"name": "secret00"}]}`, - ExpectedEtcdPath: "/registry/serviceaccounts/" + namespace + "/sa1name", + 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", + 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"}]}}}}`, - ExpectedEtcdPath: "/registry/controllers/" + namespace + "/rc1", + Stub: `{"metadata": {"name": "rc1"}, "spec": {"selector": {"new": "stuff"}, "template": {"metadata": {"labels": {"new": "stuff"}}, "spec": {"containers": [{"image": "` + image + `", "name": "container8"}]}}}}`, + 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"}]}}}}`, - ExpectedEtcdPath: "/registry/daemonsets/" + namespace + "/ds6", + Stub: `{"metadata": {"name": "ds6"}, "spec": {"selector": {"matchLabels": {"a": "b"}}, "template": {"metadata": {"labels": {"a": "b"}}, "spec": {"containers": [{"image": "` + image + `", "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"}]}}}}`, - ExpectedEtcdPath: "/registry/deployments/" + namespace + "/deployment4", + Stub: `{"metadata": {"name": "deployment4"}, "spec": {"selector": {"matchLabels": {"f": "z"}}, "template": {"metadata": {"labels": {"f": "z"}}, "spec": {"containers": [{"image": "` + image + `", "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"}}}}}`, - ExpectedEtcdPath: "/registry/statefulsets/" + namespace + "/ss3", + Stub: `{"metadata": {"name": "ss3"}, "spec": {"selector": {"matchLabels": {"a": "b"}}, "template": {"metadata": {"labels": {"a": "b"}}}}}`, + 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"}]}}}}`, - ExpectedEtcdPath: "/registry/replicasets/" + namespace + "/rs3", + Stub: `{"metadata": {"name": "rs3"}, "spec": {"selector": {"matchLabels": {"g": "h"}}, "template": {"metadata": {"labels": {"g": "h"}}, "spec": {"containers": [{"image": "` + image + `", "name": "container4"}]}}}}`, + ExpectedEtcdPath: "/registry/replicasets/" + namespace + "/rs3", + IntroducedVersion: "1.9", }, gvr("apps", "v1", "controllerrevisions"): { - Stub: `{"metadata":{"name":"crs3"},"data":{"name":"abc","namespace":"default","creationTimestamp":null,"Spec":{"Replicas":0,"Selector":{"matchLabels":{"foo":"bar"}},"Template":{"creationTimestamp":null,"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", + Stub: `{"metadata":{"name":"crs3"},"data":{"name":"abc","namespace":"default","creationTimestamp":null,"Spec":{"Replicas":0,"Selector":{"matchLabels":{"foo":"bar"}},"Template":{"creationTimestamp":null,"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"}}}`, - ExpectedEtcdPath: "/registry/horizontalpodautoscalers/" + namespace + "/hpa2", - ExpectedGVK: gvkP("autoscaling", "v2", "HorizontalPodAutoscaler"), + Stub: `{"metadata": {"name": "hpa2"}, "spec": {"maxReplicas": 3, "scaleTargetRef": {"kind": "something", "name": "cross"}}}`, + ExpectedEtcdPath: "/registry/horizontalpodautoscalers/" + namespace + "/hpa2", + ExpectedGVK: gvkP("autoscaling", "v2", "HorizontalPodAutoscaler"), + IntroducedVersion: "1.2", }, // -- // k8s.io/kubernetes/pkg/apis/autoscaling/v2 gvr("autoscaling", "v2", "horizontalpodautoscalers"): { - Stub: `{"metadata": {"name": "hpa4"}, "spec": {"maxReplicas": 3, "scaleTargetRef": {"kind": "something", "name": "cross"}}}`, - ExpectedEtcdPath: "/registry/horizontalpodautoscalers/" + namespace + "/hpa4", + Stub: `{"metadata": {"name": "hpa4"}, "spec": {"maxReplicas": 3, "scaleTargetRef": {"kind": "something", "name": "cross"}}}`, + ExpectedEtcdPath: "/registry/horizontalpodautoscalers/" + namespace + "/hpa4", + IntroducedVersion: "1.23", }, // -- // 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"}}}}`, - ExpectedEtcdPath: "/registry/jobs/" + namespace + "/job1", + 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"}}}}`, + 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": "* * * * *"}}`, - ExpectedEtcdPath: "/registry/cronjobs/" + namespace + "/cjv1", + Stub: `{"metadata": {"name": "cjv1"}, "spec": {"jobTemplate": {"spec": {"template": {"metadata": {"labels": {"controller-uid": "uid0"}}, "spec": {"containers": [{"image": "` + image + `", "name": "container0"}], "dnsPolicy": "ClusterFirst", "restartPolicy": "Never"}}}}, "schedule": "* * * * *"}}`, + ExpectedEtcdPath: "/registry/cronjobs/" + namespace + "/cjv1", + IntroducedVersion: "1.21", }, // -- // 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="}}`, - ExpectedEtcdPath: "/registry/certificatesigningrequests/csr2", + Stub: `{"metadata": {"name": "csr2"}, "spec": {"signerName":"example.com/signer", "usages":["any"], "request": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0KTUlJQnlqQ0NBVE1DQVFBd2dZa3hDekFKQmdOVkJBWVRBbFZUTVJNd0VRWURWUVFJRXdwRFlXeHBabTl5Ym1saApNUll3RkFZRFZRUUhFdzFOYjNWdWRHRnBiaUJXYVdWM01STXdFUVlEVlFRS0V3cEhiMjluYkdVZ1NXNWpNUjh3CkhRWURWUVFMRXhaSmJtWnZjbTFoZEdsdmJpQlVaV05vYm05c2IyZDVNUmN3RlFZRFZRUURFdzUzZDNjdVoyOXYKWjJ4bExtTnZiVENCbnpBTkJna3Foa2lHOXcwQkFRRUZBQU9CalFBd2dZa0NnWUVBcFp0WUpDSEo0VnBWWEhmVgpJbHN0UVRsTzRxQzAzaGpYK1prUHl2ZFlkMVE0K3FiQWVUd1htQ1VLWUhUaFZSZDVhWFNxbFB6eUlCd2llTVpyCldGbFJRZGRaMUl6WEFsVlJEV3dBbzYwS2VjcWVBWG5uVUsrNWZYb1RJL1VnV3NocmU4dEoreC9UTUhhUUtSL0oKY0lXUGhxYVFoc0p1elpidkFkR0E4MEJMeGRNQ0F3RUFBYUFBTUEwR0NTcUdTSWIzRFFFQkJRVUFBNEdCQUlobAo0UHZGcStlN2lwQVJnSTVaTStHWng2bXBDejQ0RFRvMEprd2ZSRGYrQnRyc2FDMHE2OGVUZjJYaFlPc3E0ZmtIClEwdUEwYVZvZzNmNWlKeENhM0hwNWd4YkpRNnpWNmtKMFRFc3VhYU9oRWtvOXNkcENvUE9uUkJtMmkvWFJEMkQKNmlOaDhmOHowU2hHc0ZxakRnRkh5RjNvK2xVeWorVUM2SDFRVzdibgotLS0tLUVORCBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0="}}`, + 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: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", + 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.26", + RemovedVersion: "1.37", }, // -- // 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", + Stub: `{"metadata": {"name": "leasev1"}, "spec": {"holderIdentity": "holder", "leaseDurationSeconds": 5}}`, + ExpectedEtcdPath: "/registry/leases/" + namespace + "/leasev1", + IntroducedVersion: "1.14", }, // -- // 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", + Stub: `{"metadata": {"name": "leasecandidatev1alpha2"}, "spec": {"leaseName": "lease", "binaryVersion": "0.1.0", "emulationVersion": "0.1.0", "strategy": "OldestEmulationVersion"}}`, + ExpectedEtcdPath: "/registry/leasecandidates/" + namespace + "/leasecandidatev1alpha2", + 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", + 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"), + 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}}}}}`, - ExpectedEtcdPath: "/registry/ingress/" + namespace + "/ingress3", + Stub: `{"metadata": {"name": "ingress3"}, "spec": {"defaultBackend": {"service":{"name":"service", "port":{"number": 5000}}}}}`, + ExpectedEtcdPath: "/registry/ingress/" + namespace + "/ingress3", + IntroducedVersion: "1.19", }, gvr("networking.k8s.io", "v1", "ingressclasses"): { - Stub: `{"metadata": {"name": "ingressclass3"}, "spec": {"controller": "example.com/controller"}}`, - ExpectedEtcdPath: "/registry/ingressclasses/ingressclass3", + 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", + Stub: `{"metadata": {"name": "np2"}, "spec": {"podSelector": {"matchLabels": {"e": "f"}}}}`, + ExpectedEtcdPath: "/registry/networkpolicies/" + namespace + "/np2", + IntroducedVersion: "1.7", }, // -- // 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", + Stub: `{"metadata": {"name": "192.168.1.3"}, "spec": {"parentRef": {"resource": "services","name": "test", "namespace": "ns"}}}`, + ExpectedEtcdPath: "/registry/ipaddresses/192.168.1.3", + 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"]}}`, - ExpectedEtcdPath: "/registry/servicecidrs/range-b1", + Stub: `{"metadata": {"name": "range-b1"}, "spec": {"cidrs": ["192.168.0.0/16","fd00:1::/120"]}}`, + ExpectedEtcdPath: "/registry/servicecidrs/range-b1", + IntroducedVersion: "1.31", + RemovedVersion: "1.37", }, // -- // k8s.io/kubernetes/pkg/apis/policy/v1 gvr("policy", "v1", "poddisruptionbudgets"): { - Stub: `{"metadata": {"name": "pdbv1"}, "spec": {"selector": {"matchLabels": {"anokkey": "anokvalue"}}}}`, - ExpectedEtcdPath: "/registry/poddisruptionbudgets/" + namespace + "/pdbv1", + Stub: `{"metadata": {"name": "pdbv1"}, "spec": {"selector": {"matchLabels": {"anokkey": "anokvalue"}}}}`, + ExpectedEtcdPath: "/registry/poddisruptionbudgets/" + namespace + "/pdbv1", + IntroducedVersion: "1.21", }, // -- // k8s.io/kubernetes/pkg/apis/storagemigration/v1alpha1 gvr("storagemigration.k8s.io", "v1alpha1", "storageversionmigrations"): { - Stub: `{"metadata": {"name": "test-migration"}, "spec":{"resource": {"group": "test-group", "resource": "test-resource", "version": "test-version"}}}`, - ExpectedEtcdPath: "/registry/storageversionmigrations/test-migration", + Stub: `{"metadata": {"name": "test-migration"}, "spec":{"resource": {"group": "test-group", "resource": "test-resource", "version": "test-version"}}}`, + ExpectedEtcdPath: "/registry/storageversionmigrations/test-migration", + IntroducedVersion: "1.30", + RemovedVersion: "1.36", }, // -- // k8s.io/kubernetes/pkg/apis/flowcontrol/v1beta3 gvr("flowcontrol.apiserver.k8s.io", "v1beta3", "flowschemas"): { - Stub: `{"metadata": {"name": "fs-2"}, "spec": {"priorityLevelConfiguration": {"name": "name1"}}}`, - ExpectedEtcdPath: "/registry/flowschemas/fs-2", - ExpectedGVK: gvkP("flowcontrol.apiserver.k8s.io", "v1", "FlowSchema"), + Stub: `{"metadata": {"name": "fs-2"}, "spec": {"priorityLevelConfiguration": {"name": "name1"}}}`, + ExpectedEtcdPath: "/registry/flowschemas/fs-2", + ExpectedGVK: gvkP("flowcontrol.apiserver.k8s.io", "v1", "FlowSchema"), + IntroducedVersion: "1.26", + RemovedVersion: "1.32", }, // -- // k8s.io/kubernetes/pkg/apis/flowcontrol/v1beta3 gvr("flowcontrol.apiserver.k8s.io", "v1beta3", "prioritylevelconfigurations"): { - Stub: `{"metadata": {"name": "conf4"}, "spec": {"type": "Limited", "limited": {"nominalConcurrencyShares":3, "limitResponse": {"type": "Reject"}}}}`, - ExpectedEtcdPath: "/registry/prioritylevelconfigurations/conf4", - ExpectedGVK: gvkP("flowcontrol.apiserver.k8s.io", "v1", "PriorityLevelConfiguration"), + Stub: `{"metadata": {"name": "conf4"}, "spec": {"type": "Limited", "limited": {"nominalConcurrencyShares":3, "limitResponse": {"type": "Reject"}}}}`, + ExpectedEtcdPath: "/registry/prioritylevelconfigurations/conf4", + ExpectedGVK: gvkP("flowcontrol.apiserver.k8s.io", "v1", "PriorityLevelConfiguration"), + IntroducedVersion: "1.26", + RemovedVersion: "1.32", }, // -- // k8s.io/kubernetes/pkg/apis/flowcontrol/v1 gvr("flowcontrol.apiserver.k8s.io", "v1", "flowschemas"): { - Stub: `{"metadata": {"name": "fs-3"}, "spec": {"priorityLevelConfiguration": {"name": "name1"}}}`, - ExpectedEtcdPath: "/registry/flowschemas/fs-3", + Stub: `{"metadata": {"name": "fs-3"}, "spec": {"priorityLevelConfiguration": {"name": "name1"}}}`, + 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"}}}}`, - ExpectedEtcdPath: "/registry/prioritylevelconfigurations/conf5", + Stub: `{"metadata": {"name": "conf5"}, "spec": {"type": "Limited", "limited": {"nominalConcurrencyShares":3, "limitResponse": {"type": "Reject"}}}}`, + 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"}}}`, - ExpectedEtcdPath: "/registry/volumeattachments/va3", + Stub: `{"metadata": {"name": "va3"}, "spec": {"attacher": "gce", "nodeName": "localhost", "source": {"persistentVolumeName": "pv3"}}}`, + ExpectedEtcdPath: "/registry/volumeattachments/va3", + IntroducedVersion: "1.13", }, // -- // k8s.io/kubernetes/pkg/apis/storage/v1alpha1 gvr("storage.k8s.io", "v1alpha1", "volumeattributesclasses"): { - Stub: `{"metadata": {"name": "vac1"}, "driverName": "example.com/driver", "parameters": {"foo": "bar"}}`, - ExpectedEtcdPath: "/registry/volumeattributesclasses/vac1", - ExpectedGVK: gvkP("storage.k8s.io", "v1beta1", "VolumeAttributesClass"), + Stub: `{"metadata": {"name": "vac1"}, "driverName": "example.com/driver", "parameters": {"foo": "bar"}}`, + ExpectedEtcdPath: "/registry/volumeattributesclasses/vac1", + ExpectedGVK: gvkP("storage.k8s.io", "v1beta1", "VolumeAttributesClass"), + IntroducedVersion: "1.29", + RemovedVersion: "1.35", }, // -- // 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", + Stub: `{"metadata": {"name": "vac2"}, "driverName": "example.com/driver", "parameters": {"foo": "bar"}}`, + ExpectedEtcdPath: "/registry/volumeattributesclasses/vac2", + IntroducedVersion: "1.31", + RemovedVersion: "1.37", }, // -- // k8s.io/kubernetes/pkg/apis/storage/v1 gvr("storage.k8s.io", "v1", "storageclasses"): { - Stub: `{"metadata": {"name": "sc2"}, "provisioner": "aws"}`, - ExpectedEtcdPath: "/registry/storageclasses/sc2", + 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", + 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", + 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", + 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", + 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", + 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","creationTimestamp":null},"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", + Stub: `{"metadata":{"name":"hook2","creationTimestamp":null},"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","creationTimestamp":null},"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", + Stub: `{"metadata":{"name":"hook2","creationTimestamp":null},"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","creationTimestamp":null},"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/vap1", + Stub: `{"metadata":{"name":"vap1","creationTimestamp":null},"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/vap1", + IntroducedVersion: "1.30", }, gvr("admissionregistration.k8s.io", "v1", "validatingadmissionpolicybindings"): { - Stub: `{"metadata":{"name":"pb1","creationTimestamp":null},"spec":{"policyName":"replicalimit-policy.example.com","paramRef":{"name":"replica-limit-test.example.com","parameterNotFoundAction":"Deny"},"validationActions":["Deny"]}}`, - ExpectedEtcdPath: "/registry/validatingadmissionpolicybindings/pb1", + Stub: `{"metadata":{"name":"pb1","creationTimestamp":null},"spec":{"policyName":"replicalimit-policy.example.com","paramRef":{"name":"replica-limit-test.example.com","parameterNotFoundAction":"Deny"},"validationActions":["Deny"]}}`, + ExpectedEtcdPath: "/registry/validatingadmissionpolicybindings/pb1", + IntroducedVersion: "1.30", }, // -- // k8s.io/kubernetes/pkg/apis/admissionregistration/v1beta1 gvr("admissionregistration.k8s.io", "v1beta1", "validatingadmissionpolicies"): { - Stub: `{"metadata":{"name":"vap1b1","creationTimestamp":null},"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"), + Stub: `{"metadata":{"name":"vap1b1","creationTimestamp":null},"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","creationTimestamp":null},"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"), + Stub: `{"metadata":{"name":"pb1b1","creationTimestamp":null},"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", }, // -- // k8s.io/kubernetes/pkg/apis/admissionregistration/v1alpha1 gvr("admissionregistration.k8s.io", "v1alpha1", "mutatingadmissionpolicies"): { - Stub: `{"metadata":{"name":"map1","creationTimestamp":null},"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", + Stub: `{"metadata":{"name":"map1","creationTimestamp":null},"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", + IntroducedVersion: "1.32", + RemovedVersion: "1.38", }, gvr("admissionregistration.k8s.io", "v1alpha1", "mutatingadmissionpolicybindings"): { - Stub: `{"metadata":{"name":"mpb1","creationTimestamp":null},"spec":{"policyName":"replicalimit-policy.example.com","paramRef":{"name":"replica-limit-test.example.com"}}}`, - ExpectedEtcdPath: "/registry/mutatingadmissionpolicybindings/mpb1", + Stub: `{"metadata":{"name":"mpb1","creationTimestamp":null},"spec":{"policyName":"replicalimit-policy.example.com","paramRef":{"name":"replica-limit-test.example.com"}}}`, + ExpectedEtcdPath: "/registry/mutatingadmissionpolicybindings/mpb1", + 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", + Stub: `{"metadata":{"name":"pc3"},"Value":1000}`, + ExpectedEtcdPath: "/registry/priorityclasses/pc3", + IntroducedVersion: "1.14", }, // -- // 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}}`, - ExpectedEtcdPath: "/registry/apiregistration.k8s.io/apiservices/as2.foo.com", + Stub: `{"metadata": {"name": "as2.foo.com"}, "spec": {"group": "foo.com", "version": "as2", "groupPriorityMinimum":100, "versionPriority":10}}`, + ExpectedEtcdPath: "/registry/apiregistration.k8s.io/apiservices/as2.foo.com", + IntroducedVersion: "1.10", }, // -- @@ -411,78 +486,101 @@ func GetEtcdStorageDataForNamespaceServedAt(namespace string, version string, re `"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"}}}`, - ExpectedEtcdPath: "/registry/apiextensions.k8s.io/customresourcedefinitions/openshiftwebconsoleconfigs.webconsole2.operator.openshift.io", - ExpectedGVK: gvkP("apiextensions.k8s.io", "v1beta1", "CustomResourceDefinition"), + 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", + 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", + 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 - ExpectedEtcdPath: "/registry/awesome.bears.com/pandas/cr3panda", + Stub: `{"kind": "Panda", "apiVersion": "awesome.bears.com/v1", "metadata": {"name": "cr3panda"}, "spec":{"replicas": 100}}`, // requires TypeMeta due to CRD scheme's UnstructuredObjectTyper + 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 - ExpectedEtcdPath: "/registry/awesome.bears.com/pandas/cr4panda", - ExpectedGVK: gvkP("awesome.bears.com", "v1", "Panda"), + Stub: `{"kind": "Panda", "apiVersion": "awesome.bears.com/v3", "metadata": {"name": "cr4panda"}, "spec":{"replicas": 300}}`, // requires TypeMeta due to CRD scheme's UnstructuredObjectTyper + 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", + 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", + 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", "deviceclasses"): { - Stub: `{"metadata": {"name": "class1name"}}`, - ExpectedEtcdPath: "/registry/deviceclasses/class1name", - ExpectedGVK: gvkP("resource.k8s.io", "v1beta1", "DeviceClass"), + Stub: `{"metadata": {"name": "class1name"}}`, + ExpectedEtcdPath: "/registry/deviceclasses/class1name", + ExpectedGVK: gvkP("resource.k8s.io", "v1beta1", "DeviceClass"), + IntroducedVersion: "1.31", + RemovedVersion: "1.37", }, gvr("resource.k8s.io", "v1alpha3", "resourceclaims"): { - Stub: `{"metadata": {"name": "claim1name"}, "spec": {"devices": {"requests": [{"name": "req-0", "deviceClassName": "example-class", "allocationMode": "ExactCount", "count": 1}]}}}`, - ExpectedEtcdPath: "/registry/resourceclaims/" + namespace + "/claim1name", - ExpectedGVK: gvkP("resource.k8s.io", "v1beta1", "ResourceClaim"), + Stub: `{"metadata": {"name": "claim1name"}, "spec": {"devices": {"requests": [{"name": "req-0", "deviceClassName": "example-class", "allocationMode": "ExactCount", "count": 1}]}}}`, + ExpectedEtcdPath: "/registry/resourceclaims/" + namespace + "/claim1name", + ExpectedGVK: gvkP("resource.k8s.io", "v1beta1", "ResourceClaim"), + IntroducedVersion: "1.31", + RemovedVersion: "1.37", }, gvr("resource.k8s.io", "v1alpha3", "resourceclaimtemplates"): { - Stub: `{"metadata": {"name": "claimtemplate1name"}, "spec": {"spec": {"devices": {"requests": [{"name": "req-0", "deviceClassName": "example-class", "allocationMode": "ExactCount", "count": 1}]}}}}`, - ExpectedEtcdPath: "/registry/resourceclaimtemplates/" + namespace + "/claimtemplate1name", - ExpectedGVK: gvkP("resource.k8s.io", "v1beta1", "ResourceClaimTemplate"), + Stub: `{"metadata": {"name": "claimtemplate1name"}, "spec": {"spec": {"devices": {"requests": [{"name": "req-0", "deviceClassName": "example-class", "allocationMode": "ExactCount", "count": 1}]}}}}`, + ExpectedEtcdPath: "/registry/resourceclaimtemplates/" + namespace + "/claimtemplate1name", + ExpectedGVK: gvkP("resource.k8s.io", "v1beta1", "ResourceClaimTemplate"), + IntroducedVersion: "1.31", + RemovedVersion: "1.37", }, gvr("resource.k8s.io", "v1alpha3", "resourceslices"): { - Stub: `{"metadata": {"name": "node1slice"}, "spec": {"nodeName": "worker1", "driver": "dra.example.com", "pool": {"name": "worker1", "resourceSliceCount": 1}}}`, - ExpectedEtcdPath: "/registry/resourceslices/node1slice", - ExpectedGVK: gvkP("resource.k8s.io", "v1beta1", "ResourceSlice"), + Stub: `{"metadata": {"name": "node1slice"}, "spec": {"nodeName": "worker1", "driver": "dra.example.com", "pool": {"name": "worker1", "resourceSliceCount": 1}}}`, + ExpectedEtcdPath: "/registry/resourceslices/node1slice", + ExpectedGVK: gvkP("resource.k8s.io", "v1beta1", "ResourceSlice"), + IntroducedVersion: "1.31", + RemovedVersion: "1.37", }, // -- // k8s.io/kubernetes/pkg/apis/resource/v1beta1 gvr("resource.k8s.io", "v1beta1", "deviceclasses"): { - Stub: `{"metadata": {"name": "class2name"}}`, - ExpectedEtcdPath: "/registry/deviceclasses/class2name", + Stub: `{"metadata": {"name": "class2name"}}`, + ExpectedEtcdPath: "/registry/deviceclasses/class2name", + 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}]}}}`, - ExpectedEtcdPath: "/registry/resourceclaims/" + namespace + "/claim2name", + Stub: `{"metadata": {"name": "claim2name"}, "spec": {"devices": {"requests": [{"name": "req-0", "deviceClassName": "example-class", "allocationMode": "ExactCount", "count": 1}]}}}`, + ExpectedEtcdPath: "/registry/resourceclaims/" + namespace + "/claim2name", + IntroducedVersion: "1.32", + RemovedVersion: "1.38", }, gvr("resource.k8s.io", "v1beta1", "resourceclaimtemplates"): { - Stub: `{"metadata": {"name": "claimtemplate2name"}, "spec": {"spec": {"devices": {"requests": [{"name": "req-0", "deviceClassName": "example-class", "allocationMode": "ExactCount", "count": 1}]}}}}`, - ExpectedEtcdPath: "/registry/resourceclaimtemplates/" + namespace + "/claimtemplate2name", + Stub: `{"metadata": {"name": "claimtemplate2name"}, "spec": {"spec": {"devices": {"requests": [{"name": "req-0", "deviceClassName": "example-class", "allocationMode": "ExactCount", "count": 1}]}}}}`, + ExpectedEtcdPath: "/registry/resourceclaimtemplates/" + namespace + "/claimtemplate2name", + 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", + Stub: `{"metadata": {"name": "node2slice"}, "spec": {"nodeName": "worker1", "driver": "dra.example.com", "pool": {"name": "worker1", "resourceSliceCount": 1}}}`, + ExpectedEtcdPath: "/registry/resourceslices/node2slice", + IntroducedVersion: "1.32", + RemovedVersion: "1.38", }, // -- @@ -495,35 +593,30 @@ func GetEtcdStorageDataForNamespaceServedAt(namespace string, version string, re // 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", + 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", + Stub: `{"metadata": {"name": "csid2"}, "spec": {"attachRequired": true, "podInfoOnMount": true}}`, + ExpectedEtcdPath: "/registry/csidrivers/csid2", + IntroducedVersion: "1.18", }, // -- } // Delete types no longer served or not yet added at a particular emulated version. - // When adding a brand new type non-alpha type in the latest release, please ensure that - // it is removed in previous emulated versions otherwise emulated version tests will fail. - // TODO: derive this programatically from gvk --> instance --> APILifecycle info - switch version { - case "1.33": - delete(etcdStorageData, gvr("flowcontrol.apiserver.k8s.io", "v1beta3", "flowschemas")) - delete(etcdStorageData, gvr("flowcontrol.apiserver.k8s.io", "v1beta3", "prioritylevelconfigurations")) - case "1.32": - delete(etcdStorageData, gvr("flowcontrol.apiserver.k8s.io", "v1beta3", "flowschemas")) - delete(etcdStorageData, gvr("flowcontrol.apiserver.k8s.io", "v1beta3", "prioritylevelconfigurations")) - case "1.31": - delete(etcdStorageData, gvr("resource.k8s.io", "v1beta1", "deviceclasses")) - delete(etcdStorageData, gvr("resource.k8s.io", "v1beta1", "resourceclaims")) - delete(etcdStorageData, gvr("resource.k8s.io", "v1beta1", "resourceclaimtemplates")) - delete(etcdStorageData, gvr("resource.k8s.io", "v1beta1", "resourceslices")) + for key, data := range etcdStorageData { + if data.IntroducedVersion != "" && version.MustParse(data.IntroducedVersion).GreaterThan(version.MustParse(v)) { + delete(etcdStorageData, key) + } + + if data.RemovedVersion != "" && version.MustParse(v).AtLeast(version.MustParse(data.RemovedVersion)) { + delete(etcdStorageData, key) + } } if removeAlphas { @@ -533,16 +626,40 @@ func GetEtcdStorageDataForNamespaceServedAt(namespace string, version string, re } } } + 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())) + } + } +} + // 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 string // Valid JSON stub to use during create - 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 + Stub string // Valid JSON stub to use during create + 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 } // Prerequisite contains information required to create a resource (but not verify it) diff --git a/test/integration/etcd/etcd_storage_path_test.go b/test/integration/etcd/etcd_storage_path_test.go index e4e8ed4473a..2e015d2470e 100644 --- a/test/integration/etcd/etcd_storage_path_test.go +++ b/test/integration/etcd/etcd_storage_path_test.go @@ -94,8 +94,8 @@ func testEtcdStoragePathWithVersion(t *testing.T, v string) { // Only test for beta and GA APIs with emulated version. featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, feature.DefaultFeatureGate, version.MustParse(v)) featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, "AllBeta", true) - registerEffectiveEmulationVersion(t) } + registerEffectiveEmulationVersion(t) apiServer := StartRealAPIServerOrDie(t, func(opts *options.ServerRunOptions) { // Disable alphas when emulating previous versions.