github.com/justincormack/cli@v0.0.0-20201215022714-831ebeae9675/cli/command/node/formatter.go (about) 1 package node 2 3 import ( 4 "encoding/base64" 5 "fmt" 6 "reflect" 7 "strings" 8 9 "github.com/docker/cli/cli/command" 10 "github.com/docker/cli/cli/command/formatter" 11 "github.com/docker/cli/cli/command/inspect" 12 "github.com/docker/docker/api/types" 13 "github.com/docker/docker/api/types/swarm" 14 units "github.com/docker/go-units" 15 ) 16 17 const ( 18 defaultNodeTableFormat = "table {{.ID}} {{if .Self}}*{{else}} {{ end }}\t{{.Hostname}}\t{{.Status}}\t{{.Availability}}\t{{.ManagerStatus}}\t{{.EngineVersion}}" 19 nodeInspectPrettyTemplate formatter.Format = `ID: {{.ID}} 20 {{- if .Name }} 21 Name: {{.Name}} 22 {{- end }} 23 {{- if .Labels }} 24 Labels: 25 {{- range $k, $v := .Labels }} 26 - {{ $k }}{{if $v }}={{ $v }}{{ end }} 27 {{- end }}{{ end }} 28 Hostname: {{.Hostname}} 29 Joined at: {{.CreatedAt}} 30 Status: 31 State: {{.StatusState}} 32 {{- if .HasStatusMessage}} 33 Message: {{.StatusMessage}} 34 {{- end}} 35 Availability: {{.SpecAvailability}} 36 {{- if .Status.Addr}} 37 Address: {{.StatusAddr}} 38 {{- end}} 39 {{- if .HasManagerStatus}} 40 Manager Status: 41 Address: {{.ManagerStatusAddr}} 42 Raft Status: {{.ManagerStatusReachability}} 43 {{- if .IsManagerStatusLeader}} 44 Leader: Yes 45 {{- else}} 46 Leader: No 47 {{- end}} 48 {{- end}} 49 Platform: 50 Operating System: {{.PlatformOS}} 51 Architecture: {{.PlatformArchitecture}} 52 Resources: 53 CPUs: {{.ResourceNanoCPUs}} 54 Memory: {{.ResourceMemory}} 55 {{- if .HasEnginePlugins}} 56 Plugins: 57 {{- range $k, $v := .EnginePlugins }} 58 {{ $k }}:{{if $v }} {{ $v }}{{ end }} 59 {{- end }} 60 {{- end }} 61 Engine Version: {{.EngineVersion}} 62 {{- if .EngineLabels}} 63 Engine Labels: 64 {{- range $k, $v := .EngineLabels }} 65 - {{ $k }}{{if $v }}={{ $v }}{{ end }} 66 {{- end }}{{- end }} 67 {{- if .HasTLSInfo}} 68 TLS Info: 69 TrustRoot: 70 {{.TLSInfoTrustRoot}} 71 Issuer Subject: {{.TLSInfoCertIssuerSubject}} 72 Issuer Public Key: {{.TLSInfoCertIssuerPublicKey}} 73 {{- end}}` 74 nodeIDHeader = "ID" 75 selfHeader = "" 76 hostnameHeader = "HOSTNAME" 77 availabilityHeader = "AVAILABILITY" 78 managerStatusHeader = "MANAGER STATUS" 79 engineVersionHeader = "ENGINE VERSION" 80 tlsStatusHeader = "TLS STATUS" 81 ) 82 83 // NewFormat returns a Format for rendering using a node Context 84 func NewFormat(source string, quiet bool) formatter.Format { 85 switch source { 86 case formatter.PrettyFormatKey: 87 return nodeInspectPrettyTemplate 88 case formatter.TableFormatKey: 89 if quiet { 90 return formatter.DefaultQuietFormat 91 } 92 return defaultNodeTableFormat 93 case formatter.RawFormatKey: 94 if quiet { 95 return `node_id: {{.ID}}` 96 } 97 return `node_id: {{.ID}}\nhostname: {{.Hostname}}\nstatus: {{.Status}}\navailability: {{.Availability}}\nmanager_status: {{.ManagerStatus}}\n` 98 } 99 return formatter.Format(source) 100 } 101 102 // FormatWrite writes the context 103 func FormatWrite(ctx formatter.Context, nodes []swarm.Node, info types.Info) error { 104 render := func(format func(subContext formatter.SubContext) error) error { 105 for _, node := range nodes { 106 nodeCtx := &nodeContext{n: node, info: info} 107 if err := format(nodeCtx); err != nil { 108 return err 109 } 110 } 111 return nil 112 } 113 nodeCtx := nodeContext{} 114 nodeCtx.Header = formatter.SubHeaderContext{ 115 "ID": nodeIDHeader, 116 "Self": selfHeader, 117 "Hostname": hostnameHeader, 118 "Status": formatter.StatusHeader, 119 "Availability": availabilityHeader, 120 "ManagerStatus": managerStatusHeader, 121 "EngineVersion": engineVersionHeader, 122 "TLSStatus": tlsStatusHeader, 123 } 124 return ctx.Write(&nodeCtx, render) 125 } 126 127 type nodeContext struct { 128 formatter.HeaderContext 129 n swarm.Node 130 info types.Info 131 } 132 133 func (c *nodeContext) MarshalJSON() ([]byte, error) { 134 return formatter.MarshalJSON(c) 135 } 136 137 func (c *nodeContext) ID() string { 138 return c.n.ID 139 } 140 141 func (c *nodeContext) Self() bool { 142 return c.n.ID == c.info.Swarm.NodeID 143 } 144 145 func (c *nodeContext) Hostname() string { 146 return c.n.Description.Hostname 147 } 148 149 func (c *nodeContext) Status() string { 150 return command.PrettyPrint(string(c.n.Status.State)) 151 } 152 153 func (c *nodeContext) Availability() string { 154 return command.PrettyPrint(string(c.n.Spec.Availability)) 155 } 156 157 func (c *nodeContext) ManagerStatus() string { 158 reachability := "" 159 if c.n.ManagerStatus != nil { 160 if c.n.ManagerStatus.Leader { 161 reachability = "Leader" 162 } else { 163 reachability = string(c.n.ManagerStatus.Reachability) 164 } 165 } 166 return command.PrettyPrint(reachability) 167 } 168 169 func (c *nodeContext) TLSStatus() string { 170 if c.info.Swarm.Cluster == nil || reflect.DeepEqual(c.info.Swarm.Cluster.TLSInfo, swarm.TLSInfo{}) || reflect.DeepEqual(c.n.Description.TLSInfo, swarm.TLSInfo{}) { 171 return "Unknown" 172 } 173 if reflect.DeepEqual(c.n.Description.TLSInfo, c.info.Swarm.Cluster.TLSInfo) { 174 return "Ready" 175 } 176 return "Needs Rotation" 177 } 178 179 func (c *nodeContext) EngineVersion() string { 180 return c.n.Description.Engine.EngineVersion 181 } 182 183 // InspectFormatWrite renders the context for a list of nodes 184 func InspectFormatWrite(ctx formatter.Context, refs []string, getRef inspect.GetRefFunc) error { 185 if ctx.Format != nodeInspectPrettyTemplate { 186 return inspect.Inspect(ctx.Output, refs, string(ctx.Format), getRef) 187 } 188 render := func(format func(subContext formatter.SubContext) error) error { 189 for _, ref := range refs { 190 nodeI, _, err := getRef(ref) 191 if err != nil { 192 return err 193 } 194 node, ok := nodeI.(swarm.Node) 195 if !ok { 196 return fmt.Errorf("got wrong object to inspect :%v", ok) 197 } 198 if err := format(&nodeInspectContext{Node: node}); err != nil { 199 return err 200 } 201 } 202 return nil 203 } 204 return ctx.Write(&nodeInspectContext{}, render) 205 } 206 207 type nodeInspectContext struct { 208 swarm.Node 209 formatter.SubContext 210 } 211 212 func (ctx *nodeInspectContext) ID() string { 213 return ctx.Node.ID 214 } 215 216 func (ctx *nodeInspectContext) Name() string { 217 return ctx.Node.Spec.Name 218 } 219 220 func (ctx *nodeInspectContext) Labels() map[string]string { 221 return ctx.Node.Spec.Labels 222 } 223 224 func (ctx *nodeInspectContext) Hostname() string { 225 return ctx.Node.Description.Hostname 226 } 227 228 func (ctx *nodeInspectContext) CreatedAt() string { 229 return command.PrettyPrint(ctx.Node.CreatedAt) 230 } 231 232 func (ctx *nodeInspectContext) StatusState() string { 233 return command.PrettyPrint(ctx.Node.Status.State) 234 } 235 236 func (ctx *nodeInspectContext) HasStatusMessage() bool { 237 return ctx.Node.Status.Message != "" 238 } 239 240 func (ctx *nodeInspectContext) StatusMessage() string { 241 return command.PrettyPrint(ctx.Node.Status.Message) 242 } 243 244 func (ctx *nodeInspectContext) SpecAvailability() string { 245 return command.PrettyPrint(ctx.Node.Spec.Availability) 246 } 247 248 func (ctx *nodeInspectContext) HasStatusAddr() bool { 249 return ctx.Node.Status.Addr != "" 250 } 251 252 func (ctx *nodeInspectContext) StatusAddr() string { 253 return ctx.Node.Status.Addr 254 } 255 256 func (ctx *nodeInspectContext) HasManagerStatus() bool { 257 return ctx.Node.ManagerStatus != nil 258 } 259 260 func (ctx *nodeInspectContext) ManagerStatusAddr() string { 261 return ctx.Node.ManagerStatus.Addr 262 } 263 264 func (ctx *nodeInspectContext) ManagerStatusReachability() string { 265 return command.PrettyPrint(ctx.Node.ManagerStatus.Reachability) 266 } 267 268 func (ctx *nodeInspectContext) IsManagerStatusLeader() bool { 269 return ctx.Node.ManagerStatus.Leader 270 } 271 272 func (ctx *nodeInspectContext) PlatformOS() string { 273 return ctx.Node.Description.Platform.OS 274 } 275 276 func (ctx *nodeInspectContext) PlatformArchitecture() string { 277 return ctx.Node.Description.Platform.Architecture 278 } 279 280 func (ctx *nodeInspectContext) ResourceNanoCPUs() int { 281 if ctx.Node.Description.Resources.NanoCPUs == 0 { 282 return int(0) 283 } 284 return int(ctx.Node.Description.Resources.NanoCPUs) / 1e9 285 } 286 287 func (ctx *nodeInspectContext) ResourceMemory() string { 288 if ctx.Node.Description.Resources.MemoryBytes == 0 { 289 return "" 290 } 291 return units.BytesSize(float64(ctx.Node.Description.Resources.MemoryBytes)) 292 } 293 294 func (ctx *nodeInspectContext) HasEnginePlugins() bool { 295 return len(ctx.Node.Description.Engine.Plugins) > 0 296 } 297 298 func (ctx *nodeInspectContext) EnginePlugins() map[string]string { 299 pluginMap := map[string][]string{} 300 for _, p := range ctx.Node.Description.Engine.Plugins { 301 pluginMap[p.Type] = append(pluginMap[p.Type], p.Name) 302 } 303 304 pluginNamesByType := map[string]string{} 305 for k, v := range pluginMap { 306 pluginNamesByType[k] = strings.Join(v, ", ") 307 } 308 return pluginNamesByType 309 } 310 311 func (ctx *nodeInspectContext) EngineLabels() map[string]string { 312 return ctx.Node.Description.Engine.Labels 313 } 314 315 func (ctx *nodeInspectContext) EngineVersion() string { 316 return ctx.Node.Description.Engine.EngineVersion 317 } 318 319 func (ctx *nodeInspectContext) HasTLSInfo() bool { 320 tlsInfo := ctx.Node.Description.TLSInfo 321 return !reflect.DeepEqual(tlsInfo, swarm.TLSInfo{}) 322 } 323 324 func (ctx *nodeInspectContext) TLSInfoTrustRoot() string { 325 return ctx.Node.Description.TLSInfo.TrustRoot 326 } 327 328 func (ctx *nodeInspectContext) TLSInfoCertIssuerPublicKey() string { 329 return base64.StdEncoding.EncodeToString(ctx.Node.Description.TLSInfo.CertIssuerPublicKey) 330 } 331 332 func (ctx *nodeInspectContext) TLSInfoCertIssuerSubject() string { 333 return base64.StdEncoding.EncodeToString(ctx.Node.Description.TLSInfo.CertIssuerSubject) 334 }