Merge pull request #24113 from timstclair/docker11-godeps

Automatic merge from submit-queue

Update cAdvisor & go-dockerclient for docker v1.11 compatability
pull/6/head
k8s-merge-robot 2016-05-02 04:46:05 -07:00
commit cbb0d32868
43 changed files with 1745 additions and 950 deletions

74
Godeps/Godeps.json generated
View File

@ -511,7 +511,7 @@
},
{
"ImportPath": "github.com/fsouza/go-dockerclient",
"Rev": "0099401a7342ad77e71ca9f9a57c5e72fb80f6b2"
"Rev": "bf97c77db7c945cbcdbf09d56c6f87a66f54537b"
},
{
"ImportPath": "github.com/garyburd/redigo/internal",
@ -672,93 +672,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",

2
Godeps/LICENSES generated
View File

@ -18961,7 +18961,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
================================================================================
= Godeps/_workspace/src/github.com/fsouza/go-dockerclient licensed under: =
Copyright (c) 2015, go-dockerclient authors
Copyright (c) 2016, go-dockerclient authors
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -1,24 +1,27 @@
language: go
sudo: required
go:
- 1.3.3
- 1.4.2
- 1.5.3
- 1.6rc2
- 1.6
- tip
os:
- linux
- osx
env:
- GOARCH=amd64 DOCKER_VERSION=1.7.1
- GOARCH=386 DOCKER_VERSION=1.7.1
- GOARCH=amd64 DOCKER_VERSION=1.8.3
- GOARCH=386 DOCKER_VERSION=1.8.3
- GOARCH=amd64 DOCKER_VERSION=1.9.1
- GOARCH=386 DOCKER_VERSION=1.9.1
- GOARCH=amd64 DOCKER_VERSION=1.10.0
- GOARCH=386 DOCKER_VERSION=1.10.0
- GOARCH=amd64 DOCKER_VERSION=1.10.3
- GOARCH=386 DOCKER_VERSION=1.10.3
install:
- make prepare_docker
- travis_retry travis-scripts/install.bash
script:
- make test
- DOCKER_HOST=tcp://127.0.0.1:2375 make integration
- travis-scripts/run-tests.bash
services:
- docker
matrix:
fast_finish: true
allow_failures:
- go: tip

View File

@ -14,6 +14,7 @@ Ben Marini <ben@remind101.com>
Ben McCann <benmccann.com>
Ben Parees <bparees@redhat.com>
Benno van den Berg <bennovandenberg@gmail.com>
Bradley Cicenas <bradley.cicenas@gmail.com>
Brendan Fosberry <brendan@codeship.com>
Brian Lalor <blalor@bravo5.org>
Brian P. Hamachek <brian@brianhama.com>
@ -48,6 +49,8 @@ Fabio Rehm <fgrehm@gmail.com>
Fatih Arslan <ftharsln@gmail.com>
Flavia Missi <flaviamissi@gmail.com>
Francisco Souza <f@souza.cc>
Frank Groeneveld <frank@frankgroeneveld.nl>
George Moura <gwmoura@gmail.com>
Grégoire Delattre <gregoire.delattre@gmail.com>
Guillermo Álvarez Fernández <guillermo@cientifico.net>
Harry Zhang <harryzhang@zju.edu.cn>
@ -84,7 +87,9 @@ Michael Schmatz <michaelschmatz@gmail.com>
Michal Fojtik <mfojtik@redhat.com>
Mike Dillon <mike.dillon@synctree.com>
Mrunal Patel <mrunalp@gmail.com>
Nate Jones <nate@endot.org>
Nguyen Sy Thanh Son <sonnst@sigma-solutions.eu>
Nicholas Van Wiggeren <nvanwiggeren@digitalocean.com>
Nick Ethier <ncethier@gmail.com>
Omeid Matten <public@omeid.me>
Orivej Desh <orivej@gmx.fr>
@ -98,9 +103,11 @@ Philippe Lafoucrière <philippe.lafoucriere@tech-angels.com>
Rafe Colton <rafael.colton@gmail.com>
Rob Miller <rob@kalistra.com>
Robert Williamson <williamson.robert@gmail.com>
Roman Khlystik <roman.khlystik@gmail.com>
Salvador Gironès <salvadorgirones@gmail.com>
Sam Rijs <srijs@airpost.net>
Sami Wagiaalla <swagiaal@redhat.com>
Samuel Archambault <sarchambault@lapresse.ca>
Samuel Karp <skarp@amazon.com>
Silas Sewell <silas@sewell.org>
Simon Eskildsen <sirup@sirupsen.com>

View File

@ -1,4 +1,4 @@
Copyright (c) 2015, go-dockerclient authors
Copyright (c) 2016, go-dockerclient authors
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -11,8 +11,7 @@
cov \
clean
SRCS = $(shell git ls-files '*.go' | grep -v '^external/')
PKGS = ./. ./testing
PKGS = . ./testing
all: test
@ -22,32 +21,30 @@ vendor:
lint:
@ go get -v github.com/golang/lint/golint
$(foreach file,$(SRCS),golint $(file) || exit;)
@for file in $$(git ls-files '*.go' | grep -v 'external/'); do \
export output="$$(golint $${file} | grep -v 'type name will be used as docker.DockerInfo')"; \
[ -n "$${output}" ] && echo "$${output}" && export status=1; \
done; \
exit $${status:-0}
vet:
@-go get -v golang.org/x/tools/cmd/vet
$(foreach pkg,$(PKGS),go vet $(pkg);)
fmt:
gofmt -w $(SRCS)
gofmt -s -w $(PKGS)
fmtcheck:
$(foreach file,$(SRCS),gofmt -d $(file);)
prepare_docker:
sudo stop docker
sudo rm -rf /var/lib/docker
sudo rm -f `which docker`
sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
echo "deb https://apt.dockerproject.org/repo ubuntu-trusty main" | sudo tee /etc/apt/sources.list.d/docker.list
sudo apt-get update
sudo apt-get install docker-engine=$(DOCKER_VERSION)-0~$(shell lsb_release -cs) -y --force-yes -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold"
@ export output=$$(gofmt -s -d $(PKGS)); \
[ -n "$${output}" ] && echo "$${output}" && export status=1; \
exit $${status:-0}
pretest: lint vet fmtcheck
test: pretest
gotest:
$(foreach pkg,$(PKGS),go test $(pkg) || exit;)
test: pretest gotest
integration:
go test -tags docker_integration -run TestIntegration -v

View File

@ -4,7 +4,7 @@
[![GoDoc](https://img.shields.io/badge/api-Godoc-blue.svg?style=flat-square)](https://godoc.org/github.com/fsouza/go-dockerclient)
This package presents a client for the Docker remote API. It also provides
support for the extensions in the [Swarm API](https://docs.docker.com/swarm/api/swarm-api/).
support for the extensions in the [Swarm API](https://docs.docker.com/swarm/swarm-api/).
This package also provides support for docker's network API, which is a simple
passthrough to the libnetwork remote API. Note that docker's network API is

View File

@ -82,10 +82,12 @@ func parseDockerConfig(r io.Reader) (map[string]dockerConfig, error) {
buf.ReadFrom(r)
byteData := buf.Bytes()
var confsWrapper map[string]map[string]dockerConfig
confsWrapper := struct {
Auths map[string]dockerConfig `json:"auths"`
}{}
if err := json.Unmarshal(byteData, &confsWrapper); err == nil {
if confs, ok := confsWrapper["auths"]; ok {
return confs, nil
if len(confsWrapper.Auths) > 0 {
return confsWrapper.Auths, nil
}
}

View File

@ -555,6 +555,8 @@ type hijackOptions struct {
data interface{}
}
// CloseWaiter is an interface with methods for closing the underlying resource
// and then waiting for it to finish processing.
type CloseWaiter interface {
io.Closer
Wait() error

View File

@ -14,6 +14,8 @@ import (
"strconv"
"strings"
"time"
"github.com/fsouza/go-dockerclient/external/github.com/docker/go-units"
)
// ErrContainerAlreadyExists is the error returned by CreateContainer when the
@ -52,7 +54,14 @@ type APIContainers struct {
SizeRw int64 `json:"SizeRw,omitempty" yaml:"SizeRw,omitempty"`
SizeRootFs int64 `json:"SizeRootFs,omitempty" yaml:"SizeRootFs,omitempty"`
Names []string `json:"Names,omitempty" yaml:"Names,omitempty"`
Labels map[string]string `json:"Labels,omitempty" yaml:"Labels, omitempty"`
Labels map[string]string `json:"Labels,omitempty" yaml:"Labels,omitempty"`
Networks NetworkList `json:"NetworkSettings,omitempty" yaml:"NetworkSettings,omitempty"`
}
// NetworkList encapsulates a map of networks, as returned by the Docker API in
// ListContainers.
type NetworkList struct {
Networks map[string]ContainerNetwork `json:"Networks" yaml:"Networks,omitempty"`
}
// ListContainers returns a slice of containers matching the given criteria.
@ -92,26 +101,73 @@ func (p Port) Proto() string {
// State represents the state of a container.
type State struct {
Running bool `json:"Running,omitempty" yaml:"Running,omitempty"`
Paused bool `json:"Paused,omitempty" yaml:"Paused,omitempty"`
Restarting bool `json:"Restarting,omitempty" yaml:"Restarting,omitempty"`
OOMKilled bool `json:"OOMKilled,omitempty" yaml:"OOMKilled,omitempty"`
Pid int `json:"Pid,omitempty" yaml:"Pid,omitempty"`
ExitCode int `json:"ExitCode,omitempty" yaml:"ExitCode,omitempty"`
Error string `json:"Error,omitempty" yaml:"Error,omitempty"`
StartedAt time.Time `json:"StartedAt,omitempty" yaml:"StartedAt,omitempty"`
FinishedAt time.Time `json:"FinishedAt,omitempty" yaml:"FinishedAt,omitempty"`
Status string `json:"Status,omitempty" yaml:"Status,omitempty"`
Running bool `json:"Running,omitempty" yaml:"Running,omitempty"`
Paused bool `json:"Paused,omitempty" yaml:"Paused,omitempty"`
Restarting bool `json:"Restarting,omitempty" yaml:"Restarting,omitempty"`
OOMKilled bool `json:"OOMKilled,omitempty" yaml:"OOMKilled,omitempty"`
RemovalInProgress bool `json:"RemovalInProgress,omitempty" yaml:"RemovalInProgress,omitempty"`
Dead bool `json:"Dead,omitempty" yaml:"Dead,omitempty"`
Pid int `json:"Pid,omitempty" yaml:"Pid,omitempty"`
ExitCode int `json:"ExitCode,omitempty" yaml:"ExitCode,omitempty"`
Error string `json:"Error,omitempty" yaml:"Error,omitempty"`
StartedAt time.Time `json:"StartedAt,omitempty" yaml:"StartedAt,omitempty"`
FinishedAt time.Time `json:"FinishedAt,omitempty" yaml:"FinishedAt,omitempty"`
}
// String returns the string representation of a state.
// String returns a human-readable description of the state
func (s *State) String() string {
if s.Running {
if s.Paused {
return fmt.Sprintf("Up %s (Paused)", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt)))
}
if s.Restarting {
return fmt.Sprintf("Restarting (%d) %s ago", s.ExitCode, units.HumanDuration(time.Now().UTC().Sub(s.FinishedAt)))
}
return fmt.Sprintf("Up %s", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt)))
}
if s.RemovalInProgress {
return "Removal In Progress"
}
if s.Dead {
return "Dead"
}
if s.StartedAt.IsZero() {
return "Created"
}
if s.FinishedAt.IsZero() {
return ""
}
return fmt.Sprintf("Exited (%d) %s ago", s.ExitCode, units.HumanDuration(time.Now().UTC().Sub(s.FinishedAt)))
}
// StateString returns a single string to describe state
func (s *State) StateString() string {
if s.Running {
if s.Paused {
return "paused"
}
return fmt.Sprintf("Up %s", time.Now().UTC().Sub(s.StartedAt))
if s.Restarting {
return "restarting"
}
return "running"
}
return fmt.Sprintf("Exit %d", s.ExitCode)
if s.Dead {
return "dead"
}
if s.StartedAt.IsZero() {
return "created"
}
return "exited"
}
// PortBinding represents the host/container port mapping as returned in the
@ -135,6 +191,7 @@ type ContainerNetwork struct {
IPAddress string `json:"IPAddress,omitempty" yaml:"IPAddress,omitempty"`
Gateway string `json:"Gateway,omitempty" yaml:"Gateway,omitempty"`
EndpointID string `json:"EndpointID,omitempty" yaml:"EndpointID,omitempty"`
NetworkID string `json:"NetworkID,omitempty" yaml:"NetworkID,omitempty"`
}
// NetworkSettings contains network-related information about a container
@ -308,6 +365,34 @@ type Container struct {
AppArmorProfile string `json:"AppArmorProfile,omitempty" yaml:"AppArmorProfile,omitempty"`
}
// UpdateContainerOptions specify parameters to the UpdateContainer function.
//
// See https://goo.gl/Y6fXUy for more details.
type UpdateContainerOptions struct {
BlkioWeight int `json:"BlkioWeight"`
CPUShares int `json:"CpuShares"`
CPUPeriod int `json:"CpuPeriod"`
CPUQuota int `json:"CpuQuota"`
CpusetCpus string `json:"CpusetCpus"`
CpusetMems string `json:"CpusetMems"`
Memory int `json:"Memory"`
MemorySwap int `json:"MemorySwap"`
MemoryReservation int `json:"MemoryReservation"`
KernelMemory int `json:"KernelMemory"`
}
// UpdateContainer updates the container at ID with the options
//
// See https://goo.gl/Y6fXUy for more details.
func (c *Client) UpdateContainer(id string, opts UpdateContainerOptions) error {
resp, err := c.do("POST", fmt.Sprintf("/containers/"+id+"/update"), doOptions{data: opts, forceJSON: true})
if err != nil {
return err
}
defer resp.Body.Close()
return nil
}
// RenameContainerOptions specify parameters to the RenameContainer function.
//
// See https://goo.gl/laSOIy for more details.
@ -469,48 +554,71 @@ type Device struct {
CgroupPermissions string `json:"CgroupPermissions,omitempty" yaml:"CgroupPermissions,omitempty"`
}
// BlockWeight represents a relative device weight for an individual device inside
// of a container
//
// See https://goo.gl/FSdP0H for more details.
type BlockWeight struct {
Path string `json:"Path,omitempty"`
Weight string `json:"Weight,omitempty"`
}
// BlockLimit represents a read/write limit in IOPS or Bandwidth for a device
// inside of a container
//
// See https://goo.gl/FSdP0H for more details.
type BlockLimit struct {
Path string `json:"Path,omitempty"`
Rate string `json:"Rate,omitempty"`
}
// HostConfig contains the container options related to starting a container on
// a given host
type HostConfig struct {
Binds []string `json:"Binds,omitempty" yaml:"Binds,omitempty"`
CapAdd []string `json:"CapAdd,omitempty" yaml:"CapAdd,omitempty"`
CapDrop []string `json:"CapDrop,omitempty" yaml:"CapDrop,omitempty"`
GroupAdd []string `json:"GroupAdd,omitempty" yaml:"GroupAdd,omitempty"`
ContainerIDFile string `json:"ContainerIDFile,omitempty" yaml:"ContainerIDFile,omitempty"`
LxcConf []KeyValuePair `json:"LxcConf,omitempty" yaml:"LxcConf,omitempty"`
Privileged bool `json:"Privileged,omitempty" yaml:"Privileged,omitempty"`
PortBindings map[Port][]PortBinding `json:"PortBindings,omitempty" yaml:"PortBindings,omitempty"`
Links []string `json:"Links,omitempty" yaml:"Links,omitempty"`
PublishAllPorts bool `json:"PublishAllPorts,omitempty" yaml:"PublishAllPorts,omitempty"`
DNS []string `json:"Dns,omitempty" yaml:"Dns,omitempty"` // For Docker API v1.10 and above only
DNSOptions []string `json:"DnsOptions,omitempty" yaml:"DnsOptions,omitempty"`
DNSSearch []string `json:"DnsSearch,omitempty" yaml:"DnsSearch,omitempty"`
ExtraHosts []string `json:"ExtraHosts,omitempty" yaml:"ExtraHosts,omitempty"`
VolumesFrom []string `json:"VolumesFrom,omitempty" yaml:"VolumesFrom,omitempty"`
NetworkMode string `json:"NetworkMode,omitempty" yaml:"NetworkMode,omitempty"`
IpcMode string `json:"IpcMode,omitempty" yaml:"IpcMode,omitempty"`
PidMode string `json:"PidMode,omitempty" yaml:"PidMode,omitempty"`
UTSMode string `json:"UTSMode,omitempty" yaml:"UTSMode,omitempty"`
RestartPolicy RestartPolicy `json:"RestartPolicy,omitempty" yaml:"RestartPolicy,omitempty"`
Devices []Device `json:"Devices,omitempty" yaml:"Devices,omitempty"`
LogConfig LogConfig `json:"LogConfig,omitempty" yaml:"LogConfig,omitempty"`
ReadonlyRootfs bool `json:"ReadonlyRootfs,omitempty" yaml:"ReadonlyRootfs,omitempty"`
SecurityOpt []string `json:"SecurityOpt,omitempty" yaml:"SecurityOpt,omitempty"`
CgroupParent string `json:"CgroupParent,omitempty" yaml:"CgroupParent,omitempty"`
Memory int64 `json:"Memory,omitempty" yaml:"Memory,omitempty"`
MemorySwap int64 `json:"MemorySwap,omitempty" yaml:"MemorySwap,omitempty"`
MemorySwappiness int64 `json:"MemorySwappiness,omitempty" yaml:"MemorySwappiness,omitempty"`
OOMKillDisable bool `json:"OomKillDisable,omitempty" yaml:"OomKillDisable"`
CPUShares int64 `json:"CpuShares,omitempty" yaml:"CpuShares,omitempty"`
CPUSet string `json:"Cpuset,omitempty" yaml:"Cpuset,omitempty"`
CPUSetCPUs string `json:"CpusetCpus,omitempty" yaml:"CpusetCpus,omitempty"`
CPUSetMEMs string `json:"CpusetMems,omitempty" yaml:"CpusetMems,omitempty"`
CPUQuota int64 `json:"CpuQuota,omitempty" yaml:"CpuQuota,omitempty"`
CPUPeriod int64 `json:"CpuPeriod,omitempty" yaml:"CpuPeriod,omitempty"`
BlkioWeight int64 `json:"BlkioWeight,omitempty" yaml:"BlkioWeight"`
Ulimits []ULimit `json:"Ulimits,omitempty" yaml:"Ulimits,omitempty"`
VolumeDriver string `json:"VolumeDriver,omitempty" yaml:"VolumeDriver,omitempty"`
OomScoreAdj int `json:"OomScoreAdj,omitempty" yaml:"OomScoreAdj,omitempty"`
Binds []string `json:"Binds,omitempty" yaml:"Binds,omitempty"`
CapAdd []string `json:"CapAdd,omitempty" yaml:"CapAdd,omitempty"`
CapDrop []string `json:"CapDrop,omitempty" yaml:"CapDrop,omitempty"`
GroupAdd []string `json:"GroupAdd,omitempty" yaml:"GroupAdd,omitempty"`
ContainerIDFile string `json:"ContainerIDFile,omitempty" yaml:"ContainerIDFile,omitempty"`
LxcConf []KeyValuePair `json:"LxcConf,omitempty" yaml:"LxcConf,omitempty"`
Privileged bool `json:"Privileged,omitempty" yaml:"Privileged,omitempty"`
PortBindings map[Port][]PortBinding `json:"PortBindings,omitempty" yaml:"PortBindings,omitempty"`
Links []string `json:"Links,omitempty" yaml:"Links,omitempty"`
PublishAllPorts bool `json:"PublishAllPorts,omitempty" yaml:"PublishAllPorts,omitempty"`
DNS []string `json:"Dns,omitempty" yaml:"Dns,omitempty"` // For Docker API v1.10 and above only
DNSOptions []string `json:"DnsOptions,omitempty" yaml:"DnsOptions,omitempty"`
DNSSearch []string `json:"DnsSearch,omitempty" yaml:"DnsSearch,omitempty"`
ExtraHosts []string `json:"ExtraHosts,omitempty" yaml:"ExtraHosts,omitempty"`
VolumesFrom []string `json:"VolumesFrom,omitempty" yaml:"VolumesFrom,omitempty"`
NetworkMode string `json:"NetworkMode,omitempty" yaml:"NetworkMode,omitempty"`
IpcMode string `json:"IpcMode,omitempty" yaml:"IpcMode,omitempty"`
PidMode string `json:"PidMode,omitempty" yaml:"PidMode,omitempty"`
UTSMode string `json:"UTSMode,omitempty" yaml:"UTSMode,omitempty"`
RestartPolicy RestartPolicy `json:"RestartPolicy,omitempty" yaml:"RestartPolicy,omitempty"`
Devices []Device `json:"Devices,omitempty" yaml:"Devices,omitempty"`
LogConfig LogConfig `json:"LogConfig,omitempty" yaml:"LogConfig,omitempty"`
ReadonlyRootfs bool `json:"ReadonlyRootfs,omitempty" yaml:"ReadonlyRootfs,omitempty"`
SecurityOpt []string `json:"SecurityOpt,omitempty" yaml:"SecurityOpt,omitempty"`
CgroupParent string `json:"CgroupParent,omitempty" yaml:"CgroupParent,omitempty"`
Memory int64 `json:"Memory,omitempty" yaml:"Memory,omitempty"`
MemorySwap int64 `json:"MemorySwap,omitempty" yaml:"MemorySwap,omitempty"`
MemorySwappiness int64 `json:"MemorySwappiness,omitempty" yaml:"MemorySwappiness,omitempty"`
OOMKillDisable bool `json:"OomKillDisable,omitempty" yaml:"OomKillDisable"`
CPUShares int64 `json:"CpuShares,omitempty" yaml:"CpuShares,omitempty"`
CPUSet string `json:"Cpuset,omitempty" yaml:"Cpuset,omitempty"`
CPUSetCPUs string `json:"CpusetCpus,omitempty" yaml:"CpusetCpus,omitempty"`
CPUSetMEMs string `json:"CpusetMems,omitempty" yaml:"CpusetMems,omitempty"`
CPUQuota int64 `json:"CpuQuota,omitempty" yaml:"CpuQuota,omitempty"`
CPUPeriod int64 `json:"CpuPeriod,omitempty" yaml:"CpuPeriod,omitempty"`
BlkioWeight int64 `json:"BlkioWeight,omitempty" yaml:"BlkioWeight"`
BlkioWeightDevice []BlockWeight `json:"BlkioWeightDevice,omitempty" yaml:"BlkioWeightDevice"`
BlkioDeviceReadBps []BlockLimit `json:"BlkioDeviceReadBps,omitempty" yaml:"BlkioDeviceReadBps"`
BlkioDeviceReadIOps []BlockLimit `json:"BlkioDeviceReadIOps,omitempty" yaml:"BlkioDeviceReadIOps"`
BlkioDeviceWriteBps []BlockLimit `json:"BlkioDeviceWriteBps,omitempty" yaml:"BlkioDeviceWriteBps"`
BlkioDeviceWriteIOps []BlockLimit `json:"BlkioDeviceWriteIOps,omitempty" yaml:"BlkioDeviceWriteIOps"`
Ulimits []ULimit `json:"Ulimits,omitempty" yaml:"Ulimits,omitempty"`
VolumeDriver string `json:"VolumeDriver,omitempty" yaml:"VolumeDriver,omitempty"`
OomScoreAdj int `json:"OomScoreAdj,omitempty" yaml:"OomScoreAdj,omitempty"`
}
// StartContainer starts a container, returning an error in case of failure.

View File

@ -18,12 +18,38 @@ import (
"time"
)
// APIEvents represents an event returned by the API.
// APIEvents represents events coming from the Docker API
// The fields in the Docker API changed in API version 1.22, and
// events for more than images and containers are now fired off.
// To maintain forward and backward compatibility, go-dockerclient
// replicates the event in both the new and old format as faithfully as possible.
//
// For events that only exist in 1.22 in later, `Status` is filled in as
// `"Type:Action"` instead of just `Action` to allow for older clients to
// differentiate and not break if they rely on the pre-1.22 Status types.
//
// The transformEvent method can be consulted for more information about how
// events are translated from new/old API formats
type APIEvents struct {
Status string `json:"Status,omitempty" yaml:"Status,omitempty"`
ID string `json:"ID,omitempty" yaml:"ID,omitempty"`
From string `json:"From,omitempty" yaml:"From,omitempty"`
Time int64 `json:"Time,omitempty" yaml:"Time,omitempty"`
// New API Fields in 1.22
Action string `json:"action,omitempty"`
Type string `json:"type,omitempty"`
Actor APIActor `json:"actor,omitempty"`
// Old API fields for < 1.22
Status string `json:"status,omitempty"`
ID string `json:"id,omitempty"`
From string `json:"from,omitempty"`
// Fields in both
Time int64 `json:"time,omitempty"`
TimeNano int64 `json:"timeNano,omitempty"`
}
// APIActor represents an actor that accomplishes something for an event
type APIActor struct {
ID string `json:"id,omitempty"`
Attributes map[string]string `json:"attributes,omitempty"`
}
type eventMonitoringState struct {
@ -52,6 +78,7 @@ var (
// EOFEvent is sent when the event listener receives an EOF error.
EOFEvent = &APIEvents{
Type: "EOF",
Status: "EOF",
}
)
@ -297,8 +324,47 @@ func (c *Client) eventHijack(startTime int64, eventChan chan *APIEvents, errChan
if !c.eventMonitor.isEnabled() {
return
}
transformEvent(&event)
eventChan <- &event
}
}(res, conn)
return nil
}
// transformEvent takes an event and determines what version it is from
// then populates both versions of the event
func transformEvent(event *APIEvents) {
// if event version is <= 1.21 there will be no Action and no Type
if event.Action == "" && event.Type == "" {
event.Action = event.Status
event.Actor.ID = event.ID
event.Actor.Attributes = map[string]string{}
switch event.Status {
case "delete", "import", "pull", "push", "tag", "untag":
event.Type = "image"
default:
event.Type = "container"
if event.From != "" {
event.Actor.Attributes["image"] = event.From
}
}
} else {
if event.Status == "" {
if event.Type == "image" || event.Type == "container" {
event.Status = event.Action
} else {
// Because just the Status has been overloaded with different Types
// if an event is not for an image or a container, we prepend the type
// to avoid problems for people relying on actions being only for
// images and containers
event.Status = event.Type + ":" + event.Action
}
}
if event.ID == "" {
event.ID = event.Actor.ID
}
if event.From == "" {
event.From = event.Actor.Attributes["image"]
}
}
}

View File

@ -0,0 +1,14 @@
// +build !windows
package system
import (
"time"
)
//setCTime will set the create time on a file. On Unix, the create
//time is updated as a side effect of setting the modified time, so
//no action is required.
func setCTime(path string, ctime time.Time) error {
return nil
}

View File

@ -0,0 +1,27 @@
// +build windows
package system
import (
"syscall"
"time"
)
//setCTime will set the create time on a file. On Windows, this requires
//calling SetFileTime and explicitly including the create time.
func setCTime(path string, ctime time.Time) error {
ctimespec := syscall.NsecToTimespec(ctime.UnixNano())
pathp, e := syscall.UTF16PtrFromString(path)
if e != nil {
return e
}
h, e := syscall.CreateFile(pathp,
syscall.FILE_WRITE_ATTRIBUTES, syscall.FILE_SHARE_WRITE, nil,
syscall.OPEN_EXISTING, syscall.FILE_FLAG_BACKUP_SEMANTICS, 0)
if e != nil {
return e
}
defer syscall.Close(h)
c := syscall.NsecToFiletime(syscall.TimespecToNsec(ctimespec))
return syscall.SetFileTime(h, &c, nil, nil)
}

View File

@ -0,0 +1,15 @@
package system
import (
"syscall"
)
// fromStatT creates a system.StatT type from a syscall.Stat_t type
func fromStatT(s *syscall.Stat_t) (*StatT, error) {
return &StatT{size: s.Size,
mode: uint32(s.Mode),
uid: s.Uid,
gid: s.Gid,
rdev: uint64(s.Rdev),
mtim: s.Mtim}, nil
}

View File

@ -1,4 +1,4 @@
// +build !linux,!windows,!freebsd,!solaris
// +build !linux,!windows,!freebsd,!solaris,!openbsd
package system

View File

@ -210,13 +210,13 @@ type CancelFunc func()
// call cancel as soon as the operations running in this Context complete.
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
c := newCancelCtx(parent)
propagateCancel(parent, &c)
return &c, func() { c.cancel(true, Canceled) }
propagateCancel(parent, c)
return c, func() { c.cancel(true, Canceled) }
}
// newCancelCtx returns an initialized cancelCtx.
func newCancelCtx(parent Context) cancelCtx {
return cancelCtx{
func newCancelCtx(parent Context) *cancelCtx {
return &cancelCtx{
Context: parent,
done: make(chan struct{}),
}
@ -259,7 +259,7 @@ func parentCancelCtx(parent Context) (*cancelCtx, bool) {
case *cancelCtx:
return c, true
case *timerCtx:
return &c.cancelCtx, true
return c.cancelCtx, true
case *valueCtx:
parent = c.Context
default:
@ -377,7 +377,7 @@ func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) {
// implement Done and Err. It implements cancel by stopping its timer then
// delegating to cancelCtx.cancel.
type timerCtx struct {
cancelCtx
*cancelCtx
timer *time.Timer // Under cancelCtx.mu.
deadline time.Time

View File

@ -32,6 +32,7 @@ type APIImages struct {
// Image is the type representing a docker image and its various properties
type Image struct {
ID string `json:"Id" yaml:"Id"`
RepoTags []string `json:"RepoTags,omitempty" yaml:"RepoTags,omitempty"`
Parent string `json:"Parent,omitempty" yaml:"Parent,omitempty"`
Comment string `json:"Comment,omitempty" yaml:"Comment,omitempty"`
Created time.Time `json:"Created,omitempty" yaml:"Created,omitempty"`
@ -421,6 +422,17 @@ type BuildImageOptions struct {
AuthConfigs AuthConfigurations `qs:"-"` // for newer docker X-Registry-Config header
ContextDir string `qs:"-"`
Ulimits []ULimit `qs:"-"`
BuildArgs []BuildArg `qs:"-"`
}
// BuildArg represents arguments that can be passed to the image when building
// it from a Dockerfile.
//
// For more details about the Docker building process, see
// http://goo.gl/tlPXPu.
type BuildArg struct {
Name string `json:"Name,omitempty" yaml:"Name,omitempty"`
Value string `json:"Value,omitempty" yaml:"Value,omitempty"`
}
// BuildImage builds an image from a tarball's url or a Dockerfile in the input
@ -463,6 +475,18 @@ func (c *Client) BuildImage(opts BuildImageOptions) error {
}
}
if len(opts.BuildArgs) > 0 {
v := make(map[string]string)
for _, arg := range opts.BuildArgs {
v[arg.Name] = arg.Value
}
if b, err := json.Marshal(v); err == nil {
item := url.Values(map[string][]string{})
item.Add("buildargs", string(b))
qs = fmt.Sprintf("%s&%s", qs, item.Encode())
}
}
return c.stream("POST", fmt.Sprintf("/build?%s", qs), streamOptions{
setRawTerminal: true,
rawJSONStream: opts.RawJSONStream,

View File

@ -4,7 +4,10 @@
package docker
import "strings"
import (
"encoding/json"
"strings"
)
// Version returns version information about the docker server.
//
@ -22,17 +25,81 @@ func (c *Client) Version() (*Env, error) {
return &env, nil
}
// DockerInfo contains information about the Docker server
//
// See https://goo.gl/bHUoz9 for more details.
type DockerInfo struct {
ID string
Containers int
ContainersRunning int
ContainersPaused int
ContainersStopped int
Images int
Driver string
DriverStatus [][2]string
SystemStatus [][2]string
Plugins PluginsInfo
MemoryLimit bool
SwapLimit bool
KernelMemory bool
CPUCfsPeriod bool `json:"CpuCfsPeriod"`
CPUCfsQuota bool `json:"CpuCfsQuota"`
CPUShares bool
CPUSet bool
IPv4Forwarding bool
BridgeNfIptables bool
BridgeNfIP6tables bool `json:"BridgeNfIp6tables"`
Debug bool
NFd int
OomKillDisable bool
NGoroutines int
SystemTime string
ExecutionDriver string
LoggingDriver string
CgroupDriver string
NEventsListener int
KernelVersion string
OperatingSystem string
OSType string
Architecture string
IndexServerAddress string
NCPU int
MemTotal int64
DockerRootDir string
HTTPProxy string `json:"HttpProxy"`
HTTPSProxy string `json:"HttpsProxy"`
NoProxy string
Name string
Labels []string
ExperimentalBuild bool
ServerVersion string
ClusterStore string
ClusterAdvertise string
}
// PluginsInfo is a struct with the plugins registered with the docker daemon
//
// for more information, see: https://goo.gl/bHUoz9
type PluginsInfo struct {
// List of Volume plugins registered
Volume []string
// List of Network plugins registered
Network []string
// List of Authorization plugins registered
Authorization []string
}
// Info returns system-wide information about the Docker server.
//
// See https://goo.gl/ElTHi2 for more details.
func (c *Client) Info() (*Env, error) {
func (c *Client) Info() (*DockerInfo, error) {
resp, err := c.do("GET", "/info", doOptions{})
if err != nil {
return nil, err
}
defer resp.Body.Close()
var info Env
if err := info.Decode(resp.Body); err != nil {
var info DockerInfo
if err := json.NewDecoder(resp.Body).Decode(&info); err != nil {
return nil, err
}
return &info, nil

View File

@ -5,6 +5,7 @@
package docker
import (
"bytes"
"encoding/json"
"errors"
"fmt"
@ -26,6 +27,7 @@ type Network struct {
IPAM IPAMOptions
Containers map[string]Endpoint
Options map[string]string
Internal bool
}
// Endpoint contains network resources allocated and used for a container in a network
@ -55,6 +57,31 @@ func (c *Client) ListNetworks() ([]Network, error) {
return networks, nil
}
// NetworkFilterOpts is an aggregation of key=value that Docker
// uses to filter networks
type NetworkFilterOpts map[string]map[string]bool
// FilteredListNetworks returns all networks with the filters applied
//
// See goo.gl/zd2mx4 for more details.
func (c *Client) FilteredListNetworks(opts NetworkFilterOpts) ([]Network, error) {
params := bytes.NewBuffer(nil)
if err := json.NewEncoder(params).Encode(&opts); err != nil {
return nil, err
}
path := "/networks?filters=" + params.String()
resp, err := c.do("GET", path, doOptions{})
if err != nil {
return nil, err
}
defer resp.Body.Close()
var networks []Network
if err := json.NewDecoder(resp.Body).Decode(&networks); err != nil {
return nil, err
}
return networks, nil
}
// NetworkInfo returns information about a network by its ID.
//
// See https://goo.gl/6GugX3 for more details.
@ -158,14 +185,40 @@ func (c *Client) RemoveNetwork(id string) error {
return nil
}
// NetworkConnectionOptions specify parameters to the ConnectNetwork and DisconnectNetwork function.
// NetworkConnectionOptions specify parameters to the ConnectNetwork and
// DisconnectNetwork function.
//
// See https://goo.gl/6GugX3 for more details.
// See https://goo.gl/RV7BJU for more details.
type NetworkConnectionOptions struct {
Container string
// EndpointConfig is only applicable to the ConnectNetwork call
EndpointConfig *EndpointConfig `json:"EndpointConfig,omitempty"`
// Force is only applicable to the DisconnectNetwork call
Force bool
}
// ConnectNetwork adds a container to a network or returns an error in case of failure.
// EndpointConfig stores network endpoint details
//
// See https://goo.gl/RV7BJU for more details.
type EndpointConfig struct {
IPAMConfig *EndpointIPAMConfig
Links []string
Aliases []string
}
// EndpointIPAMConfig represents IPAM configurations for an
// endpoint
//
// See https://goo.gl/RV7BJU for more details.
type EndpointIPAMConfig struct {
IPv4Address string `json:",omitempty"`
IPv6Address string `json:",omitempty"`
}
// ConnectNetwork adds a container to a network or returns an error in case of
// failure.
//
// See https://goo.gl/6GugX3 for more details.
func (c *Client) ConnectNetwork(id string, opts NetworkConnectionOptions) error {
@ -180,7 +233,8 @@ func (c *Client) ConnectNetwork(id string, opts NetworkConnectionOptions) error
return nil
}
// DisconnectNetwork removes a container from a network or returns an error in case of failure.
// DisconnectNetwork removes a container from a network or returns an error in
// case of failure.
//
// See https://goo.gl/6GugX3 for more details.
func (c *Client) DisconnectNetwork(id string, opts NetworkConnectionOptions) error {
@ -204,7 +258,8 @@ func (err *NoSuchNetwork) Error() string {
return fmt.Sprintf("No such network: %s", err.ID)
}
// NoSuchNetwork is the error returned when a given network or container does not exist.
// NoSuchNetworkOrContainer is the error returned when a given network or
// container does not exist.
type NoSuchNetworkOrContainer struct {
NetworkID string
ContainerID string

View File

@ -144,6 +144,7 @@ func (s *DockerServer) buildMuxer() {
s.mux.Path("/volumes/create").Methods("POST").HandlerFunc(s.handlerWrapper(s.createVolume))
s.mux.Path("/volumes/{name:.*}").Methods("GET").HandlerFunc(s.handlerWrapper(s.inspectVolume))
s.mux.Path("/volumes/{name:.*}").Methods("DELETE").HandlerFunc(s.handlerWrapper(s.removeVolume))
s.mux.Path("/info").Methods("GET").HandlerFunc(s.handlerWrapper(s.infoDocker))
}
// SetHook changes the hook function used by the server.
@ -743,10 +744,9 @@ func (s *DockerServer) commitContainer(w http.ResponseWriter, r *http.Request) {
http.Error(w, err.Error(), http.StatusNotFound)
return
}
var config *docker.Config
config := new(docker.Config)
runConfig := r.URL.Query().Get("run")
if runConfig != "" {
config = new(docker.Config)
err = json.Unmarshal([]byte(runConfig), config)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
@ -828,7 +828,8 @@ func (s *DockerServer) pullImage(w http.ResponseWriter, r *http.Request) {
fromImageName := r.URL.Query().Get("fromImage")
tag := r.URL.Query().Get("tag")
image := docker.Image{
ID: s.generateID(),
ID: s.generateID(),
Config: &docker.Config{},
}
s.iMut.Lock()
s.images = append(s.images, image)
@ -1244,3 +1245,86 @@ func (s *DockerServer) removeVolume(w http.ResponseWriter, r *http.Request) {
s.volStore[vol.volume.Name] = nil
w.WriteHeader(http.StatusNoContent)
}
func (s *DockerServer) infoDocker(w http.ResponseWriter, r *http.Request) {
s.cMut.RLock()
defer s.cMut.RUnlock()
s.iMut.RLock()
defer s.iMut.RUnlock()
var running, stopped, paused int
for _, c := range s.containers {
if c.State.Running {
running++
} else {
stopped++
}
if c.State.Paused {
paused++
}
}
envs := map[string]interface{}{
"ID": "AAAA:XXXX:0000:BBBB:AAAA:XXXX:0000:BBBB:AAAA:XXXX:0000:BBBB",
"Containers": len(s.containers),
"ContainersRunning": running,
"ContainersPaused": paused,
"ContainersStopped": stopped,
"Images": len(s.images),
"Driver": "aufs",
"DriverStatus": [][]string{},
"SystemStatus": nil,
"Plugins": map[string]interface{}{
"Volume": []string{
"local",
},
"Network": []string{
"bridge",
"null",
"host",
},
"Authorization": nil,
},
"MemoryLimit": true,
"SwapLimit": false,
"CpuCfsPeriod": true,
"CpuCfsQuota": true,
"CPUShares": true,
"CPUSet": true,
"IPv4Forwarding": true,
"BridgeNfIptables": true,
"BridgeNfIp6tables": true,
"Debug": false,
"NFd": 79,
"OomKillDisable": true,
"NGoroutines": 101,
"SystemTime": "2016-02-25T18:13:10.25870078Z",
"ExecutionDriver": "native-0.2",
"LoggingDriver": "json-file",
"NEventsListener": 0,
"KernelVersion": "3.13.0-77-generic",
"OperatingSystem": "Ubuntu 14.04.3 LTS",
"OSType": "linux",
"Architecture": "x86_64",
"IndexServerAddress": "https://index.docker.io/v1/",
"RegistryConfig": map[string]interface{}{
"InsecureRegistryCIDRs": []string{},
"IndexConfigs": map[string]interface{}{},
"Mirrors": nil,
},
"InitSha1": "e2042dbb0fcf49bb9da199186d9a5063cda92a01",
"InitPath": "/usr/lib/docker/dockerinit",
"NCPU": 1,
"MemTotal": 2099204096,
"DockerRootDir": "/var/lib/docker",
"HttpProxy": "",
"HttpsProxy": "",
"NoProxy": "",
"Name": "vagrant-ubuntu-trusty-64",
"Labels": nil,
"ExperimentalBuild": false,
"ServerVersion": "1.10.1",
"ClusterStore": "",
"ClusterAdvertise": "",
}
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(envs)
}

View File

@ -0,0 +1,17 @@
#!/bin/bash -x
# Copyright 2016 go-dockerclient authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
if [[ $TRAVIS_OS_NAME == "linux" ]]; then
sudo stop docker || true
sudo rm -rf /var/lib/docker
sudo rm -f `which docker`
set -e
sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
echo "deb https://apt.dockerproject.org/repo ubuntu-trusty main" | sudo tee /etc/apt/sources.list.d/docker.list
sudo apt-get update
sudo apt-get install docker-engine=${DOCKER_VERSION}-0~$(lsb_release -cs) -y --force-yes -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold"
fi

View File

@ -0,0 +1,15 @@
#!/bin/bash -ex
# Copyright 2016 go-dockerclient authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
if ! [[ $TRAVIS_GO_VERSION =~ ^1\.[34] ]]; then
make lint vet
fi
make fmtcheck gotest
if [[ $TRAVIS_OS_NAME == "linux" ]]; then
DOCKER_HOST=tcp://127.0.0.1:2375 make integration
fi

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