From 416e55bedb4a083a7378c6e857903bd856fc50d9 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Mon, 10 Apr 2023 16:35:08 +0200 Subject: [PATCH 1/9] github.com/docker/cli/cli/command/system: add BenchmarkPrettyPrintInfo goos: linux goarch: arm64 pkg: github.com/docker/cli/cli/command/system BenchmarkPrettyPrintInfo BenchmarkPrettyPrintInfo-5 189028 6156 ns/op 1776 B/op 88 allocs/op Signed-off-by: Sebastiaan van Stijn --- cli/command/system/info_test.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/cli/command/system/info_test.go b/cli/command/system/info_test.go index 298fa5d963..5fc55ccd1e 100644 --- a/cli/command/system/info_test.go +++ b/cli/command/system/info_test.go @@ -419,6 +419,30 @@ func TestPrettyPrintInfo(t *testing.T) { } } +func BenchmarkPrettyPrintInfo(b *testing.B) { + infoWithSwarm := sampleInfoNoSwarm + infoWithSwarm.Swarm = sampleSwarmInfo + + dockerInfo := info{ + Info: &infoWithSwarm, + ClientInfo: &clientInfo{ + clientVersion: clientVersion{ + Platform: &platformInfo{Name: "Docker Engine - Community"}, + Version: "24.0.0", + Context: "default", + }, + Debug: true, + }, + } + cli := test.NewFakeCli(&fakeClient{}) + + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = prettyPrintInfo(cli, dockerInfo) + cli.ResetOutputBuffers() + } +} + func TestFormatInfo(t *testing.T) { for _, tc := range []struct { doc string From 1d70f7cdb449c2604de2f7c3166e3a9a59ec4f00 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Tue, 2 May 2023 16:01:46 +0200 Subject: [PATCH 2/9] cli/command/system: prettyPrintServerInfo: rename var that collided with import Signed-off-by: Sebastiaan van Stijn --- cli/command/system/info.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cli/command/system/info.go b/cli/command/system/info.go index b95db351ac..66608613e8 100644 --- a/cli/command/system/info.go +++ b/cli/command/system/info.go @@ -334,15 +334,15 @@ func prettyPrintServerInfo(dockerCli command.Cli, info *info) []error { if info.RegistryConfig != nil && (len(info.RegistryConfig.InsecureRegistryCIDRs) > 0 || len(info.RegistryConfig.IndexConfigs) > 0) { fmt.Fprintln(dockerCli.Out(), " Insecure Registries:") - for _, registry := range info.RegistryConfig.IndexConfigs { - if !registry.Secure { - fmt.Fprintln(dockerCli.Out(), " "+registry.Name) + for _, reg := range info.RegistryConfig.IndexConfigs { + if !reg.Secure { + fmt.Fprintln(dockerCli.Out(), " "+reg.Name) } } - for _, registry := range info.RegistryConfig.InsecureRegistryCIDRs { - mask, _ := registry.Mask.Size() - fmt.Fprintf(dockerCli.Out(), " %s/%d\n", registry.IP.String(), mask) + for _, reg := range info.RegistryConfig.InsecureRegistryCIDRs { + mask, _ := reg.Mask.Size() + fmt.Fprintf(dockerCli.Out(), " %s/%d\n", reg.IP.String(), mask) } } From be6f4cd56c894a04b332bd08f12bedd81ccb8f7b Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Tue, 2 May 2023 16:05:52 +0200 Subject: [PATCH 3/9] cli/command/system: prettyPrintServerInfo: refactor printing "runtimes" Signed-off-by: Sebastiaan van Stijn --- cli/command/system/info.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/command/system/info.go b/cli/command/system/info.go index 66608613e8..5719a61be1 100644 --- a/cli/command/system/info.go +++ b/cli/command/system/info.go @@ -253,11 +253,11 @@ func prettyPrintServerInfo(dockerCli command.Cli, info *info) []error { printSwarmInfo(dockerCli, *info.Info) if len(info.Runtimes) > 0 { - fmt.Fprint(dockerCli.Out(), " Runtimes:") + names := make([]string, 0, len(info.Runtimes)) for name := range info.Runtimes { - fmt.Fprintf(dockerCli.Out(), " %s", name) + names = append(names, name) } - fmt.Fprint(dockerCli.Out(), "\n") + fmt.Fprintln(dockerCli.Out(), " Runtimes:", strings.Join(names, " ")) fmt.Fprintln(dockerCli.Out(), " Default Runtime:", info.DefaultRuntime) } From 2d0ea86b0a32e01d4cfc72c76bc9402585173bf3 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Tue, 2 May 2023 16:14:23 +0200 Subject: [PATCH 4/9] cli/command/system: use io.Writer for printing warnings Don't require whole of DockerCLI to be passed, as all we need is a writer. Signed-off-by: Sebastiaan van Stijn --- cli/command/system/info.go | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/cli/command/system/info.go b/cli/command/system/info.go index 5719a61be1..4c90874bd7 100644 --- a/cli/command/system/info.go +++ b/cli/command/system/info.go @@ -366,8 +366,7 @@ func prettyPrintServerInfo(dockerCli command.Cli, info *info) []error { } fmt.Fprint(dockerCli.Out(), "\n") - - printServerWarnings(dockerCli, info) + printServerWarnings(dockerCli.Err(), info) return errs } @@ -441,16 +440,16 @@ func printSwarmInfo(dockerCli command.Cli, info types.Info) { } } -func printServerWarnings(dockerCli command.Cli, info *info) { +func printServerWarnings(stdErr io.Writer, info *info) { if versions.LessThan(info.ClientInfo.APIVersion, "1.42") { - printSecurityOptionsWarnings(dockerCli, *info.Info) + printSecurityOptionsWarnings(stdErr, *info.Info) } if len(info.Warnings) > 0 { - fmt.Fprintln(dockerCli.Err(), strings.Join(info.Warnings, "\n")) + fmt.Fprintln(stdErr, strings.Join(info.Warnings, "\n")) return } // daemon didn't return warnings. Fallback to old behavior - printServerWarningsLegacy(dockerCli, *info.Info) + printServerWarningsLegacy(stdErr, *info.Info) } // printSecurityOptionsWarnings prints warnings based on the security options @@ -459,7 +458,7 @@ func printServerWarnings(dockerCli command.Cli, info *info) { // info.Warnings. This function is used to provide backward compatibility with // daemons that do not provide these warnings. No new warnings should be added // here. -func printSecurityOptionsWarnings(dockerCli command.Cli, info types.Info) { +func printSecurityOptionsWarnings(stdErr io.Writer, info types.Info) { if info.OSType == "windows" { return } @@ -470,7 +469,7 @@ func printSecurityOptionsWarnings(dockerCli command.Cli, info types.Info) { } for _, o := range so.Options { if o.Key == "profile" && o.Value != "default" && o.Value != "builtin" { - _, _ = fmt.Fprintln(dockerCli.Err(), "WARNING: You're not using the default seccomp profile") + _, _ = fmt.Fprintln(stdErr, "WARNING: You're not using the default seccomp profile") } } } @@ -481,39 +480,39 @@ func printSecurityOptionsWarnings(dockerCli command.Cli, info types.Info) { // info.Warnings. This function is used to provide backward compatibility with // daemons that do not provide these warnings. No new warnings should be added // here. -func printServerWarningsLegacy(dockerCli command.Cli, info types.Info) { +func printServerWarningsLegacy(stdErr io.Writer, info types.Info) { if info.OSType == "windows" { return } if !info.MemoryLimit { - fmt.Fprintln(dockerCli.Err(), "WARNING: No memory limit support") + fmt.Fprintln(stdErr, "WARNING: No memory limit support") } if !info.SwapLimit { - fmt.Fprintln(dockerCli.Err(), "WARNING: No swap limit support") + fmt.Fprintln(stdErr, "WARNING: No swap limit support") } if !info.OomKillDisable && info.CgroupVersion != "2" { - fmt.Fprintln(dockerCli.Err(), "WARNING: No oom kill disable support") + fmt.Fprintln(stdErr, "WARNING: No oom kill disable support") } if !info.CPUCfsQuota { - fmt.Fprintln(dockerCli.Err(), "WARNING: No cpu cfs quota support") + fmt.Fprintln(stdErr, "WARNING: No cpu cfs quota support") } if !info.CPUCfsPeriod { - fmt.Fprintln(dockerCli.Err(), "WARNING: No cpu cfs period support") + fmt.Fprintln(stdErr, "WARNING: No cpu cfs period support") } if !info.CPUShares { - fmt.Fprintln(dockerCli.Err(), "WARNING: No cpu shares support") + fmt.Fprintln(stdErr, "WARNING: No cpu shares support") } if !info.CPUSet { - fmt.Fprintln(dockerCli.Err(), "WARNING: No cpuset support") + fmt.Fprintln(stdErr, "WARNING: No cpuset support") } if !info.IPv4Forwarding { - fmt.Fprintln(dockerCli.Err(), "WARNING: IPv4 forwarding is disabled") + fmt.Fprintln(stdErr, "WARNING: IPv4 forwarding is disabled") } if !info.BridgeNfIptables { - fmt.Fprintln(dockerCli.Err(), "WARNING: bridge-nf-call-iptables is disabled") + fmt.Fprintln(stdErr, "WARNING: bridge-nf-call-iptables is disabled") } if !info.BridgeNfIP6tables { - fmt.Fprintln(dockerCli.Err(), "WARNING: bridge-nf-call-ip6tables is disabled") + fmt.Fprintln(stdErr, "WARNING: bridge-nf-call-ip6tables is disabled") } } From 73938cd6182817dffda6c189de5f32dcc1ae69f1 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Tue, 2 May 2023 16:50:29 +0200 Subject: [PATCH 5/9] cli/command/system: printSwarmInfo(): accept io.Writer Don't require whole of DockerCLI to be passed, as all we need is a writer. Signed-off-by: Sebastiaan van Stijn --- cli/command/system/info.go | 62 +++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/cli/command/system/info.go b/cli/command/system/info.go index 4c90874bd7..eb906f9748 100644 --- a/cli/command/system/info.go +++ b/cli/command/system/info.go @@ -250,7 +250,7 @@ func prettyPrintServerInfo(dockerCli command.Cli, info *info) []error { fmt.Fprintln(dockerCli.Out(), " Log:", strings.Join(info.Plugins.Log, " ")) fmt.Fprintln(dockerCli.Out(), " Swarm:", info.Swarm.LocalNodeState) - printSwarmInfo(dockerCli, *info.Info) + printSwarmInfo(dockerCli.Out(), *info.Info) if len(info.Runtimes) > 0 { names := make([]string, 0, len(info.Runtimes)) @@ -371,71 +371,71 @@ func prettyPrintServerInfo(dockerCli command.Cli, info *info) []error { } //nolint:gocyclo -func printSwarmInfo(dockerCli command.Cli, info types.Info) { +func printSwarmInfo(output io.Writer, info types.Info) { if info.Swarm.LocalNodeState == swarm.LocalNodeStateInactive || info.Swarm.LocalNodeState == swarm.LocalNodeStateLocked { return } - fmt.Fprintln(dockerCli.Out(), " NodeID:", info.Swarm.NodeID) + fmt.Fprintln(output, " NodeID:", info.Swarm.NodeID) if info.Swarm.Error != "" { - fmt.Fprintln(dockerCli.Out(), " Error:", info.Swarm.Error) + fmt.Fprintln(output, " Error:", info.Swarm.Error) } - fmt.Fprintln(dockerCli.Out(), " Is Manager:", info.Swarm.ControlAvailable) + fmt.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(dockerCli.Out(), " ClusterID:", info.Swarm.Cluster.ID) - fmt.Fprintln(dockerCli.Out(), " Managers:", info.Swarm.Managers) - fmt.Fprintln(dockerCli.Out(), " Nodes:", info.Swarm.Nodes) + fmt.Fprintln(output, " ClusterID:", info.Swarm.Cluster.ID) + fmt.Fprintln(output, " Managers:", info.Swarm.Managers) + fmt.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(dockerCli.Out(), " Default Address Pool:", strAddrPool.String()) - fmt.Fprintln(dockerCli.Out(), " SubnetSize:", info.Swarm.Cluster.SubnetSize) + fmt.Fprintln(output, " Default Address Pool:", strAddrPool.String()) + fmt.Fprintln(output, " SubnetSize:", info.Swarm.Cluster.SubnetSize) } if info.Swarm.Cluster.DataPathPort > 0 { - fmt.Fprintln(dockerCli.Out(), " Data Path Port:", info.Swarm.Cluster.DataPathPort) + fmt.Fprintln(output, " Data Path Port:", info.Swarm.Cluster.DataPathPort) } - fmt.Fprintln(dockerCli.Out(), " Orchestration:") + fmt.Fprintln(output, " Orchestration:") taskHistoryRetentionLimit := int64(0) if info.Swarm.Cluster.Spec.Orchestration.TaskHistoryRetentionLimit != nil { taskHistoryRetentionLimit = *info.Swarm.Cluster.Spec.Orchestration.TaskHistoryRetentionLimit } - fmt.Fprintln(dockerCli.Out(), " Task History Retention Limit:", taskHistoryRetentionLimit) - fmt.Fprintln(dockerCli.Out(), " Raft:") - fmt.Fprintln(dockerCli.Out(), " Snapshot Interval:", info.Swarm.Cluster.Spec.Raft.SnapshotInterval) + fmt.Fprintln(output, " Task History Retention Limit:", taskHistoryRetentionLimit) + fmt.Fprintln(output, " Raft:") + fmt.Fprintln(output, " Snapshot Interval:", info.Swarm.Cluster.Spec.Raft.SnapshotInterval) if info.Swarm.Cluster.Spec.Raft.KeepOldSnapshots != nil { - fmt.Fprintf(dockerCli.Out(), " Number of Old Snapshots to Retain: %d\n", *info.Swarm.Cluster.Spec.Raft.KeepOldSnapshots) + fmt.Fprintf(output, " Number of Old Snapshots to Retain: %d\n", *info.Swarm.Cluster.Spec.Raft.KeepOldSnapshots) } - fmt.Fprintln(dockerCli.Out(), " Heartbeat Tick:", info.Swarm.Cluster.Spec.Raft.HeartbeatTick) - fmt.Fprintln(dockerCli.Out(), " Election Tick:", info.Swarm.Cluster.Spec.Raft.ElectionTick) - fmt.Fprintln(dockerCli.Out(), " Dispatcher:") - fmt.Fprintln(dockerCli.Out(), " Heartbeat Period:", units.HumanDuration(info.Swarm.Cluster.Spec.Dispatcher.HeartbeatPeriod)) - fmt.Fprintln(dockerCli.Out(), " CA Configuration:") - fmt.Fprintln(dockerCli.Out(), " Expiry Duration:", units.HumanDuration(info.Swarm.Cluster.Spec.CAConfig.NodeCertExpiry)) - fmt.Fprintln(dockerCli.Out(), " Force Rotate:", info.Swarm.Cluster.Spec.CAConfig.ForceRotate) + 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) if caCert := strings.TrimSpace(info.Swarm.Cluster.Spec.CAConfig.SigningCACert); caCert != "" { - fmt.Fprintf(dockerCli.Out(), " Signing CA Certificate: \n%s\n\n", caCert) + fmt.Fprintf(output, " Signing CA Certificate: \n%s\n\n", caCert) } if len(info.Swarm.Cluster.Spec.CAConfig.ExternalCAs) > 0 { - fmt.Fprintln(dockerCli.Out(), " External CAs:") + fmt.Fprintln(output, " External CAs:") for _, entry := range info.Swarm.Cluster.Spec.CAConfig.ExternalCAs { - fmt.Fprintf(dockerCli.Out(), " %s: %s\n", entry.Protocol, entry.URL) + fmt.Fprintf(output, " %s: %s\n", entry.Protocol, entry.URL) } } - fmt.Fprintln(dockerCli.Out(), " Autolock Managers:", info.Swarm.Cluster.Spec.EncryptionConfig.AutoLockManagers) - fmt.Fprintln(dockerCli.Out(), " Root Rotation In Progress:", info.Swarm.Cluster.RootRotationInProgress) + fmt.Fprintln(output, " Autolock Managers:", info.Swarm.Cluster.Spec.EncryptionConfig.AutoLockManagers) + fmt.Fprintln(output, " Root Rotation In Progress:", info.Swarm.Cluster.RootRotationInProgress) } - fmt.Fprintln(dockerCli.Out(), " Node Address:", info.Swarm.NodeAddr) + fmt.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(dockerCli.Out(), " Manager Addresses:") + fmt.Fprintln(output, " Manager Addresses:") for _, entry := range managers { - fmt.Fprintf(dockerCli.Out(), " %s\n", entry) + fmt.Fprintf(output, " %s\n", entry) } } } From ba7a200f0a3e986827ed4762e66cb877ab841eca Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Tue, 2 May 2023 16:38:01 +0200 Subject: [PATCH 6/9] cli/command/system: prettyPrintClientInfo: accept Streams No need to pass whole of DockerCLI, as all it needs is the outputs. Signed-off-by: Sebastiaan van Stijn --- cli/command/system/info.go | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/cli/command/system/info.go b/cli/command/system/info.go index eb906f9748..8b347f7f72 100644 --- a/cli/command/system/info.go +++ b/cli/command/system/info.go @@ -191,18 +191,19 @@ func prettyPrintInfo(dockerCli command.Cli, info info) error { return nil } -func prettyPrintClientInfo(dockerCli command.Cli, info clientInfo) { - fprintlnNonEmpty(dockerCli.Out(), " Version: ", info.Version) - fmt.Fprintln(dockerCli.Out(), " Context: ", info.Context) - fmt.Fprintln(dockerCli.Out(), " Debug Mode:", info.Debug) +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) if len(info.Plugins) > 0 { - fmt.Fprintln(dockerCli.Out(), " Plugins:") + fmt.Fprintln(output, " Plugins:") for _, p := range info.Plugins { if p.Err == nil { - fmt.Fprintf(dockerCli.Out(), " %s: %s (%s)\n", p.Name, p.ShortDescription, p.Vendor) - fprintlnNonEmpty(dockerCli.Out(), " Version: ", p.Version) - fprintlnNonEmpty(dockerCli.Out(), " Path: ", p.Path) + fmt.Fprintf(output, " %s: %s (%s)\n", p.Name, p.ShortDescription, p.Vendor) + fprintlnNonEmpty(output, " Version: ", p.Version) + fprintlnNonEmpty(output, " Path: ", p.Path) } else { info.Warnings = append(info.Warnings, fmt.Sprintf("WARNING: Plugin %q is not valid: %s", p.Path, p.Err)) } @@ -210,7 +211,7 @@ func prettyPrintClientInfo(dockerCli command.Cli, info clientInfo) { } if len(info.Warnings) > 0 { - fmt.Fprintln(dockerCli.Err(), strings.Join(info.Warnings, "\n")) + fmt.Fprintln(streams.Err(), strings.Join(info.Warnings, "\n")) } } From 8cfefc6ea2923a4b68e2e628bf9a73580564120d Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Tue, 2 May 2023 16:40:54 +0200 Subject: [PATCH 7/9] cli/command/system: prettyPrintServerInfo: accept Streams No need to pass whole of DockerCLI, as all it needs is the outputs. Signed-off-by: Sebastiaan van Stijn --- cli/command/system/info.go | 125 +++++++++++++++++++------------------ 1 file changed, 63 insertions(+), 62 deletions(-) diff --git a/cli/command/system/info.go b/cli/command/system/info.go index 8b347f7f72..c668e5987e 100644 --- a/cli/command/system/info.go +++ b/cli/command/system/info.go @@ -216,54 +216,55 @@ func prettyPrintClientInfo(streams command.Streams, info clientInfo) { } //nolint:gocyclo -func prettyPrintServerInfo(dockerCli command.Cli, info *info) []error { +func prettyPrintServerInfo(streams command.Streams, info *info) []error { var errs []error + output := streams.Out() - fmt.Fprintln(dockerCli.Out(), " Containers:", info.Containers) - fmt.Fprintln(dockerCli.Out(), " Running:", info.ContainersRunning) - fmt.Fprintln(dockerCli.Out(), " Paused:", info.ContainersPaused) - fmt.Fprintln(dockerCli.Out(), " Stopped:", info.ContainersStopped) - fmt.Fprintln(dockerCli.Out(), " Images:", info.Images) - fprintlnNonEmpty(dockerCli.Out(), " Server Version:", info.ServerVersion) - fprintlnNonEmpty(dockerCli.Out(), " Storage Driver:", info.Driver) + 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) + fprintlnNonEmpty(output, " Server Version:", info.ServerVersion) + fprintlnNonEmpty(output, " Storage Driver:", info.Driver) if info.DriverStatus != nil { for _, pair := range info.DriverStatus { - fmt.Fprintf(dockerCli.Out(), " %s: %s\n", pair[0], pair[1]) + fmt.Fprintf(output, " %s: %s\n", pair[0], pair[1]) } } if info.SystemStatus != nil { for _, pair := range info.SystemStatus { - fmt.Fprintf(dockerCli.Out(), " %s: %s\n", pair[0], pair[1]) + fmt.Fprintf(output, " %s: %s\n", pair[0], pair[1]) } } - fprintlnNonEmpty(dockerCli.Out(), " Logging Driver:", info.LoggingDriver) - fprintlnNonEmpty(dockerCli.Out(), " Cgroup Driver:", info.CgroupDriver) - fprintlnNonEmpty(dockerCli.Out(), " Cgroup Version:", info.CgroupVersion) + fprintlnNonEmpty(output, " Logging Driver:", info.LoggingDriver) + fprintlnNonEmpty(output, " Cgroup Driver:", info.CgroupDriver) + fprintlnNonEmpty(output, " Cgroup Version:", info.CgroupVersion) - fmt.Fprintln(dockerCli.Out(), " Plugins:") - fmt.Fprintln(dockerCli.Out(), " Volume:", strings.Join(info.Plugins.Volume, " ")) - fmt.Fprintln(dockerCli.Out(), " Network:", strings.Join(info.Plugins.Network, " ")) + fmt.Fprintln(output, " Plugins:") + fmt.Fprintln(output, " Volume:", strings.Join(info.Plugins.Volume, " ")) + fmt.Fprintln(output, " Network:", strings.Join(info.Plugins.Network, " ")) if len(info.Plugins.Authorization) != 0 { - fmt.Fprintln(dockerCli.Out(), " Authorization:", strings.Join(info.Plugins.Authorization, " ")) + fmt.Fprintln(output, " Authorization:", strings.Join(info.Plugins.Authorization, " ")) } - fmt.Fprintln(dockerCli.Out(), " Log:", strings.Join(info.Plugins.Log, " ")) + fmt.Fprintln(output, " Log:", strings.Join(info.Plugins.Log, " ")) - fmt.Fprintln(dockerCli.Out(), " Swarm:", info.Swarm.LocalNodeState) - printSwarmInfo(dockerCli.Out(), *info.Info) + fmt.Fprintln(output, " Swarm:", info.Swarm.LocalNodeState) + printSwarmInfo(output, *info.Info) if len(info.Runtimes) > 0 { names := make([]string, 0, len(info.Runtimes)) for name := range info.Runtimes { names = append(names, name) } - fmt.Fprintln(dockerCli.Out(), " Runtimes:", strings.Join(names, " ")) - fmt.Fprintln(dockerCli.Out(), " Default Runtime:", info.DefaultRuntime) + fmt.Fprintln(output, " Runtimes:", strings.Join(names, " ")) + fmt.Fprintln(output, " Default Runtime:", info.DefaultRuntime) } if info.OSType == "linux" { - fmt.Fprintln(dockerCli.Out(), " Init Binary:", info.InitBinary) + fmt.Fprintln(output, " Init Binary:", info.InitBinary) for _, ci := range []struct { Name string @@ -273,23 +274,23 @@ func prettyPrintServerInfo(dockerCli command.Cli, info *info) []error { {"runc", info.RuncCommit}, {"init", info.InitCommit}, } { - fmt.Fprintf(dockerCli.Out(), " %s version: %s", ci.Name, ci.Commit.ID) + fmt.Fprintf(output, " %s version: %s", ci.Name, ci.Commit.ID) if ci.Commit.ID != ci.Commit.Expected { - fmt.Fprintf(dockerCli.Out(), " (expected: %s)", ci.Commit.Expected) + fmt.Fprintf(output, " (expected: %s)", ci.Commit.Expected) } - fmt.Fprint(dockerCli.Out(), "\n") + fmt.Fprint(output, "\n") } if len(info.SecurityOptions) != 0 { if kvs, err := types.DecodeSecurityOptions(info.SecurityOptions); err != nil { errs = append(errs, err) } else { - fmt.Fprintln(dockerCli.Out(), " Security Options:") + fmt.Fprintln(output, " Security Options:") for _, so := range kvs { - fmt.Fprintln(dockerCli.Out(), " "+so.Name) + fmt.Fprintln(output, " "+so.Name) for _, o := range so.Options { switch o.Key { case "profile": - fmt.Fprintln(dockerCli.Out(), " Profile:", o.Value) + fmt.Fprintln(output, " Profile:", o.Value) } } } @@ -299,75 +300,75 @@ func prettyPrintServerInfo(dockerCli command.Cli, info *info) []error { // Isolation only has meaning on a Windows daemon. if info.OSType == "windows" { - fmt.Fprintln(dockerCli.Out(), " Default Isolation:", info.Isolation) + fmt.Fprintln(output, " Default Isolation:", info.Isolation) } - fprintlnNonEmpty(dockerCli.Out(), " Kernel Version:", info.KernelVersion) - fprintlnNonEmpty(dockerCli.Out(), " Operating System:", info.OperatingSystem) - fprintlnNonEmpty(dockerCli.Out(), " OSType:", info.OSType) - fprintlnNonEmpty(dockerCli.Out(), " Architecture:", info.Architecture) - fmt.Fprintln(dockerCli.Out(), " CPUs:", info.NCPU) - fmt.Fprintln(dockerCli.Out(), " Total Memory:", units.BytesSize(float64(info.MemTotal))) - fprintlnNonEmpty(dockerCli.Out(), " Name:", info.Name) - fprintlnNonEmpty(dockerCli.Out(), " ID:", info.ID) - fmt.Fprintln(dockerCli.Out(), " Docker Root Dir:", info.DockerRootDir) - fmt.Fprintln(dockerCli.Out(), " Debug Mode:", info.Debug) + 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))) + 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) if info.Debug { - fmt.Fprintln(dockerCli.Out(), " File Descriptors:", info.NFd) - fmt.Fprintln(dockerCli.Out(), " Goroutines:", info.NGoroutines) - fmt.Fprintln(dockerCli.Out(), " System Time:", info.SystemTime) - fmt.Fprintln(dockerCli.Out(), " EventsListeners:", info.NEventsListener) + 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) } - fprintlnNonEmpty(dockerCli.Out(), " HTTP Proxy:", info.HTTPProxy) - fprintlnNonEmpty(dockerCli.Out(), " HTTPS Proxy:", info.HTTPSProxy) - fprintlnNonEmpty(dockerCli.Out(), " No Proxy:", info.NoProxy) - fprintlnNonEmpty(dockerCli.Out(), " Username:", info.UserName) + fprintlnNonEmpty(output, " HTTP Proxy:", info.HTTPProxy) + fprintlnNonEmpty(output, " HTTPS Proxy:", info.HTTPSProxy) + fprintlnNonEmpty(output, " No Proxy:", info.NoProxy) + fprintlnNonEmpty(output, " Username:", info.UserName) if len(info.Labels) > 0 { - fmt.Fprintln(dockerCli.Out(), " Labels:") + fmt.Fprintln(output, " Labels:") for _, lbl := range info.Labels { - fmt.Fprintln(dockerCli.Out(), " "+lbl) + fmt.Fprintln(output, " "+lbl) } } - fmt.Fprintln(dockerCli.Out(), " Experimental:", info.ExperimentalBuild) + fmt.Fprintln(output, " Experimental:", info.ExperimentalBuild) if info.RegistryConfig != nil && (len(info.RegistryConfig.InsecureRegistryCIDRs) > 0 || len(info.RegistryConfig.IndexConfigs) > 0) { - fmt.Fprintln(dockerCli.Out(), " Insecure Registries:") + fmt.Fprintln(output, " Insecure Registries:") for _, reg := range info.RegistryConfig.IndexConfigs { if !reg.Secure { - fmt.Fprintln(dockerCli.Out(), " "+reg.Name) + fmt.Fprintln(output, " "+reg.Name) } } for _, reg := range info.RegistryConfig.InsecureRegistryCIDRs { mask, _ := reg.Mask.Size() - fmt.Fprintf(dockerCli.Out(), " %s/%d\n", reg.IP.String(), mask) + fmt.Fprintf(output, " %s/%d\n", reg.IP.String(), mask) } } if info.RegistryConfig != nil && len(info.RegistryConfig.Mirrors) > 0 { - fmt.Fprintln(dockerCli.Out(), " Registry Mirrors:") + fmt.Fprintln(output, " Registry Mirrors:") for _, mirror := range info.RegistryConfig.Mirrors { - fmt.Fprintln(dockerCli.Out(), " "+mirror) + fmt.Fprintln(output, " "+mirror) } } - fmt.Fprintln(dockerCli.Out(), " Live Restore Enabled:", info.LiveRestoreEnabled) + fmt.Fprintln(output, " Live Restore Enabled:", info.LiveRestoreEnabled) if info.ProductLicense != "" { - fmt.Fprintln(dockerCli.Out(), " Product License:", info.ProductLicense) + fmt.Fprintln(output, " Product License:", info.ProductLicense) } if info.DefaultAddressPools != nil && len(info.DefaultAddressPools) > 0 { - fmt.Fprintln(dockerCli.Out(), " Default Address Pools:") + fmt.Fprintln(output, " Default Address Pools:") for _, pool := range info.DefaultAddressPools { - fmt.Fprintf(dockerCli.Out(), " Base: %s, Size: %d\n", pool.Base, pool.Size) + fmt.Fprintf(output, " Base: %s, Size: %d\n", pool.Base, pool.Size) } } - fmt.Fprint(dockerCli.Out(), "\n") - printServerWarnings(dockerCli.Err(), info) + fmt.Fprint(output, "\n") + printServerWarnings(streams.Err(), info) return errs } From 1e89037d724b9b2596f1ffd717531a9483bd9237 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Tue, 2 May 2023 16:54:05 +0200 Subject: [PATCH 8/9] cli/command/system: prettyInfo: accept Streams No need to pass whole of DockerCLI, as all it needs is the outputs. Signed-off-by: Sebastiaan van Stijn --- cli/command/system/info.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/cli/command/system/info.go b/cli/command/system/info.go index c668e5987e..471d732104 100644 --- a/cli/command/system/info.go +++ b/cli/command/system/info.go @@ -160,29 +160,29 @@ func needsServerInfo(template string, info info) bool { return err != nil } -func prettyPrintInfo(dockerCli command.Cli, info info) error { +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(dockerCli.Out(), "Client:", p) + _, _ = fmt.Fprintln(streams.Out(), "Client:", p) } else { - _, _ = fmt.Fprintln(dockerCli.Out(), "Client:") + _, _ = fmt.Fprintln(streams.Out(), "Client:") } if info.ClientInfo != nil { - prettyPrintClientInfo(dockerCli, *info.ClientInfo) + prettyPrintClientInfo(streams, *info.ClientInfo) } for _, err := range info.ClientErrors { - fmt.Fprintln(dockerCli.Err(), "ERROR:", err) + fmt.Fprintln(streams.Err(), "ERROR:", err) } - fmt.Fprintln(dockerCli.Out()) - fmt.Fprintln(dockerCli.Out(), "Server:") + fmt.Fprintln(streams.Out()) + fmt.Fprintln(streams.Out(), "Server:") if info.Info != nil { - for _, err := range prettyPrintServerInfo(dockerCli, &info) { + for _, err := range prettyPrintServerInfo(streams, &info) { info.ServerErrors = append(info.ServerErrors, err.Error()) } } for _, err := range info.ServerErrors { - fmt.Fprintln(dockerCli.Err(), "ERROR:", err) + fmt.Fprintln(streams.Err(), "ERROR:", err) } if len(info.ServerErrors) > 0 || len(info.ClientErrors) > 0 { From 155f7d9e2ba65d49de771baedbb1a8817fea29a4 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Mon, 10 Apr 2023 16:48:22 +0200 Subject: [PATCH 9/9] 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 --- cli/command/system/info.go | 225 ++++++++++++++++---------------- cli/command/system/info_test.go | 6 +- 2 files changed, 119 insertions(+), 112 deletions(-) diff --git a/cli/command/system/info.go b/cli/command/system/info.go index 471d732104..153c968bd2 100644 --- a/cli/command/system/info.go +++ b/cli/command/system/info.go @@ -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) } } diff --git a/cli/command/system/info_test.go b/cli/command/system/info_test.go index 5fc55ccd1e..4d3d2823a4 100644 --- a/cli/command/system/info_test.go +++ b/cli/command/system/info_test.go @@ -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)