Implement podman-remote wait command and container subcommand

Signed-off-by: Jhon Honce <jhonce@redhat.com>
This commit is contained in:
Jhon Honce 2019-03-04 17:21:23 -07:00
parent c6c0b54c36
commit 8a6758d5fd
8 changed files with 86 additions and 44 deletions

10
API.md
View File

@ -143,7 +143,7 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in
[func VolumesPrune() []string, []string](#VolumesPrune) [func VolumesPrune() []string, []string](#VolumesPrune)
[func WaitContainer(name: string) int](#WaitContainer) [func WaitContainer(name: string, interval: int) int](#WaitContainer)
[type BuildInfo](#BuildInfo) [type BuildInfo](#BuildInfo)
@ -1013,10 +1013,10 @@ VolumesPrune removes unused volumes on the host
### <a name="WaitContainer"></a>func WaitContainer ### <a name="WaitContainer"></a>func WaitContainer
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> <div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
method WaitContainer(name: [string](https://godoc.org/builtin#string)) [int](https://godoc.org/builtin#int)</div> method WaitContainer(name: [string](https://godoc.org/builtin#string), interval: [int](https://godoc.org/builtin#int)) [int](https://godoc.org/builtin#int)</div>
WaitContainer takes the name or ID of a container and waits until the container stops. Upon stopping, the return WaitContainer takes the name or ID of a container and waits the given interval in milliseconds until the container
code of the container is returned. If the container container cannot be found by ID or name, stops. Upon stopping, the return code of the container is returned. If the container container cannot be found by ID
a [ContainerNotFound](#ContainerNotFound) error is returned. or name, a [ContainerNotFound](#ContainerNotFound) error is returned.
## Types ## Types
### <a name="BuildInfo"></a>type BuildInfo ### <a name="BuildInfo"></a>type BuildInfo

View File

@ -35,7 +35,6 @@ func getMainCommands() []*cobra.Command {
_topCommand, _topCommand,
_umountCommand, _umountCommand,
_unpauseCommand, _unpauseCommand,
_waitCommand,
} }
if len(_varlinkCommand.Use) > 0 { if len(_varlinkCommand.Use) > 0 {

View File

@ -52,6 +52,7 @@ var mainCommands = []*cobra.Command{
_stopCommand, _stopCommand,
_tagCommand, _tagCommand,
_versionCommand, _versionCommand,
_waitCommand,
imageCommand.Command, imageCommand.Command,
systemCommand.Command, systemCommand.Command,
} }

View File

@ -617,10 +617,10 @@ method UnpauseContainer(name: string) -> (container: string)
# ~~~ # ~~~
method GetAttachSockets(name: string) -> (sockets: Sockets) method GetAttachSockets(name: string) -> (sockets: Sockets)
# WaitContainer takes the name or ID of a container and waits until the container stops. Upon stopping, the return # WaitContainer takes the name or ID of a container and waits the given interval in milliseconds until the container
# code of the container is returned. If the container container cannot be found by ID or name, # stops. Upon stopping, the return code of the container is returned. If the container container cannot be found by ID
# a [ContainerNotFound](#ContainerNotFound) error is returned. # or name, a [ContainerNotFound](#ContainerNotFound) error is returned.
method WaitContainer(name: string) -> (exitcode: int) method WaitContainer(name: string, interval: int) -> (exitcode: int)
# RemoveContainer requires the name or ID of container as well a boolean representing whether a running container can be stopped and removed, and a boolean # RemoveContainer requires the name or ID of container as well a boolean representing whether a running container can be stopped and removed, and a boolean
# indicating whether to remove builtin volumes. Upon successful removal of the # indicating whether to remove builtin volumes. Upon successful removal of the

View File

@ -2,11 +2,11 @@ package main
import ( import (
"fmt" "fmt"
"os" "reflect"
"time" "time"
"github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/pkg/adapter"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -49,43 +49,36 @@ func waitCmd(c *cliconfig.WaitValues) error {
return errors.Errorf("you must provide at least one container name or id") return errors.Errorf("you must provide at least one container name or id")
} }
runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) if c.Interval == 0 {
return errors.Errorf("interval must be greater then 0")
}
interval := time.Duration(c.Interval) * time.Millisecond
runtime, err := adapter.GetRuntime(&c.PodmanCommand)
if err != nil { if err != nil {
return errors.Wrapf(err, "error creating libpod runtime") return errors.Wrapf(err, "error creating runtime")
} }
defer runtime.Shutdown(false) defer runtime.Shutdown(false)
ok, failures, err := runtime.WaitOnContainers(getContext(), c, interval)
if err != nil { if err != nil {
return errors.Wrapf(err, "could not get config") return err
} }
var lastError error for _, id := range ok {
if c.Latest { fmt.Println(id)
latestCtr, err := runtime.GetLatestContainer()
if err != nil {
return errors.Wrapf(err, "unable to wait on latest container")
}
args = append(args, latestCtr.ID())
} }
for _, container := range args { if len(failures) > 0 {
ctr, err := runtime.LookupContainer(container) keys := reflect.ValueOf(failures).MapKeys()
if err != nil { lastKey := keys[len(keys)-1].String()
return errors.Wrapf(err, "unable to find container %s", container) lastErr := failures[lastKey]
} delete(failures, lastKey)
if c.Interval == 0 {
return errors.Errorf("interval must be greater then 0")
}
returnCode, err := ctr.WaitWithInterval(time.Duration(c.Interval) * time.Millisecond)
if err != nil {
if lastError != nil {
fmt.Fprintln(os.Stderr, lastError)
}
lastError = errors.Wrapf(err, "failed to wait for the container %v", container)
} else {
fmt.Println(returnCode)
}
}
return lastError for _, err := range failures {
outputError(err)
}
return lastErr
}
return nil
} }

View File

@ -4,7 +4,9 @@ package adapter
import ( import (
"context" "context"
"strconv"
"syscall" "syscall"
"time"
"github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod"
@ -103,3 +105,25 @@ func (r *LocalRuntime) KillContainers(ctx context.Context, cli *cliconfig.KillVa
} }
return ok, failures, nil return ok, failures, nil
} }
// WaitOnContainers waits for all given container(s) to stop
func (r *LocalRuntime) WaitOnContainers(ctx context.Context, cli *cliconfig.WaitValues, interval time.Duration) ([]string, map[string]error, error) {
var (
ok = []string{}
failures = map[string]error{}
)
ctrs, err := shortcuts.GetContainersByContext(false, cli.Latest, cli.InputArgs, r.Runtime)
if err != nil {
return ok, failures, err
}
for _, c := range ctrs {
if returnCode, err := c.WaitWithInterval(interval); err == nil {
ok = append(ok, strconv.Itoa(int(returnCode)))
} else {
failures[c.ID()] = err
}
}
return ok, failures, err
}

View File

@ -6,7 +6,9 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"errors" "errors"
"strconv"
"syscall" "syscall"
"time"
"github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/cmd/podman/shared"
@ -173,6 +175,30 @@ func (r *LocalRuntime) KillContainers(ctx context.Context, cli *cliconfig.KillVa
return ok, failures, nil return ok, failures, nil
} }
// WaitOnContainers waits for all given container(s) to stop.
// interval is currently ignored.
func (r *LocalRuntime) WaitOnContainers(ctx context.Context, cli *cliconfig.WaitValues, interval time.Duration) ([]string, map[string]error, error) {
var (
ok = []string{}
failures = map[string]error{}
)
ids, err := iopodman.GetContainersByContext().Call(r.Conn, false, cli.Latest, cli.InputArgs)
if err != nil {
return ok, failures, err
}
for _, id := range ids {
stopped, err := iopodman.WaitContainer().Call(r.Conn, id, int64(interval))
if err != nil {
failures[id] = err
} else {
ok = append(ok, strconv.FormatInt(stopped, 10))
}
}
return ok, failures, nil
}
// BatchContainerOp is wrapper func to mimic shared's function with a similar name meant for libpod // BatchContainerOp is wrapper func to mimic shared's function with a similar name meant for libpod
func BatchContainerOp(ctr *Container, opts shared.PsOptions) (shared.BatchContainerStruct, error) { func BatchContainerOp(ctr *Container, opts shared.PsOptions) (shared.BatchContainerStruct, error) {
// TODO If pod ps ever shows container's sizes, re-enable this code; otherwise it isn't needed // TODO If pod ps ever shows container's sizes, re-enable this code; otherwise it isn't needed

View File

@ -360,17 +360,16 @@ func (i *LibpodAPI) UnpauseContainer(call iopodman.VarlinkCall, name string) err
} }
// WaitContainer ... // WaitContainer ...
func (i *LibpodAPI) WaitContainer(call iopodman.VarlinkCall, name string) error { func (i *LibpodAPI) WaitContainer(call iopodman.VarlinkCall, name string, interval int64) error {
ctr, err := i.Runtime.LookupContainer(name) ctr, err := i.Runtime.LookupContainer(name)
if err != nil { if err != nil {
return call.ReplyContainerNotFound(name, err.Error()) return call.ReplyContainerNotFound(name, err.Error())
} }
exitCode, err := ctr.Wait() exitCode, err := ctr.WaitWithInterval(time.Duration(interval))
if err != nil { if err != nil {
return call.ReplyErrorOccurred(err.Error()) return call.ReplyErrorOccurred(err.Error())
} }
return call.ReplyWaitContainer(int64(exitCode)) return call.ReplyWaitContainer(int64(exitCode))
} }
// RemoveContainer ... // RemoveContainer ...