浏览代码

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 11 年之前
父节点
当前提交
f110401437

+ 11 - 30
daemon/execdriver/native/driver.go

@@ -29,7 +29,7 @@ func init() {
 	execdriver.RegisterInitFunc(DriverName, func(args *execdriver.InitArgs) error {
 		var (
 			container *libcontainer.Container
-			ns        = nsinit.NewNsInit(&nsinit.DefaultCommandFactory{}, &nsinit.DefaultStateWriter{args.Root})
+			ns        = nsinit.NewNsInit(&nsinit.DefaultCommandFactory{})
 		)
 		f, err := os.Open(filepath.Join(args.Root, "container.json"))
 		if err != nil {
@@ -93,15 +93,11 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba
 	d.activeContainers[c.ID] = &c.Cmd
 
 	var (
-		term        nsinit.Terminal
-		factory     = &dockerCommandFactory{c: c, driver: d}
-		stateWriter = &dockerStateWriter{
-			callback: startCallback,
-			c:        c,
-			dsw:      &nsinit.DefaultStateWriter{filepath.Join(d.root, c.ID)},
-		}
-		ns   = nsinit.NewNsInit(factory, stateWriter)
-		args = append([]string{c.Entrypoint}, c.Arguments...)
+		term    nsinit.Terminal
+		factory = &dockerCommandFactory{c: c, driver: d}
+		pidRoot = filepath.Join(d.root, c.ID)
+		ns      = nsinit.NewNsInit(factory)
+		args    = append([]string{c.Entrypoint}, c.Arguments...)
 	)
 	if err := d.createContainerRoot(c.ID); err != nil {
 		return -1, err
@@ -121,7 +117,11 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba
 	if err := d.writeContainerFile(container, c.ID); err != nil {
 		return -1, err
 	}
-	return ns.Exec(container, term, args)
+	return ns.Exec(container, term, pidRoot, args, func() {
+		if startCallback != nil {
+			startCallback(c)
+		}
+	})
 }
 
 func (d *driver) Kill(p *execdriver.Command, sig int) error {
@@ -266,22 +266,3 @@ func (d *dockerCommandFactory) Create(container *libcontainer.Container, console
 
 	return &d.c.Cmd
 }
-
-type dockerStateWriter struct {
-	dsw      nsinit.StateWriter
-	c        *execdriver.Command
-	callback execdriver.StartCallback
-}
-
-func (d *dockerStateWriter) WritePid(pid int, started string) error {
-	d.c.ContainerPid = pid
-	err := d.dsw.WritePid(pid, started)
-	if d.callback != nil {
-		d.callback(d.c)
-	}
-	return err
-}
-
-func (d *dockerStateWriter) DeletePid() error {
-	return d.dsw.DeletePid()
-}

+ 40 - 9
pkg/libcontainer/nsinit/exec.go

@@ -3,8 +3,11 @@
 package nsinit
 
 import (
+	"fmt"
+	"io/ioutil"
 	"os"
 	"os/exec"
+	"path/filepath"
 	"syscall"
 
 	"github.com/dotcloud/docker/pkg/cgroups"
@@ -17,7 +20,7 @@ import (
 
 // Exec performes setup outside of a namespace so that a container can be
 // executed.  Exec is a high level function for working with container namespaces.
-func (ns *linuxNs) Exec(container *libcontainer.Container, term Terminal, args []string) (int, error) {
+func (ns *linuxNs) Exec(container *libcontainer.Container, term Terminal, pidRoot string, args []string, startCallback func()) (int, error) {
 	var (
 		master  *os.File
 		console string
@@ -53,24 +56,24 @@ func (ns *linuxNs) Exec(container *libcontainer.Container, term Terminal, args [
 	if err != nil {
 		return -1, err
 	}
-	if err := ns.stateWriter.WritePid(command.Process.Pid, started); err != nil {
+	if err := WritePid(pidRoot, command.Process.Pid, started); err != nil {
 		command.Process.Kill()
 		return -1, err
 	}
-	defer ns.stateWriter.DeletePid()
+	defer DeletePid(pidRoot)
 
 	// Do this before syncing with child so that no children
 	// can escape the cgroup
-	activeCgroup, err := ns.SetupCgroups(container, command.Process.Pid)
+	cleaner, err := SetupCgroups(container, command.Process.Pid)
 	if err != nil {
 		command.Process.Kill()
 		return -1, err
 	}
-	if activeCgroup != nil {
-		defer activeCgroup.Cleanup()
+	if cleaner != nil {
+		defer cleaner.Cleanup()
 	}
 
-	if err := ns.InitializeNetworking(container, command.Process.Pid, syncPipe); err != nil {
+	if err := InitializeNetworking(container, command.Process.Pid, syncPipe); err != nil {
 		command.Process.Kill()
 		return -1, err
 	}
@@ -78,6 +81,10 @@ func (ns *linuxNs) Exec(container *libcontainer.Container, term Terminal, args [
 	// Sync with child
 	syncPipe.Close()
 
+	if startCallback != nil {
+		startCallback()
+	}
+
 	if err := command.Wait(); err != nil {
 		if _, ok := err.(*exec.ExitError); !ok {
 			return -1, err
@@ -87,7 +94,9 @@ func (ns *linuxNs) Exec(container *libcontainer.Container, term Terminal, args [
 	return status, err
 }
 
-func (ns *linuxNs) SetupCgroups(container *libcontainer.Container, nspid int) (cgroups.ActiveCgroup, error) {
+// SetupCgroups applies the cgroup restrictions to the process running in the contaienr based
+// on the container's configuration
+func SetupCgroups(container *libcontainer.Container, nspid int) (cgroups.ActiveCgroup, error) {
 	if container.Cgroups != nil {
 		c := container.Cgroups
 		if systemd.UseSystemd() {
@@ -98,7 +107,9 @@ func (ns *linuxNs) SetupCgroups(container *libcontainer.Container, nspid int) (c
 	return nil, nil
 }
 
-func (ns *linuxNs) InitializeNetworking(container *libcontainer.Container, nspid int, pipe *SyncPipe) error {
+// InitializeNetworking creates the container's network stack outside of the namespace and moves
+// interfaces into the container's net namespaces if necessary
+func InitializeNetworking(container *libcontainer.Container, nspid int, pipe *SyncPipe) error {
 	context := libcontainer.Context{}
 	for _, config := range container.Networks {
 		strategy, err := network.GetStrategy(config.Type)
@@ -111,3 +122,23 @@ func (ns *linuxNs) InitializeNetworking(container *libcontainer.Container, nspid
 	}
 	return pipe.SendToChild(context)
 }
+
+// WritePid writes the namespaced processes pid to pid and it's start time
+// to the path specified
+func WritePid(path string, pid int, startTime string) error {
+	err := ioutil.WriteFile(filepath.Join(path, "pid"), []byte(fmt.Sprint(pid)), 0655)
+	if err != nil {
+		return err
+	}
+	return ioutil.WriteFile(filepath.Join(path, "start"), []byte(startTime), 0655)
+}
+
+// DeletePid removes the pid and started file from disk when the container's process
+// dies and the container is cleanly removed
+func DeletePid(path string) error {
+	err := os.Remove(filepath.Join(path, "pid"))
+	if serr := os.Remove(filepath.Join(path, "start")); err == nil {
+		err = serr
+	}
+	return err
+}

+ 2 - 4
pkg/libcontainer/nsinit/nsinit.go

@@ -5,7 +5,7 @@ import "github.com/dotcloud/docker/pkg/libcontainer"
 // NsInit is an interface with the public facing methods to provide high level
 // exec operations on a container
 type NsInit interface {
-	Exec(container *libcontainer.Container, term Terminal, args []string) (int, error)
+	Exec(container *libcontainer.Container, term Terminal, pidRoot string, args []string, startCallback func()) (int, error)
 	ExecIn(container *libcontainer.Container, nspid int, args []string) (int, error)
 	Init(container *libcontainer.Container, uncleanRootfs, console string, syncPipe *SyncPipe, args []string) error
 }
@@ -13,12 +13,10 @@ type NsInit interface {
 type linuxNs struct {
 	root           string
 	commandFactory CommandFactory
-	stateWriter    StateWriter
 }
 
-func NewNsInit(command CommandFactory, state StateWriter) NsInit {
+func NewNsInit(command CommandFactory) NsInit {
 	return &linuxNs{
 		commandFactory: command,
-		stateWriter:    state,
 	}
 }

+ 2 - 2
pkg/libcontainer/nsinit/nsinit/main.go

@@ -56,7 +56,7 @@ func main() {
 			exitCode, err = ns.ExecIn(container, nspid, flag.Args()[1:])
 		} else {
 			term := nsinit.NewTerminal(os.Stdin, os.Stdout, os.Stderr, container.Tty)
-			exitCode, err = ns.Exec(container, term, flag.Args()[1:])
+			exitCode, err = ns.Exec(container, term, root, flag.Args()[1:], nil)
 		}
 		if err != nil {
 			log.Fatalf("Failed to exec: %s", err)
@@ -109,5 +109,5 @@ func readPid() (int, error) {
 }
 
 func newNsInit() (nsinit.NsInit, error) {
-	return nsinit.NewNsInit(&nsinit.DefaultCommandFactory{root}, &nsinit.DefaultStateWriter{root}), nil
+	return nsinit.NewNsInit(&nsinit.DefaultCommandFactory{root}), nil
 }

+ 0 - 36
pkg/libcontainer/nsinit/state.go

@@ -1,36 +0,0 @@
-package nsinit
-
-import (
-	"fmt"
-	"io/ioutil"
-	"os"
-	"path/filepath"
-)
-
-// StateWriter handles writing and deleting the pid file
-// on disk
-type StateWriter interface {
-	WritePid(pid int, startTime string) error
-	DeletePid() error
-}
-
-type DefaultStateWriter struct {
-	Root string
-}
-
-// writePidFile writes the namespaced processes pid to pid in the rootfs for the container
-func (d *DefaultStateWriter) WritePid(pid int, startTime string) error {
-	err := ioutil.WriteFile(filepath.Join(d.Root, "pid"), []byte(fmt.Sprint(pid)), 0655)
-	if err != nil {
-		return err
-	}
-	return ioutil.WriteFile(filepath.Join(d.Root, "start"), []byte(startTime), 0655)
-}
-
-func (d *DefaultStateWriter) DeletePid() error {
-	err := os.Remove(filepath.Join(d.Root, "pid"))
-	if serr := os.Remove(filepath.Join(d.Root, "start")); err == nil {
-		err = serr
-	}
-	return err
-}

+ 1 - 1
pkg/libcontainer/nsinit/unsupported.go

@@ -6,7 +6,7 @@ import (
 	"github.com/dotcloud/docker/pkg/libcontainer"
 )
 
-func (ns *linuxNs) Exec(container *libcontainer.Container, term Terminal, args []string) (int, error) {
+func (ns *linuxNs) Exec(container *libcontainer.Container, term Terminal, pidRoot string, args []string, startCallback func()) (int, error) {
 	return -1, libcontainer.ErrUnsupported
 }