diff options
-rw-r--r-- | libpod/container_api.go | 20 | ||||
-rw-r--r-- | pkg/api/handlers/containers.go | 3 | ||||
-rw-r--r-- | pkg/api/handlers/containers_attach.go | 13 | ||||
-rw-r--r-- | pkg/api/handlers/generic/containers.go | 11 | ||||
-rw-r--r-- | pkg/api/handlers/images.go | 4 | ||||
-rw-r--r-- | pkg/api/handlers/images_build.go | 19 | ||||
-rw-r--r-- | pkg/api/handlers/libpod/containers.go | 1 | ||||
-rw-r--r-- | pkg/api/handlers/libpod/pods.go | 8 | ||||
-rw-r--r-- | pkg/api/handlers/utils/containers.go | 38 | ||||
-rw-r--r-- | pkg/api/handlers/utils/images.go | 3 | ||||
-rw-r--r-- | pkg/api/server/register_containers.go | 31 | ||||
-rw-r--r-- | pkg/bindings/containers/containers.go | 13 | ||||
-rw-r--r-- | pkg/bindings/test/containers_test.go | 57 |
13 files changed, 161 insertions, 60 deletions
diff --git a/libpod/container_api.go b/libpod/container_api.go index d612341bc..77fa372cc 100644 --- a/libpod/container_api.go +++ b/libpod/container_api.go @@ -629,6 +629,26 @@ func (c *Container) WaitWithInterval(waitTimeout time.Duration) (int32, error) { } } +func (c *Container) WaitForConditionWithInterval(waitTimeout time.Duration, condition define.ContainerStatus) (int32, error) { + if !c.valid { + return -1, define.ErrCtrRemoved + } + if condition == define.ContainerStateStopped || condition == define.ContainerStateExited { + return c.WaitWithInterval(waitTimeout) + } + for { + state, err := c.State() + if err != nil { + return -1, err + } + if state == condition { + break + } + time.Sleep(waitTimeout) + } + return -1, nil +} + // Cleanup unmounts all mount points in container and cleans up container storage // It also cleans up the network stack func (c *Container) Cleanup(ctx context.Context) error { diff --git a/pkg/api/handlers/containers.go b/pkg/api/handlers/containers.go index d7d040ce2..ee080e794 100644 --- a/pkg/api/handlers/containers.go +++ b/pkg/api/handlers/containers.go @@ -8,7 +8,6 @@ import ( "github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/pkg/api/handlers/utils" - "github.com/gorilla/mux" "github.com/gorilla/schema" "github.com/pkg/errors" ) @@ -179,7 +178,7 @@ func RestartContainer(w http.ResponseWriter, r *http.Request) { } timeout := con.StopTimeout() - if _, found := mux.Vars(r)["t"]; found { + if _, found := r.URL.Query()["t"]; found { timeout = uint(query.Timeout) } diff --git a/pkg/api/handlers/containers_attach.go b/pkg/api/handlers/containers_attach.go index facbd22a5..5a799a20c 100644 --- a/pkg/api/handlers/containers_attach.go +++ b/pkg/api/handlers/containers_attach.go @@ -6,7 +6,6 @@ import ( "github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/pkg/api/handlers/utils" - "github.com/gorilla/mux" "github.com/gorilla/schema" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -30,12 +29,10 @@ func AttachContainer(w http.ResponseWriter, r *http.Request) { return } - muxVars := mux.Vars(r) - // Detach keys: explicitly set to "" is very different from unset // TODO: Our format for parsing these may be different from Docker. var detachKeys *string - if _, found := muxVars["detachKeys"]; found { + if _, found := r.URL.Query()["detachKeys"]; found { detachKeys = &query.DetachKeys } @@ -44,15 +41,15 @@ func AttachContainer(w http.ResponseWriter, r *http.Request) { streams.Stderr = true streams.Stdin = true useStreams := false - if _, found := muxVars["stdin"]; found { + if _, found := r.URL.Query()["stdin"]; found { streams.Stdin = query.Stdin useStreams = true } - if _, found := muxVars["stdout"]; found { + if _, found := r.URL.Query()["stdout"]; found { streams.Stdout = query.Stdout useStreams = true } - if _, found := muxVars["stderr"]; found { + if _, found := r.URL.Query()["stderr"]; found { streams.Stderr = query.Stderr useStreams = true } @@ -72,7 +69,7 @@ func AttachContainer(w http.ResponseWriter, r *http.Request) { return } // We only support stream=true or unset - if _, found := muxVars["stream"]; found && query.Stream { + if _, found := r.URL.Query()["stream"]; found && query.Stream { utils.Error(w, "Unsupported parameter", http.StatusBadRequest, errors.Errorf("the stream parameter to attach is not presently supported")) return } diff --git a/pkg/api/handlers/generic/containers.go b/pkg/api/handlers/generic/containers.go index b16b87e73..ab587ded4 100644 --- a/pkg/api/handlers/generic/containers.go +++ b/pkg/api/handlers/generic/containers.go @@ -14,7 +14,6 @@ import ( "github.com/containers/libpod/pkg/api/handlers" "github.com/containers/libpod/pkg/api/handlers/utils" "github.com/containers/libpod/pkg/util" - "github.com/gorilla/mux" "github.com/gorilla/schema" "github.com/pkg/errors" log "github.com/sirupsen/logrus" @@ -71,7 +70,7 @@ func ListContainers(w http.ResponseWriter, r *http.Request) { utils.InternalServerError(w, err) return } - if _, found := mux.Vars(r)["limit"]; found { + if _, found := r.URL.Query()["limit"]; found { last := query.Limit if len(containers) > last { containers = containers[len(containers)-last:] @@ -136,7 +135,7 @@ func WaitContainer(w http.ResponseWriter, r *http.Request) { // /{version}/containers/(name)/wait exitCode, err := utils.WaitContainer(w, r) if err != nil { - msg = err.Error() + return } utils.WriteResponse(w, http.StatusOK, handlers.ContainerWaitOKBody{ StatusCode: int(exitCode), @@ -191,7 +190,7 @@ func LogsFromContainer(w http.ResponseWriter, r *http.Request) { } var since time.Time - if _, found := mux.Vars(r)["since"]; found { + if _, found := r.URL.Query()["since"]; found { since, err = util.ParseInputTime(query.Since) if err != nil { utils.BadRequest(w, "since", query.Since, err) @@ -200,7 +199,7 @@ func LogsFromContainer(w http.ResponseWriter, r *http.Request) { } var until time.Time - if _, found := mux.Vars(r)["until"]; found { + if _, found := r.URL.Query()["until"]; found { since, err = util.ParseInputTime(query.Until) if err != nil { utils.BadRequest(w, "until", query.Until, err) @@ -233,7 +232,7 @@ func LogsFromContainer(w http.ResponseWriter, r *http.Request) { var builder strings.Builder for ok := true; ok; ok = query.Follow { for line := range logChannel { - if _, found := mux.Vars(r)["until"]; found { + if _, found := r.URL.Query()["until"]; found { if line.Time.After(until) { break } diff --git a/pkg/api/handlers/images.go b/pkg/api/handlers/images.go index 2086ce748..d4549e5b4 100644 --- a/pkg/api/handlers/images.go +++ b/pkg/api/handlers/images.go @@ -11,7 +11,6 @@ import ( "github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/api/handlers/utils" - "github.com/gorilla/mux" "github.com/gorilla/schema" "github.com/pkg/errors" ) @@ -87,8 +86,7 @@ func RemoveImage(w http.ResponseWriter, r *http.Request) { utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String())) return } - muxVars := mux.Vars(r) - if _, found := muxVars["noprune"]; found { + if _, found := r.URL.Query()["noprune"]; found { if query.noPrune { utils.UnSupportedParameter("noprune") } diff --git a/pkg/api/handlers/images_build.go b/pkg/api/handlers/images_build.go index b29c45574..d969e3a47 100644 --- a/pkg/api/handlers/images_build.go +++ b/pkg/api/handlers/images_build.go @@ -17,7 +17,6 @@ import ( "github.com/containers/buildah/imagebuildah" "github.com/containers/libpod/pkg/api/handlers/utils" "github.com/containers/storage/pkg/archive" - "github.com/gorilla/mux" ) func BuildImage(w http.ResponseWriter, r *http.Request) { @@ -114,24 +113,24 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { tag = tokens[1] } - if t, found := mux.Vars(r)["target"]; found { - name = t + if _, found := r.URL.Query()["target"]; found { + name = query.Target } var buildArgs = map[string]string{} - if a, found := mux.Vars(r)["buildargs"]; found { - if err := json.Unmarshal([]byte(a), &buildArgs); err != nil { - utils.BadRequest(w, "buildargs", a, err) + if _, found := r.URL.Query()["buildargs"]; found { + if err := json.Unmarshal([]byte(query.BuildArgs), &buildArgs); err != nil { + utils.BadRequest(w, "buildargs", query.BuildArgs, err) return } } // convert label formats var labels = []string{} - if l, found := mux.Vars(r)["labels"]; found { + if _, found := r.URL.Query()["labels"]; found { var m = map[string]string{} - if err := json.Unmarshal([]byte(l), &m); err != nil { - utils.BadRequest(w, "labels", l, err) + if err := json.Unmarshal([]byte(query.Labels), &m); err != nil { + utils.BadRequest(w, "labels", query.Labels, err) return } @@ -141,7 +140,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { } pullPolicy := buildah.PullIfMissing - if _, found := mux.Vars(r)["pull"]; found { + if _, found := r.URL.Query()["pull"]; found { if query.Pull { pullPolicy = buildah.PullAlways } diff --git a/pkg/api/handlers/libpod/containers.go b/pkg/api/handlers/libpod/containers.go index 752b004d8..d8dd0d69b 100644 --- a/pkg/api/handlers/libpod/containers.go +++ b/pkg/api/handlers/libpod/containers.go @@ -178,7 +178,6 @@ func KillContainer(w http.ResponseWriter, r *http.Request) { func WaitContainer(w http.ResponseWriter, r *http.Request) { exitCode, err := utils.WaitContainer(w, r) if err != nil { - utils.InternalServerError(w, err) return } utils.WriteResponse(w, http.StatusOK, strconv.Itoa(int(exitCode))) diff --git a/pkg/api/handlers/libpod/pods.go b/pkg/api/handlers/libpod/pods.go index f5700579b..e8dc5bde2 100644 --- a/pkg/api/handlers/libpod/pods.go +++ b/pkg/api/handlers/libpod/pods.go @@ -13,7 +13,6 @@ import ( "github.com/containers/libpod/pkg/api/handlers" "github.com/containers/libpod/pkg/api/handlers/utils" "github.com/containers/libpod/pkg/util" - "github.com/gorilla/mux" "github.com/gorilla/schema" "github.com/pkg/errors" ) @@ -353,7 +352,7 @@ func PodKill(w http.ResponseWriter, r *http.Request) { signal = "SIGKILL" ) query := struct { - signal string `schema:"signal"` + Signal string `schema:"signal"` }{ // override any golang type defaults } @@ -362,9 +361,8 @@ func PodKill(w http.ResponseWriter, r *http.Request) { errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String())) return } - muxVars := mux.Vars(r) - if _, found := muxVars["signal"]; found { - signal = query.signal + if _, found := r.URL.Query()["signal"]; found { + signal = query.Signal } sig, err := util.ParseSignal(signal) diff --git a/pkg/api/handlers/utils/containers.go b/pkg/api/handlers/utils/containers.go index 402005581..07efef0f5 100644 --- a/pkg/api/handlers/utils/containers.go +++ b/pkg/api/handlers/utils/containers.go @@ -78,9 +78,12 @@ func RemoveContainer(w http.ResponseWriter, r *http.Request, force, vols bool) { } func WaitContainer(w http.ResponseWriter, r *http.Request) (int32, error) { + var ( + err error + interval time.Duration + ) runtime := r.Context().Value("runtime").(*libpod.Runtime) decoder := r.Context().Value("decoder").(*schema.Decoder) - // /{version}/containers/(name)/restart query := struct { Interval string `schema:"interval"` Condition string `schema:"condition"` @@ -91,25 +94,34 @@ func WaitContainer(w http.ResponseWriter, r *http.Request) (int32, error) { Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String())) return 0, err } - - if len(query.Condition) > 0 { - UnSupportedParameter("condition") + if _, found := r.URL.Query()["interval"]; found { + interval, err = time.ParseDuration(query.Interval) + if err != nil { + InternalServerError(w, err) + return 0, err + } + } else { + interval, err = time.ParseDuration("250ms") + if err != nil { + InternalServerError(w, err) + return 0, err + } + } + condition := define.ContainerStateStopped + if _, found := r.URL.Query()["condition"]; found { + condition, err = define.StringToContainerStatus(query.Condition) + if err != nil { + InternalServerError(w, err) + return 0, err + } } - name := GetName(r) con, err := runtime.LookupContainer(name) if err != nil { ContainerNotFound(w, name, err) return 0, err } - if len(query.Interval) > 0 { - d, err := time.ParseDuration(query.Interval) - if err != nil { - Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse %s for interval", query.Interval)) - } - return con.WaitWithInterval(d) - } - return con.Wait() + return con.WaitForConditionWithInterval(interval, condition) } // GenerateFilterFuncsFromMap is used to generate un-executed functions that can be used to filter diff --git a/pkg/api/handlers/utils/images.go b/pkg/api/handlers/utils/images.go index f68a71561..a97fd5c07 100644 --- a/pkg/api/handlers/utils/images.go +++ b/pkg/api/handlers/utils/images.go @@ -6,7 +6,6 @@ import ( "github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod/image" - "github.com/gorilla/mux" "github.com/gorilla/schema" ) @@ -28,7 +27,7 @@ func GetImages(w http.ResponseWriter, r *http.Request) ([]*image.Image, error) { return nil, err } var filters = []string{} - if _, found := mux.Vars(r)["digests"]; found && query.Digests { + if _, found := r.URL.Query()["digests"]; found && query.Digests { UnSupportedParameter("digests") } diff --git a/pkg/api/server/register_containers.go b/pkg/api/server/register_containers.go index 6a2ba4b1e..6aad7ff88 100644 --- a/pkg/api/server/register_containers.go +++ b/pkg/api/server/register_containers.go @@ -436,8 +436,8 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error { // --- // tags: // - containers (compat) - // summary: Wait on a container to exit - // description: Block until a container stops, then returns the exit code. + // summary: Wait on a container + // description: Block until a container stops or given condition is met. // parameters: // - in: path // name: name @@ -447,7 +447,14 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error { // - in: query // name: condition // type: string - // description: not supported + // description: | + // wait until container is to a given condition. default is stopped. valid conditions are: + // - configured + // - created + // - exited + // - paused + // - running + // - stopped // produces: // - application/json // responses: @@ -1030,18 +1037,30 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error { // --- // tags: // - containers - // summary: Wait on a container to exit + // summary: Wait on a container + // description: Wait on a container to met a given condition // parameters: // - in: path // name: name // type: string // required: true // description: the name or ID of the container + // - in: query + // name: condition + // type: string + // description: | + // wait until container is to a given condition. default is stopped. valid conditions are: + // - configured + // - created + // - exited + // - paused + // - running + // - stopped // produces: // - application/json // responses: - // 204: - // description: no error + // 200: + // $ref: "#/responses/ContainerWaitResponse" // 404: // $ref: "#/responses/NoSuchContainer" // 500: diff --git a/pkg/bindings/containers/containers.go b/pkg/bindings/containers/containers.go index 2985787a6..75322ead4 100644 --- a/pkg/bindings/containers/containers.go +++ b/pkg/bindings/containers/containers.go @@ -210,15 +210,20 @@ func Unpause(ctx context.Context, nameOrID string) error { return response.Process(nil) } -// Wait blocks until the given container exits and returns its exit code. The nameOrID can be a container name -// or a partial/full ID. -func Wait(ctx context.Context, nameOrID string) (int32, error) { +// Wait blocks until the given container reaches a condition. If not provided, the condition will +// default to stopped. If the condition is stopped, an exit code for the container will be provided. The +// nameOrID can be a container name or a partial/full ID. +func Wait(ctx context.Context, nameOrID string, condition *string) (int32, error) { var exitCode int32 conn, err := bindings.GetClient(ctx) if err != nil { return exitCode, err } - response, err := conn.DoRequest(nil, http.MethodPost, "containers/%s/wait", nil, nameOrID) + params := url.Values{} + if condition != nil { + params.Set("condition", *condition) + } + response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/wait", params, nameOrID) if err != nil { return exitCode, err } diff --git a/pkg/bindings/test/containers_test.go b/pkg/bindings/test/containers_test.go index 6756e81c7..e875fb2f8 100644 --- a/pkg/bindings/test/containers_test.go +++ b/pkg/bindings/test/containers_test.go @@ -250,4 +250,61 @@ var _ = Describe("Podman containers ", func() { Expect(data.State.Status).To(Equal("exited")) }) + It("podman wait no condition", func() { + var ( + name = "top" + exitCode int32 = -1 + ) + _, err := containers.Wait(connText, "foobar", nil) + Expect(err).ToNot(BeNil()) + code, _ := bindings.CheckResponseCode(err) + Expect(code).To(BeNumerically("==", http.StatusNotFound)) + + errChan := make(chan error) + bt.RunTopContainer(&name, nil, nil) + go func() { + exitCode, err = containers.Wait(connText, name, nil) + errChan <- err + close(errChan) + }() + err = containers.Stop(connText, name, nil) + Expect(err).To(BeNil()) + wait := <-errChan + Expect(wait).To(BeNil()) + Expect(exitCode).To(BeNumerically("==", 143)) + }) + + It("podman wait to pause|unpause condition", func() { + var ( + name = "top" + exitCode int32 = -1 + pause = "paused" + unpause = "running" + ) + errChan := make(chan error) + bt.RunTopContainer(&name, nil, nil) + go func() { + exitCode, err = containers.Wait(connText, name, &pause) + errChan <- err + close(errChan) + }() + err := containers.Pause(connText, name) + Expect(err).To(BeNil()) + wait := <-errChan + Expect(wait).To(BeNil()) + Expect(exitCode).To(BeNumerically("==", -1)) + + errChan = make(chan error) + go func() { + exitCode, err = containers.Wait(connText, name, &unpause) + errChan <- err + close(errChan) + }() + err = containers.Unpause(connText, name) + Expect(err).To(BeNil()) + unPausewait := <-errChan + Expect(unPausewait).To(BeNil()) + Expect(exitCode).To(BeNumerically("==", -1)) + }) + }) |