Browse code

service tasks: Improve error reporting

- Tasks will display all tasks (`-a` is the default and was removed)
- Nest tasks to help display history
- Display task errors inline

Signed-off-by: Andrea Luzzardi <aluzzardi@gmail.com>
(cherry picked from commit edd67fd4ad961f0782f1f94e6a26c95810dd037e)
Signed-off-by: Tibor Vass <tibor@docker.com>

Andrea Luzzardi authored on 2016/07/22 11:19:11
Showing 3 changed files
... ...
@@ -9,13 +9,11 @@ import (
9 9
 	"github.com/docker/docker/cli"
10 10
 	"github.com/docker/docker/opts"
11 11
 	"github.com/docker/engine-api/types"
12
-	"github.com/docker/engine-api/types/swarm"
13 12
 	"github.com/spf13/cobra"
14 13
 )
15 14
 
16 15
 type tasksOptions struct {
17 16
 	nodeID    string
18
-	all       bool
19 17
 	noResolve bool
20 18
 	filter    opts.FilterOpt
21 19
 }
... ...
@@ -33,7 +31,6 @@ func newTasksCommand(dockerCli *client.DockerCli) *cobra.Command {
33 33
 		},
34 34
 	}
35 35
 	flags := cmd.Flags()
36
-	flags.BoolVarP(&opts.all, "all", "a", false, "Display all instances")
37 36
 	flags.BoolVar(&opts.noResolve, "no-resolve", false, "Do not map IDs to Names")
38 37
 	flags.VarP(&opts.filter, "filter", "f", "Filter output based on conditions provided")
39 38
 
... ...
@@ -55,11 +52,6 @@ func runTasks(dockerCli *client.DockerCli, opts tasksOptions) error {
55 55
 
56 56
 	filter := opts.filter.Value()
57 57
 	filter.Add("node", node.ID)
58
-	if !opts.all && !filter.Include("desired-state") {
59
-		filter.Add("desired-state", string(swarm.TaskStateRunning))
60
-		filter.Add("desired-state", string(swarm.TaskStateAccepted))
61
-	}
62
-
63 58
 	tasks, err := client.TaskList(
64 59
 		ctx,
65 60
 		types.TaskListOptions{Filter: filter})
... ...
@@ -10,13 +10,11 @@ import (
10 10
 	"github.com/docker/docker/cli"
11 11
 	"github.com/docker/docker/opts"
12 12
 	"github.com/docker/engine-api/types"
13
-	"github.com/docker/engine-api/types/swarm"
14 13
 	"github.com/spf13/cobra"
15 14
 )
16 15
 
17 16
 type tasksOptions struct {
18 17
 	serviceID string
19
-	all       bool
20 18
 	noResolve bool
21 19
 	filter    opts.FilterOpt
22 20
 }
... ...
@@ -34,7 +32,6 @@ func newTasksCommand(dockerCli *client.DockerCli) *cobra.Command {
34 34
 		},
35 35
 	}
36 36
 	flags := cmd.Flags()
37
-	flags.BoolVarP(&opts.all, "all", "a", false, "Display all tasks")
38 37
 	flags.BoolVar(&opts.noResolve, "no-resolve", false, "Do not map IDs to Names")
39 38
 	flags.VarP(&opts.filter, "filter", "f", "Filter output based on conditions provided")
40 39
 
... ...
@@ -52,11 +49,6 @@ func runTasks(dockerCli *client.DockerCli, opts tasksOptions) error {
52 52
 
53 53
 	filter := opts.filter.Value()
54 54
 	filter.Add("service", service.ID)
55
-	if !opts.all && !filter.Include("desired-state") {
56
-		filter.Add("desired-state", string(swarm.TaskStateRunning))
57
-		filter.Add("desired-state", string(swarm.TaskStateAccepted))
58
-	}
59
-
60 55
 	if filter.Include("node") {
61 56
 		nodeFilters := filter.Get("node")
62 57
 		for _, nodeFilter := range nodeFilters {
... ...
@@ -16,7 +16,8 @@ import (
16 16
 )
17 17
 
18 18
 const (
19
-	psTaskItemFmt = "%s\t%s\t%s\t%s\t%s %s ago\t%s\t%s\n"
19
+	psTaskItemFmt = "%s\t%s\t%s\t%s\t%s\t%s %s ago\t%s\n"
20
+	maxErrLength  = 30
20 21
 )
21 22
 
22 23
 type tasksBySlot []swarm.Task
... ...
@@ -47,7 +48,9 @@ func Print(dockerCli *client.DockerCli, ctx context.Context, tasks []swarm.Task,
47 47
 
48 48
 	// Ignore flushing errors
49 49
 	defer writer.Flush()
50
-	fmt.Fprintln(writer, strings.Join([]string{"ID", "NAME", "SERVICE", "IMAGE", "LAST STATE", "DESIRED STATE", "NODE"}, "\t"))
50
+	fmt.Fprintln(writer, strings.Join([]string{"ID", "NAME", "IMAGE", "NODE", "DESIRED STATE", "CURRENT STATE", "ERROR"}, "\t"))
51
+
52
+	prevName := ""
51 53
 	for _, task := range tasks {
52 54
 		serviceValue, err := resolver.Resolve(ctx, swarm.Service{}, task.ServiceID)
53 55
 		if err != nil {
... ...
@@ -57,21 +60,39 @@ func Print(dockerCli *client.DockerCli, ctx context.Context, tasks []swarm.Task,
57 57
 		if err != nil {
58 58
 			return err
59 59
 		}
60
+
60 61
 		name := serviceValue
61 62
 		if task.Slot > 0 {
62 63
 			name = fmt.Sprintf("%s.%d", name, task.Slot)
63 64
 		}
65
+
66
+		// Indent the name if necessary
67
+		indentedName := name
68
+		if prevName == name {
69
+			indentedName = fmt.Sprintf(" \\_ %s", indentedName)
70
+		}
71
+		prevName = name
72
+
73
+		// Trim and quote the error message.
74
+		taskErr := task.Status.Err
75
+		if len(taskErr) > maxErrLength {
76
+			taskErr = fmt.Sprintf("%s…", taskErr[:maxErrLength-1])
77
+		}
78
+		if len(taskErr) > 0 {
79
+			taskErr = fmt.Sprintf("\"%s\"", taskErr)
80
+		}
81
+
64 82
 		fmt.Fprintf(
65 83
 			writer,
66 84
 			psTaskItemFmt,
67 85
 			task.ID,
68
-			name,
69
-			serviceValue,
86
+			indentedName,
70 87
 			task.Spec.ContainerSpec.Image,
88
+			nodeValue,
89
+			client.PrettyPrint(task.DesiredState),
71 90
 			client.PrettyPrint(task.Status.State),
72 91
 			strings.ToLower(units.HumanDuration(time.Since(task.Status.Timestamp))),
73
-			client.PrettyPrint(task.DesiredState),
74
-			nodeValue,
92
+			taskErr,
75 93
 		)
76 94
 	}
77 95