Merge pull request #24113 from timstclair/docker11-godeps

Automatic merge from submit-queue

Update cAdvisor & go-dockerclient for docker v1.11 compatability
This commit is contained in:
k8s-merge-robot
2016-05-02 04:46:05 -07:00
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", "ImportPath": "github.com/fsouza/go-dockerclient",
"Rev": "0099401a7342ad77e71ca9f9a57c5e72fb80f6b2" "Rev": "bf97c77db7c945cbcdbf09d56c6f87a66f54537b"
}, },
{ {
"ImportPath": "github.com/garyburd/redigo/internal", "ImportPath": "github.com/garyburd/redigo/internal",
@@ -672,93 +672,93 @@
}, },
{ {
"ImportPath": "github.com/google/cadvisor/api", "ImportPath": "github.com/google/cadvisor/api",
"Comment": "v0.22.2", "Comment": "v0.23.0",
"Rev": "546a3771589bdb356777c646c6eca24914fdd48b" "Rev": "750f18e5eac3f6193b354fc14c03d92d4318a0ec"
}, },
{ {
"ImportPath": "github.com/google/cadvisor/cache/memory", "ImportPath": "github.com/google/cadvisor/cache/memory",
"Comment": "v0.22.2", "Comment": "v0.23.0",
"Rev": "546a3771589bdb356777c646c6eca24914fdd48b" "Rev": "750f18e5eac3f6193b354fc14c03d92d4318a0ec"
}, },
{ {
"ImportPath": "github.com/google/cadvisor/collector", "ImportPath": "github.com/google/cadvisor/collector",
"Comment": "v0.22.2", "Comment": "v0.23.0",
"Rev": "546a3771589bdb356777c646c6eca24914fdd48b" "Rev": "750f18e5eac3f6193b354fc14c03d92d4318a0ec"
}, },
{ {
"ImportPath": "github.com/google/cadvisor/container", "ImportPath": "github.com/google/cadvisor/container",
"Comment": "v0.22.2", "Comment": "v0.23.0",
"Rev": "546a3771589bdb356777c646c6eca24914fdd48b" "Rev": "750f18e5eac3f6193b354fc14c03d92d4318a0ec"
}, },
{ {
"ImportPath": "github.com/google/cadvisor/events", "ImportPath": "github.com/google/cadvisor/events",
"Comment": "v0.22.2", "Comment": "v0.23.0",
"Rev": "546a3771589bdb356777c646c6eca24914fdd48b" "Rev": "750f18e5eac3f6193b354fc14c03d92d4318a0ec"
}, },
{ {
"ImportPath": "github.com/google/cadvisor/fs", "ImportPath": "github.com/google/cadvisor/fs",
"Comment": "v0.22.2", "Comment": "v0.23.0",
"Rev": "546a3771589bdb356777c646c6eca24914fdd48b" "Rev": "750f18e5eac3f6193b354fc14c03d92d4318a0ec"
}, },
{ {
"ImportPath": "github.com/google/cadvisor/healthz", "ImportPath": "github.com/google/cadvisor/healthz",
"Comment": "v0.22.2", "Comment": "v0.23.0",
"Rev": "546a3771589bdb356777c646c6eca24914fdd48b" "Rev": "750f18e5eac3f6193b354fc14c03d92d4318a0ec"
}, },
{ {
"ImportPath": "github.com/google/cadvisor/http", "ImportPath": "github.com/google/cadvisor/http",
"Comment": "v0.22.2", "Comment": "v0.23.0",
"Rev": "546a3771589bdb356777c646c6eca24914fdd48b" "Rev": "750f18e5eac3f6193b354fc14c03d92d4318a0ec"
}, },
{ {
"ImportPath": "github.com/google/cadvisor/info/v1", "ImportPath": "github.com/google/cadvisor/info/v1",
"Comment": "v0.22.2", "Comment": "v0.23.0",
"Rev": "546a3771589bdb356777c646c6eca24914fdd48b" "Rev": "750f18e5eac3f6193b354fc14c03d92d4318a0ec"
}, },
{ {
"ImportPath": "github.com/google/cadvisor/info/v2", "ImportPath": "github.com/google/cadvisor/info/v2",
"Comment": "v0.22.2", "Comment": "v0.23.0",
"Rev": "546a3771589bdb356777c646c6eca24914fdd48b" "Rev": "750f18e5eac3f6193b354fc14c03d92d4318a0ec"
}, },
{ {
"ImportPath": "github.com/google/cadvisor/manager", "ImportPath": "github.com/google/cadvisor/manager",
"Comment": "v0.22.2", "Comment": "v0.23.0",
"Rev": "546a3771589bdb356777c646c6eca24914fdd48b" "Rev": "750f18e5eac3f6193b354fc14c03d92d4318a0ec"
}, },
{ {
"ImportPath": "github.com/google/cadvisor/metrics", "ImportPath": "github.com/google/cadvisor/metrics",
"Comment": "v0.22.2", "Comment": "v0.23.0",
"Rev": "546a3771589bdb356777c646c6eca24914fdd48b" "Rev": "750f18e5eac3f6193b354fc14c03d92d4318a0ec"
}, },
{ {
"ImportPath": "github.com/google/cadvisor/pages", "ImportPath": "github.com/google/cadvisor/pages",
"Comment": "v0.22.2", "Comment": "v0.23.0",
"Rev": "546a3771589bdb356777c646c6eca24914fdd48b" "Rev": "750f18e5eac3f6193b354fc14c03d92d4318a0ec"
}, },
{ {
"ImportPath": "github.com/google/cadvisor/storage", "ImportPath": "github.com/google/cadvisor/storage",
"Comment": "v0.22.2", "Comment": "v0.23.0",
"Rev": "546a3771589bdb356777c646c6eca24914fdd48b" "Rev": "750f18e5eac3f6193b354fc14c03d92d4318a0ec"
}, },
{ {
"ImportPath": "github.com/google/cadvisor/summary", "ImportPath": "github.com/google/cadvisor/summary",
"Comment": "v0.22.2", "Comment": "v0.23.0",
"Rev": "546a3771589bdb356777c646c6eca24914fdd48b" "Rev": "750f18e5eac3f6193b354fc14c03d92d4318a0ec"
}, },
{ {
"ImportPath": "github.com/google/cadvisor/utils", "ImportPath": "github.com/google/cadvisor/utils",
"Comment": "v0.22.2", "Comment": "v0.23.0",
"Rev": "546a3771589bdb356777c646c6eca24914fdd48b" "Rev": "750f18e5eac3f6193b354fc14c03d92d4318a0ec"
}, },
{ {
"ImportPath": "github.com/google/cadvisor/validate", "ImportPath": "github.com/google/cadvisor/validate",
"Comment": "v0.22.2", "Comment": "v0.23.0",
"Rev": "546a3771589bdb356777c646c6eca24914fdd48b" "Rev": "750f18e5eac3f6193b354fc14c03d92d4318a0ec"
}, },
{ {
"ImportPath": "github.com/google/cadvisor/version", "ImportPath": "github.com/google/cadvisor/version",
"Comment": "v0.22.2", "Comment": "v0.23.0",
"Rev": "546a3771589bdb356777c646c6eca24914fdd48b" "Rev": "750f18e5eac3f6193b354fc14c03d92d4318a0ec"
}, },
{ {
"ImportPath": "github.com/google/gofuzz", "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: = = 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. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without

View File

@@ -1,24 +1,27 @@
language: go language: go
sudo: required sudo: required
go: go:
- 1.3.3
- 1.4.2 - 1.4.2
- 1.5.3 - 1.5.3
- 1.6rc2 - 1.6
- tip - tip
os:
- linux
- osx
env: env:
- GOARCH=amd64 DOCKER_VERSION=1.7.1
- GOARCH=386 DOCKER_VERSION=1.7.1
- GOARCH=amd64 DOCKER_VERSION=1.8.3 - GOARCH=amd64 DOCKER_VERSION=1.8.3
- GOARCH=386 DOCKER_VERSION=1.8.3 - GOARCH=386 DOCKER_VERSION=1.8.3
- GOARCH=amd64 DOCKER_VERSION=1.9.1 - GOARCH=amd64 DOCKER_VERSION=1.9.1
- GOARCH=386 DOCKER_VERSION=1.9.1 - GOARCH=386 DOCKER_VERSION=1.9.1
- GOARCH=amd64 DOCKER_VERSION=1.10.0 - GOARCH=amd64 DOCKER_VERSION=1.10.3
- GOARCH=386 DOCKER_VERSION=1.10.0 - GOARCH=386 DOCKER_VERSION=1.10.3
install: install:
- make prepare_docker - travis_retry travis-scripts/install.bash
script: script:
- make test - travis-scripts/run-tests.bash
- DOCKER_HOST=tcp://127.0.0.1:2375 make integration
services: services:
- docker - 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 McCann <benmccann.com>
Ben Parees <bparees@redhat.com> Ben Parees <bparees@redhat.com>
Benno van den Berg <bennovandenberg@gmail.com> Benno van den Berg <bennovandenberg@gmail.com>
Bradley Cicenas <bradley.cicenas@gmail.com>
Brendan Fosberry <brendan@codeship.com> Brendan Fosberry <brendan@codeship.com>
Brian Lalor <blalor@bravo5.org> Brian Lalor <blalor@bravo5.org>
Brian P. Hamachek <brian@brianhama.com> Brian P. Hamachek <brian@brianhama.com>
@@ -48,6 +49,8 @@ Fabio Rehm <fgrehm@gmail.com>
Fatih Arslan <ftharsln@gmail.com> Fatih Arslan <ftharsln@gmail.com>
Flavia Missi <flaviamissi@gmail.com> Flavia Missi <flaviamissi@gmail.com>
Francisco Souza <f@souza.cc> Francisco Souza <f@souza.cc>
Frank Groeneveld <frank@frankgroeneveld.nl>
George Moura <gwmoura@gmail.com>
Grégoire Delattre <gregoire.delattre@gmail.com> Grégoire Delattre <gregoire.delattre@gmail.com>
Guillermo Álvarez Fernández <guillermo@cientifico.net> Guillermo Álvarez Fernández <guillermo@cientifico.net>
Harry Zhang <harryzhang@zju.edu.cn> Harry Zhang <harryzhang@zju.edu.cn>
@@ -84,7 +87,9 @@ Michael Schmatz <michaelschmatz@gmail.com>
Michal Fojtik <mfojtik@redhat.com> Michal Fojtik <mfojtik@redhat.com>
Mike Dillon <mike.dillon@synctree.com> Mike Dillon <mike.dillon@synctree.com>
Mrunal Patel <mrunalp@gmail.com> Mrunal Patel <mrunalp@gmail.com>
Nate Jones <nate@endot.org>
Nguyen Sy Thanh Son <sonnst@sigma-solutions.eu> Nguyen Sy Thanh Son <sonnst@sigma-solutions.eu>
Nicholas Van Wiggeren <nvanwiggeren@digitalocean.com>
Nick Ethier <ncethier@gmail.com> Nick Ethier <ncethier@gmail.com>
Omeid Matten <public@omeid.me> Omeid Matten <public@omeid.me>
Orivej Desh <orivej@gmx.fr> Orivej Desh <orivej@gmx.fr>
@@ -98,9 +103,11 @@ Philippe Lafoucrière <philippe.lafoucriere@tech-angels.com>
Rafe Colton <rafael.colton@gmail.com> Rafe Colton <rafael.colton@gmail.com>
Rob Miller <rob@kalistra.com> Rob Miller <rob@kalistra.com>
Robert Williamson <williamson.robert@gmail.com> Robert Williamson <williamson.robert@gmail.com>
Roman Khlystik <roman.khlystik@gmail.com>
Salvador Gironès <salvadorgirones@gmail.com> Salvador Gironès <salvadorgirones@gmail.com>
Sam Rijs <srijs@airpost.net> Sam Rijs <srijs@airpost.net>
Sami Wagiaalla <swagiaal@redhat.com> Sami Wagiaalla <swagiaal@redhat.com>
Samuel Archambault <sarchambault@lapresse.ca>
Samuel Karp <skarp@amazon.com> Samuel Karp <skarp@amazon.com>
Silas Sewell <silas@sewell.org> Silas Sewell <silas@sewell.org>
Simon Eskildsen <sirup@sirupsen.com> 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. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without

View File

@@ -11,8 +11,7 @@
cov \ cov \
clean clean
SRCS = $(shell git ls-files '*.go' | grep -v '^external/') PKGS = . ./testing
PKGS = ./. ./testing
all: test all: test
@@ -22,32 +21,30 @@ vendor:
lint: lint:
@ go get -v github.com/golang/lint/golint @ 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: vet:
@-go get -v golang.org/x/tools/cmd/vet
$(foreach pkg,$(PKGS),go vet $(pkg);) $(foreach pkg,$(PKGS),go vet $(pkg);)
fmt: fmt:
gofmt -w $(SRCS) gofmt -s -w $(PKGS)
fmtcheck: fmtcheck:
$(foreach file,$(SRCS),gofmt -d $(file);) @ export output=$$(gofmt -s -d $(PKGS)); \
[ -n "$${output}" ] && echo "$${output}" && export status=1; \
prepare_docker: exit $${status:-0}
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"
pretest: lint vet fmtcheck pretest: lint vet fmtcheck
test: pretest gotest:
$(foreach pkg,$(PKGS),go test $(pkg) || exit;) $(foreach pkg,$(PKGS),go test $(pkg) || exit;)
test: pretest gotest
integration: integration:
go test -tags docker_integration -run TestIntegration -v 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) [![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 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 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 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) buf.ReadFrom(r)
byteData := buf.Bytes() 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 err := json.Unmarshal(byteData, &confsWrapper); err == nil {
if confs, ok := confsWrapper["auths"]; ok { if len(confsWrapper.Auths) > 0 {
return confs, nil return confsWrapper.Auths, nil
} }
} }

View File

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

View File

@@ -14,6 +14,8 @@ import (
"strconv" "strconv"
"strings" "strings"
"time" "time"
"github.com/fsouza/go-dockerclient/external/github.com/docker/go-units"
) )
// ErrContainerAlreadyExists is the error returned by CreateContainer when the // ErrContainerAlreadyExists is the error returned by CreateContainer when the
@@ -53,6 +55,13 @@ type APIContainers struct {
SizeRootFs int64 `json:"SizeRootFs,omitempty" yaml:"SizeRootFs,omitempty"` SizeRootFs int64 `json:"SizeRootFs,omitempty" yaml:"SizeRootFs,omitempty"`
Names []string `json:"Names,omitempty" yaml:"Names,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. // ListContainers returns a slice of containers matching the given criteria.
@@ -92,10 +101,13 @@ func (p Port) Proto() string {
// State represents the state of a container. // State represents the state of a container.
type State struct { type State struct {
Status string `json:"Status,omitempty" yaml:"Status,omitempty"`
Running bool `json:"Running,omitempty" yaml:"Running,omitempty"` Running bool `json:"Running,omitempty" yaml:"Running,omitempty"`
Paused bool `json:"Paused,omitempty" yaml:"Paused,omitempty"` Paused bool `json:"Paused,omitempty" yaml:"Paused,omitempty"`
Restarting bool `json:"Restarting,omitempty" yaml:"Restarting,omitempty"` Restarting bool `json:"Restarting,omitempty" yaml:"Restarting,omitempty"`
OOMKilled bool `json:"OOMKilled,omitempty" yaml:"OOMKilled,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"` Pid int `json:"Pid,omitempty" yaml:"Pid,omitempty"`
ExitCode int `json:"ExitCode,omitempty" yaml:"ExitCode,omitempty"` ExitCode int `json:"ExitCode,omitempty" yaml:"ExitCode,omitempty"`
Error string `json:"Error,omitempty" yaml:"Error,omitempty"` Error string `json:"Error,omitempty" yaml:"Error,omitempty"`
@@ -103,15 +115,59 @@ type State struct {
FinishedAt time.Time `json:"FinishedAt,omitempty" yaml:"FinishedAt,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 { 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.Running {
if s.Paused { if s.Paused {
return "paused" return "paused"
} }
return fmt.Sprintf("Up %s", time.Now().UTC().Sub(s.StartedAt)) if s.Restarting {
return "restarting"
} }
return fmt.Sprintf("Exit %d", s.ExitCode) return "running"
}
if s.Dead {
return "dead"
}
if s.StartedAt.IsZero() {
return "created"
}
return "exited"
} }
// PortBinding represents the host/container port mapping as returned in the // 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"` IPAddress string `json:"IPAddress,omitempty" yaml:"IPAddress,omitempty"`
Gateway string `json:"Gateway,omitempty" yaml:"Gateway,omitempty"` Gateway string `json:"Gateway,omitempty" yaml:"Gateway,omitempty"`
EndpointID string `json:"EndpointID,omitempty" yaml:"EndpointID,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 // NetworkSettings contains network-related information about a container
@@ -308,6 +365,34 @@ type Container struct {
AppArmorProfile string `json:"AppArmorProfile,omitempty" yaml:"AppArmorProfile,omitempty"` 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. // RenameContainerOptions specify parameters to the RenameContainer function.
// //
// See https://goo.gl/laSOIy for more details. // See https://goo.gl/laSOIy for more details.
@@ -469,6 +554,24 @@ type Device struct {
CgroupPermissions string `json:"CgroupPermissions,omitempty" yaml:"CgroupPermissions,omitempty"` 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 // HostConfig contains the container options related to starting a container on
// a given host // a given host
type HostConfig struct { type HostConfig struct {
@@ -508,6 +611,11 @@ type HostConfig struct {
CPUQuota int64 `json:"CpuQuota,omitempty" yaml:"CpuQuota,omitempty"` CPUQuota int64 `json:"CpuQuota,omitempty" yaml:"CpuQuota,omitempty"`
CPUPeriod int64 `json:"CpuPeriod,omitempty" yaml:"CpuPeriod,omitempty"` CPUPeriod int64 `json:"CpuPeriod,omitempty" yaml:"CpuPeriod,omitempty"`
BlkioWeight int64 `json:"BlkioWeight,omitempty" yaml:"BlkioWeight"` 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"` Ulimits []ULimit `json:"Ulimits,omitempty" yaml:"Ulimits,omitempty"`
VolumeDriver string `json:"VolumeDriver,omitempty" yaml:"VolumeDriver,omitempty"` VolumeDriver string `json:"VolumeDriver,omitempty" yaml:"VolumeDriver,omitempty"`
OomScoreAdj int `json:"OomScoreAdj,omitempty" yaml:"OomScoreAdj,omitempty"` OomScoreAdj int `json:"OomScoreAdj,omitempty" yaml:"OomScoreAdj,omitempty"`

View File

@@ -18,12 +18,38 @@ import (
"time" "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 { type APIEvents struct {
Status string `json:"Status,omitempty" yaml:"Status,omitempty"` // New API Fields in 1.22
ID string `json:"ID,omitempty" yaml:"ID,omitempty"` Action string `json:"action,omitempty"`
From string `json:"From,omitempty" yaml:"From,omitempty"` Type string `json:"type,omitempty"`
Time int64 `json:"Time,omitempty" yaml:"Time,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 { type eventMonitoringState struct {
@@ -52,6 +78,7 @@ var (
// EOFEvent is sent when the event listener receives an EOF error. // EOFEvent is sent when the event listener receives an EOF error.
EOFEvent = &APIEvents{ EOFEvent = &APIEvents{
Type: "EOF",
Status: "EOF", Status: "EOF",
} }
) )
@@ -297,8 +324,47 @@ func (c *Client) eventHijack(startTime int64, eventChan chan *APIEvents, errChan
if !c.eventMonitor.isEnabled() { if !c.eventMonitor.isEnabled() {
return return
} }
transformEvent(&event)
eventChan <- &event eventChan <- &event
} }
}(res, conn) }(res, conn)
return nil 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 package system

View File

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

View File

@@ -32,6 +32,7 @@ type APIImages struct {
// Image is the type representing a docker image and its various properties // Image is the type representing a docker image and its various properties
type Image struct { type Image struct {
ID string `json:"Id" yaml:"Id"` ID string `json:"Id" yaml:"Id"`
RepoTags []string `json:"RepoTags,omitempty" yaml:"RepoTags,omitempty"`
Parent string `json:"Parent,omitempty" yaml:"Parent,omitempty"` Parent string `json:"Parent,omitempty" yaml:"Parent,omitempty"`
Comment string `json:"Comment,omitempty" yaml:"Comment,omitempty"` Comment string `json:"Comment,omitempty" yaml:"Comment,omitempty"`
Created time.Time `json:"Created,omitempty" yaml:"Created,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 AuthConfigs AuthConfigurations `qs:"-"` // for newer docker X-Registry-Config header
ContextDir string `qs:"-"` ContextDir string `qs:"-"`
Ulimits []ULimit `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 // 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{ return c.stream("POST", fmt.Sprintf("/build?%s", qs), streamOptions{
setRawTerminal: true, setRawTerminal: true,
rawJSONStream: opts.RawJSONStream, rawJSONStream: opts.RawJSONStream,

View File

@@ -4,7 +4,10 @@
package docker package docker
import "strings" import (
"encoding/json"
"strings"
)
// Version returns version information about the docker server. // Version returns version information about the docker server.
// //
@@ -22,17 +25,81 @@ func (c *Client) Version() (*Env, error) {
return &env, nil 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. // Info returns system-wide information about the Docker server.
// //
// See https://goo.gl/ElTHi2 for more details. // 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{}) resp, err := c.do("GET", "/info", doOptions{})
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer resp.Body.Close() defer resp.Body.Close()
var info Env var info DockerInfo
if err := info.Decode(resp.Body); err != nil { if err := json.NewDecoder(resp.Body).Decode(&info); err != nil {
return nil, err return nil, err
} }
return &info, nil return &info, nil

View File

@@ -5,6 +5,7 @@
package docker package docker
import ( import (
"bytes"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
@@ -26,6 +27,7 @@ type Network struct {
IPAM IPAMOptions IPAM IPAMOptions
Containers map[string]Endpoint Containers map[string]Endpoint
Options map[string]string Options map[string]string
Internal bool
} }
// Endpoint contains network resources allocated and used for a container in a network // 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 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. // NetworkInfo returns information about a network by its ID.
// //
// See https://goo.gl/6GugX3 for more details. // See https://goo.gl/6GugX3 for more details.
@@ -158,14 +185,40 @@ func (c *Client) RemoveNetwork(id string) error {
return nil 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 { type NetworkConnectionOptions struct {
Container string 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. // See https://goo.gl/6GugX3 for more details.
func (c *Client) ConnectNetwork(id string, opts NetworkConnectionOptions) error { func (c *Client) ConnectNetwork(id string, opts NetworkConnectionOptions) error {
@@ -180,7 +233,8 @@ func (c *Client) ConnectNetwork(id string, opts NetworkConnectionOptions) error
return nil 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. // See https://goo.gl/6GugX3 for more details.
func (c *Client) DisconnectNetwork(id string, opts NetworkConnectionOptions) error { 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) 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 { type NoSuchNetworkOrContainer struct {
NetworkID string NetworkID string
ContainerID 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/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("GET").HandlerFunc(s.handlerWrapper(s.inspectVolume))
s.mux.Path("/volumes/{name:.*}").Methods("DELETE").HandlerFunc(s.handlerWrapper(s.removeVolume)) 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. // 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) http.Error(w, err.Error(), http.StatusNotFound)
return return
} }
var config *docker.Config config := new(docker.Config)
runConfig := r.URL.Query().Get("run") runConfig := r.URL.Query().Get("run")
if runConfig != "" { if runConfig != "" {
config = new(docker.Config)
err = json.Unmarshal([]byte(runConfig), config) err = json.Unmarshal([]byte(runConfig), config)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest) http.Error(w, err.Error(), http.StatusBadRequest)
@@ -829,6 +829,7 @@ func (s *DockerServer) pullImage(w http.ResponseWriter, r *http.Request) {
tag := r.URL.Query().Get("tag") tag := r.URL.Query().Get("tag")
image := docker.Image{ image := docker.Image{
ID: s.generateID(), ID: s.generateID(),
Config: &docker.Config{},
} }
s.iMut.Lock() s.iMut.Lock()
s.images = append(s.images, image) 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 s.volStore[vol.volume.Name] = nil
w.WriteHeader(http.StatusNoContent) 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 // an array of ContainerHint structs, each with a container's id and networkInterface
// This allows collecting stats about network interfaces configured outside docker // This allows collecting stats about network interfaces configured outside docker
// and lxc // and lxc
package raw package common
import ( import (
"encoding/json" "encoding/json"
@@ -25,7 +25,7 @@ import (
"os" "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 { type containerHints struct {
AllHosts []containerHint `json:"all_hosts,omitempty"` AllHosts []containerHint `json:"all_hosts,omitempty"`
@@ -34,10 +34,10 @@ type containerHints struct {
type containerHint struct { type containerHint struct {
FullName string `json:"full_path,omitempty"` FullName string `json:"full_path,omitempty"`
NetworkInterface *networkInterface `json:"network_interface,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"` HostDir string `json:"host_dir,omitempty"`
ContainerDir string `json:"container_dir,omitempty"` ContainerDir string `json:"container_dir,omitempty"`
} }
@@ -47,7 +47,7 @@ type networkInterface struct {
VethChild string `json:"veth_child,omitempty"` VethChild string `json:"veth_child,omitempty"`
} }
func getContainerHintsFromFile(containerHintsFile string) (containerHints, error) { func GetContainerHintsFromFile(containerHintsFile string) (containerHints, error) {
dat, err := ioutil.ReadFile(containerHintsFile) dat, err := ioutil.ReadFile(containerHintsFile)
if os.IsNotExist(err) { if os.IsNotExist(err) {
return containerHints{}, nil return containerHints{}, nil

View File

@@ -13,7 +13,7 @@
// limitations under the License. // limitations under the License.
// Handler for Docker containers. // Handler for Docker containers.
package docker package common
import ( import (
"sync" "sync"
@@ -24,10 +24,10 @@ import (
"github.com/golang/glog" "github.com/golang/glog"
) )
type fsHandler interface { type FsHandler interface {
start() Start()
usage() (uint64, uint64) Usage() (baseUsageBytes uint64, totalUsageBytes uint64)
stop() Stop()
} }
type realFsHandler struct { type realFsHandler struct {
@@ -50,9 +50,9 @@ const (
maxDuBackoffFactor = 20 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{ return &realFsHandler{
lastUpdate: time.Time{}, lastUpdate: time.Time{},
usageBytes: 0, usageBytes: 0,
@@ -119,15 +119,15 @@ func (fh *realFsHandler) trackUsage() {
} }
} }
func (fh *realFsHandler) start() { func (fh *realFsHandler) Start() {
go fh.trackUsage() go fh.trackUsage()
} }
func (fh *realFsHandler) stop() { func (fh *realFsHandler) Stop() {
close(fh.stopChan) close(fh.stopChan)
} }
func (fh *realFsHandler) usage() (baseUsageBytes, totalUsageBytes uint64) { func (fh *realFsHandler) Usage() (baseUsageBytes, totalUsageBytes uint64) {
fh.RLock() fh.RLock()
defer fh.RUnlock() defer fh.RUnlock()
return fh.baseUsageBytes, fh.usageBytes 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 // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package raw package common
import ( import (
"sync" "sync"

View File

@@ -27,7 +27,7 @@ import (
"github.com/google/cadvisor/fs" "github.com/google/cadvisor/fs"
info "github.com/google/cadvisor/info/v1" info "github.com/google/cadvisor/info/v1"
"github.com/fsouza/go-dockerclient" docker "github.com/fsouza/go-dockerclient"
"github.com/golang/glog" "github.com/golang/glog"
) )
@@ -163,6 +163,7 @@ var (
version_re = regexp.MustCompile(version_regexp_string) version_re = regexp.MustCompile(version_regexp_string)
) )
// TODO: switch to a semantic versioning library.
func parseDockerVersion(full_version_string string) ([]int, error) { func parseDockerVersion(full_version_string string) ([]int, error) {
matches := version_re.FindAllStringSubmatch(full_version_string, -1) matches := version_re.FindAllStringSubmatch(full_version_string, -1)
if len(matches) != 1 { if len(matches) != 1 {
@@ -186,42 +187,16 @@ func Register(factory info.MachineInfoFactory, fsInfo fs.FsInfo, ignoreMetrics c
if err != nil { if err != nil {
return fmt.Errorf("unable to communicate with docker daemon: %v", err) return fmt.Errorf("unable to communicate with docker daemon: %v", err)
} }
var dockerVersion []int
if version, err := client.Version(); err != nil { dockerInfo, err := ValidateInfo()
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 { if err != nil {
return fmt.Errorf("couldn't parse docker version: %v", err) return fmt.Errorf("failed to validate Docker info: %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() // Version already validated above, assume no error here.
if err != nil { dockerVersion, _ := parseDockerVersion(dockerInfo.ServerVersion)
return fmt.Errorf("failed to detect Docker info: %v", err)
}
// Check that the libcontainer execdriver is used. storageDir := dockerInfo.DockerRootDir
execDriver := information.Get("ExecutionDriver")
if !strings.HasPrefix(execDriver, "native") {
return fmt.Errorf("docker found, but not using native exec driver")
}
sd := information.Get("Driver")
if sd == "" {
return fmt.Errorf("failed to find docker storage driver")
}
storageDir := information.Get("DockerRootDir")
if storageDir == "" { if storageDir == "" {
storageDir = *dockerRootDir storageDir = *dockerRootDir
} }
@@ -237,7 +212,7 @@ func Register(factory info.MachineInfoFactory, fsInfo fs.FsInfo, ignoreMetrics c
dockerVersion: dockerVersion, dockerVersion: dockerVersion,
fsInfo: fsInfo, fsInfo: fsInfo,
machineInfoFactory: factory, machineInfoFactory: factory,
storageDriver: storageDriver(sd), storageDriver: storageDriver(dockerInfo.Driver),
storageDir: storageDir, storageDir: storageDir,
ignoreMetrics: ignoreMetrics, ignoreMetrics: ignoreMetrics,
} }

View File

@@ -18,16 +18,15 @@ package docker
import ( import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"math"
"path" "path"
"strings" "strings"
"time" "time"
"github.com/google/cadvisor/container" "github.com/google/cadvisor/container"
"github.com/google/cadvisor/container/common"
containerlibcontainer "github.com/google/cadvisor/container/libcontainer" containerlibcontainer "github.com/google/cadvisor/container/libcontainer"
"github.com/google/cadvisor/fs" "github.com/google/cadvisor/fs"
info "github.com/google/cadvisor/info/v1" info "github.com/google/cadvisor/info/v1"
"github.com/google/cadvisor/utils"
docker "github.com/fsouza/go-dockerclient" docker "github.com/fsouza/go-dockerclient"
"github.com/opencontainers/runc/libcontainer/cgroups" "github.com/opencontainers/runc/libcontainer/cgroups"
@@ -80,7 +79,7 @@ type dockerContainerHandler struct {
networkMode string networkMode string
// Filesystem handler. // Filesystem handler.
fsHandler fsHandler fsHandler common.FsHandler
ignoreMetrics container.MetricSet ignoreMetrics container.MetricSet
} }
@@ -169,7 +168,7 @@ func newDockerContainerHandler(
} }
if !ignoreMetrics.Has(container.DiskUsageMetrics) { 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. // We assume that if Inspect fails then the container is not known to docker.
@@ -200,15 +199,14 @@ func newDockerContainerHandler(
} }
func (self *dockerContainerHandler) Start() { func (self *dockerContainerHandler) Start() {
// Start the filesystem handler.
if self.fsHandler != nil { if self.fsHandler != nil {
self.fsHandler.start() self.fsHandler.Start()
} }
} }
func (self *dockerContainerHandler) Cleanup() { func (self *dockerContainerHandler) Cleanup() {
if self.fsHandler != nil { if self.fsHandler != nil {
self.fsHandler.stop() self.fsHandler.Stop()
} }
} }
@@ -222,50 +220,6 @@ func (self *dockerContainerHandler) ContainerReference() (info.ContainerReferenc
}, nil }, 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 { func (self *dockerContainerHandler) needNet() bool {
if !self.ignoreMetrics.Has(container.NetworkUsageMetrics) { if !self.ignoreMetrics.Has(container.NetworkUsageMetrics) {
return !strings.HasPrefix(self.networkMode, "container:") return !strings.HasPrefix(self.networkMode, "container:")
@@ -274,29 +228,12 @@ func (self *dockerContainerHandler) needNet() bool {
} }
func (self *dockerContainerHandler) GetSpec() (info.ContainerSpec, error) { func (self *dockerContainerHandler) GetSpec() (info.ContainerSpec, error) {
mi, err := self.machineInfoFactory.GetMachineInfo() hasFilesystem := !self.ignoreMetrics.Has(container.DiskUsageMetrics)
if err != nil { spec, err := common.GetSpec(self.cgroupPaths, self.machineInfoFactory, self.needNet(), hasFilesystem)
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
}
}
spec.Labels = self.labels spec.Labels = self.labels
spec.Envs = self.envs spec.Envs = self.envs
spec.Image = self.image spec.Image = self.image
spec.HasNetwork = self.needNet()
return spec, err return spec, err
} }
@@ -320,6 +257,7 @@ func (self *dockerContainerHandler) getFsStats(stats *info.ContainerStats) error
if err != nil { if err != nil {
return err return err
} }
var ( var (
limit uint64 limit uint64
fsType string 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 := 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) stats.Filesystem = append(stats.Filesystem, fsStat)
return nil return nil
@@ -401,19 +339,19 @@ func (self *dockerContainerHandler) StopWatchingSubcontainers() error {
} }
func (self *dockerContainerHandler) Exists() bool { 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() client, err := Client()
if err != nil { 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() info, err := client.Info()
if err != nil { if err != nil {
return nil, err return docker.DockerInfo{}, err
} }
return info.Map(), nil return *info, nil
} }
func DockerImages() ([]docker.APIImages, error) { func DockerImages() ([]docker.APIImages, error) {
@@ -427,3 +365,47 @@ func DockerImages() ([]docker.APIImages, error) {
} }
return images, nil 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" "fmt"
"github.com/google/cadvisor/container" "github.com/google/cadvisor/container"
"github.com/google/cadvisor/container/common"
"github.com/google/cadvisor/container/libcontainer" "github.com/google/cadvisor/container/libcontainer"
"github.com/google/cadvisor/fs" "github.com/google/cadvisor/fs"
info "github.com/google/cadvisor/info/v1" info "github.com/google/cadvisor/info/v1"
@@ -39,7 +40,7 @@ type rawFactory struct {
fsInfo fs.FsInfo fsInfo fs.FsInfo
// Watcher for inotify events. // Watcher for inotify events.
watcher *InotifyWatcher watcher *common.InotifyWatcher
// List of metrics to be ignored. // List of metrics to be ignored.
ignoreMetrics map[container.MetricKind]struct{} 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 { func (self *rawFactory) DebugInfo() map[string][]string {
out := make(map[string][]string) return common.DebugInfo(self.watcher.GetWatches())
// 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
} }
func Register(machineInfoFactory info.MachineInfoFactory, fsInfo fs.FsInfo, ignoreMetrics map[container.MetricKind]struct{}) error { 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") return fmt.Errorf("failed to find supported cgroup mounts for the raw factory")
} }
watcher, err := NewInotifyWatcher() watcher, err := common.NewInotifyWatcher()
if err != nil { if err != nil {
return err return err
} }

View File

@@ -18,17 +18,14 @@ package raw
import ( import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os"
"path" "path"
"strconv"
"strings" "strings"
"time"
"github.com/google/cadvisor/container" "github.com/google/cadvisor/container"
"github.com/google/cadvisor/container/common"
"github.com/google/cadvisor/container/libcontainer" "github.com/google/cadvisor/container/libcontainer"
"github.com/google/cadvisor/fs" "github.com/google/cadvisor/fs"
info "github.com/google/cadvisor/info/v1" info "github.com/google/cadvisor/info/v1"
"github.com/google/cadvisor/utils"
"github.com/google/cadvisor/utils/machine" "github.com/google/cadvisor/utils/machine"
"github.com/golang/glog" "github.com/golang/glog"
@@ -45,7 +42,7 @@ type rawContainerHandler struct {
machineInfoFactory info.MachineInfoFactory machineInfoFactory info.MachineInfoFactory
// Inotify event watcher. // Inotify event watcher.
watcher *InotifyWatcher watcher *common.InotifyWatcher
// Signal for watcher thread to stop. // Signal for watcher thread to stop.
stopWatcher chan error stopWatcher chan error
@@ -58,7 +55,7 @@ type rawContainerHandler struct {
cgroupManager cgroups.Manager cgroupManager cgroups.Manager
fsInfo fs.FsInfo fsInfo fs.FsInfo
externalMounts []mount externalMounts []common.Mount
rootFs string rootFs string
@@ -72,14 +69,10 @@ func isRootCgroup(name string) bool {
return name == "/" 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) { func newRawContainerHandler(name string, cgroupSubsystems *libcontainer.CgroupSubsystems, machineInfoFactory info.MachineInfoFactory, fsInfo fs.FsInfo, watcher *common.InotifyWatcher, rootFs string, ignoreMetrics container.MetricSet) (container.ContainerHandler, error) {
// Create the cgroup paths. cgroupPaths := common.MakeCgroupPaths(cgroupSubsystems.MountPoints, name)
cgroupPaths := make(map[string]string, len(cgroupSubsystems.MountPoints))
for key, val := range cgroupSubsystems.MountPoints {
cgroupPaths[key] = path.Join(val, name)
}
cHints, err := getContainerHintsFromFile(*argContainerHints) cHints, err := common.GetContainerHintsFromFile(*common.ArgContainerHints)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -92,7 +85,7 @@ func newRawContainerHandler(name string, cgroupSubsystems *libcontainer.CgroupSu
Paths: cgroupPaths, Paths: cgroupPaths,
} }
var externalMounts []mount var externalMounts []common.Mount
for _, container := range cHints.AllHosts { for _, container := range cHints.AllHosts {
if name == container.FullName { if name == container.FullName {
externalMounts = container.Mounts externalMounts = container.Mounts
@@ -128,38 +121,6 @@ func (self *rawContainerHandler) ContainerReference() (info.ContainerReference,
}, nil }, 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) { func (self *rawContainerHandler) GetRootNetworkDevices() ([]info.NetInfo, error) {
nd := []info.NetInfo{} nd := []info.NetInfo{}
if isRootCgroup(self.name) { if isRootCgroup(self.name) {
@@ -179,66 +140,21 @@ func (self *rawContainerHandler) Start() {}
func (self *rawContainerHandler) Cleanup() {} func (self *rawContainerHandler) Cleanup() {}
func (self *rawContainerHandler) GetSpec() (info.ContainerSpec, error) { func (self *rawContainerHandler) GetSpec() (info.ContainerSpec, error) {
var spec info.ContainerSpec const hasNetwork = false
hasFilesystem := isRootCgroup(self.name) || len(self.externalMounts) > 0
// The raw driver assumes unified hierarchy containers. spec, err := common.GetSpec(self.cgroupPaths, self.machineInfoFactory, hasNetwork, hasFilesystem)
// 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()
if err != nil { if err != nil {
return spec, err return spec, err
} }
// CPU. if isRootCgroup(self.name) {
cpuRoot, ok := self.cgroupPaths["cpu"] // Check physical network devices for root container.
if ok { nd, err := self.GetRootNetworkDevices()
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 { if err != nil {
glog.Errorf("raw driver: Failed to parse CPUQuota from %q: %s", path.Join(cpuRoot, "cpu.cfs_quota_us"), err) return spec, err
}
spec.Cpu.Quota = val
}
}
} }
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 // Get memory and swap limits of the running machine
memLimit, err := machine.GetMachineMemoryCapacity() memLimit, err := machine.GetMachineMemoryCapacity()
if err != nil { if err != nil {
@@ -256,35 +172,8 @@ func (self *rawContainerHandler) GetSpec() (info.ContainerSpec, error) {
} else { } else {
spec.Memory.SwapLimit = uint64(swapLimit) 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 return spec, nil
} }
@@ -379,39 +268,10 @@ func (self *rawContainerHandler) GetContainerLabels() map[string]string {
return 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) { func (self *rawContainerHandler) ListContainers(listType container.ListType) ([]info.ContainerReference, error) {
containers := make(map[string]struct{}) containers := make(map[string]struct{})
for _, cgroupPath := range self.cgroupPaths { 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 { if err != nil {
return nil, err return nil, err
} }
@@ -583,11 +443,5 @@ func (self *rawContainerHandler) StopWatchingSubcontainers() error {
} }
func (self *rawContainerHandler) Exists() bool { func (self *rawContainerHandler) Exists() bool {
// If any cgroup exists, the container is still alive. return common.CgroupExists(self.cgroupPaths)
for _, cgroupPath := range self.cgroupPaths {
if utils.FileExists(cgroupPath) {
return true
}
}
return false
} }

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 ( import (
"bufio" "bufio"
"encoding/json"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
@@ -40,6 +39,7 @@ import (
const ( const (
LabelSystemRoot = "root" LabelSystemRoot = "root"
LabelDockerImages = "docker-images" LabelDockerImages = "docker-images"
LabelRktImages = "rkt-images"
) )
type partition struct { type partition struct {
@@ -62,8 +62,14 @@ type RealFsInfo struct {
type Context struct { type Context struct {
// docker root directory. // docker root directory.
DockerRoot string Docker DockerContext
DockerInfo map[string]string RktPath string
}
type DockerContext struct {
Root string
Driver string
DriverStatus map[string]string
} }
func NewFsInfo(context Context) (FsInfo, error) { func NewFsInfo(context Context) (FsInfo, error) {
@@ -76,6 +82,11 @@ func NewFsInfo(context Context) (FsInfo, error) {
labels: make(map[string]string, 0), labels: make(map[string]string, 0),
dmsetup: &defaultDmsetupClient{}, dmsetup: &defaultDmsetupClient{},
} }
fsInfo.addSystemRootLabel(mounts)
fsInfo.addDockerImagesLabel(context, mounts)
fsInfo.addRktImagesLabel(context, mounts)
supportedFsType := map[string]bool{ supportedFsType := map[string]bool{
// all ext systems are checked through prefix. // all ext systems are checked through prefix.
"btrfs": true, "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) glog.Infof("Filesystem partitions: %+v", fsInfo.partitions)
fsInfo.addSystemRootLabel()
return fsInfo, nil 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 // 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 // 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. // loopback file resides, inside of the loopback file itself.
func (self *RealFsInfo) getDockerDeviceMapperInfo(dockerInfo map[string]string) (string, *partition, error) { func (self *RealFsInfo) getDockerDeviceMapperInfo(context DockerContext) (string, *partition, error) {
if storageDriver, ok := dockerInfo["Driver"]; ok && storageDriver != DeviceMapper.String() { if context.Driver != DeviceMapper.String() {
return "", nil, nil return "", nil, nil
} }
var driverStatus [][]string dataLoopFile := context.DriverStatus["Data loop file"]
if err := json.Unmarshal([]byte(dockerInfo["DriverStatus"]), &driverStatus); err != nil {
return "", nil, err
}
dataLoopFile := dockerStatusValue(driverStatus, "Data loop file")
if len(dataLoopFile) > 0 { if len(dataLoopFile) > 0 {
return "", nil, nil 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 { if err != nil {
return "", nil, err 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 /. // addSystemRootLabel attempts to determine which device contains the mount for /.
func (self *RealFsInfo) addSystemRootLabel() { func (self *RealFsInfo) addSystemRootLabel(mounts []*mount.Info) {
for src, p := range self.partitions { for _, m := range mounts {
if p.mountpoint == "/" { if m.Mountpoint == "/" {
if _, ok := self.labels[LabelSystemRoot]; !ok { self.partitions[m.Source] = partition{
self.labels[LabelSystemRoot] = src 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. // addDockerImagesLabel attempts to determine which device contains the mount for docker images.
func (self *RealFsInfo) addDockerImagesLabel(context Context) { func (self *RealFsInfo) addDockerImagesLabel(context Context, mounts []*mount.Info) {
dockerDev, dockerPartition, err := self.getDockerDeviceMapperInfo(context.DockerInfo) dockerDev, dockerPartition, err := self.getDockerDeviceMapperInfo(context.Docker)
if err != nil { if err != nil {
glog.Warningf("Could not get Docker devicemapper device: %v", err) 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.partitions[dockerDev] = *dockerPartition
self.labels[LabelDockerImages] = dockerDev self.labels[LabelDockerImages] = dockerDev
} else { } 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. // 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 // 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`. // 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. // TODO(rjnagal): Detect docker root and graphdriver directories from docker info.
dockerRoot := context.DockerRoot dockerRoot := context.Docker.Root
dockerImagePaths := []string{}
for _, dir := range []string{"devicemapper", "btrfs", "aufs", "overlay", "zfs"} { 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 != "." { for dockerRoot != "/" && dockerRoot != "." {
dockerImagePaths = append(dockerImagePaths, dockerRoot) dockerImagePaths[dockerRoot] = struct{}{}
dockerRoot = filepath.Dir(dockerRoot) dockerRoot = filepath.Dir(dockerRoot)
} }
dockerImagePaths = append(dockerImagePaths, "/")
return dockerImagePaths return dockerImagePaths
} }
// This method compares the mountpoint with possible docker image mount points. If a match is found, // This method compares the mountpoints with possible container image mount points. If a match is found,
// docker images label is added to the partition. // the label is added to the partition.
func (self *RealFsInfo) updateDockerImagesPath(source string, mountpoint string, dockerImagePaths []string) { func (self *RealFsInfo) updateContainerImagesPath(label string, mounts []*mount.Info, containerImagePaths map[string]struct{}) {
for _, v := range dockerImagePaths { var useMount *mount.Info
if v == mountpoint { for _, m := range mounts {
if i, ok := self.labels[LabelDockerImages]; ok { if _, ok := containerImagePaths[m.Mountpoint]; ok {
// pick the innermost mountpoint. if useMount == nil || (len(useMount.Mountpoint) < len(m.Mountpoint)) {
mnt := self.partitions[i].mountpoint useMount = m
if len(mnt) < len(mountpoint) {
self.labels[LabelDockerImages] = source
}
} else {
self.labels[LabelDockerImages] = source
} }
} }
} }
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) { 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 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. // dmsetupClient knows to to interact with dmsetup to retrieve information about devicemapper.
type dmsetupClient interface { type dmsetupClient interface {
table(poolName string) ([]byte, error) table(poolName string) ([]byte, error)
@@ -438,9 +451,9 @@ func (*defaultDmsetupClient) table(poolName string) ([]byte, error) {
// Devicemapper thin provisioning is detailed at // Devicemapper thin provisioning is detailed at
// https://www.kernel.org/doc/Documentation/device-mapper/thin-provisioning.txt // https://www.kernel.org/doc/Documentation/device-mapper/thin-provisioning.txt
func dockerDMDevice(driverStatus [][]string, dmsetup dmsetupClient) (string, uint, uint, uint, error) { func dockerDMDevice(driverStatus map[string]string, dmsetup dmsetupClient) (string, uint, uint, uint, error) {
poolName := dockerStatusValue(driverStatus, "Pool Name") poolName, ok := driverStatus["Pool Name"]
if len(poolName) == 0 { if !ok || len(poolName) == 0 {
return "", 0, 0, 0, fmt.Errorf("Could not get dm pool name") return "", 0, 0, 0, fmt.Errorf("Could not get dm pool name")
} }

View File

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

View File

@@ -18,6 +18,7 @@ import (
"bytes" "bytes"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"os"
"syscall" "syscall"
info "github.com/google/cadvisor/info/v1" info "github.com/google/cadvisor/info/v1"
@@ -219,10 +220,10 @@ func verifyHeader(msg syscall.NetlinkMessage) error {
// Get load stats for a task group. // Get load stats for a task group.
// id: family id for taskstats. // 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. // conn: open netlink connection used to communicate with kernel.
func getLoadStats(id uint16, fd uintptr, conn *Connection) (info.LoadStats, error) { func getLoadStats(id uint16, cfd *os.File, conn *Connection) (info.LoadStats, error) {
msg := prepareCmdMessage(id, fd) msg := prepareCmdMessage(id, cfd.Fd())
err := conn.WriteMessage(msg.toRawMsg()) err := conn.WriteMessage(msg.toRawMsg())
if err != nil { if err != nil {
return info.LoadStats{}, err 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) cfd, err := os.Open(path)
defer cfd.Close()
if err != nil { if err != nil {
return info.LoadStats{}, fmt.Errorf("failed to open cgroup path %s: %q", path, err) 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 { if err != nil {
return info.LoadStats{}, err return info.LoadStats{}, err
} }

View File

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

View File

@@ -1 +1 @@
0.22.0 0.23.0