diff --git a/container/docker/docker.go b/container/docker/docker.go new file mode 100644 index 00000000..203ac7f7 --- /dev/null +++ b/container/docker/docker.go @@ -0,0 +1,162 @@ +// Copyright 2016 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. + +// Provides global docker information. +package docker + +import ( + "fmt" + "strconv" + "strings" + + "golang.org/x/net/context" + + dockertypes "github.com/docker/engine-api/types" + "github.com/google/cadvisor/utils/machine" +) + +func Status() (DockerStatus, error) { + client, err := Client() + if err != nil { + return DockerStatus{}, fmt.Errorf("unable to communicate with docker daemon: %v", err) + } + dockerInfo, err := client.Info(context.Background()) + if err != nil { + return DockerStatus{}, err + } + + out := DockerStatus{} + out.Version = VersionString() + out.KernelVersion = machine.KernelVersion() + out.OS = dockerInfo.OperatingSystem + out.Hostname = dockerInfo.Name + out.RootDir = dockerInfo.DockerRootDir + out.Driver = dockerInfo.Driver + out.ExecDriver = dockerInfo.ExecutionDriver + out.NumImages = dockerInfo.Images + out.NumContainers = dockerInfo.Containers + out.DriverStatus = make(map[string]string, len(dockerInfo.DriverStatus)) + for _, v := range dockerInfo.DriverStatus { + out.DriverStatus[v[0]] = v[1] + } + return out, nil +} + +func Images() ([]DockerImage, error) { + client, err := Client() + if err != nil { + return nil, fmt.Errorf("unable to communicate with docker daemon: %v", err) + } + images, err := client.ImageList(context.Background(), dockertypes.ImageListOptions{All: false}) + if err != nil { + return nil, err + } + + out := []DockerImage{} + const unknownTag = ":" + for _, image := range images { + if len(image.RepoTags) == 1 && image.RepoTags[0] == unknownTag { + // images with repo or tags are uninteresting. + continue + } + di := DockerImage{ + ID: image.ID, + RepoTags: image.RepoTags, + Created: image.Created, + VirtualSize: image.VirtualSize, + Size: image.Size, + } + out = append(out, di) + } + return out, nil + +} + +// Checks whether the dockerInfo reflects a valid docker setup, and returns it if it does, or an +// error otherwise. +func ValidateInfo() (*dockertypes.Info, error) { + client, err := Client() + if err != nil { + return nil, fmt.Errorf("unable to communicate with docker daemon: %v", err) + } + + dockerInfo, err := client.Info(context.Background()) + if err != nil { + return nil, fmt.Errorf("failed to detect Docker info: %v", err) + } + + // Fall back to version API if ServerVersion is not set in info. + if dockerInfo.ServerVersion == "" { + version, err := client.ServerVersion(context.Background()) + if err != nil { + return nil, fmt.Errorf("unable to get docker version: %v", err) + } + dockerInfo.ServerVersion = version.Version + } + version, err := parseDockerVersion(dockerInfo.ServerVersion) + if err != nil { + return nil, err + } + + if version[0] < 1 { + return nil, fmt.Errorf("cAdvisor requires docker version %v or above but we have found version %v reported as %q", []int{1, 0, 0}, version, dockerInfo.ServerVersion) + } + + // Check that the libcontainer execdriver is used if the version is < 1.11 + // (execution drivers are no longer supported as of 1.11). + if version[0] <= 1 && version[1] <= 10 && + !strings.HasPrefix(dockerInfo.ExecutionDriver, "native") { + return nil, fmt.Errorf("docker found, but not using native exec driver") + } + + if dockerInfo.Driver == "" { + return nil, fmt.Errorf("failed to find docker storage driver") + } + + return &dockerInfo, nil +} + +func Version() ([]int, error) { + return parseDockerVersion(VersionString()) +} + +func VersionString() string { + docker_version := "Unknown" + client, err := Client() + if err == nil { + version, err := client.ServerVersion(context.Background()) + if err == nil { + docker_version = version.Version + } + } + 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) + if len(matches) != 1 { + return nil, fmt.Errorf("version string \"%v\" doesn't match expected regular expression: \"%v\"", full_version_string, version_regexp_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) + if err != nil { + return nil, fmt.Errorf("error while parsing \"%v\" in \"%v\"", version_string, full_version_string) + } + version_array[index] = version + } + return version_array, nil +} diff --git a/container/docker/factory.go b/container/docker/factory.go index b3938bc1..302a53ff 100644 --- a/container/docker/factory.go +++ b/container/docker/factory.go @@ -19,7 +19,6 @@ import ( "fmt" "path" "regexp" - "strconv" "strings" "github.com/google/cadvisor/container" @@ -164,24 +163,6 @@ var ( version_re = regexp.MustCompile(version_regexp_string) ) -// TODO: switch to a semantic versioning library. -func parseDockerVersion(full_version_string string) ([]int, error) { - matches := version_re.FindAllStringSubmatch(full_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) - } - 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) - if err != nil { - return nil, fmt.Errorf("error while parsing \"%v\" in \"%v\"", version_string, full_version_string) - } - version_array[index] = version - } - return version_array, nil -} - // Register root container before running this function! func Register(factory info.MachineInfoFactory, fsInfo fs.FsInfo, ignoreMetrics container.MetricSet) error { client, err := Client() diff --git a/container/docker/handler.go b/container/docker/handler.go index 5e813989..d94b7177 100644 --- a/container/docker/handler.go +++ b/container/docker/handler.go @@ -29,7 +29,6 @@ import ( info "github.com/google/cadvisor/info/v1" docker "github.com/docker/engine-api/client" - dockertypes "github.com/docker/engine-api/types" dockercontainer "github.com/docker/engine-api/types/container" "github.com/opencontainers/runc/libcontainer/cgroups" cgroupfs "github.com/opencontainers/runc/libcontainer/cgroups/fs" @@ -349,63 +348,3 @@ func (self *dockerContainerHandler) StopWatchingSubcontainers() error { func (self *dockerContainerHandler) Exists() bool { return common.CgroupExists(self.cgroupPaths) } - -func DockerInfo() (dockertypes.Info, error) { - client, err := Client() - if err != nil { - return dockertypes.Info{}, fmt.Errorf("unable to communicate with docker daemon: %v", err) - } - return client.Info(context.Background()) -} - -func DockerImages() ([]dockertypes.Image, error) { - client, err := Client() - if err != nil { - return nil, fmt.Errorf("unable to communicate with docker daemon: %v", err) - } - return client.ImageList(context.Background(), dockertypes.ImageListOptions{All: false}) -} - -// Checks whether the dockerInfo reflects a valid docker setup, and returns it if it does, or an -// error otherwise. -func ValidateInfo() (*dockertypes.Info, error) { - client, err := Client() - if err != nil { - return nil, fmt.Errorf("unable to communicate with docker daemon: %v", err) - } - - dockerInfo, err := client.Info(context.Background()) - if err != nil { - return nil, fmt.Errorf("failed to detect Docker info: %v", err) - } - - // Fall back to version API if ServerVersion is not set in info. - if dockerInfo.ServerVersion == "" { - version, err := client.ServerVersion(context.Background()) - if err != nil { - return nil, fmt.Errorf("unable to get docker version: %v", err) - } - dockerInfo.ServerVersion = version.Version - } - version, err := parseDockerVersion(dockerInfo.ServerVersion) - if err != nil { - return nil, err - } - - if version[0] < 1 { - return nil, fmt.Errorf("cAdvisor requires docker version %v or above but we have found version %v reported as %q", []int{1, 0, 0}, version, dockerInfo.ServerVersion) - } - - // Check that the libcontainer execdriver is used if the version is < 1.11 - // (execution drivers are no longer supported as of 1.11). - if version[0] <= 1 && version[1] <= 10 && - !strings.HasPrefix(dockerInfo.ExecutionDriver, "native") { - return nil, fmt.Errorf("docker found, but not using native exec driver") - } - - if dockerInfo.Driver == "" { - return nil, fmt.Errorf("failed to find docker storage driver") - } - - return &dockerInfo, nil -} diff --git a/container/docker/types.go b/container/docker/types.go new file mode 100644 index 00000000..8be66b86 --- /dev/null +++ b/container/docker/types.go @@ -0,0 +1,37 @@ +// Copyright 2016 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. + +// Types used for docker containers. +package docker + +type DockerStatus struct { + Version string `json:"version"` + KernelVersion string `json:"kernel_version"` + OS string `json:"os"` + Hostname string `json:"hostname"` + RootDir string `json:"root_dir"` + Driver string `json:"driver"` + DriverStatus map[string]string `json:"driver_status"` + ExecDriver string `json:"exec_driver"` + NumImages int `json:"num_images"` + NumContainers int `json:"num_containers"` +} + +type DockerImage struct { + ID string `json:"id"` + RepoTags []string `json:"repo_tags"` // repository name and tags. + Created int64 `json:"created"` // unix time since creation. + VirtualSize int64 `json:"virtual_size"` + Size int64 `json:"size"` +} diff --git a/manager/manager.go b/manager/manager.go index 21a47dc7..c4543a52 100644 --- a/manager/manager.go +++ b/manager/manager.go @@ -112,10 +112,10 @@ type Manager interface { CloseEventChannel(watch_id int) // Get status information about docker. - DockerInfo() (DockerStatus, error) + DockerInfo() (docker.DockerStatus, error) // Get details about interesting docker images. - DockerImages() ([]DockerImage, error) + DockerImages() ([]docker.DockerImage, error) // Returns debugging information. Map of lines per category. DebugInfo() map[string][]string @@ -134,7 +134,7 @@ func New(memoryCache *memory.InMemoryCache, sysfs sysfs.SysFs, maxHousekeepingIn } glog.Infof("cAdvisor running in container: %q", selfContainer) - dockerInfo, err := dockerInfo() + dockerStatus, err := docker.Status() if err != nil { glog.Warningf("Unable to connect to Docker: %v", err) } @@ -146,8 +146,8 @@ func New(memoryCache *memory.InMemoryCache, sysfs sysfs.SysFs, maxHousekeepingIn context := fs.Context{ Docker: fs.DockerContext{ Root: docker.RootDir(), - Driver: dockerInfo.Driver, - DriverStatus: dockerInfo.DriverStatus, + Driver: dockerStatus.Driver, + DriverStatus: dockerStatus.DriverStatus, }, RktPath: rktPath, } @@ -241,9 +241,6 @@ func (self *manager) Start() error { glog.Errorf("Registration of the raw container factory failed: %v", err) } - self.DockerInfo() - self.DockerImages() - if *enableLoadReader { // Create cpu load reader. cpuLoadReader, err := cpuload.New() @@ -1127,79 +1124,12 @@ func parseEventsStoragePolicy() events.StoragePolicy { return policy } -type DockerStatus struct { - Version string `json:"version"` - KernelVersion string `json:"kernel_version"` - OS string `json:"os"` - Hostname string `json:"hostname"` - RootDir string `json:"root_dir"` - Driver string `json:"driver"` - DriverStatus map[string]string `json:"driver_status"` - ExecDriver string `json:"exec_driver"` - NumImages int `json:"num_images"` - NumContainers int `json:"num_containers"` +func (m *manager) DockerImages() ([]docker.DockerImage, error) { + return docker.Images() } -type DockerImage struct { - ID string `json:"id"` - RepoTags []string `json:"repo_tags"` // repository name and tags. - Created int64 `json:"created"` // unix time since creation. - VirtualSize int64 `json:"virtual_size"` - Size int64 `json:"size"` -} - -func (m *manager) DockerImages() ([]DockerImage, error) { - images, err := docker.DockerImages() - if err != nil { - return nil, err - } - out := []DockerImage{} - const unknownTag = ":" - for _, image := range images { - if len(image.RepoTags) == 1 && image.RepoTags[0] == unknownTag { - // images with repo or tags are uninteresting. - continue - } - di := DockerImage{ - ID: image.ID, - RepoTags: image.RepoTags, - Created: image.Created, - VirtualSize: image.VirtualSize, - Size: image.Size, - } - out = append(out, di) - } - return out, nil -} - -func (m *manager) DockerInfo() (DockerStatus, error) { - return dockerInfo() -} - -func dockerInfo() (DockerStatus, error) { - dockerInfo, err := docker.DockerInfo() - if err != nil { - return DockerStatus{}, err - } - versionInfo, err := getVersionInfo() - if err != nil { - return DockerStatus{}, err - } - out := DockerStatus{} - out.Version = versionInfo.DockerVersion - out.KernelVersion = dockerInfo.KernelVersion - out.OS = dockerInfo.OperatingSystem - out.Hostname = dockerInfo.Name - out.RootDir = dockerInfo.DockerRootDir - out.Driver = dockerInfo.Driver - out.ExecDriver = dockerInfo.ExecutionDriver - out.NumImages = dockerInfo.Images - out.NumContainers = dockerInfo.Containers - out.DriverStatus = make(map[string]string, len(dockerInfo.DriverStatus)) - for _, v := range dockerInfo.DriverStatus { - out.DriverStatus[v[0]] = v[1] - } - return out, nil +func (m *manager) DockerInfo() (docker.DockerStatus, error) { + return docker.Status() } func (m *manager) DebugInfo() map[string][]string { @@ -1241,7 +1171,7 @@ func getVersionInfo() (*info.VersionInfo, error) { kernel_version := machine.KernelVersion() container_os := machine.ContainerOsVersion() - docker_version := machine.DockerVersion() + docker_version := docker.VersionString() return &info.VersionInfo{ KernelVersion: kernel_version, diff --git a/pages/docker.go b/pages/docker.go index 0a244295..1a5a6580 100644 --- a/pages/docker.go +++ b/pages/docker.go @@ -31,7 +31,7 @@ import ( const DockerPage = "/docker/" -func toStatusKV(status manager.DockerStatus) ([]keyVal, []keyVal) { +func toStatusKV(status docker.DockerStatus) ([]keyVal, []keyVal) { ds := []keyVal{ {Key: "Driver", Value: status.Driver}, } diff --git a/pages/pages.go b/pages/pages.go index c382d424..7f5b61ee 100644 --- a/pages/pages.go +++ b/pages/pages.go @@ -21,6 +21,7 @@ import ( "net/url" "strings" + "github.com/google/cadvisor/container/docker" httpmux "github.com/google/cadvisor/http/mux" info "github.com/google/cadvisor/info/v1" "github.com/google/cadvisor/manager" @@ -62,7 +63,7 @@ type pageData struct { Root string DockerStatus []keyVal DockerDriverStatus []keyVal - DockerImages []manager.DockerImage + DockerImages []docker.DockerImage } func init() { diff --git a/utils/machine/info.go b/utils/machine/info.go index 07759e4a..e26f950f 100644 --- a/utils/machine/info.go +++ b/utils/machine/info.go @@ -22,7 +22,6 @@ import ( "strings" "syscall" - "github.com/google/cadvisor/container/docker" "github.com/google/cadvisor/fs" info "github.com/google/cadvisor/info/v1" "github.com/google/cadvisor/utils/cloudinfo" @@ -30,7 +29,6 @@ import ( "github.com/google/cadvisor/utils/sysinfo" "github.com/golang/glog" - "golang.org/x/net/context" ) var machineIdFilePath = flag.String("machine_id_file", "/etc/machine-id,/var/lib/dbus/machine-id", "Comma-separated list of files to check for machine-id. Use the first one that exists.") @@ -136,18 +134,6 @@ func ContainerOsVersion() string { return container_os } -func DockerVersion() string { - docker_version := "Unknown" - client, err := docker.Client() - if err == nil { - version, err := client.ServerVersion(context.Background()) - if err == nil { - docker_version = version.Version - } - } - return docker_version -} - func KernelVersion() string { uname := &syscall.Utsname{}