Browse code

Refactor network creation and initialization into strategies Docker-DCO-1.1-Signed-off-by: Michael Crosby <michael@crosbymichael.com> (github: crosbymichael)

Michael Crosby authored on 2014/02/22 15:20:15
Showing 7 changed files
... ...
@@ -45,12 +45,17 @@ Sample `container.json` file:
45 45
         "AUDIT_WRITE",
46 46
         "AUDIT_CONTROL",
47 47
         "MAC_OVERRIDE",
48
-        "MAC_ADMIN"
48
+        "MAC_ADMIN",
49
+        "NET_ADMIN"
49 50
     ],
50 51
     "network": {
52
+        "type": "veth",
53
+        "context": {
54
+            "bridge": "docker0",
55
+            "prefix": "dock"
56
+        },
51 57
         "address": "172.17.0.100/16",
52 58
         "gateway": "172.17.42.1",
53
-        "bridge": "docker0",
54 59
         "mtu": 1500
55 60
     },
56 61
     "cgroups": {
... ...
@@ -4,6 +4,10 @@ import (
4 4
 	"github.com/dotcloud/docker/pkg/cgroups"
5 5
 )
6 6
 
7
+// Context is a generic key value pair that allows
8
+// arbatrary data to be sent
9
+type Context map[string]string
10
+
7 11
 // Container defines configuration options for how a
8 12
 // container is setup inside a directory and how a process should be executed
9 13
 type Container struct {
... ...
@@ -24,8 +28,9 @@ type Container struct {
24 24
 // The network configuration can be omited from a container causing the
25 25
 // container to be setup with the host's networking stack
26 26
 type Network struct {
27
-	Address string `json:"address,omitempty"`
28
-	Gateway string `json:"gateway,omitempty"`
29
-	Bridge  string `json:"bridge,omitempty"`
30
-	Mtu     int    `json:"mtu,omitempty"`
27
+	Type    string  `json:"type,omitempty"`    // type of networking to setup i.e. veth, macvlan, etc
28
+	Context Context `json:"context,omitempty"` // generic context for type specific networking options
29
+	Address string  `json:"address,omitempty"`
30
+	Gateway string  `json:"gateway,omitempty"`
31
+	Mtu     int     `json:"mtu,omitempty"`
31 32
 }
... ...
@@ -28,12 +28,17 @@
28 28
         "AUDIT_WRITE",
29 29
         "AUDIT_CONTROL",
30 30
         "MAC_OVERRIDE",
31
-        "MAC_ADMIN"
31
+        "MAC_ADMIN",
32
+        "NET_ADMIN"
32 33
     ],
33 34
     "network": {
35
+        "type": "veth",
36
+        "context": {
37
+            "bridge": "docker0",
38
+            "prefix": "dock"
39
+        },
34 40
         "address": "172.17.0.100/16",
35 41
         "gateway": "172.17.42.1",
36
-        "bridge": "docker0",
37 42
         "mtu": 1500
38 43
     },
39 44
     "cgroups": {
40 45
new file mode 100644
... ...
@@ -0,0 +1,32 @@
0
+package network
1
+
2
+import (
3
+	"errors"
4
+	"github.com/dotcloud/docker/pkg/libcontainer"
5
+)
6
+
7
+var (
8
+	ErrNotValidStrategyType = errors.New("not a valid network strategy type")
9
+)
10
+
11
+var strategies = map[string]NetworkStrategy{
12
+	"veth": &Veth{},
13
+}
14
+
15
+// NetworkStrategy represends a specific network configuration for
16
+// a containers networking stack
17
+type NetworkStrategy interface {
18
+	Create(*libcontainer.Network, int) (libcontainer.Context, error)
19
+	Initialize(*libcontainer.Network, libcontainer.Context) error
20
+}
21
+
22
+// GetStrategy returns the specific network strategy for the
23
+// provided type.  If no strategy is registered for the type an
24
+// ErrNotValidStrategyType is returned.
25
+func GetStrategy(tpe string) (NetworkStrategy, error) {
26
+	s, exists := strategies[tpe]
27
+	if !exists {
28
+		return nil, ErrNotValidStrategyType
29
+	}
30
+	return s, nil
31
+}
0 32
new file mode 100644
... ...
@@ -0,0 +1,103 @@
0
+package network
1
+
2
+import (
3
+	"fmt"
4
+	"github.com/dotcloud/docker/pkg/libcontainer"
5
+	"github.com/dotcloud/docker/pkg/libcontainer/utils"
6
+	"log"
7
+)
8
+
9
+type Veth struct {
10
+}
11
+
12
+func (v *Veth) Create(n *libcontainer.Network, nspid int) (libcontainer.Context, error) {
13
+	log.Printf("creating veth network")
14
+	var (
15
+		bridge string
16
+		prefix string
17
+		exists bool
18
+	)
19
+	if bridge, exists = n.Context["bridge"]; !exists {
20
+		return nil, fmt.Errorf("bridge does not exist in network context")
21
+	}
22
+	if prefix, exists = n.Context["prefix"]; !exists {
23
+		return nil, fmt.Errorf("veth prefix does not exist in network context")
24
+	}
25
+	name1, name2, err := createVethPair(prefix)
26
+	if err != nil {
27
+		return nil, err
28
+	}
29
+	context := libcontainer.Context{
30
+		"vethHost":  name1,
31
+		"vethChild": name2,
32
+	}
33
+	log.Printf("veth pair created %s <> %s", name1, name2)
34
+	if err := SetInterfaceMaster(name1, bridge); err != nil {
35
+		return context, err
36
+	}
37
+	if err := SetMtu(name1, n.Mtu); err != nil {
38
+		return context, err
39
+	}
40
+	if err := InterfaceUp(name1); err != nil {
41
+		return context, err
42
+	}
43
+	log.Printf("setting %s inside %d namespace", name2, nspid)
44
+	if err := SetInterfaceInNamespacePid(name2, nspid); err != nil {
45
+		return context, err
46
+	}
47
+	return context, nil
48
+}
49
+
50
+func (v *Veth) Initialize(config *libcontainer.Network, context libcontainer.Context) error {
51
+	var (
52
+		vethChild string
53
+		exists    bool
54
+	)
55
+	if vethChild, exists = context["vethChild"]; !exists {
56
+		return fmt.Errorf("vethChild does not exist in network context")
57
+	}
58
+	if err := InterfaceDown(vethChild); err != nil {
59
+		return fmt.Errorf("interface down %s %s", vethChild, err)
60
+	}
61
+	if err := ChangeInterfaceName(vethChild, "eth0"); err != nil {
62
+		return fmt.Errorf("change %s to eth0 %s", vethChild, err)
63
+	}
64
+	if err := SetInterfaceIp("eth0", config.Address); err != nil {
65
+		return fmt.Errorf("set eth0 ip %s", err)
66
+	}
67
+	if err := SetMtu("eth0", config.Mtu); err != nil {
68
+		return fmt.Errorf("set eth0 mtu to %d %s", config.Mtu, err)
69
+	}
70
+	if err := InterfaceUp("eth0"); err != nil {
71
+		return fmt.Errorf("eth0 up %s", err)
72
+	}
73
+	if err := SetMtu("lo", config.Mtu); err != nil {
74
+		return fmt.Errorf("set lo mtu to %d %s", config.Mtu, err)
75
+	}
76
+	if err := InterfaceUp("lo"); err != nil {
77
+		return fmt.Errorf("lo up %s", err)
78
+	}
79
+	if config.Gateway != "" {
80
+		if err := SetDefaultGateway(config.Gateway); err != nil {
81
+			return fmt.Errorf("set gateway to %s %s", config.Gateway, err)
82
+		}
83
+	}
84
+	return nil
85
+}
86
+
87
+// createVethPair will automatically generage two random names for
88
+// the veth pair and ensure that they have been created
89
+func createVethPair(prefix string) (name1 string, name2 string, err error) {
90
+	name1, err = utils.GenerateRandomName(prefix, 4)
91
+	if err != nil {
92
+		return
93
+	}
94
+	name2, err = utils.GenerateRandomName(prefix, 4)
95
+	if err != nil {
96
+		return
97
+	}
98
+	if err = CreateVethPair(name1, name2); err != nil {
99
+		return
100
+	}
101
+	return
102
+}
... ...
@@ -3,10 +3,10 @@
3 3
 package nsinit
4 4
 
5 5
 import (
6
+	"encoding/json"
6 7
 	"fmt"
7 8
 	"github.com/dotcloud/docker/pkg/libcontainer"
8 9
 	"github.com/dotcloud/docker/pkg/libcontainer/network"
9
-	"github.com/dotcloud/docker/pkg/libcontainer/utils"
10 10
 	"github.com/dotcloud/docker/pkg/system"
11 11
 	"github.com/dotcloud/docker/pkg/term"
12 12
 	"io"
... ...
@@ -19,11 +19,11 @@ import (
19 19
 
20 20
 // Exec performes setup outside of a namespace so that a container can be
21 21
 // executed.  Exec is a high level function for working with container namespaces.
22
-func Exec(container *libcontainer.Container, stdin io.Reader, stdout, stderr io.Writer, master *os.File, logFile string, args []string) (int, error) {
22
+func Exec(container *libcontainer.Container, stdin io.Reader, stdout, stderr io.Writer,
23
+	master *os.File, logFile string, args []string) (int, error) {
23 24
 	var (
24
-		console string
25
-		err     error
26
-
25
+		console          string
26
+		err              error
27 27
 		inPipe           io.WriteCloser
28 28
 		outPipe, errPipe io.ReadCloser
29 29
 	)
... ...
@@ -46,7 +46,7 @@ func Exec(container *libcontainer.Container, stdin io.Reader, stdout, stderr io.
46 46
 
47 47
 	command := CreateCommand(container, console, logFile, r.Fd(), args)
48 48
 	if !container.Tty {
49
-		log.Printf("opening pipes on command")
49
+		log.Printf("opening std pipes")
50 50
 		if inPipe, err = command.StdinPipe(); err != nil {
51 51
 			return -1, err
52 52
 		}
... ...
@@ -78,15 +78,9 @@ func Exec(container *libcontainer.Container, stdin io.Reader, stdout, stderr io.
78 78
 			return -1, err
79 79
 		}
80 80
 	}
81
-
82
-	if container.Network != nil {
83
-		log.Printf("creating veth pair")
84
-		vethPair, err := InitializeContainerVeth(container.Network.Bridge, container.Network.Mtu, command.Process.Pid)
85
-		if err != nil {
86
-			return -1, err
87
-		}
88
-		log.Printf("sending %s as veth pair name", vethPair)
89
-		SendVethName(w, vethPair)
81
+	if err := InitializeNetworking(container, command.Process.Pid, w); err != nil {
82
+		command.Process.Kill()
83
+		return -1, err
90 84
 	}
91 85
 
92 86
 	// Sync with child
... ...
@@ -104,7 +98,7 @@ func Exec(container *libcontainer.Container, stdin io.Reader, stdout, stderr io.
104 104
 			command.Process.Kill()
105 105
 			return -1, err
106 106
 		}
107
-		defer term.RestoreTerminal(uintptr(syscall.Stdin), state)
107
+		defer term.RestoreTerminal(os.Stdin.Fd(), state)
108 108
 	} else {
109 109
 		log.Printf("starting copy for std pipes")
110 110
 		go func() {
... ...
@@ -125,39 +119,34 @@ func Exec(container *libcontainer.Container, stdin io.Reader, stdout, stderr io.
125 125
 	return command.ProcessState.Sys().(syscall.WaitStatus).ExitStatus(), nil
126 126
 }
127 127
 
128
-// SendVethName writes the veth pair name to the child's stdin then closes the
129
-// pipe so that the child stops waiting for more data
130
-func SendVethName(pipe io.Writer, name string) {
131
-	fmt.Fprint(pipe, name)
128
+func InitializeNetworking(container *libcontainer.Container, nspid int, pipe io.Writer) error {
129
+	if container.Network != nil {
130
+		log.Printf("creating host network configuration type %s", container.Network.Type)
131
+		strategy, err := network.GetStrategy(container.Network.Type)
132
+		if err != nil {
133
+			return err
134
+		}
135
+		networkContext, err := strategy.Create(container.Network, nspid)
136
+		if err != nil {
137
+			return err
138
+		}
139
+		log.Printf("sending %v as network context", networkContext)
140
+		if err := SendContext(pipe, networkContext); err != nil {
141
+			return err
142
+		}
143
+	}
144
+	return nil
132 145
 }
133 146
 
134
-// initializeContainerVeth will create a veth pair and setup the host's
135
-// side of the pair by setting the specified bridge as the master and bringing
136
-// up the interface.
137
-//
138
-// Then will with set the other side of the veth pair into the container's namespaced
139
-// using the pid and returns the veth's interface name to provide to the container to
140
-// finish setting up the interface inside the namespace
141
-func InitializeContainerVeth(bridge string, mtu, nspid int) (string, error) {
142
-	name1, name2, err := createVethPair()
147
+// SendContext writes the veth pair name to the child's stdin then closes the
148
+// pipe so that the child stops waiting for more data
149
+func SendContext(pipe io.Writer, context libcontainer.Context) error {
150
+	data, err := json.Marshal(context)
143 151
 	if err != nil {
144
-		return "", err
145
-	}
146
-	log.Printf("veth pair created %s <> %s", name1, name2)
147
-	if err := network.SetInterfaceMaster(name1, bridge); err != nil {
148
-		return "", err
149
-	}
150
-	if err := network.SetMtu(name1, mtu); err != nil {
151
-		return "", err
152
-	}
153
-	if err := network.InterfaceUp(name1); err != nil {
154
-		return "", err
155
-	}
156
-	log.Printf("setting %s inside %d namespace", name2, nspid)
157
-	if err := network.SetInterfaceInNamespacePid(name2, nspid); err != nil {
158
-		return "", err
152
+		return err
159 153
 	}
160
-	return name2, nil
154
+	pipe.Write(data)
155
+	return nil
161 156
 }
162 157
 
163 158
 // SetupWindow gets the parent window size and sets the master
... ...
@@ -190,29 +179,13 @@ func CreateMasterAndConsole() (*os.File, string, error) {
190 190
 	return master, console, nil
191 191
 }
192 192
 
193
-// createVethPair will automatically generage two random names for
194
-// the veth pair and ensure that they have been created
195
-func createVethPair() (name1 string, name2 string, err error) {
196
-	name1, err = utils.GenerateRandomName("dock", 4)
197
-	if err != nil {
198
-		return
199
-	}
200
-	name2, err = utils.GenerateRandomName("dock", 4)
201
-	if err != nil {
202
-		return
203
-	}
204
-	if err = network.CreateVethPair(name1, name2); err != nil {
205
-		return
206
-	}
207
-	return
208
-}
209
-
210 193
 // writePidFile writes the namespaced processes pid to .nspid in the rootfs for the container
211 194
 func writePidFile(command *exec.Cmd) error {
212 195
 	return ioutil.WriteFile(".nspid", []byte(fmt.Sprint(command.Process.Pid)), 0655)
213 196
 }
214 197
 
215 198
 func deletePidFile() error {
199
+	log.Printf("removing .nspid file")
216 200
 	return os.Remove(".nspid")
217 201
 }
218 202
 
... ...
@@ -3,6 +3,7 @@
3 3
 package nsinit
4 4
 
5 5
 import (
6
+	"encoding/json"
6 7
 	"fmt"
7 8
 	"github.com/dotcloud/docker/pkg/libcontainer"
8 9
 	"github.com/dotcloud/docker/pkg/libcontainer/capabilities"
... ...
@@ -27,13 +28,10 @@ func Init(container *libcontainer.Container, uncleanRootfs, console string, pipe
27 27
 	log.Printf("initializing namespace at %s", rootfs)
28 28
 
29 29
 	// We always read this as it is a way to sync with the parent as well
30
-	tempVethName, err := getVethName(pipe)
30
+	context, err := GetContextFromParent(pipe)
31 31
 	if err != nil {
32 32
 		return err
33 33
 	}
34
-	if tempVethName != "" {
35
-		log.Printf("received veth name %s", tempVethName)
36
-	}
37 34
 	if console != "" {
38 35
 		log.Printf("setting up console for %s", console)
39 36
 		// close pipes so that we can replace it with the pty
... ...
@@ -62,7 +60,7 @@ func Init(container *libcontainer.Container, uncleanRootfs, console string, pipe
62 62
 	if err := setupNewMountNamespace(rootfs, console, container.ReadonlyFs); err != nil {
63 63
 		return fmt.Errorf("setup mount namespace %s", err)
64 64
 	}
65
-	if err := setupVethNetwork(container.Network, tempVethName); err != nil {
65
+	if err := setupNetwork(container.Network, context); err != nil {
66 66
 		return fmt.Errorf("setup networking %s", err)
67 67
 	}
68 68
 	if err := system.Sethostname(container.Hostname); err != nil {
... ...
@@ -145,46 +143,29 @@ func openTerminal(name string, flag int) (*os.File, error) {
145 145
 // setupVethNetwork uses the Network config if it is not nil to initialize
146 146
 // the new veth interface inside the container for use by changing the name to eth0
147 147
 // setting the MTU and IP address along with the default gateway
148
-func setupVethNetwork(config *libcontainer.Network, tempVethName string) error {
148
+func setupNetwork(config *libcontainer.Network, context libcontainer.Context) error {
149 149
 	if config != nil {
150
-		if err := network.InterfaceDown(tempVethName); err != nil {
151
-			return fmt.Errorf("interface down %s %s", tempVethName, err)
152
-		}
153
-		if err := network.ChangeInterfaceName(tempVethName, "eth0"); err != nil {
154
-			return fmt.Errorf("change %s to eth0 %s", tempVethName, err)
155
-		}
156
-		if err := network.SetInterfaceIp("eth0", config.Address); err != nil {
157
-			return fmt.Errorf("set eth0 ip %s", err)
158
-		}
159
-		if err := network.SetMtu("eth0", config.Mtu); err != nil {
160
-			return fmt.Errorf("set eth0 mtu to %d %s", config.Mtu, err)
161
-		}
162
-		if err := network.InterfaceUp("eth0"); err != nil {
163
-			return fmt.Errorf("eth0 up %s", err)
164
-		}
165
-		if err := network.SetMtu("lo", config.Mtu); err != nil {
166
-			return fmt.Errorf("set lo mtu to %d %s", config.Mtu, err)
167
-		}
168
-		if err := network.InterfaceUp("lo"); err != nil {
169
-			return fmt.Errorf("lo up %s", err)
170
-		}
171
-		if config.Gateway != "" {
172
-			if err := network.SetDefaultGateway(config.Gateway); err != nil {
173
-				return fmt.Errorf("set gateway to %s %s", config.Gateway, err)
174
-			}
150
+		strategy, err := network.GetStrategy(config.Type)
151
+		if err != nil {
152
+			return err
175 153
 		}
154
+		return strategy.Initialize(config, context)
176 155
 	}
177 156
 	return nil
178 157
 }
179 158
 
180
-// getVethName reads from Stdin the temp veth name
181
-// sent by the parent processes after the veth pair
182
-// has been created and setup
183
-func getVethName(pipe io.ReadCloser) (string, error) {
159
+func GetContextFromParent(pipe io.ReadCloser) (libcontainer.Context, error) {
184 160
 	defer pipe.Close()
185 161
 	data, err := ioutil.ReadAll(pipe)
186 162
 	if err != nil {
187
-		return "", fmt.Errorf("error reading from stdin %s", err)
163
+		return nil, fmt.Errorf("error reading from stdin %s", err)
164
+	}
165
+	var context libcontainer.Context
166
+	if len(data) > 0 {
167
+		if err := json.Unmarshal(data, &context); err != nil {
168
+			return nil, err
169
+		}
170
+		log.Printf("received context %v", context)
188 171
 	}
189
-	return string(data), nil
172
+	return context, nil
190 173
 }