DockerCLI/cli/command/system/info_test.go

527 lines
14 KiB
Go
Raw Normal View History

package system
import (
"encoding/base64"
"net"
"testing"
"time"
pluginmanager "github.com/docker/cli/cli-plugins/manager"
"github.com/docker/cli/internal/test"
registrytypes "github.com/docker/docker/api/types/registry"
"github.com/docker/docker/api/types/swarm"
"github.com/docker/docker/api/types/system"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
"gotest.tools/v3/golden"
)
// helper function that base64 decodes a string and ignores the error
func base64Decode(val string) []byte {
decoded, _ := base64.StdEncoding.DecodeString(val)
return decoded
}
const sampleID = "EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX"
var sampleInfoNoSwarm = system.Info{
ID: sampleID,
Containers: 0,
ContainersRunning: 0,
ContainersPaused: 0,
ContainersStopped: 0,
Images: 0,
Driver: "overlay2",
DriverStatus: [][2]string{
{"Backing Filesystem", "extfs"},
{"Supports d_type", "true"},
{"Using metacopy", "false"},
{"Native Overlay Diff", "true"},
},
SystemStatus: nil,
Plugins: system.PluginsInfo{
Volume: []string{"local"},
Network: []string{"bridge", "host", "macvlan", "null", "overlay"},
Authorization: nil,
Log: []string{"awslogs", "fluentd", "gcplogs", "gelf", "journald", "json-file", "splunk", "syslog"},
},
MemoryLimit: true,
SwapLimit: true,
KernelMemory: true,
CPUCfsPeriod: true,
CPUCfsQuota: true,
CPUShares: true,
CPUSet: true,
IPv4Forwarding: true,
BridgeNfIptables: true,
BridgeNfIP6tables: true,
Debug: true,
NFd: 33,
OomKillDisable: true,
NGoroutines: 135,
SystemTime: "2017-08-24T17:44:34.077811894Z",
LoggingDriver: "json-file",
CgroupDriver: "cgroupfs",
NEventsListener: 0,
KernelVersion: "4.4.0-87-generic",
OperatingSystem: "Ubuntu 16.04.3 LTS",
OSVersion: "",
OSType: "linux",
Architecture: "x86_64",
IndexServerAddress: "https://index.docker.io/v1/",
RegistryConfig: &registrytypes.ServiceConfig{
AllowNondistributableArtifactsCIDRs: nil,
AllowNondistributableArtifactsHostnames: nil,
InsecureRegistryCIDRs: []*registrytypes.NetIPNet{
{
IP: net.ParseIP("127.0.0.0"),
Mask: net.IPv4Mask(255, 0, 0, 0),
},
},
IndexConfigs: map[string]*registrytypes.IndexInfo{
"docker.io": {
Name: "docker.io",
Mirrors: nil,
Secure: true,
Official: true,
},
},
Mirrors: nil,
},
NCPU: 2,
MemTotal: 2097356800,
DockerRootDir: "/var/lib/docker",
HTTPProxy: "",
HTTPSProxy: "",
NoProxy: "",
Name: "system-sample",
Labels: []string{"provider=digitalocean"},
ExperimentalBuild: false,
ServerVersion: "17.06.1-ce",
Runtimes: map[string]system.RuntimeWithStatus{
"runc": {
Runtime: system.Runtime{
Path: "docker-runc",
Args: nil,
},
},
},
DefaultRuntime: "runc",
Swarm: swarm.Info{LocalNodeState: "inactive"},
LiveRestoreEnabled: false,
Isolation: "",
InitBinary: "docker-init",
ContainerdCommit: system.Commit{
ID: "6e23458c129b551d5c9871e5174f6b1b7f6d1170",
Expected: "6e23458c129b551d5c9871e5174f6b1b7f6d1170",
},
RuncCommit: system.Commit{
ID: "810190ceaa507aa2727d7ae6f4790c76ec150bd2",
Expected: "810190ceaa507aa2727d7ae6f4790c76ec150bd2",
},
InitCommit: system.Commit{
ID: "949e6fa",
Expected: "949e6fa",
},
SecurityOptions: []string{"name=apparmor", "name=seccomp,profile=default"},
DefaultAddressPools: []system.NetworkAddressPool{
{
Base: "10.123.0.0/16",
Size: 24,
},
},
CDISpecDirs: []string{"/etc/cdi", "/var/run/cdi"},
}
var sampleSwarmInfo = swarm.Info{
NodeID: "qo2dfdig9mmxqkawulggepdih",
NodeAddr: "165.227.107.89",
LocalNodeState: "active",
ControlAvailable: true,
Error: "",
RemoteManagers: []swarm.Peer{
{
NodeID: "qo2dfdig9mmxqkawulggepdih",
Addr: "165.227.107.89:2377",
},
},
Nodes: 1,
Managers: 1,
Cluster: &swarm.ClusterInfo{
ID: "9vs5ygs0gguyyec4iqf2314c0",
Meta: swarm.Meta{
Version: swarm.Version{Index: 11},
CreatedAt: time.Date(2017, 8, 24, 17, 34, 19, 278062352, time.UTC),
UpdatedAt: time.Date(2017, 8, 24, 17, 34, 42, 398815481, time.UTC),
},
Spec: swarm.Spec{
Annotations: swarm.Annotations{
Name: "default",
Labels: nil,
},
Orchestration: swarm.OrchestrationConfig{
TaskHistoryRetentionLimit: &[]int64{5}[0],
},
Raft: swarm.RaftConfig{
SnapshotInterval: 10000,
KeepOldSnapshots: &[]uint64{0}[0],
LogEntriesForSlowFollowers: 500,
ElectionTick: 3,
HeartbeatTick: 1,
},
Dispatcher: swarm.DispatcherConfig{
HeartbeatPeriod: 5000000000,
},
CAConfig: swarm.CAConfig{
NodeCertExpiry: 7776000000000000,
},
TaskDefaults: swarm.TaskDefaults{},
EncryptionConfig: swarm.EncryptionConfig{
AutoLockManagers: true,
},
},
TLSInfo: swarm.TLSInfo{
TrustRoot: `
-----BEGIN CERTIFICATE-----
MIIBajCCARCgAwIBAgIUaFCW5xsq8eyiJ+Pmcv3MCflMLnMwCgYIKoZIzj0EAwIw
EzERMA8GA1UEAxMIc3dhcm0tY2EwHhcNMTcwODI0MTcyOTAwWhcNMzcwODE5MTcy
OTAwWjATMREwDwYDVQQDEwhzd2FybS1jYTBZMBMGByqGSM49AgEGCCqGSM49AwEH
A0IABDy7NebyUJyUjWJDBUdnZoV6GBxEGKO4TZPNDwnxDxJcUdLVaB7WGa4/DLrW
UfsVgh1JGik2VTiLuTMA1tLlNPOjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB
Af8EBTADAQH/MB0GA1UdDgQWBBQl16XFtaaXiUAwEuJptJlDjfKskDAKBggqhkjO
PQQDAgNIADBFAiEAo9fTQNM5DP9bHVcTJYfl2Cay1bFu1E+lnpmN+EYJfeACIGKH
1pCUkZ+D0IB6CiEZGWSHyLuXPM1rlP+I5KuS7sB8
-----END CERTIFICATE-----
`,
CertIssuerSubject: base64Decode("MBMxETAPBgNVBAMTCHN3YXJtLWNh"),
CertIssuerPublicKey: base64Decode(
"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEPLs15vJQnJSNYkMFR2dmhXoYHEQYo7hNk80PCfEPElxR0tVoHtYZrj8MutZR+xWCHUkaKTZVOIu5MwDW0uU08w=="),
},
RootRotationInProgress: false,
},
}
var samplePluginsInfo = []pluginmanager.Plugin{
{
Name: "goodplugin",
Path: "/path/to/docker-goodplugin",
Metadata: pluginmanager.Metadata{
SchemaVersion: "0.1.0",
ShortDescription: "unit test is good",
Vendor: "ACME Corp",
Version: "0.1.0",
},
},
{
Name: "unversionedplugin",
Path: "/path/to/docker-unversionedplugin",
Metadata: pluginmanager.Metadata{
SchemaVersion: "0.1.0",
ShortDescription: "this plugin has no version",
Vendor: "ACME Corp",
},
},
{
Name: "badplugin",
Path: "/path/to/docker-badplugin",
Err: pluginmanager.NewPluginError("something wrong"),
},
}
func TestPrettyPrintInfo(t *testing.T) {
infoWithSwarm := sampleInfoNoSwarm
infoWithSwarm.Swarm = sampleSwarmInfo
infoWithWarningsLinux := sampleInfoNoSwarm
infoWithWarningsLinux.MemoryLimit = false
infoWithWarningsLinux.SwapLimit = false
infoWithWarningsLinux.KernelMemory = false
infoWithWarningsLinux.OomKillDisable = false
infoWithWarningsLinux.CPUCfsQuota = false
infoWithWarningsLinux.CPUCfsPeriod = false
infoWithWarningsLinux.CPUShares = false
infoWithWarningsLinux.CPUSet = false
infoWithWarningsLinux.IPv4Forwarding = false
infoWithWarningsLinux.BridgeNfIptables = false
infoWithWarningsLinux.BridgeNfIP6tables = false
sampleInfoDaemonWarnings := sampleInfoNoSwarm
sampleInfoDaemonWarnings.Warnings = []string{
"WARNING: No memory limit support",
"WARNING: No swap limit support",
"WARNING: No oom kill disable support",
"WARNING: No cpu cfs quota support",
"WARNING: No cpu cfs period support",
"WARNING: No cpu shares support",
"WARNING: No cpuset support",
"WARNING: IPv4 forwarding is disabled",
"WARNING: bridge-nf-call-iptables is disabled",
"WARNING: bridge-nf-call-ip6tables is disabled",
}
sampleInfoBadSecurity := sampleInfoNoSwarm
sampleInfoBadSecurity.SecurityOptions = []string{"foo="}
sampleInfoLabelsNil := sampleInfoNoSwarm
sampleInfoLabelsNil.Labels = nil
sampleInfoLabelsEmpty := sampleInfoNoSwarm
sampleInfoLabelsEmpty.Labels = []string{}
for _, tc := range []struct {
doc string
dockerInfo dockerInfo
prettyGolden string
warningsGolden string
jsonGolden string
expectedError string
}{
{
doc: "info without swarm",
dockerInfo: dockerInfo{
Info: &sampleInfoNoSwarm,
ClientInfo: &clientInfo{
clientVersion: clientVersion{
Platform: &platformInfo{Name: "Docker Engine - Community"},
Version: "24.0.0",
Context: "default",
},
Debug: true,
},
},
prettyGolden: "docker-info-no-swarm",
jsonGolden: "docker-info-no-swarm",
},
{
doc: "info with plugins",
dockerInfo: dockerInfo{
Info: &sampleInfoNoSwarm,
ClientInfo: &clientInfo{
clientVersion: clientVersion{Context: "default"},
Plugins: samplePluginsInfo,
},
},
prettyGolden: "docker-info-plugins",
jsonGolden: "docker-info-plugins",
warningsGolden: "docker-info-plugins-warnings",
},
{
doc: "info with nil labels",
dockerInfo: dockerInfo{
Info: &sampleInfoLabelsNil,
ClientInfo: &clientInfo{clientVersion: clientVersion{Context: "default"}},
},
prettyGolden: "docker-info-with-labels-nil",
},
{
doc: "info with empty labels",
dockerInfo: dockerInfo{
Info: &sampleInfoLabelsEmpty,
ClientInfo: &clientInfo{clientVersion: clientVersion{Context: "default"}},
},
prettyGolden: "docker-info-with-labels-empty",
},
{
doc: "info with swarm",
dockerInfo: dockerInfo{
Info: &infoWithSwarm,
ClientInfo: &clientInfo{
clientVersion: clientVersion{Context: "default"},
Debug: false,
},
},
prettyGolden: "docker-info-with-swarm",
jsonGolden: "docker-info-with-swarm",
},
{
doc: "info with daemon warnings",
dockerInfo: dockerInfo{
Info: &sampleInfoDaemonWarnings,
ClientInfo: &clientInfo{
clientVersion: clientVersion{
Platform: &platformInfo{Name: "Docker Engine - Community"},
Version: "24.0.0",
Context: "default",
},
Debug: true,
},
},
prettyGolden: "docker-info-no-swarm",
warningsGolden: "docker-info-warnings",
jsonGolden: "docker-info-daemon-warnings",
},
{
doc: "errors for both",
dockerInfo: dockerInfo{
ServerErrors: []string{"a server error occurred"},
ClientErrors: []string{"a client error occurred"},
},
prettyGolden: "docker-info-errors",
jsonGolden: "docker-info-errors",
warningsGolden: "docker-info-errors-stderr",
expectedError: "errors pretty printing info",
},
{
doc: "bad security info",
dockerInfo: dockerInfo{
Info: &sampleInfoBadSecurity,
ServerErrors: []string{"a server error occurred"},
ClientInfo: &clientInfo{Debug: false},
},
prettyGolden: "docker-info-badsec",
jsonGolden: "docker-info-badsec",
warningsGolden: "docker-info-badsec-stderr",
expectedError: "errors pretty printing info",
},
} {
tc := tc
t.Run(tc.doc, func(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{})
err := prettyPrintInfo(cli, tc.dockerInfo)
if tc.expectedError == "" {
assert.NilError(t, err)
} else {
assert.Error(t, err, tc.expectedError)
}
golden.Assert(t, cli.OutBuffer().String(), tc.prettyGolden+".golden")
if tc.warningsGolden != "" {
golden.Assert(t, cli.ErrBuffer().String(), tc.warningsGolden+".golden")
} else {
assert.Check(t, is.Equal("", cli.ErrBuffer().String()))
}
if tc.jsonGolden != "" {
cli = test.NewFakeCli(&fakeClient{})
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.Out(), tc.dockerInfo, "json"))
golden.Assert(t, cli.OutBuffer().String(), tc.jsonGolden+".json.golden")
assert.Check(t, is.Equal("", cli.ErrBuffer().String()))
}
})
}
}
func BenchmarkPrettyPrintInfo(b *testing.B) {
infoWithSwarm := sampleInfoNoSwarm
infoWithSwarm.Swarm = sampleSwarmInfo
info := dockerInfo{
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, info)
cli.ResetOutputBuffers()
}
}
func TestFormatInfo(t *testing.T) {
for _, tc := range []struct {
doc string
template string
expectedError string
expectedOut string
}{
{
doc: "basic",
template: "{{.ID}}",
expectedOut: sampleID + "\n",
},
{
doc: "syntax",
template: "{{}",
cli: make cli.StatusError slightly prettier This error didn't do a great job at formatting. If a StatusError was produced without a Status message, it would print a very non-informative error, with information missing. Let's update the output: - If a status-message is provided; print just that (after all the status code is something that can be found from the shell, e.g. through `echo $?` in Bash). - If no status-message is provided: print a message more similar to Go's `exec.ExecError`, which uses `os.rocessState.String()` (see [1]). Before this patch, an error without custom status would print: Status: , Code: 2 After this patch: exit status 2 In situations where a custom error-message is provided, the error-message is print as-is, whereas before this patch, the message got combined with the `Status:` and `Code:`, which resulted in some odd output. Before this patch: docker volume --no-such-flag Status: unknown flag: --no-such-flag See 'docker volume --help'. Usage: docker volume COMMAND Manage volumes Commands: create Create a volume inspect Display detailed information on one or more volumes ls List volumes prune Remove unused local volumes rm Remove one or more volumes update Update a volume (cluster volumes only) Run 'docker volume COMMAND --help' for more information on a command. , Code: 125 With this patch, the error is shown as-is; docker volume --no-such-flag unknown flag: --no-such-flag See 'docker volume --help'. Usage: docker volume COMMAND Manage volumes Commands: create Create a volume inspect Display detailed information on one or more volumes ls List volumes prune Remove unused local volumes rm Remove one or more volumes update Update a volume (cluster volumes only) Run 'docker volume COMMAND --help' for more information on a command. While the exit-code is no longer printed, it's still properly handled; echo $? 125 [1]: https://github.com/golang/go/blob/82c14346d89ec0eeca114f9ca0e88516b2cda454/src/os/exec_posix.go#L107-L135 Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-07-04 13:02:33 -04:00
expectedError: `template parsing error: template: :1: unexpected "}" in command`,
},
{
doc: "syntax",
template: "{{.badString}}",
expectedError: `template: :1:2: executing "" at <.badString>: can't evaluate field badString in type system.dockerInfo`,
},
} {
tc := tc
t.Run(tc.doc, func(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{})
info := dockerInfo{
Info: &sampleInfoNoSwarm,
ClientInfo: &clientInfo{Debug: true},
}
err := formatInfo(cli.Out(), info, tc.template)
linting: address else/if/elseif statements found by gocritic cli/command/formatter/tabwriter/tabwriter.go:579:10: elseif: can replace 'else {if cond {}}' with 'else if cond {}' (gocritic) } else { ^ cli/connhelper/connhelper.go:43:2: singleCaseSwitch: should rewrite switch statement to if statement (gocritic) switch scheme := u.Scheme; scheme { ^ cli/compose/loader/loader.go:666:10: elseif: can replace 'else {if cond {}}' with 'else if cond {}' (gocritic) } else { ^ opts/hosts_test.go:173:10: elseif: can replace 'else {if cond {}}' with 'else if cond {}' (gocritic) } else { ^ cli-plugins/manager/candidate_test.go:78:4: ifElseChain: rewrite if-else to switch statement (gocritic) if tc.err != "" { ^ cli/command/checkpoint/formatter.go:15:2: singleCaseSwitch: should rewrite switch statement to if statement (gocritic) switch source { ^ cli/command/image/formatter_history.go:25:2: singleCaseSwitch: should rewrite switch statement to if statement (gocritic) switch source { ^ cli/command/service/scale.go:107:2: ifElseChain: rewrite if-else to switch statement (gocritic) if serviceMode.Replicated != nil { ^ cli/command/service/update.go:804:9: elseif: can replace 'else {if cond {}}' with 'else if cond {}' (gocritic) } else { ^ cli/command/service/update.go:222:2: ifElseChain: rewrite if-else to switch statement (gocritic) if sendAuth { ^ cli/command/container/formatter_diff.go:17:2: singleCaseSwitch: should rewrite switch statement to if statement (gocritic) switch source { ^ cli/command/container/start.go:79:2: ifElseChain: rewrite if-else to switch statement (gocritic) if opts.Attach || opts.OpenStdin { ^ cli/command/container/utils.go:84:11: elseif: can replace 'else {if cond {}}' with 'else if cond {}' (gocritic) } else { ^ cli/command/container/exec_test.go:200:11: elseif: can replace 'else {if cond {}}' with 'else if cond {}' (gocritic) } else { ^ cli/command/container/logs_test.go:52:11: elseif: can replace 'else {if cond {}}' with 'else if cond {}' (gocritic) } else { ^ cli/command/container/opts_test.go:1014:10: elseif: can replace 'else {if cond {}}' with 'else if cond {}' (gocritic) } else { ^ cli/command/system/info.go:297:7: singleCaseSwitch: should rewrite switch statement to if statement (gocritic) switch o.Key { ^ cli/command/system/version.go:164:4: singleCaseSwitch: should rewrite switch statement to if statement (gocritic) switch component.Name { ^ cli/command/system/info_test.go:478:4: ifElseChain: rewrite if-else to switch statement (gocritic) if tc.expectedOut != "" { ^ Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2023-11-20 07:54:53 -05:00
switch {
case tc.expectedOut != "":
assert.NilError(t, err)
assert.Equal(t, cli.OutBuffer().String(), tc.expectedOut)
linting: address else/if/elseif statements found by gocritic cli/command/formatter/tabwriter/tabwriter.go:579:10: elseif: can replace 'else {if cond {}}' with 'else if cond {}' (gocritic) } else { ^ cli/connhelper/connhelper.go:43:2: singleCaseSwitch: should rewrite switch statement to if statement (gocritic) switch scheme := u.Scheme; scheme { ^ cli/compose/loader/loader.go:666:10: elseif: can replace 'else {if cond {}}' with 'else if cond {}' (gocritic) } else { ^ opts/hosts_test.go:173:10: elseif: can replace 'else {if cond {}}' with 'else if cond {}' (gocritic) } else { ^ cli-plugins/manager/candidate_test.go:78:4: ifElseChain: rewrite if-else to switch statement (gocritic) if tc.err != "" { ^ cli/command/checkpoint/formatter.go:15:2: singleCaseSwitch: should rewrite switch statement to if statement (gocritic) switch source { ^ cli/command/image/formatter_history.go:25:2: singleCaseSwitch: should rewrite switch statement to if statement (gocritic) switch source { ^ cli/command/service/scale.go:107:2: ifElseChain: rewrite if-else to switch statement (gocritic) if serviceMode.Replicated != nil { ^ cli/command/service/update.go:804:9: elseif: can replace 'else {if cond {}}' with 'else if cond {}' (gocritic) } else { ^ cli/command/service/update.go:222:2: ifElseChain: rewrite if-else to switch statement (gocritic) if sendAuth { ^ cli/command/container/formatter_diff.go:17:2: singleCaseSwitch: should rewrite switch statement to if statement (gocritic) switch source { ^ cli/command/container/start.go:79:2: ifElseChain: rewrite if-else to switch statement (gocritic) if opts.Attach || opts.OpenStdin { ^ cli/command/container/utils.go:84:11: elseif: can replace 'else {if cond {}}' with 'else if cond {}' (gocritic) } else { ^ cli/command/container/exec_test.go:200:11: elseif: can replace 'else {if cond {}}' with 'else if cond {}' (gocritic) } else { ^ cli/command/container/logs_test.go:52:11: elseif: can replace 'else {if cond {}}' with 'else if cond {}' (gocritic) } else { ^ cli/command/container/opts_test.go:1014:10: elseif: can replace 'else {if cond {}}' with 'else if cond {}' (gocritic) } else { ^ cli/command/system/info.go:297:7: singleCaseSwitch: should rewrite switch statement to if statement (gocritic) switch o.Key { ^ cli/command/system/version.go:164:4: singleCaseSwitch: should rewrite switch statement to if statement (gocritic) switch component.Name { ^ cli/command/system/info_test.go:478:4: ifElseChain: rewrite if-else to switch statement (gocritic) if tc.expectedOut != "" { ^ Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2023-11-20 07:54:53 -05:00
case tc.expectedError != "":
assert.Error(t, err, tc.expectedError)
linting: address else/if/elseif statements found by gocritic cli/command/formatter/tabwriter/tabwriter.go:579:10: elseif: can replace 'else {if cond {}}' with 'else if cond {}' (gocritic) } else { ^ cli/connhelper/connhelper.go:43:2: singleCaseSwitch: should rewrite switch statement to if statement (gocritic) switch scheme := u.Scheme; scheme { ^ cli/compose/loader/loader.go:666:10: elseif: can replace 'else {if cond {}}' with 'else if cond {}' (gocritic) } else { ^ opts/hosts_test.go:173:10: elseif: can replace 'else {if cond {}}' with 'else if cond {}' (gocritic) } else { ^ cli-plugins/manager/candidate_test.go:78:4: ifElseChain: rewrite if-else to switch statement (gocritic) if tc.err != "" { ^ cli/command/checkpoint/formatter.go:15:2: singleCaseSwitch: should rewrite switch statement to if statement (gocritic) switch source { ^ cli/command/image/formatter_history.go:25:2: singleCaseSwitch: should rewrite switch statement to if statement (gocritic) switch source { ^ cli/command/service/scale.go:107:2: ifElseChain: rewrite if-else to switch statement (gocritic) if serviceMode.Replicated != nil { ^ cli/command/service/update.go:804:9: elseif: can replace 'else {if cond {}}' with 'else if cond {}' (gocritic) } else { ^ cli/command/service/update.go:222:2: ifElseChain: rewrite if-else to switch statement (gocritic) if sendAuth { ^ cli/command/container/formatter_diff.go:17:2: singleCaseSwitch: should rewrite switch statement to if statement (gocritic) switch source { ^ cli/command/container/start.go:79:2: ifElseChain: rewrite if-else to switch statement (gocritic) if opts.Attach || opts.OpenStdin { ^ cli/command/container/utils.go:84:11: elseif: can replace 'else {if cond {}}' with 'else if cond {}' (gocritic) } else { ^ cli/command/container/exec_test.go:200:11: elseif: can replace 'else {if cond {}}' with 'else if cond {}' (gocritic) } else { ^ cli/command/container/logs_test.go:52:11: elseif: can replace 'else {if cond {}}' with 'else if cond {}' (gocritic) } else { ^ cli/command/container/opts_test.go:1014:10: elseif: can replace 'else {if cond {}}' with 'else if cond {}' (gocritic) } else { ^ cli/command/system/info.go:297:7: singleCaseSwitch: should rewrite switch statement to if statement (gocritic) switch o.Key { ^ cli/command/system/version.go:164:4: singleCaseSwitch: should rewrite switch statement to if statement (gocritic) switch component.Name { ^ cli/command/system/info_test.go:478:4: ifElseChain: rewrite if-else to switch statement (gocritic) if tc.expectedOut != "" { ^ Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2023-11-20 07:54:53 -05:00
default:
t.Fatal("test expected to neither pass nor fail")
}
})
}
}
docker info: skip API connection if possible The docker info output contains both "local" and "remote" (daemon-side) information. The API endpoint to collect daemon information (`/info`) is known to be "heavy", and (depending on what information is needed) not needed. This patch checks if the template (`--format`) used requires information from the daemon, and if not, omits making an API request. This will improve performance if (for example), the current "context" is requested from `docker info` or if only plugin information is requested. Before: time docker info --format '{{range .ClientInfo.Plugins}}Plugin: {{.Name}}, {{end}}' Plugin: buildx, Plugin: compose, Plugin: scan, ________________________________________________________ Executed in 301.91 millis fish external usr time 168.64 millis 82.00 micros 168.56 millis sys time 113.72 millis 811.00 micros 112.91 millis time docker info --format '{{json .ClientInfo.Plugins}}' time docker info --format '{{.ClientInfo.Context}}' default ________________________________________________________ Executed in 334.38 millis fish external usr time 177.23 millis 93.00 micros 177.13 millis sys time 124.90 millis 927.00 micros 123.97 millis docker context use remote-ssh-daemon time docker info --format '{{.ClientInfo.Context}}' remote-ssh-daemon ________________________________________________________ Executed in 1.22 secs fish external usr time 116.93 millis 110.00 micros 116.82 millis sys time 144.36 millis 887.00 micros 143.47 millis And daemon logs: Jul 06 12:42:12 remote-ssh-daemon dockerd[14377]: time="2021-07-06T12:42:12.139529947Z" level=debug msg="Calling HEAD /_ping" Jul 06 12:42:12 remote-ssh-daemon dockerd[14377]: time="2021-07-06T12:42:12.140772052Z" level=debug msg="Calling HEAD /_ping" Jul 06 12:42:12 remote-ssh-daemon dockerd[14377]: time="2021-07-06T12:42:12.163832016Z" level=debug msg="Calling GET /v1.41/info" After: time ./build/docker info --format '{{range .ClientInfo.Plugins}}Plugin: {{.Name}}, {{end}}' Plugin: buildx, Plugin: compose, Plugin: scan, ________________________________________________________ Executed in 139.84 millis fish external usr time 76.53 millis 62.00 micros 76.46 millis sys time 69.25 millis 723.00 micros 68.53 millis time ./build/docker info --format '{{.ClientInfo.Context}}' default ________________________________________________________ Executed in 136.94 millis fish external usr time 74.61 millis 74.00 micros 74.54 millis sys time 65.77 millis 858.00 micros 64.91 millis docker context use remote-ssh-daemon time ./build/docker info --format '{{.ClientInfo.Context}}' remote-ssh-daemon ________________________________________________________ Executed in 1.02 secs fish external usr time 74.25 millis 76.00 micros 74.17 millis sys time 65.09 millis 643.00 micros 64.44 millis And daemon logs: Jul 06 12:42:55 remote-ssh-daemon dockerd[14377]: time="2021-07-06T12:42:55.313654687Z" level=debug msg="Calling HEAD /_ping" Jul 06 12:42:55 remote-ssh-daemon dockerd[14377]: time="2021-07-06T12:42:55.314811624Z" level=debug msg="Calling HEAD /_ping" Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-07-06 08:43:42 -04:00
func TestNeedsServerInfo(t *testing.T) {
tests := []struct {
doc string
template string
expected bool
}{
{
doc: "no template",
template: "",
expected: true,
},
{
doc: "JSON",
template: "json",
expected: true,
},
{
doc: "JSON (all fields)",
template: "{{json .}}",
expected: true,
},
{
doc: "JSON (Server ID)",
template: "{{json .ID}}",
expected: true,
},
{
doc: "ClientInfo",
template: "{{json .ClientInfo}}",
expected: false,
},
{
doc: "JSON ClientInfo",
template: "{{json .ClientInfo}}",
expected: false,
},
{
doc: "JSON (Active context)",
template: "{{json .ClientInfo.Context}}",
expected: false,
},
}
inf := dockerInfo{ClientInfo: &clientInfo{}}
docker info: skip API connection if possible The docker info output contains both "local" and "remote" (daemon-side) information. The API endpoint to collect daemon information (`/info`) is known to be "heavy", and (depending on what information is needed) not needed. This patch checks if the template (`--format`) used requires information from the daemon, and if not, omits making an API request. This will improve performance if (for example), the current "context" is requested from `docker info` or if only plugin information is requested. Before: time docker info --format '{{range .ClientInfo.Plugins}}Plugin: {{.Name}}, {{end}}' Plugin: buildx, Plugin: compose, Plugin: scan, ________________________________________________________ Executed in 301.91 millis fish external usr time 168.64 millis 82.00 micros 168.56 millis sys time 113.72 millis 811.00 micros 112.91 millis time docker info --format '{{json .ClientInfo.Plugins}}' time docker info --format '{{.ClientInfo.Context}}' default ________________________________________________________ Executed in 334.38 millis fish external usr time 177.23 millis 93.00 micros 177.13 millis sys time 124.90 millis 927.00 micros 123.97 millis docker context use remote-ssh-daemon time docker info --format '{{.ClientInfo.Context}}' remote-ssh-daemon ________________________________________________________ Executed in 1.22 secs fish external usr time 116.93 millis 110.00 micros 116.82 millis sys time 144.36 millis 887.00 micros 143.47 millis And daemon logs: Jul 06 12:42:12 remote-ssh-daemon dockerd[14377]: time="2021-07-06T12:42:12.139529947Z" level=debug msg="Calling HEAD /_ping" Jul 06 12:42:12 remote-ssh-daemon dockerd[14377]: time="2021-07-06T12:42:12.140772052Z" level=debug msg="Calling HEAD /_ping" Jul 06 12:42:12 remote-ssh-daemon dockerd[14377]: time="2021-07-06T12:42:12.163832016Z" level=debug msg="Calling GET /v1.41/info" After: time ./build/docker info --format '{{range .ClientInfo.Plugins}}Plugin: {{.Name}}, {{end}}' Plugin: buildx, Plugin: compose, Plugin: scan, ________________________________________________________ Executed in 139.84 millis fish external usr time 76.53 millis 62.00 micros 76.46 millis sys time 69.25 millis 723.00 micros 68.53 millis time ./build/docker info --format '{{.ClientInfo.Context}}' default ________________________________________________________ Executed in 136.94 millis fish external usr time 74.61 millis 74.00 micros 74.54 millis sys time 65.77 millis 858.00 micros 64.91 millis docker context use remote-ssh-daemon time ./build/docker info --format '{{.ClientInfo.Context}}' remote-ssh-daemon ________________________________________________________ Executed in 1.02 secs fish external usr time 74.25 millis 76.00 micros 74.17 millis sys time 65.09 millis 643.00 micros 64.44 millis And daemon logs: Jul 06 12:42:55 remote-ssh-daemon dockerd[14377]: time="2021-07-06T12:42:55.313654687Z" level=debug msg="Calling HEAD /_ping" Jul 06 12:42:55 remote-ssh-daemon dockerd[14377]: time="2021-07-06T12:42:55.314811624Z" level=debug msg="Calling HEAD /_ping" Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-07-06 08:43:42 -04:00
for _, tc := range tests {
tc := tc
t.Run(tc.doc, func(t *testing.T) {
assert.Equal(t, needsServerInfo(tc.template, inf), tc.expected)
})
}
}