Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
| ... | ... |
@@ -25,6 +25,7 @@ import ( |
| 25 | 25 |
"github.com/docker/docker/daemon/network" |
| 26 | 26 |
"github.com/docker/docker/image" |
| 27 | 27 |
"github.com/docker/docker/layer" |
| 28 |
+ "github.com/docker/docker/libcontainerd" |
|
| 28 | 29 |
"github.com/docker/docker/pkg/idtools" |
| 29 | 30 |
"github.com/docker/docker/pkg/ioutils" |
| 30 | 31 |
"github.com/docker/docker/pkg/promise" |
| ... | ... |
@@ -1012,3 +1013,46 @@ func (container *Container) CancelAttachContext() {
|
| 1012 | 1012 |
} |
| 1013 | 1013 |
container.attachContext.mu.Unlock() |
| 1014 | 1014 |
} |
| 1015 |
+ |
|
| 1016 |
+func (container *Container) startLogging() error {
|
|
| 1017 |
+ if container.HostConfig.LogConfig.Type == "none" {
|
|
| 1018 |
+ return nil // do not start logging routines |
|
| 1019 |
+ } |
|
| 1020 |
+ |
|
| 1021 |
+ l, err := container.StartLogger(container.HostConfig.LogConfig) |
|
| 1022 |
+ if err != nil {
|
|
| 1023 |
+ return fmt.Errorf("Failed to initialize logging driver: %v", err)
|
|
| 1024 |
+ } |
|
| 1025 |
+ |
|
| 1026 |
+ copier := logger.NewCopier(map[string]io.Reader{"stdout": container.StdoutPipe(), "stderr": container.StderrPipe()}, l)
|
|
| 1027 |
+ container.LogCopier = copier |
|
| 1028 |
+ copier.Run() |
|
| 1029 |
+ container.LogDriver = l |
|
| 1030 |
+ |
|
| 1031 |
+ // set LogPath field only for json-file logdriver |
|
| 1032 |
+ if jl, ok := l.(*jsonfilelog.JSONFileLogger); ok {
|
|
| 1033 |
+ container.LogPath = jl.LogPath() |
|
| 1034 |
+ } |
|
| 1035 |
+ |
|
| 1036 |
+ return nil |
|
| 1037 |
+} |
|
| 1038 |
+ |
|
| 1039 |
+// InitializeStdio is called by libcontainerd to connect the stdio. |
|
| 1040 |
+func (container *Container) InitializeStdio(iop libcontainerd.IOPipe) error {
|
|
| 1041 |
+ if err := container.startLogging(); err != nil {
|
|
| 1042 |
+ container.Reset(false) |
|
| 1043 |
+ return err |
|
| 1044 |
+ } |
|
| 1045 |
+ |
|
| 1046 |
+ container.StreamConfig.CopyToPipe(iop) |
|
| 1047 |
+ |
|
| 1048 |
+ if container.Stdin() == nil && !container.Config.Tty {
|
|
| 1049 |
+ if iop.Stdin != nil {
|
|
| 1050 |
+ if err := iop.Stdin.Close(); err != nil {
|
|
| 1051 |
+ logrus.Error("error closing stdin: %+v", err)
|
|
| 1052 |
+ } |
|
| 1053 |
+ } |
|
| 1054 |
+ } |
|
| 1055 |
+ |
|
| 1056 |
+ return nil |
|
| 1057 |
+} |
| ... | ... |
@@ -185,7 +185,7 @@ func (daemon *Daemon) restore() error {
|
| 185 | 185 |
|
| 186 | 186 |
if c.IsRunning() || c.IsPaused() {
|
| 187 | 187 |
c.RestartManager().Cancel() // manually start containers because some need to wait for swarm networking |
| 188 |
- if err := daemon.containerd.Restore(c.ID); err != nil {
|
|
| 188 |
+ if err := daemon.containerd.Restore(c.ID, c.InitializeStdio); err != nil {
|
|
| 189 | 189 |
logrus.Errorf("Failed to restore %s with containerd: %s", c.ID, err)
|
| 190 | 190 |
return |
| 191 | 191 |
} |
| ... | ... |
@@ -212,7 +212,7 @@ func (d *Daemon) ContainerExecStart(ctx context.Context, name string, stdin io.R |
| 212 | 212 |
|
| 213 | 213 |
attachErr := container.AttachStreams(ctx, ec.StreamConfig, ec.OpenStdin, true, ec.Tty, cStdin, cStdout, cStderr, ec.DetachKeys) |
| 214 | 214 |
|
| 215 |
- systemPid, err := d.containerd.AddProcess(ctx, c.ID, name, p) |
|
| 215 |
+ systemPid, err := d.containerd.AddProcess(ctx, c.ID, name, p, ec.InitializeStdio) |
|
| 216 | 216 |
if err != nil {
|
| 217 | 217 |
return err |
| 218 | 218 |
} |
| ... | ... |
@@ -1,8 +1,11 @@ |
| 1 | 1 |
package exec |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "runtime" |
|
| 4 | 5 |
"sync" |
| 5 | 6 |
|
| 7 |
+ "github.com/Sirupsen/logrus" |
|
| 8 |
+ "github.com/docker/docker/libcontainerd" |
|
| 6 | 9 |
"github.com/docker/docker/pkg/stringid" |
| 7 | 10 |
"github.com/docker/docker/runconfig" |
| 8 | 11 |
) |
| ... | ... |
@@ -39,6 +42,21 @@ func NewConfig() *Config {
|
| 39 | 39 |
} |
| 40 | 40 |
} |
| 41 | 41 |
|
| 42 |
+// InitializeStdio is called by libcontainerd to connect the stdio. |
|
| 43 |
+func (c *Config) InitializeStdio(iop libcontainerd.IOPipe) error {
|
|
| 44 |
+ c.StreamConfig.CopyToPipe(iop) |
|
| 45 |
+ |
|
| 46 |
+ if c.Stdin() == nil && !c.Tty && runtime.GOOS == "windows" {
|
|
| 47 |
+ if iop.Stdin != nil {
|
|
| 48 |
+ if err := iop.Stdin.Close(); err != nil {
|
|
| 49 |
+ logrus.Error("error closing exec stdin: %+v", err)
|
|
| 50 |
+ } |
|
| 51 |
+ } |
|
| 52 |
+ } |
|
| 53 |
+ |
|
| 54 |
+ return nil |
|
| 55 |
+} |
|
| 56 |
+ |
|
| 42 | 57 |
// Store keeps track of the exec configurations. |
| 43 | 58 |
type Store struct {
|
| 44 | 59 |
commands map[string]*Config |
| ... | ... |
@@ -14,7 +14,6 @@ import ( |
| 14 | 14 |
timetypes "github.com/docker/docker/api/types/time" |
| 15 | 15 |
"github.com/docker/docker/container" |
| 16 | 16 |
"github.com/docker/docker/daemon/logger" |
| 17 |
- "github.com/docker/docker/daemon/logger/jsonfilelog" |
|
| 18 | 17 |
"github.com/docker/docker/pkg/ioutils" |
| 19 | 18 |
"github.com/docker/docker/pkg/stdcopy" |
| 20 | 19 |
) |
| ... | ... |
@@ -121,30 +120,6 @@ func (daemon *Daemon) getLogger(container *container.Container) (logger.Logger, |
| 121 | 121 |
return container.StartLogger(container.HostConfig.LogConfig) |
| 122 | 122 |
} |
| 123 | 123 |
|
| 124 |
-// StartLogging initializes and starts the container logging stream. |
|
| 125 |
-func (daemon *Daemon) StartLogging(container *container.Container) error {
|
|
| 126 |
- if container.HostConfig.LogConfig.Type == "none" {
|
|
| 127 |
- return nil // do not start logging routines |
|
| 128 |
- } |
|
| 129 |
- |
|
| 130 |
- l, err := container.StartLogger(container.HostConfig.LogConfig) |
|
| 131 |
- if err != nil {
|
|
| 132 |
- return fmt.Errorf("Failed to initialize logging driver: %v", err)
|
|
| 133 |
- } |
|
| 134 |
- |
|
| 135 |
- copier := logger.NewCopier(map[string]io.Reader{"stdout": container.StdoutPipe(), "stderr": container.StderrPipe()}, l)
|
|
| 136 |
- container.LogCopier = copier |
|
| 137 |
- copier.Run() |
|
| 138 |
- container.LogDriver = l |
|
| 139 |
- |
|
| 140 |
- // set LogPath field only for json-file logdriver |
|
| 141 |
- if jl, ok := l.(*jsonfilelog.JSONFileLogger); ok {
|
|
| 142 |
- container.LogPath = jl.LogPath() |
|
| 143 |
- } |
|
| 144 |
- |
|
| 145 |
- return nil |
|
| 146 |
-} |
|
| 147 |
- |
|
| 148 | 124 |
// mergeLogConfig merges the daemon log config to the container's log config if the container's log driver is not specified. |
| 149 | 125 |
func (daemon *Daemon) mergeAndVerifyLogConfig(cfg *containertypes.LogConfig) error {
|
| 150 | 126 |
if cfg.Type == "" {
|
| ... | ... |
@@ -3,17 +3,14 @@ package daemon |
| 3 | 3 |
import ( |
| 4 | 4 |
"errors" |
| 5 | 5 |
"fmt" |
| 6 |
- "io" |
|
| 7 | 6 |
"runtime" |
| 8 | 7 |
"strconv" |
| 9 | 8 |
"time" |
| 10 | 9 |
|
| 11 | 10 |
"github.com/Sirupsen/logrus" |
| 12 | 11 |
"github.com/docker/docker/api/types" |
| 13 |
- "github.com/docker/docker/daemon/exec" |
|
| 14 | 12 |
"github.com/docker/docker/libcontainerd" |
| 15 | 13 |
"github.com/docker/docker/restartmanager" |
| 16 |
- "github.com/docker/docker/runconfig" |
|
| 17 | 14 |
) |
| 18 | 15 |
|
| 19 | 16 |
// StateChanged updates daemon state changes from containerd |
| ... | ... |
@@ -133,69 +130,3 @@ func (daemon *Daemon) StateChanged(id string, e libcontainerd.StateInfo) error {
|
| 133 | 133 |
|
| 134 | 134 |
return nil |
| 135 | 135 |
} |
| 136 |
- |
|
| 137 |
-// AttachStreams is called by libcontainerd to connect the stdio. |
|
| 138 |
-func (daemon *Daemon) AttachStreams(id string, iop libcontainerd.IOPipe) error {
|
|
| 139 |
- var ( |
|
| 140 |
- s *runconfig.StreamConfig |
|
| 141 |
- ec *exec.Config |
|
| 142 |
- ) |
|
| 143 |
- |
|
| 144 |
- c := daemon.containers.Get(id) |
|
| 145 |
- if c == nil {
|
|
| 146 |
- var err error |
|
| 147 |
- ec, err = daemon.getExecConfig(id) |
|
| 148 |
- if err != nil {
|
|
| 149 |
- return fmt.Errorf("no such exec/container: %s", id)
|
|
| 150 |
- } |
|
| 151 |
- s = ec.StreamConfig |
|
| 152 |
- } else {
|
|
| 153 |
- s = c.StreamConfig |
|
| 154 |
- if err := daemon.StartLogging(c); err != nil {
|
|
| 155 |
- c.Reset(false) |
|
| 156 |
- return err |
|
| 157 |
- } |
|
| 158 |
- } |
|
| 159 |
- |
|
| 160 |
- copyFunc := func(w io.Writer, r io.Reader) {
|
|
| 161 |
- s.Add(1) |
|
| 162 |
- go func() {
|
|
| 163 |
- if _, err := io.Copy(w, r); err != nil {
|
|
| 164 |
- logrus.Errorf("%v stream copy error: %v", id, err)
|
|
| 165 |
- } |
|
| 166 |
- s.Done() |
|
| 167 |
- }() |
|
| 168 |
- } |
|
| 169 |
- |
|
| 170 |
- if iop.Stdout != nil {
|
|
| 171 |
- copyFunc(s.Stdout(), iop.Stdout) |
|
| 172 |
- } |
|
| 173 |
- if iop.Stderr != nil {
|
|
| 174 |
- copyFunc(s.Stderr(), iop.Stderr) |
|
| 175 |
- } |
|
| 176 |
- |
|
| 177 |
- if stdin := s.Stdin(); stdin != nil {
|
|
| 178 |
- if iop.Stdin != nil {
|
|
| 179 |
- go func() {
|
|
| 180 |
- io.Copy(iop.Stdin, stdin) |
|
| 181 |
- if err := iop.Stdin.Close(); err != nil {
|
|
| 182 |
- logrus.Error(err) |
|
| 183 |
- } |
|
| 184 |
- }() |
|
| 185 |
- } |
|
| 186 |
- } else {
|
|
| 187 |
- //TODO(swernli): On Windows, not closing stdin when no tty is requested by the exec Config |
|
| 188 |
- // results in a hang. We should re-evaluate generalizing this fix for all OSes if |
|
| 189 |
- // we can determine that is the right thing to do more generally. |
|
| 190 |
- if (c != nil && !c.Config.Tty) || (ec != nil && !ec.Tty && runtime.GOOS == "windows") {
|
|
| 191 |
- // tty is enabled, so dont close containerd's iopipe stdin. |
|
| 192 |
- if iop.Stdin != nil {
|
|
| 193 |
- if err := iop.Stdin.Close(); err != nil {
|
|
| 194 |
- logrus.Error(err) |
|
| 195 |
- } |
|
| 196 |
- } |
|
| 197 |
- } |
|
| 198 |
- } |
|
| 199 |
- |
|
| 200 |
- return nil |
|
| 201 |
-} |
| ... | ... |
@@ -37,7 +37,7 @@ func (daemon *Daemon) postRunProcessing(container *container.Container, e libcon |
| 37 | 37 |
|
| 38 | 38 |
// Create a new servicing container, which will start, complete the update, and merge back the |
| 39 | 39 |
// results if it succeeded, all as part of the below function call. |
| 40 |
- if err := daemon.containerd.Create((container.ID + "_servicing"), "", "", *spec, newOpts...); err != nil {
|
|
| 40 |
+ if err := daemon.containerd.Create((container.ID + "_servicing"), "", "", *spec, container.InitializeStdio, newOpts...); err != nil {
|
|
| 41 | 41 |
container.SetExitCode(-1) |
| 42 | 42 |
return fmt.Errorf("Post-run update servicing failed: %s", err)
|
| 43 | 43 |
} |
| ... | ... |
@@ -149,7 +149,7 @@ func (daemon *Daemon) containerStart(container *container.Container, checkpoint |
| 149 | 149 |
container.ResetRestartManager(true) |
| 150 | 150 |
} |
| 151 | 151 |
|
| 152 |
- if err := daemon.containerd.Create(container.ID, checkpoint, container.CheckpointDir(), *spec, createOptions...); err != nil {
|
|
| 152 |
+ if err := daemon.containerd.Create(container.ID, checkpoint, container.CheckpointDir(), *spec, container.InitializeStdio, createOptions...); err != nil {
|
|
| 153 | 153 |
errDesc := grpc.ErrorDesc(err) |
| 154 | 154 |
logrus.Errorf("Create container failed with error: %s", errDesc)
|
| 155 | 155 |
// if we receive an internal error from the initial start of a container then lets |
| ... | ... |
@@ -34,7 +34,7 @@ type client struct {
|
| 34 | 34 |
// AddProcess is the handler for adding a process to an already running |
| 35 | 35 |
// container. It's called through docker exec. It returns the system pid of the |
| 36 | 36 |
// exec'd process. |
| 37 |
-func (clnt *client) AddProcess(ctx context.Context, containerID, processFriendlyName string, specp Process) (int, error) {
|
|
| 37 |
+func (clnt *client) AddProcess(ctx context.Context, containerID, processFriendlyName string, specp Process, attachStdio StdioCallback) (int, error) {
|
|
| 38 | 38 |
clnt.lock(containerID) |
| 39 | 39 |
defer clnt.unlock(containerID) |
| 40 | 40 |
container, err := clnt.getContainer(containerID) |
| ... | ... |
@@ -116,14 +116,10 @@ func (clnt *client) AddProcess(ctx context.Context, containerID, processFriendly |
| 116 | 116 |
|
| 117 | 117 |
container.processes[processFriendlyName] = p |
| 118 | 118 |
|
| 119 |
- clnt.unlock(containerID) |
|
| 120 |
- |
|
| 121 |
- if err := clnt.backend.AttachStreams(processFriendlyName, *iopipe); err != nil {
|
|
| 122 |
- clnt.lock(containerID) |
|
| 119 |
+ if err := attachStdio(*iopipe); err != nil {
|
|
| 123 | 120 |
p.closeFifos(iopipe) |
| 124 | 121 |
return -1, err |
| 125 | 122 |
} |
| 126 |
- clnt.lock(containerID) |
|
| 127 | 123 |
|
| 128 | 124 |
return int(resp.SystemPid), nil |
| 129 | 125 |
} |
| ... | ... |
@@ -153,7 +149,7 @@ func (clnt *client) prepareBundleDir(uid, gid int) (string, error) {
|
| 153 | 153 |
return p, nil |
| 154 | 154 |
} |
| 155 | 155 |
|
| 156 |
-func (clnt *client) Create(containerID string, checkpoint string, checkpointDir string, spec specs.Spec, options ...CreateOption) (err error) {
|
|
| 156 |
+func (clnt *client) Create(containerID string, checkpoint string, checkpointDir string, spec specs.Spec, attachStdio StdioCallback, options ...CreateOption) (err error) {
|
|
| 157 | 157 |
clnt.lock(containerID) |
| 158 | 158 |
defer clnt.unlock(containerID) |
| 159 | 159 |
|
| ... | ... |
@@ -195,7 +191,7 @@ func (clnt *client) Create(containerID string, checkpoint string, checkpointDir |
| 195 | 195 |
return err |
| 196 | 196 |
} |
| 197 | 197 |
|
| 198 |
- return container.start(checkpoint, checkpointDir) |
|
| 198 |
+ return container.start(checkpoint, checkpointDir, attachStdio) |
|
| 199 | 199 |
} |
| 200 | 200 |
|
| 201 | 201 |
func (clnt *client) Signal(containerID string, sig int) error {
|
| ... | ... |
@@ -404,7 +400,7 @@ func (clnt *client) getOrCreateExitNotifier(containerID string) *exitNotifier {
|
| 404 | 404 |
return w |
| 405 | 405 |
} |
| 406 | 406 |
|
| 407 |
-func (clnt *client) restore(cont *containerd.Container, lastEvent *containerd.Event, options ...CreateOption) (err error) {
|
|
| 407 |
+func (clnt *client) restore(cont *containerd.Container, lastEvent *containerd.Event, attachStdio StdioCallback, options ...CreateOption) (err error) {
|
|
| 408 | 408 |
clnt.lock(cont.Id) |
| 409 | 409 |
defer clnt.unlock(cont.Id) |
| 410 | 410 |
|
| ... | ... |
@@ -445,7 +441,7 @@ func (clnt *client) restore(cont *containerd.Container, lastEvent *containerd.Ev |
| 445 | 445 |
return err |
| 446 | 446 |
}) |
| 447 | 447 |
|
| 448 |
- if err := clnt.backend.AttachStreams(containerID, *iopipe); err != nil {
|
|
| 448 |
+ if err := attachStdio(*iopipe); err != nil {
|
|
| 449 | 449 |
container.closeFifos(iopipe) |
| 450 | 450 |
return err |
| 451 | 451 |
} |
| ... | ... |
@@ -537,7 +533,7 @@ func (clnt *client) getContainerLastEvent(id string) (*containerd.Event, error) |
| 537 | 537 |
return ev, err |
| 538 | 538 |
} |
| 539 | 539 |
|
| 540 |
-func (clnt *client) Restore(containerID string, options ...CreateOption) error {
|
|
| 540 |
+func (clnt *client) Restore(containerID string, attachStdio StdioCallback, options ...CreateOption) error {
|
|
| 541 | 541 |
// Synchronize with live events |
| 542 | 542 |
clnt.remote.Lock() |
| 543 | 543 |
defer clnt.remote.Unlock() |
| ... | ... |
@@ -585,7 +581,7 @@ func (clnt *client) Restore(containerID string, options ...CreateOption) error {
|
| 585 | 585 |
|
| 586 | 586 |
// container is still alive |
| 587 | 587 |
if clnt.liveRestore {
|
| 588 |
- if err := clnt.restore(cont, ev, options...); err != nil {
|
|
| 588 |
+ if err := clnt.restore(cont, ev, attachStdio, options...); err != nil {
|
|
| 589 | 589 |
logrus.Errorf("libcontainerd: error restoring %s: %v", containerID, err)
|
| 590 | 590 |
} |
| 591 | 591 |
return nil |
| ... | ... |
@@ -94,7 +94,7 @@ const defaultOwner = "docker" |
| 94 | 94 |
// }, |
| 95 | 95 |
// "Servicing": false |
| 96 | 96 |
//} |
| 97 |
-func (clnt *client) Create(containerID string, checkpoint string, checkpointDir string, spec specs.Spec, options ...CreateOption) error {
|
|
| 97 |
+func (clnt *client) Create(containerID string, checkpoint string, checkpointDir string, spec specs.Spec, attachStdio StdioCallback, options ...CreateOption) error {
|
|
| 98 | 98 |
clnt.lock(containerID) |
| 99 | 99 |
defer clnt.unlock(containerID) |
| 100 | 100 |
logrus.Debugln("libcontainerd: client.Create() with spec", spec)
|
| ... | ... |
@@ -253,7 +253,7 @@ func (clnt *client) Create(containerID string, checkpoint string, checkpointDir |
| 253 | 253 |
// internal structure, start will keep HCS in sync by deleting the |
| 254 | 254 |
// container there. |
| 255 | 255 |
logrus.Debugf("libcontainerd: Create() id=%s, Calling start()", containerID)
|
| 256 |
- if err := container.start(); err != nil {
|
|
| 256 |
+ if err := container.start(attachStdio); err != nil {
|
|
| 257 | 257 |
clnt.deleteContainer(containerID) |
| 258 | 258 |
return err |
| 259 | 259 |
} |
| ... | ... |
@@ -266,7 +266,7 @@ func (clnt *client) Create(containerID string, checkpoint string, checkpointDir |
| 266 | 266 |
// AddProcess is the handler for adding a process to an already running |
| 267 | 267 |
// container. It's called through docker exec. It returns the system pid of the |
| 268 | 268 |
// exec'd process. |
| 269 |
-func (clnt *client) AddProcess(ctx context.Context, containerID, processFriendlyName string, procToAdd Process) (int, error) {
|
|
| 269 |
+func (clnt *client) AddProcess(ctx context.Context, containerID, processFriendlyName string, procToAdd Process, attachStdio StdioCallback) (int, error) {
|
|
| 270 | 270 |
clnt.lock(containerID) |
| 271 | 271 |
defer clnt.unlock(containerID) |
| 272 | 272 |
container, err := clnt.getContainer(containerID) |
| ... | ... |
@@ -343,18 +343,11 @@ func (clnt *client) AddProcess(ctx context.Context, containerID, processFriendly |
| 343 | 343 |
// Add the process to the container's list of processes |
| 344 | 344 |
container.processes[processFriendlyName] = proc |
| 345 | 345 |
|
| 346 |
- // Make sure the lock is not held while calling back into the daemon |
|
| 347 |
- clnt.unlock(containerID) |
|
| 348 |
- |
|
| 349 | 346 |
// Tell the engine to attach streams back to the client |
| 350 |
- if err := clnt.backend.AttachStreams(processFriendlyName, *iopipe); err != nil {
|
|
| 351 |
- clnt.lock(containerID) |
|
| 347 |
+ if err := attachStdio(*iopipe); err != nil {
|
|
| 352 | 348 |
return -1, err |
| 353 | 349 |
} |
| 354 | 350 |
|
| 355 |
- // Lock again so that the defer unlock doesn't fail. (I really don't like this code) |
|
| 356 |
- clnt.lock(containerID) |
|
| 357 |
- |
|
| 358 | 351 |
// Spin up a go routine waiting for exit to handle cleanup |
| 359 | 352 |
go container.waitExit(proc, false) |
| 360 | 353 |
|
| ... | ... |
@@ -544,7 +537,7 @@ func (clnt *client) Stats(containerID string) (*Stats, error) {
|
| 544 | 544 |
} |
| 545 | 545 |
|
| 546 | 546 |
// Restore is the handler for restoring a container |
| 547 |
-func (clnt *client) Restore(containerID string, unusedOnWindows ...CreateOption) error {
|
|
| 547 |
+func (clnt *client) Restore(containerID string, _ StdioCallback, unusedOnWindows ...CreateOption) error {
|
|
| 548 | 548 |
// TODO Windows: Implement this. For now, just tell the backend the container exited. |
| 549 | 549 |
logrus.Debugf("libcontainerd: Restore(%s)", containerID)
|
| 550 | 550 |
return clnt.backend.StateChanged(containerID, StateInfo{
|
| ... | ... |
@@ -88,7 +88,7 @@ func (ctr *container) spec() (*specs.Spec, error) {
|
| 88 | 88 |
return &spec, nil |
| 89 | 89 |
} |
| 90 | 90 |
|
| 91 |
-func (ctr *container) start(checkpoint string, checkpointDir string) error {
|
|
| 91 |
+func (ctr *container) start(checkpoint string, checkpointDir string, attachStdio StdioCallback) error {
|
|
| 92 | 92 |
spec, err := ctr.spec() |
| 93 | 93 |
if err != nil {
|
| 94 | 94 |
return nil |
| ... | ... |
@@ -107,7 +107,7 @@ func (ctr *container) start(checkpoint string, checkpointDir string) error {
|
| 107 | 107 |
|
| 108 | 108 |
// we need to delay stdin closure after container start or else "stdin close" |
| 109 | 109 |
// event will be rejected by containerd. |
| 110 |
- // stdin closure happens in AttachStreams |
|
| 110 |
+ // stdin closure happens in attachStdio |
|
| 111 | 111 |
stdin := iopipe.Stdin |
| 112 | 112 |
iopipe.Stdin = ioutils.NewWriteCloserWrapper(stdin, func() error {
|
| 113 | 113 |
var err error |
| ... | ... |
@@ -141,7 +141,7 @@ func (ctr *container) start(checkpoint string, checkpointDir string) error {
|
| 141 | 141 |
} |
| 142 | 142 |
ctr.client.appendContainer(ctr) |
| 143 | 143 |
|
| 144 |
- if err := ctr.client.backend.AttachStreams(ctr.containerID, *iopipe); err != nil {
|
|
| 144 |
+ if err := attachStdio(*iopipe); err != nil {
|
|
| 145 | 145 |
ctr.closeFifos(iopipe) |
| 146 | 146 |
return err |
| 147 | 147 |
} |
| ... | ... |
@@ -40,7 +40,7 @@ func (ctr *container) newProcess(friendlyName string) *process {
|
| 40 | 40 |
|
| 41 | 41 |
// start starts a created container. |
| 42 | 42 |
// Caller needs to lock container ID before calling this method. |
| 43 |
-func (ctr *container) start() error {
|
|
| 43 |
+func (ctr *container) start(attachStdio StdioCallback) error {
|
|
| 44 | 44 |
var err error |
| 45 | 45 |
isServicing := false |
| 46 | 46 |
|
| ... | ... |
@@ -147,7 +147,7 @@ func (ctr *container) start() error {
|
| 147 | 147 |
|
| 148 | 148 |
ctr.client.appendContainer(ctr) |
| 149 | 149 |
|
| 150 |
- if err := ctr.client.backend.AttachStreams(ctr.containerID, *iopipe); err != nil {
|
|
| 150 |
+ if err := attachStdio(*iopipe); err != nil {
|
|
| 151 | 151 |
// OK to return the error here, as waitExit will handle tear-down in HCS |
| 152 | 152 |
return err |
| 153 | 153 |
} |
| ... | ... |
@@ -31,19 +31,18 @@ type CommonStateInfo struct { // FIXME: event?
|
| 31 | 31 |
// Backend defines callbacks that the client of the library needs to implement. |
| 32 | 32 |
type Backend interface {
|
| 33 | 33 |
StateChanged(containerID string, state StateInfo) error |
| 34 |
- AttachStreams(processFriendlyName string, io IOPipe) error |
|
| 35 | 34 |
} |
| 36 | 35 |
|
| 37 | 36 |
// Client provides access to containerd features. |
| 38 | 37 |
type Client interface {
|
| 39 |
- Create(containerID string, checkpoint string, checkpointDir string, spec specs.Spec, options ...CreateOption) error |
|
| 38 |
+ Create(containerID string, checkpoint string, checkpointDir string, spec specs.Spec, attachStdio StdioCallback, options ...CreateOption) error |
|
| 40 | 39 |
Signal(containerID string, sig int) error |
| 41 | 40 |
SignalProcess(containerID string, processFriendlyName string, sig int) error |
| 42 |
- AddProcess(ctx context.Context, containerID, processFriendlyName string, process Process) (int, error) |
|
| 41 |
+ AddProcess(ctx context.Context, containerID, processFriendlyName string, process Process, attachStdio StdioCallback) (int, error) |
|
| 43 | 42 |
Resize(containerID, processFriendlyName string, width, height int) error |
| 44 | 43 |
Pause(containerID string) error |
| 45 | 44 |
Resume(containerID string) error |
| 46 |
- Restore(containerID string, options ...CreateOption) error |
|
| 45 |
+ Restore(containerID string, attachStdio StdioCallback, options ...CreateOption) error |
|
| 47 | 46 |
Stats(containerID string) (*Stats, error) |
| 48 | 47 |
GetPidsForContainer(containerID string) ([]int, error) |
| 49 | 48 |
Summary(containerID string) ([]Summary, error) |
| ... | ... |
@@ -58,6 +57,9 @@ type CreateOption interface {
|
| 58 | 58 |
Apply(interface{}) error
|
| 59 | 59 |
} |
| 60 | 60 |
|
| 61 |
+// StdioCallback is called to connect a container or process stdio. |
|
| 62 |
+type StdioCallback func(IOPipe) error |
|
| 63 |
+ |
|
| 61 | 64 |
// IOPipe contains the stdio streams. |
| 62 | 65 |
type IOPipe struct {
|
| 63 | 66 |
Stdin io.WriteCloser |
| ... | ... |
@@ -100,24 +100,6 @@ func (pm *Manager) StateChanged(id string, e libcontainerd.StateInfo) error {
|
| 100 | 100 |
return nil |
| 101 | 101 |
} |
| 102 | 102 |
|
| 103 |
-// AttachStreams attaches io streams to the plugin |
|
| 104 |
-func (pm *Manager) AttachStreams(id string, iop libcontainerd.IOPipe) error {
|
|
| 105 |
- iop.Stdin.Close() |
|
| 106 |
- |
|
| 107 |
- logger := logrus.New() |
|
| 108 |
- logger.Hooks.Add(logHook{id})
|
|
| 109 |
- // TODO: cache writer per id |
|
| 110 |
- w := logger.Writer() |
|
| 111 |
- go func() {
|
|
| 112 |
- io.Copy(w, iop.Stdout) |
|
| 113 |
- }() |
|
| 114 |
- go func() {
|
|
| 115 |
- // TODO: update logrus and use logger.WriterLevel |
|
| 116 |
- io.Copy(w, iop.Stderr) |
|
| 117 |
- }() |
|
| 118 |
- return nil |
|
| 119 |
-} |
|
| 120 |
- |
|
| 121 | 103 |
func (pm *Manager) init() error {
|
| 122 | 104 |
dt, err := os.Open(filepath.Join(pm.libRoot, "plugins.json")) |
| 123 | 105 |
if err != nil {
|
| ... | ... |
@@ -169,3 +151,22 @@ func (l logHook) Fire(entry *logrus.Entry) error {
|
| 169 | 169 |
entry.Data = logrus.Fields{"plugin": l.id}
|
| 170 | 170 |
return nil |
| 171 | 171 |
} |
| 172 |
+ |
|
| 173 |
+func attachToLog(id string) func(libcontainerd.IOPipe) error {
|
|
| 174 |
+ return func(iop libcontainerd.IOPipe) error {
|
|
| 175 |
+ iop.Stdin.Close() |
|
| 176 |
+ |
|
| 177 |
+ logger := logrus.New() |
|
| 178 |
+ logger.Hooks.Add(logHook{id})
|
|
| 179 |
+ // TODO: cache writer per id |
|
| 180 |
+ w := logger.Writer() |
|
| 181 |
+ go func() {
|
|
| 182 |
+ io.Copy(w, iop.Stdout) |
|
| 183 |
+ }() |
|
| 184 |
+ go func() {
|
|
| 185 |
+ // TODO: update logrus and use logger.WriterLevel |
|
| 186 |
+ io.Copy(w, iop.Stderr) |
|
| 187 |
+ }() |
|
| 188 |
+ return nil |
|
| 189 |
+ } |
|
| 190 |
+} |
| ... | ... |
@@ -26,7 +26,7 @@ func (pm *Manager) enable(p *v2.Plugin, force bool) error {
|
| 26 | 26 |
p.Lock() |
| 27 | 27 |
p.Restart = true |
| 28 | 28 |
p.Unlock() |
| 29 |
- if err := pm.containerdClient.Create(p.GetID(), "", "", specs.Spec(*spec)); err != nil {
|
|
| 29 |
+ if err := pm.containerdClient.Create(p.GetID(), "", "", specs.Spec(*spec), attachToLog(p.GetID())); err != nil {
|
|
| 30 | 30 |
return err |
| 31 | 31 |
} |
| 32 | 32 |
|
| ... | ... |
@@ -45,7 +45,7 @@ func (pm *Manager) enable(p *v2.Plugin, force bool) error {
|
| 45 | 45 |
} |
| 46 | 46 |
|
| 47 | 47 |
func (pm *Manager) restore(p *v2.Plugin) error {
|
| 48 |
- return pm.containerdClient.Restore(p.GetID()) |
|
| 48 |
+ return pm.containerdClient.Restore(p.GetID(), attachToLog(p.GetID())) |
|
| 49 | 49 |
} |
| 50 | 50 |
|
| 51 | 51 |
func (pm *Manager) disable(p *v2.Plugin) error {
|
| ... | ... |
@@ -7,8 +7,11 @@ import ( |
| 7 | 7 |
"strings" |
| 8 | 8 |
"sync" |
| 9 | 9 |
|
| 10 |
+ "github.com/Sirupsen/logrus" |
|
| 11 |
+ "github.com/docker/docker/libcontainerd" |
|
| 10 | 12 |
"github.com/docker/docker/pkg/broadcaster" |
| 11 | 13 |
"github.com/docker/docker/pkg/ioutils" |
| 14 |
+ "github.com/docker/docker/pkg/pools" |
|
| 12 | 15 |
) |
| 13 | 16 |
|
| 14 | 17 |
// StreamConfig holds information about I/O streams managed together. |
| ... | ... |
@@ -107,3 +110,34 @@ func (streamConfig *StreamConfig) CloseStreams() error {
|
| 107 | 107 |
|
| 108 | 108 |
return nil |
| 109 | 109 |
} |
| 110 |
+ |
|
| 111 |
+// CopyToPipe connects streamconfig with a libcontainerd.IOPipe |
|
| 112 |
+func (streamConfig *StreamConfig) CopyToPipe(iop libcontainerd.IOPipe) {
|
|
| 113 |
+ copyFunc := func(w io.Writer, r io.Reader) {
|
|
| 114 |
+ streamConfig.Add(1) |
|
| 115 |
+ go func() {
|
|
| 116 |
+ if _, err := pools.Copy(w, r); err != nil {
|
|
| 117 |
+ logrus.Errorf("stream copy error: %+v", err)
|
|
| 118 |
+ } |
|
| 119 |
+ streamConfig.Done() |
|
| 120 |
+ }() |
|
| 121 |
+ } |
|
| 122 |
+ |
|
| 123 |
+ if iop.Stdout != nil {
|
|
| 124 |
+ copyFunc(streamConfig.Stdout(), iop.Stdout) |
|
| 125 |
+ } |
|
| 126 |
+ if iop.Stderr != nil {
|
|
| 127 |
+ copyFunc(streamConfig.Stderr(), iop.Stderr) |
|
| 128 |
+ } |
|
| 129 |
+ |
|
| 130 |
+ if stdin := streamConfig.Stdin(); stdin != nil {
|
|
| 131 |
+ if iop.Stdin != nil {
|
|
| 132 |
+ go func() {
|
|
| 133 |
+ pools.Copy(iop.Stdin, stdin) |
|
| 134 |
+ if err := iop.Stdin.Close(); err != nil {
|
|
| 135 |
+ logrus.Error("failed to clise stdin: %+v", err)
|
|
| 136 |
+ } |
|
| 137 |
+ }() |
|
| 138 |
+ } |
|
| 139 |
+ } |
|
| 140 |
+} |