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>
This commit is contained in:
parent
0021339b92
commit
91c2b12205
6 changed files with 200 additions and 1 deletions
|
@ -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")
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
77
opts/opts.go
77
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
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue