Update cadvisor godeps

This commit is contained in:
Tim St. Clair 2016-04-15 11:46:21 -07:00
parent 903067c6c2
commit c4eacd3b76
22 changed files with 1134 additions and 842 deletions

72
Godeps/Godeps.json generated
View File

@ -663,93 +663,93 @@
},
{
"ImportPath": "github.com/google/cadvisor/api",
"Comment": "v0.22.2",
"Rev": "546a3771589bdb356777c646c6eca24914fdd48b"
"Comment": "v0.23.0",
"Rev": "750f18e5eac3f6193b354fc14c03d92d4318a0ec"
},
{
"ImportPath": "github.com/google/cadvisor/cache/memory",
"Comment": "v0.22.2",
"Rev": "546a3771589bdb356777c646c6eca24914fdd48b"
"Comment": "v0.23.0",
"Rev": "750f18e5eac3f6193b354fc14c03d92d4318a0ec"
},
{
"ImportPath": "github.com/google/cadvisor/collector",
"Comment": "v0.22.2",
"Rev": "546a3771589bdb356777c646c6eca24914fdd48b"
"Comment": "v0.23.0",
"Rev": "750f18e5eac3f6193b354fc14c03d92d4318a0ec"
},
{
"ImportPath": "github.com/google/cadvisor/container",
"Comment": "v0.22.2",
"Rev": "546a3771589bdb356777c646c6eca24914fdd48b"
"Comment": "v0.23.0",
"Rev": "750f18e5eac3f6193b354fc14c03d92d4318a0ec"
},
{
"ImportPath": "github.com/google/cadvisor/events",
"Comment": "v0.22.2",
"Rev": "546a3771589bdb356777c646c6eca24914fdd48b"
"Comment": "v0.23.0",
"Rev": "750f18e5eac3f6193b354fc14c03d92d4318a0ec"
},
{
"ImportPath": "github.com/google/cadvisor/fs",
"Comment": "v0.22.2",
"Rev": "546a3771589bdb356777c646c6eca24914fdd48b"
"Comment": "v0.23.0",
"Rev": "750f18e5eac3f6193b354fc14c03d92d4318a0ec"
},
{
"ImportPath": "github.com/google/cadvisor/healthz",
"Comment": "v0.22.2",
"Rev": "546a3771589bdb356777c646c6eca24914fdd48b"
"Comment": "v0.23.0",
"Rev": "750f18e5eac3f6193b354fc14c03d92d4318a0ec"
},
{
"ImportPath": "github.com/google/cadvisor/http",
"Comment": "v0.22.2",
"Rev": "546a3771589bdb356777c646c6eca24914fdd48b"
"Comment": "v0.23.0",
"Rev": "750f18e5eac3f6193b354fc14c03d92d4318a0ec"
},
{
"ImportPath": "github.com/google/cadvisor/info/v1",
"Comment": "v0.22.2",
"Rev": "546a3771589bdb356777c646c6eca24914fdd48b"
"Comment": "v0.23.0",
"Rev": "750f18e5eac3f6193b354fc14c03d92d4318a0ec"
},
{
"ImportPath": "github.com/google/cadvisor/info/v2",
"Comment": "v0.22.2",
"Rev": "546a3771589bdb356777c646c6eca24914fdd48b"
"Comment": "v0.23.0",
"Rev": "750f18e5eac3f6193b354fc14c03d92d4318a0ec"
},
{
"ImportPath": "github.com/google/cadvisor/manager",
"Comment": "v0.22.2",
"Rev": "546a3771589bdb356777c646c6eca24914fdd48b"
"Comment": "v0.23.0",
"Rev": "750f18e5eac3f6193b354fc14c03d92d4318a0ec"
},
{
"ImportPath": "github.com/google/cadvisor/metrics",
"Comment": "v0.22.2",
"Rev": "546a3771589bdb356777c646c6eca24914fdd48b"
"Comment": "v0.23.0",
"Rev": "750f18e5eac3f6193b354fc14c03d92d4318a0ec"
},
{
"ImportPath": "github.com/google/cadvisor/pages",
"Comment": "v0.22.2",
"Rev": "546a3771589bdb356777c646c6eca24914fdd48b"
"Comment": "v0.23.0",
"Rev": "750f18e5eac3f6193b354fc14c03d92d4318a0ec"
},
{
"ImportPath": "github.com/google/cadvisor/storage",
"Comment": "v0.22.2",
"Rev": "546a3771589bdb356777c646c6eca24914fdd48b"
"Comment": "v0.23.0",
"Rev": "750f18e5eac3f6193b354fc14c03d92d4318a0ec"
},
{
"ImportPath": "github.com/google/cadvisor/summary",
"Comment": "v0.22.2",
"Rev": "546a3771589bdb356777c646c6eca24914fdd48b"
"Comment": "v0.23.0",
"Rev": "750f18e5eac3f6193b354fc14c03d92d4318a0ec"
},
{
"ImportPath": "github.com/google/cadvisor/utils",
"Comment": "v0.22.2",
"Rev": "546a3771589bdb356777c646c6eca24914fdd48b"
"Comment": "v0.23.0",
"Rev": "750f18e5eac3f6193b354fc14c03d92d4318a0ec"
},
{
"ImportPath": "github.com/google/cadvisor/validate",
"Comment": "v0.22.2",
"Rev": "546a3771589bdb356777c646c6eca24914fdd48b"
"Comment": "v0.23.0",
"Rev": "750f18e5eac3f6193b354fc14c03d92d4318a0ec"
},
{
"ImportPath": "github.com/google/cadvisor/version",
"Comment": "v0.22.2",
"Rev": "546a3771589bdb356777c646c6eca24914fdd48b"
"Comment": "v0.23.0",
"Rev": "750f18e5eac3f6193b354fc14c03d92d4318a0ec"
},
{
"ImportPath": "github.com/google/gofuzz",

View File

@ -16,7 +16,7 @@
// an array of ContainerHint structs, each with a container's id and networkInterface
// This allows collecting stats about network interfaces configured outside docker
// and lxc
package raw
package common
import (
"encoding/json"
@ -25,7 +25,7 @@ import (
"os"
)
var argContainerHints = flag.String("container_hints", "/etc/cadvisor/container_hints.json", "location of the container hints file")
var ArgContainerHints = flag.String("container_hints", "/etc/cadvisor/container_hints.json", "location of the container hints file")
type containerHints struct {
AllHosts []containerHint `json:"all_hosts,omitempty"`
@ -34,10 +34,10 @@ type containerHints struct {
type containerHint struct {
FullName string `json:"full_path,omitempty"`
NetworkInterface *networkInterface `json:"network_interface,omitempty"`
Mounts []mount `json:"mounts,omitempty"`
Mounts []Mount `json:"mounts,omitempty"`
}
type mount struct {
type Mount struct {
HostDir string `json:"host_dir,omitempty"`
ContainerDir string `json:"container_dir,omitempty"`
}
@ -47,7 +47,7 @@ type networkInterface struct {
VethChild string `json:"veth_child,omitempty"`
}
func getContainerHintsFromFile(containerHintsFile string) (containerHints, error) {
func GetContainerHintsFromFile(containerHintsFile string) (containerHints, error) {
dat, err := ioutil.ReadFile(containerHintsFile)
if os.IsNotExist(err) {
return containerHints{}, nil

View File

@ -13,7 +13,7 @@
// limitations under the License.
// Handler for Docker containers.
package docker
package common
import (
"sync"
@ -24,10 +24,10 @@ import (
"github.com/golang/glog"
)
type fsHandler interface {
start()
usage() (uint64, uint64)
stop()
type FsHandler interface {
Start()
Usage() (baseUsageBytes uint64, totalUsageBytes uint64)
Stop()
}
type realFsHandler struct {
@ -50,9 +50,9 @@ const (
maxDuBackoffFactor = 20
)
var _ fsHandler = &realFsHandler{}
var _ FsHandler = &realFsHandler{}
func newFsHandler(period time.Duration, rootfs, extraDir string, fsInfo fs.FsInfo) fsHandler {
func NewFsHandler(period time.Duration, rootfs, extraDir string, fsInfo fs.FsInfo) FsHandler {
return &realFsHandler{
lastUpdate: time.Time{},
usageBytes: 0,
@ -119,15 +119,15 @@ func (fh *realFsHandler) trackUsage() {
}
}
func (fh *realFsHandler) start() {
func (fh *realFsHandler) Start() {
go fh.trackUsage()
}
func (fh *realFsHandler) stop() {
func (fh *realFsHandler) Stop() {
close(fh.stopChan)
}
func (fh *realFsHandler) usage() (baseUsageBytes, totalUsageBytes uint64) {
func (fh *realFsHandler) Usage() (baseUsageBytes, totalUsageBytes uint64) {
fh.RLock()
defer fh.RUnlock()
return fh.baseUsageBytes, fh.usageBytes

View File

@ -0,0 +1,203 @@
// 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.
package common
import (
"fmt"
"io/ioutil"
"os"
"path"
"strconv"
"strings"
"time"
info "github.com/google/cadvisor/info/v1"
"github.com/google/cadvisor/utils"
"github.com/golang/glog"
)
func DebugInfo(watches map[string][]string) map[string][]string {
out := make(map[string][]string)
lines := make([]string, 0, len(watches))
for containerName, cgroupWatches := range watches {
lines = append(lines, fmt.Sprintf("%s:", containerName))
for _, cg := range cgroupWatches {
lines = append(lines, fmt.Sprintf("\t%s", cg))
}
}
out["Inotify watches"] = lines
return out
}
func GetSpec(cgroupPaths map[string]string, machineInfoFactory info.MachineInfoFactory, hasNetwork, hasFilesystem bool) (info.ContainerSpec, error) {
var spec info.ContainerSpec
// Assume unified hierarchy containers.
// Get the lowest creation time from all hierarchies as the container creation time.
now := time.Now()
lowestTime := now
for _, cgroupPath := range cgroupPaths {
// The modified time of the cgroup directory changes whenever a subcontainer is created.
// eg. /docker will have creation time matching the creation of latest docker container.
// Use clone_children as a workaround as it isn't usually modified. It is only likely changed
// immediately after creating a container.
cgroupPath = path.Join(cgroupPath, "cgroup.clone_children")
fi, err := os.Stat(cgroupPath)
if err == nil && fi.ModTime().Before(lowestTime) {
lowestTime = fi.ModTime()
}
}
if lowestTime != now {
spec.CreationTime = lowestTime
}
// Get machine info.
mi, err := machineInfoFactory.GetMachineInfo()
if err != nil {
return spec, err
}
// CPU.
cpuRoot, ok := cgroupPaths["cpu"]
if ok {
if utils.FileExists(cpuRoot) {
spec.HasCpu = true
spec.Cpu.Limit = readUInt64(cpuRoot, "cpu.shares")
spec.Cpu.Period = readUInt64(cpuRoot, "cpu.cfs_period_us")
quota := readString(cpuRoot, "cpu.cfs_quota_us")
if quota != "" && quota != "-1" {
val, err := strconv.ParseUint(quota, 10, 64)
if err != nil {
glog.Errorf("GetSpec: Failed to parse CPUQuota from %q: %s", path.Join(cpuRoot, "cpu.cfs_quota_us"), err)
}
spec.Cpu.Quota = val
}
}
}
// Cpu Mask.
// This will fail for non-unified hierarchies. We'll return the whole machine mask in that case.
cpusetRoot, ok := cgroupPaths["cpuset"]
if ok {
if utils.FileExists(cpusetRoot) {
spec.HasCpu = true
mask := readString(cpusetRoot, "cpuset.cpus")
spec.Cpu.Mask = utils.FixCpuMask(mask, mi.NumCores)
}
}
// Memory
memoryRoot, ok := cgroupPaths["memory"]
if ok {
if utils.FileExists(memoryRoot) {
spec.HasMemory = true
spec.Memory.Limit = readUInt64(memoryRoot, "memory.limit_in_bytes")
spec.Memory.SwapLimit = readUInt64(memoryRoot, "memory.memsw.limit_in_bytes")
}
}
spec.HasNetwork = hasNetwork
spec.HasFilesystem = hasFilesystem
if blkioRoot, ok := cgroupPaths["blkio"]; ok && utils.FileExists(blkioRoot) {
spec.HasDiskIo = true
}
return spec, nil
}
func readString(dirpath string, file string) string {
cgroupFile := path.Join(dirpath, file)
// Ignore non-existent files
if !utils.FileExists(cgroupFile) {
return ""
}
// Read
out, err := ioutil.ReadFile(cgroupFile)
if err != nil {
glog.Errorf("readString: Failed to read %q: %s", cgroupFile, err)
return ""
}
return strings.TrimSpace(string(out))
}
func readUInt64(dirpath string, file string) uint64 {
out := readString(dirpath, file)
if out == "" {
return 0
}
val, err := strconv.ParseUint(out, 10, 64)
if err != nil {
glog.Errorf("readUInt64: Failed to parse int %q from file %q: %s", out, path.Join(dirpath, file), err)
return 0
}
return val
}
// Lists all directories under "path" and outputs the results as children of "parent".
func ListDirectories(dirpath string, parent string, recursive bool, output map[string]struct{}) error {
// Ignore if this hierarchy does not exist.
if !utils.FileExists(dirpath) {
return nil
}
entries, err := ioutil.ReadDir(dirpath)
if err != nil {
return err
}
for _, entry := range entries {
// We only grab directories.
if entry.IsDir() {
name := path.Join(parent, entry.Name())
output[name] = struct{}{}
// List subcontainers if asked to.
if recursive {
err := ListDirectories(path.Join(dirpath, entry.Name()), name, true, output)
if err != nil {
return err
}
}
}
}
return nil
}
func MakeCgroupPaths(mountPoints map[string]string, name string) map[string]string {
cgroupPaths := make(map[string]string, len(mountPoints))
for key, val := range mountPoints {
cgroupPaths[key] = path.Join(val, name)
}
return cgroupPaths
}
func CgroupExists(cgroupPaths map[string]string) bool {
// If any cgroup exists, the container is still alive.
for _, cgroupPath := range cgroupPaths {
if utils.FileExists(cgroupPath) {
return true
}
}
return false
}

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package raw
package common
import (
"sync"

View File

@ -27,7 +27,7 @@ import (
"github.com/google/cadvisor/fs"
info "github.com/google/cadvisor/info/v1"
"github.com/fsouza/go-dockerclient"
docker "github.com/fsouza/go-dockerclient"
"github.com/golang/glog"
)
@ -163,6 +163,7 @@ 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 {
@ -186,42 +187,16 @@ func Register(factory info.MachineInfoFactory, fsInfo fs.FsInfo, ignoreMetrics c
if err != nil {
return fmt.Errorf("unable to communicate with docker daemon: %v", err)
}
var dockerVersion []int
if version, err := client.Version(); err != nil {
return fmt.Errorf("unable to communicate with docker daemon: %v", err)
} else {
expected_version := []int{1, 0, 0}
version_string := version.Get("Version")
dockerVersion, err = parseDockerVersion(version_string)
if err != nil {
return fmt.Errorf("couldn't parse docker version: %v", err)
}
for index, number := range dockerVersion {
if number > expected_version[index] {
break
} else if number < expected_version[index] {
return fmt.Errorf("cAdvisor requires docker version %v or above but we have found version %v reported as \"%v\"", expected_version, dockerVersion, version_string)
}
}
}
information, err := client.Info()
dockerInfo, err := ValidateInfo()
if err != nil {
return fmt.Errorf("failed to detect Docker info: %v", err)
return fmt.Errorf("failed to validate Docker info: %v", err)
}
// Check that the libcontainer execdriver is used.
execDriver := information.Get("ExecutionDriver")
if !strings.HasPrefix(execDriver, "native") {
return fmt.Errorf("docker found, but not using native exec driver")
}
// Version already validated above, assume no error here.
dockerVersion, _ := parseDockerVersion(dockerInfo.ServerVersion)
sd := information.Get("Driver")
if sd == "" {
return fmt.Errorf("failed to find docker storage driver")
}
storageDir := information.Get("DockerRootDir")
storageDir := dockerInfo.DockerRootDir
if storageDir == "" {
storageDir = *dockerRootDir
}
@ -237,7 +212,7 @@ func Register(factory info.MachineInfoFactory, fsInfo fs.FsInfo, ignoreMetrics c
dockerVersion: dockerVersion,
fsInfo: fsInfo,
machineInfoFactory: factory,
storageDriver: storageDriver(sd),
storageDriver: storageDriver(dockerInfo.Driver),
storageDir: storageDir,
ignoreMetrics: ignoreMetrics,
}

View File

@ -18,16 +18,15 @@ package docker
import (
"fmt"
"io/ioutil"
"math"
"path"
"strings"
"time"
"github.com/google/cadvisor/container"
"github.com/google/cadvisor/container/common"
containerlibcontainer "github.com/google/cadvisor/container/libcontainer"
"github.com/google/cadvisor/fs"
info "github.com/google/cadvisor/info/v1"
"github.com/google/cadvisor/utils"
docker "github.com/fsouza/go-dockerclient"
"github.com/opencontainers/runc/libcontainer/cgroups"
@ -80,7 +79,7 @@ type dockerContainerHandler struct {
networkMode string
// Filesystem handler.
fsHandler fsHandler
fsHandler common.FsHandler
ignoreMetrics container.MetricSet
}
@ -169,7 +168,7 @@ func newDockerContainerHandler(
}
if !ignoreMetrics.Has(container.DiskUsageMetrics) {
handler.fsHandler = newFsHandler(time.Minute, rootfsStorageDir, otherStorageDir, fsInfo)
handler.fsHandler = common.NewFsHandler(time.Minute, rootfsStorageDir, otherStorageDir, fsInfo)
}
// We assume that if Inspect fails then the container is not known to docker.
@ -200,15 +199,14 @@ func newDockerContainerHandler(
}
func (self *dockerContainerHandler) Start() {
// Start the filesystem handler.
if self.fsHandler != nil {
self.fsHandler.start()
self.fsHandler.Start()
}
}
func (self *dockerContainerHandler) Cleanup() {
if self.fsHandler != nil {
self.fsHandler.stop()
self.fsHandler.Stop()
}
}
@ -222,50 +220,6 @@ func (self *dockerContainerHandler) ContainerReference() (info.ContainerReferenc
}, nil
}
func (self *dockerContainerHandler) readLibcontainerConfig() (*libcontainerconfigs.Config, error) {
config, err := containerlibcontainer.ReadConfig(*dockerRootDir, *dockerRunDir, self.id)
if err != nil {
return nil, fmt.Errorf("failed to read libcontainer config: %v", err)
}
// Replace cgroup parent and name with our own since we may be running in a different context.
if config.Cgroups == nil {
config.Cgroups = new(libcontainerconfigs.Cgroup)
}
config.Cgroups.Name = self.name
config.Cgroups.Parent = "/"
return config, nil
}
func libcontainerConfigToContainerSpec(config *libcontainerconfigs.Config, mi *info.MachineInfo) info.ContainerSpec {
var spec info.ContainerSpec
spec.HasMemory = true
spec.Memory.Limit = math.MaxUint64
spec.Memory.SwapLimit = math.MaxUint64
if config.Cgroups.Resources != nil {
if config.Cgroups.Resources.Memory > 0 {
spec.Memory.Limit = uint64(config.Cgroups.Resources.Memory)
}
if config.Cgroups.Resources.MemorySwap > 0 {
spec.Memory.SwapLimit = uint64(config.Cgroups.Resources.MemorySwap)
}
// Get CPU info
spec.HasCpu = true
spec.Cpu.Limit = 1024
if config.Cgroups.Resources.CpuShares != 0 {
spec.Cpu.Limit = uint64(config.Cgroups.Resources.CpuShares)
}
spec.Cpu.Mask = utils.FixCpuMask(config.Cgroups.Resources.CpusetCpus, mi.NumCores)
}
spec.HasDiskIo = true
return spec
}
func (self *dockerContainerHandler) needNet() bool {
if !self.ignoreMetrics.Has(container.NetworkUsageMetrics) {
return !strings.HasPrefix(self.networkMode, "container:")
@ -274,29 +228,12 @@ func (self *dockerContainerHandler) needNet() bool {
}
func (self *dockerContainerHandler) GetSpec() (info.ContainerSpec, error) {
mi, err := self.machineInfoFactory.GetMachineInfo()
if err != nil {
return info.ContainerSpec{}, err
}
libcontainerConfig, err := self.readLibcontainerConfig()
if err != nil {
return info.ContainerSpec{}, err
}
spec := libcontainerConfigToContainerSpec(libcontainerConfig, mi)
spec.CreationTime = self.creationTime
if !self.ignoreMetrics.Has(container.DiskUsageMetrics) {
switch self.storageDriver {
case aufsStorageDriver, overlayStorageDriver, zfsStorageDriver:
spec.HasFilesystem = true
}
}
hasFilesystem := !self.ignoreMetrics.Has(container.DiskUsageMetrics)
spec, err := common.GetSpec(self.cgroupPaths, self.machineInfoFactory, self.needNet(), hasFilesystem)
spec.Labels = self.labels
spec.Envs = self.envs
spec.Image = self.image
spec.HasNetwork = self.needNet()
return spec, err
}
@ -320,6 +257,7 @@ func (self *dockerContainerHandler) getFsStats(stats *info.ContainerStats) error
if err != nil {
return err
}
var (
limit uint64
fsType string
@ -336,7 +274,7 @@ func (self *dockerContainerHandler) getFsStats(stats *info.ContainerStats) error
fsStat := info.FsStats{Device: deviceInfo.Device, Type: fsType, Limit: limit}
fsStat.BaseUsage, fsStat.Usage = self.fsHandler.usage()
fsStat.BaseUsage, fsStat.Usage = self.fsHandler.Usage()
stats.Filesystem = append(stats.Filesystem, fsStat)
return nil
@ -401,19 +339,19 @@ func (self *dockerContainerHandler) StopWatchingSubcontainers() error {
}
func (self *dockerContainerHandler) Exists() bool {
return containerlibcontainer.Exists(*dockerRootDir, *dockerRunDir, self.id)
return common.CgroupExists(self.cgroupPaths)
}
func DockerInfo() (map[string]string, error) {
func DockerInfo() (docker.DockerInfo, error) {
client, err := Client()
if err != nil {
return nil, fmt.Errorf("unable to communicate with docker daemon: %v", err)
return docker.DockerInfo{}, fmt.Errorf("unable to communicate with docker daemon: %v", err)
}
info, err := client.Info()
if err != nil {
return nil, err
return docker.DockerInfo{}, err
}
return info.Map(), nil
return *info, nil
}
func DockerImages() ([]docker.APIImages, error) {
@ -427,3 +365,47 @@ func DockerImages() ([]docker.APIImages, error) {
}
return images, nil
}
// Checks whether the dockerInfo reflects a valid docker setup, and returns it if it does, or an
// error otherwise.
func ValidateInfo() (*docker.DockerInfo, error) {
client, err := Client()
if err != nil {
return nil, fmt.Errorf("unable to communicate with docker daemon: %v", err)
}
dockerInfo, err := client.Info()
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.Version()
if err != nil {
return nil, fmt.Errorf("unable to get docker version: %v", err)
}
dockerInfo.ServerVersion = version.Get("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
}

View File

@ -1,367 +0,0 @@
// Copyright 2015 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 libcontainer
import (
"encoding/json"
"io/ioutil"
"path"
"github.com/google/cadvisor/utils"
"github.com/opencontainers/runc/libcontainer"
"github.com/opencontainers/runc/libcontainer/configs"
)
// State represents a running container's state
type preAPIState struct {
// InitPid is the init process id in the parent namespace
InitPid int `json:"init_pid,omitempty"`
// InitStartTime is the init process start time
InitStartTime string `json:"init_start_time,omitempty"`
// Network runtime state.
NetworkState preAPINetworkState `json:"network_state,omitempty"`
// Path to all the cgroups setup for a container. Key is cgroup subsystem name.
CgroupPaths map[string]string `json:"cgroup_paths,omitempty"`
}
// Struct describing the network specific runtime state that will be maintained by libcontainer for all running containers
// Do not depend on it outside of libcontainer.
type preAPINetworkState struct {
// The name of the veth interface on the Host.
VethHost string `json:"veth_host,omitempty"`
// The name of the veth interface created inside the container for the child.
VethChild string `json:"veth_child,omitempty"`
// Net namespace path.
NsPath string `json:"ns_path,omitempty"`
}
type preAPIConfig struct {
// Pathname to container's root filesystem
RootFs string `json:"root_fs,omitempty"`
// Hostname optionally sets the container's hostname if provided
Hostname string `json:"hostname,omitempty"`
// User will set the uid and gid of the executing process running inside the container
User string `json:"user,omitempty"`
// WorkingDir will change the processes current working directory inside the container's rootfs
WorkingDir string `json:"working_dir,omitempty"`
// Env will populate the processes environment with the provided values
// Any values from the parent processes will be cleared before the values
// provided in Env are provided to the process
Env []string `json:"environment,omitempty"`
// Tty when true will allocate a pty slave on the host for access by the container's process
// and ensure that it is mounted inside the container's rootfs
Tty bool `json:"tty,omitempty"`
// Namespaces specifies the container's namespaces that it should setup when cloning the init process
// If a namespace is not provided that namespace is shared from the container's parent process
Namespaces []configs.Namespace `json:"namespaces,omitempty"`
// Capabilities specify the capabilities to keep when executing the process inside the container
// All capbilities not specified will be dropped from the processes capability mask
Capabilities []string `json:"capabilities,omitempty"`
// Networks specifies the container's network setup to be created
Networks []preAPINetwork `json:"networks,omitempty"`
// Routes can be specified to create entries in the route table as the container is started
Routes []*configs.Route `json:"routes,omitempty"`
// Cgroups specifies specific cgroup settings for the various subsystems that the container is
// placed into to limit the resources the container has available
Cgroups *configs.Cgroup `json:"cgroups,omitempty"`
// AppArmorProfile specifies the profile to apply to the process running in the container and is
// change at the time the process is execed
AppArmorProfile string `json:"apparmor_profile,omitempty"`
// ProcessLabel specifies the label to apply to the process running in the container. It is
// commonly used by selinux
ProcessLabel string `json:"process_label,omitempty"`
// RestrictSys will remount /proc/sys, /sys, and mask over sysrq-trigger as well as /proc/irq and
// /proc/bus
RestrictSys bool `json:"restrict_sys,omitempty"`
}
// Network defines configuration for a container's networking stack
//
// The network configuration can be omited from a container causing the
// container to be setup with the host's networking stack
type preAPINetwork struct {
// Type sets the networks type, commonly veth and loopback
Type string `json:"type,omitempty"`
// The bridge to use.
Bridge string `json:"bridge,omitempty"`
// Prefix for the veth interfaces.
VethPrefix string `json:"veth_prefix,omitempty"`
// MacAddress contains the MAC address to set on the network interface
MacAddress string `json:"mac_address,omitempty"`
// Address contains the IPv4 and mask to set on the network interface
Address string `json:"address,omitempty"`
// IPv6Address contains the IPv6 and mask to set on the network interface
IPv6Address string `json:"ipv6_address,omitempty"`
// Gateway sets the gateway address that is used as the default for the interface
Gateway string `json:"gateway,omitempty"`
// IPv6Gateway sets the ipv6 gateway address that is used as the default for the interface
IPv6Gateway string `json:"ipv6_gateway,omitempty"`
// Mtu sets the mtu value for the interface and will be mirrored on both the host and
// container's interfaces if a pair is created, specifically in the case of type veth
// Note: This does not apply to loopback interfaces.
Mtu int `json:"mtu,omitempty"`
// TxQueueLen sets the tx_queuelen value for the interface and will be mirrored on both the host and
// container's interfaces if a pair is created, specifically in the case of type veth
// Note: This does not apply to loopback interfaces.
TxQueueLen int `json:"txqueuelen,omitempty"`
}
type v1Cgroup struct {
configs.Cgroup
// Weight per cgroup per device, can override BlkioWeight.
BlkioWeightDevice string `json:"blkio_weight_device"`
// IO read rate limit per cgroup per device, bytes per second.
BlkioThrottleReadBpsDevice string `json:"blkio_throttle_read_bps_device"`
// IO write rate limit per cgroup per divice, bytes per second.
BlkioThrottleWriteBpsDevice string `json:"blkio_throttle_write_bps_device"`
// IO read rate limit per cgroup per device, IO per second.
BlkioThrottleReadIOPSDevice string `json:"blkio_throttle_read_iops_device"`
// IO write rate limit per cgroup per device, IO per second.
BlkioThrottleWriteIOPSDevice string `json:"blkio_throttle_write_iops_device"`
}
type v1Config struct {
configs.Config
// Cgroups specifies specific cgroup settings for the various subsystems that the container is
// placed into to limit the resources the container has available
Cgroup *v1Cgroup `json:"cgroups"`
}
// State represents a running container's state
type v1State struct {
libcontainer.State
// Config is the container's configuration.
Config v1Config `json:"config"`
}
// Relative path to the libcontainer execdriver directory.
const libcontainerExecDriverPath = "execdriver/native"
// TODO(vmarmol): Deprecate over time as old Dockers are phased out.
func ReadConfig(dockerRoot, dockerRun, containerID string) (*configs.Config, error) {
// Try using the new config if it is available.
configPath := configPath(dockerRun, containerID)
if utils.FileExists(configPath) {
out, err := ioutil.ReadFile(configPath)
if err != nil {
return nil, err
}
var state libcontainer.State
if err = json.Unmarshal(out, &state); err != nil {
if _, ok := err.(*json.UnmarshalTypeError); ok {
// Since some fields changes in Cgroup struct, it will be failed while unmarshalling to libcontainer.State struct.
// This failure is caused by a change of runc(https://github.com/opencontainers/runc/commit/c6e406af243fab0c9636539c1cb5f4d60fe0787f).
// If we encountered the UnmarshalTypeError, try to unmarshal it again to v1State struct and convert it.
var state v1State
err2 := json.Unmarshal(out, &state)
if err2 != nil {
return nil, err
}
return convertOldConfigToNew(state.Config), nil
} else {
return nil, err
}
}
return &state.Config, nil
}
// Fallback to reading the old config which is comprised of the state and config files.
oldConfigPath := oldConfigPath(dockerRoot, containerID)
out, err := ioutil.ReadFile(oldConfigPath)
if err != nil {
return nil, err
}
// Try reading the preAPIConfig.
var config preAPIConfig
err = json.Unmarshal(out, &config)
if err != nil {
// Try to parse the old pre-API config. The main difference is that namespaces used to be a map, now it is a slice of structs.
// The JSON marshaler will use the non-nested field before the nested one.
type oldLibcontainerConfig struct {
preAPIConfig
OldNamespaces map[string]bool `json:"namespaces,omitempty"`
}
var oldConfig oldLibcontainerConfig
err2 := json.Unmarshal(out, &oldConfig)
if err2 != nil {
// Use original error.
return nil, err
}
// Translate the old pre-API config into the new config.
config = oldConfig.preAPIConfig
for ns := range oldConfig.OldNamespaces {
config.Namespaces = append(config.Namespaces, configs.Namespace{
Type: configs.NamespaceType(ns),
})
}
}
// Read the old state file as well.
state, err := readState(dockerRoot, containerID)
if err != nil {
return nil, err
}
// Convert preAPIConfig + old state file to Config.
// This only converts some of the fields, the ones we use.
// You may need to add fields if the one you're interested in is not available.
var result configs.Config
result.Cgroups = new(configs.Cgroup)
result.Rootfs = config.RootFs
result.Hostname = config.Hostname
result.Namespaces = config.Namespaces
result.Capabilities = config.Capabilities
for _, net := range config.Networks {
n := &configs.Network{
Name: state.NetworkState.VethChild,
Bridge: net.Bridge,
MacAddress: net.MacAddress,
Address: net.Address,
Gateway: net.Gateway,
IPv6Address: net.IPv6Address,
IPv6Gateway: net.IPv6Gateway,
HostInterfaceName: state.NetworkState.VethHost,
}
result.Networks = append(result.Networks, n)
}
result.Routes = config.Routes
if config.Cgroups != nil {
result.Cgroups = config.Cgroups
}
return &result, nil
}
func convertOldConfigToNew(config v1Config) *configs.Config {
var (
result configs.Config
old *v1Cgroup = config.Cgroup
)
result.Rootfs = config.Config.Rootfs
result.Hostname = config.Config.Hostname
result.Namespaces = config.Config.Namespaces
result.Capabilities = config.Config.Capabilities
result.Networks = config.Config.Networks
result.Routes = config.Config.Routes
var newCgroup = &configs.Cgroup{
Name: old.Name,
Parent: old.Parent,
Resources: &configs.Resources{
AllowAllDevices: old.Resources.AllowAllDevices,
AllowedDevices: old.Resources.AllowedDevices,
DeniedDevices: old.Resources.DeniedDevices,
Memory: old.Resources.Memory,
MemoryReservation: old.Resources.MemoryReservation,
MemorySwap: old.Resources.MemorySwap,
KernelMemory: old.Resources.KernelMemory,
CpuShares: old.Resources.CpuShares,
CpuQuota: old.Resources.CpuQuota,
CpuPeriod: old.Resources.CpuPeriod,
CpuRtRuntime: old.Resources.CpuRtRuntime,
CpuRtPeriod: old.Resources.CpuRtPeriod,
CpusetCpus: old.Resources.CpusetCpus,
CpusetMems: old.Resources.CpusetMems,
BlkioWeight: old.Resources.BlkioWeight,
BlkioLeafWeight: old.Resources.BlkioLeafWeight,
Freezer: old.Resources.Freezer,
HugetlbLimit: old.Resources.HugetlbLimit,
OomKillDisable: old.Resources.OomKillDisable,
MemorySwappiness: old.Resources.MemorySwappiness,
NetPrioIfpriomap: old.Resources.NetPrioIfpriomap,
NetClsClassid: old.Resources.NetClsClassid,
},
}
result.Cgroups = newCgroup
return &result
}
func readState(dockerRoot, containerID string) (preAPIState, error) {
// pre-API libcontainer changed how its state was stored, try the old way of a "pid" file
statePath := path.Join(dockerRoot, libcontainerExecDriverPath, containerID, "state.json")
if !utils.FileExists(statePath) {
pidPath := path.Join(dockerRoot, libcontainerExecDriverPath, containerID, "pid")
if utils.FileExists(pidPath) {
// We don't need the old state, return an empty state and we'll gracefully degrade.
return preAPIState{}, nil
}
}
out, err := ioutil.ReadFile(statePath)
if err != nil {
return preAPIState{}, err
}
// Parse the state.
var state preAPIState
err = json.Unmarshal(out, &state)
if err != nil {
return preAPIState{}, err
}
return state, nil
}
// Gets the path to the libcontainer configuration.
func configPath(dockerRun, containerID string) string {
return path.Join(dockerRun, libcontainerExecDriverPath, containerID, "state.json")
}
// Gets the path to the old libcontainer configuration.
func oldConfigPath(dockerRoot, containerID string) string {
return path.Join(dockerRoot, libcontainerExecDriverPath, containerID, "container.json")
}
// Gets whether the specified container exists.
func Exists(dockerRoot, dockerRun, containerID string) bool {
// New or old config must exist for the container to be considered alive.
return utils.FileExists(configPath(dockerRun, containerID)) || utils.FileExists(oldConfigPath(dockerRoot, containerID))
}

View File

@ -19,6 +19,7 @@ import (
"fmt"
"github.com/google/cadvisor/container"
"github.com/google/cadvisor/container/common"
"github.com/google/cadvisor/container/libcontainer"
"github.com/google/cadvisor/fs"
info "github.com/google/cadvisor/info/v1"
@ -39,7 +40,7 @@ type rawFactory struct {
fsInfo fs.FsInfo
// Watcher for inotify events.
watcher *InotifyWatcher
watcher *common.InotifyWatcher
// List of metrics to be ignored.
ignoreMetrics map[container.MetricKind]struct{}
@ -64,20 +65,7 @@ func (self *rawFactory) CanHandleAndAccept(name string) (bool, bool, error) {
}
func (self *rawFactory) DebugInfo() map[string][]string {
out := make(map[string][]string)
// Get information about inotify watches.
watches := self.watcher.GetWatches()
lines := make([]string, 0, len(watches))
for containerName, cgroupWatches := range watches {
lines = append(lines, fmt.Sprintf("%s:", containerName))
for _, cg := range cgroupWatches {
lines = append(lines, fmt.Sprintf("\t%s", cg))
}
}
out["Inotify watches"] = lines
return out
return common.DebugInfo(self.watcher.GetWatches())
}
func Register(machineInfoFactory info.MachineInfoFactory, fsInfo fs.FsInfo, ignoreMetrics map[container.MetricKind]struct{}) error {
@ -89,7 +77,7 @@ func Register(machineInfoFactory info.MachineInfoFactory, fsInfo fs.FsInfo, igno
return fmt.Errorf("failed to find supported cgroup mounts for the raw factory")
}
watcher, err := NewInotifyWatcher()
watcher, err := common.NewInotifyWatcher()
if err != nil {
return err
}

View File

@ -18,17 +18,14 @@ package raw
import (
"fmt"
"io/ioutil"
"os"
"path"
"strconv"
"strings"
"time"
"github.com/google/cadvisor/container"
"github.com/google/cadvisor/container/common"
"github.com/google/cadvisor/container/libcontainer"
"github.com/google/cadvisor/fs"
info "github.com/google/cadvisor/info/v1"
"github.com/google/cadvisor/utils"
"github.com/google/cadvisor/utils/machine"
"github.com/golang/glog"
@ -45,7 +42,7 @@ type rawContainerHandler struct {
machineInfoFactory info.MachineInfoFactory
// Inotify event watcher.
watcher *InotifyWatcher
watcher *common.InotifyWatcher
// Signal for watcher thread to stop.
stopWatcher chan error
@ -58,7 +55,7 @@ type rawContainerHandler struct {
cgroupManager cgroups.Manager
fsInfo fs.FsInfo
externalMounts []mount
externalMounts []common.Mount
rootFs string
@ -72,14 +69,10 @@ func isRootCgroup(name string) bool {
return name == "/"
}
func newRawContainerHandler(name string, cgroupSubsystems *libcontainer.CgroupSubsystems, machineInfoFactory info.MachineInfoFactory, fsInfo fs.FsInfo, watcher *InotifyWatcher, rootFs string, ignoreMetrics container.MetricSet) (container.ContainerHandler, error) {
// Create the cgroup paths.
cgroupPaths := make(map[string]string, len(cgroupSubsystems.MountPoints))
for key, val := range cgroupSubsystems.MountPoints {
cgroupPaths[key] = path.Join(val, name)
}
func newRawContainerHandler(name string, cgroupSubsystems *libcontainer.CgroupSubsystems, machineInfoFactory info.MachineInfoFactory, fsInfo fs.FsInfo, watcher *common.InotifyWatcher, rootFs string, ignoreMetrics container.MetricSet) (container.ContainerHandler, error) {
cgroupPaths := common.MakeCgroupPaths(cgroupSubsystems.MountPoints, name)
cHints, err := getContainerHintsFromFile(*argContainerHints)
cHints, err := common.GetContainerHintsFromFile(*common.ArgContainerHints)
if err != nil {
return nil, err
}
@ -92,7 +85,7 @@ func newRawContainerHandler(name string, cgroupSubsystems *libcontainer.CgroupSu
Paths: cgroupPaths,
}
var externalMounts []mount
var externalMounts []common.Mount
for _, container := range cHints.AllHosts {
if name == container.FullName {
externalMounts = container.Mounts
@ -128,38 +121,6 @@ func (self *rawContainerHandler) ContainerReference() (info.ContainerReference,
}, nil
}
func readString(dirpath string, file string) string {
cgroupFile := path.Join(dirpath, file)
// Ignore non-existent files
if !utils.FileExists(cgroupFile) {
return ""
}
// Read
out, err := ioutil.ReadFile(cgroupFile)
if err != nil {
glog.Errorf("raw driver: Failed to read %q: %s", cgroupFile, err)
return ""
}
return strings.TrimSpace(string(out))
}
func readUInt64(dirpath string, file string) uint64 {
out := readString(dirpath, file)
if out == "" {
return 0
}
val, err := strconv.ParseUint(out, 10, 64)
if err != nil {
glog.Errorf("raw driver: Failed to parse int %q from file %q: %s", out, path.Join(dirpath, file), err)
return 0
}
return val
}
func (self *rawContainerHandler) GetRootNetworkDevices() ([]info.NetInfo, error) {
nd := []info.NetInfo{}
if isRootCgroup(self.name) {
@ -179,66 +140,21 @@ func (self *rawContainerHandler) Start() {}
func (self *rawContainerHandler) Cleanup() {}
func (self *rawContainerHandler) GetSpec() (info.ContainerSpec, error) {
var spec info.ContainerSpec
// The raw driver assumes unified hierarchy containers.
// Get the lowest creation time from all hierarchies as the container creation time.
now := time.Now()
lowestTime := now
for _, cgroupPath := range self.cgroupPaths {
// The modified time of the cgroup directory changes whenever a subcontainer is created.
// eg. /docker will have creation time matching the creation of latest docker container.
// Use clone_children as a workaround as it isn't usually modified. It is only likely changed
// immediately after creating a container.
cgroupPath = path.Join(cgroupPath, "cgroup.clone_children")
fi, err := os.Stat(cgroupPath)
if err == nil && fi.ModTime().Before(lowestTime) {
lowestTime = fi.ModTime()
}
}
if lowestTime != now {
spec.CreationTime = lowestTime
}
// Get machine info.
mi, err := self.machineInfoFactory.GetMachineInfo()
const hasNetwork = false
hasFilesystem := isRootCgroup(self.name) || len(self.externalMounts) > 0
spec, err := common.GetSpec(self.cgroupPaths, self.machineInfoFactory, hasNetwork, hasFilesystem)
if err != nil {
return spec, err
}
// CPU.
cpuRoot, ok := self.cgroupPaths["cpu"]
if ok {
if utils.FileExists(cpuRoot) {
spec.HasCpu = true
spec.Cpu.Limit = readUInt64(cpuRoot, "cpu.shares")
spec.Cpu.Period = readUInt64(cpuRoot, "cpu.cfs_period_us")
quota := readString(cpuRoot, "cpu.cfs_quota_us")
if quota != "" && quota != "-1" {
val, err := strconv.ParseUint(quota, 10, 64)
if err != nil {
glog.Errorf("raw driver: Failed to parse CPUQuota from %q: %s", path.Join(cpuRoot, "cpu.cfs_quota_us"), err)
}
spec.Cpu.Quota = val
}
if isRootCgroup(self.name) {
// Check physical network devices for root container.
nd, err := self.GetRootNetworkDevices()
if err != nil {
return spec, err
}
}
spec.HasNetwork = spec.HasNetwork || len(nd) != 0
// Cpu Mask.
// This will fail for non-unified hierarchies. We'll return the whole machine mask in that case.
cpusetRoot, ok := self.cgroupPaths["cpuset"]
if ok {
if utils.FileExists(cpusetRoot) {
spec.HasCpu = true
mask := readString(cpusetRoot, "cpuset.cpus")
spec.Cpu.Mask = utils.FixCpuMask(mask, mi.NumCores)
}
}
// Memory
if self.name == "/" {
// Get memory and swap limits of the running machine
memLimit, err := machine.GetMachineMemoryCapacity()
if err != nil {
@ -256,35 +172,8 @@ func (self *rawContainerHandler) GetSpec() (info.ContainerSpec, error) {
} else {
spec.Memory.SwapLimit = uint64(swapLimit)
}
} else {
memoryRoot, ok := self.cgroupPaths["memory"]
if ok {
if utils.FileExists(memoryRoot) {
spec.HasMemory = true
spec.Memory.Limit = readUInt64(memoryRoot, "memory.limit_in_bytes")
spec.Memory.SwapLimit = readUInt64(memoryRoot, "memory.memsw.limit_in_bytes")
}
}
}
// Fs.
if self.name == "/" || self.externalMounts != nil {
spec.HasFilesystem = true
}
// DiskIo.
if blkioRoot, ok := self.cgroupPaths["blkio"]; ok && utils.FileExists(blkioRoot) {
spec.HasDiskIo = true
}
// Check physical network devices for root container.
nd, err := self.GetRootNetworkDevices()
if err != nil {
return spec, err
}
if len(nd) != 0 {
spec.HasNetwork = true
}
return spec, nil
}
@ -379,39 +268,10 @@ func (self *rawContainerHandler) GetContainerLabels() map[string]string {
return map[string]string{}
}
// Lists all directories under "path" and outputs the results as children of "parent".
func listDirectories(dirpath string, parent string, recursive bool, output map[string]struct{}) error {
// Ignore if this hierarchy does not exist.
if !utils.FileExists(dirpath) {
return nil
}
entries, err := ioutil.ReadDir(dirpath)
if err != nil {
return err
}
for _, entry := range entries {
// We only grab directories.
if entry.IsDir() {
name := path.Join(parent, entry.Name())
output[name] = struct{}{}
// List subcontainers if asked to.
if recursive {
err := listDirectories(path.Join(dirpath, entry.Name()), name, true, output)
if err != nil {
return err
}
}
}
}
return nil
}
func (self *rawContainerHandler) ListContainers(listType container.ListType) ([]info.ContainerReference, error) {
containers := make(map[string]struct{})
for _, cgroupPath := range self.cgroupPaths {
err := listDirectories(cgroupPath, self.name, listType == container.ListRecursive, containers)
err := common.ListDirectories(cgroupPath, self.name, listType == container.ListRecursive, containers)
if err != nil {
return nil, err
}
@ -583,11 +443,5 @@ func (self *rawContainerHandler) StopWatchingSubcontainers() error {
}
func (self *rawContainerHandler) Exists() bool {
// If any cgroup exists, the container is still alive.
for _, cgroupPath := range self.cgroupPaths {
if utils.FileExists(cgroupPath) {
return true
}
}
return false
return common.CgroupExists(self.cgroupPaths)
}

View File

@ -0,0 +1,76 @@
// 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.
package rkt
import (
"fmt"
"net"
"sync"
"time"
rktapi "github.com/coreos/rkt/api/v1alpha"
"golang.org/x/net/context"
"google.golang.org/grpc"
)
const (
defaultRktAPIServiceAddr = "localhost:15441"
timeout = 2 * time.Second
)
var (
rktClient rktapi.PublicAPIClient
rktClientErr error
once sync.Once
)
func Client() (rktapi.PublicAPIClient, error) {
once.Do(func() {
conn, err := net.DialTimeout("tcp", defaultRktAPIServiceAddr, timeout)
if err != nil {
rktClient = nil
rktClientErr = fmt.Errorf("rkt: cannot tcp Dial rkt api service: %v", err)
return
}
conn.Close()
apisvcConn, err := grpc.Dial(defaultRktAPIServiceAddr, grpc.WithInsecure(), grpc.WithTimeout(timeout))
if err != nil {
rktClient = nil
rktClientErr = fmt.Errorf("rkt: cannot grpc Dial rkt api service: %v", err)
return
}
rktClient = rktapi.NewPublicAPIClient(apisvcConn)
})
return rktClient, rktClientErr
}
func RktPath() (string, error) {
client, err := Client()
if err != nil {
return "", err
}
resp, err := client.GetInfo(context.Background(), &rktapi.GetInfoRequest{})
if err != nil {
return "", fmt.Errorf("couldn't GetInfo from rkt api service: %v", err)
}
return resp.Info.GlobalFlags.Dir, nil
}

View File

@ -0,0 +1,104 @@
// 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.
package rkt
import (
"fmt"
"strings"
"github.com/google/cadvisor/container"
"github.com/google/cadvisor/container/libcontainer"
"github.com/google/cadvisor/fs"
info "github.com/google/cadvisor/info/v1"
"github.com/golang/glog"
)
const RktNamespace = "rkt"
type rktFactory struct {
machineInfoFactory info.MachineInfoFactory
cgroupSubsystems *libcontainer.CgroupSubsystems
fsInfo fs.FsInfo
ignoreMetrics container.MetricSet
rktPath string
}
func (self *rktFactory) String() string {
return "rkt"
}
func (self *rktFactory) NewContainerHandler(name string, inHostNamespace bool) (container.ContainerHandler, error) {
client, err := Client()
if err != nil {
return nil, err
}
rootFs := "/"
if !inHostNamespace {
rootFs = "/rootfs"
}
return newRktContainerHandler(name, client, self.rktPath, self.cgroupSubsystems, self.machineInfoFactory, self.fsInfo, rootFs, self.ignoreMetrics)
}
func (self *rktFactory) CanHandleAndAccept(name string) (bool, bool, error) {
// will ignore all cgroup names that don't either correspond to the machine.slice that is the pod or the containers that belong to the pod
// only works for machined rkt pods at the moment
if strings.HasPrefix(name, "/machine.slice/machine-rkt\\x2d") {
accept, err := verifyName(name)
return true, accept, err
}
return false, false, fmt.Errorf("%s not handled by rkt handler", name)
}
func (self *rktFactory) DebugInfo() map[string][]string {
return map[string][]string{}
}
func Register(machineInfoFactory info.MachineInfoFactory, fsInfo fs.FsInfo, ignoreMetrics container.MetricSet) error {
_, err := Client()
if err != nil {
return fmt.Errorf("unable to communicate with Rkt api service: %v", err)
}
rktPath, err := RktPath()
if err != nil {
return fmt.Errorf("unable to get the RktPath variable %v", err)
}
cgroupSubsystems, err := libcontainer.GetCgroupSubsystems()
if err != nil {
return fmt.Errorf("failed to get cgroup subsystems: %v", err)
}
if len(cgroupSubsystems.Mounts) == 0 {
return fmt.Errorf("failed to find supported cgroup mounts for the raw factory")
}
glog.Infof("Registering Rkt factory")
factory := &rktFactory{
machineInfoFactory: machineInfoFactory,
fsInfo: fsInfo,
cgroupSubsystems: &cgroupSubsystems,
ignoreMetrics: ignoreMetrics,
rktPath: rktPath,
}
container.RegisterContainerHandlerFactory(factory)
return nil
}

View File

@ -0,0 +1,327 @@
// 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.
// Handler for "rkt" containers.
package rkt
import (
"fmt"
"os"
"path"
"time"
rktapi "github.com/coreos/rkt/api/v1alpha"
"github.com/google/cadvisor/container"
"github.com/google/cadvisor/container/common"
"github.com/google/cadvisor/container/libcontainer"
"github.com/google/cadvisor/fs"
info "github.com/google/cadvisor/info/v1"
"golang.org/x/net/context"
"github.com/golang/glog"
"github.com/opencontainers/runc/libcontainer/cgroups"
cgroupfs "github.com/opencontainers/runc/libcontainer/cgroups/fs"
"github.com/opencontainers/runc/libcontainer/configs"
)
type rktContainerHandler struct {
rktClient rktapi.PublicAPIClient
// Name of the container for this handler.
name string
cgroupSubsystems *libcontainer.CgroupSubsystems
machineInfoFactory info.MachineInfoFactory
// Absolute path to the cgroup hierarchies of this container.
// (e.g.: "cpu" -> "/sys/fs/cgroup/cpu/test")
cgroupPaths map[string]string
// Manager of this container's cgroups.
cgroupManager cgroups.Manager
// Whether this container has network isolation enabled.
hasNetwork bool
fsInfo fs.FsInfo
rootFs string
isPod bool
aliases []string
pid int
rootfsStorageDir string
labels map[string]string
// Filesystem handler.
fsHandler common.FsHandler
ignoreMetrics container.MetricSet
apiPod *rktapi.Pod
}
func newRktContainerHandler(name string, rktClient rktapi.PublicAPIClient, rktPath string, cgroupSubsystems *libcontainer.CgroupSubsystems, machineInfoFactory info.MachineInfoFactory, fsInfo fs.FsInfo, rootFs string, ignoreMetrics container.MetricSet) (container.ContainerHandler, error) {
aliases := make([]string, 1)
isPod := false
apiPod := &rktapi.Pod{}
parsed, err := parseName(name)
if err != nil {
return nil, fmt.Errorf("this should be impossible!, new handler failing, but factory allowed, name = %s", name)
}
//rktnetes uses containerID: rkt://fff40827-b994-4e3a-8f88-6427c2c8a5ac:nginx
if parsed.Container == "" {
isPod = true
aliases = append(aliases, "rkt://"+parsed.Pod)
} else {
aliases = append(aliases, "rkt://"+parsed.Pod+":"+parsed.Container)
}
pid := os.Getpid()
labels := make(map[string]string)
resp, err := rktClient.InspectPod(context.Background(), &rktapi.InspectPodRequest{
Id: parsed.Pod,
})
if err != nil {
return nil, err
} else {
var annotations []*rktapi.KeyValue
if parsed.Container == "" {
pid = int(resp.Pod.Pid)
apiPod = resp.Pod
annotations = resp.Pod.Annotations
} else {
var ok bool
if annotations, ok = findAnnotations(resp.Pod.Apps, parsed.Container); !ok {
glog.Warningf("couldn't find application in Pod matching %v", parsed.Container)
}
}
labels = createLabels(annotations)
}
cgroupPaths := common.MakeCgroupPaths(cgroupSubsystems.MountPoints, name)
// Generate the equivalent cgroup manager for this container.
cgroupManager := &cgroupfs.Manager{
Cgroups: &configs.Cgroup{
Name: name,
},
Paths: cgroupPaths,
}
hasNetwork := false
if isPod {
hasNetwork = true
}
rootfsStorageDir := getRootFs(rktPath, parsed)
handler := &rktContainerHandler{
name: name,
rktClient: rktClient,
cgroupSubsystems: cgroupSubsystems,
machineInfoFactory: machineInfoFactory,
cgroupPaths: cgroupPaths,
cgroupManager: cgroupManager,
fsInfo: fsInfo,
hasNetwork: hasNetwork,
rootFs: rootFs,
isPod: isPod,
aliases: aliases,
pid: pid,
labels: labels,
rootfsStorageDir: rootfsStorageDir,
ignoreMetrics: ignoreMetrics,
apiPod: apiPod,
}
if !ignoreMetrics.Has(container.DiskUsageMetrics) {
handler.fsHandler = common.NewFsHandler(time.Minute, rootfsStorageDir, "", fsInfo)
}
return handler, nil
}
func findAnnotations(apps []*rktapi.App, container string) ([]*rktapi.KeyValue, bool) {
for _, app := range apps {
if app.Name == container {
return app.Annotations, true
}
}
return nil, false
}
func createLabels(annotations []*rktapi.KeyValue) map[string]string {
labels := make(map[string]string)
for _, kv := range annotations {
labels[kv.Key] = kv.Value
}
return labels
}
func (handler *rktContainerHandler) ContainerReference() (info.ContainerReference, error) {
return info.ContainerReference{
Name: handler.name,
Aliases: handler.aliases,
Namespace: RktNamespace,
Labels: handler.labels,
}, nil
}
func (handler *rktContainerHandler) Start() {
handler.fsHandler.Start()
}
func (handler *rktContainerHandler) Cleanup() {
handler.fsHandler.Stop()
}
func (handler *rktContainerHandler) GetSpec() (info.ContainerSpec, error) {
hasNetwork := handler.hasNetwork && !handler.ignoreMetrics.Has(container.NetworkUsageMetrics)
hasFilesystem := !handler.ignoreMetrics.Has(container.DiskUsageMetrics)
return common.GetSpec(handler.cgroupPaths, handler.machineInfoFactory, hasNetwork, hasFilesystem)
}
func (handler *rktContainerHandler) getFsStats(stats *info.ContainerStats) error {
if handler.ignoreMetrics.Has(container.DiskUsageMetrics) {
return nil
}
deviceInfo, err := handler.fsInfo.GetDirFsDevice(handler.rootfsStorageDir)
if err != nil {
return err
}
mi, err := handler.machineInfoFactory.GetMachineInfo()
if err != nil {
return err
}
var limit uint64 = 0
// Use capacity as limit.
for _, fs := range mi.Filesystems {
if fs.Device == deviceInfo.Device {
limit = fs.Capacity
break
}
}
fsStat := info.FsStats{Device: deviceInfo.Device, Limit: limit}
fsStat.BaseUsage, fsStat.Usage = handler.fsHandler.Usage()
stats.Filesystem = append(stats.Filesystem, fsStat)
return nil
}
func (handler *rktContainerHandler) GetStats() (*info.ContainerStats, error) {
stats, err := libcontainer.GetStats(handler.cgroupManager, handler.rootFs, handler.pid, handler.ignoreMetrics)
if err != nil {
return stats, err
}
// Get filesystem stats.
err = handler.getFsStats(stats)
if err != nil {
return stats, err
}
return stats, nil
}
func (handler *rktContainerHandler) GetCgroupPath(resource string) (string, error) {
path, ok := handler.cgroupPaths[resource]
if !ok {
return "", fmt.Errorf("could not find path for resource %q for container %q\n", resource, handler.name)
}
return path, nil
}
func (handler *rktContainerHandler) GetContainerLabels() map[string]string {
return handler.labels
}
func (handler *rktContainerHandler) ListContainers(listType container.ListType) ([]info.ContainerReference, error) {
containers := make(map[string]struct{})
// Rkt containers do not have subcontainers, only the "Pod" does.
if handler.isPod == false {
var ret []info.ContainerReference
return ret, nil
}
// Turn the system.slice cgroups into the Pod's subcontainers
for _, cgroupPath := range handler.cgroupPaths {
err := common.ListDirectories(path.Join(cgroupPath, "system.slice"), path.Join(handler.name, "system.slice"), listType == container.ListRecursive, containers)
if err != nil {
return nil, err
}
}
// Create the container references. for the Pod's subcontainers
ret := make([]info.ContainerReference, 0, len(handler.apiPod.Apps))
for cont := range containers {
aliases := make([]string, 1)
parsed, err := parseName(cont)
if err != nil {
return nil, fmt.Errorf("this should be impossible!, unable to parse rkt subcontainer name = %s", cont)
}
aliases = append(aliases, parsed.Pod+":"+parsed.Container)
labels := make(map[string]string)
if annotations, ok := findAnnotations(handler.apiPod.Apps, parsed.Container); !ok {
glog.Warningf("couldn't find application in Pod matching %v", parsed.Container)
} else {
labels = createLabels(annotations)
}
ret = append(ret, info.ContainerReference{
Name: cont,
Aliases: aliases,
Namespace: RktNamespace,
Labels: labels,
})
}
return ret, nil
}
func (handler *rktContainerHandler) ListThreads(listType container.ListType) ([]int, error) {
// TODO(sjpotter): Implement? Not implemented with docker yet
return nil, nil
}
func (handler *rktContainerHandler) ListProcesses(listType container.ListType) ([]int, error) {
return libcontainer.GetProcesses(handler.cgroupManager)
}
func (handler *rktContainerHandler) WatchSubcontainers(events chan container.SubcontainerEvent) error {
return fmt.Errorf("watch is unimplemented in the Rkt container driver")
}
func (handler *rktContainerHandler) StopWatchingSubcontainers() error {
// No-op for Rkt driver.
return nil
}
func (handler *rktContainerHandler) Exists() bool {
return common.CgroupExists(handler.cgroupPaths)
}

View File

@ -0,0 +1,93 @@
// 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.
package rkt
import (
"fmt"
"io/ioutil"
"path"
"strings"
"github.com/golang/glog"
)
type parsedName struct {
Pod string
Container string
}
func verifyName(name string) (bool, error) {
_, err := parseName(name)
return err == nil, err
}
/* Parse cgroup name into a pod/container name struct
Example cgroup fs name
pod - /sys/fs/cgroup/cpu/machine.slice/machine-rkt\\x2df556b64a\\x2d17a7\\x2d47d7\\x2d93ec\\x2def2275c3d67e.scope/
container under pod - /sys/fs/cgroup/cpu/machine.slice/machine-rkt\\x2df556b64a\\x2d17a7\\x2d47d7\\x2d93ec\\x2def2275c3d67e.scope/system.slice/alpine-sh.service
*/
//TODO{sjpotter}: this currently only recognizes machined started pods, which actually doesn't help with k8s which uses them as systemd services, need a solution for both
func parseName(name string) (*parsedName, error) {
splits := strings.Split(name, "/")
if len(splits) == 3 || len(splits) == 5 {
parsed := &parsedName{}
if splits[1] == "machine.slice" {
replacer := strings.NewReplacer("machine-rkt\\x2d", "", ".scope", "", "\\x2d", "-")
parsed.Pod = replacer.Replace(splits[2])
if len(splits) == 3 {
return parsed, nil
}
if splits[3] == "system.slice" {
parsed.Container = strings.Replace(splits[4], ".service", "", -1)
return parsed, nil
}
}
}
return nil, fmt.Errorf("%s not handled by rkt handler", name)
}
// Gets a Rkt container's overlay upper dir
func getRootFs(root string, parsed *parsedName) string {
/* Example of where it stores the upper dir key
for container
/var/lib/rkt/pods/run/bc793ec6-c48f-4480-99b5-6bec16d52210/appsinfo/alpine-sh/treeStoreID
for pod
/var/lib/rkt/pods/run/f556b64a-17a7-47d7-93ec-ef2275c3d67e/stage1TreeStoreID
*/
var tree string
if parsed.Container == "" {
tree = path.Join(root, "pods/run", parsed.Pod, "stage1TreeStoreID")
} else {
tree = path.Join(root, "pods/run", parsed.Pod, "appsinfo", parsed.Container, "treeStoreID")
}
bytes, err := ioutil.ReadFile(tree)
if err != nil {
glog.Infof("ReadFile failed, couldn't read %v to get upper dir: %v", tree, err)
return ""
}
s := string(bytes)
/* Example of where the upper dir is stored via key read above
/var/lib/rkt/pods/run/bc793ec6-c48f-4480-99b5-6bec16d52210/overlay/deps-sha512-82a099e560a596662b15dec835e9adabab539cad1f41776a30195a01a8f2f22b/
*/
return path.Join(root, "pods/run", parsed.Pod, "overlay", s)
}

View File

@ -0,0 +1,57 @@
// 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.
package systemd
import (
"fmt"
"strings"
"github.com/google/cadvisor/container"
"github.com/google/cadvisor/fs"
info "github.com/google/cadvisor/info/v1"
"github.com/golang/glog"
)
type systemdFactory struct{}
func (f *systemdFactory) String() string {
return "systemd"
}
func (f *systemdFactory) NewContainerHandler(name string, inHostNamespace bool) (container.ContainerHandler, error) {
return nil, fmt.Errorf("Not yet supported")
}
func (f *systemdFactory) CanHandleAndAccept(name string) (bool, bool, error) {
// on systemd using devicemapper each mount into the container has an associated cgroup that we ignore.
// for details on .mount units: http://man7.org/linux/man-pages/man5/systemd.mount.5.html
if strings.HasSuffix(name, ".mount") {
return true, false, nil
}
return false, false, fmt.Errorf("%s not handled by systemd handler", name)
}
func (f *systemdFactory) DebugInfo() map[string][]string {
return map[string][]string{}
}
// Register registers the systemd container factory.
func Register(machineInfoFactory info.MachineInfoFactory, fsInfo fs.FsInfo, ignoreMetrics container.MetricSet) error {
glog.Infof("Registering systemd factory")
factory := &systemdFactory{}
container.RegisterContainerHandlerFactory(factory)
return nil
}

View File

@ -19,7 +19,6 @@ package fs
import (
"bufio"
"encoding/json"
"fmt"
"io/ioutil"
"os"
@ -40,6 +39,7 @@ import (
const (
LabelSystemRoot = "root"
LabelDockerImages = "docker-images"
LabelRktImages = "rkt-images"
)
type partition struct {
@ -62,8 +62,14 @@ type RealFsInfo struct {
type Context struct {
// docker root directory.
DockerRoot string
DockerInfo map[string]string
Docker DockerContext
RktPath string
}
type DockerContext struct {
Root string
Driver string
DriverStatus map[string]string
}
func NewFsInfo(context Context) (FsInfo, error) {
@ -76,6 +82,11 @@ func NewFsInfo(context Context) (FsInfo, error) {
labels: make(map[string]string, 0),
dmsetup: &defaultDmsetupClient{},
}
fsInfo.addSystemRootLabel(mounts)
fsInfo.addDockerImagesLabel(context, mounts)
fsInfo.addRktImagesLabel(context, mounts)
supportedFsType := map[string]bool{
// all ext systems are checked through prefix.
"btrfs": true,
@ -102,12 +113,7 @@ func NewFsInfo(context Context) (FsInfo, error) {
}
}
// need to call this before the log line below printing out the partitions, as this function may
// add a "partition" for devicemapper to fsInfo.partitions
fsInfo.addDockerImagesLabel(context)
glog.Infof("Filesystem partitions: %+v", fsInfo.partitions)
fsInfo.addSystemRootLabel()
return fsInfo, nil
}
@ -115,22 +121,17 @@ func NewFsInfo(context Context) (FsInfo, error) {
// docker is using devicemapper for its storage driver. If a loopback device is being used, don't
// return any information or error, as we want to report based on the actual partition where the
// loopback file resides, inside of the loopback file itself.
func (self *RealFsInfo) getDockerDeviceMapperInfo(dockerInfo map[string]string) (string, *partition, error) {
if storageDriver, ok := dockerInfo["Driver"]; ok && storageDriver != DeviceMapper.String() {
func (self *RealFsInfo) getDockerDeviceMapperInfo(context DockerContext) (string, *partition, error) {
if context.Driver != DeviceMapper.String() {
return "", nil, nil
}
var driverStatus [][]string
if err := json.Unmarshal([]byte(dockerInfo["DriverStatus"]), &driverStatus); err != nil {
return "", nil, err
}
dataLoopFile := dockerStatusValue(driverStatus, "Data loop file")
dataLoopFile := context.DriverStatus["Data loop file"]
if len(dataLoopFile) > 0 {
return "", nil, nil
}
dev, major, minor, blockSize, err := dockerDMDevice(driverStatus, self.dmsetup)
dev, major, minor, blockSize, err := dockerDMDevice(context.DriverStatus, self.dmsetup)
if err != nil {
return "", nil, err
}
@ -144,19 +145,24 @@ func (self *RealFsInfo) getDockerDeviceMapperInfo(dockerInfo map[string]string)
}
// addSystemRootLabel attempts to determine which device contains the mount for /.
func (self *RealFsInfo) addSystemRootLabel() {
for src, p := range self.partitions {
if p.mountpoint == "/" {
if _, ok := self.labels[LabelSystemRoot]; !ok {
self.labels[LabelSystemRoot] = src
func (self *RealFsInfo) addSystemRootLabel(mounts []*mount.Info) {
for _, m := range mounts {
if m.Mountpoint == "/" {
self.partitions[m.Source] = partition{
fsType: m.Fstype,
mountpoint: m.Mountpoint,
major: uint(m.Major),
minor: uint(m.Minor),
}
self.labels[LabelSystemRoot] = m.Source
return
}
}
}
// addDockerImagesLabel attempts to determine which device contains the mount for docker images.
func (self *RealFsInfo) addDockerImagesLabel(context Context) {
dockerDev, dockerPartition, err := self.getDockerDeviceMapperInfo(context.DockerInfo)
func (self *RealFsInfo) addDockerImagesLabel(context Context, mounts []*mount.Info) {
dockerDev, dockerPartition, err := self.getDockerDeviceMapperInfo(context.Docker)
if err != nil {
glog.Warningf("Could not get Docker devicemapper device: %v", err)
}
@ -164,48 +170,64 @@ func (self *RealFsInfo) addDockerImagesLabel(context Context) {
self.partitions[dockerDev] = *dockerPartition
self.labels[LabelDockerImages] = dockerDev
} else {
dockerPaths := getDockerImagePaths(context)
self.updateContainerImagesPath(LabelDockerImages, mounts, getDockerImagePaths(context))
}
}
for src, p := range self.partitions {
self.updateDockerImagesPath(src, p.mountpoint, dockerPaths)
func (self *RealFsInfo) addRktImagesLabel(context Context, mounts []*mount.Info) {
if context.RktPath != "" {
rktPath := context.RktPath
rktImagesPaths := map[string]struct{}{
"/": {},
}
for rktPath != "/" && rktPath != "." {
rktImagesPaths[rktPath] = struct{}{}
rktPath = filepath.Dir(rktPath)
}
self.updateContainerImagesPath(LabelRktImages, mounts, rktImagesPaths)
}
}
// Generate a list of possible mount points for docker image management from the docker root directory.
// Right now, we look for each type of supported graph driver directories, but we can do better by parsing
// some of the context from `docker info`.
func getDockerImagePaths(context Context) []string {
func getDockerImagePaths(context Context) map[string]struct{} {
dockerImagePaths := map[string]struct{}{
"/": {},
}
// TODO(rjnagal): Detect docker root and graphdriver directories from docker info.
dockerRoot := context.DockerRoot
dockerImagePaths := []string{}
dockerRoot := context.Docker.Root
for _, dir := range []string{"devicemapper", "btrfs", "aufs", "overlay", "zfs"} {
dockerImagePaths = append(dockerImagePaths, path.Join(dockerRoot, dir))
dockerImagePaths[path.Join(dockerRoot, dir)] = struct{}{}
}
for dockerRoot != "/" && dockerRoot != "." {
dockerImagePaths = append(dockerImagePaths, dockerRoot)
dockerImagePaths[dockerRoot] = struct{}{}
dockerRoot = filepath.Dir(dockerRoot)
}
dockerImagePaths = append(dockerImagePaths, "/")
return dockerImagePaths
}
// This method compares the mountpoint with possible docker image mount points. If a match is found,
// docker images label is added to the partition.
func (self *RealFsInfo) updateDockerImagesPath(source string, mountpoint string, dockerImagePaths []string) {
for _, v := range dockerImagePaths {
if v == mountpoint {
if i, ok := self.labels[LabelDockerImages]; ok {
// pick the innermost mountpoint.
mnt := self.partitions[i].mountpoint
if len(mnt) < len(mountpoint) {
self.labels[LabelDockerImages] = source
}
} else {
self.labels[LabelDockerImages] = source
// This method compares the mountpoints with possible container image mount points. If a match is found,
// the label is added to the partition.
func (self *RealFsInfo) updateContainerImagesPath(label string, mounts []*mount.Info, containerImagePaths map[string]struct{}) {
var useMount *mount.Info
for _, m := range mounts {
if _, ok := containerImagePaths[m.Mountpoint]; ok {
if useMount == nil || (len(useMount.Mountpoint) < len(m.Mountpoint)) {
useMount = m
}
}
}
if useMount != nil {
self.partitions[useMount.Source] = partition{
fsType: useMount.Fstype,
mountpoint: useMount.Mountpoint,
major: uint(useMount.Major),
minor: uint(useMount.Minor),
}
self.labels[label] = useMount.Source
}
}
func (self *RealFsInfo) GetDeviceForLabel(label string) (string, error) {
@ -412,15 +434,6 @@ func getVfsStats(path string) (total uint64, free uint64, avail uint64, inodes u
return total, free, avail, inodes, inodesFree, nil
}
func dockerStatusValue(status [][]string, target string) string {
for _, v := range status {
if len(v) == 2 && strings.ToLower(v[0]) == strings.ToLower(target) {
return v[1]
}
}
return ""
}
// dmsetupClient knows to to interact with dmsetup to retrieve information about devicemapper.
type dmsetupClient interface {
table(poolName string) ([]byte, error)
@ -438,9 +451,9 @@ func (*defaultDmsetupClient) table(poolName string) ([]byte, error) {
// Devicemapper thin provisioning is detailed at
// https://www.kernel.org/doc/Documentation/device-mapper/thin-provisioning.txt
func dockerDMDevice(driverStatus [][]string, dmsetup dmsetupClient) (string, uint, uint, uint, error) {
poolName := dockerStatusValue(driverStatus, "Pool Name")
if len(poolName) == 0 {
func dockerDMDevice(driverStatus map[string]string, dmsetup dmsetupClient) (string, uint, uint, uint, error) {
poolName, ok := driverStatus["Pool Name"]
if !ok || len(poolName) == 0 {
return "", 0, 0, 0, fmt.Errorf("Could not get dm pool name")
}

View File

@ -16,7 +16,6 @@
package manager
import (
"encoding/json"
"flag"
"fmt"
"os"
@ -31,6 +30,8 @@ import (
"github.com/google/cadvisor/container"
"github.com/google/cadvisor/container/docker"
"github.com/google/cadvisor/container/raw"
"github.com/google/cadvisor/container/rkt"
"github.com/google/cadvisor/container/systemd"
"github.com/google/cadvisor/events"
"github.com/google/cadvisor/fs"
info "github.com/google/cadvisor/info/v1"
@ -60,7 +61,7 @@ type Manager interface {
// Stops the manager.
Stop() error
// Get information about a container.
// information about a container.
GetContainerInfo(containerName string, query *info.ContainerInfoRequest) (*info.ContainerInfo, error)
// Get V2 information about a container.
@ -131,11 +132,23 @@ func New(memoryCache *memory.InMemoryCache, sysfs sysfs.SysFs, maxHousekeepingIn
}
glog.Infof("cAdvisor running in container: %q", selfContainer)
dockerInfo, err := docker.DockerInfo()
dockerInfo, err := dockerInfo()
if err != nil {
glog.Warningf("Unable to connect to Docker: %v", err)
}
context := fs.Context{DockerRoot: docker.RootDir(), DockerInfo: dockerInfo}
rktPath, err := rkt.RktPath()
if err != nil {
glog.Warningf("unable to connect to Rkt api service: %v", err)
}
context := fs.Context{
Docker: fs.DockerContext{
Root: docker.RootDir(),
Driver: dockerInfo.Driver,
DriverStatus: dockerInfo.DriverStatus,
},
RktPath: rktPath,
}
fsInfo, err := fs.NewFsInfo(context)
if err != nil {
return nil, err
@ -206,13 +219,21 @@ type manager struct {
// Start the container manager.
func (self *manager) Start() error {
// Register Docker container factory.
err := docker.Register(self, self.fsInfo, self.ignoreMetrics)
if err != nil {
glog.Errorf("Docker container factory registration failed: %v.", err)
}
// Register the raw driver.
err = rkt.Register(self, self.fsInfo, self.ignoreMetrics)
if err != nil {
glog.Errorf("Registration of the rkt container factory failed: %v", err)
}
err = systemd.Register(self, self.fsInfo, self.ignoreMetrics)
if err != nil {
glog.Errorf("Registration of the systemd container factory failed: %v", err)
}
err = raw.Register(self, self.fsInfo, self.ignoreMetrics)
if err != nil {
glog.Errorf("Registration of the raw container factory failed: %v", err)
@ -1150,58 +1171,31 @@ func (m *manager) DockerImages() ([]DockerImage, error) {
}
func (m *manager) DockerInfo() (DockerStatus, error) {
info, err := docker.DockerInfo()
return dockerInfo()
}
func dockerInfo() (DockerStatus, error) {
dockerInfo, err := docker.DockerInfo()
if err != nil {
return DockerStatus{}, err
}
versionInfo, err := m.GetVersionInfo()
versionInfo, err := getVersionInfo()
if err != nil {
return DockerStatus{}, err
}
out := DockerStatus{}
out.Version = versionInfo.DockerVersion
if val, ok := info["KernelVersion"]; ok {
out.KernelVersion = val
}
if val, ok := info["OperatingSystem"]; ok {
out.OS = val
}
if val, ok := info["Name"]; ok {
out.Hostname = val
}
if val, ok := info["DockerRootDir"]; ok {
out.RootDir = val
}
if val, ok := info["Driver"]; ok {
out.Driver = val
}
if val, ok := info["ExecutionDriver"]; ok {
out.ExecDriver = val
}
if val, ok := info["Images"]; ok {
n, err := strconv.Atoi(val)
if err == nil {
out.NumImages = n
}
}
if val, ok := info["Containers"]; ok {
n, err := strconv.Atoi(val)
if err == nil {
out.NumContainers = n
}
}
if val, ok := info["DriverStatus"]; ok {
var driverStatus [][]string
err := json.Unmarshal([]byte(val), &driverStatus)
if err != nil {
return DockerStatus{}, err
}
out.DriverStatus = make(map[string]string)
for _, v := range driverStatus {
if len(v) == 2 {
out.DriverStatus[v[0]] = v[1]
}
}
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
}

View File

@ -18,6 +18,7 @@ import (
"bytes"
"encoding/binary"
"fmt"
"os"
"syscall"
info "github.com/google/cadvisor/info/v1"
@ -219,10 +220,10 @@ func verifyHeader(msg syscall.NetlinkMessage) error {
// Get load stats for a task group.
// id: family id for taskstats.
// fd: fd to path to the cgroup directory under cpu hierarchy.
// cfd: open file to path to the cgroup directory under cpu hierarchy.
// conn: open netlink connection used to communicate with kernel.
func getLoadStats(id uint16, fd uintptr, conn *Connection) (info.LoadStats, error) {
msg := prepareCmdMessage(id, fd)
func getLoadStats(id uint16, cfd *os.File, conn *Connection) (info.LoadStats, error) {
msg := prepareCmdMessage(id, cfd.Fd())
err := conn.WriteMessage(msg.toRawMsg())
if err != nil {
return info.LoadStats{}, err

View File

@ -66,11 +66,12 @@ func (self *NetlinkReader) GetCpuLoad(name string, path string) (info.LoadStats,
}
cfd, err := os.Open(path)
defer cfd.Close()
if err != nil {
return info.LoadStats{}, fmt.Errorf("failed to open cgroup path %s: %q", path, err)
}
stats, err := getLoadStats(self.familyId, cfd.Fd(), self.conn)
stats, err := getLoadStats(self.familyId, cfd, self.conn)
if err != nil {
return info.LoadStats{}, err
}

View File

@ -185,28 +185,19 @@ func validateCgroups() (string, string) {
}
func validateDockerInfo() (string, string) {
client, err := docker.Client()
if err == nil {
info, err := client.Info()
if err == nil {
execDriver := info.Get("ExecutionDriver")
storageDriver := info.Get("Driver")
desc := fmt.Sprintf("Docker exec driver is %s. Storage driver is %s.\n", execDriver, storageDriver)
if strings.Contains(execDriver, "native") {
stateFile := docker.DockerStateDir()
if !utils.FileExists(stateFile) {
desc += fmt.Sprintf("\tDocker container state directory %q is not accessible.\n", stateFile)
return Unsupported, desc
}
desc += fmt.Sprintf("\tDocker container state directory is at %q and is accessible.\n", stateFile)
return Recommended, desc
} else if strings.Contains(execDriver, "lxc") {
return Supported, desc
}
return Unknown, desc
}
info, err := docker.ValidateInfo()
if err != nil {
return Unsupported, fmt.Sprintf("Docker setup is invalid: %v", err)
}
return Unknown, "Docker remote API not reachable\n\t"
desc := fmt.Sprintf("Docker exec driver is %s. Storage driver is %s.\n", info.ExecutionDriver, info.Driver)
stateFile := docker.DockerStateDir()
if !utils.FileExists(stateFile) {
desc += fmt.Sprintf("\tDocker container state directory %q is not accessible.\n", stateFile)
return Unsupported, desc
}
desc += fmt.Sprintf("\tDocker container state directory is at %q and is accessible.\n", stateFile)
return Recommended, desc
}
func validateCgroupMounts() (string, string) {

View File

@ -1 +1 @@
0.22.0
0.23.0