2018-02-05 21:05:59 +00:00
package daemon // import "github.com/docker/docker/daemon"
2014-07-31 21:12:12 +00:00
import (
2021-08-20 22:23:26 +00:00
"context"
2015-09-04 00:51:04 +00:00
"fmt"
2014-07-31 21:12:12 +00:00
"os"
"path"
2015-09-04 00:51:04 +00:00
"strings"
2016-07-20 23:11:28 +00:00
"time"
2014-07-31 21:12:12 +00:00
2023-08-21 14:52:32 +00:00
cerrdefs "github.com/containerd/containerd/errdefs"
2022-07-25 12:22:05 +00:00
"github.com/containerd/containerd/leases"
2023-09-13 15:41:45 +00:00
"github.com/containerd/log"
2023-12-05 14:58:22 +00:00
"github.com/docker/docker/api/types/backend"
2021-08-20 22:23:26 +00:00
containertypes "github.com/docker/docker/api/types/container"
2023-08-26 13:24:46 +00:00
"github.com/docker/docker/api/types/events"
2015-11-12 19:55:17 +00:00
"github.com/docker/docker/container"
2022-08-17 21:13:49 +00:00
"github.com/docker/docker/daemon/config"
2018-01-11 19:53:06 +00:00
"github.com/docker/docker/errdefs"
pkg/system: move EnsureRemoveAll() to pkg/containerfs
pkg/system historically has been a bit of a kitchen-sink of things that were
somewhat "system" related, but didn't have a good place for. EnsureRemoveAll()
is one of those utilities. EnsureRemoveAll() is used to both unmount and remove
a path, for which it depends on both github.com/moby/sys/mount, which in turn
depends on github.com/moby/sys/mountinfo.
pkg/system is imported in the CLI, but neither EnsureRemoveAll(), nor any of its
moby/sys dependencies are used on the client side, so let's move this function
somewhere else, to remove those dependencies from the CLI.
I looked for plausible locations that were related; it's used in:
- daemon
- daemon/graphdriver/XXX/
- plugin
I considered moving it into a (e.g.) "utils" package within graphdriver (but not
a huge fan of "utils" packages), and given that it felt (mostly) related to
cleaning up container filesystems, I decided to move it there.
Some things to follow-up on after this:
- Verify if this function is still needed (it feels a bit like a big hammer in
a "YOLO, let's try some things just in case it fails")
- Perhaps it should be integrated in `containerfs.Remove()` (so that it's used
automatically)
- Look if there's other implementations (and if they should be consolidated),
although (e.g.) the one in containerd is a copy of ours:
https://github.com/containerd/containerd/blob/v1.5.9/pkg/cri/server/helpers_linux.go#L200
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2022-03-02 21:43:07 +00:00
"github.com/docker/docker/pkg/containerfs"
2020-12-14 10:46:58 +00:00
"github.com/opencontainers/selinux/go-selinux"
2017-03-01 13:36:09 +00:00
"github.com/pkg/errors"
2014-07-31 21:12:12 +00:00
)
2015-07-30 21:01:53 +00:00
// ContainerRm removes the container id from the filesystem. An error
// is returned if the container is not found, or if the remove
// fails. If the remove succeeds, the container name is released, and
// network links are removed.
2023-12-05 14:58:22 +00:00
func ( daemon * Daemon ) ContainerRm ( name string , config * backend . ContainerRmConfig ) error {
2022-08-31 20:12:30 +00:00
return daemon . containerRm ( & daemon . config ( ) . Config , name , config )
2022-08-17 21:13:49 +00:00
}
2023-12-05 14:58:22 +00:00
func ( daemon * Daemon ) containerRm ( cfg * config . Config , name string , opts * backend . ContainerRmConfig ) error {
2016-07-20 23:11:28 +00:00
start := time . Now ( )
2021-08-21 22:46:07 +00:00
ctr , err := daemon . GetContainer ( name )
2014-12-16 23:06:35 +00:00
if err != nil {
2015-03-25 07:44:12 +00:00
return err
2014-08-28 14:18:08 +00:00
}
2015-12-01 17:54:26 +00:00
// Container state RemovalInProgress should be used to avoid races.
2021-08-21 22:46:07 +00:00
if inProgress := ctr . SetRemovalInProgress ( ) ; inProgress {
2016-08-27 13:39:34 +00:00
err := fmt . Errorf ( "removal of container %s is already in progress" , name )
2017-11-29 04:09:37 +00:00
return errdefs . Conflict ( err )
2015-12-01 17:54:26 +00:00
}
2021-08-21 22:46:07 +00:00
defer ctr . ResetRemovalInProgress ( )
2015-12-01 17:54:26 +00:00
// check if container wasn't deregistered by previous rm since Get
2021-08-21 22:46:07 +00:00
if c := daemon . containers . Get ( ctr . ID ) ; c == nil {
2015-12-01 17:54:26 +00:00
return nil
}
2022-08-17 21:13:49 +00:00
if opts . RemoveLink {
return daemon . rmLink ( cfg , ctr , name )
2014-07-31 21:12:12 +00:00
}
2022-08-17 21:13:49 +00:00
err = daemon . cleanupContainer ( ctr , * opts )
2016-07-20 23:11:28 +00:00
containerActions . WithValues ( "delete" ) . UpdateSince ( start )
return err
2014-07-31 21:12:12 +00:00
}
2022-08-17 21:13:49 +00:00
func ( daemon * Daemon ) rmLink ( cfg * config . Config , container * container . Container , name string ) error {
2015-09-04 00:51:04 +00:00
if name [ 0 ] != '/' {
name = "/" + name
2015-12-02 17:18:34 +00:00
}
parent , n := path . Split ( name )
if parent == "/" {
2019-04-25 16:57:56 +00:00
return fmt . Errorf ( "Conflict, cannot remove the default link name of the container" )
2015-12-02 17:18:34 +00:00
}
2015-09-04 00:51:04 +00:00
parent = strings . TrimSuffix ( parent , "/" )
2017-06-30 01:56:22 +00:00
pe , err := daemon . containersReplica . Snapshot ( ) . GetID ( parent )
2015-09-04 00:51:04 +00:00
if err != nil {
2019-04-25 16:57:56 +00:00
return fmt . Errorf ( "Cannot get parent %s for link name %s" , parent , name )
2015-12-02 17:18:34 +00:00
}
2015-09-04 00:51:04 +00:00
daemon . releaseName ( name )
parentContainer , _ := daemon . GetContainer ( pe )
2015-12-02 17:18:34 +00:00
if parentContainer != nil {
2015-09-04 00:51:04 +00:00
daemon . linkIndex . unlink ( name , container , parentContainer )
2022-08-17 21:13:49 +00:00
if err := daemon . updateNetwork ( cfg , parentContainer ) ; err != nil {
2023-06-23 00:33:17 +00:00
log . G ( context . TODO ( ) ) . Debugf ( "Could not update network to remove link %s: %v" , n , err )
2015-12-02 17:18:34 +00:00
}
}
return nil
}
// cleanupContainer unregisters a container from the daemon, stops stats
// collection and cleanly removes contents and metadata from the filesystem.
2023-12-05 14:58:22 +00:00
func ( daemon * Daemon ) cleanupContainer ( container * container . Container , config backend . ContainerRmConfig ) error {
2015-05-26 19:38:52 +00:00
if container . IsRunning ( ) {
2021-08-20 22:14:25 +00:00
if ! config . ForceRemove {
2023-08-13 19:47:25 +00:00
if state := container . StateString ( ) ; state == "paused" {
return errdefs . Conflict ( fmt . Errorf ( "cannot remove container %q: container is %s and must be unpaused first" , container . Name , state ) )
} else {
return errdefs . Conflict ( fmt . Errorf ( "cannot remove container %q: container is %s: stop the container before removing or force remove" , container . Name , state ) )
2017-02-09 10:48:33 +00:00
}
2015-05-26 19:38:52 +00:00
}
2023-08-13 19:37:24 +00:00
if err := daemon . Kill ( container ) ; err != nil && ! isNotRunning ( err ) {
return fmt . Errorf ( "cannot remove container %q: could not kill: %w" , container . Name , err )
2015-05-26 19:38:52 +00:00
}
2014-07-31 21:12:12 +00:00
}
2015-10-20 20:36:09 +00:00
// stop collection of stats for the container regardless
// if stats are currently getting collected.
2017-01-04 17:01:59 +00:00
daemon . statsCollector . StopCollection ( container )
2015-10-20 20:36:09 +00:00
2021-08-20 20:43:13 +00:00
// stopTimeout is the number of seconds to wait for the container to stop
// gracefully before forcibly killing it.
//
// Why 3 seconds? The timeout specified here was originally added in commit
// 1615bb08c7c3fc6c4b22db0a633edda516f97cf0, which added a custom timeout to
// some commands, but lacking an option for a timeout on "docker rm", was
// hardcoded to 10 seconds. Commit 28fd289b448164b77affd8103c0d96fd8110daf9
// later on updated this to 3 seconds (but no background on that change).
//
// If you arrived here and know the answer, you earned yourself a picture
// of a cute animal of your own choosing.
2022-01-20 13:25:24 +00:00
stopTimeout := 3
2021-08-20 22:23:26 +00:00
if err := daemon . containerStop ( context . TODO ( ) , container , containertypes . StopOptions { Timeout : & stopTimeout } ) ; err != nil {
2014-07-31 21:12:12 +00:00
return err
}
2015-03-12 19:26:17 +00:00
// Mark container dead. We don't want anybody to be restarting it.
2017-02-22 22:02:20 +00:00
container . Lock ( )
container . Dead = true
2015-03-12 19:26:17 +00:00
// Save container state to disk. So that if error happens before
// container meta file got removed from disk, then a restart of
// docker should not make a dead container alive.
2017-03-27 17:18:53 +00:00
if err := container . CheckpointTo ( daemon . containersReplica ) ; err != nil && ! os . IsNotExist ( err ) {
2023-06-23 00:33:17 +00:00
log . G ( context . TODO ( ) ) . Errorf ( "Error saving dying container to disk: %v" , err )
2015-07-02 10:24:35 +00:00
}
2017-02-22 22:02:20 +00:00
container . Unlock ( )
2015-03-12 19:26:17 +00:00
2016-04-05 01:30:05 +00:00
// When container creation fails and `RWLayer` has not been created yet, we
// do not call `ReleaseRWLayer`
if container . RWLayer != nil {
2022-01-25 11:36:38 +00:00
if err := daemon . imageService . ReleaseLayer ( container . RWLayer ) ; err != nil {
2018-02-02 22:18:46 +00:00
err = errors . Wrapf ( err , "container %s" , container . ID )
container . SetRemovalError ( err )
return err
2016-04-05 01:30:05 +00:00
}
2018-01-30 23:01:45 +00:00
container . RWLayer = nil
2022-07-25 12:22:05 +00:00
} else {
if daemon . UsesSnapshotter ( ) {
2023-07-18 11:57:27 +00:00
ls := daemon . containerdClient . LeasesService ( )
2022-07-25 12:22:05 +00:00
lease := leases . Lease {
ID : container . ID ,
}
if err := ls . Delete ( context . Background ( ) , lease , leases . SynchronousDelete ) ; err != nil {
2023-08-21 14:52:32 +00:00
if ! cerrdefs . IsNotFound ( err ) {
container . SetRemovalError ( err )
return err
}
2022-07-25 12:22:05 +00:00
}
}
2014-07-31 21:12:12 +00:00
}
2022-05-09 19:06:37 +00:00
// Hold the container lock while deleting the container root directory
// so that other goroutines don't attempt to concurrently open files
// within it. Having any file open on Windows (without the
// FILE_SHARE_DELETE flag) will block it from being deleted.
container . Lock ( )
err := containerfs . EnsureRemoveAll ( container . Root )
container . Unlock ( )
if err != nil {
2022-01-25 11:36:38 +00:00
err = errors . Wrapf ( err , "unable to remove filesystem for %s" , container . ID )
container . SetRemovalError ( err )
return err
2017-02-14 18:35:20 +00:00
}
2017-07-14 09:25:09 +00:00
linkNames := daemon . linkIndex . delete ( container )
2020-12-14 10:46:58 +00:00
selinux . ReleaseLabel ( container . ProcessLabel )
2017-02-14 18:35:20 +00:00
daemon . containers . Delete ( container . ID )
2017-03-27 17:18:53 +00:00
daemon . containersReplica . Delete ( container )
2021-08-20 22:14:25 +00:00
if err := daemon . removeMountPoints ( container , config . RemoveVolume ) ; err != nil {
2023-06-23 00:33:17 +00:00
log . G ( context . TODO ( ) ) . Error ( err )
2017-02-14 18:35:20 +00:00
}
2017-07-14 09:25:09 +00:00
for _ , name := range linkNames {
daemon . releaseName ( name )
}
2017-03-30 20:52:40 +00:00
container . SetRemoved ( )
2017-02-14 18:35:20 +00:00
stateCtr . del ( container . ID )
2017-09-22 13:52:41 +00:00
2023-08-26 13:24:46 +00:00
daemon . LogContainerEvent ( container , events . ActionDestroy )
2014-07-31 21:12:12 +00:00
return nil
}