// ListAppTasks lists all the tasks for the app. func (c *Client) ListAppTasks(ctx context.Context, appID string, input *ecs.ListTasksInput) (*ecs.ListTasksOutput, error) { var arns []*string resp, err := c.ListAppServices(ctx, appID, &ecs.ListServicesInput{ Cluster: input.Cluster, }) if err != nil { return nil, err } for _, s := range resp.ServiceARNs { id, err := arn.ResourceID(*s) if err != nil { return nil, err } t, err := c.ListTasks(ctx, &ecs.ListTasksInput{ Cluster: input.Cluster, ServiceName: aws.String(id), }) if err != nil { return nil, err } if len(t.TaskARNs) == 0 { continue } arns = append(arns, t.TaskARNs...) } return &ecs.ListTasksOutput{ TaskARNs: arns, }, nil }
// ListAppServices lists all services for the app. func (c *Client) ListAppServices(ctx context.Context, appID string, input *ecs.ListServicesInput) (*ecs.ListServicesOutput, error) { var serviceArns []*string if err := c.ListServicesPages(ctx, input, func(resp *ecs.ListServicesOutput, lastPage bool) bool { serviceArns = append(serviceArns, resp.ServiceArns...) return true }); err != nil { return nil, err } var arns []*string for _, a := range serviceArns { if a == nil { continue } id, err := arn.ResourceID(*a) if err != nil { return nil, err } appName, _ := c.split(&id) if appName == appID { arns = append(arns, a) } } return &ecs.ListServicesOutput{ ServiceArns: arns, }, nil }
// ListAppServices lists all services for the app. func (c *Client) ListAppServices(ctx context.Context, appID string, input *ecs.ListServicesInput) (*ecs.ListServicesOutput, error) { resp, err := c.ListServices(ctx, input) if err != nil { return resp, err } var arns []*string for _, a := range resp.ServiceARNs { if a == nil { continue } id, err := arn.ResourceID(*a) if err != nil { return resp, err } appName, _ := c.split(&id) if appName == appID { arns = append(arns, a) } } return &ecs.ListServicesOutput{ ServiceARNs: arns, }, nil }
// tasks returns all of the ECS tasks for this app. func (s *Scheduler) tasks(app string) ([]*ecs.Task, error) { services, err := s.Services(app) if err != nil { return nil, err } var arns []*string // Find all of the tasks started by the ECS services. for process, serviceArn := range services { id, err := arn.ResourceID(serviceArn) if err != nil { return nil, err } var taskArns []*string if err := s.ecs.ListTasksPages(&ecs.ListTasksInput{ Cluster: aws.String(s.Cluster), ServiceName: aws.String(id), }, func(resp *ecs.ListTasksOutput, lastPage bool) bool { taskArns = append(taskArns, resp.TaskArns...) return true }); err != nil { return nil, fmt.Errorf("error listing tasks for %s: %v", process, err) } if len(taskArns) == 0 { continue } arns = append(arns, taskArns...) } // Find all of the tasks started by Run. if err := s.ecs.ListTasksPages(&ecs.ListTasksInput{ Cluster: aws.String(s.Cluster), StartedBy: aws.String(app), }, func(resp *ecs.ListTasksOutput, lastPage bool) bool { arns = append(arns, resp.TaskArns...) return true }); err != nil { return nil, fmt.Errorf("error listing tasks started by %s: %v", app, err) } var tasks []*ecs.Task for _, chunk := range chunkStrings(arns, MaxDescribeTasks) { resp, err := s.ecs.DescribeTasks(&ecs.DescribeTasksInput{ Cluster: aws.String(s.Cluster), Tasks: chunk, }) if err != nil { return nil, fmt.Errorf("error describing %d tasks: %v", len(chunk), err) } tasks = append(tasks, resp.Tasks...) } return tasks, nil }
// Instances returns all of the running tasks for this application. func (s *Scheduler) Instances(ctx context.Context, app string) ([]*scheduler.Instance, error) { var instances []*scheduler.Instance tasks, err := s.tasks(app) if err != nil { return nil, err } taskDefinitions := make(map[string]*ecs.TaskDefinition) for _, t := range tasks { k := *t.TaskDefinitionArn if _, ok := taskDefinitions[k]; !ok { resp, err := s.ecs.DescribeTaskDefinition(&ecs.DescribeTaskDefinitionInput{ TaskDefinition: t.TaskDefinitionArn, }) if err != nil { return nil, err } taskDefinitions[k] = resp.TaskDefinition } } for _, t := range tasks { taskDefinition := taskDefinitions[*t.TaskDefinitionArn] id, err := arn.ResourceID(*t.TaskArn) if err != nil { return instances, err } p, err := taskDefinitionToProcess(taskDefinition) if err != nil { return instances, err } state := safeString(t.LastStatus) var updatedAt time.Time switch state { case "PENDING": updatedAt = *t.CreatedAt case "RUNNING": updatedAt = *t.StartedAt case "STOPPED": updatedAt = *t.StoppedAt } instances = append(instances, &scheduler.Instance{ Process: p, State: state, ID: id, UpdatedAt: updatedAt, }) } return instances, nil }
// Instances returns all instances that are currently running, pending or // draining. func (m *Scheduler) Instances(ctx context.Context, appID string) ([]*scheduler.Instance, error) { var instances []*scheduler.Instance tasks, err := m.describeAppTasks(ctx, appID) if err != nil { return instances, err } for _, t := range tasks { resp, err := m.ecs.DescribeTaskDefinition(ctx, &ecs.DescribeTaskDefinitionInput{ TaskDefinition: t.TaskDefinitionArn, }) if err != nil { return instances, err } id, err := arn.ResourceID(*t.TaskArn) if err != nil { return instances, err } p, err := taskDefinitionToProcess(resp.TaskDefinition) if err != nil { return instances, err } state := safeString(t.LastStatus) var updatedAt time.Time switch state { case "PENDING": updatedAt = *t.CreatedAt case "RUNNING": updatedAt = *t.StartedAt case "STOPPED": updatedAt = *t.StoppedAt } instances = append(instances, &scheduler.Instance{ Process: p, State: state, ID: id, UpdatedAt: updatedAt, }) } return instances, nil }
// ListAppTasks lists all the tasks for the app. func (c *Client) ListAppTasks(ctx context.Context, appID string, input *ecs.ListTasksInput) (*ecs.ListTasksOutput, error) { var arns []*string resp, err := c.ListAppServices(ctx, appID, &ecs.ListServicesInput{ Cluster: input.Cluster, }) if err != nil { return nil, err } // TODO(ejholmes): Parallelize the calls to list the tasks. for _, s := range resp.ServiceArns { id, err := arn.ResourceID(*s) if err != nil { return nil, err } var taskArns []*string if err := c.ListTasksPages(ctx, &ecs.ListTasksInput{ Cluster: input.Cluster, ServiceName: aws.String(id), }, func(resp *ecs.ListTasksOutput, lastPage bool) bool { taskArns = append(taskArns, resp.TaskArns...) return true }); err != nil { return nil, err } if len(taskArns) == 0 { continue } arns = append(arns, taskArns...) } return &ecs.ListTasksOutput{ TaskArns: arns, }, nil }
// Instances returns all instances that are currently running, pending or // draining. func (m *Scheduler) Instances(ctx context.Context, appID string) ([]*scheduler.Instance, error) { var instances []*scheduler.Instance tasks, err := m.describeAppTasks(ctx, appID) if err != nil { return instances, err } for _, t := range tasks { resp, err := m.ecs.DescribeTaskDefinition(ctx, &ecs.DescribeTaskDefinitionInput{ TaskDefinition: t.TaskDefinitionArn, }) if err != nil { return instances, err } id, err := arn.ResourceID(*t.TaskArn) if err != nil { return instances, err } p, err := taskDefinitionToProcess(resp.TaskDefinition) if err != nil { return instances, err } instances = append(instances, &scheduler.Instance{ Process: p, State: safeString(t.LastStatus), ID: id, UpdatedAt: timex.Now(), }) } return instances, nil }
// Instances returns all of the running tasks for this application. func (s *Scheduler) Instances(ctx context.Context, app string) ([]*scheduler.Instance, error) { var instances []*scheduler.Instance tasks, err := s.tasks(app) if err != nil { return nil, err } taskDefinitions := make(map[string]*ecs.TaskDefinition) for _, t := range tasks { k := *t.TaskDefinitionArn if _, ok := taskDefinitions[k]; !ok { resp, err := s.ecs.DescribeTaskDefinition(&ecs.DescribeTaskDefinitionInput{ TaskDefinition: t.TaskDefinitionArn, }) if err != nil { return nil, err } taskDefinitions[k] = resp.TaskDefinition } } // Map from clusterARN to containerInstanceARN pointers to batch tasks from the same cluster clusterMap := make(map[string][]*string) for _, t := range tasks { k := *t.ClusterArn clusterMap[k] = append(clusterMap[k], t.ContainerInstanceArn) } // Map from containerInstanceARN to ec2-instance-id hostMap := make(map[string]string) for clusterArn, containerArnPtrs := range clusterMap { for _, chunk := range chunkStrings(containerArnPtrs, MaxDescribeContainerInstances) { resp, err := s.ecs.DescribeContainerInstances(&ecs.DescribeContainerInstancesInput{ Cluster: aws.String(clusterArn), ContainerInstances: chunk, }) if err != nil { return nil, err } for _, f := range resp.Failures { return nil, fmt.Errorf("error describing container instance %s: %s", aws.StringValue(f.Arn), aws.StringValue(f.Reason)) } for _, ci := range resp.ContainerInstances { hostMap[aws.StringValue(ci.ContainerInstanceArn)] = aws.StringValue(ci.Ec2InstanceId) } } } for _, t := range tasks { taskDefinition := taskDefinitions[*t.TaskDefinitionArn] id, err := arn.ResourceID(*t.TaskArn) if err != nil { return instances, err } hostId := hostMap[*t.ContainerInstanceArn] p, err := taskDefinitionToProcess(taskDefinition) if err != nil { return instances, err } state := aws.StringValue(t.LastStatus) var updatedAt time.Time switch state { case "PENDING": updatedAt = *t.CreatedAt case "RUNNING": updatedAt = *t.StartedAt case "STOPPED": updatedAt = *t.StoppedAt } instances = append(instances, &scheduler.Instance{ Process: p, State: state, ID: id, Host: scheduler.Host{ID: hostId}, UpdatedAt: updatedAt, }) } return instances, nil }