// 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) containerKnownStatus := container.GetKnownStatus() containerDesiredStatus := container.GetDesiredStatus() if containerKnownStatus == containerDesiredStatus { clog.Debug("Container at desired status", "desired", containerDesiredStatus) return api.ContainerStatusNone, false, false } if containerKnownStatus > containerDesiredStatus { 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", containerDesiredStatus) return api.ContainerStatusNone, false, false } var nextState api.ContainerStatus if container.DesiredTerminal() { nextState = api.ContainerStopped if containerKnownStatus != 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 = containerKnownStatus + 1 } return nextState, true, true }
// 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 linkIsResolved(target *api.Container, link *api.Container) bool { targetDesiredStatus := target.GetDesiredStatus() if targetDesiredStatus == api.ContainerCreated { knownStatus := link.GetKnownStatus() return knownStatus == api.ContainerCreated || knownStatus == api.ContainerRunning } else if targetDesiredStatus == api.ContainerRunning { return link.GetKnownStatus() == 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 }
// 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 }