github.com/Prakhar-Agarwal-byte/moby@v0.0.0-20231027092010-a14e3e8ab87e/integration/network/macvlan/macvlan_test.go (about)

     1  //go:build !windows
     2  
     3  package macvlan // import "github.com/Prakhar-Agarwal-byte/moby/integration/network/macvlan"
     4  
     5  import (
     6  	"context"
     7  	"strings"
     8  	"testing"
     9  
    10  	"github.com/Prakhar-Agarwal-byte/moby/client"
    11  	"github.com/Prakhar-Agarwal-byte/moby/integration/internal/container"
    12  	net "github.com/Prakhar-Agarwal-byte/moby/integration/internal/network"
    13  	n "github.com/Prakhar-Agarwal-byte/moby/integration/network"
    14  	"github.com/Prakhar-Agarwal-byte/moby/testutil"
    15  	"github.com/Prakhar-Agarwal-byte/moby/testutil/daemon"
    16  	"gotest.tools/v3/assert"
    17  	"gotest.tools/v3/skip"
    18  )
    19  
    20  func TestDockerNetworkMacvlanPersistance(t *testing.T) {
    21  	// verify the driver automatically provisions the 802.1q link (dm-dummy0.60)
    22  	skip.If(t, testEnv.IsRemoteDaemon)
    23  	skip.If(t, testEnv.IsRootless, "rootless mode has different view of network")
    24  
    25  	ctx := testutil.StartSpan(baseContext, t)
    26  
    27  	d := daemon.New(t)
    28  	d.StartWithBusybox(ctx, t)
    29  	defer d.Stop(t)
    30  
    31  	master := "dm-dummy0"
    32  	n.CreateMasterDummy(ctx, t, master)
    33  	defer n.DeleteInterface(ctx, t, master)
    34  
    35  	c := d.NewClientT(t)
    36  
    37  	netName := "dm-persist"
    38  	net.CreateNoError(ctx, t, c, netName,
    39  		net.WithMacvlan("dm-dummy0.60"),
    40  	)
    41  	assert.Check(t, n.IsNetworkAvailable(ctx, c, netName))
    42  	d.Restart(t)
    43  	assert.Check(t, n.IsNetworkAvailable(ctx, c, netName))
    44  }
    45  
    46  func TestDockerNetworkMacvlan(t *testing.T) {
    47  	skip.If(t, testEnv.IsRemoteDaemon)
    48  	skip.If(t, testEnv.IsRootless, "rootless mode has different view of network")
    49  
    50  	ctx := testutil.StartSpan(baseContext, t)
    51  
    52  	for _, tc := range []struct {
    53  		name string
    54  		test func(context.Context, client.APIClient) func(*testing.T)
    55  	}{
    56  		{
    57  			name: "Subinterface",
    58  			test: testMacvlanSubinterface,
    59  		}, {
    60  			name: "OverlapParent",
    61  			test: testMacvlanOverlapParent,
    62  		}, {
    63  			name: "NilParent",
    64  			test: testMacvlanNilParent,
    65  		}, {
    66  			name: "InternalMode",
    67  			test: testMacvlanInternalMode,
    68  		}, {
    69  			name: "MultiSubnet",
    70  			test: testMacvlanMultiSubnet,
    71  		}, {
    72  			name: "Addressing",
    73  			test: testMacvlanAddressing,
    74  		},
    75  	} {
    76  		tc := tc
    77  		t.Run(tc.name, func(t *testing.T) {
    78  			testutil.StartSpan(ctx, t)
    79  
    80  			d := daemon.New(t)
    81  			t.Cleanup(func() { d.Stop(t) })
    82  			d.StartWithBusybox(ctx, t)
    83  			c := d.NewClientT(t)
    84  
    85  			tc.test(ctx, c)
    86  		})
    87  
    88  		// FIXME(vdemeester) clean network
    89  	}
    90  }
    91  
    92  func testMacvlanOverlapParent(ctx context.Context, client client.APIClient) func(*testing.T) {
    93  	return func(t *testing.T) {
    94  		// verify the same parent interface cannot be used if already in use by an existing network
    95  		master := "dm-dummy0"
    96  		n.CreateMasterDummy(ctx, t, master)
    97  		defer n.DeleteInterface(ctx, t, master)
    98  
    99  		netName := "dm-subinterface"
   100  		parentName := "dm-dummy0.40"
   101  		net.CreateNoError(ctx, t, client, netName,
   102  			net.WithMacvlan(parentName),
   103  		)
   104  		assert.Check(t, n.IsNetworkAvailable(ctx, client, netName))
   105  
   106  		_, err := net.Create(ctx, client, "dm-parent-net-overlap",
   107  			net.WithMacvlan(parentName),
   108  		)
   109  		assert.Check(t, err != nil)
   110  
   111  		// delete the network while preserving the parent link
   112  		err = client.NetworkRemove(ctx, netName)
   113  		assert.NilError(t, err)
   114  
   115  		assert.Check(t, n.IsNetworkNotAvailable(ctx, client, netName))
   116  		// verify the network delete did not delete the predefined link
   117  		n.LinkExists(ctx, t, master)
   118  	}
   119  }
   120  
   121  func testMacvlanSubinterface(ctx context.Context, client client.APIClient) func(*testing.T) {
   122  	return func(t *testing.T) {
   123  		// verify the same parent interface cannot be used if already in use by an existing network
   124  		master := "dm-dummy0"
   125  		parentName := "dm-dummy0.20"
   126  		n.CreateMasterDummy(ctx, t, master)
   127  		defer n.DeleteInterface(ctx, t, master)
   128  		n.CreateVlanInterface(ctx, t, master, parentName, "20")
   129  
   130  		netName := "dm-subinterface"
   131  		net.CreateNoError(ctx, t, client, netName,
   132  			net.WithMacvlan(parentName),
   133  		)
   134  		assert.Check(t, n.IsNetworkAvailable(ctx, client, netName))
   135  
   136  		// delete the network while preserving the parent link
   137  		err := client.NetworkRemove(ctx, netName)
   138  		assert.NilError(t, err)
   139  
   140  		assert.Check(t, n.IsNetworkNotAvailable(ctx, client, netName))
   141  		// verify the network delete did not delete the predefined link
   142  		n.LinkExists(ctx, t, parentName)
   143  	}
   144  }
   145  
   146  func testMacvlanNilParent(ctx context.Context, client client.APIClient) func(*testing.T) {
   147  	return func(t *testing.T) {
   148  		// macvlan bridge mode - dummy parent interface is provisioned dynamically
   149  		netName := "dm-nil-parent"
   150  		net.CreateNoError(ctx, t, client, netName,
   151  			net.WithMacvlan(""),
   152  		)
   153  		assert.Check(t, n.IsNetworkAvailable(ctx, client, netName))
   154  
   155  		id1 := container.Run(ctx, t, client, container.WithNetworkMode(netName))
   156  		id2 := container.Run(ctx, t, client, container.WithNetworkMode(netName))
   157  
   158  		_, err := container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1})
   159  		assert.Check(t, err == nil)
   160  	}
   161  }
   162  
   163  func testMacvlanInternalMode(ctx context.Context, client client.APIClient) func(*testing.T) {
   164  	return func(t *testing.T) {
   165  		// macvlan bridge mode - dummy parent interface is provisioned dynamically
   166  		netName := "dm-internal"
   167  		net.CreateNoError(ctx, t, client, netName,
   168  			net.WithMacvlan(""),
   169  			net.WithInternal(),
   170  		)
   171  		assert.Check(t, n.IsNetworkAvailable(ctx, client, netName))
   172  
   173  		id1 := container.Run(ctx, t, client, container.WithNetworkMode(netName))
   174  		id2 := container.Run(ctx, t, client, container.WithNetworkMode(netName))
   175  
   176  		result, _ := container.Exec(ctx, client, id1, []string{"ping", "-c", "1", "8.8.8.8"})
   177  		assert.Check(t, strings.Contains(result.Combined(), "Network is unreachable"))
   178  
   179  		_, err := container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1})
   180  		assert.Check(t, err == nil)
   181  	}
   182  }
   183  
   184  func testMacvlanMultiSubnet(ctx context.Context, client client.APIClient) func(*testing.T) {
   185  	return func(t *testing.T) {
   186  		netName := "dualstackbridge"
   187  		net.CreateNoError(ctx, t, client, netName,
   188  			net.WithMacvlan(""),
   189  			net.WithIPv6(),
   190  			net.WithIPAM("172.28.100.0/24", ""),
   191  			net.WithIPAM("172.28.102.0/24", "172.28.102.254"),
   192  			net.WithIPAM("2001:db8:abc2::/64", ""),
   193  			net.WithIPAM("2001:db8:abc4::/64", "2001:db8:abc4::254"),
   194  		)
   195  
   196  		assert.Check(t, n.IsNetworkAvailable(ctx, client, netName))
   197  
   198  		// start dual stack containers and verify the user specified --ip and --ip6 addresses on subnets 172.28.100.0/24 and 2001:db8:abc2::/64
   199  		id1 := container.Run(ctx, t, client,
   200  			container.WithNetworkMode("dualstackbridge"),
   201  			container.WithIPv4("dualstackbridge", "172.28.100.20"),
   202  			container.WithIPv6("dualstackbridge", "2001:db8:abc2::20"),
   203  		)
   204  		id2 := container.Run(ctx, t, client,
   205  			container.WithNetworkMode("dualstackbridge"),
   206  			container.WithIPv4("dualstackbridge", "172.28.100.21"),
   207  			container.WithIPv6("dualstackbridge", "2001:db8:abc2::21"),
   208  		)
   209  		c1, err := client.ContainerInspect(ctx, id1)
   210  		assert.NilError(t, err)
   211  
   212  		// verify ipv4 connectivity to the explicit --ipv address second to first
   213  		_, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", c1.NetworkSettings.Networks["dualstackbridge"].IPAddress})
   214  		assert.NilError(t, err)
   215  		// verify ipv6 connectivity to the explicit --ipv6 address second to first
   216  		_, err = container.Exec(ctx, client, id2, []string{"ping6", "-c", "1", c1.NetworkSettings.Networks["dualstackbridge"].GlobalIPv6Address})
   217  		assert.NilError(t, err)
   218  
   219  		// start dual stack containers and verify the user specified --ip and --ip6 addresses on subnets 172.28.102.0/24 and 2001:db8:abc4::/64
   220  		id3 := container.Run(ctx, t, client,
   221  			container.WithNetworkMode("dualstackbridge"),
   222  			container.WithIPv4("dualstackbridge", "172.28.102.20"),
   223  			container.WithIPv6("dualstackbridge", "2001:db8:abc4::20"),
   224  		)
   225  		id4 := container.Run(ctx, t, client,
   226  			container.WithNetworkMode("dualstackbridge"),
   227  			container.WithIPv4("dualstackbridge", "172.28.102.21"),
   228  			container.WithIPv6("dualstackbridge", "2001:db8:abc4::21"),
   229  		)
   230  		c3, err := client.ContainerInspect(ctx, id3)
   231  		assert.NilError(t, err)
   232  
   233  		// verify ipv4 connectivity to the explicit --ipv address from third to fourth
   234  		_, err = container.Exec(ctx, client, id4, []string{"ping", "-c", "1", c3.NetworkSettings.Networks["dualstackbridge"].IPAddress})
   235  		assert.NilError(t, err)
   236  		// verify ipv6 connectivity to the explicit --ipv6 address from third to fourth
   237  		_, err = container.Exec(ctx, client, id4, []string{"ping6", "-c", "1", c3.NetworkSettings.Networks["dualstackbridge"].GlobalIPv6Address})
   238  		assert.NilError(t, err)
   239  
   240  		// Inspect the v4 gateway to ensure the proper default GW was assigned
   241  		assert.Equal(t, c1.NetworkSettings.Networks["dualstackbridge"].Gateway, "172.28.100.1")
   242  		// Inspect the v6 gateway to ensure the proper default GW was assigned
   243  		assert.Equal(t, c1.NetworkSettings.Networks["dualstackbridge"].IPv6Gateway, "2001:db8:abc2::1")
   244  		// Inspect the v4 gateway to ensure the proper explicitly assigned default GW was assigned
   245  		assert.Equal(t, c3.NetworkSettings.Networks["dualstackbridge"].Gateway, "172.28.102.254")
   246  		// Inspect the v6 gateway to ensure the proper explicitly assigned default GW was assigned
   247  		assert.Equal(t, c3.NetworkSettings.Networks["dualstackbridge"].IPv6Gateway, "2001:db8:abc4::254")
   248  	}
   249  }
   250  
   251  func testMacvlanAddressing(ctx context.Context, client client.APIClient) func(*testing.T) {
   252  	return func(t *testing.T) {
   253  		// Ensure the default gateways, next-hops and default dev devices are properly set
   254  		netName := "dualstackbridge"
   255  		net.CreateNoError(ctx, t, client, netName,
   256  			net.WithMacvlan(""),
   257  			net.WithIPv6(),
   258  			net.WithOption("macvlan_mode", "bridge"),
   259  			net.WithIPAM("172.28.130.0/24", ""),
   260  			net.WithIPAM("2001:db8:abca::/64", "2001:db8:abca::254"),
   261  		)
   262  		assert.Check(t, n.IsNetworkAvailable(ctx, client, netName))
   263  
   264  		id1 := container.Run(ctx, t, client,
   265  			container.WithNetworkMode("dualstackbridge"),
   266  		)
   267  
   268  		// Validate macvlan bridge mode defaults gateway sets the default IPAM next-hop inferred from the subnet
   269  		result, err := container.Exec(ctx, client, id1, []string{"ip", "route"})
   270  		assert.NilError(t, err)
   271  		assert.Check(t, strings.Contains(result.Combined(), "default via 172.28.130.1 dev eth0"))
   272  		// Validate macvlan bridge mode sets the v6 gateway to the user specified default gateway/next-hop
   273  		result, err = container.Exec(ctx, client, id1, []string{"ip", "-6", "route"})
   274  		assert.NilError(t, err)
   275  		assert.Check(t, strings.Contains(result.Combined(), "default via 2001:db8:abca::254 dev eth0"))
   276  	}
   277  }