Browse code

Remove statewriter interface, export more libcontainer funcs

This temp. expands the Exec method's signature but adds a more robust
way to know when the container's process is actually released and begins
to run. The network interfaces are not guaranteed to be up yet but this
provides a more accurate view with a single callback at this time.
Docker-DCO-1.1-Signed-off-by: Michael Crosby <michael@crosbymichael.com> (github: crosbymichael)

Michael Crosby authored on 2014/05/01 07:52:40
Showing 6 changed files
... ...
@@ -29,7 +29,7 @@ func init() {
29 29
 	execdriver.RegisterInitFunc(DriverName, func(args *execdriver.InitArgs) error {
30 30
 		var (
31 31
 			container *libcontainer.Container
32
-			ns        = nsinit.NewNsInit(&nsinit.DefaultCommandFactory{}, &nsinit.DefaultStateWriter{args.Root})
32
+			ns        = nsinit.NewNsInit(&nsinit.DefaultCommandFactory{})
33 33
 		)
34 34
 		f, err := os.Open(filepath.Join(args.Root, "container.json"))
35 35
 		if err != nil {
... ...
@@ -93,15 +93,11 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba
93 93
 	d.activeContainers[c.ID] = &c.Cmd
94 94
 
95 95
 	var (
96
-		term        nsinit.Terminal
97
-		factory     = &dockerCommandFactory{c: c, driver: d}
98
-		stateWriter = &dockerStateWriter{
99
-			callback: startCallback,
100
-			c:        c,
101
-			dsw:      &nsinit.DefaultStateWriter{filepath.Join(d.root, c.ID)},
102
-		}
103
-		ns   = nsinit.NewNsInit(factory, stateWriter)
104
-		args = append([]string{c.Entrypoint}, c.Arguments...)
96
+		term    nsinit.Terminal
97
+		factory = &dockerCommandFactory{c: c, driver: d}
98
+		pidRoot = filepath.Join(d.root, c.ID)
99
+		ns      = nsinit.NewNsInit(factory)
100
+		args    = append([]string{c.Entrypoint}, c.Arguments...)
105 101
 	)
106 102
 	if err := d.createContainerRoot(c.ID); err != nil {
107 103
 		return -1, err
... ...
@@ -121,7 +117,11 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba
121 121
 	if err := d.writeContainerFile(container, c.ID); err != nil {
122 122
 		return -1, err
123 123
 	}
124
-	return ns.Exec(container, term, args)
124
+	return ns.Exec(container, term, pidRoot, args, func() {
125
+		if startCallback != nil {
126
+			startCallback(c)
127
+		}
128
+	})
125 129
 }
126 130
 
127 131
 func (d *driver) Kill(p *execdriver.Command, sig int) error {
... ...
@@ -266,22 +266,3 @@ func (d *dockerCommandFactory) Create(container *libcontainer.Container, console
266 266
 
267 267
 	return &d.c.Cmd
268 268
 }
269
-
270
-type dockerStateWriter struct {
271
-	dsw      nsinit.StateWriter
272
-	c        *execdriver.Command
273
-	callback execdriver.StartCallback
274
-}
275
-
276
-func (d *dockerStateWriter) WritePid(pid int, started string) error {
277
-	d.c.ContainerPid = pid
278
-	err := d.dsw.WritePid(pid, started)
279
-	if d.callback != nil {
280
-		d.callback(d.c)
281
-	}
282
-	return err
283
-}
284
-
285
-func (d *dockerStateWriter) DeletePid() error {
286
-	return d.dsw.DeletePid()
287
-}
... ...
@@ -3,8 +3,11 @@
3 3
 package nsinit
4 4
 
5 5
 import (
6
+	"fmt"
7
+	"io/ioutil"
6 8
 	"os"
7 9
 	"os/exec"
10
+	"path/filepath"
8 11
 	"syscall"
9 12
 
10 13
 	"github.com/dotcloud/docker/pkg/cgroups"
... ...
@@ -17,7 +20,7 @@ import (
17 17
 
18 18
 // Exec performes setup outside of a namespace so that a container can be
19 19
 // executed.  Exec is a high level function for working with container namespaces.
20
-func (ns *linuxNs) Exec(container *libcontainer.Container, term Terminal, args []string) (int, error) {
20
+func (ns *linuxNs) Exec(container *libcontainer.Container, term Terminal, pidRoot string, args []string, startCallback func()) (int, error) {
21 21
 	var (
22 22
 		master  *os.File
23 23
 		console string
... ...
@@ -53,24 +56,24 @@ func (ns *linuxNs) Exec(container *libcontainer.Container, term Terminal, args [
53 53
 	if err != nil {
54 54
 		return -1, err
55 55
 	}
56
-	if err := ns.stateWriter.WritePid(command.Process.Pid, started); err != nil {
56
+	if err := WritePid(pidRoot, command.Process.Pid, started); err != nil {
57 57
 		command.Process.Kill()
58 58
 		return -1, err
59 59
 	}
60
-	defer ns.stateWriter.DeletePid()
60
+	defer DeletePid(pidRoot)
61 61
 
62 62
 	// Do this before syncing with child so that no children
63 63
 	// can escape the cgroup
64
-	activeCgroup, err := ns.SetupCgroups(container, command.Process.Pid)
64
+	cleaner, err := SetupCgroups(container, command.Process.Pid)
65 65
 	if err != nil {
66 66
 		command.Process.Kill()
67 67
 		return -1, err
68 68
 	}
69
-	if activeCgroup != nil {
70
-		defer activeCgroup.Cleanup()
69
+	if cleaner != nil {
70
+		defer cleaner.Cleanup()
71 71
 	}
72 72
 
73
-	if err := ns.InitializeNetworking(container, command.Process.Pid, syncPipe); err != nil {
73
+	if err := InitializeNetworking(container, command.Process.Pid, syncPipe); err != nil {
74 74
 		command.Process.Kill()
75 75
 		return -1, err
76 76
 	}
... ...
@@ -78,6 +81,10 @@ func (ns *linuxNs) Exec(container *libcontainer.Container, term Terminal, args [
78 78
 	// Sync with child
79 79
 	syncPipe.Close()
80 80
 
81
+	if startCallback != nil {
82
+		startCallback()
83
+	}
84
+
81 85
 	if err := command.Wait(); err != nil {
82 86
 		if _, ok := err.(*exec.ExitError); !ok {
83 87
 			return -1, err
... ...
@@ -87,7 +94,9 @@ func (ns *linuxNs) Exec(container *libcontainer.Container, term Terminal, args [
87 87
 	return status, err
88 88
 }
89 89
 
90
-func (ns *linuxNs) SetupCgroups(container *libcontainer.Container, nspid int) (cgroups.ActiveCgroup, error) {
90
+// SetupCgroups applies the cgroup restrictions to the process running in the contaienr based
91
+// on the container's configuration
92
+func SetupCgroups(container *libcontainer.Container, nspid int) (cgroups.ActiveCgroup, error) {
91 93
 	if container.Cgroups != nil {
92 94
 		c := container.Cgroups
93 95
 		if systemd.UseSystemd() {
... ...
@@ -98,7 +107,9 @@ func (ns *linuxNs) SetupCgroups(container *libcontainer.Container, nspid int) (c
98 98
 	return nil, nil
99 99
 }
100 100
 
101
-func (ns *linuxNs) InitializeNetworking(container *libcontainer.Container, nspid int, pipe *SyncPipe) error {
101
+// InitializeNetworking creates the container's network stack outside of the namespace and moves
102
+// interfaces into the container's net namespaces if necessary
103
+func InitializeNetworking(container *libcontainer.Container, nspid int, pipe *SyncPipe) error {
102 104
 	context := libcontainer.Context{}
103 105
 	for _, config := range container.Networks {
104 106
 		strategy, err := network.GetStrategy(config.Type)
... ...
@@ -111,3 +122,23 @@ func (ns *linuxNs) InitializeNetworking(container *libcontainer.Container, nspid
111 111
 	}
112 112
 	return pipe.SendToChild(context)
113 113
 }
114
+
115
+// WritePid writes the namespaced processes pid to pid and it's start time
116
+// to the path specified
117
+func WritePid(path string, pid int, startTime string) error {
118
+	err := ioutil.WriteFile(filepath.Join(path, "pid"), []byte(fmt.Sprint(pid)), 0655)
119
+	if err != nil {
120
+		return err
121
+	}
122
+	return ioutil.WriteFile(filepath.Join(path, "start"), []byte(startTime), 0655)
123
+}
124
+
125
+// DeletePid removes the pid and started file from disk when the container's process
126
+// dies and the container is cleanly removed
127
+func DeletePid(path string) error {
128
+	err := os.Remove(filepath.Join(path, "pid"))
129
+	if serr := os.Remove(filepath.Join(path, "start")); err == nil {
130
+		err = serr
131
+	}
132
+	return err
133
+}
... ...
@@ -5,7 +5,7 @@ import "github.com/dotcloud/docker/pkg/libcontainer"
5 5
 // NsInit is an interface with the public facing methods to provide high level
6 6
 // exec operations on a container
7 7
 type NsInit interface {
8
-	Exec(container *libcontainer.Container, term Terminal, args []string) (int, error)
8
+	Exec(container *libcontainer.Container, term Terminal, pidRoot string, args []string, startCallback func()) (int, error)
9 9
 	ExecIn(container *libcontainer.Container, nspid int, args []string) (int, error)
10 10
 	Init(container *libcontainer.Container, uncleanRootfs, console string, syncPipe *SyncPipe, args []string) error
11 11
 }
... ...
@@ -13,12 +13,10 @@ type NsInit interface {
13 13
 type linuxNs struct {
14 14
 	root           string
15 15
 	commandFactory CommandFactory
16
-	stateWriter    StateWriter
17 16
 }
18 17
 
19
-func NewNsInit(command CommandFactory, state StateWriter) NsInit {
18
+func NewNsInit(command CommandFactory) NsInit {
20 19
 	return &linuxNs{
21 20
 		commandFactory: command,
22
-		stateWriter:    state,
23 21
 	}
24 22
 }
... ...
@@ -56,7 +56,7 @@ func main() {
56 56
 			exitCode, err = ns.ExecIn(container, nspid, flag.Args()[1:])
57 57
 		} else {
58 58
 			term := nsinit.NewTerminal(os.Stdin, os.Stdout, os.Stderr, container.Tty)
59
-			exitCode, err = ns.Exec(container, term, flag.Args()[1:])
59
+			exitCode, err = ns.Exec(container, term, root, flag.Args()[1:], nil)
60 60
 		}
61 61
 		if err != nil {
62 62
 			log.Fatalf("Failed to exec: %s", err)
... ...
@@ -109,5 +109,5 @@ func readPid() (int, error) {
109 109
 }
110 110
 
111 111
 func newNsInit() (nsinit.NsInit, error) {
112
-	return nsinit.NewNsInit(&nsinit.DefaultCommandFactory{root}, &nsinit.DefaultStateWriter{root}), nil
112
+	return nsinit.NewNsInit(&nsinit.DefaultCommandFactory{root}), nil
113 113
 }
114 114
deleted file mode 100644
... ...
@@ -1,36 +0,0 @@
1
-package nsinit
2
-
3
-import (
4
-	"fmt"
5
-	"io/ioutil"
6
-	"os"
7
-	"path/filepath"
8
-)
9
-
10
-// StateWriter handles writing and deleting the pid file
11
-// on disk
12
-type StateWriter interface {
13
-	WritePid(pid int, startTime string) error
14
-	DeletePid() error
15
-}
16
-
17
-type DefaultStateWriter struct {
18
-	Root string
19
-}
20
-
21
-// writePidFile writes the namespaced processes pid to pid in the rootfs for the container
22
-func (d *DefaultStateWriter) WritePid(pid int, startTime string) error {
23
-	err := ioutil.WriteFile(filepath.Join(d.Root, "pid"), []byte(fmt.Sprint(pid)), 0655)
24
-	if err != nil {
25
-		return err
26
-	}
27
-	return ioutil.WriteFile(filepath.Join(d.Root, "start"), []byte(startTime), 0655)
28
-}
29
-
30
-func (d *DefaultStateWriter) DeletePid() error {
31
-	err := os.Remove(filepath.Join(d.Root, "pid"))
32
-	if serr := os.Remove(filepath.Join(d.Root, "start")); err == nil {
33
-		err = serr
34
-	}
35
-	return err
36
-}
... ...
@@ -6,7 +6,7 @@ import (
6 6
 	"github.com/dotcloud/docker/pkg/libcontainer"
7 7
 )
8 8
 
9
-func (ns *linuxNs) Exec(container *libcontainer.Container, term Terminal, args []string) (int, error) {
9
+func (ns *linuxNs) Exec(container *libcontainer.Container, term Terminal, pidRoot string, args []string, startCallback func()) (int, error) {
10 10
 	return -1, libcontainer.ErrUnsupported
11 11
 }
12 12