// startSession creates a session with ACS and handles requests from ACS // It also tries to repeatedly connect to ACS when disconnected func startSession(ctx context.Context, args StartSessionArguments, backoff *utils.SimpleBackoff, acsResources sessionResources) error { for { acsError := startSessionOnce(ctx, args, backoff, acsResources) select { case <-ctx.Done(): return ctx.Err() default: } if acsError == nil || acsError == io.EOF { backoff.Reset() } else { seelog.Infof("Error from acs; backing off, err: %v", acsError) args.time().Sleep(backoff.Duration()) } } }
// startSession creates a session with ACS and handles requests from ACS // It also tries to repeatedly connect to ACS when disconnected func startSession(ctx context.Context, args StartSessionArguments, backoff *utils.SimpleBackoff, acsResources sessionResources) error { for { acsError := startSessionOnce(ctx, args, backoff, acsResources) select { case <-ctx.Done(): return ctx.Err() default: } if acsError == nil || acsError == io.EOF { backoff.Reset() } else if strings.HasPrefix(acsError.Error(), "InactiveInstanceException:") { seelog.Debug("Container instance is deregistered, notifying listeners") err := args.DeregisterInstanceEventStream.WriteToEventStream(struct{}{}) if err != nil { seelog.Debugf("Failed to write to deregister container instance event stream, err: %v", err) } } else { seelog.Infof("Error from acs; backing off, err: %v", acsError) args.time().Sleep(backoff.Duration()) } } }
// startACSSession starts a session with ACS. It adds request handlers for various // kinds of messages expected from ACS. It returns on server disconnection or when // the context is cancelled func startACSSession(ctx context.Context, client wsclient.ClientServer, timer ttime.Timer, args StartSessionArguments, backoff *utils.SimpleBackoff, acsSessionState sessionState) error { // Any message from the server resets the disconnect timeout client.SetAnyRequestHandler(anyMessageHandler(timer)) cfg := args.Config refreshCredsHandler := newRefreshCredentialsHandler(ctx, cfg.Cluster, args.ContainerInstanceArn, client, args.CredentialsManager, args.TaskEngine) defer refreshCredsHandler.clearAcks() refreshCredsHandler.start() client.AddRequestHandler(refreshCredsHandler.handlerFunc()) // Add request handler for handling payload messages from ACS payloadHandler := newPayloadRequestHandler(ctx, args.TaskEngine, args.ECSClient, cfg.Cluster, args.ContainerInstanceArn, client, args.StateManager, refreshCredsHandler, args.CredentialsManager) // Clear the acks channel on return because acks of messageids don't have any value across sessions defer payloadHandler.clearAcks() payloadHandler.start() client.AddRequestHandler(payloadHandler.handlerFunc()) // Ignore heartbeat messages; anyMessageHandler gets 'em client.AddRequestHandler(func(*ecsacs.HeartbeatMessage) {}) updater.AddAgentUpdateHandlers(client, cfg, args.StateManager, args.TaskEngine) err := client.Connect() if err != nil { seelog.Errorf("Error connecting to ACS: %v", err) return err } acsSessionState.connectedToACS() backoffResetTimer := args.time().AfterFunc(utils.AddJitter(args.heartbeatTimeout(), args.heartbeatJitter()), func() { // If we do not have an error connecting and remain connected for at // least 5 or so minutes, reset the backoff. This prevents disconnect // errors that only happen infrequently from damaging the // reconnectability as significantly. backoff.Reset() }) defer backoffResetTimer.Stop() serveErr := make(chan error, 1) go func() { serveErr <- client.Serve() }() for { select { case <-ctx.Done(): // Stop receiving and sending messages from and to ACS when // the context received from the main function is canceled payloadHandler.stop() refreshCredsHandler.stop() return ctx.Err() case err := <-serveErr: // Stop receiving and sending messages from and to ACS when // client.Serve returns an error. This can happen when the // the connection is closed by ACS or the agent payloadHandler.stop() refreshCredsHandler.stop() return err } } }