From 0d93e184fa360872bbfa31245ad75619e975d592 Mon Sep 17 00:00:00 2001 From: Salvatore Dario Minonne Date: Thu, 18 May 2017 13:52:41 +0200 Subject: [PATCH 1/2] kube-apiextensions-server integraton test: etcd serialization --- .../test/integration/registration_test.go | 160 +++++++++++++++++- .../test/integration/testserver/resources.go | 4 +- 2 files changed, 161 insertions(+), 3 deletions(-) diff --git a/staging/src/k8s.io/kube-apiextensions-server/test/integration/registration_test.go b/staging/src/k8s.io/kube-apiextensions-server/test/integration/registration_test.go index 36f22f2d35..22edc8f3be 100644 --- a/staging/src/k8s.io/kube-apiextensions-server/test/integration/registration_test.go +++ b/staging/src/k8s.io/kube-apiextensions-server/test/integration/registration_test.go @@ -17,10 +17,17 @@ limitations under the License. package integration import ( + "context" + "encoding/json" + "fmt" + "os" + "path" "reflect" "testing" "time" + "github.com/coreos/etcd/clientv3" + "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -28,6 +35,7 @@ import ( "k8s.io/apimachinery/pkg/watch" "k8s.io/client-go/dynamic" apiextensionsv1alpha1 "k8s.io/kube-apiextensions-server/pkg/apis/apiextensions/v1alpha1" + extensionsapiserver "k8s.io/kube-apiextensions-server/pkg/apiserver" "k8s.io/kube-apiextensions-server/test/integration/testserver" ) @@ -217,7 +225,7 @@ func TestMultipleRegistration(t *testing.T) { t.Errorf("expected %v, got %v", e, a) } - curletDefinition := testserver.NewCurletCustomResourceDefinition() + curletDefinition := testserver.NewCurletCustomResourceDefinition(apiextensionsv1alpha1.NamespaceScoped) curletVersionClient, err := testserver.CreateNewCustomResourceDefinition(curletDefinition, apiExtensionClient, clientPool) if err != nil { t.Fatal(err) @@ -338,3 +346,153 @@ func TestDeRegistrationAndReRegistration(t *testing.T) { } }() } + +func TestEtcdStorage(t *testing.T) { + config, err := testserver.DefaultServerConfig() + if err != nil { + t.Fatal(err) + } + stopCh, apiExtensionClient, clientPool, err := testserver.StartServer(config) + if err != nil { + t.Fatal(err) + } + defer close(stopCh) + + etcdPrefix := getPrefixFromConfig(t, config) + + ns1 := "another-default-is-possible" + curletDefinition := testserver.NewCurletCustomResourceDefinition(apiextensionsv1alpha1.ClusterScoped) + curletVersionClient, err := testserver.CreateNewCustomResourceDefinition(curletDefinition, apiExtensionClient, clientPool) + if err != nil { + t.Fatal(err) + } + curletNamespacedResourceClient := NewNamespacedCustomResourceClient(ns1, curletVersionClient, curletDefinition) + if _, err := instantiateCustomResource(t, testserver.NewCurletInstance(ns1, "bar"), curletNamespacedResourceClient, curletDefinition); err != nil { + t.Fatalf("unable to create curlet cluster scoped Instance:%v", err) + } + + ns2 := "the-cruel-default" + noxuDefinition := testserver.NewNoxuCustomResourceDefinition(apiextensionsv1alpha1.NamespaceScoped) + noxuVersionClient, err := testserver.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, clientPool) + if err != nil { + t.Fatal(err) + } + noxuNamespacedResourceClient := NewNamespacedCustomResourceClient(ns2, noxuVersionClient, noxuDefinition) + if _, err := instantiateCustomResource(t, testserver.NewNoxuInstance(ns2, "foo"), noxuNamespacedResourceClient, noxuDefinition); err != nil { + t.Fatalf("unable to create noxu namespace scoped Instance:%v", err) + } + + testcases := map[string]struct { + etcdPath string + expectedObject *metaObject + }{ + "namespacedNoxuDefinition": { + etcdPath: path.Join("/", etcdPrefix, "apiextensions.k8s.io/customresourcedefinitions/noxus.mygroup.example.com"), // TODO: Double check this, no namespace? + expectedObject: &metaObject{ + Kind: "CustomResourceDefinition", + APIVersion: "apiextensions.k8s.io/v1alpha1", + Metadata: Metadata{ + Name: "noxus.mygroup.example.com", + Namespace: "", + SelfLink: "/apis/apiextensions.k8s.io/v1alpha1/customresourcedefinitions/noxus.mygroup.example.com", + }, + }, + }, + "namespacedNoxuInstance": { + etcdPath: path.Join("/", etcdPrefix, "mygroup.example.com/noxus/the-cruel-default/foo"), + expectedObject: &metaObject{ + Kind: "WishIHadChosenNoxu", + APIVersion: "mygroup.example.com/v1alpha1", + Metadata: Metadata{ + Name: "foo", + Namespace: "the-cruel-default", + SelfLink: "", // TODO double check: empty? + }, + }, + }, + + "clusteredCurletDefinition": { + etcdPath: path.Join("/", etcdPrefix, "apiextensions.k8s.io/customresourcedefinitions/curlets.mygroup.example.com"), + expectedObject: &metaObject{ + Kind: "CustomResourceDefinition", + APIVersion: "apiextensions.k8s.io/v1alpha1", + Metadata: Metadata{ + Name: "curlets.mygroup.example.com", + Namespace: "", + SelfLink: "/apis/apiextensions.k8s.io/v1alpha1/customresourcedefinitions/curlets.mygroup.example.com", + }, + }, + }, + + "clusteredCurletInstance": { + etcdPath: path.Join("/", etcdPrefix, "mygroup.example.com/curlets/bar"), + expectedObject: &metaObject{ + Kind: "Curlet", + APIVersion: "mygroup.example.com/v1alpha1", + Metadata: Metadata{ + Name: "bar", + Namespace: "", + SelfLink: "", // TODO double check: empty? + }, + }, + }, + } + + etcdURL, ok := os.LookupEnv("KUBE_INTEGRATION_ETCD_URL") + if !ok { + etcdURL = "http://127.0.0.1:2379" + } + cfg := clientv3.Config{ + Endpoints: []string{etcdURL}, + } + c, err := clientv3.New(cfg) + if err != nil { + t.Fatal(err) + } + kv := clientv3.NewKV(c) + for testName, tc := range testcases { + output, err := getFromEtcd(kv, etcdPrefix, tc.etcdPath) + if err != nil { + t.Fatalf("%s - no path gotten from etcd:%v", testName, err) + } + if e, a := tc.expectedObject, output; !reflect.DeepEqual(e, a) { + t.Errorf("%s - expected %#v\n got %#v\n", testName, e, a) + } + } +} + +func getPrefixFromConfig(t *testing.T, config *extensionsapiserver.Config) string { + extensionsOptionsGetter, ok := config.CustomResourceDefinitionRESTOptionsGetter.(extensionsapiserver.CustomResourceDefinitionRESTOptionsGetter) + if !ok { + t.Fatal("can't obtain etcd prefix: unable to cast config.CustomResourceDefinitionRESTOptionsGetter to extensionsapiserver.CustomResourceDefinitionRESTOptionsGetter") + } + return extensionsOptionsGetter.StoragePrefix +} + +func getFromEtcd(keys clientv3.KV, prefix, localPath string) (*metaObject, error) { + internalPath := path.Join("/", prefix, localPath) // TODO: Double check, should we concatenate two prefixes? + response, err := keys.Get(context.Background(), internalPath) + if err != nil { + return nil, err + } + if response.More || response.Count != 1 || len(response.Kvs) != 1 { + return nil, fmt.Errorf("Invalid etcd response (not found == %v): %#v", response.Count == 0, response) + } + obj := &metaObject{} + if err := json.Unmarshal(response.Kvs[0].Value, obj); err != nil { + return nil, err + } + return obj, nil +} + +type metaObject struct { + Kind string `json:"kind,omitempty" protobuf:"bytes,1,opt,name=kind"` + APIVersion string `json:"apiVersion,omitempty" protobuf:"bytes,2,opt,name=apiVersion"` + Metadata `json:"metadata,omitempty" protobuf:"bytes,3,opt,name=metadata"` +} + +type Metadata struct { + Name string `json:"name,omitempty" protobuf:"bytes,1,opt,name=name"` + Namespace string `json:"namespace,omitempty" protobuf:"bytes,2,opt,name=namespace"` + SelfLink string `json:"selfLink,omitempty" protobuf:"bytes,3,opt,name=selfLink"` +} diff --git a/staging/src/k8s.io/kube-apiextensions-server/test/integration/testserver/resources.go b/staging/src/k8s.io/kube-apiextensions-server/test/integration/testserver/resources.go index aa00391526..099c82c1d9 100644 --- a/staging/src/k8s.io/kube-apiextensions-server/test/integration/testserver/resources.go +++ b/staging/src/k8s.io/kube-apiextensions-server/test/integration/testserver/resources.go @@ -63,7 +63,7 @@ func NewNoxuInstance(namespace, name string) *unstructured.Unstructured { } } -func NewCurletCustomResourceDefinition() *apiextensionsv1alpha1.CustomResourceDefinition { +func NewCurletCustomResourceDefinition(scope apiextensionsv1alpha1.ResourceScope) *apiextensionsv1alpha1.CustomResourceDefinition { return &apiextensionsv1alpha1.CustomResourceDefinition{ ObjectMeta: metav1.ObjectMeta{Name: "curlets.mygroup.example.com"}, Spec: apiextensionsv1alpha1.CustomResourceDefinitionSpec{ @@ -75,7 +75,7 @@ func NewCurletCustomResourceDefinition() *apiextensionsv1alpha1.CustomResourceDe Kind: "Curlet", ListKind: "CurletList", }, - Scope: apiextensionsv1alpha1.NamespaceScoped, + Scope: scope, }, } } From 2ab03260c5a3dc7960fc9791205686eb008e290b Mon Sep 17 00:00:00 2001 From: Salvatore Dario Minonne Date: Thu, 18 May 2017 15:09:43 +0200 Subject: [PATCH 2/2] for bazel --- .../src/k8s.io/kube-apiextensions-server/test/integration/BUILD | 2 ++ 1 file changed, 2 insertions(+) diff --git a/staging/src/k8s.io/kube-apiextensions-server/test/integration/BUILD b/staging/src/k8s.io/kube-apiextensions-server/test/integration/BUILD index 7d68d57382..dca4da9927 100644 --- a/staging/src/k8s.io/kube-apiextensions-server/test/integration/BUILD +++ b/staging/src/k8s.io/kube-apiextensions-server/test/integration/BUILD @@ -18,6 +18,7 @@ go_test( "integration", ], deps = [ + "//vendor/github.com/coreos/etcd/clientv3:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", @@ -25,6 +26,7 @@ go_test( "//vendor/k8s.io/apimachinery/pkg/watch:go_default_library", "//vendor/k8s.io/client-go/dynamic:go_default_library", "//vendor/k8s.io/kube-apiextensions-server/pkg/apis/apiextensions/v1alpha1:go_default_library", + "//vendor/k8s.io/kube-apiextensions-server/pkg/apiserver:go_default_library", "//vendor/k8s.io/kube-apiextensions-server/test/integration/testserver:go_default_library", ], )