diff --git a/pkg/master/master_test.go b/pkg/master/master_test.go index 0d03b5e3b55..763ff82660b 100644 --- a/pkg/master/master_test.go +++ b/pkg/master/master_test.go @@ -17,7 +17,6 @@ limitations under the License. package master import ( - "bytes" "crypto/tls" "encoding/json" "fmt" @@ -30,7 +29,6 @@ import ( "testing" "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/api/testapi" "k8s.io/kubernetes/pkg/api/unversioned" apiv1 "k8s.io/kubernetes/pkg/api/v1" @@ -50,13 +48,8 @@ import ( "k8s.io/kubernetes/pkg/genericapiserver" "k8s.io/kubernetes/pkg/kubelet/client" ipallocator "k8s.io/kubernetes/pkg/registry/core/service/ipallocator" - extensionsrest "k8s.io/kubernetes/pkg/registry/extensions/rest" - "k8s.io/kubernetes/pkg/registry/extensions/thirdpartyresourcedata" - "k8s.io/kubernetes/pkg/registry/generic" "k8s.io/kubernetes/pkg/registry/registrytest" "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/storage" - "k8s.io/kubernetes/pkg/storage/etcd/etcdtest" etcdtesting "k8s.io/kubernetes/pkg/storage/etcd/testing" utilnet "k8s.io/kubernetes/pkg/util/net" "k8s.io/kubernetes/pkg/util/sets" @@ -67,7 +60,6 @@ import ( "github.com/go-openapi/strfmt" "github.com/go-openapi/validate" "github.com/stretchr/testify/assert" - "golang.org/x/net/context" ) // setUp is a convience function for setting up for (most) tests. @@ -289,6 +281,19 @@ func TestGetNodeAddresses(t *testing.T) { assert.Equal([]string{"127.0.0.2", "127.0.0.2"}, addrs) } +func decodeResponse(resp *http.Response, obj interface{}) error { + defer resp.Body.Close() + + data, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + if err := json.Unmarshal(data, obj); err != nil { + return err + } + return nil +} + // Because we need to be backwards compatible with release 1.1, at endpoints // that exist in release 1.1, the responses should have empty APIVersion. func TestAPIVersionOfDiscoveryEndpoints(t *testing.T) { @@ -477,613 +482,6 @@ func TestDiscoveryAtAPIS(t *testing.T) { } } -var versionsToTest = []string{"v1", "v3"} - -type Foo struct { - unversioned.TypeMeta `json:",inline"` - api.ObjectMeta `json:"metadata,omitempty" description:"standard object metadata"` - - SomeField string `json:"someField"` - OtherField int `json:"otherField"` -} - -type FooList struct { - unversioned.TypeMeta `json:",inline"` - unversioned.ListMeta `json:"metadata,omitempty" description:"standard list metadata; see http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata"` - - Items []Foo `json:"items"` -} - -func initThirdParty(t *testing.T, version, name string) (*Master, *etcdtesting.EtcdTestServer, *httptest.Server, *assert.Assertions) { - return initThirdPartyMultiple(t, []string{version}, []string{name}) -} - -func initThirdPartyMultiple(t *testing.T, versions, names []string) (*Master, *etcdtesting.EtcdTestServer, *httptest.Server, *assert.Assertions) { - master, etcdserver, _, assert := newMaster(t) - _, master.ServiceClusterIPRange, _ = net.ParseCIDR("10.0.0.0/24") - - for ix := range names { - api := &extensions.ThirdPartyResource{ - ObjectMeta: api.ObjectMeta{ - Name: names[ix], - }, - Versions: []extensions.APIVersion{ - { - Name: versions[ix], - }, - }, - } - hasRsrc, err := master.HasThirdPartyResource(api) - if err != nil { - t.Errorf("Unexpected error: %v", err) - } - if !hasRsrc { - err := master.InstallThirdPartyResource(api) - if !assert.NoError(err) { - t.Errorf("Failed to install API: %v", err) - t.FailNow() - } - } else { - t.Errorf("Expected %s: %v not to be present!", names[ix], api) - } - } - - server := httptest.NewServer(master.HandlerContainer.ServeMux) - return master, etcdserver, server, assert -} - -func TestInstallMultipleAPIs(t *testing.T) { - names := []string{"foo.company.com", "bar.company.com"} - versions := []string{"v1", "v1"} - - _, etcdserver, server, assert := initThirdPartyMultiple(t, versions, names) - defer server.Close() - defer etcdserver.Terminate(t) - for ix := range names { - kind, group, err := thirdpartyresourcedata.ExtractApiGroupAndKind( - &extensions.ThirdPartyResource{ObjectMeta: api.ObjectMeta{Name: names[ix]}}) - assert.NoError(err, "Failed to extract group & kind") - - plural, _ := meta.KindToResource(unversioned.GroupVersionKind{ - Group: group, - Version: versions[ix], - Kind: kind, - }) - - resp, err := http.Get( - fmt.Sprintf("%s/apis/%s/%s/namespaces/default/%s", server.URL, group, versions[ix], plural.Resource)) - if !assert.NoError(err, "Failed to do HTTP GET") { - return - } - defer resp.Body.Close() - - assert.Equal(http.StatusOK, resp.StatusCode) - - data, err := ioutil.ReadAll(resp.Body) - assert.NoError(err) - - obj := map[string]interface{}{} - if err = json.Unmarshal(data, &obj); err != nil { - assert.NoError(err, fmt.Sprintf("unexpected error: %v", err)) - } - kindOut, found := obj["kind"] - if !found { - t.Errorf("Missing 'kind' in %v", obj) - } - assert.Equal(kindOut, kind+"List") - } -} - -func TestInstallThirdPartyAPIList(t *testing.T) { - for _, version := range versionsToTest { - testInstallThirdPartyAPIListVersion(t, version) - } -} - -func testInstallThirdPartyAPIListVersion(t *testing.T, version string) { - tests := []struct { - items []Foo - name string - test string - }{ - { - name: "foo.company.com", - test: "null", - }, - { - items: []Foo{}, - name: "foo.company.com", - test: "empty", - }, - { - items: []Foo{}, - name: "policy.company.com", - test: "plurals", - }, - { - items: []Foo{ - { - ObjectMeta: api.ObjectMeta{ - Name: "test", - }, - TypeMeta: unversioned.TypeMeta{ - Kind: "Foo", - APIVersion: version, - }, - SomeField: "test field", - OtherField: 10, - }, - { - ObjectMeta: api.ObjectMeta{ - Name: "bar", - }, - TypeMeta: unversioned.TypeMeta{ - Kind: "Foo", - APIVersion: version, - }, - SomeField: "test field another", - OtherField: 20, - }, - }, - name: "foo.company.com", - test: "real list", - }, - } - for _, test := range tests { - func() { - master, etcdserver, server, assert := initThirdParty(t, version, test.name) - defer server.Close() - defer etcdserver.Terminate(t) - - kind, group, err := thirdpartyresourcedata.ExtractApiGroupAndKind( - &extensions.ThirdPartyResource{ObjectMeta: api.ObjectMeta{Name: test.name}}) - assert.NoError(err, test.test) - - plural, _ := meta.KindToResource(unversioned.GroupVersionKind{ - Group: group, - Version: version, - Kind: kind, - }) - - if test.items != nil { - s, destroyFunc := generic.NewRawStorage(master.thirdPartyStorageConfig) - defer destroyFunc() - err := createThirdPartyList( - s, - fmt.Sprintf("/ThirdPartyResourceData/%s/%s/default", group, plural.Resource), - test.items) - if !assert.NoError(err, test.test) { - return - } - } - - resp, err := http.Get( - fmt.Sprintf("%s/apis/%s/%s/namespaces/default/%s", server.URL, group, version, plural.Resource)) - if !assert.NoError(err, test.test) { - return - } - defer resp.Body.Close() - - assert.Equal(http.StatusOK, resp.StatusCode, test.test) - - data, err := ioutil.ReadAll(resp.Body) - assert.NoError(err, test.test) - - list := FooList{} - if err = json.Unmarshal(data, &list); err != nil { - assert.NoError(err, "unexpected error: %v %s", err, test.test) - } - - if test.items == nil { - if len(list.Items) != 0 { - assert.NoError(err, "expected no items, saw: %v %s", err, list.Items, test.test) - } - return - } - - if len(list.Items) != len(test.items) { - t.Fatalf("(%s) unexpected length: %d vs %d", test.name, len(list.Items), len(test.items)) - } - // The order of elements in LIST is not guaranteed. - mapping := make(map[string]int) - for ix := range test.items { - mapping[test.items[ix].Name] = ix - } - for ix := range list.Items { - // Copy things that are set dynamically on the server - expectedObj := test.items[mapping[list.Items[ix].Name]] - expectedObj.SelfLink = list.Items[ix].SelfLink - expectedObj.ResourceVersion = list.Items[ix].ResourceVersion - expectedObj.Namespace = list.Items[ix].Namespace - expectedObj.UID = list.Items[ix].UID - expectedObj.CreationTimestamp = list.Items[ix].CreationTimestamp - - // We endure the order of items by sorting them (using 'mapping') - // so that this function passes. - if !reflect.DeepEqual(list.Items[ix], expectedObj) { - t.Errorf("(%s) expected:\n%#v\nsaw:\n%#v\n", test.name, expectedObj, list.Items[ix]) - } - } - }() - } -} - -func encodeToThirdParty(name string, obj interface{}) (runtime.Object, error) { - serial, err := json.Marshal(obj) - if err != nil { - return nil, err - } - thirdPartyData := extensions.ThirdPartyResourceData{ - ObjectMeta: api.ObjectMeta{Name: name}, - Data: serial, - } - return &thirdPartyData, nil -} - -func createThirdPartyObject(s storage.Interface, path, name string, obj interface{}) error { - data, err := encodeToThirdParty(name, obj) - if err != nil { - return err - } - return s.Create(context.TODO(), etcdtest.AddPrefix(path), data, nil, 0) -} - -func createThirdPartyList(s storage.Interface, path string, list []Foo) error { - for _, obj := range list { - if err := createThirdPartyObject(s, path+"/"+obj.Name, obj.Name, obj); err != nil { - return err - } - } - return nil -} - -func decodeResponse(resp *http.Response, obj interface{}) error { - defer resp.Body.Close() - - data, err := ioutil.ReadAll(resp.Body) - if err != nil { - return err - } - if err := json.Unmarshal(data, obj); err != nil { - return err - } - return nil -} - -func writeResponseToFile(resp *http.Response, filename string) error { - defer resp.Body.Close() - - data, err := ioutil.ReadAll(resp.Body) - if err != nil { - return err - } - return ioutil.WriteFile(filename, data, 0755) -} - -func TestInstallThirdPartyAPIGet(t *testing.T) { - for _, version := range versionsToTest { - testInstallThirdPartyAPIGetVersion(t, version) - } -} - -func testInstallThirdPartyAPIGetVersion(t *testing.T, version string) { - master, etcdserver, server, assert := initThirdParty(t, version, "foo.company.com") - defer server.Close() - defer etcdserver.Terminate(t) - - expectedObj := Foo{ - ObjectMeta: api.ObjectMeta{ - Name: "test", - }, - TypeMeta: unversioned.TypeMeta{ - Kind: "Foo", - APIVersion: version, - }, - SomeField: "test field", - OtherField: 10, - } - s, destroyFunc := generic.NewRawStorage(master.thirdPartyStorageConfig) - defer destroyFunc() - if !assert.NoError(createThirdPartyObject(s, "/ThirdPartyResourceData/company.com/foos/default/test", "test", expectedObj)) { - t.FailNow() - return - } - - resp, err := http.Get(server.URL + "/apis/company.com/" + version + "/namespaces/default/foos/test") - if !assert.NoError(err) { - return - } - - assert.Equal(http.StatusOK, resp.StatusCode) - - item := Foo{} - assert.NoError(decodeResponse(resp, &item)) - if !assert.False(reflect.DeepEqual(item, expectedObj)) { - t.Errorf("expected objects to not be equal:\n%v\nsaw:\n%v\n", expectedObj, item) - } - // Fill in data that the apiserver injects - expectedObj.SelfLink = item.SelfLink - expectedObj.ResourceVersion = item.ResourceVersion - if !assert.True(reflect.DeepEqual(item, expectedObj)) { - t.Errorf("expected:\n%#v\nsaw:\n%#v\n", expectedObj, item) - } -} - -func TestInstallThirdPartyAPIPost(t *testing.T) { - registered.AddThirdPartyAPIGroupVersions(unversioned.GroupVersion{Group: "company.com", Version: "v1"}, unversioned.GroupVersion{Group: "company.com", Version: "v3"}) - - for _, version := range versionsToTest { - testInstallThirdPartyAPIPostForVersion(t, version) - } -} - -func testInstallThirdPartyAPIPostForVersion(t *testing.T, version string) { - master, etcdserver, server, assert := initThirdParty(t, version, "foo.company.com") - defer server.Close() - defer etcdserver.Terminate(t) - - inputObj := Foo{ - ObjectMeta: api.ObjectMeta{ - Name: "test", - }, - TypeMeta: unversioned.TypeMeta{ - Kind: "Foo", - APIVersion: "company.com/" + version, - }, - SomeField: "test field", - OtherField: 10, - } - data, err := json.Marshal(inputObj) - if !assert.NoError(err) { - return - } - - resp, err := http.Post(server.URL+"/apis/company.com/"+version+"/namespaces/default/foos", "application/json", bytes.NewBuffer(data)) - if !assert.NoError(err) { - t.Fatalf("unexpected error: %v", err) - } - - assert.Equal(http.StatusCreated, resp.StatusCode) - - item := Foo{} - assert.NoError(decodeResponse(resp, &item)) - - // fill in fields set by the apiserver - expectedObj := inputObj - expectedObj.SelfLink = item.SelfLink - expectedObj.ResourceVersion = item.ResourceVersion - expectedObj.Namespace = item.Namespace - expectedObj.UID = item.UID - expectedObj.CreationTimestamp = item.CreationTimestamp - if !assert.True(reflect.DeepEqual(item, expectedObj)) { - t.Errorf("expected:\n%v\nsaw:\n%v\n", expectedObj, item) - } - - thirdPartyObj := extensions.ThirdPartyResourceData{} - s, destroyFunc := generic.NewRawStorage(master.thirdPartyStorageConfig) - defer destroyFunc() - err = s.Get(context.TODO(), etcdtest.AddPrefix("/ThirdPartyResourceData/company.com/foos/default/test"), &thirdPartyObj, false) - if !assert.NoError(err) { - t.FailNow() - } - - item = Foo{} - assert.NoError(json.Unmarshal(thirdPartyObj.Data, &item)) - - if !assert.True(reflect.DeepEqual(item, inputObj)) { - t.Errorf("expected:\n%v\nsaw:\n%v\n", inputObj, item) - } -} - -func TestInstallThirdPartyAPIDelete(t *testing.T) { - for _, version := range versionsToTest { - testInstallThirdPartyAPIDeleteVersion(t, version) - } -} - -func testInstallThirdPartyAPIDeleteVersion(t *testing.T, version string) { - master, etcdserver, server, assert := initThirdParty(t, version, "foo.company.com") - defer server.Close() - defer etcdserver.Terminate(t) - - expectedObj := Foo{ - ObjectMeta: api.ObjectMeta{ - Name: "test", - Namespace: "default", - }, - TypeMeta: unversioned.TypeMeta{ - Kind: "Foo", - }, - SomeField: "test field", - OtherField: 10, - } - s, destroyFunc := generic.NewRawStorage(master.thirdPartyStorageConfig) - defer destroyFunc() - if !assert.NoError(createThirdPartyObject(s, "/ThirdPartyResourceData/company.com/foos/default/test", "test", expectedObj)) { - t.FailNow() - return - } - - resp, err := http.Get(server.URL + "/apis/company.com/" + version + "/namespaces/default/foos/test") - if !assert.NoError(err) { - return - } - - assert.Equal(http.StatusOK, resp.StatusCode) - - item := Foo{} - assert.NoError(decodeResponse(resp, &item)) - - // Fill in fields set by the apiserver - expectedObj.SelfLink = item.SelfLink - expectedObj.ResourceVersion = item.ResourceVersion - expectedObj.Namespace = item.Namespace - if !assert.True(reflect.DeepEqual(item, expectedObj)) { - t.Errorf("expected:\n%v\nsaw:\n%v\n", expectedObj, item) - } - - resp, err = httpDelete(server.URL + "/apis/company.com/" + version + "/namespaces/default/foos/test") - if !assert.NoError(err) { - return - } - - assert.Equal(http.StatusOK, resp.StatusCode) - - resp, err = http.Get(server.URL + "/apis/company.com/" + version + "/namespaces/default/foos/test") - if !assert.NoError(err) { - return - } - - assert.Equal(http.StatusNotFound, resp.StatusCode) - - expectedDeletedKey := etcdtest.AddPrefix("ThirdPartyResourceData/company.com/foos/default/test") - thirdPartyObj := extensions.ThirdPartyResourceData{} - err = s.Get(context.TODO(), expectedDeletedKey, &thirdPartyObj, false) - if !storage.IsNotFound(err) { - t.Errorf("expected deletion didn't happen: %v", err) - } -} - -func httpDelete(url string) (*http.Response, error) { - req, err := http.NewRequest("DELETE", url, nil) - if err != nil { - return nil, err - } - client := &http.Client{} - return client.Do(req) -} - -func TestInstallThirdPartyAPIListOptions(t *testing.T) { - for _, version := range versionsToTest { - testInstallThirdPartyAPIListOptionsForVersion(t, version) - } -} - -func testInstallThirdPartyAPIListOptionsForVersion(t *testing.T, version string) { - _, etcdserver, server, assert := initThirdParty(t, version, "foo.company.com") - defer server.Close() - defer etcdserver.Terminate(t) - - // send a GET request with query parameter - resp, err := httpGetWithRV(server.URL + "/apis/company.com/" + version + "/namespaces/default/foos") - if !assert.NoError(err) { - t.Fatalf("unexpected error: %v", err) - } - assert.Equal(http.StatusOK, resp.StatusCode) -} - -func httpGetWithRV(url string) (*http.Response, error) { - req, err := http.NewRequest("GET", url, nil) - if err != nil { - return nil, err - } - q := req.URL.Query() - // resourceversion is part of a ListOptions - q.Add("resourceversion", "0") - req.URL.RawQuery = q.Encode() - client := &http.Client{} - return client.Do(req) -} - -func TestInstallThirdPartyResourceRemove(t *testing.T) { - for _, version := range versionsToTest { - testInstallThirdPartyResourceRemove(t, version) - } -} - -func testInstallThirdPartyResourceRemove(t *testing.T, version string) { - master, etcdserver, server, assert := initThirdParty(t, version, "foo.company.com") - defer server.Close() - defer etcdserver.Terminate(t) - - expectedObj := Foo{ - ObjectMeta: api.ObjectMeta{ - Name: "test", - }, - TypeMeta: unversioned.TypeMeta{ - Kind: "Foo", - }, - SomeField: "test field", - OtherField: 10, - } - s, destroyFunc := generic.NewRawStorage(master.thirdPartyStorageConfig) - defer destroyFunc() - if !assert.NoError(createThirdPartyObject(s, "/ThirdPartyResourceData/company.com/foos/default/test", "test", expectedObj)) { - t.FailNow() - return - } - secondObj := expectedObj - secondObj.Name = "bar" - if !assert.NoError(createThirdPartyObject(s, "/ThirdPartyResourceData/company.com/foos/default/bar", "bar", secondObj)) { - t.FailNow() - return - } - - resp, err := http.Get(server.URL + "/apis/company.com/" + version + "/namespaces/default/foos/test") - if !assert.NoError(err) { - t.FailNow() - return - } - - if resp.StatusCode != http.StatusOK { - t.Errorf("unexpected status: %v", resp) - } - - item := Foo{} - if err := decodeResponse(resp, &item); err != nil { - t.Errorf("unexpected error: %v", err) - } - - // TODO: validate etcd set things here - item.ObjectMeta = expectedObj.ObjectMeta - - if !assert.True(reflect.DeepEqual(item, expectedObj)) { - t.Errorf("expected:\n%v\nsaw:\n%v\n", expectedObj, item) - } - - path := extensionsrest.MakeThirdPartyPath("company.com") - master.RemoveThirdPartyResource(path + "/foos") - - resp, err = http.Get(server.URL + "/apis/company.com/" + version + "/namespaces/default/foos/test") - if !assert.NoError(err) { - return - } - - if resp.StatusCode != http.StatusNotFound { - t.Errorf("unexpected status: %v", resp) - } - - expectedDeletedKeys := []string{ - etcdtest.AddPrefix("/ThirdPartyResourceData/company.com/foos/default/test"), - etcdtest.AddPrefix("/ThirdPartyResourceData/company.com/foos/default/bar"), - } - for _, key := range expectedDeletedKeys { - thirdPartyObj := extensions.ThirdPartyResourceData{} - s, destroyFunc := generic.NewRawStorage(master.thirdPartyStorageConfig) - err := s.Get(context.TODO(), key, &thirdPartyObj, false) - if !storage.IsNotFound(err) { - t.Errorf("expected deletion didn't happen: %v", err) - } - destroyFunc() - } - installed := master.ListThirdPartyResources() - if len(installed) != 0 { - t.Errorf("Resource(s) still installed: %v", installed) - } - services := master.HandlerContainer.RegisteredWebServices() - for ix := range services { - if strings.HasPrefix(services[ix].RootPath(), "/apis/company.com") { - t.Errorf("Web service still installed at %s: %#v", services[ix].RootPath(), services[ix]) - } - } -} - -func TestThirdPartyDiscovery(t *testing.T) { - for _, version := range versionsToTest { - testThirdPartyDiscovery(t, version) - } -} - type FakeTunneler struct { SecondsSinceSyncValue int64 SecondsSinceSSHKeySyncValue int64 @@ -1116,48 +514,14 @@ func TestIsTunnelSyncHealthy(t *testing.T) { assert.Error(err, "IsTunnelSyncHealthy() should have returned an error.") } -func testThirdPartyDiscovery(t *testing.T, version string) { - _, etcdserver, server, assert := initThirdParty(t, version, "foo.company.com") - defer server.Close() - defer etcdserver.Terminate(t) +func writeResponseToFile(resp *http.Response, filename string) error { + defer resp.Body.Close() - resp, err := http.Get(server.URL + "/apis/company.com/") - if !assert.NoError(err) { - return + data, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err } - assert.Equal(http.StatusOK, resp.StatusCode) - - group := unversioned.APIGroup{} - assert.NoError(decodeResponse(resp, &group)) - assert.Equal(group.APIVersion, "v1") - assert.Equal(group.Kind, "APIGroup") - assert.Equal(group.Name, "company.com") - expectedVersion := unversioned.GroupVersionForDiscovery{ - GroupVersion: "company.com/" + version, - Version: version, - } - - assert.Equal(group.Versions, []unversioned.GroupVersionForDiscovery{expectedVersion}) - assert.Equal(group.PreferredVersion, expectedVersion) - - resp, err = http.Get(server.URL + "/apis/company.com/" + version) - if !assert.NoError(err) { - return - } - assert.Equal(http.StatusOK, resp.StatusCode) - - resourceList := unversioned.APIResourceList{} - assert.NoError(decodeResponse(resp, &resourceList)) - assert.Equal(resourceList.APIVersion, "v1") - assert.Equal(resourceList.Kind, "APIResourceList") - assert.Equal(resourceList.GroupVersion, "company.com/"+version) - assert.Equal(resourceList.APIResources, []unversioned.APIResource{ - { - Name: "foos", - Namespaced: true, - Kind: "Foo", - }, - }) + return ioutil.WriteFile(filename, data, 0755) } // TestValidOpenAPISpec verifies that the open api is added diff --git a/test/integration/thirdparty/thirdparty_test.go b/test/integration/thirdparty/thirdparty_test.go new file mode 100644 index 00000000000..d55a4005d40 --- /dev/null +++ b/test/integration/thirdparty/thirdparty_test.go @@ -0,0 +1,229 @@ +// +build integration,!no-etcd + +/* +Copyright 2015 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 thirdparty + +// This file contains tests for the storage classes API resource. + +import ( + "encoding/json" + "reflect" + "testing" + "time" + + "k8s.io/kubernetes/pkg/api" + apierrors "k8s.io/kubernetes/pkg/api/errors" + "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/apis/extensions" + "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" + "k8s.io/kubernetes/pkg/client/restclient" + "k8s.io/kubernetes/pkg/util/diff" + "k8s.io/kubernetes/pkg/util/wait" + "k8s.io/kubernetes/test/integration/framework" +) + +// TODO these tests will eventually be runnable in a single test +func TestThirdPartyDelete(t *testing.T) { + _, s := framework.RunAMaster(framework.NewIntegrationTestMasterConfig()) + defer s.Close() + + clientConfig := &restclient.Config{Host: s.URL, ContentConfig: restclient.ContentConfig{NegotiatedSerializer: api.Codecs}} + client := internalclientset.NewForConfigOrDie(clientConfig) + + DoTestInstallThirdPartyAPIDelete(t, client, clientConfig) +} + +func TestThirdPartyMultiple(t *testing.T) { + _, s := framework.RunAMaster(framework.NewIntegrationTestMasterConfig()) + defer s.Close() + + clientConfig := &restclient.Config{Host: s.URL, ContentConfig: restclient.ContentConfig{NegotiatedSerializer: api.Codecs}} + client := internalclientset.NewForConfigOrDie(clientConfig) + + DoTestInstallMultipleAPIs(t, client, clientConfig) +} + +// TODO make multiple versions work. they've been broken +var versionsToTest = []string{"v1"} + +type Foo struct { + unversioned.TypeMeta `json:",inline"` + api.ObjectMeta `json:"metadata,omitempty" description:"standard object metadata"` + + SomeField string `json:"someField"` + OtherField int `json:"otherField"` +} + +type FooList struct { + unversioned.TypeMeta `json:",inline"` + unversioned.ListMeta `json:"metadata,omitempty" description:"standard list metadata; see http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata"` + + Items []Foo `json:"items"` +} + +// installThirdParty installs a third party resoure and returns a defer func +func installThirdParty(t *testing.T, client internalclientset.Interface, clientConfig *restclient.Config, tpr *extensions.ThirdPartyResource, group, version, resource string) func() { + var err error + _, err = client.Extensions().ThirdPartyResources().Create(tpr) + if err != nil { + t.Fatal(err) + } + + fooClientConfig := *clientConfig + fooClientConfig.APIPath = "apis" + fooClientConfig.GroupVersion = &unversioned.GroupVersion{Group: group, Version: version} + fooClient, err := restclient.RESTClientFor(&fooClientConfig) + if err != nil { + t.Fatal(err) + } + + err = wait.Poll(100*time.Millisecond, 60*time.Second, func() (bool, error) { + _, err := fooClient.Get().Namespace("default").Resource(resource).DoRaw() + if err == nil { + return true, nil + } + if apierrors.IsNotFound(err) { + return false, nil + } + + return false, err + }) + if err != nil { + t.Fatal(err) + } + + return func() { + client.Extensions().ThirdPartyResources().Delete(tpr.Name, nil) + err = wait.Poll(100*time.Millisecond, 60*time.Second, func() (bool, error) { + _, err := fooClient.Get().Namespace("default").Resource(resource).DoRaw() + if apierrors.IsNotFound(err) { + return true, nil + } + + return false, err + }) + if err != nil { + t.Fatal(err) + } + } +} + +func DoTestInstallMultipleAPIs(t *testing.T, client internalclientset.Interface, clientConfig *restclient.Config) { + group := "company.com" + version := "v1" + + defer installThirdParty(t, client, clientConfig, + &extensions.ThirdPartyResource{ + ObjectMeta: api.ObjectMeta{Name: "foo.company.com"}, + Versions: []extensions.APIVersion{{Name: version}}, + }, group, version, "foos", + )() + + // TODO make multiple resources in one version work + // defer installThirdParty(t, client, clientConfig, + // &extensions.ThirdPartyResource{ + // ObjectMeta: api.ObjectMeta{Name: "bar.company.com"}, + // Versions: []extensions.APIVersion{{Name: version}}, + // }, group, version, "bars", + // )() +} + +func DoTestInstallThirdPartyAPIDelete(t *testing.T, client internalclientset.Interface, clientConfig *restclient.Config) { + for _, version := range versionsToTest { + testInstallThirdPartyAPIDeleteVersion(t, client, clientConfig, version) + } +} + +func testInstallThirdPartyAPIDeleteVersion(t *testing.T, client internalclientset.Interface, clientConfig *restclient.Config, version string) { + group := "company.com" + + defer installThirdParty(t, client, clientConfig, + &extensions.ThirdPartyResource{ + ObjectMeta: api.ObjectMeta{Name: "foo.company.com"}, + Versions: []extensions.APIVersion{{Name: version}}, + }, group, version, "foos", + )() + + fooClientConfig := *clientConfig + fooClientConfig.APIPath = "apis" + fooClientConfig.GroupVersion = &unversioned.GroupVersion{Group: group, Version: version} + fooClient, err := restclient.RESTClientFor(&fooClientConfig) + if err != nil { + t.Fatal(err) + } + + expectedObj := Foo{ + ObjectMeta: api.ObjectMeta{ + Name: "test", + Namespace: "default", + }, + TypeMeta: unversioned.TypeMeta{ + Kind: "Foo", + }, + SomeField: "test field", + OtherField: 10, + } + objBytes, err := json.Marshal(&expectedObj) + if err != nil { + t.Fatal(err) + } + + if _, err := fooClient.Post().Namespace("default").Resource("foos").Body(objBytes).DoRaw(); err != nil { + t.Fatal(err) + } + + apiBytes, err := fooClient.Get().Namespace("default").Resource("foos").Name("test").DoRaw() + if err != nil { + t.Fatal(err) + } + item := Foo{} + err = json.Unmarshal(apiBytes, &item) + if err != nil { + t.Fatal(err) + } + + // Fill in fields set by the apiserver + item.SelfLink = expectedObj.SelfLink + item.ResourceVersion = expectedObj.ResourceVersion + item.Namespace = expectedObj.Namespace + item.UID = expectedObj.UID + item.CreationTimestamp = expectedObj.CreationTimestamp + if !reflect.DeepEqual(item, expectedObj) { + t.Fatalf("expected:\n%v\n", diff.ObjectGoPrintSideBySide(expectedObj, item)) + } + + listBytes, err := fooClient.Get().Namespace("default").Resource("foos").DoRaw() + if err != nil { + t.Fatal(err) + } + list := FooList{} + err = json.Unmarshal(listBytes, &list) + if err != nil { + t.Fatal(err) + } + if len(list.Items) != 1 { + t.Fatalf("wrong item: %v", list) + } + + if _, err := fooClient.Delete().Namespace("default").Resource("foos").Name("test").DoRaw(); err != nil { + t.Fatal(err) + } + if _, err := fooClient.Get().Namespace("default").Resource("foos").Name("test").DoRaw(); !apierrors.IsNotFound(err) { + t.Fatal(err) + } +}