Adding /exec/<name>/resize to resize tty session of an exec command.
Docker-DCO-1.1-Signed-off-by: Vishnu Kannan <vishnuk@google.com> (github: vishh)
| ... | ... |
@@ -793,7 +793,7 @@ func postContainersResize(eng *engine.Engine, version version.Version, w http.Re |
| 793 | 793 |
if vars == nil {
|
| 794 | 794 |
return fmt.Errorf("Missing parameter")
|
| 795 | 795 |
} |
| 796 |
- if err := eng.Job("resize", vars["name"], r.Form.Get("h"), r.Form.Get("w")).Run(); err != nil {
|
|
| 796 |
+ if err := eng.Job("resize", vars["name"], r.Form.Get("h"), r.Form.Get("w"), r.Form.Get("exec")).Run(); err != nil {
|
|
| 797 | 797 |
return err |
| 798 | 798 |
} |
| 799 | 799 |
return nil |
| ... | ... |
@@ -1025,18 +1025,45 @@ func postContainersCopy(eng *engine.Engine, version version.Version, w http.Resp |
| 1025 | 1025 |
return nil |
| 1026 | 1026 |
} |
| 1027 | 1027 |
|
| 1028 |
-func postContainersExec(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 1028 |
+func postContainerExecCreate(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 1029 | 1029 |
if err := parseForm(r); err != nil {
|
| 1030 | 1030 |
return nil |
| 1031 | 1031 |
} |
| 1032 | 1032 |
var ( |
| 1033 |
- name = vars["name"] |
|
| 1034 |
- job = eng.Job("exec", name)
|
|
| 1033 |
+ out engine.Env |
|
| 1034 |
+ name = vars["name"] |
|
| 1035 |
+ job = eng.Job("execCreate", name)
|
|
| 1036 |
+ stdoutBuffer = bytes.NewBuffer(nil) |
|
| 1037 |
+ ) |
|
| 1038 |
+ if err := job.DecodeEnv(r.Body); err != nil {
|
|
| 1039 |
+ return err |
|
| 1040 |
+ } |
|
| 1041 |
+ |
|
| 1042 |
+ job.Stdout.Add(stdoutBuffer) |
|
| 1043 |
+ // Register an instance of Exec in container. |
|
| 1044 |
+ if err := job.Run(); err != nil {
|
|
| 1045 |
+ fmt.Fprintf(os.Stderr, "Error setting up exec command in container %s: %s\n", name, err) |
|
| 1046 |
+ return err |
|
| 1047 |
+ } |
|
| 1048 |
+ // Return the ID |
|
| 1049 |
+ out.Set("Id", engine.Tail(stdoutBuffer, 1))
|
|
| 1050 |
+ |
|
| 1051 |
+ return writeJSON(w, http.StatusCreated, out) |
|
| 1052 |
+} |
|
| 1053 |
+ |
|
| 1054 |
+func postContainerExecStart(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 1055 |
+ if err := parseForm(r); err != nil {
|
|
| 1056 |
+ return nil |
|
| 1057 |
+ } |
|
| 1058 |
+ var ( |
|
| 1059 |
+ name = vars["name"] |
|
| 1060 |
+ job = eng.Job("execStart", name)
|
|
| 1061 |
+ errOut io.Writer = os.Stderr |
|
| 1035 | 1062 |
) |
| 1063 |
+ |
|
| 1036 | 1064 |
if err := job.DecodeEnv(r.Body); err != nil {
|
| 1037 | 1065 |
return err |
| 1038 | 1066 |
} |
| 1039 |
- var errOut io.Writer = os.Stderr |
|
| 1040 | 1067 |
|
| 1041 | 1068 |
if !job.GetenvBool("Detach") {
|
| 1042 | 1069 |
// Setting up the streaming http interface. |
| ... | ... |
@@ -1076,7 +1103,7 @@ func postContainersExec(eng *engine.Engine, version version.Version, w http.Resp |
| 1076 | 1076 |
} |
| 1077 | 1077 |
// Now run the user process in container. |
| 1078 | 1078 |
if err := job.Run(); err != nil {
|
| 1079 |
- fmt.Fprintf(errOut, "Error running in container %s: %s\n", name, err) |
|
| 1079 |
+ fmt.Fprintf(errOut, "Error starting exec command in container %s: %s\n", name, err) |
|
| 1080 | 1080 |
return err |
| 1081 | 1081 |
} |
| 1082 | 1082 |
w.WriteHeader(http.StatusNoContent) |
| ... | ... |
@@ -1084,6 +1111,19 @@ func postContainersExec(eng *engine.Engine, version version.Version, w http.Resp |
| 1084 | 1084 |
return nil |
| 1085 | 1085 |
} |
| 1086 | 1086 |
|
| 1087 |
+func postContainerExecResize(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 1088 |
+ if err := parseForm(r); err != nil {
|
|
| 1089 |
+ return err |
|
| 1090 |
+ } |
|
| 1091 |
+ if vars == nil {
|
|
| 1092 |
+ return fmt.Errorf("Missing parameter")
|
|
| 1093 |
+ } |
|
| 1094 |
+ if err := eng.Job("execResize", vars["name"], r.Form.Get("h"), r.Form.Get("w")).Run(); err != nil {
|
|
| 1095 |
+ return err |
|
| 1096 |
+ } |
|
| 1097 |
+ return nil |
|
| 1098 |
+} |
|
| 1099 |
+ |
|
| 1087 | 1100 |
func optionsHandler(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
| 1088 | 1101 |
w.WriteHeader(http.StatusOK) |
| 1089 | 1102 |
return nil |
| ... | ... |
@@ -1206,7 +1246,9 @@ func createRouter(eng *engine.Engine, logging, enableCors bool, dockerVersion st |
| 1206 | 1206 |
"/containers/{name:.*}/resize": postContainersResize,
|
| 1207 | 1207 |
"/containers/{name:.*}/attach": postContainersAttach,
|
| 1208 | 1208 |
"/containers/{name:.*}/copy": postContainersCopy,
|
| 1209 |
- "/containers/{name:.*}/exec": postContainersExec,
|
|
| 1209 |
+ "/containers/{name:.*}/exec": postContainerExecCreate,
|
|
| 1210 |
+ "/exec/{name:.*}/start": postContainerExecStart,
|
|
| 1211 |
+ "/exec/{name:.*}/resize": postContainerExecResize,
|
|
| 1210 | 1212 |
}, |
| 1211 | 1213 |
"DELETE": {
|
| 1212 | 1214 |
"/containers/{name:.*}": deleteContainers,
|
| ... | ... |
@@ -1393,6 +1435,7 @@ func ListenAndServe(proto, addr string, job *engine.Job) error {
|
| 1393 | 1393 |
return err |
| 1394 | 1394 |
} |
| 1395 | 1395 |
} |
| 1396 |
+ |
|
| 1396 | 1397 |
} |
| 1397 | 1398 |
if err := os.Chmod(addr, 0660); err != nil {
|
| 1398 | 1399 |
return err |
| ... | ... |
@@ -86,8 +86,9 @@ type Container struct {
|
| 86 | 86 |
VolumesRW map[string]bool |
| 87 | 87 |
hostConfig *runconfig.HostConfig |
| 88 | 88 |
|
| 89 |
- activeLinks map[string]*links.Link |
|
| 90 |
- monitor *containerMonitor |
|
| 89 |
+ activeLinks map[string]*links.Link |
|
| 90 |
+ monitor *containerMonitor |
|
| 91 |
+ execCommands *execStore |
|
| 91 | 92 |
} |
| 92 | 93 |
|
| 93 | 94 |
func (container *Container) FromDisk() error {
|
| ... | ... |
@@ -85,6 +85,7 @@ type Daemon struct {
|
| 85 | 85 |
repository string |
| 86 | 86 |
sysInitPath string |
| 87 | 87 |
containers *contStore |
| 88 |
+ execCommands *execStore |
|
| 88 | 89 |
graph *graph.Graph |
| 89 | 90 |
repositories *graph.TagStore |
| 90 | 91 |
idIndex *truncindex.TruncIndex |
| ... | ... |
@@ -122,7 +123,9 @@ func (daemon *Daemon) Install(eng *engine.Engine) error {
|
| 122 | 122 |
"unpause": daemon.ContainerUnpause, |
| 123 | 123 |
"wait": daemon.ContainerWait, |
| 124 | 124 |
"image_delete": daemon.ImageDelete, // FIXME: see above |
| 125 |
- "exec": daemon.ContainerExec, |
|
| 125 |
+ "execCreate": daemon.ContainerExecCreate, |
|
| 126 |
+ "execStart": daemon.ContainerExecStart, |
|
| 127 |
+ "execResize": daemon.ContainerExecResize, |
|
| 126 | 128 |
} {
|
| 127 | 129 |
if err := eng.Register(name, method); err != nil {
|
| 128 | 130 |
return err |
| ... | ... |
@@ -539,6 +542,7 @@ func (daemon *Daemon) newContainer(name string, config *runconfig.Config, img *i |
| 539 | 539 |
Driver: daemon.driver.String(), |
| 540 | 540 |
ExecDriver: daemon.execDriver.Name(), |
| 541 | 541 |
State: NewState(), |
| 542 |
+ execCommands: newExecStore(), |
|
| 542 | 543 |
} |
| 543 | 544 |
container.root = daemon.containerRoot(container.ID) |
| 544 | 545 |
|
| ... | ... |
@@ -847,6 +851,7 @@ func NewDaemonFromDirectory(config *Config, eng *engine.Engine) (*Daemon, error) |
| 847 | 847 |
daemon := &Daemon{
|
| 848 | 848 |
repository: daemonRepo, |
| 849 | 849 |
containers: &contStore{s: make(map[string]*Container)},
|
| 850 |
+ execCommands: newExecStore(), |
|
| 850 | 851 |
graph: g, |
| 851 | 852 |
repositories: repositories, |
| 852 | 853 |
idIndex: truncindex.NewTruncIndex([]string{}),
|
| ... | ... |
@@ -6,6 +6,7 @@ import ( |
| 6 | 6 |
"fmt" |
| 7 | 7 |
"io" |
| 8 | 8 |
"io/ioutil" |
| 9 |
+ "sync" |
|
| 9 | 10 |
|
| 10 | 11 |
"github.com/docker/docker/daemon/execdriver" |
| 11 | 12 |
"github.com/docker/docker/engine" |
| ... | ... |
@@ -16,52 +17,99 @@ import ( |
| 16 | 16 |
"github.com/docker/docker/utils" |
| 17 | 17 |
) |
| 18 | 18 |
|
| 19 |
-type ExecConfig struct {
|
|
| 19 |
+type execConfig struct {
|
|
| 20 |
+ ID string |
|
| 20 | 21 |
ProcessConfig execdriver.ProcessConfig |
| 21 | 22 |
StreamConfig |
| 22 |
- OpenStdin bool |
|
| 23 |
+ OpenStdin bool |
|
| 24 |
+ OpenStderr bool |
|
| 25 |
+ OpenStdout bool |
|
| 26 |
+ Container *Container |
|
| 23 | 27 |
} |
| 24 | 28 |
|
| 25 |
-func (d *Daemon) ContainerExec(job *engine.Job) engine.Status {
|
|
| 26 |
- if len(job.Args) != 1 {
|
|
| 27 |
- return job.Errorf("Usage: %s [options] container command [args]", job.Name)
|
|
| 29 |
+type execStore struct {
|
|
| 30 |
+ s map[string]*execConfig |
|
| 31 |
+ sync.Mutex |
|
| 32 |
+} |
|
| 33 |
+ |
|
| 34 |
+func newExecStore() *execStore {
|
|
| 35 |
+ return &execStore{s: make(map[string]*execConfig, 0)}
|
|
| 36 |
+} |
|
| 37 |
+ |
|
| 38 |
+func (e *execStore) Add(id string, execConfig *execConfig) {
|
|
| 39 |
+ e.Lock() |
|
| 40 |
+ e.s[id] = execConfig |
|
| 41 |
+ e.Unlock() |
|
| 42 |
+} |
|
| 43 |
+ |
|
| 44 |
+func (e *execStore) Get(id string) *execConfig {
|
|
| 45 |
+ e.Lock() |
|
| 46 |
+ res := e.s[id] |
|
| 47 |
+ e.Unlock() |
|
| 48 |
+ return res |
|
| 49 |
+} |
|
| 50 |
+ |
|
| 51 |
+func (e *execStore) Delete(id string) {
|
|
| 52 |
+ e.Lock() |
|
| 53 |
+ delete(e.s, id) |
|
| 54 |
+ e.Unlock() |
|
| 55 |
+} |
|
| 56 |
+ |
|
| 57 |
+func (execConfig *execConfig) Resize(h, w int) error {
|
|
| 58 |
+ return execConfig.ProcessConfig.Terminal.Resize(h, w) |
|
| 59 |
+} |
|
| 60 |
+ |
|
| 61 |
+func (d *Daemon) registerExecCommand(execConfig *execConfig) {
|
|
| 62 |
+ // Storing execs in container inorder to kill them gracefully whenever the container is stopped or removed. |
|
| 63 |
+ execConfig.Container.execCommands.Add(execConfig.ID, execConfig) |
|
| 64 |
+ // Storing execs in daemon for easy access via remote API. |
|
| 65 |
+ d.execCommands.Add(execConfig.ID, execConfig) |
|
| 66 |
+} |
|
| 67 |
+ |
|
| 68 |
+func (d *Daemon) getExecConfig(name string) (*execConfig, error) {
|
|
| 69 |
+ if execConfig := d.execCommands.Get(name); execConfig != nil {
|
|
| 70 |
+ if !execConfig.Container.IsRunning() {
|
|
| 71 |
+ return nil, fmt.Errorf("Container %s is not not running", execConfig.Container.ID)
|
|
| 72 |
+ } |
|
| 73 |
+ return execConfig, nil |
|
| 28 | 74 |
} |
| 29 | 75 |
|
| 30 |
- var ( |
|
| 31 |
- cStdin io.ReadCloser |
|
| 32 |
- cStdout, cStderr io.Writer |
|
| 33 |
- cStdinCloser io.Closer |
|
| 34 |
- name = job.Args[0] |
|
| 35 |
- ) |
|
| 76 |
+ return nil, fmt.Errorf("No exec '%s' in found in daemon", name)
|
|
| 77 |
+} |
|
| 36 | 78 |
|
| 79 |
+func (d *Daemon) unregisterExecCommand(execConfig *execConfig) {
|
|
| 80 |
+ execConfig.Container.execCommands.Delete(execConfig.ID) |
|
| 81 |
+ d.execCommands.Delete(execConfig.ID) |
|
| 82 |
+} |
|
| 83 |
+ |
|
| 84 |
+func (d *Daemon) getActiveContainer(name string) (*Container, error) {
|
|
| 37 | 85 |
container := d.Get(name) |
| 38 | 86 |
|
| 39 | 87 |
if container == nil {
|
| 40 |
- return job.Errorf("No such container: %s", name)
|
|
| 88 |
+ return nil, fmt.Errorf("No such container: %s", name)
|
|
| 41 | 89 |
} |
| 42 | 90 |
|
| 43 | 91 |
if !container.IsRunning() {
|
| 44 |
- return job.Errorf("Container %s is not not running", name)
|
|
| 92 |
+ return nil, fmt.Errorf("Container %s is not not running", name)
|
|
| 45 | 93 |
} |
| 46 | 94 |
|
| 47 |
- config := runconfig.ExecConfigFromJob(job) |
|
| 95 |
+ return container, nil |
|
| 96 |
+} |
|
| 48 | 97 |
|
| 49 |
- if config.AttachStdin {
|
|
| 50 |
- r, w := io.Pipe() |
|
| 51 |
- go func() {
|
|
| 52 |
- defer w.Close() |
|
| 53 |
- io.Copy(w, job.Stdin) |
|
| 54 |
- }() |
|
| 55 |
- cStdin = r |
|
| 56 |
- cStdinCloser = job.Stdin |
|
| 57 |
- } |
|
| 58 |
- if config.AttachStdout {
|
|
| 59 |
- cStdout = job.Stdout |
|
| 98 |
+func (d *Daemon) ContainerExecCreate(job *engine.Job) engine.Status {
|
|
| 99 |
+ if len(job.Args) != 1 {
|
|
| 100 |
+ return job.Errorf("Usage: %s [options] container command [args]", job.Name)
|
|
| 60 | 101 |
} |
| 61 |
- if config.AttachStderr {
|
|
| 62 |
- cStderr = job.Stderr |
|
| 102 |
+ |
|
| 103 |
+ var name = job.Args[0] |
|
| 104 |
+ |
|
| 105 |
+ container, err := d.getActiveContainer(name) |
|
| 106 |
+ if err != nil {
|
|
| 107 |
+ return job.Error(err) |
|
| 63 | 108 |
} |
| 64 | 109 |
|
| 110 |
+ config := runconfig.ExecConfigFromJob(job) |
|
| 111 |
+ |
|
| 65 | 112 |
entrypoint, args := d.getEntrypointAndArgs(nil, config.Cmd) |
| 66 | 113 |
|
| 67 | 114 |
processConfig := execdriver.ProcessConfig{
|
| ... | ... |
@@ -72,10 +120,60 @@ func (d *Daemon) ContainerExec(job *engine.Job) engine.Status {
|
| 72 | 72 |
Arguments: args, |
| 73 | 73 |
} |
| 74 | 74 |
|
| 75 |
- execConfig := &ExecConfig{
|
|
| 75 |
+ execConfig := &execConfig{
|
|
| 76 |
+ ID: utils.GenerateRandomID(), |
|
| 76 | 77 |
OpenStdin: config.AttachStdin, |
| 78 |
+ OpenStdout: config.AttachStdout, |
|
| 79 |
+ OpenStderr: config.AttachStderr, |
|
| 77 | 80 |
StreamConfig: StreamConfig{},
|
| 78 | 81 |
ProcessConfig: processConfig, |
| 82 |
+ Container: container, |
|
| 83 |
+ } |
|
| 84 |
+ |
|
| 85 |
+ d.registerExecCommand(execConfig) |
|
| 86 |
+ |
|
| 87 |
+ job.Printf("%s\n", execConfig.ID)
|
|
| 88 |
+ |
|
| 89 |
+ return engine.StatusOK |
|
| 90 |
+} |
|
| 91 |
+ |
|
| 92 |
+func (d *Daemon) ContainerExecStart(job *engine.Job) engine.Status {
|
|
| 93 |
+ if len(job.Args) != 2 {
|
|
| 94 |
+ return job.Errorf("Usage: %s [options] container exec", job.Name)
|
|
| 95 |
+ } |
|
| 96 |
+ |
|
| 97 |
+ var ( |
|
| 98 |
+ cStdin io.ReadCloser |
|
| 99 |
+ cStdout, cStderr io.Writer |
|
| 100 |
+ cStdinCloser io.Closer |
|
| 101 |
+ execName = job.Args[0] |
|
| 102 |
+ ) |
|
| 103 |
+ |
|
| 104 |
+ if execName == "" {
|
|
| 105 |
+ return job.Errorf("ExecName not specified. Cannot start exec command")
|
|
| 106 |
+ } |
|
| 107 |
+ |
|
| 108 |
+ execConfig, err := d.getExecConfig(execName) |
|
| 109 |
+ if err != nil {
|
|
| 110 |
+ return job.Error(err) |
|
| 111 |
+ } |
|
| 112 |
+ |
|
| 113 |
+ container := execConfig.Container |
|
| 114 |
+ |
|
| 115 |
+ if execConfig.OpenStdin {
|
|
| 116 |
+ r, w := io.Pipe() |
|
| 117 |
+ go func() {
|
|
| 118 |
+ defer w.Close() |
|
| 119 |
+ io.Copy(w, job.Stdin) |
|
| 120 |
+ }() |
|
| 121 |
+ cStdin = r |
|
| 122 |
+ cStdinCloser = job.Stdin |
|
| 123 |
+ } |
|
| 124 |
+ if execConfig.OpenStdout {
|
|
| 125 |
+ cStdout = job.Stdout |
|
| 126 |
+ } |
|
| 127 |
+ if execConfig.OpenStderr {
|
|
| 128 |
+ cStderr = job.Stderr |
|
| 79 | 129 |
} |
| 80 | 130 |
|
| 81 | 131 |
execConfig.StreamConfig.stderr = broadcastwriter.New() |
| ... | ... |
@@ -87,13 +185,17 @@ func (d *Daemon) ContainerExec(job *engine.Job) engine.Status {
|
| 87 | 87 |
execConfig.StreamConfig.stdinPipe = ioutils.NopWriteCloser(ioutil.Discard) // Silently drop stdin |
| 88 | 88 |
} |
| 89 | 89 |
|
| 90 |
- attachErr := d.Attach(&execConfig.StreamConfig, config.AttachStdin, false, config.Tty, cStdin, cStdinCloser, cStdout, cStderr) |
|
| 90 |
+ attachErr := d.Attach(&execConfig.StreamConfig, execConfig.OpenStdin, false, execConfig.ProcessConfig.Tty, cStdin, cStdinCloser, cStdout, cStderr) |
|
| 91 | 91 |
|
| 92 | 92 |
execErr := make(chan error) |
| 93 |
+ |
|
| 94 |
+ // Remove exec from daemon and container. |
|
| 95 |
+ defer d.unregisterExecCommand(execConfig) |
|
| 96 |
+ |
|
| 93 | 97 |
go func() {
|
| 94 | 98 |
err := container.Exec(execConfig) |
| 95 | 99 |
if err != nil {
|
| 96 |
- execErr <- fmt.Errorf("Cannot run in container %s: %s", name, err)
|
|
| 100 |
+ execErr <- fmt.Errorf("Cannot run exec command %s in container %s: %s", execName, container.ID, err)
|
|
| 97 | 101 |
} |
| 98 | 102 |
}() |
| 99 | 103 |
|
| ... | ... |
@@ -110,11 +212,11 @@ func (d *Daemon) ContainerExec(job *engine.Job) engine.Status {
|
| 110 | 110 |
return engine.StatusOK |
| 111 | 111 |
} |
| 112 | 112 |
|
| 113 |
-func (daemon *Daemon) Exec(c *Container, execConfig *ExecConfig, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) {
|
|
| 114 |
- return daemon.execDriver.Exec(c.command, &execConfig.ProcessConfig, pipes, startCallback) |
|
| 113 |
+func (d *Daemon) Exec(c *Container, execConfig *execConfig, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) {
|
|
| 114 |
+ return d.execDriver.Exec(c.command, &execConfig.ProcessConfig, pipes, startCallback) |
|
| 115 | 115 |
} |
| 116 | 116 |
|
| 117 |
-func (container *Container) Exec(execConfig *ExecConfig) error {
|
|
| 117 |
+func (container *Container) Exec(execConfig *execConfig) error {
|
|
| 118 | 118 |
container.Lock() |
| 119 | 119 |
defer container.Unlock() |
| 120 | 120 |
|
| ... | ... |
@@ -146,7 +248,7 @@ func (container *Container) Exec(execConfig *ExecConfig) error {
|
| 146 | 146 |
return nil |
| 147 | 147 |
} |
| 148 | 148 |
|
| 149 |
-func (container *Container) monitorExec(execConfig *ExecConfig, callback execdriver.StartCallback) error {
|
|
| 149 |
+func (container *Container) monitorExec(execConfig *execConfig, callback execdriver.StartCallback) error {
|
|
| 150 | 150 |
var ( |
| 151 | 151 |
err error |
| 152 | 152 |
exitCode int |
| ... | ... |
@@ -19,6 +19,7 @@ func (daemon *Daemon) ContainerResize(job *engine.Job) engine.Status {
|
| 19 | 19 |
if err != nil {
|
| 20 | 20 |
return job.Error(err) |
| 21 | 21 |
} |
| 22 |
+ |
|
| 22 | 23 |
if container := daemon.Get(name); container != nil {
|
| 23 | 24 |
if err := container.Resize(height, width); err != nil {
|
| 24 | 25 |
return job.Error(err) |
| ... | ... |
@@ -27,3 +28,26 @@ func (daemon *Daemon) ContainerResize(job *engine.Job) engine.Status {
|
| 27 | 27 |
} |
| 28 | 28 |
return job.Errorf("No such container: %s", name)
|
| 29 | 29 |
} |
| 30 |
+ |
|
| 31 |
+func (daemon *Daemon) ContainerExecResize(job *engine.Job) engine.Status {
|
|
| 32 |
+ if len(job.Args) != 3 {
|
|
| 33 |
+ return job.Errorf("Not enough arguments. Usage: %s EXEC HEIGHT WIDTH\n", job.Name)
|
|
| 34 |
+ } |
|
| 35 |
+ name := job.Args[0] |
|
| 36 |
+ height, err := strconv.Atoi(job.Args[1]) |
|
| 37 |
+ if err != nil {
|
|
| 38 |
+ return job.Error(err) |
|
| 39 |
+ } |
|
| 40 |
+ width, err := strconv.Atoi(job.Args[2]) |
|
| 41 |
+ if err != nil {
|
|
| 42 |
+ return job.Error(err) |
|
| 43 |
+ } |
|
| 44 |
+ execConfig, err := daemon.getExecConfig(name) |
|
| 45 |
+ if err != nil {
|
|
| 46 |
+ return job.Error(err) |
|
| 47 |
+ } |
|
| 48 |
+ if err := execConfig.Resize(height, width); err != nil {
|
|
| 49 |
+ return job.Error(err) |
|
| 50 |
+ } |
|
| 51 |
+ return engine.StatusOK |
|
| 52 |
+} |