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  }