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 }