Add kpod wait

Waits on one or more containers to stop and prints the container's
return code

Signed-off-by: baude <bbaude@redhat.com>
This commit is contained in:
baude 2017-09-19 12:45:08 -05:00
parent 214adee0ef
commit 3bf23b684a
12 changed files with 340 additions and 1 deletions

View file

@ -8,7 +8,7 @@ services:
before_install: before_install:
- sudo apt-get -qq update - sudo apt-get -qq update
- sudo apt-get -qq install btrfs-tools libdevmapper-dev libgpgme11-dev libapparmor-dev libseccomp-dev - sudo apt-get -qq install btrfs-tools libdevmapper-dev libgpgme11-dev libapparmor-dev libseccomp-dev
- sudo apt-get -qq install autoconf automake bison e2fslibs-dev libfuse-dev libtool liblzma-dev - sudo apt-get -qq install autoconf automake bison e2fslibs-dev libfuse-dev libtool liblzma-dev gettext
install: install:
- make install.tools - make install.tools

View file

@ -12,6 +12,7 @@ RUN apt-get update && apt-get install -y \
curl \ curl \
e2fslibs-dev \ e2fslibs-dev \
gawk \ gawk \
gettext \
iptables \ iptables \
pkg-config \ pkg-config \
libaio-dev \ libaio-dev \

View file

@ -65,6 +65,7 @@ It is currently in active development in the Kubernetes community through the [d
| [kpod-umount(1)](/docs/kpod-umount.1.md) | Unmount a working container's root filesystem || | [kpod-umount(1)](/docs/kpod-umount.1.md) | Unmount a working container's root filesystem ||
| [kpod-unpause(1)](/docs/kpod-unpause.1.md) | Unpause one or more running containers || | [kpod-unpause(1)](/docs/kpod-unpause.1.md) | Unpause one or more running containers ||
| [kpod-version(1)](/docs/kpod-version.1.md) | Display the version information |[![...](/docs/play.png)](https://asciinema.org/a/mfrn61pjZT9Fc8L4NbfdSqfgu)| | [kpod-version(1)](/docs/kpod-version.1.md) | Display the version information |[![...](/docs/play.png)](https://asciinema.org/a/mfrn61pjZT9Fc8L4NbfdSqfgu)|
| [kpod-wait(1)](/docs/kpod-wait.1.md) | Wait on one or more containers to stop and print their exit codes||
## Configuration ## Configuration
| File | Description | | File | Description |

View file

@ -54,6 +54,7 @@ func main() {
umountCommand, umountCommand,
unpauseCommand, unpauseCommand,
versionCommand, versionCommand,
waitCommand,
} }
app.Before = func(c *cli.Context) error { app.Before = func(c *cli.Context) error {
logrus.SetLevel(logrus.ErrorLevel) logrus.SetLevel(logrus.ErrorLevel)

62
cmd/kpod/wait.go Normal file
View file

@ -0,0 +1,62 @@
package main
import (
"fmt"
"os"
"github.com/kubernetes-incubator/cri-o/libkpod"
"github.com/pkg/errors"
"github.com/urfave/cli"
)
var (
waitDescription = `
kpod wait
Block until one or more containers stop and then print their exit codes
`
waitCommand = cli.Command{
Name: "wait",
Usage: "Block on one or more containers",
Description: waitDescription,
Action: waitCmd,
ArgsUsage: "CONTAINER-NAME [CONTAINER-NAME ...]",
}
)
func waitCmd(c *cli.Context) error {
args := c.Args()
if len(args) < 1 {
return errors.Errorf("you must provide at least one container name or id")
}
config, err := getConfig(c)
if err != nil {
return errors.Wrapf(err, "could not get config")
}
server, err := libkpod.New(config)
if err != nil {
return errors.Wrapf(err, "could not get container server")
}
defer server.Shutdown()
err = server.Update()
if err != nil {
return errors.Wrapf(err, "could not update list of containers")
}
var lastError error
for _, container := range c.Args() {
returnCode, err := server.ContainerWait(container)
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
}

View file

@ -388,6 +388,11 @@ _kpod_unpause() {
" "
local boolean_options="" local boolean_options=""
_complete_ "$options_with_args" "$boolean_options" _complete_ "$options_with_args" "$boolean_options"
_kpod_wait() {
local options_with_args=""
local boolean_options="--help -h"
_complete_ "$options_with_args" "$boolean_options"
} }
_complete_() { _complete_() {
@ -455,6 +460,7 @@ _kpod_kpod() {
unmount unmount
unpause unpause
version version
wait
" "
case "$prev" in case "$prev" in

36
docs/kpod-wait.1.md Normal file
View file

@ -0,0 +1,36 @@
% kpod(1) kpod-wait - Waits on a container
% Brent Baude
# kpod-wait "1" "September 2017" "kpod"
## NAME
kpod wait - Waits on one or more containers to stop and prints exit code
## SYNOPSIS
**kpod wait**
[**--help**|**-h**]
## DESCRIPTION
Waits on one or more containers to stop. The container can be referred to by its
name or ID. In the case of multiple containers, kpod will wait on each consecutively.
After the container stops, the container's return code is printed.
**kpod [GLOBAL OPTIONS] wait **
## GLOBAL OPTIONS
**--help, -h**
Print usage statement
## EXAMPLES
kpod wait mywebserver
kpod wait 860a4b23
kpod wait mywebserver myftpserver
## SEE ALSO
kpod(1), crio(8), crio.conf(5)
## HISTORY
September 2017, Originally compiled by Brent Baude<bbaude@redhat.com>

42
libkpod/wait.go Normal file
View file

@ -0,0 +1,42 @@
package libkpod
import (
"github.com/kubernetes-incubator/cri-o/oci"
"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/util/wait"
)
func isStopped(c *ContainerServer, ctr *oci.Container) bool {
c.runtime.UpdateStatus(ctr)
cStatus := c.runtime.ContainerStatus(ctr)
if cStatus.Status == oci.ContainerStateStopped {
return true
}
return false
}
// ContainerWait stops a running container with a grace period (i.e., timeout).
func (c *ContainerServer) ContainerWait(container string) (int32, error) {
ctr, err := c.LookupContainer(container)
if err != nil {
return 0, errors.Wrapf(err, "failed to find container %s", container)
}
err = wait.PollImmediateInfinite(1,
func() (bool, error) {
if !isStopped(c, ctr) {
return false, nil
} else { // nolint
return true, nil // nolint
} // nolint
},
)
if err != nil {
return 0, err
}
exitCode := ctr.State().ExitCode
c.ContainerStateToDisk(ctr)
return exitCode, nil
}

70
test/kpod_wait.bats Normal file
View file

@ -0,0 +1,70 @@
#!/usr/bin/env bats
load helpers
IMAGE="redis:alpine"
ROOT="$TESTDIR/crio"
RUNROOT="$TESTDIR/crio-run"
KPOD_OPTIONS="--root $ROOT --runroot $RUNROOT ${STORAGE_OPTS}"
# Returns the POD ID
function pod_run_from_template(){
#1=name, 2=uid, 3=namespace) {
NAME=$1 CUID=$2 NAMESPACE=$3 envsubst < ${TESTDATA}/template_sandbox_config.json > ${TESTDIR}/pod-${1}.json
crioctl pod run --config ${TESTDIR}/pod-${1}.json
}
# Returns the container ID
function container_create_from_template() {
#1=name, 2=image, 3=command, 4=id) {
NAME=$1 IMAGE=$2 COMMAND=$3 envsubst < ${TESTDATA}/template_container_config.json > ${TESTDIR}/ctr-${1}.json
crioctl ctr create --config ${TESTDIR}/ctr-${1}.json --pod "$4"
}
function container_start() {
#1=id
crioctl ctr start --id "$1"
}
@test "wait on a bogus container" {
start_crio
run ${KPOD_BINARY} ${KPOD_OPTIONS} wait 12343
echo $output
[ "$status" -eq 1 ]
stop_crio
}
@test "wait on a stopped container" {
run ${KPOD_BINARY} ${KPOD_OPTIONS} pull docker.io/library/busybox:latest
echo $output
start_crio
pod_id=$( pod_run_from_template "test" "test" "test1-1" )
echo $pod_id
ctr_id=$(container_create_from_template "test-CTR" "docker.io/library/busybox:latest" '["ls"]' "${pod_id}")
echo $ctr_id
container_start $ctr_id
run ${KPOD_BINARY} ${KPOD_OPTIONS} wait $ctr_id
[ "$status" -eq 0 ]
cleanup_ctrs
cleanup_pods
stop_crio
}
@test "wait on a sleeping container" {
run ${KPOD_BINARY} ${KPOD_OPTIONS} pull docker.io/library/busybox:latest
echo $output
start_crio
pod_id=$( pod_run_from_template "test" "test" "test1-1" )
echo $pod_id
ctr_id=$(container_create_from_template "test-CTR" "docker.io/library/busybox:latest" '["sleep", "5"]' "${pod_id}")
echo $ctr_id
run container_start $ctr_id
echo $output
run ${KPOD_BINARY} ${KPOD_OPTIONS} wait $ctr_id
echo $output
[ "$status" -eq 0 ]
cleanup_ctrs
cleanup_pods
stop_crio
}

View file

@ -0,0 +1,68 @@
{
"metadata": {
"name": "${NAME}",
"attempt": 1
},
"image": {
"image": "${IMAGE}"
},
"command": ${COMMAND},
"args": [],
"working_dir": "/",
"envs": [
{
"key": "PATH",
"value": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
},
{
"key": "TERM",
"value": "xterm"
},
{
"key": "TESTDIR",
"value": "test/dir1"
},
{
"key": "TESTFILE",
"value": "test/file1"
}
],
"labels": {
"type": "small",
"batch": "no"
},
"annotations": {
"owner": "dragon",
"daemon": "crio"
},
"privileged": true,
"log_path": "",
"stdin": false,
"stdin_once": false,
"tty": false,
"linux": {
"resources": {
"cpu_period": 10000,
"cpu_quota": 20000,
"cpu_shares": 512,
"oom_score_adj": 30
},
"security_context": {
"readonly_rootfs": false,
"selinux_options": {
"user": "system_u",
"role": "system_r",
"type": "svirt_lxc_net_t",
"level": "s0:c4,c5"
},
"capabilities": {
"add_capabilities": [
"setuid",
"setgid"
],
"drop_capabilities": [
]
}
}
}
}

View file

@ -0,0 +1,51 @@
{
"metadata": {
"name": "${NAME}",
"uid": "${CUID}",
"namespace": "${NAMESPACE}",
"attempt": 1
},
"hostname": "crioctl_host",
"log_directory": "",
"dns_config": {
"searches": [
"8.8.8.8"
]
},
"port_mappings": [],
"resources": {
"cpu": {
"limits": 3,
"requests": 2
},
"memory": {
"limits": 50000000,
"requests": 2000000
}
},
"labels": {
"group": "test"
},
"annotations": {
"owner": "hmeng",
"security.alpha.kubernetes.io/sysctls": "kernel.shm_rmid_forced=1,net.ipv4.ip_local_port_range=1024 65000",
"security.alpha.kubernetes.io/unsafe-sysctls": "kernel.msgmax=8192" ,
"security.alpha.kubernetes.io/seccomp/pod": "unconfined"
},
"linux": {
"cgroup_parent": "/Burstable/pod_123-456",
"security_context": {
"namespace_options": {
"host_network": false,
"host_pid": false,
"host_ipc": false
},
"selinux_options": {
"user": "system_u",
"role": "system_r",
"type": "svirt_lxc_net_t",
"level": "s0:c4,c5"
}
}
}
}

View file

@ -58,5 +58,6 @@ There are other equivalents for these tools
| `docker stop` | [`kpod stop`](./docs/kpod-stop.1.md) | | `docker stop` | [`kpod stop`](./docs/kpod-stop.1.md) |
| `docker unpause`| [`kpod unpause`](./docs/kpod-unpause.1.md)| | `docker unpause`| [`kpod unpause`](./docs/kpod-unpause.1.md)|
| `docker version`| [`kpod version`](./docs/kpod-version.1.md)| | `docker version`| [`kpod version`](./docs/kpod-version.1.md)|
| `docker wait` | [`kpod wait`](./docs/kpod-wait.1.md)|
*** Use mount to take advantage of the entire linux tool chain rather then just cp. Read [`here`](./docs/kpod-cp.1.md) for more information. *** Use mount to take advantage of the entire linux tool chain rather then just cp. Read [`here`](./docs/kpod-cp.1.md) for more information.