github.com/rawahars/moby@v24.0.4+incompatible/daemon/delete.go (about)

     1  package daemon // import "github.com/docker/docker/daemon"
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"path"
     8  	"strings"
     9  	"time"
    10  
    11  	"github.com/containerd/containerd/leases"
    12  	"github.com/docker/docker/api/types"
    13  	containertypes "github.com/docker/docker/api/types/container"
    14  	"github.com/docker/docker/container"
    15  	"github.com/docker/docker/errdefs"
    16  	"github.com/docker/docker/pkg/containerfs"
    17  	"github.com/opencontainers/selinux/go-selinux"
    18  	"github.com/pkg/errors"
    19  	"github.com/sirupsen/logrus"
    20  )
    21  
    22  // ContainerRm removes the container id from the filesystem. An error
    23  // is returned if the container is not found, or if the remove
    24  // fails. If the remove succeeds, the container name is released, and
    25  // network links are removed.
    26  func (daemon *Daemon) ContainerRm(name string, config *types.ContainerRmConfig) error {
    27  	start := time.Now()
    28  	ctr, err := daemon.GetContainer(name)
    29  	if err != nil {
    30  		return err
    31  	}
    32  
    33  	// Container state RemovalInProgress should be used to avoid races.
    34  	if inProgress := ctr.SetRemovalInProgress(); inProgress {
    35  		err := fmt.Errorf("removal of container %s is already in progress", name)
    36  		return errdefs.Conflict(err)
    37  	}
    38  	defer ctr.ResetRemovalInProgress()
    39  
    40  	// check if container wasn't deregistered by previous rm since Get
    41  	if c := daemon.containers.Get(ctr.ID); c == nil {
    42  		return nil
    43  	}
    44  
    45  	if config.RemoveLink {
    46  		return daemon.rmLink(ctr, name)
    47  	}
    48  
    49  	err = daemon.cleanupContainer(ctr, *config)
    50  	containerActions.WithValues("delete").UpdateSince(start)
    51  
    52  	return err
    53  }
    54  
    55  func (daemon *Daemon) rmLink(container *container.Container, name string) error {
    56  	if name[0] != '/' {
    57  		name = "/" + name
    58  	}
    59  	parent, n := path.Split(name)
    60  	if parent == "/" {
    61  		return fmt.Errorf("Conflict, cannot remove the default link name of the container")
    62  	}
    63  
    64  	parent = strings.TrimSuffix(parent, "/")
    65  	pe, err := daemon.containersReplica.Snapshot().GetID(parent)
    66  	if err != nil {
    67  		return fmt.Errorf("Cannot get parent %s for link name %s", parent, name)
    68  	}
    69  
    70  	daemon.releaseName(name)
    71  	parentContainer, _ := daemon.GetContainer(pe)
    72  	if parentContainer != nil {
    73  		daemon.linkIndex.unlink(name, container, parentContainer)
    74  		if err := daemon.updateNetwork(parentContainer); err != nil {
    75  			logrus.Debugf("Could not update network to remove link %s: %v", n, err)
    76  		}
    77  	}
    78  	return nil
    79  }
    80  
    81  // cleanupContainer unregisters a container from the daemon, stops stats
    82  // collection and cleanly removes contents and metadata from the filesystem.
    83  func (daemon *Daemon) cleanupContainer(container *container.Container, config types.ContainerRmConfig) error {
    84  	if container.IsRunning() {
    85  		if !config.ForceRemove {
    86  			state := container.StateString()
    87  			procedure := "Stop the container before attempting removal or force remove"
    88  			if state == "paused" {
    89  				procedure = "Unpause and then " + strings.ToLower(procedure)
    90  			}
    91  			err := fmt.Errorf("You cannot remove a %s container %s. %s", state, container.ID, procedure)
    92  			return errdefs.Conflict(err)
    93  		}
    94  		if err := daemon.Kill(container); err != nil {
    95  			return fmt.Errorf("Could not kill running container %s, cannot remove - %v", container.ID, err)
    96  		}
    97  	}
    98  
    99  	// stop collection of stats for the container regardless
   100  	// if stats are currently getting collected.
   101  	daemon.statsCollector.StopCollection(container)
   102  
   103  	// stopTimeout is the number of seconds to wait for the container to stop
   104  	// gracefully before forcibly killing it.
   105  	//
   106  	// Why 3 seconds? The timeout specified here was originally added in commit
   107  	// 1615bb08c7c3fc6c4b22db0a633edda516f97cf0, which added a custom timeout to
   108  	// some commands, but lacking an option for a timeout on "docker rm", was
   109  	// hardcoded to 10 seconds. Commit 28fd289b448164b77affd8103c0d96fd8110daf9
   110  	// later on updated this to 3 seconds (but no background on that change).
   111  	//
   112  	// If you arrived here and know the answer, you earned yourself a picture
   113  	// of a cute animal of your own choosing.
   114  	var stopTimeout = 3
   115  	if err := daemon.containerStop(context.TODO(), container, containertypes.StopOptions{Timeout: &stopTimeout}); err != nil {
   116  		return err
   117  	}
   118  
   119  	// Mark container dead. We don't want anybody to be restarting it.
   120  	container.Lock()
   121  	container.Dead = true
   122  
   123  	// Save container state to disk. So that if error happens before
   124  	// container meta file got removed from disk, then a restart of
   125  	// docker should not make a dead container alive.
   126  	if err := container.CheckpointTo(daemon.containersReplica); err != nil && !os.IsNotExist(err) {
   127  		logrus.Errorf("Error saving dying container to disk: %v", err)
   128  	}
   129  	container.Unlock()
   130  
   131  	// When container creation fails and `RWLayer` has not been created yet, we
   132  	// do not call `ReleaseRWLayer`
   133  	if container.RWLayer != nil {
   134  		if err := daemon.imageService.ReleaseLayer(container.RWLayer); err != nil {
   135  			err = errors.Wrapf(err, "container %s", container.ID)
   136  			container.SetRemovalError(err)
   137  			return err
   138  		}
   139  		container.RWLayer = nil
   140  	} else {
   141  		if daemon.UsesSnapshotter() {
   142  			ls := daemon.containerdCli.LeasesService()
   143  			lease := leases.Lease{
   144  				ID: container.ID,
   145  			}
   146  			if err := ls.Delete(context.Background(), lease, leases.SynchronousDelete); err != nil {
   147  				container.SetRemovalError(err)
   148  				return err
   149  			}
   150  		}
   151  	}
   152  
   153  	// Hold the container lock while deleting the container root directory
   154  	// so that other goroutines don't attempt to concurrently open files
   155  	// within it. Having any file open on Windows (without the
   156  	// FILE_SHARE_DELETE flag) will block it from being deleted.
   157  	container.Lock()
   158  	err := containerfs.EnsureRemoveAll(container.Root)
   159  	container.Unlock()
   160  	if err != nil {
   161  		err = errors.Wrapf(err, "unable to remove filesystem for %s", container.ID)
   162  		container.SetRemovalError(err)
   163  		return err
   164  	}
   165  
   166  	linkNames := daemon.linkIndex.delete(container)
   167  	selinux.ReleaseLabel(container.ProcessLabel)
   168  	daemon.containers.Delete(container.ID)
   169  	daemon.containersReplica.Delete(container)
   170  	if err := daemon.removeMountPoints(container, config.RemoveVolume); err != nil {
   171  		logrus.Error(err)
   172  	}
   173  	for _, name := range linkNames {
   174  		daemon.releaseName(name)
   175  	}
   176  	container.SetRemoved()
   177  	stateCtr.del(container.ID)
   178  
   179  	daemon.LogContainerEvent(container, "destroy")
   180  	return nil
   181  }