mirror of https://github.com/docker/compose.git
Detect task failures
Signed-off-by: aiordache <anca.iordache@docker.com>
This commit is contained in:
parent
144c403e96
commit
d3effd2ead
|
@ -77,4 +77,5 @@ type Stack struct {
|
||||||
ID string
|
ID string
|
||||||
Name string
|
Name string
|
||||||
Status string
|
Status string
|
||||||
|
Reason string
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
|
@ -60,7 +61,8 @@ func runList(ctx context.Context, opts composeOptions) error {
|
||||||
view := viewFromStackList(stackList)
|
view := viewFromStackList(stackList)
|
||||||
return formatter.Print(view, opts.Format, os.Stdout, func(w io.Writer) {
|
return formatter.Print(view, opts.Format, os.Stdout, func(w io.Writer) {
|
||||||
for _, stack := range view {
|
for _, stack := range view {
|
||||||
_, _ = fmt.Fprintf(w, "%s\t%s\n", stack.Name, stack.Status)
|
_, _ = fmt.Fprintf(w, "%s\t%s\n", stack.Name, strings.TrimSpace(
|
||||||
|
fmt.Sprintf("%s %s", stack.Status, stack.Reason))
|
||||||
}
|
}
|
||||||
}, "NAME", "STATUS")
|
}, "NAME", "STATUS")
|
||||||
}
|
}
|
||||||
|
|
112
ecs/sdk.go
112
ecs/sdk.go
|
@ -304,29 +304,113 @@ func (s sdk) ListStacks(ctx context.Context, name string) ([]compose.Stack, erro
|
||||||
}
|
}
|
||||||
stacks := []compose.Stack{}
|
stacks := []compose.Stack{}
|
||||||
for _, stack := range cfStacks.Stacks {
|
for _, stack := range cfStacks.Stacks {
|
||||||
|
skip := true
|
||||||
for _, t := range stack.Tags {
|
for _, t := range stack.Tags {
|
||||||
if *t.Key == compose.ProjectTag {
|
if *t.Key == compose.ProjectTag {
|
||||||
status := compose.RUNNING
|
skip = false
|
||||||
switch aws.StringValue(stack.StackStatus) {
|
|
||||||
case "CREATE_IN_PROGRESS":
|
|
||||||
status = compose.STARTING
|
|
||||||
case "DELETE_IN_PROGRESS":
|
|
||||||
status = compose.REMOVING
|
|
||||||
case "UPDATE_IN_PROGRESS":
|
|
||||||
status = compose.UPDATING
|
|
||||||
}
|
|
||||||
stacks = append(stacks, compose.Stack{
|
|
||||||
ID: aws.StringValue(stack.StackId),
|
|
||||||
Name: aws.StringValue(stack.StackName),
|
|
||||||
Status: status,
|
|
||||||
})
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if skip {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
status := compose.RUNNING
|
||||||
|
reason := ""
|
||||||
|
switch aws.StringValue(stack.StackStatus) {
|
||||||
|
case "CREATE_IN_PROGRESS":
|
||||||
|
status = compose.STARTING
|
||||||
|
case "DELETE_IN_PROGRESS":
|
||||||
|
status = compose.REMOVING
|
||||||
|
case "UPDATE_IN_PROGRESS":
|
||||||
|
status = compose.UPDATING
|
||||||
|
}
|
||||||
|
if status == compose.STARTING {
|
||||||
|
if err := s.CheckStackState(ctx, aws.StringValue(stack.StackName)); err != nil {
|
||||||
|
status = compose.FAILED
|
||||||
|
reason = err.Error()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stacks = append(stacks, compose.Stack{
|
||||||
|
ID: aws.StringValue(stack.StackId),
|
||||||
|
Name: aws.StringValue(stack.StackName),
|
||||||
|
Status: status,
|
||||||
|
Reason: reason,
|
||||||
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
return stacks, nil
|
return stacks, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s sdk) CheckStackState(ctx context.Context, name string) error {
|
||||||
|
resources, err := s.CF.ListStackResourcesWithContext(ctx, &cloudformation.ListStackResourcesInput{
|
||||||
|
StackName: aws.String(name),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
services := []*string{}
|
||||||
|
serviceNames := []string{}
|
||||||
|
var cluster *string
|
||||||
|
for _, r := range resources.StackResourceSummaries {
|
||||||
|
if aws.StringValue(r.ResourceType) == "AWS::ECS::Cluster" {
|
||||||
|
cluster = r.PhysicalResourceId
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if aws.StringValue(r.ResourceType) == "AWS::ECS::Service" {
|
||||||
|
if r.PhysicalResourceId == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
services = append(services, r.PhysicalResourceId)
|
||||||
|
serviceNames = append(serviceNames, *r.LogicalResourceId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i, service := range services {
|
||||||
|
err := s.CheckTaskState(ctx, aws.StringValue(cluster), aws.StringValue(service))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%s error: %s", serviceNames[i], err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s sdk) CheckTaskState(ctx context.Context, cluster string, serviceName string) error {
|
||||||
|
tasks, err := s.ECS.ListTasksWithContext(ctx, &ecs.ListTasksInput{
|
||||||
|
Cluster: aws.String(cluster),
|
||||||
|
ServiceName: aws.String(serviceName),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(tasks.TaskArns) > 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
tasks, err = s.ECS.ListTasksWithContext(ctx, &ecs.ListTasksInput{
|
||||||
|
Cluster: aws.String(cluster),
|
||||||
|
ServiceName: aws.String(serviceName),
|
||||||
|
DesiredStatus: aws.String("STOPPED"),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(tasks.TaskArns) > 0 {
|
||||||
|
taskDescriptions, err := s.ECS.DescribeTasksWithContext(ctx, &ecs.DescribeTasksInput{
|
||||||
|
Cluster: aws.String(cluster),
|
||||||
|
Tasks: tasks.TaskArns,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(taskDescriptions.Tasks) > 0 {
|
||||||
|
recentTask := taskDescriptions.Tasks[0]
|
||||||
|
switch aws.StringValue(recentTask.StopCode) {
|
||||||
|
case "TaskFailedToStart":
|
||||||
|
return fmt.Errorf(aws.StringValue(recentTask.StoppedReason))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s sdk) DescribeStackEvents(ctx context.Context, stackID string) ([]*cloudformation.StackEvent, error) {
|
func (s sdk) DescribeStackEvents(ctx context.Context, stackID string) ([]*cloudformation.StackEvent, error) {
|
||||||
// Fixme implement Paginator on Events and return as a chan(events)
|
// Fixme implement Paginator on Events and return as a chan(events)
|
||||||
events := []*cloudformation.StackEvent{}
|
events := []*cloudformation.StackEvent{}
|
||||||
|
|
26
ecs/wait.go
26
ecs/wait.go
|
@ -52,6 +52,7 @@ func (b *ecsAPIService) WaitStackCompletion(ctx context.Context, name string, op
|
||||||
|
|
||||||
var completed bool
|
var completed bool
|
||||||
var stackErr error
|
var stackErr error
|
||||||
|
|
||||||
for !completed {
|
for !completed {
|
||||||
select {
|
select {
|
||||||
case <-done:
|
case <-done:
|
||||||
|
@ -77,8 +78,8 @@ func (b *ecsAPIService) WaitStackCompletion(ctx context.Context, name string, op
|
||||||
reason := aws.StringValue(event.ResourceStatusReason)
|
reason := aws.StringValue(event.ResourceStatusReason)
|
||||||
status := aws.StringValue(event.ResourceStatus)
|
status := aws.StringValue(event.ResourceStatus)
|
||||||
progressStatus := progress.Working
|
progressStatus := progress.Working
|
||||||
|
|
||||||
switch status {
|
switch status {
|
||||||
|
|
||||||
case "CREATE_COMPLETE":
|
case "CREATE_COMPLETE":
|
||||||
if operation == stackCreate {
|
if operation == stackCreate {
|
||||||
progressStatus = progress.Done
|
progressStatus = progress.Done
|
||||||
|
@ -100,12 +101,33 @@ func (b *ecsAPIService) WaitStackCompletion(ctx context.Context, name string, op
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Event(progress.Event{
|
w.Event(progress.Event{
|
||||||
ID: resource,
|
ID: resource,
|
||||||
Status: progressStatus,
|
Status: progressStatus,
|
||||||
StatusText: status,
|
StatusText: reason,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
if operation != stackCreate || stackErr != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := b.SDK.CheckStackState(ctx, name); err != nil {
|
||||||
|
stackErr = err
|
||||||
|
b.SDK.DeleteStack(ctx, name)
|
||||||
|
operation = stackDelete
|
||||||
|
|
||||||
|
reason := err.Error()
|
||||||
|
if len(reason) > 30 {
|
||||||
|
reason = reason[:30] + "..."
|
||||||
|
}
|
||||||
|
w.Event(progress.Event{
|
||||||
|
ID: name,
|
||||||
|
Status: progress.Error,
|
||||||
|
StatusText: reason,
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return stackErr
|
return stackErr
|
||||||
|
|
Loading…
Reference in New Issue