Skip to content

Commit bff56f0

Browse files
committed
cli/command/system: define struct for formatting version
The client.ServerVersion method in the moby/client module defines an output struct that's separate from the API response. These output structs are not designed to be marshaled as JSON, but the CLI depended on them defining `json` labels, which it used to format the output as JSON (`docker version --format=json`); as a result, the JSON output changed in docker v29, as it would now use the naming based on the Go struct's fields (`APIVersion` instead of `ApiVersion`). In future, we should consider having a `--raw` (or similar) option for the CLI to print API responses as-is, instead of using client structs or CLI structs for this (this would also make sure the JSON output does not inherit client-side formatting of fields). For now, let's create a struct for formatting the output, similar to what we do for the client-side information. Signed-off-by: Sebastiaan van Stijn <[email protected]>
1 parent 3d4129b commit bff56f0

File tree

4 files changed

+85
-36
lines changed

4 files changed

+85
-36
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"Client":{"Version":"18.99.5-ce","ApiVersion":"1.38","DefaultAPIVersion":"1.38","GitCommit":"deadbeef","GoVersion":"go1.10.2","Os":"linux","Arch":"amd64","BuildTime":"Wed May 30 22:21:05 2018","Context":"my-context"},"Server":{"Platform":{"Name":"Docker Enterprise Edition (EE) 2.0"},"Version":"","APIVersion":"","MinAPIVersion":"","Os":"","Arch":"","Experimental":false,"Components":[{"Name":"Engine","Version":"17.06.2-ee-15","Details":{"ApiVersion":"1.30","Arch":"amd64","BuildTime":"Mon Jul 9 23:38:38 2018","Experimental":"false","GitCommit":"64ddfa6","GoVersion":"go1.8.7","MinAPIVersion":"1.12","Os":"linux"}},{"Name":"Universal Control Plane","Version":"17.06.2-ee-15","Details":{"ApiVersion":"1.30","Arch":"amd64","BuildTime":"Mon Jul 2 21:24:07 UTC 2018","GitCommit":"4513922","GoVersion":"go1.9.4","MinApiVersion":"1.20","Os":"linux","Version":"3.0.3-tp2"}},{"Name":"Kubernetes","Version":"1.8+","Details":{"buildDate":"2018-04-26T16:51:21Z","compiler":"gc","gitCommit":"8d637aedf46b9c21dde723e29c645b9f27106fa5","gitTreeState":"clean","gitVersion":"v1.8.11-docker-8d637ae","goVersion":"go1.8.3","major":"1","minor":"8+","platform":"linux/amd64"}},{"Name":"Calico","Version":"v3.0.8","Details":{"cni":"v2.0.6","kube-controllers":"v2.0.5","node":"v3.0.8"}}]}}
1+
{"Client":{"Version":"18.99.5-ce","ApiVersion":"1.38","DefaultAPIVersion":"1.38","GitCommit":"deadbeef","GoVersion":"go1.10.2","Os":"linux","Arch":"amd64","BuildTime":"Wed May 30 22:21:05 2018","Context":"my-context"},"Server":{"Platform":{"Name":"Docker Enterprise Edition (EE) 2.0"},"Version":"18.99.5-ce","ApiVersion":"1.30","MinAPIVersion":"1.12","Os":"linux","Arch":"amd64","Components":[{"Name":"Engine","Version":"17.06.2-ee-15","Details":{"ApiVersion":"1.30","Arch":"amd64","BuildTime":"Mon Jul 9 23:38:38 2018","Experimental":"false","GitCommit":"64ddfa6","GoVersion":"go1.8.7","MinAPIVersion":"1.12","Os":"linux"}},{"Name":"Universal Control Plane","Version":"17.06.2-ee-15","Details":{"ApiVersion":"1.30","Arch":"amd64","BuildTime":"Mon Jul 2 21:24:07 UTC 2018","GitCommit":"4513922","GoVersion":"go1.9.4","MinApiVersion":"1.20","Os":"linux","Version":"3.0.3-tp2"}},{"Name":"Kubernetes","Version":"1.8+","Details":{"buildDate":"2018-04-26T16:51:21Z","compiler":"gc","gitCommit":"8d637aedf46b9c21dde723e29c645b9f27106fa5","gitTreeState":"clean","gitVersion":"v1.8.11-docker-8d637ae","goVersion":"go1.8.3","major":"1","minor":"8+","platform":"linux/amd64"}},{"Name":"Calico","Version":"v3.0.8","Details":{"cni":"v2.0.6","kube-controllers":"v2.0.5","node":"v3.0.8"}}],"GitCommit":"64ddfa6","GoVersion":"go1.8.7","KernelVersion":"v1.0.0","BuildTime":"Mon Jul 9 23:38:38 2018"}}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"Client":{"Version":"18.99.5-ce","ApiVersion":"1.38","DefaultAPIVersion":"1.38","GitCommit":"deadbeef","GoVersion":"go1.10.2","Os":"linux","Arch":"amd64","BuildTime":"Wed May 30 22:21:05 2018","Context":"my-context"},"Server":{"Platform":{"Name":"Docker Enterprise Edition (EE) 2.0"},"Version":"","APIVersion":"","MinAPIVersion":"","Os":"","Arch":"","Experimental":false,"Components":[{"Name":"Engine","Version":"17.06.2-ee-15","Details":{"ApiVersion":"1.30","Arch":"amd64","BuildTime":"Mon Jul 9 23:38:38 2018","Experimental":"false","GitCommit":"64ddfa6","GoVersion":"go1.8.7","MinAPIVersion":"1.12","Os":"linux"}},{"Name":"Universal Control Plane","Version":"17.06.2-ee-15","Details":{"ApiVersion":"1.30","Arch":"amd64","BuildTime":"Mon Jul 2 21:24:07 UTC 2018","GitCommit":"4513922","GoVersion":"go1.9.4","MinApiVersion":"1.20","Os":"linux","Version":"3.0.3-tp2"}},{"Name":"Kubernetes","Version":"1.8+","Details":{"buildDate":"2018-04-26T16:51:21Z","compiler":"gc","gitCommit":"8d637aedf46b9c21dde723e29c645b9f27106fa5","gitTreeState":"clean","gitVersion":"v1.8.11-docker-8d637ae","goVersion":"go1.8.3","major":"1","minor":"8+","platform":"linux/amd64"}},{"Name":"Calico","Version":"v3.0.8","Details":{"cni":"v2.0.6","kube-controllers":"v2.0.5","node":"v3.0.8"}}]}}
1+
{"Client":{"Version":"18.99.5-ce","ApiVersion":"1.38","DefaultAPIVersion":"1.38","GitCommit":"deadbeef","GoVersion":"go1.10.2","Os":"linux","Arch":"amd64","BuildTime":"Wed May 30 22:21:05 2018","Context":"my-context"},"Server":{"Platform":{"Name":"Docker Enterprise Edition (EE) 2.0"},"Version":"18.99.5-ce","ApiVersion":"1.30","MinAPIVersion":"1.12","Os":"linux","Arch":"amd64","Components":[{"Name":"Engine","Version":"17.06.2-ee-15","Details":{"ApiVersion":"1.30","Arch":"amd64","BuildTime":"Mon Jul 9 23:38:38 2018","Experimental":"false","GitCommit":"64ddfa6","GoVersion":"go1.8.7","MinAPIVersion":"1.12","Os":"linux"}},{"Name":"Universal Control Plane","Version":"17.06.2-ee-15","Details":{"ApiVersion":"1.30","Arch":"amd64","BuildTime":"Mon Jul 2 21:24:07 UTC 2018","GitCommit":"4513922","GoVersion":"go1.9.4","MinApiVersion":"1.20","Os":"linux","Version":"3.0.3-tp2"}},{"Name":"Kubernetes","Version":"1.8+","Details":{"buildDate":"2018-04-26T16:51:21Z","compiler":"gc","gitCommit":"8d637aedf46b9c21dde723e29c645b9f27106fa5","gitTreeState":"clean","gitVersion":"v1.8.11-docker-8d637ae","goVersion":"go1.8.3","major":"1","minor":"8+","platform":"linux/amd64"}},{"Name":"Calico","Version":"v3.0.8","Details":{"cni":"v2.0.6","kube-controllers":"v2.0.5","node":"v3.0.8"}}],"GitCommit":"64ddfa6","GoVersion":"go1.8.7","KernelVersion":"v1.0.0","BuildTime":"Mon Jul 9 23:38:38 2018"}}

cli/command/system/version.go

Lines changed: 71 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"io"
77
"runtime"
88
"sort"
9+
"strconv"
910
"text/template"
1011
"time"
1112

@@ -63,7 +64,7 @@ type versionOptions struct {
6364
// versionInfo contains version information of both the Client, and Server
6465
type versionInfo struct {
6566
Client clientVersion
66-
Server *client.ServerVersionResult
67+
Server *serverVersion
6768
}
6869

6970
type platformInfo struct {
@@ -83,6 +84,26 @@ type clientVersion struct {
8384
Context string `json:"Context"`
8485
}
8586

87+
// serverVersion contains information about the Docker server host.
88+
// it's the client-side presentation of [client.ServerVersionResult].
89+
type serverVersion struct {
90+
Platform client.PlatformInfo `json:",omitempty"` // Platform is the platform (product name) the server is running on.
91+
Version string `json:"Version"` // Version is the version of the daemon.
92+
APIVersion string `json:"ApiVersion"` // APIVersion is the highest API version supported by the server.
93+
MinAPIVersion string `json:"MinAPIVersion,omitempty"` // MinAPIVersion is the minimum API version the server supports.
94+
Os string `json:"Os"` // Os is the operating system the server runs on.
95+
Arch string `json:"Arch"` // Arch is the hardware architecture the server runs on.
96+
Components []system.ComponentVersion `json:"Components,omitempty"` // Components contains version information for the components making up the server.
97+
98+
// The following fields are deprecated, they relate to the Engine component and are kept for backwards compatibility
99+
100+
GitCommit string `json:"GitCommit,omitempty"`
101+
GoVersion string `json:"GoVersion,omitempty"`
102+
KernelVersion string `json:"KernelVersion,omitempty"`
103+
Experimental bool `json:"Experimental,omitempty"`
104+
BuildTime string `json:"BuildTime,omitempty"`
105+
}
106+
86107
// newClientVersion constructs a new clientVersion. If a dockerCLI is
87108
// passed as argument, additional information is included (API version),
88109
// which may invoke an API connection. Pass nil to omit the additional
@@ -107,6 +128,47 @@ func newClientVersion(contextName string, dockerCli command.Cli) clientVersion {
107128
return v
108129
}
109130

131+
func newServerVersion(sv client.ServerVersionResult) *serverVersion {
132+
out := &serverVersion{
133+
Platform: sv.Platform,
134+
Version: sv.Version,
135+
APIVersion: sv.APIVersion,
136+
MinAPIVersion: sv.MinAPIVersion,
137+
Os: sv.Os,
138+
Arch: sv.Arch,
139+
Experimental: sv.Experimental, //nolint:staticcheck // ignore deprecated field.
140+
}
141+
foundEngine := false
142+
for _, component := range sv.Components {
143+
if component.Name == "Engine" {
144+
foundEngine = true
145+
buildTime, ok := component.Details["BuildTime"]
146+
if ok {
147+
component.Details["BuildTime"] = reformatDate(buildTime)
148+
}
149+
out.GitCommit = component.Details["GitCommit"]
150+
out.GoVersion = component.Details["GoVersion"]
151+
out.KernelVersion = component.Details["KernelVersion"]
152+
out.Experimental = func() bool { b, _ := strconv.ParseBool(component.Details["Experimental"]); return b }()
153+
out.BuildTime = reformatDate(component.Details["BuildTime"])
154+
}
155+
}
156+
157+
if !foundEngine {
158+
out.Components = append(out.Components, system.ComponentVersion{
159+
Name: "Engine",
160+
Version: sv.Version,
161+
Details: map[string]string{
162+
"ApiVersion": sv.APIVersion,
163+
"MinAPIVersion": sv.MinAPIVersion,
164+
"Os": sv.Os,
165+
"Arch": sv.Arch,
166+
},
167+
})
168+
}
169+
return out
170+
}
171+
110172
// newVersionCommand creates a new cobra.Command for `docker version`
111173
func newVersionCommand(dockerCLI command.Cli) *cobra.Command {
112174
var opts versionOptions
@@ -138,51 +200,28 @@ func reformatDate(buildTime string) string {
138200
}
139201

140202
func arch() string {
141-
arch := runtime.GOARCH
203+
out := runtime.GOARCH
142204
if rosetta.Enabled() {
143-
arch += " (rosetta)"
205+
out += " (rosetta)"
144206
}
145-
return arch
207+
return out
146208
}
147209

148-
func runVersion(ctx context.Context, dockerCli command.Cli, opts *versionOptions) error {
210+
func runVersion(ctx context.Context, dockerCLI command.Cli, opts *versionOptions) error {
149211
var err error
150212
tmpl, err := newVersionTemplate(opts.format)
151213
if err != nil {
152214
return cli.StatusError{StatusCode: 64, Status: err.Error()}
153215
}
154216

155217
vd := versionInfo{
156-
Client: newClientVersion(dockerCli.CurrentContext(), dockerCli),
218+
Client: newClientVersion(dockerCLI.CurrentContext(), dockerCLI),
157219
}
158-
sv, err := dockerCli.Client().ServerVersion(ctx, client.ServerVersionOptions{})
220+
sv, err := dockerCLI.Client().ServerVersion(ctx, client.ServerVersionOptions{})
159221
if err == nil {
160-
vd.Server = &sv
161-
foundEngine := false
162-
for _, component := range sv.Components {
163-
if component.Name == "Engine" {
164-
foundEngine = true
165-
buildTime, ok := component.Details["BuildTime"]
166-
if ok {
167-
component.Details["BuildTime"] = reformatDate(buildTime)
168-
}
169-
}
170-
}
171-
172-
if !foundEngine {
173-
vd.Server.Components = append(vd.Server.Components, system.ComponentVersion{
174-
Name: "Engine",
175-
Version: sv.Version,
176-
Details: map[string]string{
177-
"ApiVersion": sv.APIVersion,
178-
"MinAPIVersion": sv.MinAPIVersion,
179-
"Os": sv.Os,
180-
"Arch": sv.Arch,
181-
},
182-
})
183-
}
222+
vd.Server = newServerVersion(sv)
184223
}
185-
if err2 := prettyPrintVersion(dockerCli.Out(), vd, tmpl); err2 != nil && err == nil {
224+
if err2 := prettyPrintVersion(dockerCLI.Out(), vd, tmpl); err2 != nil && err == nil {
186225
err = err2
187226
}
188227
return err

cli/command/system/version_test.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,18 @@ func TestVersionFormat(t *testing.T) {
4747
BuildTime: "Wed May 30 22:21:05 2018",
4848
Context: "my-context",
4949
},
50-
Server: &client.ServerVersionResult{
51-
Platform: client.PlatformInfo{Name: "Docker Enterprise Edition (EE) 2.0"},
50+
Server: &serverVersion{
51+
Platform: client.PlatformInfo{Name: "Docker Enterprise Edition (EE) 2.0"},
52+
Version: "18.99.5-ce",
53+
APIVersion: "1.30",
54+
MinAPIVersion: "1.12",
55+
Os: "linux",
56+
Arch: "amd64",
57+
GitCommit: "64ddfa6",
58+
GoVersion: "go1.8.7",
59+
KernelVersion: "v1.0.0",
60+
Experimental: false,
61+
BuildTime: "Mon Jul 9 23:38:38 2018",
5262
Components: []system.ComponentVersion{
5363
{
5464
Name: "Engine",

0 commit comments

Comments
 (0)