cc39d579 |
package daemon
import ( |
0f9f9950 |
"fmt" |
cc39d579 |
"os"
"path" |
0f9f9950 |
"strings" |
3343d234 |
"time" |
cc39d579 |
|
91e197d6 |
"github.com/docker/docker/api/types" |
6bb0d181 |
"github.com/docker/docker/container" |
d453fe35 |
"github.com/docker/docker/errdefs" |
4352da78 |
"github.com/docker/docker/layer" |
54dcbab2 |
"github.com/docker/docker/pkg/system" |
63864ad8 |
"github.com/docker/docker/volume" |
43012fe8 |
volumestore "github.com/docker/docker/volume/store" |
9d521a4d |
"github.com/pkg/errors" |
1009e6a4 |
"github.com/sirupsen/logrus" |
cc39d579 |
)
|
abd72d40 |
// 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. |
63fb931a |
func (daemon *Daemon) ContainerRm(name string, config *types.ContainerRmConfig) error { |
3343d234 |
start := time.Now() |
d7d512bb |
container, err := daemon.GetContainer(name) |
d25a6537 |
if err != nil { |
c79b9bab |
return err |
45407cf0 |
}
|
4d1007d7 |
// Container state RemovalInProgress should be used to avoid races. |
a793564b |
if inProgress := container.SetRemovalInProgress(); inProgress { |
9a58f298 |
err := fmt.Errorf("removal of container %s is already in progress", name) |
87a12421 |
return errdefs.Conflict(err) |
4d1007d7 |
} |
6bb0d181 |
defer container.ResetRemovalInProgress() |
4d1007d7 |
// check if container wasn't deregistered by previous rm since Get
if c := daemon.containers.Get(container.ID); c == nil {
return nil
}
|
db13cb7f |
if config.RemoveLink { |
0f9f9950 |
return daemon.rmLink(container, name) |
cc39d579 |
}
|
3343d234 |
err = daemon.cleanupContainer(container, config.ForceRemove, config.RemoveVolume)
containerActions.WithValues("delete").UpdateSince(start)
return err |
cc39d579 |
}
|
0f9f9950 |
func (daemon *Daemon) rmLink(container *container.Container, name string) error {
if name[0] != '/' {
name = "/" + name |
828f63f1 |
}
parent, n := path.Split(name)
if parent == "/" { |
0f9f9950 |
return fmt.Errorf("Conflict, cannot remove the default name of the container") |
828f63f1 |
}
|
0f9f9950 |
parent = strings.TrimSuffix(parent, "/") |
1128fc1a |
pe, err := daemon.containersReplica.Snapshot().GetID(parent) |
0f9f9950 |
if err != nil {
return fmt.Errorf("Cannot get parent %s for name %s", parent, name) |
828f63f1 |
}
|
0f9f9950 |
daemon.releaseName(name)
parentContainer, _ := daemon.GetContainer(pe) |
828f63f1 |
if parentContainer != nil { |
0f9f9950 |
daemon.linkIndex.unlink(name, container, parentContainer) |
828f63f1 |
if err := daemon.updateNetwork(parentContainer); err != nil {
logrus.Debugf("Could not update network to remove link %s: %v", n, err)
}
}
return nil
}
// cleanupContainer unregisters a container from the daemon, stops stats
// collection and cleanly removes contents and metadata from the filesystem. |
4df77c11 |
func (daemon *Daemon) cleanupContainer(container *container.Container, forceRemove, removeVolume bool) (err error) { |
f8628ba8 |
if container.IsRunning() {
if !forceRemove { |
0ec8f56a |
state := container.StateString()
procedure := "Stop the container before attempting removal or force remove"
if state == "paused" {
procedure = "Unpause and then " + strings.ToLower(procedure)
}
err := fmt.Errorf("You cannot remove a %s container %s. %s", state, container.ID, procedure) |
87a12421 |
return errdefs.Conflict(err) |
f8628ba8 |
} |
4f2a5ba3 |
if err := daemon.Kill(container); err != nil { |
a793564b |
return fmt.Errorf("Could not kill running container %s, cannot remove - %v", container.ID, err) |
f8628ba8 |
} |
cc39d579 |
}
|
c4e49d10 |
// stop collection of stats for the container regardless
// if stats are currently getting collected. |
835971c6 |
daemon.statsCollector.StopCollection(container) |
c4e49d10 |
|
4f2a5ba3 |
if err = daemon.containerStop(container, 3); err != nil { |
cc39d579 |
return err
}
|
40945fc1 |
// Mark container dead. We don't want anybody to be restarting it. |
eed4c7b7 |
container.Lock()
container.Dead = true |
40945fc1 |
// 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. |
edad5270 |
if err := container.CheckpointTo(daemon.containersReplica); err != nil && !os.IsNotExist(err) { |
af7f8187 |
logrus.Errorf("Error saving dying container to disk: %v", err)
} |
eed4c7b7 |
container.Unlock() |
40945fc1 |
|
0bfc9c8d |
// When container creation fails and `RWLayer` has not been created yet, we
// do not call `ReleaseRWLayer`
if container.RWLayer != nil { |
afd305c4 |
metadata, err := daemon.layerStores[container.OS].ReleaseRWLayer(container.RWLayer) |
0bfc9c8d |
layer.LogReleaseMetadata(metadata) |
d42dbdd3 |
if err != nil && err != layer.ErrMountDoesNotExist && !os.IsNotExist(errors.Cause(err)) { |
f963500c |
e := errors.Wrapf(err, "driver %q failed to remove root filesystem for %s", daemon.GraphDriverName(container.OS), container.ID)
container.SetRemovalError(e)
return e |
0bfc9c8d |
} |
cc39d579 |
}
|
54dcbab2 |
if err := system.EnsureRemoveAll(container.Root); err != nil { |
f963500c |
e := errors.Wrapf(err, "unable to remove filesystem for %s", container.ID)
container.SetRemovalError(e)
return e |
54dcbab2 |
}
|
600ad5c1 |
linkNames := daemon.linkIndex.delete(container) |
54dcbab2 |
selinuxFreeLxcContexts(container.ProcessLabel)
daemon.idIndex.Delete(container.ID)
daemon.containers.Delete(container.ID) |
edad5270 |
daemon.containersReplica.Delete(container) |
54dcbab2 |
if e := daemon.removeMountPoints(container, removeVolume); e != nil {
logrus.Error(e)
} |
600ad5c1 |
for _, name := range linkNames {
daemon.releaseName(name)
} |
cfdf84d5 |
container.SetRemoved() |
54dcbab2 |
stateCtr.del(container.ID) |
ddae20c0 |
|
54dcbab2 |
daemon.LogContainerEvent(container, "destroy") |
cc39d579 |
return nil
} |
81fa9feb |
|
b3b7eb27 |
// VolumeRm removes the volume with the given name.
// If the volume is referenced by a container it is not removed |
f0d55cd0 |
// This is called directly from the Engine API |
6c5c34d5 |
func (daemon *Daemon) VolumeRm(name string, force bool) error { |
63864ad8 |
v, err := daemon.volumes.Get(name)
if err != nil {
if force && volumestore.IsNotExist(err) {
return nil
}
return err
}
err = daemon.volumeRm(v) |
9d521a4d |
if err != nil && volumestore.IsInUse(err) { |
87a12421 |
return errdefs.Conflict(err) |
9d521a4d |
} |
63864ad8 |
|
6c5c34d5 |
if err == nil || force {
daemon.volumes.Purge(name)
return nil
}
return err
}
|
63864ad8 |
func (daemon *Daemon) volumeRm(v volume.Volume) error { |
b3b7eb27 |
if err := daemon.volumes.Remove(v); err != nil { |
9d521a4d |
return errors.Wrap(err, "unable to remove volume") |
b3b7eb27 |
} |
9d12d093 |
daemon.LogVolumeEvent(v.Name(), "destroy", map[string]string{"driver": v.DriverName()}) |
b3b7eb27 |
return nil |
81fa9feb |
} |