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