route_controller_test.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  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. "context"
  16. "net"
  17. "testing"
  18. "time"
  19. "k8s.io/api/core/v1"
  20. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  21. "k8s.io/apimachinery/pkg/types"
  22. "k8s.io/client-go/informers"
  23. "k8s.io/client-go/kubernetes/fake"
  24. core "k8s.io/client-go/testing"
  25. cloudprovider "k8s.io/cloud-provider"
  26. fakecloud "k8s.io/cloud-provider/fake"
  27. cloudnodeutil "k8s.io/cloud-provider/node/helpers"
  28. )
  29. func alwaysReady() bool { return true }
  30. func TestIsResponsibleForRoute(t *testing.T) {
  31. myClusterName := "my-awesome-cluster"
  32. myClusterRoute := "my-awesome-cluster-12345678-90ab-cdef-1234-567890abcdef"
  33. testCases := []struct {
  34. clusterCIDR string
  35. routeName string
  36. routeCIDR string
  37. expectedResponsible bool
  38. }{
  39. // Routes that belong to this cluster
  40. {"10.244.0.0/16", myClusterRoute, "10.244.0.0/24", true},
  41. {"10.244.0.0/16", myClusterRoute, "10.244.10.0/24", true},
  42. {"10.244.0.0/16", myClusterRoute, "10.244.255.0/24", true},
  43. {"10.244.0.0/14", myClusterRoute, "10.244.0.0/24", true},
  44. {"10.244.0.0/14", myClusterRoute, "10.247.255.0/24", true},
  45. {"a00:100::/10", myClusterRoute, "a00:100::/24", true},
  46. // Routes that match our naming/tagging scheme, but are outside our cidr
  47. {"10.244.0.0/16", myClusterRoute, "10.224.0.0/24", false},
  48. {"10.244.0.0/16", myClusterRoute, "10.0.10.0/24", false},
  49. {"10.244.0.0/16", myClusterRoute, "10.255.255.0/24", false},
  50. {"10.244.0.0/14", myClusterRoute, "10.248.0.0/24", false},
  51. {"10.244.0.0/14", myClusterRoute, "10.243.255.0/24", false},
  52. {"a00:100::/10", myClusterRoute, "b00:100::/24", false},
  53. }
  54. for i, testCase := range testCases {
  55. _, cidr, err := net.ParseCIDR(testCase.clusterCIDR)
  56. if err != nil {
  57. t.Errorf("%d. Error in test case: unparsable cidr %q", i, testCase.clusterCIDR)
  58. }
  59. client := fake.NewSimpleClientset()
  60. informerFactory := informers.NewSharedInformerFactory(client, 0)
  61. rc := New(nil, nil, informerFactory.Core().V1().Nodes(), myClusterName, []*net.IPNet{cidr})
  62. rc.nodeListerSynced = alwaysReady
  63. route := &cloudprovider.Route{
  64. Name: testCase.routeName,
  65. TargetNode: types.NodeName("doesnt-matter-for-this-test"),
  66. DestinationCIDR: testCase.routeCIDR,
  67. }
  68. if resp := rc.isResponsibleForRoute(route); resp != testCase.expectedResponsible {
  69. t.Errorf("%d. isResponsibleForRoute() = %t; want %t", i, resp, testCase.expectedResponsible)
  70. }
  71. }
  72. }
  73. func TestReconcile(t *testing.T) {
  74. cluster := "my-k8s"
  75. node1 := v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "node-1", UID: "01"}, Spec: v1.NodeSpec{PodCIDR: "10.120.0.0/24", PodCIDRs: []string{"10.120.0.0/24"}}}
  76. node2 := v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "node-2", UID: "02"}, Spec: v1.NodeSpec{PodCIDR: "10.120.1.0/24", PodCIDRs: []string{"10.120.1.0/24"}}}
  77. nodeNoCidr := v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "node-2", UID: "02"}, Spec: v1.NodeSpec{PodCIDR: ""}}
  78. node3 := v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "node-3", UID: "03"}, Spec: v1.NodeSpec{PodCIDR: "10.120.0.0/24", PodCIDRs: []string{"10.120.0.0/24", "a00:100::/24"}}}
  79. node4 := v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "node-4", UID: "04"}, Spec: v1.NodeSpec{PodCIDR: "10.120.1.0/24", PodCIDRs: []string{"10.120.1.0/24", "a00:200::/24"}}}
  80. testCases := []struct {
  81. nodes []*v1.Node
  82. initialRoutes []*cloudprovider.Route
  83. expectedRoutes []*cloudprovider.Route
  84. expectedNetworkUnavailable []bool
  85. clientset *fake.Clientset
  86. dualStack bool
  87. }{
  88. // multicidr
  89. // 2 nodes, no routes yet
  90. {
  91. dualStack: true,
  92. nodes: []*v1.Node{
  93. &node3,
  94. &node4,
  95. },
  96. initialRoutes: []*cloudprovider.Route{},
  97. expectedRoutes: []*cloudprovider.Route{
  98. {Name: cluster + "-01", TargetNode: "node-3", DestinationCIDR: "10.120.0.0/24", Blackhole: false},
  99. {Name: cluster + "-02", TargetNode: "node-4", DestinationCIDR: "10.120.1.0/24", Blackhole: false},
  100. {Name: cluster + "-03", TargetNode: "node-3", DestinationCIDR: "a00:100::/24", Blackhole: false},
  101. {Name: cluster + "-04", TargetNode: "node-4", DestinationCIDR: "a00:200::/24", Blackhole: false},
  102. },
  103. expectedNetworkUnavailable: []bool{true, true},
  104. clientset: fake.NewSimpleClientset(&v1.NodeList{Items: []v1.Node{node1, node2}}),
  105. },
  106. // 2 nodes, all routes already created
  107. {
  108. dualStack: true,
  109. nodes: []*v1.Node{
  110. &node3,
  111. &node4,
  112. },
  113. initialRoutes: []*cloudprovider.Route{
  114. {Name: cluster + "-01", TargetNode: "node-3", DestinationCIDR: "10.120.0.0/24", Blackhole: false},
  115. {Name: cluster + "-02", TargetNode: "node-4", DestinationCIDR: "10.120.1.0/24", Blackhole: false},
  116. {Name: cluster + "-03", TargetNode: "node-3", DestinationCIDR: "a00:100::/24", Blackhole: false},
  117. {Name: cluster + "-04", TargetNode: "node-4", DestinationCIDR: "a00:200::/24", Blackhole: false},
  118. },
  119. expectedRoutes: []*cloudprovider.Route{
  120. {Name: cluster + "-01", TargetNode: "node-3", DestinationCIDR: "10.120.0.0/24", Blackhole: false},
  121. {Name: cluster + "-02", TargetNode: "node-4", DestinationCIDR: "10.120.1.0/24", Blackhole: false},
  122. {Name: cluster + "-03", TargetNode: "node-3", DestinationCIDR: "a00:100::/24", Blackhole: false},
  123. {Name: cluster + "-04", TargetNode: "node-4", DestinationCIDR: "a00:200::/24", Blackhole: false},
  124. },
  125. expectedNetworkUnavailable: []bool{true, true},
  126. clientset: fake.NewSimpleClientset(&v1.NodeList{Items: []v1.Node{node1, node2}}),
  127. },
  128. // 2 nodes, few wrong routes
  129. {
  130. dualStack: true,
  131. nodes: []*v1.Node{
  132. &node3,
  133. &node4,
  134. },
  135. initialRoutes: []*cloudprovider.Route{
  136. {Name: cluster + "-01", TargetNode: "node-3", DestinationCIDR: "10.120.1.0/24", Blackhole: false},
  137. {Name: cluster + "-02", TargetNode: "node-4", DestinationCIDR: "10.120.0.0/24", Blackhole: false},
  138. {Name: cluster + "-03", TargetNode: "node-3", DestinationCIDR: "a00:200::/24", Blackhole: false},
  139. {Name: cluster + "-04", TargetNode: "node-4", DestinationCIDR: "a00:100::/24", Blackhole: false},
  140. },
  141. expectedRoutes: []*cloudprovider.Route{
  142. {Name: cluster + "-01", TargetNode: "node-3", DestinationCIDR: "10.120.0.0/24", Blackhole: false},
  143. {Name: cluster + "-02", TargetNode: "node-4", DestinationCIDR: "10.120.1.0/24", Blackhole: false},
  144. {Name: cluster + "-03", TargetNode: "node-3", DestinationCIDR: "a00:100::/24", Blackhole: false},
  145. {Name: cluster + "-04", TargetNode: "node-4", DestinationCIDR: "a00:200::/24", Blackhole: false},
  146. },
  147. expectedNetworkUnavailable: []bool{true, true},
  148. clientset: fake.NewSimpleClientset(&v1.NodeList{Items: []v1.Node{node1, node2}}),
  149. },
  150. // 2 nodes, some routes already created
  151. {
  152. dualStack: true,
  153. nodes: []*v1.Node{
  154. &node3,
  155. &node4,
  156. },
  157. initialRoutes: []*cloudprovider.Route{
  158. {Name: cluster + "-01", TargetNode: "node-3", DestinationCIDR: "10.120.0.0/24", Blackhole: false},
  159. {Name: cluster + "-04", TargetNode: "node-4", DestinationCIDR: "a00:200::/24", Blackhole: false},
  160. },
  161. expectedRoutes: []*cloudprovider.Route{
  162. {Name: cluster + "-01", TargetNode: "node-3", DestinationCIDR: "10.120.0.0/24", Blackhole: false},
  163. {Name: cluster + "-02", TargetNode: "node-4", DestinationCIDR: "10.120.1.0/24", Blackhole: false},
  164. {Name: cluster + "-03", TargetNode: "node-3", DestinationCIDR: "a00:100::/24", Blackhole: false},
  165. {Name: cluster + "-04", TargetNode: "node-4", DestinationCIDR: "a00:200::/24", Blackhole: false},
  166. },
  167. expectedNetworkUnavailable: []bool{true, true},
  168. clientset: fake.NewSimpleClientset(&v1.NodeList{Items: []v1.Node{node1, node2}}),
  169. },
  170. // 2 nodes, too many routes
  171. {
  172. dualStack: true,
  173. nodes: []*v1.Node{
  174. &node3,
  175. &node4,
  176. },
  177. initialRoutes: []*cloudprovider.Route{
  178. {Name: cluster + "-01", TargetNode: "node-3", DestinationCIDR: "10.120.0.0/24", Blackhole: false},
  179. {Name: cluster + "-02", TargetNode: "node-4", DestinationCIDR: "10.120.1.0/24", Blackhole: false},
  180. {Name: cluster + "-001", TargetNode: "node-x", DestinationCIDR: "10.120.2.0/24", Blackhole: false},
  181. {Name: cluster + "-03", TargetNode: "node-3", DestinationCIDR: "a00:100::/24", Blackhole: false},
  182. {Name: cluster + "-04", TargetNode: "node-4", DestinationCIDR: "a00:200::/24", Blackhole: false},
  183. {Name: cluster + "-0002", TargetNode: "node-y", DestinationCIDR: "a00:300::/24", Blackhole: false},
  184. },
  185. expectedRoutes: []*cloudprovider.Route{
  186. {Name: cluster + "-01", TargetNode: "node-3", DestinationCIDR: "10.120.0.0/24", Blackhole: false},
  187. {Name: cluster + "-02", TargetNode: "node-4", DestinationCIDR: "10.120.1.0/24", Blackhole: false},
  188. {Name: cluster + "-03", TargetNode: "node-3", DestinationCIDR: "a00:100::/24", Blackhole: false},
  189. {Name: cluster + "-04", TargetNode: "node-4", DestinationCIDR: "a00:200::/24", Blackhole: false},
  190. },
  191. expectedNetworkUnavailable: []bool{true, true},
  192. clientset: fake.NewSimpleClientset(&v1.NodeList{Items: []v1.Node{node1, node2}}),
  193. },
  194. // single cidr
  195. // 2 nodes, routes already there
  196. {
  197. nodes: []*v1.Node{
  198. &node1,
  199. &node2,
  200. },
  201. initialRoutes: []*cloudprovider.Route{
  202. {Name: cluster + "-01", TargetNode: "node-1", DestinationCIDR: "10.120.0.0/24", Blackhole: false},
  203. {Name: cluster + "-02", TargetNode: "node-2", DestinationCIDR: "10.120.1.0/24", Blackhole: false},
  204. },
  205. expectedRoutes: []*cloudprovider.Route{
  206. {Name: cluster + "-01", TargetNode: "node-1", DestinationCIDR: "10.120.0.0/24", Blackhole: false},
  207. {Name: cluster + "-02", TargetNode: "node-2", DestinationCIDR: "10.120.1.0/24", Blackhole: false},
  208. },
  209. expectedNetworkUnavailable: []bool{true, true},
  210. clientset: fake.NewSimpleClientset(&v1.NodeList{Items: []v1.Node{node1, node2}}),
  211. },
  212. // 2 nodes, one route already there
  213. {
  214. nodes: []*v1.Node{
  215. &node1,
  216. &node2,
  217. },
  218. initialRoutes: []*cloudprovider.Route{
  219. {Name: cluster + "-01", TargetNode: "node-1", DestinationCIDR: "10.120.0.0/24", Blackhole: false},
  220. },
  221. expectedRoutes: []*cloudprovider.Route{
  222. {Name: cluster + "-01", TargetNode: "node-1", DestinationCIDR: "10.120.0.0/24", Blackhole: false},
  223. {Name: cluster + "-02", TargetNode: "node-2", DestinationCIDR: "10.120.1.0/24", Blackhole: false},
  224. },
  225. expectedNetworkUnavailable: []bool{true, true},
  226. clientset: fake.NewSimpleClientset(&v1.NodeList{Items: []v1.Node{node1, node2}}),
  227. },
  228. // 2 nodes, no routes yet
  229. {
  230. nodes: []*v1.Node{
  231. &node1,
  232. &node2,
  233. },
  234. initialRoutes: []*cloudprovider.Route{},
  235. expectedRoutes: []*cloudprovider.Route{
  236. {Name: cluster + "-01", TargetNode: "node-1", DestinationCIDR: "10.120.0.0/24", Blackhole: false},
  237. {Name: cluster + "-02", TargetNode: "node-2", DestinationCIDR: "10.120.1.0/24", Blackhole: false},
  238. },
  239. expectedNetworkUnavailable: []bool{true, true},
  240. clientset: fake.NewSimpleClientset(&v1.NodeList{Items: []v1.Node{node1, node2}}),
  241. },
  242. // 2 nodes, a few too many routes
  243. {
  244. nodes: []*v1.Node{
  245. &node1,
  246. &node2,
  247. },
  248. initialRoutes: []*cloudprovider.Route{
  249. {Name: cluster + "-01", TargetNode: "node-1", DestinationCIDR: "10.120.0.0/24", Blackhole: false},
  250. {Name: cluster + "-02", TargetNode: "node-2", DestinationCIDR: "10.120.1.0/24", Blackhole: false},
  251. {Name: cluster + "-03", TargetNode: "node-3", DestinationCIDR: "10.120.2.0/24", Blackhole: false},
  252. {Name: cluster + "-04", TargetNode: "node-4", DestinationCIDR: "10.120.3.0/24", Blackhole: false},
  253. },
  254. expectedRoutes: []*cloudprovider.Route{
  255. {Name: cluster + "-01", TargetNode: "node-1", DestinationCIDR: "10.120.0.0/24", Blackhole: false},
  256. {Name: cluster + "-02", TargetNode: "node-2", DestinationCIDR: "10.120.1.0/24", Blackhole: false},
  257. },
  258. expectedNetworkUnavailable: []bool{true, true},
  259. clientset: fake.NewSimpleClientset(&v1.NodeList{Items: []v1.Node{node1, node2}}),
  260. },
  261. // 2 nodes, 2 routes, but only 1 is right
  262. {
  263. nodes: []*v1.Node{
  264. &node1,
  265. &node2,
  266. },
  267. initialRoutes: []*cloudprovider.Route{
  268. {Name: cluster + "-01", TargetNode: "node-1", DestinationCIDR: "10.120.0.0/24", Blackhole: false},
  269. {Name: cluster + "-03", TargetNode: "node-3", DestinationCIDR: "10.120.2.0/24", Blackhole: false},
  270. },
  271. expectedRoutes: []*cloudprovider.Route{
  272. {Name: cluster + "-01", TargetNode: "node-1", DestinationCIDR: "10.120.0.0/24", Blackhole: false},
  273. {Name: cluster + "-02", TargetNode: "node-2", DestinationCIDR: "10.120.1.0/24", Blackhole: false},
  274. },
  275. expectedNetworkUnavailable: []bool{true, true},
  276. clientset: fake.NewSimpleClientset(&v1.NodeList{Items: []v1.Node{node1, node2}}),
  277. },
  278. // 2 nodes, one node without CIDR assigned.
  279. {
  280. nodes: []*v1.Node{
  281. &node1,
  282. &nodeNoCidr,
  283. },
  284. initialRoutes: []*cloudprovider.Route{},
  285. expectedRoutes: []*cloudprovider.Route{
  286. {Name: cluster + "-01", TargetNode: "node-1", DestinationCIDR: "10.120.0.0/24", Blackhole: false},
  287. },
  288. expectedNetworkUnavailable: []bool{true, false},
  289. clientset: fake.NewSimpleClientset(&v1.NodeList{Items: []v1.Node{node1, nodeNoCidr}}),
  290. },
  291. // 2 nodes, an extra blackhole route in our range
  292. {
  293. nodes: []*v1.Node{
  294. &node1,
  295. &node2,
  296. },
  297. initialRoutes: []*cloudprovider.Route{
  298. {Name: cluster + "-01", TargetNode: "node-1", DestinationCIDR: "10.120.0.0/24", Blackhole: false},
  299. {Name: cluster + "-02", TargetNode: "node-2", DestinationCIDR: "10.120.1.0/24", Blackhole: false},
  300. {Name: cluster + "-03", TargetNode: "", DestinationCIDR: "10.120.2.0/24", Blackhole: true},
  301. },
  302. expectedRoutes: []*cloudprovider.Route{
  303. {Name: cluster + "-01", TargetNode: "node-1", DestinationCIDR: "10.120.0.0/24", Blackhole: false},
  304. {Name: cluster + "-02", TargetNode: "node-2", DestinationCIDR: "10.120.1.0/24", Blackhole: false},
  305. },
  306. expectedNetworkUnavailable: []bool{true, true},
  307. clientset: fake.NewSimpleClientset(&v1.NodeList{Items: []v1.Node{node1, node2}}),
  308. },
  309. // 2 nodes, an extra blackhole route not in our range
  310. {
  311. nodes: []*v1.Node{
  312. &node1,
  313. &node2,
  314. },
  315. initialRoutes: []*cloudprovider.Route{
  316. {Name: cluster + "-01", TargetNode: "node-1", DestinationCIDR: "10.120.0.0/24", Blackhole: false},
  317. {Name: cluster + "-02", TargetNode: "node-2", DestinationCIDR: "10.120.1.0/24", Blackhole: false},
  318. {Name: cluster + "-03", TargetNode: "", DestinationCIDR: "10.1.2.0/24", Blackhole: true},
  319. },
  320. expectedRoutes: []*cloudprovider.Route{
  321. {Name: cluster + "-01", TargetNode: "node-1", DestinationCIDR: "10.120.0.0/24", Blackhole: false},
  322. {Name: cluster + "-02", TargetNode: "node-2", DestinationCIDR: "10.120.1.0/24", Blackhole: false},
  323. {Name: cluster + "-03", TargetNode: "", DestinationCIDR: "10.1.2.0/24", Blackhole: true},
  324. },
  325. expectedNetworkUnavailable: []bool{true, true},
  326. clientset: fake.NewSimpleClientset(&v1.NodeList{Items: []v1.Node{node1, node2}}),
  327. },
  328. }
  329. for i, testCase := range testCases {
  330. cloud := &fakecloud.Cloud{RouteMap: make(map[string]*fakecloud.Route)}
  331. for _, route := range testCase.initialRoutes {
  332. fakeRoute := &fakecloud.Route{}
  333. fakeRoute.ClusterName = cluster
  334. fakeRoute.Route = *route
  335. cloud.RouteMap[route.Name] = fakeRoute
  336. }
  337. routes, ok := cloud.Routes()
  338. if !ok {
  339. t.Error("Error in test: fakecloud doesn't support Routes()")
  340. }
  341. cidrs := make([]*net.IPNet, 0)
  342. _, cidr, _ := net.ParseCIDR("10.120.0.0/16")
  343. cidrs = append(cidrs, cidr)
  344. if testCase.dualStack {
  345. _, cidrv6, _ := net.ParseCIDR("ace:cab:deca::/8")
  346. cidrs = append(cidrs, cidrv6)
  347. }
  348. informerFactory := informers.NewSharedInformerFactory(testCase.clientset, 0)
  349. rc := New(routes, testCase.clientset, informerFactory.Core().V1().Nodes(), cluster, cidrs)
  350. rc.nodeListerSynced = alwaysReady
  351. if err := rc.reconcile(testCase.nodes, testCase.initialRoutes); err != nil {
  352. t.Errorf("%d. Error from rc.reconcile(): %v", i, err)
  353. }
  354. for _, action := range testCase.clientset.Actions() {
  355. if action.GetVerb() == "update" && action.GetResource().Resource == "nodes" {
  356. node := action.(core.UpdateAction).GetObject().(*v1.Node)
  357. _, condition := cloudnodeutil.GetNodeCondition(&node.Status, v1.NodeNetworkUnavailable)
  358. if condition == nil {
  359. t.Errorf("%d. Missing NodeNetworkUnavailable condition for Node %v", i, node.Name)
  360. } else {
  361. check := func(index int) bool {
  362. return (condition.Status == v1.ConditionFalse) == testCase.expectedNetworkUnavailable[index]
  363. }
  364. index := -1
  365. for j := range testCase.nodes {
  366. if testCase.nodes[j].Name == node.Name {
  367. index = j
  368. }
  369. }
  370. if index == -1 {
  371. // Something's wrong
  372. continue
  373. }
  374. if !check(index) {
  375. t.Errorf("%d. Invalid NodeNetworkUnavailable condition for Node %v, expected %v, got %v",
  376. i, node.Name, testCase.expectedNetworkUnavailable[index], (condition.Status == v1.ConditionFalse))
  377. }
  378. }
  379. }
  380. }
  381. var finalRoutes []*cloudprovider.Route
  382. var err error
  383. timeoutChan := time.After(200 * time.Millisecond)
  384. tick := time.NewTicker(10 * time.Millisecond)
  385. defer tick.Stop()
  386. poll:
  387. for {
  388. select {
  389. case <-tick.C:
  390. if finalRoutes, err = routes.ListRoutes(context.TODO(), cluster); err == nil && routeListEqual(finalRoutes, testCase.expectedRoutes) {
  391. break poll
  392. }
  393. case <-timeoutChan:
  394. t.Errorf("%d. rc.reconcile() = %v,\nfound routes:\n%v\nexpected routes:\n%v\n", i, err, flatten(finalRoutes), flatten(testCase.expectedRoutes))
  395. break poll
  396. }
  397. }
  398. }
  399. }
  400. func routeListEqual(list1, list2 []*cloudprovider.Route) bool {
  401. if len(list1) != len(list2) {
  402. return false
  403. }
  404. // nodename+cidr:bool
  405. seen := make(map[string]bool)
  406. for _, route1 := range list1 {
  407. for _, route2 := range list2 {
  408. if route1.DestinationCIDR == route2.DestinationCIDR && route1.TargetNode == route2.TargetNode {
  409. seen[string(route1.TargetNode)+route1.DestinationCIDR] = true
  410. break
  411. }
  412. }
  413. }
  414. if len(seen) == len(list1) {
  415. return true
  416. }
  417. return false
  418. }
  419. func flatten(list []*cloudprovider.Route) []cloudprovider.Route {
  420. var structList []cloudprovider.Route
  421. for _, route := range list {
  422. structList = append(structList, *route)
  423. }
  424. return structList
  425. }