daemon/start.go
4180b875
 package daemon
 
 import (
ddae20c0
 	"context"
39ad38cc
 	"runtime"
3343d234
 	"time"
c79b9bab
 
91e197d6
 	"github.com/docker/docker/api/types"
 	containertypes "github.com/docker/docker/api/types/container"
6bb0d181
 	"github.com/docker/docker/container"
d453fe35
 	"github.com/docker/docker/errdefs"
eaa51928
 	"github.com/docker/docker/pkg/mount"
ebcb7d6b
 	"github.com/pkg/errors"
1009e6a4
 	"github.com/sirupsen/logrus"
4180b875
 )
 
abd72d40
 // ContainerStart starts a container.
ef39256d
 func (daemon *Daemon) ContainerStart(name string, hostConfig *containertypes.HostConfig, checkpoint string, checkpointDir string) error {
3c0a2bb2
 	if checkpoint != "" && !daemon.HasExperimental() {
87a12421
 		return errdefs.InvalidParameter(errors.New("checkpoint is only supported in experimental mode"))
3c0a2bb2
 	}
 
d7d512bb
 	container, err := daemon.GetContainer(name)
d25a6537
 	if err != nil {
c79b9bab
 		return err
4180b875
 	}
 
ebcb7d6b
 	validateState := func() error {
 		container.Lock()
 		defer container.Unlock()
 
 		if container.Paused {
87a12421
 			return errdefs.Conflict(errors.New("cannot start a paused container, try unpause instead"))
ebcb7d6b
 		}
 
 		if container.Running {
 			return containerNotModifiedError{running: true}
 		}
 
 		if container.RemovalInProgress || container.Dead {
87a12421
 			return errdefs.Conflict(errors.New("container is marked for removal and cannot be started"))
ebcb7d6b
 		}
 		return nil
02246d2d
 	}
 
ebcb7d6b
 	if err := validateState(); err != nil {
 		return err
4180b875
 	}
 
51462327
 	// Windows does not have the backwards compatibility issue here.
39ad38cc
 	if runtime.GOOS != "windows" {
 		// This is kept for backward compatibility - hostconfig should be passed when
 		// creating a container, not during start.
 		if hostConfig != nil {
26b6b474
 			logrus.Warn("DEPRECATED: Setting host configuration options when the container starts is deprecated and has been removed in Docker 1.12")
3d2539d3
 			oldNetworkMode := container.HostConfig.NetworkMode
d8e09066
 			if err := daemon.setSecurityOptions(container, hostConfig); err != nil {
87a12421
 				return errdefs.InvalidParameter(err)
d8e09066
 			}
1790980e
 			if err := daemon.mergeAndVerifyLogConfig(&hostConfig.LogConfig); err != nil {
87a12421
 				return errdefs.InvalidParameter(err)
1790980e
 			}
b08f071e
 			if err := daemon.setHostConfig(container, hostConfig); err != nil {
87a12421
 				return errdefs.InvalidParameter(err)
39ad38cc
 			}
3d2539d3
 			newNetworkMode := container.HostConfig.NetworkMode
 			if string(oldNetworkMode) != string(newNetworkMode) {
 				// if user has change the network mode on starting, clean up the
26b6b474
 				// old networks. It is a deprecated feature and has been removed in Docker 1.12
3d2539d3
 				container.NetworkSettings.Networks = nil
edad5270
 				if err := container.CheckpointTo(daemon.containersReplica); err != nil {
87a12421
 					return errdefs.System(err)
3d2539d3
 				}
 			}
6bb0d181
 			container.InitDNSHostConfig()
39ad38cc
 		}
 	} else {
 		if hostConfig != nil {
87a12421
 			return errdefs.InvalidParameter(errors.New("Supplying a hostconfig on start is not supported. It should be supplied on create"))
4180b875
 		}
 	}
767df67e
 
4177b0ba
 	// check if hostConfig is in line with the current system settings.
 	// It may happen cgroups are umounted or the like.
0380fbff
 	if _, err = daemon.verifyContainerSettings(container.OS, container.HostConfig, nil, false); err != nil {
87a12421
 		return errdefs.InvalidParameter(err)
4177b0ba
 	}
f408b4f4
 	// Adapt for old containers in case we have updates in this function and
 	// old containers never have chance to call the new function in create stage.
4c10c2de
 	if hostConfig != nil {
 		if err := daemon.adaptContainerSettings(container.HostConfig, false); err != nil {
87a12421
 			return errdefs.InvalidParameter(err)
4c10c2de
 		}
4177b0ba
 	}
b4a63139
 	return daemon.containerStart(container, checkpoint, checkpointDir, true)
4180b875
 }
3a497650
 
 // containerStart prepares the container to run by setting up everything the
 // container needs, such as storage and networking, as well as links
 // between containers. The container is left waiting for a signal to
 // begin running.
bd7d5129
 func (daemon *Daemon) containerStart(container *container.Container, checkpoint string, checkpointDir string, resetRestartManager bool) (err error) {
3343d234
 	start := time.Now()
3a497650
 	container.Lock()
 	defer container.Unlock()
 
606a245d
 	if resetRestartManager && container.Running { // skip this check if already in restarting step and resetRestartManager==false
3a497650
 		return nil
 	}
 
6bb0d181
 	if container.RemovalInProgress || container.Dead {
87a12421
 		return errdefs.Conflict(errors.New("container is marked for removal and cannot be started"))
3a497650
 	}
 
ddae20c0
 	if checkpointDir != "" {
 		// TODO(mlaventure): how would we support that?
87a12421
 		return errdefs.Forbidden(errors.New("custom checkpointdir is not supported"))
ddae20c0
 	}
 
3a497650
 	// if we encounter an error during start we need to ensure that any other
 	// setup has been cleaned up properly
 	defer func() {
 		if err != nil {
6bb0d181
 			container.SetError(err)
3a497650
 			// if no one else has set it, make sure we don't leave it at zero
dcfe9927
 			if container.ExitCode() == 0 {
 				container.SetExitCode(128)
3a497650
 			}
edad5270
 			if err := container.CheckpointTo(daemon.containersReplica); err != nil {
aacddda8
 				logrus.Errorf("%s: failed saving state on start failure: %v", container.ID, err)
eed4c7b7
 			}
e806821b
 			container.Reset(false)
 
019c337b
 			daemon.Cleanup(container)
1537dbe2
 			// if containers AutoRemove flag is set, remove it after clean up
 			if container.HostConfig.AutoRemove {
 				container.Unlock()
 				if err := daemon.ContainerRm(container.ID, &types.ContainerRmConfig{ForceRemove: true, RemoveVolume: true}); err != nil {
 					logrus.Errorf("can't remove container %s: %v", container.ID, err)
 				}
 				container.Lock()
 			}
3a497650
 		}
 	}()
 
 	if err := daemon.conditionalMountOnStart(container); err != nil {
 		return err
 	}
 
669949d6
 	if err := daemon.initializeNetworking(container); err != nil {
3a497650
 		return err
 	}
9c4570a9
 
 	spec, err := daemon.createSpec(container)
3a497650
 	if err != nil {
87a12421
 		return errdefs.System(err)
3a497650
 	}
 
606a245d
 	if resetRestartManager {
 		container.ResetRestartManager(true)
7b2e5216
 	}
 
ddae20c0
 	if daemon.saveApparmorConfig(container); err != nil {
 		return err
bd7d5129
 	}
 
ddae20c0
 	if checkpoint != "" {
 		checkpointDir, err = getCheckpointDir(checkpointDir, checkpoint, container.Name, container.ID, container.CheckpointDir(), false)
 		if err != nil {
 			return err
 		}
 	}
 
 	createOptions, err := daemon.getLibcontainerdCreateOptions(container)
 	if err != nil {
d97a00df
 		return err
 	}
 
ddae20c0
 	err = daemon.containerd.Create(context.Background(), container.ID, spec, createOptions)
 	if err != nil {
 		return translateContainerdStartErr(container.Path, container.SetExitCode, err)
 	}
 
 	// TODO(mlaventure): we need to specify checkpoint options here
 	pid, err := daemon.containerd.Start(context.Background(), container.ID, checkpointDir,
 		container.StreamConfig.Stdin() != nil || container.Config.Tty,
 		container.InitializeStdio)
 	if err != nil {
 		if err := daemon.containerd.Delete(context.Background(), container.ID); err != nil {
 			logrus.WithError(err).WithField("container", container.ID).
 				Error("failed to delete failed start container")
 		}
ebcb7d6b
 		return translateContainerdStartErr(container.Path, container.SetExitCode, err)
ddae20c0
 	}
 
 	container.SetRunning(pid, true)
 	container.HasBeenManuallyStopped = false
 	container.HasBeenStartedBefore = true
 	daemon.setStateCounter(container)
 
 	daemon.initHealthMonitor(container)
83ae501f
 
ddae20c0
 	if err := container.CheckpointTo(daemon.containersReplica); err != nil {
 		logrus.WithError(err).WithField("container", container.ID).
 			Errorf("failed to store container")
3a497650
 	}
 
ddae20c0
 	daemon.LogContainerEvent(container, "start")
3343d234
 	containerActions.WithValues("start").UpdateSince(start)
 
8da44905
 	return nil
ca5ede2d
 }
 
019c337b
 // Cleanup releases any network resources allocated to the container along with any rules
 // around how containers are linked together.  It also unmounts the container's root filesystem.
6bb0d181
 func (daemon *Daemon) Cleanup(container *container.Container) {
019c337b
 	daemon.releaseNetwork(container)
 
7120976d
 	if err := container.UnmountIpcMount(detachMounted); err != nil {
 		logrus.Warnf("%s cleanup: failed to unmount IPC: %s", container.ID, err)
 	}
019c337b
 
9c4570a9
 	if err := daemon.conditionalUnmountOnCleanup(container); err != nil {
 		// FIXME: remove once reference counting for graphdrivers has been refactored
 		// Ensure that all the mounts are gone
afd305c4
 		if mountid, err := daemon.layerStores[container.OS].GetMountID(container.ID); err == nil {
9c4570a9
 			daemon.cleanupMountsByID(mountid)
 		}
 	}
019c337b
 
3716ec25
 	if err := container.UnmountSecrets(); err != nil {
 		logrus.Warnf("%s cleanup: failed to unmount secrets: %s", container.ID, err)
 	}
 
eaa51928
 	if err := mount.RecursiveUnmount(container.Root); err != nil {
 		logrus.WithError(err).WithField("container", container.ID).Warn("Error while cleaning up container resource mounts.")
 	}
 
6bb0d181
 	for _, eConfig := range container.ExecCommands.Commands() {
9ca2e4e8
 		daemon.unregisterExecCommand(container, eConfig)
019c337b
 	}
 
7a7357da
 	if container.BaseFS != nil && container.BaseFS.Path() != "" {
9a2d0bc3
 		if err := container.UnmountVolumes(daemon.LogVolumeEvent); err != nil {
05cc737f
 			logrus.Warnf("%s cleanup: Failed to umount volumes: %v", container.ID, err)
 		}
019c337b
 	}
ddae20c0
 
7bb815e2
 	container.CancelAttachContext()
ddae20c0
 
 	if err := daemon.containerd.Delete(context.Background(), container.ID); err != nil {
 		logrus.Errorf("%s cleanup: failed to delete container from containerd: %v", container.ID, err)
 	}
019c337b
 }