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)