123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269 |
- /*
- Copyright 2014 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 master
- import (
- "bytes"
- "crypto/tls"
- "encoding/json"
- "fmt"
- "io/ioutil"
- "net"
- "net/http"
- "net/http/httptest"
- "reflect"
- "strings"
- "testing"
- "time"
- "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"
- "k8s.io/kubernetes/pkg/apimachinery/registered"
- "k8s.io/kubernetes/pkg/apis/apps"
- appsapi "k8s.io/kubernetes/pkg/apis/apps"
- "k8s.io/kubernetes/pkg/apis/autoscaling"
- autoscalingapiv1 "k8s.io/kubernetes/pkg/apis/autoscaling/v1"
- "k8s.io/kubernetes/pkg/apis/batch"
- batchapiv1 "k8s.io/kubernetes/pkg/apis/batch/v1"
- batchapiv2alpha1 "k8s.io/kubernetes/pkg/apis/batch/v2alpha1"
- "k8s.io/kubernetes/pkg/apis/certificates"
- "k8s.io/kubernetes/pkg/apis/extensions"
- extensionsapiv1beta1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
- "k8s.io/kubernetes/pkg/apis/rbac"
- "k8s.io/kubernetes/pkg/apiserver"
- "k8s.io/kubernetes/pkg/genericapiserver"
- "k8s.io/kubernetes/pkg/kubelet/client"
- "k8s.io/kubernetes/pkg/registry/endpoint"
- "k8s.io/kubernetes/pkg/registry/generic"
- "k8s.io/kubernetes/pkg/registry/namespace"
- "k8s.io/kubernetes/pkg/registry/registrytest"
- "k8s.io/kubernetes/pkg/registry/thirdpartyresourcedata"
- "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"
- "k8s.io/kubernetes/pkg/storage/storagebackend"
- "k8s.io/kubernetes/pkg/util/intstr"
- utilnet "k8s.io/kubernetes/pkg/util/net"
- "k8s.io/kubernetes/pkg/util/sets"
- "github.com/go-openapi/loads"
- "github.com/go-openapi/spec"
- "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.
- func setUp(t *testing.T) (*Master, *etcdtesting.EtcdTestServer, Config, *assert.Assertions) {
- server := etcdtesting.NewUnsecuredEtcdTestClientServer(t)
- master := &Master{
- GenericAPIServer: &genericapiserver.GenericAPIServer{},
- }
- config := Config{
- Config: &genericapiserver.Config{},
- }
- storageConfig := storagebackend.Config{
- Prefix: etcdtest.PathPrefix(),
- CAFile: server.CAFile,
- KeyFile: server.KeyFile,
- CertFile: server.CertFile,
- }
- for _, url := range server.ClientURLs {
- storageConfig.ServerList = append(storageConfig.ServerList, url.String())
- }
- resourceEncoding := genericapiserver.NewDefaultResourceEncodingConfig()
- resourceEncoding.SetVersionEncoding(api.GroupName, *testapi.Default.GroupVersion(), unversioned.GroupVersion{Group: api.GroupName, Version: runtime.APIVersionInternal})
- resourceEncoding.SetVersionEncoding(autoscaling.GroupName, *testapi.Autoscaling.GroupVersion(), unversioned.GroupVersion{Group: autoscaling.GroupName, Version: runtime.APIVersionInternal})
- resourceEncoding.SetVersionEncoding(batch.GroupName, *testapi.Batch.GroupVersion(), unversioned.GroupVersion{Group: batch.GroupName, Version: runtime.APIVersionInternal})
- resourceEncoding.SetVersionEncoding(apps.GroupName, *testapi.Apps.GroupVersion(), unversioned.GroupVersion{Group: apps.GroupName, Version: runtime.APIVersionInternal})
- resourceEncoding.SetVersionEncoding(extensions.GroupName, *testapi.Extensions.GroupVersion(), unversioned.GroupVersion{Group: extensions.GroupName, Version: runtime.APIVersionInternal})
- resourceEncoding.SetVersionEncoding(rbac.GroupName, *testapi.Rbac.GroupVersion(), unversioned.GroupVersion{Group: rbac.GroupName, Version: runtime.APIVersionInternal})
- resourceEncoding.SetVersionEncoding(certificates.GroupName, *testapi.Certificates.GroupVersion(), unversioned.GroupVersion{Group: certificates.GroupName, Version: runtime.APIVersionInternal})
- storageFactory := genericapiserver.NewDefaultStorageFactory(storageConfig, testapi.StorageMediaType(), api.Codecs, resourceEncoding, DefaultAPIResourceConfigSource())
- config.StorageFactory = storageFactory
- config.APIResourceConfigSource = DefaultAPIResourceConfigSource()
- config.PublicAddress = net.ParseIP("192.168.10.4")
- config.Serializer = api.Codecs
- config.KubeletClient = client.FakeKubeletClient{}
- config.APIPrefix = "/api"
- config.APIGroupPrefix = "/apis"
- config.APIResourceConfigSource = DefaultAPIResourceConfigSource()
- config.ProxyDialer = func(network, addr string) (net.Conn, error) { return nil, nil }
- config.ProxyTLSClientConfig = &tls.Config{}
- // TODO: this is kind of hacky. The trouble is that the sync loop
- // runs in a go-routine and there is no way to validate in the test
- // that the sync routine has actually run. The right answer here
- // is probably to add some sort of callback that we can register
- // to validate that it's actually been run, but for now we don't
- // run the sync routine and register types manually.
- config.disableThirdPartyControllerForTesting = true
- master.nodeRegistry = registrytest.NewNodeRegistry([]string{"node1", "node2"}, api.NodeResources{})
- return master, server, config, assert.New(t)
- }
- func newMaster(t *testing.T) (*Master, *etcdtesting.EtcdTestServer, Config, *assert.Assertions) {
- _, etcdserver, config, assert := setUp(t)
- master, err := New(&config)
- if err != nil {
- t.Fatalf("Error in bringing up the master: %v", err)
- }
- return master, etcdserver, config, assert
- }
- // limitedAPIResourceConfigSource only enables the core group, the extensions group, the batch group, and the autoscaling group.
- func limitedAPIResourceConfigSource() *genericapiserver.ResourceConfig {
- ret := genericapiserver.NewResourceConfig()
- ret.EnableVersions(
- apiv1.SchemeGroupVersion,
- extensionsapiv1beta1.SchemeGroupVersion,
- batchapiv1.SchemeGroupVersion,
- batchapiv2alpha1.SchemeGroupVersion,
- appsapi.SchemeGroupVersion,
- autoscalingapiv1.SchemeGroupVersion,
- )
- return ret
- }
- // newLimitedMaster only enables the core group, the extensions group, the batch group, and the autoscaling group.
- func newLimitedMaster(t *testing.T) (*Master, *etcdtesting.EtcdTestServer, Config, *assert.Assertions) {
- _, etcdserver, config, assert := setUp(t)
- config.APIResourceConfigSource = limitedAPIResourceConfigSource()
- master, err := New(&config)
- if err != nil {
- t.Fatalf("Error in bringing up the master: %v", err)
- }
- return master, etcdserver, config, assert
- }
- // TestNew verifies that the New function returns a Master
- // using the configuration properly.
- func TestNew(t *testing.T) {
- master, etcdserver, config, assert := newMaster(t)
- defer etcdserver.Terminate(t)
- // Verify many of the variables match their config counterparts
- assert.Equal(master.enableCoreControllers, config.EnableCoreControllers)
- assert.Equal(master.tunneler, config.Tunneler)
- assert.Equal(master.APIPrefix, config.APIPrefix)
- assert.Equal(master.APIGroupPrefix, config.APIGroupPrefix)
- assert.Equal(master.RequestContextMapper, config.RequestContextMapper)
- assert.Equal(master.MasterCount, config.MasterCount)
- assert.Equal(master.ClusterIP, config.PublicAddress)
- assert.Equal(master.PublicReadWritePort, config.ReadWritePort)
- assert.Equal(master.ServiceReadWriteIP, config.ServiceReadWriteIP)
- // These functions should point to the same memory location
- masterDialer, _ := utilnet.Dialer(master.ProxyTransport)
- masterDialerFunc := fmt.Sprintf("%p", masterDialer)
- configDialerFunc := fmt.Sprintf("%p", config.ProxyDialer)
- assert.Equal(masterDialerFunc, configDialerFunc)
- assert.Equal(master.ProxyTransport.(*http.Transport).TLSClientConfig, config.ProxyTLSClientConfig)
- }
- // TestNamespaceSubresources ensures the namespace subresource parsing in apiserver/handlers.go doesn't drift
- func TestNamespaceSubresources(t *testing.T) {
- master, etcdserver, _, _ := newMaster(t)
- defer etcdserver.Terminate(t)
- expectedSubresources := apiserver.NamespaceSubResourcesForTest
- foundSubresources := sets.NewString()
- for k := range master.v1ResourcesStorage {
- parts := strings.Split(k, "/")
- if len(parts) == 2 && parts[0] == "namespaces" {
- foundSubresources.Insert(parts[1])
- }
- }
- if !reflect.DeepEqual(expectedSubresources.List(), foundSubresources.List()) {
- t.Errorf("Expected namespace subresources %#v, got %#v. Update apiserver/handlers.go#namespaceSubresources", expectedSubresources.List(), foundSubresources.List())
- }
- }
- // TestGetServersToValidate verifies the unexported getServersToValidate function
- func TestGetServersToValidate(t *testing.T) {
- master, etcdserver, config, assert := setUp(t)
- defer etcdserver.Terminate(t)
- servers := master.getServersToValidate(&config)
- // Expected servers to validate: scheduler, controller-manager and etcd.
- assert.Equal(3, len(servers), "unexpected server list: %#v", servers)
- for _, server := range []string{"scheduler", "controller-manager", "etcd-0"} {
- if _, ok := servers[server]; !ok {
- t.Errorf("server list missing: %s", server)
- }
- }
- }
- // TestFindExternalAddress verifies both pass and fail cases for the unexported
- // findExternalAddress function
- func TestFindExternalAddress(t *testing.T) {
- assert := assert.New(t)
- expectedIP := "172.0.0.1"
- nodes := []*api.Node{new(api.Node), new(api.Node), new(api.Node)}
- nodes[0].Status.Addresses = []api.NodeAddress{{Type: "ExternalIP", Address: expectedIP}}
- nodes[1].Status.Addresses = []api.NodeAddress{{Type: "LegacyHostIP", Address: expectedIP}}
- nodes[2].Status.Addresses = []api.NodeAddress{{Type: "ExternalIP", Address: expectedIP}, {Type: "LegacyHostIP", Address: "172.0.0.2"}}
- // Pass Case
- for _, node := range nodes {
- ip, err := findExternalAddress(node)
- assert.NoError(err, "error getting node external address")
- assert.Equal(expectedIP, ip, "expected ip to be %s, but was %s", expectedIP, ip)
- }
- // Fail case
- _, err := findExternalAddress(new(api.Node))
- assert.Error(err, "expected findExternalAddress to fail on a node with missing ip information")
- }
- type fakeEndpointReconciler struct{}
- func (*fakeEndpointReconciler) ReconcileEndpoints(serviceName string, ip net.IP, endpointPorts []api.EndpointPort, reconcilePorts bool) error {
- return nil
- }
- // TestNewBootstrapController verifies master fields are properly copied into controller
- func TestNewBootstrapController(t *testing.T) {
- // Tests a subset of inputs to ensure they are set properly in the controller
- master, etcdserver, _, assert := setUp(t)
- defer etcdserver.Terminate(t)
- portRange := utilnet.PortRange{Base: 10, Size: 10}
- master.namespaceRegistry = namespace.NewRegistry(nil)
- master.serviceRegistry = registrytest.NewServiceRegistry()
- master.endpointRegistry = endpoint.NewRegistry(nil)
- master.ServiceNodePortRange = portRange
- master.MasterCount = 1
- master.ServiceReadWritePort = 1000
- master.PublicReadWritePort = 1010
- // test with an empty EndpointReconcilerConfig to ensure the defaults are applied
- controller := master.NewBootstrapController(EndpointReconcilerConfig{})
- assert.Equal(controller.NamespaceRegistry, master.namespaceRegistry)
- assert.Equal(controller.EndpointReconciler, NewMasterCountEndpointReconciler(master.MasterCount, master.endpointRegistry))
- assert.Equal(controller.EndpointInterval, DefaultEndpointReconcilerInterval)
- assert.Equal(controller.ServiceRegistry, master.serviceRegistry)
- assert.Equal(controller.ServiceNodePortRange, portRange)
- assert.Equal(controller.ServicePort, master.ServiceReadWritePort)
- assert.Equal(controller.PublicServicePort, master.PublicReadWritePort)
- // test with a filled-in EndpointReconcilerConfig to make sure its values are used
- controller = master.NewBootstrapController(EndpointReconcilerConfig{
- Reconciler: &fakeEndpointReconciler{},
- Interval: 5 * time.Second,
- })
- assert.Equal(controller.EndpointReconciler, &fakeEndpointReconciler{})
- assert.Equal(controller.EndpointInterval, 5*time.Second)
- }
- // TestControllerServicePorts verifies master extraServicePorts are
- // correctly copied into controller
- func TestControllerServicePorts(t *testing.T) {
- master, etcdserver, _, assert := setUp(t)
- defer etcdserver.Terminate(t)
- master.namespaceRegistry = namespace.NewRegistry(nil)
- master.serviceRegistry = registrytest.NewServiceRegistry()
- master.endpointRegistry = endpoint.NewRegistry(nil)
- master.ExtraServicePorts = []api.ServicePort{
- {
- Name: "additional-port-1",
- Port: 1000,
- Protocol: api.ProtocolTCP,
- TargetPort: intstr.FromInt(1000),
- },
- {
- Name: "additional-port-2",
- Port: 1010,
- Protocol: api.ProtocolTCP,
- TargetPort: intstr.FromInt(1010),
- },
- }
- controller := master.NewBootstrapController(EndpointReconcilerConfig{})
- assert.Equal(int32(1000), controller.ExtraServicePorts[0].Port)
- assert.Equal(int32(1010), controller.ExtraServicePorts[1].Port)
- }
- // TestGetNodeAddresses verifies that proper results are returned
- // when requesting node addresses.
- func TestGetNodeAddresses(t *testing.T) {
- master, etcdserver, _, assert := setUp(t)
- defer etcdserver.Terminate(t)
- // Fail case (no addresses associated with nodes)
- nodes, _ := master.nodeRegistry.ListNodes(api.NewDefaultContext(), nil)
- addrs, err := master.getNodeAddresses()
- assert.Error(err, "getNodeAddresses should have caused an error as there are no addresses.")
- assert.Equal([]string(nil), addrs)
- // Pass case with External type IP
- nodes, _ = master.nodeRegistry.ListNodes(api.NewDefaultContext(), nil)
- for index := range nodes.Items {
- nodes.Items[index].Status.Addresses = []api.NodeAddress{{Type: api.NodeExternalIP, Address: "127.0.0.1"}}
- }
- addrs, err = master.getNodeAddresses()
- assert.NoError(err, "getNodeAddresses should not have returned an error.")
- assert.Equal([]string{"127.0.0.1", "127.0.0.1"}, addrs)
- // Pass case with LegacyHost type IP
- nodes, _ = master.nodeRegistry.ListNodes(api.NewDefaultContext(), nil)
- for index := range nodes.Items {
- nodes.Items[index].Status.Addresses = []api.NodeAddress{{Type: api.NodeLegacyHostIP, Address: "127.0.0.2"}}
- }
- addrs, err = master.getNodeAddresses()
- assert.NoError(err, "getNodeAddresses failback should not have returned an error.")
- assert.Equal([]string{"127.0.0.2", "127.0.0.2"}, addrs)
- }
- // 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) {
- master, etcdserver, _, assert := newMaster(t)
- defer etcdserver.Terminate(t)
- server := httptest.NewServer(master.HandlerContainer.ServeMux)
- // /api exists in release-1.1
- resp, err := http.Get(server.URL + "/api")
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- apiVersions := unversioned.APIVersions{}
- assert.NoError(decodeResponse(resp, &apiVersions))
- assert.Equal(apiVersions.APIVersion, "")
- // /api/v1 exists in release-1.1
- resp, err = http.Get(server.URL + "/api/v1")
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- resourceList := unversioned.APIResourceList{}
- assert.NoError(decodeResponse(resp, &resourceList))
- assert.Equal(resourceList.APIVersion, "")
- // /apis exists in release-1.1
- resp, err = http.Get(server.URL + "/apis")
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- groupList := unversioned.APIGroupList{}
- assert.NoError(decodeResponse(resp, &groupList))
- assert.Equal(groupList.APIVersion, "")
- // /apis/extensions exists in release-1.1
- resp, err = http.Get(server.URL + "/apis/extensions")
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- group := unversioned.APIGroup{}
- assert.NoError(decodeResponse(resp, &group))
- assert.Equal(group.APIVersion, "")
- // /apis/extensions/v1beta1 exists in release-1.1
- resp, err = http.Get(server.URL + "/apis/extensions/v1beta1")
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- resourceList = unversioned.APIResourceList{}
- assert.NoError(decodeResponse(resp, &resourceList))
- assert.Equal(resourceList.APIVersion, "")
- // /apis/autoscaling doesn't exist in release-1.1, so the APIVersion field
- // should be non-empty in the results returned by the server.
- resp, err = http.Get(server.URL + "/apis/autoscaling")
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- group = unversioned.APIGroup{}
- assert.NoError(decodeResponse(resp, &group))
- assert.Equal(group.APIVersion, "v1")
- // apis/autoscaling/v1 doesn't exist in release-1.1, so the APIVersion field
- // should be non-empty in the results returned by the server.
- resp, err = http.Get(server.URL + "/apis/autoscaling/v1")
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- resourceList = unversioned.APIResourceList{}
- assert.NoError(decodeResponse(resp, &resourceList))
- assert.Equal(resourceList.APIVersion, "v1")
- }
- func TestDiscoveryAtAPIS(t *testing.T) {
- master, etcdserver, _, assert := newLimitedMaster(t)
- defer etcdserver.Terminate(t)
- server := httptest.NewServer(master.HandlerContainer.ServeMux)
- resp, err := http.Get(server.URL + "/apis")
- if !assert.NoError(err) {
- t.Errorf("unexpected error: %v", err)
- }
- assert.Equal(http.StatusOK, resp.StatusCode)
- groupList := unversioned.APIGroupList{}
- assert.NoError(decodeResponse(resp, &groupList))
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- expectGroupNames := sets.NewString(autoscaling.GroupName, batch.GroupName, apps.GroupName, extensions.GroupName)
- expectVersions := map[string][]unversioned.GroupVersionForDiscovery{
- autoscaling.GroupName: {
- {
- GroupVersion: testapi.Autoscaling.GroupVersion().String(),
- Version: testapi.Autoscaling.GroupVersion().Version,
- },
- },
- // batch is using its pkg/apis/batch/ types here since during installation
- // both versions get installed and testapi.go currently does not support
- // multi-versioned clients
- batch.GroupName: {
- {
- GroupVersion: batchapiv1.SchemeGroupVersion.String(),
- Version: batchapiv1.SchemeGroupVersion.Version,
- },
- {
- GroupVersion: batchapiv2alpha1.SchemeGroupVersion.String(),
- Version: batchapiv2alpha1.SchemeGroupVersion.Version,
- },
- },
- apps.GroupName: {
- {
- GroupVersion: testapi.Apps.GroupVersion().String(),
- Version: testapi.Apps.GroupVersion().Version,
- },
- },
- extensions.GroupName: {
- {
- GroupVersion: testapi.Extensions.GroupVersion().String(),
- Version: testapi.Extensions.GroupVersion().Version,
- },
- },
- }
- expectPreferredVersion := map[string]unversioned.GroupVersionForDiscovery{
- autoscaling.GroupName: {
- GroupVersion: registered.GroupOrDie(autoscaling.GroupName).GroupVersion.String(),
- Version: registered.GroupOrDie(autoscaling.GroupName).GroupVersion.Version,
- },
- batch.GroupName: {
- GroupVersion: registered.GroupOrDie(batch.GroupName).GroupVersion.String(),
- Version: registered.GroupOrDie(batch.GroupName).GroupVersion.Version,
- },
- apps.GroupName: {
- GroupVersion: registered.GroupOrDie(apps.GroupName).GroupVersion.String(),
- Version: registered.GroupOrDie(apps.GroupName).GroupVersion.Version,
- },
- extensions.GroupName: {
- GroupVersion: registered.GroupOrDie(extensions.GroupName).GroupVersion.String(),
- Version: registered.GroupOrDie(extensions.GroupName).GroupVersion.Version,
- },
- }
- assert.Equal(4, len(groupList.Groups))
- for _, group := range groupList.Groups {
- if !expectGroupNames.Has(group.Name) {
- t.Errorf("got unexpected group %s", group.Name)
- }
- assert.Equal(expectVersions[group.Name], group.Versions)
- assert.Equal(expectPreferredVersion[group.Name], group.PreferredVersion)
- }
- thirdPartyGV := unversioned.GroupVersionForDiscovery{GroupVersion: "company.com/v1", Version: "v1"}
- master.addThirdPartyResourceStorage("/apis/company.com/v1", "foos", nil,
- unversioned.APIGroup{
- Name: "company.com",
- Versions: []unversioned.GroupVersionForDiscovery{thirdPartyGV},
- PreferredVersion: thirdPartyGV,
- })
- resp, err = http.Get(server.URL + "/apis")
- if !assert.NoError(err) {
- t.Errorf("unexpected error: %v", err)
- }
- assert.Equal(http.StatusOK, resp.StatusCode)
- assert.NoError(decodeResponse(resp, &groupList))
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- assert.Equal(5, len(groupList.Groups))
- expectGroupNames.Insert("company.com")
- expectVersions["company.com"] = []unversioned.GroupVersionForDiscovery{thirdPartyGV}
- expectPreferredVersion["company.com"] = thirdPartyGV
- for _, group := range groupList.Groups {
- if !expectGroupNames.Has(group.Name) {
- t.Errorf("got unexpected group %s", group.Name)
- }
- assert.Equal(expectVersions[group.Name], group.Versions)
- assert.Equal(expectPreferredVersion[group.Name], group.PreferredVersion)
- }
- }
- 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 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 := 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
- }
- func (t *FakeTunneler) Run(genericapiserver.AddressFunc) {}
- func (t *FakeTunneler) Stop() {}
- func (t *FakeTunneler) Dial(net, addr string) (net.Conn, error) { return nil, nil }
- func (t *FakeTunneler) SecondsSinceSync() int64 { return t.SecondsSinceSyncValue }
- func (t *FakeTunneler) SecondsSinceSSHKeySync() int64 { return t.SecondsSinceSSHKeySyncValue }
- // TestIsTunnelSyncHealthy verifies that the 600 second lag test
- // is honored.
- func TestIsTunnelSyncHealthy(t *testing.T) {
- assert := assert.New(t)
- tunneler := &FakeTunneler{}
- master := &Master{
- GenericAPIServer: &genericapiserver.GenericAPIServer{},
- tunneler: tunneler,
- }
- // Pass case: 540 second lag
- tunneler.SecondsSinceSyncValue = 540
- err := master.IsTunnelSyncHealthy(nil)
- assert.NoError(err, "IsTunnelSyncHealthy() should not have returned an error.")
- // Fail case: 720 second lag
- tunneler.SecondsSinceSyncValue = 720
- err = master.IsTunnelSyncHealthy(nil)
- 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)
- resp, err := http.Get(server.URL + "/apis/company.com/")
- if !assert.NoError(err) {
- return
- }
- 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",
- },
- })
- }
- // TestValidOpenAPISpec verifies that the open api is added
- // at the proper endpoint and the spec is valid.
- func TestValidOpenAPISpec(t *testing.T) {
- _, etcdserver, config, assert := setUp(t)
- defer etcdserver.Terminate(t)
- config.EnableOpenAPISupport = true
- config.OpenAPIInfo = spec.Info{
- InfoProps: spec.InfoProps{
- Title: "Kubernetes",
- Version: "unversioned",
- },
- }
- master, err := New(&config)
- if err != nil {
- t.Fatalf("Error in bringing up the master: %v", err)
- }
- // make sure swagger.json is not registered before calling install api.
- server := httptest.NewServer(master.HandlerContainer.ServeMux)
- resp, err := http.Get(server.URL + "/swagger.json")
- if !assert.NoError(err) {
- t.Errorf("unexpected error: %v", err)
- }
- assert.Equal(http.StatusNotFound, resp.StatusCode)
- master.InstallOpenAPI()
- resp, err = http.Get(server.URL + "/swagger.json")
- if !assert.NoError(err) {
- t.Errorf("unexpected error: %v", err)
- }
- assert.Equal(http.StatusOK, resp.StatusCode)
- // as json schema
- var sch spec.Schema
- if assert.NoError(decodeResponse(resp, &sch)) {
- validator := validate.NewSchemaValidator(spec.MustLoadSwagger20Schema(), nil, "", strfmt.Default)
- res := validator.Validate(&sch)
- assert.NoError(res.AsError())
- }
- // Validate OpenApi spec
- doc, err := loads.Spec(server.URL + "/swagger.json")
- if assert.NoError(err) {
- // TODO(mehdy): This test is timing out on jerkin but passing locally. Enable it after debugging timeout issue.
- _ = validate.NewSpecValidator(doc.Schema(), strfmt.Default)
- // res, _ := validator.Validate(doc)
- // assert.NoError(res.AsError())
- }
- }
|