From c16edb27387e6abe08588819659558b6bfa8268e Mon Sep 17 00:00:00 2001 From: Monis Khan Date: Thu, 18 Oct 2018 15:47:51 -0400 Subject: [PATCH] Refactor dry run test to reuse ETCD storage data This change updates the ETCD storage test so that its data is exported. Thus it can be used by other tests. The dry run test was updated to consume this data instead of having a duplicate copy. The code to start a master that can be used for "one of every resource" style tests was also factored out. It is reused in the dry run test as well. This prevents these tests from drifting in the future and reduces the long term maintenance burden. Signed-off-by: Monis Khan --- test/integration/dryrun/BUILD | 12 +- test/integration/dryrun/dryrun_test.go | 543 +------------- test/integration/etcd/BUILD | 47 +- test/integration/etcd/data.go | 455 ++++++++++++ .../etcd/etcd_storage_path_test.go | 684 ++---------------- test/integration/etcd/server.go | 283 ++++++++ .../master/transformation_testcase.go | 2 +- test/integration/utils.go | 8 +- 8 files changed, 844 insertions(+), 1190 deletions(-) create mode 100644 test/integration/etcd/data.go create mode 100644 test/integration/etcd/server.go diff --git a/test/integration/dryrun/BUILD b/test/integration/dryrun/BUILD index 8770b8b5fd2..1ad06a3aeeb 100644 --- a/test/integration/dryrun/BUILD +++ b/test/integration/dryrun/BUILD @@ -17,28 +17,18 @@ go_test( "integration", ], deps = [ - "//cmd/kube-apiserver/app:go_default_library", - "//cmd/kube-apiserver/app/options:go_default_library", - "//pkg/master:go_default_library", "//staging/src/k8s.io/api/core/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/api/meta:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/types:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library", "//staging/src/k8s.io/apiserver/pkg/features:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/server/options:go_default_library", "//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library", "//staging/src/k8s.io/apiserver/pkg/util/feature/testing:go_default_library", - "//staging/src/k8s.io/client-go/discovery/cached:go_default_library", "//staging/src/k8s.io/client-go/dynamic:go_default_library", - "//staging/src/k8s.io/client-go/kubernetes:go_default_library", - "//staging/src/k8s.io/client-go/rest:go_default_library", - "//staging/src/k8s.io/client-go/restmapper:go_default_library", + "//test/integration/etcd:go_default_library", "//test/integration/framework:go_default_library", ], ) diff --git a/test/integration/dryrun/dryrun_test.go b/test/integration/dryrun/dryrun_test.go index 1152068428a..99e039d3539 100644 --- a/test/integration/dryrun/dryrun_test.go +++ b/test/integration/dryrun/dryrun_test.go @@ -17,354 +17,22 @@ limitations under the License. package dryrun import ( - "encoding/json" - "io/ioutil" - "net" - "net/http" - "os" - "strings" "testing" - "time" "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/sets" - "k8s.io/apimachinery/pkg/util/wait" "k8s.io/apiserver/pkg/features" - genericapiserveroptions "k8s.io/apiserver/pkg/server/options" utilfeature "k8s.io/apiserver/pkg/util/feature" utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing" - cacheddiscovery "k8s.io/client-go/discovery/cached" "k8s.io/client-go/dynamic" - clientset "k8s.io/client-go/kubernetes" - restclient "k8s.io/client-go/rest" - "k8s.io/client-go/restmapper" - "k8s.io/kubernetes/cmd/kube-apiserver/app" - "k8s.io/kubernetes/cmd/kube-apiserver/app/options" - "k8s.io/kubernetes/test/integration/framework" - - // install all APIs - _ "k8s.io/kubernetes/pkg/master" // TODO what else is needed + "k8s.io/kubernetes/test/integration/etcd" ) -// dryrun data for all persisted objects. -var dryrunData = map[schema.GroupVersionResource]struct { - stub string // Valid JSON stub to use during create -}{ - // k8s.io/kubernetes/pkg/api/v1 - gvr("", "v1", "configmaps"): { - stub: `{"data": {"foo": "bar"}, "metadata": {"name": "cm1"}}`, - }, - gvr("", "v1", "services"): { - stub: `{"metadata": {"name": "service1"}, "spec": {"externalName": "service1name", "ports": [{"port": 10000, "targetPort": 11000}], "selector": {"test": "data"}}}`, - }, - gvr("", "v1", "podtemplates"): { - stub: `{"metadata": {"name": "pt1name"}, "template": {"metadata": {"labels": {"pt": "01"}}, "spec": {"containers": [{"image": "fedora:latest", "name": "container9"}]}}}`, - }, - gvr("", "v1", "pods"): { - stub: `{"metadata": {"name": "pod1"}, "spec": {"containers": [{"image": "fedora:latest", "name": "container7", "resources": {"limits": {"cpu": "1M"}, "requests": {"cpu": "1M"}}}]}}`, - }, - gvr("", "v1", "endpoints"): { - stub: `{"metadata": {"name": "ep1name"}, "subsets": [{"addresses": [{"hostname": "bar-001", "ip": "192.168.3.1"}], "ports": [{"port": 8000}]}]}`, - }, - gvr("", "v1", "resourcequotas"): { - stub: `{"metadata": {"name": "rq1name"}, "spec": {"hard": {"cpu": "5M"}}}`, - }, - gvr("", "v1", "limitranges"): { - stub: `{"metadata": {"name": "lr1name"}, "spec": {"limits": [{"type": "Pod"}]}}`, - }, - gvr("", "v1", "namespaces"): { - stub: `{"metadata": {"name": "namespace2"}, "spec": {"finalizers": ["kubernetes"]}}`, - }, - gvr("", "v1", "nodes"): { - stub: `{"metadata": {"name": "node1"}, "spec": {"unschedulable": true}}`, - }, - gvr("", "v1", "persistentvolumes"): { - stub: `{"metadata": {"name": "pv1name"}, "spec": {"accessModes": ["ReadWriteOnce"], "capacity": {"storage": "3M"}, "hostPath": {"path": "/tmp/test/"}}}`, - }, - gvr("", "v1", "events"): { - stub: `{"involvedObject": {"namespace": "dryrunnamespace"}, "message": "some data here", "metadata": {"name": "event1"}}`, - }, - gvr("", "v1", "persistentvolumeclaims"): { - stub: `{"metadata": {"name": "pvc1"}, "spec": {"accessModes": ["ReadWriteOnce"], "resources": {"limits": {"storage": "1M"}, "requests": {"storage": "2M"}}, "selector": {"matchLabels": {"pvc": "stuff"}}}}`, - }, - gvr("", "v1", "serviceaccounts"): { - stub: `{"metadata": {"name": "sa1name"}, "secrets": [{"name": "secret00"}]}`, - }, - gvr("", "v1", "secrets"): { - stub: `{"data": {"key": "ZGF0YSBmaWxl"}, "metadata": {"name": "secret1"}}`, - }, - gvr("", "v1", "replicationcontrollers"): { - stub: `{"metadata": {"name": "rc1"}, "spec": {"selector": {"new": "stuff"}, "template": {"metadata": {"labels": {"new": "stuff"}}, "spec": {"containers": [{"image": "fedora:latest", "name": "container8"}]}}}}`, - }, - // -- - - // k8s.io/kubernetes/pkg/apis/apps/v1beta1 - gvr("apps", "v1beta1", "statefulsets"): { - stub: `{"metadata": {"name": "ss1"}, "spec": {"selector": {"matchLabels": {"a": "b"}}, "template": {"metadata": {"labels": {"a": "b"}}}}}`, - }, - gvr("apps", "v1beta1", "deployments"): { - stub: `{"metadata": {"name": "deployment2"}, "spec": {"selector": {"matchLabels": {"f": "z"}}, "template": {"metadata": {"labels": {"f": "z"}}, "spec": {"containers": [{"image": "fedora:latest", "name": "container6"}]}}}}`, - }, - gvr("apps", "v1beta1", "controllerrevisions"): { - stub: `{"metadata":{"name":"crs1"},"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}`, - }, - // -- - - // k8s.io/kubernetes/pkg/apis/apps/v1beta2 - gvr("apps", "v1beta2", "statefulsets"): { - stub: `{"metadata": {"name": "ss2"}, "spec": {"selector": {"matchLabels": {"a": "b"}}, "template": {"metadata": {"labels": {"a": "b"}}}}}`, - }, - gvr("apps", "v1beta2", "deployments"): { - stub: `{"metadata": {"name": "deployment3"}, "spec": {"selector": {"matchLabels": {"f": "z"}}, "template": {"metadata": {"labels": {"f": "z"}}, "spec": {"containers": [{"image": "fedora:latest", "name": "container6"}]}}}}`, - }, - gvr("apps", "v1beta2", "daemonsets"): { - stub: `{"metadata": {"name": "ds5"}, "spec": {"selector": {"matchLabels": {"a": "b"}}, "template": {"metadata": {"labels": {"a": "b"}}, "spec": {"containers": [{"image": "fedora:latest", "name": "container6"}]}}}}`, - }, - gvr("apps", "v1beta2", "replicasets"): { - stub: `{"metadata": {"name": "rs2"}, "spec": {"selector": {"matchLabels": {"g": "h"}}, "template": {"metadata": {"labels": {"g": "h"}}, "spec": {"containers": [{"image": "fedora:latest", "name": "container4"}]}}}}`, - }, - gvr("apps", "v1beta2", "controllerrevisions"): { - stub: `{"metadata":{"name":"crs2"},"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}`, - }, - // -- - - // 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": "fedora:latest", "name": "container6"}]}}}}`, - }, - gvr("apps", "v1", "deployments"): { - stub: `{"metadata": {"name": "deployment4"}, "spec": {"selector": {"matchLabels": {"f": "z"}}, "template": {"metadata": {"labels": {"f": "z"}}, "spec": {"containers": [{"image": "fedora:latest", "name": "container6"}]}}}}`, - }, - gvr("apps", "v1", "statefulsets"): { - stub: `{"metadata": {"name": "ss3"}, "spec": {"selector": {"matchLabels": {"a": "b"}}, "template": {"metadata": {"labels": {"a": "b"}}}}}`, - }, - gvr("apps", "v1", "replicasets"): { - stub: `{"metadata": {"name": "rs3"}, "spec": {"selector": {"matchLabels": {"g": "h"}}, "template": {"metadata": {"labels": {"g": "h"}}, "spec": {"containers": [{"image": "fedora:latest", "name": "container4"}]}}}}`, - }, - 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}`, - }, - // -- - - // k8s.io/kubernetes/pkg/apis/autoscaling/v1 - gvr("autoscaling", "v1", "horizontalpodautoscalers"): { - stub: `{"metadata": {"name": "hpa2"}, "spec": {"maxReplicas": 3, "scaleTargetRef": {"kind": "something", "name": "cross"}}}`, - }, - // -- - - // k8s.io/kubernetes/pkg/apis/autoscaling/v2beta1 - gvr("autoscaling", "v2beta1", "horizontalpodautoscalers"): { - stub: `{"metadata": {"name": "hpa1"}, "spec": {"maxReplicas": 3, "scaleTargetRef": {"kind": "something", "name": "cross"}}}`, - }, - // -- - - // k8s.io/kubernetes/pkg/apis/autoscaling/v2beta2 - gvr("autoscaling", "v2beta2", "horizontalpodautoscalers"): { - stub: `{"metadata": {"name": "hpa3"}, "spec": {"maxReplicas": 3, "scaleTargetRef": {"kind": "something", "name": "cross"}}}`, - }, - // -- - - // 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": "fedora:latest", "name": "container1"}], "dnsPolicy": "ClusterFirst", "restartPolicy": "Never"}}}}`, - }, - // -- - - // k8s.io/kubernetes/pkg/apis/batch/v1beta1 - gvr("batch", "v1beta1", "cronjobs"): { - stub: `{"metadata": {"name": "cjv1beta1"}, "spec": {"jobTemplate": {"spec": {"template": {"metadata": {"labels": {"controller-uid": "uid0"}}, "spec": {"containers": [{"image": "fedora:latest", "name": "container0"}], "dnsPolicy": "ClusterFirst", "restartPolicy": "Never"}}}}, "schedule": "* * * * *"}}`, - }, - // -- - - // k8s.io/kubernetes/pkg/apis/batch/v2alpha1 - gvr("batch", "v2alpha1", "cronjobs"): { - stub: `{"metadata": {"name": "cjv2alpha1"}, "spec": {"jobTemplate": {"spec": {"template": {"metadata": {"labels": {"controller-uid": "uid0"}}, "spec": {"containers": [{"image": "fedora:latest", "name": "container0"}], "dnsPolicy": "ClusterFirst", "restartPolicy": "Never"}}}}, "schedule": "* * * * *"}}`, - }, - // -- - - // k8s.io/kubernetes/pkg/apis/certificates/v1beta1 - gvr("certificates.k8s.io", "v1beta1", "certificatesigningrequests"): { - stub: `{"metadata": {"name": "csr1"}, "spec": {"request": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0KTUlJQnlqQ0NBVE1DQVFBd2dZa3hDekFKQmdOVkJBWVRBbFZUTVJNd0VRWURWUVFJRXdwRFlXeHBabTl5Ym1saApNUll3RkFZRFZRUUhFdzFOYjNWdWRHRnBiaUJXYVdWM01STXdFUVlEVlFRS0V3cEhiMjluYkdVZ1NXNWpNUjh3CkhRWURWUVFMRXhaSmJtWnZjbTFoZEdsdmJpQlVaV05vYm05c2IyZDVNUmN3RlFZRFZRUURFdzUzZDNjdVoyOXYKWjJ4bExtTnZiVENCbnpBTkJna3Foa2lHOXcwQkFRRUZBQU9CalFBd2dZa0NnWUVBcFp0WUpDSEo0VnBWWEhmVgpJbHN0UVRsTzRxQzAzaGpYK1prUHl2ZFlkMVE0K3FiQWVUd1htQ1VLWUhUaFZSZDVhWFNxbFB6eUlCd2llTVpyCldGbFJRZGRaMUl6WEFsVlJEV3dBbzYwS2VjcWVBWG5uVUsrNWZYb1RJL1VnV3NocmU4dEoreC9UTUhhUUtSL0oKY0lXUGhxYVFoc0p1elpidkFkR0E4MEJMeGRNQ0F3RUFBYUFBTUEwR0NTcUdTSWIzRFFFQkJRVUFBNEdCQUlobAo0UHZGcStlN2lwQVJnSTVaTStHWng2bXBDejQ0RFRvMEprd2ZSRGYrQnRyc2FDMHE2OGVUZjJYaFlPc3E0ZmtIClEwdUEwYVZvZzNmNWlKeENhM0hwNWd4YkpRNnpWNmtKMFRFc3VhYU9oRWtvOXNkcENvUE9uUkJtMmkvWFJEMkQKNmlOaDhmOHowU2hHc0ZxakRnRkh5RjNvK2xVeWorVUM2SDFRVzdibgotLS0tLUVORCBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0="}}`, - }, - // -- - - // k8s.io/kubernetes/pkg/apis/coordination/v1beta1 - gvr("coordination.k8s.io", "v1beta1", "leases"): { - stub: `{"metadata": {"name": "lease1"}, "spec": {"holderIdentity": "holder", "leaseDurationSeconds": 5}}`, - }, - // -- - - // k8s.io/kubernetes/pkg/apis/events/v1beta1 - gvr("events.k8s.io", "v1beta1", "events"): { - stub: `{"metadata": {"name": "event2"}, "regarding": {"namespace": "dryrunnamespace"}, "note": "some data here", "eventTime": "2017-08-09T15:04:05.000000Z", "reportingInstance": "node-xyz", "reportingController": "k8s.io/my-controller", "action": "DidNothing", "reason": "Laziness"}`, - }, - // -- - - // k8s.io/kubernetes/pkg/apis/extensions/v1beta1 - gvr("extensions", "v1beta1", "daemonsets"): { - stub: `{"metadata": {"name": "ds1"}, "spec": {"selector": {"matchLabels": {"u": "t"}}, "template": {"metadata": {"labels": {"u": "t"}}, "spec": {"containers": [{"image": "fedora:latest", "name": "container5"}]}}}}`, - }, - gvr("extensions", "v1beta1", "podsecuritypolicies"): { - stub: `{"metadata": {"name": "psp1"}, "spec": {"fsGroup": {"rule": "RunAsAny"}, "privileged": true, "runAsUser": {"rule": "RunAsAny"}, "seLinux": {"rule": "MustRunAs"}, "supplementalGroups": {"rule": "RunAsAny"}}}`, - }, - gvr("extensions", "v1beta1", "ingresses"): { - stub: `{"metadata": {"name": "ingress1"}, "spec": {"backend": {"serviceName": "service", "servicePort": 5000}}}`, - }, - gvr("extensions", "v1beta1", "networkpolicies"): { - stub: `{"metadata": {"name": "np1"}, "spec": {"podSelector": {"matchLabels": {"e": "f"}}}}`, - }, - gvr("extensions", "v1beta1", "deployments"): { - stub: `{"metadata": {"name": "deployment1"}, "spec": {"selector": {"matchLabels": {"f": "z"}}, "template": {"metadata": {"labels": {"f": "z"}}, "spec": {"containers": [{"image": "fedora:latest", "name": "container6"}]}}}}`, - }, - gvr("extensions", "v1beta1", "replicasets"): { - stub: `{"metadata": {"name": "rs1"}, "spec": {"selector": {"matchLabels": {"g": "h"}}, "template": {"metadata": {"labels": {"g": "h"}}, "spec": {"containers": [{"image": "fedora:latest", "name": "container4"}]}}}}`, - }, - // -- - - // k8s.io/kubernetes/pkg/apis/networking/v1 - gvr("networking.k8s.io", "v1", "networkpolicies"): { - stub: `{"metadata": {"name": "np2"}, "spec": {"podSelector": {"matchLabels": {"e": "f"}}}}`, - }, - // -- - - // k8s.io/kubernetes/pkg/apis/policy/v1beta1 - gvr("policy", "v1beta1", "poddisruptionbudgets"): { - stub: `{"metadata": {"name": "pdb1"}, "spec": {"selector": {"matchLabels": {"anokkey": "anokvalue"}}}}`, - }, - gvr("policy", "v1beta1", "podsecuritypolicies"): { - stub: `{"metadata": {"name": "psp2"}, "spec": {"fsGroup": {"rule": "RunAsAny"}, "privileged": true, "runAsUser": {"rule": "RunAsAny"}, "seLinux": {"rule": "MustRunAs"}, "supplementalGroups": {"rule": "RunAsAny"}}}`, - }, - // -- - - // k8s.io/kubernetes/pkg/apis/storage/v1alpha1 - gvr("storage.k8s.io", "v1alpha1", "volumeattachments"): { - stub: `{"metadata": {"name": "va1"}, "spec": {"attacher": "gce", "nodeName": "localhost", "source": {"persistentVolumeName": "pv1"}}}`, - }, - // -- - - // k8s.io/kubernetes/pkg/apis/storage/v1beta1 - gvr("storage.k8s.io", "v1beta1", "volumeattachments"): { - stub: `{"metadata": {"name": "va2"}, "spec": {"attacher": "gce", "nodeName": "localhost", "source": {"persistentVolumeName": "pv2"}}}`, - }, - // -- - - // k8s.io/kubernetes/pkg/apis/storage/v1beta1 - gvr("storage.k8s.io", "v1beta1", "storageclasses"): { - stub: `{"metadata": {"name": "sc1"}, "provisioner": "aws"}`, - }, - // -- - - // k8s.io/kubernetes/pkg/apis/storage/v1 - gvr("storage.k8s.io", "v1", "storageclasses"): { - stub: `{"metadata": {"name": "sc2"}, "provisioner": "aws"}`, - }, - // -- - - // k8s.io/kubernetes/pkg/apis/settings/v1alpha1 - gvr("settings.k8s.io", "v1alpha1", "podpresets"): { - stub: `{"metadata": {"name": "podpre1"}, "spec": {"env": [{"name": "FOO"}]}}`, - }, - // -- - - // k8s.io/kubernetes/pkg/apis/rbac/v1alpha1 - gvr("rbac.authorization.k8s.io", "v1alpha1", "roles"): { - stub: `{"metadata": {"name": "role1"}, "rules": [{"apiGroups": ["v1"], "resources": ["events"], "verbs": ["watch"]}]}`, - }, - gvr("rbac.authorization.k8s.io", "v1alpha1", "clusterroles"): { - stub: `{"metadata": {"name": "drcrole1"}, "rules": [{"nonResourceURLs": ["/version"], "verbs": ["get"]}]}`, - }, - gvr("rbac.authorization.k8s.io", "v1alpha1", "rolebindings"): { - stub: `{"metadata": {"name": "drroleb1"}, "roleRef": {"apiGroup": "rbac.authorization.k8s.io", "kind": "ClusterRole", "name": "somecr"}, "subjects": [{"apiVersion": "rbac.authorization.k8s.io/v1alpha1", "kind": "Group", "name": "system:authenticated"}]}`, - }, - gvr("rbac.authorization.k8s.io", "v1alpha1", "clusterrolebindings"): { - stub: `{"metadata": {"name": "drcroleb1"}, "roleRef": {"apiGroup": "rbac.authorization.k8s.io", "kind": "ClusterRole", "name": "somecr"}, "subjects": [{"apiVersion": "rbac.authorization.k8s.io/v1alpha1", "kind": "Group", "name": "system:authenticated"}]}`, - }, - // -- - - // k8s.io/kubernetes/pkg/apis/rbac/v1beta1 - gvr("rbac.authorization.k8s.io", "v1beta1", "roles"): { - stub: `{"metadata": {"name": "drrole2"}, "rules": [{"apiGroups": ["v1"], "resources": ["events"], "verbs": ["watch"]}]}`, - }, - gvr("rbac.authorization.k8s.io", "v1beta1", "clusterroles"): { - stub: `{"metadata": {"name": "drcrole2"}, "rules": [{"nonResourceURLs": ["/version"], "verbs": ["get"]}]}`, - }, - gvr("rbac.authorization.k8s.io", "v1beta1", "rolebindings"): { - stub: `{"metadata": {"name": "drroleb2"}, "roleRef": {"apiGroup": "rbac.authorization.k8s.io", "kind": "ClusterRole", "name": "somecr"}, "subjects": [{"apiVersion": "rbac.authorization.k8s.io/v1alpha1", "kind": "Group", "name": "system:authenticated"}]}`, - }, - gvr("rbac.authorization.k8s.io", "v1beta1", "clusterrolebindings"): { - stub: `{"metadata": {"name": "drcroleb2"}, "roleRef": {"apiGroup": "rbac.authorization.k8s.io", "kind": "ClusterRole", "name": "somecr"}, "subjects": [{"apiVersion": "rbac.authorization.k8s.io/v1alpha1", "kind": "Group", "name": "system:authenticated"}]}`, - }, - // -- - - // k8s.io/kubernetes/pkg/apis/rbac/v1 - gvr("rbac.authorization.k8s.io", "v1", "roles"): { - stub: `{"metadata": {"name": "drrole3"}, "rules": [{"apiGroups": ["v1"], "resources": ["events"], "verbs": ["watch"]}]}`, - }, - gvr("rbac.authorization.k8s.io", "v1", "clusterroles"): { - stub: `{"metadata": {"name": "drcrole3"}, "rules": [{"nonResourceURLs": ["/version"], "verbs": ["get"]}]}`, - }, - gvr("rbac.authorization.k8s.io", "v1", "rolebindings"): { - stub: `{"metadata": {"name": "drroleb3"}, "roleRef": {"apiGroup": "rbac.authorization.k8s.io", "kind": "ClusterRole", "name": "somecr"}, "subjects": [{"apiVersion": "rbac.authorization.k8s.io/v1alpha1", "kind": "Group", "name": "system:authenticated"}]}`, - }, - gvr("rbac.authorization.k8s.io", "v1", "clusterrolebindings"): { - stub: `{"metadata": {"name": "drcroleb3"}, "roleRef": {"apiGroup": "rbac.authorization.k8s.io", "kind": "ClusterRole", "name": "somecr"}, "subjects": [{"apiVersion": "rbac.authorization.k8s.io/v1alpha1", "kind": "Group", "name": "system:authenticated"}]}`, - }, - // -- - - // k8s.io/kubernetes/pkg/apis/admissionregistration/v1alpha1 - gvr("admissionregistration.k8s.io", "v1alpha1", "initializerconfigurations"): { - stub: `{"metadata":{"name":"ic1"},"initializers":[{"name":"initializer.k8s.io","rules":[{"apiGroups":["group"],"apiVersions":["version"],"resources":["resource"]}],"failurePolicy":"Ignore"}]}`, - }, - // k8s.io/kubernetes/pkg/apis/admissionregistration/v1beta1 - gvr("admissionregistration.k8s.io", "v1beta1", "validatingwebhookconfigurations"): { - stub: `{"metadata":{"name":"hook1","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"}]}`, - }, - gvr("admissionregistration.k8s.io", "v1beta1", "mutatingwebhookconfigurations"): { - stub: `{"metadata":{"name":"hook1","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"}]}`, - }, - // -- - - // k8s.io/kubernetes/pkg/apis/scheduling/v1alpha1 - gvr("scheduling.k8s.io", "v1alpha1", "priorityclasses"): { - stub: `{"metadata":{"name":"pc1"},"Value":1000}`, - }, - // -- - - // k8s.io/kubernetes/pkg/apis/scheduling/v1beta1 - gvr("scheduling.k8s.io", "v1beta1", "priorityclasses"): { - stub: `{"metadata":{"name":"pc2"},"Value":1000}`, - }, - // -- - - // k8s.io/kube-aggregator/pkg/apis/apiregistration/v1beta1 - // depends on aggregator using the same ungrouped RESTOptionsGetter as the kube apiserver, not SimpleRestOptionsFactory in aggregator.go - gvr("apiregistration.k8s.io", "v1beta1", "apiservices"): { - stub: `{"metadata": {"name": "dras1.foo.com"}, "spec": {"group": "foo.com", "version": "dras1", "groupPriorityMinimum":100, "versionPriority":10}}`, - }, - // -- - - // 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": "dras2.foo.com"}, "spec": {"group": "foo.com", "version": "dras2", "groupPriorityMinimum":100, "versionPriority":10}}`, - }, - // -- - - // k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1 - gvr("apiextensions.k8s.io", "v1beta1", "customresourcedefinitions"): { - stub: `{"metadata": {"name": "openshiftwebconsoleconfigs.webconsole.operator.openshift.io"},"spec": {"scope": "Cluster","group": "webconsole.operator.openshift.io","version": "v1alpha1","names": {"kind": "OpenShiftWebConsoleConfig","plural": "openshiftwebconsoleconfigs","singular": "openshiftwebconsoleconfig"}}}`, - }, - // -- - -} - // Only add kinds to this list when this a virtual resource with get and create verbs that doesn't actually // store into it's kind. We've used this downstream for mappings before. var kindWhiteList = sets.NewString() @@ -418,18 +86,6 @@ func getReplicasOrFail(t *testing.T, obj *unstructured.Unstructured) int64 { return replicas } -func setReplicasOrFail(t *testing.T, obj *unstructured.Unstructured, replicas int64) { - m, found, err := unstructured.NestedMap(obj.UnstructuredContent(), "spec") - if err != nil { - t.Fatalf("failed to get spec: %v", err) - } - if !found { - t.Fatal("object doesn't have spec") - } - m["replicas"] = replicas - unstructured.SetNestedMap(obj.UnstructuredContent(), m, "spec") -} - func DryRunScalePatchTest(t *testing.T, rsc dynamic.ResourceInterface, name string) { obj, err := rsc.Get(name, metav1.GetOptions{}, "scale") if errors.IsNotFound(err) { @@ -467,7 +123,9 @@ func DryRunScaleUpdateTest(t *testing.T, rsc dynamic.ResourceInterface, name str } replicas := getReplicasOrFail(t, obj) - unstructured.SetNestedField(obj.Object, int64(10), "spec", "replicas") + if err := unstructured.SetNestedField(obj.Object, int64(10), "spec", "replicas"); err != nil { + t.Fatalf("failed to set spec.replicas: %v", err) + } updatedObj, err := rsc.Update(obj, metav1.UpdateOptions{DryRun: []string{metav1.DryRunAll}}, "scale") if err != nil { t.Fatalf("failed to dry-run update scale sub-resource: %v", err) @@ -546,42 +204,34 @@ func DryRunDeleteTest(t *testing.T, rsc dynamic.ResourceInterface, name string) // TestDryRun tests dry-run on all types. func TestDryRun(t *testing.T) { - certDir, _ := ioutil.TempDir("", "test-integration-dryrun") - defer os.RemoveAll(certDir) - defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DryRun, true)() - clientConfig := startRealMasterOrDie(t, certDir) - dClient := dynamic.NewForConfigOrDie(clientConfig) - kubeClient := clientset.NewForConfigOrDie(clientConfig) - if _, err := kubeClient.CoreV1().Namespaces().Create(&v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: testNamespace}}); err != nil { + + master := etcd.StartRealMasterOrDie(t) + defer master.Cleanup() + + if _, err := master.Client.CoreV1().Namespaces().Create(&v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: testNamespace}}); err != nil { t.Fatal(err) } - discoveryClient := cacheddiscovery.NewMemCacheClient(kubeClient.Discovery()) - restMapper := restmapper.NewDeferredDiscoveryRESTMapper(discoveryClient) - restMapper.Reset() + dryrunData := etcd.GetEtcdStorageData() - serverResources, err := kubeClient.Discovery().ServerResources() - if err != nil { - t.Fatal(err) + // dry run specific stub overrides + for resource, stub := range map[schema.GroupVersionResource]string{ + // need to change event's namespace field to match dry run test + gvr("", "v1", "events"): `{"involvedObject": {"namespace": "dryrunnamespace"}, "message": "some data here", "metadata": {"name": "event1"}}`, + } { + data := dryrunData[resource] + data.Stub = stub + dryrunData[resource] = data } - resourcesToTest := getResourcesToTest(serverResources, false, t) - for _, resourceToTest := range resourcesToTest { - t.Run(resourceToTest.gvr.String(), func(t *testing.T) { - gvk := resourceToTest.gvk - gvResource := resourceToTest.gvr + for _, resourceToTest := range master.Resources { + t.Run(resourceToTest.Mapping.Resource.String(), func(t *testing.T) { + mapping := resourceToTest.Mapping + gvk := resourceToTest.Mapping.GroupVersionKind + gvResource := resourceToTest.Mapping.Resource kind := gvk.Kind - mapping := &meta.RESTMapping{ - Resource: resourceToTest.gvr, - GroupVersionKind: resourceToTest.gvk, - Scope: meta.RESTScopeRoot, - } - if resourceToTest.namespaced { - mapping.Scope = meta.RESTScopeNamespace - } - if kindWhiteList.Has(kind) { t.Skip("whitelisted") } @@ -589,23 +239,14 @@ func TestDryRun(t *testing.T) { testData, hasTest := dryrunData[gvResource] if !hasTest { - t.Fatalf("no test data for %s. Please add a test for your new type to dryrunData.", gvResource) + t.Fatalf("no test data for %s. Please add a test for your new type to etcd.GetEtcdStorageData().", gvResource) } - // we don't require GVK on the data we provide, so we fill it in here. We could, but that seems extraneous. - typeMetaAdder := map[string]interface{}{} - err := json.Unmarshal([]byte(testData.stub), &typeMetaAdder) + rsc, obj, err := etcd.JSONToUnstructured(testData.Stub, testNamespace, mapping, master.Dynamic) if err != nil { - t.Fatalf("failed to unmarshal stub (%v): %v", testData.stub, err) + t.Fatalf("failed to unmarshal stub (%v): %v", testData.Stub, err) } - typeMetaAdder["apiVersion"] = mapping.GroupVersionKind.GroupVersion().String() - typeMetaAdder["kind"] = mapping.GroupVersionKind.Kind - rsc := dClient.Resource(mapping.Resource).Namespace(testNamespace) - if mapping.Scope == meta.RESTScopeRoot { - rsc = dClient.Resource(mapping.Resource) - } - obj := &unstructured.Unstructured{Object: typeMetaAdder} name := obj.GetName() DryRunCreateTest(t, rsc, obj, gvResource) @@ -618,7 +259,7 @@ func TestDryRun(t *testing.T) { DryRunPatchTest(t, rsc, name) DryRunScalePatchTest(t, rsc, name) DryRunScaleUpdateTest(t, rsc, name) - if resourceToTest.hasDeleteCollection { + if resourceToTest.HasDeleteCollection { DryRunDeleteCollectionTest(t, rsc, name) } DryRunDeleteTest(t, rsc, name) @@ -630,136 +271,6 @@ func TestDryRun(t *testing.T) { } } -func startRealMasterOrDie(t *testing.T, certDir string) *restclient.Config { - _, defaultServiceClusterIPRange, err := net.ParseCIDR("10.0.0.0/24") - if err != nil { - t.Fatal(err) - } - - listener, _, err := genericapiserveroptions.CreateListener("tcp", "127.0.0.1:0") - if err != nil { - t.Fatal(err) - } - - kubeAPIServerOptions := options.NewServerRunOptions() - kubeAPIServerOptions.InsecureServing.BindPort = 0 - kubeAPIServerOptions.SecureServing.Listener = listener - kubeAPIServerOptions.SecureServing.ServerCert.CertDirectory = certDir - kubeAPIServerOptions.Etcd.StorageConfig.ServerList = []string{framework.GetEtcdURL()} - kubeAPIServerOptions.Etcd.DefaultStorageMediaType = runtime.ContentTypeJSON // force json we can easily interpret the result in etcd - kubeAPIServerOptions.ServiceClusterIPRange = *defaultServiceClusterIPRange - kubeAPIServerOptions.Authorization.Modes = []string{"RBAC"} - kubeAPIServerOptions.Admission.GenericAdmission.DisablePlugins = []string{"ServiceAccount"} - completedOptions, err := app.Complete(kubeAPIServerOptions) - if err != nil { - t.Fatal(err) - } - kubeAPIServerOptions.APIEnablement.RuntimeConfig.Set("api/all=true") - - kubeAPIServer, err := app.CreateServerChain(completedOptions, wait.NeverStop) - if err != nil { - t.Fatal(err) - } - kubeClientConfig := restclient.CopyConfig(kubeAPIServer.LoopbackClientConfig) - - go func() { - // Catch panics that occur in this go routine so we get a comprehensible failure - defer func() { - if err := recover(); err != nil { - t.Errorf("Unexpected panic trying to start API master: %#v", err) - } - }() - - if err := kubeAPIServer.PrepareRun().Run(wait.NeverStop); err != nil { - t.Fatal(err) - } - }() - - lastHealth := "" - if err := wait.PollImmediate(time.Second, time.Minute, func() (done bool, err error) { - // wait for the server to be healthy - result := clientset.NewForConfigOrDie(kubeClientConfig).RESTClient().Get().AbsPath("/healthz").Do() - content, _ := result.Raw() - lastHealth = string(content) - if errResult := result.Error(); errResult != nil { - t.Log(errResult) - return false, nil - } - var status int - result.StatusCode(&status) - return status == http.StatusOK, nil - }); err != nil { - t.Log(lastHealth) - t.Fatal(err) - } - - // this test makes lots of requests, don't be slow - kubeClientConfig.QPS = 99999 - kubeClientConfig.Burst = 9999 - - return kubeClientConfig -} - func gvr(g, v, r string) schema.GroupVersionResource { return schema.GroupVersionResource{Group: g, Version: v, Resource: r} } - -type resourceToTest struct { - gvk schema.GroupVersionKind - gvr schema.GroupVersionResource - namespaced bool - hasDeleteCollection bool -} - -func getResourcesToTest(serverResources []*metav1.APIResourceList, isOAPI bool, t *testing.T) []resourceToTest { - resourcesToTest := []resourceToTest{} - - for _, discoveryGroup := range serverResources { - for _, discoveryResource := range discoveryGroup.APIResources { - // this is a subresource, skip it - if strings.Contains(discoveryResource.Name, "/") { - continue - } - hasCreate := false - hasGet := false - hasDeleteCollection := false - for _, verb := range discoveryResource.Verbs { - if string(verb) == "get" { - hasGet = true - } - if string(verb) == "create" { - hasCreate = true - } - if string(verb) == "deletecollection" { - hasDeleteCollection = true - } - } - if !(hasCreate && hasGet) { - continue - } - - resourceGV, err := schema.ParseGroupVersion(discoveryGroup.GroupVersion) - if err != nil { - t.Fatal(err) - } - gvk := resourceGV.WithKind(discoveryResource.Kind) - if len(discoveryResource.Group) > 0 || len(discoveryResource.Version) > 0 { - gvk = schema.GroupVersionKind{ - Group: discoveryResource.Group, - Version: discoveryResource.Version, - Kind: discoveryResource.Kind, - } - } - gvr := resourceGV.WithResource(discoveryResource.Name) - - resourcesToTest = append(resourcesToTest, resourceToTest{ - gvk: gvk, - gvr: gvr, - namespaced: discoveryResource.Namespaced, - hasDeleteCollection: hasDeleteCollection, - }) - } - } - - return resourcesToTest -} diff --git a/test/integration/etcd/BUILD b/test/integration/etcd/BUILD index d5af8ea88b2..d17427684fa 100644 --- a/test/integration/etcd/BUILD +++ b/test/integration/etcd/BUILD @@ -1,9 +1,6 @@ package(default_visibility = ["//visibility:public"]) -load( - "@io_bazel_rules_go//go:def.bzl", - "go_test", -) +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_test( name = "go_default_test", @@ -12,31 +9,21 @@ go_test( "etcd_storage_path_test.go", "main_test.go", ], + embed = [":go_default_library"], tags = [ "etcd", "integration", ], deps = [ - "//cmd/kube-apiserver/app:go_default_library", - "//cmd/kube-apiserver/app/options:go_default_library", - "//pkg/master:go_default_library", "//staging/src/k8s.io/api/core/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/api/equality:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/api/meta:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/server/options:go_default_library", - "//staging/src/k8s.io/client-go/discovery/cached:go_default_library", "//staging/src/k8s.io/client-go/dynamic:go_default_library", - "//staging/src/k8s.io/client-go/kubernetes:go_default_library", - "//staging/src/k8s.io/client-go/rest:go_default_library", - "//staging/src/k8s.io/client-go/restmapper:go_default_library", - "//test/integration:go_default_library", "//test/integration/framework:go_default_library", "//vendor/github.com/coreos/etcd/clientv3:go_default_library", ], @@ -54,3 +41,33 @@ filegroup( srcs = [":package-srcs"], tags = ["automanaged"], ) + +go_library( + name = "go_default_library", + srcs = [ + "data.go", + "server.go", + ], + importpath = "k8s.io/kubernetes/test/integration/etcd", + deps = [ + "//cmd/kube-apiserver/app:go_default_library", + "//cmd/kube-apiserver/app/options:go_default_library", + "//pkg/master:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/api/meta:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library", + "//staging/src/k8s.io/apiserver/pkg/server/options:go_default_library", + "//staging/src/k8s.io/client-go/discovery/cached:go_default_library", + "//staging/src/k8s.io/client-go/dynamic:go_default_library", + "//staging/src/k8s.io/client-go/kubernetes:go_default_library", + "//staging/src/k8s.io/client-go/rest:go_default_library", + "//staging/src/k8s.io/client-go/restmapper:go_default_library", + "//test/integration:go_default_library", + "//test/integration/framework:go_default_library", + "//vendor/github.com/coreos/etcd/clientv3:go_default_library", + "//vendor/github.com/coreos/etcd/clientv3/concurrency:go_default_library", + ], +) diff --git a/test/integration/etcd/data.go b/test/integration/etcd/data.go new file mode 100644 index 00000000000..ab495b9dae1 --- /dev/null +++ b/test/integration/etcd/data.go @@ -0,0 +1,455 @@ +/* +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 "k8s.io/apimachinery/pkg/runtime/schema" + +// GetEtcdStorageData returns etcd data for all persisted objects. +// 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 map[schema.GroupVersionResource]StorageData{ + // k8s.io/kubernetes/pkg/api/v1 + gvr("", "v1", "configmaps"): { + Stub: `{"data": {"foo": "bar"}, "metadata": {"name": "cm1"}}`, + ExpectedEtcdPath: "/registry/configmaps/etcdstoragepathtestnamespace/cm1", + }, + gvr("", "v1", "services"): { + Stub: `{"metadata": {"name": "service1"}, "spec": {"externalName": "service1name", "ports": [{"port": 10000, "targetPort": 11000}], "selector": {"test": "data"}}}`, + ExpectedEtcdPath: "/registry/services/specs/etcdstoragepathtestnamespace/service1", + }, + gvr("", "v1", "podtemplates"): { + Stub: `{"metadata": {"name": "pt1name"}, "template": {"metadata": {"labels": {"pt": "01"}}, "spec": {"containers": [{"image": "fedora:latest", "name": "container9"}]}}}`, + ExpectedEtcdPath: "/registry/podtemplates/etcdstoragepathtestnamespace/pt1name", + }, + gvr("", "v1", "pods"): { + Stub: `{"metadata": {"name": "pod1"}, "spec": {"containers": [{"image": "fedora:latest", "name": "container7", "resources": {"limits": {"cpu": "1M"}, "requests": {"cpu": "1M"}}}]}}`, + ExpectedEtcdPath: "/registry/pods/etcdstoragepathtestnamespace/pod1", + }, + gvr("", "v1", "endpoints"): { + Stub: `{"metadata": {"name": "ep1name"}, "subsets": [{"addresses": [{"hostname": "bar-001", "ip": "192.168.3.1"}], "ports": [{"port": 8000}]}]}`, + ExpectedEtcdPath: "/registry/services/endpoints/etcdstoragepathtestnamespace/ep1name", + }, + gvr("", "v1", "resourcequotas"): { + Stub: `{"metadata": {"name": "rq1name"}, "spec": {"hard": {"cpu": "5M"}}}`, + ExpectedEtcdPath: "/registry/resourcequotas/etcdstoragepathtestnamespace/rq1name", + }, + gvr("", "v1", "limitranges"): { + Stub: `{"metadata": {"name": "lr1name"}, "spec": {"limits": [{"type": "Pod"}]}}`, + ExpectedEtcdPath: "/registry/limitranges/etcdstoragepathtestnamespace/lr1name", + }, + gvr("", "v1", "namespaces"): { + Stub: `{"metadata": {"name": "namespace1"}, "spec": {"finalizers": ["kubernetes"]}}`, + ExpectedEtcdPath: "/registry/namespaces/namespace1", + }, + gvr("", "v1", "nodes"): { + Stub: `{"metadata": {"name": "node1"}, "spec": {"unschedulable": true}}`, + ExpectedEtcdPath: "/registry/minions/node1", + }, + gvr("", "v1", "persistentvolumes"): { + Stub: `{"metadata": {"name": "pv1name"}, "spec": {"accessModes": ["ReadWriteOnce"], "capacity": {"storage": "3M"}, "hostPath": {"path": "/tmp/test/"}}}`, + ExpectedEtcdPath: "/registry/persistentvolumes/pv1name", + }, + gvr("", "v1", "events"): { + Stub: `{"involvedObject": {"namespace": "etcdstoragepathtestnamespace"}, "message": "some data here", "metadata": {"name": "event1"}}`, + ExpectedEtcdPath: "/registry/events/etcdstoragepathtestnamespace/event1", + }, + gvr("", "v1", "persistentvolumeclaims"): { + Stub: `{"metadata": {"name": "pvc1"}, "spec": {"accessModes": ["ReadWriteOnce"], "resources": {"limits": {"storage": "1M"}, "requests": {"storage": "2M"}}, "selector": {"matchLabels": {"pvc": "stuff"}}}}`, + ExpectedEtcdPath: "/registry/persistentvolumeclaims/etcdstoragepathtestnamespace/pvc1", + }, + gvr("", "v1", "serviceaccounts"): { + Stub: `{"metadata": {"name": "sa1name"}, "secrets": [{"name": "secret00"}]}`, + ExpectedEtcdPath: "/registry/serviceaccounts/etcdstoragepathtestnamespace/sa1name", + }, + gvr("", "v1", "secrets"): { + Stub: `{"data": {"key": "ZGF0YSBmaWxl"}, "metadata": {"name": "secret1"}}`, + ExpectedEtcdPath: "/registry/secrets/etcdstoragepathtestnamespace/secret1", + }, + gvr("", "v1", "replicationcontrollers"): { + Stub: `{"metadata": {"name": "rc1"}, "spec": {"selector": {"new": "stuff"}, "template": {"metadata": {"labels": {"new": "stuff"}}, "spec": {"containers": [{"image": "fedora:latest", "name": "container8"}]}}}}`, + ExpectedEtcdPath: "/registry/controllers/etcdstoragepathtestnamespace/rc1", + }, + // -- + + // k8s.io/kubernetes/pkg/apis/apps/v1beta1 + gvr("apps", "v1beta1", "statefulsets"): { + Stub: `{"metadata": {"name": "ss1"}, "spec": {"selector": {"matchLabels": {"a": "b"}}, "template": {"metadata": {"labels": {"a": "b"}}}}}`, + ExpectedEtcdPath: "/registry/statefulsets/etcdstoragepathtestnamespace/ss1", + ExpectedGVK: gvkP("apps", "v1", "StatefulSet"), + }, + gvr("apps", "v1beta1", "deployments"): { + Stub: `{"metadata": {"name": "deployment2"}, "spec": {"selector": {"matchLabels": {"f": "z"}}, "template": {"metadata": {"labels": {"f": "z"}}, "spec": {"containers": [{"image": "fedora:latest", "name": "container6"}]}}}}`, + ExpectedEtcdPath: "/registry/deployments/etcdstoragepathtestnamespace/deployment2", + ExpectedGVK: gvkP("apps", "v1", "Deployment"), + }, + gvr("apps", "v1beta1", "controllerrevisions"): { + Stub: `{"metadata":{"name":"crs1"},"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/etcdstoragepathtestnamespace/crs1", + ExpectedGVK: gvkP("apps", "v1", "ControllerRevision"), + }, + // -- + + // k8s.io/kubernetes/pkg/apis/apps/v1beta2 + gvr("apps", "v1beta2", "statefulsets"): { + Stub: `{"metadata": {"name": "ss2"}, "spec": {"selector": {"matchLabels": {"a": "b"}}, "template": {"metadata": {"labels": {"a": "b"}}}}}`, + ExpectedEtcdPath: "/registry/statefulsets/etcdstoragepathtestnamespace/ss2", + ExpectedGVK: gvkP("apps", "v1", "StatefulSet"), + }, + gvr("apps", "v1beta2", "deployments"): { + Stub: `{"metadata": {"name": "deployment3"}, "spec": {"selector": {"matchLabels": {"f": "z"}}, "template": {"metadata": {"labels": {"f": "z"}}, "spec": {"containers": [{"image": "fedora:latest", "name": "container6"}]}}}}`, + ExpectedEtcdPath: "/registry/deployments/etcdstoragepathtestnamespace/deployment3", + ExpectedGVK: gvkP("apps", "v1", "Deployment"), + }, + gvr("apps", "v1beta2", "daemonsets"): { + Stub: `{"metadata": {"name": "ds5"}, "spec": {"selector": {"matchLabels": {"a": "b"}}, "template": {"metadata": {"labels": {"a": "b"}}, "spec": {"containers": [{"image": "fedora:latest", "name": "container6"}]}}}}`, + ExpectedEtcdPath: "/registry/daemonsets/etcdstoragepathtestnamespace/ds5", + ExpectedGVK: gvkP("apps", "v1", "DaemonSet"), + }, + gvr("apps", "v1beta2", "replicasets"): { + Stub: `{"metadata": {"name": "rs2"}, "spec": {"selector": {"matchLabels": {"g": "h"}}, "template": {"metadata": {"labels": {"g": "h"}}, "spec": {"containers": [{"image": "fedora:latest", "name": "container4"}]}}}}`, + ExpectedEtcdPath: "/registry/replicasets/etcdstoragepathtestnamespace/rs2", + ExpectedGVK: gvkP("apps", "v1", "ReplicaSet"), + }, + gvr("apps", "v1beta2", "controllerrevisions"): { + Stub: `{"metadata":{"name":"crs2"},"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/etcdstoragepathtestnamespace/crs2", + ExpectedGVK: gvkP("apps", "v1", "ControllerRevision"), + }, + // -- + + // 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": "fedora:latest", "name": "container6"}]}}}}`, + ExpectedEtcdPath: "/registry/daemonsets/etcdstoragepathtestnamespace/ds6", + }, + gvr("apps", "v1", "deployments"): { + Stub: `{"metadata": {"name": "deployment4"}, "spec": {"selector": {"matchLabels": {"f": "z"}}, "template": {"metadata": {"labels": {"f": "z"}}, "spec": {"containers": [{"image": "fedora:latest", "name": "container6"}]}}}}`, + ExpectedEtcdPath: "/registry/deployments/etcdstoragepathtestnamespace/deployment4", + }, + gvr("apps", "v1", "statefulsets"): { + Stub: `{"metadata": {"name": "ss3"}, "spec": {"selector": {"matchLabels": {"a": "b"}}, "template": {"metadata": {"labels": {"a": "b"}}}}}`, + ExpectedEtcdPath: "/registry/statefulsets/etcdstoragepathtestnamespace/ss3", + }, + gvr("apps", "v1", "replicasets"): { + Stub: `{"metadata": {"name": "rs3"}, "spec": {"selector": {"matchLabels": {"g": "h"}}, "template": {"metadata": {"labels": {"g": "h"}}, "spec": {"containers": [{"image": "fedora:latest", "name": "container4"}]}}}}`, + ExpectedEtcdPath: "/registry/replicasets/etcdstoragepathtestnamespace/rs3", + }, + 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/etcdstoragepathtestnamespace/crs3", + }, + // -- + + // 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/etcdstoragepathtestnamespace/hpa2", + }, + // -- + + // k8s.io/kubernetes/pkg/apis/autoscaling/v2beta1 + gvr("autoscaling", "v2beta1", "horizontalpodautoscalers"): { + Stub: `{"metadata": {"name": "hpa1"}, "spec": {"maxReplicas": 3, "scaleTargetRef": {"kind": "something", "name": "cross"}}}`, + ExpectedEtcdPath: "/registry/horizontalpodautoscalers/etcdstoragepathtestnamespace/hpa1", + ExpectedGVK: gvkP("autoscaling", "v1", "HorizontalPodAutoscaler"), + }, + // -- + + // k8s.io/kubernetes/pkg/apis/autoscaling/v2beta2 + gvr("autoscaling", "v2beta2", "horizontalpodautoscalers"): { + Stub: `{"metadata": {"name": "hpa3"}, "spec": {"maxReplicas": 3, "scaleTargetRef": {"kind": "something", "name": "cross"}}}`, + ExpectedEtcdPath: "/registry/horizontalpodautoscalers/etcdstoragepathtestnamespace/hpa3", + ExpectedGVK: gvkP("autoscaling", "v1", "HorizontalPodAutoscaler"), + }, + // -- + + // 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": "fedora:latest", "name": "container1"}], "dnsPolicy": "ClusterFirst", "restartPolicy": "Never"}}}}`, + ExpectedEtcdPath: "/registry/jobs/etcdstoragepathtestnamespace/job1", + }, + // -- + + // k8s.io/kubernetes/pkg/apis/batch/v1beta1 + gvr("batch", "v1beta1", "cronjobs"): { + Stub: `{"metadata": {"name": "cjv1beta1"}, "spec": {"jobTemplate": {"spec": {"template": {"metadata": {"labels": {"controller-uid": "uid0"}}, "spec": {"containers": [{"image": "fedora:latest", "name": "container0"}], "dnsPolicy": "ClusterFirst", "restartPolicy": "Never"}}}}, "schedule": "* * * * *"}}`, + ExpectedEtcdPath: "/registry/cronjobs/etcdstoragepathtestnamespace/cjv1beta1", + }, + // -- + + // k8s.io/kubernetes/pkg/apis/batch/v2alpha1 + gvr("batch", "v2alpha1", "cronjobs"): { + Stub: `{"metadata": {"name": "cjv2alpha1"}, "spec": {"jobTemplate": {"spec": {"template": {"metadata": {"labels": {"controller-uid": "uid0"}}, "spec": {"containers": [{"image": "fedora:latest", "name": "container0"}], "dnsPolicy": "ClusterFirst", "restartPolicy": "Never"}}}}, "schedule": "* * * * *"}}`, + ExpectedEtcdPath: "/registry/cronjobs/etcdstoragepathtestnamespace/cjv2alpha1", + ExpectedGVK: gvkP("batch", "v1beta1", "CronJob"), + }, + // -- + + // k8s.io/kubernetes/pkg/apis/certificates/v1beta1 + gvr("certificates.k8s.io", "v1beta1", "certificatesigningrequests"): { + Stub: `{"metadata": {"name": "csr1"}, "spec": {"request": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0KTUlJQnlqQ0NBVE1DQVFBd2dZa3hDekFKQmdOVkJBWVRBbFZUTVJNd0VRWURWUVFJRXdwRFlXeHBabTl5Ym1saApNUll3RkFZRFZRUUhFdzFOYjNWdWRHRnBiaUJXYVdWM01STXdFUVlEVlFRS0V3cEhiMjluYkdVZ1NXNWpNUjh3CkhRWURWUVFMRXhaSmJtWnZjbTFoZEdsdmJpQlVaV05vYm05c2IyZDVNUmN3RlFZRFZRUURFdzUzZDNjdVoyOXYKWjJ4bExtTnZiVENCbnpBTkJna3Foa2lHOXcwQkFRRUZBQU9CalFBd2dZa0NnWUVBcFp0WUpDSEo0VnBWWEhmVgpJbHN0UVRsTzRxQzAzaGpYK1prUHl2ZFlkMVE0K3FiQWVUd1htQ1VLWUhUaFZSZDVhWFNxbFB6eUlCd2llTVpyCldGbFJRZGRaMUl6WEFsVlJEV3dBbzYwS2VjcWVBWG5uVUsrNWZYb1RJL1VnV3NocmU4dEoreC9UTUhhUUtSL0oKY0lXUGhxYVFoc0p1elpidkFkR0E4MEJMeGRNQ0F3RUFBYUFBTUEwR0NTcUdTSWIzRFFFQkJRVUFBNEdCQUlobAo0UHZGcStlN2lwQVJnSTVaTStHWng2bXBDejQ0RFRvMEprd2ZSRGYrQnRyc2FDMHE2OGVUZjJYaFlPc3E0ZmtIClEwdUEwYVZvZzNmNWlKeENhM0hwNWd4YkpRNnpWNmtKMFRFc3VhYU9oRWtvOXNkcENvUE9uUkJtMmkvWFJEMkQKNmlOaDhmOHowU2hHc0ZxakRnRkh5RjNvK2xVeWorVUM2SDFRVzdibgotLS0tLUVORCBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0="}}`, + ExpectedEtcdPath: "/registry/certificatesigningrequests/csr1", + }, + // -- + + // k8s.io/kubernetes/pkg/apis/coordination/v1beta1 + gvr("coordination.k8s.io", "v1beta1", "leases"): { + Stub: `{"metadata": {"name": "lease1"}, "spec": {"holderIdentity": "holder", "leaseDurationSeconds": 5}}`, + ExpectedEtcdPath: "/registry/leases/etcdstoragepathtestnamespace/lease1", + }, + // -- + + // k8s.io/kubernetes/pkg/apis/events/v1beta1 + gvr("events.k8s.io", "v1beta1", "events"): { + Stub: `{"metadata": {"name": "event2"}, "regarding": {"namespace": "etcdstoragepathtestnamespace"}, "note": "some data here", "eventTime": "2017-08-09T15:04:05.000000Z", "reportingInstance": "node-xyz", "reportingController": "k8s.io/my-controller", "action": "DidNothing", "reason": "Laziness"}`, + ExpectedEtcdPath: "/registry/events/etcdstoragepathtestnamespace/event2", + ExpectedGVK: gvkP("", "v1", "Event"), + }, + // -- + + // k8s.io/kubernetes/pkg/apis/extensions/v1beta1 + gvr("extensions", "v1beta1", "daemonsets"): { + Stub: `{"metadata": {"name": "ds1"}, "spec": {"selector": {"matchLabels": {"u": "t"}}, "template": {"metadata": {"labels": {"u": "t"}}, "spec": {"containers": [{"image": "fedora:latest", "name": "container5"}]}}}}`, + ExpectedEtcdPath: "/registry/daemonsets/etcdstoragepathtestnamespace/ds1", + ExpectedGVK: gvkP("apps", "v1", "DaemonSet"), + }, + gvr("extensions", "v1beta1", "podsecuritypolicies"): { + Stub: `{"metadata": {"name": "psp1"}, "spec": {"fsGroup": {"rule": "RunAsAny"}, "privileged": true, "runAsUser": {"rule": "RunAsAny"}, "seLinux": {"rule": "MustRunAs"}, "supplementalGroups": {"rule": "RunAsAny"}}}`, + ExpectedEtcdPath: "/registry/podsecuritypolicy/psp1", + ExpectedGVK: gvkP("policy", "v1beta1", "PodSecurityPolicy"), + }, + gvr("extensions", "v1beta1", "ingresses"): { + Stub: `{"metadata": {"name": "ingress1"}, "spec": {"backend": {"serviceName": "service", "servicePort": 5000}}}`, + ExpectedEtcdPath: "/registry/ingress/etcdstoragepathtestnamespace/ingress1", + }, + gvr("extensions", "v1beta1", "networkpolicies"): { + Stub: `{"metadata": {"name": "np1"}, "spec": {"podSelector": {"matchLabels": {"e": "f"}}}}`, + ExpectedEtcdPath: "/registry/networkpolicies/etcdstoragepathtestnamespace/np1", + ExpectedGVK: gvkP("networking.k8s.io", "v1", "NetworkPolicy"), + }, + gvr("extensions", "v1beta1", "deployments"): { + Stub: `{"metadata": {"name": "deployment1"}, "spec": {"selector": {"matchLabels": {"f": "z"}}, "template": {"metadata": {"labels": {"f": "z"}}, "spec": {"containers": [{"image": "fedora:latest", "name": "container6"}]}}}}`, + ExpectedEtcdPath: "/registry/deployments/etcdstoragepathtestnamespace/deployment1", + ExpectedGVK: gvkP("apps", "v1", "Deployment"), + }, + gvr("extensions", "v1beta1", "replicasets"): { + Stub: `{"metadata": {"name": "rs1"}, "spec": {"selector": {"matchLabels": {"g": "h"}}, "template": {"metadata": {"labels": {"g": "h"}}, "spec": {"containers": [{"image": "fedora:latest", "name": "container4"}]}}}}`, + ExpectedEtcdPath: "/registry/replicasets/etcdstoragepathtestnamespace/rs1", + ExpectedGVK: gvkP("apps", "v1", "ReplicaSet"), + }, + // -- + + // k8s.io/kubernetes/pkg/apis/networking/v1 + gvr("networking.k8s.io", "v1", "networkpolicies"): { + Stub: `{"metadata": {"name": "np2"}, "spec": {"podSelector": {"matchLabels": {"e": "f"}}}}`, + ExpectedEtcdPath: "/registry/networkpolicies/etcdstoragepathtestnamespace/np2", + }, + // -- + + // k8s.io/kubernetes/pkg/apis/policy/v1beta1 + gvr("policy", "v1beta1", "poddisruptionbudgets"): { + Stub: `{"metadata": {"name": "pdb1"}, "spec": {"selector": {"matchLabels": {"anokkey": "anokvalue"}}}}`, + ExpectedEtcdPath: "/registry/poddisruptionbudgets/etcdstoragepathtestnamespace/pdb1", + }, + gvr("policy", "v1beta1", "podsecuritypolicies"): { + Stub: `{"metadata": {"name": "psp2"}, "spec": {"fsGroup": {"rule": "RunAsAny"}, "privileged": true, "runAsUser": {"rule": "RunAsAny"}, "seLinux": {"rule": "MustRunAs"}, "supplementalGroups": {"rule": "RunAsAny"}}}`, + ExpectedEtcdPath: "/registry/podsecuritypolicy/psp2", + }, + // -- + + // k8s.io/kubernetes/pkg/apis/storage/v1alpha1 + gvr("storage.k8s.io", "v1alpha1", "volumeattachments"): { + Stub: `{"metadata": {"name": "va1"}, "spec": {"attacher": "gce", "nodeName": "localhost", "source": {"persistentVolumeName": "pv1"}}}`, + ExpectedEtcdPath: "/registry/volumeattachments/va1", + ExpectedGVK: gvkP("storage.k8s.io", "v1beta1", "VolumeAttachment"), + }, + // -- + + // k8s.io/kubernetes/pkg/apis/storage/v1beta1 + gvr("storage.k8s.io", "v1beta1", "volumeattachments"): { + Stub: `{"metadata": {"name": "va2"}, "spec": {"attacher": "gce", "nodeName": "localhost", "source": {"persistentVolumeName": "pv2"}}}`, + ExpectedEtcdPath: "/registry/volumeattachments/va2", + }, + // -- + + // k8s.io/kubernetes/pkg/apis/storage/v1beta1 + gvr("storage.k8s.io", "v1beta1", "storageclasses"): { + Stub: `{"metadata": {"name": "sc1"}, "provisioner": "aws"}`, + ExpectedEtcdPath: "/registry/storageclasses/sc1", + ExpectedGVK: gvkP("storage.k8s.io", "v1", "StorageClass"), + }, + // -- + + // k8s.io/kubernetes/pkg/apis/storage/v1 + gvr("storage.k8s.io", "v1", "storageclasses"): { + Stub: `{"metadata": {"name": "sc2"}, "provisioner": "aws"}`, + ExpectedEtcdPath: "/registry/storageclasses/sc2", + }, + // -- + + // k8s.io/kubernetes/pkg/apis/settings/v1alpha1 + gvr("settings.k8s.io", "v1alpha1", "podpresets"): { + Stub: `{"metadata": {"name": "podpre1"}, "spec": {"env": [{"name": "FOO"}]}}`, + ExpectedEtcdPath: "/registry/podpresets/etcdstoragepathtestnamespace/podpre1", + }, + // -- + + // k8s.io/kubernetes/pkg/apis/rbac/v1alpha1 + gvr("rbac.authorization.k8s.io", "v1alpha1", "roles"): { + Stub: `{"metadata": {"name": "role1"}, "rules": [{"apiGroups": ["v1"], "resources": ["events"], "verbs": ["watch"]}]}`, + ExpectedEtcdPath: "/registry/roles/etcdstoragepathtestnamespace/role1", + ExpectedGVK: gvkP("rbac.authorization.k8s.io", "v1", "Role"), + }, + gvr("rbac.authorization.k8s.io", "v1alpha1", "clusterroles"): { + Stub: `{"metadata": {"name": "crole1"}, "rules": [{"nonResourceURLs": ["/version"], "verbs": ["get"]}]}`, + ExpectedEtcdPath: "/registry/clusterroles/crole1", + ExpectedGVK: gvkP("rbac.authorization.k8s.io", "v1", "ClusterRole"), + }, + gvr("rbac.authorization.k8s.io", "v1alpha1", "rolebindings"): { + Stub: `{"metadata": {"name": "roleb1"}, "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/etcdstoragepathtestnamespace/roleb1", + ExpectedGVK: gvkP("rbac.authorization.k8s.io", "v1", "RoleBinding"), + }, + gvr("rbac.authorization.k8s.io", "v1alpha1", "clusterrolebindings"): { + Stub: `{"metadata": {"name": "croleb1"}, "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/croleb1", + ExpectedGVK: gvkP("rbac.authorization.k8s.io", "v1", "ClusterRoleBinding"), + }, + // -- + + // k8s.io/kubernetes/pkg/apis/rbac/v1beta1 + gvr("rbac.authorization.k8s.io", "v1beta1", "roles"): { + Stub: `{"metadata": {"name": "role2"}, "rules": [{"apiGroups": ["v1"], "resources": ["events"], "verbs": ["watch"]}]}`, + ExpectedEtcdPath: "/registry/roles/etcdstoragepathtestnamespace/role2", + ExpectedGVK: gvkP("rbac.authorization.k8s.io", "v1", "Role"), + }, + gvr("rbac.authorization.k8s.io", "v1beta1", "clusterroles"): { + Stub: `{"metadata": {"name": "crole2"}, "rules": [{"nonResourceURLs": ["/version"], "verbs": ["get"]}]}`, + ExpectedEtcdPath: "/registry/clusterroles/crole2", + ExpectedGVK: gvkP("rbac.authorization.k8s.io", "v1", "ClusterRole"), + }, + gvr("rbac.authorization.k8s.io", "v1beta1", "rolebindings"): { + Stub: `{"metadata": {"name": "roleb2"}, "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/etcdstoragepathtestnamespace/roleb2", + ExpectedGVK: gvkP("rbac.authorization.k8s.io", "v1", "RoleBinding"), + }, + gvr("rbac.authorization.k8s.io", "v1beta1", "clusterrolebindings"): { + Stub: `{"metadata": {"name": "croleb2"}, "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/croleb2", + ExpectedGVK: gvkP("rbac.authorization.k8s.io", "v1", "ClusterRoleBinding"), + }, + // -- + + // 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/etcdstoragepathtestnamespace/role3", + }, + gvr("rbac.authorization.k8s.io", "v1", "clusterroles"): { + Stub: `{"metadata": {"name": "crole3"}, "rules": [{"nonResourceURLs": ["/version"], "verbs": ["get"]}]}`, + ExpectedEtcdPath: "/registry/clusterroles/crole3", + }, + 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/etcdstoragepathtestnamespace/roleb3", + }, + 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", + }, + // -- + + // k8s.io/kubernetes/pkg/apis/admissionregistration/v1alpha1 + gvr("admissionregistration.k8s.io", "v1alpha1", "initializerconfigurations"): { + Stub: `{"metadata":{"name":"ic1"},"initializers":[{"name":"initializer.k8s.io","rules":[{"apiGroups":["group"],"apiVersions":["version"],"resources":["resource"]}],"failurePolicy":"Ignore"}]}`, + ExpectedEtcdPath: "/registry/initializerconfigurations/ic1", + }, + // k8s.io/kubernetes/pkg/apis/admissionregistration/v1beta1 + gvr("admissionregistration.k8s.io", "v1beta1", "validatingwebhookconfigurations"): { + Stub: `{"metadata":{"name":"hook1","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"}]}`, + ExpectedEtcdPath: "/registry/validatingwebhookconfigurations/hook1", + }, + gvr("admissionregistration.k8s.io", "v1beta1", "mutatingwebhookconfigurations"): { + Stub: `{"metadata":{"name":"hook1","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"}]}`, + ExpectedEtcdPath: "/registry/mutatingwebhookconfigurations/hook1", + }, + // -- + + // k8s.io/kubernetes/pkg/apis/scheduling/v1alpha1 + gvr("scheduling.k8s.io", "v1alpha1", "priorityclasses"): { + Stub: `{"metadata":{"name":"pc1"},"Value":1000}`, + ExpectedEtcdPath: "/registry/priorityclasses/pc1", + ExpectedGVK: gvkP("scheduling.k8s.io", "v1beta1", "PriorityClass"), + }, + // -- + + // k8s.io/kubernetes/pkg/apis/scheduling/v1beta1 + gvr("scheduling.k8s.io", "v1beta1", "priorityclasses"): { + Stub: `{"metadata":{"name":"pc2"},"Value":1000}`, + ExpectedEtcdPath: "/registry/priorityclasses/pc2", + }, + // -- + + // k8s.io/kube-aggregator/pkg/apis/apiregistration/v1beta1 + // depends on aggregator using the same ungrouped RESTOptionsGetter as the kube apiserver, not SimpleRestOptionsFactory in aggregator.go + gvr("apiregistration.k8s.io", "v1beta1", "apiservices"): { + Stub: `{"metadata": {"name": "as1.foo.com"}, "spec": {"group": "foo.com", "version": "as1", "groupPriorityMinimum":100, "versionPriority":10}}`, + ExpectedEtcdPath: "/registry/apiregistration.k8s.io/apiservices/as1.foo.com", + }, + // -- + + // 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", + ExpectedGVK: gvkP("apiregistration.k8s.io", "v1beta1", "APIService"), + }, + // -- + + // k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1 + gvr("apiextensions.k8s.io", "v1beta1", "customresourcedefinitions"): { + Stub: `{"metadata": {"name": "openshiftwebconsoleconfigs.webconsole.operator.openshift.io"},"spec": {"scope": "Cluster","group": "webconsole.operator.openshift.io","version": "v1alpha1","names": {"kind": "OpenShiftWebConsoleConfig","plural": "openshiftwebconsoleconfigs","singular": "openshiftwebconsoleconfig"}}}`, + ExpectedEtcdPath: "/registry/apiextensions.k8s.io/customresourcedefinitions/openshiftwebconsoleconfigs.webconsole.operator.openshift.io", + }, + // -- + } +} + +// 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 +} + +// Prerequisite contains information required to create a resource (but not verify it) +type Prerequisite struct { + GvrData schema.GroupVersionResource + Stub string +} + +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} +} diff --git a/test/integration/etcd/etcd_storage_path_test.go b/test/integration/etcd/etcd_storage_path_test.go index b32e7989c04..1ec75a315f7 100644 --- a/test/integration/etcd/etcd_storage_path_test.go +++ b/test/integration/etcd/etcd_storage_path_test.go @@ -20,457 +20,23 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" - "net" - "net/http" - "os" "reflect" "strings" "testing" - "time" + + "github.com/coreos/etcd/clientv3" "k8s.io/api/core/v1" apiequality "k8s.io/apimachinery/pkg/api/equality" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/diff" "k8s.io/apimachinery/pkg/util/sets" - "k8s.io/apimachinery/pkg/util/wait" - genericapiserveroptions "k8s.io/apiserver/pkg/server/options" - cacheddiscovery "k8s.io/client-go/discovery/cached" "k8s.io/client-go/dynamic" - clientset "k8s.io/client-go/kubernetes" - restclient "k8s.io/client-go/rest" - "k8s.io/client-go/restmapper" - "k8s.io/kubernetes/cmd/kube-apiserver/app" - "k8s.io/kubernetes/cmd/kube-apiserver/app/options" - "k8s.io/kubernetes/test/integration" - "k8s.io/kubernetes/test/integration/framework" - - // install all APIs - _ "k8s.io/kubernetes/pkg/master" // TODO what else is needed - - "github.com/coreos/etcd/clientv3" ) -// Etcd data for all persisted objects. -var etcdStorageData = map[schema.GroupVersionResource]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 -}{ - // k8s.io/kubernetes/pkg/api/v1 - gvr("", "v1", "configmaps"): { - stub: `{"data": {"foo": "bar"}, "metadata": {"name": "cm1"}}`, - expectedEtcdPath: "/registry/configmaps/etcdstoragepathtestnamespace/cm1", - }, - gvr("", "v1", "services"): { - stub: `{"metadata": {"name": "service1"}, "spec": {"externalName": "service1name", "ports": [{"port": 10000, "targetPort": 11000}], "selector": {"test": "data"}}}`, - expectedEtcdPath: "/registry/services/specs/etcdstoragepathtestnamespace/service1", - }, - gvr("", "v1", "podtemplates"): { - stub: `{"metadata": {"name": "pt1name"}, "template": {"metadata": {"labels": {"pt": "01"}}, "spec": {"containers": [{"image": "fedora:latest", "name": "container9"}]}}}`, - expectedEtcdPath: "/registry/podtemplates/etcdstoragepathtestnamespace/pt1name", - }, - gvr("", "v1", "pods"): { - stub: `{"metadata": {"name": "pod1"}, "spec": {"containers": [{"image": "fedora:latest", "name": "container7", "resources": {"limits": {"cpu": "1M"}, "requests": {"cpu": "1M"}}}]}}`, - expectedEtcdPath: "/registry/pods/etcdstoragepathtestnamespace/pod1", - }, - gvr("", "v1", "endpoints"): { - stub: `{"metadata": {"name": "ep1name"}, "subsets": [{"addresses": [{"hostname": "bar-001", "ip": "192.168.3.1"}], "ports": [{"port": 8000}]}]}`, - expectedEtcdPath: "/registry/services/endpoints/etcdstoragepathtestnamespace/ep1name", - }, - gvr("", "v1", "resourcequotas"): { - stub: `{"metadata": {"name": "rq1name"}, "spec": {"hard": {"cpu": "5M"}}}`, - expectedEtcdPath: "/registry/resourcequotas/etcdstoragepathtestnamespace/rq1name", - }, - gvr("", "v1", "limitranges"): { - stub: `{"metadata": {"name": "lr1name"}, "spec": {"limits": [{"type": "Pod"}]}}`, - expectedEtcdPath: "/registry/limitranges/etcdstoragepathtestnamespace/lr1name", - }, - gvr("", "v1", "namespaces"): { - stub: `{"metadata": {"name": "namespace1"}, "spec": {"finalizers": ["kubernetes"]}}`, - expectedEtcdPath: "/registry/namespaces/namespace1", - }, - gvr("", "v1", "nodes"): { - stub: `{"metadata": {"name": "node1"}, "spec": {"unschedulable": true}}`, - expectedEtcdPath: "/registry/minions/node1", - }, - gvr("", "v1", "persistentvolumes"): { - stub: `{"metadata": {"name": "pv1name"}, "spec": {"accessModes": ["ReadWriteOnce"], "capacity": {"storage": "3M"}, "hostPath": {"path": "/tmp/test/"}}}`, - expectedEtcdPath: "/registry/persistentvolumes/pv1name", - }, - gvr("", "v1", "events"): { - stub: `{"involvedObject": {"namespace": "etcdstoragepathtestnamespace"}, "message": "some data here", "metadata": {"name": "event1"}}`, - expectedEtcdPath: "/registry/events/etcdstoragepathtestnamespace/event1", - }, - gvr("", "v1", "persistentvolumeclaims"): { - stub: `{"metadata": {"name": "pvc1"}, "spec": {"accessModes": ["ReadWriteOnce"], "resources": {"limits": {"storage": "1M"}, "requests": {"storage": "2M"}}, "selector": {"matchLabels": {"pvc": "stuff"}}}}`, - expectedEtcdPath: "/registry/persistentvolumeclaims/etcdstoragepathtestnamespace/pvc1", - }, - gvr("", "v1", "serviceaccounts"): { - stub: `{"metadata": {"name": "sa1name"}, "secrets": [{"name": "secret00"}]}`, - expectedEtcdPath: "/registry/serviceaccounts/etcdstoragepathtestnamespace/sa1name", - }, - gvr("", "v1", "secrets"): { - stub: `{"data": {"key": "ZGF0YSBmaWxl"}, "metadata": {"name": "secret1"}}`, - expectedEtcdPath: "/registry/secrets/etcdstoragepathtestnamespace/secret1", - }, - gvr("", "v1", "replicationcontrollers"): { - stub: `{"metadata": {"name": "rc1"}, "spec": {"selector": {"new": "stuff"}, "template": {"metadata": {"labels": {"new": "stuff"}}, "spec": {"containers": [{"image": "fedora:latest", "name": "container8"}]}}}}`, - expectedEtcdPath: "/registry/controllers/etcdstoragepathtestnamespace/rc1", - }, - // -- - - // k8s.io/kubernetes/pkg/apis/apps/v1beta1 - gvr("apps", "v1beta1", "statefulsets"): { - stub: `{"metadata": {"name": "ss1"}, "spec": {"selector": {"matchLabels": {"a": "b"}}, "template": {"metadata": {"labels": {"a": "b"}}}}}`, - expectedEtcdPath: "/registry/statefulsets/etcdstoragepathtestnamespace/ss1", - expectedGVK: gvkP("apps", "v1", "StatefulSet"), - }, - gvr("apps", "v1beta1", "deployments"): { - stub: `{"metadata": {"name": "deployment2"}, "spec": {"selector": {"matchLabels": {"f": "z"}}, "template": {"metadata": {"labels": {"f": "z"}}, "spec": {"containers": [{"image": "fedora:latest", "name": "container6"}]}}}}`, - expectedEtcdPath: "/registry/deployments/etcdstoragepathtestnamespace/deployment2", - expectedGVK: gvkP("apps", "v1", "Deployment"), - }, - gvr("apps", "v1beta1", "controllerrevisions"): { - stub: `{"metadata":{"name":"crs1"},"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/etcdstoragepathtestnamespace/crs1", - expectedGVK: gvkP("apps", "v1", "ControllerRevision"), - }, - // -- - - // k8s.io/kubernetes/pkg/apis/apps/v1beta2 - gvr("apps", "v1beta2", "statefulsets"): { - stub: `{"metadata": {"name": "ss2"}, "spec": {"selector": {"matchLabels": {"a": "b"}}, "template": {"metadata": {"labels": {"a": "b"}}}}}`, - expectedEtcdPath: "/registry/statefulsets/etcdstoragepathtestnamespace/ss2", - expectedGVK: gvkP("apps", "v1", "StatefulSet"), - }, - gvr("apps", "v1beta2", "deployments"): { - stub: `{"metadata": {"name": "deployment3"}, "spec": {"selector": {"matchLabels": {"f": "z"}}, "template": {"metadata": {"labels": {"f": "z"}}, "spec": {"containers": [{"image": "fedora:latest", "name": "container6"}]}}}}`, - expectedEtcdPath: "/registry/deployments/etcdstoragepathtestnamespace/deployment3", - expectedGVK: gvkP("apps", "v1", "Deployment"), - }, - gvr("apps", "v1beta2", "daemonsets"): { - stub: `{"metadata": {"name": "ds5"}, "spec": {"selector": {"matchLabels": {"a": "b"}}, "template": {"metadata": {"labels": {"a": "b"}}, "spec": {"containers": [{"image": "fedora:latest", "name": "container6"}]}}}}`, - expectedEtcdPath: "/registry/daemonsets/etcdstoragepathtestnamespace/ds5", - expectedGVK: gvkP("apps", "v1", "DaemonSet"), - }, - gvr("apps", "v1beta2", "replicasets"): { - stub: `{"metadata": {"name": "rs2"}, "spec": {"selector": {"matchLabels": {"g": "h"}}, "template": {"metadata": {"labels": {"g": "h"}}, "spec": {"containers": [{"image": "fedora:latest", "name": "container4"}]}}}}`, - expectedEtcdPath: "/registry/replicasets/etcdstoragepathtestnamespace/rs2", - expectedGVK: gvkP("apps", "v1", "ReplicaSet"), - }, - gvr("apps", "v1beta2", "controllerrevisions"): { - stub: `{"metadata":{"name":"crs2"},"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/etcdstoragepathtestnamespace/crs2", - expectedGVK: gvkP("apps", "v1", "ControllerRevision"), - }, - // -- - - // 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": "fedora:latest", "name": "container6"}]}}}}`, - expectedEtcdPath: "/registry/daemonsets/etcdstoragepathtestnamespace/ds6", - }, - gvr("apps", "v1", "deployments"): { - stub: `{"metadata": {"name": "deployment4"}, "spec": {"selector": {"matchLabels": {"f": "z"}}, "template": {"metadata": {"labels": {"f": "z"}}, "spec": {"containers": [{"image": "fedora:latest", "name": "container6"}]}}}}`, - expectedEtcdPath: "/registry/deployments/etcdstoragepathtestnamespace/deployment4", - }, - gvr("apps", "v1", "statefulsets"): { - stub: `{"metadata": {"name": "ss3"}, "spec": {"selector": {"matchLabels": {"a": "b"}}, "template": {"metadata": {"labels": {"a": "b"}}}}}`, - expectedEtcdPath: "/registry/statefulsets/etcdstoragepathtestnamespace/ss3", - }, - gvr("apps", "v1", "replicasets"): { - stub: `{"metadata": {"name": "rs3"}, "spec": {"selector": {"matchLabels": {"g": "h"}}, "template": {"metadata": {"labels": {"g": "h"}}, "spec": {"containers": [{"image": "fedora:latest", "name": "container4"}]}}}}`, - expectedEtcdPath: "/registry/replicasets/etcdstoragepathtestnamespace/rs3", - }, - 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/etcdstoragepathtestnamespace/crs3", - }, - // -- - - // 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/etcdstoragepathtestnamespace/hpa2", - }, - // -- - - // k8s.io/kubernetes/pkg/apis/autoscaling/v2beta1 - gvr("autoscaling", "v2beta1", "horizontalpodautoscalers"): { - stub: `{"metadata": {"name": "hpa1"}, "spec": {"maxReplicas": 3, "scaleTargetRef": {"kind": "something", "name": "cross"}}}`, - expectedEtcdPath: "/registry/horizontalpodautoscalers/etcdstoragepathtestnamespace/hpa1", - expectedGVK: gvkP("autoscaling", "v1", "HorizontalPodAutoscaler"), - }, - // -- - - // k8s.io/kubernetes/pkg/apis/autoscaling/v2beta2 - gvr("autoscaling", "v2beta2", "horizontalpodautoscalers"): { - stub: `{"metadata": {"name": "hpa3"}, "spec": {"maxReplicas": 3, "scaleTargetRef": {"kind": "something", "name": "cross"}}}`, - expectedEtcdPath: "/registry/horizontalpodautoscalers/etcdstoragepathtestnamespace/hpa3", - expectedGVK: gvkP("autoscaling", "v1", "HorizontalPodAutoscaler"), - }, - // -- - - // 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": "fedora:latest", "name": "container1"}], "dnsPolicy": "ClusterFirst", "restartPolicy": "Never"}}}}`, - expectedEtcdPath: "/registry/jobs/etcdstoragepathtestnamespace/job1", - }, - // -- - - // k8s.io/kubernetes/pkg/apis/batch/v1beta1 - gvr("batch", "v1beta1", "cronjobs"): { - stub: `{"metadata": {"name": "cjv1beta1"}, "spec": {"jobTemplate": {"spec": {"template": {"metadata": {"labels": {"controller-uid": "uid0"}}, "spec": {"containers": [{"image": "fedora:latest", "name": "container0"}], "dnsPolicy": "ClusterFirst", "restartPolicy": "Never"}}}}, "schedule": "* * * * *"}}`, - expectedEtcdPath: "/registry/cronjobs/etcdstoragepathtestnamespace/cjv1beta1", - }, - // -- - - // k8s.io/kubernetes/pkg/apis/batch/v2alpha1 - gvr("batch", "v2alpha1", "cronjobs"): { - stub: `{"metadata": {"name": "cjv2alpha1"}, "spec": {"jobTemplate": {"spec": {"template": {"metadata": {"labels": {"controller-uid": "uid0"}}, "spec": {"containers": [{"image": "fedora:latest", "name": "container0"}], "dnsPolicy": "ClusterFirst", "restartPolicy": "Never"}}}}, "schedule": "* * * * *"}}`, - expectedEtcdPath: "/registry/cronjobs/etcdstoragepathtestnamespace/cjv2alpha1", - expectedGVK: gvkP("batch", "v1beta1", "CronJob"), - }, - // -- - - // k8s.io/kubernetes/pkg/apis/certificates/v1beta1 - gvr("certificates.k8s.io", "v1beta1", "certificatesigningrequests"): { - stub: `{"metadata": {"name": "csr1"}, "spec": {"request": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0KTUlJQnlqQ0NBVE1DQVFBd2dZa3hDekFKQmdOVkJBWVRBbFZUTVJNd0VRWURWUVFJRXdwRFlXeHBabTl5Ym1saApNUll3RkFZRFZRUUhFdzFOYjNWdWRHRnBiaUJXYVdWM01STXdFUVlEVlFRS0V3cEhiMjluYkdVZ1NXNWpNUjh3CkhRWURWUVFMRXhaSmJtWnZjbTFoZEdsdmJpQlVaV05vYm05c2IyZDVNUmN3RlFZRFZRUURFdzUzZDNjdVoyOXYKWjJ4bExtTnZiVENCbnpBTkJna3Foa2lHOXcwQkFRRUZBQU9CalFBd2dZa0NnWUVBcFp0WUpDSEo0VnBWWEhmVgpJbHN0UVRsTzRxQzAzaGpYK1prUHl2ZFlkMVE0K3FiQWVUd1htQ1VLWUhUaFZSZDVhWFNxbFB6eUlCd2llTVpyCldGbFJRZGRaMUl6WEFsVlJEV3dBbzYwS2VjcWVBWG5uVUsrNWZYb1RJL1VnV3NocmU4dEoreC9UTUhhUUtSL0oKY0lXUGhxYVFoc0p1elpidkFkR0E4MEJMeGRNQ0F3RUFBYUFBTUEwR0NTcUdTSWIzRFFFQkJRVUFBNEdCQUlobAo0UHZGcStlN2lwQVJnSTVaTStHWng2bXBDejQ0RFRvMEprd2ZSRGYrQnRyc2FDMHE2OGVUZjJYaFlPc3E0ZmtIClEwdUEwYVZvZzNmNWlKeENhM0hwNWd4YkpRNnpWNmtKMFRFc3VhYU9oRWtvOXNkcENvUE9uUkJtMmkvWFJEMkQKNmlOaDhmOHowU2hHc0ZxakRnRkh5RjNvK2xVeWorVUM2SDFRVzdibgotLS0tLUVORCBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0="}}`, - expectedEtcdPath: "/registry/certificatesigningrequests/csr1", - }, - // -- - - // k8s.io/kubernetes/pkg/apis/coordination/v1beta1 - gvr("coordination.k8s.io", "v1beta1", "leases"): { - stub: `{"metadata": {"name": "lease1"}, "spec": {"holderIdentity": "holder", "leaseDurationSeconds": 5}}`, - expectedEtcdPath: "/registry/leases/etcdstoragepathtestnamespace/lease1", - }, - // -- - - // k8s.io/kubernetes/pkg/apis/events/v1beta1 - gvr("events.k8s.io", "v1beta1", "events"): { - stub: `{"metadata": {"name": "event2"}, "regarding": {"namespace": "etcdstoragepathtestnamespace"}, "note": "some data here", "eventTime": "2017-08-09T15:04:05.000000Z", "reportingInstance": "node-xyz", "reportingController": "k8s.io/my-controller", "action": "DidNothing", "reason": "Laziness"}`, - expectedEtcdPath: "/registry/events/etcdstoragepathtestnamespace/event2", - expectedGVK: gvkP("", "v1", "Event"), - }, - // -- - - // k8s.io/kubernetes/pkg/apis/extensions/v1beta1 - gvr("extensions", "v1beta1", "daemonsets"): { - stub: `{"metadata": {"name": "ds1"}, "spec": {"selector": {"matchLabels": {"u": "t"}}, "template": {"metadata": {"labels": {"u": "t"}}, "spec": {"containers": [{"image": "fedora:latest", "name": "container5"}]}}}}`, - expectedEtcdPath: "/registry/daemonsets/etcdstoragepathtestnamespace/ds1", - expectedGVK: gvkP("apps", "v1", "DaemonSet"), - }, - gvr("extensions", "v1beta1", "podsecuritypolicies"): { - stub: `{"metadata": {"name": "psp1"}, "spec": {"fsGroup": {"rule": "RunAsAny"}, "privileged": true, "runAsUser": {"rule": "RunAsAny"}, "seLinux": {"rule": "MustRunAs"}, "supplementalGroups": {"rule": "RunAsAny"}}}`, - expectedEtcdPath: "/registry/podsecuritypolicy/psp1", - expectedGVK: gvkP("policy", "v1beta1", "PodSecurityPolicy"), - }, - gvr("extensions", "v1beta1", "ingresses"): { - stub: `{"metadata": {"name": "ingress1"}, "spec": {"backend": {"serviceName": "service", "servicePort": 5000}}}`, - expectedEtcdPath: "/registry/ingress/etcdstoragepathtestnamespace/ingress1", - }, - gvr("extensions", "v1beta1", "networkpolicies"): { - stub: `{"metadata": {"name": "np1"}, "spec": {"podSelector": {"matchLabels": {"e": "f"}}}}`, - expectedEtcdPath: "/registry/networkpolicies/etcdstoragepathtestnamespace/np1", - expectedGVK: gvkP("networking.k8s.io", "v1", "NetworkPolicy"), - }, - gvr("extensions", "v1beta1", "deployments"): { - stub: `{"metadata": {"name": "deployment1"}, "spec": {"selector": {"matchLabels": {"f": "z"}}, "template": {"metadata": {"labels": {"f": "z"}}, "spec": {"containers": [{"image": "fedora:latest", "name": "container6"}]}}}}`, - expectedEtcdPath: "/registry/deployments/etcdstoragepathtestnamespace/deployment1", - expectedGVK: gvkP("apps", "v1", "Deployment"), - }, - gvr("extensions", "v1beta1", "replicasets"): { - stub: `{"metadata": {"name": "rs1"}, "spec": {"selector": {"matchLabels": {"g": "h"}}, "template": {"metadata": {"labels": {"g": "h"}}, "spec": {"containers": [{"image": "fedora:latest", "name": "container4"}]}}}}`, - expectedEtcdPath: "/registry/replicasets/etcdstoragepathtestnamespace/rs1", - expectedGVK: gvkP("apps", "v1", "ReplicaSet"), - }, - // -- - - // k8s.io/kubernetes/pkg/apis/networking/v1 - gvr("networking.k8s.io", "v1", "networkpolicies"): { - stub: `{"metadata": {"name": "np2"}, "spec": {"podSelector": {"matchLabels": {"e": "f"}}}}`, - expectedEtcdPath: "/registry/networkpolicies/etcdstoragepathtestnamespace/np2", - }, - // -- - - // k8s.io/kubernetes/pkg/apis/policy/v1beta1 - gvr("policy", "v1beta1", "poddisruptionbudgets"): { - stub: `{"metadata": {"name": "pdb1"}, "spec": {"selector": {"matchLabels": {"anokkey": "anokvalue"}}}}`, - expectedEtcdPath: "/registry/poddisruptionbudgets/etcdstoragepathtestnamespace/pdb1", - }, - gvr("policy", "v1beta1", "podsecuritypolicies"): { - stub: `{"metadata": {"name": "psp2"}, "spec": {"fsGroup": {"rule": "RunAsAny"}, "privileged": true, "runAsUser": {"rule": "RunAsAny"}, "seLinux": {"rule": "MustRunAs"}, "supplementalGroups": {"rule": "RunAsAny"}}}`, - expectedEtcdPath: "/registry/podsecuritypolicy/psp2", - }, - // -- - - // k8s.io/kubernetes/pkg/apis/storage/v1alpha1 - gvr("storage.k8s.io", "v1alpha1", "volumeattachments"): { - stub: `{"metadata": {"name": "va1"}, "spec": {"attacher": "gce", "nodeName": "localhost", "source": {"persistentVolumeName": "pv1"}}}`, - expectedEtcdPath: "/registry/volumeattachments/va1", - expectedGVK: gvkP("storage.k8s.io", "v1beta1", "VolumeAttachment"), - }, - // -- - - // k8s.io/kubernetes/pkg/apis/storage/v1beta1 - gvr("storage.k8s.io", "v1beta1", "volumeattachments"): { - stub: `{"metadata": {"name": "va2"}, "spec": {"attacher": "gce", "nodeName": "localhost", "source": {"persistentVolumeName": "pv2"}}}`, - expectedEtcdPath: "/registry/volumeattachments/va2", - }, - // -- - - // k8s.io/kubernetes/pkg/apis/storage/v1beta1 - gvr("storage.k8s.io", "v1beta1", "storageclasses"): { - stub: `{"metadata": {"name": "sc1"}, "provisioner": "aws"}`, - expectedEtcdPath: "/registry/storageclasses/sc1", - expectedGVK: gvkP("storage.k8s.io", "v1", "StorageClass"), - }, - // -- - - // k8s.io/kubernetes/pkg/apis/storage/v1 - gvr("storage.k8s.io", "v1", "storageclasses"): { - stub: `{"metadata": {"name": "sc2"}, "provisioner": "aws"}`, - expectedEtcdPath: "/registry/storageclasses/sc2", - }, - // -- - - // k8s.io/kubernetes/pkg/apis/settings/v1alpha1 - gvr("settings.k8s.io", "v1alpha1", "podpresets"): { - stub: `{"metadata": {"name": "podpre1"}, "spec": {"env": [{"name": "FOO"}]}}`, - expectedEtcdPath: "/registry/podpresets/etcdstoragepathtestnamespace/podpre1", - }, - // -- - - // k8s.io/kubernetes/pkg/apis/rbac/v1alpha1 - gvr("rbac.authorization.k8s.io", "v1alpha1", "roles"): { - stub: `{"metadata": {"name": "role1"}, "rules": [{"apiGroups": ["v1"], "resources": ["events"], "verbs": ["watch"]}]}`, - expectedEtcdPath: "/registry/roles/etcdstoragepathtestnamespace/role1", - expectedGVK: gvkP("rbac.authorization.k8s.io", "v1", "Role"), - }, - gvr("rbac.authorization.k8s.io", "v1alpha1", "clusterroles"): { - stub: `{"metadata": {"name": "crole1"}, "rules": [{"nonResourceURLs": ["/version"], "verbs": ["get"]}]}`, - expectedEtcdPath: "/registry/clusterroles/crole1", - expectedGVK: gvkP("rbac.authorization.k8s.io", "v1", "ClusterRole"), - }, - gvr("rbac.authorization.k8s.io", "v1alpha1", "rolebindings"): { - stub: `{"metadata": {"name": "roleb1"}, "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/etcdstoragepathtestnamespace/roleb1", - expectedGVK: gvkP("rbac.authorization.k8s.io", "v1", "RoleBinding"), - }, - gvr("rbac.authorization.k8s.io", "v1alpha1", "clusterrolebindings"): { - stub: `{"metadata": {"name": "croleb1"}, "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/croleb1", - expectedGVK: gvkP("rbac.authorization.k8s.io", "v1", "ClusterRoleBinding"), - }, - // -- - - // k8s.io/kubernetes/pkg/apis/rbac/v1beta1 - gvr("rbac.authorization.k8s.io", "v1beta1", "roles"): { - stub: `{"metadata": {"name": "role2"}, "rules": [{"apiGroups": ["v1"], "resources": ["events"], "verbs": ["watch"]}]}`, - expectedEtcdPath: "/registry/roles/etcdstoragepathtestnamespace/role2", - expectedGVK: gvkP("rbac.authorization.k8s.io", "v1", "Role"), - }, - gvr("rbac.authorization.k8s.io", "v1beta1", "clusterroles"): { - stub: `{"metadata": {"name": "crole2"}, "rules": [{"nonResourceURLs": ["/version"], "verbs": ["get"]}]}`, - expectedEtcdPath: "/registry/clusterroles/crole2", - expectedGVK: gvkP("rbac.authorization.k8s.io", "v1", "ClusterRole"), - }, - gvr("rbac.authorization.k8s.io", "v1beta1", "rolebindings"): { - stub: `{"metadata": {"name": "roleb2"}, "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/etcdstoragepathtestnamespace/roleb2", - expectedGVK: gvkP("rbac.authorization.k8s.io", "v1", "RoleBinding"), - }, - gvr("rbac.authorization.k8s.io", "v1beta1", "clusterrolebindings"): { - stub: `{"metadata": {"name": "croleb2"}, "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/croleb2", - expectedGVK: gvkP("rbac.authorization.k8s.io", "v1", "ClusterRoleBinding"), - }, - // -- - - // 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/etcdstoragepathtestnamespace/role3", - }, - gvr("rbac.authorization.k8s.io", "v1", "clusterroles"): { - stub: `{"metadata": {"name": "crole3"}, "rules": [{"nonResourceURLs": ["/version"], "verbs": ["get"]}]}`, - expectedEtcdPath: "/registry/clusterroles/crole3", - }, - 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/etcdstoragepathtestnamespace/roleb3", - }, - 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", - }, - // -- - - // k8s.io/kubernetes/pkg/apis/admissionregistration/v1alpha1 - gvr("admissionregistration.k8s.io", "v1alpha1", "initializerconfigurations"): { - stub: `{"metadata":{"name":"ic1"},"initializers":[{"name":"initializer.k8s.io","rules":[{"apiGroups":["group"],"apiVersions":["version"],"resources":["resource"]}],"failurePolicy":"Ignore"}]}`, - expectedEtcdPath: "/registry/initializerconfigurations/ic1", - }, - // k8s.io/kubernetes/pkg/apis/admissionregistration/v1beta1 - gvr("admissionregistration.k8s.io", "v1beta1", "validatingwebhookconfigurations"): { - stub: `{"metadata":{"name":"hook1","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"}]}`, - expectedEtcdPath: "/registry/validatingwebhookconfigurations/hook1", - }, - gvr("admissionregistration.k8s.io", "v1beta1", "mutatingwebhookconfigurations"): { - stub: `{"metadata":{"name":"hook1","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"}]}`, - expectedEtcdPath: "/registry/mutatingwebhookconfigurations/hook1", - }, - // -- - - // k8s.io/kubernetes/pkg/apis/scheduling/v1alpha1 - gvr("scheduling.k8s.io", "v1alpha1", "priorityclasses"): { - stub: `{"metadata":{"name":"pc1"},"Value":1000}`, - expectedEtcdPath: "/registry/priorityclasses/pc1", - expectedGVK: gvkP("scheduling.k8s.io", "v1beta1", "PriorityClass"), - }, - // -- - - // k8s.io/kubernetes/pkg/apis/scheduling/v1beta1 - gvr("scheduling.k8s.io", "v1beta1", "priorityclasses"): { - stub: `{"metadata":{"name":"pc2"},"Value":1000}`, - expectedEtcdPath: "/registry/priorityclasses/pc2", - }, - // -- - - // k8s.io/kube-aggregator/pkg/apis/apiregistration/v1beta1 - // depends on aggregator using the same ungrouped RESTOptionsGetter as the kube apiserver, not SimpleRestOptionsFactory in aggregator.go - gvr("apiregistration.k8s.io", "v1beta1", "apiservices"): { - stub: `{"metadata": {"name": "as1.foo.com"}, "spec": {"group": "foo.com", "version": "as1", "groupPriorityMinimum":100, "versionPriority":10}}`, - expectedEtcdPath: "/registry/apiregistration.k8s.io/apiservices/as1.foo.com", - }, - // -- - - // 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", - expectedGVK: gvkP("apiregistration.k8s.io", "v1beta1", "APIService"), - }, - // -- - - // k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1 - gvr("apiextensions.k8s.io", "v1beta1", "customresourcedefinitions"): { - stub: `{"metadata": {"name": "openshiftwebconsoleconfigs.webconsole.operator.openshift.io"},"spec": {"scope": "Cluster","group": "webconsole.operator.openshift.io","version": "v1alpha1","names": {"kind": "OpenShiftWebConsoleConfig","plural": "openshiftwebconsoleconfigs","singular": "openshiftwebconsoleconfig"}}}`, - expectedEtcdPath: "/registry/apiextensions.k8s.io/customresourcedefinitions/openshiftwebconsoleconfigs.webconsole.operator.openshift.io", - }, - // -- - -} - // Only add kinds to this list when this a virtual resource with get and create verbs that doesn't actually // store into it's kind. We've used this downstream for mappings before. var kindWhiteList = sets.NewString() @@ -483,51 +49,30 @@ const testNamespace = "etcdstoragepathtestnamespace" // It will also fail when a type gets moved to a different location. Be very careful in this situation because // it essentially means that you will be break old clusters unless you create some migration path for the old data. func TestEtcdStoragePath(t *testing.T) { - certDir, _ := ioutil.TempDir("", "test-integration-etcd") - defer os.RemoveAll(certDir) + master := StartRealMasterOrDie(t) + defer master.Cleanup() + defer dumpEtcdKVOnFailure(t, master.KV) - clientConfig, kvClient := startRealMasterOrDie(t, certDir) - defer func() { - dumpEtcdKVOnFailure(t, kvClient) - }() + client := &allClient{dynamicClient: master.Dynamic} - client := &allClient{dynamicClient: dynamic.NewForConfigOrDie(clientConfig)} - kubeClient := clientset.NewForConfigOrDie(clientConfig) - if _, err := kubeClient.CoreV1().Namespaces().Create(&v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: testNamespace}}); err != nil { + if _, err := master.Client.CoreV1().Namespaces().Create(&v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: testNamespace}}); err != nil { t.Fatal(err) } - discoveryClient := cacheddiscovery.NewMemCacheClient(kubeClient.Discovery()) - restMapper := restmapper.NewDeferredDiscoveryRESTMapper(discoveryClient) - restMapper.Reset() - - resourcesToPersist := []resourceToPersist{} - serverResources, err := kubeClient.Discovery().ServerResources() - if err != nil { - t.Fatal(err) - } - resourcesToPersist = append(resourcesToPersist, getResourcesToPersist(serverResources, false, t)...) + etcdStorageData := GetEtcdStorageData() kindSeen := sets.NewString() pathSeen := map[string][]schema.GroupVersionResource{} etcdSeen := map[schema.GroupVersionResource]empty{} cohabitatingResources := map[string]map[schema.GroupVersionKind]empty{} - for _, resourceToPersist := range resourcesToPersist { - t.Run(resourceToPersist.gvr.String(), func(t *testing.T) { - gvk := resourceToPersist.gvk - gvResource := resourceToPersist.gvr + for _, resourceToPersist := range master.Resources { + t.Run(resourceToPersist.Mapping.Resource.String(), func(t *testing.T) { + mapping := resourceToPersist.Mapping + gvk := resourceToPersist.Mapping.GroupVersionKind + gvResource := resourceToPersist.Mapping.Resource kind := gvk.Kind - mapping := &meta.RESTMapping{ - Resource: resourceToPersist.gvr, - GroupVersionKind: resourceToPersist.gvk, - Scope: meta.RESTScopeRoot, - } - if resourceToPersist.namespaced { - mapping.Scope = meta.RESTScopeNamespace - } - if kindWhiteList.Has(kind) { kindSeen.Insert(kind) t.Skip("whitelisted") @@ -537,18 +82,21 @@ func TestEtcdStoragePath(t *testing.T) { testData, hasTest := etcdStorageData[gvResource] if !hasTest { - t.Fatalf("no test data for %s. Please add a test for your new type to etcdStorageData.", gvResource) + t.Fatalf("no test data for %s. Please add a test for your new type to GetEtcdStorageData().", gvResource) } - if len(testData.expectedEtcdPath) == 0 { + if len(testData.ExpectedEtcdPath) == 0 { t.Fatalf("empty test data for %s", gvResource) } - shouldCreate := len(testData.stub) != 0 // try to create only if we have a stub + shouldCreate := len(testData.Stub) != 0 // try to create only if we have a stub - var input *metaObject + var ( + input *metaObject + err error + ) if shouldCreate { - if input, err = jsonToMetaObject([]byte(testData.stub)); err != nil || input.isEmpty() { + if input, err = jsonToMetaObject([]byte(testData.Stub)); err != nil || input.isEmpty() { t.Fatalf("invalid test data for %s: %v", gvResource, err) } } @@ -562,27 +110,27 @@ func TestEtcdStoragePath(t *testing.T) { } }() - if err := client.createPrerequisites(restMapper, testNamespace, testData.prerequisites, all); err != nil { + if err := client.createPrerequisites(master.Mapper, testNamespace, testData.Prerequisites, all); err != nil { t.Fatalf("failed to create prerequisites for %s: %#v", gvResource, err) } if shouldCreate { // do not try to create items with no stub - if err := client.create(testData.stub, testNamespace, mapping, all); err != nil { + if err := client.create(testData.Stub, testNamespace, mapping, all); err != nil { t.Fatalf("failed to create stub for %s: %#v", gvResource, err) } } - output, err := getFromEtcd(kvClient, testData.expectedEtcdPath) + output, err := getFromEtcd(master.KV, testData.ExpectedEtcdPath) if err != nil { t.Fatalf("failed to get from etcd for %s: %#v", gvResource, err) } expectedGVK := gvk - if testData.expectedGVK != nil { - if gvk == *testData.expectedGVK { - t.Errorf("GVK override %s for %s is unnecessary or something was changed incorrectly", testData.expectedGVK, gvk) + if testData.ExpectedGVK != nil { + if gvk == *testData.ExpectedGVK { + t.Errorf("GVK override %s for %s is unnecessary or something was changed incorrectly", testData.ExpectedGVK, gvk) } - expectedGVK = *testData.expectedGVK + expectedGVK = *testData.ExpectedGVK } actualGVK := output.getGVK() @@ -594,8 +142,8 @@ func TestEtcdStoragePath(t *testing.T) { t.Errorf("Test stub for %s does not match: %s", kind, diff.ObjectGoPrintDiff(input, output)) } - addGVKToEtcdBucket(cohabitatingResources, actualGVK, getEtcdBucket(testData.expectedEtcdPath)) - pathSeen[testData.expectedEtcdPath] = append(pathSeen[testData.expectedEtcdPath], mapping.Resource) + addGVKToEtcdBucket(cohabitatingResources, actualGVK, getEtcdBucket(testData.ExpectedEtcdPath)) + pathSeen[testData.ExpectedEtcdPath] = append(pathSeen[testData.ExpectedEtcdPath], mapping.Resource) }) } @@ -627,81 +175,6 @@ func TestEtcdStoragePath(t *testing.T) { } } -func startRealMasterOrDie(t *testing.T, certDir string) (*restclient.Config, clientv3.KV) { - _, defaultServiceClusterIPRange, err := net.ParseCIDR("10.0.0.0/24") - if err != nil { - t.Fatal(err) - } - - listener, _, err := genericapiserveroptions.CreateListener("tcp", "127.0.0.1:0") - if err != nil { - t.Fatal(err) - } - - kubeAPIServerOptions := options.NewServerRunOptions() - kubeAPIServerOptions.InsecureServing.BindPort = 0 - kubeAPIServerOptions.SecureServing.Listener = listener - kubeAPIServerOptions.SecureServing.ServerCert.CertDirectory = certDir - kubeAPIServerOptions.Etcd.StorageConfig.ServerList = []string{framework.GetEtcdURL()} - kubeAPIServerOptions.Etcd.DefaultStorageMediaType = runtime.ContentTypeJSON // force json we can easily interpret the result in etcd - kubeAPIServerOptions.ServiceClusterIPRange = *defaultServiceClusterIPRange - kubeAPIServerOptions.Authorization.Modes = []string{"RBAC"} - kubeAPIServerOptions.Admission.GenericAdmission.DisablePlugins = []string{"ServiceAccount"} - completedOptions, err := app.Complete(kubeAPIServerOptions) - if err != nil { - t.Fatal(err) - } - kubeAPIServerOptions.APIEnablement.RuntimeConfig.Set("api/all=true") - - kubeAPIServer, err := app.CreateServerChain(completedOptions, wait.NeverStop) - if err != nil { - t.Fatal(err) - } - kubeClientConfig := restclient.CopyConfig(kubeAPIServer.LoopbackClientConfig) - - go func() { - // Catch panics that occur in this go routine so we get a comprehensible failure - defer func() { - if err := recover(); err != nil { - t.Errorf("Unexpected panic trying to start API master: %#v", err) - } - }() - - if err := kubeAPIServer.PrepareRun().Run(wait.NeverStop); err != nil { - t.Fatal(err) - } - }() - - lastHealth := "" - if err := wait.PollImmediate(time.Second, time.Minute, func() (done bool, err error) { - // wait for the server to be healthy - result := clientset.NewForConfigOrDie(kubeClientConfig).RESTClient().Get().AbsPath("/healthz").Do() - content, _ := result.Raw() - lastHealth = string(content) - if errResult := result.Error(); errResult != nil { - t.Log(errResult) - return false, nil - } - var status int - result.StatusCode(&status) - return status == http.StatusOK, nil - }); err != nil { - t.Log(lastHealth) - t.Fatal(err) - } - - // this test makes lots of requests, don't be slow - kubeClientConfig.QPS = 99999 - kubeClientConfig.Burst = 9999 - - kvClient, err := integration.GetEtcdKVClient(kubeAPIServerOptions.Etcd.StorageConfig) - if err != nil { - t.Fatal(err) - } - - return kubeClientConfig, kvClient -} - func dumpEtcdKVOnFailure(t *testing.T, kvClient clientv3.KV) { if t.Failed() { response, err := kvClient.Get(context.Background(), "/", clientv3.WithPrefix()) @@ -758,11 +231,6 @@ func (obj *metaObject) isEmpty() bool { return obj == nil || *obj == metaObject{} // compare to zero value since all fields are strings } -type prerequisite struct { - gvrData schema.GroupVersionResource - stub string -} - type empty struct{} type cleanupData struct { @@ -770,14 +238,6 @@ type cleanupData struct { resource schema.GroupVersionResource } -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 jsonToMetaObject(stub []byte) (*metaObject, error) { obj := &metaObject{} if err := json.Unmarshal(stub, obj); err != nil { @@ -805,25 +265,18 @@ type allClient struct { } func (c *allClient) create(stub, ns string, mapping *meta.RESTMapping, all *[]cleanupData) error { - // we don't require GVK on the data we provide, so we fill it in here. We could, but that seems extraneous. - typeMetaAdder := map[string]interface{}{} - err := json.Unmarshal([]byte(stub), &typeMetaAdder) - if err != nil { - return err - } - typeMetaAdder["apiVersion"] = mapping.GroupVersionKind.GroupVersion().String() - typeMetaAdder["kind"] = mapping.GroupVersionKind.Kind - - if mapping.Scope == meta.RESTScopeRoot { - ns = "" - } - obj := &unstructured.Unstructured{Object: typeMetaAdder} - actual, err := c.dynamicClient.Resource(mapping.Resource).Namespace(ns).Create(obj, metav1.CreateOptions{}) + resourceClient, obj, err := JSONToUnstructured(stub, ns, mapping, c.dynamicClient) if err != nil { return err } - *all = append(*all, cleanupData{actual, mapping.Resource}) + actual, err := resourceClient.Create(obj, metav1.CreateOptions{}) + if err != nil { + return err + } + + *all = append(*all, cleanupData{obj: actual, resource: mapping.Resource}) + return nil } @@ -839,9 +292,9 @@ func (c *allClient) cleanup(all *[]cleanupData) error { return nil } -func (c *allClient) createPrerequisites(mapper meta.RESTMapper, ns string, prerequisites []prerequisite, all *[]cleanupData) error { +func (c *allClient) createPrerequisites(mapper meta.RESTMapper, ns string, prerequisites []Prerequisite, all *[]cleanupData) error { for _, prerequisite := range prerequisites { - gvk, err := mapper.KindFor(prerequisite.gvrData) + gvk, err := mapper.KindFor(prerequisite.GvrData) if err != nil { return err } @@ -849,7 +302,7 @@ func (c *allClient) createPrerequisites(mapper meta.RESTMapper, ns string, prere if err != nil { return err } - if err := c.create(prerequisite.stub, ns, mapping, all); err != nil { + if err := c.create(prerequisite.Stub, ns, mapping, all); err != nil { return err } } @@ -895,58 +348,3 @@ func diffMapKeys(a, b interface{}, stringer func(interface{}) string) []string { return ret } - -type resourceToPersist struct { - gvk schema.GroupVersionKind - gvr schema.GroupVersionResource - golangType reflect.Type - namespaced bool -} - -func getResourcesToPersist(serverResources []*metav1.APIResourceList, isOAPI bool, t *testing.T) []resourceToPersist { - resourcesToPersist := []resourceToPersist{} - - for _, discoveryGroup := range serverResources { - for _, discoveryResource := range discoveryGroup.APIResources { - // this is a subresource, skip it - if strings.Contains(discoveryResource.Name, "/") { - continue - } - hasCreate := false - hasGet := false - for _, verb := range discoveryResource.Verbs { - if string(verb) == "get" { - hasGet = true - } - if string(verb) == "create" { - hasCreate = true - } - } - if !(hasCreate && hasGet) { - continue - } - - resourceGV, err := schema.ParseGroupVersion(discoveryGroup.GroupVersion) - if err != nil { - t.Fatal(err) - } - gvk := resourceGV.WithKind(discoveryResource.Kind) - if len(discoveryResource.Group) > 0 || len(discoveryResource.Version) > 0 { - gvk = schema.GroupVersionKind{ - Group: discoveryResource.Group, - Version: discoveryResource.Version, - Kind: discoveryResource.Kind, - } - } - gvr := resourceGV.WithResource(discoveryResource.Name) - - resourcesToPersist = append(resourcesToPersist, resourceToPersist{ - gvk: gvk, - gvr: gvr, - namespaced: discoveryResource.Namespaced, - }) - } - } - - return resourcesToPersist -} diff --git a/test/integration/etcd/server.go b/test/integration/etcd/server.go new file mode 100644 index 00000000000..d47465d4ea0 --- /dev/null +++ b/test/integration/etcd/server.go @@ -0,0 +1,283 @@ +/* +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 ( + "context" + "encoding/json" + "io/ioutil" + "net" + "net/http" + "os" + "strings" + "testing" + "time" + + "github.com/coreos/etcd/clientv3" + "github.com/coreos/etcd/clientv3/concurrency" + + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/wait" + genericapiserveroptions "k8s.io/apiserver/pkg/server/options" + cacheddiscovery "k8s.io/client-go/discovery/cached" + "k8s.io/client-go/dynamic" + clientset "k8s.io/client-go/kubernetes" + restclient "k8s.io/client-go/rest" + "k8s.io/client-go/restmapper" + "k8s.io/kubernetes/cmd/kube-apiserver/app" + "k8s.io/kubernetes/cmd/kube-apiserver/app/options" + "k8s.io/kubernetes/test/integration" + "k8s.io/kubernetes/test/integration/framework" + + // install all APIs + _ "k8s.io/kubernetes/pkg/master" +) + +// StartRealMasterOrDie starts an API master that is appropriate for use in tests that require one of every resource +func StartRealMasterOrDie(t *testing.T) *Master { + certDir, err := ioutil.TempDir("", t.Name()) + if err != nil { + t.Fatal(err) + } + + _, defaultServiceClusterIPRange, err := net.ParseCIDR("10.0.0.0/24") + if err != nil { + t.Fatal(err) + } + + listener, _, err := genericapiserveroptions.CreateListener("tcp", "127.0.0.1:0") + if err != nil { + t.Fatal(err) + } + + kubeAPIServerOptions := options.NewServerRunOptions() + kubeAPIServerOptions.InsecureServing.BindPort = 0 + kubeAPIServerOptions.SecureServing.Listener = listener + kubeAPIServerOptions.SecureServing.ServerCert.CertDirectory = certDir + kubeAPIServerOptions.Etcd.StorageConfig.ServerList = []string{framework.GetEtcdURL()} + kubeAPIServerOptions.Etcd.DefaultStorageMediaType = runtime.ContentTypeJSON // force json we can easily interpret the result in etcd + kubeAPIServerOptions.ServiceClusterIPRange = *defaultServiceClusterIPRange + kubeAPIServerOptions.Authorization.Modes = []string{"RBAC"} + kubeAPIServerOptions.Admission.GenericAdmission.DisablePlugins = []string{"ServiceAccount"} + completedOptions, err := app.Complete(kubeAPIServerOptions) + if err != nil { + t.Fatal(err) + } + if err := completedOptions.APIEnablement.RuntimeConfig.Set("api/all=true"); err != nil { + t.Fatal(err) + } + + // get etcd client before starting API server + rawClient, kvClient, err := integration.GetEtcdClients(completedOptions.Etcd.StorageConfig) + if err != nil { + t.Fatal(err) + } + + // get a leased session + session, err := concurrency.NewSession(rawClient) + if err != nil { + t.Fatal(err) + } + + // then build and use an etcd lock + // this prevents more than one of these masters from running at the same time + lock := concurrency.NewLocker(session, "kube_integration_etcd_raw") + lock.Lock() + + // make sure we start with a clean slate + if _, err := kvClient.Delete(context.Background(), "/registry/", clientv3.WithPrefix()); err != nil { + t.Fatal(err) + } + + stopCh := make(chan struct{}) + + kubeAPIServer, err := app.CreateServerChain(completedOptions, stopCh) + if err != nil { + t.Fatal(err) + } + + kubeClientConfig := restclient.CopyConfig(kubeAPIServer.LoopbackClientConfig) + + // we make lots of requests, don't be slow + kubeClientConfig.QPS = 99999 + kubeClientConfig.Burst = 9999 + + kubeClient := clientset.NewForConfigOrDie(kubeClientConfig) + + go func() { + // Catch panics that occur in this go routine so we get a comprehensible failure + defer func() { + if err := recover(); err != nil { + t.Errorf("Unexpected panic trying to start API master: %#v", err) + } + }() + + if err := kubeAPIServer.PrepareRun().Run(stopCh); err != nil { + t.Fatal(err) + } + }() + + lastHealth := "" + if err := wait.PollImmediate(time.Second, time.Minute, func() (done bool, err error) { + // wait for the server to be healthy + result := kubeClient.RESTClient().Get().AbsPath("/healthz").Do() + content, _ := result.Raw() + lastHealth = string(content) + if errResult := result.Error(); errResult != nil { + t.Log(errResult) + return false, nil + } + var status int + result.StatusCode(&status) + return status == http.StatusOK, nil + }); err != nil { + t.Log(lastHealth) + t.Fatal(err) + } + + // force cached discovery reset + discoveryClient := cacheddiscovery.NewMemCacheClient(kubeClient.Discovery()) + restMapper := restmapper.NewDeferredDiscoveryRESTMapper(discoveryClient) + restMapper.Reset() + + serverResources, err := kubeClient.Discovery().ServerResources() + if err != nil { + t.Fatal(err) + } + + cleanup := func() { + if err := os.RemoveAll(certDir); err != nil { + t.Log(err) + } + close(stopCh) + lock.Unlock() + } + + return &Master{ + Client: kubeClient, + Dynamic: dynamic.NewForConfigOrDie(kubeClientConfig), + Config: kubeClientConfig, + KV: kvClient, + Mapper: restMapper, + Resources: GetResources(t, serverResources), + Cleanup: cleanup, + } +} + +// Master represents a running API server that is ready for use +// The Cleanup func must be deferred to prevent resource leaks +type Master struct { + Client clientset.Interface + Dynamic dynamic.Interface + Config *restclient.Config + KV clientv3.KV + Mapper meta.RESTMapper + Resources []Resource + Cleanup func() +} + +// Resource contains REST mapping information for a specific resource and extra metadata such as delete collection support +type Resource struct { + Mapping *meta.RESTMapping + HasDeleteCollection bool +} + +// GetResources fetches the Resources associated with serverResources that support get and create +func GetResources(t *testing.T, serverResources []*metav1.APIResourceList) []Resource { + var resources []Resource + + for _, discoveryGroup := range serverResources { + for _, discoveryResource := range discoveryGroup.APIResources { + // this is a subresource, skip it + if strings.Contains(discoveryResource.Name, "/") { + continue + } + hasCreate := false + hasGet := false + hasDeleteCollection := false + for _, verb := range discoveryResource.Verbs { + if verb == "get" { + hasGet = true + } + if verb == "create" { + hasCreate = true + } + if verb == "deletecollection" { + hasDeleteCollection = true + } + } + if !(hasCreate && hasGet) { + continue + } + + resourceGV, err := schema.ParseGroupVersion(discoveryGroup.GroupVersion) + if err != nil { + t.Fatal(err) + } + gvk := resourceGV.WithKind(discoveryResource.Kind) + if len(discoveryResource.Group) > 0 || len(discoveryResource.Version) > 0 { + gvk = schema.GroupVersionKind{ + Group: discoveryResource.Group, + Version: discoveryResource.Version, + Kind: discoveryResource.Kind, + } + } + gvr := resourceGV.WithResource(discoveryResource.Name) + + resources = append(resources, Resource{ + Mapping: &meta.RESTMapping{ + Resource: gvr, + GroupVersionKind: gvk, + Scope: scope(discoveryResource.Namespaced), + }, + HasDeleteCollection: hasDeleteCollection, + }) + } + } + + return resources +} + +func scope(namespaced bool) meta.RESTScope { + if namespaced { + return meta.RESTScopeNamespace + } + return meta.RESTScopeRoot +} + +// JSONToUnstructured converts a JSON stub to unstructured.Unstructured and +// returns a dynamic resource client that can be used to interact with it +func JSONToUnstructured(stub, namespace string, mapping *meta.RESTMapping, dynamicClient dynamic.Interface) (dynamic.ResourceInterface, *unstructured.Unstructured, error) { + typeMetaAdder := map[string]interface{}{} + if err := json.Unmarshal([]byte(stub), &typeMetaAdder); err != nil { + return nil, nil, err + } + + // we don't require GVK on the data we provide, so we fill it in here. We could, but that seems extraneous. + typeMetaAdder["apiVersion"] = mapping.GroupVersionKind.GroupVersion().String() + typeMetaAdder["kind"] = mapping.GroupVersionKind.Kind + + if mapping.Scope == meta.RESTScopeRoot { + namespace = "" + } + + return dynamicClient.Resource(mapping.Resource).Namespace(namespace), &unstructured.Unstructured{Object: typeMetaAdder}, nil +} diff --git a/test/integration/master/transformation_testcase.go b/test/integration/master/transformation_testcase.go index bfe3bc8415e..8f8171b7daf 100644 --- a/test/integration/master/transformation_testcase.go +++ b/test/integration/master/transformation_testcase.go @@ -228,7 +228,7 @@ func (e *transformTest) createSecret(name, namespace string) (*corev1.Secret, er } func (e *transformTest) readRawRecordFromETCD(path string) (*clientv3.GetResponse, error) { - etcdClient, err := integration.GetEtcdKVClient(e.kubeAPIServer.ServerOpts.Etcd.StorageConfig) + _, etcdClient, err := integration.GetEtcdClients(e.kubeAPIServer.ServerOpts.Etcd.StorageConfig) if err != nil { return nil, fmt.Errorf("failed to create etcd client: %v", err) } diff --git a/test/integration/utils.go b/test/integration/utils.go index eabda738ad8..5eb72c86553 100644 --- a/test/integration/utils.go +++ b/test/integration/utils.go @@ -67,7 +67,7 @@ func WaitForPodToDisappear(podClient coreclient.PodInterface, podName string, in }) } -func GetEtcdKVClient(config storagebackend.Config) (clientv3.KV, error) { +func GetEtcdClients(config storagebackend.Config) (*clientv3.Client, clientv3.KV, error) { tlsInfo := transport.TLSInfo{ CertFile: config.CertFile, KeyFile: config.KeyFile, @@ -76,7 +76,7 @@ func GetEtcdKVClient(config storagebackend.Config) (clientv3.KV, error) { tlsConfig, err := tlsInfo.ClientConfig() if err != nil { - return nil, err + return nil, nil, err } cfg := clientv3.Config{ @@ -86,8 +86,8 @@ func GetEtcdKVClient(config storagebackend.Config) (clientv3.KV, error) { c, err := clientv3.New(cfg) if err != nil { - return nil, err + return nil, nil, err } - return clientv3.NewKV(c), nil + return c, clientv3.NewKV(c), nil }