|
@@ -0,0 +1,181 @@
|
|
|
+package daemon
|
|
|
+
|
|
|
+import (
|
|
|
+ "fmt"
|
|
|
+ "log"
|
|
|
+ "os"
|
|
|
+ "path"
|
|
|
+ "path/filepath"
|
|
|
+ "strings"
|
|
|
+
|
|
|
+ "github.com/docker/docker/engine"
|
|
|
+ "github.com/docker/docker/utils"
|
|
|
+)
|
|
|
+
|
|
|
+// FIXME: rename to ContainerRemove for consistency with the CLI command.
|
|
|
+func (daemon *Daemon) ContainerDestroy(job *engine.Job) engine.Status {
|
|
|
+ if len(job.Args) != 1 {
|
|
|
+ return job.Errorf("Not enough arguments. Usage: %s CONTAINER\n", job.Name)
|
|
|
+ }
|
|
|
+ name := job.Args[0]
|
|
|
+ removeVolume := job.GetenvBool("removeVolume")
|
|
|
+ removeLink := job.GetenvBool("removeLink")
|
|
|
+ stop := job.GetenvBool("stop")
|
|
|
+ kill := job.GetenvBool("kill")
|
|
|
+
|
|
|
+ container := daemon.Get(name)
|
|
|
+
|
|
|
+ if removeLink {
|
|
|
+ if container == nil {
|
|
|
+ return job.Errorf("No such link: %s", name)
|
|
|
+ }
|
|
|
+ name, err := GetFullContainerName(name)
|
|
|
+ if err != nil {
|
|
|
+ job.Error(err)
|
|
|
+ }
|
|
|
+ parent, n := path.Split(name)
|
|
|
+ if parent == "/" {
|
|
|
+ return job.Errorf("Conflict, cannot remove the default name of the container")
|
|
|
+ }
|
|
|
+ pe := daemon.ContainerGraph().Get(parent)
|
|
|
+ if pe == nil {
|
|
|
+ return job.Errorf("Cannot get parent %s for name %s", parent, name)
|
|
|
+ }
|
|
|
+ parentContainer := daemon.Get(pe.ID())
|
|
|
+
|
|
|
+ if parentContainer != nil {
|
|
|
+ parentContainer.DisableLink(n)
|
|
|
+ }
|
|
|
+
|
|
|
+ if err := daemon.ContainerGraph().Delete(name); err != nil {
|
|
|
+ return job.Error(err)
|
|
|
+ }
|
|
|
+ return engine.StatusOK
|
|
|
+ }
|
|
|
+
|
|
|
+ if container != nil {
|
|
|
+ if container.State.IsRunning() {
|
|
|
+ if stop {
|
|
|
+ if err := container.Stop(5); err != nil {
|
|
|
+ return job.Errorf("Could not stop running container, cannot remove - %v", err)
|
|
|
+ }
|
|
|
+ } else if kill {
|
|
|
+ if err := container.Kill(); err != nil {
|
|
|
+ return job.Errorf("Could not kill running container, cannot remove - %v", err)
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ return job.Errorf("You cannot remove a running container. Stop the container before attempting removal or use -s or -k")
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if err := daemon.Destroy(container); err != nil {
|
|
|
+ return job.Errorf("Cannot destroy container %s: %s", name, err)
|
|
|
+ }
|
|
|
+ job.Eng.Job("log", "destroy", container.ID, daemon.Repositories().ImageName(container.Image)).Run()
|
|
|
+
|
|
|
+ if removeVolume {
|
|
|
+ var (
|
|
|
+ volumes = make(map[string]struct{})
|
|
|
+ binds = make(map[string]struct{})
|
|
|
+ usedVolumes = make(map[string]*Container)
|
|
|
+ )
|
|
|
+
|
|
|
+ // the volume id is always the base of the path
|
|
|
+ getVolumeId := func(p string) string {
|
|
|
+ return filepath.Base(strings.TrimSuffix(p, "/layer"))
|
|
|
+ }
|
|
|
+
|
|
|
+ // populate bind map so that they can be skipped and not removed
|
|
|
+ for _, bind := range container.HostConfig().Binds {
|
|
|
+ source := strings.Split(bind, ":")[0]
|
|
|
+ // TODO: refactor all volume stuff, all of it
|
|
|
+ // it is very important that we eval the link or comparing the keys to container.Volumes will not work
|
|
|
+ //
|
|
|
+ // eval symlink can fail, ref #5244 if we receive an is not exist error we can ignore it
|
|
|
+ p, err := filepath.EvalSymlinks(source)
|
|
|
+ if err != nil && !os.IsNotExist(err) {
|
|
|
+ return job.Error(err)
|
|
|
+ }
|
|
|
+ if p != "" {
|
|
|
+ source = p
|
|
|
+ }
|
|
|
+ binds[source] = struct{}{}
|
|
|
+ }
|
|
|
+
|
|
|
+ // Store all the deleted containers volumes
|
|
|
+ for _, volumeId := range container.Volumes {
|
|
|
+ // Skip the volumes mounted from external
|
|
|
+ // bind mounts here will will be evaluated for a symlink
|
|
|
+ if _, exists := binds[volumeId]; exists {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ volumeId = getVolumeId(volumeId)
|
|
|
+ volumes[volumeId] = struct{}{}
|
|
|
+ }
|
|
|
+
|
|
|
+ // Retrieve all volumes from all remaining containers
|
|
|
+ for _, container := range daemon.List() {
|
|
|
+ for _, containerVolumeId := range container.Volumes {
|
|
|
+ containerVolumeId = getVolumeId(containerVolumeId)
|
|
|
+ usedVolumes[containerVolumeId] = container
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ for volumeId := range volumes {
|
|
|
+ // If the requested volu
|
|
|
+ if c, exists := usedVolumes[volumeId]; exists {
|
|
|
+ log.Printf("The volume %s is used by the container %s. Impossible to remove it. Skipping.\n", volumeId, c.ID)
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ if err := daemon.Volumes().Delete(volumeId); err != nil {
|
|
|
+ return job.Errorf("Error calling volumes.Delete(%q): %v", volumeId, err)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ return job.Errorf("No such container: %s", name)
|
|
|
+ }
|
|
|
+ return engine.StatusOK
|
|
|
+}
|
|
|
+
|
|
|
+// Destroy unregisters a container from the daemon and cleanly removes its contents from the filesystem.
|
|
|
+// FIXME: rename to Rm for consistency with the CLI command
|
|
|
+func (daemon *Daemon) Destroy(container *Container) error {
|
|
|
+ if container == nil {
|
|
|
+ return fmt.Errorf("The given container is <nil>")
|
|
|
+ }
|
|
|
+
|
|
|
+ element := daemon.containers.Get(container.ID)
|
|
|
+ if element == nil {
|
|
|
+ return fmt.Errorf("Container %v not found - maybe it was already destroyed?", container.ID)
|
|
|
+ }
|
|
|
+
|
|
|
+ if err := container.Stop(3); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ // Deregister the container before removing its directory, to avoid race conditions
|
|
|
+ daemon.idIndex.Delete(container.ID)
|
|
|
+ daemon.containers.Delete(container.ID)
|
|
|
+
|
|
|
+ if _, err := daemon.containerGraph.Purge(container.ID); err != nil {
|
|
|
+ utils.Debugf("Unable to remove container from link graph: %s", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ if err := daemon.driver.Remove(container.ID); err != nil {
|
|
|
+ return fmt.Errorf("Driver %s failed to remove root filesystem %s: %s", daemon.driver, container.ID, err)
|
|
|
+ }
|
|
|
+
|
|
|
+ initID := fmt.Sprintf("%s-init", container.ID)
|
|
|
+ if err := daemon.driver.Remove(initID); err != nil {
|
|
|
+ return fmt.Errorf("Driver %s failed to remove init filesystem %s: %s", daemon.driver, initID, err)
|
|
|
+ }
|
|
|
+
|
|
|
+ if err := os.RemoveAll(container.root); err != nil {
|
|
|
+ return fmt.Errorf("Unable to remove filesystem for %v: %v", container.ID, err)
|
|
|
+ }
|
|
|
+
|
|
|
+ selinuxFreeLxcContexts(container.ProcessLabel)
|
|
|
+
|
|
|
+ return nil
|
|
|
+}
|