123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317 |
- /*
- Copyright 2015 The Kubernetes Authors.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- package route
- import (
- "context"
- "net"
- "testing"
- "time"
- "k8s.io/api/core/v1"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/apimachinery/pkg/types"
- "k8s.io/client-go/informers"
- "k8s.io/client-go/kubernetes/fake"
- core "k8s.io/client-go/testing"
- cloudprovider "k8s.io/cloud-provider"
- fakecloud "k8s.io/cloud-provider/fake"
- "k8s.io/kubernetes/pkg/controller"
- nodeutil "k8s.io/kubernetes/pkg/controller/util/node"
- )
- func alwaysReady() bool { return true }
- func TestIsResponsibleForRoute(t *testing.T) {
- myClusterName := "my-awesome-cluster"
- myClusterRoute := "my-awesome-cluster-12345678-90ab-cdef-1234-567890abcdef"
- testCases := []struct {
- clusterCIDR string
- routeName string
- routeCIDR string
- expectedResponsible bool
- }{
- // Routes that belong to this cluster
- {"10.244.0.0/16", myClusterRoute, "10.244.0.0/24", true},
- {"10.244.0.0/16", myClusterRoute, "10.244.10.0/24", true},
- {"10.244.0.0/16", myClusterRoute, "10.244.255.0/24", true},
- {"10.244.0.0/14", myClusterRoute, "10.244.0.0/24", true},
- {"10.244.0.0/14", myClusterRoute, "10.247.255.0/24", true},
- // Routes that match our naming/tagging scheme, but are outside our cidr
- {"10.244.0.0/16", myClusterRoute, "10.224.0.0/24", false},
- {"10.244.0.0/16", myClusterRoute, "10.0.10.0/24", false},
- {"10.244.0.0/16", myClusterRoute, "10.255.255.0/24", false},
- {"10.244.0.0/14", myClusterRoute, "10.248.0.0/24", false},
- {"10.244.0.0/14", myClusterRoute, "10.243.255.0/24", false},
- }
- for i, testCase := range testCases {
- _, cidr, err := net.ParseCIDR(testCase.clusterCIDR)
- if err != nil {
- t.Errorf("%d. Error in test case: unparsable cidr %q", i, testCase.clusterCIDR)
- }
- client := fake.NewSimpleClientset()
- informerFactory := informers.NewSharedInformerFactory(client, controller.NoResyncPeriodFunc())
- rc := New(nil, nil, informerFactory.Core().V1().Nodes(), myClusterName, cidr)
- rc.nodeListerSynced = alwaysReady
- route := &cloudprovider.Route{
- Name: testCase.routeName,
- TargetNode: types.NodeName("doesnt-matter-for-this-test"),
- DestinationCIDR: testCase.routeCIDR,
- }
- if resp := rc.isResponsibleForRoute(route); resp != testCase.expectedResponsible {
- t.Errorf("%d. isResponsibleForRoute() = %t; want %t", i, resp, testCase.expectedResponsible)
- }
- }
- }
- func TestReconcile(t *testing.T) {
- cluster := "my-k8s"
- node1 := v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "node-1", UID: "01"}, Spec: v1.NodeSpec{PodCIDR: "10.120.0.0/24"}}
- node2 := v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "node-2", UID: "02"}, Spec: v1.NodeSpec{PodCIDR: "10.120.1.0/24"}}
- nodeNoCidr := v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "node-2", UID: "02"}, Spec: v1.NodeSpec{PodCIDR: ""}}
- testCases := []struct {
- nodes []*v1.Node
- initialRoutes []*cloudprovider.Route
- expectedRoutes []*cloudprovider.Route
- expectedNetworkUnavailable []bool
- clientset *fake.Clientset
- }{
- // 2 nodes, routes already there
- {
- nodes: []*v1.Node{
- &node1,
- &node2,
- },
- initialRoutes: []*cloudprovider.Route{
- {Name: cluster + "-01", TargetNode: "node-1", DestinationCIDR: "10.120.0.0/24", Blackhole: false},
- {Name: cluster + "-02", TargetNode: "node-2", DestinationCIDR: "10.120.1.0/24", Blackhole: false},
- },
- expectedRoutes: []*cloudprovider.Route{
- {Name: cluster + "-01", TargetNode: "node-1", DestinationCIDR: "10.120.0.0/24", Blackhole: false},
- {Name: cluster + "-02", TargetNode: "node-2", DestinationCIDR: "10.120.1.0/24", Blackhole: false},
- },
- expectedNetworkUnavailable: []bool{true, true},
- clientset: fake.NewSimpleClientset(&v1.NodeList{Items: []v1.Node{node1, node2}}),
- },
- // 2 nodes, one route already there
- {
- nodes: []*v1.Node{
- &node1,
- &node2,
- },
- initialRoutes: []*cloudprovider.Route{
- {Name: cluster + "-01", TargetNode: "node-1", DestinationCIDR: "10.120.0.0/24", Blackhole: false},
- },
- expectedRoutes: []*cloudprovider.Route{
- {Name: cluster + "-01", TargetNode: "node-1", DestinationCIDR: "10.120.0.0/24", Blackhole: false},
- {Name: cluster + "-02", TargetNode: "node-2", DestinationCIDR: "10.120.1.0/24", Blackhole: false},
- },
- expectedNetworkUnavailable: []bool{true, true},
- clientset: fake.NewSimpleClientset(&v1.NodeList{Items: []v1.Node{node1, node2}}),
- },
- // 2 nodes, no routes yet
- {
- nodes: []*v1.Node{
- &node1,
- &node2,
- },
- initialRoutes: []*cloudprovider.Route{},
- expectedRoutes: []*cloudprovider.Route{
- {Name: cluster + "-01", TargetNode: "node-1", DestinationCIDR: "10.120.0.0/24", Blackhole: false},
- {Name: cluster + "-02", TargetNode: "node-2", DestinationCIDR: "10.120.1.0/24", Blackhole: false},
- },
- expectedNetworkUnavailable: []bool{true, true},
- clientset: fake.NewSimpleClientset(&v1.NodeList{Items: []v1.Node{node1, node2}}),
- },
- // 2 nodes, a few too many routes
- {
- nodes: []*v1.Node{
- &node1,
- &node2,
- },
- initialRoutes: []*cloudprovider.Route{
- {Name: cluster + "-01", TargetNode: "node-1", DestinationCIDR: "10.120.0.0/24", Blackhole: false},
- {Name: cluster + "-02", TargetNode: "node-2", DestinationCIDR: "10.120.1.0/24", Blackhole: false},
- {Name: cluster + "-03", TargetNode: "node-3", DestinationCIDR: "10.120.2.0/24", Blackhole: false},
- {Name: cluster + "-04", TargetNode: "node-4", DestinationCIDR: "10.120.3.0/24", Blackhole: false},
- },
- expectedRoutes: []*cloudprovider.Route{
- {Name: cluster + "-01", TargetNode: "node-1", DestinationCIDR: "10.120.0.0/24", Blackhole: false},
- {Name: cluster + "-02", TargetNode: "node-2", DestinationCIDR: "10.120.1.0/24", Blackhole: false},
- },
- expectedNetworkUnavailable: []bool{true, true},
- clientset: fake.NewSimpleClientset(&v1.NodeList{Items: []v1.Node{node1, node2}}),
- },
- // 2 nodes, 2 routes, but only 1 is right
- {
- nodes: []*v1.Node{
- &node1,
- &node2,
- },
- initialRoutes: []*cloudprovider.Route{
- {Name: cluster + "-01", TargetNode: "node-1", DestinationCIDR: "10.120.0.0/24", Blackhole: false},
- {Name: cluster + "-03", TargetNode: "node-3", DestinationCIDR: "10.120.2.0/24", Blackhole: false},
- },
- expectedRoutes: []*cloudprovider.Route{
- {Name: cluster + "-01", TargetNode: "node-1", DestinationCIDR: "10.120.0.0/24", Blackhole: false},
- {Name: cluster + "-02", TargetNode: "node-2", DestinationCIDR: "10.120.1.0/24", Blackhole: false},
- },
- expectedNetworkUnavailable: []bool{true, true},
- clientset: fake.NewSimpleClientset(&v1.NodeList{Items: []v1.Node{node1, node2}}),
- },
- // 2 nodes, one node without CIDR assigned.
- {
- nodes: []*v1.Node{
- &node1,
- &nodeNoCidr,
- },
- initialRoutes: []*cloudprovider.Route{},
- expectedRoutes: []*cloudprovider.Route{
- {Name: cluster + "-01", TargetNode: "node-1", DestinationCIDR: "10.120.0.0/24", Blackhole: false},
- },
- expectedNetworkUnavailable: []bool{true, false},
- clientset: fake.NewSimpleClientset(&v1.NodeList{Items: []v1.Node{node1, nodeNoCidr}}),
- },
- // 2 nodes, an extra blackhole route in our range
- {
- nodes: []*v1.Node{
- &node1,
- &node2,
- },
- initialRoutes: []*cloudprovider.Route{
- {Name: cluster + "-01", TargetNode: "node-1", DestinationCIDR: "10.120.0.0/24", Blackhole: false},
- {Name: cluster + "-02", TargetNode: "node-2", DestinationCIDR: "10.120.1.0/24", Blackhole: false},
- {Name: cluster + "-03", TargetNode: "", DestinationCIDR: "10.120.2.0/24", Blackhole: true},
- },
- expectedRoutes: []*cloudprovider.Route{
- {Name: cluster + "-01", TargetNode: "node-1", DestinationCIDR: "10.120.0.0/24", Blackhole: false},
- {Name: cluster + "-02", TargetNode: "node-2", DestinationCIDR: "10.120.1.0/24", Blackhole: false},
- },
- expectedNetworkUnavailable: []bool{true, true},
- clientset: fake.NewSimpleClientset(&v1.NodeList{Items: []v1.Node{node1, node2}}),
- },
- // 2 nodes, an extra blackhole route not in our range
- {
- nodes: []*v1.Node{
- &node1,
- &node2,
- },
- initialRoutes: []*cloudprovider.Route{
- {Name: cluster + "-01", TargetNode: "node-1", DestinationCIDR: "10.120.0.0/24", Blackhole: false},
- {Name: cluster + "-02", TargetNode: "node-2", DestinationCIDR: "10.120.1.0/24", Blackhole: false},
- {Name: cluster + "-03", TargetNode: "", DestinationCIDR: "10.1.2.0/24", Blackhole: true},
- },
- expectedRoutes: []*cloudprovider.Route{
- {Name: cluster + "-01", TargetNode: "node-1", DestinationCIDR: "10.120.0.0/24", Blackhole: false},
- {Name: cluster + "-02", TargetNode: "node-2", DestinationCIDR: "10.120.1.0/24", Blackhole: false},
- {Name: cluster + "-03", TargetNode: "", DestinationCIDR: "10.1.2.0/24", Blackhole: true},
- },
- expectedNetworkUnavailable: []bool{true, true},
- clientset: fake.NewSimpleClientset(&v1.NodeList{Items: []v1.Node{node1, node2}}),
- },
- }
- for i, testCase := range testCases {
- cloud := &fakecloud.Cloud{RouteMap: make(map[string]*fakecloud.Route)}
- for _, route := range testCase.initialRoutes {
- fakeRoute := &fakecloud.Route{}
- fakeRoute.ClusterName = cluster
- fakeRoute.Route = *route
- cloud.RouteMap[route.Name] = fakeRoute
- }
- routes, ok := cloud.Routes()
- if !ok {
- t.Error("Error in test: fakecloud doesn't support Routes()")
- }
- _, cidr, _ := net.ParseCIDR("10.120.0.0/16")
- informerFactory := informers.NewSharedInformerFactory(testCase.clientset, controller.NoResyncPeriodFunc())
- rc := New(routes, testCase.clientset, informerFactory.Core().V1().Nodes(), cluster, cidr)
- rc.nodeListerSynced = alwaysReady
- if err := rc.reconcile(testCase.nodes, testCase.initialRoutes); err != nil {
- t.Errorf("%d. Error from rc.reconcile(): %v", i, err)
- }
- for _, action := range testCase.clientset.Actions() {
- if action.GetVerb() == "update" && action.GetResource().Resource == "nodes" {
- node := action.(core.UpdateAction).GetObject().(*v1.Node)
- _, condition := nodeutil.GetNodeCondition(&node.Status, v1.NodeNetworkUnavailable)
- if condition == nil {
- t.Errorf("%d. Missing NodeNetworkUnavailable condition for Node %v", i, node.Name)
- } else {
- check := func(index int) bool {
- return (condition.Status == v1.ConditionFalse) == testCase.expectedNetworkUnavailable[index]
- }
- index := -1
- for j := range testCase.nodes {
- if testCase.nodes[j].Name == node.Name {
- index = j
- }
- }
- if index == -1 {
- // Something's wrong
- continue
- }
- if !check(index) {
- t.Errorf("%d. Invalid NodeNetworkUnavailable condition for Node %v, expected %v, got %v",
- i, node.Name, testCase.expectedNetworkUnavailable[index], (condition.Status == v1.ConditionFalse))
- }
- }
- }
- }
- var finalRoutes []*cloudprovider.Route
- var err error
- timeoutChan := time.After(200 * time.Millisecond)
- tick := time.NewTicker(10 * time.Millisecond)
- defer tick.Stop()
- poll:
- for {
- select {
- case <-tick.C:
- if finalRoutes, err = routes.ListRoutes(context.TODO(), cluster); err == nil && routeListEqual(finalRoutes, testCase.expectedRoutes) {
- break poll
- }
- case <-timeoutChan:
- t.Errorf("%d. rc.reconcile() = %v, routes:\n%v\nexpected: nil, routes:\n%v\n", i, err, flatten(finalRoutes), flatten(testCase.expectedRoutes))
- break poll
- }
- }
- }
- }
- func routeListEqual(list1, list2 []*cloudprovider.Route) bool {
- if len(list1) != len(list2) {
- return false
- }
- routeMap1 := make(map[string]*cloudprovider.Route)
- for _, route1 := range list1 {
- routeMap1[route1.Name] = route1
- }
- for _, route2 := range list2 {
- if route1, exists := routeMap1[route2.Name]; !exists || *route1 != *route2 {
- return false
- }
- }
- return true
- }
- func flatten(list []*cloudprovider.Route) []cloudprovider.Route {
- var structList []cloudprovider.Route
- for _, route := range list {
- structList = append(structList, *route)
- }
- return structList
- }
|