Skip to content

Commit

Permalink
rm: add containers eviction with rm --force
Browse files Browse the repository at this point in the history
Add ability to evict a container when it becomes unusable. This may
happen when the host setup changes after a container creation, making it
impossible for that container to be used or removed.
Evicting a container is done using the `rm --force` command.

Signed-off-by: Marco Vedovati <mvedovati@suse.com>
  • Loading branch information
marcov committed Sep 16, 2019
1 parent 1f5514e commit 081d7b1
Show file tree
Hide file tree
Showing 17 changed files with 492 additions and 88 deletions.
22 changes: 21 additions & 1 deletion API.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in

[func Diff(name: string) DiffInfo](#Diff)

[func EvictContainer(name: string, removeVolumes: bool) string](#EvictContainer)

[func ExecContainer(opts: ExecOpts) ](#ExecContainer)

[func ExportContainer(name: string, path: string) string](#ExportContainer)
Expand Down Expand Up @@ -445,6 +447,22 @@ $ varlink call -m unix:/run/podman/io.podman/io.podman.DeleteUnusedImages

method Diff(name: [string](https://godoc.org/builtin#string)) [DiffInfo](#DiffInfo)</div>
Diff returns a diff between libpod objects
### <a name="EvictContainer"></a>func EvictContainer
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">

method EvictContainer(name: [string](https://godoc.org/builtin#string), removeVolumes: [bool](https://godoc.org/builtin#bool)) [string](https://godoc.org/builtin#string)</div>
EvictContainer requires the name or ID of a container as well as a boolean that
indicates to remove builtin volumes. Upon successful eviction of the container,
its ID is returned. If the container cannot be found by name or ID,
a [ContainerNotFound](#ContainerNotFound) error will be returned.
See also [RemoveContainer](RemoveContainer).
#### Example
~~~
$ varlink call -m unix:/run/podman/io.podman/io.podman.EvictContainer '{"name": "62f4fd98cb57"}'
{
"container": "62f4fd98cb57f529831e8f90610e54bba74bd6f02920ffb485e15376ed365c20"
}
~~~
### <a name="ExecContainer"></a>func ExecContainer
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">

Expand Down Expand Up @@ -953,10 +971,12 @@ ReceiveFile allows the host to send a remote client a file
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">

method RemoveContainer(name: [string](https://godoc.org/builtin#string), force: [bool](https://godoc.org/builtin#bool), removeVolumes: [bool](https://godoc.org/builtin#bool)) [string](https://godoc.org/builtin#string)</div>
RemoveContainer requires the name or ID of container as well a boolean representing whether a running container can be stopped and removed, and a boolean
RemoveContainer requires the name or ID of a container as well as a boolean that
indicates whether a container should be forcefully removed (e.g., by stopping it), and a boolean
indicating whether to remove builtin volumes. Upon successful removal of the
container, its ID is returned. If the
container cannot be found by name or ID, a [ContainerNotFound](#ContainerNotFound) error will be returned.
See also [EvictContainer](EvictContainer).
#### Example
~~~
$ varlink call -m unix:/run/podman/io.podman/io.podman.RemoveContainer '{"name": "62f4fd98cb57"}'
Expand Down
7 changes: 4 additions & 3 deletions cmd/podman/rm.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ var (
rmCommand cliconfig.RmValues
rmDescription = fmt.Sprintf(`Removes one or more containers from the host. The container name or ID can be used.
Command does not remove images. Running containers will not be removed without the -f option.`)
Command does not remove images. Running or unusable containers will not be removed without the -f option.`)
_rmCommand = &cobra.Command{
Use: "rm [flags] CONTAINER [CONTAINER...]",
Short: "Remove one or more containers",
Expand All @@ -29,7 +29,8 @@ var (
},
Example: `podman rm imageID
podman rm mywebserver myflaskserver 860a4b23
podman rm --force --all`,
podman rm --force --all
podman rm -f c684f0d469f2`,
}
)

Expand All @@ -39,7 +40,7 @@ func init() {
rmCommand.SetUsageTemplate(UsageTemplate())
flags := rmCommand.Flags()
flags.BoolVarP(&rmCommand.All, "all", "a", false, "Remove all containers")
flags.BoolVarP(&rmCommand.Force, "force", "f", false, "Force removal of a running container. The default is false")
flags.BoolVarP(&rmCommand.Force, "force", "f", false, "Force removal of a running or unusable container. The default is false")
flags.BoolVarP(&rmCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
flags.BoolVar(&rmCommand.Storage, "storage", false, "Remove container from storage library")
flags.BoolVarP(&rmCommand.Volumes, "volumes", "v", false, "Remove the volumes associated with the container")
Expand Down
18 changes: 17 additions & 1 deletion cmd/podman/varlink/io.podman.varlink
Original file line number Diff line number Diff line change
Expand Up @@ -727,10 +727,12 @@ method GetAttachSockets(name: string) -> (sockets: Sockets)
# or name, a [ContainerNotFound](#ContainerNotFound) error is returned.
method WaitContainer(name: string, interval: int) -> (exitcode: int)

# RemoveContainer requires the name or ID of container as well a boolean representing whether a running container can be stopped and removed, and a boolean
# RemoveContainer requires the name or ID of a container as well as a boolean that
# indicates whether a container should be forcefully removed (e.g., by stopping it), and a boolean
# indicating whether to remove builtin volumes. Upon successful removal of the
# container, its ID is returned. If the
# container cannot be found by name or ID, a [ContainerNotFound](#ContainerNotFound) error will be returned.
# See also [EvictContainer](EvictContainer).
# #### Example
# ~~~
# $ varlink call -m unix:/run/podman/io.podman/io.podman.RemoveContainer '{"name": "62f4fd98cb57"}'
Expand All @@ -740,6 +742,20 @@ method WaitContainer(name: string, interval: int) -> (exitcode: int)
# ~~~
method RemoveContainer(name: string, force: bool, removeVolumes: bool) -> (container: string)

# EvictContainer requires the name or ID of a container as well as a boolean that
# indicates to remove builtin volumes. Upon successful eviction of the container,
# its ID is returned. If the container cannot be found by name or ID,
# a [ContainerNotFound](#ContainerNotFound) error will be returned.
# See also [RemoveContainer](RemoveContainer).
# #### Example
# ~~~
# $ varlink call -m unix:/run/podman/io.podman/io.podman.EvictContainer '{"name": "62f4fd98cb57"}'
# {
# "container": "62f4fd98cb57f529831e8f90610e54bba74bd6f02920ffb485e15376ed365c20"
# }
# ~~~
method EvictContainer(name: string, removeVolumes: bool) -> (container: string)

# DeleteStoppedContainers will delete all containers that are not running. It will return a list the deleted
# container IDs. See also [RemoveContainer](RemoveContainer).
# #### Example
Expand Down
7 changes: 5 additions & 2 deletions docs/podman-rm.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ podman\-rm - Remove one or more containers
**podman container rm** [*options*] *container*

## DESCRIPTION
**podman 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
**podman rm** will remove one or more containers from the host. The container name or ID can be used. This does not remove images.
Running or unusable containers will not be removed without the `-f` option.

## OPTIONS

Expand All @@ -19,9 +20,11 @@ Remove all containers. Can be used in conjunction with -f as well.

**--force**, **-f**

Force the removal of running and paused containers. Forcing a containers removal also
Force the removal of running and paused containers. Forcing a container removal also
removes containers from container storage even if the container is not known to podman.
Containers could have been created by a different container engine.
In addition, forcing can be used to remove unusable containers, e.g. containers
whose OCI runtime has become unavailable.

**--latest**, **-l**

Expand Down
139 changes: 80 additions & 59 deletions libpod/boltdb_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,52 @@ func (s *BoltState) Container(id string) (*Container, error) {
return ctr, nil
}

// LookupContainerID retrieves a container ID from the state by full or unique
// partial ID or name
func (s *BoltState) LookupContainerID(idOrName string) (string, error) {
if idOrName == "" {
return "", define.ErrEmptyID
}

if !s.valid {
return "", define.ErrDBClosed
}

db, err := s.getDBCon()
if err != nil {
return "", err
}
defer s.deferredCloseDBCon(db)

var id []byte
err = db.View(func(tx *bolt.Tx) error {
ctrBucket, err := getCtrBucket(tx)
if err != nil {
return err
}

namesBucket, err := getNamesBucket(tx)
if err != nil {
return err
}

nsBucket, err := getNSBucket(tx)
if err != nil {
return err
}

id, err = s.lookupContainerID(idOrName, ctrBucket, namesBucket, nsBucket)
return err
})

if err != nil {
return "", err
}

retID := string(id)
return retID, nil
}

// LookupContainer retrieves a container from the state by full or unique
// partial ID or name
func (s *BoltState) LookupContainer(idOrName string) (*Container, error) {
Expand Down Expand Up @@ -444,67 +490,9 @@ func (s *BoltState) LookupContainer(idOrName string) (*Container, error) {
return err
}

// First, check if the ID given was the actual container ID
var id []byte
ctrExists := ctrBucket.Bucket([]byte(idOrName))
if ctrExists != nil {
// A full container ID was given.
// It might not be in our namespace, but
// getContainerFromDB() will handle that case.
id = []byte(idOrName)
return s.getContainerFromDB(id, ctr, ctrBucket)
}

// Next, check if the full name was given
isPod := false
fullID := namesBucket.Get([]byte(idOrName))
if fullID != nil {
// The name exists and maps to an ID.
// However, we are not yet certain the ID is a
// container.
ctrExists = ctrBucket.Bucket(fullID)
if ctrExists != nil {
// A container bucket matching the full ID was
// found.
return s.getContainerFromDB(fullID, ctr, ctrBucket)
}
// Don't error if we have a name match but it's not a
// container - there's a chance we have a container with
// an ID starting with those characters.
// However, so we can return a good error, note whether
// this is a pod.
isPod = true
}

// We were not given a full container ID or name.
// Search for partial ID matches.
exists := false
err = ctrBucket.ForEach(func(checkID, checkName []byte) error {
// If the container isn't in our namespace, we
// can't match it
if s.namespaceBytes != nil {
ns := nsBucket.Get(checkID)
if !bytes.Equal(ns, s.namespaceBytes) {
return nil
}
}
if strings.HasPrefix(string(checkID), idOrName) {
if exists {
return errors.Wrapf(define.ErrCtrExists, "more than one result for container ID %s", idOrName)
}
id = checkID
exists = true
}

return nil
})
id, err := s.lookupContainerID(idOrName, ctrBucket, namesBucket, nsBucket)
if err != nil {
return err
} else if !exists {
if isPod {
return errors.Wrapf(define.ErrNoSuchCtr, "%s is a pod, not a container", idOrName)
}
return errors.Wrapf(define.ErrNoSuchCtr, "no container with name or ID %s found", idOrName)
}

return s.getContainerFromDB(id, ctr, ctrBucket)
Expand Down Expand Up @@ -860,6 +848,39 @@ func (s *BoltState) AllContainers() ([]*Container, error) {
return ctrs, nil
}

// GetContainerConfig returns a container config from the database by full ID
func (s *BoltState) GetContainerConfig(id string) (*ContainerConfig, error) {
if len(id) == 0 {
return nil, define.ErrEmptyID
}

if !s.valid {
return nil, define.ErrDBClosed
}

config := new(ContainerConfig)

db, err := s.getDBCon()
if err != nil {
return nil, err
}
defer s.deferredCloseDBCon(db)

err = db.View(func(tx *bolt.Tx) error {
ctrBucket, err := getCtrBucket(tx)
if err != nil {
return err
}

return s.getContainerConfigFromDB([]byte(id), config, ctrBucket)
})
if err != nil {
return nil, err
}

return config, nil
}

// RewriteContainerConfig rewrites a container's configuration.
// WARNING: This function is DANGEROUS. Do not use without reading the full
// comment on this function in state.go.
Expand Down
Loading

0 comments on commit 081d7b1

Please sign in to comment.