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>
This commit is contained in:
Alessandro Boch 2016-12-13 15:04:59 -08:00 committed by selansen
parent 1a57535aa2
commit 173b3c364e
8 changed files with 300 additions and 1 deletions

View File

@ -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")
}

View File

@ -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

View File

@ -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"`

View File

@ -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))
}

View File

@ -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 {

View File

@ -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
opts/address_pools.go Normal file
View File

@ -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"
}

View File

@ -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)
}
}