mirror of
https://github.com/containers/podman.git
synced 2025-10-17 19:24:04 +08:00
Merge pull request #5347 from baude/apiv2wait
rework apiv2 wait endpoint|binding
This commit is contained in:
@ -624,6 +624,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 {
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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")
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)))
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
}
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -250,4 +250,61 @@ var _ = Describe("Podman containers ", func() {
|
||||
Expect(isStopped(data.State.Status)).To(BeTrue())
|
||||
})
|
||||
|
||||
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))
|
||||
})
|
||||
|
||||
})
|
||||
|
Reference in New Issue
Block a user