routecontroller_test.go 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  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 route
  14. import (
  15. "net"
  16. "testing"
  17. "time"
  18. "k8s.io/kubernetes/pkg/api"
  19. "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
  20. "k8s.io/kubernetes/pkg/client/testing/core"
  21. "k8s.io/kubernetes/pkg/cloudprovider"
  22. fakecloud "k8s.io/kubernetes/pkg/cloudprovider/providers/fake"
  23. )
  24. func TestIsResponsibleForRoute(t *testing.T) {
  25. myClusterName := "my-awesome-cluster"
  26. myClusterRoute := "my-awesome-cluster-12345678-90ab-cdef-1234-567890abcdef"
  27. testCases := []struct {
  28. clusterCIDR string
  29. routeName string
  30. routeCIDR string
  31. expectedResponsible bool
  32. }{
  33. // Routes that belong to this cluster
  34. {"10.244.0.0/16", myClusterRoute, "10.244.0.0/24", true},
  35. {"10.244.0.0/16", myClusterRoute, "10.244.10.0/24", true},
  36. {"10.244.0.0/16", myClusterRoute, "10.244.255.0/24", true},
  37. {"10.244.0.0/14", myClusterRoute, "10.244.0.0/24", true},
  38. {"10.244.0.0/14", myClusterRoute, "10.247.255.0/24", true},
  39. // Routes that match our naming/tagging scheme, but are outside our cidr
  40. {"10.244.0.0/16", myClusterRoute, "10.224.0.0/24", false},
  41. {"10.244.0.0/16", myClusterRoute, "10.0.10.0/24", false},
  42. {"10.244.0.0/16", myClusterRoute, "10.255.255.0/24", false},
  43. {"10.244.0.0/14", myClusterRoute, "10.248.0.0/24", false},
  44. {"10.244.0.0/14", myClusterRoute, "10.243.255.0/24", false},
  45. }
  46. for i, testCase := range testCases {
  47. _, cidr, err := net.ParseCIDR(testCase.clusterCIDR)
  48. if err != nil {
  49. t.Errorf("%d. Error in test case: unparsable cidr %q", i, testCase.clusterCIDR)
  50. }
  51. rc := New(nil, nil, myClusterName, cidr)
  52. route := &cloudprovider.Route{
  53. Name: testCase.routeName,
  54. TargetInstance: "doesnt-matter-for-this-test",
  55. DestinationCIDR: testCase.routeCIDR,
  56. }
  57. if resp := rc.isResponsibleForRoute(route); resp != testCase.expectedResponsible {
  58. t.Errorf("%d. isResponsibleForRoute() = %t; want %t", i, resp, testCase.expectedResponsible)
  59. }
  60. }
  61. }
  62. func TestReconcile(t *testing.T) {
  63. cluster := "my-k8s"
  64. node1 := api.Node{ObjectMeta: api.ObjectMeta{Name: "node-1", UID: "01"}, Spec: api.NodeSpec{PodCIDR: "10.120.0.0/24"}}
  65. node2 := api.Node{ObjectMeta: api.ObjectMeta{Name: "node-2", UID: "02"}, Spec: api.NodeSpec{PodCIDR: "10.120.1.0/24"}}
  66. nodeNoCidr := api.Node{ObjectMeta: api.ObjectMeta{Name: "node-2", UID: "02"}, Spec: api.NodeSpec{PodCIDR: ""}}
  67. testCases := []struct {
  68. nodes []api.Node
  69. initialRoutes []*cloudprovider.Route
  70. expectedRoutes []*cloudprovider.Route
  71. expectedNetworkUnavailable []bool
  72. clientset *fake.Clientset
  73. }{
  74. // 2 nodes, routes already there
  75. {
  76. nodes: []api.Node{
  77. node1,
  78. node2,
  79. },
  80. initialRoutes: []*cloudprovider.Route{
  81. {cluster + "-01", "node-1", "10.120.0.0/24"},
  82. {cluster + "-02", "node-2", "10.120.1.0/24"},
  83. },
  84. expectedRoutes: []*cloudprovider.Route{
  85. {cluster + "-01", "node-1", "10.120.0.0/24"},
  86. {cluster + "-02", "node-2", "10.120.1.0/24"},
  87. },
  88. expectedNetworkUnavailable: []bool{true, true},
  89. clientset: fake.NewSimpleClientset(&api.NodeList{Items: []api.Node{node1, node2}}),
  90. },
  91. // 2 nodes, one route already there
  92. {
  93. nodes: []api.Node{
  94. node1,
  95. node2,
  96. },
  97. initialRoutes: []*cloudprovider.Route{
  98. {cluster + "-01", "node-1", "10.120.0.0/24"},
  99. },
  100. expectedRoutes: []*cloudprovider.Route{
  101. {cluster + "-01", "node-1", "10.120.0.0/24"},
  102. {cluster + "-02", "node-2", "10.120.1.0/24"},
  103. },
  104. expectedNetworkUnavailable: []bool{true, true},
  105. clientset: fake.NewSimpleClientset(&api.NodeList{Items: []api.Node{node1, node2}}),
  106. },
  107. // 2 nodes, no routes yet
  108. {
  109. nodes: []api.Node{
  110. node1,
  111. node2,
  112. },
  113. initialRoutes: []*cloudprovider.Route{},
  114. expectedRoutes: []*cloudprovider.Route{
  115. {cluster + "-01", "node-1", "10.120.0.0/24"},
  116. {cluster + "-02", "node-2", "10.120.1.0/24"},
  117. },
  118. expectedNetworkUnavailable: []bool{true, true},
  119. clientset: fake.NewSimpleClientset(&api.NodeList{Items: []api.Node{node1, node2}}),
  120. },
  121. // 2 nodes, a few too many routes
  122. {
  123. nodes: []api.Node{
  124. node1,
  125. node2,
  126. },
  127. initialRoutes: []*cloudprovider.Route{
  128. {cluster + "-01", "node-1", "10.120.0.0/24"},
  129. {cluster + "-02", "node-2", "10.120.1.0/24"},
  130. {cluster + "-03", "node-3", "10.120.2.0/24"},
  131. {cluster + "-04", "node-4", "10.120.3.0/24"},
  132. },
  133. expectedRoutes: []*cloudprovider.Route{
  134. {cluster + "-01", "node-1", "10.120.0.0/24"},
  135. {cluster + "-02", "node-2", "10.120.1.0/24"},
  136. },
  137. expectedNetworkUnavailable: []bool{true, true},
  138. clientset: fake.NewSimpleClientset(&api.NodeList{Items: []api.Node{node1, node2}}),
  139. },
  140. // 2 nodes, 2 routes, but only 1 is right
  141. {
  142. nodes: []api.Node{
  143. node1,
  144. node2,
  145. },
  146. initialRoutes: []*cloudprovider.Route{
  147. {cluster + "-01", "node-1", "10.120.0.0/24"},
  148. {cluster + "-03", "node-3", "10.120.2.0/24"},
  149. },
  150. expectedRoutes: []*cloudprovider.Route{
  151. {cluster + "-01", "node-1", "10.120.0.0/24"},
  152. {cluster + "-02", "node-2", "10.120.1.0/24"},
  153. },
  154. expectedNetworkUnavailable: []bool{true, true},
  155. clientset: fake.NewSimpleClientset(&api.NodeList{Items: []api.Node{node1, node2}}),
  156. },
  157. // 2 nodes, one node without CIDR assigned.
  158. {
  159. nodes: []api.Node{
  160. node1,
  161. nodeNoCidr,
  162. },
  163. initialRoutes: []*cloudprovider.Route{},
  164. expectedRoutes: []*cloudprovider.Route{
  165. {cluster + "-01", "node-1", "10.120.0.0/24"},
  166. },
  167. expectedNetworkUnavailable: []bool{true, false},
  168. clientset: fake.NewSimpleClientset(&api.NodeList{Items: []api.Node{node1, nodeNoCidr}}),
  169. },
  170. }
  171. for i, testCase := range testCases {
  172. cloud := &fakecloud.FakeCloud{RouteMap: make(map[string]*fakecloud.FakeRoute)}
  173. for _, route := range testCase.initialRoutes {
  174. fakeRoute := &fakecloud.FakeRoute{}
  175. fakeRoute.ClusterName = cluster
  176. fakeRoute.Route = *route
  177. cloud.RouteMap[route.Name] = fakeRoute
  178. }
  179. routes, ok := cloud.Routes()
  180. if !ok {
  181. t.Error("Error in test: fakecloud doesn't support Routes()")
  182. }
  183. _, cidr, _ := net.ParseCIDR("10.120.0.0/16")
  184. rc := New(routes, testCase.clientset, cluster, cidr)
  185. if err := rc.reconcile(testCase.nodes, testCase.initialRoutes); err != nil {
  186. t.Errorf("%d. Error from rc.reconcile(): %v", i, err)
  187. }
  188. for _, action := range testCase.clientset.Actions() {
  189. if action.GetVerb() == "update" && action.GetResource().Resource == "nodes" {
  190. node := action.(core.UpdateAction).GetObject().(*api.Node)
  191. _, condition := api.GetNodeCondition(&node.Status, api.NodeNetworkUnavailable)
  192. if condition == nil {
  193. t.Errorf("%d. Missing NodeNetworkUnavailable condition for Node %v", i, node.Name)
  194. } else {
  195. check := func(index int) bool {
  196. return (condition.Status == api.ConditionFalse) == testCase.expectedNetworkUnavailable[index]
  197. }
  198. index := -1
  199. for j := range testCase.nodes {
  200. if testCase.nodes[j].Name == node.Name {
  201. index = j
  202. }
  203. }
  204. if index == -1 {
  205. // Something's wrong
  206. continue
  207. }
  208. if !check(index) {
  209. t.Errorf("%d. Invalid NodeNetworkUnavailable condition for Node %v, expected %v, got %v",
  210. i, node.Name, testCase.expectedNetworkUnavailable[index], (condition.Status == api.ConditionFalse))
  211. }
  212. }
  213. }
  214. }
  215. var finalRoutes []*cloudprovider.Route
  216. var err error
  217. timeoutChan := time.After(200 * time.Millisecond)
  218. tick := time.NewTicker(10 * time.Millisecond)
  219. defer tick.Stop()
  220. poll:
  221. for {
  222. select {
  223. case <-tick.C:
  224. if finalRoutes, err = routes.ListRoutes(cluster); err == nil && routeListEqual(finalRoutes, testCase.expectedRoutes) {
  225. break poll
  226. }
  227. case <-timeoutChan:
  228. t.Errorf("%d. rc.reconcile() = %v, routes:\n%v\nexpected: nil, routes:\n%v\n", i, err, flatten(finalRoutes), flatten(testCase.expectedRoutes))
  229. break poll
  230. }
  231. }
  232. }
  233. }
  234. func routeListEqual(list1, list2 []*cloudprovider.Route) bool {
  235. if len(list1) != len(list2) {
  236. return false
  237. }
  238. routeMap1 := make(map[string]*cloudprovider.Route)
  239. for _, route1 := range list1 {
  240. routeMap1[route1.Name] = route1
  241. }
  242. for _, route2 := range list2 {
  243. if route1, exists := routeMap1[route2.Name]; !exists || *route1 != *route2 {
  244. return false
  245. }
  246. }
  247. return true
  248. }
  249. func flatten(list []*cloudprovider.Route) []cloudprovider.Route {
  250. var structList []cloudprovider.Route
  251. for _, route := range list {
  252. structList = append(structList, *route)
  253. }
  254. return structList
  255. }