Browse code

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 authored on 2016/12/14 08:04:59
Showing 8 changed files
... ...
@@ -18,6 +18,7 @@ func installConfigFlags(conf *config.Config, flags *pflag.FlagSet) {
18 18
 	installUnixConfigFlags(conf, flags)
19 19
 
20 20
 	conf.Ulimits = make(map[string]*units.Ulimit)
21
+	conf.NetworkConfig.DefaultAddressPools = opts.PoolsOpt{}
21 22
 
22 23
 	// Set default value for `--default-shm-size`
23 24
 	conf.ShmSize = opts.MemBytes(config.DefaultShmSize)
... ...
@@ -44,4 +45,6 @@ func installConfigFlags(conf *config.Config, flags *pflag.FlagSet) {
44 44
 	flags.Var(&conf.ShmSize, "default-shm-size", "Default shm size for containers")
45 45
 	flags.BoolVar(&conf.NoNewPrivileges, "no-new-privileges", false, "Set no-new-privileges by default for new containers")
46 46
 	flags.StringVar(&conf.IpcMode, "default-ipc-mode", config.DefaultIpcMode, `Default mode for containers ipc ("shareable" | "private")`)
47
+	flags.Var(&conf.NetworkConfig.DefaultAddressPools, "default-address-pool", "Default address pools for node specific local networks")
48
+
47 49
 }
... ...
@@ -71,6 +71,12 @@ type commonBridgeConfig struct {
71 71
 	FixedCIDR string `json:"fixed-cidr,omitempty"`
72 72
 }
73 73
 
74
+// NetworkConfig stores the daemon-wide networking configurations
75
+type NetworkConfig struct {
76
+	// Default address pools for docker networks
77
+	DefaultAddressPools opts.PoolsOpt `json:"default-address-pools,omitempty"`
78
+}
79
+
74 80
 // CommonTLSOptions defines TLS configuration for the daemon server.
75 81
 // It includes json tags to deserialize configuration from a file
76 82
 // using the same names that the flags in the command line use.
... ...
@@ -173,6 +179,7 @@ type CommonConfig struct {
173 173
 
174 174
 	LogConfig
175 175
 	BridgeConfig // bridgeConfig holds bridge network specific configuration.
176
+	NetworkConfig
176 177
 	registry.ServiceOptions
177 178
 
178 179
 	sync.Mutex
... ...
@@ -23,7 +23,6 @@ type Config struct {
23 23
 
24 24
 	// These fields are common to all unix platforms.
25 25
 	CommonUnixConfig
26
-
27 26
 	// Fields below here are platform specific.
28 27
 	CgroupParent         string                   `json:"cgroup-parent,omitempty"`
29 28
 	EnableSelinuxSupport bool                     `json:"selinux-enabled,omitempty"`
... ...
@@ -1223,6 +1223,10 @@ func (daemon *Daemon) networkOptions(dconfig *config.Config, pg plugingetter.Plu
1223 1223
 	options = append(options, nwconfig.OptionLabels(dconfig.Labels))
1224 1224
 	options = append(options, driverOptions(dconfig)...)
1225 1225
 
1226
+	if len(dconfig.NetworkConfig.DefaultAddressPools.Value()) > 0 {
1227
+		options = append(options, nwconfig.OptionDefaultAddressPoolConfig(dconfig.NetworkConfig.DefaultAddressPools.Value()))
1228
+	}
1229
+
1226 1230
 	if daemon.configStore != nil && daemon.configStore.LiveRestoreEnabled && len(activeSandboxes) != 0 {
1227 1231
 		options = append(options, nwconfig.OptionActiveSandboxes(activeSandboxes))
1228 1232
 	}
... ...
@@ -833,6 +833,9 @@ func (daemon *Daemon) initNetworkController(config *config.Config, activeSandbox
833 833
 		if err = n.Delete(); err != nil {
834 834
 			return nil, fmt.Errorf("could not delete the default bridge network: %v", err)
835 835
 		}
836
+		if len(config.NetworkConfig.DefaultAddressPools.Value()) > 0 && !daemon.configStore.LiveRestoreEnabled {
837
+			removeDefaultBridgeInterface()
838
+		}
836 839
 	}
837 840
 
838 841
 	if !config.DisableBridge {
... ...
@@ -9,10 +9,189 @@ import (
9 9
 	swarmtypes "github.com/docker/docker/api/types/swarm"
10 10
 	"github.com/docker/docker/client"
11 11
 	"github.com/docker/docker/integration/internal/swarm"
12
+	"github.com/docker/docker/internal/test/daemon"
12 13
 	"github.com/gotestyourself/gotestyourself/assert"
14
+	"github.com/gotestyourself/gotestyourself/icmd"
13 15
 	"github.com/gotestyourself/gotestyourself/poll"
14 16
 )
15 17
 
18
+// delInterface removes given network interface
19
+func delInterface(t *testing.T, ifName string) {
20
+	icmd.RunCommand("ip", "link", "delete", ifName).Assert(t, icmd.Success)
21
+	icmd.RunCommand("iptables", "-t", "nat", "--flush").Assert(t, icmd.Success)
22
+	icmd.RunCommand("iptables", "--flush").Assert(t, icmd.Success)
23
+}
24
+
25
+func TestDaemonRestartWithLiveRestore(t *testing.T) {
26
+	d := daemon.New(t)
27
+	defer d.Stop(t)
28
+	d.Start(t)
29
+	d.Restart(t, "--live-restore=true",
30
+		"--default-address-pool", "base=175.30.0.0/16,size=16",
31
+		"--default-address-pool", "base=175.33.0.0/16,size=24")
32
+
33
+	// Verify bridge network's subnet
34
+	cli, err := d.NewClient()
35
+	assert.Assert(t, err)
36
+	defer cli.Close()
37
+	out, err := cli.NetworkInspect(context.Background(), "bridge", types.NetworkInspectOptions{})
38
+	assert.NilError(t, err)
39
+	// Make sure docker0 doesn't get override with new IP in live restore case
40
+	assert.Equal(t, out.IPAM.Config[0].Subnet, "172.18.0.0/16")
41
+}
42
+
43
+func TestDaemonDefaultNetworkPools(t *testing.T) {
44
+	// Remove docker0 bridge and the start daemon defining the predefined address pools
45
+	defaultNetworkBridge := "docker0"
46
+	delInterface(t, defaultNetworkBridge)
47
+	d := daemon.New(t)
48
+	defer d.Stop(t)
49
+	d.Start(t,
50
+		"--default-address-pool", "base=175.30.0.0/16,size=16",
51
+		"--default-address-pool", "base=175.33.0.0/16,size=24")
52
+
53
+	// Verify bridge network's subnet
54
+	cli, err := d.NewClient()
55
+	assert.Assert(t, err)
56
+	defer cli.Close()
57
+	out, err := cli.NetworkInspect(context.Background(), "bridge", types.NetworkInspectOptions{})
58
+	assert.NilError(t, err)
59
+	assert.Equal(t, out.IPAM.Config[0].Subnet, "175.30.0.0/16")
60
+
61
+	// Create a bridge network and verify its subnet is the second default pool
62
+	name := "elango"
63
+	networkCreate := types.NetworkCreate{
64
+		CheckDuplicate: false,
65
+	}
66
+	networkCreate.Driver = "bridge"
67
+	_, err = cli.NetworkCreate(context.Background(), name, networkCreate)
68
+	assert.NilError(t, err)
69
+	out, err = cli.NetworkInspect(context.Background(), name, types.NetworkInspectOptions{})
70
+	assert.NilError(t, err)
71
+	assert.Equal(t, out.IPAM.Config[0].Subnet, "175.33.0.0/24")
72
+
73
+	// Create a bridge network and verify its subnet is the third default pool
74
+	name = "saanvi"
75
+	networkCreate = types.NetworkCreate{
76
+		CheckDuplicate: false,
77
+	}
78
+	networkCreate.Driver = "bridge"
79
+	_, err = cli.NetworkCreate(context.Background(), name, networkCreate)
80
+	assert.NilError(t, err)
81
+	out, err = cli.NetworkInspect(context.Background(), name, types.NetworkInspectOptions{})
82
+	assert.NilError(t, err)
83
+	assert.Equal(t, out.IPAM.Config[0].Subnet, "175.33.1.0/24")
84
+	delInterface(t, defaultNetworkBridge)
85
+
86
+}
87
+
88
+func TestDaemonRestartWithExistingNetwork(t *testing.T) {
89
+	defaultNetworkBridge := "docker0"
90
+	d := daemon.New(t)
91
+	d.Start(t)
92
+	defer d.Stop(t)
93
+	// Verify bridge network's subnet
94
+	cli, err := d.NewClient()
95
+	assert.Assert(t, err)
96
+	defer cli.Close()
97
+
98
+	// Create a bridge network
99
+	name := "elango"
100
+	networkCreate := types.NetworkCreate{
101
+		CheckDuplicate: false,
102
+	}
103
+	networkCreate.Driver = "bridge"
104
+	_, err = cli.NetworkCreate(context.Background(), name, networkCreate)
105
+	assert.NilError(t, err)
106
+	out, err := cli.NetworkInspect(context.Background(), name, types.NetworkInspectOptions{})
107
+	assert.NilError(t, err)
108
+	networkip := out.IPAM.Config[0].Subnet
109
+
110
+	// Restart daemon with default address pool option
111
+	d.Restart(t,
112
+		"--default-address-pool", "base=175.30.0.0/16,size=16",
113
+		"--default-address-pool", "base=175.33.0.0/16,size=24")
114
+
115
+	out1, err := cli.NetworkInspect(context.Background(), name, types.NetworkInspectOptions{})
116
+	assert.NilError(t, err)
117
+	assert.Equal(t, out1.IPAM.Config[0].Subnet, networkip)
118
+	delInterface(t, defaultNetworkBridge)
119
+}
120
+
121
+func TestDaemonRestartWithExistingNetworkWithDefaultPoolRange(t *testing.T) {
122
+	defaultNetworkBridge := "docker0"
123
+	d := daemon.New(t)
124
+	d.Start(t)
125
+	defer d.Stop(t)
126
+	// Verify bridge network's subnet
127
+	cli, err := d.NewClient()
128
+	assert.Assert(t, err)
129
+	defer cli.Close()
130
+
131
+	// Create a bridge network
132
+	name := "elango"
133
+	networkCreate := types.NetworkCreate{
134
+		CheckDuplicate: false,
135
+	}
136
+	networkCreate.Driver = "bridge"
137
+	_, err = cli.NetworkCreate(context.Background(), name, networkCreate)
138
+	assert.NilError(t, err)
139
+	out, err := cli.NetworkInspect(context.Background(), name, types.NetworkInspectOptions{})
140
+	assert.NilError(t, err)
141
+	networkip := out.IPAM.Config[0].Subnet
142
+
143
+	// Create a bridge network
144
+	name = "sthira"
145
+	networkCreate = types.NetworkCreate{
146
+		CheckDuplicate: false,
147
+	}
148
+	networkCreate.Driver = "bridge"
149
+	_, err = cli.NetworkCreate(context.Background(), name, networkCreate)
150
+	assert.NilError(t, err)
151
+	out, err = cli.NetworkInspect(context.Background(), name, types.NetworkInspectOptions{})
152
+	assert.NilError(t, err)
153
+	networkip2 := out.IPAM.Config[0].Subnet
154
+
155
+	// Restart daemon with default address pool option
156
+	d.Restart(t,
157
+		"--default-address-pool", "base=175.18.0.0/16,size=16",
158
+		"--default-address-pool", "base=175.19.0.0/16,size=24")
159
+
160
+	// Create a bridge network
161
+	name = "saanvi"
162
+	networkCreate = types.NetworkCreate{
163
+		CheckDuplicate: false,
164
+	}
165
+	networkCreate.Driver = "bridge"
166
+	_, err = cli.NetworkCreate(context.Background(), name, networkCreate)
167
+	assert.NilError(t, err)
168
+	out1, err := cli.NetworkInspect(context.Background(), name, types.NetworkInspectOptions{})
169
+	assert.NilError(t, err)
170
+
171
+	assert.Check(t, (out1.IPAM.Config[0].Subnet != networkip))
172
+	assert.Check(t, (out1.IPAM.Config[0].Subnet != networkip2))
173
+	delInterface(t, defaultNetworkBridge)
174
+}
175
+
176
+func TestDaemonWithBipAndDefaultNetworkPool(t *testing.T) {
177
+	defaultNetworkBridge := "docker0"
178
+	d := daemon.New(t)
179
+	defer d.Stop(t)
180
+	d.Start(t, "--bip=172.60.0.1/16",
181
+		"--default-address-pool", "base=175.30.0.0/16,size=16",
182
+		"--default-address-pool", "base=175.33.0.0/16,size=24")
183
+
184
+	// Verify bridge network's subnet
185
+	cli, err := d.NewClient()
186
+	assert.Assert(t, err)
187
+	defer cli.Close()
188
+	out, err := cli.NetworkInspect(context.Background(), "bridge", types.NetworkInspectOptions{})
189
+	assert.NilError(t, err)
190
+	// Make sure BIP IP doesn't get override with new default address pool .
191
+	assert.Equal(t, out.IPAM.Config[0].Subnet, "172.60.0.1/16")
192
+	delInterface(t, defaultNetworkBridge)
193
+}
194
+
16 195
 func TestServiceWithPredefinedNetwork(t *testing.T) {
17 196
 	defer setupTest(t)()
18 197
 	d := swarm.NewSwarm(t, testEnv)
19 198
new file mode 100644
... ...
@@ -0,0 +1,84 @@
0
+package opts
1
+
2
+import (
3
+	"encoding/csv"
4
+	"encoding/json"
5
+	"fmt"
6
+	"strconv"
7
+	"strings"
8
+
9
+	types "github.com/docker/libnetwork/ipamutils"
10
+)
11
+
12
+// PoolsOpt is a Value type for parsing the default address pools definitions
13
+type PoolsOpt struct {
14
+	values []*types.NetworkToSplit
15
+}
16
+
17
+// UnmarshalJSON fills values structure  info from JSON input
18
+func (p *PoolsOpt) UnmarshalJSON(raw []byte) error {
19
+	return json.Unmarshal(raw, &(p.values))
20
+}
21
+
22
+// Set predefined pools
23
+func (p *PoolsOpt) Set(value string) error {
24
+	csvReader := csv.NewReader(strings.NewReader(value))
25
+	fields, err := csvReader.Read()
26
+	if err != nil {
27
+		return err
28
+	}
29
+
30
+	poolsDef := types.NetworkToSplit{}
31
+
32
+	for _, field := range fields {
33
+		parts := strings.SplitN(field, "=", 2)
34
+		if len(parts) != 2 {
35
+			return fmt.Errorf("invalid field '%s' must be a key=value pair", field)
36
+		}
37
+
38
+		key := strings.ToLower(parts[0])
39
+		value := strings.ToLower(parts[1])
40
+
41
+		switch key {
42
+		case "base":
43
+			poolsDef.Base = value
44
+		case "size":
45
+			size, err := strconv.Atoi(value)
46
+			if err != nil {
47
+				return fmt.Errorf("invalid size value: %q (must be integer): %v", value, err)
48
+			}
49
+			poolsDef.Size = size
50
+		default:
51
+			return fmt.Errorf("unexpected key '%s' in '%s'", key, field)
52
+		}
53
+	}
54
+
55
+	p.values = append(p.values, &poolsDef)
56
+
57
+	return nil
58
+}
59
+
60
+// Type returns the type of this option
61
+func (p *PoolsOpt) Type() string {
62
+	return "pool-options"
63
+}
64
+
65
+// String returns a string repr of this option
66
+func (p *PoolsOpt) String() string {
67
+	pools := []string{}
68
+	for _, pool := range p.values {
69
+		repr := fmt.Sprintf("%s %d", pool.Base, pool.Size)
70
+		pools = append(pools, repr)
71
+	}
72
+	return strings.Join(pools, ", ")
73
+}
74
+
75
+// Value returns the mounts
76
+func (p *PoolsOpt) Value() []*types.NetworkToSplit {
77
+	return p.values
78
+}
79
+
80
+// Name returns the flag name of this option
81
+func (p *PoolsOpt) Name() string {
82
+	return "default-address-pools"
83
+}
0 84
new file mode 100644
... ...
@@ -0,0 +1,20 @@
0
+package opts // import "github.com/docker/docker/opts"
1
+
2
+import (
3
+	"testing"
4
+)
5
+
6
+func TestAddressPoolOpt(t *testing.T) {
7
+	poolopt := &PoolsOpt{}
8
+	var addresspool = "base=175.30.0.0/16,size=16"
9
+	var invalidAddresspoolString = "base=175.30.0.0/16,size=16, base=175.33.0.0/16,size=24"
10
+
11
+	if err := poolopt.Set(addresspool); err != nil {
12
+		t.Fatal(err)
13
+	}
14
+
15
+	if err := poolopt.Set(invalidAddresspoolString); err == nil {
16
+		t.Fatal(err)
17
+	}
18
+
19
+}