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
|
|
|
"runtime"
|
2017-12-06 15:35:43 -05:00
|
|
|
"sort"
|
2020-10-02 08:19:34 -04:00
|
|
|
"strconv"
|
2017-12-06 15:35:43 -05:00
|
|
|
"text/template"
|
2016-09-08 13:11:39 -04:00
|
|
|
"time"
|
|
|
|
|
2017-04-17 18:07:56 -04:00
|
|
|
"github.com/docker/cli/cli"
|
|
|
|
"github.com/docker/cli/cli/command"
|
2022-03-30 09:27:25 -04:00
|
|
|
"github.com/docker/cli/cli/command/completion"
|
2023-04-09 20:27:01 -04:00
|
|
|
"github.com/docker/cli/cli/command/formatter"
|
2022-04-25 06:00:55 -04:00
|
|
|
"github.com/docker/cli/cli/command/formatter/tabwriter"
|
2023-04-09 20:27:01 -04:00
|
|
|
flagsHelper "github.com/docker/cli/cli/flags"
|
2019-01-08 10:03:51 -05:00
|
|
|
"github.com/docker/cli/cli/version"
|
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"
|
2018-05-31 04:10:56 -04:00
|
|
|
"github.com/pkg/errors"
|
2016-09-08 13:11:39 -04:00
|
|
|
"github.com/spf13/cobra"
|
2020-07-27 00:31:00 -04:00
|
|
|
"github.com/tonistiigi/go-rosetta"
|
2016-09-08 13:11:39 -04:00
|
|
|
)
|
|
|
|
|
2023-04-09 20:11:17 -04:00
|
|
|
const defaultVersionTemplate = `{{with .Client -}}
|
2023-04-09 20:04:12 -04:00
|
|
|
Client:{{if ne .Platform nil}}{{if ne .Platform.Name ""}} {{.Platform.Name}}{{end}}{{end}}
|
2017-12-01 08:47:20 -05:00
|
|
|
Version: {{.Version}}
|
|
|
|
API version: {{.APIVersion}}{{if ne .APIVersion .DefaultAPIVersion}} (downgraded from {{.DefaultAPIVersion}}){{end}}
|
|
|
|
Go version: {{.GoVersion}}
|
|
|
|
Git commit: {{.GitCommit}}
|
|
|
|
Built: {{.BuildTime}}
|
|
|
|
OS/Arch: {{.Os}}/{{.Arch}}
|
2020-05-07 06:57:20 -04:00
|
|
|
Context: {{.Context}}
|
2017-12-01 08:47:20 -05:00
|
|
|
{{- end}}
|
|
|
|
|
2023-04-09 09:36:12 -04:00
|
|
|
{{- if ne .Server nil}}{{with .Server}}
|
2017-12-01 08:47:20 -05:00
|
|
|
|
|
|
|
Server:{{if ne .Platform.Name ""}} {{.Platform.Name}}{{end}}
|
|
|
|
{{- range $component := .Components}}
|
|
|
|
{{$component.Name}}:
|
|
|
|
{{- if eq $component.Name "Engine" }}
|
|
|
|
Version: {{.Version}}
|
|
|
|
API version: {{index .Details "ApiVersion"}} (minimum version {{index .Details "MinAPIVersion"}})
|
|
|
|
Go version: {{index .Details "GoVersion"}}
|
|
|
|
Git commit: {{index .Details "GitCommit"}}
|
|
|
|
Built: {{index .Details "BuildTime"}}
|
|
|
|
OS/Arch: {{index .Details "Os"}}/{{index .Details "Arch"}}
|
|
|
|
Experimental: {{index .Details "Experimental"}}
|
2017-12-06 15:35:43 -05:00
|
|
|
{{- else }}
|
2017-12-01 08:47:20 -05:00
|
|
|
Version: {{$component.Version}}
|
2017-12-06 15:35:43 -05:00
|
|
|
{{- $detailsOrder := getDetailsOrder $component}}
|
|
|
|
{{- range $key := $detailsOrder}}
|
2018-05-18 08:46:23 -04:00
|
|
|
{{$key}}: {{index $component.Details $key}}
|
2017-12-01 08:47:20 -05:00
|
|
|
{{- end}}
|
|
|
|
{{- end}}
|
|
|
|
{{- end}}
|
2018-03-20 11:42:57 -04:00
|
|
|
{{- end}}{{- end}}`
|
2016-09-08 13:11:39 -04:00
|
|
|
|
|
|
|
type versionOptions struct {
|
2021-06-16 03:08:42 -04:00
|
|
|
format string
|
2016-09-08 13:11:39 -04:00
|
|
|
}
|
|
|
|
|
2017-02-14 19:21:40 -05:00
|
|
|
// versionInfo contains version information of both the Client, and Server
|
|
|
|
type versionInfo struct {
|
2018-03-20 11:42:57 -04:00
|
|
|
Client clientVersion
|
|
|
|
Server *types.Version
|
2017-02-14 19:21:40 -05:00
|
|
|
}
|
|
|
|
|
2023-04-09 20:04:12 -04:00
|
|
|
type platformInfo struct {
|
|
|
|
Name string `json:"Name,omitempty"`
|
|
|
|
}
|
2017-12-01 08:47:20 -05:00
|
|
|
|
2023-04-09 20:04:12 -04:00
|
|
|
type clientVersion struct {
|
|
|
|
Platform *platformInfo `json:"Platform,omitempty"`
|
2023-04-09 20:41:50 -04:00
|
|
|
Version string `json:"Version,omitempty"`
|
|
|
|
APIVersion string `json:"ApiVersion,omitempty"`
|
|
|
|
DefaultAPIVersion string `json:"DefaultAPIVersion,omitempty"`
|
|
|
|
GitCommit string `json:"GitCommit,omitempty"`
|
|
|
|
GoVersion string `json:"GoVersion,omitempty"`
|
|
|
|
Os string `json:"Os,omitempty"`
|
|
|
|
Arch string `json:"Arch,omitempty"`
|
|
|
|
BuildTime string `json:"BuildTime,omitempty"`
|
|
|
|
Context string `json:"Context"`
|
2017-02-14 19:21:40 -05:00
|
|
|
}
|
|
|
|
|
2023-04-09 20:44:04 -04:00
|
|
|
// newClientVersion constructs a new clientVersion. If a dockerCLI is
|
|
|
|
// passed as argument, additional information is included (API version),
|
|
|
|
// which may invoke an API connection. Pass nil to omit the additional
|
|
|
|
// information.
|
|
|
|
func newClientVersion(contextName string, dockerCli command.Cli) clientVersion {
|
|
|
|
v := clientVersion{
|
|
|
|
Version: version.Version,
|
|
|
|
GoVersion: runtime.Version(),
|
|
|
|
GitCommit: version.GitCommit,
|
|
|
|
BuildTime: reformatDate(version.BuildTime),
|
|
|
|
Os: runtime.GOOS,
|
|
|
|
Arch: arch(),
|
|
|
|
Context: contextName,
|
|
|
|
}
|
|
|
|
if version.PlatformName != "" {
|
|
|
|
v.Platform = &platformInfo{Name: version.PlatformName}
|
|
|
|
}
|
|
|
|
if dockerCli != nil {
|
|
|
|
v.APIVersion = dockerCli.CurrentVersion()
|
|
|
|
v.DefaultAPIVersion = dockerCli.DefaultVersion()
|
|
|
|
}
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
2016-09-08 13:11:39 -04:00
|
|
|
// NewVersionCommand creates a new cobra.Command for `docker version`
|
2017-12-05 09:41:03 -05:00
|
|
|
func NewVersionCommand(dockerCli command.Cli) *cobra.Command {
|
2016-09-08 13:11:39 -04:00
|
|
|
var opts versionOptions
|
|
|
|
|
|
|
|
cmd := &cobra.Command{
|
|
|
|
Use: "version [OPTIONS]",
|
|
|
|
Short: "Show the Docker version information",
|
|
|
|
Args: cli.NoArgs,
|
|
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
2023-09-09 18:27:44 -04:00
|
|
|
return runVersion(cmd.Context(), dockerCli, &opts)
|
2016-09-08 13:11:39 -04:00
|
|
|
},
|
2022-03-30 03:37:08 -04:00
|
|
|
Annotations: map[string]string{
|
|
|
|
"category-top": "10",
|
|
|
|
},
|
2022-03-30 09:27:25 -04:00
|
|
|
ValidArgsFunction: completion.NoComplete,
|
2016-09-08 13:11:39 -04:00
|
|
|
}
|
|
|
|
|
2023-04-09 20:27:01 -04:00
|
|
|
cmd.Flags().StringVarP(&opts.format, "format", "f", "", flagsHelper.InspectFormatHelp)
|
2016-09-08 13:11:39 -04:00
|
|
|
return cmd
|
|
|
|
}
|
|
|
|
|
2017-12-01 08:47:20 -05:00
|
|
|
func reformatDate(buildTime string) string {
|
|
|
|
t, errTime := time.Parse(time.RFC3339Nano, buildTime)
|
|
|
|
if errTime == nil {
|
|
|
|
return t.Format(time.ANSIC)
|
|
|
|
}
|
|
|
|
return buildTime
|
|
|
|
}
|
|
|
|
|
2020-07-27 00:31:00 -04:00
|
|
|
func arch() string {
|
|
|
|
arch := runtime.GOARCH
|
|
|
|
if rosetta.Enabled() {
|
|
|
|
arch += " (rosetta)"
|
|
|
|
}
|
|
|
|
return arch
|
|
|
|
}
|
|
|
|
|
2023-09-09 18:27:44 -04:00
|
|
|
func runVersion(ctx context.Context, dockerCli command.Cli, opts *versionOptions) error {
|
2017-12-06 15:35:43 -05:00
|
|
|
var err error
|
2018-05-31 04:10:56 -04:00
|
|
|
tmpl, err := newVersionTemplate(opts.format)
|
2016-09-08 13:11:39 -04:00
|
|
|
if err != nil {
|
2018-05-31 04:10:56 -04:00
|
|
|
return cli.StatusError{StatusCode: 64, Status: err.Error()}
|
2016-09-08 13:11:39 -04:00
|
|
|
}
|
|
|
|
|
2021-06-16 03:08:42 -04:00
|
|
|
// TODO print error if kubernetes is used?
|
2018-06-20 08:48:50 -04:00
|
|
|
|
2017-02-14 19:21:40 -05:00
|
|
|
vd := versionInfo{
|
2023-04-09 20:44:04 -04:00
|
|
|
Client: newClientVersion(dockerCli.CurrentContext(), dockerCli),
|
2016-09-08 13:11:39 -04:00
|
|
|
}
|
2023-09-09 18:27:44 -04:00
|
|
|
sv, err := dockerCli.Client().ServerVersion(ctx)
|
2017-12-01 08:47:20 -05:00
|
|
|
if err == nil {
|
|
|
|
vd.Server = &sv
|
|
|
|
foundEngine := false
|
|
|
|
for _, component := range sv.Components {
|
2023-11-20 07:54:53 -05:00
|
|
|
if component.Name == "Engine" {
|
2017-12-01 08:47:20 -05:00
|
|
|
foundEngine = true
|
|
|
|
buildTime, ok := component.Details["BuildTime"]
|
|
|
|
if ok {
|
|
|
|
component.Details["BuildTime"] = reformatDate(buildTime)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-09-08 13:11:39 -04:00
|
|
|
|
2017-12-01 08:47:20 -05:00
|
|
|
if !foundEngine {
|
|
|
|
vd.Server.Components = append(vd.Server.Components, types.ComponentVersion{
|
|
|
|
Name: "Engine",
|
|
|
|
Version: sv.Version,
|
|
|
|
Details: map[string]string{
|
|
|
|
"ApiVersion": sv.APIVersion,
|
|
|
|
"MinAPIVersion": sv.MinAPIVersion,
|
|
|
|
"GitCommit": sv.GitCommit,
|
|
|
|
"GoVersion": sv.GoVersion,
|
|
|
|
"Os": sv.Os,
|
|
|
|
"Arch": sv.Arch,
|
|
|
|
"BuildTime": reformatDate(vd.Server.BuildTime),
|
2020-10-02 08:19:34 -04:00
|
|
|
"Experimental": strconv.FormatBool(sv.Experimental),
|
2017-12-01 08:47:20 -05:00
|
|
|
},
|
|
|
|
})
|
2016-09-08 13:11:39 -04:00
|
|
|
}
|
|
|
|
}
|
2018-05-31 04:10:56 -04:00
|
|
|
if err2 := prettyPrintVersion(dockerCli, vd, tmpl); err2 != nil && err == nil {
|
2016-09-08 13:11:39 -04:00
|
|
|
err = err2
|
|
|
|
}
|
2018-05-31 04:10:56 -04:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
func prettyPrintVersion(dockerCli command.Cli, vd versionInfo, tmpl *template.Template) error {
|
2018-07-11 19:34:34 -04:00
|
|
|
t := tabwriter.NewWriter(dockerCli.Out(), 20, 1, 1, ' ', 0)
|
2018-05-31 04:10:56 -04:00
|
|
|
err := tmpl.Execute(t, vd)
|
2018-03-26 10:09:52 -04:00
|
|
|
t.Write([]byte("\n"))
|
|
|
|
t.Flush()
|
2016-09-08 13:11:39 -04:00
|
|
|
return err
|
|
|
|
}
|
2017-12-06 15:35:43 -05:00
|
|
|
|
2018-05-31 04:10:56 -04:00
|
|
|
func newVersionTemplate(templateFormat string) (*template.Template, error) {
|
2023-04-09 20:27:01 -04:00
|
|
|
switch templateFormat {
|
|
|
|
case "":
|
2023-04-09 20:11:17 -04:00
|
|
|
templateFormat = defaultVersionTemplate
|
2023-04-09 20:27:01 -04:00
|
|
|
case formatter.JSONFormatKey:
|
|
|
|
templateFormat = formatter.JSONFormat
|
2018-05-31 04:10:56 -04:00
|
|
|
}
|
|
|
|
tmpl := templates.New("version").Funcs(template.FuncMap{"getDetailsOrder": getDetailsOrder})
|
|
|
|
tmpl, err := tmpl.Parse(templateFormat)
|
|
|
|
|
linting: fix incorrectly formatted errors (revive)
cli/compose/interpolation/interpolation.go:102:4: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
"invalid interpolation format for %s: %#v. You may need to escape any $ with another $.",
^
cli/command/stack/loader/loader.go:30:30: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
return nil, errors.Errorf("Compose file contains unsupported options:\n\n%s\n",
^
cli/command/formatter/formatter.go:76:30: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
return tmpl, errors.Errorf("Template parsing error: %v\n", err)
^
cli/command/formatter/formatter.go:97:24: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
return errors.Errorf("Template parsing error: %v\n", err)
^
cli/command/image/build.go:257:25: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
return errors.Errorf("error checking context: '%s'.", err)
^
cli/command/volume/create.go:35:27: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
return errors.Errorf("Conflicting options: either specify --name or provide positional arg, not both\n")
^
cli/command/container/create.go:160:24: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
return errors.Errorf("failed to remove the CID file '%s': %s \n", cid.path, err)
^
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2022-03-27 15:13:03 -04:00
|
|
|
return tmpl, errors.Wrap(err, "template parsing error")
|
2018-05-31 04:10:56 -04:00
|
|
|
}
|
|
|
|
|
2017-12-06 15:35:43 -05:00
|
|
|
func getDetailsOrder(v types.ComponentVersion) []string {
|
|
|
|
out := make([]string, 0, len(v.Details))
|
|
|
|
for k := range v.Details {
|
|
|
|
out = append(out, k)
|
|
|
|
}
|
|
|
|
sort.Strings(out)
|
|
|
|
return out
|
|
|
|
}
|