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  }