فهرست منبع

Allow user to control the default address pools

- Via daemon flag --default-address-pools base=<CIDR>,size=<int>

Signed-off-by: Elango Siva  <elango@docker.com>
Alessandro Boch 8 سال پیش
والد
کامیت
173b3c364e

+ 3 - 0
cmd/dockerd/config_unix.go

@@ -18,6 +18,7 @@ func installConfigFlags(conf *config.Config, flags *pflag.FlagSet) {
 	installUnixConfigFlags(conf, flags)
 
 	conf.Ulimits = make(map[string]*units.Ulimit)
+	conf.NetworkConfig.DefaultAddressPools = opts.PoolsOpt{}
 
 	// Set default value for `--default-shm-size`
 	conf.ShmSize = opts.MemBytes(config.DefaultShmSize)
@@ -44,4 +45,6 @@ func installConfigFlags(conf *config.Config, flags *pflag.FlagSet) {
 	flags.Var(&conf.ShmSize, "default-shm-size", "Default shm size for containers")
 	flags.BoolVar(&conf.NoNewPrivileges, "no-new-privileges", false, "Set no-new-privileges by default for new containers")
 	flags.StringVar(&conf.IpcMode, "default-ipc-mode", config.DefaultIpcMode, `Default mode for containers ipc ("shareable" | "private")`)
+	flags.Var(&conf.NetworkConfig.DefaultAddressPools, "default-address-pool", "Default address pools for node specific local networks")
+
 }

+ 7 - 0
daemon/config/config.go

@@ -71,6 +71,12 @@ type commonBridgeConfig struct {
 	FixedCIDR string `json:"fixed-cidr,omitempty"`
 }
 
+// NetworkConfig stores the daemon-wide networking configurations
+type NetworkConfig struct {
+	// Default address pools for docker networks
+	DefaultAddressPools opts.PoolsOpt `json:"default-address-pools,omitempty"`
+}
+
 // CommonTLSOptions defines TLS configuration for the daemon server.
 // It includes json tags to deserialize configuration from a file
 // using the same names that the flags in the command line use.
@@ -173,6 +179,7 @@ type CommonConfig struct {
 
 	LogConfig
 	BridgeConfig // bridgeConfig holds bridge network specific configuration.
+	NetworkConfig
 	registry.ServiceOptions
 
 	sync.Mutex

+ 0 - 1
daemon/config/config_unix.go

@@ -23,7 +23,6 @@ type Config struct {
 
 	// These fields are common to all unix platforms.
 	CommonUnixConfig
-
 	// Fields below here are platform specific.
 	CgroupParent         string                   `json:"cgroup-parent,omitempty"`
 	EnableSelinuxSupport bool                     `json:"selinux-enabled,omitempty"`

+ 4 - 0
daemon/daemon.go

@@ -1223,6 +1223,10 @@ func (daemon *Daemon) networkOptions(dconfig *config.Config, pg plugingetter.Plu
 	options = append(options, nwconfig.OptionLabels(dconfig.Labels))
 	options = append(options, driverOptions(dconfig)...)
 
+	if len(dconfig.NetworkConfig.DefaultAddressPools.Value()) > 0 {
+		options = append(options, nwconfig.OptionDefaultAddressPoolConfig(dconfig.NetworkConfig.DefaultAddressPools.Value()))
+	}
+
 	if daemon.configStore != nil && daemon.configStore.LiveRestoreEnabled && len(activeSandboxes) != 0 {
 		options = append(options, nwconfig.OptionActiveSandboxes(activeSandboxes))
 	}

+ 3 - 0
daemon/daemon_unix.go

@@ -833,6 +833,9 @@ func (daemon *Daemon) initNetworkController(config *config.Config, activeSandbox
 		if err = n.Delete(); err != nil {
 			return nil, fmt.Errorf("could not delete the default bridge network: %v", err)
 		}
+		if len(config.NetworkConfig.DefaultAddressPools.Value()) > 0 && !daemon.configStore.LiveRestoreEnabled {
+			removeDefaultBridgeInterface()
+		}
 	}
 
 	if !config.DisableBridge {

+ 179 - 0
integration/network/service_test.go

@@ -9,10 +9,189 @@ import (
 	swarmtypes "github.com/docker/docker/api/types/swarm"
 	"github.com/docker/docker/client"
 	"github.com/docker/docker/integration/internal/swarm"
+	"github.com/docker/docker/internal/test/daemon"
 	"github.com/gotestyourself/gotestyourself/assert"
+	"github.com/gotestyourself/gotestyourself/icmd"
 	"github.com/gotestyourself/gotestyourself/poll"
 )
 
+// delInterface removes given network interface
+func delInterface(t *testing.T, ifName string) {
+	icmd.RunCommand("ip", "link", "delete", ifName).Assert(t, icmd.Success)
+	icmd.RunCommand("iptables", "-t", "nat", "--flush").Assert(t, icmd.Success)
+	icmd.RunCommand("iptables", "--flush").Assert(t, icmd.Success)
+}
+
+func TestDaemonRestartWithLiveRestore(t *testing.T) {
+	d := daemon.New(t)
+	defer d.Stop(t)
+	d.Start(t)
+	d.Restart(t, "--live-restore=true",
+		"--default-address-pool", "base=175.30.0.0/16,size=16",
+		"--default-address-pool", "base=175.33.0.0/16,size=24")
+
+	// Verify bridge network's subnet
+	cli, err := d.NewClient()
+	assert.Assert(t, err)
+	defer cli.Close()
+	out, err := cli.NetworkInspect(context.Background(), "bridge", types.NetworkInspectOptions{})
+	assert.NilError(t, err)
+	// Make sure docker0 doesn't get override with new IP in live restore case
+	assert.Equal(t, out.IPAM.Config[0].Subnet, "172.18.0.0/16")
+}
+
+func TestDaemonDefaultNetworkPools(t *testing.T) {
+	// Remove docker0 bridge and the start daemon defining the predefined address pools
+	defaultNetworkBridge := "docker0"
+	delInterface(t, defaultNetworkBridge)
+	d := daemon.New(t)
+	defer d.Stop(t)
+	d.Start(t,
+		"--default-address-pool", "base=175.30.0.0/16,size=16",
+		"--default-address-pool", "base=175.33.0.0/16,size=24")
+
+	// Verify bridge network's subnet
+	cli, err := d.NewClient()
+	assert.Assert(t, err)
+	defer cli.Close()
+	out, err := cli.NetworkInspect(context.Background(), "bridge", types.NetworkInspectOptions{})
+	assert.NilError(t, err)
+	assert.Equal(t, out.IPAM.Config[0].Subnet, "175.30.0.0/16")
+
+	// Create a bridge network and verify its subnet is the second default pool
+	name := "elango"
+	networkCreate := types.NetworkCreate{
+		CheckDuplicate: false,
+	}
+	networkCreate.Driver = "bridge"
+	_, err = cli.NetworkCreate(context.Background(), name, networkCreate)
+	assert.NilError(t, err)
+	out, err = cli.NetworkInspect(context.Background(), name, types.NetworkInspectOptions{})
+	assert.NilError(t, err)
+	assert.Equal(t, out.IPAM.Config[0].Subnet, "175.33.0.0/24")
+
+	// Create a bridge network and verify its subnet is the third default pool
+	name = "saanvi"
+	networkCreate = types.NetworkCreate{
+		CheckDuplicate: false,
+	}
+	networkCreate.Driver = "bridge"
+	_, err = cli.NetworkCreate(context.Background(), name, networkCreate)
+	assert.NilError(t, err)
+	out, err = cli.NetworkInspect(context.Background(), name, types.NetworkInspectOptions{})
+	assert.NilError(t, err)
+	assert.Equal(t, out.IPAM.Config[0].Subnet, "175.33.1.0/24")
+	delInterface(t, defaultNetworkBridge)
+
+}
+
+func TestDaemonRestartWithExistingNetwork(t *testing.T) {
+	defaultNetworkBridge := "docker0"
+	d := daemon.New(t)
+	d.Start(t)
+	defer d.Stop(t)
+	// Verify bridge network's subnet
+	cli, err := d.NewClient()
+	assert.Assert(t, err)
+	defer cli.Close()
+
+	// Create a bridge network
+	name := "elango"
+	networkCreate := types.NetworkCreate{
+		CheckDuplicate: false,
+	}
+	networkCreate.Driver = "bridge"
+	_, err = cli.NetworkCreate(context.Background(), name, networkCreate)
+	assert.NilError(t, err)
+	out, err := cli.NetworkInspect(context.Background(), name, types.NetworkInspectOptions{})
+	assert.NilError(t, err)
+	networkip := out.IPAM.Config[0].Subnet
+
+	// Restart daemon with default address pool option
+	d.Restart(t,
+		"--default-address-pool", "base=175.30.0.0/16,size=16",
+		"--default-address-pool", "base=175.33.0.0/16,size=24")
+
+	out1, err := cli.NetworkInspect(context.Background(), name, types.NetworkInspectOptions{})
+	assert.NilError(t, err)
+	assert.Equal(t, out1.IPAM.Config[0].Subnet, networkip)
+	delInterface(t, defaultNetworkBridge)
+}
+
+func TestDaemonRestartWithExistingNetworkWithDefaultPoolRange(t *testing.T) {
+	defaultNetworkBridge := "docker0"
+	d := daemon.New(t)
+	d.Start(t)
+	defer d.Stop(t)
+	// Verify bridge network's subnet
+	cli, err := d.NewClient()
+	assert.Assert(t, err)
+	defer cli.Close()
+
+	// Create a bridge network
+	name := "elango"
+	networkCreate := types.NetworkCreate{
+		CheckDuplicate: false,
+	}
+	networkCreate.Driver = "bridge"
+	_, err = cli.NetworkCreate(context.Background(), name, networkCreate)
+	assert.NilError(t, err)
+	out, err := cli.NetworkInspect(context.Background(), name, types.NetworkInspectOptions{})
+	assert.NilError(t, err)
+	networkip := out.IPAM.Config[0].Subnet
+
+	// Create a bridge network
+	name = "sthira"
+	networkCreate = types.NetworkCreate{
+		CheckDuplicate: false,
+	}
+	networkCreate.Driver = "bridge"
+	_, err = cli.NetworkCreate(context.Background(), name, networkCreate)
+	assert.NilError(t, err)
+	out, err = cli.NetworkInspect(context.Background(), name, types.NetworkInspectOptions{})
+	assert.NilError(t, err)
+	networkip2 := out.IPAM.Config[0].Subnet
+
+	// Restart daemon with default address pool option
+	d.Restart(t,
+		"--default-address-pool", "base=175.18.0.0/16,size=16",
+		"--default-address-pool", "base=175.19.0.0/16,size=24")
+
+	// Create a bridge network
+	name = "saanvi"
+	networkCreate = types.NetworkCreate{
+		CheckDuplicate: false,
+	}
+	networkCreate.Driver = "bridge"
+	_, err = cli.NetworkCreate(context.Background(), name, networkCreate)
+	assert.NilError(t, err)
+	out1, err := cli.NetworkInspect(context.Background(), name, types.NetworkInspectOptions{})
+	assert.NilError(t, err)
+
+	assert.Check(t, (out1.IPAM.Config[0].Subnet != networkip))
+	assert.Check(t, (out1.IPAM.Config[0].Subnet != networkip2))
+	delInterface(t, defaultNetworkBridge)
+}
+
+func TestDaemonWithBipAndDefaultNetworkPool(t *testing.T) {
+	defaultNetworkBridge := "docker0"
+	d := daemon.New(t)
+	defer d.Stop(t)
+	d.Start(t, "--bip=172.60.0.1/16",
+		"--default-address-pool", "base=175.30.0.0/16,size=16",
+		"--default-address-pool", "base=175.33.0.0/16,size=24")
+
+	// Verify bridge network's subnet
+	cli, err := d.NewClient()
+	assert.Assert(t, err)
+	defer cli.Close()
+	out, err := cli.NetworkInspect(context.Background(), "bridge", types.NetworkInspectOptions{})
+	assert.NilError(t, err)
+	// Make sure BIP IP doesn't get override with new default address pool .
+	assert.Equal(t, out.IPAM.Config[0].Subnet, "172.60.0.1/16")
+	delInterface(t, defaultNetworkBridge)
+}
+
 func TestServiceWithPredefinedNetwork(t *testing.T) {
 	defer setupTest(t)()
 	d := swarm.NewSwarm(t, testEnv)

+ 84 - 0
opts/address_pools.go

@@ -0,0 +1,84 @@
+package opts
+
+import (
+	"encoding/csv"
+	"encoding/json"
+	"fmt"
+	"strconv"
+	"strings"
+
+	types "github.com/docker/libnetwork/ipamutils"
+)
+
+// PoolsOpt is a Value type for parsing the default address pools definitions
+type PoolsOpt struct {
+	values []*types.NetworkToSplit
+}
+
+// UnmarshalJSON fills values structure  info from JSON input
+func (p *PoolsOpt) UnmarshalJSON(raw []byte) error {
+	return json.Unmarshal(raw, &(p.values))
+}
+
+// Set predefined pools
+func (p *PoolsOpt) Set(value string) error {
+	csvReader := csv.NewReader(strings.NewReader(value))
+	fields, err := csvReader.Read()
+	if err != nil {
+		return err
+	}
+
+	poolsDef := types.NetworkToSplit{}
+
+	for _, field := range fields {
+		parts := strings.SplitN(field, "=", 2)
+		if len(parts) != 2 {
+			return fmt.Errorf("invalid field '%s' must be a key=value pair", field)
+		}
+
+		key := strings.ToLower(parts[0])
+		value := strings.ToLower(parts[1])
+
+		switch key {
+		case "base":
+			poolsDef.Base = value
+		case "size":
+			size, err := strconv.Atoi(value)
+			if err != nil {
+				return fmt.Errorf("invalid size value: %q (must be integer): %v", value, err)
+			}
+			poolsDef.Size = size
+		default:
+			return fmt.Errorf("unexpected key '%s' in '%s'", key, field)
+		}
+	}
+
+	p.values = append(p.values, &poolsDef)
+
+	return nil
+}
+
+// Type returns the type of this option
+func (p *PoolsOpt) Type() string {
+	return "pool-options"
+}
+
+// String returns a string repr of this option
+func (p *PoolsOpt) String() string {
+	pools := []string{}
+	for _, pool := range p.values {
+		repr := fmt.Sprintf("%s %d", pool.Base, pool.Size)
+		pools = append(pools, repr)
+	}
+	return strings.Join(pools, ", ")
+}
+
+// Value returns the mounts
+func (p *PoolsOpt) Value() []*types.NetworkToSplit {
+	return p.values
+}
+
+// Name returns the flag name of this option
+func (p *PoolsOpt) Name() string {
+	return "default-address-pools"
+}

+ 20 - 0
opts/address_pools_test.go

@@ -0,0 +1,20 @@
+package opts // import "github.com/docker/docker/opts"
+
+import (
+	"testing"
+)
+
+func TestAddressPoolOpt(t *testing.T) {
+	poolopt := &PoolsOpt{}
+	var addresspool = "base=175.30.0.0/16,size=16"
+	var invalidAddresspoolString = "base=175.30.0.0/16,size=16, base=175.33.0.0/16,size=24"
+
+	if err := poolopt.Set(addresspool); err != nil {
+		t.Fatal(err)
+	}
+
+	if err := poolopt.Set(invalidAddresspoolString); err == nil {
+		t.Fatal(err)
+	}
+
+}