diff --git a/README.md b/README.md index 34b56727..db4a6021 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,7 @@ It is currently in active development in the Kubernetes community through the [d | [kpod(1)](/docs/kpod.1.md) | Simple management tool for pods and images || | [kpod-cp(1)](/docs/kpod-cp.1.md) | Instead of providing a `kpod cp` command, the man page `kpod-cp` describes how to use the `kpod mount` command to have even more flexibility and functionality.|| | [kpod-diff(1)](/docs/kpod-diff.1.md) | Inspect changes on a container or image's filesystem || +| [kpod-exec(1)](/docs/kpod-exec.1.md) | Execute a command in a running container | [kpod-export(1)](/docs/kpod-export.1.md) | Export container's filesystem contents as a tar archive || | [kpod-history(1)](/docs/kpod-history.1.md) | Shows the history of an image |[![...](/docs/play.png)](https://asciinema.org/a/bCvUQJ6DkxInMELZdc5DinNSx)| | [kpod-images(1)](/docs/kpod-images.1.md) | List images in local storage |[![...](/docs/play.png)](https://asciinema.org/a/133649)| diff --git a/cmd/kpod/exec.go b/cmd/kpod/exec.go new file mode 100644 index 00000000..7e0f41d5 --- /dev/null +++ b/cmd/kpod/exec.go @@ -0,0 +1,82 @@ +package main + +import ( + "github.com/kubernetes-incubator/cri-o/libkpod" + "github.com/pkg/errors" + "github.com/urfave/cli" +) + +var ( + execFlags = []cli.Flag{ + cli.BoolFlag{ + Name: "detach, d", + Usage: "detached mode: command will run in the background", + }, + cli.StringSliceFlag{ + Name: "env, e", + Usage: "set environment variables", + }, + cli.BoolFlag{ + Name: "tty, t", + Usage: "Allocate a pseudo TTY", + }, + cli.StringFlag{ + Name: "user, u", + Usage: "Sets the username (UID) and groupname (GID) for the specified command", + }, + } + execDescription = ` + kpod exec + + Run a command in a running container. The command will only run while the primary + PID in the container is running. +` + + execCommand = cli.Command{ + Name: "exec", + Usage: "Run a command in a running container", + Description: execDescription, + Flags: execFlags, + Action: execCmd, + ArgsUsage: "[OPTIONS} CONTAINER-NAME COMMAND", + } +) + +func execCmd(c *cli.Context) error { + args := c.Args() + detachFlag := c.Bool("detach") + envFlags := c.StringSlice("env") + ttyFlag := c.Bool("tty") + userFlag := c.String("user") + + if len(args) < 2 { + return errors.Errorf("you must provide a container name or ID and a command to run") + } + if err := validateFlags(c, execFlags); err != nil { + return err + } + container := args[0] + var command []string + for _, cmd := range args[1:] { + command = append(command, cmd) + } + + 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") + } + if err := server.ContainerExec(container, command, detachFlag, envFlags, ttyFlag, userFlag); err != nil { + return err + } + return nil + +} diff --git a/cmd/kpod/main.go b/cmd/kpod/main.go index 804f762a..0b6c45b8 100644 --- a/cmd/kpod/main.go +++ b/cmd/kpod/main.go @@ -32,6 +32,7 @@ func main() { app.Commands = []cli.Command{ diffCommand, + execCommand, exportCommand, historyCommand, imagesCommand, diff --git a/completions/bash/kpod b/completions/bash/kpod index a18c4f8d..d3ca7dd3 100644 --- a/completions/bash/kpod +++ b/completions/bash/kpod @@ -404,6 +404,12 @@ _kpod_wait() { _complete_ "$options_with_args" "$boolean_options" } +_kpod_exec() { + local options_with_args="--env -e --user -u" + local boolean_options="--help -h --tty -t --detach -d" + _complete_ "$options_with_args" "$boolean_options" +} + _complete_() { local options_with_args=$1 local boolean_options="$2 -h --help" @@ -446,6 +452,7 @@ _kpod_kpod() { " commands=" diff + exec export history images diff --git a/docs/kpod-exec.1.md b/docs/kpod-exec.1.md new file mode 100644 index 00000000..d1c55f3b --- /dev/null +++ b/docs/kpod-exec.1.md @@ -0,0 +1,48 @@ +% kpod(1) kpod-exec - Run a command in a running container +% Brent Baude +# kpod-exec "1" "October 2017" "kpod" + +## NAME +kpod exec - Run a command in a running container + +## SYNOPSIS +**kpod exec [OPTIONS] CONTAINER COMMAND + +## DESCRIPTION +Run a command in a running container. The command will only run while the container's primary process (PID 1) is running. +restarted if the container is restarted. + +## OPTIONS + +**--detach, -d** + +Detached mode: run the command in the background. + +**--env, -e** + +Set one or environment variables for the container environment. + +**--tty, -t** + +Allocate a pseudo-TTY. + +**--user, -u** + +Sets the username or UID used and optionally the groupname or GID for the specified command. The format is *uid:gid*. + + +## EXAMPLE + +kpod exec mywebserver ls + +kpod exec 860a4b23 ls + +kpod exec -e FOO=bar -e BAR=foo 860a4b23 ls + +kpod exec -u foo:users mywebserver ls + +## SEE ALSO +kpod(1) + +## HISTORY +October 2017, Originally compiled by Brent Baude diff --git a/docs/kpod-stop.1.md b/docs/kpod-stop.1.md index 52a35815..de1e7bd8 100644 --- a/docs/kpod-stop.1.md +++ b/docs/kpod-stop.1.md @@ -32,4 +32,4 @@ kpod stop --timeout 2 860a4b23 kpod(1), kpod-rm(1) ## HISTORY -September 2018, Originally compiled by Brent Baude +September 2017, Originally compiled by Brent Baude diff --git a/libkpod/exec.go b/libkpod/exec.go new file mode 100644 index 00000000..2676903e --- /dev/null +++ b/libkpod/exec.go @@ -0,0 +1,29 @@ +package libkpod + +import ( + "github.com/kubernetes-incubator/cri-o/oci" + "github.com/pkg/errors" +) + +// ContainerExec runs a command in a running container +func (c *ContainerServer) ContainerExec(container string, command []string, detach bool, environmentVars []string, tty bool, user string) error { + ctr, err := c.LookupContainer(container) + if err != nil { + return errors.Wrapf(err, "failed to find container %s", container) + } + ctrID := ctr.ID() + + cStatus := c.runtime.ContainerStatus(ctr) + switch cStatus.Status { + case oci.ContainerStatePaused: + return errors.Errorf("cannot run a command in a paused container %s", ctrID) + case oci.ContainerStateRunning: + if err := c.runtime.ExecContainerCommand(ctr, command, detach, environmentVars, tty, user); err != nil { + return errors.Wrapf(err, "failed to exec command in container %s", ctrID) + } + default: + return errors.Errorf("container %s is not running", ctrID) + } + + return nil +} diff --git a/oci/oci.go b/oci/oci.go index e9babb7f..b48f7294 100644 --- a/oci/oci.go +++ b/oci/oci.go @@ -716,3 +716,30 @@ func (r *Runtime) UnpauseContainer(c *Container) error { _, err := utils.ExecCmd(r.Path(c), "resume", c.id) return err } + +// ExecContainerCommand runs a command inside a running container +func (r *Runtime) ExecContainerCommand(c *Container, command []string, detach bool, environmentVars []string, tty bool, user string) error { + //var args []string + commandArgs := []string{"exec"} + if tty { + commandArgs = append(commandArgs, "-t") + } + if detach { + commandArgs = append(commandArgs, "-d") + } + for _, envVar := range environmentVars { + commandArgs = append(commandArgs, "-e", envVar) + } + if user != "" { + commandArgs = append(commandArgs, "-u", user) + } + commandArgs = append(commandArgs, c.id) + commandArgs = append(commandArgs, command...) + logrus.Debug("Running command: %s", strings.Join(commandArgs, " ")) + c.opLock.Lock() + defer c.opLock.Unlock() + err := utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, r.Path(c), commandArgs...) + return err + return nil + +} diff --git a/test/kpod_exec.bats b/test/kpod_exec.bats new file mode 100644 index 00000000..fd3958d9 --- /dev/null +++ b/test/kpod_exec.bats @@ -0,0 +1,78 @@ +#!/usr/bin/env bats + +load helpers + +IMAGE="redis:alpine" + +function teardown() { + cleanup_test +} + +@test "exec on a bogus container" { + run ${KPOD_BINARY} ${KPOD_OPTIONS} exec foobar ls + echo "$output" + [ "$status" -eq 1 ] +} + +@test "Run a simple command" { + start_crio + run crioctl pod run --config "$TESTDATA"/sandbox_config.json + echo "$output" + [ "$status" -eq 0 ] + pod_id="$output" + run crioctl image pull "$IMAGE" + [ "$status" -eq 0 ] + run crioctl ctr create --config "$TESTDATA"/container_redis.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} exec "$ctr_id" ls + echo "$output" + [ "$status" -eq 0 ] + cleanup_pods + stop_crio +} + +# Disabled until runc is fixed upstream to handle +# longer and more complicated command sequences. +#@test "Check for environment variable" { +# start_crio +# run crioctl pod run --config "$TESTDATA"/sandbox_config.json +# echo "$output" +# [ "$status" -eq 0 ] +# pod_id="$output" +# run crioctl image pull "$IMAGE" +# [ "$status" -eq 0 ] +# run crioctl ctr create --config "$TESTDATA"/container_redis.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} exec -e FOO=bar "$ctr_id" env | grep FOO +# echo "$output" +# [ "$status" -eq 0 ] +# cleanup_pods +# stop_crio +#} + +@test "Execute command in a non-running container" { + start_crio + run crioctl pod run --config "$TESTDATA"/sandbox_config.json + echo "$output" + [ "$status" -eq 0 ] + pod_id="$output" + run crioctl image pull "$IMAGE" + [ "$status" -eq 0 ] + run crioctl ctr create --config "$TESTDATA"/container_config.json --pod "$pod_id" + echo "$output" + [ "$status" -eq 0 ] + ctr_id="$output" + run ${KPOD_BINARY} ${KPOD_OPTIONS} exec ls + echo "$output" + [ "$status" -eq 1 ] + cleanup_pods + stop_crio +} diff --git a/transfer.md b/transfer.md index b6d23b47..f0fc5a6c 100644 --- a/transfer.md +++ b/transfer.md @@ -24,7 +24,6 @@ Following provides equivalent with cri-o tools for gathering information or jump | Existing Step | CRI-O (and friends) | | :---: | :---: | -| `docker exec` | [`crioctl ctr exec`](./docs/crio.8.md) | | `docker info` | [`kpod info`](./docs/kpod-info.1.md) | | `docker inspect` | [`kpod inspect`](./docs/kpod-inspect.1.md) | | `docker logs` | [`kpod logs`](./docs/kpod-logs.1.md) | @@ -42,6 +41,7 @@ There are other equivalents for these tools | `docker build` | [`buildah bud`](https://github.com/projectatomic/buildah/blob/master/docs/buildah-bud.md) | | `docker cp` | [`kpod mount`](./docs/kpod-cp.1.md) *** | | `docker diff` | [`kpod diff`](./docs/kpod-diff.1.md) | +| `docker exec` | [`kpod exec`](./docs/kpod-exec.1.md) | | `docker export` | [`kpod export`](./docs/kpod-export.1.md) | | `docker history`| [`kpod history`](./docs/kpod-history.1.md)| | `docker images` | [`kpod images`](./docs/kpod-images.1.md) |