mirror of
https://github.com/moby/moby.git
synced 2026-01-11 18:51:37 +00:00
Compare commits
24 Commits
00166d05d9
...
docker-v29
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4612690e23 | ||
|
|
6280a80f32 | ||
|
|
9cbafeac46 | ||
|
|
1fa8a31556 | ||
|
|
3c6e5f0f5a | ||
|
|
e9ff10bf36 | ||
|
|
7faaa44e18 | ||
|
|
e9f9d7a81e | ||
|
|
28665176e5 | ||
|
|
43f91f775a | ||
|
|
bb0d79cb1a | ||
|
|
198b5e3ed5 | ||
|
|
2ad480ccf5 | ||
|
|
cb6c1c3aca | ||
|
|
2a18530fb2 | ||
|
|
14c4e0d73a | ||
|
|
d23fd38f8b | ||
|
|
3076530aa6 | ||
|
|
7a3cdd2c86 | ||
|
|
d7b6f3a7d3 | ||
|
|
7f5694cda1 | ||
|
|
0e2d804e48 | ||
|
|
7242ccd7a0 | ||
|
|
b6705d5e1a |
@@ -228,9 +228,10 @@ else
|
||||
# When running with --firewall-backend=nftables, IP forwarding needs to be enabled
|
||||
# because the daemon won't enable it. IP forwarding is harmless in the rootless
|
||||
# netns, there's only a single external interface and only Docker uses the netns.
|
||||
# So, always enable IPv4 and IPv6 forwarding.
|
||||
# So, always enable IPv4 and IPv6 forwarding. But ignore failure to enable IPv6
|
||||
# forwarding, for hosts with IPv6 disabled.
|
||||
sysctl -w net.ipv4.ip_forward=1
|
||||
sysctl -w net.ipv6.conf.all.forwarding=1
|
||||
sysctl -w net.ipv6.conf.all.forwarding=1 || true
|
||||
|
||||
exec "$dockerd" "$@"
|
||||
fi
|
||||
|
||||
@@ -540,8 +540,8 @@ func validateEndpointSettings(nw *libnetwork.Network, nwName string, epConfig *n
|
||||
errs = normalizeEndpointIPAMConfig(errs, ipamConfig)
|
||||
|
||||
if nw != nil {
|
||||
_, _, v4Configs, v6Configs := nw.IpamConfig()
|
||||
errs = validateIPAMConfigIsInRange(errs, ipamConfig, v4Configs, v6Configs)
|
||||
v4Info, v6Info := nw.IpamInfo()
|
||||
errs = validateIPAMConfigIsInRange(errs, ipamConfig, v4Info, v6Info)
|
||||
}
|
||||
|
||||
if sysctls, ok := epConfig.DriverOpts[netlabel.EndpointSysctls]; ok {
|
||||
@@ -598,36 +598,28 @@ func normalizeEndpointIPAMConfig(errs []error, cfg *networktypes.EndpointIPAMCon
|
||||
}
|
||||
|
||||
// validateIPAMConfigIsInRange checks whether static IP addresses are valid in a specific network.
|
||||
func validateIPAMConfigIsInRange(errs []error, cfg *networktypes.EndpointIPAMConfig, v4Subnets, v6Subnets []*libnetwork.IpamConf) []error {
|
||||
if err := validateEndpointIPAddress(cfg.IPv4Address, v4Subnets); err != nil {
|
||||
func validateIPAMConfigIsInRange(errs []error, cfg *networktypes.EndpointIPAMConfig, v4Info, v6Info []*libnetwork.IpamInfo) []error {
|
||||
if err := validateEndpointIPAddress(cfg.IPv4Address, v4Info); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
if err := validateEndpointIPAddress(cfg.IPv6Address, v6Subnets); err != nil {
|
||||
if err := validateEndpointIPAddress(cfg.IPv6Address, v6Info); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
func validateEndpointIPAddress(epAddr netip.Addr, ipamSubnets []*libnetwork.IpamConf) error {
|
||||
func validateEndpointIPAddress(epAddr netip.Addr, ipamInfo []*libnetwork.IpamInfo) error {
|
||||
if !epAddr.IsValid() {
|
||||
return nil
|
||||
}
|
||||
|
||||
var staticSubnet bool
|
||||
for _, subnet := range ipamSubnets {
|
||||
if subnet.IsStatic() {
|
||||
staticSubnet = true
|
||||
if subnet.Contains(epAddr) {
|
||||
return nil
|
||||
}
|
||||
for _, subnet := range ipamInfo {
|
||||
if subnet.Pool.Contains(epAddr.AsSlice()) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if staticSubnet {
|
||||
return fmt.Errorf("no configured subnet or ip-range contain the IP address %s", epAddr)
|
||||
}
|
||||
|
||||
return errors.New("user specified IP address is supported only when connecting to networks with user configured subnets")
|
||||
return fmt.Errorf("no configured subnet contains IP address %s", epAddr)
|
||||
}
|
||||
|
||||
// cleanOperationalData resets the operational data from the passed endpoint settings
|
||||
|
||||
@@ -3,6 +3,7 @@ package daemon
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net"
|
||||
"net/netip"
|
||||
"testing"
|
||||
|
||||
@@ -10,6 +11,7 @@ import (
|
||||
networktypes "github.com/moby/moby/api/types/network"
|
||||
"github.com/moby/moby/v2/daemon/container"
|
||||
"github.com/moby/moby/v2/daemon/libnetwork"
|
||||
"github.com/moby/moby/v2/daemon/libnetwork/driverapi"
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
)
|
||||
@@ -57,12 +59,12 @@ func buildNetwork(t *testing.T, config map[string]any) *libnetwork.Network {
|
||||
return nw
|
||||
}
|
||||
|
||||
func TestEndpointIPAMConfigWithOutOfRangeAddrs(t *testing.T) {
|
||||
func TestEndpointIPAMInfoWithOutOfRangeAddrs(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
ipamConfig *networktypes.EndpointIPAMConfig
|
||||
v4Subnets []*libnetwork.IpamConf
|
||||
v6Subnets []*libnetwork.IpamConf
|
||||
v4Pool string
|
||||
v6Pool string
|
||||
expectedErrors []string
|
||||
}{
|
||||
{
|
||||
@@ -72,12 +74,8 @@ func TestEndpointIPAMConfigWithOutOfRangeAddrs(t *testing.T) {
|
||||
IPv6Address: netip.MustParseAddr("2a01:d2:af:420b:25c1:1816:bb33:855c"),
|
||||
LinkLocalIPs: []netip.Addr{netip.MustParseAddr("169.254.169.254"), netip.MustParseAddr("fe80::42:a8ff:fe33:6230")},
|
||||
},
|
||||
v4Subnets: []*libnetwork.IpamConf{
|
||||
{PreferredPool: "192.168.100.0/24"},
|
||||
},
|
||||
v6Subnets: []*libnetwork.IpamConf{
|
||||
{PreferredPool: "2a01:d2:af:420b:25c1:1816:bb33::/112"},
|
||||
},
|
||||
v4Pool: "192.168.100.0/24",
|
||||
v6Pool: "2a01:d2:af:420b:25c1:1816:bb33::/112",
|
||||
},
|
||||
{
|
||||
name: "static addresses out of range",
|
||||
@@ -85,39 +83,28 @@ func TestEndpointIPAMConfigWithOutOfRangeAddrs(t *testing.T) {
|
||||
IPv4Address: netip.MustParseAddr("192.168.100.10"),
|
||||
IPv6Address: netip.MustParseAddr("2a01:d2:af:420b:25c1:1816:bb33:855c"),
|
||||
},
|
||||
v4Subnets: []*libnetwork.IpamConf{
|
||||
{PreferredPool: "192.168.255.0/24"},
|
||||
},
|
||||
v6Subnets: []*libnetwork.IpamConf{
|
||||
{PreferredPool: "2001:db8::/112"},
|
||||
},
|
||||
v4Pool: "192.168.255.0/24",
|
||||
v6Pool: "2001:db8::/112",
|
||||
expectedErrors: []string{
|
||||
"no configured subnet or ip-range contain the IP address 192.168.100.10",
|
||||
"no configured subnet or ip-range contain the IP address 2a01:d2:af:420b:25c1:1816:bb33:855c",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "static addresses with dynamic network subnets",
|
||||
ipamConfig: &networktypes.EndpointIPAMConfig{
|
||||
IPv4Address: netip.MustParseAddr("192.168.100.10"),
|
||||
IPv6Address: netip.MustParseAddr("2a01:d2:af:420b:25c1:1816:bb33:855c"),
|
||||
},
|
||||
v4Subnets: []*libnetwork.IpamConf{
|
||||
{},
|
||||
},
|
||||
v6Subnets: []*libnetwork.IpamConf{
|
||||
{},
|
||||
},
|
||||
expectedErrors: []string{
|
||||
"user specified IP address is supported only when connecting to networks with user configured subnets",
|
||||
"user specified IP address is supported only when connecting to networks with user configured subnets",
|
||||
"no configured subnet contains IP address 192.168.100.10",
|
||||
"no configured subnet contains IP address 2a01:d2:af:420b:25c1:1816:bb33:855c",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
errs := validateIPAMConfigIsInRange(nil, tc.ipamConfig, tc.v4Subnets, tc.v6Subnets)
|
||||
_, v4Pool, err := net.ParseCIDR(tc.v4Pool)
|
||||
assert.NilError(t, err)
|
||||
v4Info := []*libnetwork.IpamInfo{
|
||||
{IPAMData: driverapi.IPAMData{Pool: v4Pool}},
|
||||
}
|
||||
_, v6Pool, err := net.ParseCIDR(tc.v6Pool)
|
||||
assert.NilError(t, err)
|
||||
v6Info := []*libnetwork.IpamInfo{
|
||||
{IPAMData: driverapi.IPAMData{Pool: v6Pool}},
|
||||
}
|
||||
errs := validateIPAMConfigIsInRange(nil, tc.ipamConfig, v4Info, v6Info)
|
||||
if tc.expectedErrors == nil {
|
||||
assert.NilError(t, errors.Join(errs...))
|
||||
return
|
||||
@@ -125,7 +112,7 @@ func TestEndpointIPAMConfigWithOutOfRangeAddrs(t *testing.T) {
|
||||
|
||||
assert.Check(t, len(errs) == len(tc.expectedErrors), "errs: %+v", errs)
|
||||
|
||||
err := errors.Join(errs...)
|
||||
err = errors.Join(errs...)
|
||||
for _, expected := range tc.expectedErrors {
|
||||
assert.Check(t, is.ErrorContains(err, expected))
|
||||
}
|
||||
|
||||
@@ -873,7 +873,7 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S
|
||||
}
|
||||
d.configStore.Store(cfgStore)
|
||||
|
||||
imgStoreChoice, err := determineImageStoreChoice(config)
|
||||
imgStoreChoice, err := determineImageStoreChoice(config, determineImageStoreChoiceOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1098,7 +1098,7 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S
|
||||
return nil, err
|
||||
}
|
||||
|
||||
driverName := chooseDriver(ctx, cfgStore.GraphDriver, imgStoreChoice)
|
||||
driverName := getDriverOverride(ctx, cfgStore.GraphDriver, imgStoreChoice)
|
||||
|
||||
var migrationConfig migration.Config
|
||||
if imgStoreChoice.IsGraphDriver() {
|
||||
|
||||
@@ -48,14 +48,14 @@ func (c imageStoreChoice) IsExplicit() bool {
|
||||
}
|
||||
}
|
||||
|
||||
// chooseDriver determines the storage driver name based on environment variables,
|
||||
// getDriverOverride determines the storage driver name based on environment variables,
|
||||
// configuration, and platform-specific logic.
|
||||
// On Windows we don't support the environment variable, or a user supplied graphdriver,
|
||||
// but it is allowed when using snapshotters.
|
||||
// Unix platforms however run a single graphdriver for all containers, and it can
|
||||
// be set through an environment variable, a daemon start parameter, or chosen through
|
||||
// initialization of the layerstore through driver priority order for example.
|
||||
func chooseDriver(ctx context.Context, cfgGraphDriver string, imgStoreChoice imageStoreChoice) string {
|
||||
func getDriverOverride(ctx context.Context, cfgGraphDriver string, imgStoreChoice imageStoreChoice) string {
|
||||
driverName := os.Getenv("DOCKER_DRIVER")
|
||||
if driverName == "" {
|
||||
driverName = cfgGraphDriver
|
||||
@@ -85,9 +85,25 @@ func chooseDriver(ctx context.Context, cfgGraphDriver string, imgStoreChoice ima
|
||||
return driverName
|
||||
}
|
||||
|
||||
func determineImageStoreChoice(cfgStore *config.Config) (imageStoreChoice, error) {
|
||||
type determineImageStoreChoiceOptions struct {
|
||||
hasPriorDriver func(root string) bool
|
||||
isRegisteredGraphdriver func(driverName string) bool
|
||||
runtimeOS string
|
||||
}
|
||||
|
||||
func determineImageStoreChoice(cfgStore *config.Config, opts determineImageStoreChoiceOptions) (imageStoreChoice, error) {
|
||||
if opts.hasPriorDriver == nil {
|
||||
opts.hasPriorDriver = graphdriver.HasPriorDriver
|
||||
}
|
||||
if opts.isRegisteredGraphdriver == nil {
|
||||
opts.isRegisteredGraphdriver = graphdriver.IsRegistered
|
||||
}
|
||||
if opts.runtimeOS == "" {
|
||||
opts.runtimeOS = runtime.GOOS
|
||||
}
|
||||
|
||||
out := imageStoreChoiceContainerd
|
||||
if runtime.GOOS == "windows" {
|
||||
if opts.runtimeOS == "windows" {
|
||||
out = imageStoreChoiceGraphdriver
|
||||
}
|
||||
|
||||
@@ -111,44 +127,27 @@ func determineImageStoreChoice(cfgStore *config.Config) (imageStoreChoice, error
|
||||
out = imageStoreChoiceGraphdriverExplicit
|
||||
}
|
||||
|
||||
if out == imageStoreChoiceContainerd {
|
||||
if opts.hasPriorDriver(cfgStore.Root) {
|
||||
return imageStoreChoiceGraphdriverPrior, nil
|
||||
}
|
||||
}
|
||||
|
||||
if driverName != "" {
|
||||
if !out.IsExplicit() {
|
||||
switch driverName {
|
||||
case "vfs", "overlay2":
|
||||
out = imageStoreChoiceGraphdriverExplicit
|
||||
case "btrfs":
|
||||
// The btrfs driver is not heavily used in containerd and has no
|
||||
// advantage over overlayfs anymore since overlay works fine.
|
||||
// If btrfs is explicitly chosen, the user most likely means graphdrivers.
|
||||
out = imageStoreChoiceGraphdriverExplicit
|
||||
}
|
||||
if !out.IsExplicit() && opts.isRegisteredGraphdriver(driverName) {
|
||||
return imageStoreChoiceGraphdriverExplicit, nil
|
||||
}
|
||||
if out.IsGraphDriver() {
|
||||
if graphdriver.IsRegistered(driverName) {
|
||||
if opts.isRegisteredGraphdriver(driverName) {
|
||||
return imageStoreChoiceGraphdriverExplicit, nil
|
||||
} else {
|
||||
} else if out.IsExplicit() {
|
||||
return imageStoreChoiceGraphdriverExplicit, fmt.Errorf("graphdriver is explicitly enabled but %q is not registered, %v %v", driverName, cfgStore.Features, os.Getenv("TEST_INTEGRATION_USE_GRAPHDRIVER"))
|
||||
}
|
||||
}
|
||||
|
||||
if runtime.GOOS == "windows" && !out.IsExplicit() {
|
||||
switch driverName {
|
||||
case "windows":
|
||||
return imageStoreChoiceContainerdExplicit, nil
|
||||
case "windowsfilter":
|
||||
return imageStoreChoiceGraphdriverExplicit, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Assume snapshotter is chosen
|
||||
return imageStoreChoiceContainerdExplicit, nil
|
||||
}
|
||||
|
||||
if out == imageStoreChoiceContainerd {
|
||||
if graphdriver.HasPriorDriver(cfgStore.Root) {
|
||||
return imageStoreChoiceGraphdriverPrior, nil
|
||||
}
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
269
daemon/image_store_choice_test.go
Normal file
269
daemon/image_store_choice_test.go
Normal file
@@ -0,0 +1,269 @@
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
"github.com/moby/moby/v2/daemon/config"
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
)
|
||||
|
||||
func TestDetermineImageStoreChoice(t *testing.T) {
|
||||
str := func(s string) *string {
|
||||
return &s
|
||||
}
|
||||
|
||||
type testCase struct {
|
||||
name string
|
||||
envDockerDriver *string
|
||||
envTestUseGraphDriver *string
|
||||
priorGraphDriver bool
|
||||
cfg *config.Config
|
||||
expectedChoice imageStoreChoice
|
||||
expectError bool
|
||||
skipPlatform string
|
||||
onlyPlatform string
|
||||
}
|
||||
|
||||
tests := []testCase{
|
||||
{
|
||||
name: "containerd-snapshotter feature enabled",
|
||||
cfg: &config.Config{
|
||||
CommonConfig: config.CommonConfig{
|
||||
Features: map[string]bool{
|
||||
"containerd-snapshotter": true,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedChoice: imageStoreChoiceContainerdExplicit,
|
||||
},
|
||||
{
|
||||
name: "containerd-snapshotter feature disabled",
|
||||
cfg: &config.Config{
|
||||
CommonConfig: config.CommonConfig{
|
||||
Features: map[string]bool{
|
||||
"containerd-snapshotter": false,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedChoice: imageStoreChoiceGraphdriverExplicit,
|
||||
},
|
||||
{
|
||||
name: "TEST_INTEGRATION_USE_GRAPHDRIVER env var set",
|
||||
envTestUseGraphDriver: str("1"),
|
||||
cfg: &config.Config{
|
||||
CommonConfig: config.CommonConfig{
|
||||
Features: map[string]bool{},
|
||||
},
|
||||
},
|
||||
expectedChoice: imageStoreChoiceGraphdriverExplicit,
|
||||
},
|
||||
{
|
||||
name: "TEST_INTEGRATION_USE_GRAPHDRIVER takes precedence over feature flag",
|
||||
envTestUseGraphDriver: str("1"),
|
||||
cfg: &config.Config{
|
||||
CommonConfig: config.CommonConfig{
|
||||
Features: map[string]bool{
|
||||
"containerd-snapshotter": true,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedChoice: imageStoreChoiceGraphdriverExplicit,
|
||||
},
|
||||
{
|
||||
name: "native driver in config",
|
||||
cfg: &config.Config{
|
||||
CommonConfig: config.CommonConfig{
|
||||
GraphDriver: "native",
|
||||
Features: map[string]bool{},
|
||||
},
|
||||
},
|
||||
expectedChoice: imageStoreChoiceContainerdExplicit,
|
||||
},
|
||||
{
|
||||
name: "vfs driver in config",
|
||||
cfg: &config.Config{
|
||||
CommonConfig: config.CommonConfig{
|
||||
GraphDriver: "vfs",
|
||||
Features: map[string]bool{},
|
||||
},
|
||||
},
|
||||
expectedChoice: imageStoreChoiceGraphdriverExplicit,
|
||||
},
|
||||
{
|
||||
name: "custom snapshotter",
|
||||
envDockerDriver: str("my-custom-snapshotter"),
|
||||
cfg: &config.Config{
|
||||
CommonConfig: config.CommonConfig{
|
||||
Features: map[string]bool{},
|
||||
},
|
||||
},
|
||||
expectedChoice: imageStoreChoiceContainerdExplicit,
|
||||
},
|
||||
}
|
||||
|
||||
nonWindows := []testCase{
|
||||
{
|
||||
name: "default containerd on non-Windows",
|
||||
cfg: &config.Config{
|
||||
CommonConfig: config.CommonConfig{
|
||||
Features: map[string]bool{},
|
||||
},
|
||||
},
|
||||
expectedChoice: imageStoreChoiceContainerd,
|
||||
},
|
||||
}
|
||||
|
||||
for _, gd := range []string{"fuse-overlayfs", "overlay2", "btrfs", "zfs"} {
|
||||
nonWindows = append(nonWindows, testCase{
|
||||
name: gd + " driver in config",
|
||||
cfg: &config.Config{
|
||||
CommonConfig: config.CommonConfig{
|
||||
GraphDriver: gd,
|
||||
Features: map[string]bool{},
|
||||
},
|
||||
},
|
||||
expectedChoice: imageStoreChoiceGraphdriverExplicit,
|
||||
})
|
||||
|
||||
nonWindows = append(nonWindows, testCase{
|
||||
name: gd + " driver in config with prior data",
|
||||
cfg: &config.Config{
|
||||
CommonConfig: config.CommonConfig{
|
||||
GraphDriver: gd,
|
||||
Features: map[string]bool{},
|
||||
},
|
||||
},
|
||||
priorGraphDriver: true,
|
||||
expectedChoice: imageStoreChoiceGraphdriverPrior,
|
||||
})
|
||||
|
||||
nonWindows = append(nonWindows, testCase{
|
||||
name: gd + " driver in config with containerd snapshotter feature enabled",
|
||||
cfg: &config.Config{
|
||||
CommonConfig: config.CommonConfig{
|
||||
GraphDriver: gd,
|
||||
Features: map[string]bool{
|
||||
"containerd-snapshotter": true,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedChoice: imageStoreChoiceContainerdExplicit,
|
||||
})
|
||||
|
||||
nonWindows = append(nonWindows, testCase{
|
||||
name: gd + " driver in config with containerd snapshotter feature disabled",
|
||||
cfg: &config.Config{
|
||||
CommonConfig: config.CommonConfig{
|
||||
GraphDriver: gd,
|
||||
Features: map[string]bool{
|
||||
"containerd-snapshotter": false,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedChoice: imageStoreChoiceGraphdriverExplicit,
|
||||
})
|
||||
|
||||
nonWindows = append(nonWindows, testCase{
|
||||
name: gd + " driver in config with TEST_INTEGRATION_USE_GRAPHDRIVER env var set",
|
||||
envTestUseGraphDriver: str("1"),
|
||||
cfg: &config.Config{
|
||||
CommonConfig: config.CommonConfig{
|
||||
GraphDriver: gd,
|
||||
Features: map[string]bool{},
|
||||
},
|
||||
},
|
||||
expectedChoice: imageStoreChoiceGraphdriverExplicit,
|
||||
})
|
||||
}
|
||||
|
||||
windows := []testCase{
|
||||
{
|
||||
name: "default graphdriver on Windows",
|
||||
cfg: &config.Config{
|
||||
CommonConfig: config.CommonConfig{
|
||||
Features: map[string]bool{},
|
||||
},
|
||||
},
|
||||
expectedChoice: imageStoreChoiceGraphdriver,
|
||||
},
|
||||
{
|
||||
name: "windows driver on Windows",
|
||||
envDockerDriver: str("windows"),
|
||||
cfg: &config.Config{
|
||||
CommonConfig: config.CommonConfig{
|
||||
Features: map[string]bool{},
|
||||
},
|
||||
},
|
||||
expectedChoice: imageStoreChoiceContainerdExplicit,
|
||||
},
|
||||
{
|
||||
name: "windowsfilter driver on Windows",
|
||||
envDockerDriver: str("windowsfilter"),
|
||||
cfg: &config.Config{
|
||||
CommonConfig: config.CommonConfig{
|
||||
Features: map[string]bool{},
|
||||
},
|
||||
},
|
||||
expectedChoice: imageStoreChoiceGraphdriverExplicit,
|
||||
},
|
||||
}
|
||||
|
||||
for i := range nonWindows {
|
||||
nonWindows[i].skipPlatform = "windows"
|
||||
}
|
||||
tests = append(tests, nonWindows...)
|
||||
|
||||
for i := range windows {
|
||||
windows[i].onlyPlatform = "windows"
|
||||
}
|
||||
tests = append(tests, windows...)
|
||||
|
||||
registeredDrivers := []string{"fuse-overlayfs", "overlay2", "btrfs", "zfs", "vfs"}
|
||||
windowsRegisteredDrivers := []string{"vfs", "windowsfilter"}
|
||||
|
||||
for _, os := range []string{"linux", "windows"} {
|
||||
for _, tc := range tests {
|
||||
if tc.skipPlatform != "" && os == tc.skipPlatform {
|
||||
continue
|
||||
}
|
||||
if tc.onlyPlatform != "" && os != tc.onlyPlatform {
|
||||
continue
|
||||
}
|
||||
|
||||
t.Run(os+"/"+tc.name, func(t *testing.T) {
|
||||
if tc.envDockerDriver != nil {
|
||||
t.Setenv("DOCKER_DRIVER", *tc.envDockerDriver)
|
||||
} else {
|
||||
t.Setenv("DOCKER_DRIVER", "")
|
||||
}
|
||||
if tc.envTestUseGraphDriver != nil {
|
||||
t.Setenv("TEST_INTEGRATION_USE_GRAPHDRIVER", *tc.envTestUseGraphDriver)
|
||||
} else {
|
||||
t.Setenv("TEST_INTEGRATION_USE_GRAPHDRIVER", "")
|
||||
}
|
||||
|
||||
choice, err := determineImageStoreChoice(tc.cfg, determineImageStoreChoiceOptions{
|
||||
runtimeOS: os,
|
||||
hasPriorDriver: func(root string) bool {
|
||||
return tc.priorGraphDriver
|
||||
},
|
||||
isRegisteredGraphdriver: func(driverName string) bool {
|
||||
if os == "windows" {
|
||||
return slices.Contains(windowsRegisteredDrivers, driverName)
|
||||
}
|
||||
return slices.Contains(registeredDrivers, driverName)
|
||||
},
|
||||
})
|
||||
if tc.expectError {
|
||||
assert.Error(t, err, "expected an error but got none")
|
||||
} else {
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
||||
assert.Check(t, is.Equal(tc.expectedChoice, choice))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,7 +48,10 @@ func (e *imageExporterMobyWrapper) Resolve(ctx context.Context, id int, exporter
|
||||
return nil, err
|
||||
}
|
||||
exporterAttrs[string(exptypes.OptKeyName)] = strings.Join(reposAndTags, ",")
|
||||
exporterAttrs[string(exptypes.OptKeyUnpack)] = "true"
|
||||
|
||||
if _, has := exporterAttrs[string(exptypes.OptKeyUnpack)]; !has {
|
||||
exporterAttrs[string(exptypes.OptKeyUnpack)] = "true"
|
||||
}
|
||||
if _, has := exporterAttrs[string(exptypes.OptKeyDanglingPrefix)]; !has {
|
||||
exporterAttrs[string(exptypes.OptKeyDanglingPrefix)] = "moby-dangling"
|
||||
}
|
||||
|
||||
@@ -415,6 +415,7 @@ func (d *driver) ProgramExternalConnectivity(_ context.Context, nid, eid string,
|
||||
|
||||
// revokeExternalConnectivity method is invoked to remove any external connectivity programming related to the endpoint.
|
||||
func (d *driver) revokeExternalConnectivity(nid, eid string) error {
|
||||
d.nwEndpointsMu.Lock()
|
||||
ep, ok := d.nwEndpoints[eid]
|
||||
d.nwEndpointsMu.Unlock()
|
||||
if !ok {
|
||||
|
||||
@@ -567,6 +567,16 @@ func (ep *Endpoint) sbJoin(ctx context.Context, sb *Sandbox, options ...Endpoint
|
||||
if err := sb.populateNetworkResources(ctx, ep); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If the old gateway was in the docker_gwbridge network, it's already been removed if
|
||||
// the new endpoint provides a gateway. Don't try to remove it again.
|
||||
if gwepBefore4 != nil && sb.GetEndpoint(gwepBefore4.ID()) == nil {
|
||||
gwepBefore4 = nil
|
||||
}
|
||||
if gwepBefore6 != nil && sb.GetEndpoint(gwepBefore6.ID()) == nil {
|
||||
gwepBefore6 = nil
|
||||
}
|
||||
|
||||
if err := ep.updateExternalConnectivity(ctx, sb, gwepBefore4, gwepBefore6); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -348,10 +348,8 @@ func (sb *Sandbox) populateNetworkResources(ctx context.Context, ep *Endpoint) (
|
||||
|
||||
// Populate DNS records.
|
||||
n := ep.getNetwork()
|
||||
if !n.getController().isAgent() {
|
||||
if !n.getController().isSwarmNode() || n.Scope() != scope.Swarm || !n.driverIsMultihost() {
|
||||
n.updateSvcRecord(context.WithoutCancel(ctx), ep, true)
|
||||
}
|
||||
if !n.getController().isSwarmNode() || n.Scope() != scope.Swarm || !n.driverIsMultihost() {
|
||||
n.updateSvcRecord(context.WithoutCancel(ctx), ep, true)
|
||||
}
|
||||
|
||||
if err := ep.addDriverInfoToCluster(); err != nil {
|
||||
|
||||
2
go.mod
2
go.mod
@@ -51,7 +51,7 @@ require (
|
||||
github.com/hashicorp/go-memdb v1.3.5
|
||||
github.com/hashicorp/memberlist v0.4.0
|
||||
github.com/hashicorp/serf v0.8.5
|
||||
github.com/ishidawataru/sctp v0.0.0-20250829011129-4b890084db30
|
||||
github.com/ishidawataru/sctp v0.0.0-20251114114122-19ddcbc6aae2
|
||||
github.com/miekg/dns v1.1.66
|
||||
github.com/mistifyio/go-zfs/v3 v3.0.1
|
||||
github.com/mitchellh/copystructure v1.2.0
|
||||
|
||||
4
go.sum
4
go.sum
@@ -343,8 +343,8 @@ github.com/in-toto/in-toto-golang v0.9.0/go.mod h1:xsBVrVsHNsB61++S6Dy2vWosKhuA3
|
||||
github.com/inconshreveable/log15 v0.0.0-20170622235902-74a0988b5f80/go.mod h1:cOaXtrgN4ScfRrD9Bre7U1thNq5RtJ8ZoP4iXVGRj6o=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/ishidawataru/sctp v0.0.0-20250829011129-4b890084db30 h1:SF8DGX8bGAXMAvxtJvFFy2KIAPwxIEDP3XpzZVhz0i4=
|
||||
github.com/ishidawataru/sctp v0.0.0-20250829011129-4b890084db30/go.mod h1:co9pwDoBCm1kGxawmb4sPq0cSIOOWNPT4KnHotMP1Zg=
|
||||
github.com/ishidawataru/sctp v0.0.0-20251114114122-19ddcbc6aae2 h1:36qep4gxKs+JgeHGWeQ040RyZdt9kQlLglL1rFVn/oQ=
|
||||
github.com/ishidawataru/sctp v0.0.0-20251114114122-19ddcbc6aae2/go.mod h1:co9pwDoBCm1kGxawmb4sPq0cSIOOWNPT4KnHotMP1Zg=
|
||||
github.com/jmoiron/sqlx v1.3.3 h1:j82X0bf7oQ27XeqxicSZsTU5suPwKElg3oyxNn43iTk=
|
||||
github.com/jmoiron/sqlx v1.3.3/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ=
|
||||
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
|
||||
|
||||
@@ -4,7 +4,7 @@ set -e
|
||||
source hack/make/.integration-test-helpers
|
||||
|
||||
# The commit or tag to use for testing
|
||||
: "${DOCKER_PY_COMMIT:=65f7f0c772577beb5e2cd6daac4e5ca806ccc4af}" # master (v7.2.0-dev)
|
||||
: "${DOCKER_PY_COMMIT:=df3f8e2abc5a03de482e37214dddef9e0cee1bb1}" # master (v7.2.0-dev)
|
||||
|
||||
# custom options to pass py.test
|
||||
#
|
||||
|
||||
@@ -38,14 +38,17 @@ if [ -n "${api_pkg_list}" ]; then
|
||||
# is not provided by any known module.
|
||||
# - `-mod=readonly` tells the go command to ignore the vendor directory
|
||||
# and to report an error if `go.mod` needs to be updated.
|
||||
gotestsum --format=standard-quiet --jsonfile=bundles/api-go-test-report.json --junitfile=bundles/api-junit-report.xml -- \
|
||||
"${BUILDFLAGS[@]}" \
|
||||
-cover \
|
||||
-coverprofile=bundles/api-coverage.out \
|
||||
-covermode=atomic \
|
||||
-mod=readonly \
|
||||
${TESTFLAGS} \
|
||||
${api_pkg_list}
|
||||
(
|
||||
cd api
|
||||
gotestsum --format=standard-quiet --jsonfile=../bundles/api-go-test-report.json --junitfile=../bundles/api-junit-report.xml -- \
|
||||
"${BUILDFLAGS[@]}" \
|
||||
-cover \
|
||||
-coverprofile=../bundles/api-coverage.out \
|
||||
-covermode=atomic \
|
||||
-mod=readonly \
|
||||
${TESTFLAGS} \
|
||||
${api_pkg_list} || exit $?
|
||||
)
|
||||
fi
|
||||
|
||||
case "$TESTDIRS" in
|
||||
@@ -65,14 +68,17 @@ if [ -n "${client_pkg_list}" ]; then
|
||||
# is not provided by any known module.
|
||||
# - `-mod=readonly` tells the go command to ignore the vendor directory
|
||||
# and to report an error if `go.mod` needs to be updated.
|
||||
gotestsum --format=standard-quiet --jsonfile=bundles/client-go-test-report.json --junitfile=bundles/client-junit-report.xml -- \
|
||||
"${BUILDFLAGS[@]}" \
|
||||
-cover \
|
||||
-coverprofile=bundles/client-coverage.out \
|
||||
-covermode=atomic \
|
||||
-mod=readonly \
|
||||
${TESTFLAGS} \
|
||||
${client_pkg_list}
|
||||
(
|
||||
cd client
|
||||
gotestsum --format=standard-quiet --jsonfile=../bundles/client-go-test-report.json --junitfile=../bundles/client-junit-report.xml -- \
|
||||
"${BUILDFLAGS[@]}" \
|
||||
-cover \
|
||||
-coverprofile=../bundles/client-coverage.out \
|
||||
-covermode=atomic \
|
||||
-mod=readonly \
|
||||
${TESTFLAGS} \
|
||||
${client_pkg_list} || exit $?
|
||||
)
|
||||
fi
|
||||
|
||||
case "$TESTDIRS" in
|
||||
|
||||
@@ -1357,10 +1357,10 @@ func (s *DockerNetworkSuite) TestDockerNetworkUnsupportedRequiredIP(c *testing.T
|
||||
|
||||
out, _, err := dockerCmdWithError("run", "-d", "--ip", "172.28.99.88", "--net", "n0", "busybox", "top")
|
||||
assert.Assert(c, err != nil, "out: %s", out)
|
||||
assert.Assert(c, is.Contains(out, "user specified IP address is supported only when connecting to networks with user configured subnets"))
|
||||
assert.Assert(c, is.Contains(out, "no configured subnet contains IP address 172.28.99.88"))
|
||||
out, _, err = dockerCmdWithError("run", "-d", "--ip6", "2001:db8:1234::9988", "--net", "n0", "busybox", "top")
|
||||
assert.Assert(c, err != nil, "out: %s", out)
|
||||
assert.Assert(c, is.Contains(out, "user specified IP address is supported only when connecting to networks with user configured subnets"))
|
||||
assert.Assert(c, is.Contains(out, "no configured subnet contains IP address 2001:db8:1234::9988"))
|
||||
cli.DockerCmd(c, "network", "rm", "n0")
|
||||
assertNwNotAvailable(c, "n0")
|
||||
}
|
||||
|
||||
@@ -475,6 +475,7 @@ RUN [ ! -f foo ]
|
||||
// #37581
|
||||
// #40444 (Windows Containers only)
|
||||
func TestBuildWithHugeFile(t *testing.T) {
|
||||
t.Skip("Test is flaky, and often causes out of space issues on GitHub Actions")
|
||||
ctx := setupTest(t)
|
||||
|
||||
var dockerfile string
|
||||
|
||||
@@ -1092,7 +1092,7 @@ func TestDisableIPv6OnInterface(t *testing.T) {
|
||||
// There should not be an IPv6 DNS or /etc/hosts entry.
|
||||
runRes := container.RunAttach(ctx, t, c,
|
||||
container.WithNetworkMode(tc.netName),
|
||||
container.WithCmd("ping", "-6", ctrName),
|
||||
container.WithCmd("ping", "-6", "-c1", ctrName),
|
||||
)
|
||||
assert.Check(t, is.Equal(runRes.ExitCode, 1))
|
||||
assert.Check(t, is.Contains(runRes.Stderr.String(), "bad address"))
|
||||
@@ -2032,3 +2032,61 @@ func TestLegacyLinksEnvVars(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestDNSNamesForNonSwarmScopedNetworks checks that container names can be resolved for non-swarm-scoped networks once
|
||||
// a node has joined a Swarm cluster.
|
||||
//
|
||||
// Regression test for https://github.com/moby/moby/issues/51491.
|
||||
func TestDNSNamesForNonSwarmScopedNetworks(t *testing.T) {
|
||||
ctx := setupTest(t)
|
||||
|
||||
d := daemon.New(t)
|
||||
d.StartAndSwarmInit(ctx, t)
|
||||
defer d.Stop(t)
|
||||
|
||||
c := d.NewClientT(t)
|
||||
defer c.Close()
|
||||
|
||||
const bridgeName = "dnsnames-with-swarm"
|
||||
network.CreateNoError(ctx, t, c, bridgeName)
|
||||
defer network.RemoveNoError(ctx, t, c, bridgeName)
|
||||
|
||||
res := container.RunAttach(ctx, t, c,
|
||||
container.WithName("test"),
|
||||
container.WithCmd("nslookup", "-type=a", "test."),
|
||||
container.WithNetworkMode(bridgeName),
|
||||
container.WithAutoRemove)
|
||||
assert.Equal(t, res.ExitCode, 0, "exit code: %d, expected 0; stdout:\n%s", res.ExitCode, res.Stdout)
|
||||
}
|
||||
|
||||
// Check that when a network is created with no --subnet, a container can be
|
||||
// started with a --ip in the subnet allocated from the default pools.
|
||||
//
|
||||
// Regression test for https://github.com/moby/moby/issues/51569
|
||||
func TestSetIPWithNoConfiguredSubnet(t *testing.T) {
|
||||
ctx := setupTest(t)
|
||||
c := testEnv.APIClient()
|
||||
|
||||
const bridgeName = "subnet-from-pools"
|
||||
network.CreateNoError(ctx, t, c, bridgeName, network.WithIPv6())
|
||||
defer network.RemoveNoError(ctx, t, c, bridgeName)
|
||||
|
||||
insp := network.InspectNoError(ctx, t, c, bridgeName, client.NetworkInspectOptions{})
|
||||
assert.Assert(t, is.Len(insp.Network.IPAM.Config, 2))
|
||||
ip4 := insp.Network.IPAM.Config[0].Subnet.Addr().Next().Next().String()
|
||||
ip6 := insp.Network.IPAM.Config[1].Subnet.Addr().Next().Next().String()
|
||||
if insp.Network.IPAM.Config[0].Subnet.Addr().Is6() {
|
||||
ip4, ip6 = ip6, ip4
|
||||
}
|
||||
|
||||
res := container.RunAttach(ctx, t, c,
|
||||
container.WithCmd("ip", "addr", "show", "eth0"),
|
||||
container.WithNetworkMode(bridgeName),
|
||||
container.WithIPv4(bridgeName, ip4),
|
||||
container.WithIPv6(bridgeName, ip6),
|
||||
)
|
||||
if assert.Check(t, is.Equal(res.ExitCode, 0)) {
|
||||
assert.Check(t, is.Contains(res.Stdout.String(), ip4))
|
||||
assert.Check(t, is.Contains(res.Stdout.String(), ip6))
|
||||
}
|
||||
}
|
||||
|
||||
5
vendor/github.com/ishidawataru/sctp/sctp_unsupported.go
generated
vendored
5
vendor/github.com/ishidawataru/sctp/sctp_unsupported.go
generated
vendored
@@ -19,6 +19,7 @@ package sctp
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"os"
|
||||
"runtime"
|
||||
"syscall"
|
||||
)
|
||||
@@ -73,6 +74,10 @@ func listenSCTPExtConfig(network string, laddr *SCTPAddr, options InitMsg, contr
|
||||
return nil, ErrUnsupported
|
||||
}
|
||||
|
||||
func FileListener(file *os.File) (*SCTPListener, error) {
|
||||
return nil, ErrUnsupported
|
||||
}
|
||||
|
||||
func (ln *SCTPListener) Accept() (net.Conn, error) {
|
||||
return nil, ErrUnsupported
|
||||
}
|
||||
|
||||
2
vendor/modules.txt
vendored
2
vendor/modules.txt
vendored
@@ -756,7 +756,7 @@ github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v1
|
||||
# github.com/inconshreveable/mousetrap v1.1.0
|
||||
## explicit; go 1.18
|
||||
github.com/inconshreveable/mousetrap
|
||||
# github.com/ishidawataru/sctp v0.0.0-20250829011129-4b890084db30
|
||||
# github.com/ishidawataru/sctp v0.0.0-20251114114122-19ddcbc6aae2
|
||||
## explicit; go 1.12
|
||||
github.com/ishidawataru/sctp
|
||||
# github.com/jmoiron/sqlx v1.3.3
|
||||
|
||||
Reference in New Issue
Block a user