github.com/zhuohuang-hust/src-cbuild@v0.0.0-20230105071821-c7aab3e7c840/mergeCode/libnetwork/ipvs/ipvs_test.go (about)

     1  // +build linux
     2  
     3  package ipvs
     4  
     5  import (
     6  	"fmt"
     7  	"net"
     8  	"os/exec"
     9  	"strings"
    10  	"syscall"
    11  	"testing"
    12  
    13  	"github.com/docker/libnetwork/testutils"
    14  	"github.com/stretchr/testify/assert"
    15  	"github.com/stretchr/testify/require"
    16  	"github.com/vishvananda/netlink"
    17  	"github.com/vishvananda/netlink/nl"
    18  )
    19  
    20  var (
    21  	schedMethods = []string{
    22  		RoundRobin,
    23  		LeastConnection,
    24  		DestinationHashing,
    25  		SourceHashing,
    26  	}
    27  
    28  	protocols = []string{
    29  		"TCP",
    30  		"UDP",
    31  		"FWM",
    32  	}
    33  
    34  	fwdMethods = []uint32{
    35  		ConnectionFlagMasq,
    36  		ConnectionFlagTunnel,
    37  		ConnectionFlagDirectRoute,
    38  	}
    39  
    40  	fwdMethodStrings = []string{
    41  		"Masq",
    42  		"Tunnel",
    43  		"Route",
    44  	}
    45  )
    46  
    47  func checkDestination(t *testing.T, checkPresent bool, protocol, serviceAddress, realAddress, fwdMethod string) {
    48  	var (
    49  		realServerStart bool
    50  		realServers     []string
    51  	)
    52  
    53  	out, err := exec.Command("ipvsadm", "-Ln").CombinedOutput()
    54  	require.NoError(t, err)
    55  
    56  	for _, o := range strings.Split(string(out), "\n") {
    57  		cmpStr := serviceAddress
    58  		if protocol == "FWM" {
    59  			cmpStr = " " + cmpStr
    60  		}
    61  
    62  		if strings.Contains(o, cmpStr) {
    63  			realServerStart = true
    64  			continue
    65  		}
    66  
    67  		if realServerStart {
    68  			if !strings.Contains(o, "->") {
    69  				break
    70  			}
    71  
    72  			realServers = append(realServers, o)
    73  		}
    74  	}
    75  
    76  	for _, r := range realServers {
    77  		if strings.Contains(r, realAddress) {
    78  			parts := strings.Fields(r)
    79  			assert.Equal(t, fwdMethod, parts[2])
    80  			return
    81  		}
    82  	}
    83  
    84  	if checkPresent {
    85  		t.Fatalf("Did not find the destination %s fwdMethod %s in ipvs output", realAddress, fwdMethod)
    86  	}
    87  }
    88  
    89  func checkService(t *testing.T, checkPresent bool, protocol, schedMethod, serviceAddress string) {
    90  	out, err := exec.Command("ipvsadm", "-Ln").CombinedOutput()
    91  	require.NoError(t, err)
    92  
    93  	for _, o := range strings.Split(string(out), "\n") {
    94  		cmpStr := serviceAddress
    95  		if protocol == "FWM" {
    96  			cmpStr = " " + cmpStr
    97  		}
    98  
    99  		if strings.Contains(o, cmpStr) {
   100  			parts := strings.Split(o, " ")
   101  			assert.Equal(t, protocol, parts[0])
   102  			assert.Equal(t, serviceAddress, parts[2])
   103  			assert.Equal(t, schedMethod, parts[3])
   104  
   105  			if !checkPresent {
   106  				t.Fatalf("Did not expect the service %s in ipvs output", serviceAddress)
   107  			}
   108  
   109  			return
   110  		}
   111  	}
   112  
   113  	if checkPresent {
   114  		t.Fatalf("Did not find the service %s in ipvs output", serviceAddress)
   115  	}
   116  }
   117  
   118  func TestGetFamily(t *testing.T) {
   119  	if testutils.RunningOnCircleCI() {
   120  		t.Skipf("Skipping as not supported on CIRCLE CI kernel")
   121  	}
   122  
   123  	id, err := getIPVSFamily()
   124  	require.NoError(t, err)
   125  	assert.NotEqual(t, 0, id)
   126  }
   127  
   128  func TestService(t *testing.T) {
   129  	if testutils.RunningOnCircleCI() {
   130  		t.Skipf("Skipping as not supported on CIRCLE CI kernel")
   131  	}
   132  
   133  	defer testutils.SetupTestOSContext(t)()
   134  
   135  	i, err := New("")
   136  	require.NoError(t, err)
   137  
   138  	for _, protocol := range protocols {
   139  		for _, schedMethod := range schedMethods {
   140  			var serviceAddress string
   141  
   142  			s := Service{
   143  				AddressFamily: nl.FAMILY_V4,
   144  				SchedName:     schedMethod,
   145  			}
   146  
   147  			switch protocol {
   148  			case "FWM":
   149  				s.FWMark = 1234
   150  				serviceAddress = fmt.Sprintf("%d", 1234)
   151  			case "TCP":
   152  				s.Protocol = syscall.IPPROTO_TCP
   153  				s.Port = 80
   154  				s.Address = net.ParseIP("1.2.3.4")
   155  				s.Netmask = 0xFFFFFFFF
   156  				serviceAddress = "1.2.3.4:80"
   157  			case "UDP":
   158  				s.Protocol = syscall.IPPROTO_UDP
   159  				s.Port = 53
   160  				s.Address = net.ParseIP("2.3.4.5")
   161  				serviceAddress = "2.3.4.5:53"
   162  			}
   163  
   164  			err := i.NewService(&s)
   165  			assert.NoError(t, err)
   166  			checkService(t, true, protocol, schedMethod, serviceAddress)
   167  			var lastMethod string
   168  			for _, updateSchedMethod := range schedMethods {
   169  				if updateSchedMethod == schedMethod {
   170  					continue
   171  				}
   172  
   173  				s.SchedName = updateSchedMethod
   174  				err = i.UpdateService(&s)
   175  				assert.NoError(t, err)
   176  				checkService(t, true, protocol, updateSchedMethod, serviceAddress)
   177  				lastMethod = updateSchedMethod
   178  			}
   179  
   180  			err = i.DelService(&s)
   181  			checkService(t, false, protocol, lastMethod, serviceAddress)
   182  		}
   183  	}
   184  
   185  }
   186  
   187  func createDummyInterface(t *testing.T) {
   188  	if testutils.RunningOnCircleCI() {
   189  		t.Skipf("Skipping as not supported on CIRCLE CI kernel")
   190  	}
   191  
   192  	dummy := &netlink.Dummy{
   193  		LinkAttrs: netlink.LinkAttrs{
   194  			Name: "dummy",
   195  		},
   196  	}
   197  
   198  	err := netlink.LinkAdd(dummy)
   199  	require.NoError(t, err)
   200  
   201  	dummyLink, err := netlink.LinkByName("dummy")
   202  	require.NoError(t, err)
   203  
   204  	ip, ipNet, err := net.ParseCIDR("10.1.1.1/24")
   205  	require.NoError(t, err)
   206  
   207  	ipNet.IP = ip
   208  
   209  	ipAddr := &netlink.Addr{IPNet: ipNet, Label: ""}
   210  	err = netlink.AddrAdd(dummyLink, ipAddr)
   211  	require.NoError(t, err)
   212  }
   213  
   214  func TestDestination(t *testing.T) {
   215  	defer testutils.SetupTestOSContext(t)()
   216  
   217  	createDummyInterface(t)
   218  	i, err := New("")
   219  	require.NoError(t, err)
   220  
   221  	for _, protocol := range protocols {
   222  		var serviceAddress string
   223  
   224  		s := Service{
   225  			AddressFamily: nl.FAMILY_V4,
   226  			SchedName:     RoundRobin,
   227  		}
   228  
   229  		switch protocol {
   230  		case "FWM":
   231  			s.FWMark = 1234
   232  			serviceAddress = fmt.Sprintf("%d", 1234)
   233  		case "TCP":
   234  			s.Protocol = syscall.IPPROTO_TCP
   235  			s.Port = 80
   236  			s.Address = net.ParseIP("1.2.3.4")
   237  			s.Netmask = 0xFFFFFFFF
   238  			serviceAddress = "1.2.3.4:80"
   239  		case "UDP":
   240  			s.Protocol = syscall.IPPROTO_UDP
   241  			s.Port = 53
   242  			s.Address = net.ParseIP("2.3.4.5")
   243  			serviceAddress = "2.3.4.5:53"
   244  		}
   245  
   246  		err := i.NewService(&s)
   247  		assert.NoError(t, err)
   248  		checkService(t, true, protocol, RoundRobin, serviceAddress)
   249  
   250  		s.SchedName = ""
   251  		for j, fwdMethod := range fwdMethods {
   252  			d1 := Destination{
   253  				AddressFamily:   nl.FAMILY_V4,
   254  				Address:         net.ParseIP("10.1.1.2"),
   255  				Port:            5000,
   256  				Weight:          1,
   257  				ConnectionFlags: fwdMethod,
   258  			}
   259  
   260  			realAddress := "10.1.1.2:5000"
   261  			err := i.NewDestination(&s, &d1)
   262  			assert.NoError(t, err)
   263  			checkDestination(t, true, protocol, serviceAddress, realAddress, fwdMethodStrings[j])
   264  			d2 := Destination{
   265  				AddressFamily:   nl.FAMILY_V4,
   266  				Address:         net.ParseIP("10.1.1.3"),
   267  				Port:            5000,
   268  				Weight:          1,
   269  				ConnectionFlags: fwdMethod,
   270  			}
   271  
   272  			realAddress = "10.1.1.3:5000"
   273  			err = i.NewDestination(&s, &d2)
   274  			assert.NoError(t, err)
   275  			checkDestination(t, true, protocol, serviceAddress, realAddress, fwdMethodStrings[j])
   276  
   277  			d3 := Destination{
   278  				AddressFamily:   nl.FAMILY_V4,
   279  				Address:         net.ParseIP("10.1.1.4"),
   280  				Port:            5000,
   281  				Weight:          1,
   282  				ConnectionFlags: fwdMethod,
   283  			}
   284  
   285  			realAddress = "10.1.1.4:5000"
   286  			err = i.NewDestination(&s, &d3)
   287  			assert.NoError(t, err)
   288  			checkDestination(t, true, protocol, serviceAddress, realAddress, fwdMethodStrings[j])
   289  
   290  			for m, updateFwdMethod := range fwdMethods {
   291  				if updateFwdMethod == fwdMethod {
   292  					continue
   293  				}
   294  				d1.ConnectionFlags = updateFwdMethod
   295  				realAddress = "10.1.1.2:5000"
   296  				err = i.UpdateDestination(&s, &d1)
   297  				assert.NoError(t, err)
   298  				checkDestination(t, true, protocol, serviceAddress, realAddress, fwdMethodStrings[m])
   299  
   300  				d2.ConnectionFlags = updateFwdMethod
   301  				realAddress = "10.1.1.3:5000"
   302  				err = i.UpdateDestination(&s, &d2)
   303  				assert.NoError(t, err)
   304  				checkDestination(t, true, protocol, serviceAddress, realAddress, fwdMethodStrings[m])
   305  
   306  				d3.ConnectionFlags = updateFwdMethod
   307  				realAddress = "10.1.1.4:5000"
   308  				err = i.UpdateDestination(&s, &d3)
   309  				assert.NoError(t, err)
   310  				checkDestination(t, true, protocol, serviceAddress, realAddress, fwdMethodStrings[m])
   311  			}
   312  
   313  			err = i.DelDestination(&s, &d1)
   314  			assert.NoError(t, err)
   315  			err = i.DelDestination(&s, &d2)
   316  			assert.NoError(t, err)
   317  			err = i.DelDestination(&s, &d3)
   318  			assert.NoError(t, err)
   319  		}
   320  	}
   321  }