servicecontroller_test.go 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. /*
  2. Copyright 2015 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 service
  14. import (
  15. "reflect"
  16. "testing"
  17. "k8s.io/kubernetes/pkg/api"
  18. "k8s.io/kubernetes/pkg/api/testapi"
  19. "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
  20. fakecloud "k8s.io/kubernetes/pkg/cloudprovider/providers/fake"
  21. "k8s.io/kubernetes/pkg/types"
  22. )
  23. const region = "us-central"
  24. func newService(name string, uid types.UID, serviceType api.ServiceType) *api.Service {
  25. return &api.Service{ObjectMeta: api.ObjectMeta{Name: name, Namespace: "namespace", UID: uid, SelfLink: testapi.Default.SelfLink("services", name)}, Spec: api.ServiceSpec{Type: serviceType}}
  26. }
  27. func TestCreateExternalLoadBalancer(t *testing.T) {
  28. table := []struct {
  29. service *api.Service
  30. expectErr bool
  31. expectCreateAttempt bool
  32. }{
  33. {
  34. service: &api.Service{
  35. ObjectMeta: api.ObjectMeta{
  36. Name: "no-external-balancer",
  37. Namespace: "default",
  38. },
  39. Spec: api.ServiceSpec{
  40. Type: api.ServiceTypeClusterIP,
  41. },
  42. },
  43. expectErr: false,
  44. expectCreateAttempt: false,
  45. },
  46. {
  47. service: &api.Service{
  48. ObjectMeta: api.ObjectMeta{
  49. Name: "udp-service",
  50. Namespace: "default",
  51. SelfLink: testapi.Default.SelfLink("services", "udp-service"),
  52. },
  53. Spec: api.ServiceSpec{
  54. Ports: []api.ServicePort{{
  55. Port: 80,
  56. Protocol: api.ProtocolUDP,
  57. }},
  58. Type: api.ServiceTypeLoadBalancer,
  59. },
  60. },
  61. expectErr: false,
  62. expectCreateAttempt: true,
  63. },
  64. {
  65. service: &api.Service{
  66. ObjectMeta: api.ObjectMeta{
  67. Name: "basic-service1",
  68. Namespace: "default",
  69. SelfLink: testapi.Default.SelfLink("services", "basic-service1"),
  70. },
  71. Spec: api.ServiceSpec{
  72. Ports: []api.ServicePort{{
  73. Port: 80,
  74. Protocol: api.ProtocolTCP,
  75. }},
  76. Type: api.ServiceTypeLoadBalancer,
  77. },
  78. },
  79. expectErr: false,
  80. expectCreateAttempt: true,
  81. },
  82. }
  83. for _, item := range table {
  84. cloud := &fakecloud.FakeCloud{}
  85. cloud.Region = region
  86. client := &fake.Clientset{}
  87. controller, _ := New(cloud, client, "test-cluster")
  88. controller.init()
  89. cloud.Calls = nil // ignore any cloud calls made in init()
  90. client.ClearActions() // ignore any client calls made in init()
  91. err, _ := controller.createLoadBalancerIfNeeded("foo/bar", item.service)
  92. if !item.expectErr && err != nil {
  93. t.Errorf("unexpected error: %v", err)
  94. } else if item.expectErr && err == nil {
  95. t.Errorf("expected error creating %v, got nil", item.service)
  96. }
  97. actions := client.Actions()
  98. if !item.expectCreateAttempt {
  99. if len(cloud.Calls) > 0 {
  100. t.Errorf("unexpected cloud provider calls: %v", cloud.Calls)
  101. }
  102. if len(actions) > 0 {
  103. t.Errorf("unexpected client actions: %v", actions)
  104. }
  105. } else {
  106. var balancer *fakecloud.FakeBalancer
  107. for k := range cloud.Balancers {
  108. if balancer == nil {
  109. b := cloud.Balancers[k]
  110. balancer = &b
  111. } else {
  112. t.Errorf("expected one load balancer to be created, got %v", cloud.Balancers)
  113. break
  114. }
  115. }
  116. if balancer == nil {
  117. t.Errorf("expected one load balancer to be created, got none")
  118. } else if balancer.Name != controller.loadBalancerName(item.service) ||
  119. balancer.Region != region ||
  120. balancer.Ports[0].Port != item.service.Spec.Ports[0].Port {
  121. t.Errorf("created load balancer has incorrect parameters: %v", balancer)
  122. }
  123. actionFound := false
  124. for _, action := range actions {
  125. if action.GetVerb() == "update" && action.GetResource().Resource == "services" {
  126. actionFound = true
  127. }
  128. }
  129. if !actionFound {
  130. t.Errorf("expected updated service to be sent to client, got these actions instead: %v", actions)
  131. }
  132. }
  133. }
  134. }
  135. // TODO: Finish converting and update comments
  136. func TestUpdateNodesInExternalLoadBalancer(t *testing.T) {
  137. hosts := []string{"node0", "node1", "node73"}
  138. table := []struct {
  139. services []*api.Service
  140. expectedUpdateCalls []fakecloud.FakeUpdateBalancerCall
  141. }{
  142. {
  143. // No services present: no calls should be made.
  144. services: []*api.Service{},
  145. expectedUpdateCalls: nil,
  146. },
  147. {
  148. // Services do not have external load balancers: no calls should be made.
  149. services: []*api.Service{
  150. newService("s0", "111", api.ServiceTypeClusterIP),
  151. newService("s1", "222", api.ServiceTypeNodePort),
  152. },
  153. expectedUpdateCalls: nil,
  154. },
  155. {
  156. // Services does have an external load balancer: one call should be made.
  157. services: []*api.Service{
  158. newService("s0", "333", api.ServiceTypeLoadBalancer),
  159. },
  160. expectedUpdateCalls: []fakecloud.FakeUpdateBalancerCall{
  161. {newService("s0", "333", api.ServiceTypeLoadBalancer), hosts},
  162. },
  163. },
  164. {
  165. // Three services have an external load balancer: three calls.
  166. services: []*api.Service{
  167. newService("s0", "444", api.ServiceTypeLoadBalancer),
  168. newService("s1", "555", api.ServiceTypeLoadBalancer),
  169. newService("s2", "666", api.ServiceTypeLoadBalancer),
  170. },
  171. expectedUpdateCalls: []fakecloud.FakeUpdateBalancerCall{
  172. {newService("s0", "444", api.ServiceTypeLoadBalancer), hosts},
  173. {newService("s1", "555", api.ServiceTypeLoadBalancer), hosts},
  174. {newService("s2", "666", api.ServiceTypeLoadBalancer), hosts},
  175. },
  176. },
  177. {
  178. // Two services have an external load balancer and two don't: two calls.
  179. services: []*api.Service{
  180. newService("s0", "777", api.ServiceTypeNodePort),
  181. newService("s1", "888", api.ServiceTypeLoadBalancer),
  182. newService("s3", "999", api.ServiceTypeLoadBalancer),
  183. newService("s4", "123", api.ServiceTypeClusterIP),
  184. },
  185. expectedUpdateCalls: []fakecloud.FakeUpdateBalancerCall{
  186. {newService("s1", "888", api.ServiceTypeLoadBalancer), hosts},
  187. {newService("s3", "999", api.ServiceTypeLoadBalancer), hosts},
  188. },
  189. },
  190. {
  191. // One service has an external load balancer and one is nil: one call.
  192. services: []*api.Service{
  193. newService("s0", "234", api.ServiceTypeLoadBalancer),
  194. nil,
  195. },
  196. expectedUpdateCalls: []fakecloud.FakeUpdateBalancerCall{
  197. {newService("s0", "234", api.ServiceTypeLoadBalancer), hosts},
  198. },
  199. },
  200. }
  201. for _, item := range table {
  202. cloud := &fakecloud.FakeCloud{}
  203. cloud.Region = region
  204. client := &fake.Clientset{}
  205. controller, _ := New(cloud, client, "test-cluster2")
  206. controller.init()
  207. cloud.Calls = nil // ignore any cloud calls made in init()
  208. var services []*api.Service
  209. for _, service := range item.services {
  210. services = append(services, service)
  211. }
  212. if err := controller.updateLoadBalancerHosts(services, hosts); err != nil {
  213. t.Errorf("unexpected error: %v", err)
  214. }
  215. if !reflect.DeepEqual(item.expectedUpdateCalls, cloud.UpdateCalls) {
  216. t.Errorf("expected update calls mismatch, expected %+v, got %+v", item.expectedUpdateCalls, cloud.UpdateCalls)
  217. }
  218. }
  219. }
  220. func TestHostsFromNodeList(t *testing.T) {
  221. tests := []struct {
  222. nodes *api.NodeList
  223. expectedHosts []string
  224. }{
  225. {
  226. nodes: &api.NodeList{},
  227. expectedHosts: []string{},
  228. },
  229. {
  230. nodes: &api.NodeList{
  231. Items: []api.Node{
  232. {
  233. ObjectMeta: api.ObjectMeta{Name: "foo"},
  234. Status: api.NodeStatus{Phase: api.NodeRunning},
  235. },
  236. {
  237. ObjectMeta: api.ObjectMeta{Name: "bar"},
  238. Status: api.NodeStatus{Phase: api.NodeRunning},
  239. },
  240. },
  241. },
  242. expectedHosts: []string{"foo", "bar"},
  243. },
  244. {
  245. nodes: &api.NodeList{
  246. Items: []api.Node{
  247. {
  248. ObjectMeta: api.ObjectMeta{Name: "foo"},
  249. Status: api.NodeStatus{Phase: api.NodeRunning},
  250. },
  251. {
  252. ObjectMeta: api.ObjectMeta{Name: "bar"},
  253. Status: api.NodeStatus{Phase: api.NodeRunning},
  254. },
  255. {
  256. ObjectMeta: api.ObjectMeta{Name: "unschedulable"},
  257. Spec: api.NodeSpec{Unschedulable: true},
  258. Status: api.NodeStatus{Phase: api.NodeRunning},
  259. },
  260. },
  261. },
  262. expectedHosts: []string{"foo", "bar"},
  263. },
  264. }
  265. for _, test := range tests {
  266. hosts := hostsFromNodeList(test.nodes)
  267. if !reflect.DeepEqual(hosts, test.expectedHosts) {
  268. t.Errorf("expected: %v, saw: %v", test.expectedHosts, hosts)
  269. }
  270. }
  271. }
  272. func TestGetNodeConditionPredicate(t *testing.T) {
  273. tests := []struct {
  274. node api.Node
  275. expectAccept bool
  276. name string
  277. }{
  278. {
  279. node: api.Node{},
  280. expectAccept: false,
  281. name: "empty",
  282. },
  283. {
  284. node: api.Node{
  285. Status: api.NodeStatus{
  286. Conditions: []api.NodeCondition{
  287. {Type: api.NodeReady, Status: api.ConditionTrue},
  288. },
  289. },
  290. },
  291. expectAccept: true,
  292. name: "basic",
  293. },
  294. {
  295. node: api.Node{
  296. Spec: api.NodeSpec{Unschedulable: true},
  297. Status: api.NodeStatus{
  298. Conditions: []api.NodeCondition{
  299. {Type: api.NodeReady, Status: api.ConditionTrue},
  300. },
  301. },
  302. },
  303. expectAccept: false,
  304. name: "unschedulable",
  305. },
  306. }
  307. pred := getNodeConditionPredicate()
  308. for _, test := range tests {
  309. accept := pred(&test.node)
  310. if accept != test.expectAccept {
  311. t.Errorf("Test failed for %s, expected %v, saw %v", test.name, test.expectAccept, accept)
  312. }
  313. }
  314. }
  315. // TODO(a-robinson): Add tests for update/sync/delete.