cli/command/system: add utilities for printing

Adding some utilities to print the output, to keep the linters happier
without having to either suppress errors, or ignore them.

Perhaps we should consider adding utilities for this on the "command.Streams"
outputs.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn 2023-04-10 16:48:22 +02:00
parent 1e89037d72
commit 155f7d9e2b
No known key found for this signature in database
GPG Key ID: 76698F39D527CE8C
2 changed files with 119 additions and 112 deletions

View File

@ -108,7 +108,7 @@ func runInfo(cmd *cobra.Command, dockerCli command.Cli, opts *infoOptions) error
} else {
// if a format is provided, print the error, as it may be hidden
// otherwise if the template doesn't include the ServerErrors field.
fmt.Fprintln(dockerCli.Err(), err)
fprintln(dockerCli.Err(), err)
}
}
}
@ -118,7 +118,7 @@ func runInfo(cmd *cobra.Command, dockerCli command.Cli, opts *infoOptions) error
info.ClientInfo.APIVersion = dockerCli.CurrentVersion()
return prettyPrintInfo(dockerCli, info)
}
return formatInfo(dockerCli, info, opts.format)
return formatInfo(dockerCli.Out(), info, opts.format)
}
// placeHolders does a rudimentary match for possible placeholders in a
@ -163,26 +163,26 @@ func needsServerInfo(template string, info info) bool {
func prettyPrintInfo(streams command.Streams, info info) error {
// Only append the platform info if it's not empty, to prevent printing a trailing space.
if p := info.clientPlatform(); p != "" {
_, _ = fmt.Fprintln(streams.Out(), "Client:", p)
fprintln(streams.Out(), "Client:", p)
} else {
_, _ = fmt.Fprintln(streams.Out(), "Client:")
fprintln(streams.Out(), "Client:")
}
if info.ClientInfo != nil {
prettyPrintClientInfo(streams, *info.ClientInfo)
}
for _, err := range info.ClientErrors {
fmt.Fprintln(streams.Err(), "ERROR:", err)
fprintln(streams.Err(), "ERROR:", err)
}
fmt.Fprintln(streams.Out())
fmt.Fprintln(streams.Out(), "Server:")
fprintln(streams.Out())
fprintln(streams.Out(), "Server:")
if info.Info != nil {
for _, err := range prettyPrintServerInfo(streams, &info) {
info.ServerErrors = append(info.ServerErrors, err.Error())
}
}
for _, err := range info.ServerErrors {
fmt.Fprintln(streams.Err(), "ERROR:", err)
fprintln(streams.Err(), "ERROR:", err)
}
if len(info.ServerErrors) > 0 || len(info.ClientErrors) > 0 {
@ -192,18 +192,17 @@ func prettyPrintInfo(streams command.Streams, info info) error {
}
func prettyPrintClientInfo(streams command.Streams, info clientInfo) {
output := streams.Out()
fprintlnNonEmpty(output, " Version: ", info.Version)
fmt.Fprintln(output, " Context: ", info.Context)
fmt.Fprintln(output, " Debug Mode:", info.Debug)
fprintlnNonEmpty(streams.Out(), " Version: ", info.Version)
fprintln(streams.Out(), " Context: ", info.Context)
fprintln(streams.Out(), " Debug Mode:", info.Debug)
if len(info.Plugins) > 0 {
fmt.Fprintln(output, " Plugins:")
fprintln(streams.Out(), " Plugins:")
for _, p := range info.Plugins {
if p.Err == nil {
fmt.Fprintf(output, " %s: %s (%s)\n", p.Name, p.ShortDescription, p.Vendor)
fprintlnNonEmpty(output, " Version: ", p.Version)
fprintlnNonEmpty(output, " Path: ", p.Path)
fprintf(streams.Out(), " %s: %s (%s)\n", p.Name, p.ShortDescription, p.Vendor)
fprintlnNonEmpty(streams.Out(), " Version: ", p.Version)
fprintlnNonEmpty(streams.Out(), " Path: ", p.Path)
} else {
info.Warnings = append(info.Warnings, fmt.Sprintf("WARNING: Plugin %q is not valid: %s", p.Path, p.Err))
}
@ -211,7 +210,7 @@ func prettyPrintClientInfo(streams command.Streams, info clientInfo) {
}
if len(info.Warnings) > 0 {
fmt.Fprintln(streams.Err(), strings.Join(info.Warnings, "\n"))
fprintln(streams.Err(), strings.Join(info.Warnings, "\n"))
}
}
@ -220,38 +219,38 @@ func prettyPrintServerInfo(streams command.Streams, info *info) []error {
var errs []error
output := streams.Out()
fmt.Fprintln(output, " Containers:", info.Containers)
fmt.Fprintln(output, " Running:", info.ContainersRunning)
fmt.Fprintln(output, " Paused:", info.ContainersPaused)
fmt.Fprintln(output, " Stopped:", info.ContainersStopped)
fmt.Fprintln(output, " Images:", info.Images)
fprintln(output, " Containers:", info.Containers)
fprintln(output, " Running:", info.ContainersRunning)
fprintln(output, " Paused:", info.ContainersPaused)
fprintln(output, " Stopped:", info.ContainersStopped)
fprintln(output, " Images:", info.Images)
fprintlnNonEmpty(output, " Server Version:", info.ServerVersion)
fprintlnNonEmpty(output, " Storage Driver:", info.Driver)
if info.DriverStatus != nil {
for _, pair := range info.DriverStatus {
fmt.Fprintf(output, " %s: %s\n", pair[0], pair[1])
fprintf(output, " %s: %s\n", pair[0], pair[1])
}
}
if info.SystemStatus != nil {
for _, pair := range info.SystemStatus {
fmt.Fprintf(output, " %s: %s\n", pair[0], pair[1])
fprintf(output, " %s: %s\n", pair[0], pair[1])
}
}
fprintlnNonEmpty(output, " Logging Driver:", info.LoggingDriver)
fprintlnNonEmpty(output, " Cgroup Driver:", info.CgroupDriver)
fprintlnNonEmpty(output, " Cgroup Version:", info.CgroupVersion)
fmt.Fprintln(output, " Plugins:")
fmt.Fprintln(output, " Volume:", strings.Join(info.Plugins.Volume, " "))
fmt.Fprintln(output, " Network:", strings.Join(info.Plugins.Network, " "))
fprintln(output, " Plugins:")
fprintln(output, " Volume:", strings.Join(info.Plugins.Volume, " "))
fprintln(output, " Network:", strings.Join(info.Plugins.Network, " "))
if len(info.Plugins.Authorization) != 0 {
fmt.Fprintln(output, " Authorization:", strings.Join(info.Plugins.Authorization, " "))
fprintln(output, " Authorization:", strings.Join(info.Plugins.Authorization, " "))
}
fmt.Fprintln(output, " Log:", strings.Join(info.Plugins.Log, " "))
fprintln(output, " Log:", strings.Join(info.Plugins.Log, " "))
fmt.Fprintln(output, " Swarm:", info.Swarm.LocalNodeState)
fprintln(output, " Swarm:", info.Swarm.LocalNodeState)
printSwarmInfo(output, *info.Info)
if len(info.Runtimes) > 0 {
@ -259,12 +258,12 @@ func prettyPrintServerInfo(streams command.Streams, info *info) []error {
for name := range info.Runtimes {
names = append(names, name)
}
fmt.Fprintln(output, " Runtimes:", strings.Join(names, " "))
fmt.Fprintln(output, " Default Runtime:", info.DefaultRuntime)
fprintln(output, " Runtimes:", strings.Join(names, " "))
fprintln(output, " Default Runtime:", info.DefaultRuntime)
}
if info.OSType == "linux" {
fmt.Fprintln(output, " Init Binary:", info.InitBinary)
fprintln(output, " Init Binary:", info.InitBinary)
for _, ci := range []struct {
Name string
@ -274,23 +273,23 @@ func prettyPrintServerInfo(streams command.Streams, info *info) []error {
{"runc", info.RuncCommit},
{"init", info.InitCommit},
} {
fmt.Fprintf(output, " %s version: %s", ci.Name, ci.Commit.ID)
fprintf(output, " %s version: %s", ci.Name, ci.Commit.ID)
if ci.Commit.ID != ci.Commit.Expected {
fmt.Fprintf(output, " (expected: %s)", ci.Commit.Expected)
fprintf(output, " (expected: %s)", ci.Commit.Expected)
}
fmt.Fprint(output, "\n")
fprintln(output)
}
if len(info.SecurityOptions) != 0 {
if kvs, err := types.DecodeSecurityOptions(info.SecurityOptions); err != nil {
errs = append(errs, err)
} else {
fmt.Fprintln(output, " Security Options:")
fprintln(output, " Security Options:")
for _, so := range kvs {
fmt.Fprintln(output, " "+so.Name)
fprintln(output, " "+so.Name)
for _, o := range so.Options {
switch o.Key {
case "profile":
fmt.Fprintln(output, " Profile:", o.Value)
fprintln(output, " Profile:", o.Value)
}
}
}
@ -300,25 +299,25 @@ func prettyPrintServerInfo(streams command.Streams, info *info) []error {
// Isolation only has meaning on a Windows daemon.
if info.OSType == "windows" {
fmt.Fprintln(output, " Default Isolation:", info.Isolation)
fprintln(output, " Default Isolation:", info.Isolation)
}
fprintlnNonEmpty(output, " Kernel Version:", info.KernelVersion)
fprintlnNonEmpty(output, " Operating System:", info.OperatingSystem)
fprintlnNonEmpty(output, " OSType:", info.OSType)
fprintlnNonEmpty(output, " Architecture:", info.Architecture)
fmt.Fprintln(output, " CPUs:", info.NCPU)
fmt.Fprintln(output, " Total Memory:", units.BytesSize(float64(info.MemTotal)))
fprintln(output, " CPUs:", info.NCPU)
fprintln(output, " Total Memory:", units.BytesSize(float64(info.MemTotal)))
fprintlnNonEmpty(output, " Name:", info.Name)
fprintlnNonEmpty(output, " ID:", info.ID)
fmt.Fprintln(output, " Docker Root Dir:", info.DockerRootDir)
fmt.Fprintln(output, " Debug Mode:", info.Debug)
fprintln(output, " Docker Root Dir:", info.DockerRootDir)
fprintln(output, " Debug Mode:", info.Debug)
if info.Debug {
fmt.Fprintln(output, " File Descriptors:", info.NFd)
fmt.Fprintln(output, " Goroutines:", info.NGoroutines)
fmt.Fprintln(output, " System Time:", info.SystemTime)
fmt.Fprintln(output, " EventsListeners:", info.NEventsListener)
fprintln(output, " File Descriptors:", info.NFd)
fprintln(output, " Goroutines:", info.NGoroutines)
fprintln(output, " System Time:", info.SystemTime)
fprintln(output, " EventsListeners:", info.NEventsListener)
}
fprintlnNonEmpty(output, " HTTP Proxy:", info.HTTPProxy)
@ -326,48 +325,48 @@ func prettyPrintServerInfo(streams command.Streams, info *info) []error {
fprintlnNonEmpty(output, " No Proxy:", info.NoProxy)
fprintlnNonEmpty(output, " Username:", info.UserName)
if len(info.Labels) > 0 {
fmt.Fprintln(output, " Labels:")
fprintln(output, " Labels:")
for _, lbl := range info.Labels {
fmt.Fprintln(output, " "+lbl)
fprintln(output, " "+lbl)
}
}
fmt.Fprintln(output, " Experimental:", info.ExperimentalBuild)
fprintln(output, " Experimental:", info.ExperimentalBuild)
if info.RegistryConfig != nil && (len(info.RegistryConfig.InsecureRegistryCIDRs) > 0 || len(info.RegistryConfig.IndexConfigs) > 0) {
fmt.Fprintln(output, " Insecure Registries:")
for _, reg := range info.RegistryConfig.IndexConfigs {
if !reg.Secure {
fmt.Fprintln(output, " "+reg.Name)
fprintln(output, " Insecure Registries:")
for _, registryConfig := range info.RegistryConfig.IndexConfigs {
if !registryConfig.Secure {
fprintln(output, " "+registryConfig.Name)
}
}
for _, reg := range info.RegistryConfig.InsecureRegistryCIDRs {
mask, _ := reg.Mask.Size()
fmt.Fprintf(output, " %s/%d\n", reg.IP.String(), mask)
for _, registryConfig := range info.RegistryConfig.InsecureRegistryCIDRs {
mask, _ := registryConfig.Mask.Size()
fprintf(output, " %s/%d\n", registryConfig.IP.String(), mask)
}
}
if info.RegistryConfig != nil && len(info.RegistryConfig.Mirrors) > 0 {
fmt.Fprintln(output, " Registry Mirrors:")
fprintln(output, " Registry Mirrors:")
for _, mirror := range info.RegistryConfig.Mirrors {
fmt.Fprintln(output, " "+mirror)
fprintln(output, " "+mirror)
}
}
fmt.Fprintln(output, " Live Restore Enabled:", info.LiveRestoreEnabled)
fprintln(output, " Live Restore Enabled:", info.LiveRestoreEnabled)
if info.ProductLicense != "" {
fmt.Fprintln(output, " Product License:", info.ProductLicense)
fprintln(output, " Product License:", info.ProductLicense)
}
if info.DefaultAddressPools != nil && len(info.DefaultAddressPools) > 0 {
fmt.Fprintln(output, " Default Address Pools:")
fprintln(output, " Default Address Pools:")
for _, pool := range info.DefaultAddressPools {
fmt.Fprintf(output, " Base: %s, Size: %d\n", pool.Base, pool.Size)
fprintf(output, " Base: %s, Size: %d\n", pool.Base, pool.Size)
}
}
fmt.Fprint(output, "\n")
fprintln(output)
printServerWarnings(streams.Err(), info)
return errs
}
@ -377,67 +376,67 @@ func printSwarmInfo(output io.Writer, info types.Info) {
if info.Swarm.LocalNodeState == swarm.LocalNodeStateInactive || info.Swarm.LocalNodeState == swarm.LocalNodeStateLocked {
return
}
fmt.Fprintln(output, " NodeID:", info.Swarm.NodeID)
fprintln(output, " NodeID:", info.Swarm.NodeID)
if info.Swarm.Error != "" {
fmt.Fprintln(output, " Error:", info.Swarm.Error)
fprintln(output, " Error:", info.Swarm.Error)
}
fmt.Fprintln(output, " Is Manager:", info.Swarm.ControlAvailable)
fprintln(output, " Is Manager:", info.Swarm.ControlAvailable)
if info.Swarm.Cluster != nil && info.Swarm.ControlAvailable && info.Swarm.Error == "" && info.Swarm.LocalNodeState != swarm.LocalNodeStateError {
fmt.Fprintln(output, " ClusterID:", info.Swarm.Cluster.ID)
fmt.Fprintln(output, " Managers:", info.Swarm.Managers)
fmt.Fprintln(output, " Nodes:", info.Swarm.Nodes)
fprintln(output, " ClusterID:", info.Swarm.Cluster.ID)
fprintln(output, " Managers:", info.Swarm.Managers)
fprintln(output, " Nodes:", info.Swarm.Nodes)
var strAddrPool strings.Builder
if info.Swarm.Cluster.DefaultAddrPool != nil {
for _, p := range info.Swarm.Cluster.DefaultAddrPool {
strAddrPool.WriteString(p + " ")
}
fmt.Fprintln(output, " Default Address Pool:", strAddrPool.String())
fmt.Fprintln(output, " SubnetSize:", info.Swarm.Cluster.SubnetSize)
fprintln(output, " Default Address Pool:", strAddrPool.String())
fprintln(output, " SubnetSize:", info.Swarm.Cluster.SubnetSize)
}
if info.Swarm.Cluster.DataPathPort > 0 {
fmt.Fprintln(output, " Data Path Port:", info.Swarm.Cluster.DataPathPort)
fprintln(output, " Data Path Port:", info.Swarm.Cluster.DataPathPort)
}
fmt.Fprintln(output, " Orchestration:")
fprintln(output, " Orchestration:")
taskHistoryRetentionLimit := int64(0)
if info.Swarm.Cluster.Spec.Orchestration.TaskHistoryRetentionLimit != nil {
taskHistoryRetentionLimit = *info.Swarm.Cluster.Spec.Orchestration.TaskHistoryRetentionLimit
}
fmt.Fprintln(output, " Task History Retention Limit:", taskHistoryRetentionLimit)
fmt.Fprintln(output, " Raft:")
fmt.Fprintln(output, " Snapshot Interval:", info.Swarm.Cluster.Spec.Raft.SnapshotInterval)
fprintln(output, " Task History Retention Limit:", taskHistoryRetentionLimit)
fprintln(output, " Raft:")
fprintln(output, " Snapshot Interval:", info.Swarm.Cluster.Spec.Raft.SnapshotInterval)
if info.Swarm.Cluster.Spec.Raft.KeepOldSnapshots != nil {
fmt.Fprintf(output, " Number of Old Snapshots to Retain: %d\n", *info.Swarm.Cluster.Spec.Raft.KeepOldSnapshots)
fprintf(output, " Number of Old Snapshots to Retain: %d\n", *info.Swarm.Cluster.Spec.Raft.KeepOldSnapshots)
}
fmt.Fprintln(output, " Heartbeat Tick:", info.Swarm.Cluster.Spec.Raft.HeartbeatTick)
fmt.Fprintln(output, " Election Tick:", info.Swarm.Cluster.Spec.Raft.ElectionTick)
fmt.Fprintln(output, " Dispatcher:")
fmt.Fprintln(output, " Heartbeat Period:", units.HumanDuration(info.Swarm.Cluster.Spec.Dispatcher.HeartbeatPeriod))
fmt.Fprintln(output, " CA Configuration:")
fmt.Fprintln(output, " Expiry Duration:", units.HumanDuration(info.Swarm.Cluster.Spec.CAConfig.NodeCertExpiry))
fmt.Fprintln(output, " Force Rotate:", info.Swarm.Cluster.Spec.CAConfig.ForceRotate)
fprintln(output, " Heartbeat Tick:", info.Swarm.Cluster.Spec.Raft.HeartbeatTick)
fprintln(output, " Election Tick:", info.Swarm.Cluster.Spec.Raft.ElectionTick)
fprintln(output, " Dispatcher:")
fprintln(output, " Heartbeat Period:", units.HumanDuration(info.Swarm.Cluster.Spec.Dispatcher.HeartbeatPeriod))
fprintln(output, " CA Configuration:")
fprintln(output, " Expiry Duration:", units.HumanDuration(info.Swarm.Cluster.Spec.CAConfig.NodeCertExpiry))
fprintln(output, " Force Rotate:", info.Swarm.Cluster.Spec.CAConfig.ForceRotate)
if caCert := strings.TrimSpace(info.Swarm.Cluster.Spec.CAConfig.SigningCACert); caCert != "" {
fmt.Fprintf(output, " Signing CA Certificate: \n%s\n\n", caCert)
fprintf(output, " Signing CA Certificate: \n%s\n\n", caCert)
}
if len(info.Swarm.Cluster.Spec.CAConfig.ExternalCAs) > 0 {
fmt.Fprintln(output, " External CAs:")
fprintln(output, " External CAs:")
for _, entry := range info.Swarm.Cluster.Spec.CAConfig.ExternalCAs {
fmt.Fprintf(output, " %s: %s\n", entry.Protocol, entry.URL)
fprintf(output, " %s: %s\n", entry.Protocol, entry.URL)
}
}
fmt.Fprintln(output, " Autolock Managers:", info.Swarm.Cluster.Spec.EncryptionConfig.AutoLockManagers)
fmt.Fprintln(output, " Root Rotation In Progress:", info.Swarm.Cluster.RootRotationInProgress)
fprintln(output, " Autolock Managers:", info.Swarm.Cluster.Spec.EncryptionConfig.AutoLockManagers)
fprintln(output, " Root Rotation In Progress:", info.Swarm.Cluster.RootRotationInProgress)
}
fmt.Fprintln(output, " Node Address:", info.Swarm.NodeAddr)
fprintln(output, " Node Address:", info.Swarm.NodeAddr)
if len(info.Swarm.RemoteManagers) > 0 {
managers := []string{}
for _, entry := range info.Swarm.RemoteManagers {
managers = append(managers, entry.Addr)
}
sort.Strings(managers)
fmt.Fprintln(output, " Manager Addresses:")
fprintln(output, " Manager Addresses:")
for _, entry := range managers {
fmt.Fprintf(output, " %s\n", entry)
fprintf(output, " %s\n", entry)
}
}
}
@ -447,7 +446,7 @@ func printServerWarnings(stdErr io.Writer, info *info) {
printSecurityOptionsWarnings(stdErr, *info.Info)
}
if len(info.Warnings) > 0 {
fmt.Fprintln(stdErr, strings.Join(info.Warnings, "\n"))
fprintln(stdErr, strings.Join(info.Warnings, "\n"))
return
}
// daemon didn't return warnings. Fallback to old behavior
@ -487,38 +486,38 @@ func printServerWarningsLegacy(stdErr io.Writer, info types.Info) {
return
}
if !info.MemoryLimit {
fmt.Fprintln(stdErr, "WARNING: No memory limit support")
fprintln(stdErr, "WARNING: No memory limit support")
}
if !info.SwapLimit {
fmt.Fprintln(stdErr, "WARNING: No swap limit support")
fprintln(stdErr, "WARNING: No swap limit support")
}
if !info.OomKillDisable && info.CgroupVersion != "2" {
fmt.Fprintln(stdErr, "WARNING: No oom kill disable support")
fprintln(stdErr, "WARNING: No oom kill disable support")
}
if !info.CPUCfsQuota {
fmt.Fprintln(stdErr, "WARNING: No cpu cfs quota support")
fprintln(stdErr, "WARNING: No cpu cfs quota support")
}
if !info.CPUCfsPeriod {
fmt.Fprintln(stdErr, "WARNING: No cpu cfs period support")
fprintln(stdErr, "WARNING: No cpu cfs period support")
}
if !info.CPUShares {
fmt.Fprintln(stdErr, "WARNING: No cpu shares support")
fprintln(stdErr, "WARNING: No cpu shares support")
}
if !info.CPUSet {
fmt.Fprintln(stdErr, "WARNING: No cpuset support")
fprintln(stdErr, "WARNING: No cpuset support")
}
if !info.IPv4Forwarding {
fmt.Fprintln(stdErr, "WARNING: IPv4 forwarding is disabled")
fprintln(stdErr, "WARNING: IPv4 forwarding is disabled")
}
if !info.BridgeNfIptables {
fmt.Fprintln(stdErr, "WARNING: bridge-nf-call-iptables is disabled")
fprintln(stdErr, "WARNING: bridge-nf-call-iptables is disabled")
}
if !info.BridgeNfIP6tables {
fmt.Fprintln(stdErr, "WARNING: bridge-nf-call-ip6tables is disabled")
fprintln(stdErr, "WARNING: bridge-nf-call-ip6tables is disabled")
}
}
func formatInfo(dockerCli command.Cli, info info, format string) error {
func formatInfo(output io.Writer, info info, format string) error {
if format == formatter.JSONFormatKey {
format = formatter.JSONFormat
}
@ -535,13 +534,21 @@ func formatInfo(dockerCli command.Cli, info info, format string) error {
Status: "template parsing error: " + err.Error(),
}
}
err = tmpl.Execute(dockerCli.Out(), info)
dockerCli.Out().Write([]byte{'\n'})
err = tmpl.Execute(output, info)
fprintln(output)
return err
}
func fprintf(w io.Writer, format string, a ...any) {
_, _ = fmt.Fprintf(w, format, a...)
}
func fprintln(w io.Writer, a ...any) {
_, _ = fmt.Fprintln(w, a...)
}
func fprintlnNonEmpty(w io.Writer, label, value string) {
if value != "" {
fmt.Fprintln(w, label, value)
_, _ = fmt.Fprintln(w, label, value)
}
}

View File

@ -406,12 +406,12 @@ func TestPrettyPrintInfo(t *testing.T) {
if tc.jsonGolden != "" {
cli = test.NewFakeCli(&fakeClient{})
assert.NilError(t, formatInfo(cli, tc.dockerInfo, "{{json .}}"))
assert.NilError(t, formatInfo(cli.Out(), tc.dockerInfo, "{{json .}}"))
golden.Assert(t, cli.OutBuffer().String(), tc.jsonGolden+".json.golden")
assert.Check(t, is.Equal("", cli.ErrBuffer().String()))
cli = test.NewFakeCli(&fakeClient{})
assert.NilError(t, formatInfo(cli, tc.dockerInfo, "json"))
assert.NilError(t, formatInfo(cli.Out(), tc.dockerInfo, "json"))
golden.Assert(t, cli.OutBuffer().String(), tc.jsonGolden+".json.golden")
assert.Check(t, is.Equal("", cli.ErrBuffer().String()))
}
@ -473,7 +473,7 @@ func TestFormatInfo(t *testing.T) {
Info: &sampleInfoNoSwarm,
ClientInfo: &clientInfo{Debug: true},
}
err := formatInfo(cli, info, tc.template)
err := formatInfo(cli.Out(), info, tc.template)
if tc.expectedOut != "" {
assert.NilError(t, err)
assert.Equal(t, cli.OutBuffer().String(), tc.expectedOut)