mirror of https://github.com/docker/docs.git
Splitting the exec remote API into two separate APIs inorder to support resizing of tty sessions.
1. /container/<name>/exec - Creates a new exec command instance in the daemon and container '<name>'. Returns an unique ID for each exec command. 2. /exec/<name>/start - Starts an existing exec command instance. Removes the exec command from the daemon once it completes. 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)
This commit is contained in:
parent
0029180f7f
commit
bfebdfde78
|
@ -793,7 +793,7 @@ func postContainersResize(eng *engine.Engine, version version.Version, w http.Re
|
||||||
if vars == nil {
|
if vars == nil {
|
||||||
return fmt.Errorf("Missing parameter")
|
return fmt.Errorf("Missing parameter")
|
||||||
}
|
}
|
||||||
if err := eng.Job("resize", vars["name"], r.Form.Get("h"), r.Form.Get("w")).Run(); err != nil {
|
if err := eng.Job("resize", vars["name"], r.Form.Get("h"), r.Form.Get("w"), r.Form.Get("exec")).Run(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -1025,18 +1025,45 @@ func postContainersCopy(eng *engine.Engine, version version.Version, w http.Resp
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func postContainersExec(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
func postContainerExecCreate(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
if err := parseForm(r); err != nil {
|
if err := parseForm(r); err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
name = vars["name"]
|
out engine.Env
|
||||||
job = eng.Job("exec", name)
|
name = vars["name"]
|
||||||
|
job = eng.Job("execCreate", name)
|
||||||
|
stdoutBuffer = bytes.NewBuffer(nil)
|
||||||
)
|
)
|
||||||
if err := job.DecodeEnv(r.Body); err != nil {
|
if err := job.DecodeEnv(r.Body); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
var errOut io.Writer = os.Stderr
|
|
||||||
|
job.Stdout.Add(stdoutBuffer)
|
||||||
|
// Register an instance of Exec in container.
|
||||||
|
if err := job.Run(); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Error setting up exec command in container %s: %s\n", name, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Return the ID
|
||||||
|
out.Set("Id", engine.Tail(stdoutBuffer, 1))
|
||||||
|
|
||||||
|
return writeJSON(w, http.StatusCreated, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
func postContainerExecStart(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
if err := parseForm(r); err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
name = vars["name"]
|
||||||
|
job = eng.Job("execStart", name)
|
||||||
|
errOut io.Writer = os.Stderr
|
||||||
|
)
|
||||||
|
|
||||||
|
if err := job.DecodeEnv(r.Body); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if !job.GetenvBool("Detach") {
|
if !job.GetenvBool("Detach") {
|
||||||
// Setting up the streaming http interface.
|
// Setting up the streaming http interface.
|
||||||
|
@ -1076,7 +1103,7 @@ func postContainersExec(eng *engine.Engine, version version.Version, w http.Resp
|
||||||
}
|
}
|
||||||
// Now run the user process in container.
|
// Now run the user process in container.
|
||||||
if err := job.Run(); err != nil {
|
if err := job.Run(); err != nil {
|
||||||
fmt.Fprintf(errOut, "Error running in container %s: %s\n", name, err)
|
fmt.Fprintf(errOut, "Error starting exec command in container %s: %s\n", name, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
w.WriteHeader(http.StatusNoContent)
|
w.WriteHeader(http.StatusNoContent)
|
||||||
|
@ -1084,6 +1111,19 @@ func postContainersExec(eng *engine.Engine, version version.Version, w http.Resp
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func postContainerExecResize(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
if err := parseForm(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if vars == nil {
|
||||||
|
return fmt.Errorf("Missing parameter")
|
||||||
|
}
|
||||||
|
if err := eng.Job("execResize", vars["name"], r.Form.Get("h"), r.Form.Get("w")).Run(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func optionsHandler(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
func optionsHandler(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
return nil
|
return nil
|
||||||
|
@ -1206,7 +1246,9 @@ func createRouter(eng *engine.Engine, logging, enableCors bool, dockerVersion st
|
||||||
"/containers/{name:.*}/resize": postContainersResize,
|
"/containers/{name:.*}/resize": postContainersResize,
|
||||||
"/containers/{name:.*}/attach": postContainersAttach,
|
"/containers/{name:.*}/attach": postContainersAttach,
|
||||||
"/containers/{name:.*}/copy": postContainersCopy,
|
"/containers/{name:.*}/copy": postContainersCopy,
|
||||||
"/containers/{name:.*}/exec": postContainersExec,
|
"/containers/{name:.*}/exec": postContainerExecCreate,
|
||||||
|
"/exec/{name:.*}/start": postContainerExecStart,
|
||||||
|
"/exec/{name:.*}/resize": postContainerExecResize,
|
||||||
},
|
},
|
||||||
"DELETE": {
|
"DELETE": {
|
||||||
"/containers/{name:.*}": deleteContainers,
|
"/containers/{name:.*}": deleteContainers,
|
||||||
|
@ -1393,6 +1435,7 @@ func ListenAndServe(proto, addr string, job *engine.Job) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
if err := os.Chmod(addr, 0660); err != nil {
|
if err := os.Chmod(addr, 0660); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -86,8 +86,9 @@ type Container struct {
|
||||||
VolumesRW map[string]bool
|
VolumesRW map[string]bool
|
||||||
hostConfig *runconfig.HostConfig
|
hostConfig *runconfig.HostConfig
|
||||||
|
|
||||||
activeLinks map[string]*links.Link
|
activeLinks map[string]*links.Link
|
||||||
monitor *containerMonitor
|
monitor *containerMonitor
|
||||||
|
execCommands *execStore
|
||||||
}
|
}
|
||||||
|
|
||||||
func (container *Container) FromDisk() error {
|
func (container *Container) FromDisk() error {
|
||||||
|
|
|
@ -85,6 +85,7 @@ type Daemon struct {
|
||||||
repository string
|
repository string
|
||||||
sysInitPath string
|
sysInitPath string
|
||||||
containers *contStore
|
containers *contStore
|
||||||
|
execCommands *execStore
|
||||||
graph *graph.Graph
|
graph *graph.Graph
|
||||||
repositories *graph.TagStore
|
repositories *graph.TagStore
|
||||||
idIndex *truncindex.TruncIndex
|
idIndex *truncindex.TruncIndex
|
||||||
|
@ -122,7 +123,9 @@ func (daemon *Daemon) Install(eng *engine.Engine) error {
|
||||||
"unpause": daemon.ContainerUnpause,
|
"unpause": daemon.ContainerUnpause,
|
||||||
"wait": daemon.ContainerWait,
|
"wait": daemon.ContainerWait,
|
||||||
"image_delete": daemon.ImageDelete, // FIXME: see above
|
"image_delete": daemon.ImageDelete, // FIXME: see above
|
||||||
"exec": daemon.ContainerExec,
|
"execCreate": daemon.ContainerExecCreate,
|
||||||
|
"execStart": daemon.ContainerExecStart,
|
||||||
|
"execResize": daemon.ContainerExecResize,
|
||||||
} {
|
} {
|
||||||
if err := eng.Register(name, method); err != nil {
|
if err := eng.Register(name, method); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -539,6 +542,7 @@ func (daemon *Daemon) newContainer(name string, config *runconfig.Config, img *i
|
||||||
Driver: daemon.driver.String(),
|
Driver: daemon.driver.String(),
|
||||||
ExecDriver: daemon.execDriver.Name(),
|
ExecDriver: daemon.execDriver.Name(),
|
||||||
State: NewState(),
|
State: NewState(),
|
||||||
|
execCommands: newExecStore(),
|
||||||
}
|
}
|
||||||
container.root = daemon.containerRoot(container.ID)
|
container.root = daemon.containerRoot(container.ID)
|
||||||
|
|
||||||
|
@ -847,6 +851,7 @@ func NewDaemonFromDirectory(config *Config, eng *engine.Engine) (*Daemon, error)
|
||||||
daemon := &Daemon{
|
daemon := &Daemon{
|
||||||
repository: daemonRepo,
|
repository: daemonRepo,
|
||||||
containers: &contStore{s: make(map[string]*Container)},
|
containers: &contStore{s: make(map[string]*Container)},
|
||||||
|
execCommands: newExecStore(),
|
||||||
graph: g,
|
graph: g,
|
||||||
repositories: repositories,
|
repositories: repositories,
|
||||||
idIndex: truncindex.NewTruncIndex([]string{}),
|
idIndex: truncindex.NewTruncIndex([]string{}),
|
||||||
|
|
182
daemon/exec.go
182
daemon/exec.go
|
@ -6,6 +6,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/docker/docker/daemon/execdriver"
|
"github.com/docker/docker/daemon/execdriver"
|
||||||
"github.com/docker/docker/engine"
|
"github.com/docker/docker/engine"
|
||||||
|
@ -16,52 +17,99 @@ import (
|
||||||
"github.com/docker/docker/utils"
|
"github.com/docker/docker/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ExecConfig struct {
|
type execConfig struct {
|
||||||
|
ID string
|
||||||
ProcessConfig execdriver.ProcessConfig
|
ProcessConfig execdriver.ProcessConfig
|
||||||
StreamConfig
|
StreamConfig
|
||||||
OpenStdin bool
|
OpenStdin bool
|
||||||
|
OpenStderr bool
|
||||||
|
OpenStdout bool
|
||||||
|
Container *Container
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Daemon) ContainerExec(job *engine.Job) engine.Status {
|
type execStore struct {
|
||||||
|
s map[string]*execConfig
|
||||||
|
sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
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.Lock()
|
||||||
|
res := e.s[id]
|
||||||
|
e.Unlock()
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *execStore) Delete(id string) {
|
||||||
|
e.Lock()
|
||||||
|
delete(e.s, id)
|
||||||
|
e.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (execConfig *execConfig) Resize(h, w int) error {
|
||||||
|
return execConfig.ProcessConfig.Terminal.Resize(h, w)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Daemon) registerExecCommand(execConfig *execConfig) {
|
||||||
|
// Storing execs in container inorder to kill them gracefully whenever the container is stopped or removed.
|
||||||
|
execConfig.Container.execCommands.Add(execConfig.ID, execConfig)
|
||||||
|
// Storing execs in daemon for easy access via remote API.
|
||||||
|
d.execCommands.Add(execConfig.ID, execConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Daemon) getExecConfig(name string) (*execConfig, error) {
|
||||||
|
if execConfig := d.execCommands.Get(name); execConfig != nil {
|
||||||
|
if !execConfig.Container.IsRunning() {
|
||||||
|
return nil, fmt.Errorf("Container %s is not not running", execConfig.Container.ID)
|
||||||
|
}
|
||||||
|
return execConfig, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("No exec '%s' in found in daemon", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Daemon) unregisterExecCommand(execConfig *execConfig) {
|
||||||
|
execConfig.Container.execCommands.Delete(execConfig.ID)
|
||||||
|
d.execCommands.Delete(execConfig.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Daemon) getActiveContainer(name string) (*Container, error) {
|
||||||
|
container := d.Get(name)
|
||||||
|
|
||||||
|
if container == nil {
|
||||||
|
return nil, fmt.Errorf("No such container: %s", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !container.IsRunning() {
|
||||||
|
return nil, fmt.Errorf("Container %s is not not running", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return container, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Daemon) ContainerExecCreate(job *engine.Job) engine.Status {
|
||||||
if len(job.Args) != 1 {
|
if len(job.Args) != 1 {
|
||||||
return job.Errorf("Usage: %s [options] container command [args]", job.Name)
|
return job.Errorf("Usage: %s [options] container command [args]", job.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var name = job.Args[0]
|
||||||
cStdin io.ReadCloser
|
|
||||||
cStdout, cStderr io.Writer
|
|
||||||
cStdinCloser io.Closer
|
|
||||||
name = job.Args[0]
|
|
||||||
)
|
|
||||||
|
|
||||||
container := d.Get(name)
|
container, err := d.getActiveContainer(name)
|
||||||
|
if err != nil {
|
||||||
if container == nil {
|
return job.Error(err)
|
||||||
return job.Errorf("No such container: %s", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !container.IsRunning() {
|
|
||||||
return job.Errorf("Container %s is not not running", name)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
config := runconfig.ExecConfigFromJob(job)
|
config := runconfig.ExecConfigFromJob(job)
|
||||||
|
|
||||||
if config.AttachStdin {
|
|
||||||
r, w := io.Pipe()
|
|
||||||
go func() {
|
|
||||||
defer w.Close()
|
|
||||||
io.Copy(w, job.Stdin)
|
|
||||||
}()
|
|
||||||
cStdin = r
|
|
||||||
cStdinCloser = job.Stdin
|
|
||||||
}
|
|
||||||
if config.AttachStdout {
|
|
||||||
cStdout = job.Stdout
|
|
||||||
}
|
|
||||||
if config.AttachStderr {
|
|
||||||
cStderr = job.Stderr
|
|
||||||
}
|
|
||||||
|
|
||||||
entrypoint, args := d.getEntrypointAndArgs(nil, config.Cmd)
|
entrypoint, args := d.getEntrypointAndArgs(nil, config.Cmd)
|
||||||
|
|
||||||
processConfig := execdriver.ProcessConfig{
|
processConfig := execdriver.ProcessConfig{
|
||||||
|
@ -72,10 +120,60 @@ func (d *Daemon) ContainerExec(job *engine.Job) engine.Status {
|
||||||
Arguments: args,
|
Arguments: args,
|
||||||
}
|
}
|
||||||
|
|
||||||
execConfig := &ExecConfig{
|
execConfig := &execConfig{
|
||||||
|
ID: utils.GenerateRandomID(),
|
||||||
OpenStdin: config.AttachStdin,
|
OpenStdin: config.AttachStdin,
|
||||||
|
OpenStdout: config.AttachStdout,
|
||||||
|
OpenStderr: config.AttachStderr,
|
||||||
StreamConfig: StreamConfig{},
|
StreamConfig: StreamConfig{},
|
||||||
ProcessConfig: processConfig,
|
ProcessConfig: processConfig,
|
||||||
|
Container: container,
|
||||||
|
}
|
||||||
|
|
||||||
|
d.registerExecCommand(execConfig)
|
||||||
|
|
||||||
|
job.Printf("%s\n", execConfig.ID)
|
||||||
|
|
||||||
|
return engine.StatusOK
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Daemon) ContainerExecStart(job *engine.Job) engine.Status {
|
||||||
|
if len(job.Args) != 2 {
|
||||||
|
return job.Errorf("Usage: %s [options] container exec", job.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
cStdin io.ReadCloser
|
||||||
|
cStdout, cStderr io.Writer
|
||||||
|
cStdinCloser io.Closer
|
||||||
|
execName = job.Args[0]
|
||||||
|
)
|
||||||
|
|
||||||
|
if execName == "" {
|
||||||
|
return job.Errorf("ExecName not specified. Cannot start exec command")
|
||||||
|
}
|
||||||
|
|
||||||
|
execConfig, err := d.getExecConfig(execName)
|
||||||
|
if err != nil {
|
||||||
|
return job.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
container := execConfig.Container
|
||||||
|
|
||||||
|
if execConfig.OpenStdin {
|
||||||
|
r, w := io.Pipe()
|
||||||
|
go func() {
|
||||||
|
defer w.Close()
|
||||||
|
io.Copy(w, job.Stdin)
|
||||||
|
}()
|
||||||
|
cStdin = r
|
||||||
|
cStdinCloser = job.Stdin
|
||||||
|
}
|
||||||
|
if execConfig.OpenStdout {
|
||||||
|
cStdout = job.Stdout
|
||||||
|
}
|
||||||
|
if execConfig.OpenStderr {
|
||||||
|
cStderr = job.Stderr
|
||||||
}
|
}
|
||||||
|
|
||||||
execConfig.StreamConfig.stderr = broadcastwriter.New()
|
execConfig.StreamConfig.stderr = broadcastwriter.New()
|
||||||
|
@ -87,13 +185,17 @@ func (d *Daemon) ContainerExec(job *engine.Job) engine.Status {
|
||||||
execConfig.StreamConfig.stdinPipe = ioutils.NopWriteCloser(ioutil.Discard) // Silently drop stdin
|
execConfig.StreamConfig.stdinPipe = ioutils.NopWriteCloser(ioutil.Discard) // Silently drop stdin
|
||||||
}
|
}
|
||||||
|
|
||||||
attachErr := d.Attach(&execConfig.StreamConfig, config.AttachStdin, false, config.Tty, cStdin, cStdinCloser, cStdout, cStderr)
|
attachErr := d.Attach(&execConfig.StreamConfig, execConfig.OpenStdin, false, execConfig.ProcessConfig.Tty, cStdin, cStdinCloser, cStdout, cStderr)
|
||||||
|
|
||||||
execErr := make(chan error)
|
execErr := make(chan error)
|
||||||
|
|
||||||
|
// Remove exec from daemon and container.
|
||||||
|
defer d.unregisterExecCommand(execConfig)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
err := container.Exec(execConfig)
|
err := container.Exec(execConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
execErr <- fmt.Errorf("Cannot run in container %s: %s", name, err)
|
execErr <- fmt.Errorf("Cannot run exec command %s in container %s: %s", execName, container.ID, err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -110,11 +212,11 @@ func (d *Daemon) ContainerExec(job *engine.Job) engine.Status {
|
||||||
return engine.StatusOK
|
return engine.StatusOK
|
||||||
}
|
}
|
||||||
|
|
||||||
func (daemon *Daemon) Exec(c *Container, execConfig *ExecConfig, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) {
|
func (d *Daemon) Exec(c *Container, execConfig *execConfig, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) {
|
||||||
return daemon.execDriver.Exec(c.command, &execConfig.ProcessConfig, pipes, startCallback)
|
return d.execDriver.Exec(c.command, &execConfig.ProcessConfig, pipes, startCallback)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (container *Container) Exec(execConfig *ExecConfig) error {
|
func (container *Container) Exec(execConfig *execConfig) error {
|
||||||
container.Lock()
|
container.Lock()
|
||||||
defer container.Unlock()
|
defer container.Unlock()
|
||||||
|
|
||||||
|
@ -146,7 +248,7 @@ func (container *Container) Exec(execConfig *ExecConfig) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (container *Container) monitorExec(execConfig *ExecConfig, callback execdriver.StartCallback) error {
|
func (container *Container) monitorExec(execConfig *execConfig, callback execdriver.StartCallback) error {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
exitCode int
|
exitCode int
|
||||||
|
|
|
@ -19,6 +19,7 @@ func (daemon *Daemon) ContainerResize(job *engine.Job) engine.Status {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return job.Error(err)
|
return job.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if container := daemon.Get(name); container != nil {
|
if container := daemon.Get(name); container != nil {
|
||||||
if err := container.Resize(height, width); err != nil {
|
if err := container.Resize(height, width); err != nil {
|
||||||
return job.Error(err)
|
return job.Error(err)
|
||||||
|
@ -27,3 +28,26 @@ func (daemon *Daemon) ContainerResize(job *engine.Job) engine.Status {
|
||||||
}
|
}
|
||||||
return job.Errorf("No such container: %s", name)
|
return job.Errorf("No such container: %s", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (daemon *Daemon) ContainerExecResize(job *engine.Job) engine.Status {
|
||||||
|
if len(job.Args) != 3 {
|
||||||
|
return job.Errorf("Not enough arguments. Usage: %s EXEC HEIGHT WIDTH\n", job.Name)
|
||||||
|
}
|
||||||
|
name := job.Args[0]
|
||||||
|
height, err := strconv.Atoi(job.Args[1])
|
||||||
|
if err != nil {
|
||||||
|
return job.Error(err)
|
||||||
|
}
|
||||||
|
width, err := strconv.Atoi(job.Args[2])
|
||||||
|
if err != nil {
|
||||||
|
return job.Error(err)
|
||||||
|
}
|
||||||
|
execConfig, err := daemon.getExecConfig(name)
|
||||||
|
if err != nil {
|
||||||
|
return job.Error(err)
|
||||||
|
}
|
||||||
|
if err := execConfig.Resize(height, width); err != nil {
|
||||||
|
return job.Error(err)
|
||||||
|
}
|
||||||
|
return engine.StatusOK
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue