mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Move exec store to its own package inside the daemon.
Remove double reference between containers and exec configurations by keeping only the container id. Signed-off-by: David Calavera <david.calavera@gmail.com>
This commit is contained in:
parent
b5c507750f
commit
9ca2e4e81c
7 changed files with 174 additions and 135 deletions
|
@ -14,6 +14,7 @@ import (
|
||||||
"github.com/opencontainers/runc/libcontainer/label"
|
"github.com/opencontainers/runc/libcontainer/label"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/docker/docker/daemon/exec"
|
||||||
"github.com/docker/docker/daemon/execdriver"
|
"github.com/docker/docker/daemon/execdriver"
|
||||||
"github.com/docker/docker/daemon/logger"
|
"github.com/docker/docker/daemon/logger"
|
||||||
"github.com/docker/docker/daemon/logger/jsonfilelog"
|
"github.com/docker/docker/daemon/logger/jsonfilelog"
|
||||||
|
@ -62,7 +63,7 @@ type CommonContainer struct {
|
||||||
hostConfig *runconfig.HostConfig
|
hostConfig *runconfig.HostConfig
|
||||||
command *execdriver.Command
|
command *execdriver.Command
|
||||||
monitor *containerMonitor
|
monitor *containerMonitor
|
||||||
execCommands *execStore
|
execCommands *exec.Store
|
||||||
// logDriver for closing
|
// logDriver for closing
|
||||||
logDriver logger.Logger
|
logDriver logger.Logger
|
||||||
logCopier *logger.Copier
|
logCopier *logger.Copier
|
||||||
|
@ -75,7 +76,7 @@ func newBaseContainer(id, root string) *Container {
|
||||||
CommonContainer: CommonContainer{
|
CommonContainer: CommonContainer{
|
||||||
ID: id,
|
ID: id,
|
||||||
State: NewState(),
|
State: NewState(),
|
||||||
execCommands: newExecStore(),
|
execCommands: exec.NewStore(),
|
||||||
root: root,
|
root: root,
|
||||||
MountPoints: make(map[string]*volume.MountPoint),
|
MountPoints: make(map[string]*volume.MountPoint),
|
||||||
StreamConfig: runconfig.NewStreamConfig(),
|
StreamConfig: runconfig.NewStreamConfig(),
|
||||||
|
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/cliconfig"
|
"github.com/docker/docker/cliconfig"
|
||||||
"github.com/docker/docker/daemon/events"
|
"github.com/docker/docker/daemon/events"
|
||||||
|
"github.com/docker/docker/daemon/exec"
|
||||||
"github.com/docker/docker/daemon/execdriver"
|
"github.com/docker/docker/daemon/execdriver"
|
||||||
"github.com/docker/docker/daemon/execdriver/execdrivers"
|
"github.com/docker/docker/daemon/execdriver/execdrivers"
|
||||||
"github.com/docker/docker/daemon/graphdriver"
|
"github.com/docker/docker/daemon/graphdriver"
|
||||||
|
@ -106,7 +107,7 @@ type Daemon struct {
|
||||||
repository string
|
repository string
|
||||||
sysInitPath string
|
sysInitPath string
|
||||||
containers *contStore
|
containers *contStore
|
||||||
execCommands *execStore
|
execCommands *exec.Store
|
||||||
graph *graph.Graph
|
graph *graph.Graph
|
||||||
repositories *graph.TagStore
|
repositories *graph.TagStore
|
||||||
idIndex *truncindex.TruncIndex
|
idIndex *truncindex.TruncIndex
|
||||||
|
@ -790,7 +791,7 @@ func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemo
|
||||||
d.ID = trustKey.PublicKey().KeyID()
|
d.ID = trustKey.PublicKey().KeyID()
|
||||||
d.repository = daemonRepo
|
d.repository = daemonRepo
|
||||||
d.containers = &contStore{s: make(map[string]*Container)}
|
d.containers = &contStore{s: make(map[string]*Container)}
|
||||||
d.execCommands = newExecStore()
|
d.execCommands = exec.NewStore()
|
||||||
d.graph = g
|
d.graph = g
|
||||||
d.repositories = repositories
|
d.repositories = repositories
|
||||||
d.idIndex = truncindex.NewTruncIndex([]string{})
|
d.idIndex = truncindex.NewTruncIndex([]string{})
|
||||||
|
|
171
daemon/exec.go
171
daemon/exec.go
|
@ -3,91 +3,23 @@ package daemon
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/docker/docker/daemon/exec"
|
||||||
"github.com/docker/docker/daemon/execdriver"
|
"github.com/docker/docker/daemon/execdriver"
|
||||||
derr "github.com/docker/docker/errors"
|
derr "github.com/docker/docker/errors"
|
||||||
"github.com/docker/docker/pkg/pools"
|
"github.com/docker/docker/pkg/pools"
|
||||||
"github.com/docker/docker/pkg/promise"
|
"github.com/docker/docker/pkg/promise"
|
||||||
"github.com/docker/docker/pkg/stringid"
|
|
||||||
"github.com/docker/docker/pkg/stringutils"
|
"github.com/docker/docker/pkg/stringutils"
|
||||||
"github.com/docker/docker/runconfig"
|
"github.com/docker/docker/runconfig"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ExecConfig holds the configurations for execs. The Daemon keeps
|
func (d *Daemon) registerExecCommand(container *Container, config *exec.Config) {
|
||||||
// track of both running and finished execs so that they can be
|
|
||||||
// examined both during and after completion.
|
|
||||||
type ExecConfig struct {
|
|
||||||
sync.Mutex
|
|
||||||
ID string
|
|
||||||
Running bool
|
|
||||||
ExitCode int
|
|
||||||
ProcessConfig *execdriver.ProcessConfig
|
|
||||||
OpenStdin bool
|
|
||||||
OpenStderr bool
|
|
||||||
OpenStdout bool
|
|
||||||
streamConfig *runconfig.StreamConfig
|
|
||||||
Container *Container
|
|
||||||
canRemove bool
|
|
||||||
|
|
||||||
// waitStart will be closed immediately after the exec is really started.
|
|
||||||
waitStart chan struct{}
|
|
||||||
}
|
|
||||||
|
|
||||||
type execStore struct {
|
|
||||||
s map[string]*ExecConfig
|
|
||||||
sync.RWMutex
|
|
||||||
}
|
|
||||||
|
|
||||||
func newExecStore() *execStore {
|
|
||||||
return &execStore{s: make(map[string]*ExecConfig, 0)}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *execStore) Add(id string, ExecConfig *ExecConfig) {
|
|
||||||
e.Lock()
|
|
||||||
e.s[id] = ExecConfig
|
|
||||||
e.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *execStore) Get(id string) *ExecConfig {
|
|
||||||
e.RLock()
|
|
||||||
res := e.s[id]
|
|
||||||
e.RUnlock()
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *execStore) Delete(id string) {
|
|
||||||
e.Lock()
|
|
||||||
delete(e.s, id)
|
|
||||||
e.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *execStore) List() []string {
|
|
||||||
var IDs []string
|
|
||||||
e.RLock()
|
|
||||||
for id := range e.s {
|
|
||||||
IDs = append(IDs, id)
|
|
||||||
}
|
|
||||||
e.RUnlock()
|
|
||||||
return IDs
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ExecConfig *ExecConfig) resize(h, w int) error {
|
|
||||||
select {
|
|
||||||
case <-ExecConfig.waitStart:
|
|
||||||
case <-time.After(time.Second):
|
|
||||||
return derr.ErrorCodeExecResize.WithArgs(ExecConfig.ID)
|
|
||||||
}
|
|
||||||
return ExecConfig.ProcessConfig.Terminal.Resize(h, w)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Daemon) registerExecCommand(ExecConfig *ExecConfig) {
|
|
||||||
// Storing execs in container in order to kill them gracefully whenever the container is stopped or removed.
|
// Storing execs in container in order to kill them gracefully whenever the container is stopped or removed.
|
||||||
ExecConfig.Container.execCommands.Add(ExecConfig.ID, ExecConfig)
|
container.execCommands.Add(config.ID, config)
|
||||||
// Storing execs in daemon for easy access via remote API.
|
// Storing execs in daemon for easy access via remote API.
|
||||||
d.execCommands.Add(ExecConfig.ID, ExecConfig)
|
d.execCommands.Add(config.ID, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExecExists looks up the exec instance and returns a bool if it exists or not.
|
// ExecExists looks up the exec instance and returns a bool if it exists or not.
|
||||||
|
@ -101,7 +33,7 @@ func (d *Daemon) ExecExists(name string) (bool, error) {
|
||||||
|
|
||||||
// getExecConfig looks up the exec instance by name. If the container associated
|
// getExecConfig looks up the exec instance by name. If the container associated
|
||||||
// with the exec instance is stopped or paused, it will return an error.
|
// with the exec instance is stopped or paused, it will return an error.
|
||||||
func (d *Daemon) getExecConfig(name string) (*ExecConfig, error) {
|
func (d *Daemon) getExecConfig(name string) (*exec.Config, error) {
|
||||||
ec := d.execCommands.Get(name)
|
ec := d.execCommands.Get(name)
|
||||||
|
|
||||||
// If the exec is found but its container is not in the daemon's list of
|
// If the exec is found but its container is not in the daemon's list of
|
||||||
|
@ -110,22 +42,24 @@ func (d *Daemon) getExecConfig(name string) (*ExecConfig, error) {
|
||||||
// the user sees the same error now that they will after the
|
// the user sees the same error now that they will after the
|
||||||
// 5 minute clean-up loop is run which erases old/dead execs.
|
// 5 minute clean-up loop is run which erases old/dead execs.
|
||||||
|
|
||||||
if ec != nil && d.containers.Get(ec.Container.ID) != nil {
|
if ec != nil {
|
||||||
if !ec.Container.IsRunning() {
|
if container := d.containers.Get(ec.ContainerID); container != nil {
|
||||||
return nil, derr.ErrorCodeContainerNotRunning.WithArgs(ec.Container.ID, ec.Container.State.String())
|
if !container.IsRunning() {
|
||||||
|
return nil, derr.ErrorCodeContainerNotRunning.WithArgs(container.ID, container.State.String())
|
||||||
}
|
}
|
||||||
if ec.Container.isPaused() {
|
if container.isPaused() {
|
||||||
return nil, derr.ErrorCodeExecPaused.WithArgs(ec.Container.ID)
|
return nil, derr.ErrorCodeExecPaused.WithArgs(container.ID)
|
||||||
}
|
}
|
||||||
return ec, nil
|
return ec, nil
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil, derr.ErrorCodeNoExecID.WithArgs(name)
|
return nil, derr.ErrorCodeNoExecID.WithArgs(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Daemon) unregisterExecCommand(ExecConfig *ExecConfig) {
|
func (d *Daemon) unregisterExecCommand(container *Container, execConfig *exec.Config) {
|
||||||
ExecConfig.Container.execCommands.Delete(ExecConfig.ID)
|
container.execCommands.Delete(execConfig.ID)
|
||||||
d.execCommands.Delete(ExecConfig.ID)
|
d.execCommands.Delete(execConfig.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Daemon) getActiveContainer(name string) (*Container, error) {
|
func (d *Daemon) getActiveContainer(name string) (*Container, error) {
|
||||||
|
@ -162,23 +96,18 @@ func (d *Daemon) ContainerExecCreate(config *runconfig.ExecConfig) (string, erro
|
||||||
}
|
}
|
||||||
setPlatformSpecificExecProcessConfig(config, container, processConfig)
|
setPlatformSpecificExecProcessConfig(config, container, processConfig)
|
||||||
|
|
||||||
ExecConfig := &ExecConfig{
|
execConfig := exec.NewConfig()
|
||||||
ID: stringid.GenerateNonCryptoID(),
|
execConfig.OpenStdin = config.AttachStdin
|
||||||
OpenStdin: config.AttachStdin,
|
execConfig.OpenStdout = config.AttachStdout
|
||||||
OpenStdout: config.AttachStdout,
|
execConfig.OpenStderr = config.AttachStderr
|
||||||
OpenStderr: config.AttachStderr,
|
execConfig.ProcessConfig = processConfig
|
||||||
streamConfig: runconfig.NewStreamConfig(),
|
execConfig.ContainerID = container.ID
|
||||||
ProcessConfig: processConfig,
|
|
||||||
Container: container,
|
|
||||||
Running: false,
|
|
||||||
waitStart: make(chan struct{}),
|
|
||||||
}
|
|
||||||
|
|
||||||
d.registerExecCommand(ExecConfig)
|
d.registerExecCommand(container, execConfig)
|
||||||
|
|
||||||
d.LogContainerEvent(container, "exec_create: "+ExecConfig.ProcessConfig.Entrypoint+" "+strings.Join(ExecConfig.ProcessConfig.Arguments, " "))
|
d.LogContainerEvent(container, "exec_create: "+execConfig.ProcessConfig.Entrypoint+" "+strings.Join(execConfig.ProcessConfig.Arguments, " "))
|
||||||
|
|
||||||
return ExecConfig.ID, nil
|
return execConfig.ID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContainerExecStart starts a previously set up exec instance. The
|
// ContainerExecStart starts a previously set up exec instance. The
|
||||||
|
@ -202,8 +131,8 @@ func (d *Daemon) ContainerExecStart(name string, stdin io.ReadCloser, stdout io.
|
||||||
ec.Running = true
|
ec.Running = true
|
||||||
ec.Unlock()
|
ec.Unlock()
|
||||||
|
|
||||||
logrus.Debugf("starting exec command %s in container %s", ec.ID, ec.Container.ID)
|
container := d.containers.Get(ec.ContainerID)
|
||||||
container := ec.Container
|
logrus.Debugf("starting exec command %s in container %s", ec.ID, container.ID)
|
||||||
d.LogContainerEvent(container, "exec_start: "+ec.ProcessConfig.Entrypoint+" "+strings.Join(ec.ProcessConfig.Arguments, " "))
|
d.LogContainerEvent(container, "exec_start: "+ec.ProcessConfig.Entrypoint+" "+strings.Join(ec.ProcessConfig.Arguments, " "))
|
||||||
|
|
||||||
if ec.OpenStdin {
|
if ec.OpenStdin {
|
||||||
|
@ -223,12 +152,12 @@ func (d *Daemon) ContainerExecStart(name string, stdin io.ReadCloser, stdout io.
|
||||||
}
|
}
|
||||||
|
|
||||||
if ec.OpenStdin {
|
if ec.OpenStdin {
|
||||||
ec.streamConfig.NewInputPipes()
|
ec.NewInputPipes()
|
||||||
} else {
|
} else {
|
||||||
ec.streamConfig.NewNopInputPipe()
|
ec.NewNopInputPipe()
|
||||||
}
|
}
|
||||||
|
|
||||||
attachErr := attach(ec.streamConfig, ec.OpenStdin, true, ec.ProcessConfig.Tty, cStdin, cStdout, cStderr)
|
attachErr := attach(ec.StreamConfig, ec.OpenStdin, true, ec.ProcessConfig.Tty, cStdin, cStdout, cStderr)
|
||||||
|
|
||||||
execErr := make(chan error)
|
execErr := make(chan error)
|
||||||
|
|
||||||
|
@ -263,19 +192,19 @@ func (d *Daemon) ContainerExecStart(name string, stdin io.ReadCloser, stdout io.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exec calls the underlying exec driver to run
|
// Exec calls the underlying exec driver to run
|
||||||
func (d *Daemon) Exec(c *Container, ExecConfig *ExecConfig, pipes *execdriver.Pipes, startCallback execdriver.DriverCallback) (int, error) {
|
func (d *Daemon) Exec(c *Container, execConfig *exec.Config, pipes *execdriver.Pipes, startCallback execdriver.DriverCallback) (int, error) {
|
||||||
hooks := execdriver.Hooks{
|
hooks := execdriver.Hooks{
|
||||||
Start: startCallback,
|
Start: startCallback,
|
||||||
}
|
}
|
||||||
exitStatus, err := d.execDriver.Exec(c.command, ExecConfig.ProcessConfig, pipes, hooks)
|
exitStatus, err := d.execDriver.Exec(c.command, execConfig.ProcessConfig, pipes, hooks)
|
||||||
|
|
||||||
// On err, make sure we don't leave ExitCode at zero
|
// On err, make sure we don't leave ExitCode at zero
|
||||||
if err != nil && exitStatus == 0 {
|
if err != nil && exitStatus == 0 {
|
||||||
exitStatus = 128
|
exitStatus = 128
|
||||||
}
|
}
|
||||||
|
|
||||||
ExecConfig.ExitCode = exitStatus
|
execConfig.ExitCode = exitStatus
|
||||||
ExecConfig.Running = false
|
execConfig.Running = false
|
||||||
|
|
||||||
return exitStatus, err
|
return exitStatus, err
|
||||||
}
|
}
|
||||||
|
@ -288,13 +217,13 @@ func (d *Daemon) execCommandGC() {
|
||||||
cleaned int
|
cleaned int
|
||||||
liveExecCommands = d.containerExecIds()
|
liveExecCommands = d.containerExecIds()
|
||||||
)
|
)
|
||||||
for id, config := range d.execCommands.s {
|
for id, config := range d.execCommands.Commands() {
|
||||||
if config.canRemove {
|
if config.CanRemove {
|
||||||
cleaned++
|
cleaned++
|
||||||
d.execCommands.Delete(id)
|
d.execCommands.Delete(id)
|
||||||
} else {
|
} else {
|
||||||
if _, exists := liveExecCommands[id]; !exists {
|
if _, exists := liveExecCommands[id]; !exists {
|
||||||
config.canRemove = true
|
config.CanRemove = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -316,7 +245,7 @@ func (d *Daemon) containerExecIds() map[string]struct{} {
|
||||||
return ids
|
return ids
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Daemon) containerExec(container *Container, ec *ExecConfig) error {
|
func (d *Daemon) containerExec(container *Container, ec *exec.Config) error {
|
||||||
container.Lock()
|
container.Lock()
|
||||||
defer container.Unlock()
|
defer container.Unlock()
|
||||||
|
|
||||||
|
@ -329,43 +258,35 @@ func (d *Daemon) containerExec(container *Container, ec *ExecConfig) error {
|
||||||
c.Close()
|
c.Close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
close(ec.waitStart)
|
ec.Close()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// We use a callback here instead of a goroutine and an chan for
|
// We use a callback here instead of a goroutine and an chan for
|
||||||
// synchronization purposes
|
// synchronization purposes
|
||||||
cErr := promise.Go(func() error { return d.monitorExec(container, ec, callback) })
|
cErr := promise.Go(func() error { return d.monitorExec(container, ec, callback) })
|
||||||
|
return ec.Wait(cErr)
|
||||||
// Exec should not return until the process is actually running
|
|
||||||
select {
|
|
||||||
case <-ec.waitStart:
|
|
||||||
case err := <-cErr:
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Daemon) monitorExec(container *Container, ExecConfig *ExecConfig, callback execdriver.DriverCallback) error {
|
func (d *Daemon) monitorExec(container *Container, execConfig *exec.Config, callback execdriver.DriverCallback) error {
|
||||||
pipes := execdriver.NewPipes(ExecConfig.streamConfig.Stdin(), ExecConfig.streamConfig.Stdout(), ExecConfig.streamConfig.Stderr(), ExecConfig.OpenStdin)
|
pipes := execdriver.NewPipes(execConfig.Stdin(), execConfig.Stdout(), execConfig.Stderr(), execConfig.OpenStdin)
|
||||||
exitCode, err := d.Exec(container, ExecConfig, pipes, callback)
|
exitCode, err := d.Exec(container, execConfig, pipes, callback)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Errorf("Error running command in existing container %s: %s", container.ID, err)
|
logrus.Errorf("Error running command in existing container %s: %s", container.ID, err)
|
||||||
}
|
}
|
||||||
logrus.Debugf("Exec task in container %s exited with code %d", container.ID, exitCode)
|
logrus.Debugf("Exec task in container %s exited with code %d", container.ID, exitCode)
|
||||||
|
|
||||||
if err := ExecConfig.streamConfig.CloseStreams(); err != nil {
|
if err := execConfig.CloseStreams(); err != nil {
|
||||||
logrus.Errorf("%s: %s", container.ID, err)
|
logrus.Errorf("%s: %s", container.ID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ExecConfig.ProcessConfig.Terminal != nil {
|
if execConfig.ProcessConfig.Terminal != nil {
|
||||||
if err := ExecConfig.ProcessConfig.Terminal.Close(); err != nil {
|
if err := execConfig.ProcessConfig.Terminal.Close(); err != nil {
|
||||||
logrus.Errorf("Error closing terminal while running in container %s: %s", container.ID, err)
|
logrus.Errorf("Error closing terminal while running in container %s: %s", container.ID, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// remove the exec command from the container's store only and not the
|
// remove the exec command from the container's store only and not the
|
||||||
// daemon's store so that the exec command can be inspected.
|
// daemon's store so that the exec command can be inspected.
|
||||||
container.execCommands.Delete(ExecConfig.ID)
|
container.execCommands.Delete(execConfig.ID)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
115
daemon/exec/exec.go
Normal file
115
daemon/exec/exec.go
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
package exec
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/docker/docker/daemon/execdriver"
|
||||||
|
derr "github.com/docker/docker/errors"
|
||||||
|
"github.com/docker/docker/pkg/stringid"
|
||||||
|
"github.com/docker/docker/runconfig"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Config holds the configurations for execs. The Daemon keeps
|
||||||
|
// track of both running and finished execs so that they can be
|
||||||
|
// examined both during and after completion.
|
||||||
|
type Config struct {
|
||||||
|
sync.Mutex
|
||||||
|
*runconfig.StreamConfig
|
||||||
|
ID string
|
||||||
|
Running bool
|
||||||
|
ExitCode int
|
||||||
|
ProcessConfig *execdriver.ProcessConfig
|
||||||
|
OpenStdin bool
|
||||||
|
OpenStderr bool
|
||||||
|
OpenStdout bool
|
||||||
|
CanRemove bool
|
||||||
|
ContainerID string
|
||||||
|
|
||||||
|
// waitStart will be closed immediately after the exec is really started.
|
||||||
|
waitStart chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewConfig initializes the a new exec configuration
|
||||||
|
func NewConfig() *Config {
|
||||||
|
return &Config{
|
||||||
|
ID: stringid.GenerateNonCryptoID(),
|
||||||
|
StreamConfig: runconfig.NewStreamConfig(),
|
||||||
|
waitStart: make(chan struct{}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store keeps track of the exec configurations.
|
||||||
|
type Store struct {
|
||||||
|
commands map[string]*Config
|
||||||
|
sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewStore initializes a new exec store.
|
||||||
|
func NewStore() *Store {
|
||||||
|
return &Store{commands: make(map[string]*Config, 0)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commands returns the exec configurations in the store.
|
||||||
|
func (e *Store) Commands() map[string]*Config {
|
||||||
|
return e.commands
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add adds a new exec configuration to the store.
|
||||||
|
func (e *Store) Add(id string, Config *Config) {
|
||||||
|
e.Lock()
|
||||||
|
e.commands[id] = Config
|
||||||
|
e.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns an exec configuration by its id.
|
||||||
|
func (e *Store) Get(id string) *Config {
|
||||||
|
e.RLock()
|
||||||
|
res := e.commands[id]
|
||||||
|
e.RUnlock()
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete removes an exec configuration from the store.
|
||||||
|
func (e *Store) Delete(id string) {
|
||||||
|
e.Lock()
|
||||||
|
delete(e.commands, id)
|
||||||
|
e.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// List returns the list of exec ids in the store.
|
||||||
|
func (e *Store) List() []string {
|
||||||
|
var IDs []string
|
||||||
|
e.RLock()
|
||||||
|
for id := range e.commands {
|
||||||
|
IDs = append(IDs, id)
|
||||||
|
}
|
||||||
|
e.RUnlock()
|
||||||
|
return IDs
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait waits until the exec process finishes or there is an error in the error channel.
|
||||||
|
func (c *Config) Wait(cErr chan error) error {
|
||||||
|
// Exec should not return until the process is actually running
|
||||||
|
select {
|
||||||
|
case <-c.waitStart:
|
||||||
|
case err := <-cErr:
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the wait channel for the progress.
|
||||||
|
func (c *Config) Close() {
|
||||||
|
close(c.waitStart)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resize changes the size of the terminal for the exec process.
|
||||||
|
func (c *Config) Resize(h, w int) error {
|
||||||
|
select {
|
||||||
|
case <-c.waitStart:
|
||||||
|
case <-time.After(time.Second):
|
||||||
|
return derr.ErrorCodeExecResize.WithArgs(c.ID)
|
||||||
|
}
|
||||||
|
return c.ProcessConfig.Terminal.Resize(h, w)
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ import (
|
||||||
|
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/versions/v1p20"
|
"github.com/docker/docker/api/types/versions/v1p20"
|
||||||
|
"github.com/docker/docker/daemon/exec"
|
||||||
"github.com/docker/docker/daemon/network"
|
"github.com/docker/docker/daemon/network"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -159,7 +160,7 @@ func (daemon *Daemon) getInspectData(container *Container, size bool) (*types.Co
|
||||||
|
|
||||||
// ContainerExecInspect returns low-level information about the exec
|
// ContainerExecInspect returns low-level information about the exec
|
||||||
// command. An error is returned if the exec cannot be found.
|
// command. An error is returned if the exec cannot be found.
|
||||||
func (daemon *Daemon) ContainerExecInspect(id string) (*ExecConfig, error) {
|
func (daemon *Daemon) ContainerExecInspect(id string) (*exec.Config, error) {
|
||||||
eConfig, err := daemon.getExecConfig(id)
|
eConfig, err := daemon.getExecConfig(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -29,5 +29,5 @@ func (daemon *Daemon) ContainerExecResize(name string, height, width int) error
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return ExecConfig.resize(height, width)
|
return ExecConfig.Resize(height, width)
|
||||||
}
|
}
|
||||||
|
|
|
@ -160,8 +160,8 @@ func (daemon *Daemon) Cleanup(container *Container) {
|
||||||
|
|
||||||
daemon.conditionalUnmountOnCleanup(container)
|
daemon.conditionalUnmountOnCleanup(container)
|
||||||
|
|
||||||
for _, eConfig := range container.execCommands.s {
|
for _, eConfig := range container.execCommands.Commands() {
|
||||||
daemon.unregisterExecCommand(eConfig)
|
daemon.unregisterExecCommand(container, eConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := container.unmountVolumes(false); err != nil {
|
if err := container.unmountVolumes(false); err != nil {
|
||||||
|
|
Loading…
Reference in a new issue