github.com/rawahars/moby@v24.0.4+incompatible/daemon/start.go (about)

     1  package daemon // import "github.com/docker/docker/daemon"
     2  
     3  import (
     4  	"context"
     5  	"runtime"
     6  	"time"
     7  
     8  	"github.com/docker/docker/api/types"
     9  	containertypes "github.com/docker/docker/api/types/container"
    10  	"github.com/docker/docker/container"
    11  	"github.com/docker/docker/errdefs"
    12  	"github.com/docker/docker/libcontainerd"
    13  	"github.com/pkg/errors"
    14  	"github.com/sirupsen/logrus"
    15  )
    16  
    17  // ContainerStart starts a container.
    18  func (daemon *Daemon) ContainerStart(ctx context.Context, name string, hostConfig *containertypes.HostConfig, checkpoint string, checkpointDir string) error {
    19  	if checkpoint != "" && !daemon.HasExperimental() {
    20  		return errdefs.InvalidParameter(errors.New("checkpoint is only supported in experimental mode"))
    21  	}
    22  
    23  	ctr, err := daemon.GetContainer(name)
    24  	if err != nil {
    25  		return err
    26  	}
    27  
    28  	validateState := func() error {
    29  		ctr.Lock()
    30  		defer ctr.Unlock()
    31  
    32  		if ctr.Paused {
    33  			return errdefs.Conflict(errors.New("cannot start a paused container, try unpause instead"))
    34  		}
    35  
    36  		if ctr.Running {
    37  			return containerNotModifiedError{running: true}
    38  		}
    39  
    40  		if ctr.RemovalInProgress || ctr.Dead {
    41  			return errdefs.Conflict(errors.New("container is marked for removal and cannot be started"))
    42  		}
    43  		return nil
    44  	}
    45  
    46  	if err := validateState(); err != nil {
    47  		return err
    48  	}
    49  
    50  	// Windows does not have the backwards compatibility issue here.
    51  	if runtime.GOOS != "windows" {
    52  		// This is kept for backward compatibility - hostconfig should be passed when
    53  		// creating a container, not during start.
    54  		if hostConfig != nil {
    55  			logrus.Warn("DEPRECATED: Setting host configuration options when the container starts is deprecated and has been removed in Docker 1.12")
    56  			oldNetworkMode := ctr.HostConfig.NetworkMode
    57  			if err := daemon.setSecurityOptions(ctr, hostConfig); err != nil {
    58  				return errdefs.InvalidParameter(err)
    59  			}
    60  			if err := daemon.mergeAndVerifyLogConfig(&hostConfig.LogConfig); err != nil {
    61  				return errdefs.InvalidParameter(err)
    62  			}
    63  			if err := daemon.setHostConfig(ctr, hostConfig); err != nil {
    64  				return errdefs.InvalidParameter(err)
    65  			}
    66  			newNetworkMode := ctr.HostConfig.NetworkMode
    67  			if string(oldNetworkMode) != string(newNetworkMode) {
    68  				// if user has change the network mode on starting, clean up the
    69  				// old networks. It is a deprecated feature and has been removed in Docker 1.12
    70  				ctr.NetworkSettings.Networks = nil
    71  			}
    72  			if err := ctr.CheckpointTo(daemon.containersReplica); err != nil {
    73  				return errdefs.System(err)
    74  			}
    75  			ctr.InitDNSHostConfig()
    76  		}
    77  	} else {
    78  		if hostConfig != nil {
    79  			return errdefs.InvalidParameter(errors.New("Supplying a hostconfig on start is not supported. It should be supplied on create"))
    80  		}
    81  	}
    82  
    83  	// check if hostConfig is in line with the current system settings.
    84  	// It may happen cgroups are umounted or the like.
    85  	if _, err = daemon.verifyContainerSettings(ctr.HostConfig, nil, false); err != nil {
    86  		return errdefs.InvalidParameter(err)
    87  	}
    88  	// Adapt for old containers in case we have updates in this function and
    89  	// old containers never have chance to call the new function in create stage.
    90  	if hostConfig != nil {
    91  		if err := daemon.adaptContainerSettings(ctr.HostConfig, false); err != nil {
    92  			return errdefs.InvalidParameter(err)
    93  		}
    94  	}
    95  	return daemon.containerStart(ctx, ctr, checkpoint, checkpointDir, true)
    96  }
    97  
    98  // containerStart prepares the container to run by setting up everything the
    99  // container needs, such as storage and networking, as well as links
   100  // between containers. The container is left waiting for a signal to
   101  // begin running.
   102  func (daemon *Daemon) containerStart(ctx context.Context, container *container.Container, checkpoint string, checkpointDir string, resetRestartManager bool) (retErr error) {
   103  	start := time.Now()
   104  	container.Lock()
   105  	defer container.Unlock()
   106  
   107  	if resetRestartManager && container.Running { // skip this check if already in restarting step and resetRestartManager==false
   108  		return nil
   109  	}
   110  
   111  	if container.RemovalInProgress || container.Dead {
   112  		return errdefs.Conflict(errors.New("container is marked for removal and cannot be started"))
   113  	}
   114  
   115  	if checkpointDir != "" {
   116  		// TODO(mlaventure): how would we support that?
   117  		return errdefs.Forbidden(errors.New("custom checkpointdir is not supported"))
   118  	}
   119  
   120  	// if we encounter an error during start we need to ensure that any other
   121  	// setup has been cleaned up properly
   122  	defer func() {
   123  		if retErr != nil {
   124  			container.SetError(retErr)
   125  			// if no one else has set it, make sure we don't leave it at zero
   126  			if container.ExitCode() == 0 {
   127  				container.SetExitCode(exitUnknown)
   128  			}
   129  			if err := container.CheckpointTo(daemon.containersReplica); err != nil {
   130  				logrus.Errorf("%s: failed saving state on start failure: %v", container.ID, err)
   131  			}
   132  			container.Reset(false)
   133  
   134  			daemon.Cleanup(container)
   135  			// if containers AutoRemove flag is set, remove it after clean up
   136  			if container.HostConfig.AutoRemove {
   137  				container.Unlock()
   138  				if err := daemon.ContainerRm(container.ID, &types.ContainerRmConfig{ForceRemove: true, RemoveVolume: true}); err != nil {
   139  					logrus.Errorf("can't remove container %s: %v", container.ID, err)
   140  				}
   141  				container.Lock()
   142  			}
   143  		}
   144  	}()
   145  
   146  	if err := daemon.conditionalMountOnStart(container); err != nil {
   147  		return err
   148  	}
   149  
   150  	if err := daemon.initializeNetworking(container); err != nil {
   151  		return err
   152  	}
   153  
   154  	spec, err := daemon.createSpec(ctx, container)
   155  	if err != nil {
   156  		return errdefs.System(err)
   157  	}
   158  
   159  	if resetRestartManager {
   160  		container.ResetRestartManager(true)
   161  		container.HasBeenManuallyStopped = false
   162  	}
   163  
   164  	if err := daemon.saveAppArmorConfig(container); err != nil {
   165  		return err
   166  	}
   167  
   168  	if checkpoint != "" {
   169  		checkpointDir, err = getCheckpointDir(checkpointDir, checkpoint, container.Name, container.ID, container.CheckpointDir(), false)
   170  		if err != nil {
   171  			return err
   172  		}
   173  	}
   174  
   175  	shim, createOptions, err := daemon.getLibcontainerdCreateOptions(container)
   176  	if err != nil {
   177  		return err
   178  	}
   179  
   180  	ctr, err := libcontainerd.ReplaceContainer(ctx, daemon.containerd, container.ID, spec, shim, createOptions)
   181  	if err != nil {
   182  		return setExitCodeFromError(container.SetExitCode, err)
   183  	}
   184  
   185  	// TODO(mlaventure): we need to specify checkpoint options here
   186  	tsk, err := ctr.Start(context.TODO(), // Passing ctx to ctr.Start caused integration tests to be stuck in the cleanup phase
   187  		checkpointDir, container.StreamConfig.Stdin() != nil || container.Config.Tty,
   188  		container.InitializeStdio)
   189  	if err != nil {
   190  		if err := ctr.Delete(context.Background()); err != nil {
   191  			logrus.WithError(err).WithField("container", container.ID).
   192  				Error("failed to delete failed start container")
   193  		}
   194  		return setExitCodeFromError(container.SetExitCode, err)
   195  	}
   196  
   197  	container.HasBeenManuallyRestarted = false
   198  	container.SetRunning(ctr, tsk, true)
   199  	container.HasBeenStartedBefore = true
   200  	daemon.setStateCounter(container)
   201  
   202  	daemon.initHealthMonitor(container)
   203  
   204  	if err := container.CheckpointTo(daemon.containersReplica); err != nil {
   205  		logrus.WithError(err).WithField("container", container.ID).
   206  			Errorf("failed to store container")
   207  	}
   208  
   209  	daemon.LogContainerEvent(container, "start")
   210  	containerActions.WithValues("start").UpdateSince(start)
   211  
   212  	return nil
   213  }
   214  
   215  // Cleanup releases any network resources allocated to the container along with any rules
   216  // around how containers are linked together.  It also unmounts the container's root filesystem.
   217  func (daemon *Daemon) Cleanup(container *container.Container) {
   218  	// Microsoft HCS containers get in a bad state if host resources are
   219  	// released while the container still exists.
   220  	if ctr, ok := container.C8dContainer(); ok {
   221  		if err := ctr.Delete(context.Background()); err != nil {
   222  			logrus.Errorf("%s cleanup: failed to delete container from containerd: %v", container.ID, err)
   223  		}
   224  	}
   225  
   226  	daemon.releaseNetwork(container)
   227  
   228  	if err := container.UnmountIpcMount(); err != nil {
   229  		logrus.Warnf("%s cleanup: failed to unmount IPC: %s", container.ID, err)
   230  	}
   231  
   232  	if err := daemon.conditionalUnmountOnCleanup(container); err != nil {
   233  		// FIXME: remove once reference counting for graphdrivers has been refactored
   234  		// Ensure that all the mounts are gone
   235  		if mountid, err := daemon.imageService.GetLayerMountID(container.ID); err == nil {
   236  			daemon.cleanupMountsByID(mountid)
   237  		}
   238  	}
   239  
   240  	if err := container.UnmountSecrets(); err != nil {
   241  		logrus.Warnf("%s cleanup: failed to unmount secrets: %s", container.ID, err)
   242  	}
   243  
   244  	if err := recursiveUnmount(container.Root); err != nil {
   245  		logrus.WithError(err).WithField("container", container.ID).Warn("Error while cleaning up container resource mounts.")
   246  	}
   247  
   248  	for _, eConfig := range container.ExecCommands.Commands() {
   249  		daemon.unregisterExecCommand(container, eConfig)
   250  	}
   251  
   252  	if container.BaseFS != "" {
   253  		if err := container.UnmountVolumes(daemon.LogVolumeEvent); err != nil {
   254  			logrus.Warnf("%s cleanup: Failed to umount volumes: %v", container.ID, err)
   255  		}
   256  	}
   257  
   258  	container.CancelAttachContext()
   259  }