diff --git a/container/docker/docker.go b/container/docker/docker.go index ae9f1fdf91..14c717a7c5 100644 --- a/container/docker/docker.go +++ b/container/docker/docker.go @@ -17,6 +17,7 @@ package docker import ( "fmt" + "regexp" "strconv" "strings" @@ -39,6 +40,7 @@ func Status() (v1.DockerStatus, error) { out := v1.DockerStatus{} out.Version = VersionString() + out.APIVersion = APIVersionString() out.KernelVersion = machine.KernelVersion() out.OS = dockerInfo.OperatingSystem out.Hostname = dockerInfo.Name @@ -105,7 +107,7 @@ func ValidateInfo() (*dockertypes.Info, error) { } dockerInfo.ServerVersion = version.Version } - version, err := parseDockerVersion(dockerInfo.ServerVersion) + version, err := parseVersion(dockerInfo.ServerVersion, version_re, 3) if err != nil { return nil, err } @@ -129,7 +131,11 @@ func ValidateInfo() (*dockertypes.Info, error) { } func Version() ([]int, error) { - return parseDockerVersion(VersionString()) + return parseVersion(VersionString(), version_re, 3) +} + +func APIVersion() ([]int, error) { + return parseVersion(APIVersionString(), apiversion_re, 2) } func VersionString() string { @@ -144,18 +150,29 @@ func VersionString() string { return docker_version } -// TODO: switch to a semantic versioning library. -func parseDockerVersion(full_version_string string) ([]int, error) { - matches := version_re.FindAllStringSubmatch(full_version_string, -1) +func APIVersionString() string { + docker_api_version := "Unknown" + client, err := Client() + if err == nil { + version, err := client.ServerVersion(context.Background()) + if err == nil { + docker_api_version = version.APIVersion + } + } + return docker_api_version +} + +func parseVersion(version_string string, regex *regexp.Regexp, length int) ([]int, error) { + matches := regex.FindAllStringSubmatch(version_string, -1) if len(matches) != 1 { - return nil, fmt.Errorf("version string \"%v\" doesn't match expected regular expression: \"%v\"", full_version_string, version_regexp_string) + return nil, fmt.Errorf("version string \"%v\" doesn't match expected regular expression: \"%v\"", version_string, regex.String()) } version_string_array := matches[0][1:] - version_array := make([]int, 3) - for index, version_string := range version_string_array { - version, err := strconv.Atoi(version_string) + version_array := make([]int, length) + for index, version_str := range version_string_array { + version, err := strconv.Atoi(version_str) if err != nil { - return nil, fmt.Errorf("error while parsing \"%v\" in \"%v\"", version_string, full_version_string) + return nil, fmt.Errorf("error while parsing \"%v\" in \"%v\"", version_str, version_string) } version_array[index] = version } diff --git a/container/docker/docker_test.go b/container/docker/docker_test.go new file mode 100644 index 0000000000..07bb784ddd --- /dev/null +++ b/container/docker/docker_test.go @@ -0,0 +1,51 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package docker + +import ( + "reflect" + "regexp" + "testing" +) + +func TestParseDockerAPIVersion(t *testing.T) { + tests := []struct { + version string + regex *regexp.Regexp + length int + expected []int + expectedError string + }{ + {"17.03.0", version_re, 3, []int{17, 03, 0}, ""}, + {"17.a3.0", version_re, 3, []int{}, `version string "17.a3.0" doesn't match expected regular expression: "(\d+)\.(\d+)\.(\d+)"`}, + {"1.20", apiversion_re, 2, []int{1, 20}, ""}, + {"1.a", apiversion_re, 2, []int{}, `version string "1.a" doesn't match expected regular expression: "(\d+)\.(\d+)"`}, + } + + for _, test := range tests { + actual, err := parseVersion(test.version, test.regex, test.length) + if err != nil { + if len(test.expectedError) == 0 { + t.Errorf("%s: expected no error, got %v", test.version, err) + } else if err.Error() != test.expectedError { + t.Errorf("%s: expected error %v, got %v", test.version, test.expectedError, err) + } + } else { + if !reflect.DeepEqual(actual, test.expected) { + t.Errorf("%s: expected array %v, got %v", test.version, test.expected, actual) + } + } + } +} diff --git a/container/docker/factory.go b/container/docker/factory.go index 08beeddfbc..09d1d3aaba 100644 --- a/container/docker/factory.go +++ b/container/docker/factory.go @@ -103,6 +103,8 @@ type dockerFactory struct { dockerVersion []int + dockerAPIVersion []int + ignoreMetrics container.MetricSet thinPoolWatcher *devicemapper.ThinPoolWatcher @@ -185,8 +187,10 @@ func (self *dockerFactory) DebugInfo() map[string][]string { } var ( - version_regexp_string = `(\d+)\.(\d+)\.(\d+)` - version_re = regexp.MustCompile(version_regexp_string) + version_regexp_string = `(\d+)\.(\d+)\.(\d+)` + version_re = regexp.MustCompile(version_regexp_string) + apiversion_regexp_string = `(\d+)\.(\d+)` + apiversion_re = regexp.MustCompile(apiversion_regexp_string) ) func startThinPoolWatcher(dockerInfo *dockertypes.Info) (*devicemapper.ThinPoolWatcher, error) { @@ -310,7 +314,9 @@ func Register(factory info.MachineInfoFactory, fsInfo fs.FsInfo, ignoreMetrics c } // Version already validated above, assume no error here. - dockerVersion, _ := parseDockerVersion(dockerInfo.ServerVersion) + dockerVersion, _ := parseVersion(dockerInfo.ServerVersion, version_re, 3) + + dockerAPIVersion, _ := APIVersion() cgroupSubsystems, err := libcontainer.GetCgroupSubsystems() if err != nil { @@ -338,6 +344,7 @@ func Register(factory info.MachineInfoFactory, fsInfo fs.FsInfo, ignoreMetrics c cgroupSubsystems: cgroupSubsystems, client: client, dockerVersion: dockerVersion, + dockerAPIVersion: dockerAPIVersion, fsInfo: fsInfo, machineInfoFactory: factory, storageDriver: storageDriver(dockerInfo.Driver), diff --git a/info/v1/docker.go b/info/v1/docker.go index 2703c53424..7b5fb7137c 100644 --- a/info/v1/docker.go +++ b/info/v1/docker.go @@ -17,6 +17,7 @@ package v1 type DockerStatus struct { Version string `json:"version"` + APIVersion string `json:"api_version"` KernelVersion string `json:"kernel_version"` OS string `json:"os"` Hostname string `json:"hostname"` diff --git a/info/v1/machine.go b/info/v1/machine.go index 933b6f3020..c259e0ba81 100644 --- a/info/v1/machine.go +++ b/info/v1/machine.go @@ -196,6 +196,9 @@ type VersionInfo struct { // Docker version. DockerVersion string `json:"docker_version"` + // Docker API Version + DockerAPIVersion string `json:"docker_api_version"` + // cAdvisor version. CadvisorVersion string `json:"cadvisor_version"` // cAdvisor git revision. diff --git a/info/v2/machine.go b/info/v2/machine.go index 0e5a613601..ecf04bf58d 100644 --- a/info/v2/machine.go +++ b/info/v2/machine.go @@ -31,6 +31,9 @@ type Attributes struct { // Docker version. DockerVersion string `json:"docker_version"` + // Docker API version. + DockerAPIVersion string `json:"docker_api_version"` + // cAdvisor version. CadvisorVersion string `json:"cadvisor_version"` @@ -74,6 +77,7 @@ func GetAttributes(mi *v1.MachineInfo, vi *v1.VersionInfo) Attributes { KernelVersion: vi.KernelVersion, ContainerOsVersion: vi.ContainerOsVersion, DockerVersion: vi.DockerVersion, + DockerAPIVersion: vi.DockerAPIVersion, CadvisorVersion: vi.CadvisorVersion, NumCores: mi.NumCores, CpuFrequency: mi.CpuFrequency, diff --git a/manager/manager.go b/manager/manager.go index 4de5e650d7..dc5273ac86 100644 --- a/manager/manager.go +++ b/manager/manager.go @@ -1255,11 +1255,13 @@ func getVersionInfo() (*info.VersionInfo, error) { kernel_version := machine.KernelVersion() container_os := machine.ContainerOsVersion() docker_version := docker.VersionString() + docker_api_version := docker.APIVersionString() return &info.VersionInfo{ KernelVersion: kernel_version, ContainerOsVersion: container_os, DockerVersion: docker_version, + DockerAPIVersion: docker_api_version, CadvisorVersion: version.Info["version"], CadvisorRevision: version.Info["revision"], }, nil diff --git a/pages/docker.go b/pages/docker.go index 8cc0177c96..3d40e2f662 100644 --- a/pages/docker.go +++ b/pages/docker.go @@ -40,6 +40,7 @@ func toStatusKV(status info.DockerStatus) ([]keyVal, []keyVal) { } return []keyVal{ {Key: "Docker Version", Value: status.Version}, + {Key: "Docker API Version", Value: status.APIVersion}, {Key: "Kernel Version", Value: status.KernelVersion}, {Key: "OS Version", Value: status.OS}, {Key: "Host Name", Value: status.Hostname},