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