aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrent Baude <bbaude@redhat.com>2020-02-27 10:59:53 -0600
committerBrent Baude <bbaude@redhat.com>2020-02-28 09:36:53 -0600
commit09048731000e73b44a0243a0339d8c122eb8a165 (patch)
treedb939805cbb41f4ddd2db610256ba2186a8367db
parentbaf27fa25eed668b5a73a1d7d4fe16214f1c260f (diff)
downloadpodman-09048731000e73b44a0243a0339d8c122eb8a165.tar.gz
podman-09048731000e73b44a0243a0339d8c122eb8a165.tar.bz2
podman-09048731000e73b44a0243a0339d8c122eb8a165.zip
rework apiv2 wait endpoint|binding
added the ability to wait on a condition (stopped, running, paused...) for a container. if a condition is not provided, wait will default to the stopped condition which uses the original wait code paths. if the condition is stopped, the container exit code will be returned. also, correct a mux issue we discovered. Signed-off-by: Brent Baude <bbaude@redhat.com>
-rw-r--r--libpod/container_api.go20
-rw-r--r--pkg/api/handlers/containers.go3
-rw-r--r--pkg/api/handlers/containers_attach.go13
-rw-r--r--pkg/api/handlers/generic/containers.go11
-rw-r--r--pkg/api/handlers/images.go4
-rw-r--r--pkg/api/handlers/images_build.go19
-rw-r--r--pkg/api/handlers/libpod/containers.go1
-rw-r--r--pkg/api/handlers/libpod/pods.go8
-rw-r--r--pkg/api/handlers/utils/containers.go38
-rw-r--r--pkg/api/handlers/utils/images.go3
-rw-r--r--pkg/api/server/register_containers.go31
-rw-r--r--pkg/bindings/containers/containers.go13
-rw-r--r--pkg/bindings/test/containers_test.go57
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))
+ })
+
})