diff --git a/README.md b/README.md index bff46ef1..938dbf38 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,7 @@ It is currently in active development in the Kubernetes community through the [d | [kpod-logs(1)](/docs/kpod-logs.1.md) | Display the logs of a container || | [kpod-mount(1)](/docs/kpod-mount.1.md) | Mount a working container's root filesystem || | [kpod-ps(1)](/docs/kpod-ps.1.md) | Prints out information about containers |[![...](/docs/play.png)](https://asciinema.org/a/bbT41kac6CwZ5giESmZLIaTLR)| +| [kpod-pause(1)](/docs/kpod-pause.1.md) | Pause one or more running containers || | [kpod-pull(1)](/docs/kpod-pull.1.md) | Pull an image from a registry |[![...](/docs/play.png)](https://asciinema.org/a/lr4zfoynHJOUNu1KaXa1dwG2X)| | [kpod-push(1)](/docs/kpod-push.1.md) | Push an image to a specified destination |[![...](/docs/play.png)](https://asciinema.org/a/133276)| | [kpod-rename(1)](/docs/kpod-rename.1.md) | Rename a container || @@ -59,9 +60,10 @@ It is currently in active development in the Kubernetes community through the [d | [kpod-rmi(1)](/docs/kpod-rmi.1.md) | Removes one or more images |[![...](/docs/play.png)](https://asciinema.org/a/133799)| | [kpod-save(1)](/docs/kpod-save.1.md) | Saves an image to an archive |[![...](/docs/play.png)](https://asciinema.org/a/kp8kOaexEhEa20P1KLZ3L5X4g)| | [kpod-stats(1)](/docs/kpod-stats.1.md) | Display a live stream of one or more containers' resource usage statistics|| -| [kpod-stop(1)](/docs/kpod-stop.1.md) | Stops one or more running containers.|| +| [kpod-stop(1)](/docs/kpod-stop.1.md) | Stops one or more running containers || | [kpod-tag(1)](/docs/kpod-tag.1.md) | Add an additional name to a local image |[![...](/docs/play.png)](https://asciinema.org/a/133803)| | [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-version(1)](/docs/kpod-version.1.md) | Display the version information |[![...](/docs/play.png)](https://asciinema.org/a/mfrn61pjZT9Fc8L4NbfdSqfgu)| ## Configuration diff --git a/cmd/kpod/main.go b/cmd/kpod/main.go index 13f71cd2..75c32d6b 100644 --- a/cmd/kpod/main.go +++ b/cmd/kpod/main.go @@ -40,6 +40,7 @@ func main() { loadCommand, logsCommand, mountCommand, + pauseCommand, psCommand, pullCommand, pushCommand, @@ -51,6 +52,7 @@ func main() { stopCommand, tagCommand, umountCommand, + unpauseCommand, versionCommand, } app.Before = func(c *cli.Context) error { diff --git a/cmd/kpod/pause.go b/cmd/kpod/pause.go new file mode 100644 index 00000000..2f15b09d --- /dev/null +++ b/cmd/kpod/pause.go @@ -0,0 +1,59 @@ +package main + +import ( + "fmt" + "github.com/kubernetes-incubator/cri-o/libkpod" + "github.com/pkg/errors" + "github.com/urfave/cli" + "os" +) + +var ( + pauseDescription = ` + kpod pause + + Pauses one or more running containers. The container name or ID can be used. +` + pauseCommand = cli.Command{ + Name: "pause", + Usage: "Pause one or more containers", + Description: pauseDescription, + Action: pauseCmd, + ArgsUsage: "CONTAINER-NAME [CONTAINER-NAME ...]", + } +) + +func pauseCmd(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() { + cid, err := server.ContainerPause(container) + if err != nil { + if lastError != nil { + fmt.Fprintln(os.Stderr, lastError) + } + lastError = errors.Wrapf(err, "failed to pause container %v", container) + } else { + fmt.Println(cid) + } + } + + return lastError +} diff --git a/cmd/kpod/unpause.go b/cmd/kpod/unpause.go new file mode 100644 index 00000000..8b3c6167 --- /dev/null +++ b/cmd/kpod/unpause.go @@ -0,0 +1,59 @@ +package main + +import ( + "fmt" + "github.com/kubernetes-incubator/cri-o/libkpod" + "github.com/pkg/errors" + "github.com/urfave/cli" + "os" +) + +var ( + unpauseDescription = ` + kpod unpause + + Unpauses one or more running containers. The container name or ID can be used. +` + unpauseCommand = cli.Command{ + Name: "unpause", + Usage: "Unpause one or more containers", + Description: unpauseDescription, + Action: unpauseCmd, + ArgsUsage: "CONTAINER-NAME [CONTAINER-NAME ...]", + } +) + +func unpauseCmd(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() { + cid, err := server.ContainerUnpause(container) + if err != nil { + if lastError != nil { + fmt.Fprintln(os.Stderr, lastError) + } + lastError = errors.Wrapf(err, "failed to unpause container %v", container) + } else { + fmt.Println(cid) + } + } + + return lastError +} diff --git a/completions/bash/kpod b/completions/bash/kpod index 07022bc9..e6a95519 100644 --- a/completions/bash/kpod +++ b/completions/bash/kpod @@ -349,6 +349,14 @@ _kpod_export() { _complete_ "$options_with_args" "$boolean_options" } +_kpod_pause() { + local options_with_args=" + --help -h + " + local boolean_options="" + _complete_ "$options_with_args" "$boolean_options" +} + _kpod_ps() { local options_with_args=" --filter -f @@ -374,6 +382,14 @@ _kpod_stop() { _complete_ "$options_with_args" "$boolean_options" } +_kpod_unpause() { + local options_with_args=" + --help -h + " + local boolean_options="" + _complete_ "$options_with_args" "$boolean_options" +} + _complete_() { local options_with_args=$1 local boolean_options="$2 -h --help" @@ -424,6 +440,7 @@ _kpod_kpod() { load logs mount + pause ps pull push @@ -436,6 +453,7 @@ _kpod_kpod() { tag umount unmount + unpause version " diff --git a/docs/kpod-pause.1.md b/docs/kpod-pause.1.md new file mode 100644 index 00000000..e3328d7a --- /dev/null +++ b/docs/kpod-pause.1.md @@ -0,0 +1,24 @@ +% kpod(1) kpod-pause - Pause one or more containers +% Dan Walsh +# kpod-pause "1" "September 2017" "kpod" + +## NAME +kpod pause - Pause one or more containers + +## SYNOPSIS +**kpod pause [OPTIONS] CONTAINER [...]** + +## DESCRIPTION +Pauses one or more containers. You may use container IDs or names as input. + +## EXAMPLE + +kpod pause mywebserver + +kpod pause 860a4b23 + +## SEE ALSO +kpod(1), kpod-unpause(1) + +## HISTORY +September 2017, Originally compiled by Dan Walsh diff --git a/docs/kpod-unpause.1.md b/docs/kpod-unpause.1.md new file mode 100644 index 00000000..b64323af --- /dev/null +++ b/docs/kpod-unpause.1.md @@ -0,0 +1,24 @@ +% kpod(1) kpod-unpause - Unpause one or more containers +% Dan Walsh +# kpod-unpause "1" "September 2017" "kpod" + +## NAME +kpod unpause - Unpause one or more containers + +## SYNOPSIS +**kpod unpause [OPTIONS] CONTAINER [...]** + +## DESCRIPTION +Unpauses one or more containers. You may use container IDs or names as input. + +## EXAMPLE + +kpod unpause mywebserver + +kpod unpause 860a4b23 + +## SEE ALSO +kpod(1), kpod-pause(1) + +## HISTORY +September 2017, Originally compiled by Dan Walsh diff --git a/libkpod/pause.go b/libkpod/pause.go new file mode 100644 index 00000000..23a3e0c7 --- /dev/null +++ b/libkpod/pause.go @@ -0,0 +1,44 @@ +package libkpod + +import ( + "github.com/kubernetes-incubator/cri-o/oci" + "github.com/pkg/errors" +) + +// ContainerPause pauses a running container. +func (c *ContainerServer) ContainerPause(container string) (string, error) { + ctr, err := c.LookupContainer(container) + if err != nil { + return "", errors.Wrapf(err, "failed to find container %s", container) + } + + cStatus := c.runtime.ContainerStatus(ctr) + if cStatus.Status != oci.ContainerStatePaused { + if err := c.runtime.PauseContainer(ctr); err != nil { + return "", errors.Wrapf(err, "failed to pause container %s", ctr.ID()) + } + } + + c.ContainerStateToDisk(ctr) + + return ctr.ID(), nil +} + +// ContainerUnpause unpauses a running container with a grace period (i.e., timeout). +func (c *ContainerServer) ContainerUnpause(container string) (string, error) { + ctr, err := c.LookupContainer(container) + if err != nil { + return "", errors.Wrapf(err, "failed to find container %s", container) + } + + cStatus := c.runtime.ContainerStatus(ctr) + if cStatus.Status == oci.ContainerStatePaused { + if err := c.runtime.UnpauseContainer(ctr); err != nil { + return "", errors.Wrapf(err, "failed to unpause container %s", ctr.ID()) + } + } + + c.ContainerStateToDisk(ctr) + + return ctr.ID(), nil +} diff --git a/oci/oci.go b/oci/oci.go index 7f03ef0f..0a603092 100644 --- a/oci/oci.go +++ b/oci/oci.go @@ -24,6 +24,8 @@ import ( const ( // ContainerStateCreated represents the created state of a container ContainerStateCreated = "created" + // ContainerStatePaused represents the paused state of a container + ContainerStatePaused = "paused" // ContainerStateRunning represents the running state of a container ContainerStateRunning = "running" // ContainerStateStopped represents the stopped state of a container @@ -678,3 +680,23 @@ func (r *Runtime) RuntimeReady() (bool, error) { func (r *Runtime) NetworkReady() (bool, error) { return true, nil } + +// PauseContainer pauses a container. +func (r *Runtime) PauseContainer(c *Container) error { + c.opLock.Lock() + defer c.opLock.Unlock() + if _, err := utils.ExecCmd(r.Path(c), "pause", c.id); err != nil { + return err + } + return nil +} + +// UnpauseContainer unpauses a container. +func (r *Runtime) UnpauseContainer(c *Container) error { + c.opLock.Lock() + defer c.opLock.Unlock() + if _, err := utils.ExecCmd(r.Path(c), "unpause", c.id); err != nil { + return err + } + return nil +} diff --git a/test/kpod_pause.bats b/test/kpod_pause.bats new file mode 100644 index 00000000..6a9069a4 --- /dev/null +++ b/test/kpod_pause.bats @@ -0,0 +1,67 @@ +#!/usr/bin/env bats + +load helpers + +ROOT="$TESTDIR/crio" +RUNROOT="$TESTDIR/crio-run" +KPOD_OPTIONS="--root $ROOT --runroot $RUNROOT $STORAGE_OPTS" +function teardown() { + cleanup_test +} + +@test "pause a bogus container" { + run ${KPOD_BINARY} ${KPOD_OPTIONS} pause foobar + echo "$output" + [ "$status" -eq 1 ] +} + +@test "unpause a bogus container" { + run ${KPOD_BINARY} ${KPOD_OPTIONS} unpause foobar + echo "$output" + [ "$status" -eq 1 ] +} + +@test "pause a running container by id" { + start_crio + run crioctl pod run --config "$TESTDATA"/sandbox_config.json + echo "$output" + [ "$status" -eq 0 ] + pod_id="$output" + run crioctl ctr create --config "$TESTDATA"/container_config.json --pod "$pod_id" + echo "$output" + [ "$status" -eq 0 ] + ctr_id="$output" + run crioctl ctr start --id "$ctr_id" + echo "$output" + id="$output" + run ${KPOD_BINARY} ${KPOD_OPTIONS} pause "$id" + echo "$output" + [ "$status" -eq 0 ] + run ${KPOD_BINARY} ${KPOD_OPTIONS} unpause "$id" + echo "$output" + [ "$status" -eq 0 ] + cleanup_pods + pause_crio +} + +@test "pause a running container by name" { + start_crio + run crioctl pod run --config "$TESTDATA"/sandbox_config.json + echo "$output" + [ "$status" -eq 0 ] + pod_id="$output" + run crioctl ctr create --config "$TESTDATA"/container_config.json --pod "$pod_id" + echo "$output" + [ "$status" -eq 0 ] + ctr_id="$output" + run crioctl ctr start --id "$ctr_id" + echo "$output" + run ${KPOD_BINARY} ${KPOD_OPTIONS} pause "k8s_podsandbox1-redis_podsandbox1_redhat.test.crio_redhat-test-crio_0" + echo "$output" + [ "$status" -eq 0 ] + run ${KPOD_BINARY} ${KPOD_OPTIONS} unpause "k8s_podsandbox1-redis_podsandbox1_redhat.test.crio_redhat-test-crio_0" + echo "$output" + [ "$status" -eq 0 ] + cleanup_pods + pause_crio +} diff --git a/transfer.md b/transfer.md index fdf18cbd..c8b94228 100644 --- a/transfer.md +++ b/transfer.md @@ -46,6 +46,7 @@ There are other equivalents for these tools | `docker history`| [`kpod history`](./docs/kpod-history.1.md)| | `docker images` | [`kpod images`](./docs/kpod-images.1.md) | | `docker load` | [`kpod load`](./docs/kpod-load.1.md) | +| `docker pause` | [`kpod pause`](./docs/kpod-pause.1.md) | | `docker ps` | [`kpod ps`](./docs/kpod-ps.1.md) | | `docker pull` | [`kpod pull`](./docs/kpod-pull.1.md) | | `docker push` | [`kpod push`](./docs/kpod-push.1.md) | @@ -54,4 +55,6 @@ There are other equivalents for these tools | `docker rmi` | [`kpod rmi`](./docs/kpod-rmi.1.md) | | `docker save` | [`kpod save`](./docs/kpod-save.1.md) | | `docker tag` | [`kpod tag`](./docs/kpod-tag.1.md) | +| `docker stop` | [`kpod stop`](./docs/kpod-stop.1.md) | +| `docker unpause`| [`kpod unpause`](./docs/kpod-unpause.1.md)| | `docker version`| [`kpod version`](./docs/kpod-version.1.md)|