github.com/moby/docker@v26.1.3+incompatible/integration/network/service_test.go (about)

     1  package network // import "github.com/docker/docker/integration/network"
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/docker/docker/api/types"
     9  	swarmtypes "github.com/docker/docker/api/types/swarm"
    10  	"github.com/docker/docker/client"
    11  	"github.com/docker/docker/integration/internal/network"
    12  	"github.com/docker/docker/integration/internal/swarm"
    13  	"github.com/docker/docker/testutil"
    14  	"github.com/docker/docker/testutil/daemon"
    15  	"gotest.tools/v3/assert"
    16  	"gotest.tools/v3/icmd"
    17  	"gotest.tools/v3/poll"
    18  	"gotest.tools/v3/skip"
    19  )
    20  
    21  // delInterface removes given network interface
    22  func delInterface(ctx context.Context, t *testing.T, ifName string) {
    23  	t.Helper()
    24  	testutil.RunCommand(ctx, "ip", "link", "delete", ifName).Assert(t, icmd.Success)
    25  	testutil.RunCommand(ctx, "iptables", "-t", "nat", "--flush").Assert(t, icmd.Success)
    26  	testutil.RunCommand(ctx, "iptables", "--flush").Assert(t, icmd.Success)
    27  }
    28  
    29  func TestDaemonRestartWithLiveRestore(t *testing.T) {
    30  	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
    31  	skip.If(t, testEnv.IsRemoteDaemon)
    32  	skip.If(t, testEnv.IsRootless, "rootless mode has different view of network")
    33  	ctx := testutil.StartSpan(baseContext, t)
    34  
    35  	d := daemon.New(t)
    36  	defer d.Stop(t)
    37  	d.Start(t)
    38  
    39  	c := d.NewClientT(t)
    40  	defer c.Close()
    41  
    42  	// Verify bridge network's subnet
    43  	out, err := c.NetworkInspect(ctx, "bridge", types.NetworkInspectOptions{})
    44  	assert.NilError(t, err)
    45  	subnet := out.IPAM.Config[0].Subnet
    46  
    47  	d.Restart(t,
    48  		"--live-restore=true",
    49  		"--default-address-pool", "base=175.30.0.0/16,size=16",
    50  		"--default-address-pool", "base=175.33.0.0/16,size=24",
    51  	)
    52  
    53  	out1, err := c.NetworkInspect(ctx, "bridge", types.NetworkInspectOptions{})
    54  	assert.NilError(t, err)
    55  	// Make sure docker0 doesn't get override with new IP in live restore case
    56  	assert.Equal(t, out1.IPAM.Config[0].Subnet, subnet)
    57  }
    58  
    59  func TestDaemonDefaultNetworkPools(t *testing.T) {
    60  	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
    61  	// Remove docker0 bridge and the start daemon defining the predefined address pools
    62  	skip.If(t, testEnv.IsRemoteDaemon)
    63  	skip.If(t, testEnv.IsRootless, "rootless mode has different view of network")
    64  	ctx := testutil.StartSpan(baseContext, t)
    65  
    66  	defaultNetworkBridge := "docker0"
    67  	delInterface(ctx, t, defaultNetworkBridge)
    68  	d := daemon.New(t)
    69  	defer d.Stop(t)
    70  	d.Start(t,
    71  		"--default-address-pool", "base=175.30.0.0/16,size=16",
    72  		"--default-address-pool", "base=175.33.0.0/16,size=24",
    73  	)
    74  
    75  	c := d.NewClientT(t)
    76  	defer c.Close()
    77  
    78  	// Verify bridge network's subnet
    79  	out, err := c.NetworkInspect(ctx, "bridge", types.NetworkInspectOptions{})
    80  	assert.NilError(t, err)
    81  	assert.Equal(t, out.IPAM.Config[0].Subnet, "175.30.0.0/16")
    82  
    83  	// Create a bridge network and verify its subnet is the second default pool
    84  	name := "elango" + t.Name()
    85  	network.CreateNoError(ctx, t, c, name,
    86  		network.WithDriver("bridge"),
    87  	)
    88  	out, err = c.NetworkInspect(ctx, name, types.NetworkInspectOptions{})
    89  	assert.NilError(t, err)
    90  	assert.Equal(t, out.IPAM.Config[0].Subnet, "175.33.0.0/24")
    91  
    92  	// Create a bridge network and verify its subnet is the third default pool
    93  	name = "saanvi" + t.Name()
    94  	network.CreateNoError(ctx, t, c, name,
    95  		network.WithDriver("bridge"),
    96  	)
    97  	out, err = c.NetworkInspect(ctx, name, types.NetworkInspectOptions{})
    98  	assert.NilError(t, err)
    99  	assert.Equal(t, out.IPAM.Config[0].Subnet, "175.33.1.0/24")
   100  	delInterface(ctx, t, defaultNetworkBridge)
   101  }
   102  
   103  func TestDaemonRestartWithExistingNetwork(t *testing.T) {
   104  	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
   105  	skip.If(t, testEnv.IsRemoteDaemon)
   106  	skip.If(t, testEnv.IsRootless, "rootless mode has different view of network")
   107  	ctx := testutil.StartSpan(baseContext, t)
   108  
   109  	defaultNetworkBridge := "docker0"
   110  	d := daemon.New(t)
   111  	d.Start(t)
   112  	defer d.Stop(t)
   113  	c := d.NewClientT(t)
   114  	defer c.Close()
   115  
   116  	// Create a bridge network
   117  	name := "elango" + t.Name()
   118  	network.CreateNoError(ctx, t, c, name,
   119  		network.WithDriver("bridge"),
   120  	)
   121  
   122  	// Verify bridge network's subnet
   123  	out, err := c.NetworkInspect(ctx, name, types.NetworkInspectOptions{})
   124  	assert.NilError(t, err)
   125  	networkip := out.IPAM.Config[0].Subnet
   126  
   127  	// Restart daemon with default address pool option
   128  	d.Restart(t,
   129  		"--default-address-pool", "base=175.30.0.0/16,size=16",
   130  		"--default-address-pool", "base=175.33.0.0/16,size=24")
   131  
   132  	out1, err := c.NetworkInspect(ctx, name, types.NetworkInspectOptions{})
   133  	assert.NilError(t, err)
   134  	assert.Equal(t, out1.IPAM.Config[0].Subnet, networkip)
   135  	delInterface(ctx, t, defaultNetworkBridge)
   136  }
   137  
   138  func TestDaemonRestartWithExistingNetworkWithDefaultPoolRange(t *testing.T) {
   139  	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
   140  	skip.If(t, testEnv.IsRemoteDaemon)
   141  	skip.If(t, testEnv.IsRootless, "rootless mode has different view of network")
   142  
   143  	ctx := testutil.StartSpan(baseContext, t)
   144  
   145  	defaultNetworkBridge := "docker0"
   146  	d := daemon.New(t)
   147  	d.Start(t)
   148  	defer d.Stop(t)
   149  	c := d.NewClientT(t)
   150  	defer c.Close()
   151  
   152  	// Create a bridge network
   153  	name := "elango" + t.Name()
   154  	network.CreateNoError(ctx, t, c, name,
   155  		network.WithDriver("bridge"),
   156  	)
   157  
   158  	// Verify bridge network's subnet
   159  	out, err := c.NetworkInspect(ctx, name, types.NetworkInspectOptions{})
   160  	assert.NilError(t, err)
   161  	networkip := out.IPAM.Config[0].Subnet
   162  
   163  	// Create a bridge network
   164  	name = "sthira" + t.Name()
   165  	network.CreateNoError(ctx, t, c, name,
   166  		network.WithDriver("bridge"),
   167  	)
   168  	out, err = c.NetworkInspect(ctx, name, types.NetworkInspectOptions{})
   169  	assert.NilError(t, err)
   170  	networkip2 := out.IPAM.Config[0].Subnet
   171  
   172  	// Restart daemon with default address pool option
   173  	d.Restart(t,
   174  		"--default-address-pool", "base=175.18.0.0/16,size=16",
   175  		"--default-address-pool", "base=175.19.0.0/16,size=24",
   176  	)
   177  
   178  	// Create a bridge network
   179  	name = "saanvi" + t.Name()
   180  	network.CreateNoError(ctx, t, c, name,
   181  		network.WithDriver("bridge"),
   182  	)
   183  	out1, err := c.NetworkInspect(ctx, name, types.NetworkInspectOptions{})
   184  	assert.NilError(t, err)
   185  
   186  	assert.Check(t, out1.IPAM.Config[0].Subnet != networkip)
   187  	assert.Check(t, out1.IPAM.Config[0].Subnet != networkip2)
   188  	delInterface(ctx, t, defaultNetworkBridge)
   189  }
   190  
   191  func TestDaemonWithBipAndDefaultNetworkPool(t *testing.T) {
   192  	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
   193  	skip.If(t, testEnv.IsRemoteDaemon)
   194  	skip.If(t, testEnv.IsRootless, "rootless mode has different view of network")
   195  
   196  	ctx := testutil.StartSpan(baseContext, t)
   197  
   198  	defaultNetworkBridge := "docker0"
   199  	d := daemon.New(t)
   200  	defer d.Stop(t)
   201  	d.Start(t,
   202  		"--bip=172.60.0.1/16",
   203  		"--default-address-pool", "base=175.30.0.0/16,size=16",
   204  		"--default-address-pool", "base=175.33.0.0/16,size=24",
   205  	)
   206  
   207  	c := d.NewClientT(t)
   208  	defer c.Close()
   209  
   210  	// Verify bridge network's subnet
   211  	out, err := c.NetworkInspect(ctx, "bridge", types.NetworkInspectOptions{})
   212  	assert.NilError(t, err)
   213  	// Make sure BIP IP doesn't get override with new default address pool .
   214  	assert.Equal(t, out.IPAM.Config[0].Subnet, "172.60.0.0/16")
   215  	delInterface(ctx, t, defaultNetworkBridge)
   216  }
   217  
   218  func TestServiceWithPredefinedNetwork(t *testing.T) {
   219  	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
   220  	skip.If(t, testEnv.IsRootless, "rootless mode doesn't support Swarm-mode")
   221  	ctx := setupTest(t)
   222  
   223  	d := swarm.NewSwarm(ctx, t, testEnv)
   224  	defer d.Stop(t)
   225  	c := d.NewClientT(t)
   226  	defer c.Close()
   227  
   228  	hostName := "host"
   229  	var instances uint64 = 1
   230  	serviceName := "TestService" + t.Name()
   231  
   232  	serviceID := swarm.CreateService(ctx, t, d,
   233  		swarm.ServiceWithReplicas(instances),
   234  		swarm.ServiceWithName(serviceName),
   235  		swarm.ServiceWithNetwork(hostName),
   236  	)
   237  
   238  	poll.WaitOn(t, swarm.RunningTasksCount(ctx, c, serviceID, instances), swarm.ServicePoll)
   239  
   240  	_, _, err := c.ServiceInspectWithRaw(ctx, serviceID, types.ServiceInspectOptions{})
   241  	assert.NilError(t, err)
   242  
   243  	err = c.ServiceRemove(ctx, serviceID)
   244  	assert.NilError(t, err)
   245  }
   246  
   247  const ingressNet = "ingress"
   248  
   249  func TestServiceRemoveKeepsIngressNetwork(t *testing.T) {
   250  	t.Skip("FLAKY_TEST")
   251  	skip.If(t, testEnv.IsRootless, "rootless mode doesn't support Swarm-mode")
   252  
   253  	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
   254  	ctx := setupTest(t)
   255  
   256  	d := swarm.NewSwarm(ctx, t, testEnv)
   257  	defer d.Stop(t)
   258  	c := d.NewClientT(t)
   259  	defer c.Close()
   260  
   261  	poll.WaitOn(t, swarmIngressReady(ctx, c), swarm.NetworkPoll)
   262  
   263  	var instances uint64 = 1
   264  
   265  	serviceID := swarm.CreateService(ctx, t, d,
   266  		swarm.ServiceWithReplicas(instances),
   267  		swarm.ServiceWithName(t.Name()+"-service"),
   268  		swarm.ServiceWithEndpoint(&swarmtypes.EndpointSpec{
   269  			Ports: []swarmtypes.PortConfig{
   270  				{
   271  					Protocol:    swarmtypes.PortConfigProtocolTCP,
   272  					TargetPort:  80,
   273  					PublishMode: swarmtypes.PortConfigPublishModeIngress,
   274  				},
   275  			},
   276  		}),
   277  	)
   278  
   279  	poll.WaitOn(t, swarm.RunningTasksCount(ctx, c, serviceID, instances), swarm.ServicePoll)
   280  
   281  	_, _, err := c.ServiceInspectWithRaw(ctx, serviceID, types.ServiceInspectOptions{})
   282  	assert.NilError(t, err)
   283  
   284  	err = c.ServiceRemove(ctx, serviceID)
   285  	assert.NilError(t, err)
   286  
   287  	poll.WaitOn(t, noServices(ctx, c), swarm.ServicePoll)
   288  	poll.WaitOn(t, swarm.NoTasks(ctx, c), swarm.ServicePoll)
   289  
   290  	// Ensure that "ingress" is not removed or corrupted
   291  	time.Sleep(10 * time.Second)
   292  	netInfo, err := c.NetworkInspect(ctx, ingressNet, types.NetworkInspectOptions{
   293  		Verbose: true,
   294  		Scope:   "swarm",
   295  	})
   296  	assert.NilError(t, err, "Ingress network was removed after removing service!")
   297  	assert.Assert(t, len(netInfo.Containers) != 0, "No load balancing endpoints in ingress network")
   298  	assert.Assert(t, len(netInfo.Peers) != 0, "No peers (including self) in ingress network")
   299  	_, ok := netInfo.Containers["ingress-sbox"]
   300  	assert.Assert(t, ok, "ingress-sbox not present in ingress network")
   301  }
   302  
   303  //nolint:unused // for some reason, the "unused" linter marks this function as "unused"
   304  func swarmIngressReady(ctx context.Context, client client.NetworkAPIClient) func(log poll.LogT) poll.Result {
   305  	return func(log poll.LogT) poll.Result {
   306  		netInfo, err := client.NetworkInspect(ctx, ingressNet, types.NetworkInspectOptions{
   307  			Verbose: true,
   308  			Scope:   "swarm",
   309  		})
   310  		if err != nil {
   311  			return poll.Error(err)
   312  		}
   313  		np := len(netInfo.Peers)
   314  		nc := len(netInfo.Containers)
   315  		if np == 0 || nc == 0 {
   316  			return poll.Continue("ingress not ready: %d peers and %d containers", nc, np)
   317  		}
   318  		_, ok := netInfo.Containers["ingress-sbox"]
   319  		if !ok {
   320  			return poll.Continue("ingress not ready: does not contain the ingress-sbox")
   321  		}
   322  		return poll.Success()
   323  	}
   324  }
   325  
   326  func noServices(ctx context.Context, client client.ServiceAPIClient) func(log poll.LogT) poll.Result {
   327  	return func(log poll.LogT) poll.Result {
   328  		services, err := client.ServiceList(ctx, types.ServiceListOptions{})
   329  		switch {
   330  		case err != nil:
   331  			return poll.Error(err)
   332  		case len(services) == 0:
   333  			return poll.Success()
   334  		default:
   335  			return poll.Continue("waiting for all services to be removed: service count at %d", len(services))
   336  		}
   337  	}
   338  }
   339  
   340  func TestServiceWithDataPathPortInit(t *testing.T) {
   341  	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
   342  	skip.If(t, testEnv.IsRootless, "rootless mode doesn't support Swarm-mode")
   343  	ctx := setupTest(t)
   344  
   345  	var datapathPort uint32 = 7777
   346  	d := swarm.NewSwarm(ctx, t, testEnv, daemon.WithSwarmDataPathPort(datapathPort))
   347  	c := d.NewClientT(t)
   348  	// Create a overlay network
   349  	name := "saanvisthira" + t.Name()
   350  	overlayID := network.CreateNoError(ctx, t, c, name,
   351  		network.WithDriver("overlay"))
   352  
   353  	var instances uint64 = 1
   354  	serviceID := swarm.CreateService(ctx, t, d,
   355  		swarm.ServiceWithReplicas(instances),
   356  		swarm.ServiceWithName(name),
   357  		swarm.ServiceWithNetwork(name),
   358  	)
   359  
   360  	poll.WaitOn(t, swarm.RunningTasksCount(ctx, c, serviceID, instances), swarm.ServicePoll)
   361  
   362  	info := d.Info(t)
   363  	assert.Equal(t, info.Swarm.Cluster.DataPathPort, datapathPort)
   364  	err := c.ServiceRemove(ctx, serviceID)
   365  	assert.NilError(t, err)
   366  	poll.WaitOn(t, noServices(ctx, c), swarm.ServicePoll)
   367  	poll.WaitOn(t, swarm.NoTasks(ctx, c), swarm.ServicePoll)
   368  	err = c.NetworkRemove(ctx, overlayID)
   369  	assert.NilError(t, err)
   370  	c.Close()
   371  	err = d.SwarmLeave(ctx, t, true)
   372  	assert.NilError(t, err)
   373  	d.Stop(t)
   374  
   375  	// Clean up , set it back to original one to make sure other tests don't fail
   376  	// call without datapath port option.
   377  	d = swarm.NewSwarm(ctx, t, testEnv)
   378  	defer d.Stop(t)
   379  	nc := d.NewClientT(t)
   380  	defer nc.Close()
   381  	// Create a overlay network
   382  	name = "not-saanvisthira" + t.Name()
   383  	overlayID = network.CreateNoError(ctx, t, nc, name,
   384  		network.WithDriver("overlay"))
   385  
   386  	serviceID = swarm.CreateService(ctx, t, d,
   387  		swarm.ServiceWithReplicas(instances),
   388  		swarm.ServiceWithName(name),
   389  		swarm.ServiceWithNetwork(name),
   390  	)
   391  
   392  	poll.WaitOn(t, swarm.RunningTasksCount(ctx, nc, serviceID, instances), swarm.ServicePoll)
   393  
   394  	info = d.Info(t)
   395  	var defaultDataPathPort uint32 = 4789
   396  	assert.Equal(t, info.Swarm.Cluster.DataPathPort, defaultDataPathPort)
   397  	err = nc.ServiceRemove(ctx, serviceID)
   398  	assert.NilError(t, err)
   399  	poll.WaitOn(t, noServices(ctx, nc), swarm.ServicePoll)
   400  	poll.WaitOn(t, swarm.NoTasks(ctx, nc), swarm.ServicePoll)
   401  	err = nc.NetworkRemove(ctx, overlayID)
   402  	assert.NilError(t, err)
   403  	err = d.SwarmLeave(ctx, t, true)
   404  	assert.NilError(t, err)
   405  }
   406  
   407  func TestServiceWithDefaultAddressPoolInit(t *testing.T) {
   408  	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
   409  	skip.If(t, testEnv.IsRootless, "rootless mode doesn't support Swarm-mode")
   410  	ctx := setupTest(t)
   411  
   412  	d := swarm.NewSwarm(ctx, t, testEnv,
   413  		daemon.WithSwarmDefaultAddrPool([]string{"20.20.0.0/16"}),
   414  		daemon.WithSwarmDefaultAddrPoolSubnetSize(24))
   415  	defer d.Stop(t)
   416  	cli := d.NewClientT(t)
   417  	defer cli.Close()
   418  
   419  	// Create a overlay network
   420  	name := "sthira" + t.Name()
   421  	overlayID := network.CreateNoError(ctx, t, cli, name,
   422  		network.WithDriver("overlay"),
   423  	)
   424  
   425  	var instances uint64 = 1
   426  	serviceName := "TestService" + t.Name()
   427  	serviceID := swarm.CreateService(ctx, t, d,
   428  		swarm.ServiceWithReplicas(instances),
   429  		swarm.ServiceWithName(serviceName),
   430  		swarm.ServiceWithNetwork(name),
   431  	)
   432  
   433  	poll.WaitOn(t, swarm.RunningTasksCount(ctx, cli, serviceID, instances), swarm.ServicePoll)
   434  
   435  	_, _, err := cli.ServiceInspectWithRaw(ctx, serviceID, types.ServiceInspectOptions{})
   436  	assert.NilError(t, err)
   437  
   438  	out, err := cli.NetworkInspect(ctx, overlayID, types.NetworkInspectOptions{Verbose: true})
   439  	assert.NilError(t, err)
   440  	t.Logf("%s: NetworkInspect: %+v", t.Name(), out)
   441  	assert.Assert(t, len(out.IPAM.Config) > 0)
   442  	// As of docker/swarmkit#2890, the ingress network uses the default address
   443  	// pool (whereas before, the subnet for the ingress network was hard-coded.
   444  	// This means that the ingress network gets the subnet 20.20.0.0/24, and
   445  	// the network we just created gets subnet 20.20.1.0/24.
   446  	assert.Equal(t, out.IPAM.Config[0].Subnet, "20.20.1.0/24")
   447  
   448  	// Also inspect ingress network and make sure its in the same subnet
   449  	out, err = cli.NetworkInspect(ctx, "ingress", types.NetworkInspectOptions{Verbose: true})
   450  	assert.NilError(t, err)
   451  	assert.Assert(t, len(out.IPAM.Config) > 0)
   452  	assert.Equal(t, out.IPAM.Config[0].Subnet, "20.20.0.0/24")
   453  
   454  	err = cli.ServiceRemove(ctx, serviceID)
   455  	poll.WaitOn(t, noServices(ctx, cli), swarm.ServicePoll)
   456  	poll.WaitOn(t, swarm.NoTasks(ctx, cli), swarm.ServicePoll)
   457  	assert.NilError(t, err)
   458  	err = cli.NetworkRemove(ctx, overlayID)
   459  	assert.NilError(t, err)
   460  	err = d.SwarmLeave(ctx, t, true)
   461  	assert.NilError(t, err)
   462  }