From c88bc13b077ee2e3d8d3dab02044a1c4d3a3d43a Mon Sep 17 00:00:00 2001 From: Daniel J Walsh Date: Wed, 30 Aug 2017 16:03:26 -0400 Subject: [PATCH] Implement kpod rm Kpod rm removes a container from the system Signed-off-by: Ryan Cole Signed-off-by: Daniel J Walsh Signed-off-by: umohnani8 --- README.md | 1 + cmd/kpod/main.go | 1 + cmd/kpod/rm.go | 65 +++++++++++++++++++++++++++ completions/bash/kpod | 22 +++++++++ docs/kpod-rm.1.md | 31 +++++++++++++ docs/kpod.1.md | 5 ++- libkpod/remove.go | 49 ++++++++++++++++++++ libkpod/stop.go | 28 ++++++++++++ server/container_remove.go | 38 +--------------- server/container_stop.go | 20 +-------- test/kpod_diff.bats | 7 ++- test/kpod_load.bats | 2 +- test/kpod_rm.bats | 92 ++++++++++++++++++++++++++++++++++++++ test/kpod_save.bats | 2 +- 14 files changed, 303 insertions(+), 60 deletions(-) create mode 100644 cmd/kpod/rm.go create mode 100644 docs/kpod-rm.1.md create mode 100644 libkpod/remove.go create mode 100644 libkpod/stop.go create mode 100644 test/kpod_rm.bats diff --git a/README.md b/README.md index 2153d2b1..da6a15d7 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,7 @@ It is currently in active development in the Kubernetes community through the [d | [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 || +| [kpod-rm(1)](/docs/kpod-rm.1.md) | Removes one or more containers || | [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|| diff --git a/cmd/kpod/main.go b/cmd/kpod/main.go index a4a30b6b..2a51aa28 100644 --- a/cmd/kpod/main.go +++ b/cmd/kpod/main.go @@ -32,6 +32,7 @@ func main() { pullCommand, pushCommand, renameCommand, + rmCommand, rmiCommand, saveCommand, statsCommand, diff --git a/cmd/kpod/rm.go b/cmd/kpod/rm.go new file mode 100644 index 00000000..bed72642 --- /dev/null +++ b/cmd/kpod/rm.go @@ -0,0 +1,65 @@ +package main + +import ( + "fmt" + + "github.com/kubernetes-incubator/cri-o/libkpod" + "github.com/pkg/errors" + "github.com/urfave/cli" +) + +var ( + rmFlags = []cli.Flag{ + cli.BoolFlag{ + Name: "force, f", + Usage: "Force removal of a running container. The default is false", + }, + } + rmDescription = "Remove one or more containers" + rmCommand = cli.Command{ + Name: "rm", + Usage: fmt.Sprintf(`kpod rm will remove one or more containers from the host. The container name or ID can be used. + This does not remove images. Running containers will not be removed without the -f option.`), + Description: rmDescription, + Flags: rmFlags, + Action: rmCmd, + ArgsUsage: "", + } +) + +// saveCmd saves the image to either docker-archive or oci +func rmCmd(c *cli.Context) error { + args := c.Args() + if len(args) == 0 { + return errors.Errorf("specify one or more containers to remove") + } + + 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") + } + force := c.Bool("force") + + for _, container := range c.Args() { + id, err2 := server.Remove(container, force) + if err2 != nil { + if err == nil { + err = err2 + } else { + err = errors.Wrapf(err, "%v. Stop the container before attempting removal or use -f\n", err2) + } + } else { + fmt.Println(id) + } + } + return err +} diff --git a/completions/bash/kpod b/completions/bash/kpod index 3893c9dc..e68f9814 100644 --- a/completions/bash/kpod +++ b/completions/bash/kpod @@ -255,6 +255,27 @@ _kpod_rename() { esac } +_kpod_rm() { + local boolean_options=" + --force + -f + " + + local options_with_args=" + " + + local all_options="$options_with_args $boolean_options" + + case "$cur" in + -*) + COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur")) + ;; + *) + __kpod_list_containers + ;; + esac +} + _kpod_rmi() { local boolean_options=" --help @@ -397,6 +418,7 @@ _kpod_kpod() { pull push rename + rm rmi save stats diff --git a/docs/kpod-rm.1.md b/docs/kpod-rm.1.md new file mode 100644 index 00000000..4e2c490a --- /dev/null +++ b/docs/kpod-rm.1.md @@ -0,0 +1,31 @@ +% kpod(1) kpod-rm - Remove one or more containers +% Ryan Cole +# kpod-rm "1" "August 2017" "kpod" + +## NAME +kpod rm - Remove one or more containers + +## SYNOPSIS +**kpod** **rm** [*options* [...]] container + +## DESCRIPTION +Kpod rm will remove one or more containers from the host. The container name or ID can be used. This does not remove images. Running containers will not be removed without the -f option + +## OPTIONS + +**--force, f** + +Force the removal of a running container + + +## EXAMPLE + +kpod rm mywebserver + +kpod rm -f 860a4b23 + +## SEE ALSO +kpod(1), kpod-rmi(1) + +## HISTORY +August 2017, Originally compiled by Ryan Cole diff --git a/docs/kpod.1.md b/docs/kpod.1.md index 70238a76..1e2fc412 100644 --- a/docs/kpod.1.md +++ b/docs/kpod.1.md @@ -36,7 +36,7 @@ has the capability to debug pods/images created by crio. Path to the 'run directory' where all state information is stored **--runtime**=**value** - Path to the OCI-compatible binary used to run containers + Path to the OCI compatible binary used to run containers **--storage-driver, -s**=**value** Select which storage driver is used to manage storage of images and containers (default is overlay) @@ -88,6 +88,9 @@ Push an image from local storage to elsewhere ### rename Rename a container +### rm +Remove one or more containers + ### rmi Removes one or more locally stored images diff --git a/libkpod/remove.go b/libkpod/remove.go new file mode 100644 index 00000000..bfe4fef8 --- /dev/null +++ b/libkpod/remove.go @@ -0,0 +1,49 @@ +package libkpod + +import ( + "os" + "path/filepath" + + "github.com/kubernetes-incubator/cri-o/oci" + "github.com/pkg/errors" +) + +// Remove removes a container +func (c *ContainerServer) Remove(container string, force bool) (string, error) { + ctr, err := c.LookupContainer(container) + if err != nil { + return "", err + } + ctrID := ctr.ID() + + cState := c.runtime.ContainerStatus(ctr) + if cState.Status == oci.ContainerStateCreated || cState.Status == oci.ContainerStateRunning { + if force { + _, err = c.ContainerStop(container, -1) + if err != nil { + return "", errors.Wrapf(err, "unable to stop container %s", ctrID) + } + } else { + return "", errors.Errorf("cannot remove running container %s", ctrID) + } + } + + if err := c.runtime.DeleteContainer(ctr); err != nil { + return "", errors.Wrapf(err, "failed to delete container %s", ctrID) + } + if err := os.Remove(filepath.Join(c.Config().RuntimeConfig.ContainerExitsDir, ctrID)); err != nil && !os.IsNotExist(err) { + return "", errors.Wrapf(err, "failed to remove container exit file %s", ctrID) + } + c.RemoveContainer(ctr) + + if err := c.storageRuntimeServer.DeleteContainer(ctrID); err != nil { + return "", errors.Wrapf(err, "failed to delete storage for container %s", ctrID) + } + + c.ReleaseContainerName(ctr.Name()) + + if err := c.ctrIDIndex.Delete(ctrID); err != nil { + return "", err + } + return ctrID, nil +} diff --git a/libkpod/stop.go b/libkpod/stop.go new file mode 100644 index 00000000..af7a8c5d --- /dev/null +++ b/libkpod/stop.go @@ -0,0 +1,28 @@ +package libkpod + +import ( + "github.com/kubernetes-incubator/cri-o/oci" + "github.com/pkg/errors" +) + +// ContainerStop stops a running container with a grace period (i.e., timeout). +func (c *ContainerServer) ContainerStop(container string, timeout int64) (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.ContainerStateStopped { + if err := c.runtime.StopContainer(ctr, timeout); err != nil { + return "", errors.Wrapf(err, "failed to stop container %s", ctr.ID()) + } + if err := c.storageRuntimeServer.StopContainer(ctr.ID()); err != nil { + return "", errors.Wrapf(err, "failed to unmount container %s", ctr.ID()) + } + } + + c.ContainerStateToDisk(ctr) + + return ctr.ID(), nil +} diff --git a/server/container_remove.go b/server/container_remove.go index 45413d2a..cedfc602 100644 --- a/server/container_remove.go +++ b/server/container_remove.go @@ -1,11 +1,6 @@ package server import ( - "fmt" - "os" - "path/filepath" - - "github.com/kubernetes-incubator/cri-o/oci" "github.com/sirupsen/logrus" "golang.org/x/net/context" pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime" @@ -14,42 +9,11 @@ import ( // RemoveContainer removes the container. If the container is running, the container // should be force removed. func (s *Server) RemoveContainer(ctx context.Context, req *pb.RemoveContainerRequest) (*pb.RemoveContainerResponse, error) { - logrus.Debugf("RemoveContainerRequest %+v", req) - c, err := s.GetContainerFromRequest(req.ContainerId) + _, err := s.ContainerServer.Remove(req.ContainerId, true) if err != nil { return nil, err } - cState := s.Runtime().ContainerStatus(c) - if cState.Status == oci.ContainerStateCreated || cState.Status == oci.ContainerStateRunning { - if err := s.Runtime().StopContainer(c, -1); err != nil { - return nil, fmt.Errorf("failed to stop container %s: %v", c.ID(), err) - } - if err := s.StorageRuntimeServer().StopContainer(c.ID()); err != nil { - return nil, fmt.Errorf("failed to unmount container %s: %v", c.ID(), err) - } - } - - if err := s.Runtime().DeleteContainer(c); err != nil { - return nil, fmt.Errorf("failed to delete container %s: %v", c.ID(), err) - } - - if err := os.Remove(filepath.Join(s.config.ContainerExitsDir, c.ID())); err != nil && !os.IsNotExist(err) { - return nil, fmt.Errorf("failed to remove container exit file %s: %v", c.ID(), err) - } - - s.removeContainer(c) - - if err := s.StorageRuntimeServer().DeleteContainer(c.ID()); err != nil { - return nil, fmt.Errorf("failed to delete storage for container %s: %v", c.ID(), err) - } - - s.ReleaseContainerName(c.Name()) - - if err := s.CtrIDIndex().Delete(c.ID()); err != nil { - return nil, err - } - resp := &pb.RemoveContainerResponse{} logrus.Debugf("RemoveContainerResponse: %+v", resp) return resp, nil diff --git a/server/container_stop.go b/server/container_stop.go index 4389308f..c0093cfd 100644 --- a/server/container_stop.go +++ b/server/container_stop.go @@ -1,9 +1,6 @@ package server import ( - "fmt" - - "github.com/kubernetes-incubator/cri-o/oci" "github.com/sirupsen/logrus" "golang.org/x/net/context" pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime" @@ -11,25 +8,12 @@ import ( // StopContainer stops a running container with a grace period (i.e., timeout). func (s *Server) StopContainer(ctx context.Context, req *pb.StopContainerRequest) (*pb.StopContainerResponse, error) { - logrus.Debugf("StopContainerRequest %+v", req) - c, err := s.GetContainerFromRequest(req.ContainerId) + _, err := s.ContainerServer.ContainerStop(req.ContainerId, req.Timeout) if err != nil { return nil, err } - cStatus := s.Runtime().ContainerStatus(c) - if cStatus.Status != oci.ContainerStateStopped { - if err := s.Runtime().StopContainer(c, req.Timeout); err != nil { - return nil, fmt.Errorf("failed to stop container %s: %v", c.ID(), err) - } - if err := s.StorageRuntimeServer().StopContainer(c.ID()); err != nil { - return nil, fmt.Errorf("failed to unmount container %s: %v", c.ID(), err) - } - } - - s.ContainerStateToDisk(c) - resp := &pb.StopContainerResponse{} - logrus.Debugf("StopContainerResponse %s: %+v", c.ID(), resp) + logrus.Debugf("StopContainerResponse %s: %+v", req.ContainerId, resp) return resp, nil } diff --git a/test/kpod_diff.bats b/test/kpod_diff.bats index 328419b7..50a82d5c 100644 --- a/test/kpod_diff.bats +++ b/test/kpod_diff.bats @@ -5,7 +5,7 @@ load helpers IMAGE="alpine:latest" ROOT="$TESTDIR/crio" RUNROOT="$TESTDIR/crio-run" -KPOD_OPTIONS="--root $ROOT --runroot $RUNROOT --storage-driver vfs" +KPOD_OPTIONS="--root $ROOT --runroot $RUNROOT $STORAGE_OPTS" function teardown() { cleanup_test @@ -13,8 +13,10 @@ function teardown() { @test "test diff of image and parent" { run ${KPOD_BINARY} $KPOD_OPTIONS pull $IMAGE + echo "$output" [ "$status" -eq 0 ] run ${KPOD_BINARY} $KPOD_OPTIONS diff $IMAGE + echo "$output" [ "$status" -eq 0 ] echo "$output" run ${KKPOD_BINARY} $KPOD_OPTIONS rmi $IMAGE @@ -28,10 +30,11 @@ function teardown() { @test "test diff with json output" { run ${KPOD_BINARY} $KPOD_OPTIONS pull $IMAGE + echo "$output" [ "$status" -eq 0 ] # run bash -c "${KPOD_BINARY} ${KPOD_OPTIONS} diff --format json $IMAGE | python -m json.tool" run ${KPOD_BINARY} $KPOD_OPTIONS diff --format json $IMAGE - [ "$status" -eq 0 ] echo "$output" + [ "$status" -eq 0 ] run ${KKPOD_BINARY} $KPOD_OPTIONS rmi $IMAGE } diff --git a/test/kpod_load.bats b/test/kpod_load.bats index 6dcdd2f8..8cf5cf1b 100644 --- a/test/kpod_load.bats +++ b/test/kpod_load.bats @@ -5,7 +5,7 @@ load helpers IMAGE="alpine:latest" ROOT="$TESTDIR/crio" RUNROOT="$TESTDIR/crio-run" -KPOD_OPTIONS="--root $ROOT --runroot $RUNROOT --storage-driver vfs" +KPOD_OPTIONS="--root $ROOT --runroot $RUNROOT $STORAGE_OPTS" function teardown() { cleanup_test diff --git a/test/kpod_rm.bats b/test/kpod_rm.bats new file mode 100644 index 00000000..b41f58dd --- /dev/null +++ b/test/kpod_rm.bats @@ -0,0 +1,92 @@ +#!/usr/bin/env bats + +load helpers + +IMAGE="alpine:latest" +ROOT="$TESTDIR/crio" +RUNROOT="$TESTDIR/crio-run" +KPOD_OPTIONS="--root $ROOT --runroot $RUNROOT $STORAGE_OPTS --runtime $RUNTIME_BINARY" +function teardown() { + cleanup_test +} + +@test "remove a stopped container" { + 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" + [ "$status" -eq 0 ] + run crioctl ctr stop --id "$ctr_id" + echo "$output" + [ "$status" -eq 0 ] + run ${KPOD_BINARY} $KPOD_OPTIONS rm "$ctr_id" + echo "$output" + [ "$status" -eq 0 ] + cleanup_pods + stop_crio +} + +@test "refuse to remove a running container" { + 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_redis.json --pod "$pod_id" + echo "$output" + [ "$status" -eq 0 ] + ctr_id="$output" + run crioctl ctr start --id "$ctr_id" + echo "$output" + [ "$status" -eq 0 ] + run ${KPOD_BINARY} $KPOD_OPTIONS rm "$ctr_id" + echo "$output" + [ "$status" -ne 0 ] + cleanup_ctrs + cleanup_pods + stop_crio +} + +@test "remove a created container" { + 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 ${KPOD_BINARY} $KPOD_OPTIONS rm -f "$ctr_id" + echo "$output" + [ "$status" -eq 0 ] + cleanup_pods + stop_crio +} + +@test "remove a running container" { + 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_redis.json --pod "$pod_id" + echo "$output" + [ "$status" -eq 0 ] + ctr_id="$output" + run crioctl ctr start --id "$ctr_id" + echo "$output" + [ "$status" -eq 0 ] + run ${KPOD_BINARY} $KPOD_OPTIONS rm -f "$ctr_id" + echo "$output" + [ "$status" -eq 0 ] + cleanup_pods + stop_crio +} diff --git a/test/kpod_save.bats b/test/kpod_save.bats index cfa3bad9..d77cd6b5 100644 --- a/test/kpod_save.bats +++ b/test/kpod_save.bats @@ -5,7 +5,7 @@ load helpers IMAGE="alpine:latest" ROOT="$TESTDIR/crio" RUNROOT="$TESTDIR/crio-run" -KPOD_OPTIONS="--root $ROOT --runroot $RUNROOT --storage-driver vfs" +KPOD_OPTIONS="--root $ROOT --runroot $RUNROOT $STORAGE_OPTS" function teardown() { cleanup_test