2016-09-08 13:11:39 -04:00
|
|
|
package system
|
|
|
|
|
|
|
|
import (
|
2018-05-03 21:02:44 -04:00
|
|
|
"context"
|
2016-09-08 13:11:39 -04:00
|
|
|
"fmt"
|
2017-05-10 16:38:06 -04:00
|
|
|
"io"
|
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
|
|
|
"regexp"
|
2016-11-03 14:23:58 -04:00
|
|
|
"sort"
|
2016-09-08 13:11:39 -04:00
|
|
|
"strings"
|
|
|
|
|
2017-04-17 18:07:56 -04:00
|
|
|
"github.com/docker/cli/cli"
|
2018-12-19 09:49:20 -05:00
|
|
|
pluginmanager "github.com/docker/cli/cli-plugins/manager"
|
2017-04-17 18:07:56 -04:00
|
|
|
"github.com/docker/cli/cli/command"
|
2022-03-30 09:27:25 -04:00
|
|
|
"github.com/docker/cli/cli/command/completion"
|
2023-04-09 07:11:19 -04:00
|
|
|
"github.com/docker/cli/cli/command/formatter"
|
2017-04-17 18:07:56 -04:00
|
|
|
"github.com/docker/cli/cli/debug"
|
2023-04-09 07:11:19 -04:00
|
|
|
flagsHelper "github.com/docker/cli/cli/flags"
|
2017-08-08 11:26:24 -04:00
|
|
|
"github.com/docker/cli/templates"
|
2016-09-08 13:11:39 -04:00
|
|
|
"github.com/docker/docker/api/types/swarm"
|
2023-07-14 17:42:40 -04:00
|
|
|
"github.com/docker/docker/api/types/system"
|
2021-08-03 10:46:18 -04:00
|
|
|
"github.com/docker/docker/api/types/versions"
|
2023-04-13 17:10:53 -04:00
|
|
|
"github.com/docker/docker/registry"
|
2016-09-08 13:11:39 -04:00
|
|
|
"github.com/docker/go-units"
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
)
|
|
|
|
|
|
|
|
type infoOptions struct {
|
|
|
|
format string
|
|
|
|
}
|
|
|
|
|
2019-01-23 08:34:43 -05:00
|
|
|
type clientInfo struct {
|
2023-04-10 09:25:31 -04:00
|
|
|
Debug bool
|
|
|
|
clientVersion
|
2018-12-19 09:49:20 -05:00
|
|
|
Plugins []pluginmanager.Plugin
|
2019-01-23 08:34:43 -05:00
|
|
|
Warnings []string
|
|
|
|
}
|
|
|
|
|
2023-11-20 07:04:09 -05:00
|
|
|
type dockerInfo struct {
|
2019-01-23 08:34:43 -05:00
|
|
|
// This field should/could be ServerInfo but is anonymous to
|
|
|
|
// preserve backwards compatibility in the JSON rendering
|
|
|
|
// which has ServerInfo immediately within the top-level
|
|
|
|
// object.
|
2023-07-14 17:42:40 -04:00
|
|
|
*system.Info `json:",omitempty"`
|
2019-01-23 08:34:43 -05:00
|
|
|
ServerErrors []string `json:",omitempty"`
|
2023-04-13 18:25:08 -04:00
|
|
|
UserName string `json:"-"`
|
2019-01-23 08:34:43 -05:00
|
|
|
|
|
|
|
ClientInfo *clientInfo `json:",omitempty"`
|
|
|
|
ClientErrors []string `json:",omitempty"`
|
|
|
|
}
|
|
|
|
|
2023-11-20 07:04:09 -05:00
|
|
|
func (i *dockerInfo) clientPlatform() string {
|
2023-04-10 09:25:31 -04:00
|
|
|
if i.ClientInfo != nil && i.ClientInfo.Platform != nil {
|
|
|
|
return i.ClientInfo.Platform.Name
|
|
|
|
}
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
2016-09-08 13:11:39 -04:00
|
|
|
// NewInfoCommand creates a new cobra.Command for `docker info`
|
2017-10-11 12:18:27 -04:00
|
|
|
func NewInfoCommand(dockerCli command.Cli) *cobra.Command {
|
2016-09-08 13:11:39 -04:00
|
|
|
var opts infoOptions
|
|
|
|
|
|
|
|
cmd := &cobra.Command{
|
|
|
|
Use: "info [OPTIONS]",
|
|
|
|
Short: "Display system-wide information",
|
|
|
|
Args: cli.NoArgs,
|
|
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
2018-12-19 09:49:20 -05:00
|
|
|
return runInfo(cmd, dockerCli, &opts)
|
2016-09-08 13:11:39 -04:00
|
|
|
},
|
2022-03-30 03:37:08 -04:00
|
|
|
Annotations: map[string]string{
|
|
|
|
"category-top": "12",
|
cli: use custom annotation for aliases
Cobra allows for aliases to be defined for a command, but only allows these
to be defined at the same level (for example, `docker image ls` as alias for
`docker image list`). Our CLI has some commands that are available both as a
top-level shorthand as well as `docker <object> <verb>` subcommands. For example,
`docker ps` is a shorthand for `docker container ps` / `docker container ls`.
This patch introduces a custom "aliases" annotation that can be used to print
all available aliases for a command. While this requires these aliases to be
defined manually, in practice the list of aliases rarely changes, so maintenance
should be minimal.
As a convention, we could consider the first command in this list to be the
canonical command, so that we can use this information to add redirects in
our documentation in future.
Before this patch:
docker images --help
Usage: docker images [OPTIONS] [REPOSITORY[:TAG]]
List images
Options:
-a, --all Show all images (default hides intermediate images)
...
With this patch:
docker images --help
Usage: docker images [OPTIONS] [REPOSITORY[:TAG]]
List images
Aliases:
docker image ls, docker image list, docker images
Options:
-a, --all Show all images (default hides intermediate images)
...
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2022-06-28 04:52:25 -04:00
|
|
|
"aliases": "docker system info, docker info",
|
2022-03-30 03:37:08 -04:00
|
|
|
},
|
2022-03-30 09:27:25 -04:00
|
|
|
ValidArgsFunction: completion.NoComplete,
|
2016-09-08 13:11:39 -04:00
|
|
|
}
|
|
|
|
|
2023-04-09 07:11:19 -04:00
|
|
|
cmd.Flags().StringVarP(&opts.format, "format", "f", "", flagsHelper.InspectFormatHelp)
|
2016-09-08 13:11:39 -04:00
|
|
|
return cmd
|
|
|
|
}
|
|
|
|
|
2018-12-19 09:49:20 -05:00
|
|
|
func runInfo(cmd *cobra.Command, dockerCli command.Cli, opts *infoOptions) error {
|
2023-11-20 07:04:09 -05:00
|
|
|
info := dockerInfo{
|
2022-05-06 10:15:20 -04:00
|
|
|
ClientInfo: &clientInfo{
|
2023-04-10 09:25:31 -04:00
|
|
|
// Don't pass a dockerCLI to newClientVersion(), because we currently
|
|
|
|
// don't include negotiated API version, and want to avoid making an
|
|
|
|
// API connection when only printing the Client section.
|
|
|
|
clientVersion: newClientVersion(dockerCli.CurrentContext(), nil),
|
|
|
|
Debug: debug.IsEnabled(),
|
2022-05-06 10:15:20 -04:00
|
|
|
},
|
2023-07-14 17:42:40 -04:00
|
|
|
Info: &system.Info{},
|
2019-01-23 08:34:43 -05:00
|
|
|
}
|
2018-12-19 09:49:20 -05:00
|
|
|
if plugins, err := pluginmanager.ListPlugins(dockerCli, cmd.Root()); err == nil {
|
|
|
|
info.ClientInfo.Plugins = plugins
|
|
|
|
} else {
|
|
|
|
info.ClientErrors = append(info.ClientErrors, err.Error())
|
|
|
|
}
|
2019-01-23 08:34:43 -05:00
|
|
|
|
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
|
|
|
if needsServerInfo(opts.format, info) {
|
|
|
|
ctx := context.Background()
|
|
|
|
if dinfo, err := dockerCli.Client().Info(ctx); err == nil {
|
|
|
|
info.Info = &dinfo
|
|
|
|
} else {
|
|
|
|
info.ServerErrors = append(info.ServerErrors, err.Error())
|
2022-05-10 06:13:03 -04:00
|
|
|
if opts.format == "" {
|
|
|
|
// reset the server info to prevent printing "empty" Server info
|
|
|
|
// and warnings, but don't reset it if a custom format was specified
|
|
|
|
// to prevent errors from Go's template parsing during format.
|
|
|
|
info.Info = nil
|
|
|
|
} else {
|
|
|
|
// if a format is provided, print the error, as it may be hidden
|
|
|
|
// otherwise if the template doesn't include the ServerErrors field.
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(dockerCli.Err(), err)
|
2022-05-10 06:13:03 -04:00
|
|
|
}
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-08 13:11:39 -04:00
|
|
|
if opts.format == "" {
|
2023-04-13 18:25:08 -04:00
|
|
|
info.UserName = dockerCli.ConfigFile().AuthConfigs[registry.IndexServer].Username
|
2023-04-13 18:49:49 -04:00
|
|
|
info.ClientInfo.APIVersion = dockerCli.CurrentVersion()
|
2016-09-08 13:11:39 -04:00
|
|
|
return prettyPrintInfo(dockerCli, info)
|
|
|
|
}
|
2023-04-10 10:48:22 -04:00
|
|
|
return formatInfo(dockerCli.Out(), info, opts.format)
|
2016-09-08 13:11:39 -04:00
|
|
|
}
|
|
|
|
|
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
|
|
|
// placeHolders does a rudimentary match for possible placeholders in a
|
|
|
|
// template, matching a '.', followed by an letter (a-z/A-Z).
|
|
|
|
var placeHolders = regexp.MustCompile(`\.[a-zA-Z]`)
|
|
|
|
|
|
|
|
// needsServerInfo detects if the given template uses any server information.
|
|
|
|
// If only client-side information is used in the template, we can skip
|
|
|
|
// connecting to the daemon. This allows (e.g.) to only get cli-plugin
|
|
|
|
// information, without also making a (potentially expensive) API call.
|
2023-11-20 07:04:09 -05:00
|
|
|
func needsServerInfo(template string, info dockerInfo) bool {
|
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
|
|
|
if len(template) == 0 || placeHolders.FindString(template) == "" {
|
|
|
|
// The template is empty, or does not contain formatting fields
|
|
|
|
// (e.g. `table` or `raw` or `{{ json .}}`). Assume we need server-side
|
|
|
|
// information to render it.
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
// A template is provided and has at least one field set.
|
|
|
|
tmpl, err := templates.NewParse("", template)
|
|
|
|
if err != nil {
|
|
|
|
// ignore parsing errors here, and let regular code handle them
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
type sparseInfo struct {
|
|
|
|
ClientInfo *clientInfo `json:",omitempty"`
|
|
|
|
ClientErrors []string `json:",omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// This constructs an "info" object that only has the client-side fields.
|
2022-02-25 08:32:43 -05:00
|
|
|
err = tmpl.Execute(io.Discard, sparseInfo{
|
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
|
|
|
ClientInfo: info.ClientInfo,
|
|
|
|
ClientErrors: info.ClientErrors,
|
|
|
|
})
|
|
|
|
// If executing the template failed, it means the template needs
|
|
|
|
// server-side information as well. If it succeeded without server-side
|
|
|
|
// information, we don't need to make API calls to collect that information.
|
|
|
|
return err != nil
|
|
|
|
}
|
|
|
|
|
2023-11-20 07:04:09 -05:00
|
|
|
func prettyPrintInfo(streams command.Streams, info dockerInfo) error {
|
2023-04-10 09:25:31 -04:00
|
|
|
// Only append the platform info if it's not empty, to prevent printing a trailing space.
|
|
|
|
if p := info.clientPlatform(); p != "" {
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(streams.Out(), "Client:", p)
|
2023-04-10 09:25:31 -04:00
|
|
|
} else {
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(streams.Out(), "Client:")
|
2023-04-10 09:25:31 -04:00
|
|
|
}
|
2019-01-23 08:34:43 -05:00
|
|
|
if info.ClientInfo != nil {
|
2023-05-02 10:54:05 -04:00
|
|
|
prettyPrintClientInfo(streams, *info.ClientInfo)
|
2019-01-23 08:34:43 -05:00
|
|
|
}
|
|
|
|
for _, err := range info.ClientErrors {
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(streams.Err(), "ERROR:", err)
|
2019-01-23 08:34:43 -05:00
|
|
|
}
|
|
|
|
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(streams.Out())
|
|
|
|
fprintln(streams.Out(), "Server:")
|
2019-01-23 08:34:43 -05:00
|
|
|
if info.Info != nil {
|
2023-05-02 10:54:05 -04:00
|
|
|
for _, err := range prettyPrintServerInfo(streams, &info) {
|
2019-01-23 08:34:43 -05:00
|
|
|
info.ServerErrors = append(info.ServerErrors, err.Error())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for _, err := range info.ServerErrors {
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(streams.Err(), "ERROR:", err)
|
2019-01-23 08:34:43 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if len(info.ServerErrors) > 0 || len(info.ClientErrors) > 0 {
|
|
|
|
return fmt.Errorf("errors pretty printing info")
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-05-02 10:38:01 -04:00
|
|
|
func prettyPrintClientInfo(streams command.Streams, info clientInfo) {
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintlnNonEmpty(streams.Out(), " Version: ", info.Version)
|
|
|
|
fprintln(streams.Out(), " Context: ", info.Context)
|
|
|
|
fprintln(streams.Out(), " Debug Mode:", info.Debug)
|
2019-01-23 08:34:43 -05:00
|
|
|
|
2018-12-19 09:49:20 -05:00
|
|
|
if len(info.Plugins) > 0 {
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(streams.Out(), " Plugins:")
|
2018-12-19 09:49:20 -05:00
|
|
|
for _, p := range info.Plugins {
|
|
|
|
if p.Err == nil {
|
2023-04-10 10:48:22 -04:00
|
|
|
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)
|
2018-12-19 09:49:20 -05:00
|
|
|
} else {
|
|
|
|
info.Warnings = append(info.Warnings, fmt.Sprintf("WARNING: Plugin %q is not valid: %s", p.Path, p.Err))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-23 08:34:43 -05:00
|
|
|
if len(info.Warnings) > 0 {
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(streams.Err(), strings.Join(info.Warnings, "\n"))
|
2019-01-23 08:34:43 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-13 06:29:49 -04:00
|
|
|
//nolint:gocyclo
|
2023-11-20 07:04:09 -05:00
|
|
|
func prettyPrintServerInfo(streams command.Streams, info *dockerInfo) []error {
|
2019-01-24 09:22:55 -05:00
|
|
|
var errs []error
|
2023-05-02 10:40:54 -04:00
|
|
|
output := streams.Out()
|
2019-01-24 09:22:55 -05:00
|
|
|
|
2023-04-10 10:48:22 -04:00
|
|
|
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)
|
2023-05-02 10:40:54 -04:00
|
|
|
fprintlnNonEmpty(output, " Server Version:", info.ServerVersion)
|
|
|
|
fprintlnNonEmpty(output, " Storage Driver:", info.Driver)
|
2016-09-08 13:11:39 -04:00
|
|
|
if info.DriverStatus != nil {
|
|
|
|
for _, pair := range info.DriverStatus {
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintf(output, " %s: %s\n", pair[0], pair[1])
|
2016-09-08 13:11:39 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if info.SystemStatus != nil {
|
|
|
|
for _, pair := range info.SystemStatus {
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintf(output, " %s: %s\n", pair[0], pair[1])
|
2016-09-08 13:11:39 -04:00
|
|
|
}
|
|
|
|
}
|
2023-05-02 10:40:54 -04:00
|
|
|
fprintlnNonEmpty(output, " Logging Driver:", info.LoggingDriver)
|
|
|
|
fprintlnNonEmpty(output, " Cgroup Driver:", info.CgroupDriver)
|
|
|
|
fprintlnNonEmpty(output, " Cgroup Version:", info.CgroupVersion)
|
2016-09-08 13:11:39 -04:00
|
|
|
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(output, " Plugins:")
|
|
|
|
fprintln(output, " Volume:", strings.Join(info.Plugins.Volume, " "))
|
|
|
|
fprintln(output, " Network:", strings.Join(info.Plugins.Network, " "))
|
2016-09-08 13:11:39 -04:00
|
|
|
|
|
|
|
if len(info.Plugins.Authorization) != 0 {
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(output, " Authorization:", strings.Join(info.Plugins.Authorization, " "))
|
2016-09-08 13:11:39 -04:00
|
|
|
}
|
|
|
|
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(output, " Log:", strings.Join(info.Plugins.Log, " "))
|
2017-04-11 17:21:21 -04:00
|
|
|
|
2023-08-22 08:05:07 -04:00
|
|
|
if len(info.CDISpecDirs) > 0 {
|
|
|
|
fprintln(output, " CDI spec directories:")
|
|
|
|
for _, dir := range info.CDISpecDirs {
|
|
|
|
fprintf(output, " %s\n", dir)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(output, " Swarm:", info.Swarm.LocalNodeState)
|
2023-05-02 10:40:54 -04:00
|
|
|
printSwarmInfo(output, *info.Info)
|
2016-09-08 13:11:39 -04:00
|
|
|
|
|
|
|
if len(info.Runtimes) > 0 {
|
2023-05-02 10:05:52 -04:00
|
|
|
names := make([]string, 0, len(info.Runtimes))
|
2016-09-08 13:11:39 -04:00
|
|
|
for name := range info.Runtimes {
|
2023-05-02 10:05:52 -04:00
|
|
|
names = append(names, name)
|
2016-09-08 13:11:39 -04:00
|
|
|
}
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(output, " Runtimes:", strings.Join(names, " "))
|
|
|
|
fprintln(output, " Default Runtime:", info.DefaultRuntime)
|
2016-09-08 13:11:39 -04:00
|
|
|
}
|
|
|
|
|
2016-09-07 14:14:49 -04:00
|
|
|
if info.OSType == "linux" {
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(output, " Init Binary:", info.InitBinary)
|
2016-10-24 18:18:58 -04:00
|
|
|
|
|
|
|
for _, ci := range []struct {
|
|
|
|
Name string
|
2023-07-14 17:42:40 -04:00
|
|
|
Commit system.Commit
|
2016-10-24 18:18:58 -04:00
|
|
|
}{
|
|
|
|
{"containerd", info.ContainerdCommit},
|
|
|
|
{"runc", info.RuncCommit},
|
|
|
|
{"init", info.InitCommit},
|
|
|
|
} {
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintf(output, " %s version: %s", ci.Name, ci.Commit.ID)
|
2016-10-24 18:18:58 -04:00
|
|
|
if ci.Commit.ID != ci.Commit.Expected {
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintf(output, " (expected: %s)", ci.Commit.Expected)
|
2016-10-24 18:18:58 -04:00
|
|
|
}
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(output)
|
2016-10-24 18:18:58 -04:00
|
|
|
}
|
2016-09-02 09:20:54 -04:00
|
|
|
if len(info.SecurityOptions) != 0 {
|
2023-07-14 17:42:40 -04:00
|
|
|
if kvs, err := system.DecodeSecurityOptions(info.SecurityOptions); err != nil {
|
2019-01-24 09:22:55 -05:00
|
|
|
errs = append(errs, err)
|
|
|
|
} else {
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(output, " Security Options:")
|
2019-01-24 09:22:55 -05:00
|
|
|
for _, so := range kvs {
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(output, " "+so.Name)
|
2019-01-24 09:22:55 -05:00
|
|
|
for _, o := range so.Options {
|
2023-11-20 07:54:53 -05:00
|
|
|
if o.Key == "profile" {
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(output, " Profile:", o.Value)
|
2016-11-16 16:30:29 -05:00
|
|
|
}
|
2016-09-02 09:20:54 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-09-07 14:14:49 -04:00
|
|
|
}
|
2016-09-08 13:11:39 -04:00
|
|
|
|
2016-09-07 18:10:00 -04:00
|
|
|
// Isolation only has meaning on a Windows daemon.
|
|
|
|
if info.OSType == "windows" {
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(output, " Default Isolation:", info.Isolation)
|
2016-09-07 18:10:00 -04:00
|
|
|
}
|
|
|
|
|
2023-05-02 10:40:54 -04:00
|
|
|
fprintlnNonEmpty(output, " Kernel Version:", info.KernelVersion)
|
|
|
|
fprintlnNonEmpty(output, " Operating System:", info.OperatingSystem)
|
|
|
|
fprintlnNonEmpty(output, " OSType:", info.OSType)
|
|
|
|
fprintlnNonEmpty(output, " Architecture:", info.Architecture)
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(output, " CPUs:", info.NCPU)
|
|
|
|
fprintln(output, " Total Memory:", units.BytesSize(float64(info.MemTotal)))
|
2023-05-02 10:40:54 -04:00
|
|
|
fprintlnNonEmpty(output, " Name:", info.Name)
|
|
|
|
fprintlnNonEmpty(output, " ID:", info.ID)
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(output, " Docker Root Dir:", info.DockerRootDir)
|
|
|
|
fprintln(output, " Debug Mode:", info.Debug)
|
2016-09-08 13:11:39 -04:00
|
|
|
|
2023-06-30 08:58:40 -04:00
|
|
|
// The daemon collects this information regardless if "debug" is
|
|
|
|
// enabled. Print the debugging information if either the daemon,
|
|
|
|
// or the client has debug enabled. We should probably improve this
|
|
|
|
// logic and print any of these if set (but some special rules are
|
|
|
|
// needed for file-descriptors, which may use "-1".
|
|
|
|
if info.Debug || debug.IsEnabled() {
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(output, " File Descriptors:", info.NFd)
|
|
|
|
fprintln(output, " Goroutines:", info.NGoroutines)
|
|
|
|
fprintln(output, " System Time:", info.SystemTime)
|
|
|
|
fprintln(output, " EventsListeners:", info.NEventsListener)
|
2016-09-08 13:11:39 -04:00
|
|
|
}
|
|
|
|
|
2023-05-02 10:40:54 -04:00
|
|
|
fprintlnNonEmpty(output, " HTTP Proxy:", info.HTTPProxy)
|
|
|
|
fprintlnNonEmpty(output, " HTTPS Proxy:", info.HTTPSProxy)
|
|
|
|
fprintlnNonEmpty(output, " No Proxy:", info.NoProxy)
|
|
|
|
fprintlnNonEmpty(output, " Username:", info.UserName)
|
2022-06-05 08:44:08 -04:00
|
|
|
if len(info.Labels) > 0 {
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(output, " Labels:")
|
2017-08-06 17:23:15 -04:00
|
|
|
for _, lbl := range info.Labels {
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(output, " "+lbl)
|
2016-09-08 13:11:39 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(output, " Experimental:", info.ExperimentalBuild)
|
2016-09-08 13:11:39 -04:00
|
|
|
|
|
|
|
if info.RegistryConfig != nil && (len(info.RegistryConfig.InsecureRegistryCIDRs) > 0 || len(info.RegistryConfig.IndexConfigs) > 0) {
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(output, " Insecure Registries:")
|
|
|
|
for _, registryConfig := range info.RegistryConfig.IndexConfigs {
|
|
|
|
if !registryConfig.Secure {
|
|
|
|
fprintln(output, " "+registryConfig.Name)
|
2016-09-08 13:11:39 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-10 10:48:22 -04:00
|
|
|
for _, registryConfig := range info.RegistryConfig.InsecureRegistryCIDRs {
|
|
|
|
mask, _ := registryConfig.Mask.Size()
|
|
|
|
fprintf(output, " %s/%d\n", registryConfig.IP.String(), mask)
|
2016-09-08 13:11:39 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if info.RegistryConfig != nil && len(info.RegistryConfig.Mirrors) > 0 {
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(output, " Registry Mirrors:")
|
2016-09-08 13:11:39 -04:00
|
|
|
for _, mirror := range info.RegistryConfig.Mirrors {
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(output, " "+mirror)
|
2016-09-08 13:11:39 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(output, " Live Restore Enabled:", info.LiveRestoreEnabled)
|
2018-08-27 18:33:16 -04:00
|
|
|
if info.ProductLicense != "" {
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(output, " Product License:", info.ProductLicense)
|
2018-08-27 18:33:16 -04:00
|
|
|
}
|
2020-03-18 11:58:23 -04:00
|
|
|
|
|
|
|
if info.DefaultAddressPools != nil && len(info.DefaultAddressPools) > 0 {
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(output, " Default Address Pools:")
|
2020-03-18 11:58:23 -04:00
|
|
|
for _, pool := range info.DefaultAddressPools {
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintf(output, " Base: %s, Size: %d\n", pool.Base, pool.Size)
|
2020-03-18 11:58:23 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(output)
|
2023-05-02 10:40:54 -04:00
|
|
|
printServerWarnings(streams.Err(), info)
|
2019-01-24 09:22:55 -05:00
|
|
|
return errs
|
2016-09-08 13:11:39 -04:00
|
|
|
}
|
|
|
|
|
2022-07-13 06:29:49 -04:00
|
|
|
//nolint:gocyclo
|
2023-07-14 17:42:40 -04:00
|
|
|
func printSwarmInfo(output io.Writer, info system.Info) {
|
2017-08-06 17:23:15 -04:00
|
|
|
if info.Swarm.LocalNodeState == swarm.LocalNodeStateInactive || info.Swarm.LocalNodeState == swarm.LocalNodeStateLocked {
|
|
|
|
return
|
|
|
|
}
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(output, " NodeID:", info.Swarm.NodeID)
|
2017-08-06 17:23:15 -04:00
|
|
|
if info.Swarm.Error != "" {
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(output, " Error:", info.Swarm.Error)
|
2017-08-06 17:23:15 -04:00
|
|
|
}
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(output, " Is Manager:", info.Swarm.ControlAvailable)
|
2017-08-06 17:23:15 -04:00
|
|
|
if info.Swarm.Cluster != nil && info.Swarm.ControlAvailable && info.Swarm.Error == "" && info.Swarm.LocalNodeState != swarm.LocalNodeStateError {
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(output, " ClusterID:", info.Swarm.Cluster.ID)
|
|
|
|
fprintln(output, " Managers:", info.Swarm.Managers)
|
|
|
|
fprintln(output, " Nodes:", info.Swarm.Nodes)
|
2018-07-28 22:44:11 -04:00
|
|
|
var strAddrPool strings.Builder
|
|
|
|
if info.Swarm.Cluster.DefaultAddrPool != nil {
|
|
|
|
for _, p := range info.Swarm.Cluster.DefaultAddrPool {
|
|
|
|
strAddrPool.WriteString(p + " ")
|
|
|
|
}
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(output, " Default Address Pool:", strAddrPool.String())
|
|
|
|
fprintln(output, " SubnetSize:", info.Swarm.Cluster.SubnetSize)
|
2018-07-28 22:44:11 -04:00
|
|
|
}
|
2018-11-26 16:04:39 -05:00
|
|
|
if info.Swarm.Cluster.DataPathPort > 0 {
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(output, " Data Path Port:", info.Swarm.Cluster.DataPathPort)
|
2018-11-26 16:04:39 -05:00
|
|
|
}
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(output, " Orchestration:")
|
2018-07-28 22:44:11 -04:00
|
|
|
|
2017-08-06 17:23:15 -04:00
|
|
|
taskHistoryRetentionLimit := int64(0)
|
|
|
|
if info.Swarm.Cluster.Spec.Orchestration.TaskHistoryRetentionLimit != nil {
|
|
|
|
taskHistoryRetentionLimit = *info.Swarm.Cluster.Spec.Orchestration.TaskHistoryRetentionLimit
|
|
|
|
}
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(output, " Task History Retention Limit:", taskHistoryRetentionLimit)
|
|
|
|
fprintln(output, " Raft:")
|
|
|
|
fprintln(output, " Snapshot Interval:", info.Swarm.Cluster.Spec.Raft.SnapshotInterval)
|
2017-08-06 17:23:15 -04:00
|
|
|
if info.Swarm.Cluster.Spec.Raft.KeepOldSnapshots != nil {
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintf(output, " Number of Old Snapshots to Retain: %d\n", *info.Swarm.Cluster.Spec.Raft.KeepOldSnapshots)
|
2017-08-06 17:23:15 -04:00
|
|
|
}
|
2023-04-10 10:48:22 -04:00
|
|
|
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)
|
2017-08-06 17:23:15 -04:00
|
|
|
if caCert := strings.TrimSpace(info.Swarm.Cluster.Spec.CAConfig.SigningCACert); caCert != "" {
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintf(output, " Signing CA Certificate: \n%s\n\n", caCert)
|
2017-08-06 17:23:15 -04:00
|
|
|
}
|
|
|
|
if len(info.Swarm.Cluster.Spec.CAConfig.ExternalCAs) > 0 {
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(output, " External CAs:")
|
2017-08-06 17:23:15 -04:00
|
|
|
for _, entry := range info.Swarm.Cluster.Spec.CAConfig.ExternalCAs {
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintf(output, " %s: %s\n", entry.Protocol, entry.URL)
|
2017-08-06 17:23:15 -04:00
|
|
|
}
|
|
|
|
}
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(output, " Autolock Managers:", info.Swarm.Cluster.Spec.EncryptionConfig.AutoLockManagers)
|
|
|
|
fprintln(output, " Root Rotation In Progress:", info.Swarm.Cluster.RootRotationInProgress)
|
2017-08-06 17:23:15 -04:00
|
|
|
}
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(output, " Node Address:", info.Swarm.NodeAddr)
|
2017-08-06 17:23:15 -04:00
|
|
|
if len(info.Swarm.RemoteManagers) > 0 {
|
|
|
|
managers := []string{}
|
|
|
|
for _, entry := range info.Swarm.RemoteManagers {
|
|
|
|
managers = append(managers, entry.Addr)
|
|
|
|
}
|
|
|
|
sort.Strings(managers)
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(output, " Manager Addresses:")
|
2017-08-06 17:23:15 -04:00
|
|
|
for _, entry := range managers {
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintf(output, " %s\n", entry)
|
2017-08-06 17:23:15 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-20 07:04:09 -05:00
|
|
|
func printServerWarnings(stdErr io.Writer, info *dockerInfo) {
|
2023-04-13 18:49:49 -04:00
|
|
|
if versions.LessThan(info.ClientInfo.APIVersion, "1.42") {
|
2023-05-02 10:14:23 -04:00
|
|
|
printSecurityOptionsWarnings(stdErr, *info.Info)
|
2021-08-03 10:46:18 -04:00
|
|
|
}
|
2018-07-19 13:45:04 -04:00
|
|
|
if len(info.Warnings) > 0 {
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(stdErr, strings.Join(info.Warnings, "\n"))
|
2018-07-19 13:45:04 -04:00
|
|
|
return
|
|
|
|
}
|
|
|
|
// daemon didn't return warnings. Fallback to old behavior
|
2023-05-02 10:14:23 -04:00
|
|
|
printServerWarningsLegacy(stdErr, *info.Info)
|
2018-07-19 13:45:04 -04:00
|
|
|
}
|
|
|
|
|
2021-08-03 10:46:18 -04:00
|
|
|
// printSecurityOptionsWarnings prints warnings based on the security options
|
|
|
|
// returned by the daemon.
|
2023-11-20 07:10:24 -05:00
|
|
|
//
|
|
|
|
// Deprecated: warnings are now generated by the daemon, and returned in
|
2021-08-03 10:46:18 -04:00
|
|
|
// 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.
|
2023-07-14 17:42:40 -04:00
|
|
|
func printSecurityOptionsWarnings(stdErr io.Writer, info system.Info) {
|
2021-08-03 10:46:18 -04:00
|
|
|
if info.OSType == "windows" {
|
|
|
|
return
|
|
|
|
}
|
2023-07-14 17:42:40 -04:00
|
|
|
kvs, _ := system.DecodeSecurityOptions(info.SecurityOptions)
|
2021-08-03 10:46:18 -04:00
|
|
|
for _, so := range kvs {
|
|
|
|
if so.Name != "seccomp" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
for _, o := range so.Options {
|
|
|
|
if o.Key == "profile" && o.Value != "default" && o.Value != "builtin" {
|
2023-05-02 10:14:23 -04:00
|
|
|
_, _ = fmt.Fprintln(stdErr, "WARNING: You're not using the default seccomp profile")
|
2021-08-03 10:46:18 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-23 08:34:43 -05:00
|
|
|
// printServerWarningsLegacy generates warnings based on information returned by the daemon.
|
2023-11-20 07:10:24 -05:00
|
|
|
//
|
|
|
|
// Deprecated: warnings are now generated by the daemon, and returned in
|
2018-07-19 13:45:04 -04:00
|
|
|
// 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.
|
2023-07-14 17:42:40 -04:00
|
|
|
func printServerWarningsLegacy(stdErr io.Writer, info system.Info) {
|
2018-07-19 13:45:04 -04:00
|
|
|
if info.OSType == "windows" {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if !info.MemoryLimit {
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(stdErr, "WARNING: No memory limit support")
|
2018-07-19 13:45:04 -04:00
|
|
|
}
|
|
|
|
if !info.SwapLimit {
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(stdErr, "WARNING: No swap limit support")
|
2018-07-19 13:45:04 -04:00
|
|
|
}
|
2021-05-04 05:43:18 -04:00
|
|
|
if !info.OomKillDisable && info.CgroupVersion != "2" {
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(stdErr, "WARNING: No oom kill disable support")
|
2018-07-19 13:45:04 -04:00
|
|
|
}
|
|
|
|
if !info.CPUCfsQuota {
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(stdErr, "WARNING: No cpu cfs quota support")
|
2018-07-19 13:45:04 -04:00
|
|
|
}
|
|
|
|
if !info.CPUCfsPeriod {
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(stdErr, "WARNING: No cpu cfs period support")
|
2018-07-19 13:45:04 -04:00
|
|
|
}
|
|
|
|
if !info.CPUShares {
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(stdErr, "WARNING: No cpu shares support")
|
2018-07-19 13:45:04 -04:00
|
|
|
}
|
|
|
|
if !info.CPUSet {
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(stdErr, "WARNING: No cpuset support")
|
2018-07-19 13:45:04 -04:00
|
|
|
}
|
|
|
|
if !info.IPv4Forwarding {
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(stdErr, "WARNING: IPv4 forwarding is disabled")
|
2018-07-19 13:45:04 -04:00
|
|
|
}
|
|
|
|
if !info.BridgeNfIptables {
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(stdErr, "WARNING: bridge-nf-call-iptables is disabled")
|
2018-07-19 13:45:04 -04:00
|
|
|
}
|
|
|
|
if !info.BridgeNfIP6tables {
|
2023-04-10 10:48:22 -04:00
|
|
|
fprintln(stdErr, "WARNING: bridge-nf-call-ip6tables is disabled")
|
2018-07-19 13:45:04 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-20 07:04:09 -05:00
|
|
|
func formatInfo(output io.Writer, info dockerInfo, format string) error {
|
2023-04-09 07:11:19 -04:00
|
|
|
if format == formatter.JSONFormatKey {
|
|
|
|
format = formatter.JSONFormat
|
|
|
|
}
|
|
|
|
|
2018-12-19 09:49:20 -05:00
|
|
|
// Ensure slice/array fields render as `[]` not `null`
|
|
|
|
if info.ClientInfo != nil && info.ClientInfo.Plugins == nil {
|
|
|
|
info.ClientInfo.Plugins = make([]pluginmanager.Plugin, 0)
|
|
|
|
}
|
|
|
|
|
2016-09-08 13:11:39 -04:00
|
|
|
tmpl, err := templates.Parse(format)
|
|
|
|
if err != nil {
|
2022-09-29 11:21:51 -04:00
|
|
|
return cli.StatusError{
|
|
|
|
StatusCode: 64,
|
|
|
|
Status: "template parsing error: " + err.Error(),
|
|
|
|
}
|
2016-09-08 13:11:39 -04:00
|
|
|
}
|
2023-04-10 10:48:22 -04:00
|
|
|
err = tmpl.Execute(output, info)
|
|
|
|
fprintln(output)
|
2016-09-08 13:11:39 -04:00
|
|
|
return err
|
|
|
|
}
|
2017-05-10 16:38:06 -04:00
|
|
|
|
2023-04-10 10:48:22 -04:00
|
|
|
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...)
|
|
|
|
}
|
|
|
|
|
2017-08-06 17:23:15 -04:00
|
|
|
func fprintlnNonEmpty(w io.Writer, label, value string) {
|
2017-05-10 16:38:06 -04:00
|
|
|
if value != "" {
|
2023-04-10 10:48:22 -04:00
|
|
|
_, _ = fmt.Fprintln(w, label, value)
|
2017-05-10 16:38:06 -04:00
|
|
|
}
|
|
|
|
}
|