github.com/Mirantis/virtlet@v1.5.2-0.20191204181327-1659b8a48e9b/pkg/nettools/calico_test.go (about) 1 /* 2 Copyright 2018 Mirantis 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package nettools 18 19 import ( 20 "log" 21 "net" 22 "reflect" 23 "testing" 24 25 "github.com/containernetworking/cni/pkg/ns" 26 cnitypes "github.com/containernetworking/cni/pkg/types" 27 cnicurrent "github.com/containernetworking/cni/pkg/types/current" 28 "github.com/davecgh/go-spew/spew" 29 "github.com/vishvananda/netlink" 30 ) 31 32 func addCalicoRoutes(t *testing.T, origContVeth netlink.Link) { 33 addTestRoute(t, &netlink.Route{ 34 Dst: parseAddr("169.254.1.1/32").IPNet, 35 Scope: SCOPE_LINK, 36 LinkIndex: origContVeth.Attrs().Index, 37 }) 38 addTestRoute(t, &netlink.Route{ 39 Gw: parseAddr("169.254.1.1/32").IPNet.IP, 40 Scope: SCOPE_UNIVERSE, 41 }) 42 } 43 44 func withDummyNetworkNamespace(t *testing.T, toRun func(dummyNS ns.NetNS, dummyInfo *cnicurrent.Result)) { 45 withHostAndContNS(t, func(hostNS, contNS ns.NetNS) { 46 origHostVeth, origContVeth, err := CreateEscapeVethPair(contNS, "eth0", 1500) 47 if err != nil { 48 log.Panicf("failed to create veth pair: %v", err) 49 } 50 // need to force hostNS here because of side effects of NetNS.Do() 51 // See https://github.com/vishvananda/netns/issues/17 52 inNS(hostNS, "hostNS", func() { 53 origHostVeth = setupLink(outerHwAddr, origHostVeth) 54 }) 55 var dummyInfo *cnicurrent.Result 56 inNS(contNS, "contNS", func() { 57 origContVeth = setupLink(innerHwAddr, origContVeth) 58 59 if err = netlink.AddrAdd(origContVeth, parseAddr("10.1.90.100/24")); err != nil { 60 log.Panicf("failed to add addr for origContVeth: %v", err) 61 } 62 63 addCalicoRoutes(t, origContVeth) 64 65 dummyInfo, err = ExtractLinkInfo(origContVeth, contNS.Path()) 66 if err != nil { 67 log.Panicf("failed to grab dummy interface info: %v", err) 68 } 69 }) 70 toRun(contNS, dummyInfo) 71 }) 72 73 } 74 75 func TestCalicoDetection(t *testing.T) { 76 for _, tc := range []struct { 77 name string 78 routes []netlink.Route 79 haveCalico bool 80 haveCalicoGateway bool 81 }{ 82 { 83 name: "no routes", 84 haveCalico: false, 85 haveCalicoGateway: false, 86 }, 87 { 88 name: "non-calico default route", 89 routes: []netlink.Route{ 90 { 91 // LinkIndex: origContVeth.Attrs().Index, 92 Gw: parseAddr("10.1.90.1/24").IPNet.IP, 93 Scope: SCOPE_UNIVERSE, 94 }, 95 }, 96 haveCalico: false, 97 haveCalicoGateway: false, 98 }, 99 { 100 name: "calico w/o default gw", 101 routes: []netlink.Route{ 102 { 103 Dst: parseAddr("169.254.1.1/32").IPNet, 104 Scope: SCOPE_LINK, 105 }, 106 }, 107 haveCalico: true, 108 haveCalicoGateway: false, 109 }, 110 { 111 name: "calico with default gw", 112 routes: []netlink.Route{ 113 { 114 Dst: parseAddr("169.254.1.1/32").IPNet, 115 Scope: SCOPE_LINK, 116 }, 117 { 118 Gw: parseAddr("169.254.1.1/32").IPNet.IP, 119 Scope: SCOPE_UNIVERSE, 120 }, 121 }, 122 haveCalico: true, 123 haveCalicoGateway: true, 124 }, 125 } { 126 t.Run(tc.name, func(t *testing.T) { 127 withFakeCNIVeth(t, defaultMTU, func(hostNS, contNS ns.NetNS, origHostVeth, origContVeth netlink.Link) { 128 for _, r := range tc.routes { 129 if r.Scope == SCOPE_LINK { 130 r.LinkIndex = origContVeth.Attrs().Index 131 } 132 addTestRoute(t, &r) 133 } 134 haveCalico, haveCalicoGateway, err := DetectCalico(origContVeth) 135 if err != nil { 136 log.Panicf("DetectCalico(): %v", err) 137 } 138 if haveCalico != tc.haveCalico { 139 log.Panicf("haveCalico is expected to be %v but is %v", haveCalico, tc.haveCalico) 140 } 141 if haveCalicoGateway != tc.haveCalicoGateway { 142 log.Panicf("haveCalico is expected to be %v but is %v", haveCalicoGateway, tc.haveCalicoGateway) 143 } 144 }) 145 }) 146 } 147 } 148 149 func TestFixCalicoNetworking(t *testing.T) { 150 withDummyNetworkNamespace(t, func(dummyNS ns.NetNS, dummyInfo *cnicurrent.Result) { 151 withFakeCNIVeth(t, defaultMTU, func(hostNS, contNS ns.NetNS, origHostVeth, origContVeth netlink.Link) { 152 addCalicoRoutes(t, origContVeth) 153 info, err := ExtractLinkInfo(origContVeth, contNS.Path()) 154 if err != nil { 155 log.Panicf("failed to grab interface info: %v", err) 156 } 157 // reuse 2nd copy of the CNI result as the dummy network config 158 if err := FixCalicoNetworking(info, 24, func() (*cnicurrent.Result, string, error) { 159 return dummyInfo, dummyNS.Path(), nil 160 }); err != nil { 161 log.Panicf("FixCalicoNetworking(): %v", err) 162 } 163 expectedResult := &cnicurrent.Result{ 164 Interfaces: []*cnicurrent.Interface{ 165 { 166 Name: "eth0", 167 Mac: innerHwAddr, 168 Sandbox: contNS.Path(), 169 }, 170 }, 171 IPs: []*cnicurrent.IPConfig{ 172 { 173 Version: "4", 174 Interface: 0, 175 Address: net.IPNet{ 176 IP: net.IP{10, 1, 90, 5}, 177 Mask: net.IPMask{255, 255, 255, 0}, 178 }, 179 Gateway: net.IP{10, 1, 90, 100}, 180 }, 181 }, 182 Routes: []*cnitypes.Route{ 183 { 184 Dst: net.IPNet{ 185 IP: net.IP{0, 0, 0, 0}, 186 Mask: net.IPMask{0, 0, 0, 0}, 187 }, 188 GW: net.IP{10, 1, 90, 100}, 189 }, 190 }, 191 } 192 if !reflect.DeepEqual(info, expectedResult) { 193 t.Errorf("interface info mismatch. Expected:\n%s\nActual:\n%s", 194 spew.Sdump(expectedResult), spew.Sdump(info)) 195 } 196 }) 197 }) 198 }