// containerNextState determines the next state a container should go to. // It returns: The state it should transition to, a bool indicating whether any // action is required, and a bool indicating whether a known status change is // possible. // 'Stopped, false, true' -> "You can move it to known stopped, but you don't have to call a transition function" // 'Running, true, true' -> "You can move it to running and you need to call the transition function" // 'None, false, false' -> "This should not be moved; it has unresolved dependencies or is complete; no knownstatus change" func (mtask *managedTask) containerNextState(container *api.Container) (api.ContainerStatus, bool, bool) { clog := log.New("task", mtask.Task, "container", container) if container.KnownStatus == container.DesiredStatus { clog.Debug("Container at desired status", "desired", container.DesiredStatus) return api.ContainerStatusNone, false, false } if container.KnownStatus > container.DesiredStatus { clog.Debug("Container past desired status") return api.ContainerStatusNone, false, false } if !dependencygraph.DependenciesAreResolved(container, mtask.Containers) { clog.Debug("Can't apply state to container yet; dependencies unresolved", "state", container.DesiredStatus) return api.ContainerStatusNone, false, false } var nextState api.ContainerStatus if container.DesiredTerminal() { nextState = api.ContainerStopped if container.KnownStatus != api.ContainerRunning { // If it's not currently running we do not need to do anything to make it become stopped. return nextState, false, true } } else { nextState = container.KnownStatus + 1 } return nextState, true, true }
func (engine *DockerTaskEngine) pullContainer(task *api.Task, container *api.Container) DockerContainerMetadata { log.Info("Pulling container", "task", task, "container", container) seelog.Debugf("Attempting to obtain ImagePullDeleteLock to pull image - %s", container.Image) ImagePullDeleteLock.Lock() seelog.Debugf("Obtained ImagePullDeleteLock to pull image - %s", container.Image) defer seelog.Debugf("Released ImagePullDeleteLock after pulling image - %s", container.Image) defer ImagePullDeleteLock.Unlock() // If a task is blocked here for some time, and before it starts pulling image, // the task's desired status is set to stopped, then don't pull the image if task.GetDesiredStatus() == api.TaskStopped { seelog.Infof("Task desired status is stopped, skip pull container: %v, task %v", container, task) container.SetDesiredStatus(api.ContainerStopped) return DockerContainerMetadata{Error: TaskStoppedBeforePullBeginError{task.Arn}} } metadata := engine.client.PullImage(container.Image, container.RegistryAuthentication) err := engine.imageManager.AddContainerReferenceToImageState(container) if err != nil { seelog.Errorf("Error adding container reference to image state: %v", err) } imageState := engine.imageManager.GetImageStateFromImageName(container.Image) engine.state.AddImageState(imageState) engine.saver.Save() return metadata }
// emitContainerEvent passes a given event up through the containerEvents channel if necessary. // It will omit events the backend would not process and will perform best-effort deduplication of events. func (engine *DockerTaskEngine) emitContainerEvent(task *api.Task, cont *api.Container, reason string) { contKnownStatus := cont.GetKnownStatus() if !contKnownStatus.BackendRecognized() { return } if cont.IsInternal { return } if cont.SentStatus >= contKnownStatus { log.Debug("Already sent container event; no need to re-send", "task", task.Arn, "container", cont.Name, "event", contKnownStatus.String()) return } if reason == "" && cont.ApplyingError != nil { reason = cont.ApplyingError.Error() } event := api.ContainerStateChange{ TaskArn: task.Arn, ContainerName: cont.Name, Status: contKnownStatus, ExitCode: cont.KnownExitCode, PortBindings: cont.KnownPortBindings, Reason: reason, SentStatus: &cont.SentStatus, } log.Debug("Container change event", "event", event) engine.containerEvents <- event log.Debug("Container change event passed on", "event", event) }
func linkCanResolve(target *api.Container, link *api.Container) bool { targetDesiredStatus := target.GetDesiredStatus() linkDesiredStatus := link.GetDesiredStatus() if targetDesiredStatus == api.ContainerCreated { return linkDesiredStatus == api.ContainerCreated || linkDesiredStatus == api.ContainerRunning } else if targetDesiredStatus == api.ContainerRunning { return linkDesiredStatus == api.ContainerRunning } log.Error("Unexpected desired status", "target", target) return false }
func volumeIsResolved(target *api.Container, volume *api.Container) bool { targetDesiredStatus := target.GetDesiredStatus() if targetDesiredStatus == api.ContainerCreated || targetDesiredStatus == api.ContainerRunning { knownStatus := volume.GetKnownStatus() return knownStatus == api.ContainerCreated || knownStatus == api.ContainerRunning || knownStatus == api.ContainerStopped } log.Error("Unexpected desired status", "target", target) return false }
// verifyStatusResolveable validates that `target` can be resolved given that // target depends on `dependencies` (which are container names) and there are // `existingContainers` (map from name to container). The `resolves` function // passed should return true if the named container is resolved. func verifyStatusResolveable(target *api.Container, existingContainers map[string]*api.Container, dependencies []string, resolves func(*api.Container, *api.Container) bool) bool { targetGoal := target.GetDesiredStatus() if targetGoal != api.ContainerRunning && targetGoal != api.ContainerCreated { // A container can always stop, die, or reach whatever other statre it // wants regardless of what dependencies it has return true } for _, dependency := range dependencies { maybeResolves, exists := existingContainers[dependency] if !exists { return false } if !resolves(target, maybeResolves) { return false } } return true }
func (imageState *ImageState) RemoveContainerReference(container *api.Container) error { // Get the image state write lock for updating container reference imageState.updateLock.Lock() defer imageState.updateLock.Unlock() for i, _ := range imageState.Containers { if imageState.Containers[i].Name == container.Name { // Container reference found; hence remove it seelog.Infof("Removing Container Reference: %v from Image State- %v", container.Name, imageState.Image.ImageID) imageState.Containers = append(imageState.Containers[:i], imageState.Containers[i+1:]...) // Update the last used time for the image imageState.LastUsedAt = time.Now() return nil } } return fmt.Errorf("Container reference is not found in the image state container: %s", container.String()) }
// onRunIsResolved defines a relationship where a target cannot be created until // 'run' has reached a running state. func onRunIsResolved(target *api.Container, run *api.Container) bool { if target.GetDesiredStatus() >= api.ContainerCreated { return run.GetKnownStatus() >= api.ContainerRunning } return false }