Browse code

Adopt text/template in node inspect

Signed-off-by: Manjunath A Kumatagi <mkumatag@in.ibm.com>

Manjunath A Kumatagi authored on 2017/03/27 02:18:40
Showing 2 changed files
... ...
@@ -1,14 +1,67 @@
1 1
 package formatter
2 2
 
3 3
 import (
4
+	"fmt"
5
+	"strings"
6
+
4 7
 	"github.com/docker/docker/api/types"
5 8
 	"github.com/docker/docker/api/types/swarm"
6 9
 	"github.com/docker/docker/cli/command"
10
+	"github.com/docker/docker/cli/command/inspect"
11
+	units "github.com/docker/go-units"
7 12
 )
8 13
 
9 14
 const (
10
-	defaultNodeTableFormat = "table {{.ID}} {{if .Self}}*{{else}} {{ end }}\t{{.Hostname}}\t{{.Status}}\t{{.Availability}}\t{{.ManagerStatus}}"
11
-
15
+	defaultNodeTableFormat           = "table {{.ID}} {{if .Self}}*{{else}} {{ end }}\t{{.Hostname}}\t{{.Status}}\t{{.Availability}}\t{{.ManagerStatus}}"
16
+	nodeInspectPrettyTemplate Format = `ID:			{{.ID}}
17
+{{- if .Name }}
18
+Name:			{{.Name}}
19
+{{- end }}
20
+{{- if .Labels }}
21
+Labels:
22
+{{- range $k, $v := .Labels }}
23
+ - {{ $k }}{{if $v }}={{ $v }}{{ end }}
24
+{{- end }}{{ end }}
25
+Hostname:              	{{.Hostname}}
26
+Joined at:             	{{.CreatedAt}}
27
+Status:
28
+ State:			{{.StatusState}}
29
+ {{- if .HasStatusMessage}}
30
+ Message:              	{{.StatusMessage}}
31
+ {{- end}}
32
+ Availability:         	{{.SpecAvailability}}
33
+ {{- if .Status.Addr}}
34
+ Address:		{{.StatusAddr}}
35
+ {{- end}}
36
+{{- if .HasManagerStatus}}
37
+Manager Status:
38
+ Address:		{{.ManagerStatusAddr}}
39
+ Raft Status:		{{.ManagerStatusReachability}}
40
+ {{- if .IsManagerStatusLeader}}
41
+ Leader:		Yes
42
+ {{- else}}
43
+ Leader:		No
44
+ {{- end}}
45
+{{- end}}
46
+Platform:
47
+ Operating System:	{{.PlatformOS}}
48
+ Architecture:		{{.PlatformArchitecture}}
49
+Resources:
50
+ CPUs:			{{.ResourceNanoCPUs}}
51
+ Memory:		{{.ResourceMemory}}
52
+{{- if .HasEnginePlugins}}
53
+Plugins:
54
+{{- range $k, $v := .EnginePlugins }}
55
+ {{ $k }}:{{if $v }}		{{ $v }}{{ end }}
56
+{{- end }}
57
+{{- end }}
58
+Engine Version:		{{.EngineVersion}}
59
+{{- if .EngineLabels}}
60
+Engine Labels:
61
+{{- range $k, $v := .EngineLabels }}
62
+ - {{ $k }}{{if $v }}={{ $v }}{{ end }}
63
+{{- end }}{{- end }}
64
+`
12 65
 	nodeIDHeader        = "ID"
13 66
 	selfHeader          = ""
14 67
 	hostnameHeader      = "HOSTNAME"
... ...
@@ -19,6 +72,8 @@ const (
19 19
 // NewNodeFormat returns a Format for rendering using a node Context
20 20
 func NewNodeFormat(source string, quiet bool) Format {
21 21
 	switch source {
22
+	case PrettyFormatKey:
23
+		return nodeInspectPrettyTemplate
22 24
 	case TableFormatKey:
23 25
 		if quiet {
24 26
 			return defaultQuietFormat
... ...
@@ -99,3 +154,139 @@ func (c *nodeContext) ManagerStatus() string {
99 99
 	}
100 100
 	return command.PrettyPrint(reachability)
101 101
 }
102
+
103
+// NodeInspectWrite renders the context for a list of services
104
+func NodeInspectWrite(ctx Context, refs []string, getRef inspect.GetRefFunc) error {
105
+	if ctx.Format != nodeInspectPrettyTemplate {
106
+		return inspect.Inspect(ctx.Output, refs, string(ctx.Format), getRef)
107
+	}
108
+	render := func(format func(subContext subContext) error) error {
109
+		for _, ref := range refs {
110
+			nodeI, _, err := getRef(ref)
111
+			if err != nil {
112
+				return err
113
+			}
114
+			node, ok := nodeI.(swarm.Node)
115
+			if !ok {
116
+				return fmt.Errorf("got wrong object to inspect :%v", ok)
117
+			}
118
+			if err := format(&nodeInspectContext{Node: node}); err != nil {
119
+				return err
120
+			}
121
+		}
122
+		return nil
123
+	}
124
+	return ctx.Write(&nodeInspectContext{}, render)
125
+}
126
+
127
+type nodeInspectContext struct {
128
+	swarm.Node
129
+	subContext
130
+}
131
+
132
+func (ctx *nodeInspectContext) ID() string {
133
+	return ctx.Node.ID
134
+}
135
+
136
+func (ctx *nodeInspectContext) Name() string {
137
+	return ctx.Node.Spec.Name
138
+}
139
+
140
+func (ctx *nodeInspectContext) Labels() map[string]string {
141
+	return ctx.Node.Spec.Labels
142
+}
143
+
144
+func (ctx *nodeInspectContext) Hostname() string {
145
+	return ctx.Node.Description.Hostname
146
+}
147
+
148
+func (ctx *nodeInspectContext) CreatedAt() string {
149
+	return command.PrettyPrint(ctx.Node.CreatedAt)
150
+}
151
+
152
+func (ctx *nodeInspectContext) StatusState() string {
153
+	return command.PrettyPrint(ctx.Node.Status.State)
154
+}
155
+
156
+func (ctx *nodeInspectContext) HasStatusMessage() bool {
157
+	return ctx.Node.Status.Message != ""
158
+}
159
+
160
+func (ctx *nodeInspectContext) StatusMessage() string {
161
+	return command.PrettyPrint(ctx.Node.Status.Message)
162
+}
163
+
164
+func (ctx *nodeInspectContext) SpecAvailability() string {
165
+	return command.PrettyPrint(ctx.Node.Spec.Availability)
166
+}
167
+
168
+func (ctx *nodeInspectContext) HasStatusAddr() bool {
169
+	return ctx.Node.Status.Addr != ""
170
+}
171
+
172
+func (ctx *nodeInspectContext) StatusAddr() string {
173
+	return ctx.Node.Status.Addr
174
+}
175
+
176
+func (ctx *nodeInspectContext) HasManagerStatus() bool {
177
+	return ctx.Node.ManagerStatus != nil
178
+}
179
+
180
+func (ctx *nodeInspectContext) ManagerStatusAddr() string {
181
+	return ctx.Node.ManagerStatus.Addr
182
+}
183
+
184
+func (ctx *nodeInspectContext) ManagerStatusReachability() string {
185
+	return command.PrettyPrint(ctx.Node.ManagerStatus.Reachability)
186
+}
187
+
188
+func (ctx *nodeInspectContext) IsManagerStatusLeader() bool {
189
+	return ctx.Node.ManagerStatus.Leader
190
+}
191
+
192
+func (ctx *nodeInspectContext) PlatformOS() string {
193
+	return ctx.Node.Description.Platform.OS
194
+}
195
+
196
+func (ctx *nodeInspectContext) PlatformArchitecture() string {
197
+	return ctx.Node.Description.Platform.Architecture
198
+}
199
+
200
+func (ctx *nodeInspectContext) ResourceNanoCPUs() int {
201
+	if ctx.Node.Description.Resources.NanoCPUs == 0 {
202
+		return int(0)
203
+	}
204
+	return int(ctx.Node.Description.Resources.NanoCPUs) / 1e9
205
+}
206
+
207
+func (ctx *nodeInspectContext) ResourceMemory() string {
208
+	if ctx.Node.Description.Resources.MemoryBytes == 0 {
209
+		return ""
210
+	}
211
+	return units.BytesSize(float64(ctx.Node.Description.Resources.MemoryBytes))
212
+}
213
+
214
+func (ctx *nodeInspectContext) HasEnginePlugins() bool {
215
+	return len(ctx.Node.Description.Engine.Plugins) > 0
216
+}
217
+
218
+func (ctx *nodeInspectContext) EnginePlugins() map[string]string {
219
+	pluginMap := map[string][]string{}
220
+	for _, p := range ctx.Node.Description.Engine.Plugins {
221
+		pluginMap[p.Type] = append(pluginMap[p.Type], p.Name)
222
+	}
223
+
224
+	pluginNamesByType := map[string]string{}
225
+	for k, v := range pluginMap {
226
+		pluginNamesByType[k] = strings.Join(v, ", ")
227
+	}
228
+	return pluginNamesByType
229
+}
230
+
231
+func (ctx *nodeInspectContext) EngineLabels() map[string]string {
232
+	return ctx.Node.Description.Engine.Labels
233
+}
234
+
235
+func (ctx *nodeInspectContext) EngineVersion() string {
236
+	return ctx.Node.Description.Engine.EngineVersion
237
+}
... ...
@@ -2,16 +2,11 @@ package node
2 2
 
3 3
 import (
4 4
 	"fmt"
5
-	"io"
6
-	"sort"
7 5
 	"strings"
8 6
 
9
-	"github.com/docker/docker/api/types/swarm"
10 7
 	"github.com/docker/docker/cli"
11 8
 	"github.com/docker/docker/cli/command"
12
-	"github.com/docker/docker/cli/command/inspect"
13
-	"github.com/docker/docker/pkg/ioutils"
14
-	"github.com/docker/go-units"
9
+	"github.com/docker/docker/cli/command/formatter"
15 10
 	"github.com/spf13/cobra"
16 11
 	"golang.org/x/net/context"
17 12
 )
... ...
@@ -44,6 +39,11 @@ func newInspectCommand(dockerCli command.Cli) *cobra.Command {
44 44
 func runInspect(dockerCli command.Cli, opts inspectOptions) error {
45 45
 	client := dockerCli.Client()
46 46
 	ctx := context.Background()
47
+
48
+	if opts.pretty {
49
+		opts.format = "pretty"
50
+	}
51
+
47 52
 	getRef := func(ref string) (interface{}, []byte, error) {
48 53
 		nodeRef, err := Reference(ctx, client, ref)
49 54
 		if err != nil {
... ...
@@ -52,93 +52,21 @@ func runInspect(dockerCli command.Cli, opts inspectOptions) error {
52 52
 		node, _, err := client.NodeInspectWithRaw(ctx, nodeRef)
53 53
 		return node, nil, err
54 54
 	}
55
+	f := opts.format
55 56
 
56
-	if !opts.pretty {
57
-		return inspect.Inspect(dockerCli.Out(), opts.nodeIds, opts.format, getRef)
57
+	// check if the user is trying to apply a template to the pretty format, which
58
+	// is not supported
59
+	if strings.HasPrefix(f, "pretty") && f != "pretty" {
60
+		return fmt.Errorf("Cannot supply extra formatting options to the pretty template")
58 61
 	}
59
-	return printHumanFriendly(dockerCli.Out(), opts.nodeIds, getRef)
60
-}
61
-
62
-func printHumanFriendly(out io.Writer, refs []string, getRef inspect.GetRefFunc) error {
63
-	for idx, ref := range refs {
64
-		obj, _, err := getRef(ref)
65
-		if err != nil {
66
-			return err
67
-		}
68
-		printNode(out, obj.(swarm.Node))
69 62
 
70
-		// TODO: better way to do this?
71
-		// print extra space between objects, but not after the last one
72
-		if idx+1 != len(refs) {
73
-			fmt.Fprintf(out, "\n\n")
74
-		} else {
75
-			fmt.Fprintf(out, "\n")
76
-		}
63
+	nodeCtx := formatter.Context{
64
+		Output: dockerCli.Out(),
65
+		Format: formatter.NewNodeFormat(f, false),
77 66
 	}
78
-	return nil
79
-}
80 67
 
81
-// TODO: use a template
82
-func printNode(out io.Writer, node swarm.Node) {
83
-	fmt.Fprintf(out, "ID:\t\t\t%s\n", node.ID)
84
-	ioutils.FprintfIfNotEmpty(out, "Name:\t\t\t%s\n", node.Spec.Name)
85
-	if node.Spec.Labels != nil {
86
-		fmt.Fprintln(out, "Labels:")
87
-		for k, v := range node.Spec.Labels {
88
-			fmt.Fprintf(out, " - %s = %s\n", k, v)
89
-		}
90
-	}
91
-
92
-	ioutils.FprintfIfNotEmpty(out, "Hostname:\t\t%s\n", node.Description.Hostname)
93
-	fmt.Fprintf(out, "Joined at:\t\t%s\n", command.PrettyPrint(node.CreatedAt))
94
-	fmt.Fprintln(out, "Status:")
95
-	fmt.Fprintf(out, " State:\t\t\t%s\n", command.PrettyPrint(node.Status.State))
96
-	ioutils.FprintfIfNotEmpty(out, " Message:\t\t%s\n", command.PrettyPrint(node.Status.Message))
97
-	fmt.Fprintf(out, " Availability:\t\t%s\n", command.PrettyPrint(node.Spec.Availability))
98
-	ioutils.FprintfIfNotEmpty(out, " Address:\t\t%s\n", command.PrettyPrint(node.Status.Addr))
99
-
100
-	if node.ManagerStatus != nil {
101
-		fmt.Fprintln(out, "Manager Status:")
102
-		fmt.Fprintf(out, " Address:\t\t%s\n", node.ManagerStatus.Addr)
103
-		fmt.Fprintf(out, " Raft Status:\t\t%s\n", command.PrettyPrint(node.ManagerStatus.Reachability))
104
-		leader := "No"
105
-		if node.ManagerStatus.Leader {
106
-			leader = "Yes"
107
-		}
108
-		fmt.Fprintf(out, " Leader:\t\t%s\n", leader)
109
-	}
110
-
111
-	fmt.Fprintln(out, "Platform:")
112
-	fmt.Fprintf(out, " Operating System:\t%s\n", node.Description.Platform.OS)
113
-	fmt.Fprintf(out, " Architecture:\t\t%s\n", node.Description.Platform.Architecture)
114
-
115
-	fmt.Fprintln(out, "Resources:")
116
-	fmt.Fprintf(out, " CPUs:\t\t\t%d\n", node.Description.Resources.NanoCPUs/1e9)
117
-	fmt.Fprintf(out, " Memory:\t\t%s\n", units.BytesSize(float64(node.Description.Resources.MemoryBytes)))
118
-
119
-	var pluginTypes []string
120
-	pluginNamesByType := map[string][]string{}
121
-	for _, p := range node.Description.Engine.Plugins {
122
-		// append to pluginTypes only if not done previously
123
-		if _, ok := pluginNamesByType[p.Type]; !ok {
124
-			pluginTypes = append(pluginTypes, p.Type)
125
-		}
126
-		pluginNamesByType[p.Type] = append(pluginNamesByType[p.Type], p.Name)
127
-	}
128
-
129
-	if len(pluginTypes) > 0 {
130
-		fmt.Fprintln(out, "Plugins:")
131
-		sort.Strings(pluginTypes) // ensure stable output
132
-		for _, pluginType := range pluginTypes {
133
-			fmt.Fprintf(out, "  %s:\t\t%s\n", pluginType, strings.Join(pluginNamesByType[pluginType], ", "))
134
-		}
135
-	}
136
-	fmt.Fprintf(out, "Engine Version:\t\t%s\n", node.Description.Engine.EngineVersion)
137
-
138
-	if len(node.Description.Engine.Labels) != 0 {
139
-		fmt.Fprintln(out, "Engine Labels:")
140
-		for k, v := range node.Description.Engine.Labels {
141
-			fmt.Fprintf(out, " - %s = %s\n", k, v)
142
-		}
68
+	if err := formatter.NodeInspectWrite(nodeCtx, opts.nodeIds, getRef); err != nil {
69
+		return cli.StatusError{StatusCode: 1, Status: err.Error()}
143 70
 	}
71
+	return nil
144 72
 }