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") forceRemove := job.GetenvBool("forceRemove") 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 forceRemove { 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 -f") } } if err := daemon.Destroy(container); err != nil { return job.Errorf("Cannot destroy container %s: %s", name, err) } container.LogEvent("destroy") 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 ") } 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 }