From bef8de93194a442c21481f0b7e72f2fac781f799 Mon Sep 17 00:00:00 2001 From: Solomon Hykes Date: Wed, 20 Nov 2013 07:37:03 +0000 Subject: [PATCH 1/3] Engine: integer job status, improved stream API * Jobs return an integer status instead of a string * Status convention mimics unix process execution: 0=success, 1=generic error, 127="no such command" * Stdout and Stderr support multiple thread-safe data receivers and ring buffer filtering --- engine/job.go | 2 +- engine/streams.go | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/engine/job.go b/engine/job.go index 478f06cd60..465086af91 100644 --- a/engine/job.go +++ b/engine/job.go @@ -24,7 +24,7 @@ type Job struct { Eng *Engine Name string Args []string - env *Env + env *Env Stdout *Output Stderr *Output Stdin *Input diff --git a/engine/streams.go b/engine/streams.go index ee26fe5f1e..7cd4a60cf7 100644 --- a/engine/streams.go +++ b/engine/streams.go @@ -168,7 +168,7 @@ func Tail(src io.Reader, n int, dst *[]string) { // AddEnv starts a new goroutine which will decode all subsequent data // as a stream of json-encoded objects, and point `dst` to the last // decoded object. -// The result `env` can be queried using the type-neutral Env interface. +// The result `env` can be queried using the type-neutral Env interface. // It is not safe to query `env` until the Output is closed. func (o *Output) AddEnv() (dst *Env, err error) { src, err := o.AddPipe() @@ -185,9 +185,8 @@ func (o *Output) AddEnv() (dst *Env, err error) { if err != nil { return } - *dst= *env + *dst = *env } }() return dst, nil } - From dbe1915fee1beb034459fb875928c96e27ecbfce Mon Sep 17 00:00:00 2001 From: Solomon Hykes Date: Sun, 17 Nov 2013 03:00:16 +0000 Subject: [PATCH 2/3] Engine: new command 'stop' gracefully stops a container. --- api.go | 13 +++++-------- integration/server_test.go | 5 ++--- server.go | 32 +++++++++++++++++++++++++++----- 3 files changed, 34 insertions(+), 16 deletions(-) diff --git a/api.go b/api.go index 58fa310240..1555923018 100644 --- a/api.go +++ b/api.go @@ -683,17 +683,14 @@ func postContainersStop(srv *Server, version float64, w http.ResponseWriter, r * if err := parseForm(r); err != nil { return err } - t, err := strconv.Atoi(r.Form.Get("t")) - if err != nil || t < 0 { - t = 10 - } - if vars == nil { return fmt.Errorf("Missing parameter") } - name := vars["name"] - - if err := srv.ContainerStop(name, t); err != nil { + job := srv.Eng.Job("stop", vars["name"]) + if t := r.Form.Get("t"); t != "" { + job.Args = append(job.Args, t) + } + if err := job.Run(); err != nil { return err } w.WriteHeader(http.StatusNoContent) diff --git a/integration/server_test.go b/integration/server_test.go index c1499e26cc..9eab174b99 100644 --- a/integration/server_test.go +++ b/integration/server_test.go @@ -128,8 +128,7 @@ func TestCreateRmVolumes(t *testing.T) { t.Fatal(err) } - err = srv.ContainerStop(id, 1) - if err != nil { + if err := eng.Job("stop", id, "1").Run(); err != nil { t.Fatal(err) } @@ -187,7 +186,7 @@ func TestCreateStartRestartStopStartKillRm(t *testing.T) { t.Fatal(err) } - if err := srv.ContainerStop(id, 15); err != nil { + if err := eng.Job("stop", id, "15").Run(); err != nil { t.Fatal(err) } diff --git a/server.go b/server.go index c32cfe23ed..01cdee0473 100644 --- a/server.go +++ b/server.go @@ -79,6 +79,10 @@ func jobInitApi(job *engine.Job) engine.Status { job.Error(err) return engine.StatusErr } + if err := job.Eng.Register("stop", srv.ContainerStop); err != nil { + job.Error(err) + return engine.StatusErr + } if err := job.Eng.Register("start", srv.ContainerStart); err != nil { job.Error(err) return engine.StatusErr @@ -1713,16 +1717,34 @@ func (srv *Server) ContainerStart(job *engine.Job) engine.Status { return engine.StatusOK } -func (srv *Server) ContainerStop(name string, t int) error { +func (srv *Server) ContainerStop(job *engine.Job) engine.Status { + if len(job.Args) < 1 { + job.Errorf("Not enough arguments. Usage: %s CONTAINER TIMEOUT\n", job.Name) + return engine.StatusErr + } + name := job.Args[0] + var t uint64 + if len(job.Args) == 2 { + var err error + t, err = strconv.ParseUint(job.Args[1], 10, 32) + if err != nil { + job.Errorf("Invalid delay format: %s. Please provide an integer number of seconds.\n", job.Args[1]) + return engine.StatusErr + } + } else { + t = 10 + } if container := srv.runtime.Get(name); container != nil { - if err := container.Stop(t); err != nil { - return fmt.Errorf("Cannot stop container %s: %s", name, err) + if err := container.Stop(int(t)); err != nil { + job.Errorf("Cannot stop container %s: %s\n", name, err) + return engine.StatusErr } srv.LogEvent("stop", container.ID, srv.runtime.repositories.ImageName(container.Image)) } else { - return fmt.Errorf("No such container: %s", name) + job.Errorf("No such container: %s\n", name) + return engine.StatusErr } - return nil + return engine.StatusOK } func (srv *Server) ContainerWait(job *engine.Job) engine.Status { From 6ba456ff87cfe2687db7df896e2983636ca9fe1e Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Wed, 11 Dec 2013 15:36:50 -0800 Subject: [PATCH 3/3] move t from arg to env --- api.go | 4 +--- integration/server_test.go | 8 ++++++-- server.go | 15 ++++----------- 3 files changed, 11 insertions(+), 16 deletions(-) diff --git a/api.go b/api.go index 1555923018..f72e1f5afd 100644 --- a/api.go +++ b/api.go @@ -687,9 +687,7 @@ func postContainersStop(srv *Server, version float64, w http.ResponseWriter, r * return fmt.Errorf("Missing parameter") } job := srv.Eng.Job("stop", vars["name"]) - if t := r.Form.Get("t"); t != "" { - job.Args = append(job.Args, t) - } + job.Setenv("t", r.Form.Get("t")) if err := job.Run(); err != nil { return err } diff --git a/integration/server_test.go b/integration/server_test.go index 9eab174b99..e469abb610 100644 --- a/integration/server_test.go +++ b/integration/server_test.go @@ -128,7 +128,9 @@ func TestCreateRmVolumes(t *testing.T) { t.Fatal(err) } - if err := eng.Job("stop", id, "1").Run(); err != nil { + job = eng.Job("stop", id) + job.SetenvInt("t", 1) + if err := job.Run(); err != nil { t.Fatal(err) } @@ -186,7 +188,9 @@ func TestCreateStartRestartStopStartKillRm(t *testing.T) { t.Fatal(err) } - if err := eng.Job("stop", id, "15").Run(); err != nil { + job = eng.Job("stop", id) + job.SetenvInt("t", 15) + if err := job.Run(); err != nil { t.Fatal(err) } diff --git a/server.go b/server.go index 01cdee0473..ed0c4ac1fa 100644 --- a/server.go +++ b/server.go @@ -1718,20 +1718,13 @@ func (srv *Server) ContainerStart(job *engine.Job) engine.Status { } func (srv *Server) ContainerStop(job *engine.Job) engine.Status { - if len(job.Args) < 1 { - job.Errorf("Not enough arguments. Usage: %s CONTAINER TIMEOUT\n", job.Name) + if len(job.Args) != 1 { + job.Errorf("Usage: %s CONTAINER\n", job.Name) return engine.StatusErr } name := job.Args[0] - var t uint64 - if len(job.Args) == 2 { - var err error - t, err = strconv.ParseUint(job.Args[1], 10, 32) - if err != nil { - job.Errorf("Invalid delay format: %s. Please provide an integer number of seconds.\n", job.Args[1]) - return engine.StatusErr - } - } else { + t := job.GetenvInt("t") + if t == -1 { t = 10 } if container := srv.runtime.Get(name); container != nil {