diff options
-rw-r--r-- | cmd/podman/rm.go | 23 | ||||
-rw-r--r-- | completions/bash/podman | 1 | ||||
-rw-r--r-- | docs/podman-rm.1.md | 7 | ||||
-rw-r--r-- | libpod/runtime_cstorage.go | 118 |
4 files changed, 149 insertions, 0 deletions
diff --git a/cmd/podman/rm.go b/cmd/podman/rm.go index 7c0569b78..d6978a460 100644 --- a/cmd/podman/rm.go +++ b/cmd/podman/rm.go @@ -21,6 +21,10 @@ var ( }, LatestFlag, cli.BoolFlag{ + Name: "storage", + Usage: "Remove container from storage library", + }, + cli.BoolFlag{ Name: "volumes, v", Usage: "Remove the volumes associated with the container (Not implemented yet)", }, @@ -62,6 +66,25 @@ func rmCmd(c *cli.Context) error { return err } + // Storage conflicts with --all/--latest/--volumes + if c.Bool("storage") { + if c.Bool("all") || c.Bool("latest") || c.Bool("volumes") { + return errors.Errorf("--storage conflicts with --volumes, --all, and --latest") + } + + var lastErr error + for _, ctr := range c.Args() { + if err := runtime.RemoveStorageContainer(ctr, c.Bool("force")); err != nil { + if lastErr != nil { + logrus.Errorf("Error removing container %s from storage: %v", ctr, lastErr) + } + lastErr = err + } + fmt.Printf("%s\n", ctr) + } + return lastErr + } + delContainers, err := getAllOrLatestContainers(c, runtime, -1, "all") if err != nil { if len(delContainers) == 0 { diff --git a/completions/bash/podman b/completions/bash/podman index 410180638..49fc0c7b7 100644 --- a/completions/bash/podman +++ b/completions/bash/podman @@ -1911,6 +1911,7 @@ _podman_rm() { -h --latest -l + --storage --volumes -v " diff --git a/docs/podman-rm.1.md b/docs/podman-rm.1.md index 56664a8c1..944e74d81 100644 --- a/docs/podman-rm.1.md +++ b/docs/podman-rm.1.md @@ -24,6 +24,13 @@ Remove all containers. Can be used in conjunction with -f as well. Instead of providing the container name or ID, use the last created container. If you use methods other than Podman to run containers such as CRI-O, the last started container could be from either of those methods. +**--storage** + +Remove the container from the storage library only. +This is only possible with containers that are not present in libpod (cannot be seen by `podman ps`). +It is used to remove containers from `podman build` and `buildah`, and orphan containers which were only partially removed by `podman rm`. +The storage option conflicts with the **--all**, **--latest**, and **--volumes** options. + **--volumes, -v** Remove the volumes associated with the container. (Not yet implemented) diff --git a/libpod/runtime_cstorage.go b/libpod/runtime_cstorage.go new file mode 100644 index 000000000..569f63322 --- /dev/null +++ b/libpod/runtime_cstorage.go @@ -0,0 +1,118 @@ +package libpod + +import ( + "github.com/containers/storage" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +// StorageContainer represents a container present in c/storage but not in +// libpod. +type StorageContainer struct { + ID string + Names []string + PresentInLibpod bool +} + +// ListStorageContainers lists all containers visible to c/storage. +func (r *Runtime) ListStorageContainers() ([]*StorageContainer, error) { + r.lock.RLock() + defer r.lock.RUnlock() + + finalCtrs := []*StorageContainer{} + + ctrs, err := r.store.Containers() + if err != nil { + return nil, err + } + + for _, ctr := range ctrs { + storageCtr := new(StorageContainer) + storageCtr.ID = ctr.ID + storageCtr.Names = ctr.Names + + // Look up if container is in state + hasCtr, err := r.state.HasContainer(ctr.ID) + if err != nil { + return nil, errors.Wrapf(err, "error looking up container %s in state", ctr.ID) + } + + storageCtr.PresentInLibpod = hasCtr + + finalCtrs = append(finalCtrs, storageCtr) + } + + return finalCtrs, nil +} + +// RemoveStorageContainer removes a container from c/storage. +// The container WILL NOT be removed if it exists in libpod. +// Accepts ID or full name of container. +// If force is set, the container will be unmounted first to ensure removal. +func (r *Runtime) RemoveStorageContainer(idOrName string, force bool) error { + r.lock.Lock() + defer r.lock.Unlock() + + targetID, err := r.store.Lookup(idOrName) + if err != nil { + if err == storage.ErrLayerUnknown { + return errors.Wrapf(ErrNoSuchCtr, "no container with ID or name %q found", idOrName) + } + return errors.Wrapf(err, "error looking up container %q", idOrName) + } + + // Lookup returns an ID but it's not guaranteed to be a container ID. + // So we can still error here. + ctr, err := r.store.Container(targetID) + if err != nil { + if err == storage.ErrContainerUnknown { + return errors.Wrapf(ErrNoSuchCtr, "%q does not refer to a container", idOrName) + } + return errors.Wrapf(err, "error retrieving container %q", idOrName) + } + + // Error out if the container exists in libpod + exists, err := r.state.HasContainer(ctr.ID) + if err != nil { + return err + } + if exists { + return errors.Wrapf(ErrCtrExists, "refusing to remove %q as it exists in libpod as container %s", idOrName, ctr.ID) + } + + if !force { + timesMounted, err := r.store.Mounted(ctr.ID) + if err != nil { + if err == storage.ErrContainerUnknown { + // Container was removed from under us. + // It's gone, so don't bother erroring. + logrus.Warnf("Storage for container %s already removed", ctr.ID) + return nil + } + return errors.Wrapf(err, "error looking up container %q mounts", idOrName) + } + if timesMounted > 0 { + return errors.Wrapf(ErrCtrStateInvalid, "container %q is mounted and cannot be removed without using force", idOrName) + } + } else { + if _, err := r.store.Unmount(ctr.ID, true); err != nil { + if err == storage.ErrContainerUnknown { + // Container again gone, no error + logrus.Warnf("Storage for container %s already removed", ctr.ID) + return nil + } + return errors.Wrapf(err, "error unmounting container %q", idOrName) + } + } + + if err := r.store.DeleteContainer(ctr.ID); err != nil { + if err == storage.ErrContainerUnknown { + // Container again gone, no error + logrus.Warnf("Storage for container %s already removed", ctr.ID) + return nil + } + return errors.Wrapf(err, "error removing storage for container %q", idOrName) + } + + return nil +} |