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