docker源码学习之 docker version 子命令

说明: 使用的代码是 https://github.com/golang108/docker-cli.git (tag: v24.0.6)

1. 添加容器相关的几个子命令

其中就有我们这里说到 version 子命令。

容器相关的 子命令 在这里 被加入到 corba 中去

cli/command/commands/commands.go 文件

1
2
3
4
5
6
7
8
9

func AddCommands(cmd *cobra.Command, dockerCli command.Cli) {
cmd.AddCommand(
system.NewVersionCommand(dockerCli),
......
......
......
)
}

2. NewVersionCommand() 函数

1
2
3
4
func NewVersionCommand(dockerCli command.Cli) *cobra.Command {

}

基本上可以发现docker所有的子命令 都有个 NewXXXXCommand 的函数,cli/command/commands/commands.go 文件能一眼看到全部的。

1️⃣这个函数 主要作用 就是 初始化 cobra.Command{} 结构体 指针。然后返回这个指针。

2️⃣然后 设置 flags的解析。这里只有一个 -f、–format 选项。

通过打印 帮助可以看到 选项

1
2
3
4
5
6
7
8
9
10
11
12
13
$ ./build/docker version -h  
Flag shorthand -h has been deprecated, please use --help

Usage: docker version [OPTIONS]

Show the Docker version information

Options:
-f, --format string Format output using a custom template:
'json': Print in JSON format
'TEMPLATE': Print output using the given Go template.
Refer to https://docs.docker.com/go/formatting/ for more information about formatting output with
templates

2.1 cmd := &cobra.Command{} 结构体的初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
cmd := &cobra.Command{
Use: "version [OPTIONS]",
Short: "Show the Docker version information",
Args: cli.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
return runVersion(dockerCli, &opts)
},
Annotations: map[string]string{
"category-top": "10",
},
ValidArgsFunction: completion.NoComplete,
}


1️⃣ 这里注册 runVersion(dockerCli, &opts) 函数

2️⃣Annotations中”category-top”: “10”的作用,这个和docker help显示 Common Commands 命令的排序有关系。

如下,为什么info命令排在最下面,是因为 info 这个值设置的是12,最大的。run命令排第一是因为设置的是最小的是1。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ docker help 

Usage: docker [OPTIONS] COMMAND

A self-sufficient runtime for containers

Common Commands:
run Create and run a new container from an image
exec Execute a command in a running container
ps List containers
build Build an image from a Dockerfile
pull Download an image from a registry
push Upload an image to a registry
images List images
login Log in to a registry
logout Log out from a registry
search Search Docker Hub for images
version Show the Docker version information
info Display system-wide information

在 cli/cobra.go 文件中可以看到:

1
2
3
4
func topCommands(cmd *cobra.Command) []*cobra.Command {
省略。。。。
return sortorder.NaturalLess(cmds[i].Annotations["category-top"], cmds[j].Annotations["category-top"])
省略。。。。

2.2 添加 flags

1
cmd.Flags().StringVarP(&opts.format, "format", "f", "", flagsHelper.InspectFormatHelp)

这里只有一个 -f、–format 选项。

3. runVersion(dockerCli , opts) 函数

3.1 opts *versionOptions 参数介绍

1
2
3
type versionOptions struct {
format string
}
opts 这个参数 是 `var opts versionOptions `,  在 func NewRunCommand() 函数开头
定义的一个, 是一个结构体类型。这里面就一个 format 属性字段,和flags -f/--format 绑定的。

4. newVersionTemplate(string) 函数

参数 就是上面刚刚讲的 versionOptions.format 。

如果参数是空字符串,也就是没有指定 -f、–format选项,这里会使用一个 默认的模板:defaultVersionTemplate。

如果指定了 -f、–format选项,这里会使用一个 formatter.JSONFormat 的模板。

1️⃣ 默认模板打印如下格式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
$ ./build/docker version 
Client:
Version: 24.0.6-6-g13466649e5
API version: 1.43
Go version: go1.19.1
Git commit: 13466649e5
Built: Sun Sep 24 12:54:21 2023
OS/Arch: linux/amd64
Context: default

Server: Docker Engine - Community
Engine:
Version: 24.0.2
API version: 1.43 (minimum version 1.12)
Go version: go1.20.4
Git commit: 659604f
Built: Thu May 25 21:52:13 2023
OS/Arch: linux/amd64
Experimental: false
containerd:
Version: 1.6.21
GitCommit: 3dce8eb055cbb6872793272b4f20ed16117344f8
runc:
Version: 1.1.7
GitCommit: v1.1.7-0-g860f061
docker-init:
Version: 0.19.0
GitCommit: de40ad0

2️⃣ json模式打印如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
{
"Client": {
"Version": "24.0.6-6-g13466649e5",
"ApiVersion": "1.43",
"DefaultAPIVersion": "1.43",
"GitCommit": "13466649e5",
"GoVersion": "go1.19.1",
"Os": "linux",
"Arch": "amd64",
"BuildTime": "Sun Sep 24 12:54:21 2023",
"Context": "default"
},
"Server": {
"Platform": {
"Name": "Docker Engine - Community"
},
"Components": [{
"Name": "Engine",
"Version": "24.0.2",
"Details": {
"ApiVersion": "1.43",
"Arch": "amd64",
"BuildTime": "Thu May 25 21:52:13 2023",
"Experimental": "false",
"GitCommit": "659604f",
"GoVersion": "go1.20.4",
"KernelVersion": "5.4.0-150-generic",
"MinAPIVersion": "1.12",
"Os": "linux"
}
}, {
"Name": "containerd",
"Version": "1.6.21",
"Details": {
"GitCommit": "3dce8eb055cbb6872793272b4f20ed16117344f8"
}
}, {
"Name": "runc",
"Version": "1.1.7",
"Details": {
"GitCommit": "v1.1.7-0-g860f061"
}
}, {
"Name": "docker-init",
"Version": "0.19.0",
"Details": {
"GitCommit": "de40ad0"
}
}],
"Version": "24.0.2",
"ApiVersion": "1.43",
"MinAPIVersion": "1.12",
"GitCommit": "659604f",
"GoVersion": "go1.20.4",
"Os": "linux",
"Arch": "amd64",
"KernelVersion": "5.4.0-150-generic",
"BuildTime": "2023-05-25T21:52:13.000000000+00:00"
}
}

3️⃣自定义模板字符串打印如下:

1
2
$ docker version --format '{{ .Client.Version }} {{ .Server.Version }}'
24.0.6-6-g13466649e5 24.0.2

模板引擎用的 go 自带的,text/template/template.go。

这个 双大括号的写法 和 vue 中很类似,web中目前流行有 Mustache、Hogan、doT.js、JsRender、Kendo UI Templates等。

5 newClientVersion(ctx, dockerCli)

获取 客户端的 版本信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

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() // DefaultVersion of Current REST API, DefaultVersion = "1.43"
}
return v
}

其中有几个变量是在 cli/version/version.go 中定义的,然后编译的时候会加上

1
2
3
4
5
6
var (
PlatformName = ""
Version = "unknown-version"
GitCommit = "unknown-commit"
BuildTime = "unknown-buildtime"
)

6 dockerCli.Client().ServerVersion(ctx)

获取 服务端的 版本信息

1
2
3
4
5
6
7
8
9
10
11
func (cli *Client) ServerVersion(ctx context.Context) (types.Version, error) {
resp, err := cli.get(ctx, "/version", nil, nil)
defer ensureReaderClosed(resp)
if err != nil {
return types.Version{}, err
}

var server types.Version
err = json.NewDecoder(resp.body).Decode(&server)
return server, err
}

类似的可以使用 curl 命令 发送http请求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
$ curl --unix-socket /var/run/docker.sock   http://localhost/version
{
"Platform": {
"Name": "Docker Engine - Community"
},
"Components": [{
"Name": "Engine",
"Version": "24.0.2",
"Details": {
"ApiVersion": "1.43",
"Arch": "amd64",
"BuildTime": "2023-05-25T21:52:13.000000000+00:00",
"Experimental": "false",
"GitCommit": "659604f",
"GoVersion": "go1.20.4",
"KernelVersion": "5.4.0-150-generic",
"MinAPIVersion": "1.12",
"Os": "linux"
}
}, {
"Name": "containerd",
"Version": "1.6.21",
"Details": {
"GitCommit": "3dce8eb055cbb6872793272b4f20ed16117344f8"
}
}, {
"Name": "runc",
"Version": "1.1.7",
"Details": {
"GitCommit": "v1.1.7-0-g860f061"
}
}, {
"Name": "docker-init",
"Version": "0.19.0",
"Details": {
"GitCommit": "de40ad0"
}
}],
"Version": "24.0.2",
"ApiVersion": "1.43",
"MinAPIVersion": "1.12",
"GitCommit": "659604f",
"GoVersion": "go1.20.4",
"Os": "linux",
"Arch": "amd64",
"KernelVersion": "5.4.0-150-generic",
"BuildTime": "2023-05-25T21:52:13.000000000+00:00"
}