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:
parent
214adee0ef
commit
3bf23b684a
12 changed files with 340 additions and 1 deletions
|
@ -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
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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 |
|
||||||
|
|
|
@ -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
62
cmd/kpod/wait.go
Normal 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
|
||||||
|
}
|
|
@ -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
36
docs/kpod-wait.1.md
Normal 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
42
libkpod/wait.go
Normal 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
70
test/kpod_wait.bats
Normal 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
|
||||||
|
}
|
68
test/testdata/template_container_config.json
vendored
Normal file
68
test/testdata/template_container_config.json
vendored
Normal 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": [
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
51
test/testdata/template_sandbox_config.json
vendored
Normal file
51
test/testdata/template_sandbox_config.json
vendored
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.
|
||||||
|
|
Loading…
Reference in a new issue