Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add containerd, runc, and docker-init versions to /version #37974

Merged
merged 1 commit into from Jan 15, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions daemon/info.go
Expand Up @@ -118,6 +118,7 @@ func (daemon *Daemon) SystemVersion() types.Version {

v.Platform.Name = dockerversion.PlatformName

daemon.fillPlatformVersion(&v)
return v
}

Expand Down
127 changes: 101 additions & 26 deletions daemon/info_unix.go
Expand Up @@ -6,6 +6,7 @@ import (
"context"
"fmt"
"os/exec"
"path/filepath"
"strings"

"github.com/docker/docker/api/types"
Expand All @@ -32,17 +33,11 @@ func (daemon *Daemon) fillPlatformInfo(v *types.Info, sysInfo *sysinfo.SysInfo)

defaultRuntimeBinary := daemon.configStore.GetRuntime(v.DefaultRuntime).Path
if rv, err := exec.Command(defaultRuntimeBinary, "--version").Output(); err == nil {
parts := strings.Split(strings.TrimSpace(string(rv)), "\n")
if len(parts) == 3 {
parts = strings.Split(parts[1], ": ")
if len(parts) == 2 {
v.RuncCommit.ID = strings.TrimSpace(parts[1])
}
}

if v.RuncCommit.ID == "" {
logrus.Warnf("failed to retrieve %s version: unknown output format: %s", defaultRuntimeBinary, string(rv))
if _, commit, err := parseRuncVersion(string(rv)); err != nil {
logrus.Warnf("failed to parse %s version: %v", defaultRuntimeBinary, err)
v.RuncCommit.ID = "N/A"
} else {
v.RuncCommit.ID = commit
}
} else {
logrus.Warnf("failed to retrieve %s version: %v", defaultRuntimeBinary, err)
Expand All @@ -64,14 +59,19 @@ func (daemon *Daemon) fillPlatformInfo(v *types.Info, sysInfo *sysinfo.SysInfo)
// value as "ID" to prevent clients from reporting a version-mismatch
v.ContainerdCommit.Expected = v.ContainerdCommit.ID

// TODO is there still a need to check the expected version for tini?
// if not, we can change this, and just set "Expected" to v.InitCommit.ID
v.InitCommit.Expected = dockerversion.InitCommitID

defaultInitBinary := daemon.configStore.GetInitPath()
if rv, err := exec.Command(defaultInitBinary, "--version").Output(); err == nil {
ver, err := parseInitVersion(string(rv))

if err != nil {
logrus.Warnf("failed to retrieve %s version: %s", defaultInitBinary, err)
if _, commit, err := parseInitVersion(string(rv)); err != nil {
logrus.Warnf("failed to parse %s version: %s", defaultInitBinary, err)
v.InitCommit.ID = "N/A"
} else {
v.InitCommit.ID = commit
v.InitCommit.Expected = dockerversion.InitCommitID[0:len(commit)]
}
v.InitCommit = ver
} else {
logrus.Warnf("failed to retrieve %s version: %s", defaultInitBinary, err)
v.InitCommit.ID = "N/A"
Expand Down Expand Up @@ -115,6 +115,53 @@ func (daemon *Daemon) fillPlatformInfo(v *types.Info, sysInfo *sysinfo.SysInfo)
}
}

func (daemon *Daemon) fillPlatformVersion(v *types.Version) {
if rv, err := daemon.containerd.Version(context.Background()); err == nil {
v.Components = append(v.Components, types.ComponentVersion{
Name: "containerd",
Version: rv.Version,
Details: map[string]string{
"GitCommit": rv.Revision,
},
})
}

defaultRuntime := daemon.configStore.GetDefaultRuntimeName()
defaultRuntimeBinary := daemon.configStore.GetRuntime(defaultRuntime).Path
if rv, err := exec.Command(defaultRuntimeBinary, "--version").Output(); err == nil {
if ver, commit, err := parseRuncVersion(string(rv)); err != nil {
logrus.Warnf("failed to parse %s version: %v", defaultRuntimeBinary, err)
} else {
v.Components = append(v.Components, types.ComponentVersion{
Name: defaultRuntime,
Version: ver,
Details: map[string]string{
"GitCommit": commit,
},
})
}
} else {
logrus.Warnf("failed to retrieve %s version: %v", defaultRuntimeBinary, err)
}

defaultInitBinary := daemon.configStore.GetInitPath()
if rv, err := exec.Command(defaultInitBinary, "--version").Output(); err == nil {
if ver, commit, err := parseInitVersion(string(rv)); err != nil {
logrus.Warnf("failed to parse %s version: %s", defaultInitBinary, err)
} else {
v.Components = append(v.Components, types.ComponentVersion{
Name: filepath.Base(defaultInitBinary),
Version: ver,
Details: map[string]string{
"GitCommit": commit,
},
})
}
} else {
logrus.Warnf("failed to retrieve %s version: %s", defaultInitBinary, err)
}
}

func fillDriverWarnings(v *types.Info) {
for _, pair := range v.DriverStatus {
if pair[0] == "Data loop file" {
Expand Down Expand Up @@ -149,24 +196,52 @@ func getBackingFs(v *types.Info) string {
return ""
}

// parseInitVersion parses a Tini version string, and extracts the version.
func parseInitVersion(v string) (types.Commit, error) {
version := types.Commit{ID: "", Expected: dockerversion.InitCommitID}
// parseInitVersion parses a Tini version string, and extracts the "version"
// and "git commit" from the output.
//
// Output example from `docker-init --version`:
//
// tini version 0.18.0 - git.fec3683
func parseInitVersion(v string) (version string, commit string, err error) {
parts := strings.Split(strings.TrimSpace(v), " - ")

if len(parts) >= 2 {
gitParts := strings.Split(parts[1], ".")
if len(gitParts) == 2 && gitParts[0] == "git" {
version.ID = gitParts[1]
version.Expected = dockerversion.InitCommitID[0:len(version.ID)]
commit = gitParts[1]
}
}
if version.ID == "" && strings.HasPrefix(parts[0], "tini version ") {
version.ID = "v" + strings.TrimPrefix(parts[0], "tini version ")
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dropped the v prefix. Looking at this again, I don't think we should modify versions that are returned by an external binary, and just return it as-is

if strings.HasPrefix(parts[0], "tini version ") {
version = strings.TrimPrefix(parts[0], "tini version ")
}
if version == "" && commit == "" {
err = errors.Errorf("unknown output format: %s", v)
}
return version, commit, err
}

// parseRuncVersion parses the output of `runc --version` and extracts the
// "version" and "git commit" from the output.
//
// Output example from `runc --version`:
//
// runc version 1.0.0-rc5+dev
// commit: 69663f0bd4b60df09991c08812a60108003fa340
// spec: 1.0.0
func parseRuncVersion(v string) (version string, commit string, err error) {
lines := strings.Split(strings.TrimSpace(v), "\n")
for _, line := range lines {
if strings.HasPrefix(line, "runc version") {
version = strings.TrimSpace(strings.TrimPrefix(line, "runc version"))
continue
}
if strings.HasPrefix(line, "commit:") {
commit = strings.TrimSpace(strings.TrimPrefix(line, "commit:"))
continue
}
}
if version.ID == "" {
version.ID = "N/A"
return version, errors.Errorf("unknown output format: %s", v)
if version == "" && commit == "" {
err = errors.Errorf("unknown output format: %s", v)
}
return version, nil
return version, commit, err
}
84 changes: 67 additions & 17 deletions daemon/info_unix_test.go
Expand Up @@ -5,49 +5,99 @@ package daemon // import "github.com/docker/docker/daemon"
import (
"testing"

"github.com/docker/docker/api/types"
"github.com/docker/docker/dockerversion"
"gotest.tools/assert"
is "gotest.tools/assert/cmp"
)

func TestParseInitVersion(t *testing.T) {
tests := []struct {
output string
version string
result types.Commit
commit string
invalid bool
}{
{
version: "tini version 0.13.0 - git.949e6fa",
result: types.Commit{ID: "949e6fa", Expected: dockerversion.InitCommitID[0:7]},
output: "tini version 0.13.0 - git.949e6fa",
version: "0.13.0",
commit: "949e6fa",
}, {
version: "tini version 0.13.0\n",
result: types.Commit{ID: "v0.13.0", Expected: dockerversion.InitCommitID},
output: "tini version 0.13.0\n",
version: "0.13.0",
}, {
version: "tini version 0.13.2",
result: types.Commit{ID: "v0.13.2", Expected: dockerversion.InitCommitID},
output: "tini version 0.13.2",
version: "0.13.2",
}, {
version: "tini version0.13.2",
result: types.Commit{ID: "N/A", Expected: dockerversion.InitCommitID},
output: "tini version0.13.2",
invalid: true,
}, {
version: "",
result: types.Commit{ID: "N/A", Expected: dockerversion.InitCommitID},
output: "",
invalid: true,
}, {
version: "hello world",
result: types.Commit{ID: "N/A", Expected: dockerversion.InitCommitID},
output: "hello world",
invalid: true,
},
}

for _, test := range tests {
ver, err := parseInitVersion(string(test.version))
version, commit, err := parseInitVersion(string(test.output))
if test.invalid {
assert.Check(t, is.ErrorContains(err, ""))
} else {
assert.Check(t, err)
}
assert.Check(t, is.DeepEqual(test.result, ver))
assert.Equal(t, test.version, version)
assert.Equal(t, test.commit, commit)
}
}

func TestParseRuncVersion(t *testing.T) {
tests := []struct {
output string
version string
commit string
invalid bool
}{
{
output: `
runc version 1.0.0-rc5+dev
commit: 69663f0bd4b60df09991c08812a60108003fa340
spec: 1.0.0
`,
version: "1.0.0-rc5+dev",
commit: "69663f0bd4b60df09991c08812a60108003fa340",
},
{
output: `
runc version 1.0.0-rc5+dev
spec: 1.0.0
`,
version: "1.0.0-rc5+dev",
},
{
output: `
commit: 69663f0bd4b60df09991c08812a60108003fa340
spec: 1.0.0
`,
commit: "69663f0bd4b60df09991c08812a60108003fa340",
},
{
output: "",
invalid: true,
},
{
output: "hello world",
invalid: true,
},
}

for _, test := range tests {
version, commit, err := parseRuncVersion(string(test.output))
if test.invalid {
assert.Check(t, is.ErrorContains(err, ""))
} else {
assert.Check(t, err)
}
assert.Equal(t, test.version, version)
assert.Equal(t, test.commit, commit)
}
}
2 changes: 2 additions & 0 deletions daemon/info_windows.go
Expand Up @@ -9,5 +9,7 @@ import (
func (daemon *Daemon) fillPlatformInfo(v *types.Info, sysInfo *sysinfo.SysInfo) {
}

func (daemon *Daemon) fillPlatformVersion(v *types.Version) {}

func fillDriverWarnings(v *types.Info) {
}