drop things from vendor/ directory

Signed-off-by: Davanum Srinivas <davanum@gmail.com>
This commit is contained in:
Davanum Srinivas
2021-12-07 14:47:35 -05:00
parent bc78dff42e
commit 420145e529
273 changed files with 0 additions and 35569 deletions

View File

@@ -1,61 +0,0 @@
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Handler for /validate content.
// Validates cadvisor dependencies - kernel, os, docker setup.
package docker
import (
"net/http"
"sync"
dclient "github.com/docker/docker/client"
"github.com/docker/go-connections/tlsconfig"
)
var (
dockerClient *dclient.Client
dockerClientErr error
dockerClientOnce sync.Once
)
// Client creates a Docker API client based on the given Docker flags
func Client() (*dclient.Client, error) {
dockerClientOnce.Do(func() {
var client *http.Client
if *ArgDockerTLS {
client = &http.Client{}
options := tlsconfig.Options{
CAFile: *ArgDockerCA,
CertFile: *ArgDockerCert,
KeyFile: *ArgDockerKey,
InsecureSkipVerify: false,
}
tlsc, err := tlsconfig.Client(options)
if err != nil {
dockerClientErr = err
return
}
client.Transport = &http.Transport{
TLSClientConfig: tlsc,
}
}
dockerClient, dockerClientErr = dclient.NewClientWithOpts(
dclient.WithHost(*ArgDockerEndpoint),
dclient.WithHTTPClient(client),
dclient.WithAPIVersionNegotiation())
})
return dockerClient, dockerClientErr
}

View File

@@ -1,200 +0,0 @@
// 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"
"regexp"
"strconv"
dockertypes "github.com/docker/docker/api/types"
"golang.org/x/net/context"
"time"
v1 "github.com/google/cadvisor/info/v1"
"github.com/google/cadvisor/machine"
)
var dockerTimeout = 10 * time.Second
func defaultContext() context.Context {
ctx, _ := context.WithTimeout(context.Background(), dockerTimeout)
return ctx
}
func SetTimeout(timeout time.Duration) {
dockerTimeout = timeout
}
func Status() (v1.DockerStatus, error) {
return StatusWithContext(defaultContext())
}
func StatusWithContext(ctx context.Context) (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(ctx)
if err != nil {
return v1.DockerStatus{}, err
}
return StatusFromDockerInfo(dockerInfo)
}
func StatusFromDockerInfo(dockerInfo dockertypes.Info) (v1.DockerStatus, error) {
out := v1.DockerStatus{}
out.KernelVersion = machine.KernelVersion()
out.OS = dockerInfo.OperatingSystem
out.Hostname = dockerInfo.Name
out.RootDir = dockerInfo.DockerRootDir
out.Driver = dockerInfo.Driver
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]
}
var err error
ver, err := VersionString()
if err != nil {
return out, err
}
out.Version = ver
ver, err = APIVersionString()
if err != nil {
return out, err
}
out.APIVersion = ver
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(defaultContext(), 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(defaultContext())
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(defaultContext())
if err != nil {
return nil, fmt.Errorf("unable to get docker version: %v", err)
}
dockerInfo.ServerVersion = version.Version
}
version, err := parseVersion(dockerInfo.ServerVersion, versionRe, 3)
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)
}
if dockerInfo.Driver == "" {
return nil, fmt.Errorf("failed to find docker storage driver")
}
return &dockerInfo, nil
}
func APIVersion() ([]int, error) {
ver, err := APIVersionString()
if err != nil {
return nil, err
}
return parseVersion(ver, apiVersionRe, 2)
}
func VersionString() (string, error) {
dockerVersion := "Unknown"
client, err := Client()
if err == nil {
version, err := client.ServerVersion(defaultContext())
if err == nil {
dockerVersion = version.Version
}
}
return dockerVersion, err
}
func APIVersionString() (string, error) {
apiVersion := "Unknown"
client, err := Client()
if err == nil {
version, err := client.ServerVersion(defaultContext())
if err == nil {
apiVersion = version.APIVersion
}
}
return apiVersion, err
}
func parseVersion(versionString string, regex *regexp.Regexp, length int) ([]int, error) {
matches := regex.FindAllStringSubmatch(versionString, -1)
if len(matches) != 1 {
return nil, fmt.Errorf("version string \"%v\" doesn't match expected regular expression: \"%v\"", versionString, regex.String())
}
versionStringArray := matches[0][1:]
versionArray := make([]int, length)
for index, versionStr := range versionStringArray {
version, err := strconv.Atoi(versionStr)
if err != nil {
return nil, fmt.Errorf("error while parsing \"%v\" in \"%v\"", versionStr, versionString)
}
versionArray[index] = version
}
return versionArray, nil
}

View File

@@ -1,396 +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 docker
import (
"flag"
"fmt"
"path"
"regexp"
"strconv"
"strings"
"sync"
"time"
"github.com/blang/semver"
dockertypes "github.com/docker/docker/api/types"
"github.com/google/cadvisor/container"
dockerutil "github.com/google/cadvisor/container/docker/utils"
"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/machine"
"github.com/google/cadvisor/watcher"
"github.com/google/cadvisor/zfs"
docker "github.com/docker/docker/client"
"golang.org/x/net/context"
"k8s.io/klog/v2"
)
var ArgDockerEndpoint = flag.String("docker", "unix:///var/run/docker.sock", "docker endpoint")
var ArgDockerTLS = flag.Bool("docker-tls", false, "use TLS to connect to docker")
var ArgDockerCert = flag.String("docker-tls-cert", "cert.pem", "path to client certificate")
var ArgDockerKey = flag.String("docker-tls-key", "key.pem", "path to private key")
var ArgDockerCA = flag.String("docker-tls-ca", "ca.pem", "path to trusted CA")
var dockerEnvMetadataWhiteList = flag.String("docker_env_metadata_whitelist", "", "DEPRECATED: this flag will be removed, please use `env_metadata_whitelist`. A comma-separated list of environment variable keys matched with specified prefix that needs to be collected for docker containers")
// The namespace under which Docker aliases are unique.
const DockerNamespace = "docker"
// The retry times for getting docker root dir
const rootDirRetries = 5
// The retry period for getting docker root dir, Millisecond
const rootDirRetryPeriod time.Duration = 1000 * time.Millisecond
// Regexp that identifies docker cgroups, containers started with
// --cgroup-parent have another prefix than 'docker'
var dockerCgroupRegexp = regexp.MustCompile(`([a-z0-9]{64})`)
var (
// Basepath to all container specific information that libcontainer stores.
dockerRootDir string
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
// flag that controls globally disabling thin_ls pending future enhancements.
// in production, it has been found that thin_ls makes excessive use of iops.
// in an iops restricted environment, usage of thin_ls must be controlled via blkio.
// pending that enhancement, disable its usage.
disableThinLs = true
)
func RootDir() string {
dockerRootDirOnce.Do(func() {
for i := 0; i < rootDirRetries; i++ {
status, err := Status()
if err == nil && status.RootDir != "" {
dockerRootDir = status.RootDir
break
} else {
time.Sleep(rootDirRetryPeriod)
}
}
if dockerRootDir == "" {
dockerRootDir = *dockerRootDirFlag
}
})
return dockerRootDir
}
type storageDriver string
const (
devicemapperStorageDriver storageDriver = "devicemapper"
aufsStorageDriver storageDriver = "aufs"
overlayStorageDriver storageDriver = "overlay"
overlay2StorageDriver storageDriver = "overlay2"
zfsStorageDriver storageDriver = "zfs"
vfsStorageDriver storageDriver = "vfs"
)
type dockerFactory struct {
machineInfoFactory info.MachineInfoFactory
storageDriver storageDriver
storageDir string
client *docker.Client
// Information about the mounted cgroup subsystems.
cgroupSubsystems map[string]string
// Information about mounted filesystems.
fsInfo fs.FsInfo
dockerVersion []int
dockerAPIVersion []int
includedMetrics container.MetricSet
thinPoolName string
thinPoolWatcher *devicemapper.ThinPoolWatcher
zfsWatcher *zfs.ZfsWatcher
}
func (f *dockerFactory) String() string {
return DockerNamespace
}
func (f *dockerFactory) NewContainerHandler(name string, metadataEnvAllowList []string, inHostNamespace bool) (handler container.ContainerHandler, err error) {
client, err := Client()
if err != nil {
return
}
dockerMetadataEnvAllowList := strings.Split(*dockerEnvMetadataWhiteList, ",")
// prefer using the unified metadataEnvAllowList
if len(metadataEnvAllowList) != 0 {
dockerMetadataEnvAllowList = metadataEnvAllowList
}
handler, err = newDockerContainerHandler(
client,
name,
f.machineInfoFactory,
f.fsInfo,
f.storageDriver,
f.storageDir,
f.cgroupSubsystems,
inHostNamespace,
dockerMetadataEnvAllowList,
f.dockerVersion,
f.includedMetrics,
f.thinPoolName,
f.thinPoolWatcher,
f.zfsWatcher,
)
return
}
// Returns the Docker ID from the full container name.
func ContainerNameToDockerId(name string) string {
id := path.Base(name)
if matches := dockerCgroupRegexp.FindStringSubmatch(id); matches != nil {
return matches[1]
}
return id
}
// isContainerName returns true if the cgroup with associated name
// corresponds to a docker container.
func isContainerName(name string) bool {
// always ignore .mount cgroup even if associated with docker and delegate to systemd
if strings.HasSuffix(name, ".mount") {
return false
}
return dockerCgroupRegexp.MatchString(path.Base(name))
}
// Docker handles all containers under /docker
func (f *dockerFactory) CanHandleAndAccept(name string) (bool, bool, error) {
// if the container is not associated with docker, we can't handle it or accept it.
if !isContainerName(name) {
return false, false, nil
}
// Check if the container is known to docker and it is active.
id := ContainerNameToDockerId(name)
// We assume that if Inspect fails then the container is not known to docker.
ctnr, err := f.client.ContainerInspect(context.Background(), id)
if err != nil || !ctnr.State.Running {
return false, true, fmt.Errorf("error inspecting container: %v", err)
}
return true, true, nil
}
func (f *dockerFactory) DebugInfo() map[string][]string {
return map[string][]string{}
}
var (
versionRegexpString = `(\d+)\.(\d+)\.(\d+)`
versionRe = regexp.MustCompile(versionRegexpString)
apiVersionRegexpString = `(\d+)\.(\d+)`
apiVersionRe = regexp.MustCompile(apiVersionRegexpString)
)
func startThinPoolWatcher(dockerInfo *dockertypes.Info) (*devicemapper.ThinPoolWatcher, error) {
_, err := devicemapper.ThinLsBinaryPresent()
if err != nil {
return nil, err
}
if err := ensureThinLsKernelVersion(machine.KernelVersion()); err != nil {
return nil, err
}
if disableThinLs {
return nil, fmt.Errorf("usage of thin_ls is disabled to preserve iops")
}
dockerThinPoolName, err := dockerutil.DockerThinPoolName(*dockerInfo)
if err != nil {
return nil, err
}
dockerMetadataDevice, err := dockerutil.DockerMetadataDevice(*dockerInfo)
if err != nil {
return nil, err
}
thinPoolWatcher, err := devicemapper.NewThinPoolWatcher(dockerThinPoolName, dockerMetadataDevice)
if err != nil {
return nil, err
}
go thinPoolWatcher.Start()
return thinPoolWatcher, nil
}
func startZfsWatcher(dockerInfo *dockertypes.Info) (*zfs.ZfsWatcher, error) {
filesystem, err := dockerutil.DockerZfsFilesystem(*dockerInfo)
if err != nil {
return nil, err
}
zfsWatcher, err := zfs.NewZfsWatcher(filesystem)
if err != nil {
return nil, err
}
go zfsWatcher.Start()
return zfsWatcher, nil
}
func ensureThinLsKernelVersion(kernelVersion string) error {
// kernel 4.4.0 has the proper bug fixes to allow thin_ls to work without corrupting the thin pool
minKernelVersion := semver.MustParse("4.4.0")
// RHEL 7 kernel 3.10.0 release >= 366 has the proper bug fixes backported from 4.4.0 to allow
// thin_ls to work without corrupting the thin pool
minRhel7KernelVersion := semver.MustParse("3.10.0")
matches := versionRe.FindStringSubmatch(kernelVersion)
if len(matches) < 4 {
return fmt.Errorf("error parsing kernel version: %q is not a semver", kernelVersion)
}
sem, err := semver.Make(matches[0])
if err != nil {
return err
}
if sem.GTE(minKernelVersion) {
// kernel 4.4+ - good
return nil
}
// Certain RHEL/Centos 7.x kernels have a backport to fix the corruption bug
if !strings.Contains(kernelVersion, ".el7") {
// not a RHEL 7.x kernel - won't work
return fmt.Errorf("kernel version 4.4.0 or later is required to use thin_ls - you have %q", kernelVersion)
}
// RHEL/Centos 7.x from here on
if sem.Major != 3 {
// only 3.x kernels *may* work correctly
return fmt.Errorf("RHEL/Centos 7.x kernel version 3.10.0-366 or later is required to use thin_ls - you have %q", kernelVersion)
}
if sem.GT(minRhel7KernelVersion) {
// 3.10.1+ - good
return nil
}
if sem.EQ(minRhel7KernelVersion) {
// need to check release
releaseRE := regexp.MustCompile(`^[^-]+-([0-9]+)\.`)
releaseMatches := releaseRE.FindStringSubmatch(kernelVersion)
if len(releaseMatches) != 2 {
return fmt.Errorf("unable to determine RHEL/Centos 7.x kernel release from %q", kernelVersion)
}
release, err := strconv.Atoi(releaseMatches[1])
if err != nil {
return fmt.Errorf("error parsing release %q: %v", releaseMatches[1], err)
}
if release >= 366 {
return nil
}
}
return fmt.Errorf("RHEL/Centos 7.x kernel version 3.10.0-366 or later is required to use thin_ls - you have %q", kernelVersion)
}
// Register root container before running this function!
func Register(factory info.MachineInfoFactory, fsInfo fs.FsInfo, includedMetrics container.MetricSet) error {
client, err := Client()
if err != nil {
return fmt.Errorf("unable to communicate with docker daemon: %v", err)
}
dockerInfo, err := ValidateInfo()
if err != nil {
return fmt.Errorf("failed to validate Docker info: %v", err)
}
// Version already validated above, assume no error here.
dockerVersion, _ := parseVersion(dockerInfo.ServerVersion, versionRe, 3)
dockerAPIVersion, _ := APIVersion()
cgroupSubsystems, err := libcontainer.GetCgroupSubsystems(includedMetrics)
if err != nil {
return fmt.Errorf("failed to get cgroup subsystems: %v", err)
}
var (
thinPoolWatcher *devicemapper.ThinPoolWatcher
thinPoolName string
zfsWatcher *zfs.ZfsWatcher
)
if includedMetrics.Has(container.DiskUsageMetrics) {
if storageDriver(dockerInfo.Driver) == devicemapperStorageDriver {
thinPoolWatcher, err = startThinPoolWatcher(dockerInfo)
if err != nil {
klog.Errorf("devicemapper filesystem stats will not be reported: %v", err)
}
// Safe to ignore error - driver status should always be populated.
status, _ := StatusFromDockerInfo(*dockerInfo)
thinPoolName = status.DriverStatus[dockerutil.DriverStatusPoolName]
}
if storageDriver(dockerInfo.Driver) == zfsStorageDriver {
zfsWatcher, err = startZfsWatcher(dockerInfo)
if err != nil {
klog.Errorf("zfs filesystem stats will not be reported: %v", err)
}
}
}
klog.V(1).Infof("Registering Docker factory")
f := &dockerFactory{
cgroupSubsystems: cgroupSubsystems,
client: client,
dockerVersion: dockerVersion,
dockerAPIVersion: dockerAPIVersion,
fsInfo: fsInfo,
machineInfoFactory: factory,
storageDriver: storageDriver(dockerInfo.Driver),
storageDir: RootDir(),
includedMetrics: includedMetrics,
thinPoolName: thinPoolName,
thinPoolWatcher: thinPoolWatcher,
zfsWatcher: zfsWatcher,
}
container.RegisterContainerHandlerFactory(f, []watcher.ContainerWatchSource{watcher.Raw})
return nil
}

View File

@@ -1,517 +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.
// Handler for Docker containers.
package docker
import (
"fmt"
"io/ioutil"
"path"
"strconv"
"strings"
"time"
"github.com/google/cadvisor/container"
"github.com/google/cadvisor/container/common"
dockerutil "github.com/google/cadvisor/container/docker/utils"
containerlibcontainer "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/zfs"
"github.com/opencontainers/runc/libcontainer/cgroups"
dockercontainer "github.com/docker/docker/api/types/container"
docker "github.com/docker/docker/client"
"golang.org/x/net/context"
"k8s.io/klog/v2"
)
const (
// The read write layers exist here.
aufsRWLayer = "diff"
overlayRWLayer = "upper"
overlay2RWLayer = "diff"
// Path to the directory where docker stores log files if the json logging driver is enabled.
pathToContainersDir = "containers"
)
type dockerContainerHandler struct {
// machineInfoFactory provides info.MachineInfo
machineInfoFactory info.MachineInfoFactory
// Absolute path to the cgroup hierarchies of this container.
// (e.g.: "cpu" -> "/sys/fs/cgroup/cpu/test")
cgroupPaths map[string]string
// the docker storage driver
storageDriver storageDriver
fsInfo fs.FsInfo
rootfsStorageDir string
// Time at which this container was created.
creationTime time.Time
// Metadata associated with the container.
envs map[string]string
labels map[string]string
// Image name used for this container.
image string
// The network mode of the container
networkMode dockercontainer.NetworkMode
// Filesystem handler.
fsHandler common.FsHandler
// The IP address of the container
ipAddress string
includedMetrics container.MetricSet
// the devicemapper poolname
poolName string
// zfsParent is the parent for docker zfs
zfsParent string
// Reference to the container
reference info.ContainerReference
libcontainerHandler *containerlibcontainer.Handler
}
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.
randomizedRWLayerMinorVersion = 10
rwLayerIDFile = "mount-id"
)
if (dockerVersion[0] <= 1) && (dockerVersion[1] < randomizedRWLayerMinorVersion) {
return containerID, nil
}
bytes, err := ioutil.ReadFile(path.Join(storageDir, "image", string(sd), "layerdb", "mounts", containerID, rwLayerIDFile))
if err != nil {
return "", fmt.Errorf("failed to identify the read-write layer ID for container %q. - %v", containerID, err)
}
return string(bytes), err
}
// newDockerContainerHandler returns a new container.ContainerHandler
func newDockerContainerHandler(
client *docker.Client,
name string,
machineInfoFactory info.MachineInfoFactory,
fsInfo fs.FsInfo,
storageDriver storageDriver,
storageDir string,
cgroupSubsystems map[string]string,
inHostNamespace bool,
metadataEnvAllowList []string,
dockerVersion []int,
includedMetrics container.MetricSet,
thinPoolName string,
thinPoolWatcher *devicemapper.ThinPoolWatcher,
zfsWatcher *zfs.ZfsWatcher,
) (container.ContainerHandler, error) {
// Create the cgroup paths.
cgroupPaths := common.MakeCgroupPaths(cgroupSubsystems, name)
// Generate the equivalent cgroup manager for this container.
cgroupManager, err := containerlibcontainer.NewCgroupManager(name, cgroupPaths)
if err != nil {
return nil, err
}
rootFs := "/"
if !inHostNamespace {
rootFs = "/rootfs"
storageDir = path.Join(rootFs, storageDir)
}
id := ContainerNameToDockerId(name)
// Add the Containers dir where the log files are stored.
// FIXME: Give `otherStorageDir` a more descriptive name.
otherStorageDir := path.Join(storageDir, pathToContainersDir, id)
rwLayerID, err := getRwLayerID(id, storageDir, storageDriver, dockerVersion)
if err != nil {
return nil, err
}
// Determine the rootfs storage dir OR the pool name to determine the device.
// For devicemapper, we only need the thin pool name, and that is passed in to this call
var (
rootfsStorageDir string
zfsFilesystem string
zfsParent string
)
switch storageDriver {
case aufsStorageDriver:
rootfsStorageDir = path.Join(storageDir, string(aufsStorageDriver), aufsRWLayer, rwLayerID)
case overlayStorageDriver:
rootfsStorageDir = path.Join(storageDir, string(storageDriver), rwLayerID, overlayRWLayer)
case overlay2StorageDriver:
rootfsStorageDir = path.Join(storageDir, string(storageDriver), rwLayerID, overlay2RWLayer)
case vfsStorageDriver:
rootfsStorageDir = path.Join(storageDir)
case zfsStorageDriver:
status, err := Status()
if err != nil {
return nil, fmt.Errorf("unable to determine docker status: %v", err)
}
zfsParent = status.DriverStatus[dockerutil.DriverStatusParentDataset]
zfsFilesystem = path.Join(zfsParent, rwLayerID)
}
// We assume that if Inspect fails then the container is not known to docker.
ctnr, err := client.ContainerInspect(context.Background(), id)
if err != nil {
return nil, fmt.Errorf("failed to inspect container %q: %v", id, err)
}
// TODO: extract object mother method
handler := &dockerContainerHandler{
machineInfoFactory: machineInfoFactory,
cgroupPaths: cgroupPaths,
fsInfo: fsInfo,
storageDriver: storageDriver,
poolName: thinPoolName,
rootfsStorageDir: rootfsStorageDir,
envs: make(map[string]string),
labels: ctnr.Config.Labels,
includedMetrics: includedMetrics,
zfsParent: zfsParent,
}
// 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.libcontainerHandler = containerlibcontainer.NewHandler(cgroupManager, rootFs, ctnr.State.Pid, includedMetrics)
// Add the name and bare ID as aliases of the container.
handler.reference = info.ContainerReference{
Id: id,
Name: name,
Aliases: []string{strings.TrimPrefix(ctnr.Name, "/"), id},
Namespace: DockerNamespace,
}
handler.image = ctnr.Config.Image
handler.networkMode = ctnr.HostConfig.NetworkMode
// Only adds restartcount label if it's greater than 0
if ctnr.RestartCount > 0 {
handler.labels["restartcount"] = strconv.Itoa(ctnr.RestartCount)
}
// Obtain the IP address for the container.
// If the NetworkMode starts with 'container:' then we need to use the IP address of the container specified.
// This happens in cases such as kubernetes where the containers doesn't have an IP address itself and we need to use the pod's address
ipAddress := ctnr.NetworkSettings.IPAddress
networkMode := string(ctnr.HostConfig.NetworkMode)
if ipAddress == "" && strings.HasPrefix(networkMode, "container:") {
containerID := strings.TrimPrefix(networkMode, "container:")
c, err := client.ContainerInspect(context.Background(), containerID)
if err != nil {
return nil, fmt.Errorf("failed to inspect container %q: %v", id, err)
}
ipAddress = c.NetworkSettings.IPAddress
}
handler.ipAddress = ipAddress
if includedMetrics.Has(container.DiskUsageMetrics) {
handler.fsHandler = &dockerFsHandler{
fsHandler: common.NewFsHandler(common.DefaultPeriod, rootfsStorageDir, otherStorageDir, fsInfo),
thinPoolWatcher: thinPoolWatcher,
zfsWatcher: zfsWatcher,
deviceID: ctnr.GraphDriver.Data["DeviceId"],
zfsFilesystem: zfsFilesystem,
}
}
// split env vars to get metadata map.
for _, exposedEnv := range metadataEnvAllowList {
if exposedEnv == "" {
// if no dockerEnvWhitelist provided, len(metadataEnvAllowList) == 1, metadataEnvAllowList[0] == ""
continue
}
for _, envVar := range ctnr.Config.Env {
if envVar != "" {
splits := strings.SplitN(envVar, "=", 2)
if len(splits) == 2 && strings.HasPrefix(splits[0], exposedEnv) {
handler.envs[strings.ToLower(splits[0])] = splits[1]
}
}
}
}
return handler, nil
}
// dockerFsHandler is a composite FsHandler implementation the incorporates
// the common fs handler, a devicemapper ThinPoolWatcher, and a zfsWatcher
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
// zfsWatcher is the zfs filesystem watcher
zfsWatcher *zfs.ZfsWatcher
// zfsFilesystem is the docker zfs filesystem
zfsFilesystem string
}
var _ common.FsHandler = &dockerFsHandler{}
func (h *dockerFsHandler) Start() {
h.fsHandler.Start()
}
func (h *dockerFsHandler) Stop() {
h.fsHandler.Stop()
}
func (h *dockerFsHandler) Usage() common.FsUsage {
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 {
// TODO: ideally we should keep track of how many times we failed to get the usage for this
// device vs how many refreshes of the cache there have been, and display an error e.g. if we've
// had at least 1 refresh and we still can't find the device.
klog.V(5).Infof("unable to get fs usage from thin pool for device %s: %v", h.deviceID, err)
} else {
usage.BaseUsageBytes = thinPoolUsage
usage.TotalUsageBytes += thinPoolUsage
}
}
if h.zfsWatcher != nil {
zfsUsage, err := h.zfsWatcher.GetUsage(h.zfsFilesystem)
if err != nil {
klog.V(5).Infof("unable to get fs usage from zfs for filesystem %s: %v", h.zfsFilesystem, err)
} else {
usage.BaseUsageBytes = zfsUsage
usage.TotalUsageBytes += zfsUsage
}
}
return usage
}
func (h *dockerContainerHandler) Start() {
if h.fsHandler != nil {
h.fsHandler.Start()
}
}
func (h *dockerContainerHandler) Cleanup() {
if h.fsHandler != nil {
h.fsHandler.Stop()
}
}
func (h *dockerContainerHandler) ContainerReference() (info.ContainerReference, error) {
return h.reference, nil
}
func (h *dockerContainerHandler) needNet() bool {
if h.includedMetrics.Has(container.NetworkUsageMetrics) {
return !h.networkMode.IsContainer()
}
return false
}
func (h *dockerContainerHandler) GetSpec() (info.ContainerSpec, error) {
hasFilesystem := h.includedMetrics.Has(container.DiskUsageMetrics)
spec, err := common.GetSpec(h.cgroupPaths, h.machineInfoFactory, h.needNet(), hasFilesystem)
spec.Labels = h.labels
spec.Envs = h.envs
spec.Image = h.image
spec.CreationTime = h.creationTime
return spec, err
}
func (h *dockerContainerHandler) getFsStats(stats *info.ContainerStats) error {
mi, err := h.machineInfoFactory.GetMachineInfo()
if err != nil {
return err
}
if h.includedMetrics.Has(container.DiskIOMetrics) {
common.AssignDeviceNamesToDiskStats((*common.MachineInfoNamer)(mi), &stats.DiskIo)
}
if !h.includedMetrics.Has(container.DiskUsageMetrics) {
return nil
}
var device string
switch h.storageDriver {
case devicemapperStorageDriver:
// Device has to be the pool name to correlate with the device name as
// set in the machine info filesystems.
device = h.poolName
case aufsStorageDriver, overlayStorageDriver, overlay2StorageDriver, vfsStorageDriver:
deviceInfo, err := h.fsInfo.GetDirFsDevice(h.rootfsStorageDir)
if err != nil {
return fmt.Errorf("unable to determine device info for dir: %v: %v", h.rootfsStorageDir, err)
}
device = deviceInfo.Device
case zfsStorageDriver:
device = h.zfsParent
default:
return nil
}
var (
limit uint64
fsType string
)
var fsInfo *info.FsInfo
// Docker does not impose any filesystem limits for containers. So use capacity as limit.
for _, fs := range mi.Filesystems {
if fs.Device == device {
limit = fs.Capacity
fsType = fs.Type
fsInfo = &fs
break
}
}
fsStat := info.FsStats{Device: device, Type: fsType, Limit: limit}
usage := h.fsHandler.Usage()
fsStat.BaseUsage = usage.BaseUsageBytes
fsStat.Usage = usage.TotalUsageBytes
fsStat.Inodes = usage.InodeUsage
if fsInfo != nil {
fileSystems, err := h.fsInfo.GetGlobalFsInfo()
if err == nil {
addDiskStats(fileSystems, fsInfo, &fsStat)
} else {
klog.Errorf("Unable to obtain diskstats for filesystem %s: %v", fsStat.Device, err)
}
}
stats.Filesystem = append(stats.Filesystem, fsStat)
return nil
}
func addDiskStats(fileSystems []fs.Fs, fsInfo *info.FsInfo, fsStats *info.FsStats) {
if fsInfo == nil {
return
}
for _, fileSys := range fileSystems {
if fsInfo.DeviceMajor == fileSys.DiskStats.Major &&
fsInfo.DeviceMinor == fileSys.DiskStats.Minor {
fsStats.ReadsCompleted = fileSys.DiskStats.ReadsCompleted
fsStats.ReadsMerged = fileSys.DiskStats.ReadsMerged
fsStats.SectorsRead = fileSys.DiskStats.SectorsRead
fsStats.ReadTime = fileSys.DiskStats.ReadTime
fsStats.WritesCompleted = fileSys.DiskStats.WritesCompleted
fsStats.WritesMerged = fileSys.DiskStats.WritesMerged
fsStats.SectorsWritten = fileSys.DiskStats.SectorsWritten
fsStats.WriteTime = fileSys.DiskStats.WriteTime
fsStats.IoInProgress = fileSys.DiskStats.IoInProgress
fsStats.IoTime = fileSys.DiskStats.IoTime
fsStats.WeightedIoTime = fileSys.DiskStats.WeightedIoTime
break
}
}
}
// TODO(vmarmol): Get from libcontainer API instead of cgroup manager when we don't have to support older Dockers.
func (h *dockerContainerHandler) GetStats() (*info.ContainerStats, error) {
stats, err := h.libcontainerHandler.GetStats()
if err != nil {
return stats, err
}
// Clean up stats for containers that don't have their own network - this
// includes containers running in Kubernetes pods that use the network of the
// infrastructure container. This stops metrics being reported multiple times
// for each container in a pod.
if !h.needNet() {
stats.Network = info.NetworkStats{}
}
// Get filesystem stats.
err = h.getFsStats(stats)
if err != nil {
return stats, err
}
return stats, nil
}
func (h *dockerContainerHandler) ListContainers(listType container.ListType) ([]info.ContainerReference, error) {
// No-op for Docker driver.
return []info.ContainerReference{}, nil
}
func (h *dockerContainerHandler) GetCgroupPath(resource string) (string, error) {
var res string
if !cgroups.IsCgroup2UnifiedMode() {
res = resource
}
path, ok := h.cgroupPaths[res]
if !ok {
return "", fmt.Errorf("could not find path for resource %q for container %q", resource, h.reference.Name)
}
return path, nil
}
func (h *dockerContainerHandler) GetContainerLabels() map[string]string {
return h.labels
}
func (h *dockerContainerHandler) GetContainerIPAddress() string {
return h.ipAddress
}
func (h *dockerContainerHandler) ListProcesses(listType container.ListType) ([]int, error) {
return h.libcontainerHandler.GetProcesses()
}
func (h *dockerContainerHandler) Exists() bool {
return common.CgroupExists(h.cgroupPaths)
}
func (h *dockerContainerHandler) Type() container.ContainerType {
return container.ContainerTypeDocker
}

View File

@@ -1,30 +0,0 @@
// Copyright 2019 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.
// The install package registers docker.NewPlugin() as the "docker" container provider when imported
package install
import (
"k8s.io/klog/v2"
"github.com/google/cadvisor/container"
"github.com/google/cadvisor/container/docker"
)
func init() {
err := container.RegisterPlugin("docker", docker.NewPlugin())
if err != nil {
klog.Fatalf("Failed to register docker plugin: %v", err)
}
}

View File

@@ -1,78 +0,0 @@
// Copyright 2019 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 (
"time"
"golang.org/x/net/context"
"k8s.io/klog/v2"
"github.com/google/cadvisor/container"
"github.com/google/cadvisor/fs"
info "github.com/google/cadvisor/info/v1"
"github.com/google/cadvisor/watcher"
)
const dockerClientTimeout = 10 * time.Second
// NewPlugin returns an implementation of container.Plugin suitable for passing to container.RegisterPlugin()
func NewPlugin() container.Plugin {
return &plugin{}
}
type plugin struct{}
func (p *plugin) InitializeFSContext(context *fs.Context) error {
SetTimeout(dockerClientTimeout)
// Try to connect to docker indefinitely on startup.
dockerStatus := retryDockerStatus()
context.Docker = fs.DockerContext{
Root: RootDir(),
Driver: dockerStatus.Driver,
DriverStatus: dockerStatus.DriverStatus,
}
return nil
}
func (p *plugin) Register(factory info.MachineInfoFactory, fsInfo fs.FsInfo, includedMetrics container.MetricSet) (watcher.ContainerWatcher, error) {
err := Register(factory, fsInfo, includedMetrics)
return nil, err
}
func retryDockerStatus() info.DockerStatus {
startupTimeout := dockerClientTimeout
maxTimeout := 4 * startupTimeout
for {
ctx, _ := context.WithTimeout(context.Background(), startupTimeout)
dockerStatus, err := StatusWithContext(ctx)
if err == nil {
return dockerStatus
}
switch err {
case context.DeadlineExceeded:
klog.Warningf("Timeout trying to communicate with docker during initialization, will retry")
default:
klog.V(5).Infof("Docker not connected: %v", err)
return info.DockerStatus{}
}
startupTimeout = 2 * startupTimeout
if startupTimeout > maxTimeout {
startupTimeout = maxTimeout
}
}
}

View File

@@ -1,77 +0,0 @@
// 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 utils
import (
"fmt"
"os"
"strings"
dockertypes "github.com/docker/docker/api/types"
)
const (
DriverStatusPoolName = "Pool Name"
DriverStatusMetadataFile = "Metadata file"
DriverStatusParentDataset = "Parent Dataset"
)
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 metadataDevice, nil
}
poolName, err := DockerThinPoolName(info)
if err != nil {
return "", err
}
metadataDevice = fmt.Sprintf("/dev/mapper/%s_tmeta", poolName)
if _, err := os.Stat(metadataDevice); err != nil {
return "", err
}
return metadataDevice, nil
}
func DockerZfsFilesystem(info dockertypes.Info) (string, error) {
filesystem := DriverStatusValue(info.DriverStatus, DriverStatusParentDataset)
if len(filesystem) == 0 {
return "", fmt.Errorf("Could not get zfs filesystem")
}
return filesystem, nil
}

View File

@@ -1,113 +0,0 @@
// 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 zfs
import (
"fmt"
"sync"
"time"
zfs "github.com/mistifyio/go-zfs"
"k8s.io/klog/v2"
)
// zfsWatcher maintains a cache of filesystem -> usage stats for a
// zfs filesystem
type ZfsWatcher struct {
filesystem string
lock *sync.RWMutex
cache map[string]uint64
period time.Duration
stopChan chan struct{}
}
// NewThinPoolWatcher returns a new ThinPoolWatcher for the given devicemapper
// thin pool name and metadata device or an error.
func NewZfsWatcher(filesystem string) (*ZfsWatcher, error) {
return &ZfsWatcher{
filesystem: filesystem,
lock: &sync.RWMutex{},
cache: make(map[string]uint64),
period: 15 * time.Second,
stopChan: make(chan struct{}),
}, nil
}
// Start starts the ZfsWatcher.
func (w *ZfsWatcher) Start() {
err := w.Refresh()
if err != nil {
klog.Errorf("encountered error refreshing zfs watcher: %v", err)
}
for {
select {
case <-w.stopChan:
return
case <-time.After(w.period):
start := time.Now()
err = w.Refresh()
if err != nil {
klog.Errorf("encountered error refreshing zfs watcher: %v", err)
}
// print latency for refresh
duration := time.Since(start)
klog.V(5).Infof("zfs(%d) took %s", start.Unix(), duration)
}
}
}
// Stop stops the ZfsWatcher.
func (w *ZfsWatcher) Stop() {
close(w.stopChan)
}
// GetUsage gets the cached usage value of the given filesystem.
func (w *ZfsWatcher) GetUsage(filesystem string) (uint64, error) {
w.lock.RLock()
defer w.lock.RUnlock()
v, ok := w.cache[filesystem]
if !ok {
return 0, fmt.Errorf("no cached value for usage of filesystem %v", filesystem)
}
return v, nil
}
// Refresh performs a zfs get
func (w *ZfsWatcher) Refresh() error {
w.lock.Lock()
defer w.lock.Unlock()
newCache := make(map[string]uint64)
parent, err := zfs.GetDataset(w.filesystem)
if err != nil {
klog.Errorf("encountered error getting zfs filesystem: %s: %v", w.filesystem, err)
return err
}
children, err := parent.Children(0)
if err != nil {
klog.Errorf("encountered error getting children of zfs filesystem: %s: %v", w.filesystem, err)
return err
}
for _, ds := range children {
newCache[ds.Name] = ds.Used
}
w.cache = newCache
return nil
}