master_test.go 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269
  1. /*
  2. Copyright 2014 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package master
  14. import (
  15. "bytes"
  16. "crypto/tls"
  17. "encoding/json"
  18. "fmt"
  19. "io/ioutil"
  20. "net"
  21. "net/http"
  22. "net/http/httptest"
  23. "reflect"
  24. "strings"
  25. "testing"
  26. "time"
  27. "k8s.io/kubernetes/pkg/api"
  28. "k8s.io/kubernetes/pkg/api/meta"
  29. "k8s.io/kubernetes/pkg/api/testapi"
  30. "k8s.io/kubernetes/pkg/api/unversioned"
  31. apiv1 "k8s.io/kubernetes/pkg/api/v1"
  32. "k8s.io/kubernetes/pkg/apimachinery/registered"
  33. "k8s.io/kubernetes/pkg/apis/apps"
  34. appsapi "k8s.io/kubernetes/pkg/apis/apps"
  35. "k8s.io/kubernetes/pkg/apis/autoscaling"
  36. autoscalingapiv1 "k8s.io/kubernetes/pkg/apis/autoscaling/v1"
  37. "k8s.io/kubernetes/pkg/apis/batch"
  38. batchapiv1 "k8s.io/kubernetes/pkg/apis/batch/v1"
  39. batchapiv2alpha1 "k8s.io/kubernetes/pkg/apis/batch/v2alpha1"
  40. "k8s.io/kubernetes/pkg/apis/certificates"
  41. "k8s.io/kubernetes/pkg/apis/extensions"
  42. extensionsapiv1beta1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
  43. "k8s.io/kubernetes/pkg/apis/rbac"
  44. "k8s.io/kubernetes/pkg/apiserver"
  45. "k8s.io/kubernetes/pkg/genericapiserver"
  46. "k8s.io/kubernetes/pkg/kubelet/client"
  47. "k8s.io/kubernetes/pkg/registry/endpoint"
  48. "k8s.io/kubernetes/pkg/registry/generic"
  49. "k8s.io/kubernetes/pkg/registry/namespace"
  50. "k8s.io/kubernetes/pkg/registry/registrytest"
  51. "k8s.io/kubernetes/pkg/registry/thirdpartyresourcedata"
  52. "k8s.io/kubernetes/pkg/runtime"
  53. "k8s.io/kubernetes/pkg/storage"
  54. "k8s.io/kubernetes/pkg/storage/etcd/etcdtest"
  55. etcdtesting "k8s.io/kubernetes/pkg/storage/etcd/testing"
  56. "k8s.io/kubernetes/pkg/storage/storagebackend"
  57. "k8s.io/kubernetes/pkg/util/intstr"
  58. utilnet "k8s.io/kubernetes/pkg/util/net"
  59. "k8s.io/kubernetes/pkg/util/sets"
  60. "github.com/go-openapi/loads"
  61. "github.com/go-openapi/spec"
  62. "github.com/go-openapi/strfmt"
  63. "github.com/go-openapi/validate"
  64. "github.com/stretchr/testify/assert"
  65. "golang.org/x/net/context"
  66. )
  67. // setUp is a convience function for setting up for (most) tests.
  68. func setUp(t *testing.T) (*Master, *etcdtesting.EtcdTestServer, Config, *assert.Assertions) {
  69. server := etcdtesting.NewUnsecuredEtcdTestClientServer(t)
  70. master := &Master{
  71. GenericAPIServer: &genericapiserver.GenericAPIServer{},
  72. }
  73. config := Config{
  74. Config: &genericapiserver.Config{},
  75. }
  76. storageConfig := storagebackend.Config{
  77. Prefix: etcdtest.PathPrefix(),
  78. CAFile: server.CAFile,
  79. KeyFile: server.KeyFile,
  80. CertFile: server.CertFile,
  81. }
  82. for _, url := range server.ClientURLs {
  83. storageConfig.ServerList = append(storageConfig.ServerList, url.String())
  84. }
  85. resourceEncoding := genericapiserver.NewDefaultResourceEncodingConfig()
  86. resourceEncoding.SetVersionEncoding(api.GroupName, *testapi.Default.GroupVersion(), unversioned.GroupVersion{Group: api.GroupName, Version: runtime.APIVersionInternal})
  87. resourceEncoding.SetVersionEncoding(autoscaling.GroupName, *testapi.Autoscaling.GroupVersion(), unversioned.GroupVersion{Group: autoscaling.GroupName, Version: runtime.APIVersionInternal})
  88. resourceEncoding.SetVersionEncoding(batch.GroupName, *testapi.Batch.GroupVersion(), unversioned.GroupVersion{Group: batch.GroupName, Version: runtime.APIVersionInternal})
  89. resourceEncoding.SetVersionEncoding(apps.GroupName, *testapi.Apps.GroupVersion(), unversioned.GroupVersion{Group: apps.GroupName, Version: runtime.APIVersionInternal})
  90. resourceEncoding.SetVersionEncoding(extensions.GroupName, *testapi.Extensions.GroupVersion(), unversioned.GroupVersion{Group: extensions.GroupName, Version: runtime.APIVersionInternal})
  91. resourceEncoding.SetVersionEncoding(rbac.GroupName, *testapi.Rbac.GroupVersion(), unversioned.GroupVersion{Group: rbac.GroupName, Version: runtime.APIVersionInternal})
  92. resourceEncoding.SetVersionEncoding(certificates.GroupName, *testapi.Certificates.GroupVersion(), unversioned.GroupVersion{Group: certificates.GroupName, Version: runtime.APIVersionInternal})
  93. storageFactory := genericapiserver.NewDefaultStorageFactory(storageConfig, testapi.StorageMediaType(), api.Codecs, resourceEncoding, DefaultAPIResourceConfigSource())
  94. config.StorageFactory = storageFactory
  95. config.APIResourceConfigSource = DefaultAPIResourceConfigSource()
  96. config.PublicAddress = net.ParseIP("192.168.10.4")
  97. config.Serializer = api.Codecs
  98. config.KubeletClient = client.FakeKubeletClient{}
  99. config.APIPrefix = "/api"
  100. config.APIGroupPrefix = "/apis"
  101. config.APIResourceConfigSource = DefaultAPIResourceConfigSource()
  102. config.ProxyDialer = func(network, addr string) (net.Conn, error) { return nil, nil }
  103. config.ProxyTLSClientConfig = &tls.Config{}
  104. // TODO: this is kind of hacky. The trouble is that the sync loop
  105. // runs in a go-routine and there is no way to validate in the test
  106. // that the sync routine has actually run. The right answer here
  107. // is probably to add some sort of callback that we can register
  108. // to validate that it's actually been run, but for now we don't
  109. // run the sync routine and register types manually.
  110. config.disableThirdPartyControllerForTesting = true
  111. master.nodeRegistry = registrytest.NewNodeRegistry([]string{"node1", "node2"}, api.NodeResources{})
  112. return master, server, config, assert.New(t)
  113. }
  114. func newMaster(t *testing.T) (*Master, *etcdtesting.EtcdTestServer, Config, *assert.Assertions) {
  115. _, etcdserver, config, assert := setUp(t)
  116. master, err := New(&config)
  117. if err != nil {
  118. t.Fatalf("Error in bringing up the master: %v", err)
  119. }
  120. return master, etcdserver, config, assert
  121. }
  122. // limitedAPIResourceConfigSource only enables the core group, the extensions group, the batch group, and the autoscaling group.
  123. func limitedAPIResourceConfigSource() *genericapiserver.ResourceConfig {
  124. ret := genericapiserver.NewResourceConfig()
  125. ret.EnableVersions(
  126. apiv1.SchemeGroupVersion,
  127. extensionsapiv1beta1.SchemeGroupVersion,
  128. batchapiv1.SchemeGroupVersion,
  129. batchapiv2alpha1.SchemeGroupVersion,
  130. appsapi.SchemeGroupVersion,
  131. autoscalingapiv1.SchemeGroupVersion,
  132. )
  133. return ret
  134. }
  135. // newLimitedMaster only enables the core group, the extensions group, the batch group, and the autoscaling group.
  136. func newLimitedMaster(t *testing.T) (*Master, *etcdtesting.EtcdTestServer, Config, *assert.Assertions) {
  137. _, etcdserver, config, assert := setUp(t)
  138. config.APIResourceConfigSource = limitedAPIResourceConfigSource()
  139. master, err := New(&config)
  140. if err != nil {
  141. t.Fatalf("Error in bringing up the master: %v", err)
  142. }
  143. return master, etcdserver, config, assert
  144. }
  145. // TestNew verifies that the New function returns a Master
  146. // using the configuration properly.
  147. func TestNew(t *testing.T) {
  148. master, etcdserver, config, assert := newMaster(t)
  149. defer etcdserver.Terminate(t)
  150. // Verify many of the variables match their config counterparts
  151. assert.Equal(master.enableCoreControllers, config.EnableCoreControllers)
  152. assert.Equal(master.tunneler, config.Tunneler)
  153. assert.Equal(master.APIPrefix, config.APIPrefix)
  154. assert.Equal(master.APIGroupPrefix, config.APIGroupPrefix)
  155. assert.Equal(master.RequestContextMapper, config.RequestContextMapper)
  156. assert.Equal(master.MasterCount, config.MasterCount)
  157. assert.Equal(master.ClusterIP, config.PublicAddress)
  158. assert.Equal(master.PublicReadWritePort, config.ReadWritePort)
  159. assert.Equal(master.ServiceReadWriteIP, config.ServiceReadWriteIP)
  160. // These functions should point to the same memory location
  161. masterDialer, _ := utilnet.Dialer(master.ProxyTransport)
  162. masterDialerFunc := fmt.Sprintf("%p", masterDialer)
  163. configDialerFunc := fmt.Sprintf("%p", config.ProxyDialer)
  164. assert.Equal(masterDialerFunc, configDialerFunc)
  165. assert.Equal(master.ProxyTransport.(*http.Transport).TLSClientConfig, config.ProxyTLSClientConfig)
  166. }
  167. // TestNamespaceSubresources ensures the namespace subresource parsing in apiserver/handlers.go doesn't drift
  168. func TestNamespaceSubresources(t *testing.T) {
  169. master, etcdserver, _, _ := newMaster(t)
  170. defer etcdserver.Terminate(t)
  171. expectedSubresources := apiserver.NamespaceSubResourcesForTest
  172. foundSubresources := sets.NewString()
  173. for k := range master.v1ResourcesStorage {
  174. parts := strings.Split(k, "/")
  175. if len(parts) == 2 && parts[0] == "namespaces" {
  176. foundSubresources.Insert(parts[1])
  177. }
  178. }
  179. if !reflect.DeepEqual(expectedSubresources.List(), foundSubresources.List()) {
  180. t.Errorf("Expected namespace subresources %#v, got %#v. Update apiserver/handlers.go#namespaceSubresources", expectedSubresources.List(), foundSubresources.List())
  181. }
  182. }
  183. // TestGetServersToValidate verifies the unexported getServersToValidate function
  184. func TestGetServersToValidate(t *testing.T) {
  185. master, etcdserver, config, assert := setUp(t)
  186. defer etcdserver.Terminate(t)
  187. servers := master.getServersToValidate(&config)
  188. // Expected servers to validate: scheduler, controller-manager and etcd.
  189. assert.Equal(3, len(servers), "unexpected server list: %#v", servers)
  190. for _, server := range []string{"scheduler", "controller-manager", "etcd-0"} {
  191. if _, ok := servers[server]; !ok {
  192. t.Errorf("server list missing: %s", server)
  193. }
  194. }
  195. }
  196. // TestFindExternalAddress verifies both pass and fail cases for the unexported
  197. // findExternalAddress function
  198. func TestFindExternalAddress(t *testing.T) {
  199. assert := assert.New(t)
  200. expectedIP := "172.0.0.1"
  201. nodes := []*api.Node{new(api.Node), new(api.Node), new(api.Node)}
  202. nodes[0].Status.Addresses = []api.NodeAddress{{Type: "ExternalIP", Address: expectedIP}}
  203. nodes[1].Status.Addresses = []api.NodeAddress{{Type: "LegacyHostIP", Address: expectedIP}}
  204. nodes[2].Status.Addresses = []api.NodeAddress{{Type: "ExternalIP", Address: expectedIP}, {Type: "LegacyHostIP", Address: "172.0.0.2"}}
  205. // Pass Case
  206. for _, node := range nodes {
  207. ip, err := findExternalAddress(node)
  208. assert.NoError(err, "error getting node external address")
  209. assert.Equal(expectedIP, ip, "expected ip to be %s, but was %s", expectedIP, ip)
  210. }
  211. // Fail case
  212. _, err := findExternalAddress(new(api.Node))
  213. assert.Error(err, "expected findExternalAddress to fail on a node with missing ip information")
  214. }
  215. type fakeEndpointReconciler struct{}
  216. func (*fakeEndpointReconciler) ReconcileEndpoints(serviceName string, ip net.IP, endpointPorts []api.EndpointPort, reconcilePorts bool) error {
  217. return nil
  218. }
  219. // TestNewBootstrapController verifies master fields are properly copied into controller
  220. func TestNewBootstrapController(t *testing.T) {
  221. // Tests a subset of inputs to ensure they are set properly in the controller
  222. master, etcdserver, _, assert := setUp(t)
  223. defer etcdserver.Terminate(t)
  224. portRange := utilnet.PortRange{Base: 10, Size: 10}
  225. master.namespaceRegistry = namespace.NewRegistry(nil)
  226. master.serviceRegistry = registrytest.NewServiceRegistry()
  227. master.endpointRegistry = endpoint.NewRegistry(nil)
  228. master.ServiceNodePortRange = portRange
  229. master.MasterCount = 1
  230. master.ServiceReadWritePort = 1000
  231. master.PublicReadWritePort = 1010
  232. // test with an empty EndpointReconcilerConfig to ensure the defaults are applied
  233. controller := master.NewBootstrapController(EndpointReconcilerConfig{})
  234. assert.Equal(controller.NamespaceRegistry, master.namespaceRegistry)
  235. assert.Equal(controller.EndpointReconciler, NewMasterCountEndpointReconciler(master.MasterCount, master.endpointRegistry))
  236. assert.Equal(controller.EndpointInterval, DefaultEndpointReconcilerInterval)
  237. assert.Equal(controller.ServiceRegistry, master.serviceRegistry)
  238. assert.Equal(controller.ServiceNodePortRange, portRange)
  239. assert.Equal(controller.ServicePort, master.ServiceReadWritePort)
  240. assert.Equal(controller.PublicServicePort, master.PublicReadWritePort)
  241. // test with a filled-in EndpointReconcilerConfig to make sure its values are used
  242. controller = master.NewBootstrapController(EndpointReconcilerConfig{
  243. Reconciler: &fakeEndpointReconciler{},
  244. Interval: 5 * time.Second,
  245. })
  246. assert.Equal(controller.EndpointReconciler, &fakeEndpointReconciler{})
  247. assert.Equal(controller.EndpointInterval, 5*time.Second)
  248. }
  249. // TestControllerServicePorts verifies master extraServicePorts are
  250. // correctly copied into controller
  251. func TestControllerServicePorts(t *testing.T) {
  252. master, etcdserver, _, assert := setUp(t)
  253. defer etcdserver.Terminate(t)
  254. master.namespaceRegistry = namespace.NewRegistry(nil)
  255. master.serviceRegistry = registrytest.NewServiceRegistry()
  256. master.endpointRegistry = endpoint.NewRegistry(nil)
  257. master.ExtraServicePorts = []api.ServicePort{
  258. {
  259. Name: "additional-port-1",
  260. Port: 1000,
  261. Protocol: api.ProtocolTCP,
  262. TargetPort: intstr.FromInt(1000),
  263. },
  264. {
  265. Name: "additional-port-2",
  266. Port: 1010,
  267. Protocol: api.ProtocolTCP,
  268. TargetPort: intstr.FromInt(1010),
  269. },
  270. }
  271. controller := master.NewBootstrapController(EndpointReconcilerConfig{})
  272. assert.Equal(int32(1000), controller.ExtraServicePorts[0].Port)
  273. assert.Equal(int32(1010), controller.ExtraServicePorts[1].Port)
  274. }
  275. // TestGetNodeAddresses verifies that proper results are returned
  276. // when requesting node addresses.
  277. func TestGetNodeAddresses(t *testing.T) {
  278. master, etcdserver, _, assert := setUp(t)
  279. defer etcdserver.Terminate(t)
  280. // Fail case (no addresses associated with nodes)
  281. nodes, _ := master.nodeRegistry.ListNodes(api.NewDefaultContext(), nil)
  282. addrs, err := master.getNodeAddresses()
  283. assert.Error(err, "getNodeAddresses should have caused an error as there are no addresses.")
  284. assert.Equal([]string(nil), addrs)
  285. // Pass case with External type IP
  286. nodes, _ = master.nodeRegistry.ListNodes(api.NewDefaultContext(), nil)
  287. for index := range nodes.Items {
  288. nodes.Items[index].Status.Addresses = []api.NodeAddress{{Type: api.NodeExternalIP, Address: "127.0.0.1"}}
  289. }
  290. addrs, err = master.getNodeAddresses()
  291. assert.NoError(err, "getNodeAddresses should not have returned an error.")
  292. assert.Equal([]string{"127.0.0.1", "127.0.0.1"}, addrs)
  293. // Pass case with LegacyHost type IP
  294. nodes, _ = master.nodeRegistry.ListNodes(api.NewDefaultContext(), nil)
  295. for index := range nodes.Items {
  296. nodes.Items[index].Status.Addresses = []api.NodeAddress{{Type: api.NodeLegacyHostIP, Address: "127.0.0.2"}}
  297. }
  298. addrs, err = master.getNodeAddresses()
  299. assert.NoError(err, "getNodeAddresses failback should not have returned an error.")
  300. assert.Equal([]string{"127.0.0.2", "127.0.0.2"}, addrs)
  301. }
  302. // Because we need to be backwards compatible with release 1.1, at endpoints
  303. // that exist in release 1.1, the responses should have empty APIVersion.
  304. func TestAPIVersionOfDiscoveryEndpoints(t *testing.T) {
  305. master, etcdserver, _, assert := newMaster(t)
  306. defer etcdserver.Terminate(t)
  307. server := httptest.NewServer(master.HandlerContainer.ServeMux)
  308. // /api exists in release-1.1
  309. resp, err := http.Get(server.URL + "/api")
  310. if err != nil {
  311. t.Errorf("unexpected error: %v", err)
  312. }
  313. apiVersions := unversioned.APIVersions{}
  314. assert.NoError(decodeResponse(resp, &apiVersions))
  315. assert.Equal(apiVersions.APIVersion, "")
  316. // /api/v1 exists in release-1.1
  317. resp, err = http.Get(server.URL + "/api/v1")
  318. if err != nil {
  319. t.Errorf("unexpected error: %v", err)
  320. }
  321. resourceList := unversioned.APIResourceList{}
  322. assert.NoError(decodeResponse(resp, &resourceList))
  323. assert.Equal(resourceList.APIVersion, "")
  324. // /apis exists in release-1.1
  325. resp, err = http.Get(server.URL + "/apis")
  326. if err != nil {
  327. t.Errorf("unexpected error: %v", err)
  328. }
  329. groupList := unversioned.APIGroupList{}
  330. assert.NoError(decodeResponse(resp, &groupList))
  331. assert.Equal(groupList.APIVersion, "")
  332. // /apis/extensions exists in release-1.1
  333. resp, err = http.Get(server.URL + "/apis/extensions")
  334. if err != nil {
  335. t.Errorf("unexpected error: %v", err)
  336. }
  337. group := unversioned.APIGroup{}
  338. assert.NoError(decodeResponse(resp, &group))
  339. assert.Equal(group.APIVersion, "")
  340. // /apis/extensions/v1beta1 exists in release-1.1
  341. resp, err = http.Get(server.URL + "/apis/extensions/v1beta1")
  342. if err != nil {
  343. t.Errorf("unexpected error: %v", err)
  344. }
  345. resourceList = unversioned.APIResourceList{}
  346. assert.NoError(decodeResponse(resp, &resourceList))
  347. assert.Equal(resourceList.APIVersion, "")
  348. // /apis/autoscaling doesn't exist in release-1.1, so the APIVersion field
  349. // should be non-empty in the results returned by the server.
  350. resp, err = http.Get(server.URL + "/apis/autoscaling")
  351. if err != nil {
  352. t.Errorf("unexpected error: %v", err)
  353. }
  354. group = unversioned.APIGroup{}
  355. assert.NoError(decodeResponse(resp, &group))
  356. assert.Equal(group.APIVersion, "v1")
  357. // apis/autoscaling/v1 doesn't exist in release-1.1, so the APIVersion field
  358. // should be non-empty in the results returned by the server.
  359. resp, err = http.Get(server.URL + "/apis/autoscaling/v1")
  360. if err != nil {
  361. t.Errorf("unexpected error: %v", err)
  362. }
  363. resourceList = unversioned.APIResourceList{}
  364. assert.NoError(decodeResponse(resp, &resourceList))
  365. assert.Equal(resourceList.APIVersion, "v1")
  366. }
  367. func TestDiscoveryAtAPIS(t *testing.T) {
  368. master, etcdserver, _, assert := newLimitedMaster(t)
  369. defer etcdserver.Terminate(t)
  370. server := httptest.NewServer(master.HandlerContainer.ServeMux)
  371. resp, err := http.Get(server.URL + "/apis")
  372. if !assert.NoError(err) {
  373. t.Errorf("unexpected error: %v", err)
  374. }
  375. assert.Equal(http.StatusOK, resp.StatusCode)
  376. groupList := unversioned.APIGroupList{}
  377. assert.NoError(decodeResponse(resp, &groupList))
  378. if err != nil {
  379. t.Fatalf("unexpected error: %v", err)
  380. }
  381. expectGroupNames := sets.NewString(autoscaling.GroupName, batch.GroupName, apps.GroupName, extensions.GroupName)
  382. expectVersions := map[string][]unversioned.GroupVersionForDiscovery{
  383. autoscaling.GroupName: {
  384. {
  385. GroupVersion: testapi.Autoscaling.GroupVersion().String(),
  386. Version: testapi.Autoscaling.GroupVersion().Version,
  387. },
  388. },
  389. // batch is using its pkg/apis/batch/ types here since during installation
  390. // both versions get installed and testapi.go currently does not support
  391. // multi-versioned clients
  392. batch.GroupName: {
  393. {
  394. GroupVersion: batchapiv1.SchemeGroupVersion.String(),
  395. Version: batchapiv1.SchemeGroupVersion.Version,
  396. },
  397. {
  398. GroupVersion: batchapiv2alpha1.SchemeGroupVersion.String(),
  399. Version: batchapiv2alpha1.SchemeGroupVersion.Version,
  400. },
  401. },
  402. apps.GroupName: {
  403. {
  404. GroupVersion: testapi.Apps.GroupVersion().String(),
  405. Version: testapi.Apps.GroupVersion().Version,
  406. },
  407. },
  408. extensions.GroupName: {
  409. {
  410. GroupVersion: testapi.Extensions.GroupVersion().String(),
  411. Version: testapi.Extensions.GroupVersion().Version,
  412. },
  413. },
  414. }
  415. expectPreferredVersion := map[string]unversioned.GroupVersionForDiscovery{
  416. autoscaling.GroupName: {
  417. GroupVersion: registered.GroupOrDie(autoscaling.GroupName).GroupVersion.String(),
  418. Version: registered.GroupOrDie(autoscaling.GroupName).GroupVersion.Version,
  419. },
  420. batch.GroupName: {
  421. GroupVersion: registered.GroupOrDie(batch.GroupName).GroupVersion.String(),
  422. Version: registered.GroupOrDie(batch.GroupName).GroupVersion.Version,
  423. },
  424. apps.GroupName: {
  425. GroupVersion: registered.GroupOrDie(apps.GroupName).GroupVersion.String(),
  426. Version: registered.GroupOrDie(apps.GroupName).GroupVersion.Version,
  427. },
  428. extensions.GroupName: {
  429. GroupVersion: registered.GroupOrDie(extensions.GroupName).GroupVersion.String(),
  430. Version: registered.GroupOrDie(extensions.GroupName).GroupVersion.Version,
  431. },
  432. }
  433. assert.Equal(4, len(groupList.Groups))
  434. for _, group := range groupList.Groups {
  435. if !expectGroupNames.Has(group.Name) {
  436. t.Errorf("got unexpected group %s", group.Name)
  437. }
  438. assert.Equal(expectVersions[group.Name], group.Versions)
  439. assert.Equal(expectPreferredVersion[group.Name], group.PreferredVersion)
  440. }
  441. thirdPartyGV := unversioned.GroupVersionForDiscovery{GroupVersion: "company.com/v1", Version: "v1"}
  442. master.addThirdPartyResourceStorage("/apis/company.com/v1", "foos", nil,
  443. unversioned.APIGroup{
  444. Name: "company.com",
  445. Versions: []unversioned.GroupVersionForDiscovery{thirdPartyGV},
  446. PreferredVersion: thirdPartyGV,
  447. })
  448. resp, err = http.Get(server.URL + "/apis")
  449. if !assert.NoError(err) {
  450. t.Errorf("unexpected error: %v", err)
  451. }
  452. assert.Equal(http.StatusOK, resp.StatusCode)
  453. assert.NoError(decodeResponse(resp, &groupList))
  454. if err != nil {
  455. t.Fatalf("unexpected error: %v", err)
  456. }
  457. assert.Equal(5, len(groupList.Groups))
  458. expectGroupNames.Insert("company.com")
  459. expectVersions["company.com"] = []unversioned.GroupVersionForDiscovery{thirdPartyGV}
  460. expectPreferredVersion["company.com"] = thirdPartyGV
  461. for _, group := range groupList.Groups {
  462. if !expectGroupNames.Has(group.Name) {
  463. t.Errorf("got unexpected group %s", group.Name)
  464. }
  465. assert.Equal(expectVersions[group.Name], group.Versions)
  466. assert.Equal(expectPreferredVersion[group.Name], group.PreferredVersion)
  467. }
  468. }
  469. var versionsToTest = []string{"v1", "v3"}
  470. type Foo struct {
  471. unversioned.TypeMeta `json:",inline"`
  472. api.ObjectMeta `json:"metadata,omitempty" description:"standard object metadata"`
  473. SomeField string `json:"someField"`
  474. OtherField int `json:"otherField"`
  475. }
  476. type FooList struct {
  477. unversioned.TypeMeta `json:",inline"`
  478. unversioned.ListMeta `json:"metadata,omitempty" description:"standard list metadata; see http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata"`
  479. Items []Foo `json:"items"`
  480. }
  481. func initThirdParty(t *testing.T, version, name string) (*Master, *etcdtesting.EtcdTestServer, *httptest.Server, *assert.Assertions) {
  482. return initThirdPartyMultiple(t, []string{version}, []string{name})
  483. }
  484. func initThirdPartyMultiple(t *testing.T, versions, names []string) (*Master, *etcdtesting.EtcdTestServer, *httptest.Server, *assert.Assertions) {
  485. master, etcdserver, _, assert := newMaster(t)
  486. _, master.ServiceClusterIPRange, _ = net.ParseCIDR("10.0.0.0/24")
  487. for ix := range names {
  488. api := &extensions.ThirdPartyResource{
  489. ObjectMeta: api.ObjectMeta{
  490. Name: names[ix],
  491. },
  492. Versions: []extensions.APIVersion{
  493. {
  494. Name: versions[ix],
  495. },
  496. },
  497. }
  498. hasRsrc, err := master.HasThirdPartyResource(api)
  499. if err != nil {
  500. t.Errorf("Unexpected error: %v", err)
  501. }
  502. if !hasRsrc {
  503. err := master.InstallThirdPartyResource(api)
  504. if !assert.NoError(err) {
  505. t.Errorf("Failed to install API: %v", err)
  506. t.FailNow()
  507. }
  508. } else {
  509. t.Errorf("Expected %s: %v not to be present!", names[ix], api)
  510. }
  511. }
  512. server := httptest.NewServer(master.HandlerContainer.ServeMux)
  513. return master, etcdserver, server, assert
  514. }
  515. func TestInstallMultipleAPIs(t *testing.T) {
  516. names := []string{"foo.company.com", "bar.company.com"}
  517. versions := []string{"v1", "v1"}
  518. _, etcdserver, server, assert := initThirdPartyMultiple(t, versions, names)
  519. defer server.Close()
  520. defer etcdserver.Terminate(t)
  521. for ix := range names {
  522. kind, group, err := thirdpartyresourcedata.ExtractApiGroupAndKind(
  523. &extensions.ThirdPartyResource{ObjectMeta: api.ObjectMeta{Name: names[ix]}})
  524. assert.NoError(err, "Failed to extract group & kind")
  525. plural, _ := meta.KindToResource(unversioned.GroupVersionKind{
  526. Group: group,
  527. Version: versions[ix],
  528. Kind: kind,
  529. })
  530. resp, err := http.Get(
  531. fmt.Sprintf("%s/apis/%s/%s/namespaces/default/%s", server.URL, group, versions[ix], plural.Resource))
  532. if !assert.NoError(err, "Failed to do HTTP GET") {
  533. return
  534. }
  535. defer resp.Body.Close()
  536. assert.Equal(http.StatusOK, resp.StatusCode)
  537. data, err := ioutil.ReadAll(resp.Body)
  538. assert.NoError(err)
  539. obj := map[string]interface{}{}
  540. if err = json.Unmarshal(data, &obj); err != nil {
  541. assert.NoError(err, fmt.Sprintf("unexpected error: %v", err))
  542. }
  543. kindOut, found := obj["kind"]
  544. if !found {
  545. t.Errorf("Missing 'kind' in %v", obj)
  546. }
  547. assert.Equal(kindOut, kind+"List")
  548. }
  549. }
  550. func TestInstallThirdPartyAPIList(t *testing.T) {
  551. for _, version := range versionsToTest {
  552. testInstallThirdPartyAPIListVersion(t, version)
  553. }
  554. }
  555. func testInstallThirdPartyAPIListVersion(t *testing.T, version string) {
  556. tests := []struct {
  557. items []Foo
  558. name string
  559. test string
  560. }{
  561. {
  562. name: "foo.company.com",
  563. test: "null",
  564. },
  565. {
  566. items: []Foo{},
  567. name: "foo.company.com",
  568. test: "empty",
  569. },
  570. {
  571. items: []Foo{},
  572. name: "policy.company.com",
  573. test: "plurals",
  574. },
  575. {
  576. items: []Foo{
  577. {
  578. ObjectMeta: api.ObjectMeta{
  579. Name: "test",
  580. },
  581. TypeMeta: unversioned.TypeMeta{
  582. Kind: "Foo",
  583. APIVersion: version,
  584. },
  585. SomeField: "test field",
  586. OtherField: 10,
  587. },
  588. {
  589. ObjectMeta: api.ObjectMeta{
  590. Name: "bar",
  591. },
  592. TypeMeta: unversioned.TypeMeta{
  593. Kind: "Foo",
  594. APIVersion: version,
  595. },
  596. SomeField: "test field another",
  597. OtherField: 20,
  598. },
  599. },
  600. name: "foo.company.com",
  601. test: "real list",
  602. },
  603. }
  604. for _, test := range tests {
  605. func() {
  606. master, etcdserver, server, assert := initThirdParty(t, version, test.name)
  607. defer server.Close()
  608. defer etcdserver.Terminate(t)
  609. kind, group, err := thirdpartyresourcedata.ExtractApiGroupAndKind(
  610. &extensions.ThirdPartyResource{ObjectMeta: api.ObjectMeta{Name: test.name}})
  611. assert.NoError(err, test.test)
  612. plural, _ := meta.KindToResource(unversioned.GroupVersionKind{
  613. Group: group,
  614. Version: version,
  615. Kind: kind,
  616. })
  617. if test.items != nil {
  618. s, destroyFunc := generic.NewRawStorage(master.thirdPartyStorageConfig)
  619. defer destroyFunc()
  620. err := createThirdPartyList(
  621. s,
  622. fmt.Sprintf("/ThirdPartyResourceData/%s/%s/default", group, plural.Resource),
  623. test.items)
  624. if !assert.NoError(err, test.test) {
  625. return
  626. }
  627. }
  628. resp, err := http.Get(
  629. fmt.Sprintf("%s/apis/%s/%s/namespaces/default/%s", server.URL, group, version, plural.Resource))
  630. if !assert.NoError(err, test.test) {
  631. return
  632. }
  633. defer resp.Body.Close()
  634. assert.Equal(http.StatusOK, resp.StatusCode, test.test)
  635. data, err := ioutil.ReadAll(resp.Body)
  636. assert.NoError(err, test.test)
  637. list := FooList{}
  638. if err = json.Unmarshal(data, &list); err != nil {
  639. assert.NoError(err, "unexpected error: %v %s", err, test.test)
  640. }
  641. if test.items == nil {
  642. if len(list.Items) != 0 {
  643. assert.NoError(err, "expected no items, saw: %v %s", err, list.Items, test.test)
  644. }
  645. return
  646. }
  647. if len(list.Items) != len(test.items) {
  648. t.Fatalf("(%s) unexpected length: %d vs %d", test.name, len(list.Items), len(test.items))
  649. }
  650. // The order of elements in LIST is not guaranteed.
  651. mapping := make(map[string]int)
  652. for ix := range test.items {
  653. mapping[test.items[ix].Name] = ix
  654. }
  655. for ix := range list.Items {
  656. // Copy things that are set dynamically on the server
  657. expectedObj := test.items[mapping[list.Items[ix].Name]]
  658. expectedObj.SelfLink = list.Items[ix].SelfLink
  659. expectedObj.ResourceVersion = list.Items[ix].ResourceVersion
  660. expectedObj.Namespace = list.Items[ix].Namespace
  661. expectedObj.UID = list.Items[ix].UID
  662. expectedObj.CreationTimestamp = list.Items[ix].CreationTimestamp
  663. // We endure the order of items by sorting them (using 'mapping')
  664. // so that this function passes.
  665. if !reflect.DeepEqual(list.Items[ix], expectedObj) {
  666. t.Errorf("(%s) expected:\n%#v\nsaw:\n%#v\n", test.name, expectedObj, list.Items[ix])
  667. }
  668. }
  669. }()
  670. }
  671. }
  672. func encodeToThirdParty(name string, obj interface{}) (runtime.Object, error) {
  673. serial, err := json.Marshal(obj)
  674. if err != nil {
  675. return nil, err
  676. }
  677. thirdPartyData := extensions.ThirdPartyResourceData{
  678. ObjectMeta: api.ObjectMeta{Name: name},
  679. Data: serial,
  680. }
  681. return &thirdPartyData, nil
  682. }
  683. func createThirdPartyObject(s storage.Interface, path, name string, obj interface{}) error {
  684. data, err := encodeToThirdParty(name, obj)
  685. if err != nil {
  686. return err
  687. }
  688. return s.Create(context.TODO(), etcdtest.AddPrefix(path), data, nil, 0)
  689. }
  690. func createThirdPartyList(s storage.Interface, path string, list []Foo) error {
  691. for _, obj := range list {
  692. if err := createThirdPartyObject(s, path+"/"+obj.Name, obj.Name, obj); err != nil {
  693. return err
  694. }
  695. }
  696. return nil
  697. }
  698. func decodeResponse(resp *http.Response, obj interface{}) error {
  699. defer resp.Body.Close()
  700. data, err := ioutil.ReadAll(resp.Body)
  701. if err != nil {
  702. return err
  703. }
  704. if err := json.Unmarshal(data, obj); err != nil {
  705. return err
  706. }
  707. return nil
  708. }
  709. func TestInstallThirdPartyAPIGet(t *testing.T) {
  710. for _, version := range versionsToTest {
  711. testInstallThirdPartyAPIGetVersion(t, version)
  712. }
  713. }
  714. func testInstallThirdPartyAPIGetVersion(t *testing.T, version string) {
  715. master, etcdserver, server, assert := initThirdParty(t, version, "foo.company.com")
  716. defer server.Close()
  717. defer etcdserver.Terminate(t)
  718. expectedObj := Foo{
  719. ObjectMeta: api.ObjectMeta{
  720. Name: "test",
  721. },
  722. TypeMeta: unversioned.TypeMeta{
  723. Kind: "Foo",
  724. APIVersion: version,
  725. },
  726. SomeField: "test field",
  727. OtherField: 10,
  728. }
  729. s, destroyFunc := generic.NewRawStorage(master.thirdPartyStorageConfig)
  730. defer destroyFunc()
  731. if !assert.NoError(createThirdPartyObject(s, "/ThirdPartyResourceData/company.com/foos/default/test", "test", expectedObj)) {
  732. t.FailNow()
  733. return
  734. }
  735. resp, err := http.Get(server.URL + "/apis/company.com/" + version + "/namespaces/default/foos/test")
  736. if !assert.NoError(err) {
  737. return
  738. }
  739. assert.Equal(http.StatusOK, resp.StatusCode)
  740. item := Foo{}
  741. assert.NoError(decodeResponse(resp, &item))
  742. if !assert.False(reflect.DeepEqual(item, expectedObj)) {
  743. t.Errorf("expected objects to not be equal:\n%v\nsaw:\n%v\n", expectedObj, item)
  744. }
  745. // Fill in data that the apiserver injects
  746. expectedObj.SelfLink = item.SelfLink
  747. expectedObj.ResourceVersion = item.ResourceVersion
  748. if !assert.True(reflect.DeepEqual(item, expectedObj)) {
  749. t.Errorf("expected:\n%#v\nsaw:\n%#v\n", expectedObj, item)
  750. }
  751. }
  752. func TestInstallThirdPartyAPIPost(t *testing.T) {
  753. registered.AddThirdPartyAPIGroupVersions(unversioned.GroupVersion{Group: "company.com", Version: "v1"}, unversioned.GroupVersion{Group: "company.com", Version: "v3"})
  754. for _, version := range versionsToTest {
  755. testInstallThirdPartyAPIPostForVersion(t, version)
  756. }
  757. }
  758. func testInstallThirdPartyAPIPostForVersion(t *testing.T, version string) {
  759. master, etcdserver, server, assert := initThirdParty(t, version, "foo.company.com")
  760. defer server.Close()
  761. defer etcdserver.Terminate(t)
  762. inputObj := Foo{
  763. ObjectMeta: api.ObjectMeta{
  764. Name: "test",
  765. },
  766. TypeMeta: unversioned.TypeMeta{
  767. Kind: "Foo",
  768. APIVersion: "company.com/" + version,
  769. },
  770. SomeField: "test field",
  771. OtherField: 10,
  772. }
  773. data, err := json.Marshal(inputObj)
  774. if !assert.NoError(err) {
  775. return
  776. }
  777. resp, err := http.Post(server.URL+"/apis/company.com/"+version+"/namespaces/default/foos", "application/json", bytes.NewBuffer(data))
  778. if !assert.NoError(err) {
  779. t.Fatalf("unexpected error: %v", err)
  780. }
  781. assert.Equal(http.StatusCreated, resp.StatusCode)
  782. item := Foo{}
  783. assert.NoError(decodeResponse(resp, &item))
  784. // fill in fields set by the apiserver
  785. expectedObj := inputObj
  786. expectedObj.SelfLink = item.SelfLink
  787. expectedObj.ResourceVersion = item.ResourceVersion
  788. expectedObj.Namespace = item.Namespace
  789. expectedObj.UID = item.UID
  790. expectedObj.CreationTimestamp = item.CreationTimestamp
  791. if !assert.True(reflect.DeepEqual(item, expectedObj)) {
  792. t.Errorf("expected:\n%v\nsaw:\n%v\n", expectedObj, item)
  793. }
  794. thirdPartyObj := extensions.ThirdPartyResourceData{}
  795. s, destroyFunc := generic.NewRawStorage(master.thirdPartyStorageConfig)
  796. defer destroyFunc()
  797. err = s.Get(context.TODO(), etcdtest.AddPrefix("/ThirdPartyResourceData/company.com/foos/default/test"), &thirdPartyObj, false)
  798. if !assert.NoError(err) {
  799. t.FailNow()
  800. }
  801. item = Foo{}
  802. assert.NoError(json.Unmarshal(thirdPartyObj.Data, &item))
  803. if !assert.True(reflect.DeepEqual(item, inputObj)) {
  804. t.Errorf("expected:\n%v\nsaw:\n%v\n", inputObj, item)
  805. }
  806. }
  807. func TestInstallThirdPartyAPIDelete(t *testing.T) {
  808. for _, version := range versionsToTest {
  809. testInstallThirdPartyAPIDeleteVersion(t, version)
  810. }
  811. }
  812. func testInstallThirdPartyAPIDeleteVersion(t *testing.T, version string) {
  813. master, etcdserver, server, assert := initThirdParty(t, version, "foo.company.com")
  814. defer server.Close()
  815. defer etcdserver.Terminate(t)
  816. expectedObj := Foo{
  817. ObjectMeta: api.ObjectMeta{
  818. Name: "test",
  819. Namespace: "default",
  820. },
  821. TypeMeta: unversioned.TypeMeta{
  822. Kind: "Foo",
  823. },
  824. SomeField: "test field",
  825. OtherField: 10,
  826. }
  827. s, destroyFunc := generic.NewRawStorage(master.thirdPartyStorageConfig)
  828. defer destroyFunc()
  829. if !assert.NoError(createThirdPartyObject(s, "/ThirdPartyResourceData/company.com/foos/default/test", "test", expectedObj)) {
  830. t.FailNow()
  831. return
  832. }
  833. resp, err := http.Get(server.URL + "/apis/company.com/" + version + "/namespaces/default/foos/test")
  834. if !assert.NoError(err) {
  835. return
  836. }
  837. assert.Equal(http.StatusOK, resp.StatusCode)
  838. item := Foo{}
  839. assert.NoError(decodeResponse(resp, &item))
  840. // Fill in fields set by the apiserver
  841. expectedObj.SelfLink = item.SelfLink
  842. expectedObj.ResourceVersion = item.ResourceVersion
  843. expectedObj.Namespace = item.Namespace
  844. if !assert.True(reflect.DeepEqual(item, expectedObj)) {
  845. t.Errorf("expected:\n%v\nsaw:\n%v\n", expectedObj, item)
  846. }
  847. resp, err = httpDelete(server.URL + "/apis/company.com/" + version + "/namespaces/default/foos/test")
  848. if !assert.NoError(err) {
  849. return
  850. }
  851. assert.Equal(http.StatusOK, resp.StatusCode)
  852. resp, err = http.Get(server.URL + "/apis/company.com/" + version + "/namespaces/default/foos/test")
  853. if !assert.NoError(err) {
  854. return
  855. }
  856. assert.Equal(http.StatusNotFound, resp.StatusCode)
  857. expectedDeletedKey := etcdtest.AddPrefix("ThirdPartyResourceData/company.com/foos/default/test")
  858. thirdPartyObj := extensions.ThirdPartyResourceData{}
  859. err = s.Get(context.TODO(), expectedDeletedKey, &thirdPartyObj, false)
  860. if !storage.IsNotFound(err) {
  861. t.Errorf("expected deletion didn't happen: %v", err)
  862. }
  863. }
  864. func httpDelete(url string) (*http.Response, error) {
  865. req, err := http.NewRequest("DELETE", url, nil)
  866. if err != nil {
  867. return nil, err
  868. }
  869. client := &http.Client{}
  870. return client.Do(req)
  871. }
  872. func TestInstallThirdPartyAPIListOptions(t *testing.T) {
  873. for _, version := range versionsToTest {
  874. testInstallThirdPartyAPIListOptionsForVersion(t, version)
  875. }
  876. }
  877. func testInstallThirdPartyAPIListOptionsForVersion(t *testing.T, version string) {
  878. _, etcdserver, server, assert := initThirdParty(t, version, "foo.company.com")
  879. defer server.Close()
  880. defer etcdserver.Terminate(t)
  881. // send a GET request with query parameter
  882. resp, err := httpGetWithRV(server.URL + "/apis/company.com/" + version + "/namespaces/default/foos")
  883. if !assert.NoError(err) {
  884. t.Fatalf("unexpected error: %v", err)
  885. }
  886. assert.Equal(http.StatusOK, resp.StatusCode)
  887. }
  888. func httpGetWithRV(url string) (*http.Response, error) {
  889. req, err := http.NewRequest("GET", url, nil)
  890. if err != nil {
  891. return nil, err
  892. }
  893. q := req.URL.Query()
  894. // resourceversion is part of a ListOptions
  895. q.Add("resourceversion", "0")
  896. req.URL.RawQuery = q.Encode()
  897. client := &http.Client{}
  898. return client.Do(req)
  899. }
  900. func TestInstallThirdPartyResourceRemove(t *testing.T) {
  901. for _, version := range versionsToTest {
  902. testInstallThirdPartyResourceRemove(t, version)
  903. }
  904. }
  905. func testInstallThirdPartyResourceRemove(t *testing.T, version string) {
  906. master, etcdserver, server, assert := initThirdParty(t, version, "foo.company.com")
  907. defer server.Close()
  908. defer etcdserver.Terminate(t)
  909. expectedObj := Foo{
  910. ObjectMeta: api.ObjectMeta{
  911. Name: "test",
  912. },
  913. TypeMeta: unversioned.TypeMeta{
  914. Kind: "Foo",
  915. },
  916. SomeField: "test field",
  917. OtherField: 10,
  918. }
  919. s, destroyFunc := generic.NewRawStorage(master.thirdPartyStorageConfig)
  920. defer destroyFunc()
  921. if !assert.NoError(createThirdPartyObject(s, "/ThirdPartyResourceData/company.com/foos/default/test", "test", expectedObj)) {
  922. t.FailNow()
  923. return
  924. }
  925. secondObj := expectedObj
  926. secondObj.Name = "bar"
  927. if !assert.NoError(createThirdPartyObject(s, "/ThirdPartyResourceData/company.com/foos/default/bar", "bar", secondObj)) {
  928. t.FailNow()
  929. return
  930. }
  931. resp, err := http.Get(server.URL + "/apis/company.com/" + version + "/namespaces/default/foos/test")
  932. if !assert.NoError(err) {
  933. t.FailNow()
  934. return
  935. }
  936. if resp.StatusCode != http.StatusOK {
  937. t.Errorf("unexpected status: %v", resp)
  938. }
  939. item := Foo{}
  940. if err := decodeResponse(resp, &item); err != nil {
  941. t.Errorf("unexpected error: %v", err)
  942. }
  943. // TODO: validate etcd set things here
  944. item.ObjectMeta = expectedObj.ObjectMeta
  945. if !assert.True(reflect.DeepEqual(item, expectedObj)) {
  946. t.Errorf("expected:\n%v\nsaw:\n%v\n", expectedObj, item)
  947. }
  948. path := makeThirdPartyPath("company.com")
  949. master.RemoveThirdPartyResource(path + "/foos")
  950. resp, err = http.Get(server.URL + "/apis/company.com/" + version + "/namespaces/default/foos/test")
  951. if !assert.NoError(err) {
  952. return
  953. }
  954. if resp.StatusCode != http.StatusNotFound {
  955. t.Errorf("unexpected status: %v", resp)
  956. }
  957. expectedDeletedKeys := []string{
  958. etcdtest.AddPrefix("/ThirdPartyResourceData/company.com/foos/default/test"),
  959. etcdtest.AddPrefix("/ThirdPartyResourceData/company.com/foos/default/bar"),
  960. }
  961. for _, key := range expectedDeletedKeys {
  962. thirdPartyObj := extensions.ThirdPartyResourceData{}
  963. s, destroyFunc := generic.NewRawStorage(master.thirdPartyStorageConfig)
  964. err := s.Get(context.TODO(), key, &thirdPartyObj, false)
  965. if !storage.IsNotFound(err) {
  966. t.Errorf("expected deletion didn't happen: %v", err)
  967. }
  968. destroyFunc()
  969. }
  970. installed := master.ListThirdPartyResources()
  971. if len(installed) != 0 {
  972. t.Errorf("Resource(s) still installed: %v", installed)
  973. }
  974. services := master.HandlerContainer.RegisteredWebServices()
  975. for ix := range services {
  976. if strings.HasPrefix(services[ix].RootPath(), "/apis/company.com") {
  977. t.Errorf("Web service still installed at %s: %#v", services[ix].RootPath(), services[ix])
  978. }
  979. }
  980. }
  981. func TestThirdPartyDiscovery(t *testing.T) {
  982. for _, version := range versionsToTest {
  983. testThirdPartyDiscovery(t, version)
  984. }
  985. }
  986. type FakeTunneler struct {
  987. SecondsSinceSyncValue int64
  988. SecondsSinceSSHKeySyncValue int64
  989. }
  990. func (t *FakeTunneler) Run(genericapiserver.AddressFunc) {}
  991. func (t *FakeTunneler) Stop() {}
  992. func (t *FakeTunneler) Dial(net, addr string) (net.Conn, error) { return nil, nil }
  993. func (t *FakeTunneler) SecondsSinceSync() int64 { return t.SecondsSinceSyncValue }
  994. func (t *FakeTunneler) SecondsSinceSSHKeySync() int64 { return t.SecondsSinceSSHKeySyncValue }
  995. // TestIsTunnelSyncHealthy verifies that the 600 second lag test
  996. // is honored.
  997. func TestIsTunnelSyncHealthy(t *testing.T) {
  998. assert := assert.New(t)
  999. tunneler := &FakeTunneler{}
  1000. master := &Master{
  1001. GenericAPIServer: &genericapiserver.GenericAPIServer{},
  1002. tunneler: tunneler,
  1003. }
  1004. // Pass case: 540 second lag
  1005. tunneler.SecondsSinceSyncValue = 540
  1006. err := master.IsTunnelSyncHealthy(nil)
  1007. assert.NoError(err, "IsTunnelSyncHealthy() should not have returned an error.")
  1008. // Fail case: 720 second lag
  1009. tunneler.SecondsSinceSyncValue = 720
  1010. err = master.IsTunnelSyncHealthy(nil)
  1011. assert.Error(err, "IsTunnelSyncHealthy() should have returned an error.")
  1012. }
  1013. func testThirdPartyDiscovery(t *testing.T, version string) {
  1014. _, etcdserver, server, assert := initThirdParty(t, version, "foo.company.com")
  1015. defer server.Close()
  1016. defer etcdserver.Terminate(t)
  1017. resp, err := http.Get(server.URL + "/apis/company.com/")
  1018. if !assert.NoError(err) {
  1019. return
  1020. }
  1021. assert.Equal(http.StatusOK, resp.StatusCode)
  1022. group := unversioned.APIGroup{}
  1023. assert.NoError(decodeResponse(resp, &group))
  1024. assert.Equal(group.APIVersion, "v1")
  1025. assert.Equal(group.Kind, "APIGroup")
  1026. assert.Equal(group.Name, "company.com")
  1027. expectedVersion := unversioned.GroupVersionForDiscovery{
  1028. GroupVersion: "company.com/" + version,
  1029. Version: version,
  1030. }
  1031. assert.Equal(group.Versions, []unversioned.GroupVersionForDiscovery{expectedVersion})
  1032. assert.Equal(group.PreferredVersion, expectedVersion)
  1033. resp, err = http.Get(server.URL + "/apis/company.com/" + version)
  1034. if !assert.NoError(err) {
  1035. return
  1036. }
  1037. assert.Equal(http.StatusOK, resp.StatusCode)
  1038. resourceList := unversioned.APIResourceList{}
  1039. assert.NoError(decodeResponse(resp, &resourceList))
  1040. assert.Equal(resourceList.APIVersion, "v1")
  1041. assert.Equal(resourceList.Kind, "APIResourceList")
  1042. assert.Equal(resourceList.GroupVersion, "company.com/"+version)
  1043. assert.Equal(resourceList.APIResources, []unversioned.APIResource{
  1044. {
  1045. Name: "foos",
  1046. Namespaced: true,
  1047. Kind: "Foo",
  1048. },
  1049. })
  1050. }
  1051. // TestValidOpenAPISpec verifies that the open api is added
  1052. // at the proper endpoint and the spec is valid.
  1053. func TestValidOpenAPISpec(t *testing.T) {
  1054. _, etcdserver, config, assert := setUp(t)
  1055. defer etcdserver.Terminate(t)
  1056. config.EnableOpenAPISupport = true
  1057. config.OpenAPIInfo = spec.Info{
  1058. InfoProps: spec.InfoProps{
  1059. Title: "Kubernetes",
  1060. Version: "unversioned",
  1061. },
  1062. }
  1063. master, err := New(&config)
  1064. if err != nil {
  1065. t.Fatalf("Error in bringing up the master: %v", err)
  1066. }
  1067. // make sure swagger.json is not registered before calling install api.
  1068. server := httptest.NewServer(master.HandlerContainer.ServeMux)
  1069. resp, err := http.Get(server.URL + "/swagger.json")
  1070. if !assert.NoError(err) {
  1071. t.Errorf("unexpected error: %v", err)
  1072. }
  1073. assert.Equal(http.StatusNotFound, resp.StatusCode)
  1074. master.InstallOpenAPI()
  1075. resp, err = http.Get(server.URL + "/swagger.json")
  1076. if !assert.NoError(err) {
  1077. t.Errorf("unexpected error: %v", err)
  1078. }
  1079. assert.Equal(http.StatusOK, resp.StatusCode)
  1080. // as json schema
  1081. var sch spec.Schema
  1082. if assert.NoError(decodeResponse(resp, &sch)) {
  1083. validator := validate.NewSchemaValidator(spec.MustLoadSwagger20Schema(), nil, "", strfmt.Default)
  1084. res := validator.Validate(&sch)
  1085. assert.NoError(res.AsError())
  1086. }
  1087. // Validate OpenApi spec
  1088. doc, err := loads.Spec(server.URL + "/swagger.json")
  1089. if assert.NoError(err) {
  1090. // TODO(mehdy): This test is timing out on jerkin but passing locally. Enable it after debugging timeout issue.
  1091. _ = validate.NewSpecValidator(doc.Schema(), strfmt.Default)
  1092. // res, _ := validator.Validate(doc)
  1093. // assert.NoError(res.AsError())
  1094. }
  1095. }