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