Bump cAdvisor (and dependencies) godeps version
This commit is contained in:
15
vendor/github.com/google/cadvisor/api/versions.go
generated
vendored
15
vendor/github.com/google/cadvisor/api/versions.go
generated
vendored
@@ -361,7 +361,10 @@ func (self *version2_0) HandleRequest(requestType string, request []string, m ma
|
||||
glog.V(4).Infof("Api - Stats: Looking for stats for container %q, options %+v", name, opt)
|
||||
infos, err := m.GetRequestedContainersInfo(name, opt)
|
||||
if err != nil {
|
||||
return err
|
||||
if len(infos) == 0 {
|
||||
return err
|
||||
}
|
||||
glog.Errorf("Error calling GetRequestedContainersInfo: %v", err)
|
||||
}
|
||||
contStats := make(map[string][]v2.DeprecatedContainerStats, 0)
|
||||
for name, cinfo := range infos {
|
||||
@@ -482,7 +485,10 @@ func (self *version2_1) HandleRequest(requestType string, request []string, m ma
|
||||
glog.V(4).Infof("Api - MachineStats(%v)", request)
|
||||
cont, err := m.GetRequestedContainersInfo("/", opt)
|
||||
if err != nil {
|
||||
return err
|
||||
if len(cont) == 0 {
|
||||
return err
|
||||
}
|
||||
glog.Errorf("Error calling GetRequestedContainersInfo: %v", err)
|
||||
}
|
||||
return writeResult(v2.MachineStatsFromV1(cont["/"]), w)
|
||||
case statsApi:
|
||||
@@ -490,7 +496,10 @@ func (self *version2_1) HandleRequest(requestType string, request []string, m ma
|
||||
glog.V(4).Infof("Api - Stats: Looking for stats for container %q, options %+v", name, opt)
|
||||
conts, err := m.GetRequestedContainersInfo(name, opt)
|
||||
if err != nil {
|
||||
return err
|
||||
if len(conts) == 0 {
|
||||
return err
|
||||
}
|
||||
glog.Errorf("Error calling GetRequestedContainersInfo: %v", err)
|
||||
}
|
||||
contStats := make(map[string]v2.ContainerInfo, len(conts))
|
||||
for name, cont := range conts {
|
||||
|
21
vendor/github.com/google/cadvisor/container/common/helpers.go
generated
vendored
21
vendor/github.com/google/cadvisor/container/common/helpers.go
generated
vendored
@@ -23,6 +23,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/cadvisor/container"
|
||||
info "github.com/google/cadvisor/info/v1"
|
||||
"github.com/google/cadvisor/utils"
|
||||
|
||||
@@ -201,3 +202,23 @@ func CgroupExists(cgroupPaths map[string]string) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func ListContainers(name string, cgroupPaths map[string]string, listType container.ListType) ([]info.ContainerReference, error) {
|
||||
containers := make(map[string]struct{})
|
||||
for _, cgroupPath := range cgroupPaths {
|
||||
err := ListDirectories(cgroupPath, name, listType == container.ListRecursive, containers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Make into container references.
|
||||
ret := make([]info.ContainerReference, 0, len(containers))
|
||||
for cont := range containers {
|
||||
ret = append(ret, info.ContainerReference{
|
||||
Name: cont,
|
||||
})
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
30
vendor/github.com/google/cadvisor/container/container.go
generated
vendored
30
vendor/github.com/google/cadvisor/container/container.go
generated
vendored
@@ -27,23 +27,15 @@ const (
|
||||
ListRecursive
|
||||
)
|
||||
|
||||
// SubcontainerEventType indicates an addition or deletion event.
|
||||
type SubcontainerEventType int
|
||||
type ContainerType int
|
||||
|
||||
const (
|
||||
SubcontainerAdd SubcontainerEventType = iota
|
||||
SubcontainerDelete
|
||||
ContainerTypeRaw ContainerType = iota
|
||||
ContainerTypeDocker
|
||||
ContainerTypeRkt
|
||||
ContainerTypeSystemd
|
||||
)
|
||||
|
||||
// SubcontainerEvent represents a
|
||||
type SubcontainerEvent struct {
|
||||
// The type of event that occurred.
|
||||
EventType SubcontainerEventType
|
||||
|
||||
// The full container name of the container where the event occurred.
|
||||
Name string
|
||||
}
|
||||
|
||||
// Interface for container operation handlers.
|
||||
type ContainerHandler interface {
|
||||
// Returns the ContainerReference
|
||||
@@ -58,18 +50,9 @@ type ContainerHandler interface {
|
||||
// Returns the subcontainers of this container.
|
||||
ListContainers(listType ListType) ([]info.ContainerReference, error)
|
||||
|
||||
// Returns the threads inside this container.
|
||||
ListThreads(listType ListType) ([]int, error)
|
||||
|
||||
// Returns the processes inside this container.
|
||||
ListProcesses(listType ListType) ([]int, error)
|
||||
|
||||
// Registers a channel to listen for events affecting subcontainers (recursively).
|
||||
WatchSubcontainers(events chan SubcontainerEvent) error
|
||||
|
||||
// Stops watching for subcontainer changes.
|
||||
StopWatchingSubcontainers() error
|
||||
|
||||
// Returns absolute cgroup path for the requested resource.
|
||||
GetCgroupPath(resource string) (string, error)
|
||||
|
||||
@@ -85,4 +68,7 @@ type ContainerHandler interface {
|
||||
// Start starts any necessary background goroutines - must be cleaned up in Cleanup().
|
||||
// It is expected that most implementations will be a no-op.
|
||||
Start()
|
||||
|
||||
// Type of handler
|
||||
Type() ContainerType
|
||||
}
|
||||
|
12
vendor/github.com/google/cadvisor/container/docker/client.go
generated
vendored
12
vendor/github.com/google/cadvisor/container/docker/client.go
generated
vendored
@@ -20,18 +20,18 @@ package docker
|
||||
import (
|
||||
"sync"
|
||||
|
||||
dclient "github.com/fsouza/go-dockerclient"
|
||||
dclient "github.com/docker/engine-api/client"
|
||||
)
|
||||
|
||||
var (
|
||||
dockerClient *dclient.Client
|
||||
dockerClientErr error
|
||||
once sync.Once
|
||||
dockerClient *dclient.Client
|
||||
dockerClientErr error
|
||||
dockerClientOnce sync.Once
|
||||
)
|
||||
|
||||
func Client() (*dclient.Client, error) {
|
||||
once.Do(func() {
|
||||
dockerClient, dockerClientErr = dclient.NewClient(*ArgDockerEndpoint)
|
||||
dockerClientOnce.Do(func() {
|
||||
dockerClient, dockerClientErr = dclient.NewClient(*ArgDockerEndpoint, "", nil, nil)
|
||||
})
|
||||
return dockerClient, dockerClientErr
|
||||
}
|
||||
|
163
vendor/github.com/google/cadvisor/container/docker/docker.go
generated
vendored
Normal file
163
vendor/github.com/google/cadvisor/container/docker/docker.go
generated
vendored
Normal file
@@ -0,0 +1,163 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Provides global docker information.
|
||||
package docker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
dockertypes "github.com/docker/engine-api/types"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/google/cadvisor/info/v1"
|
||||
"github.com/google/cadvisor/machine"
|
||||
)
|
||||
|
||||
func Status() (v1.DockerStatus, error) {
|
||||
client, err := Client()
|
||||
if err != nil {
|
||||
return v1.DockerStatus{}, fmt.Errorf("unable to communicate with docker daemon: %v", err)
|
||||
}
|
||||
dockerInfo, err := client.Info(context.Background())
|
||||
if err != nil {
|
||||
return v1.DockerStatus{}, err
|
||||
}
|
||||
|
||||
out := v1.DockerStatus{}
|
||||
out.Version = VersionString()
|
||||
out.KernelVersion = machine.KernelVersion()
|
||||
out.OS = dockerInfo.OperatingSystem
|
||||
out.Hostname = dockerInfo.Name
|
||||
out.RootDir = dockerInfo.DockerRootDir
|
||||
out.Driver = dockerInfo.Driver
|
||||
out.ExecDriver = dockerInfo.ExecutionDriver
|
||||
out.NumImages = dockerInfo.Images
|
||||
out.NumContainers = dockerInfo.Containers
|
||||
out.DriverStatus = make(map[string]string, len(dockerInfo.DriverStatus))
|
||||
for _, v := range dockerInfo.DriverStatus {
|
||||
out.DriverStatus[v[0]] = v[1]
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func Images() ([]v1.DockerImage, error) {
|
||||
client, err := Client()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to communicate with docker daemon: %v", err)
|
||||
}
|
||||
images, err := client.ImageList(context.Background(), dockertypes.ImageListOptions{All: false})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out := []v1.DockerImage{}
|
||||
const unknownTag = "<none>:<none>"
|
||||
for _, image := range images {
|
||||
if len(image.RepoTags) == 1 && image.RepoTags[0] == unknownTag {
|
||||
// images with repo or tags are uninteresting.
|
||||
continue
|
||||
}
|
||||
di := v1.DockerImage{
|
||||
ID: image.ID,
|
||||
RepoTags: image.RepoTags,
|
||||
Created: image.Created,
|
||||
VirtualSize: image.VirtualSize,
|
||||
Size: image.Size,
|
||||
}
|
||||
out = append(out, di)
|
||||
}
|
||||
return out, nil
|
||||
|
||||
}
|
||||
|
||||
// Checks whether the dockerInfo reflects a valid docker setup, and returns it if it does, or an
|
||||
// error otherwise.
|
||||
func ValidateInfo() (*dockertypes.Info, error) {
|
||||
client, err := Client()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to communicate with docker daemon: %v", err)
|
||||
}
|
||||
|
||||
dockerInfo, err := client.Info(context.Background())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to detect Docker info: %v", err)
|
||||
}
|
||||
|
||||
// Fall back to version API if ServerVersion is not set in info.
|
||||
if dockerInfo.ServerVersion == "" {
|
||||
version, err := client.ServerVersion(context.Background())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to get docker version: %v", err)
|
||||
}
|
||||
dockerInfo.ServerVersion = version.Version
|
||||
}
|
||||
version, err := parseDockerVersion(dockerInfo.ServerVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if version[0] < 1 {
|
||||
return nil, fmt.Errorf("cAdvisor requires docker version %v or above but we have found version %v reported as %q", []int{1, 0, 0}, version, dockerInfo.ServerVersion)
|
||||
}
|
||||
|
||||
// Check that the libcontainer execdriver is used if the version is < 1.11
|
||||
// (execution drivers are no longer supported as of 1.11).
|
||||
if version[0] <= 1 && version[1] <= 10 &&
|
||||
!strings.HasPrefix(dockerInfo.ExecutionDriver, "native") {
|
||||
return nil, fmt.Errorf("docker found, but not using native exec driver")
|
||||
}
|
||||
|
||||
if dockerInfo.Driver == "" {
|
||||
return nil, fmt.Errorf("failed to find docker storage driver")
|
||||
}
|
||||
|
||||
return &dockerInfo, nil
|
||||
}
|
||||
|
||||
func Version() ([]int, error) {
|
||||
return parseDockerVersion(VersionString())
|
||||
}
|
||||
|
||||
func VersionString() string {
|
||||
docker_version := "Unknown"
|
||||
client, err := Client()
|
||||
if err == nil {
|
||||
version, err := client.ServerVersion(context.Background())
|
||||
if err == nil {
|
||||
docker_version = version.Version
|
||||
}
|
||||
}
|
||||
return docker_version
|
||||
}
|
||||
|
||||
// TODO: switch to a semantic versioning library.
|
||||
func parseDockerVersion(full_version_string string) ([]int, error) {
|
||||
matches := version_re.FindAllStringSubmatch(full_version_string, -1)
|
||||
if len(matches) != 1 {
|
||||
return nil, fmt.Errorf("version string \"%v\" doesn't match expected regular expression: \"%v\"", full_version_string, version_regexp_string)
|
||||
}
|
||||
version_string_array := matches[0][1:]
|
||||
version_array := make([]int, 3)
|
||||
for index, version_string := range version_string_array {
|
||||
version, err := strconv.Atoi(version_string)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error while parsing \"%v\" in \"%v\"", version_string, full_version_string)
|
||||
}
|
||||
version_array[index] = version
|
||||
}
|
||||
return version_array, nil
|
||||
}
|
96
vendor/github.com/google/cadvisor/container/docker/factory.go
generated
vendored
96
vendor/github.com/google/cadvisor/container/docker/factory.go
generated
vendored
@@ -19,27 +19,26 @@ import (
|
||||
"fmt"
|
||||
"path"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/google/cadvisor/container"
|
||||
"github.com/google/cadvisor/container/libcontainer"
|
||||
"github.com/google/cadvisor/devicemapper"
|
||||
"github.com/google/cadvisor/fs"
|
||||
info "github.com/google/cadvisor/info/v1"
|
||||
"github.com/google/cadvisor/manager/watcher"
|
||||
dockerutil "github.com/google/cadvisor/utils/docker"
|
||||
|
||||
docker "github.com/fsouza/go-dockerclient"
|
||||
docker "github.com/docker/engine-api/client"
|
||||
"github.com/golang/glog"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
var ArgDockerEndpoint = flag.String("docker", "unix:///var/run/docker.sock", "docker endpoint")
|
||||
|
||||
// The namespace under which Docker aliases are unique.
|
||||
var DockerNamespace = "docker"
|
||||
|
||||
// Basepath to all container specific information that libcontainer stores.
|
||||
// TODO: Deprecate this flag
|
||||
var dockerRootDir = flag.String("docker_root", "/var/lib/docker", "Absolute path to the Docker state root directory (default: /var/lib/docker)")
|
||||
var dockerRunDir = flag.String("docker_run", "/var/run/docker", "Absolute path to the Docker run directory (default: /var/run/docker)")
|
||||
const DockerNamespace = "docker"
|
||||
|
||||
// Regexp that identifies docker cgroups, containers started with
|
||||
// --cgroup-parent have another prefix than 'docker'
|
||||
@@ -47,24 +46,30 @@ var dockerCgroupRegexp = regexp.MustCompile(`([a-z0-9]{64})`)
|
||||
|
||||
var dockerEnvWhitelist = flag.String("docker_env_metadata_whitelist", "", "a comma-separated list of environment variable keys that needs to be collected for docker containers")
|
||||
|
||||
// TODO(vmarmol): Export run dir too for newer Dockers.
|
||||
// Directory holding Docker container state information.
|
||||
func DockerStateDir() string {
|
||||
return libcontainer.DockerStateDir(*dockerRootDir)
|
||||
}
|
||||
var (
|
||||
// Basepath to all container specific information that libcontainer stores.
|
||||
dockerRootDir string
|
||||
|
||||
const (
|
||||
dockerRootDirKey = "Root Dir"
|
||||
dockerRootDirFlag = flag.String("docker_root", "/var/lib/docker", "DEPRECATED: docker root is read from docker info (this is a fallback, default: /var/lib/docker)")
|
||||
|
||||
dockerRootDirOnce sync.Once
|
||||
)
|
||||
|
||||
func RootDir() string {
|
||||
return *dockerRootDir
|
||||
dockerRootDirOnce.Do(func() {
|
||||
status, err := Status()
|
||||
if err == nil && status.RootDir != "" {
|
||||
dockerRootDir = status.RootDir
|
||||
} else {
|
||||
dockerRootDir = *dockerRootDirFlag
|
||||
}
|
||||
})
|
||||
return dockerRootDir
|
||||
}
|
||||
|
||||
type storageDriver string
|
||||
|
||||
const (
|
||||
// TODO: Add support for devicemapper storage usage.
|
||||
devicemapperStorageDriver storageDriver = "devicemapper"
|
||||
aufsStorageDriver storageDriver = "aufs"
|
||||
overlayStorageDriver storageDriver = "overlay"
|
||||
@@ -88,6 +93,8 @@ type dockerFactory struct {
|
||||
dockerVersion []int
|
||||
|
||||
ignoreMetrics container.MetricSet
|
||||
|
||||
thinPoolWatcher *devicemapper.ThinPoolWatcher
|
||||
}
|
||||
|
||||
func (self *dockerFactory) String() string {
|
||||
@@ -114,6 +121,7 @@ func (self *dockerFactory) NewContainerHandler(name string, inHostNamespace bool
|
||||
metadataEnvs,
|
||||
self.dockerVersion,
|
||||
self.ignoreMetrics,
|
||||
self.thinPoolWatcher,
|
||||
)
|
||||
return
|
||||
}
|
||||
@@ -146,7 +154,7 @@ func (self *dockerFactory) CanHandleAndAccept(name string) (bool, bool, error) {
|
||||
id := ContainerNameToDockerId(name)
|
||||
|
||||
// We assume that if Inspect fails then the container is not known to docker.
|
||||
ctnr, err := self.client.InspectContainer(id)
|
||||
ctnr, err := self.client.ContainerInspect(context.Background(), id)
|
||||
if err != nil || !ctnr.State.Running {
|
||||
return false, canAccept, fmt.Errorf("error inspecting container: %v", err)
|
||||
}
|
||||
@@ -163,24 +171,6 @@ var (
|
||||
version_re = regexp.MustCompile(version_regexp_string)
|
||||
)
|
||||
|
||||
// TODO: switch to a semantic versioning library.
|
||||
func parseDockerVersion(full_version_string string) ([]int, error) {
|
||||
matches := version_re.FindAllStringSubmatch(full_version_string, -1)
|
||||
if len(matches) != 1 {
|
||||
return nil, fmt.Errorf("version string \"%v\" doesn't match expected regular expression: \"%v\"", full_version_string, version_regexp_string)
|
||||
}
|
||||
version_string_array := matches[0][1:]
|
||||
version_array := make([]int, 3)
|
||||
for index, version_string := range version_string_array {
|
||||
version, err := strconv.Atoi(version_string)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error while parsing \"%v\" in \"%v\"", version_string, full_version_string)
|
||||
}
|
||||
version_array[index] = version
|
||||
}
|
||||
return version_array, nil
|
||||
}
|
||||
|
||||
// Register root container before running this function!
|
||||
func Register(factory info.MachineInfoFactory, fsInfo fs.FsInfo, ignoreMetrics container.MetricSet) error {
|
||||
client, err := Client()
|
||||
@@ -196,16 +186,35 @@ func Register(factory info.MachineInfoFactory, fsInfo fs.FsInfo, ignoreMetrics c
|
||||
// Version already validated above, assume no error here.
|
||||
dockerVersion, _ := parseDockerVersion(dockerInfo.ServerVersion)
|
||||
|
||||
storageDir := dockerInfo.DockerRootDir
|
||||
if storageDir == "" {
|
||||
storageDir = *dockerRootDir
|
||||
}
|
||||
cgroupSubsystems, err := libcontainer.GetCgroupSubsystems()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get cgroup subsystems: %v", err)
|
||||
}
|
||||
|
||||
glog.Infof("Registering Docker factory")
|
||||
var (
|
||||
dockerStorageDriver = storageDriver(dockerInfo.Driver)
|
||||
thinPoolWatcher *devicemapper.ThinPoolWatcher = nil
|
||||
)
|
||||
|
||||
if dockerStorageDriver == devicemapperStorageDriver {
|
||||
// If the storage drive is devicemapper, create and start a
|
||||
// ThinPoolWatcher to monitor the size of container CoW layers with
|
||||
// thin_ls.
|
||||
dockerThinPoolName, err := dockerutil.DockerThinPoolName(*dockerInfo)
|
||||
if err != nil {
|
||||
return fmt.Errorf("couldn't find device mapper thin pool name: %v", err)
|
||||
}
|
||||
|
||||
dockerMetadataDevice, err := dockerutil.DockerMetadataDevice(*dockerInfo)
|
||||
if err != nil {
|
||||
return fmt.Errorf("couldn't determine devicemapper metadata device")
|
||||
}
|
||||
|
||||
thinPoolWatcher = devicemapper.NewThinPoolWatcher(dockerThinPoolName, dockerMetadataDevice)
|
||||
go thinPoolWatcher.Start()
|
||||
}
|
||||
|
||||
glog.Infof("registering Docker factory")
|
||||
f := &dockerFactory{
|
||||
cgroupSubsystems: cgroupSubsystems,
|
||||
client: client,
|
||||
@@ -213,10 +222,11 @@ func Register(factory info.MachineInfoFactory, fsInfo fs.FsInfo, ignoreMetrics c
|
||||
fsInfo: fsInfo,
|
||||
machineInfoFactory: factory,
|
||||
storageDriver: storageDriver(dockerInfo.Driver),
|
||||
storageDir: storageDir,
|
||||
storageDir: RootDir(),
|
||||
ignoreMetrics: ignoreMetrics,
|
||||
thinPoolWatcher: thinPoolWatcher,
|
||||
}
|
||||
|
||||
container.RegisterContainerHandlerFactory(f)
|
||||
container.RegisterContainerHandlerFactory(f, []watcher.ContainerWatchSource{watcher.Raw})
|
||||
return nil
|
||||
}
|
||||
|
210
vendor/github.com/google/cadvisor/container/docker/handler.go
generated
vendored
210
vendor/github.com/google/cadvisor/container/docker/handler.go
generated
vendored
@@ -25,13 +25,18 @@ import (
|
||||
"github.com/google/cadvisor/container"
|
||||
"github.com/google/cadvisor/container/common"
|
||||
containerlibcontainer "github.com/google/cadvisor/container/libcontainer"
|
||||
"github.com/google/cadvisor/devicemapper"
|
||||
"github.com/google/cadvisor/fs"
|
||||
info "github.com/google/cadvisor/info/v1"
|
||||
dockerutil "github.com/google/cadvisor/utils/docker"
|
||||
|
||||
docker "github.com/fsouza/go-dockerclient"
|
||||
docker "github.com/docker/engine-api/client"
|
||||
dockercontainer "github.com/docker/engine-api/types/container"
|
||||
"github.com/golang/glog"
|
||||
"github.com/opencontainers/runc/libcontainer/cgroups"
|
||||
cgroupfs "github.com/opencontainers/runc/libcontainer/cgroups/fs"
|
||||
libcontainerconfigs "github.com/opencontainers/runc/libcontainer/configs"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -55,10 +60,18 @@ type dockerContainerHandler struct {
|
||||
// Manager of this container's cgroups.
|
||||
cgroupManager cgroups.Manager
|
||||
|
||||
// the docker storage driver
|
||||
storageDriver storageDriver
|
||||
fsInfo fs.FsInfo
|
||||
rootfsStorageDir string
|
||||
|
||||
// devicemapper state
|
||||
|
||||
// the devicemapper poolname
|
||||
poolName string
|
||||
// the devicemapper device id for the container
|
||||
deviceID string
|
||||
|
||||
// Time at which this container was created.
|
||||
creationTime time.Time
|
||||
|
||||
@@ -76,14 +89,19 @@ type dockerContainerHandler struct {
|
||||
rootFs string
|
||||
|
||||
// The network mode of the container
|
||||
networkMode string
|
||||
networkMode dockercontainer.NetworkMode
|
||||
|
||||
// Filesystem handler.
|
||||
fsHandler common.FsHandler
|
||||
|
||||
ignoreMetrics container.MetricSet
|
||||
|
||||
// thin pool watcher
|
||||
thinPoolWatcher *devicemapper.ThinPoolWatcher
|
||||
}
|
||||
|
||||
var _ container.ContainerHandler = &dockerContainerHandler{}
|
||||
|
||||
func getRwLayerID(containerID, storageDir string, sd storageDriver, dockerVersion []int) (string, error) {
|
||||
const (
|
||||
// Docker version >=1.10.0 have a randomized ID for the root fs of a container.
|
||||
@@ -101,6 +119,7 @@ func getRwLayerID(containerID, storageDir string, sd storageDriver, dockerVersio
|
||||
return string(bytes), err
|
||||
}
|
||||
|
||||
// newDockerContainerHandler returns a new container.ContainerHandler
|
||||
func newDockerContainerHandler(
|
||||
client *docker.Client,
|
||||
name string,
|
||||
@@ -113,6 +132,7 @@ func newDockerContainerHandler(
|
||||
metadataEnvs []string,
|
||||
dockerVersion []int,
|
||||
ignoreMetrics container.MetricSet,
|
||||
thinPoolWatcher *devicemapper.ThinPoolWatcher,
|
||||
) (container.ContainerHandler, error) {
|
||||
// Create the cgroup paths.
|
||||
cgroupPaths := make(map[string]string, len(cgroupSubsystems.MountPoints))
|
||||
@@ -144,14 +164,27 @@ func newDockerContainerHandler(
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var rootfsStorageDir string
|
||||
|
||||
// Determine the rootfs storage dir OR the pool name to determine the device
|
||||
var (
|
||||
rootfsStorageDir string
|
||||
poolName string
|
||||
)
|
||||
switch storageDriver {
|
||||
case aufsStorageDriver:
|
||||
rootfsStorageDir = path.Join(storageDir, string(aufsStorageDriver), aufsRWLayer, rwLayerID)
|
||||
case overlayStorageDriver:
|
||||
rootfsStorageDir = path.Join(storageDir, string(overlayStorageDriver), rwLayerID)
|
||||
case devicemapperStorageDriver:
|
||||
status, err := Status()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to determine docker status: %v", err)
|
||||
}
|
||||
|
||||
poolName = status.DriverStatus[dockerutil.DriverStatusPoolName]
|
||||
}
|
||||
|
||||
// TODO: extract object mother method
|
||||
handler := &dockerContainerHandler{
|
||||
id: id,
|
||||
client: client,
|
||||
@@ -162,21 +195,24 @@ func newDockerContainerHandler(
|
||||
storageDriver: storageDriver,
|
||||
fsInfo: fsInfo,
|
||||
rootFs: rootFs,
|
||||
poolName: poolName,
|
||||
rootfsStorageDir: rootfsStorageDir,
|
||||
envs: make(map[string]string),
|
||||
ignoreMetrics: ignoreMetrics,
|
||||
}
|
||||
|
||||
if !ignoreMetrics.Has(container.DiskUsageMetrics) {
|
||||
handler.fsHandler = common.NewFsHandler(time.Minute, rootfsStorageDir, otherStorageDir, fsInfo)
|
||||
thinPoolWatcher: thinPoolWatcher,
|
||||
}
|
||||
|
||||
// We assume that if Inspect fails then the container is not known to docker.
|
||||
ctnr, err := client.InspectContainer(id)
|
||||
ctnr, err := client.ContainerInspect(context.Background(), id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to inspect container %q: %v", id, err)
|
||||
}
|
||||
handler.creationTime = ctnr.Created
|
||||
// Timestamp returned by Docker is in time.RFC3339Nano format.
|
||||
handler.creationTime, err = time.Parse(time.RFC3339Nano, ctnr.Created)
|
||||
if err != nil {
|
||||
// This should not happen, report the error just in case
|
||||
return nil, fmt.Errorf("failed to parse the create timestamp %q for container %q: %v", ctnr.Created, id, err)
|
||||
}
|
||||
handler.pid = ctnr.State.Pid
|
||||
|
||||
// Add the name and bare ID as aliases of the container.
|
||||
@@ -184,6 +220,15 @@ func newDockerContainerHandler(
|
||||
handler.labels = ctnr.Config.Labels
|
||||
handler.image = ctnr.Config.Image
|
||||
handler.networkMode = ctnr.HostConfig.NetworkMode
|
||||
handler.deviceID = ctnr.GraphDriver.Data["DeviceId"]
|
||||
|
||||
if !ignoreMetrics.Has(container.DiskUsageMetrics) {
|
||||
handler.fsHandler = &dockerFsHandler{
|
||||
fsHandler: common.NewFsHandler(time.Minute, rootfsStorageDir, otherStorageDir, fsInfo),
|
||||
thinPoolWatcher: thinPoolWatcher,
|
||||
deviceID: handler.deviceID,
|
||||
}
|
||||
}
|
||||
|
||||
// split env vars to get metadata map.
|
||||
for _, exposedEnv := range metadataEnvs {
|
||||
@@ -198,6 +243,48 @@ func newDockerContainerHandler(
|
||||
return handler, nil
|
||||
}
|
||||
|
||||
// dockerFsHandler is a composite FsHandler implementation the incorporates
|
||||
// the common fs handler and a devicemapper ThinPoolWatcher.
|
||||
type dockerFsHandler struct {
|
||||
fsHandler common.FsHandler
|
||||
|
||||
// thinPoolWatcher is the devicemapper thin pool watcher
|
||||
thinPoolWatcher *devicemapper.ThinPoolWatcher
|
||||
// deviceID is the id of the container's fs device
|
||||
deviceID string
|
||||
}
|
||||
|
||||
var _ common.FsHandler = &dockerFsHandler{}
|
||||
|
||||
func (h *dockerFsHandler) Start() {
|
||||
h.fsHandler.Start()
|
||||
}
|
||||
|
||||
func (h *dockerFsHandler) Stop() {
|
||||
h.fsHandler.Stop()
|
||||
}
|
||||
|
||||
func (h *dockerFsHandler) Usage() (uint64, uint64) {
|
||||
baseUsage, usage := h.fsHandler.Usage()
|
||||
|
||||
// When devicemapper is the storage driver, the base usage of the container comes from the thin pool.
|
||||
// We still need the result of the fsHandler for any extra storage associated with the container.
|
||||
// To correctly factor in the thin pool usage, we should:
|
||||
// * Usage the thin pool usage as the base usage
|
||||
// * Calculate the overall usage by adding the overall usage from the fs handler to the thin pool usage
|
||||
if h.thinPoolWatcher != nil {
|
||||
thinPoolUsage, err := h.thinPoolWatcher.GetUsage(h.deviceID)
|
||||
if err != nil {
|
||||
glog.Errorf("unable to get fs usage from thin pool for device %v: %v", h.deviceID, err)
|
||||
} else {
|
||||
baseUsage = thinPoolUsage
|
||||
usage += thinPoolUsage
|
||||
}
|
||||
}
|
||||
|
||||
return baseUsage, usage
|
||||
}
|
||||
|
||||
func (self *dockerContainerHandler) Start() {
|
||||
if self.fsHandler != nil {
|
||||
self.fsHandler.Start()
|
||||
@@ -222,7 +309,7 @@ func (self *dockerContainerHandler) ContainerReference() (info.ContainerReferenc
|
||||
|
||||
func (self *dockerContainerHandler) needNet() bool {
|
||||
if !self.ignoreMetrics.Has(container.NetworkUsageMetrics) {
|
||||
return !strings.HasPrefix(self.networkMode, "container:")
|
||||
return !self.networkMode.IsContainer()
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -242,17 +329,22 @@ func (self *dockerContainerHandler) getFsStats(stats *info.ContainerStats) error
|
||||
if self.ignoreMetrics.Has(container.DiskUsageMetrics) {
|
||||
return nil
|
||||
}
|
||||
var device string
|
||||
switch self.storageDriver {
|
||||
case devicemapperStorageDriver:
|
||||
// Device has to be the pool name to correlate with the device name as
|
||||
// set in the machine info filesystems.
|
||||
device = self.poolName
|
||||
case aufsStorageDriver, overlayStorageDriver, zfsStorageDriver:
|
||||
deviceInfo, err := self.fsInfo.GetDirFsDevice(self.rootfsStorageDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to determine device info for dir: %v: %v", self.rootfsStorageDir, err)
|
||||
}
|
||||
device = deviceInfo.Device
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
deviceInfo, err := self.fsInfo.GetDirFsDevice(self.rootfsStorageDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mi, err := self.machineInfoFactory.GetMachineInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -265,16 +357,16 @@ func (self *dockerContainerHandler) getFsStats(stats *info.ContainerStats) error
|
||||
|
||||
// Docker does not impose any filesystem limits for containers. So use capacity as limit.
|
||||
for _, fs := range mi.Filesystems {
|
||||
if fs.Device == deviceInfo.Device {
|
||||
if fs.Device == device {
|
||||
limit = fs.Capacity
|
||||
fsType = fs.Type
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
fsStat := info.FsStats{Device: deviceInfo.Device, Type: fsType, Limit: limit}
|
||||
|
||||
fsStat := info.FsStats{Device: device, Type: fsType, Limit: limit}
|
||||
fsStat.BaseUsage, fsStat.Usage = self.fsHandler.Usage()
|
||||
|
||||
stats.Filesystem = append(stats.Filesystem, fsStat)
|
||||
|
||||
return nil
|
||||
@@ -316,11 +408,6 @@ func (self *dockerContainerHandler) GetCgroupPath(resource string) (string, erro
|
||||
return path, nil
|
||||
}
|
||||
|
||||
func (self *dockerContainerHandler) ListThreads(listType container.ListType) ([]int, error) {
|
||||
// TODO(vmarmol): Implement.
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (self *dockerContainerHandler) GetContainerLabels() map[string]string {
|
||||
return self.labels
|
||||
}
|
||||
@@ -329,83 +416,10 @@ func (self *dockerContainerHandler) ListProcesses(listType container.ListType) (
|
||||
return containerlibcontainer.GetProcesses(self.cgroupManager)
|
||||
}
|
||||
|
||||
func (self *dockerContainerHandler) WatchSubcontainers(events chan container.SubcontainerEvent) error {
|
||||
return fmt.Errorf("watch is unimplemented in the Docker container driver")
|
||||
}
|
||||
|
||||
func (self *dockerContainerHandler) StopWatchingSubcontainers() error {
|
||||
// No-op for Docker driver.
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *dockerContainerHandler) Exists() bool {
|
||||
return common.CgroupExists(self.cgroupPaths)
|
||||
}
|
||||
|
||||
func DockerInfo() (docker.DockerInfo, error) {
|
||||
client, err := Client()
|
||||
if err != nil {
|
||||
return docker.DockerInfo{}, fmt.Errorf("unable to communicate with docker daemon: %v", err)
|
||||
}
|
||||
info, err := client.Info()
|
||||
if err != nil {
|
||||
return docker.DockerInfo{}, err
|
||||
}
|
||||
return *info, nil
|
||||
}
|
||||
|
||||
func DockerImages() ([]docker.APIImages, error) {
|
||||
client, err := Client()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to communicate with docker daemon: %v", err)
|
||||
}
|
||||
images, err := client.ListImages(docker.ListImagesOptions{All: false})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
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
|
||||
func (self *dockerContainerHandler) Type() container.ContainerType {
|
||||
return container.ContainerTypeDocker
|
||||
}
|
||||
|
24
vendor/github.com/google/cadvisor/container/factory.go
generated
vendored
24
vendor/github.com/google/cadvisor/container/factory.go
generated
vendored
@@ -18,6 +18,8 @@ import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/google/cadvisor/manager/watcher"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
@@ -67,17 +69,19 @@ func (ms MetricSet) Add(mk MetricKind) {
|
||||
// TODO(vmarmol): Consider not making this global.
|
||||
// Global list of factories.
|
||||
var (
|
||||
factories []ContainerHandlerFactory
|
||||
factories = map[watcher.ContainerWatchSource][]ContainerHandlerFactory{}
|
||||
factoriesLock sync.RWMutex
|
||||
)
|
||||
|
||||
// Register a ContainerHandlerFactory. These should be registered from least general to most general
|
||||
// as they will be asked in order whether they can handle a particular container.
|
||||
func RegisterContainerHandlerFactory(factory ContainerHandlerFactory) {
|
||||
func RegisterContainerHandlerFactory(factory ContainerHandlerFactory, watchTypes []watcher.ContainerWatchSource) {
|
||||
factoriesLock.Lock()
|
||||
defer factoriesLock.Unlock()
|
||||
|
||||
factories = append(factories, factory)
|
||||
for _, watchType := range watchTypes {
|
||||
factories[watchType] = append(factories[watchType], factory)
|
||||
}
|
||||
}
|
||||
|
||||
// Returns whether there are any container handler factories registered.
|
||||
@@ -89,12 +93,12 @@ func HasFactories() bool {
|
||||
}
|
||||
|
||||
// Create a new ContainerHandler for the specified container.
|
||||
func NewContainerHandler(name string, inHostNamespace bool) (ContainerHandler, bool, error) {
|
||||
func NewContainerHandler(name string, watchType watcher.ContainerWatchSource, inHostNamespace bool) (ContainerHandler, bool, error) {
|
||||
factoriesLock.RLock()
|
||||
defer factoriesLock.RUnlock()
|
||||
|
||||
// Create the ContainerHandler with the first factory that supports it.
|
||||
for _, factory := range factories {
|
||||
for _, factory := range factories[watchType] {
|
||||
canHandle, canAccept, err := factory.CanHandleAndAccept(name)
|
||||
if err != nil {
|
||||
glog.V(4).Infof("Error trying to work out if we can handle %s: %v", name, err)
|
||||
@@ -120,7 +124,7 @@ func ClearContainerHandlerFactories() {
|
||||
factoriesLock.Lock()
|
||||
defer factoriesLock.Unlock()
|
||||
|
||||
factories = make([]ContainerHandlerFactory, 0, 4)
|
||||
factories = map[watcher.ContainerWatchSource][]ContainerHandlerFactory{}
|
||||
}
|
||||
|
||||
func DebugInfo() map[string][]string {
|
||||
@@ -129,9 +133,11 @@ func DebugInfo() map[string][]string {
|
||||
|
||||
// Get debug information for all factories.
|
||||
out := make(map[string][]string)
|
||||
for _, factory := range factories {
|
||||
for k, v := range factory.DebugInfo() {
|
||||
out[k] = v
|
||||
for _, factoriesSlice := range factories {
|
||||
for _, factory := range factoriesSlice {
|
||||
for k, v := range factory.DebugInfo() {
|
||||
out[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
return out
|
||||
|
4
vendor/github.com/google/cadvisor/container/libcontainer/helpers.go
generated
vendored
4
vendor/github.com/google/cadvisor/container/libcontainer/helpers.go
generated
vendored
@@ -299,10 +299,6 @@ func GetProcesses(cgroupManager cgroups.Manager) ([]int, error) {
|
||||
return pids, nil
|
||||
}
|
||||
|
||||
func DockerStateDir(dockerRoot string) string {
|
||||
return path.Join(dockerRoot, "containers")
|
||||
}
|
||||
|
||||
func DiskStatsCopy0(major, minor uint64) *info.PerDiskStats {
|
||||
disk := info.PerDiskStats{
|
||||
Major: major,
|
||||
|
20
vendor/github.com/google/cadvisor/container/mock.go
generated
vendored
20
vendor/github.com/google/cadvisor/container/mock.go
generated
vendored
@@ -72,26 +72,11 @@ func (self *MockContainerHandler) ListContainers(listType ListType) ([]info.Cont
|
||||
return args.Get(0).([]info.ContainerReference), args.Error(1)
|
||||
}
|
||||
|
||||
func (self *MockContainerHandler) ListThreads(listType ListType) ([]int, error) {
|
||||
args := self.Called(listType)
|
||||
return args.Get(0).([]int), args.Error(1)
|
||||
}
|
||||
|
||||
func (self *MockContainerHandler) ListProcesses(listType ListType) ([]int, error) {
|
||||
args := self.Called(listType)
|
||||
return args.Get(0).([]int), args.Error(1)
|
||||
}
|
||||
|
||||
func (self *MockContainerHandler) WatchSubcontainers(events chan SubcontainerEvent) error {
|
||||
args := self.Called(events)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (self *MockContainerHandler) StopWatchingSubcontainers() error {
|
||||
args := self.Called()
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (self *MockContainerHandler) Exists() bool {
|
||||
args := self.Called()
|
||||
return args.Get(0).(bool)
|
||||
@@ -107,6 +92,11 @@ func (self *MockContainerHandler) GetContainerLabels() map[string]string {
|
||||
return args.Get(0).(map[string]string)
|
||||
}
|
||||
|
||||
func (self *MockContainerHandler) Type() ContainerType {
|
||||
args := self.Called()
|
||||
return args.Get(0).(ContainerType)
|
||||
}
|
||||
|
||||
type FactoryForMockContainerHandler struct {
|
||||
Name string
|
||||
PrepareContainerHandlerFunc func(name string, handler *MockContainerHandler)
|
||||
|
3
vendor/github.com/google/cadvisor/container/raw/factory.go
generated
vendored
3
vendor/github.com/google/cadvisor/container/raw/factory.go
generated
vendored
@@ -23,6 +23,7 @@ import (
|
||||
"github.com/google/cadvisor/container/libcontainer"
|
||||
"github.com/google/cadvisor/fs"
|
||||
info "github.com/google/cadvisor/info/v1"
|
||||
watch "github.com/google/cadvisor/manager/watcher"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
@@ -90,6 +91,6 @@ func Register(machineInfoFactory info.MachineInfoFactory, fsInfo fs.FsInfo, igno
|
||||
watcher: watcher,
|
||||
ignoreMetrics: ignoreMetrics,
|
||||
}
|
||||
container.RegisterContainerHandlerFactory(factory)
|
||||
container.RegisterContainerHandlerFactory(factory, []watch.ContainerWatchSource{watch.Raw})
|
||||
return nil
|
||||
}
|
||||
|
186
vendor/github.com/google/cadvisor/container/raw/handler.go
generated
vendored
186
vendor/github.com/google/cadvisor/container/raw/handler.go
generated
vendored
@@ -17,22 +17,18 @@ package raw
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/google/cadvisor/container"
|
||||
"github.com/google/cadvisor/container/common"
|
||||
"github.com/google/cadvisor/container/libcontainer"
|
||||
"github.com/google/cadvisor/fs"
|
||||
info "github.com/google/cadvisor/info/v1"
|
||||
"github.com/google/cadvisor/utils/machine"
|
||||
"github.com/google/cadvisor/machine"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/opencontainers/runc/libcontainer/cgroups"
|
||||
cgroupfs "github.com/opencontainers/runc/libcontainer/cgroups/fs"
|
||||
"github.com/opencontainers/runc/libcontainer/configs"
|
||||
"golang.org/x/exp/inotify"
|
||||
)
|
||||
|
||||
type rawContainerHandler struct {
|
||||
@@ -41,12 +37,6 @@ type rawContainerHandler struct {
|
||||
cgroupSubsystems *libcontainer.CgroupSubsystems
|
||||
machineInfoFactory info.MachineInfoFactory
|
||||
|
||||
// Inotify event watcher.
|
||||
watcher *common.InotifyWatcher
|
||||
|
||||
// Signal for watcher thread to stop.
|
||||
stopWatcher chan error
|
||||
|
||||
// Absolute path to the cgroup hierarchies of this container.
|
||||
// (e.g.: "cpu" -> "/sys/fs/cgroup/cpu/test")
|
||||
cgroupPaths map[string]string
|
||||
@@ -102,12 +92,10 @@ func newRawContainerHandler(name string, cgroupSubsystems *libcontainer.CgroupSu
|
||||
name: name,
|
||||
cgroupSubsystems: cgroupSubsystems,
|
||||
machineInfoFactory: machineInfoFactory,
|
||||
stopWatcher: make(chan error),
|
||||
cgroupPaths: cgroupPaths,
|
||||
cgroupManager: cgroupManager,
|
||||
fsInfo: fsInfo,
|
||||
externalMounts: externalMounts,
|
||||
watcher: watcher,
|
||||
rootFs: rootFs,
|
||||
ignoreMetrics: ignoreMetrics,
|
||||
pid: pid,
|
||||
@@ -269,179 +257,17 @@ func (self *rawContainerHandler) GetContainerLabels() map[string]string {
|
||||
}
|
||||
|
||||
func (self *rawContainerHandler) ListContainers(listType container.ListType) ([]info.ContainerReference, error) {
|
||||
containers := make(map[string]struct{})
|
||||
for _, cgroupPath := range self.cgroupPaths {
|
||||
err := common.ListDirectories(cgroupPath, self.name, listType == container.ListRecursive, containers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Make into container references.
|
||||
ret := make([]info.ContainerReference, 0, len(containers))
|
||||
for cont := range containers {
|
||||
ret = append(ret, info.ContainerReference{
|
||||
Name: cont,
|
||||
})
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *rawContainerHandler) ListThreads(listType container.ListType) ([]int, error) {
|
||||
// TODO(vmarmol): Implement
|
||||
return nil, nil
|
||||
return common.ListContainers(self.name, self.cgroupPaths, listType)
|
||||
}
|
||||
|
||||
func (self *rawContainerHandler) ListProcesses(listType container.ListType) ([]int, error) {
|
||||
return libcontainer.GetProcesses(self.cgroupManager)
|
||||
}
|
||||
|
||||
// Watches the specified directory and all subdirectories. Returns whether the path was
|
||||
// already being watched and an error (if any).
|
||||
func (self *rawContainerHandler) watchDirectory(dir string, containerName string) (bool, error) {
|
||||
alreadyWatching, err := self.watcher.AddWatch(containerName, dir)
|
||||
if err != nil {
|
||||
return alreadyWatching, err
|
||||
}
|
||||
|
||||
// Remove the watch if further operations failed.
|
||||
cleanup := true
|
||||
defer func() {
|
||||
if cleanup {
|
||||
_, err := self.watcher.RemoveWatch(containerName, dir)
|
||||
if err != nil {
|
||||
glog.Warningf("Failed to remove inotify watch for %q: %v", dir, err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// TODO(vmarmol): We should re-do this once we're done to ensure directories were not added in the meantime.
|
||||
// Watch subdirectories as well.
|
||||
entries, err := ioutil.ReadDir(dir)
|
||||
if err != nil {
|
||||
return alreadyWatching, err
|
||||
}
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() {
|
||||
// TODO(vmarmol): We don't have to fail here, maybe we can recover and try to get as many registrations as we can.
|
||||
_, err = self.watchDirectory(path.Join(dir, entry.Name()), path.Join(containerName, entry.Name()))
|
||||
if err != nil {
|
||||
return alreadyWatching, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cleanup = false
|
||||
return alreadyWatching, nil
|
||||
}
|
||||
|
||||
func (self *rawContainerHandler) processEvent(event *inotify.Event, events chan container.SubcontainerEvent) error {
|
||||
// Convert the inotify event type to a container create or delete.
|
||||
var eventType container.SubcontainerEventType
|
||||
switch {
|
||||
case (event.Mask & inotify.IN_CREATE) > 0:
|
||||
eventType = container.SubcontainerAdd
|
||||
case (event.Mask & inotify.IN_DELETE) > 0:
|
||||
eventType = container.SubcontainerDelete
|
||||
case (event.Mask & inotify.IN_MOVED_FROM) > 0:
|
||||
eventType = container.SubcontainerDelete
|
||||
case (event.Mask & inotify.IN_MOVED_TO) > 0:
|
||||
eventType = container.SubcontainerAdd
|
||||
default:
|
||||
// Ignore other events.
|
||||
return nil
|
||||
}
|
||||
|
||||
// Derive the container name from the path name.
|
||||
var containerName string
|
||||
for _, mount := range self.cgroupSubsystems.Mounts {
|
||||
mountLocation := path.Clean(mount.Mountpoint) + "/"
|
||||
if strings.HasPrefix(event.Name, mountLocation) {
|
||||
containerName = event.Name[len(mountLocation)-1:]
|
||||
break
|
||||
}
|
||||
}
|
||||
if containerName == "" {
|
||||
return fmt.Errorf("unable to detect container from watch event on directory %q", event.Name)
|
||||
}
|
||||
|
||||
// Maintain the watch for the new or deleted container.
|
||||
switch {
|
||||
case eventType == container.SubcontainerAdd:
|
||||
// New container was created, watch it.
|
||||
alreadyWatched, err := self.watchDirectory(event.Name, containerName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Only report container creation once.
|
||||
if alreadyWatched {
|
||||
return nil
|
||||
}
|
||||
case eventType == container.SubcontainerDelete:
|
||||
// Container was deleted, stop watching for it.
|
||||
lastWatched, err := self.watcher.RemoveWatch(containerName, event.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Only report container deletion once.
|
||||
if !lastWatched {
|
||||
return nil
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unknown event type %v", eventType)
|
||||
}
|
||||
|
||||
// Deliver the event.
|
||||
events <- container.SubcontainerEvent{
|
||||
EventType: eventType,
|
||||
Name: containerName,
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *rawContainerHandler) WatchSubcontainers(events chan container.SubcontainerEvent) error {
|
||||
// Watch this container (all its cgroups) and all subdirectories.
|
||||
for _, cgroupPath := range self.cgroupPaths {
|
||||
_, err := self.watchDirectory(cgroupPath, self.name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Process the events received from the kernel.
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case event := <-self.watcher.Event():
|
||||
err := self.processEvent(event, events)
|
||||
if err != nil {
|
||||
glog.Warningf("Error while processing event (%+v): %v", event, err)
|
||||
}
|
||||
case err := <-self.watcher.Error():
|
||||
glog.Warningf("Error while watching %q:", self.name, err)
|
||||
case <-self.stopWatcher:
|
||||
err := self.watcher.Close()
|
||||
if err == nil {
|
||||
self.stopWatcher <- err
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *rawContainerHandler) StopWatchingSubcontainers() error {
|
||||
// Rendezvous with the watcher thread.
|
||||
self.stopWatcher <- nil
|
||||
return <-self.stopWatcher
|
||||
}
|
||||
|
||||
func (self *rawContainerHandler) Exists() bool {
|
||||
return common.CgroupExists(self.cgroupPaths)
|
||||
}
|
||||
|
||||
func (self *rawContainerHandler) Type() container.ContainerType {
|
||||
return container.ContainerTypeRaw
|
||||
}
|
||||
|
22
vendor/github.com/google/cadvisor/container/rkt/client.go
generated
vendored
22
vendor/github.com/google/cadvisor/container/rkt/client.go
generated
vendored
@@ -20,6 +20,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/blang/semver"
|
||||
rktapi "github.com/coreos/rkt/api/v1alpha"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
@@ -29,6 +30,7 @@ import (
|
||||
const (
|
||||
defaultRktAPIServiceAddr = "localhost:15441"
|
||||
timeout = 2 * time.Second
|
||||
minimumRktBinVersion = "1.6.0"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -55,7 +57,25 @@ func Client() (rktapi.PublicAPIClient, error) {
|
||||
return
|
||||
}
|
||||
|
||||
rktClient = rktapi.NewPublicAPIClient(apisvcConn)
|
||||
apisvc := rktapi.NewPublicAPIClient(apisvcConn)
|
||||
|
||||
resp, err := apisvc.GetInfo(context.Background(), &rktapi.GetInfoRequest{})
|
||||
if err != nil {
|
||||
rktClientErr = fmt.Errorf("rkt: GetInfo() failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
binVersion, err := semver.Make(resp.Info.RktVersion)
|
||||
if err != nil {
|
||||
rktClientErr = fmt.Errorf("rkt: couldn't parse RtVersion: %v", err)
|
||||
return
|
||||
}
|
||||
if binVersion.LT(semver.MustParse(minimumRktBinVersion)) {
|
||||
rktClientErr = fmt.Errorf("rkt: binary version is too old(%v), requires at least %v", resp.Info.RktVersion, minimumRktBinVersion)
|
||||
return
|
||||
}
|
||||
|
||||
rktClient = apisvc
|
||||
})
|
||||
|
||||
return rktClient, rktClientErr
|
||||
|
13
vendor/github.com/google/cadvisor/container/rkt/factory.go
generated
vendored
13
vendor/github.com/google/cadvisor/container/rkt/factory.go
generated
vendored
@@ -16,12 +16,12 @@ 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/google/cadvisor/manager/watcher"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
@@ -58,14 +58,9 @@ func (self *rktFactory) NewContainerHandler(name string, inHostNamespace bool) (
|
||||
}
|
||||
|
||||
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
|
||||
accept, err := verifyPod(name)
|
||||
|
||||
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)
|
||||
return accept, accept, err
|
||||
}
|
||||
|
||||
func (self *rktFactory) DebugInfo() map[string][]string {
|
||||
@@ -99,6 +94,6 @@ func Register(machineInfoFactory info.MachineInfoFactory, fsInfo fs.FsInfo, igno
|
||||
ignoreMetrics: ignoreMetrics,
|
||||
rktPath: rktPath,
|
||||
}
|
||||
container.RegisterContainerHandlerFactory(factory)
|
||||
container.RegisterContainerHandlerFactory(factory, []watcher.ContainerWatchSource{watcher.Rkt})
|
||||
return nil
|
||||
}
|
||||
|
94
vendor/github.com/google/cadvisor/container/rkt/handler.go
generated
vendored
94
vendor/github.com/google/cadvisor/container/rkt/handler.go
generated
vendored
@@ -18,7 +18,6 @@ package rkt
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
rktapi "github.com/coreos/rkt/api/v1alpha"
|
||||
@@ -100,20 +99,19 @@ func newRktContainerHandler(name string, rktClient rktapi.PublicAPIClient, rktPa
|
||||
})
|
||||
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)
|
||||
}
|
||||
annotations := resp.Pod.Annotations
|
||||
if parsed.Container != "" { // As not empty string, an App container
|
||||
if contAnnotations, ok := findAnnotations(resp.Pod.Apps, parsed.Container); !ok {
|
||||
glog.Warningf("couldn't find app %v in pod", parsed.Container)
|
||||
} else {
|
||||
annotations = append(annotations, contAnnotations...)
|
||||
}
|
||||
} else { // The Pod container
|
||||
pid = int(resp.Pod.Pid)
|
||||
apiPod = resp.Pod
|
||||
}
|
||||
labels = createLabels(annotations)
|
||||
|
||||
cgroupPaths := common.MakeCgroupPaths(cgroupSubsystems.MountPoints, name)
|
||||
|
||||
@@ -196,7 +194,12 @@ func (handler *rktContainerHandler) Cleanup() {
|
||||
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)
|
||||
|
||||
spec, err := common.GetSpec(handler.cgroupPaths, handler.machineInfoFactory, hasNetwork, hasFilesystem)
|
||||
|
||||
spec.Labels = handler.labels
|
||||
|
||||
return spec, err
|
||||
}
|
||||
|
||||
func (handler *rktContainerHandler) getFsStats(stats *info.ContainerStats) error {
|
||||
@@ -260,68 +263,17 @@ func (handler *rktContainerHandler) GetContainerLabels() map[string]string {
|
||||
}
|
||||
|
||||
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
|
||||
return common.ListContainers(handler.name, handler.cgroupPaths, listType)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
func (handler *rktContainerHandler) Type() container.ContainerType {
|
||||
return container.ContainerTypeRkt
|
||||
}
|
||||
|
88
vendor/github.com/google/cadvisor/container/rkt/helpers.go
generated
vendored
88
vendor/github.com/google/cadvisor/container/rkt/helpers.go
generated
vendored
@@ -20,7 +20,9 @@ import (
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
rktapi "github.com/coreos/rkt/api/v1alpha"
|
||||
"github.com/golang/glog"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
type parsedName struct {
|
||||
@@ -28,34 +30,80 @@ type parsedName struct {
|
||||
Container string
|
||||
}
|
||||
|
||||
func verifyName(name string) (bool, error) {
|
||||
_, err := parseName(name)
|
||||
return err == nil, err
|
||||
func verifyPod(name string) (bool, error) {
|
||||
pod, err := cgroupToPod(name)
|
||||
|
||||
if err != nil || pod == nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Anything handler can handle is also accepted.
|
||||
// Accept cgroups that are sub the pod cgroup, except "system.slice"
|
||||
// - "system.slice" doesn't contain any processes itself
|
||||
accept := !strings.HasSuffix(name, "/system.slice")
|
||||
|
||||
return accept, nil
|
||||
}
|
||||
|
||||
func cgroupToPod(name string) (*rktapi.Pod, error) {
|
||||
rktClient, err := Client()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("couldn't get rkt api service: %v", err)
|
||||
}
|
||||
|
||||
resp, err := rktClient.ListPods(context.Background(), &rktapi.ListPodsRequest{
|
||||
Filters: []*rktapi.PodFilter{
|
||||
{
|
||||
States: []rktapi.PodState{rktapi.PodState_POD_STATE_RUNNING},
|
||||
PodSubCgroups: []string{name},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list pods: %v", err)
|
||||
}
|
||||
|
||||
if len(resp.Pods) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if len(resp.Pods) != 1 {
|
||||
return nil, fmt.Errorf("returned %d (expected 1) pods for cgroup %v", len(resp.Pods), name)
|
||||
}
|
||||
|
||||
return resp.Pods[0], nil
|
||||
}
|
||||
|
||||
/* 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
|
||||
pod - /machine.slice/machine-rkt\\x2df556b64a\\x2d17a7\\x2d47d7\\x2d93ec\\x2def2275c3d67e.scope/
|
||||
or /system.slice/k8s-..../
|
||||
container under pod - /machine.slice/machine-rkt\\x2df556b64a\\x2d17a7\\x2d47d7\\x2d93ec\\x2def2275c3d67e.scope/system.slice/alpine-sh.service
|
||||
or /system.slice/k8s-..../system.slice/pause.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{}
|
||||
pod, err := cgroupToPod(name)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parseName: couldn't convert %v to a rkt pod: %v", name, err)
|
||||
}
|
||||
if pod == nil {
|
||||
return nil, fmt.Errorf("parseName: didn't return a pod for %v", name)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
splits := strings.Split(name, "/")
|
||||
|
||||
parsed := &parsedName{}
|
||||
|
||||
if len(splits) == 3 || len(splits) == 5 {
|
||||
parsed.Pod = pod.Id
|
||||
|
||||
if len(splits) == 5 {
|
||||
parsed.Container = strings.Replace(splits[4], ".service", "", -1)
|
||||
}
|
||||
|
||||
return parsed, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("%s not handled by rkt handler", name)
|
||||
@@ -80,7 +128,7 @@ func getRootFs(root string, parsed *parsedName) string {
|
||||
|
||||
bytes, err := ioutil.ReadFile(tree)
|
||||
if err != nil {
|
||||
glog.Infof("ReadFile failed, couldn't read %v to get upper dir: %v", tree, err)
|
||||
glog.Errorf("ReadFile failed, couldn't read %v to get upper dir: %v", tree, err)
|
||||
return ""
|
||||
}
|
||||
|
||||
|
3
vendor/github.com/google/cadvisor/container/systemd/factory.go
generated
vendored
3
vendor/github.com/google/cadvisor/container/systemd/factory.go
generated
vendored
@@ -21,6 +21,7 @@ import (
|
||||
"github.com/google/cadvisor/container"
|
||||
"github.com/google/cadvisor/fs"
|
||||
info "github.com/google/cadvisor/info/v1"
|
||||
"github.com/google/cadvisor/manager/watcher"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
@@ -52,6 +53,6 @@ func (f *systemdFactory) DebugInfo() map[string][]string {
|
||||
func Register(machineInfoFactory info.MachineInfoFactory, fsInfo fs.FsInfo, ignoreMetrics container.MetricSet) error {
|
||||
glog.Infof("Registering systemd factory")
|
||||
factory := &systemdFactory{}
|
||||
container.RegisterContainerHandlerFactory(factory)
|
||||
container.RegisterContainerHandlerFactory(factory, []watcher.ContainerWatchSource{watcher.Raw})
|
||||
return nil
|
||||
}
|
||||
|
56
vendor/github.com/google/cadvisor/devicemapper/dmsetup_client.go
generated
vendored
Normal file
56
vendor/github.com/google/cadvisor/devicemapper/dmsetup_client.go
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
// 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 devicemapper
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
// DmsetupClient is a low-level client for interacting with devicemapper via
|
||||
// the dmsetup utility.
|
||||
type DmsetupClient interface {
|
||||
Table(deviceName string) ([]byte, error)
|
||||
Message(deviceName string, sector int, message string) ([]byte, error)
|
||||
Status(deviceName string) ([]byte, error)
|
||||
}
|
||||
|
||||
func NewDmsetupClient() DmsetupClient {
|
||||
return &defaultDmsetupClient{}
|
||||
}
|
||||
|
||||
// defaultDmsetupClient implements the standard behavior for interacting with dmsetup.
|
||||
type defaultDmsetupClient struct{}
|
||||
|
||||
var _ DmsetupClient = &defaultDmsetupClient{}
|
||||
|
||||
func (c *defaultDmsetupClient) Table(deviceName string) ([]byte, error) {
|
||||
return c.dmsetup("table", deviceName)
|
||||
}
|
||||
|
||||
func (c *defaultDmsetupClient) Message(deviceName string, sector int, message string) ([]byte, error) {
|
||||
return c.dmsetup("message", deviceName, strconv.Itoa(sector), message)
|
||||
}
|
||||
|
||||
func (c *defaultDmsetupClient) Status(deviceName string) ([]byte, error) {
|
||||
return c.dmsetup("status", deviceName)
|
||||
}
|
||||
|
||||
func (*defaultDmsetupClient) dmsetup(args ...string) ([]byte, error) {
|
||||
glog.V(5).Infof("running dmsetup %v", strings.Join(args, " "))
|
||||
return exec.Command("dmsetup", args...).Output()
|
||||
}
|
16
vendor/github.com/google/cadvisor/devicemapper/doc.go
generated
vendored
Normal file
16
vendor/github.com/google/cadvisor/devicemapper/doc.go
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
// 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 devicemapper contains code for working with devicemapper
|
||||
package devicemapper
|
77
vendor/github.com/google/cadvisor/devicemapper/thin_ls_client.go
generated
vendored
Normal file
77
vendor/github.com/google/cadvisor/devicemapper/thin_ls_client.go
generated
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
// 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 devicemapper
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
// thinLsClient knows how to run a thin_ls very specific to CoW usage for containers.
|
||||
type thinLsClient interface {
|
||||
ThinLs(deviceName string) (map[string]uint64, error)
|
||||
}
|
||||
|
||||
func newThinLsClient() thinLsClient {
|
||||
return &defaultThinLsClient{}
|
||||
}
|
||||
|
||||
type defaultThinLsClient struct{}
|
||||
|
||||
var _ thinLsClient = &defaultThinLsClient{}
|
||||
|
||||
func (*defaultThinLsClient) ThinLs(deviceName string) (map[string]uint64, error) {
|
||||
args := []string{"--no-headers", "-m", "-o", "DEV,EXCLUSIVE_BYTES", deviceName}
|
||||
glog.V(4).Infof("running command: thin_ls %v", strings.Join(args, " "))
|
||||
|
||||
output, err := exec.Command("thin_ls", args...).Output()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error running command `thin_ls %v`: %v\noutput:\n\n%v", strings.Join(args, " "), err, string(output))
|
||||
}
|
||||
|
||||
return parseThinLsOutput(output), nil
|
||||
}
|
||||
|
||||
// parseThinLsOutput parses the output returned by thin_ls to build a map of device id -> usage.
|
||||
func parseThinLsOutput(output []byte) map[string]uint64 {
|
||||
cache := map[string]uint64{}
|
||||
|
||||
// parse output
|
||||
scanner := bufio.NewScanner(bytes.NewReader(output))
|
||||
for scanner.Scan() {
|
||||
output := scanner.Text()
|
||||
fields := strings.Fields(output)
|
||||
if len(fields) != 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
deviceID := fields[0]
|
||||
usage, err := strconv.ParseUint(fields[1], 10, 64)
|
||||
if err != nil {
|
||||
glog.Warning("unexpected error parsing thin_ls output: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
cache[deviceID] = usage
|
||||
}
|
||||
|
||||
return cache
|
||||
|
||||
}
|
164
vendor/github.com/google/cadvisor/devicemapper/thin_pool_watcher.go
generated
vendored
Normal file
164
vendor/github.com/google/cadvisor/devicemapper/thin_pool_watcher.go
generated
vendored
Normal file
@@ -0,0 +1,164 @@
|
||||
// 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 devicemapper
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
// ThinPoolWatcher maintains a cache of device name -> usage stats for a devicemapper thin-pool using thin_ls.
|
||||
type ThinPoolWatcher struct {
|
||||
poolName string
|
||||
metadataDevice string
|
||||
lock *sync.RWMutex
|
||||
cache map[string]uint64
|
||||
period time.Duration
|
||||
stopChan chan struct{}
|
||||
dmsetup DmsetupClient
|
||||
thinLsClient thinLsClient
|
||||
}
|
||||
|
||||
// NewThinPoolWatcher returns a new ThinPoolWatcher for the given devicemapper thin pool name and metadata device.
|
||||
func NewThinPoolWatcher(poolName, metadataDevice string) *ThinPoolWatcher {
|
||||
return &ThinPoolWatcher{poolName: poolName,
|
||||
metadataDevice: metadataDevice,
|
||||
lock: &sync.RWMutex{},
|
||||
cache: make(map[string]uint64),
|
||||
period: 15 * time.Second,
|
||||
stopChan: make(chan struct{}),
|
||||
dmsetup: NewDmsetupClient(),
|
||||
thinLsClient: newThinLsClient(),
|
||||
}
|
||||
}
|
||||
|
||||
// Start starts the thin pool watcher.
|
||||
func (w *ThinPoolWatcher) Start() {
|
||||
err := w.Refresh()
|
||||
if err != nil {
|
||||
glog.Errorf("encountered error refreshing thin pool watcher: %v", err)
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-w.stopChan:
|
||||
return
|
||||
case <-time.After(w.period):
|
||||
start := time.Now()
|
||||
err = w.Refresh()
|
||||
if err != nil {
|
||||
glog.Errorf("encountered error refreshing thin pool watcher: %v", err)
|
||||
}
|
||||
|
||||
// print latency for refresh
|
||||
duration := time.Since(start)
|
||||
glog.V(3).Infof("thin_ls(%d) took %s", start.Unix(), duration)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (w *ThinPoolWatcher) Stop() {
|
||||
close(w.stopChan)
|
||||
}
|
||||
|
||||
// GetUsage gets the cached usage value of the given device.
|
||||
func (w *ThinPoolWatcher) GetUsage(deviceId string) (uint64, error) {
|
||||
w.lock.RLock()
|
||||
defer w.lock.RUnlock()
|
||||
v, ok := w.cache[deviceId]
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("no cached value for usage of device %v", deviceId)
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
const (
|
||||
reserveMetadataMessage = "reserve_metadata_snap"
|
||||
releaseMetadataMessage = "release_metadata_snap"
|
||||
)
|
||||
|
||||
// Refresh performs a `thin_ls` of the pool being watched and refreshes the
|
||||
// cached data with the result.
|
||||
func (w *ThinPoolWatcher) Refresh() error {
|
||||
w.lock.Lock()
|
||||
defer w.lock.Unlock()
|
||||
|
||||
currentlyReserved, err := w.checkReservation(w.poolName)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error determining whether snapshot is reserved: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if currentlyReserved {
|
||||
glog.V(4).Infof("metadata for %v is currently reserved; releasing", w.poolName)
|
||||
_, err = w.dmsetup.Message(w.poolName, 0, releaseMetadataMessage)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error releasing metadata snapshot for %v: %v", w.poolName, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
glog.Infof("reserving metadata snapshot for thin-pool %v", w.poolName)
|
||||
// NOTE: "0" in the call below is for the 'sector' argument to 'dmsetup message'. It's not needed for thin pools.
|
||||
if output, err := w.dmsetup.Message(w.poolName, 0, reserveMetadataMessage); err != nil {
|
||||
err = fmt.Errorf("error reserving metadata for thin-pool %v: %v output: %v", w.poolName, err, string(output))
|
||||
return err
|
||||
} else {
|
||||
glog.V(5).Infof("reserved metadata snapshot for thin-pool %v", w.poolName)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
glog.V(5).Infof("releasing metadata snapshot for thin-pool %v", w.poolName)
|
||||
w.dmsetup.Message(w.poolName, 0, releaseMetadataMessage)
|
||||
}()
|
||||
|
||||
glog.V(5).Infof("running thin_ls on metadata device %v", w.metadataDevice)
|
||||
newCache, err := w.thinLsClient.ThinLs(w.metadataDevice)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error performing thin_ls on metadata device %v: %v", w.metadataDevice, err)
|
||||
return err
|
||||
}
|
||||
|
||||
w.cache = newCache
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
thinPoolDmsetupStatusTokens = 11
|
||||
thinPoolDmsetupStatusHeldMetadataRoot = 6
|
||||
)
|
||||
|
||||
// checkReservation checks to see whether the thin device is currently holding userspace metadata.
|
||||
func (w *ThinPoolWatcher) checkReservation(poolName string) (bool, error) {
|
||||
glog.V(5).Infof("checking whether the thin-pool is holding a metadata snapshot")
|
||||
output, err := w.dmsetup.Status(poolName)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
tokens := strings.Split(string(output), " ")
|
||||
// Split returns the input as the last item in the result, adjust the number of tokens by one
|
||||
if len(tokens) != thinPoolDmsetupStatusTokens+1 {
|
||||
return false, fmt.Errorf("unexpected output of dmsetup status command; expected 11 fields, got %v; output: %v", len(tokens), string(output))
|
||||
}
|
||||
|
||||
heldMetadataRoot := tokens[thinPoolDmsetupStatusHeldMetadataRoot]
|
||||
currentlyReserved := heldMetadataRoot != "-"
|
||||
return currentlyReserved, nil
|
||||
}
|
13
vendor/github.com/google/cadvisor/events/handler.go
generated
vendored
13
vendor/github.com/google/cadvisor/events/handler.go
generated
vendored
@@ -281,14 +281,19 @@ func (self *events) updateEventStore(e *info.Event) {
|
||||
self.eventsLock.Lock()
|
||||
defer self.eventsLock.Unlock()
|
||||
if _, ok := self.eventStore[e.EventType]; !ok {
|
||||
maxAge := self.storagePolicy.DefaultMaxAge
|
||||
maxNumEvents := self.storagePolicy.DefaultMaxNumEvents
|
||||
if age, ok := self.storagePolicy.PerTypeMaxAge[e.EventType]; ok {
|
||||
maxAge = age
|
||||
}
|
||||
if numEvents, ok := self.storagePolicy.PerTypeMaxNumEvents[e.EventType]; ok {
|
||||
maxNumEvents = numEvents
|
||||
}
|
||||
if maxNumEvents == 0 {
|
||||
// Event storage is disabled for e.EventType
|
||||
return
|
||||
}
|
||||
|
||||
maxAge := self.storagePolicy.DefaultMaxAge
|
||||
if age, ok := self.storagePolicy.PerTypeMaxAge[e.EventType]; ok {
|
||||
maxAge = age
|
||||
}
|
||||
|
||||
self.eventStore[e.EventType] = utils.NewTimedStore(maxAge, maxNumEvents)
|
||||
}
|
||||
|
44
vendor/github.com/google/cadvisor/fs/fs.go
generated
vendored
44
vendor/github.com/google/cadvisor/fs/fs.go
generated
vendored
@@ -33,6 +33,8 @@ import (
|
||||
|
||||
"github.com/docker/docker/pkg/mount"
|
||||
"github.com/golang/glog"
|
||||
"github.com/google/cadvisor/devicemapper"
|
||||
dockerutil "github.com/google/cadvisor/utils/docker"
|
||||
zfs "github.com/mistifyio/go-zfs"
|
||||
)
|
||||
|
||||
@@ -56,8 +58,8 @@ type RealFsInfo struct {
|
||||
// Map from label to block device path.
|
||||
// Labels are intent-specific tags that are auto-detected.
|
||||
labels map[string]string
|
||||
|
||||
dmsetup dmsetupClient
|
||||
// devicemapper client
|
||||
dmsetup devicemapper.DmsetupClient
|
||||
}
|
||||
|
||||
type Context struct {
|
||||
@@ -80,13 +82,9 @@ func NewFsInfo(context Context) (FsInfo, error) {
|
||||
fsInfo := &RealFsInfo{
|
||||
partitions: make(map[string]partition, 0),
|
||||
labels: make(map[string]string, 0),
|
||||
dmsetup: &defaultDmsetupClient{},
|
||||
dmsetup: devicemapper.NewDmsetupClient(),
|
||||
}
|
||||
|
||||
fsInfo.addSystemRootLabel(mounts)
|
||||
fsInfo.addDockerImagesLabel(context, mounts)
|
||||
fsInfo.addRktImagesLabel(context, mounts)
|
||||
|
||||
supportedFsType := map[string]bool{
|
||||
// all ext systems are checked through prefix.
|
||||
"btrfs": true,
|
||||
@@ -113,7 +111,13 @@ func NewFsInfo(context Context) (FsInfo, error) {
|
||||
}
|
||||
}
|
||||
|
||||
fsInfo.addRktImagesLabel(context, mounts)
|
||||
// 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, mounts)
|
||||
|
||||
glog.Infof("Filesystem partitions: %+v", fsInfo.partitions)
|
||||
fsInfo.addSystemRootLabel(mounts)
|
||||
return fsInfo, nil
|
||||
}
|
||||
|
||||
@@ -126,7 +130,7 @@ func (self *RealFsInfo) getDockerDeviceMapperInfo(context DockerContext) (string
|
||||
return "", nil, nil
|
||||
}
|
||||
|
||||
dataLoopFile := context.DriverStatus["Data loop file"]
|
||||
dataLoopFile := context.DriverStatus[dockerutil.DriverStatusDataLoopFile]
|
||||
if len(dataLoopFile) > 0 {
|
||||
return "", nil, nil
|
||||
}
|
||||
@@ -274,6 +278,7 @@ func (self *RealFsInfo) GetFsInfoForPath(mountSet map[string]struct{}) ([]Fs, er
|
||||
switch partition.fsType {
|
||||
case DeviceMapper.String():
|
||||
fs.Capacity, fs.Free, fs.Available, err = getDMStats(device, partition.blockSize)
|
||||
glog.V(5).Infof("got devicemapper fs capacity stats: capacity: %v free: %v available: %v:", fs.Capacity, fs.Free, fs.Available)
|
||||
fs.Type = DeviceMapper
|
||||
case ZFS.String():
|
||||
fs.Capacity, fs.Free, fs.Available, err = getZfstats(device)
|
||||
@@ -434,30 +439,15 @@ func getVfsStats(path string) (total uint64, free uint64, avail uint64, inodes u
|
||||
return total, free, avail, inodes, inodesFree, nil
|
||||
}
|
||||
|
||||
// dmsetupClient knows to to interact with dmsetup to retrieve information about devicemapper.
|
||||
type dmsetupClient interface {
|
||||
table(poolName string) ([]byte, error)
|
||||
//TODO add status(poolName string) ([]byte, error) and use it in getDMStats so we can unit test
|
||||
}
|
||||
|
||||
// defaultDmsetupClient implements the standard behavior for interacting with dmsetup.
|
||||
type defaultDmsetupClient struct{}
|
||||
|
||||
var _ dmsetupClient = &defaultDmsetupClient{}
|
||||
|
||||
func (*defaultDmsetupClient) table(poolName string) ([]byte, error) {
|
||||
return exec.Command("dmsetup", "table", poolName).Output()
|
||||
}
|
||||
|
||||
// Devicemapper thin provisioning is detailed at
|
||||
// https://www.kernel.org/doc/Documentation/device-mapper/thin-provisioning.txt
|
||||
func dockerDMDevice(driverStatus map[string]string, dmsetup dmsetupClient) (string, uint, uint, uint, error) {
|
||||
poolName, ok := driverStatus["Pool Name"]
|
||||
func dockerDMDevice(driverStatus map[string]string, dmsetup devicemapper.DmsetupClient) (string, uint, uint, uint, error) {
|
||||
poolName, ok := driverStatus[dockerutil.DriverStatusPoolName]
|
||||
if !ok || len(poolName) == 0 {
|
||||
return "", 0, 0, 0, fmt.Errorf("Could not get dm pool name")
|
||||
}
|
||||
|
||||
out, err := dmsetup.table(poolName)
|
||||
out, err := dmsetup.Table(poolName)
|
||||
if err != nil {
|
||||
return "", 0, 0, 0, err
|
||||
}
|
||||
@@ -470,6 +460,8 @@ func dockerDMDevice(driverStatus map[string]string, dmsetup dmsetupClient) (stri
|
||||
return poolName, major, minor, dataBlkSize, nil
|
||||
}
|
||||
|
||||
// parseDMTable parses a single line of `dmsetup table` output and returns the
|
||||
// major device, minor device, block size, and an error.
|
||||
func parseDMTable(dmTable string) (uint, uint, uint, error) {
|
||||
dmTable = strings.Replace(dmTable, ":", " ", -1)
|
||||
dmFields := strings.Fields(dmTable)
|
||||
|
37
vendor/github.com/google/cadvisor/info/v1/docker.go
generated
vendored
Normal file
37
vendor/github.com/google/cadvisor/info/v1/docker.go
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Types used for docker containers.
|
||||
package v1
|
||||
|
||||
type DockerStatus struct {
|
||||
Version string `json:"version"`
|
||||
KernelVersion string `json:"kernel_version"`
|
||||
OS string `json:"os"`
|
||||
Hostname string `json:"hostname"`
|
||||
RootDir string `json:"root_dir"`
|
||||
Driver string `json:"driver"`
|
||||
DriverStatus map[string]string `json:"driver_status"`
|
||||
ExecDriver string `json:"exec_driver"`
|
||||
NumImages int `json:"num_images"`
|
||||
NumContainers int `json:"num_containers"`
|
||||
}
|
||||
|
||||
type DockerImage struct {
|
||||
ID string `json:"id"`
|
||||
RepoTags []string `json:"repo_tags"` // repository name and tags.
|
||||
Created int64 `json:"created"` // unix time since creation.
|
||||
VirtualSize int64 `json:"virtual_size"`
|
||||
Size int64 `json:"size"`
|
||||
}
|
@@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package manager
|
||||
package machine
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -22,14 +22,11 @@ import (
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/google/cadvisor/container/docker"
|
||||
"github.com/google/cadvisor/fs"
|
||||
info "github.com/google/cadvisor/info/v1"
|
||||
"github.com/google/cadvisor/utils/cloudinfo"
|
||||
"github.com/google/cadvisor/utils/machine"
|
||||
"github.com/google/cadvisor/utils/sysfs"
|
||||
"github.com/google/cadvisor/utils/sysinfo"
|
||||
version "github.com/google/cadvisor/version"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
@@ -51,19 +48,19 @@ func getInfoFromFiles(filePaths string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func getMachineInfo(sysFs sysfs.SysFs, fsInfo fs.FsInfo, inHostNamespace bool) (*info.MachineInfo, error) {
|
||||
func Info(sysFs sysfs.SysFs, fsInfo fs.FsInfo, inHostNamespace bool) (*info.MachineInfo, error) {
|
||||
rootFs := "/"
|
||||
if !inHostNamespace {
|
||||
rootFs = "/rootfs"
|
||||
}
|
||||
|
||||
cpuinfo, err := ioutil.ReadFile(filepath.Join(rootFs, "/proc/cpuinfo"))
|
||||
clockSpeed, err := machine.GetClockSpeed(cpuinfo)
|
||||
clockSpeed, err := GetClockSpeed(cpuinfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
memoryCapacity, err := machine.GetMachineMemoryCapacity()
|
||||
memoryCapacity, err := GetMachineMemoryCapacity()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -83,7 +80,7 @@ func getMachineInfo(sysFs sysfs.SysFs, fsInfo fs.FsInfo, inHostNamespace bool) (
|
||||
glog.Errorf("Failed to get network devices: %v", err)
|
||||
}
|
||||
|
||||
topology, numCores, err := machine.GetTopology(sysFs, string(cpuinfo))
|
||||
topology, numCores, err := GetTopology(sysFs, string(cpuinfo))
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to get topology information: %v", err)
|
||||
}
|
||||
@@ -120,22 +117,7 @@ func getMachineInfo(sysFs sysfs.SysFs, fsInfo fs.FsInfo, inHostNamespace bool) (
|
||||
return machineInfo, nil
|
||||
}
|
||||
|
||||
func getVersionInfo() (*info.VersionInfo, error) {
|
||||
|
||||
kernel_version := getKernelVersion()
|
||||
container_os := getContainerOsVersion()
|
||||
docker_version := getDockerVersion()
|
||||
|
||||
return &info.VersionInfo{
|
||||
KernelVersion: kernel_version,
|
||||
ContainerOsVersion: container_os,
|
||||
DockerVersion: docker_version,
|
||||
CadvisorVersion: version.Info["version"],
|
||||
CadvisorRevision: version.Info["revision"],
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getContainerOsVersion() string {
|
||||
func ContainerOsVersion() string {
|
||||
container_os := "Unknown"
|
||||
os_release, err := ioutil.ReadFile("/etc/os-release")
|
||||
if err == nil {
|
||||
@@ -152,19 +134,7 @@ func getContainerOsVersion() string {
|
||||
return container_os
|
||||
}
|
||||
|
||||
func getDockerVersion() string {
|
||||
docker_version := "Unknown"
|
||||
client, err := docker.Client()
|
||||
if err == nil {
|
||||
version, err := client.Version()
|
||||
if err == nil {
|
||||
docker_version = version.Get("Version")
|
||||
}
|
||||
}
|
||||
return docker_version
|
||||
}
|
||||
|
||||
func getKernelVersion() string {
|
||||
func KernelVersion() string {
|
||||
uname := &syscall.Utsname{}
|
||||
|
||||
if err := syscall.Uname(uname); err != nil {
|
@@ -12,6 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// The machine package contains functions that extract machine-level specs.
|
||||
package machine
|
||||
|
||||
import (
|
||||
@@ -33,8 +34,6 @@ import (
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
// The utils/machine package contains functions that extract machine-level specs.
|
||||
|
||||
var (
|
||||
cpuRegExp = regexp.MustCompile(`^processor\s*:\s*([0-9]+)$`)
|
||||
coreRegExp = regexp.MustCompile(`^core id\s*:\s*([0-9]+)$`)
|
||||
@@ -49,8 +48,8 @@ const maxFreqFile = "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq"
|
||||
|
||||
// GetClockSpeed returns the CPU clock speed, given a []byte formatted as the /proc/cpuinfo file.
|
||||
func GetClockSpeed(procInfo []byte) (uint64, error) {
|
||||
// s390/s390x and aarch64 changes
|
||||
if true == isSystemZ() || true == isAArch64() {
|
||||
// s390/s390x, aarch64 and arm32 changes
|
||||
if isSystemZ() || isAArch64() || isArm32() {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
@@ -280,13 +279,20 @@ func getMachineArch() (string, error) {
|
||||
return arch, nil
|
||||
}
|
||||
|
||||
// arm32 chanes
|
||||
func isArm32() bool {
|
||||
arch, err := getMachineArch()
|
||||
if err == nil {
|
||||
return strings.Contains(arch, "arm")
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// aarch64 changes
|
||||
func isAArch64() bool {
|
||||
arch, err := getMachineArch()
|
||||
if err == nil {
|
||||
if true == strings.Contains(arch, "aarch64") {
|
||||
return true
|
||||
}
|
||||
return strings.Contains(arch, "aarch64")
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -295,9 +301,7 @@ func isAArch64() bool {
|
||||
func isSystemZ() bool {
|
||||
arch, err := getMachineArch()
|
||||
if err == nil {
|
||||
if true == strings.Contains(arch, "390") {
|
||||
return true
|
||||
}
|
||||
return strings.Contains(arch, "390")
|
||||
}
|
||||
return false
|
||||
}
|
27
vendor/github.com/google/cadvisor/manager/container.go
generated
vendored
27
vendor/github.com/google/cadvisor/manager/container.go
generated
vendored
@@ -42,6 +42,7 @@ import (
|
||||
)
|
||||
|
||||
// Housekeeping interval.
|
||||
var enableLoadReader = flag.Bool("enable_load_reader", false, "Whether to enable cpu load reader")
|
||||
var HousekeepingInterval = flag.Duration("housekeeping_interval", 1*time.Second, "Interval between container housekeepings")
|
||||
|
||||
var cgroupPathRegExp = regexp.MustCompile(`devices[^:]*:(.*?)[,;$]`)
|
||||
@@ -303,7 +304,7 @@ func (c *containerData) GetProcessList(cadvisorContainer string, inHostNamespace
|
||||
return processes, nil
|
||||
}
|
||||
|
||||
func newContainerData(containerName string, memoryCache *memory.InMemoryCache, handler container.ContainerHandler, loadReader cpuload.CpuLoadReader, logUsage bool, collectorManager collector.CollectorManager, maxHousekeepingInterval time.Duration, allowDynamicHousekeeping bool) (*containerData, error) {
|
||||
func newContainerData(containerName string, memoryCache *memory.InMemoryCache, handler container.ContainerHandler, logUsage bool, collectorManager collector.CollectorManager, maxHousekeepingInterval time.Duration, allowDynamicHousekeeping bool) (*containerData, error) {
|
||||
if memoryCache == nil {
|
||||
return nil, fmt.Errorf("nil memory storage")
|
||||
}
|
||||
@@ -321,7 +322,6 @@ func newContainerData(containerName string, memoryCache *memory.InMemoryCache, h
|
||||
housekeepingInterval: *HousekeepingInterval,
|
||||
maxHousekeepingInterval: maxHousekeepingInterval,
|
||||
allowDynamicHousekeeping: allowDynamicHousekeeping,
|
||||
loadReader: loadReader,
|
||||
logUsage: logUsage,
|
||||
loadAvg: -1.0, // negative value indicates uninitialized.
|
||||
stop: make(chan bool, 1),
|
||||
@@ -331,6 +331,17 @@ func newContainerData(containerName string, memoryCache *memory.InMemoryCache, h
|
||||
|
||||
cont.loadDecay = math.Exp(float64(-cont.housekeepingInterval.Seconds() / 10))
|
||||
|
||||
if *enableLoadReader {
|
||||
// Create cpu load reader.
|
||||
loadReader, err := cpuload.New()
|
||||
if err != nil {
|
||||
// TODO(rjnagal): Promote to warning once we support cpu load inside namespaces.
|
||||
glog.Infof("Could not initialize cpu load reader for %q: %s", ref.Name, err)
|
||||
} else {
|
||||
cont.loadReader = loadReader
|
||||
}
|
||||
}
|
||||
|
||||
err = cont.updateSpec()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -375,6 +386,16 @@ func (self *containerData) nextHousekeeping(lastHousekeeping time.Time) time.Tim
|
||||
func (c *containerData) housekeeping() {
|
||||
// Start any background goroutines - must be cleaned up in c.handler.Cleanup().
|
||||
c.handler.Start()
|
||||
defer c.handler.Cleanup()
|
||||
|
||||
// Initialize cpuload reader - must be cleaned up in c.loadReader.Stop()
|
||||
if c.loadReader != nil {
|
||||
err := c.loadReader.Start()
|
||||
if err != nil {
|
||||
glog.Warningf("Could not start cpu load stat collector for %q: %s", c.info.Name, err)
|
||||
}
|
||||
defer c.loadReader.Stop()
|
||||
}
|
||||
|
||||
// Long housekeeping is either 100ms or half of the housekeeping interval.
|
||||
longHousekeeping := 100 * time.Millisecond
|
||||
@@ -388,8 +409,6 @@ func (c *containerData) housekeeping() {
|
||||
for {
|
||||
select {
|
||||
case <-c.stop:
|
||||
// Cleanup container resources before stopping housekeeping.
|
||||
c.handler.Cleanup()
|
||||
// Stop housekeeping when signaled.
|
||||
return
|
||||
default:
|
||||
|
312
vendor/github.com/google/cadvisor/manager/manager.go
generated
vendored
312
vendor/github.com/google/cadvisor/manager/manager.go
generated
vendored
@@ -36,9 +36,13 @@ import (
|
||||
"github.com/google/cadvisor/fs"
|
||||
info "github.com/google/cadvisor/info/v1"
|
||||
"github.com/google/cadvisor/info/v2"
|
||||
"github.com/google/cadvisor/utils/cpuload"
|
||||
"github.com/google/cadvisor/machine"
|
||||
"github.com/google/cadvisor/manager/watcher"
|
||||
rawwatcher "github.com/google/cadvisor/manager/watcher/raw"
|
||||
rktwatcher "github.com/google/cadvisor/manager/watcher/rkt"
|
||||
"github.com/google/cadvisor/utils/oomparser"
|
||||
"github.com/google/cadvisor/utils/sysfs"
|
||||
"github.com/google/cadvisor/version"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/opencontainers/runc/libcontainer/cgroups"
|
||||
@@ -46,7 +50,6 @@ import (
|
||||
|
||||
var globalHousekeepingInterval = flag.Duration("global_housekeeping_interval", 1*time.Minute, "Interval between global housekeepings")
|
||||
var logCadvisorUsage = flag.Bool("log_cadvisor_usage", false, "Whether to log the usage of the cAdvisor container")
|
||||
var enableLoadReader = flag.Bool("enable_load_reader", false, "Whether to enable cpu load reader")
|
||||
var eventStorageAgeLimit = flag.String("event_storage_age_limit", "default=24h", "Max length of time for which to store events (per type). Value is a comma separated list of key values, where the keys are event types (e.g.: creation, oom) or \"default\" and the value is a duration. Default is applied to all non-specified event types")
|
||||
var eventStorageEventLimit = flag.String("event_storage_event_limit", "default=100000", "Max number of events to store (per type). Value is a comma separated list of key values, where the keys are event types (e.g.: creation, oom) or \"default\" and the value is an integer. Default is applied to all non-specified event types")
|
||||
var applicationMetricsCountLimit = flag.Int("application_metrics_count_limit", 100, "Max number of application metrics to store (per container)")
|
||||
@@ -65,6 +68,8 @@ type Manager interface {
|
||||
GetContainerInfo(containerName string, query *info.ContainerInfoRequest) (*info.ContainerInfo, error)
|
||||
|
||||
// Get V2 information about a container.
|
||||
// Recursive (subcontainer) requests are best-effort, and may return a partial result alongside an
|
||||
// error in the partial failure case.
|
||||
GetContainerInfoV2(containerName string, options v2.RequestOptions) (map[string]v2.ContainerInfo, error)
|
||||
|
||||
// Get information about all subcontainers of the specified container (includes self).
|
||||
@@ -110,10 +115,10 @@ type Manager interface {
|
||||
CloseEventChannel(watch_id int)
|
||||
|
||||
// Get status information about docker.
|
||||
DockerInfo() (DockerStatus, error)
|
||||
DockerInfo() (info.DockerStatus, error)
|
||||
|
||||
// Get details about interesting docker images.
|
||||
DockerImages() ([]DockerImage, error)
|
||||
DockerImages() ([]info.DockerImage, error)
|
||||
|
||||
// Returns debugging information. Map of lines per category.
|
||||
DebugInfo() map[string][]string
|
||||
@@ -132,7 +137,7 @@ func New(memoryCache *memory.InMemoryCache, sysfs sysfs.SysFs, maxHousekeepingIn
|
||||
}
|
||||
glog.Infof("cAdvisor running in container: %q", selfContainer)
|
||||
|
||||
dockerInfo, err := dockerInfo()
|
||||
dockerStatus, err := docker.Status()
|
||||
if err != nil {
|
||||
glog.Warningf("Unable to connect to Docker: %v", err)
|
||||
}
|
||||
@@ -144,8 +149,8 @@ func New(memoryCache *memory.InMemoryCache, sysfs sysfs.SysFs, maxHousekeepingIn
|
||||
context := fs.Context{
|
||||
Docker: fs.DockerContext{
|
||||
Root: docker.RootDir(),
|
||||
Driver: dockerInfo.Driver,
|
||||
DriverStatus: dockerInfo.DriverStatus,
|
||||
Driver: dockerStatus.Driver,
|
||||
DriverStatus: dockerStatus.DriverStatus,
|
||||
},
|
||||
RktPath: rktPath,
|
||||
}
|
||||
@@ -161,6 +166,9 @@ func New(memoryCache *memory.InMemoryCache, sysfs sysfs.SysFs, maxHousekeepingIn
|
||||
inHostNamespace = true
|
||||
}
|
||||
|
||||
// Register for new subcontainers.
|
||||
eventsChannel := make(chan watcher.ContainerEvent, 16)
|
||||
|
||||
newManager := &manager{
|
||||
containers: make(map[namespacedContainerName]*containerData),
|
||||
quitChannels: make([]chan error, 0, 2),
|
||||
@@ -172,9 +180,11 @@ func New(memoryCache *memory.InMemoryCache, sysfs sysfs.SysFs, maxHousekeepingIn
|
||||
maxHousekeepingInterval: maxHousekeepingInterval,
|
||||
allowDynamicHousekeeping: allowDynamicHousekeeping,
|
||||
ignoreMetrics: ignoreMetricsSet,
|
||||
containerWatchers: []watcher.ContainerWatcher{},
|
||||
eventsChannel: eventsChannel,
|
||||
}
|
||||
|
||||
machineInfo, err := getMachineInfo(sysfs, fsInfo, inHostNamespace)
|
||||
machineInfo, err := machine.Info(sysfs, fsInfo, inHostNamespace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -209,12 +219,13 @@ type manager struct {
|
||||
quitChannels []chan error
|
||||
cadvisorContainer string
|
||||
inHostNamespace bool
|
||||
loadReader cpuload.CpuLoadReader
|
||||
eventHandler events.EventManager
|
||||
startupTime time.Time
|
||||
maxHousekeepingInterval time.Duration
|
||||
allowDynamicHousekeeping bool
|
||||
ignoreMetrics container.MetricSet
|
||||
containerWatchers []watcher.ContainerWatcher
|
||||
eventsChannel chan watcher.ContainerEvent
|
||||
}
|
||||
|
||||
// Start the container manager.
|
||||
@@ -227,6 +238,12 @@ func (self *manager) Start() error {
|
||||
err = rkt.Register(self, self.fsInfo, self.ignoreMetrics)
|
||||
if err != nil {
|
||||
glog.Errorf("Registration of the rkt container factory failed: %v", err)
|
||||
} else {
|
||||
watcher, err := rktwatcher.NewRktContainerWatcher()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
self.containerWatchers = append(self.containerWatchers, watcher)
|
||||
}
|
||||
|
||||
err = systemd.Register(self, self.fsInfo, self.ignoreMetrics)
|
||||
@@ -239,24 +256,11 @@ func (self *manager) Start() error {
|
||||
glog.Errorf("Registration of the raw container factory failed: %v", err)
|
||||
}
|
||||
|
||||
self.DockerInfo()
|
||||
self.DockerImages()
|
||||
|
||||
if *enableLoadReader {
|
||||
// Create cpu load reader.
|
||||
cpuLoadReader, err := cpuload.New()
|
||||
if err != nil {
|
||||
// TODO(rjnagal): Promote to warning once we support cpu load inside namespaces.
|
||||
glog.Infof("Could not initialize cpu load reader: %s", err)
|
||||
} else {
|
||||
err = cpuLoadReader.Start()
|
||||
if err != nil {
|
||||
glog.Warningf("Could not start cpu load stat collector: %s", err)
|
||||
} else {
|
||||
self.loadReader = cpuLoadReader
|
||||
}
|
||||
}
|
||||
rawWatcher, err := rawwatcher.NewRawContainerWatcher()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
self.containerWatchers = append(self.containerWatchers, rawWatcher)
|
||||
|
||||
// Watch for OOMs.
|
||||
err = self.watchForNewOoms()
|
||||
@@ -270,7 +274,7 @@ func (self *manager) Start() error {
|
||||
}
|
||||
|
||||
// Create root and then recover all containers.
|
||||
err = self.createContainer("/")
|
||||
err = self.createContainer("/", watcher.Raw)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -310,10 +314,6 @@ func (self *manager) Stop() error {
|
||||
}
|
||||
}
|
||||
self.quitChannels = make([]chan error, 0, 2)
|
||||
if self.loadReader != nil {
|
||||
self.loadReader.Stop()
|
||||
self.loadReader = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -373,15 +373,16 @@ func (self *manager) GetDerivedStats(containerName string, options v2.RequestOpt
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var errs partialFailure
|
||||
stats := make(map[string]v2.DerivedStats)
|
||||
for name, cont := range conts {
|
||||
d, err := cont.DerivedStats()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
errs.append(name, "DerivedStats", err)
|
||||
}
|
||||
stats[name] = d
|
||||
}
|
||||
return stats, nil
|
||||
return stats, errs.OrNil()
|
||||
}
|
||||
|
||||
func (self *manager) GetContainerSpec(containerName string, options v2.RequestOptions) (map[string]v2.ContainerSpec, error) {
|
||||
@@ -389,16 +390,17 @@ func (self *manager) GetContainerSpec(containerName string, options v2.RequestOp
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var errs partialFailure
|
||||
specs := make(map[string]v2.ContainerSpec)
|
||||
for name, cont := range conts {
|
||||
cinfo, err := cont.GetInfo()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
errs.append(name, "GetInfo", err)
|
||||
}
|
||||
spec := self.getV2Spec(cinfo)
|
||||
specs[name] = spec
|
||||
}
|
||||
return specs, nil
|
||||
return specs, errs.OrNil()
|
||||
}
|
||||
|
||||
// Get V2 container spec from v1 container info.
|
||||
@@ -434,26 +436,32 @@ func (self *manager) GetContainerInfoV2(containerName string, options v2.Request
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var errs partialFailure
|
||||
var nilTime time.Time // Ignored.
|
||||
|
||||
infos := make(map[string]v2.ContainerInfo, len(containers))
|
||||
for name, container := range containers {
|
||||
result := v2.ContainerInfo{}
|
||||
cinfo, err := container.GetInfo()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
errs.append(name, "GetInfo", err)
|
||||
infos[name] = result
|
||||
continue
|
||||
}
|
||||
result.Spec = self.getV2Spec(cinfo)
|
||||
|
||||
var nilTime time.Time // Ignored.
|
||||
stats, err := self.memoryCache.RecentStats(name, nilTime, nilTime, options.Count)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
errs.append(name, "RecentStats", err)
|
||||
infos[name] = result
|
||||
continue
|
||||
}
|
||||
|
||||
infos[name] = v2.ContainerInfo{
|
||||
Spec: self.getV2Spec(cinfo),
|
||||
Stats: v2.ContainerStatsFromV1(&cinfo.Spec, stats),
|
||||
}
|
||||
result.Stats = v2.ContainerStatsFromV1(&cinfo.Spec, stats)
|
||||
infos[name] = result
|
||||
}
|
||||
|
||||
return infos, nil
|
||||
return infos, errs.OrNil()
|
||||
}
|
||||
|
||||
func (self *manager) containerDataToContainerInfo(cont *containerData, query *info.ContainerInfoRequest) (*info.ContainerInfo, error) {
|
||||
@@ -594,6 +602,7 @@ func (self *manager) GetRequestedContainersInfo(containerName string, options v2
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var errs partialFailure
|
||||
containersMap := make(map[string]*info.ContainerInfo)
|
||||
query := info.ContainerInfoRequest{
|
||||
NumStats: options.Count,
|
||||
@@ -601,12 +610,11 @@ func (self *manager) GetRequestedContainersInfo(containerName string, options v2
|
||||
for name, data := range containers {
|
||||
info, err := self.containerDataToContainerInfo(data, &query)
|
||||
if err != nil {
|
||||
// Skip containers with errors, we try to degrade gracefully.
|
||||
continue
|
||||
errs.append(name, "containerDataToContainerInfo", err)
|
||||
}
|
||||
containersMap[name] = info
|
||||
}
|
||||
return containersMap, nil
|
||||
return containersMap, errs.OrNil()
|
||||
}
|
||||
|
||||
func (self *manager) getRequestedContainers(containerName string, options v2.RequestOptions) (map[string]*containerData, error) {
|
||||
@@ -769,8 +777,12 @@ func (m *manager) registerCollectors(collectorConfigs map[string]string, cont *c
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create a container.
|
||||
func (m *manager) createContainer(containerName string) error {
|
||||
// Enables overwriting an existing containerData/Handler object for a given containerName.
|
||||
// Can't use createContainer as it just returns if a given containerName has a handler already.
|
||||
// Ex: rkt handler will want to take priority over the raw handler, but the raw handler might be created first.
|
||||
|
||||
// Only allow raw handler to be overridden
|
||||
func (m *manager) overrideContainer(containerName string, watchSource watcher.ContainerWatchSource) error {
|
||||
m.containersLock.Lock()
|
||||
defer m.containersLock.Unlock()
|
||||
|
||||
@@ -778,12 +790,41 @@ func (m *manager) createContainer(containerName string) error {
|
||||
Name: containerName,
|
||||
}
|
||||
|
||||
if _, ok := m.containers[namespacedName]; ok {
|
||||
containerData := m.containers[namespacedName]
|
||||
|
||||
if containerData.handler.Type() != container.ContainerTypeRaw {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := m.destroyContainerLocked(containerName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("overrideContainer: failed to destroy containerData/handler for %v: %v", containerName, err)
|
||||
}
|
||||
}
|
||||
|
||||
return m.createContainerLocked(containerName, watchSource)
|
||||
}
|
||||
|
||||
// Create a container.
|
||||
func (m *manager) createContainer(containerName string, watchSource watcher.ContainerWatchSource) error {
|
||||
m.containersLock.Lock()
|
||||
defer m.containersLock.Unlock()
|
||||
|
||||
return m.createContainerLocked(containerName, watchSource)
|
||||
}
|
||||
|
||||
func (m *manager) createContainerLocked(containerName string, watchSource watcher.ContainerWatchSource) error {
|
||||
namespacedName := namespacedContainerName{
|
||||
Name: containerName,
|
||||
}
|
||||
|
||||
// Check that the container didn't already exist.
|
||||
if _, ok := m.containers[namespacedName]; ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
handler, accept, err := container.NewContainerHandler(containerName, m.inHostNamespace)
|
||||
handler, accept, err := container.NewContainerHandler(containerName, watchSource, m.inHostNamespace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -798,7 +839,7 @@ func (m *manager) createContainer(containerName string) error {
|
||||
}
|
||||
|
||||
logUsage := *logCadvisorUsage && containerName == m.cadvisorContainer
|
||||
cont, err := newContainerData(containerName, m.memoryCache, handler, m.loadReader, logUsage, collectorManager, m.maxHousekeepingInterval, m.allowDynamicHousekeeping)
|
||||
cont, err := newContainerData(containerName, m.memoryCache, handler, logUsage, collectorManager, m.maxHousekeepingInterval, m.allowDynamicHousekeeping)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -850,6 +891,10 @@ func (m *manager) destroyContainer(containerName string) error {
|
||||
m.containersLock.Lock()
|
||||
defer m.containersLock.Unlock()
|
||||
|
||||
return m.destroyContainerLocked(containerName)
|
||||
}
|
||||
|
||||
func (m *manager) destroyContainerLocked(containerName string) error {
|
||||
namespacedName := namespacedContainerName{
|
||||
Name: containerName,
|
||||
}
|
||||
@@ -947,7 +992,7 @@ func (m *manager) detectSubcontainers(containerName string) error {
|
||||
|
||||
// Add the new containers.
|
||||
for _, cont := range added {
|
||||
err = m.createContainer(cont.Name)
|
||||
err = m.createContainer(cont.Name, watcher.Raw)
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to create existing container: %s: %s", cont.Name, err)
|
||||
}
|
||||
@@ -966,28 +1011,15 @@ func (m *manager) detectSubcontainers(containerName string) error {
|
||||
|
||||
// Watches for new containers started in the system. Runs forever unless there is a setup error.
|
||||
func (self *manager) watchForNewContainers(quit chan error) error {
|
||||
var root *containerData
|
||||
var ok bool
|
||||
func() {
|
||||
self.containersLock.RLock()
|
||||
defer self.containersLock.RUnlock()
|
||||
root, ok = self.containers[namespacedContainerName{
|
||||
Name: "/",
|
||||
}]
|
||||
}()
|
||||
if !ok {
|
||||
return fmt.Errorf("root container does not exist when watching for new containers")
|
||||
}
|
||||
|
||||
// Register for new subcontainers.
|
||||
eventsChannel := make(chan container.SubcontainerEvent, 16)
|
||||
err := root.handler.WatchSubcontainers(eventsChannel)
|
||||
if err != nil {
|
||||
return err
|
||||
for _, watcher := range self.containerWatchers {
|
||||
err := watcher.Start(self.eventsChannel)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// There is a race between starting the watch and new container creation so we do a detection before we read new containers.
|
||||
err = self.detectSubcontainers("/")
|
||||
err := self.detectSubcontainers("/")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -996,21 +1028,37 @@ func (self *manager) watchForNewContainers(quit chan error) error {
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case event := <-eventsChannel:
|
||||
case event := <-self.eventsChannel:
|
||||
switch {
|
||||
case event.EventType == container.SubcontainerAdd:
|
||||
err = self.createContainer(event.Name)
|
||||
case event.EventType == container.SubcontainerDelete:
|
||||
case event.EventType == watcher.ContainerAdd:
|
||||
switch event.WatchSource {
|
||||
// the Rkt and Raw watchers can race, and if Raw wins, we want Rkt to override and create a new handler for Rkt containers
|
||||
case watcher.Rkt:
|
||||
err = self.overrideContainer(event.Name, event.WatchSource)
|
||||
default:
|
||||
err = self.createContainer(event.Name, event.WatchSource)
|
||||
}
|
||||
case event.EventType == watcher.ContainerDelete:
|
||||
err = self.destroyContainer(event.Name)
|
||||
}
|
||||
if err != nil {
|
||||
glog.Warningf("Failed to process watch event: %v", err)
|
||||
glog.Warningf("Failed to process watch event %+v: %v", event, err)
|
||||
}
|
||||
case <-quit:
|
||||
var errs partialFailure
|
||||
|
||||
// Stop processing events if asked to quit.
|
||||
err := root.handler.StopWatchingSubcontainers()
|
||||
quit <- err
|
||||
if err == nil {
|
||||
for i, watcher := range self.containerWatchers {
|
||||
err := watcher.Stop()
|
||||
if err != nil {
|
||||
errs.append(fmt.Sprintf("watcher %d", i), "Stop", err)
|
||||
}
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
quit <- errs
|
||||
} else {
|
||||
quit <- nil
|
||||
glog.Infof("Exiting thread watching subcontainers")
|
||||
return
|
||||
}
|
||||
@@ -1125,79 +1173,12 @@ func parseEventsStoragePolicy() events.StoragePolicy {
|
||||
return policy
|
||||
}
|
||||
|
||||
type DockerStatus struct {
|
||||
Version string `json:"version"`
|
||||
KernelVersion string `json:"kernel_version"`
|
||||
OS string `json:"os"`
|
||||
Hostname string `json:"hostname"`
|
||||
RootDir string `json:"root_dir"`
|
||||
Driver string `json:"driver"`
|
||||
DriverStatus map[string]string `json:"driver_status"`
|
||||
ExecDriver string `json:"exec_driver"`
|
||||
NumImages int `json:"num_images"`
|
||||
NumContainers int `json:"num_containers"`
|
||||
func (m *manager) DockerImages() ([]info.DockerImage, error) {
|
||||
return docker.Images()
|
||||
}
|
||||
|
||||
type DockerImage struct {
|
||||
ID string `json:"id"`
|
||||
RepoTags []string `json:"repo_tags"` // repository name and tags.
|
||||
Created int64 `json:"created"` // unix time since creation.
|
||||
VirtualSize int64 `json:"virtual_size"`
|
||||
Size int64 `json:"size"`
|
||||
}
|
||||
|
||||
func (m *manager) DockerImages() ([]DockerImage, error) {
|
||||
images, err := docker.DockerImages()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := []DockerImage{}
|
||||
const unknownTag = "<none>:<none>"
|
||||
for _, image := range images {
|
||||
if len(image.RepoTags) == 1 && image.RepoTags[0] == unknownTag {
|
||||
// images with repo or tags are uninteresting.
|
||||
continue
|
||||
}
|
||||
di := DockerImage{
|
||||
ID: image.ID,
|
||||
RepoTags: image.RepoTags,
|
||||
Created: image.Created,
|
||||
VirtualSize: image.VirtualSize,
|
||||
Size: image.Size,
|
||||
}
|
||||
out = append(out, di)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (m *manager) DockerInfo() (DockerStatus, error) {
|
||||
return dockerInfo()
|
||||
}
|
||||
|
||||
func dockerInfo() (DockerStatus, error) {
|
||||
dockerInfo, err := docker.DockerInfo()
|
||||
if err != nil {
|
||||
return DockerStatus{}, err
|
||||
}
|
||||
versionInfo, err := getVersionInfo()
|
||||
if err != nil {
|
||||
return DockerStatus{}, err
|
||||
}
|
||||
out := DockerStatus{}
|
||||
out.Version = versionInfo.DockerVersion
|
||||
out.KernelVersion = dockerInfo.KernelVersion
|
||||
out.OS = dockerInfo.OperatingSystem
|
||||
out.Hostname = dockerInfo.Name
|
||||
out.RootDir = dockerInfo.DockerRootDir
|
||||
out.Driver = dockerInfo.Driver
|
||||
out.ExecDriver = dockerInfo.ExecutionDriver
|
||||
out.NumImages = dockerInfo.Images
|
||||
out.NumContainers = dockerInfo.Containers
|
||||
out.DriverStatus = make(map[string]string, len(dockerInfo.DriverStatus))
|
||||
for _, v := range dockerInfo.DriverStatus {
|
||||
out.DriverStatus[v[0]] = v[1]
|
||||
}
|
||||
return out, nil
|
||||
func (m *manager) DockerInfo() (info.DockerStatus, error) {
|
||||
return docker.Status()
|
||||
}
|
||||
|
||||
func (m *manager) DebugInfo() map[string][]string {
|
||||
@@ -1234,3 +1215,36 @@ func (m *manager) DebugInfo() map[string][]string {
|
||||
debugInfo["Managed containers"] = lines
|
||||
return debugInfo
|
||||
}
|
||||
|
||||
func getVersionInfo() (*info.VersionInfo, error) {
|
||||
|
||||
kernel_version := machine.KernelVersion()
|
||||
container_os := machine.ContainerOsVersion()
|
||||
docker_version := docker.VersionString()
|
||||
|
||||
return &info.VersionInfo{
|
||||
KernelVersion: kernel_version,
|
||||
ContainerOsVersion: container_os,
|
||||
DockerVersion: docker_version,
|
||||
CadvisorVersion: version.Info["version"],
|
||||
CadvisorRevision: version.Info["revision"],
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Helper for accumulating partial failures.
|
||||
type partialFailure []string
|
||||
|
||||
func (f *partialFailure) append(id, operation string, err error) {
|
||||
*f = append(*f, fmt.Sprintf("[%q: %s: %s]", id, operation, err))
|
||||
}
|
||||
|
||||
func (f partialFailure) Error() string {
|
||||
return fmt.Sprintf("partial failures: %s", strings.Join(f, ", "))
|
||||
}
|
||||
|
||||
func (f partialFailure) OrNil() error {
|
||||
if len(f) == 0 {
|
||||
return nil
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
119
vendor/github.com/google/cadvisor/manager/manager_mock.go
generated
vendored
119
vendor/github.com/google/cadvisor/manager/manager_mock.go
generated
vendored
@@ -1,119 +0,0 @@
|
||||
// Copyright 2014 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.
|
||||
|
||||
// +build test
|
||||
|
||||
package manager
|
||||
|
||||
import (
|
||||
"github.com/google/cadvisor/events"
|
||||
info "github.com/google/cadvisor/info/v1"
|
||||
"github.com/google/cadvisor/info/v2"
|
||||
|
||||
"github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
type ManagerMock struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (c *ManagerMock) Start() error {
|
||||
args := c.Called()
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (c *ManagerMock) Stop() error {
|
||||
args := c.Called()
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (c *ManagerMock) GetContainerInfo(name string, query *info.ContainerInfoRequest) (*info.ContainerInfo, error) {
|
||||
args := c.Called(name, query)
|
||||
return args.Get(0).(*info.ContainerInfo), args.Error(1)
|
||||
}
|
||||
|
||||
func (c *ManagerMock) SubcontainersInfo(containerName string, query *info.ContainerInfoRequest) ([]*info.ContainerInfo, error) {
|
||||
args := c.Called(containerName, query)
|
||||
return args.Get(0).([]*info.ContainerInfo), args.Error(1)
|
||||
}
|
||||
|
||||
func (c *ManagerMock) AllDockerContainers(query *info.ContainerInfoRequest) (map[string]info.ContainerInfo, error) {
|
||||
args := c.Called(query)
|
||||
return args.Get(0).(map[string]info.ContainerInfo), args.Error(1)
|
||||
}
|
||||
|
||||
func (c *ManagerMock) DockerContainer(name string, query *info.ContainerInfoRequest) (info.ContainerInfo, error) {
|
||||
args := c.Called(name, query)
|
||||
return args.Get(0).(info.ContainerInfo), args.Error(1)
|
||||
}
|
||||
|
||||
func (c *ManagerMock) GetContainerSpec(containerName string, options v2.RequestOptions) (map[string]v2.ContainerSpec, error) {
|
||||
args := c.Called(containerName, options)
|
||||
return args.Get(0).(map[string]v2.ContainerSpec), args.Error(1)
|
||||
}
|
||||
|
||||
func (c *ManagerMock) GetDerivedStats(containerName string, options v2.RequestOptions) (map[string]v2.DerivedStats, error) {
|
||||
args := c.Called(containerName, options)
|
||||
return args.Get(0).(map[string]v2.DerivedStats), args.Error(1)
|
||||
}
|
||||
|
||||
func (c *ManagerMock) GetRequestedContainersInfo(containerName string, options v2.RequestOptions) (map[string]*info.ContainerInfo, error) {
|
||||
args := c.Called(containerName, options)
|
||||
return args.Get(0).(map[string]*info.ContainerInfo), args.Error(1)
|
||||
}
|
||||
|
||||
func (c *ManagerMock) Exists(name string) bool {
|
||||
args := c.Called(name)
|
||||
return args.Get(0).(bool)
|
||||
}
|
||||
|
||||
func (c *ManagerMock) WatchForEvents(queryuest *events.Request, passedChannel chan *info.Event) error {
|
||||
args := c.Called(queryuest, passedChannel)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (c *ManagerMock) GetPastEvents(queryuest *events.Request) ([]*info.Event, error) {
|
||||
args := c.Called(queryuest)
|
||||
return args.Get(0).([]*info.Event), args.Error(1)
|
||||
}
|
||||
|
||||
func (c *ManagerMock) GetMachineInfo() (*info.MachineInfo, error) {
|
||||
args := c.Called()
|
||||
return args.Get(0).(*info.MachineInfo), args.Error(1)
|
||||
}
|
||||
|
||||
func (c *ManagerMock) GetVersionInfo() (*info.VersionInfo, error) {
|
||||
args := c.Called()
|
||||
return args.Get(0).(*info.VersionInfo), args.Error(1)
|
||||
}
|
||||
|
||||
func (c *ManagerMock) GetFsInfo() ([]v2.FsInfo, error) {
|
||||
args := c.Called()
|
||||
return args.Get(0).([]v2.FsInfo), args.Error(1)
|
||||
}
|
||||
|
||||
func (c *ManagerMock) GetProcessList(name string, options v2.RequestOptions) ([]v2.ProcessInfo, error) {
|
||||
args := c.Called()
|
||||
return args.Get(0).([]v2.ProcessInfo), args.Error(1)
|
||||
}
|
||||
|
||||
func (c *ManagerMock) DockerInfo() (DockerStatus, error) {
|
||||
args := c.Called()
|
||||
return args.Get(0).(DockerStatus), args.Error(1)
|
||||
}
|
||||
|
||||
func (c *ManagerMock) DockerImages() ([]DockerImage, error) {
|
||||
args := c.Called()
|
||||
return args.Get(0).([]DockerImage), args.Error(1)
|
||||
}
|
214
vendor/github.com/google/cadvisor/manager/watcher/raw/raw.go
generated
vendored
Normal file
214
vendor/github.com/google/cadvisor/manager/watcher/raw/raw.go
generated
vendored
Normal file
@@ -0,0 +1,214 @@
|
||||
// Copyright 2014 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 container defines types for sub-container events and also
|
||||
// defines an interface for container operation handlers.
|
||||
package raw
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/google/cadvisor/container/common"
|
||||
"github.com/google/cadvisor/container/libcontainer"
|
||||
"github.com/google/cadvisor/manager/watcher"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"golang.org/x/exp/inotify"
|
||||
)
|
||||
|
||||
type rawContainerWatcher struct {
|
||||
// Absolute path to the root of the cgroup hierarchies
|
||||
cgroupPaths map[string]string
|
||||
|
||||
cgroupSubsystems *libcontainer.CgroupSubsystems
|
||||
|
||||
// Inotify event watcher.
|
||||
watcher *common.InotifyWatcher
|
||||
|
||||
// Signal for watcher thread to stop.
|
||||
stopWatcher chan error
|
||||
}
|
||||
|
||||
func NewRawContainerWatcher() (watcher.ContainerWatcher, error) {
|
||||
cgroupSubsystems, err := libcontainer.GetCgroupSubsystems()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get cgroup subsystems: %v", err)
|
||||
}
|
||||
if len(cgroupSubsystems.Mounts) == 0 {
|
||||
return nil, fmt.Errorf("failed to find supported cgroup mounts for the raw factory")
|
||||
}
|
||||
|
||||
watcher, err := common.NewInotifyWatcher()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rawWatcher := &rawContainerWatcher{
|
||||
cgroupPaths: common.MakeCgroupPaths(cgroupSubsystems.MountPoints, "/"),
|
||||
cgroupSubsystems: &cgroupSubsystems,
|
||||
watcher: watcher,
|
||||
stopWatcher: make(chan error),
|
||||
}
|
||||
|
||||
return rawWatcher, nil
|
||||
}
|
||||
|
||||
func (self *rawContainerWatcher) Start(events chan watcher.ContainerEvent) error {
|
||||
// Watch this container (all its cgroups) and all subdirectories.
|
||||
for _, cgroupPath := range self.cgroupPaths {
|
||||
_, err := self.watchDirectory(cgroupPath, "/")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Process the events received from the kernel.
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case event := <-self.watcher.Event():
|
||||
err := self.processEvent(event, events)
|
||||
if err != nil {
|
||||
glog.Warningf("Error while processing event (%+v): %v", event, err)
|
||||
}
|
||||
case err := <-self.watcher.Error():
|
||||
glog.Warningf("Error while watching %q:", "/", err)
|
||||
case <-self.stopWatcher:
|
||||
err := self.watcher.Close()
|
||||
if err == nil {
|
||||
self.stopWatcher <- err
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *rawContainerWatcher) Stop() error {
|
||||
// Rendezvous with the watcher thread.
|
||||
self.stopWatcher <- nil
|
||||
return <-self.stopWatcher
|
||||
}
|
||||
|
||||
// Watches the specified directory and all subdirectories. Returns whether the path was
|
||||
// already being watched and an error (if any).
|
||||
func (self *rawContainerWatcher) watchDirectory(dir string, containerName string) (bool, error) {
|
||||
alreadyWatching, err := self.watcher.AddWatch(containerName, dir)
|
||||
if err != nil {
|
||||
return alreadyWatching, err
|
||||
}
|
||||
|
||||
// Remove the watch if further operations failed.
|
||||
cleanup := true
|
||||
defer func() {
|
||||
if cleanup {
|
||||
_, err := self.watcher.RemoveWatch(containerName, dir)
|
||||
if err != nil {
|
||||
glog.Warningf("Failed to remove inotify watch for %q: %v", dir, err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// TODO(vmarmol): We should re-do this once we're done to ensure directories were not added in the meantime.
|
||||
// Watch subdirectories as well.
|
||||
entries, err := ioutil.ReadDir(dir)
|
||||
if err != nil {
|
||||
return alreadyWatching, err
|
||||
}
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() {
|
||||
// TODO(vmarmol): We don't have to fail here, maybe we can recover and try to get as many registrations as we can.
|
||||
_, err = self.watchDirectory(path.Join(dir, entry.Name()), path.Join(containerName, entry.Name()))
|
||||
if err != nil {
|
||||
return alreadyWatching, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cleanup = false
|
||||
return alreadyWatching, nil
|
||||
}
|
||||
|
||||
func (self *rawContainerWatcher) processEvent(event *inotify.Event, events chan watcher.ContainerEvent) error {
|
||||
// Convert the inotify event type to a container create or delete.
|
||||
var eventType watcher.ContainerEventType
|
||||
switch {
|
||||
case (event.Mask & inotify.IN_CREATE) > 0:
|
||||
eventType = watcher.ContainerAdd
|
||||
case (event.Mask & inotify.IN_DELETE) > 0:
|
||||
eventType = watcher.ContainerDelete
|
||||
case (event.Mask & inotify.IN_MOVED_FROM) > 0:
|
||||
eventType = watcher.ContainerDelete
|
||||
case (event.Mask & inotify.IN_MOVED_TO) > 0:
|
||||
eventType = watcher.ContainerAdd
|
||||
default:
|
||||
// Ignore other events.
|
||||
return nil
|
||||
}
|
||||
|
||||
// Derive the container name from the path name.
|
||||
var containerName string
|
||||
for _, mount := range self.cgroupSubsystems.Mounts {
|
||||
mountLocation := path.Clean(mount.Mountpoint) + "/"
|
||||
if strings.HasPrefix(event.Name, mountLocation) {
|
||||
containerName = event.Name[len(mountLocation)-1:]
|
||||
break
|
||||
}
|
||||
}
|
||||
if containerName == "" {
|
||||
return fmt.Errorf("unable to detect container from watch event on directory %q", event.Name)
|
||||
}
|
||||
|
||||
// Maintain the watch for the new or deleted container.
|
||||
switch eventType {
|
||||
case watcher.ContainerAdd:
|
||||
// New container was created, watch it.
|
||||
alreadyWatched, err := self.watchDirectory(event.Name, containerName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Only report container creation once.
|
||||
if alreadyWatched {
|
||||
return nil
|
||||
}
|
||||
case watcher.ContainerDelete:
|
||||
// Container was deleted, stop watching for it.
|
||||
lastWatched, err := self.watcher.RemoveWatch(containerName, event.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Only report container deletion once.
|
||||
if !lastWatched {
|
||||
return nil
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unknown event type %v", eventType)
|
||||
}
|
||||
|
||||
// Deliver the event.
|
||||
events <- watcher.ContainerEvent{
|
||||
EventType: eventType,
|
||||
Name: containerName,
|
||||
WatchSource: watcher.Raw,
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
154
vendor/github.com/google/cadvisor/manager/watcher/rkt/rkt.go
generated
vendored
Normal file
154
vendor/github.com/google/cadvisor/manager/watcher/rkt/rkt.go
generated
vendored
Normal file
@@ -0,0 +1,154 @@
|
||||
// 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 implements the watcher interface for rkt
|
||||
package rkt
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/google/cadvisor/container/rkt"
|
||||
"github.com/google/cadvisor/manager/watcher"
|
||||
|
||||
rktapi "github.com/coreos/rkt/api/v1alpha"
|
||||
"github.com/golang/glog"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
type rktContainerWatcher struct {
|
||||
// Signal for watcher thread to stop.
|
||||
stopWatcher chan error
|
||||
}
|
||||
|
||||
func NewRktContainerWatcher() (watcher.ContainerWatcher, error) {
|
||||
watcher := &rktContainerWatcher{
|
||||
stopWatcher: make(chan error),
|
||||
}
|
||||
|
||||
return watcher, nil
|
||||
}
|
||||
|
||||
func (self *rktContainerWatcher) Start(events chan watcher.ContainerEvent) error {
|
||||
go self.detectRktContainers(events)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *rktContainerWatcher) Stop() error {
|
||||
// Rendezvous with the watcher thread.
|
||||
self.stopWatcher <- nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *rktContainerWatcher) detectRktContainers(events chan watcher.ContainerEvent) {
|
||||
glog.Infof("starting detectRktContainers thread")
|
||||
ticker := time.Tick(10 * time.Second)
|
||||
curpods := make(map[string]*rktapi.Pod)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker:
|
||||
pods, err := listRunningPods()
|
||||
if err != nil {
|
||||
glog.Errorf("detectRktContainers: listRunningPods failed: %v", err)
|
||||
continue
|
||||
}
|
||||
curpods = self.syncRunningPods(pods, events, curpods)
|
||||
|
||||
case <-self.stopWatcher:
|
||||
glog.Infof("Exiting rktContainer Thread")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (self *rktContainerWatcher) syncRunningPods(pods []*rktapi.Pod, events chan watcher.ContainerEvent, curpods map[string]*rktapi.Pod) map[string]*rktapi.Pod {
|
||||
newpods := make(map[string]*rktapi.Pod)
|
||||
|
||||
for _, pod := range pods {
|
||||
newpods[pod.Id] = pod
|
||||
// if pods become mutable, have to handle this better
|
||||
if _, ok := curpods[pod.Id]; !ok {
|
||||
// should create all cgroups not including system.slice
|
||||
// i.e. /system.slice/rkt-test.service and /system.slice/rkt-test.service/system.slice/pause.service
|
||||
for _, cgroup := range podToCgroup(pod) {
|
||||
self.sendUpdateEvent(cgroup, events)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for id, pod := range curpods {
|
||||
if _, ok := newpods[id]; !ok {
|
||||
for _, cgroup := range podToCgroup(pod) {
|
||||
glog.Infof("cgroup to delete = %v", cgroup)
|
||||
self.sendDestroyEvent(cgroup, events)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return newpods
|
||||
}
|
||||
|
||||
func (self *rktContainerWatcher) sendUpdateEvent(cgroup string, events chan watcher.ContainerEvent) {
|
||||
events <- watcher.ContainerEvent{
|
||||
EventType: watcher.ContainerAdd,
|
||||
Name: cgroup,
|
||||
WatchSource: watcher.Rkt,
|
||||
}
|
||||
}
|
||||
|
||||
func (self *rktContainerWatcher) sendDestroyEvent(cgroup string, events chan watcher.ContainerEvent) {
|
||||
events <- watcher.ContainerEvent{
|
||||
EventType: watcher.ContainerDelete,
|
||||
Name: cgroup,
|
||||
WatchSource: watcher.Rkt,
|
||||
}
|
||||
}
|
||||
|
||||
func listRunningPods() ([]*rktapi.Pod, error) {
|
||||
client, err := rkt.Client()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := client.ListPods(context.Background(), &rktapi.ListPodsRequest{
|
||||
// Specify the request: Fetch and print only running pods and their details.
|
||||
Detail: true,
|
||||
Filters: []*rktapi.PodFilter{
|
||||
{
|
||||
States: []rktapi.PodState{rktapi.PodState_POD_STATE_RUNNING},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp.Pods, nil
|
||||
}
|
||||
|
||||
func podToCgroup(pod *rktapi.Pod) []string {
|
||||
cgroups := make([]string, 1+len(pod.Apps), 1+len(pod.Apps))
|
||||
|
||||
baseCgroup := pod.Cgroup
|
||||
cgroups[0] = baseCgroup
|
||||
|
||||
for i, app := range pod.Apps {
|
||||
cgroups[i+1] = filepath.Join(baseCgroup, "system.slice", app.Name+".service")
|
||||
}
|
||||
|
||||
return cgroups
|
||||
}
|
52
vendor/github.com/google/cadvisor/manager/watcher/watcher.go
generated
vendored
Normal file
52
vendor/github.com/google/cadvisor/manager/watcher/watcher.go
generated
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
// 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 container defines types for sub-container events and also
|
||||
// defines an interface for container operation handlers.
|
||||
package watcher
|
||||
|
||||
// SubcontainerEventType indicates an addition or deletion event.
|
||||
type ContainerEventType int
|
||||
|
||||
const (
|
||||
ContainerAdd ContainerEventType = iota
|
||||
ContainerDelete
|
||||
)
|
||||
|
||||
type ContainerWatchSource int
|
||||
|
||||
const (
|
||||
Raw ContainerWatchSource = iota
|
||||
Rkt
|
||||
)
|
||||
|
||||
// ContainerEvent represents a
|
||||
type ContainerEvent struct {
|
||||
// The type of event that occurred.
|
||||
EventType ContainerEventType
|
||||
|
||||
// The full container name of the container where the event occurred.
|
||||
Name string
|
||||
|
||||
// The watcher that detected this change event
|
||||
WatchSource ContainerWatchSource
|
||||
}
|
||||
|
||||
type ContainerWatcher interface {
|
||||
// Registers a channel to listen for events affecting subcontainers (recursively).
|
||||
Start(events chan ContainerEvent) error
|
||||
|
||||
// Stops watching for subcontainer changes.
|
||||
Stop() error
|
||||
}
|
1
vendor/github.com/google/cadvisor/pages/containers.go
generated
vendored
1
vendor/github.com/google/cadvisor/pages/containers.go
generated
vendored
@@ -232,6 +232,7 @@ func serveContainersPage(m manager.Manager, w http.ResponseWriter, u *url.URL) e
|
||||
NetworkAvailable: cont.Spec.HasNetwork,
|
||||
FsAvailable: cont.Spec.HasFilesystem,
|
||||
CustomMetricsAvailable: cont.Spec.HasCustomMetrics,
|
||||
SubcontainersAvailable: len(subcontainerLinks) > 0,
|
||||
Root: rootDir,
|
||||
}
|
||||
err = pageTemplate.Execute(w, data)
|
||||
|
243
vendor/github.com/google/cadvisor/pages/containers_html.go
generated
vendored
243
vendor/github.com/google/cadvisor/pages/containers_html.go
generated
vendored
@@ -1,243 +0,0 @@
|
||||
// Copyright 2014 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 pages
|
||||
|
||||
const containersHtmlTemplate = `
|
||||
<html>
|
||||
<head>
|
||||
<title>cAdvisor - {{.DisplayName}}</title>
|
||||
<!-- Latest compiled and minified CSS -->
|
||||
<link rel="stylesheet" href="{{.Root}}static/bootstrap-3.1.1.min.css">
|
||||
|
||||
<!-- Optional theme -->
|
||||
<link rel="stylesheet" href="{{.Root}}static/bootstrap-theme-3.1.1.min.css">
|
||||
|
||||
<link rel="stylesheet" href="{{.Root}}static/containers.css">
|
||||
|
||||
<!-- Latest compiled and minified JavaScript -->
|
||||
<script src="{{.Root}}static/jquery-1.10.2.min.js"></script>
|
||||
<script src="{{.Root}}static/bootstrap-3.1.1.min.js"></script>
|
||||
<script type="text/javascript" src="{{.Root}}static/google-jsapi.js"></script>
|
||||
|
||||
<script type="text/javascript" src="{{.Root}}static/containers.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container theme-showcase" >
|
||||
<a href="{{.Root}}" class="col-sm-12" id="logo">
|
||||
</a>
|
||||
<div class="col-sm-12">
|
||||
<div class="page-header">
|
||||
<h1>{{.DisplayName}}</h1>
|
||||
</div>
|
||||
<ol class="breadcrumb">
|
||||
{{range $parentContainer := .ParentContainers}}
|
||||
<li><a href="{{$parentContainer.Link}}">{{$parentContainer.Text}}</a></li>
|
||||
{{end}}
|
||||
</ol>
|
||||
</div>
|
||||
{{if .IsRoot}}
|
||||
<div class="col-sm-12">
|
||||
<h4><a href="../docker">Docker Containers</a></h4>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .Subcontainers}}
|
||||
<div class="col-sm-12">
|
||||
<div class="page-header">
|
||||
<h3>Subcontainers</h3>
|
||||
</div>
|
||||
<div class="list-group">
|
||||
{{range $subcontainer := .Subcontainers}}
|
||||
<a href="{{$subcontainer.Link}}" class="list-group-item">{{$subcontainer.Text}}</a>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .DockerStatus}}
|
||||
<div class="col-sm-12">
|
||||
<div class="page-header">
|
||||
<h3>Driver Status</h3>
|
||||
</div>
|
||||
<ul class="list-group">
|
||||
{{range $dockerstatus := .DockerStatus}}
|
||||
<li class ="list-group-item"><span class="stat-label">{{$dockerstatus.Key}}</span> {{$dockerstatus.Value}}</li>
|
||||
{{end}}
|
||||
{{if .DockerDriverStatus}}
|
||||
<li class ="list-group-item"><span class="stat-label">Storage<br></span>
|
||||
<ul class="list-group">
|
||||
{{range $driverstatus := .DockerDriverStatus}}
|
||||
<li class="list-group-item"><span class="stat-label">{{$driverstatus.Key}}</span> {{$driverstatus.Value}}</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .DockerImages}}
|
||||
<div class="col-sm-12">
|
||||
<div class="page-header">
|
||||
<h3>Images</h3>
|
||||
</div>
|
||||
<div id="docker-images"></div>
|
||||
<br><br>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .ResourcesAvailable}}
|
||||
<div class="col-sm-12">
|
||||
<div class="page-header">
|
||||
<h3>Isolation</h3>
|
||||
</div>
|
||||
{{if .CpuAvailable}}
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item active isolation-title panel-title">CPU</li>
|
||||
{{if .Spec.Cpu.Limit}}
|
||||
<li class="list-group-item"><span class="stat-label">Shares</span> {{printShares .Spec.Cpu.Limit}} <span class="unit-label">shares</span></li>
|
||||
{{end}}
|
||||
{{if .Spec.Cpu.MaxLimit}}
|
||||
<li class="list-group-item"><span class="stat-label">Max Limit</span> {{printCores .Spec.Cpu.MaxLimit}} <span class="unit-label">cores</span></li>
|
||||
{{end}}
|
||||
{{if .Spec.Cpu.Mask}}
|
||||
<li class="list-group-item"><span class="stat-label">Allowed Cores</span> {{printMask .Spec.Cpu.Mask .MachineInfo.NumCores}}</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
{{end}}
|
||||
{{if .MemoryAvailable}}
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item active isolation-title panel-title">Memory</li>
|
||||
{{if .Spec.Memory.Reservation}}
|
||||
<li class="list-group-item"><span class="stat-label">Reservation</span> {{printSize .Spec.Memory.Reservation}} <span class="unit-label">{{printUnit .Spec.Memory.Reservation}}</span></li>
|
||||
{{end}}
|
||||
{{if .Spec.Memory.Limit}}
|
||||
<li class="list-group-item"><span class="stat-label">Limit</span> {{printSize .Spec.Memory.Limit}} <span class="unit-label">{{printUnit .Spec.Memory.Limit}}</span></li>
|
||||
{{end}}
|
||||
{{if .Spec.Memory.SwapLimit}}
|
||||
<li class="list-group-item"><span class="stat-label">Swap Limit</span> {{printSize .Spec.Memory.SwapLimit}} <span class="unit-label">{{printUnit .Spec.Memory.SwapLimit}}</span></li>
|
||||
{{end}}
|
||||
</ul>
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
<div class="page-header">
|
||||
<h3>Usage</h3>
|
||||
</div>
|
||||
<div class="panel panel-primary">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">Overview</h3>
|
||||
</div>
|
||||
<div id="usage-gauge" class="panel-body"></div>
|
||||
</div>
|
||||
<div class="panel panel-primary">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">Processes</h3>
|
||||
</div>
|
||||
<div id="processes-top" class="panel-body"></div>
|
||||
</div>
|
||||
{{if .CpuAvailable}}
|
||||
<div class="panel panel-primary">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">CPU</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<h4>Total Usage</h4>
|
||||
<div id="cpu-total-usage-chart"></div>
|
||||
<!-- <h4>CPU Load Average</h4>
|
||||
<div id="cpu-load-chart"></div> -->
|
||||
<h4>Usage per Core</h4>
|
||||
<div id="cpu-per-core-usage-chart"></div>
|
||||
<h4>Usage Breakdown</h4>
|
||||
<div id="cpu-usage-breakdown-chart"></div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .MemoryAvailable}}
|
||||
<div class="panel panel-primary">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">Memory</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<h4>Total Usage</h4>
|
||||
<div id="memory-usage-chart"></div>
|
||||
<br/>
|
||||
<div class="row col-sm-12">
|
||||
<h4>Usage Breakdown</h4>
|
||||
<div class="col-sm-9">
|
||||
<div class="progress">
|
||||
<div class="progress-bar progress-bar-danger" id="progress-hot-memory">
|
||||
<span class="sr-only">Hot Memory</span>
|
||||
</div>
|
||||
<div class="progress-bar progress-bar-info" id="progress-cold-memory">
|
||||
<span class="sr-only">Cold Memory</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-3" id="memory-text"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .NetworkAvailable}}
|
||||
<div class="panel panel-primary">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">Network</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-default dropdown-toggle" type="button" id="network-selection-dropdown" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<span id="network-selection-text"></span>
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul id="network-selection" class="dropdown-menu" role="menu" aria-labelledby="network-selection-dropdown">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<h4>Throughput</h4>
|
||||
<div id="network-bytes-chart"></div>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<h4>Errors</h4>
|
||||
<div id="network-errors-chart"></div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .FsAvailable}}
|
||||
<div class="panel panel-primary">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">Filesystem</h3>
|
||||
</div>
|
||||
<div id="filesystem-usage" class="panel-body">
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .CustomMetricsAvailable}}
|
||||
<div class="panel panel-primary">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">Application Metrics</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div id="custom-metrics-chart"></div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
startPage({{.ContainerName}}, {{.CpuAvailable}}, {{.MemoryAvailable}}, {{.Root}}, {{.IsRoot}});
|
||||
drawImages({{.DockerImages}});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
`
|
2
vendor/github.com/google/cadvisor/pages/docker.go
generated
vendored
2
vendor/github.com/google/cadvisor/pages/docker.go
generated
vendored
@@ -31,7 +31,7 @@ import (
|
||||
|
||||
const DockerPage = "/docker/"
|
||||
|
||||
func toStatusKV(status manager.DockerStatus) ([]keyVal, []keyVal) {
|
||||
func toStatusKV(status info.DockerStatus) ([]keyVal, []keyVal) {
|
||||
ds := []keyVal{
|
||||
{Key: "Driver", Value: status.Driver},
|
||||
}
|
||||
|
6
vendor/github.com/google/cadvisor/pages/pages.go
generated
vendored
6
vendor/github.com/google/cadvisor/pages/pages.go
generated
vendored
@@ -59,15 +59,17 @@ type pageData struct {
|
||||
NetworkAvailable bool
|
||||
FsAvailable bool
|
||||
CustomMetricsAvailable bool
|
||||
SubcontainersAvailable bool
|
||||
Root string
|
||||
DockerStatus []keyVal
|
||||
DockerDriverStatus []keyVal
|
||||
DockerImages []manager.DockerImage
|
||||
DockerImages []info.DockerImage
|
||||
}
|
||||
|
||||
func init() {
|
||||
containersHtmlTemplate, _ := Asset("pages/assets/html/containers.html")
|
||||
pageTemplate = template.New("containersTemplate").Funcs(funcMap)
|
||||
_, err := pageTemplate.Parse(containersHtmlTemplate)
|
||||
_, err := pageTemplate.Parse(string(containersHtmlTemplate))
|
||||
if err != nil {
|
||||
glog.Fatalf("Failed to parse template: %s", err)
|
||||
}
|
||||
|
419
vendor/github.com/google/cadvisor/pages/static/assets.go
generated
vendored
Normal file
419
vendor/github.com/google/cadvisor/pages/static/assets.go
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
25
vendor/github.com/google/cadvisor/pages/static/bootstrap_min_css.go
generated
vendored
25
vendor/github.com/google/cadvisor/pages/static/bootstrap_min_css.go
generated
vendored
File diff suppressed because one or more lines are too long
24
vendor/github.com/google/cadvisor/pages/static/bootstrap_min_js.go
generated
vendored
24
vendor/github.com/google/cadvisor/pages/static/bootstrap_min_js.go
generated
vendored
File diff suppressed because one or more lines are too long
25
vendor/github.com/google/cadvisor/pages/static/bootstrap_theme_min_css.go
generated
vendored
25
vendor/github.com/google/cadvisor/pages/static/bootstrap_theme_min_css.go
generated
vendored
File diff suppressed because one or more lines are too long
61
vendor/github.com/google/cadvisor/pages/static/containers_css.go
generated
vendored
61
vendor/github.com/google/cadvisor/pages/static/containers_css.go
generated
vendored
File diff suppressed because one or more lines are too long
881
vendor/github.com/google/cadvisor/pages/static/containers_js.go
generated
vendored
881
vendor/github.com/google/cadvisor/pages/static/containers_js.go
generated
vendored
@@ -1,881 +0,0 @@
|
||||
// Copyright 2014 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 static
|
||||
|
||||
const containersJs = gchartsJs + `
|
||||
function humanize(num, size, units) {
|
||||
var unit;
|
||||
for (unit = units.pop(); units.length && num >= size; unit = units.pop()) {
|
||||
num /= size;
|
||||
}
|
||||
return [num, unit];
|
||||
}
|
||||
|
||||
// Following the IEC naming convention
|
||||
function humanizeIEC(num) {
|
||||
var ret = humanize(num, 1024, ["TiB", "GiB", "MiB", "KiB", "B"]);
|
||||
return ret[0].toFixed(2) + " " + ret[1];
|
||||
}
|
||||
// Following the Metric naming convention
|
||||
function humanizeMetric(num) {
|
||||
var ret = humanize(num, 1000, ["TB", "GB", "MB", "KB", "Bytes"]);
|
||||
return ret[0].toFixed(2) + " " + ret[1];
|
||||
}
|
||||
|
||||
// Draw a table.
|
||||
function drawTable(seriesTitles, titleTypes, data, elementId, numPages, sortIndex) {
|
||||
var dataTable = new google.visualization.DataTable();
|
||||
for (var i = 0; i < seriesTitles.length; i++) {
|
||||
dataTable.addColumn(titleTypes[i], seriesTitles[i]);
|
||||
}
|
||||
dataTable.addRows(data);
|
||||
if (!(elementId in window.charts)) {
|
||||
window.charts[elementId] = new google.visualization.Table(document.getElementById(elementId));
|
||||
}
|
||||
|
||||
var cssClassNames = {
|
||||
'headerRow': '',
|
||||
'tableRow': 'table-row',
|
||||
'oddTableRow': 'table-row'
|
||||
};
|
||||
var opts = {
|
||||
alternatingRowStyle: true,
|
||||
page: 'enable',
|
||||
pageSize: numPages,
|
||||
allowHtml: true,
|
||||
sortColumn: sortIndex,
|
||||
sortAscending: false,
|
||||
cssClassNames: cssClassNames,
|
||||
};
|
||||
window.charts[elementId].draw(dataTable, opts);
|
||||
}
|
||||
|
||||
// Draw a line chart.
|
||||
function drawLineChart(seriesTitles, data, elementId, unit) {
|
||||
var min = Infinity;
|
||||
var max = -Infinity;
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
// Convert the first column to a Date.
|
||||
if (data[i] != null) {
|
||||
data[i][0] = new Date(data[i][0]);
|
||||
}
|
||||
|
||||
// Find min, max.
|
||||
for (var j = 1; j < data[i].length; j++) {
|
||||
var val = data[i][j];
|
||||
if (val < min) {
|
||||
min = val;
|
||||
}
|
||||
if (val > max) {
|
||||
max = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We don't want to show any values less than 0 so cap the min value at that.
|
||||
// At the same time, show 10% of the graph below the min value if we can.
|
||||
var minWindow = min - (max - min) / 10;
|
||||
if (minWindow < 0) {
|
||||
minWindow = 0;
|
||||
}
|
||||
|
||||
// Add the definition of each column and the necessary data.
|
||||
var dataTable = new google.visualization.DataTable();
|
||||
dataTable.addColumn('datetime', seriesTitles[0]);
|
||||
for (var i = 1; i < seriesTitles.length; i++) {
|
||||
dataTable.addColumn('number', seriesTitles[i]);
|
||||
}
|
||||
dataTable.addRows(data);
|
||||
|
||||
// Create and draw the visualization.
|
||||
if (!(elementId in window.charts)) {
|
||||
window.charts[elementId] = new google.visualization.LineChart(document.getElementById(elementId));
|
||||
}
|
||||
|
||||
// TODO(vmarmol): Look into changing the view window to get a smoother animation.
|
||||
var opts = {
|
||||
curveType: 'function',
|
||||
height: 300,
|
||||
legend:{position:"none"},
|
||||
focusTarget: "category",
|
||||
vAxis: {
|
||||
title: unit,
|
||||
viewWindow: {
|
||||
min: minWindow,
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
position: 'bottom',
|
||||
},
|
||||
};
|
||||
// If the whole data series has the same value, try to center it in the chart.
|
||||
if ( min == max) {
|
||||
opts.vAxis.viewWindow.max = 1.1 * max
|
||||
opts.vAxis.viewWindow.min = 0.9 * max
|
||||
}
|
||||
|
||||
window.charts[elementId].draw(dataTable, opts);
|
||||
}
|
||||
|
||||
// Gets the length of the interval in nanoseconds.
|
||||
function getInterval(current, previous) {
|
||||
var cur = new Date(current);
|
||||
var prev = new Date(previous);
|
||||
|
||||
// ms -> ns.
|
||||
return (cur.getTime() - prev.getTime()) * 1000000;
|
||||
}
|
||||
|
||||
// Checks if the specified stats include the specified resource.
|
||||
function hasResource(stats, resource) {
|
||||
return stats.stats.length > 0 && stats.stats[0][resource];
|
||||
}
|
||||
|
||||
// Draw a set of gauges. Data is comprised of an array of arrays with two elements:
|
||||
// a string label and a numeric value for the gauge.
|
||||
function drawGauges(elementId, gauges) {
|
||||
gauges.unshift(['Label', 'Value']);
|
||||
|
||||
// Create and populate the data table.
|
||||
var data = google.visualization.arrayToDataTable(gauges);
|
||||
|
||||
// Create and draw the visualization.
|
||||
var options = {
|
||||
height: 100,
|
||||
redFrom: 90, redTo: 100,
|
||||
yellowFrom:75, yellowTo: 90,
|
||||
minorTicks: 5,
|
||||
animation: {
|
||||
duration: 900,
|
||||
easing: 'linear'
|
||||
}
|
||||
};
|
||||
var chart = new google.visualization.Gauge(document.getElementById(elementId));
|
||||
chart.draw(data, options);
|
||||
}
|
||||
|
||||
// Get the machine info.
|
||||
function getMachineInfo(rootDir, callback) {
|
||||
$.getJSON(rootDir + "api/v1.0/machine", function(data) {
|
||||
callback(data);
|
||||
});
|
||||
}
|
||||
|
||||
// Get ps info.
|
||||
function getProcessInfo(rootDir, containerName, callback) {
|
||||
$.getJSON(rootDir + "api/v2.0/ps" + containerName)
|
||||
.done(function(data) {
|
||||
callback(data);
|
||||
})
|
||||
.fail(function(jqhxr, textStatus, error) {
|
||||
callback([]);
|
||||
});
|
||||
}
|
||||
|
||||
// Get the container stats for the specified container.
|
||||
function getStats(rootDir, containerName, callback) {
|
||||
// Request 60s of container history and no samples.
|
||||
var request = JSON.stringify({
|
||||
// Update main.statsRequestedByUI while updating "num_stats" here.
|
||||
"num_stats": 60,
|
||||
"num_samples": 0
|
||||
});
|
||||
$.post(rootDir + "api/v1.0/containers" + containerName, request, function(data) {
|
||||
callback(data);
|
||||
}, "json");
|
||||
}
|
||||
|
||||
// Draw the graph for CPU usage.
|
||||
function drawCpuTotalUsage(elementId, machineInfo, stats) {
|
||||
if (stats.spec.has_cpu && !hasResource(stats, "cpu")) {
|
||||
return;
|
||||
}
|
||||
|
||||
var titles = ["Time", "Total"];
|
||||
var data = [];
|
||||
for (var i = 1; i < stats.stats.length; i++) {
|
||||
var cur = stats.stats[i];
|
||||
var prev = stats.stats[i - 1];
|
||||
var intervalInNs = getInterval(cur.timestamp, prev.timestamp);
|
||||
|
||||
var elements = [];
|
||||
elements.push(cur.timestamp);
|
||||
elements.push((cur.cpu.usage.total - prev.cpu.usage.total) / intervalInNs);
|
||||
data.push(elements);
|
||||
}
|
||||
drawLineChart(titles, data, elementId, "Cores");
|
||||
}
|
||||
|
||||
// Draw the graph for CPU load.
|
||||
function drawCpuLoad(elementId, machineInfo, stats) {
|
||||
|
||||
var titles = ["Time", "Average"];
|
||||
var data = [];
|
||||
for (var i = 1; i < stats.stats.length; i++) {
|
||||
var cur = stats.stats[i];
|
||||
|
||||
var elements = [];
|
||||
elements.push(cur.timestamp);
|
||||
elements.push(cur.cpu.load_average/1000);
|
||||
data.push(elements);
|
||||
}
|
||||
drawLineChart(titles, data, elementId, "Runnable threads");
|
||||
}
|
||||
|
||||
|
||||
// Draw the graph for per-core CPU usage.
|
||||
function drawCpuPerCoreUsage(elementId, machineInfo, stats) {
|
||||
if (stats.spec.has_cpu && !hasResource(stats, "cpu")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Add a title for each core.
|
||||
var titles = ["Time"];
|
||||
for (var i = 0; i < machineInfo.num_cores; i++) {
|
||||
titles.push("Core " + i);
|
||||
}
|
||||
var data = [];
|
||||
for (var i = 1; i < stats.stats.length; i++) {
|
||||
var cur = stats.stats[i];
|
||||
var prev = stats.stats[i - 1];
|
||||
var intervalInNs = getInterval(cur.timestamp, prev.timestamp);
|
||||
|
||||
var elements = [];
|
||||
elements.push(cur.timestamp);
|
||||
for (var j = 0; j < machineInfo.num_cores; j++) {
|
||||
elements.push((cur.cpu.usage.per_cpu_usage[j] - prev.cpu.usage.per_cpu_usage[j]) / intervalInNs);
|
||||
}
|
||||
data.push(elements);
|
||||
}
|
||||
drawLineChart(titles, data, elementId, "Cores");
|
||||
}
|
||||
|
||||
// Draw the graph for CPU usage breakdown.
|
||||
function drawCpuUsageBreakdown(elementId, machineInfo, containerInfo) {
|
||||
if (containerInfo.spec.has_cpu && !hasResource(containerInfo, "cpu")) {
|
||||
return;
|
||||
}
|
||||
|
||||
var titles = ["Time", "User", "Kernel"];
|
||||
var data = [];
|
||||
for (var i = 1; i < containerInfo.stats.length; i++) {
|
||||
var cur = containerInfo.stats[i];
|
||||
var prev = containerInfo.stats[i - 1];
|
||||
var intervalInNs = getInterval(cur.timestamp, prev.timestamp);
|
||||
|
||||
var elements = [];
|
||||
elements.push(cur.timestamp);
|
||||
elements.push((cur.cpu.usage.user - prev.cpu.usage.user) / intervalInNs);
|
||||
elements.push((cur.cpu.usage.system - prev.cpu.usage.system) / intervalInNs);
|
||||
data.push(elements);
|
||||
}
|
||||
drawLineChart(titles, data, elementId, "Cores");
|
||||
}
|
||||
|
||||
// Draw the gauges for overall resource usage.
|
||||
function drawOverallUsage(elementId, machineInfo, containerInfo) {
|
||||
var cur = containerInfo.stats[containerInfo.stats.length - 1];
|
||||
var gauges = [];
|
||||
|
||||
var cpuUsage = 0;
|
||||
if (containerInfo.spec.has_cpu && containerInfo.stats.length >= 2) {
|
||||
var prev = containerInfo.stats[containerInfo.stats.length - 2];
|
||||
var rawUsage = cur.cpu.usage.total - prev.cpu.usage.total;
|
||||
var intervalInNs = getInterval(cur.timestamp, prev.timestamp);
|
||||
|
||||
// Convert to millicores and take the percentage
|
||||
cpuUsage = Math.round(((rawUsage / intervalInNs) / machineInfo.num_cores) * 100);
|
||||
if (cpuUsage > 100) {
|
||||
cpuUsage = 100;
|
||||
}
|
||||
gauges.push(['CPU', cpuUsage]);
|
||||
}
|
||||
|
||||
var memoryUsage = 0;
|
||||
if (containerInfo.spec.has_memory) {
|
||||
// Saturate to the machine size.
|
||||
var limit = containerInfo.spec.memory.limit;
|
||||
if (limit > machineInfo.memory_capacity) {
|
||||
limit = machineInfo.memory_capacity;
|
||||
}
|
||||
|
||||
memoryUsage = Math.round((cur.memory.usage / limit) * 100);
|
||||
gauges.push(['Memory', memoryUsage]);
|
||||
}
|
||||
|
||||
var numGauges = gauges.length;
|
||||
if (cur.filesystem) {
|
||||
for (var i = 0; i < cur.filesystem.length; i++) {
|
||||
var data = cur.filesystem[i];
|
||||
var totalUsage = Math.floor((data.usage * 100.0) / data.capacity);
|
||||
var els = window.cadvisor.fsUsage.elements[data.device];
|
||||
|
||||
// Update the gauges in the right order.
|
||||
gauges[numGauges + els.index] = ['FS #' + (els.index + 1), totalUsage];
|
||||
}
|
||||
|
||||
// Limit the number of filesystem gauges displayed to 5.
|
||||
// 'Filesystem details' section still shows information for all filesystems.
|
||||
var max_gauges = numGauges + 5;
|
||||
if (gauges.length > max_gauges) {
|
||||
gauges = gauges.slice(0, max_gauges);
|
||||
}
|
||||
}
|
||||
|
||||
drawGauges(elementId, gauges);
|
||||
}
|
||||
|
||||
var oneMegabyte = 1024 * 1024;
|
||||
var oneGigabyte = 1024 * oneMegabyte;
|
||||
|
||||
function drawMemoryUsage(elementId, machineInfo, containerInfo) {
|
||||
if (containerInfo.spec.has_memory && !hasResource(containerInfo, "memory")) {
|
||||
return;
|
||||
}
|
||||
|
||||
var titles = ["Time", "Total", "Hot"];
|
||||
var data = [];
|
||||
for (var i = 0; i < containerInfo.stats.length; i++) {
|
||||
var cur = containerInfo.stats[i];
|
||||
|
||||
var elements = [];
|
||||
elements.push(cur.timestamp);
|
||||
elements.push(cur.memory.usage / oneMegabyte);
|
||||
elements.push(cur.memory.working_set / oneMegabyte);
|
||||
data.push(elements);
|
||||
}
|
||||
|
||||
// Get the memory limit, saturate to the machine size.
|
||||
var memory_limit = machineInfo.memory_capacity;
|
||||
if (containerInfo.spec.memory.limit && (containerInfo.spec.memory.limit < memory_limit)) {
|
||||
memory_limit = containerInfo.spec.memory.limit;
|
||||
}
|
||||
|
||||
// Updating the progress bar.
|
||||
var cur = containerInfo.stats[containerInfo.stats.length-1];
|
||||
var hotMemory = Math.floor((cur.memory.working_set * 100.0) / memory_limit);
|
||||
var totalMemory = Math.floor((cur.memory.usage * 100.0) / memory_limit);
|
||||
var coldMemory = totalMemory - hotMemory;
|
||||
$("#progress-hot-memory").width(hotMemory + "%");
|
||||
$("#progress-cold-memory").width(coldMemory + "%");
|
||||
$("#memory-text").text(humanizeIEC(cur.memory.usage) + " / " + humanizeIEC(memory_limit) + " ("+ totalMemory +"%)");
|
||||
|
||||
drawLineChart(titles, data, elementId, "Megabytes");
|
||||
}
|
||||
|
||||
// Get the index of the interface with the specified name.
|
||||
function getNetworkInterfaceIndex(interfaceName, interfaces) {
|
||||
for (var i = 0; i < interfaces.length; i++) {
|
||||
if (interfaces[i].name == interfaceName) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Draw the graph for network tx/rx bytes.
|
||||
function drawNetworkBytes(elementId, machineInfo, stats) {
|
||||
if (stats.spec.has_network && !hasResource(stats, "network")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get interface index.
|
||||
var interfaceIndex = -1;
|
||||
if (stats.stats.length > 0) {
|
||||
interfaceIndex = getNetworkInterfaceIndex(window.cadvisor.network.interface, stats.stats[0].network.interfaces);
|
||||
}
|
||||
if (interfaceIndex < 0) {
|
||||
console.log("Unable to find interface\"", interfaceName, "\" in ", stats.stats.network);
|
||||
return;
|
||||
}
|
||||
|
||||
var titles = ["Time", "Tx bytes", "Rx bytes"];
|
||||
var data = [];
|
||||
for (var i = 1; i < stats.stats.length; i++) {
|
||||
var cur = stats.stats[i];
|
||||
var prev = stats.stats[i - 1];
|
||||
var intervalInSec = getInterval(cur.timestamp, prev.timestamp) / 1000000000;
|
||||
|
||||
var elements = [];
|
||||
elements.push(cur.timestamp);
|
||||
elements.push((cur.network.interfaces[interfaceIndex].tx_bytes - prev.network.interfaces[interfaceIndex].tx_bytes) / intervalInSec);
|
||||
elements.push((cur.network.interfaces[interfaceIndex].rx_bytes - prev.network.interfaces[interfaceIndex].rx_bytes) / intervalInSec);
|
||||
data.push(elements);
|
||||
}
|
||||
drawLineChart(titles, data, elementId, "Bytes per second");
|
||||
}
|
||||
|
||||
// Draw the graph for network errors
|
||||
function drawNetworkErrors(elementId, machineInfo, stats) {
|
||||
if (stats.spec.has_network && !hasResource(stats, "network")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get interface index.
|
||||
var interfaceIndex = -1;
|
||||
if (stats.stats.length > 0) {
|
||||
interfaceIndex = getNetworkInterfaceIndex(window.cadvisor.network.interface, stats.stats[0].network.interfaces);
|
||||
}
|
||||
if (interfaceIndex < 0) {
|
||||
console.log("Unable to find interface\"", interfaceName, "\" in ", stats.stats.network);
|
||||
return;
|
||||
}
|
||||
|
||||
var titles = ["Time", "Tx", "Rx"];
|
||||
var data = [];
|
||||
for (var i = 1; i < stats.stats.length; i++) {
|
||||
var cur = stats.stats[i];
|
||||
var prev = stats.stats[i - 1];
|
||||
var intervalInSec = getInterval(cur.timestamp, prev.timestamp) / 1000000000;
|
||||
|
||||
var elements = [];
|
||||
elements.push(cur.timestamp);
|
||||
elements.push((cur.network.interfaces[interfaceIndex].tx_errors - prev.network.interfaces[interfaceIndex].tx_errors) / intervalInSec);
|
||||
elements.push((cur.network.interfaces[interfaceIndex].rx_errors - prev.network.interfaces[interfaceIndex].rx_errors) / intervalInSec);
|
||||
data.push(elements);
|
||||
}
|
||||
drawLineChart(titles, data, elementId, "Errors per second");
|
||||
}
|
||||
|
||||
// Update the filesystem usage values.
|
||||
function drawFileSystemUsage(machineInfo, stats) {
|
||||
var cur = stats.stats[stats.stats.length - 1];
|
||||
if (!cur.filesystem) {
|
||||
return;
|
||||
}
|
||||
|
||||
var el = $("<div>");
|
||||
for (var i = 0; i < cur.filesystem.length; i++) {
|
||||
var data = cur.filesystem[i];
|
||||
var totalUsage = Math.floor((data.usage * 100.0) / data.capacity);
|
||||
|
||||
// Update DOM elements.
|
||||
var els = window.cadvisor.fsUsage.elements[data.device];
|
||||
els.progressElement.width(totalUsage + "%");
|
||||
els.textElement.text(humanizeMetric(data.usage) + " / " + humanizeMetric(data.capacity)+ " (" + totalUsage + "%)");
|
||||
}
|
||||
}
|
||||
|
||||
function drawImages(images) {
|
||||
if (images == null || images.length == 0) {
|
||||
return;
|
||||
}
|
||||
window.charts = {};
|
||||
var titles = ["Repository", "Tags", "ID", "Virtual Size", "Creation Time"];
|
||||
var titleTypes = ['string', 'string', 'string', 'number', 'number'];
|
||||
var sortIndex = 0;
|
||||
var data = [];
|
||||
for (var i = 0; i < images.length; i++) {
|
||||
var elements = [];
|
||||
var tags = [];
|
||||
var repos = images[i].repo_tags[0].split(":");
|
||||
repos.splice(-1,1)
|
||||
for (var j = 0; j < images[i].repo_tags.length; j++) {
|
||||
var splits = images[i].repo_tags[j].split(":")
|
||||
if (splits.length > 1) {
|
||||
tags.push(splits[splits.length - 1])
|
||||
}
|
||||
}
|
||||
elements.push(repos.join(":"));
|
||||
elements.push(tags.join(", "));
|
||||
elements.push(images[i].id.substr(0,24));
|
||||
elements.push({v: images[i].virtual_size, f: humanizeIEC(images[i].virtual_size)});
|
||||
var d = new Date(images[i].created * 1000);
|
||||
elements.push({v: images[i].created, f: d.toLocaleString()});
|
||||
data.push(elements);
|
||||
}
|
||||
drawTable(titles, titleTypes, data, "docker-images", 30, sortIndex);
|
||||
}
|
||||
|
||||
function drawProcesses(isRoot, rootDir, processInfo) {
|
||||
if (processInfo.length == 0) {
|
||||
$("#processes-top").text("No processes found");
|
||||
return;
|
||||
}
|
||||
var titles = ["User", "PID", "PPID", "Start Time", "CPU %", "MEM %", "RSS", "Virtual Size", "Status", "Running Time", "Command"];
|
||||
var titleTypes = ['string', 'number', 'number', 'string', 'number', 'number', 'number', 'number', 'string', 'string', 'string'];
|
||||
var sortIndex = 4
|
||||
if (isRoot) {
|
||||
titles.push("Container");
|
||||
titleTypes.push('string');
|
||||
}
|
||||
var data = []
|
||||
for (var i = 0; i < processInfo.length; i++) {
|
||||
var elements = [];
|
||||
elements.push(processInfo[i].user);
|
||||
elements.push(processInfo[i].pid);
|
||||
elements.push(processInfo[i].parent_pid);
|
||||
elements.push(processInfo[i].start_time);
|
||||
elements.push({ v:processInfo[i].percent_cpu, f:processInfo[i].percent_cpu.toFixed(2)});
|
||||
elements.push({ v:processInfo[i].percent_mem, f:processInfo[i].percent_mem.toFixed(2)});
|
||||
elements.push({ v:processInfo[i].rss, f:humanizeIEC(processInfo[i].rss)});
|
||||
elements.push({ v:processInfo[i].virtual_size, f:humanizeIEC(processInfo[i].virtual_size)});
|
||||
elements.push(processInfo[i].status);
|
||||
elements.push(processInfo[i].running_time);
|
||||
elements.push(processInfo[i].cmd);
|
||||
if (isRoot) {
|
||||
var cgroup = processInfo[i].cgroup_path
|
||||
// Use the raw cgroup link as it works for all containers.
|
||||
var cgroupLink = '<a href="' + rootDir + 'containers/' + cgroup +'">' + cgroup.substr(0,30) + ' </a>';
|
||||
elements.push({v:cgroup, f:cgroupLink});
|
||||
}
|
||||
data.push(elements);
|
||||
}
|
||||
drawTable(titles, titleTypes, data, "processes-top", 25, sortIndex);
|
||||
}
|
||||
|
||||
// Draw the filesystem usage nodes.
|
||||
function startFileSystemUsage(elementId, machineInfo, stats) {
|
||||
window.cadvisor.fsUsage = {};
|
||||
|
||||
// A map of device name to DOM elements.
|
||||
window.cadvisor.fsUsage.elements = {};
|
||||
|
||||
var cur = stats.stats[stats.stats.length - 1];
|
||||
var el = $("<div>");
|
||||
if (!cur.filesystem) {
|
||||
return;
|
||||
}
|
||||
for (var i = 0; i < cur.filesystem.length; i++) {
|
||||
var data = cur.filesystem[i];
|
||||
el.append($("<div>")
|
||||
.addClass("row col-sm-12")
|
||||
.append($("<h4>")
|
||||
.text("FS #" + (i + 1) + ": " + data.device)));
|
||||
|
||||
var progressElement = $("<div>").addClass("progress-bar progress-bar-danger");
|
||||
el.append($("<div>")
|
||||
.addClass("col-sm-9")
|
||||
.append($("<div>")
|
||||
.addClass("progress")
|
||||
.append(progressElement)));
|
||||
|
||||
var textElement = $("<div>").addClass("col-sm-3");
|
||||
el.append(textElement);
|
||||
|
||||
window.cadvisor.fsUsage.elements[data.device] = {
|
||||
'progressElement': progressElement,
|
||||
'textElement': textElement,
|
||||
'index': i,
|
||||
};
|
||||
}
|
||||
$("#" + elementId).empty().append(el);
|
||||
|
||||
drawFileSystemUsage(machineInfo, stats);
|
||||
}
|
||||
|
||||
// Expects an array of closures to call. After each execution the JS runtime is given control back before continuing.
|
||||
// This function returns asynchronously
|
||||
function stepExecute(steps) {
|
||||
// No steps, stop.
|
||||
if (steps.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get a step and execute it.
|
||||
var step = steps.shift();
|
||||
step();
|
||||
|
||||
// Schedule the next step.
|
||||
setTimeout(function() {
|
||||
stepExecute(steps);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
// Draw all the charts on the page.
|
||||
function drawCharts(machineInfo, containerInfo) {
|
||||
var steps = [];
|
||||
|
||||
if (containerInfo.spec.has_cpu || containerInfo.spec.has_memory) {
|
||||
steps.push(function() {
|
||||
drawOverallUsage("usage-gauge", machineInfo, containerInfo)
|
||||
});
|
||||
}
|
||||
|
||||
// CPU.
|
||||
if (containerInfo.spec.has_cpu) {
|
||||
steps.push(function() {
|
||||
drawCpuTotalUsage("cpu-total-usage-chart", machineInfo, containerInfo);
|
||||
});
|
||||
// TODO(rjnagal): Re-enable CPU Load after understanding resource usage.
|
||||
// steps.push(function() {
|
||||
// drawCpuLoad("cpu-load-chart", machineInfo, containerInfo);
|
||||
// });
|
||||
steps.push(function() {
|
||||
drawCpuPerCoreUsage("cpu-per-core-usage-chart", machineInfo, containerInfo);
|
||||
});
|
||||
steps.push(function() {
|
||||
drawCpuUsageBreakdown("cpu-usage-breakdown-chart", machineInfo, containerInfo);
|
||||
});
|
||||
}
|
||||
|
||||
// Memory.
|
||||
if (containerInfo.spec.has_memory) {
|
||||
steps.push(function() {
|
||||
drawMemoryUsage("memory-usage-chart", machineInfo, containerInfo);
|
||||
});
|
||||
}
|
||||
|
||||
// Network.
|
||||
if (containerInfo.spec.has_network) {
|
||||
steps.push(function() {
|
||||
drawNetworkBytes("network-bytes-chart", machineInfo, containerInfo);
|
||||
});
|
||||
steps.push(function() {
|
||||
drawNetworkErrors("network-errors-chart", machineInfo, containerInfo);
|
||||
});
|
||||
}
|
||||
|
||||
// Filesystem.
|
||||
if (containerInfo.spec.has_filesystem) {
|
||||
steps.push(function() {
|
||||
drawFileSystemUsage(machineInfo, containerInfo);
|
||||
});
|
||||
}
|
||||
|
||||
// Custom Metrics
|
||||
if (containerInfo.spec.has_custom_metrics) {
|
||||
steps.push(function() {
|
||||
getCustomMetrics(window.cadvisor.rootDir, window.cadvisor.containerName, function(metricsInfo) {
|
||||
drawCustomMetrics("custom-metrics-chart", containerInfo, metricsInfo)
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
stepExecute(steps);
|
||||
}
|
||||
|
||||
function setNetwork(interfaceName) {
|
||||
$("#network-selection-text")
|
||||
.empty()
|
||||
.append($("<span>").text("Interface: "))
|
||||
.append($("<b>").text(interfaceName));
|
||||
window.cadvisor.network.interface = interfaceName;
|
||||
|
||||
// Draw the new stats.
|
||||
refreshStats();
|
||||
}
|
||||
|
||||
// Creates the network selection dropdown.
|
||||
function startNetwork(selectionElement, containerInfo) {
|
||||
if (!hasResource(containerInfo, "network") || containerInfo.stats.length == 0
|
||||
|| !containerInfo.stats[0].network.interfaces || containerInfo.stats[0].network.interfaces.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
window.cadvisor.network = {};
|
||||
window.cadvisor.network.interface = "";
|
||||
|
||||
// Add all interfaces to the dropdown.
|
||||
var el = $("#" + selectionElement);
|
||||
for (var i = 0; i < containerInfo.stats[0].network.interfaces.length; i++) {
|
||||
var interfaceName = containerInfo.stats[0].network.interfaces[i].name;
|
||||
el.append($("<li>")
|
||||
.attr("role", "presentation")
|
||||
.append($("<a>")
|
||||
.attr("role", "menuitem")
|
||||
.attr("tabindex", -1)
|
||||
.click(setNetwork.bind(null, interfaceName))
|
||||
.text(interfaceName)));
|
||||
}
|
||||
setNetwork(containerInfo.stats[0].network.interfaces[0].name);
|
||||
}
|
||||
|
||||
// Refresh the stats on the page.
|
||||
function refreshStats() {
|
||||
var machineInfo = window.cadvisor.machineInfo;
|
||||
getStats(window.cadvisor.rootDir, window.cadvisor.containerName, function(containerInfo){
|
||||
if (window.cadvisor.firstRun) {
|
||||
window.cadvisor.firstRun = false;
|
||||
|
||||
if (containerInfo.spec.has_filesystem) {
|
||||
startFileSystemUsage("filesystem-usage", machineInfo, containerInfo);
|
||||
}
|
||||
if (containerInfo.spec.has_network) {
|
||||
startNetwork("network-selection", containerInfo);
|
||||
}
|
||||
if (containerInfo.spec.has_custom_metrics) {
|
||||
startCustomMetrics("custom-metrics-chart", containerInfo);
|
||||
}
|
||||
}
|
||||
drawCharts(machineInfo, containerInfo);
|
||||
});
|
||||
}
|
||||
|
||||
function addAllLabels(containerInfo, metricsInfo) {
|
||||
if (metricsInfo.length == 0) {
|
||||
return;
|
||||
}
|
||||
var metricSpec = containerInfo.spec.custom_metrics;
|
||||
for (var containerName in metricsInfo) {
|
||||
var container = metricsInfo[containerName];
|
||||
for (i=0; i<metricSpec.length; i++) {
|
||||
metricName = metricSpec[i].name;
|
||||
metricLabelVal = container[metricName];
|
||||
firstLabel = true;
|
||||
for (var label in metricLabelVal) {
|
||||
if (label == "") {
|
||||
$('#button-'+metricName).hide();
|
||||
}
|
||||
|
||||
$("#"+metricName+"_labels").append($("<li>")
|
||||
.attr("role", "presentation")
|
||||
.append($("<a>")
|
||||
.attr("role", "menuitem")
|
||||
.click(setLabel.bind(null, metricName, label))
|
||||
.text(label)));
|
||||
if (firstLabel) {
|
||||
firstLabel = false;
|
||||
setLabel(metricName, label);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getMetricIndex(metricName) {
|
||||
for (i = 0; i<window.cadvisor.metricLabelPair.length; ++i) {
|
||||
if (window.cadvisor.metricLabelPair[i][0] == metricName)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
function setLabel(metric, label) {
|
||||
$("#"+metric+"-selection-text")
|
||||
.empty()
|
||||
.append($("<span>").text("Label: "))
|
||||
.append($("<b>").text(label))
|
||||
|
||||
index = getMetricIndex(metric);
|
||||
if (index == -1) {
|
||||
window.cadvisor.metricLabelPair.push([metric, label]);
|
||||
} else {
|
||||
window.cadvisor.metricLabelPair[index][1] = label;
|
||||
}
|
||||
|
||||
refreshStats();
|
||||
}
|
||||
|
||||
function getSelectedLabel(metricName) {
|
||||
index = getMetricIndex(metricName);
|
||||
if (index == -1)
|
||||
return "";
|
||||
return window.cadvisor.metricLabelPair[index][1];
|
||||
}
|
||||
|
||||
function startCustomMetrics(elementId, containerInfo) {
|
||||
var metricSpec = containerInfo.spec.custom_metrics;
|
||||
var metricStats = containerInfo.stats.custom_metrics;
|
||||
var el=$("<div>");
|
||||
|
||||
if (metricSpec.length < window.cadvisor.maxCustomMetrics)
|
||||
window.cadvisor.maxCustomMetrics = metricSpec.length
|
||||
for (i = 0; i<window.cadvisor.maxCustomMetrics; i++) {
|
||||
metricName = metricSpec[i].name;
|
||||
var divText = "<div class='dropdown'> <button class='btn btn-default dropdown-toggle' type='button' id='button-"+metricName;
|
||||
divText += "' data-toggle='dropdown' aria-haspopup='true' aria-expanded='false'>";
|
||||
divText += "<span id='"+metricName+"-selection-text'></span> <span class='caret'></span> </button>";
|
||||
divText += "<ul id='"+metricName+"_labels' class='dropdown-menu' role='menu' aria-labelledby='button-"+metricName+ "'> </ul> </div>";
|
||||
divText += "<div id='"+elementId+"-"+metricName+"'> </div>";
|
||||
el.append($(divText));
|
||||
}
|
||||
el.append($("</div>"));
|
||||
|
||||
$("#"+elementId).append(el);
|
||||
}
|
||||
|
||||
function getCustomMetrics(rootDir, containerName, callback) {
|
||||
$.getJSON(rootDir + "api/v2.0/appmetrics/" + containerName)
|
||||
.done(function(data) {
|
||||
callback(data);
|
||||
})
|
||||
.fail(function(jqhxr, textStatus, error) {
|
||||
callback([]);
|
||||
});
|
||||
}
|
||||
|
||||
function drawCustomMetrics(elementId, containerInfo, metricsInfo) {
|
||||
if(metricsInfo.length == 0) {
|
||||
return;
|
||||
}
|
||||
var metricSpec = containerInfo.spec.custom_metrics;
|
||||
for (var containerName in metricsInfo) {
|
||||
var container = metricsInfo[containerName];
|
||||
for (i=0; i<window.cadvisor.maxCustomMetrics; i++) {
|
||||
metricName = metricSpec[i].name;
|
||||
metricUnits = metricSpec[i].units;
|
||||
var titles = ["Time", metricName];
|
||||
metricLabelVal = container[metricName];
|
||||
if (window.cadvisor.firstCustomCollection) {
|
||||
window.cadvisor.firstCustomCollection = false;
|
||||
addAllLabels(containerInfo, metricsInfo);
|
||||
}
|
||||
var data = [];
|
||||
selectedLabel = getSelectedLabel(metricName);
|
||||
metricVal = metricLabelVal[selectedLabel];
|
||||
for (var index in metricVal) {
|
||||
metric = metricVal[index];
|
||||
var elements = [];
|
||||
for (var attribute in metric) {
|
||||
value = metric[attribute];
|
||||
elements.push(value);
|
||||
}
|
||||
if (elements.length<2) {
|
||||
elements.push(0);
|
||||
}
|
||||
data.push(elements);
|
||||
}
|
||||
drawLineChart(titles, data, elementId+"-"+metricName, metricUnits);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Executed when the page finishes loading.
|
||||
function startPage(containerName, hasCpu, hasMemory, rootDir, isRoot) {
|
||||
// Don't fetch data if we don't have any resource.
|
||||
if (!hasCpu && !hasMemory) {
|
||||
return;
|
||||
}
|
||||
|
||||
window.charts = {};
|
||||
window.cadvisor = {};
|
||||
window.cadvisor.firstRun = true;
|
||||
window.cadvisor.rootDir = rootDir;
|
||||
window.cadvisor.containerName = containerName;
|
||||
|
||||
window.cadvisor.firstCustomCollection = true;
|
||||
window.cadvisor.metricLabelPair = [];
|
||||
window.cadvisor.maxCustomMetrics = 10;
|
||||
|
||||
// Draw process information at start and refresh every 60s.
|
||||
getProcessInfo(rootDir, containerName, function(processInfo) {
|
||||
drawProcesses(isRoot, rootDir, processInfo)
|
||||
});
|
||||
setInterval(function() {
|
||||
getProcessInfo(rootDir, containerName, function(processInfo) {
|
||||
drawProcesses(isRoot, rootDir, processInfo)
|
||||
});
|
||||
}, 60000);
|
||||
|
||||
// Get machine info, then get the stats every 1s.
|
||||
getMachineInfo(rootDir, function(machineInfo) {
|
||||
window.cadvisor.machineInfo = machineInfo;
|
||||
setInterval(function() {
|
||||
refreshStats();
|
||||
}, 1000);
|
||||
});
|
||||
}
|
||||
`
|
1395
vendor/github.com/google/cadvisor/pages/static/gcharts_js.go
generated
vendored
1395
vendor/github.com/google/cadvisor/pages/static/gcharts_js.go
generated
vendored
File diff suppressed because it is too large
Load Diff
57
vendor/github.com/google/cadvisor/pages/static/google_jsapi_js.go
generated
vendored
57
vendor/github.com/google/cadvisor/pages/static/google_jsapi_js.go
generated
vendored
File diff suppressed because one or more lines are too long
24
vendor/github.com/google/cadvisor/pages/static/jquery_min_js.go
generated
vendored
24
vendor/github.com/google/cadvisor/pages/static/jquery_min_js.go
generated
vendored
File diff suppressed because one or more lines are too long
23
vendor/github.com/google/cadvisor/pages/static/static.go
generated
vendored
23
vendor/github.com/google/cadvisor/pages/static/static.go
generated
vendored
@@ -26,14 +26,25 @@ import (
|
||||
|
||||
const StaticResource = "/static/"
|
||||
|
||||
var staticFiles = map[string]string{
|
||||
var bootstrapJs, _ = Asset("pages/assets/js/bootstrap-3.1.1.min.js")
|
||||
var containersJs, _ = Asset("pages/assets/js/containers.js")
|
||||
var gchartsJs, _ = Asset("pages/assets/js/gcharts.js")
|
||||
var googleJsapiJs, _ = Asset("pages/assets/js/google-jsapi.js")
|
||||
var jqueryJs, _ = Asset("pages/assets/js/jquery-1.10.2.min.js")
|
||||
|
||||
var bootstrapCss, _ = Asset("pages/assets/styles/bootstrap-3.1.1.min.css")
|
||||
var bootstrapThemeCss, _ = Asset("pages/assets/styles/bootstrap-theme-3.1.1.min.css")
|
||||
var containersCss, _ = Asset("pages/assets/styles/containers.css")
|
||||
|
||||
var staticFiles = map[string][]byte{
|
||||
"bootstrap-3.1.1.min.css": bootstrapCss,
|
||||
"bootstrap-3.1.1.min.js": bootstrapJs,
|
||||
"bootstrap-theme-3.1.1.min.css": bootstrapThemeCss,
|
||||
"containers.css": containersCss,
|
||||
"containers.js": containersJs,
|
||||
"bootstrap-3.1.1.min.css": bootstrapCss,
|
||||
"bootstrap-theme-3.1.1.min.css": bootstrapThemeCss,
|
||||
"jquery-1.10.2.min.js": jqueryJs,
|
||||
"bootstrap-3.1.1.min.js": bootstrapJs,
|
||||
"gcharts.js": gchartsJs,
|
||||
"google-jsapi.js": googleJsapiJs,
|
||||
"jquery-1.10.2.min.js": jqueryJs,
|
||||
}
|
||||
|
||||
func HandleRequest(w http.ResponseWriter, u *url.URL) error {
|
||||
@@ -54,6 +65,6 @@ func HandleRequest(w http.ResponseWriter, u *url.URL) error {
|
||||
w.Header().Set("Content-Type", contentType)
|
||||
}
|
||||
|
||||
_, err := w.Write([]byte(content))
|
||||
_, err := w.Write(content)
|
||||
return err
|
||||
}
|
||||
|
256
vendor/github.com/google/cadvisor/pages/templates.go
generated
vendored
Normal file
256
vendor/github.com/google/cadvisor/pages/templates.go
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
10
vendor/github.com/google/cadvisor/storage/storage.go
generated
vendored
10
vendor/github.com/google/cadvisor/storage/storage.go
generated
vendored
@@ -16,6 +16,7 @@ package storage
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
info "github.com/google/cadvisor/info/v1"
|
||||
)
|
||||
@@ -47,3 +48,12 @@ func New(name string) (StorageDriver, error) {
|
||||
}
|
||||
return f()
|
||||
}
|
||||
|
||||
func ListDrivers() []string {
|
||||
drivers := make([]string, 0, len(registeredPlugins))
|
||||
for name := range registeredPlugins {
|
||||
drivers = append(drivers, name)
|
||||
}
|
||||
sort.Strings(drivers)
|
||||
return drivers
|
||||
}
|
||||
|
20
vendor/github.com/google/cadvisor/utils/cloudinfo/aws.go
generated
vendored
20
vendor/github.com/google/cadvisor/utils/cloudinfo/aws.go
generated
vendored
@@ -15,24 +15,26 @@
|
||||
package cloudinfo
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/ec2metadata"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
|
||||
info "github.com/google/cadvisor/info/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
ProductVerFileName = "/sys/class/dmi/id/product_version"
|
||||
Amazon = "amazon"
|
||||
)
|
||||
|
||||
func onAWS() bool {
|
||||
// the default client behavior retried the operation multiple times with a 5s timeout per attempt.
|
||||
// if you were not on aws, you would block for 20s when invoking this operation.
|
||||
// we reduce retries to 0 and set the timeout to 2s to reduce the time this blocks when not on aws.
|
||||
client := ec2metadata.New(session.New(&aws.Config{MaxRetries: aws.Int(0)}))
|
||||
if client.Config.HTTPClient != nil {
|
||||
client.Config.HTTPClient.Timeout = time.Duration(2 * time.Second)
|
||||
data, err := ioutil.ReadFile(ProductVerFileName)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return client.Available()
|
||||
return strings.Contains(string(data), Amazon)
|
||||
}
|
||||
|
||||
func getAwsMetadata(name string) string {
|
||||
|
2
vendor/github.com/google/cadvisor/utils/cpuload/cpuload.go
generated
vendored
2
vendor/github.com/google/cadvisor/utils/cpuload/cpuload.go
generated
vendored
@@ -41,6 +41,6 @@ func New() (CpuLoadReader, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create a netlink based cpuload reader: %v", err)
|
||||
}
|
||||
glog.Info("Using a netlink-based load reader")
|
||||
glog.V(3).Info("Using a netlink-based load reader")
|
||||
return reader, nil
|
||||
}
|
||||
|
58
vendor/github.com/google/cadvisor/utils/docker/docker.go
generated
vendored
Normal file
58
vendor/github.com/google/cadvisor/utils/docker/docker.go
generated
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
// 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 docker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
dockertypes "github.com/docker/engine-api/types"
|
||||
)
|
||||
|
||||
const (
|
||||
DockerInfoDriver = "Driver"
|
||||
DockerInfoDriverStatus = "DriverStatus"
|
||||
DriverStatusPoolName = "Pool Name"
|
||||
DriverStatusDataLoopFile = "Data loop file"
|
||||
DriverStatusMetadataFile = "Metadata file"
|
||||
)
|
||||
|
||||
func DriverStatusValue(status [][2]string, target string) string {
|
||||
for _, v := range status {
|
||||
if strings.EqualFold(v[0], target) {
|
||||
return v[1]
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func DockerThinPoolName(info dockertypes.Info) (string, error) {
|
||||
poolName := DriverStatusValue(info.DriverStatus, DriverStatusPoolName)
|
||||
if len(poolName) == 0 {
|
||||
return "", fmt.Errorf("Could not get devicemapper pool name")
|
||||
}
|
||||
|
||||
return poolName, nil
|
||||
}
|
||||
|
||||
func DockerMetadataDevice(info dockertypes.Info) (string, error) {
|
||||
metadataDevice := DriverStatusValue(info.DriverStatus, DriverStatusMetadataFile)
|
||||
if len(metadataDevice) == 0 {
|
||||
return "", fmt.Errorf("Could not get the devicemapper metadata device")
|
||||
}
|
||||
|
||||
return metadataDevice, nil
|
||||
}
|
71
vendor/github.com/google/cadvisor/utils/oomparser/oomparser.go
generated
vendored
71
vendor/github.com/google/cadvisor/utils/oomparser/oomparser.go
generated
vendored
@@ -18,7 +18,6 @@ import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"regexp"
|
||||
@@ -26,6 +25,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/google/cadvisor/utils"
|
||||
"github.com/google/cadvisor/utils/tail"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
@@ -105,27 +105,29 @@ func checkIfStartOfOomMessages(line string) bool {
|
||||
// Should prevent EOF errors that occur when lines are read before being fully
|
||||
// written to the log. It reads line by line splitting on
|
||||
// the "\n" character.
|
||||
func readLinesFromFile(lineChannel chan string, ioreader *bufio.Reader) {
|
||||
func readLinesFromFile(lineChannel chan string, ioreader *bufio.Reader) error {
|
||||
linefragment := ""
|
||||
var line string
|
||||
var err error
|
||||
for true {
|
||||
line, err = ioreader.ReadString('\n')
|
||||
if err == io.EOF {
|
||||
if line != "" {
|
||||
linefragment += line
|
||||
}
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
} else if err == nil {
|
||||
if linefragment != "" {
|
||||
line = linefragment + line
|
||||
linefragment = ""
|
||||
}
|
||||
lineChannel <- line
|
||||
} else if err != nil && err != io.EOF {
|
||||
if err != nil && err != io.EOF {
|
||||
glog.Errorf("exiting analyzeLinesHelper with error %v", err)
|
||||
close(lineChannel)
|
||||
break
|
||||
}
|
||||
if line == "" {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
continue
|
||||
}
|
||||
if err == nil {
|
||||
lineChannel <- linefragment + line
|
||||
linefragment = ""
|
||||
} else { // err == io.EOF
|
||||
linefragment += line
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Calls goroutine for readLinesFromFile, which feeds it complete lines.
|
||||
@@ -144,22 +146,23 @@ func (self *OomParser) StreamOoms(outStream chan *OomInstance) {
|
||||
oomCurrentInstance := &OomInstance{
|
||||
ContainerName: "/",
|
||||
}
|
||||
finished := false
|
||||
for !finished {
|
||||
for line := range lineChannel {
|
||||
err := getContainerName(line, oomCurrentInstance)
|
||||
if err != nil {
|
||||
glog.Errorf("%v", err)
|
||||
}
|
||||
finished, err = getProcessNamePid(line, oomCurrentInstance)
|
||||
finished, err := getProcessNamePid(line, oomCurrentInstance)
|
||||
if err != nil {
|
||||
glog.Errorf("%v", err)
|
||||
}
|
||||
line = <-lineChannel
|
||||
if finished {
|
||||
break
|
||||
}
|
||||
}
|
||||
outStream <- oomCurrentInstance
|
||||
}
|
||||
}
|
||||
glog.Infof("exiting analyzeLines")
|
||||
glog.Infof("exiting analyzeLines. OOM events will not be reported.")
|
||||
}
|
||||
|
||||
func callJournalctl() (io.ReadCloser, error) {
|
||||
@@ -183,7 +186,6 @@ func trySystemd() (*OomParser, error) {
|
||||
return &OomParser{
|
||||
ioreader: bufio.NewReader(readcloser),
|
||||
}, nil
|
||||
|
||||
}
|
||||
|
||||
// List of possible kernel log files. These are prioritized in order so that
|
||||
@@ -192,7 +194,7 @@ var kernelLogFiles = []string{"/var/log/kern.log", "/var/log/messages", "/var/lo
|
||||
|
||||
// looks for system files that contain kernel messages and if one is found, sets
|
||||
// the systemFile attribute of the OomParser object
|
||||
func getSystemFile() (string, error) {
|
||||
func getLogFile() (string, error) {
|
||||
for _, logFile := range kernelLogFiles {
|
||||
if utils.FileExists(logFile) {
|
||||
glog.Infof("OOM parser using kernel log file: %q", logFile)
|
||||
@@ -202,18 +204,29 @@ func getSystemFile() (string, error) {
|
||||
return "", fmt.Errorf("unable to find any kernel log file available from our set: %v", kernelLogFiles)
|
||||
}
|
||||
|
||||
// initializes an OomParser object and calls getSystemFile to set the systemFile
|
||||
// attribute. Returns and OomParser object and an error
|
||||
func New() (*OomParser, error) {
|
||||
systemFile, err := getSystemFile()
|
||||
func tryLogFile() (*OomParser, error) {
|
||||
logFile, err := getLogFile()
|
||||
if err != nil {
|
||||
return trySystemd()
|
||||
return nil, err
|
||||
}
|
||||
file, err := os.Open(systemFile)
|
||||
tail, err := tail.NewTail(logFile)
|
||||
if err != nil {
|
||||
return trySystemd()
|
||||
return nil, err
|
||||
}
|
||||
return &OomParser{
|
||||
ioreader: bufio.NewReader(file),
|
||||
ioreader: bufio.NewReader(tail),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// initializes an OomParser object. Returns an OomParser object and an error.
|
||||
func New() (*OomParser, error) {
|
||||
parser, err := trySystemd()
|
||||
if err == nil {
|
||||
return parser, nil
|
||||
}
|
||||
parser, err = tryLogFile()
|
||||
if err == nil {
|
||||
return parser, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
146
vendor/github.com/google/cadvisor/utils/tail/tail.go
generated
vendored
Normal file
146
vendor/github.com/google/cadvisor/utils/tail/tail.go
generated
vendored
Normal file
@@ -0,0 +1,146 @@
|
||||
// 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 tail implements "tail -F" functionality following rotated logs
|
||||
package tail
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"golang.org/x/exp/inotify"
|
||||
)
|
||||
|
||||
type Tail struct {
|
||||
reader *bufio.Reader
|
||||
readerErr error
|
||||
readerLock sync.RWMutex
|
||||
filename string
|
||||
file *os.File
|
||||
stop chan bool
|
||||
watcher *inotify.Watcher
|
||||
}
|
||||
|
||||
const (
|
||||
defaultRetryInterval = 100 * time.Millisecond
|
||||
maxRetryInterval = 30 * time.Second
|
||||
)
|
||||
|
||||
// NewTail starts opens the given file and watches it for deletion/rotation
|
||||
func NewTail(filename string) (*Tail, error) {
|
||||
t := &Tail{
|
||||
filename: filename,
|
||||
}
|
||||
var err error
|
||||
t.stop = make(chan bool)
|
||||
t.watcher, err = inotify.NewWatcher()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("inotify init failed on %s: %v", t.filename, err)
|
||||
}
|
||||
go t.watchLoop()
|
||||
return t, nil
|
||||
}
|
||||
|
||||
// Read implements the io.Reader interface for Tail
|
||||
func (t *Tail) Read(p []byte) (int, error) {
|
||||
t.readerLock.RLock()
|
||||
defer t.readerLock.RUnlock()
|
||||
if t.reader == nil || t.readerErr != nil {
|
||||
return 0, t.readerErr
|
||||
}
|
||||
return t.reader.Read(p)
|
||||
}
|
||||
|
||||
var _ io.Reader = &Tail{}
|
||||
|
||||
// Close stops watching and closes the file
|
||||
func (t *Tail) Close() {
|
||||
close(t.stop)
|
||||
}
|
||||
|
||||
func (t *Tail) attemptOpen() error {
|
||||
t.readerLock.Lock()
|
||||
defer t.readerLock.Unlock()
|
||||
t.reader = nil
|
||||
t.readerErr = nil
|
||||
attempt := 0
|
||||
for interval := defaultRetryInterval; ; interval *= 2 {
|
||||
attempt++
|
||||
glog.V(4).Infof("Opening %s (attempt %d)", t.filename, attempt)
|
||||
var err error
|
||||
t.file, err = os.Open(t.filename)
|
||||
if err == nil {
|
||||
// TODO: not interested in old events?
|
||||
//t.file.Seek(0, os.SEEK_END)
|
||||
t.reader = bufio.NewReader(t.file)
|
||||
return nil
|
||||
}
|
||||
if interval >= maxRetryInterval {
|
||||
break
|
||||
}
|
||||
select {
|
||||
case <-time.After(interval):
|
||||
case <-t.stop:
|
||||
t.readerErr = io.EOF
|
||||
return fmt.Errorf("watch was cancelled")
|
||||
}
|
||||
}
|
||||
err := fmt.Errorf("can't open log file %s", t.filename)
|
||||
t.readerErr = err
|
||||
return err
|
||||
}
|
||||
|
||||
func (t *Tail) watchLoop() {
|
||||
for {
|
||||
err := t.watchFile()
|
||||
if err != nil {
|
||||
glog.Errorf("Tail failed on %s: %v", t.filename, err)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Tail) watchFile() error {
|
||||
err := t.attemptOpen()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer t.file.Close()
|
||||
|
||||
watchDir := filepath.Dir(t.filename)
|
||||
err = t.watcher.AddWatch(watchDir, inotify.IN_MOVED_FROM|inotify.IN_DELETE)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to add watch to directory %s: %v", watchDir, err)
|
||||
}
|
||||
defer t.watcher.RemoveWatch(watchDir)
|
||||
|
||||
for {
|
||||
select {
|
||||
case event := <-t.watcher.Event:
|
||||
eventPath := filepath.Clean(event.Name) // Directory events have an extra '/'
|
||||
if eventPath == t.filename {
|
||||
glog.V(4).Infof("Log file %s moved/deleted", t.filename)
|
||||
return nil
|
||||
}
|
||||
case <-t.stop:
|
||||
return fmt.Errorf("watch was cancelled")
|
||||
}
|
||||
}
|
||||
}
|
31
vendor/github.com/google/cadvisor/utils/timed_store.go
generated
vendored
31
vendor/github.com/google/cadvisor/utils/timed_store.go
generated
vendored
@@ -58,19 +58,23 @@ func NewTimedStore(age time.Duration, maxItems int) *TimedStore {
|
||||
|
||||
// Adds an element to the start of the buffer (removing one from the end if necessary).
|
||||
func (self *TimedStore) Add(timestamp time.Time, item interface{}) {
|
||||
// Remove any elements if over our max size.
|
||||
if self.maxItems >= 0 && (len(self.buffer)+1) > self.maxItems {
|
||||
startIndex := len(self.buffer) + 1 - self.maxItems
|
||||
self.buffer = self.buffer[startIndex:]
|
||||
}
|
||||
// Add the new element first and sort. We can then remove an expired element, if required.
|
||||
copied := item
|
||||
self.buffer = append(self.buffer, timedStoreData{
|
||||
data := timedStoreData{
|
||||
timestamp: timestamp,
|
||||
data: copied,
|
||||
})
|
||||
data: item,
|
||||
}
|
||||
// Common case: data is added in order.
|
||||
if len(self.buffer) == 0 || !timestamp.Before(self.buffer[len(self.buffer)-1].timestamp) {
|
||||
self.buffer = append(self.buffer, data)
|
||||
} else {
|
||||
// Data is out of order; insert it in the correct position.
|
||||
index := sort.Search(len(self.buffer), func(index int) bool {
|
||||
return self.buffer[index].timestamp.After(timestamp)
|
||||
})
|
||||
self.buffer = append(self.buffer, timedStoreData{}) // Make room to shift the elements
|
||||
copy(self.buffer[index+1:], self.buffer[index:]) // Shift the elements over
|
||||
self.buffer[index] = data
|
||||
}
|
||||
|
||||
sort.Sort(self.buffer)
|
||||
// Remove any elements before eviction time.
|
||||
// TODO(rjnagal): This is assuming that the added entry has timestamp close to now.
|
||||
evictTime := timestamp.Add(-self.age)
|
||||
@@ -81,6 +85,11 @@ func (self *TimedStore) Add(timestamp time.Time, item interface{}) {
|
||||
self.buffer = self.buffer[index:]
|
||||
}
|
||||
|
||||
// Remove any elements if over our max size.
|
||||
if self.maxItems >= 0 && len(self.buffer) > self.maxItems {
|
||||
startIndex := len(self.buffer) - self.maxItems
|
||||
self.buffer = self.buffer[startIndex:]
|
||||
}
|
||||
}
|
||||
|
||||
// Returns up to maxResult elements in the specified time period (inclusive).
|
||||
|
6
vendor/github.com/google/cadvisor/validate/validate.go
generated
vendored
6
vendor/github.com/google/cadvisor/validate/validate.go
generated
vendored
@@ -191,12 +191,6 @@ func validateDockerInfo() (string, string) {
|
||||
}
|
||||
|
||||
desc := fmt.Sprintf("Docker exec driver is %s. Storage driver is %s.\n", info.ExecutionDriver, info.Driver)
|
||||
stateFile := docker.DockerStateDir()
|
||||
if !utils.FileExists(stateFile) {
|
||||
desc += fmt.Sprintf("\tDocker container state directory %q is not accessible.\n", stateFile)
|
||||
return Unsupported, desc
|
||||
}
|
||||
desc += fmt.Sprintf("\tDocker container state directory is at %q and is accessible.\n", stateFile)
|
||||
return Recommended, desc
|
||||
}
|
||||
|
||||
|
2
vendor/github.com/google/cadvisor/version/VERSION
generated
vendored
2
vendor/github.com/google/cadvisor/version/VERSION
generated
vendored
@@ -1 +1 @@
|
||||
0.23.0
|
||||
0.23.2
|
||||
|
Reference in New Issue
Block a user