mirror of
https://github.com/moby/moby.git
synced 2026-01-11 10:41:43 +00:00
Add WithAPIVersion and WithAPIVersionFromEnv to be more clear on the intent, and to align with other related options and fields. Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
337 lines
11 KiB
Go
337 lines
11 KiB
Go
package service
|
|
|
|
import (
|
|
stdnet "net"
|
|
"net/netip"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/google/go-cmp/cmp/cmpopts"
|
|
"github.com/moby/moby/api/types/network"
|
|
swarmtypes "github.com/moby/moby/api/types/swarm"
|
|
"github.com/moby/moby/client"
|
|
"github.com/moby/moby/v2/daemon/libnetwork/scope"
|
|
"github.com/moby/moby/v2/integration/internal/container"
|
|
net "github.com/moby/moby/v2/integration/internal/network"
|
|
"github.com/moby/moby/v2/integration/internal/swarm"
|
|
"github.com/moby/moby/v2/integration/internal/testutils/networking"
|
|
"github.com/moby/moby/v2/internal/testutil/daemon"
|
|
"gotest.tools/v3/assert"
|
|
is "gotest.tools/v3/assert/cmp"
|
|
"gotest.tools/v3/golden"
|
|
"gotest.tools/v3/icmd"
|
|
"gotest.tools/v3/poll"
|
|
"gotest.tools/v3/skip"
|
|
)
|
|
|
|
func TestDockerNetworkConnectAliasPreV144(t *testing.T) {
|
|
ctx := setupTest(t)
|
|
|
|
d := swarm.NewSwarm(ctx, t, testEnv, daemon.WithEnvVars("DOCKER_MIN_API_VERSION=1.43"))
|
|
defer d.Stop(t)
|
|
apiClient := d.NewClientT(t, client.WithAPIVersion("1.43"))
|
|
defer apiClient.Close()
|
|
|
|
name := t.Name() + "test-alias"
|
|
net.CreateNoError(ctx, t, apiClient, name,
|
|
net.WithDriver("overlay"),
|
|
net.WithAttachable(),
|
|
)
|
|
|
|
cID1 := container.Create(ctx, t, apiClient, func(c *container.TestContainerConfig) {
|
|
c.NetworkingConfig = &network.NetworkingConfig{
|
|
EndpointsConfig: map[string]*network.EndpointSettings{
|
|
name: {},
|
|
},
|
|
}
|
|
})
|
|
|
|
_, err := apiClient.NetworkConnect(ctx, name, client.NetworkConnectOptions{
|
|
Container: cID1,
|
|
EndpointConfig: &network.EndpointSettings{
|
|
Aliases: []string{
|
|
"aaa",
|
|
},
|
|
},
|
|
})
|
|
assert.NilError(t, err)
|
|
|
|
_, err = apiClient.ContainerStart(ctx, cID1, client.ContainerStartOptions{})
|
|
assert.NilError(t, err)
|
|
|
|
ng1, err := apiClient.ContainerInspect(ctx, cID1, client.ContainerInspectOptions{})
|
|
assert.NilError(t, err)
|
|
assert.Check(t, is.Equal(len(ng1.Container.NetworkSettings.Networks[name].Aliases), 2))
|
|
assert.Check(t, is.Equal(ng1.Container.NetworkSettings.Networks[name].Aliases[0], "aaa"))
|
|
|
|
cID2 := container.Create(ctx, t, apiClient, func(c *container.TestContainerConfig) {
|
|
c.NetworkingConfig = &network.NetworkingConfig{
|
|
EndpointsConfig: map[string]*network.EndpointSettings{
|
|
name: {},
|
|
},
|
|
}
|
|
})
|
|
|
|
_, err = apiClient.NetworkConnect(ctx, name, client.NetworkConnectOptions{
|
|
Container: cID2,
|
|
EndpointConfig: &network.EndpointSettings{
|
|
Aliases: []string{
|
|
"bbb",
|
|
},
|
|
},
|
|
})
|
|
assert.NilError(t, err)
|
|
|
|
_, err = apiClient.ContainerStart(ctx, cID2, client.ContainerStartOptions{})
|
|
assert.NilError(t, err)
|
|
|
|
ng2, err := apiClient.ContainerInspect(ctx, cID2, client.ContainerInspectOptions{})
|
|
assert.NilError(t, err)
|
|
assert.Check(t, is.Equal(len(ng2.Container.NetworkSettings.Networks[name].Aliases), 2))
|
|
assert.Check(t, is.Equal(ng2.Container.NetworkSettings.Networks[name].Aliases[0], "bbb"))
|
|
}
|
|
|
|
func TestDockerNetworkReConnect(t *testing.T) {
|
|
ctx := setupTest(t)
|
|
|
|
d := swarm.NewSwarm(ctx, t, testEnv)
|
|
defer d.Stop(t)
|
|
apiClient := d.NewClientT(t)
|
|
defer apiClient.Close()
|
|
|
|
name := t.Name() + "dummyNet"
|
|
net.CreateNoError(ctx, t, apiClient, name,
|
|
net.WithDriver("overlay"),
|
|
net.WithAttachable(),
|
|
)
|
|
|
|
c1 := container.Create(ctx, t, apiClient, func(c *container.TestContainerConfig) {
|
|
c.NetworkingConfig = &network.NetworkingConfig{
|
|
EndpointsConfig: map[string]*network.EndpointSettings{
|
|
name: {},
|
|
},
|
|
}
|
|
})
|
|
|
|
_, err := apiClient.NetworkConnect(ctx, name, client.NetworkConnectOptions{
|
|
Container: c1,
|
|
EndpointConfig: &network.EndpointSettings{},
|
|
})
|
|
assert.NilError(t, err)
|
|
|
|
_, err = apiClient.ContainerStart(ctx, c1, client.ContainerStartOptions{})
|
|
assert.NilError(t, err)
|
|
|
|
n1, err := apiClient.ContainerInspect(ctx, c1, client.ContainerInspectOptions{})
|
|
assert.NilError(t, err)
|
|
|
|
_, err = apiClient.NetworkConnect(ctx, name, client.NetworkConnectOptions{
|
|
Container: c1,
|
|
EndpointConfig: &network.EndpointSettings{},
|
|
})
|
|
assert.ErrorContains(t, err, "is already attached to network")
|
|
|
|
n2, err := apiClient.ContainerInspect(ctx, c1, client.ContainerInspectOptions{})
|
|
assert.NilError(t, err)
|
|
assert.Check(t, is.DeepEqual(n1.Container, n2.Container, cmpopts.EquateComparable(netip.Addr{}, netip.Prefix{})))
|
|
}
|
|
|
|
// Check that a swarm-scoped network can't have EnableIPv4=false.
|
|
func TestSwarmNoDisableIPv4(t *testing.T) {
|
|
ctx := setupTest(t)
|
|
|
|
d := swarm.NewSwarm(ctx, t, testEnv)
|
|
defer d.Stop(t)
|
|
apiClient := d.NewClientT(t)
|
|
defer apiClient.Close()
|
|
|
|
_, err := net.Create(ctx, apiClient, "overlay-v6-only",
|
|
net.WithDriver("overlay"),
|
|
net.WithAttachable(),
|
|
net.WithIPv4(false),
|
|
)
|
|
assert.Check(t, is.ErrorContains(err, "IPv4 cannot be disabled in a Swarm scoped network"))
|
|
}
|
|
|
|
// Regression test for https://github.com/docker/cli/issues/5857
|
|
func TestSwarmScopedNetFromConfig(t *testing.T) {
|
|
skip.If(t, testEnv.IsRootless, "rootless mode doesn't support Swarm-mode")
|
|
ctx := setupTest(t)
|
|
|
|
d := swarm.NewSwarm(ctx, t, testEnv)
|
|
defer d.Stop(t)
|
|
c := d.NewClientT(t)
|
|
defer c.Close()
|
|
|
|
const configNetName = "config-net"
|
|
_ = net.CreateNoError(ctx, t, c, configNetName,
|
|
net.WithDriver("bridge"),
|
|
net.WithConfigOnly(true),
|
|
)
|
|
const swarmNetName = "swarm-net"
|
|
_, err := net.Create(ctx, c, swarmNetName,
|
|
net.WithDriver("bridge"),
|
|
net.WithConfigFrom(configNetName),
|
|
net.WithAttachable(),
|
|
net.WithScope(scope.Swarm),
|
|
)
|
|
assert.NilError(t, err)
|
|
|
|
serviceID := swarm.CreateService(ctx, t, d,
|
|
swarm.ServiceWithName("test-ssnfc"),
|
|
swarm.ServiceWithNetwork(swarmNetName),
|
|
)
|
|
defer func() {
|
|
_, err := c.ServiceRemove(ctx, serviceID, client.ServiceRemoveOptions{})
|
|
assert.NilError(t, err)
|
|
}()
|
|
|
|
poll.WaitOn(t, swarm.RunningTasksCount(ctx, c, serviceID, 1), swarm.ServicePoll)
|
|
}
|
|
|
|
// Check that, when swarm has ports mapped to the host, the iptables jump to
|
|
// DOCKER-INGRESS remains in place after a daemon restart.
|
|
// Regression test for https://github.com/moby/moby/pull/49538
|
|
func TestDockerIngressChainPosition(t *testing.T) {
|
|
skip.If(t, testEnv.IsRemoteDaemon)
|
|
skip.If(t, testEnv.IsRootless, "rootless mode doesn't support Swarm-mode")
|
|
skip.If(t, testEnv.FirewallBackendDriver() == "nftables")
|
|
skip.If(t, networking.FirewalldRunning(), "can't use firewalld in host netns to add rules in L3Segment")
|
|
ctx := setupTest(t)
|
|
|
|
// Run the test in its own netns, to avoid interfering with iptables on the test host.
|
|
const hostAddr = "192.168.111.222"
|
|
const l3SegHost = "dicp"
|
|
l3 := networking.NewL3Segment(t, "test-"+l3SegHost)
|
|
defer l3.Destroy(t)
|
|
l3.AddHost(t, l3SegHost, "ns-"+l3SegHost, "eth0", netip.MustParsePrefix(hostAddr+"/24"))
|
|
|
|
// Check the published port is accessible.
|
|
checkHTTP := func(_ poll.LogT) poll.Result {
|
|
var res *icmd.Result
|
|
// This is called from inside a "Do()" thread in the docker host's netns, but it
|
|
// uses poll.WaitOn - which runs the command in a different goroutine.
|
|
l3.Hosts[l3SegHost].Do(t, func() {
|
|
res = icmd.RunCommand("wget", "-T1", "-t1", "-O-",
|
|
"http://"+stdnet.JoinHostPort(hostAddr, "8080"))
|
|
})
|
|
// A "404 Not Found" means the server responded, but it's got nothing to serve.
|
|
if !strings.Contains(res.Stderr(), "404 Not Found") {
|
|
return poll.Continue("404 Not Found not found in: %s", res.Stderr())
|
|
}
|
|
return poll.Success()
|
|
}
|
|
|
|
// Check the jump to DOCKER-INGRESS is at the top of the DOCKER-FORWARD chain.
|
|
checkChain := func() {
|
|
t.Helper()
|
|
res := icmd.RunCommand("iptables", "-S", "DOCKER-FORWARD")
|
|
assert.NilError(t, res.Error, "stderr: %s", res.Stderr())
|
|
// Only compare the first (fixed) part of the chain - per-bridge rules may be
|
|
// re-ordered when the daemon restarts.
|
|
out := strings.SplitAfter(res.Stdout(), "\n")
|
|
if len(out) > 5 {
|
|
out = out[:5]
|
|
}
|
|
golden.Assert(t, strings.Join(out, ""), t.Name()+"_docker_forward.golden")
|
|
}
|
|
|
|
l3.Hosts[l3SegHost].Do(t, func() {
|
|
d := swarm.NewSwarm(ctx, t, testEnv, daemon.WithSwarmIptables(true))
|
|
defer d.Stop(t)
|
|
c := d.NewClientT(t)
|
|
defer c.Close()
|
|
|
|
serviceID := swarm.CreateService(ctx, t, d,
|
|
swarm.ServiceWithName("test-dicp"),
|
|
swarm.ServiceWithCommand([]string{"httpd", "-f"}),
|
|
swarm.ServiceWithEndpoint(&swarmtypes.EndpointSpec{
|
|
Ports: []swarmtypes.PortConfig{
|
|
{
|
|
Protocol: "tcp",
|
|
TargetPort: 80,
|
|
PublishedPort: 8080,
|
|
PublishMode: swarmtypes.PortConfigPublishModeIngress,
|
|
},
|
|
},
|
|
}),
|
|
)
|
|
defer func() {
|
|
_, err := c.ServiceRemove(ctx, serviceID, client.ServiceRemoveOptions{})
|
|
assert.NilError(t, err)
|
|
}()
|
|
|
|
t.Log("Waiting for the service to start")
|
|
poll.WaitOn(t, swarm.RunningTasksCount(ctx, c, serviceID, 1), swarm.ServicePoll)
|
|
t.Log("Checking http access to the service")
|
|
poll.WaitOn(t, checkHTTP, poll.WithTimeout(30*time.Second))
|
|
checkChain()
|
|
|
|
t.Log("Restarting the daemon")
|
|
d.Restart(t)
|
|
|
|
t.Log("Waiting for the service to start")
|
|
poll.WaitOn(t, swarm.RunningTasksCount(ctx, c, serviceID, 1), swarm.ServicePoll)
|
|
t.Log("Checking http access to the service")
|
|
// It takes a while before this works ...
|
|
poll.WaitOn(t, checkHTTP, poll.WithTimeout(30*time.Second))
|
|
checkChain()
|
|
})
|
|
}
|
|
|
|
func TestRestoreIngressRulesOnFirewalldReload(t *testing.T) {
|
|
skip.If(t, testEnv.IsRemoteDaemon)
|
|
skip.If(t, testEnv.IsRootless, "rootless mode doesn't support Swarm-mode")
|
|
skip.If(t, testEnv.FirewallBackendDriver() != "iptables+firewalld", "nftables backend doesn't support Swarm-mode")
|
|
skip.If(t, !networking.FirewalldRunning(), "Need firewalld to test restoration ingress rules")
|
|
ctx := setupTest(t)
|
|
|
|
// Check the published port is accessible.
|
|
checkHTTP := func(_ poll.LogT) poll.Result {
|
|
res := icmd.RunCommand("curl", "-v", "-o", "/dev/null", "-w", "%{http_code}\n",
|
|
"http://"+stdnet.JoinHostPort("localhost", "8080"))
|
|
// A "404 Not Found" means the server responded, but it's got nothing to serve.
|
|
if !strings.Contains(res.Stdout(), "404") {
|
|
return poll.Continue("404 - not found in: %s, %+v", res.Stdout(), res)
|
|
}
|
|
return poll.Success()
|
|
}
|
|
|
|
d := swarm.NewSwarm(ctx, t, testEnv, daemon.WithSwarmIptables(true))
|
|
defer d.Stop(t)
|
|
c := d.NewClientT(t)
|
|
defer c.Close()
|
|
|
|
serviceID := swarm.CreateService(ctx, t, d,
|
|
swarm.ServiceWithName("test-ingress-on-firewalld-reload"),
|
|
swarm.ServiceWithCommand([]string{"httpd", "-f"}),
|
|
swarm.ServiceWithEndpoint(&swarmtypes.EndpointSpec{
|
|
Ports: []swarmtypes.PortConfig{
|
|
{
|
|
Protocol: "tcp",
|
|
TargetPort: 80,
|
|
PublishedPort: 8080,
|
|
PublishMode: swarmtypes.PortConfigPublishModeIngress,
|
|
},
|
|
},
|
|
}),
|
|
)
|
|
defer func() {
|
|
_, err := c.ServiceRemove(ctx, serviceID, client.ServiceRemoveOptions{})
|
|
assert.NilError(t, err)
|
|
}()
|
|
|
|
t.Log("Waiting for the service to start")
|
|
poll.WaitOn(t, swarm.RunningTasksCount(ctx, c, serviceID, 1), swarm.ServicePoll)
|
|
t.Log("Checking http access to the service")
|
|
poll.WaitOn(t, checkHTTP, poll.WithTimeout(30*time.Second))
|
|
|
|
t.Log("Firewalld reload")
|
|
networking.FirewalldReload(t, d)
|
|
|
|
t.Log("Checking http access to the service")
|
|
// It takes a while before this works ...
|
|
poll.WaitOn(t, checkHTTP, poll.WithTimeout(30*time.Second))
|
|
}
|