func (mtask *managedTask) progressTask() {
	seelog.Debugf("Managed task [%s]: progressing containers and resources in task", mtask.Arn)
	// max number of transitions length to ensure writes will never block on
	// these and if we exit early transitions can exit the goroutine and it'll
	// get GC'd eventually
	resources := mtask.GetResources()
	transitionChange := make(chan struct{}, len(mtask.Containers)+len(resources))
	transitionChangeEntity := make(chan string, len(mtask.Containers)+len(resources))

	// startResourceTransitions should always be called before startContainerTransitions,
	// else it might result in a state where none of the containers can transition and
	// task may be moved to stopped.
	// anyResourceTransition is set to true when transition function needs to be called or
	// known status can be changed
	anyResourceTransition, resTransitions := mtask.startResourceTransitions(
		func(resource taskresource.TaskResource, nextStatus resourcestatus.ResourceStatus) {
			mtask.transitionResource(resource, nextStatus)
			transitionChange <- struct{}{}
			transitionChangeEntity <- resource.GetName()
		})

	anyContainerTransition, blockedDependencies, contTransitions, reasons := mtask.startContainerTransitions(
		func(container *apicontainer.Container, nextStatus apicontainerstatus.ContainerStatus) {
			mtask.engine.transitionContainer(mtask.Task, container, nextStatus)
			transitionChange <- struct{}{}
			transitionChangeEntity <- container.Name
		})

	atLeastOneTransitionStarted := anyResourceTransition || anyContainerTransition

	blockedByOrderingDependencies := len(blockedDependencies) > 0

	// If no transitions happened and we aren't blocked by ordering dependencies, then we are possibly in a state where
	// its impossible for containers to move forward. We will do an additional check to see if we are waiting for ACS
	// execution credentials. If not, then we will abort the task progression.
	if !atLeastOneTransitionStarted && !blockedByOrderingDependencies {
		if !mtask.isWaitingForACSExecutionCredentials(reasons) {
			mtask.handleContainersUnableToTransitionState()
		}
		return
	}

	// If no containers are starting and we are blocked on ordering dependencies, we should watch for the task to change
	// over time. This will update the containers if they become healthy or stop, which makes it possible for the
	// conditions "HEALTHY" and "SUCCESS" to succeed.
	if !atLeastOneTransitionStarted && blockedByOrderingDependencies {
		go mtask.engine.checkTaskState(mtask.Task)
		ctx, cancel := context.WithTimeout(context.Background(), transitionPollTime)
		defer cancel()
		for timeout := mtask.waitEvent(ctx.Done()); !timeout; {
			timeout = mtask.waitEvent(ctx.Done())
		}
		return
	}

	// combine the resource and container transitions
	transitions := make(map[string]string)
	for k, v := range resTransitions {
		transitions[k] = v
	}
	for k, v := range contTransitions {
		transitions[k] = v.String()
	}

	// We've kicked off one or more transitions, wait for them to
	// complete, but keep reading events as we do. in fact, we have to for
	// transitions to complete
	mtask.waitForTransition(transitions, transitionChange, transitionChangeEntity)
	// update the task status
	changed := mtask.UpdateStatus()
	if changed {
		seelog.Debugf("Managed task [%s]: container or resource change also resulted in task change", mtask.Arn)

		// If knownStatus changed, let it be known
		var taskStateChangeReason string
		if mtask.GetKnownStatus().Terminal() {
			taskStateChangeReason = mtask.Task.GetTerminalReason()
		}
		mtask.emitTaskEvent(mtask.Task, taskStateChangeReason)
	}
}

// progressTask tries to step forwards all containers and resources that are able to be
// transitioned in the task's current state.
// It will continue listening to events from all channels while it does so, but
// none of those changes will be acted upon until this set of requests to
// docker completes.
// Container changes may also prompt the task status to change as well.

progressTask tries to step forwards all containers and resources that are able to be transitioned in the task s current state . It will continue listening to events from all channels while it does so but none of those changes will be acted upon until this set of requests to docker completes . Container changes may also prompt the task status to change as well .


func (mtask *managedTask) isWaitingForACSExecutionCredentials(reasons []error) bool {
	for _, reason := range reasons {
		if reason == dependencygraph.CredentialsNotResolvedErr {
			seelog.Debugf("Managed task [%s]: waiting for credentials to pull from ECR", mtask.Arn)

			timeoutCtx, timeoutCancel := context.WithTimeout(mtask.ctx, waitForPullCredentialsTimeout)
			defer timeoutCancel()

			timedOut := mtask.waitEvent(timeoutCtx.Done())
			if timedOut {
				seelog.Debugf("Managed task [%s]: timed out waiting for acs credentials message", mtask.Arn)
			}
			return true
		}
	}
	return false
}

func ( mtask * managedTask ) isWaitingForACSExecutionCredentials ( reasons [ ] error ) bool { for _ , reason := range reasons { if reason == dependencygraph . CredentialsNotResolvedErr { seelog . Debugf ( " " , mtask . Arn ) timeoutCtx , timeoutCancel := context . WithTimeout ( mtask . ctx , waitForPullCredentialsTimeout ) defer timeoutCancel ( ) timedOut := mtask . waitEvent ( timeoutCtx . Done ( ) ) if timedOut { seelog . Debugf ( " " , mtask . Arn ) } return true } } return false }

// isWaitingForACSExecutionCredentials checks if the container that can't be transitioned
// was caused by waiting for credentials and start waiting

isWaitingForACSExecutionCredentials checks if the container that can t be transitioned was caused by waiting for credentials and start waiting


func (mtask *managedTask) startContainerTransitions(transitionFunc containerTransitionFunc) (bool, map[string]apicontainer.DependsOn, map[string]apicontainerstatus.ContainerStatus, []error) {
	anyCanTransition := false
	var reasons []error
	blocked := make(map[string]apicontainer.DependsOn)
	transitions := make(map[string]apicontainerstatus.ContainerStatus)
	for _, cont := range mtask.Containers {
		transition := mtask.containerNextState(cont)
		if transition.reason != nil {
			// container can't be transitioned
			reasons = append(reasons, transition.reason)
			if transition.blockedOn != nil {
				blocked[cont.Name] = *transition.blockedOn
			}
			continue
		}

		// If the container is already in a transition, skip
		if transition.actionRequired && !cont.SetAppliedStatus(transition.nextState) {
			// At least one container is able to be moved forwards, so we're not deadlocked
			anyCanTransition = true
			continue
		}

		// At least one container is able to be moved forwards, so we're not deadlocked
		anyCanTransition = true

		if !transition.actionRequired {
			// Updating the container status without calling any docker API, send in
			// a goroutine so that it won't block here before the waitForContainerTransition
			// was called after this function. And all the events sent to mtask.dockerMessages
			// will be handled by handleContainerChange.
			go func(cont *apicontainer.Container, status apicontainerstatus.ContainerStatus) {
				mtask.dockerMessages <- dockerContainerChange{
					container: cont,
					event: dockerapi.DockerContainerChangeEvent{
						Status: status,
					},
				}
			}(cont, transition.nextState)
			continue
		}
		transitions[cont.Name] = transition.nextState
		go transitionFunc(cont, transition.nextState)
	}

	return anyCanTransition, blocked, transitions, reasons
}

// startContainerTransitions steps through each container in the task and calls
// the passed transition function when a transition should occur.

startContainerTransitions steps through each container in the task and calls the passed transition function when a transition should occur .


func (mtask *managedTask) startResourceTransitions(transitionFunc resourceTransitionFunc) (bool, map[string]string) {
	anyCanTransition := false
	transitions := make(map[string]string)
	for _, res := range mtask.GetResources() {
		knownStatus := res.GetKnownStatus()
		desiredStatus := res.GetDesiredStatus()
		if knownStatus >= desiredStatus {
			seelog.Debugf("Managed task [%s]: resource [%s] has already transitioned to or beyond the desired status %s; current known is %s",
				mtask.Arn, res.GetName(), res.StatusString(desiredStatus), res.StatusString(knownStatus))
			continue
		}
		anyCanTransition = true
		transition := mtask.resourceNextState(res)
		// If the resource is already in a transition, skip
		if transition.actionRequired && !res.SetAppliedStatus(transition.nextState) {
			// At least one resource is able to be moved forwards, so we're not deadlocked
			continue
		}
		if !transition.actionRequired {
			// no action is required for the transition, just set the known status without
			// calling any transition function
			go mtask.emitResourceChange(resourceStateChange{
				resource:  res,
				nextState: transition.nextState,
				err:       nil,
			})
			continue
		}
		// At least one resource is able to be move forwards, so we're not deadlocked
		transitions[res.GetName()] = transition.status
		go transitionFunc(res, transition.nextState)
	}
	return anyCanTransition, transitions
}

// startResourceTransitions steps through each resource in the task and calls
// the passed transition function when a transition should occur

startResourceTransitions steps through each resource in the task and calls the passed transition function when a transition should occur


func (mtask *managedTask) transitionResource(resource taskresource.TaskResource,
	to resourcestatus.ResourceStatus) {
	err := mtask.applyResourceState(resource, to)

	if mtask.engine.isTaskManaged(mtask.Arn) {
		mtask.emitResourceChange(resourceStateChange{
			resource:  resource,
			nextState: to,
			err:       err,
		})
	}
}

func ( mtask * managedTask ) transitionResource ( resource taskresource . TaskResource , to resourcestatus . ResourceStatus ) { err := mtask . applyResourceState ( resource , to ) if mtask . engine . isTaskManaged ( mtask . Arn ) { mtask . emitResourceChange ( resourceStateChange { resource : resource , nextState : to , err : err , } ) } }

// transitionResource calls applyResourceState, and then notifies the managed
// task of the change. transitionResource is called by progressTask

transitionResource calls applyResourceState and then notifies the managed task of the change . transitionResource is called by progressTask


func (mtask *managedTask) applyResourceState(resource taskresource.TaskResource,
	nextState resourcestatus.ResourceStatus) error {
	resName := resource.GetName()
	resStatus := resource.StatusString(nextState)
	err := resource.ApplyTransition(nextState)
	if err != nil {
		seelog.Infof("Managed task [%s]: error transitioning resource [%s] to [%s]: %v",
			mtask.Arn, resName, resStatus, err)
		return err
	}
	seelog.Debugf("Managed task [%s]: transitioned resource [%s] to [%s]",
		mtask.Arn, resName, resStatus)
	return nil
}

func ( mtask * managedTask ) applyResourceState ( resource taskresource . TaskResource , nextState resourcestatus . ResourceStatus ) error { resName := resource . GetName ( ) resStatus := resource . StatusString ( nextState ) err := resource . ApplyTransition ( nextState ) if err != nil { seelog . Infof ( " " , mtask . Arn , resName , resStatus , err ) return err } seelog . Debugf ( " " , mtask . Arn , resName , resStatus ) return nil }

// applyResourceState moves the resource to the given state by calling the
// function defined in the transitionFunctionMap for the state

applyResourceState moves the resource to the given state by calling the function defined in the transitionFunctionMap for the state


func (mtask *managedTask) waitForStopReported() bool {
	stoppedSentBool := make(chan struct{})
	taskStopped := false
	go func() {
		for i := 0; i < _maxStoppedWaitTimes; i++ {
			// ensure that we block until apitaskstatus.TaskStopped is actually sent
			sentStatus := mtask.GetSentStatus()
			if sentStatus >= apitaskstatus.TaskStopped {
				taskStopped = true
				break
			}
			seelog.Warnf("Managed task [%s]: blocking cleanup until the task has been reported stopped. SentStatus: %s (%d/%d)",
				mtask.Arn, sentStatus.String(), i+1, _maxStoppedWaitTimes)
			mtask._time.Sleep(_stoppedSentWaitInterval)
		}
		stoppedSentBool <- struct{}{}
		close(stoppedSentBool)
	}()
	// wait for apitaskstatus.TaskStopped to be sent
	for !mtask.waitEvent(stoppedSentBool) {
	}
	return taskStopped
}

// waitForStopReported will wait for the task to be reported stopped and return true, or will time-out and return false.
// Messages on the mtask.dockerMessages and mtask.acsMessages channels will be handled while this function is waiting.

waitForStopReported will wait for the task to be reported stopped and return true or will time - out and return false . Messages on the mtask . dockerMessages and mtask . acsMessages channels will be handled while this function is waiting .


func NewFactory(ctx context.Context, endpoint string) Factory {
	return &factory{
		endpoint: endpoint,
		clients:  findDockerVersions(ctx, endpoint),
	}
}

func NewFactory ( ctx context . Context , endpoint string ) Factory { return & factory { endpoint : endpoint , clients : findDockerVersions ( ctx , endpoint ) , } }

// NewFactory initializes a client factory using a specified endpoint.

NewFactory initializes a client factory using a specified endpoint .


func (f *factory) FindClientAPIVersion(client sdkclient.Client) dockerclient.DockerVersion {
	return dockerclient.DockerVersion(client.ClientVersion())
}

func ( f * factory ) FindClientAPIVersion ( client sdkclient . Client ) dockerclient . DockerVersion { return dockerclient . DockerVersion ( client . ClientVersion ( ) ) }

// FindClientAPIVersion returns the version of the client from the map


func (f *factory) getClient(version dockerclient.DockerVersion) (sdkclient.Client, error) {
	client, ok := f.clients[version]
	if !ok {
		return nil, errors.New("docker client factory: client not found for docker version: " + string(version))
	}
	return client, nil
}

func ( f * factory ) getClient ( version dockerclient . DockerVersion ) ( sdkclient . Client , error ) { client , ok := f . clients [ version ] if ! ok { return nil , errors . New ( " " + string ( version ) ) } return client , nil }

// getClient returns a client specified by the docker version. It's wrapped
// by GetClient so that it can do platform-specific magic.

getClient returns a client specified by the docker version . It s wrapped by GetClient so that it can do platform - specific magic .


func findDockerVersions(ctx context.Context, endpoint string) map[dockerclient.DockerVersion]sdkclient.Client {
	// if the client version returns a MinAPIVersion, use it to return all the
	// Docker clients between MinAPIVersion and APIVersion, else try pinging
	// the clients in getKnownAPIVersions
	var minAPIVersion, apiVersion string
	// get a Docker client with the default supported version
	client, err := newVersionedClient(endpoint, string(minDockerAPIVersion))
	if err == nil {
		derivedCtx, cancel := context.WithTimeout(ctx, dockerclient.VersionTimeout)
		defer cancel()
		serverVersion, err := client.ServerVersion(derivedCtx)
		if err == nil {
			apiVersion = serverVersion.APIVersion
			// check if the docker.Version has MinAPIVersion value
			if serverVersion.MinAPIVersion != "" {
				minAPIVersion = serverVersion.MinAPIVersion
			}
		}
	}
	clients := make(map[dockerclient.DockerVersion]sdkclient.Client)
	for _, version := range dockerclient.GetKnownAPIVersions() {
		dockerClient, err := getDockerClientForVersion(endpoint, string(version), minAPIVersion, apiVersion, ctx)
		if err != nil {
			log.Infof("Unable to get Docker client for version %s: %v", version, err)
			continue
		}
		clients[version] = dockerClient
	}
	return clients
}

// findDockerVersions loops over all known API versions and finds which ones
// are supported by the docker daemon on the host

findDockerVersions loops over all known API versions and finds which ones are supported by the docker daemon on the host


func (h *handler) runAgent(ctx context.Context) uint32 {
	agentCtx, cancel := context.WithCancel(ctx)
	indicator := newTermHandlerIndicator()

	terminationHandler := func(saver statemanager.Saver, taskEngine engine.TaskEngine) {
		// We're using a custom indicator to record that the handler is scheduled to be executed (has been invoked) and
		// to determine whether it should run (we skip when the agent engine has already exited).  After recording to
		// the indicator that the handler has been invoked, we wait on the context.  When we wake up, we determine
		// whether to execute or not based on whether the agent is still running.
		defer indicator.finish()
		indicator.setInvoked()
		<-agentCtx.Done()
		if !indicator.isAgentRunning() {
			return
		}

		seelog.Info("Termination handler received signal to stop")
		err := sighandlers.FinalSave(saver, taskEngine)
		if err != nil {
			seelog.Criticalf("Error saving state before final shutdown: %v", err)
		}
	}
	h.ecsAgent.setTerminationHandler(terminationHandler)

	go func() {
		defer cancel()
		exitCode := h.ecsAgent.start() // should block forever, unless there is an error

		if exitCode == exitcodes.ExitTerminal {
			seelog.Critical("Terminal exit code received from agent.  Windows SCM will not restart the AmazonECS service.")
			// We override the exit code to 0 here so that Windows does not treat this as a restartable failure even
			// when "sc.exe failureflag" is set.
			exitCode = 0
		}

		indicator.agentStopped(exitCode)
	}()

	sleepCtx(agentCtx, time.Minute) // give the agent a minute to start and invoke terminationHandler

	// wait for the termination handler to run.  Once the termination handler runs, we can safely exit.  If the agent
	// exits by itself, the termination handler doesn't need to do anything and skips.  If the agent exits before the
	// termination handler is invoked, we can exit immediately.
	return indicator.wait()
}

// runAgent runs the ECS agent inside a goroutine and waits to be told to exit.

runAgent runs the ECS agent inside a goroutine and waits to be told to exit .


func sleepCtx(ctx context.Context, duration time.Duration) {
	derivedCtx, _ := context.WithDeadline(ctx, time.Now().Add(duration))
	<-derivedCtx.Done()
}

func sleepCtx ( ctx context . Context , duration time . Duration ) { derivedCtx , _ := context . WithDeadline ( ctx , time . Now ( ) . Add ( duration ) ) <- derivedCtx . Done ( ) }


func TaskMetadataHandler(state dockerstate.TaskEngineState, ecsClient api.ECSClient, cluster, az, containerInstanceArn string, propagateTags bool) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		taskARN, err := getTaskARNByRequest(r, state)
		if err != nil {
			responseJSON, _ := json.Marshal(
				fmt.Sprintf("V3 task metadata handler: unable to get task arn from request: %s", err.Error()))
			utils.WriteJSONToResponse(w, http.StatusBadRequest, responseJSON, utils.RequestTypeTaskMetadata)
			return
		}

		seelog.Infof("V3 task metadata handler: writing response for task '%s'", taskARN)

		taskResponse, err := v2.NewTaskResponse(taskARN, state, ecsClient, cluster, az, containerInstanceArn, propagateTags)
		if err != nil {
			errResponseJSON, _ := json.Marshal("Unable to generate metadata for task: '" + taskARN + "'")
			utils.WriteJSONToResponse(w, http.StatusBadRequest, errResponseJSON, utils.RequestTypeTaskMetadata)
			return
		}
		task, _ := state.TaskByArn(taskARN)
		if task.GetTaskENI() == nil {
			// fill in non-awsvpc network details for container responses here
			responses := make([]v2.ContainerResponse, 0)
			for _, containerResponse := range taskResponse.Containers {
				networks, err := GetContainerNetworkMetadata(containerResponse.ID, state)
				if err != nil {
					errResponseJSON, _ := json.Marshal(err.Error())
					utils.WriteJSONToResponse(w, http.StatusBadRequest, errResponseJSON, utils.RequestTypeContainerMetadata)
					return
				}
				containerResponse.Networks = networks
				responses = append(responses, containerResponse)
			}
			taskResponse.Containers = responses
		}
		responseJSON, _ := json.Marshal(taskResponse)
		utils.WriteJSONToResponse(w, http.StatusOK, responseJSON, utils.RequestTypeTaskMetadata)
	}
}

// TaskMetadataHandler returns the handler method for handling task metadata requests.

TaskMetadataHandler returns the handler method for handling task metadata requests .


func (eventStream *EventStream) Subscribe(name string, handler eventHandler) error {
	eventStream.handlersLock.Lock()
	defer eventStream.handlersLock.Unlock()

	if _, ok := eventStream.handlers[name]; ok {
		return fmt.Errorf("handler %s already exists", name)
	}

	eventStream.handlers[name] = handler
	return nil
}

func ( eventStream * EventStream ) Subscribe ( name string , handler eventHandler ) error { eventStream . handlersLock . Lock ( ) defer eventStream . handlersLock . Unlock ( ) if _ , ok := eventStream . handlers [ name ] ; ok { return fmt . Errorf ( " " , name ) } eventStream . handlers [ name ] = handler return nil }

// Subscribe adds the handler to be called into EventStream


func (eventStream *EventStream) broadcast(event interface{}) {
	eventStream.handlersLock.RLock()
	defer eventStream.handlersLock.RUnlock()

	seelog.Debugf("Event stream %s received events, broadcasting to listeners...", eventStream.name)

	for _, handlerFunc := range eventStream.handlers {
		go handlerFunc(event)
	}
}

func ( eventStream * EventStream ) broadcast ( event interface { } ) { eventStream . handlersLock . RLock ( ) defer eventStream . handlersLock . RUnlock ( ) seelog . Debugf ( " " , eventStream . name ) for _ , handlerFunc := range eventStream . handlers { go handlerFunc ( event ) } }

// broadcast calls all handler's handler function

broadcast calls all handler s handler function


func (eventStream *EventStream) Unsubscribe(name string) {
	eventStream.handlersLock.Lock()
	defer eventStream.handlersLock.Unlock()

	for handler := range eventStream.handlers {
		if handler == name {
			seelog.Debugf("Unsubscribing event handler %s from event stream %s", handler, eventStream.name)
			delete(eventStream.handlers, handler)
			return
		}
	}
}

func ( eventStream * EventStream ) Unsubscribe ( name string ) { eventStream . handlersLock . Lock ( ) defer eventStream . handlersLock . Unlock ( ) for handler := range eventStream . handlers { if handler == name { seelog . Debugf ( " " , handler , eventStream . name ) delete ( eventStream . handlers , handler ) return } } }

// Unsubscribe deletes the handler from the EventStream


func (eventStream *EventStream) WriteToEventStream(event interface{}) error {
	eventStream.statusLock.RLock()
	defer eventStream.statusLock.RUnlock()

	if !eventStream.open {
		return fmt.Errorf("Event stream is closed")
	}
	eventStream.event <- event
	return nil
}

func ( eventStream * EventStream ) WriteToEventStream ( event interface { } ) error { eventStream . statusLock . RLock ( ) defer eventStream . statusLock . RUnlock ( ) if ! eventStream . open { return fmt . Errorf ( " " ) } eventStream . event <- event return nil }

// WriteToEventStream writes event to the event stream


func (eventStream *EventStream) listen() {
	seelog.Infof("Event stream %s start listening...", eventStream.name)
	for {
		select {
		case event := <-eventStream.event:
			eventStream.broadcast(event)
		case <-eventStream.ctx.Done():
			seelog.Infof("Event stream %s stopped listening...", eventStream.name)

			eventStream.statusLock.Lock()
			eventStream.open = false
			close(eventStream.event)
			eventStream.statusLock.Unlock()
			return
		}
	}
}

func ( eventStream * EventStream ) listen ( ) { seelog . Infof ( " " , eventStream . name ) for { select { case event := <- eventStream . event : eventStream . broadcast ( event ) case <- eventStream . ctx . Done ( ) : seelog . Infof ( " " , eventStream . name ) eventStream . statusLock . Lock ( ) eventStream . open = false close ( eventStream . event ) eventStream . statusLock . Unlock ( ) return } } }


func (eventStream *EventStream) StartListening() {
	eventStream.statusLock.Lock()
	defer eventStream.statusLock.Unlock()
	eventStream.open = true

	go eventStream.listen()
}

func ( eventStream * EventStream ) StartListening ( ) { eventStream . statusLock . Lock ( ) defer eventStream . statusLock . Unlock ( ) eventStream . open = true go eventStream . listen ( ) }

// StartListening mark the event stream as open and start listening


func SetLevel(logLevel string) {
	parsedLevel, ok := levels[strings.ToLower(logLevel)]

	if ok {
		levelLock.Lock()
		defer levelLock.Unlock()
		level = parsedLevel
		reloadConfig()
	}
}

func SetLevel ( logLevel string ) { parsedLevel , ok := levels [ strings . ToLower ( logLevel ) ] if ok { levelLock . Lock ( ) defer levelLock . Unlock ( ) level = parsedLevel reloadConfig ( ) } }

// SetLevel sets the log level for logging


func ForModule(module string) OldLogger {
	once.Do(initLogger)
	return logger.New("module", module)
}

func ForModule ( module string ) OldLogger { once . Do ( initLogger ) return logger . New ( " " , module ) }

// ForModule returns an OldLogger instance.  OldLogger is deprecated and kept
// for compatibility reasons.  Prefer using Seelog directly.

ForModule returns an OldLogger instance . OldLogger is deprecated and kept for compatibility reasons . Prefer using Seelog directly .


func NewMockWebsocketConn(ctrl *gomock.Controller) *MockWebsocketConn {
	mock := &MockWebsocketConn{ctrl: ctrl}
	mock.recorder = &MockWebsocketConnMockRecorder{mock}
	return mock
}

func NewMockWebsocketConn ( ctrl * gomock . Controller ) * MockWebsocketConn { mock := & MockWebsocketConn { ctrl : ctrl } mock . recorder = & MockWebsocketConnMockRecorder { mock } return mock }

// NewMockWebsocketConn creates a new mock instance


func (m *MockWebsocketConn) ReadMessage() (int, []byte, error) {
	ret := m.ctrl.Call(m, "ReadMessage")
	ret0, _ := ret[0].(int)
	ret1, _ := ret[1].([]byte)
	ret2, _ := ret[2].(error)
	return ret0, ret1, ret2
}

func ( m * MockWebsocketConn ) ReadMessage ( ) ( int , [ ] byte , error ) { ret := m . ctrl . Call ( m , " " ) ret0 , _ := ret [ 0 ] . ( int ) ret1 , _ := ret [ 1 ] . ( [ ] byte ) ret2 , _ := ret [ 2 ] . ( error ) return ret0 , ret1 , ret2 }


func (mr *MockWebsocketConnMockRecorder) SetWriteDeadline(arg0 interface{}) *gomock.Call {
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetWriteDeadline", reflect.TypeOf((*MockWebsocketConn)(nil).SetWriteDeadline), arg0)
}

func ( mr * MockWebsocketConnMockRecorder ) SetWriteDeadline ( arg0 interface { } ) * gomock . Call { return mr . mock . ctrl . RecordCallWithMethodType ( mr . mock , " " , reflect . TypeOf ( ( * MockWebsocketConn ) ( nil ) . SetWriteDeadline ) , arg0 ) }

// SetWriteDeadline indicates an expected call of SetWriteDeadline


func (mr *MockClientMockRecorder) Get(arg0 interface{}) *gomock.Call {
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockClient)(nil).Get), arg0)
}

func ( mr * MockClientMockRecorder ) Get ( arg0 interface { } ) * gomock . Call { return mr . mock . ctrl . RecordCallWithMethodType ( mr . mock , " " , reflect . TypeOf ( ( * MockClient ) ( nil ) . Get ) , arg0 ) }

// Get indicates an expected call of Get


func NewSSMSecretResource(taskARN string,
	ssmSecrets map[string][]apicontainer.Secret,
	executionCredentialsID string,
	credentialsManager credentials.Manager,
	ssmClientCreator factory.SSMClientCreator) *SSMSecretResource {

	s := &SSMSecretResource{
		taskARN:                taskARN,
		requiredSecrets:        ssmSecrets,
		credentialsManager:     credentialsManager,
		executionCredentialsID: executionCredentialsID,
		ssmClientCreator:       ssmClientCreator,
	}

	s.initStatusToTransition()
	return s
}

func NewSSMSecretResource ( taskARN string , ssmSecrets map [ string ] [ ] apicontainer . Secret , executionCredentialsID string , credentialsManager credentials . Manager , ssmClientCreator factory . SSMClientCreator ) * SSMSecretResource { s := & SSMSecretResource { taskARN : taskARN , requiredSecrets : ssmSecrets , credentialsManager : credentialsManager , executionCredentialsID : executionCredentialsID , ssmClientCreator : ssmClientCreator , } s . initStatusToTransition ( ) return s }

// NewSSMSecretResource creates a new SSMSecretResource object


func (secret *SSMSecretResource) Create() error {

	// To fail fast, check execution role first
	executionCredentials, ok := secret.credentialsManager.GetTaskCredentials(secret.getExecutionCredentialsID())
	if !ok {
		// No need to log here. managedTask.applyResourceState already does that
		err := errors.New("ssm secret resource: unable to find execution role credentials")
		secret.setTerminalReason(err.Error())
		return err
	}
	iamCredentials := executionCredentials.GetIAMRoleCredentials()

	var wg sync.WaitGroup

	// Get the maximum number of errors can be returned, which will be one error per goroutine
	chanLen := secret.getGoRoutineMaxNum()
	errorEvents := make(chan error, chanLen)

	seelog.Infof("ssm secret resource: retrieving secrets for containers in task: [%s]", secret.taskARN)
	secret.secretData = make(map[string]string)

	for region, secrets := range secret.getRequiredSecrets() {
		wg.Add(1)
		// Spin up goroutine each region to speed up processing time
		go secret.retrieveSSMSecretValuesByRegion(region, secrets, iamCredentials, &wg, errorEvents)
	}

	wg.Wait()

	// Get the first error returned and set as terminal reason
	select {
	case err := <-errorEvents:
		secret.setTerminalReason(err.Error())
		return err
	default:
		return nil
	}
}

// Create fetches secret value from SSM in batches. It spins up multiple goroutines in order to
// retrieve values in parallel.

Create fetches secret value from SSM in batches . It spins up multiple goroutines in order to retrieve values in parallel .


func (secret *SSMSecretResource) getGoRoutineMaxNum() int {
	total := 0
	for _, secrets := range secret.requiredSecrets {
		total += len(secrets)/MaxBatchNum + 1
	}
	return total
}

func ( secret * SSMSecretResource ) getGoRoutineMaxNum ( ) int { total := 0 for _ , secrets := range secret . requiredSecrets { total += len ( secrets ) / MaxBatchNum + 1 } return total }

// getGoRoutineMaxNum calculates the maximum number of goroutines that we need to spin up
// to retrieve secret values from SSM parameter store. Assume each goroutine initiates one
// SSM GetParameters call and each call will have 10 parameters

getGoRoutineMaxNum calculates the maximum number of goroutines that we need to spin up to retrieve secret values from SSM parameter store . Assume each goroutine initiates one SSM GetParameters call and each call will have 10 parameters


func (secret *SSMSecretResource) retrieveSSMSecretValuesByRegion(region string, secrets []apicontainer.Secret, iamCredentials credentials.IAMRoleCredentials, wg *sync.WaitGroup, errorEvents chan error) {
	seelog.Infof("ssm secret resource: retrieving secrets for region %s in task: [%s]", region, secret.taskARN)
	defer wg.Done()

	var wgPerRegion sync.WaitGroup
	var secretNames []string

	for _, s := range secrets {
		secretKey := s.GetSecretResourceCacheKey()
		if _, ok := secret.GetCachedSecretValue(secretKey); ok {
			continue
		}
		secretNames = append(secretNames, s.ValueFrom)
		if len(secretNames) == MaxBatchNum {
			secretNamesTmp := make([]string, MaxBatchNum)
			copy(secretNamesTmp, secretNames)
			wgPerRegion.Add(1)
			go secret.retrieveSSMSecretValues(region, secretNamesTmp, iamCredentials, &wgPerRegion, errorEvents)
			secretNames = []string{}
		}
	}

	if len(secretNames) > 0 {
		wgPerRegion.Add(1)
		go secret.retrieveSSMSecretValues(region, secretNames, iamCredentials, &wgPerRegion, errorEvents)
	}
	wgPerRegion.Wait()
}

func ( secret * SSMSecretResource ) retrieveSSMSecretValuesByRegion ( region string , secrets [ ] apicontainer . Secret , iamCredentials credentials . IAMRoleCredentials , wg * sync . WaitGroup , errorEvents chan error ) { seelog . Infof ( " " , region , secret . taskARN ) defer wg . Done ( ) var wgPerRegion sync . WaitGroup var secretNames [ ] string for _ , s := range secrets { secretKey := s . GetSecretResourceCacheKey ( ) if _ , ok := secret . GetCachedSecretValue ( secretKey ) ; ok { continue } secretNames = append ( secretNames , s . ValueFrom ) if len ( secretNames ) == MaxBatchNum { secretNamesTmp := make ( [ ] string , MaxBatchNum ) copy ( secretNamesTmp , secretNames ) wgPerRegion . Add ( 1 ) go secret . retrieveSSMSecretValues ( region , secretNamesTmp , iamCredentials , & wgPerRegion , errorEvents ) secretNames = [ ] string { } } } if len ( secretNames ) > 0 { wgPerRegion . Add ( 1 ) go secret . retrieveSSMSecretValues ( region , secretNames , iamCredentials , & wgPerRegion , errorEvents ) } wgPerRegion . Wait ( ) }

// retrieveSSMSecretValuesByRegion reads secret values from cache first, if not exists, batches secrets based on field
// valueFrom and call retrieveSSMSecretValues to retrieve values from SSM

retrieveSSMSecretValuesByRegion reads secret values from cache first if not exists batches secrets based on field valueFrom and call retrieveSSMSecretValues to retrieve values from SSM


func (secret *SSMSecretResource) retrieveSSMSecretValues(region string, names []string, iamCredentials credentials.IAMRoleCredentials, wg *sync.WaitGroup, errorEvents chan error) {
	defer wg.Done()

	ssmClient := secret.ssmClientCreator.NewSSMClient(region, iamCredentials)
	seelog.Infof("ssm secret resource: retrieving resource for secrets %v in region [%s] in task: [%s]", names, region, secret.taskARN)
	secValueMap, err := ssm.GetSecretsFromSSM(names, ssmClient)
	if err != nil {
		errorEvents <- fmt.Errorf("fetching secret data from SSM Parameter Store in %s: %v", region, err)
		return
	}

	secret.lock.Lock()
	defer secret.lock.Unlock()

	// put secret value in secretData
	for secretName, secretValue := range secValueMap {
		secretKey := secretName + "_" + region
		secret.secretData[secretKey] = secretValue
	}
}

func
(
secret
*
SSMSecretResource
)
retrieveSSMSecretValues
(
region
string
,
names
[
]
string
,
iamCredentials
credentials
.
IAMRoleCredentials
,
wg
*
sync
.
WaitGroup
,
errorEvents
chan
error
)
{
defer
wg
.
Done
(
)
ssmClient
:=
secret
.
ssmClientCreator
.
NewSSMClient
(
region
,
iamCredentials
)
seelog
.
Infof
(
"
"
,
names
,
region
,
secret
.
taskARN
)
secValueMap
,
err
:=
ssm
.
GetSecretsFromSSM
(
names
,
ssmClient
)
if
err
!=
{
errorEvents
<-
fmt
.
Errorf
(
"
"
,
region
,
err
)
return
}
secret
.
lock
.
Lock
(
)
defer
secret
.
lock
.
Unlock
(
)
// put secret value in secretData
for
secretName
,
secretValue
:=
range
secValueMap
{
secretKey
:=
secretName
+
"
"
+
region
secret
.
secretData
[
secretKey
]
=
secretValue
}
}

// retrieveSSMSecretValues retrieves secret values from SSM parameter store and caches them into memory


func (secret *SSMSecretResource) clearSSMSecretValue() {
	secret.lock.Lock()
	defer secret.lock.Unlock()

	for key := range secret.secretData {
		delete(secret.secretData, key)
	}
}

func ( secret * SSMSecretResource ) clearSSMSecretValue ( ) { secret . lock . Lock ( ) defer secret . lock . Unlock ( ) for key := range secret . secretData { delete ( secret . secretData , key ) } }

// clearSSMSecretValue cycles through the collection of secret value data and
// removes them from the task

clearSSMSecretValue cycles through the collection of secret value data and removes them from the task


func (secret *SSMSecretResource) GetCachedSecretValue(secretKey string) (string, bool) {
	secret.lock.RLock()
	defer secret.lock.RUnlock()

	s, ok := secret.secretData[secretKey]
	return s, ok
}

func ( secret * SSMSecretResource ) GetCachedSecretValue ( secretKey string ) ( string , bool ) { secret . lock . RLock ( ) defer secret . lock . RUnlock ( ) s , ok := secret . secretData [ secretKey ] return s , ok }

// GetCachedSecretValue retrieves the secret value from secretData field


func (secret *SSMSecretResource) SetCachedSecretValue(secretKey string, secretValue string) {
	secret.lock.Lock()
	defer secret.lock.Unlock()

	if secret.secretData == nil {
		secret.secretData = make(map[string]string)
	}

	secret.secretData[secretKey] = secretValue
}

func ( secret * SSMSecretResource ) SetCachedSecretValue ( secretKey string , secretValue string ) { secret . lock . Lock ( ) defer secret . lock . Unlock ( ) if secret . secretData == nil { secret . secretData = make ( map [ string ] string ) } secret . secretData [ secretKey ] = secretValue }

// SetCachedSecretValue set the secret value in the secretData field given the key and value


func (m *MockManager) Clean(arg0 string) error {
	ret := m.ctrl.Call(m, "Clean", arg0)
	ret0, _ := ret[0].(error)
	return ret0
}

func ( m * MockManager ) Clean ( arg0 string ) error { ret := m . ctrl . Call ( m , " " , arg0 ) ret0 , _ := ret [ 0 ] . ( error ) return ret0 }


func (m *MockManager) SetAvailabilityZone(arg0 string) {
	m.ctrl.Call(m, "SetAvailabilityZone", arg0)
}

func ( m * MockManager ) SetAvailabilityZone ( arg0 string ) { m . ctrl . Call ( m , " " , arg0 ) }

// SetAvailabilityZone mocks base method


func (m *MockManager) SetContainerInstanceARN(arg0 string) {
	m.ctrl.Call(m, "SetContainerInstanceARN", arg0)
}

func ( m * MockManager ) SetContainerInstanceARN ( arg0 string ) { m . ctrl . Call ( m , " " , arg0 ) }

// SetContainerInstanceARN mocks base method


func (m *MockManager) SetHostPublicIPv4Address(arg0 string) {
	m.ctrl.Call(m, "SetHostPublicIPv4Address", arg0)
}

func ( m * MockManager ) SetHostPublicIPv4Address ( arg0 string ) { m . ctrl . Call ( m , " " , arg0 ) }

// SetHostPublicIPv4Address mocks base method


func NewMockDockerMetadataClient(ctrl *gomock.Controller) *MockDockerMetadataClient {
	mock := &MockDockerMetadataClient{ctrl: ctrl}
	mock.recorder = &MockDockerMetadataClientMockRecorder{mock}
	return mock
}

func NewMockDockerMetadataClient ( ctrl * gomock . Controller ) * MockDockerMetadataClient { mock := & MockDockerMetadataClient { ctrl : ctrl } mock . recorder = & MockDockerMetadataClientMockRecorder { mock } return mock }

// NewMockDockerMetadataClient creates a new mock instance


func (mr *MockDockerMetadataClientMockRecorder) InspectContainer(arg0, arg1, arg2 interface{}) *gomock.Call {
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InspectContainer", reflect.TypeOf((*MockDockerMetadataClient)(nil).InspectContainer), arg0, arg1, arg2)
}

func ( mr * MockDockerMetadataClientMockRecorder ) InspectContainer ( arg0 , arg1 , arg2 interface { } ) * gomock . Call { return mr . mock . ctrl . RecordCallWithMethodType ( mr . mock , " " , reflect . TypeOf ( ( * MockDockerMetadataClient ) ( nil ) . InspectContainer ) , arg0 , arg1 , arg2 ) }

// InspectContainer indicates an expected call of InspectContainer


func JsonKeys(b []byte) ([]string, error) {
	var keyMap map[string]interface{}

	err := json.Unmarshal(b, &keyMap)
	if err != nil {
		return []string{}, err
	}

	keys := make([]string, len(keyMap))
	ndx := 0
	for k := range keyMap {
		keys[ndx] = k
		ndx++
	}
	return keys, nil
}

func JsonKeys ( b [ ] byte ) ( [ ] string , error ) { var keyMap map [ string ] interface { } err := json . Unmarshal ( b , & keyMap ) if err != nil { return [ ] string { } , err } keys := make ( [ ] string , len ( keyMap ) ) ndx := 0 for k := range keyMap { keys [ ndx ] = k ndx ++ } return keys , nil }

// JsonKeys takes an arbitrary byte array representing a json stringified object
// and returns all the keys of that object.

JsonKeys takes an arbitrary byte array representing a json stringified object and returns all the keys of that object .


func TaskContainerMetadataHandler(state dockerstate.TaskEngineState, ecsClient api.ECSClient, cluster, az, containerInstanceArn string, propagateTags bool) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		taskARN, err := getTaskARNByRequest(r, state)
		if err != nil {
			responseJSON, _ := json.Marshal(
				fmt.Sprintf("Unable to get task arn from request: %s", err.Error()))
			utils.WriteJSONToResponse(w, http.StatusBadRequest, responseJSON, utils.RequestTypeTaskMetadata)
			return
		}
		if containerID, ok := utils.GetMuxValueFromRequest(r, metadataContainerIDMuxName); ok {
			seelog.Infof("V2 task/container metadata handler: writing response for container '%s'", containerID)
			WriteContainerMetadataResponse(w, containerID, state)
			return
		}

		seelog.Infof("V2 task/container metadata handler: writing response for task '%s'", taskARN)
		WriteTaskMetadataResponse(w, taskARN, cluster, state, ecsClient, az, containerInstanceArn, propagateTags)
	}
}

func TaskContainerMetadataHandler ( state dockerstate . TaskEngineState , ecsClient api . ECSClient , cluster , az , containerInstanceArn string , propagateTags bool ) func ( http . ResponseWriter , * http . Request ) { return func ( w http . ResponseWriter , r * http . Request ) { taskARN , err := getTaskARNByRequest ( r , state ) if err != nil { responseJSON , _ := json . Marshal ( fmt . Sprintf ( " " , err . Error ( ) ) ) utils . WriteJSONToResponse ( w , http . StatusBadRequest , responseJSON , utils . RequestTypeTaskMetadata ) return } if containerID , ok := utils . GetMuxValueFromRequest ( r , metadataContainerIDMuxName ) ; ok { seelog . Infof ( " " , containerID ) WriteContainerMetadataResponse ( w , containerID , state ) return } seelog . Infof ( " " , taskARN ) WriteTaskMetadataResponse ( w , taskARN , cluster , state , ecsClient , az , containerInstanceArn , propagateTags ) } }

// TaskContainerMetadataHandler returns the handler method for handling task and container metadata requests.

TaskContainerMetadataHandler returns the handler method for handling task and container metadata requests .


func WriteContainerMetadataResponse(w http.ResponseWriter, containerID string, state dockerstate.TaskEngineState) {
	containerResponse, err := NewContainerResponse(containerID, state)
	if err != nil {
		errResponseJSON, _ := json.Marshal("Unable to generate metadata for container '" + containerID + "'")
		utils.WriteJSONToResponse(w, http.StatusBadRequest, errResponseJSON, utils.RequestTypeContainerMetadata)
		return
	}

	responseJSON, _ := json.Marshal(containerResponse)
	utils.WriteJSONToResponse(w, http.StatusOK, responseJSON, utils.RequestTypeContainerMetadata)
}

func WriteContainerMetadataResponse ( w http . ResponseWriter , containerID string , state dockerstate . TaskEngineState ) { containerResponse , err := NewContainerResponse ( containerID , state ) if err != nil { errResponseJSON , _ := json . Marshal ( " " + containerID + " " ) utils . WriteJSONToResponse ( w , http . StatusBadRequest , errResponseJSON , utils . RequestTypeContainerMetadata ) return } responseJSON , _ := json . Marshal ( containerResponse ) utils . WriteJSONToResponse ( w , http . StatusOK , responseJSON , utils . RequestTypeContainerMetadata ) }

// WriteContainerMetadataResponse writes the container metadata to response writer.

WriteContainerMetadataResponse writes the container metadata to response writer .


func WriteTaskMetadataResponse(w http.ResponseWriter, taskARN string, cluster string, state dockerstate.TaskEngineState, ecsClient api.ECSClient, az, containerInstanceArn string, propagateTags bool) {
	// Generate a response for the task
	taskResponse, err := NewTaskResponse(taskARN, state, ecsClient, cluster, az, containerInstanceArn, propagateTags)
	if err != nil {
		errResponseJSON, _ := json.Marshal("Unable to generate metadata for task: '" + taskARN + "'")
		utils.WriteJSONToResponse(w, http.StatusBadRequest, errResponseJSON, utils.RequestTypeTaskMetadata)
		return
	}

	responseJSON, _ := json.Marshal(taskResponse)
	utils.WriteJSONToResponse(w, http.StatusOK, responseJSON, utils.RequestTypeTaskMetadata)
}

func
WriteTaskMetadataResponse
(
w
http
.
ResponseWriter
,
taskARN
string
,
cluster
string
,
state
dockerstate
.
TaskEngineState
,
ecsClient
api
.
ECSClient
,
az
,
containerInstanceArn
string
,
propagateTags
bool
)
{
// Generate a response for the task
taskResponse
,
err
:=
NewTaskResponse
(
taskARN
,
state
,
ecsClient
,
cluster
,
az
,
containerInstanceArn
,
propagateTags
)
if
err
!=
{
errResponseJSON
,
_
:=
json
.
Marshal
(
"
"
+
taskARN
+
"
"
)
utils
.
WriteJSONToResponse
(
w
,
http
.
StatusBadRequest
,
errResponseJSON
,
utils
.
RequestTypeTaskMetadata
)
return
}
responseJSON
,
_
:=
json
.
Marshal
(
taskResponse
)
utils
.
WriteJSONToResponse
(
w
,
http
.
StatusOK
,
responseJSON
,
utils
.
RequestTypeTaskMetadata
)
}

// WriteTaskMetadataResponse writes the task metadata to response writer.

WriteTaskMetadataResponse writes the task metadata to response writer .


func (c *control) Init() error {
	seelog.Infof("Creating root ecs cgroup: %s", config.DefaultTaskCgroupPrefix)

	// Build cgroup spec
	cgroupSpec := &Spec{
		Root:  config.DefaultTaskCgroupPrefix,
		Specs: &specs.LinuxResources{},
	}
	_, err := c.Create(cgroupSpec)
	return err
}

func
(
c
*
control
)
Init
(
)
error
{
seelog
.
Infof
(
"
"
,
config
.
DefaultTaskCgroupPrefix
)
// Build cgroup spec
cgroupSpec
:=
&
Spec
{
Root
:
config
.
DefaultTaskCgroupPrefix
,
Specs
:
&
specs
.
LinuxResources
{
}
,
}
_
,
err
:=
c
.
Create
(
cgroupSpec
)
return
err
}

// Init is used to setup the cgroup root for ecs


func (err *WSError) Error() string {
	val := reflect.ValueOf(err.ErrObj)
	if val.Kind() == reflect.Ptr {
		val = val.Elem()
	}
	var typeStr = "Unknown type"
	if val.IsValid() {
		typeStr = val.Type().Name()
		msg := val.FieldByName("Message_")
		if msg.IsValid() && msg.CanInterface() {
			str, ok := msg.Interface().(*string)
			if ok {
				if str == nil {
					return typeStr + ": null"
				}
				return typeStr + ": " + *str
			}
		}
	}

	if asErr, ok := err.ErrObj.(error); ok {
		return err.Type + ": " + asErr.Error()
	}
	return err.Type + ": Unknown error (" + typeStr + ")"
}

func ( err * WSError ) Error ( ) string { val := reflect . ValueOf ( err . ErrObj ) if val . Kind ( ) == reflect . Ptr { val = val . Elem ( ) } var typeStr = " " if val . IsValid ( ) { typeStr = val . Type ( ) . Name ( ) msg := val . FieldByName ( " " ) if msg . IsValid ( ) && msg . CanInterface ( ) { str , ok := msg . Interface ( ) . ( * string ) if ok { if str == nil { return typeStr + " " } return typeStr + " " + * str } } } if asErr , ok := err . ErrObj . ( error ) ; ok { return err . Type + " " + asErr . Error ( ) } return err . Type + " " + typeStr + " " }


func (err *WSError) Retry() bool {
	for _, unretriable := range err.Get() {
		if reflect.TypeOf(err.ErrObj) == reflect.TypeOf(unretriable) {
			return false
		}
	}
	return true
}

func ( err * WSError ) Retry ( ) bool { for _ , unretriable := range err . Get ( ) { if reflect . TypeOf ( err . ErrObj ) == reflect . TypeOf ( unretriable ) { return false } } return true }

// Retry returns true if this error should be considered retriable


func NewMockDockerStateResolver(ctrl *gomock.Controller) *MockDockerStateResolver {
	mock := &MockDockerStateResolver{ctrl: ctrl}
	mock.recorder = &MockDockerStateResolverMockRecorder{mock}
	return mock
}

func NewMockDockerStateResolver ( ctrl * gomock . Controller ) * MockDockerStateResolver { mock := & MockDockerStateResolver { ctrl : ctrl } mock . recorder = & MockDockerStateResolverMockRecorder { mock } return mock }

// NewMockDockerStateResolver creates a new mock instance


func (mr *MockDockerStateResolverMockRecorder) State() *gomock.Call {
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "State", reflect.TypeOf((*MockDockerStateResolver)(nil).State))
}

func ( mr * MockDockerStateResolverMockRecorder ) State ( ) * gomock . Call { return mr . mock . ctrl . RecordCallWithMethodType ( mr . mock , " " , reflect . TypeOf ( ( * MockDockerStateResolver ) ( nil ) . State ) ) }

// State indicates an expected call of State


func printLicense() int {
	license := utils.NewLicenseProvider()
	text, err := license.GetText()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		return exitcodes.ExitError
	}
	fmt.Println(text)
	return exitcodes.ExitSuccess
}

func printLicense ( ) int { license := utils . NewLicenseProvider ( ) text , err := license . GetText ( ) if err != nil { fmt . Fprintln ( os . Stderr , err ) return exitcodes . ExitError } fmt . Println ( text ) return exitcodes . ExitSuccess }

// printLicense prints the Agent's license text

printLicense prints the Agent s license text


func (c *GlobalCgroupFactory) Load(hierarchy cgroups.Hierarchy, path cgroups.Path) (cgroups.Cgroup, error) {
	return cgroups.Load(hierarchy, path)
}

func ( c * GlobalCgroupFactory ) Load ( hierarchy cgroups . Hierarchy , path cgroups . Path ) ( cgroups . Cgroup , error ) { return cgroups . Load ( hierarchy , path ) }

// Load is used to load the cgroup hierarchy based off the cgroup path


func (c *GlobalCgroupFactory) New(hierarchy cgroups.Hierarchy, path cgroups.Path, specs *specs.LinuxResources) (cgroups.Cgroup, error) {
	return cgroups.New(hierarchy, path, specs)
}

func ( c * GlobalCgroupFactory ) New ( hierarchy cgroups . Hierarchy , path cgroups . Path , specs * specs . LinuxResources ) ( cgroups . Cgroup , error ) { return cgroups . New ( hierarchy , path , specs ) }

// New is used to create a new cgroup hierarchy


func timeoutAndMoveOn(fp loiterFunc, timeout time.Duration) {
	ch := time.After(timeout)
	for {
		select {
		case <-ch:
			return
		default:
			fp()
		}
	}
}

func timeoutAndMoveOn ( fp loiterFunc , timeout time . Duration ) { ch := time . After ( timeout ) for { select { case <- ch : return default : fp ( ) } } }

// timeoutAndMoveOn executes the long running function pointed to by 'fp' and
// times out after 'timeout' duration.

timeoutAndMoveOn executes the long running function pointed to by fp and times out after timeout duration .


func (engine *DockerTaskEngine) UnmarshalJSON(data []byte) error {
	return engine.state.UnmarshalJSON(data)
}

func ( engine * DockerTaskEngine ) UnmarshalJSON ( data [ ] byte ) error { return engine . state . UnmarshalJSON ( data ) }

// UnmarshalJSON restores a previously marshaled task-engine state from json

UnmarshalJSON restores a previously marshaled task - engine state from json


func (engine *DockerTaskEngine) Init(ctx context.Context) error {
	// TODO, pass in a a context from main from background so that other things can stop us, not just the tests
	derivedCtx, cancel := context.WithCancel(ctx)
	engine.stopEngine = cancel

	engine.ctx = derivedCtx

	// Open the event stream before we sync state so that e.g. if a container
	// goes from running to stopped after we sync with it as "running" we still
	// have the "went to stopped" event pending so we can be up to date.
	err := engine.openEventstream(derivedCtx)
	if err != nil {
		return err
	}
	engine.synchronizeState()
	// Now catch up and start processing new events per normal
	go engine.handleDockerEvents(derivedCtx)
	engine.initialized = true
	return nil
}

// Init initializes a DockerTaskEngine such that it may communicate with docker
// and operate normally.
// This function must be called before any other function, except serializing and deserializing, can succeed without error.

Init initializes a DockerTaskEngine such that it may communicate with docker and operate normally . This function must be called before any other function except serializing and deserializing can succeed without error .


func (engine *DockerTaskEngine) MustInit(ctx context.Context) {
	if engine.initialized {
		return
	}
	engine.mustInitLock.Lock()
	defer engine.mustInitLock.Unlock()

	errorOnce := sync.Once{}
	taskEngineConnectBackoff := retry.NewExponentialBackoff(minEngineConnectRetryDelay, maxEngineConnectRetryDelay,
		engineConnectRetryJitterMultiplier, engineConnectRetryDelayMultiplier)
	retry.RetryWithBackoff(taskEngineConnectBackoff, func() error {
		if engine.initialized {
			return nil
		}
		err := engine.Init(ctx)
		if err != nil {
			errorOnce.Do(func() {
				seelog.Errorf("Task engine: could not connect to docker daemon: %v", err)
			})
		}
		return err
	})
}

func ( engine * DockerTaskEngine ) MustInit ( ctx context . Context ) { if engine . initialized { return } engine . mustInitLock . Lock ( ) defer engine . mustInitLock . Unlock ( ) errorOnce := sync . Once { } taskEngineConnectBackoff := retry . NewExponentialBackoff ( minEngineConnectRetryDelay , maxEngineConnectRetryDelay , engineConnectRetryJitterMultiplier , engineConnectRetryDelayMultiplier ) retry . RetryWithBackoff ( taskEngineConnectBackoff , func ( ) error { if engine . initialized { return nil } err := engine . Init ( ctx ) if err != nil { errorOnce . Do ( func ( ) { seelog . Errorf ( " " , err ) } ) } return err } ) }

// MustInit blocks and retries until an engine can be initialized.

MustInit blocks and retries until an engine can be initialized .


func (engine *DockerTaskEngine) isTaskManaged(arn string) bool {
	engine.tasksLock.RLock()
	defer engine.tasksLock.RUnlock()
	_, ok := engine.managedTasks[arn]
	return ok
}

func ( engine * DockerTaskEngine ) isTaskManaged ( arn string ) bool { engine . tasksLock . RLock ( ) defer engine . tasksLock . RUnlock ( ) _ , ok := engine . managedTasks [ arn ] return ok }

// isTaskManaged checks if task for the corresponding arn is present


func (engine *DockerTaskEngine) synchronizeState() {
	engine.tasksLock.Lock()
	defer engine.tasksLock.Unlock()
	imageStates := engine.state.AllImageStates()
	if len(imageStates) != 0 {
		engine.imageManager.AddAllImageStates(imageStates)
	}

	tasks := engine.state.AllTasks()
	tasksToStart := engine.filterTasksToStartUnsafe(tasks)
	for _, task := range tasks {
		task.InitializeResources(engine.resourceFields)
	}

	for _, task := range tasksToStart {
		engine.startTask(task)
	}

	engine.saver.Save()
}

func ( engine * DockerTaskEngine ) synchronizeState ( ) { engine . tasksLock . Lock ( ) defer engine . tasksLock . Unlock ( ) imageStates := engine . state . AllImageStates ( ) if len ( imageStates ) != 0 { engine . imageManager . AddAllImageStates ( imageStates ) } tasks := engine . state . AllTasks ( ) tasksToStart := engine . filterTasksToStartUnsafe ( tasks ) for _ , task := range tasks { task . InitializeResources ( engine . resourceFields ) } for _ , task := range tasksToStart { engine . startTask ( task ) } engine . saver . Save ( ) }

// synchronizeState explicitly goes through each docker container stored in
// "state" and updates its KnownStatus appropriately, as well as queueing up
// events to push upstream.

synchronizeState explicitly goes through each docker container stored in state and updates its KnownStatus appropriately as well as queueing up events to push upstream .


func (engine *DockerTaskEngine) filterTasksToStartUnsafe(tasks []*apitask.Task) []*apitask.Task {
	var tasksToStart []*apitask.Task
	for _, task := range tasks {
		conts, ok := engine.state.ContainerMapByArn(task.Arn)
		if !ok {
			// task hasn't started processing, no need to check container status
			tasksToStart = append(tasksToStart, task)
			continue
		}

		for _, cont := range conts {
			engine.synchronizeContainerStatus(cont, task)
		}

		tasksToStart = append(tasksToStart, task)

		// Put tasks that are stopped by acs but hasn't been stopped in wait group
		if task.GetDesiredStatus().Terminal() && task.GetStopSequenceNumber() != 0 {
			engine.taskStopGroup.Add(task.GetStopSequenceNumber(), 1)
		}
	}

	return tasksToStart
}

// filterTasksToStartUnsafe filters only the tasks that need to be started after
// the agent has been restarted. It also synchronizes states of all of the containers
// in tasks that need to be started.

filterTasksToStartUnsafe filters only the tasks that need to be started after the agent has been restarted . It also synchronizes states of all of the containers in tasks that need to be started .


func updateContainerMetadata(metadata *dockerapi.DockerContainerMetadata, container *apicontainer.Container, task *apitask.Task) {
	container.SetCreatedAt(metadata.CreatedAt)
	container.SetStartedAt(metadata.StartedAt)
	container.SetFinishedAt(metadata.FinishedAt)

	// Set the labels if it's not set
	if len(metadata.Labels) != 0 && len(container.GetLabels()) == 0 {
		container.SetLabels(metadata.Labels)
	}

	// Update volume for empty volume container
	if metadata.Volumes != nil {
		if container.IsInternal() {
			task.UpdateMountPoints(container, metadata.Volumes)
		} else {
			container.SetVolumes(metadata.Volumes)
		}
	}

	// Set Exitcode if it's not set
	if metadata.ExitCode != nil {
		container.SetKnownExitCode(metadata.ExitCode)
	}

	// Set port mappings
	if len(metadata.PortBindings) != 0 && len(container.GetKnownPortBindings()) == 0 {
		container.SetKnownPortBindings(metadata.PortBindings)
	}
	// update the container health information
	if container.HealthStatusShouldBeReported() {
		container.SetHealthStatus(metadata.Health)
	}
	container.SetNetworkMode(metadata.NetworkMode)
	container.SetNetworkSettings(metadata.NetworkSettings)
}

// updateContainerMetadata sets the container metadata from the docker inspect


func (engine *DockerTaskEngine) synchronizeContainerStatus(container *apicontainer.DockerContainer, task *apitask.Task) {
	if container.DockerID == "" {
		seelog.Debugf("Task engine [%s]: found container potentially created while we were down: %s",
			task.Arn, container.DockerName)
		// Figure out the dockerid
		describedContainer, err := engine.client.InspectContainer(engine.ctx,
			container.DockerName, dockerclient.InspectContainerTimeout)
		if err != nil {
			seelog.Warnf("Task engine [%s]: could not find matching container for expected name [%s]: %v",
				task.Arn, container.DockerName, err)
		} else {
			// update the container metadata in case the container was created during agent restart
			metadata := dockerapi.MetadataFromContainer(describedContainer)
			updateContainerMetadata(&metadata, container.Container, task)
			container.DockerID = describedContainer.ID

			container.Container.SetKnownStatus(dockerapi.DockerStateToState(describedContainer.State))
			// update mappings that need dockerid
			engine.state.AddContainer(container, task)
			engine.imageManager.RecordContainerReference(container.Container)
		}
		return
	}

	currentState, metadata := engine.client.DescribeContainer(engine.ctx, container.DockerID)
	if metadata.Error != nil {
		currentState = apicontainerstatus.ContainerStopped
		// If this is a Docker API error
		if metadata.Error.ErrorName() == dockerapi.CannotDescribeContainerErrorName {
			seelog.Warnf("Task engine [%s]: could not describe previously known container [id=%s; name=%s]; assuming dead: %v",
				task.Arn, container.DockerID, container.DockerName, metadata.Error)
			if !container.Container.KnownTerminal() {
				container.Container.ApplyingError = apierrors.NewNamedError(&ContainerVanishedError{})
				engine.imageManager.RemoveContainerReferenceFromImageState(container.Container)
			}
		} else {
			// If this is a container state error
			updateContainerMetadata(&metadata, container.Container, task)
			container.Container.ApplyingError = apierrors.NewNamedError(metadata.Error)
		}
	} else {
		// update the container metadata in case the container status/metadata changed during agent restart
		updateContainerMetadata(&metadata, container.Container, task)
		engine.imageManager.RecordContainerReference(container.Container)
		if engine.cfg.ContainerMetadataEnabled && !container.Container.IsMetadataFileUpdated() {
			go engine.updateMetadataFile(task, container)
		}
	}
	if currentState > container.Container.GetKnownStatus() {
		// update the container known status
		container.Container.SetKnownStatus(currentState)
	}
	// Update task ExecutionStoppedAt timestamp
	task.RecordExecutionStoppedAt(container.Container)
}

// synchronizeContainerStatus checks and updates the container status with docker


func (engine *DockerTaskEngine) checkTaskState(task *apitask.Task) {
	defer metrics.MetricsEngineGlobal.RecordTaskEngineMetric("CHECK_TASK_STATE")()
	taskContainers, ok := engine.state.ContainerMapByArn(task.Arn)
	if !ok {
		seelog.Warnf("Task engine [%s]: could not check task state; no task in state", task.Arn)
		return
	}
	for _, container := range task.Containers {
		dockerContainer, ok := taskContainers[container.Name]
		if !ok {
			continue
		}
		status, metadata := engine.client.DescribeContainer(engine.ctx, dockerContainer.DockerID)
		engine.tasksLock.RLock()
		managedTask, ok := engine.managedTasks[task.Arn]
		engine.tasksLock.RUnlock()

		if ok {
			managedTask.emitDockerContainerChange(dockerContainerChange{
				container: container,
				event: dockerapi.DockerContainerChangeEvent{
					Status:                  status,
					DockerContainerMetadata: metadata,
				},
			})
		}
	}
}

func ( engine * DockerTaskEngine ) checkTaskState ( task * apitask . Task ) { defer metrics . MetricsEngineGlobal . RecordTaskEngineMetric ( " " ) ( ) taskContainers , ok := engine . state . ContainerMapByArn ( task . Arn ) if ! ok { seelog . Warnf ( " " , task . Arn ) return } for _ , container := range task . Containers { dockerContainer , ok := taskContainers [ container . Name ] if ! ok { continue } status , metadata := engine . client . DescribeContainer ( engine . ctx , dockerContainer . DockerID ) engine . tasksLock . RLock ( ) managedTask , ok := engine . managedTasks [ task . Arn ] engine . tasksLock . RUnlock ( ) if ok { managedTask . emitDockerContainerChange ( dockerContainerChange { container : container , event : dockerapi . DockerContainerChangeEvent { Status : status , DockerContainerMetadata : metadata , } , } ) } } }

// checkTaskState inspects the state of all containers within a task and writes
// their state to the managed task's container channel.

checkTaskState inspects the state of all containers within a task and writes their state to the managed task s container channel .


func (engine *DockerTaskEngine) sweepTask(task *apitask.Task) {
	for _, cont := range task.Containers {
		err := engine.removeContainer(task, cont)
		if err != nil {
			seelog.Debugf("Task engine [%s]: unable to remove old container [%s]: %v",
				task.Arn, cont.Name, err)
		}
		// Internal container(created by ecs-agent) state isn't recorded
		if cont.IsInternal() {
			continue
		}
		err = engine.imageManager.RemoveContainerReferenceFromImageState(cont)
		if err != nil {
			seelog.Errorf("Task engine [%s]: Unable to remove container [%s] reference from image state: %v",
				task.Arn, cont.Name, err)
		}
	}

	// Clean metadata directory for task
	if engine.cfg.ContainerMetadataEnabled {
		err := engine.metadataManager.Clean(task.Arn)
		if err != nil {
			seelog.Warnf("Task engine [%s]: clean task metadata failed: %v", task.Arn, err)
		}
	}
	engine.saver.Save()
}

// sweepTask deletes all the containers associated with a task


func (engine *DockerTaskEngine) startTask(task *apitask.Task) {
	// Create a channel that may be used to communicate with this task, survey
	// what tasks need to be waited for for this one to start, and then spin off
	// a goroutine to oversee this task

	thisTask := engine.newManagedTask(task)
	thisTask._time = engine.time()

	go thisTask.overseeTask()
}

// startTask creates a managedTask construct to track the task and then begins
// pushing it towards its desired state when allowed startTask is protected by
// the tasksLock lock of 'AddTask'. It should not be called from anywhere
// else and should exit quickly to allow AddTask to do more work.

startTask creates a managedTask construct to track the task and then begins pushing it towards its desired state when allowed startTask is protected by the tasksLock lock of AddTask . It should not be called from anywhere else and should exit quickly to allow AddTask to do more work .


func (engine *DockerTaskEngine) openEventstream(ctx context.Context) error {
	events, err := engine.client.ContainerEvents(ctx)
	if err != nil {
		return err
	}
	engine.events = events
	return nil
}

func ( engine * DockerTaskEngine ) openEventstream ( ctx context . Context ) error { events , err := engine . client . ContainerEvents ( ctx ) if err != nil { return err } engine . events = events return nil }

// openEventstream opens, but does not consume, the docker event stream

openEventstream opens but does not consume the docker event stream


func (engine *DockerTaskEngine) handleDockerEvents(ctx context.Context) {
	for {
		select {
		case <-ctx.Done():
			return
		case event := <-engine.events:
			engine.handleDockerEvent(event)
		}
	}
}

func ( engine * DockerTaskEngine ) handleDockerEvents ( ctx context . Context ) { for { select { case <- ctx . Done ( ) : return case event := <- engine . events : engine . handleDockerEvent ( event ) } } }

// handleDockerEvents must be called after openEventstream; it processes each
// event that it reads from the docker eventstream

handleDockerEvents must be called after openEventstream ; it processes each event that it reads from the docker eventstream


func (engine *DockerTaskEngine) handleDockerEvent(event dockerapi.DockerContainerChangeEvent) {
	seelog.Debugf("Task engine: handling a docker event: %s", event.String())

	task, ok := engine.state.TaskByID(event.DockerID)
	if !ok {
		seelog.Debugf("Task engine: event for container [%s] not managed, unable to map container id to task",
			event.DockerID)
		return
	}
	cont, ok := engine.state.ContainerByID(event.DockerID)
	if !ok {
		seelog.Debugf("Task engine: event for container [%s] not managed, unable to map container id to container",
			event.DockerID)
		return
	}

	// Container health status change does not affect the container status
	// no need to process this in task manager
	if event.Type == apicontainer.ContainerHealthEvent {
		if cont.Container.HealthStatusShouldBeReported() {
			seelog.Debugf("Task engine: updating container [%s(%s)] health status: %v",
				cont.Container.Name, cont.DockerID, event.DockerContainerMetadata.Health)
			cont.Container.SetHealthStatus(event.DockerContainerMetadata.Health)
		}
		return
	}

	engine.tasksLock.RLock()
	managedTask, ok := engine.managedTasks[task.Arn]
	// hold the lock until the message is sent so we don't send on a closed channel
	defer engine.tasksLock.RUnlock()
	if !ok {
		seelog.Criticalf("Task engine: could not find managed task [%s] corresponding to a docker event: %s",
			task.Arn, event.String())
		return
	}
	seelog.Debugf("Task engine [%s]: writing docker event to the task: %s",
		task.Arn, event.String())
	managedTask.emitDockerContainerChange(dockerContainerChange{container: cont.Container, event: event})
	seelog.Debugf("Task engine [%s]: wrote docker event to the task: %s",
		task.Arn, event.String())
}

// handleDockerEvent is the entrypoint for task modifications originating with
// events occurring through Docker, outside the task engine itself.
// handleDockerEvent is responsible for taking an event that correlates to a
// container and placing it in the context of the task to which that container
// belongs.

handleDockerEvent is the entrypoint for task modifications originating with events occurring through Docker outside the task engine itself . handleDockerEvent is responsible for taking an event that correlates to a container and placing it in the context of the task to which that container belongs .


func (engine *DockerTaskEngine) AddTask(task *apitask.Task) {
	defer metrics.MetricsEngineGlobal.RecordTaskEngineMetric("ADD_TASK")()
	err := task.PostUnmarshalTask(engine.cfg, engine.credentialsManager,
		engine.resourceFields, engine.client, engine.ctx)
	if err != nil {
		seelog.Errorf("Task engine [%s]: unable to add task to the engine: %v", task.Arn, err)
		task.SetKnownStatus(apitaskstatus.TaskStopped)
		task.SetDesiredStatus(apitaskstatus.TaskStopped)
		engine.emitTaskEvent(task, err.Error())
		return
	}

	engine.tasksLock.Lock()
	defer engine.tasksLock.Unlock()

	existingTask, exists := engine.state.TaskByArn(task.Arn)
	if !exists {
		// This will update the container desired status
		task.UpdateDesiredStatus()

		engine.state.AddTask(task)
		if dependencygraph.ValidDependencies(task) {
			engine.startTask(task)
		} else {
			seelog.Errorf("Task engine [%s]: unable to progress task with circular dependencies", task.Arn)
			task.SetKnownStatus(apitaskstatus.TaskStopped)
			task.SetDesiredStatus(apitaskstatus.TaskStopped)
			err := TaskDependencyError{task.Arn}
			engine.emitTaskEvent(task, err.Error())
		}
		return
	}

	// Update task
	engine.updateTaskUnsafe(existingTask, task)
}


func (engine *DockerTaskEngine) ListTasks() ([]*apitask.Task, error) {
	return engine.state.AllTasks(), nil
}

func ( engine * DockerTaskEngine ) ListTasks ( ) ( [ ] * apitask . Task , error ) { return engine . state . AllTasks ( ) , nil }

// ListTasks returns the tasks currently managed by the DockerTaskEngine


func (engine *DockerTaskEngine) GetTaskByArn(arn string) (*apitask.Task, bool) {
	return engine.state.TaskByArn(arn)
}

func ( engine * DockerTaskEngine ) GetTaskByArn ( arn string ) ( * apitask . Task , bool ) { return engine . state . TaskByArn ( arn ) }

// GetTaskByArn returns the task identified by that ARN


func (engine *DockerTaskEngine) imagePullRequired(imagePullBehavior config.ImagePullBehaviorType,
	container *apicontainer.Container,
	taskArn string) bool {
	switch imagePullBehavior {
	case config.ImagePullOnceBehavior:
		// If this image has been pulled successfully before, don't pull the image,
		// otherwise pull the image as usual, regardless whether the image exists or not
		// (the image can be prepopulated with the AMI and never be pulled).
		imageState, ok := engine.imageManager.GetImageStateFromImageName(container.Image)
		if ok && imageState.GetPullSucceeded() {
			seelog.Infof("Task engine [%s]: image %s for container %s has been pulled once, not pulling it again",
				taskArn, container.Image, container.Name)
			return false
		}
		return true
	case config.ImagePullPreferCachedBehavior:
		// If the behavior is prefer cached, don't pull if we found cached image
		// by inspecting the image.
		_, err := engine.client.InspectImage(container.Image)
		if err != nil {
			return true
		}
		seelog.Infof("Task engine [%s]: found cached image %s, use it directly for container %s",
			taskArn, container.Image, container.Name)
		return false
	default:
		// Need to pull the image for always and default agent pull behavior
		return true
	}
}

// imagePullRequired returns true if pulling image is required, or return false if local image cache
// should be used, by inspecting the agent pull behavior variable defined in config. The caller has
// to make sure the container passed in is not an internal container.

imagePullRequired returns true if pulling image is required or return false if local image cache should be used by inspecting the agent pull behavior variable defined in config . The caller has to make sure the container passed in is not an internal container .


func (engine *DockerTaskEngine) cleanupPauseContainerNetwork(task *apitask.Task, container *apicontainer.Container) error {
	seelog.Infof("Task engine [%s]: cleaning up the network namespace", task.Arn)

	cniConfig, err := engine.buildCNIConfigFromTaskContainer(task, container)
	if err != nil {
		return errors.Wrapf(err,
			"engine: failed cleanup task network namespace, task: %s", task.String())
	}

	return engine.cniClient.CleanupNS(engine.ctx, cniConfig, cniCleanupTimeout)
}

func ( engine * DockerTaskEngine ) cleanupPauseContainerNetwork ( task * apitask . Task , container * apicontainer . Container ) error { seelog . Infof ( " " , task . Arn ) cniConfig , err := engine . buildCNIConfigFromTaskContainer ( task , container ) if err != nil { return errors . Wrapf ( err , " " , task . String ( ) ) } return engine . cniClient . CleanupNS ( engine . ctx , cniConfig , cniCleanupTimeout ) }

// cleanupPauseContainerNetwork will clean up the network namespace of pause container


func (engine *DockerTaskEngine) updateTaskUnsafe(task *apitask.Task, update *apitask.Task) {
	managedTask, ok := engine.managedTasks[task.Arn]
	if !ok {
		seelog.Criticalf("Task engine [%s]: ACS message for a task we thought we managed, but don't!  Aborting.",
			task.Arn)
		return
	}
	// Keep the lock because sequence numbers cannot be correct unless they are
	// also read in the order addtask was called
	// This does block the engine's ability to ingest any new events (including
	// stops for past tasks, ack!), but this is necessary for correctness
	updateDesiredStatus := update.GetDesiredStatus()
	seelog.Debugf("Task engine [%s]: putting update on the acs channel: [%s] with seqnum [%d]",
		task.Arn, updateDesiredStatus.String(), update.StopSequenceNumber)
	managedTask.emitACSTransition(acsTransition{
		desiredStatus: updateDesiredStatus,
		seqnum:        update.StopSequenceNumber,
	})
	seelog.Debugf("Task engine [%s]: update taken off the acs channel: [%s] with seqnum [%d]",
		task.Arn, updateDesiredStatus.String(), update.StopSequenceNumber)
}

// updateTaskUnsafe determines if a new transition needs to be applied to the
// referenced task, and if needed applies it. It should not be called anywhere
// but from 'AddTask' and is protected by the tasksLock lock there.

updateTaskUnsafe determines if a new transition needs to be applied to the referenced task and if needed applies it . It should not be called anywhere but from AddTask and is protected by the tasksLock lock there .


func (engine *DockerTaskEngine) transitionContainer(task *apitask.Task, container *apicontainer.Container, to apicontainerstatus.ContainerStatus) {
	// Let docker events operate async so that we can continue to handle ACS / other requests
	// This is safe because 'applyContainerState' will not mutate the task
	metadata := engine.applyContainerState(task, container, to)

	engine.tasksLock.RLock()
	managedTask, ok := engine.managedTasks[task.Arn]
	engine.tasksLock.RUnlock()
	if ok {
		managedTask.emitDockerContainerChange(dockerContainerChange{
			container: container,
			event: dockerapi.DockerContainerChangeEvent{
				Status:                  to,
				DockerContainerMetadata: metadata,
			},
		})
	}
}

// transitionContainer calls applyContainerState, and then notifies the managed
// task of the change. transitionContainer is called by progressTask and
// by handleStoppedToRunningContainerTransition.


func (engine *DockerTaskEngine) applyContainerState(task *apitask.Task, container *apicontainer.Container, nextState apicontainerstatus.ContainerStatus) dockerapi.DockerContainerMetadata {
	transitionFunction, ok := engine.transitionFunctionMap()[nextState]
	if !ok {
		seelog.Criticalf("Task engine [%s]: unsupported desired state transition for container [%s]: %s",
			task.Arn, container.Name, nextState.String())
		return dockerapi.DockerContainerMetadata{Error: &impossibleTransitionError{nextState}}
	}
	metadata := transitionFunction(task, container)
	if metadata.Error != nil {
		seelog.Infof("Task engine [%s]: error transitioning container [%s] to [%s]: %v",
			task.Arn, container.Name, nextState.String(), metadata.Error)
	} else {
		seelog.Debugf("Task engine [%s]: transitioned container [%s] to [%s]",
			task.Arn, container.Name, nextState.String())
		engine.saver.Save()
	}
	return metadata
}

func ( engine * DockerTaskEngine ) applyContainerState ( task * apitask . Task , container * apicontainer . Container , nextState apicontainerstatus . ContainerStatus ) dockerapi . DockerContainerMetadata { transitionFunction , ok := engine . transitionFunctionMap ( ) [ nextState ] if ! ok { seelog . Criticalf ( " " , task . Arn , container . Name , nextState . String ( ) ) return dockerapi . DockerContainerMetadata { Error : & impossibleTransitionError { nextState } } } metadata := transitionFunction ( task , container ) if metadata . Error != nil { seelog . Infof ( " " , task . Arn , container . Name , nextState . String ( ) , metadata . Error ) } else { seelog . Debugf ( " " , task . Arn , container . Name , nextState . String ( ) ) engine . saver . Save ( ) } return metadata }

// applyContainerState moves the container to the given state by calling the
// function defined in the transitionFunctionMap for the state

applyContainerState moves the container to the given state by calling the function defined in the transitionFunctionMap for the state


func (engine *DockerTaskEngine) Version() (string, error) {
	return engine.client.Version(engine.ctx, dockerclient.VersionTimeout)
}

func ( engine * DockerTaskEngine ) Version ( ) ( string , error ) { return engine . client . Version ( engine . ctx , dockerclient . VersionTimeout ) }

// Version returns the underlying docker version.

Version returns the underlying docker version .


func NewMockSSMClient(ctrl *gomock.Controller) *MockSSMClient {
	mock := &MockSSMClient{ctrl: ctrl}
	mock.recorder = &MockSSMClientMockRecorder{mock}
	return mock
}

func NewMockSSMClient ( ctrl * gomock . Controller ) * MockSSMClient { mock := & MockSSMClient { ctrl : ctrl } mock . recorder = & MockSSMClientMockRecorder { mock } return mock }

// NewMockSSMClient creates a new mock instance


func (m *MockSSMClient) GetParameters(arg0 *ssm.GetParametersInput) (*ssm.GetParametersOutput, error) {
	ret := m.ctrl.Call(m, "GetParameters", arg0)
	ret0, _ := ret[0].(*ssm.GetParametersOutput)
	ret1, _ := ret[1].(error)
	return ret0, ret1
}

func ( m * MockSSMClient ) GetParameters ( arg0 * ssm . GetParametersInput ) ( * ssm . GetParametersOutput , error ) { ret := m . ctrl . Call ( m , " " , arg0 ) ret0 , _ := ret [ 0 ] . ( * ssm . GetParametersOutput ) ret1 , _ := ret [ 1 ] . ( error ) return ret0 , ret1 }


func (mr *MockSSMClientMockRecorder) GetParameters(arg0 interface{}) *gomock.Call {
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetParameters", reflect.TypeOf((*MockSSMClient)(nil).GetParameters), arg0)
}

func ( mr * MockSSMClientMockRecorder ) GetParameters ( arg0 interface { } ) * gomock . Call { return mr . mock . ctrl . RecordCallWithMethodType ( mr . mock , " " , reflect . TypeOf ( ( * MockSSMClient ) ( nil ) . GetParameters ) , arg0 ) }

// GetParameters indicates an expected call of GetParameters


func NewMockFactory(ctrl *gomock.Controller) *MockFactory {
	mock := &MockFactory{ctrl: ctrl}
	mock.recorder = &MockFactoryMockRecorder{mock}
	return mock
}

func NewMockFactory ( ctrl * gomock . Controller ) * MockFactory { mock := & MockFactory { ctrl : ctrl } mock . recorder = & MockFactoryMockRecorder { mock } return mock }

// NewMockFactory creates a new mock instance


func (m *MockFactory) FindClientAPIVersion(arg0 sdkclient.Client) dockerclient.DockerVersion {
	ret := m.ctrl.Call(m, "FindClientAPIVersion", arg0)
	ret0, _ := ret[0].(dockerclient.DockerVersion)
	return ret0
}

func ( m * MockFactory ) FindClientAPIVersion ( arg0 sdkclient . Client ) dockerclient . DockerVersion { ret := m . ctrl . Call ( m , " " , arg0 ) ret0 , _ := ret [ 0 ] . ( dockerclient . DockerVersion ) return ret0 }

// FindClientAPIVersion mocks base method


func (m *MockFactory) FindKnownAPIVersions() []dockerclient.DockerVersion {
	ret := m.ctrl.Call(m, "FindKnownAPIVersions")
	ret0, _ := ret[0].([]dockerclient.DockerVersion)
	return ret0
}

func ( m * MockFactory ) FindKnownAPIVersions ( ) [ ] dockerclient . DockerVersion { ret := m . ctrl . Call ( m , " " ) ret0 , _ := ret [ 0 ] . ( [ ] dockerclient . DockerVersion ) return ret0 }

// FindKnownAPIVersions mocks base method


func (m *MockFactory) GetDefaultClient() (sdkclient.Client, error) {
	ret := m.ctrl.Call(m, "GetDefaultClient")
	ret0, _ := ret[0].(sdkclient.Client)
	ret1, _ := ret[1].(error)
	return ret0, ret1
}

func ( m * MockFactory ) GetDefaultClient ( ) ( sdkclient . Client , error ) { ret := m . ctrl . Call ( m , " " ) ret0 , _ := ret [ 0 ] . ( sdkclient . Client ) ret1 , _ := ret [ 1 ] . ( error ) return ret0 , ret1 }


func NewMockFileSystem(ctrl *gomock.Controller) *MockFileSystem {
	mock := &MockFileSystem{ctrl: ctrl}
	mock.recorder = &MockFileSystemMockRecorder{mock}
	return mock
}

func NewMockFileSystem ( ctrl * gomock . Controller ) * MockFileSystem { mock := & MockFileSystem { ctrl : ctrl } mock . recorder = & MockFileSystemMockRecorder { mock } return mock }

// NewMockFileSystem creates a new mock instance


func (m *MockFileSystem) Copy(arg0 io.Writer, arg1 io.Reader) (int64, error) {
	ret := m.ctrl.Call(m, "Copy", arg0, arg1)
	ret0, _ := ret[0].(int64)
	ret1, _ := ret[1].(error)
	return ret0, ret1
}

func ( m * MockFileSystem ) Copy ( arg0 io . Writer , arg1 io . Reader ) ( int64 , error ) { ret := m . ctrl . Call ( m , " " , arg0 , arg1 ) ret0 , _ := ret [ 0 ] . ( int64 ) ret1 , _ := ret [ 1 ] . ( error ) return ret0 , ret1 }


func (m *MockFileSystem) Exit(arg0 int) {
	m.ctrl.Call(m, "Exit", arg0)
}

func ( m * MockFileSystem ) Exit ( arg0 int ) { m . ctrl . Call ( m , " " , arg0 ) }


func (m *MockFileSystem) MkdirAll(arg0 string, arg1 os.FileMode) error {
	ret := m.ctrl.Call(m, "MkdirAll", arg0, arg1)
	ret0, _ := ret[0].(error)
	return ret0
}

func ( m * MockFileSystem ) MkdirAll ( arg0 string , arg1 os . FileMode ) error { ret := m . ctrl . Call ( m , " " , arg0 , arg1 ) ret0 , _ := ret [ 0 ] . ( error ) return ret0 }


func (m *MockFileSystem) Open(arg0 string) (io.ReadWriteCloser, error) {
	ret := m.ctrl.Call(m, "Open", arg0)
	ret0, _ := ret[0].(io.ReadWriteCloser)
	ret1, _ := ret[1].(error)
	return ret0, ret1
}

func ( m * MockFileSystem ) Open ( arg0 string ) ( io . ReadWriteCloser , error ) { ret := m . ctrl . Call ( m , " " , arg0 ) ret0 , _ := ret [ 0 ] . ( io . ReadWriteCloser ) ret1 , _ := ret [ 1 ] . ( error ) return ret0 , ret1 }


func (m *MockFileSystem) ReadAll(arg0 io.Reader) ([]byte, error) {
	ret := m.ctrl.Call(m, "ReadAll", arg0)
	ret0, _ := ret[0].([]byte)
	ret1, _ := ret[1].(error)
	return ret0, ret1
}

func ( m * MockFileSystem ) ReadAll ( arg0 io . Reader ) ( [ ] byte , error ) { ret := m . ctrl . Call ( m , " " , arg0 ) ret0 , _ := ret [ 0 ] . ( [ ] byte ) ret1 , _ := ret [ 1 ] . ( error ) return ret0 , ret1 }


func (m *MockFileSystem) Remove(arg0 string) {
	m.ctrl.Call(m, "Remove", arg0)
}

func ( m * MockFileSystem ) Remove ( arg0 string ) { m . ctrl . Call ( m , " " , arg0 ) }


func (m *MockFileSystem) Rename(arg0, arg1 string) error {
	ret := m.ctrl.Call(m, "Rename", arg0, arg1)
	ret0, _ := ret[0].(error)
	return ret0
}

func ( m * MockFileSystem ) Rename ( arg0 , arg1 string ) error { ret := m . ctrl . Call ( m , " " , arg0 , arg1 ) ret0 , _ := ret [ 0 ] . ( error ) return ret0 }


func (m *MockFileSystem) TeeReader(arg0 io.Reader, arg1 io.Writer) io.Reader {
	ret := m.ctrl.Call(m, "TeeReader", arg0, arg1)
	ret0, _ := ret[0].(io.Reader)
	return ret0
}

func ( m * MockFileSystem ) TeeReader ( arg0 io . Reader , arg1 io . Writer ) io . Reader { ret := m . ctrl . Call ( m , " " , arg0 , arg1 ) ret0 , _ := ret [ 0 ] . ( io . Reader ) return ret0 }


func (agent *ecsAgent) initializeTaskENIDependencies(state dockerstate.TaskEngineState, taskEngine engine.TaskEngine) (error, bool) {
	// Check if the Agent process's pid  == 1, which means it's running without an init system
	if agent.os.Getpid() == initPID {
		// This is a terminal error. Bad things happen with invoking the
		// the ENI plugin when there's no init process in the pid namesapce.
		// Specifically, the DHClient processes that are started as children
		// of the Agent will not be reaped leading to the ENI device
		// disappearing until the Agent is killed.
		return errors.New("agent is not started with an init system"), true
	}

	// Set VPC and Subnet IDs for the instance
	if err, ok := agent.setVPCSubnet(); err != nil {
		return err, ok
	}

	// Validate that the CNI plugins exist in the expected path and that
	// they possess the right capabilities
	if err := agent.verifyCNIPluginsCapabilities(); err != nil {
		// An error here is terminal as it means that the plugins
		// do not support the ENI capability
		return err, true
	}

	// Load the pause container's image from the 'disk'
	if _, err := agent.pauseLoader.LoadImage(agent.ctx, agent.cfg, agent.dockerClient); err != nil {
		if pause.IsNoSuchFileError(err) || pause.UnsupportedPlatform(err) {
			// If the pause container's image tarball doesn't exist or if the
			// invocation is done for an unsupported platform, we cannot recover.
			// Return the error as terminal for these cases
			return err, true
		}
		return err, false
	}

	if err := agent.startUdevWatcher(state, taskEngine.StateChangeEvents()); err != nil {
		// If udev watcher was not initialized in this run because of the udev socket
		// file not being available etc, the Agent might be able to retry and succeed
		// on the next run. Hence, returning a false here for terminal bool
		return err, false
	}

	return nil, false
}

// initializeTaskENIDependencies initializes all of the dependencies required by
// the Agent to support the 'awsvpc' networking mode. A non nil error is returned
// if an error is encountered during this process. An additional boolean flag to
// indicate if this error is considered terminal is also returned

initializeTaskENIDependencies initializes all of the dependencies required by the Agent to support the awsvpc networking mode . A non nil error is returned if an error is encountered during this process . An additional boolean flag to indicate if this error is considered terminal is also returned


func (agent *ecsAgent) setVPCSubnet() (error, bool) {
	mac, err := agent.ec2MetadataClient.PrimaryENIMAC()
	if err != nil {
		return fmt.Errorf("unable to get mac address of instance's primary ENI from instance metadata: %v", err), false
	}

	vpcID, err := agent.ec2MetadataClient.VPCID(mac)
	if err != nil {
		if isInstanceLaunchedInVPC(err) {
			return fmt.Errorf("unable to get vpc id from instance metadata: %v", err), true
		}
		return instanceNotLaunchedInVPCError, false
	}

	subnetID, err := agent.ec2MetadataClient.SubnetID(mac)
	if err != nil {
		return fmt.Errorf("unable to get subnet id from instance metadata: %v", err), false
	}
	agent.vpc = vpcID
	agent.subnet = subnetID
	agent.mac = mac
	return nil, false
}

func ( agent * ecsAgent ) setVPCSubnet ( ) ( error , bool ) { mac , err := agent . ec2MetadataClient . PrimaryENIMAC ( ) if err != nil { return fmt . Errorf ( " " , err ) , false } vpcID , err := agent . ec2MetadataClient . VPCID ( mac ) if err != nil { if isInstanceLaunchedInVPC ( err ) { return fmt . Errorf ( " " , err ) , true } return instanceNotLaunchedInVPCError , false } subnetID , err := agent . ec2MetadataClient . SubnetID ( mac ) if err != nil { return fmt . Errorf ( " " , err ) , false } agent . vpc = vpcID agent . subnet = subnetID agent . mac = mac return nil , false }

// setVPCSubnet sets the vpc and subnet ids for the agent by querying the
// instance metadata service

setVPCSubnet sets the vpc and subnet ids for the agent by querying the instance metadata service


func (agent *ecsAgent) startUdevWatcher(state dockerstate.TaskEngineState, stateChangeEvents chan<- statechange.Event) error {
	seelog.Debug("Setting up ENI Watcher")
	udevMonitor, err := udevwrapper.New()
	if err != nil {
		return errors.Wrapf(err, "unable to create udev monitor")
	}
	// Create Watcher
	eniWatcher := watcher.New(agent.ctx, agent.mac, udevMonitor, state, stateChangeEvents)
	if err := eniWatcher.Init(); err != nil {
		return errors.Wrapf(err, "unable to initialize eni watcher")
	}
	go eniWatcher.Start()
	return nil
}

func
(
agent
*
ecsAgent
)
startUdevWatcher
(
state
dockerstate
.
TaskEngineState
,
stateChangeEvents
chan
<-
statechange
.
Event
)
error
{
seelog
.
Debug
(
"
"
)
udevMonitor
,
err
:=
udevwrapper
.
New
(
)
if
err
!=
{
return
errors
.
Wrapf
(
err
,
"
"
)
}
// Create Watcher
eniWatcher
:=
watcher
.
New
(
agent
.
ctx
,
agent
.
mac
,
udevMonitor
,
state
,
stateChangeEvents
)
if
err
:=
eniWatcher
.
Init
(
)
;
err
!=
{
return
errors
.
Wrapf
(
err
,
"
"
)
}
go
eniWatcher
.
Start
(
)
return
}

// startUdevWatcher starts the udev monitor and the watcher for receiving
// notifications from the monitor

startUdevWatcher starts the udev monitor and the watcher for receiving notifications from the monitor


func (gm *GenericMetrics) IncrementCallCount(callName string) {
	defer func() {
		if r := recover(); r != nil {
			seelog.Errorf("IncrementCallCount for %s panicked. Recovering quietly: %s", callName, r)
		}
	}()
	gm.lock.Lock()
	defer gm.lock.Unlock()
	gm.counterVec.WithLabelValues(callName).Inc()
}

func ( gm * GenericMetrics ) IncrementCallCount ( callName string ) { defer func ( ) { if r := recover ( ) ; r != nil { seelog . Errorf ( " " , callName , r ) } } ( ) gm . lock . Lock ( ) defer gm . lock . Unlock ( ) gm . counterVec . WithLabelValues ( callName ) . Inc ( ) }

// This function increments the call count for a specific API call
// This is invoked at the API call's start, whereas the duration metrics
// are updated at the API call's end.

This function increments the call count for a specific API call This is invoked at the API call s start whereas the duration metrics are updated at the API call s end .


func NewMockTaskEngineState(ctrl *gomock.Controller) *MockTaskEngineState {
	mock := &MockTaskEngineState{ctrl: ctrl}
	mock.recorder = &MockTaskEngineStateMockRecorder{mock}
	return mock
}

func NewMockTaskEngineState ( ctrl * gomock . Controller ) * MockTaskEngineState { mock := & MockTaskEngineState { ctrl : ctrl } mock . recorder = & MockTaskEngineStateMockRecorder { mock } return mock }

// NewMockTaskEngineState creates a new mock instance


func (m *MockTaskEngineState) AddContainer(arg0 *container.DockerContainer, arg1 *task.Task) {
	m.ctrl.Call(m, "AddContainer", arg0, arg1)
}

func ( m * MockTaskEngineState ) AddContainer ( arg0 * container . DockerContainer , arg1 * task . Task ) { m . ctrl . Call ( m , " " , arg0 , arg1 ) }


func (m *MockTaskEngineState) AddENIAttachment(arg0 *eni.ENIAttachment) {
	m.ctrl.Call(m, "AddENIAttachment", arg0)
}

func ( m * MockTaskEngineState ) AddENIAttachment ( arg0 * eni . ENIAttachment ) { m . ctrl . Call ( m , " " , arg0 ) }


func (m *MockTaskEngineState) AddImageState(arg0 *image.ImageState) {
	m.ctrl.Call(m, "AddImageState", arg0)
}

func ( m * MockTaskEngineState ) AddImageState ( arg0 * image . ImageState ) { m . ctrl . Call ( m , " " , arg0 ) }


func (m *MockTaskEngineState) AddTaskIPAddress(arg0, arg1 string) {
	m.ctrl.Call(m, "AddTaskIPAddress", arg0, arg1)
}

func ( m * MockTaskEngineState ) AddTaskIPAddress ( arg0 , arg1 string ) { m . ctrl . Call ( m , " " , arg0 , arg1 ) }


func (m *MockTaskEngineState) AllImageStates() []*image.ImageState {
	ret := m.ctrl.Call(m, "AllImageStates")
	ret0, _ := ret[0].([]*image.ImageState)
	return ret0
}

func ( m * MockTaskEngineState ) AllImageStates ( ) [ ] * image . ImageState { ret := m . ctrl . Call ( m , " " ) ret0 , _ := ret [ 0 ] . ( [ ] * image . ImageState ) return ret0 }


func (mr *MockTaskEngineStateMockRecorder) AllImageStates() *gomock.Call {
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AllImageStates", reflect.TypeOf((*MockTaskEngineState)(nil).AllImageStates))
}

func ( mr * MockTaskEngineStateMockRecorder ) AllImageStates ( ) * gomock . Call { return mr . mock . ctrl . RecordCallWithMethodType ( mr . mock , " " , reflect . TypeOf ( ( * MockTaskEngineState ) ( nil ) . AllImageStates ) ) }

// AllImageStates indicates an expected call of AllImageStates


func (m *MockTaskEngineState) AllTasks() []*task.Task {
	ret := m.ctrl.Call(m, "AllTasks")
	ret0, _ := ret[0].([]*task.Task)
	return ret0
}

func ( m * MockTaskEngineState ) AllTasks ( ) [ ] * task . Task { ret := m . ctrl . Call ( m , " " ) ret0 , _ := ret [ 0 ] . ( [ ] * task . Task ) return ret0 }


func (m *MockTaskEngineState) ContainerByID(arg0 string) (*container.DockerContainer, bool) {
	ret := m.ctrl.Call(m, "ContainerByID", arg0)
	ret0, _ := ret[0].(*container.DockerContainer)
	ret1, _ := ret[1].(bool)
	return ret0, ret1
}

func ( m * MockTaskEngineState ) ContainerByID ( arg0 string ) ( * container . DockerContainer , bool ) { ret := m . ctrl . Call ( m , " " , arg0 ) ret0 , _ := ret [ 0 ] . ( * container . DockerContainer ) ret1 , _ := ret [ 1 ] . ( bool ) return ret0 , ret1 }


func (m *MockTaskEngineState) ENIByMac(arg0 string) (*eni.ENIAttachment, bool) {
	ret := m.ctrl.Call(m, "ENIByMac", arg0)
	ret0, _ := ret[0].(*eni.ENIAttachment)
	ret1, _ := ret[1].(bool)
	return ret0, ret1
}

func ( m * MockTaskEngineState ) ENIByMac ( arg0 string ) ( * eni . ENIAttachment , bool ) { ret := m . ctrl . Call ( m , " " , arg0 ) ret0 , _ := ret [ 0 ] . ( * eni . ENIAttachment ) ret1 , _ := ret [ 1 ] . ( bool ) return ret0 , ret1 }


func (m *MockTaskEngineState) GetAllContainerIDs() []string {
	ret := m.ctrl.Call(m, "GetAllContainerIDs")
	ret0, _ := ret[0].([]string)
	return ret0
}

func ( m * MockTaskEngineState ) GetAllContainerIDs ( ) [ ] string { ret := m . ctrl . Call ( m , " " ) ret0 , _ := ret [ 0 ] . ( [ ] string ) return ret0 }


func (m *MockTaskEngineState) RemoveENIAttachment(arg0 string) {
	m.ctrl.Call(m, "RemoveENIAttachment", arg0)
}

func ( m * MockTaskEngineState ) RemoveENIAttachment ( arg0 string ) { m . ctrl . Call ( m , " " , arg0 ) }

// RemoveENIAttachment mocks base method


func (m *MockTaskEngineState) RemoveImageState(arg0 *image.ImageState) {
	m.ctrl.Call(m, "RemoveImageState", arg0)
}

func ( m * MockTaskEngineState ) RemoveImageState ( arg0 * image . ImageState ) { m . ctrl . Call ( m , " " , arg0 ) }


func (m *MockTaskEngineState) RemoveTask(arg0 *task.Task) {
	m.ctrl.Call(m, "RemoveTask", arg0)
}

func ( m * MockTaskEngineState ) RemoveTask ( arg0 * task . Task ) { m . ctrl . Call ( m , " " , arg0 ) }


func (mr *MockTaskEngineStateMockRecorder) RemoveTask(arg0 interface{}) *gomock.Call {
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveTask", reflect.TypeOf((*MockTaskEngineState)(nil).RemoveTask), arg0)
}

func ( mr * MockTaskEngineStateMockRecorder ) RemoveTask ( arg0 interface { } ) * gomock . Call { return mr . mock . ctrl . RecordCallWithMethodType ( mr . mock , " " , reflect . TypeOf ( ( * MockTaskEngineState ) ( nil ) . RemoveTask ) , arg0 ) }

// RemoveTask indicates an expected call of RemoveTask


func (m *MockTaskEngineState) TaskARNByV3EndpointID(arg0 string) (string, bool) {
	ret := m.ctrl.Call(m, "TaskARNByV3EndpointID", arg0)
	ret0, _ := ret[0].(string)
	ret1, _ := ret[1].(bool)
	return ret0, ret1
}

func ( m * MockTaskEngineState ) TaskARNByV3EndpointID ( arg0 string ) ( string , bool ) { ret := m . ctrl . Call ( m , " " , arg0 ) ret0 , _ := ret [ 0 ] . ( string ) ret1 , _ := ret [ 1 ] . ( bool ) return ret0 , ret1 }

// TaskARNByV3EndpointID mocks base method


func (m *MockTaskEngineState) TaskByArn(arg0 string) (*task.Task, bool) {
	ret := m.ctrl.Call(m, "TaskByArn", arg0)
	ret0, _ := ret[0].(*task.Task)
	ret1, _ := ret[1].(bool)
	return ret0, ret1
}

func ( m * MockTaskEngineState ) TaskByArn ( arg0 string ) ( * task . Task , bool ) { ret := m . ctrl . Call ( m , " " , arg0 ) ret0 , _ := ret [ 0 ] . ( * task . Task ) ret1 , _ := ret [ 1 ] . ( bool ) return ret0 , ret1 }


func (m *MockTaskEngineState) UnmarshalJSON(arg0 []byte) error {
	ret := m.ctrl.Call(m, "UnmarshalJSON", arg0)
	ret0, _ := ret[0].(error)
	return ret0
}

func ( m * MockTaskEngineState ) UnmarshalJSON ( arg0 [ ] byte ) error { ret := m . ctrl . Call ( m , " " , arg0 ) ret0 , _ := ret [ 0 ] . ( error ) return ret0 }


func (containerType ContainerType) String() string {
	for str, contType := range stringToContainerType {
		if contType == containerType {
			return str
		}
	}

	return "NORMAL"
}

func ( containerType ContainerType ) String ( ) string { for str , contType := range stringToContainerType { if contType == containerType { return str } } return " " }

// String converts the container type enum to a string


func (containerType *ContainerType) UnmarshalJSON(b []byte) error {
	strType := string(b)

	switch strType {
	case "null":
		*containerType = ContainerNormal
		seelog.Warn("Unmarshalled nil ContainerType as Normal")
		return nil
	// 'true' or 'false' for compatibility with state version <= 5
	case "true":
		*containerType = ContainerEmptyHostVolume
		return nil
	case "false":
		*containerType = ContainerNormal
		return nil
	}

	if len(strType) < 2 {
		*containerType = ContainerNormal
		return errors.New("invalid length set for ContainerType: " + string(b))
	}
	if b[0] != '"' || b[len(b)-1] != '"' {
		*containerType = ContainerNormal
		return errors.New("invalid value set for ContainerType, must be a string or null; got " + string(b))
	}
	strType = string(b[1 : len(b)-1])

	contType, ok := stringToContainerType[strType]
	if !ok {
		*containerType = ContainerNormal
		return errors.New("unrecognized ContainerType: " + strType)
	}
	*containerType = contType
	return nil
}

// UnmarshalJSON decodes the container type field in the JSON encoded string
// into the ContainerType object

UnmarshalJSON decodes the container type field in the JSON encoded string into the ContainerType object


func (containerType *ContainerType) MarshalJSON() ([]byte, error) {
	if containerType == nil {
		return []byte("null"), nil
	}

	return []byte(`"` + containerType.String() + `"`), nil
}

func ( containerType * ContainerType ) MarshalJSON ( ) ( [ ] byte , error ) { if containerType == nil { return [ ] byte ( " " ) , nil } return [ ] byte ( `"` + containerType . String ( ) + `"` ) , nil }

// MarshalJSON overrides the logic for JSON-encoding a ContainerType object

MarshalJSON overrides the logic for JSON - encoding a ContainerType object


func parseNanoTime(value string) time.Time {
	ts, _ := time.Parse(time.RFC3339Nano, value)
	return ts
}

func parseNanoTime ( value string ) time . Time { ts , _ := time . Parse ( time . RFC3339Nano , value ) return ts }

// parseNanoTime returns the time object from a string formatted with RFC3339Nano layout.

parseNanoTime returns the time object from a string formatted with RFC3339Nano layout .


func NewMockResponseWriter(ctrl *gomock.Controller) *MockResponseWriter {
	mock := &MockResponseWriter{ctrl: ctrl}
	mock.recorder = &MockResponseWriterMockRecorder{mock}
	return mock
}

func NewMockResponseWriter ( ctrl * gomock . Controller ) * MockResponseWriter { mock := & MockResponseWriter { ctrl : ctrl } mock . recorder = & MockResponseWriterMockRecorder { mock } return mock }

// NewMockResponseWriter creates a new mock instance


func (m *MockResponseWriter) Header() http.Header {
	ret := m.ctrl.Call(m, "Header")
	ret0, _ := ret[0].(http.Header)
	return ret0
}

func ( m * MockResponseWriter ) Header ( ) http . Header { ret := m . ctrl . Call ( m , " " ) ret0 , _ := ret [ 0 ] . ( http . Header ) return ret0 }


func (mr *MockResponseWriterMockRecorder) Header() *gomock.Call {
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Header", reflect.TypeOf((*MockResponseWriter)(nil).Header))
}

func ( mr * MockResponseWriterMockRecorder ) Header ( ) * gomock . Call { return mr . mock . ctrl . RecordCallWithMethodType ( mr . mock , " " , reflect . TypeOf ( ( * MockResponseWriter ) ( nil ) . Header ) ) }

// Header indicates an expected call of Header


func (m *MockResponseWriter) WriteHeader(arg0 int) {
	m.ctrl.Call(m, "WriteHeader", arg0)
}

func ( m * MockResponseWriter ) WriteHeader ( arg0 int ) { m . ctrl . Call ( m , " " , arg0 ) }