summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Stockinger <alex@atomicjar.com>2022-01-27 21:13:45 +0100
committerAlex Stockinger <alex@atomicjar.com>2023-03-01 07:58:26 +0100
commit91c2b12205dd8ad35d9d754e364c7b2f01a19aba (patch)
treef744e78b5125734bb5400fd9436945c0379a67a5
parent0021339b9299c5f1596bdf772a42ce346f16f060 (diff)
downloaddocker-91c2b12205dd8ad35d9d754e364c7b2f01a19aba.tar.gz
Make default options for newly created networks configurable
Signed-off-by: Alex Stockinger <alex@atomicjar.com> Co-authored-by: Sergei Egorov <bsideup@gmail.com> Co-authored-by: Cory Snider <corhere@gmail.com>
-rw-r--r--cmd/dockerd/config.go1
-rw-r--r--daemon/config/config.go3
-rw-r--r--daemon/network.go15
-rw-r--r--integration/network/network_test.go74
-rw-r--r--opts/opts.go77
-rw-r--r--opts/opts_test.go31
6 files changed, 200 insertions, 1 deletions
diff --git a/cmd/dockerd/config.go b/cmd/dockerd/config.go
index be332626ae..a90c1bbee6 100644
--- a/cmd/dockerd/config.go
+++ b/cmd/dockerd/config.go
@@ -27,6 +27,7 @@ func installCommonConfigFlags(conf *config.Config, flags *pflag.FlagSet) error {
flags.StringVar(&conf.ContainerdAddr, "containerd", "", "containerd grpc address")
flags.BoolVar(&conf.CriContainerd, "cri-containerd", false, "start containerd with cri")
+ flags.Var(opts.NewNamedMapMapOpts("default-network-opts", conf.DefaultNetworkOpts, nil), "default-network-opt", "Default network options")
flags.IntVar(&conf.Mtu, "mtu", conf.Mtu, "Set the containers network MTU")
flags.IntVar(&conf.NetworkControlPlaneMTU, "network-control-plane-mtu", conf.NetworkControlPlaneMTU, "Network Control plane MTU")
flags.IntVar(&conf.NetworkDiagnosticPort, "network-diagnostic-port", 0, "TCP port number of the network diagnostic server")
diff --git a/daemon/config/config.go b/daemon/config/config.go
index 42f7add76d..60b215227a 100644
--- a/daemon/config/config.go
+++ b/daemon/config/config.go
@@ -126,6 +126,8 @@ type NetworkConfig struct {
DefaultAddressPools opts.PoolsOpt `json:"default-address-pools,omitempty"`
// NetworkControlPlaneMTU allows to specify the control plane MTU, this will allow to optimize the network use in some components
NetworkControlPlaneMTU int `json:"network-control-plane-mtu,omitempty"`
+ // Default options for newly created networks
+ DefaultNetworkOpts map[string]map[string]string `json:"default-network-opts,omitempty"`
}
// TLSOptions defines TLS configuration for the daemon server.
@@ -289,6 +291,7 @@ func New() (*Config, error) {
Mtu: DefaultNetworkMtu,
NetworkConfig: NetworkConfig{
NetworkControlPlaneMTU: DefaultNetworkMtu,
+ DefaultNetworkOpts: make(map[string]map[string]string),
},
ContainerdNamespace: DefaultContainersNamespace,
ContainerdPluginNamespace: DefaultPluginNamespace,
diff --git a/daemon/network.go b/daemon/network.go
index 5b54d5d296..3bf8852d4f 100644
--- a/daemon/network.go
+++ b/daemon/network.go
@@ -315,9 +315,22 @@ func (daemon *Daemon) createNetwork(create types.NetworkCreateRequest, id string
driver = c.Config().DefaultDriver
}
+ networkOptions := make(map[string]string)
+ for k, v := range create.Options {
+ networkOptions[k] = v
+ }
+ if defaultOpts, ok := daemon.configStore.DefaultNetworkOpts[driver]; create.ConfigFrom == nil && ok {
+ for k, v := range defaultOpts {
+ if _, ok := networkOptions[k]; !ok {
+ logrus.WithFields(logrus.Fields{"driver": driver, "network": id, k: v}).Debug("Applying network default option")
+ networkOptions[k] = v
+ }
+ }
+ }
+
nwOptions := []libnetwork.NetworkOption{
libnetwork.NetworkOptionEnableIPv6(create.EnableIPv6),
- libnetwork.NetworkOptionDriverOpts(create.Options),
+ libnetwork.NetworkOptionDriverOpts(networkOptions),
libnetwork.NetworkOptionLabels(create.Labels),
libnetwork.NetworkOptionAttachable(create.Attachable),
libnetwork.NetworkOptionIngress(create.Ingress),
diff --git a/integration/network/network_test.go b/integration/network/network_test.go
index 00603094a2..bfc6e2998e 100644
--- a/integration/network/network_test.go
+++ b/integration/network/network_test.go
@@ -4,12 +4,14 @@ import (
"bytes"
"context"
"encoding/json"
+ "fmt"
"net/http"
"os/exec"
"strings"
"testing"
"github.com/docker/docker/api/types"
+ ntypes "github.com/docker/docker/api/types/network"
"github.com/docker/docker/integration/internal/container"
"github.com/docker/docker/integration/internal/network"
"github.com/docker/docker/testutil/daemon"
@@ -175,3 +177,75 @@ func TestHostIPv4BridgeLabel(t *testing.T) {
// Make sure the SNAT rule exists
icmd.RunCommand("iptables", "-t", "nat", "-C", "POSTROUTING", "-s", out.IPAM.Config[0].Subnet, "!", "-o", bridgeName, "-j", "SNAT", "--to-source", ipv4SNATAddr).Assert(t, icmd.Success)
}
+
+func TestDefaultNetworkOpts(t *testing.T) {
+ skip.If(t, testEnv.OSType == "windows")
+ skip.If(t, testEnv.IsRemoteDaemon)
+ skip.If(t, testEnv.IsRootless, "rootless mode has different view of network")
+
+ tests := []struct {
+ name string
+ mtu int
+ configFrom bool
+ args []string
+ }{
+ {
+ name: "default value",
+ mtu: 1500,
+ args: []string{},
+ },
+ {
+ name: "cmdline value",
+ mtu: 1234,
+ args: []string{"--default-network-opt", "bridge=com.docker.network.driver.mtu=1234"},
+ },
+ {
+ name: "config-from value",
+ configFrom: true,
+ mtu: 1233,
+ args: []string{"--default-network-opt", "bridge=com.docker.network.driver.mtu=1234"},
+ },
+ }
+
+ for _, tc := range tests {
+ tc := tc
+ t.Run(tc.name, func(t *testing.T) {
+ d := daemon.New(t)
+ d.StartWithBusybox(t, tc.args...)
+ defer d.Stop(t)
+ c := d.NewClientT(t)
+ defer c.Close()
+ ctx := context.Background()
+
+ if tc.configFrom {
+ // Create a new network config
+ network.CreateNoError(ctx, t, c, "from-net", func(create *types.NetworkCreate) {
+ create.ConfigOnly = true
+ create.Options = map[string]string{
+ "com.docker.network.driver.mtu": fmt.Sprint(tc.mtu),
+ }
+ })
+ defer c.NetworkRemove(ctx, "from-net")
+ }
+
+ // Create a new network
+ networkName := "testnet"
+ network.CreateNoError(ctx, t, c, networkName, func(create *types.NetworkCreate) {
+ if tc.configFrom {
+ create.ConfigFrom = &ntypes.ConfigReference{
+ Network: "from-net",
+ }
+ }
+ })
+ defer c.NetworkRemove(ctx, networkName)
+
+ // Start a container to inspect the MTU of its network interface
+ id1 := container.Run(ctx, t, c, container.WithNetworkMode(networkName))
+ defer c.ContainerRemove(ctx, id1, types.ContainerRemoveOptions{Force: true})
+
+ result, err := container.Exec(ctx, c, id1, []string{"ip", "l", "show", "eth0"})
+ assert.NilError(t, err)
+ assert.Check(t, is.Contains(result.Combined(), fmt.Sprintf(" mtu %d ", tc.mtu)), "Network MTU should have been set to %d", tc.mtu)
+ })
+ }
+}
diff --git a/opts/opts.go b/opts/opts.go
index aacd30af08..dbcd028525 100644
--- a/opts/opts.go
+++ b/opts/opts.go
@@ -146,6 +146,83 @@ func (o *NamedListOpts) Name() string {
return o.name
}
+// NamedMapMapOpts is a MapMapOpts with a configuration name.
+// This struct is useful to keep reference to the assigned
+// field name in the internal configuration struct.
+type NamedMapMapOpts struct {
+ name string
+ MapMapOpts
+}
+
+// NewNamedMapMapOpts creates a reference to a new NamedMapOpts struct.
+func NewNamedMapMapOpts(name string, values map[string]map[string]string, validator ValidatorFctType) *NamedMapMapOpts {
+ return &NamedMapMapOpts{
+ name: name,
+ MapMapOpts: *NewMapMapOpts(values, validator),
+ }
+}
+
+// Name returns the name of the NamedListOpts in the configuration.
+func (o *NamedMapMapOpts) Name() string {
+ return o.name
+}
+
+// MapMapOpts holds a map of maps of values and a validation function.
+type MapMapOpts struct {
+ values map[string]map[string]string
+ validator ValidatorFctType
+}
+
+// Set validates if needed the input value and add it to the
+// internal map, by splitting on '='.
+func (opts *MapMapOpts) Set(value string) error {
+ if opts.validator != nil {
+ v, err := opts.validator(value)
+ if err != nil {
+ return err
+ }
+ value = v
+ }
+ rk, rv, found := strings.Cut(value, "=")
+ if !found {
+ return fmt.Errorf("invalid value %q for map option, should be root-key=key=value", value)
+ }
+ k, v, found := strings.Cut(rv, "=")
+ if !found {
+ return fmt.Errorf("invalid value %q for map option, should be root-key=key=value", value)
+ }
+ if _, ok := opts.values[rk]; !ok {
+ opts.values[rk] = make(map[string]string)
+ }
+ opts.values[rk][k] = v
+ return nil
+}
+
+// GetAll returns the values of MapOpts as a map.
+func (opts *MapMapOpts) GetAll() map[string]map[string]string {
+ return opts.values
+}
+
+func (opts *MapMapOpts) String() string {
+ return fmt.Sprintf("%v", opts.values)
+}
+
+// Type returns a string name for this Option type
+func (opts *MapMapOpts) Type() string {
+ return "mapmap"
+}
+
+// NewMapMapOpts creates a new MapMapOpts with the specified map of values and a validator.
+func NewMapMapOpts(values map[string]map[string]string, validator ValidatorFctType) *MapMapOpts {
+ if values == nil {
+ values = make(map[string]map[string]string)
+ }
+ return &MapMapOpts{
+ values: values,
+ validator: validator,
+ }
+}
+
// MapOpts holds a map of values and a validation function.
type MapOpts struct {
values map[string]string
diff --git a/opts/opts_test.go b/opts/opts_test.go
index 850618e320..fe4b7f5ce5 100644
--- a/opts/opts_test.go
+++ b/opts/opts_test.go
@@ -334,3 +334,34 @@ func TestParseLink(t *testing.T) {
assert.Check(t, is.Equal(alias, "bar"))
})
}
+
+func TestMapMapOpts(t *testing.T) {
+ tmpMap := make(map[string]map[string]string)
+ validator := func(val string) (string, error) {
+ if strings.HasPrefix(val, "invalid-key=") {
+ return "", fmt.Errorf("invalid key %s", val)
+ }
+ return val, nil
+ }
+ o := NewMapMapOpts(tmpMap, validator)
+ o.Set("r1=k11=v11")
+ assert.Check(t, is.DeepEqual(tmpMap, map[string]map[string]string{"r1": {"k11": "v11"}}))
+
+ o.Set("r2=k21=v21")
+ assert.Check(t, is.Len(tmpMap, 2))
+
+ if err := o.Set("invalid-syntax"); err == nil {
+ t.Error("invalid mapping syntax is not being caught")
+ }
+
+ if err := o.Set("k=invalid-syntax"); err == nil {
+ t.Error("invalid value syntax is not being caught")
+ }
+
+ o.Set("r1=k12=v12")
+ assert.Check(t, is.DeepEqual(tmpMap["r1"], map[string]string{"k11": "v11", "k12": "v12"}))
+
+ if o.Set("invalid-key={\"k\":\"v\"}") == nil {
+ t.Error("validator is not being called")
+ }
+}