agent/engine/task_manager.go
func (mtask *managedTask) containerNextState(container *apicontainer.Container) *containerTransition {
	containerKnownStatus := container.GetKnownStatus()
	containerDesiredStatus := container.GetDesiredStatus()

	if containerKnownStatus == containerDesiredStatus {
		seelog.Debugf("Managed task [%s]: container [%s] at desired status: %s",
			mtask.Arn, container.Name, containerDesiredStatus.String())
		return &containerTransition{
			nextState:      apicontainerstatus.ContainerStatusNone,
			actionRequired: false,
			reason:         dependencygraph.ContainerPastDesiredStatusErr,
		}
	}

	if containerKnownStatus > containerDesiredStatus {
		seelog.Debugf("Managed task [%s]: container [%s] has already transitioned beyond desired status(%s): %s",
			mtask.Arn, container.Name, containerKnownStatus.String(), containerDesiredStatus.String())
		return &containerTransition{
			nextState:      apicontainerstatus.ContainerStatusNone,
			actionRequired: false,
			reason:         dependencygraph.ContainerPastDesiredStatusErr,
		}
	}
	if blocked, err := dependencygraph.DependenciesAreResolved(container, mtask.Containers,
		mtask.Task.GetExecutionCredentialsID(), mtask.credentialsManager, mtask.GetResources()); err != nil {
		seelog.Debugf("Managed task [%s]: can't apply state to container [%s] yet due to unresolved dependencies: %v",
			mtask.Arn, container.Name, err)
		return &containerTransition{
			nextState:      apicontainerstatus.ContainerStatusNone,
			actionRequired: false,
			reason:         err,
			blockedOn:      blocked,
		}
	}

	var nextState apicontainerstatus.ContainerStatus
	if container.DesiredTerminal() {
		nextState = apicontainerstatus.ContainerStopped
		// It's not enough to just check if container is in steady state here
		// we should really check if >= RUNNING <= STOPPED
		if !container.IsRunning() {
			// If the container's AppliedStatus is running, it means the StartContainer
			// api call has already been scheduled, we should not mark it as stopped
			// directly, because when the stopped container comes back, we will end up
			// with either:
			// 1. The task is not cleaned up, the handleStoppedToRunningContainerTransition
			// function will handle this case, but only once. If there are some
			// other stopped containers come back, they will not be stopped by
			// Agent.
			// 2. The task has already been cleaned up, in this case any stopped container
			// will not be stopped by Agent when they come back.
			if container.GetAppliedStatus() == apicontainerstatus.ContainerRunning {
				nextState = apicontainerstatus.ContainerStatusNone
			}

			return &containerTransition{
				nextState:      nextState,
				actionRequired: false,
			}
		}
	} else {
		nextState = container.GetNextKnownStateProgression()
	}
	return &containerTransition{
		nextState:      nextState,
		actionRequired: true,
	}
}
func (mtask *managedTask) containerNextState(container *apicontainer.Container) *containerTransition {
	containerKnownStatus := container.GetKnownStatus()
	containerDesiredStatus := container.GetDesiredStatus()

	if containerKnownStatus == containerDesiredStatus {
		seelog.Debugf("Managed task [%s]: container [%s] at desired status: %s",
			mtask.Arn, container.Name, containerDesiredStatus.String())
		return &containerTransition{
			nextState:      apicontainerstatus.ContainerStatusNone,
			actionRequired: false,
			reason:         dependencygraph.ContainerPastDesiredStatusErr,
		}
	}

	if containerKnownStatus > containerDesiredStatus {
		seelog.Debugf("Managed task [%s]: container [%s] has already transitioned beyond desired status(%s): %s",
			mtask.Arn, container.Name, containerKnownStatus.String(), containerDesiredStatus.String())
		return &containerTransition{
			nextState:      apicontainerstatus.ContainerStatusNone,
			actionRequired: false,
			reason:         dependencygraph.ContainerPastDesiredStatusErr,
		}
	}
	if blocked, err := dependencygraph.DependenciesAreResolved(container, mtask.Containers,
		mtask.Task.GetExecutionCredentialsID(), mtask.credentialsManager, mtask.GetResources()); err != nil {
		seelog.Debugf("Managed task [%s]: can't apply state to container [%s] yet due to unresolved dependencies: %v",
			mtask.Arn, container.Name, err)
		return &containerTransition{
			nextState:      apicontainerstatus.ContainerStatusNone,
			actionRequired: false,
			reason:         err,
			blockedOn:      blocked,
		}
	}

	var nextState apicontainerstatus.ContainerStatus
	if container.DesiredTerminal() {
		nextState = apicontainerstatus.ContainerStopped
		// It's not enough to just check if container is in steady state here
		// we should really check if >= RUNNING <= STOPPED
		if !container.IsRunning() {
			// If the container's AppliedStatus is running, it means the StartContainer
			// api call has already been scheduled, we should not mark it as stopped
			// directly, because when the stopped container comes back, we will end up
			// with either:
			// 1. The task is not cleaned up, the handleStoppedToRunningContainerTransition
			// function will handle this case, but only once. If there are some
			// other stopped containers come back, they will not be stopped by
			// Agent.
			// 2. The task has already been cleaned up, in this case any stopped container
			// will not be stopped by Agent when they come back.
			if container.GetAppliedStatus() == apicontainerstatus.ContainerRunning {
				nextState = apicontainerstatus.ContainerStatusNone
			}

			return &containerTransition{
				nextState:      nextState,
				actionRequired: false,
			}
		}
	} else {
		nextState = container.GetNextKnownStateProgression()
	}
	return &containerTransition{
		nextState:      nextState,
		actionRequired: true,
	}
}
func ( mtask * managedTask ) containerNextState ( container * apicontainer . Container ) * containerTransition { containerKnownStatus := container . GetKnownStatus ( ) containerDesiredStatus := container . GetDesiredStatus ( ) if containerKnownStatus == containerDesiredStatus { seelog . Debugf ( " " , mtask . Arn , container . Name , containerDesiredStatus . String ( ) ) return & containerTransition { nextState : apicontainerstatus . ContainerStatusNone , actionRequired : false , reason : dependencygraph . ContainerPastDesiredStatusErr , } } if containerKnownStatus > containerDesiredStatus { seelog . Debugf ( " " , mtask . Arn , container . Name , containerKnownStatus . String ( ) , containerDesiredStatus . String ( ) ) return & containerTransition { nextState : apicontainerstatus . ContainerStatusNone , actionRequired : false , reason : dependencygraph . ContainerPastDesiredStatusErr , } } if blocked , err := dependencygraph . DependenciesAreResolved ( container , mtask . Containers , mtask . Task . GetExecutionCredentialsID ( ) , mtask . credentialsManager , mtask . GetResources ( ) ) ; err != nil { seelog . Debugf ( " " , mtask . Arn , container . Name , err ) return & containerTransition { nextState : apicontainerstatus . ContainerStatusNone , actionRequired : false , reason : err , blockedOn : blocked , } } var nextState apicontainerstatus . ContainerStatus if container . DesiredTerminal ( ) { nextState = apicontainerstatus . ContainerStopped // It's not enough to just check if container is in steady state here // we should really check if >= RUNNING <= STOPPED if ! container . IsRunning ( ) { // If the container's AppliedStatus is running, it means the StartContainer // api call has already been scheduled, we should not mark it as stopped // directly, because when the stopped container comes back, we will end up // with either: // 1. The task is not cleaned up, the handleStoppedToRunningContainerTransition // function will handle this case, but only once. If there are some // other stopped containers come back, they will not be stopped by // Agent. // 2. The task has already been cleaned up, in this case any stopped container // will not be stopped by Agent when they come back. if container . GetAppliedStatus ( ) == apicontainerstatus . ContainerRunning { nextState = apicontainerstatus . ContainerStatusNone } return & containerTransition { nextState : nextState , actionRequired : false , } } } else { nextState = container . GetNextKnownStateProgression ( ) } return & containerTransition { nextState : nextState , actionRequired : true , } }
// containerNextState determines the next state a container should go to.
// It returns a transition struct including the information:
// * container state it should transition to,
// * a bool indicating whether any action is required
// * an error indicating why this transition can't happen
//
// 'Stopped, false, ""' -> "You can move it to known stopped, but you don't have to call a transition function"
// 'Running, true, ""' -> "You can move it to running and you need to call the transition function"
// 'None, false, containerDependencyNotResolved' -> "This should not be moved; it has unresolved dependencies;"
//
// Next status is determined by whether the known and desired statuses are
// equal, the next numerically greater (iota-wise) status, and whether
// dependencies are fully resolved.
containerNextState determines the next state a container should go to . It returns a transition struct including the information : * container state it should transition to * a bool indicating whether any action is required * an error indicating why this transition can t happen Stopped false - > You can move it to known stopped but you don t have to call a transition function Running true - > You can move it to running and you need to call the transition function None false containerDependencyNotResolved - > This should not be moved ; it has unresolved dependencies ; Next status is determined by whether the known and desired statuses are equal the next numerically greater ( iota - wise ) status and whether dependencies are fully resolved .