route_controller_test.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  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. "k8s.io/kubernetes/pkg/controller"
  28. nodeutil "k8s.io/kubernetes/pkg/controller/util/node"
  29. )
  30. func alwaysReady() bool { return true }
  31. func TestIsResponsibleForRoute(t *testing.T) {
  32. myClusterName := "my-awesome-cluster"
  33. myClusterRoute := "my-awesome-cluster-12345678-90ab-cdef-1234-567890abcdef"
  34. testCases := []struct {
  35. clusterCIDR string
  36. routeName string
  37. routeCIDR string
  38. expectedResponsible bool
  39. }{
  40. // Routes that belong to this cluster
  41. {"10.244.0.0/16", myClusterRoute, "10.244.0.0/24", true},
  42. {"10.244.0.0/16", myClusterRoute, "10.244.10.0/24", true},
  43. {"10.244.0.0/16", myClusterRoute, "10.244.255.0/24", true},
  44. {"10.244.0.0/14", myClusterRoute, "10.244.0.0/24", true},
  45. {"10.244.0.0/14", myClusterRoute, "10.247.255.0/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. }
  53. for i, testCase := range testCases {
  54. _, cidr, err := net.ParseCIDR(testCase.clusterCIDR)
  55. if err != nil {
  56. t.Errorf("%d. Error in test case: unparsable cidr %q", i, testCase.clusterCIDR)
  57. }
  58. client := fake.NewSimpleClientset()
  59. informerFactory := informers.NewSharedInformerFactory(client, controller.NoResyncPeriodFunc())
  60. rc := New(nil, nil, informerFactory.Core().V1().Nodes(), myClusterName, cidr)
  61. rc.nodeListerSynced = alwaysReady
  62. route := &cloudprovider.Route{
  63. Name: testCase.routeName,
  64. TargetNode: types.NodeName("doesnt-matter-for-this-test"),
  65. DestinationCIDR: testCase.routeCIDR,
  66. }
  67. if resp := rc.isResponsibleForRoute(route); resp != testCase.expectedResponsible {
  68. t.Errorf("%d. isResponsibleForRoute() = %t; want %t", i, resp, testCase.expectedResponsible)
  69. }
  70. }
  71. }
  72. func TestReconcile(t *testing.T) {
  73. cluster := "my-k8s"
  74. node1 := v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "node-1", UID: "01"}, Spec: v1.NodeSpec{PodCIDR: "10.120.0.0/24"}}
  75. node2 := v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "node-2", UID: "02"}, Spec: v1.NodeSpec{PodCIDR: "10.120.1.0/24"}}
  76. nodeNoCidr := v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "node-2", UID: "02"}, Spec: v1.NodeSpec{PodCIDR: ""}}
  77. testCases := []struct {
  78. nodes []*v1.Node
  79. initialRoutes []*cloudprovider.Route
  80. expectedRoutes []*cloudprovider.Route
  81. expectedNetworkUnavailable []bool
  82. clientset *fake.Clientset
  83. }{
  84. // 2 nodes, routes already there
  85. {
  86. nodes: []*v1.Node{
  87. &node1,
  88. &node2,
  89. },
  90. initialRoutes: []*cloudprovider.Route{
  91. {Name: cluster + "-01", TargetNode: "node-1", DestinationCIDR: "10.120.0.0/24", Blackhole: false},
  92. {Name: cluster + "-02", TargetNode: "node-2", DestinationCIDR: "10.120.1.0/24", Blackhole: false},
  93. },
  94. expectedRoutes: []*cloudprovider.Route{
  95. {Name: cluster + "-01", TargetNode: "node-1", DestinationCIDR: "10.120.0.0/24", Blackhole: false},
  96. {Name: cluster + "-02", TargetNode: "node-2", DestinationCIDR: "10.120.1.0/24", Blackhole: false},
  97. },
  98. expectedNetworkUnavailable: []bool{true, true},
  99. clientset: fake.NewSimpleClientset(&v1.NodeList{Items: []v1.Node{node1, node2}}),
  100. },
  101. // 2 nodes, one route already there
  102. {
  103. nodes: []*v1.Node{
  104. &node1,
  105. &node2,
  106. },
  107. initialRoutes: []*cloudprovider.Route{
  108. {Name: cluster + "-01", TargetNode: "node-1", DestinationCIDR: "10.120.0.0/24", Blackhole: false},
  109. },
  110. expectedRoutes: []*cloudprovider.Route{
  111. {Name: cluster + "-01", TargetNode: "node-1", DestinationCIDR: "10.120.0.0/24", Blackhole: false},
  112. {Name: cluster + "-02", TargetNode: "node-2", DestinationCIDR: "10.120.1.0/24", Blackhole: false},
  113. },
  114. expectedNetworkUnavailable: []bool{true, true},
  115. clientset: fake.NewSimpleClientset(&v1.NodeList{Items: []v1.Node{node1, node2}}),
  116. },
  117. // 2 nodes, no routes yet
  118. {
  119. nodes: []*v1.Node{
  120. &node1,
  121. &node2,
  122. },
  123. initialRoutes: []*cloudprovider.Route{},
  124. expectedRoutes: []*cloudprovider.Route{
  125. {Name: cluster + "-01", TargetNode: "node-1", DestinationCIDR: "10.120.0.0/24", Blackhole: false},
  126. {Name: cluster + "-02", TargetNode: "node-2", DestinationCIDR: "10.120.1.0/24", Blackhole: false},
  127. },
  128. expectedNetworkUnavailable: []bool{true, true},
  129. clientset: fake.NewSimpleClientset(&v1.NodeList{Items: []v1.Node{node1, node2}}),
  130. },
  131. // 2 nodes, a few too many routes
  132. {
  133. nodes: []*v1.Node{
  134. &node1,
  135. &node2,
  136. },
  137. initialRoutes: []*cloudprovider.Route{
  138. {Name: cluster + "-01", TargetNode: "node-1", DestinationCIDR: "10.120.0.0/24", Blackhole: false},
  139. {Name: cluster + "-02", TargetNode: "node-2", DestinationCIDR: "10.120.1.0/24", Blackhole: false},
  140. {Name: cluster + "-03", TargetNode: "node-3", DestinationCIDR: "10.120.2.0/24", Blackhole: false},
  141. {Name: cluster + "-04", TargetNode: "node-4", DestinationCIDR: "10.120.3.0/24", Blackhole: false},
  142. },
  143. expectedRoutes: []*cloudprovider.Route{
  144. {Name: cluster + "-01", TargetNode: "node-1", DestinationCIDR: "10.120.0.0/24", Blackhole: false},
  145. {Name: cluster + "-02", TargetNode: "node-2", DestinationCIDR: "10.120.1.0/24", Blackhole: false},
  146. },
  147. expectedNetworkUnavailable: []bool{true, true},
  148. clientset: fake.NewSimpleClientset(&v1.NodeList{Items: []v1.Node{node1, node2}}),
  149. },
  150. // 2 nodes, 2 routes, but only 1 is right
  151. {
  152. nodes: []*v1.Node{
  153. &node1,
  154. &node2,
  155. },
  156. initialRoutes: []*cloudprovider.Route{
  157. {Name: cluster + "-01", TargetNode: "node-1", DestinationCIDR: "10.120.0.0/24", Blackhole: false},
  158. {Name: cluster + "-03", TargetNode: "node-3", DestinationCIDR: "10.120.2.0/24", Blackhole: false},
  159. },
  160. expectedRoutes: []*cloudprovider.Route{
  161. {Name: cluster + "-01", TargetNode: "node-1", DestinationCIDR: "10.120.0.0/24", Blackhole: false},
  162. {Name: cluster + "-02", TargetNode: "node-2", DestinationCIDR: "10.120.1.0/24", Blackhole: false},
  163. },
  164. expectedNetworkUnavailable: []bool{true, true},
  165. clientset: fake.NewSimpleClientset(&v1.NodeList{Items: []v1.Node{node1, node2}}),
  166. },
  167. // 2 nodes, one node without CIDR assigned.
  168. {
  169. nodes: []*v1.Node{
  170. &node1,
  171. &nodeNoCidr,
  172. },
  173. initialRoutes: []*cloudprovider.Route{},
  174. expectedRoutes: []*cloudprovider.Route{
  175. {Name: cluster + "-01", TargetNode: "node-1", DestinationCIDR: "10.120.0.0/24", Blackhole: false},
  176. },
  177. expectedNetworkUnavailable: []bool{true, false},
  178. clientset: fake.NewSimpleClientset(&v1.NodeList{Items: []v1.Node{node1, nodeNoCidr}}),
  179. },
  180. // 2 nodes, an extra blackhole route in our range
  181. {
  182. nodes: []*v1.Node{
  183. &node1,
  184. &node2,
  185. },
  186. initialRoutes: []*cloudprovider.Route{
  187. {Name: cluster + "-01", TargetNode: "node-1", DestinationCIDR: "10.120.0.0/24", Blackhole: false},
  188. {Name: cluster + "-02", TargetNode: "node-2", DestinationCIDR: "10.120.1.0/24", Blackhole: false},
  189. {Name: cluster + "-03", TargetNode: "", DestinationCIDR: "10.120.2.0/24", Blackhole: true},
  190. },
  191. expectedRoutes: []*cloudprovider.Route{
  192. {Name: cluster + "-01", TargetNode: "node-1", DestinationCIDR: "10.120.0.0/24", Blackhole: false},
  193. {Name: cluster + "-02", TargetNode: "node-2", DestinationCIDR: "10.120.1.0/24", Blackhole: false},
  194. },
  195. expectedNetworkUnavailable: []bool{true, true},
  196. clientset: fake.NewSimpleClientset(&v1.NodeList{Items: []v1.Node{node1, node2}}),
  197. },
  198. // 2 nodes, an extra blackhole route not in our range
  199. {
  200. nodes: []*v1.Node{
  201. &node1,
  202. &node2,
  203. },
  204. initialRoutes: []*cloudprovider.Route{
  205. {Name: cluster + "-01", TargetNode: "node-1", DestinationCIDR: "10.120.0.0/24", Blackhole: false},
  206. {Name: cluster + "-02", TargetNode: "node-2", DestinationCIDR: "10.120.1.0/24", Blackhole: false},
  207. {Name: cluster + "-03", TargetNode: "", DestinationCIDR: "10.1.2.0/24", Blackhole: true},
  208. },
  209. expectedRoutes: []*cloudprovider.Route{
  210. {Name: cluster + "-01", TargetNode: "node-1", DestinationCIDR: "10.120.0.0/24", Blackhole: false},
  211. {Name: cluster + "-02", TargetNode: "node-2", DestinationCIDR: "10.120.1.0/24", Blackhole: false},
  212. {Name: cluster + "-03", TargetNode: "", DestinationCIDR: "10.1.2.0/24", Blackhole: true},
  213. },
  214. expectedNetworkUnavailable: []bool{true, true},
  215. clientset: fake.NewSimpleClientset(&v1.NodeList{Items: []v1.Node{node1, node2}}),
  216. },
  217. }
  218. for i, testCase := range testCases {
  219. cloud := &fakecloud.Cloud{RouteMap: make(map[string]*fakecloud.Route)}
  220. for _, route := range testCase.initialRoutes {
  221. fakeRoute := &fakecloud.Route{}
  222. fakeRoute.ClusterName = cluster
  223. fakeRoute.Route = *route
  224. cloud.RouteMap[route.Name] = fakeRoute
  225. }
  226. routes, ok := cloud.Routes()
  227. if !ok {
  228. t.Error("Error in test: fakecloud doesn't support Routes()")
  229. }
  230. _, cidr, _ := net.ParseCIDR("10.120.0.0/16")
  231. informerFactory := informers.NewSharedInformerFactory(testCase.clientset, controller.NoResyncPeriodFunc())
  232. rc := New(routes, testCase.clientset, informerFactory.Core().V1().Nodes(), cluster, cidr)
  233. rc.nodeListerSynced = alwaysReady
  234. if err := rc.reconcile(testCase.nodes, testCase.initialRoutes); err != nil {
  235. t.Errorf("%d. Error from rc.reconcile(): %v", i, err)
  236. }
  237. for _, action := range testCase.clientset.Actions() {
  238. if action.GetVerb() == "update" && action.GetResource().Resource == "nodes" {
  239. node := action.(core.UpdateAction).GetObject().(*v1.Node)
  240. _, condition := nodeutil.GetNodeCondition(&node.Status, v1.NodeNetworkUnavailable)
  241. if condition == nil {
  242. t.Errorf("%d. Missing NodeNetworkUnavailable condition for Node %v", i, node.Name)
  243. } else {
  244. check := func(index int) bool {
  245. return (condition.Status == v1.ConditionFalse) == testCase.expectedNetworkUnavailable[index]
  246. }
  247. index := -1
  248. for j := range testCase.nodes {
  249. if testCase.nodes[j].Name == node.Name {
  250. index = j
  251. }
  252. }
  253. if index == -1 {
  254. // Something's wrong
  255. continue
  256. }
  257. if !check(index) {
  258. t.Errorf("%d. Invalid NodeNetworkUnavailable condition for Node %v, expected %v, got %v",
  259. i, node.Name, testCase.expectedNetworkUnavailable[index], (condition.Status == v1.ConditionFalse))
  260. }
  261. }
  262. }
  263. }
  264. var finalRoutes []*cloudprovider.Route
  265. var err error
  266. timeoutChan := time.After(200 * time.Millisecond)
  267. tick := time.NewTicker(10 * time.Millisecond)
  268. defer tick.Stop()
  269. poll:
  270. for {
  271. select {
  272. case <-tick.C:
  273. if finalRoutes, err = routes.ListRoutes(context.TODO(), cluster); err == nil && routeListEqual(finalRoutes, testCase.expectedRoutes) {
  274. break poll
  275. }
  276. case <-timeoutChan:
  277. t.Errorf("%d. rc.reconcile() = %v, routes:\n%v\nexpected: nil, routes:\n%v\n", i, err, flatten(finalRoutes), flatten(testCase.expectedRoutes))
  278. break poll
  279. }
  280. }
  281. }
  282. }
  283. func routeListEqual(list1, list2 []*cloudprovider.Route) bool {
  284. if len(list1) != len(list2) {
  285. return false
  286. }
  287. routeMap1 := make(map[string]*cloudprovider.Route)
  288. for _, route1 := range list1 {
  289. routeMap1[route1.Name] = route1
  290. }
  291. for _, route2 := range list2 {
  292. if route1, exists := routeMap1[route2.Name]; !exists || *route1 != *route2 {
  293. return false
  294. }
  295. }
  296. return true
  297. }
  298. func flatten(list []*cloudprovider.Route) []cloudprovider.Route {
  299. var structList []cloudprovider.Route
  300. for _, route := range list {
  301. structList = append(structList, *route)
  302. }
  303. return structList
  304. }