Add kpod attach

Run a command in a running container

Signed-off-by: baude <bbaude@redhat.com>
This commit is contained in:
baude 2017-10-06 08:25:28 -05:00
parent 5b2652c3e3
commit c921dfc2f9
10 changed files with 275 additions and 2 deletions

View file

@ -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(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-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-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-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-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)| | [kpod-images(1)](/docs/kpod-images.1.md) | List images in local storage |[![...](/docs/play.png)](https://asciinema.org/a/133649)|

82
cmd/kpod/exec.go Normal file
View file

@ -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
}

View file

@ -32,6 +32,7 @@ func main() {
app.Commands = []cli.Command{ app.Commands = []cli.Command{
diffCommand, diffCommand,
execCommand,
exportCommand, exportCommand,
historyCommand, historyCommand,
imagesCommand, imagesCommand,

View file

@ -404,6 +404,12 @@ _kpod_wait() {
_complete_ "$options_with_args" "$boolean_options" _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_() { _complete_() {
local options_with_args=$1 local options_with_args=$1
local boolean_options="$2 -h --help" local boolean_options="$2 -h --help"
@ -446,6 +452,7 @@ _kpod_kpod() {
" "
commands=" commands="
diff diff
exec
export export
history history
images images

48
docs/kpod-exec.1.md Normal file
View file

@ -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 <bbaude@redhat.com>

View file

@ -32,4 +32,4 @@ kpod stop --timeout 2 860a4b23
kpod(1), kpod-rm(1) kpod(1), kpod-rm(1)
## HISTORY ## HISTORY
September 2018, Originally compiled by Brent Baude <bbaude@redhat.com> September 2017, Originally compiled by Brent Baude <bbaude@redhat.com>

29
libkpod/exec.go Normal file
View file

@ -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
}

View file

@ -716,3 +716,30 @@ func (r *Runtime) UnpauseContainer(c *Container) error {
_, err := utils.ExecCmd(r.Path(c), "resume", c.id) _, err := utils.ExecCmd(r.Path(c), "resume", c.id)
return err 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
}

78
test/kpod_exec.bats Normal file
View file

@ -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
}

View file

@ -24,7 +24,6 @@ Following provides equivalent with cri-o tools for gathering information or jump
| Existing Step | CRI-O (and friends) | | 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 info` | [`kpod info`](./docs/kpod-info.1.md) |
| `docker inspect` | [`kpod inspect`](./docs/kpod-inspect.1.md) | | `docker inspect` | [`kpod inspect`](./docs/kpod-inspect.1.md) |
| `docker logs` | [`kpod logs`](./docs/kpod-logs.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 build` | [`buildah bud`](https://github.com/projectatomic/buildah/blob/master/docs/buildah-bud.md) |
| `docker cp` | [`kpod mount`](./docs/kpod-cp.1.md) *** | | `docker cp` | [`kpod mount`](./docs/kpod-cp.1.md) *** |
| `docker diff` | [`kpod diff`](./docs/kpod-diff.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 export` | [`kpod export`](./docs/kpod-export.1.md) |
| `docker history`| [`kpod history`](./docs/kpod-history.1.md)| | `docker history`| [`kpod history`](./docs/kpod-history.1.md)|
| `docker images` | [`kpod images`](./docs/kpod-images.1.md) | | `docker images` | [`kpod images`](./docs/kpod-images.1.md) |