mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-14 18:53:35 +00:00
build: move e2e dependencies into e2e/go.mod
Several packages are only used while running the e2e suite. These packages are less important to update, as the they can not influence the final executable that is part of the Ceph-CSI container-image. By moving these dependencies out of the main Ceph-CSI go.mod, it is easier to identify if a reported CVE affects Ceph-CSI, or only the testing (like most of the Kubernetes CVEs). Signed-off-by: Niels de Vos <ndevos@ibm.com>
This commit is contained in:
committed by
mergify[bot]
parent
15da101b1b
commit
bec6090996
60
e2e/vendor/github.com/google/cadvisor/container/common/container_hints.go
generated
vendored
Normal file
60
e2e/vendor/github.com/google/cadvisor/container/common/container_hints.go
generated
vendored
Normal file
@ -0,0 +1,60 @@
|
||||
// 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.
|
||||
|
||||
// Unmarshal's a Containers description json file. The json file contains
|
||||
// an array of ContainerHint structs, each with a container's id and networkInterface
|
||||
// This allows collecting stats about network interfaces configured outside docker
|
||||
// and lxc
|
||||
package common
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"os"
|
||||
)
|
||||
|
||||
var ArgContainerHints = flag.String("container_hints", "/etc/cadvisor/container_hints.json", "location of the container hints file")
|
||||
|
||||
type ContainerHints struct {
|
||||
AllHosts []containerHint `json:"all_hosts,omitempty"`
|
||||
}
|
||||
|
||||
type containerHint struct {
|
||||
FullName string `json:"full_path,omitempty"`
|
||||
NetworkInterface *networkInterface `json:"network_interface,omitempty"`
|
||||
Mounts []Mount `json:"mounts,omitempty"`
|
||||
}
|
||||
|
||||
type Mount struct {
|
||||
HostDir string `json:"host_dir,omitempty"`
|
||||
ContainerDir string `json:"container_dir,omitempty"`
|
||||
}
|
||||
|
||||
type networkInterface struct {
|
||||
VethHost string `json:"veth_host,omitempty"`
|
||||
VethChild string `json:"veth_child,omitempty"`
|
||||
}
|
||||
|
||||
func GetContainerHintsFromFile(containerHintsFile string) (ContainerHints, error) {
|
||||
dat, err := os.ReadFile(containerHintsFile)
|
||||
if os.IsNotExist(err) {
|
||||
return ContainerHints{}, nil
|
||||
}
|
||||
var cHints ContainerHints
|
||||
if err == nil {
|
||||
err = json.Unmarshal(dat, &cHints)
|
||||
}
|
||||
|
||||
return cHints, err
|
||||
}
|
155
e2e/vendor/github.com/google/cadvisor/container/common/fsHandler.go
generated
vendored
Normal file
155
e2e/vendor/github.com/google/cadvisor/container/common/fsHandler.go
generated
vendored
Normal file
@ -0,0 +1,155 @@
|
||||
// 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 Docker containers.
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/google/cadvisor/fs"
|
||||
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
type FsHandler interface {
|
||||
Start()
|
||||
Usage() FsUsage
|
||||
Stop()
|
||||
}
|
||||
|
||||
type FsUsage struct {
|
||||
BaseUsageBytes uint64
|
||||
TotalUsageBytes uint64
|
||||
InodeUsage uint64
|
||||
}
|
||||
|
||||
type realFsHandler struct {
|
||||
sync.RWMutex
|
||||
lastUpdate time.Time
|
||||
usage FsUsage
|
||||
period time.Duration
|
||||
minPeriod time.Duration
|
||||
rootfs string
|
||||
extraDir string
|
||||
fsInfo fs.FsInfo
|
||||
// Tells the container to stop.
|
||||
stopChan chan struct{}
|
||||
}
|
||||
|
||||
const (
|
||||
maxBackoffFactor = 20
|
||||
)
|
||||
|
||||
const DefaultPeriod = time.Minute
|
||||
|
||||
var _ FsHandler = &realFsHandler{}
|
||||
|
||||
func NewFsHandler(period time.Duration, rootfs, extraDir string, fsInfo fs.FsInfo) FsHandler {
|
||||
return &realFsHandler{
|
||||
lastUpdate: time.Time{},
|
||||
usage: FsUsage{},
|
||||
period: period,
|
||||
minPeriod: period,
|
||||
rootfs: rootfs,
|
||||
extraDir: extraDir,
|
||||
fsInfo: fsInfo,
|
||||
stopChan: make(chan struct{}, 1),
|
||||
}
|
||||
}
|
||||
|
||||
func (fh *realFsHandler) update() error {
|
||||
var (
|
||||
rootUsage, extraUsage fs.UsageInfo
|
||||
rootErr, extraErr error
|
||||
)
|
||||
// TODO(vishh): Add support for external mounts.
|
||||
if fh.rootfs != "" {
|
||||
rootUsage, rootErr = fh.fsInfo.GetDirUsage(fh.rootfs)
|
||||
}
|
||||
|
||||
if fh.extraDir != "" {
|
||||
extraUsage, extraErr = fh.fsInfo.GetDirUsage(fh.extraDir)
|
||||
}
|
||||
|
||||
// Wait to handle errors until after all operartions are run.
|
||||
// An error in one will not cause an early return, skipping others
|
||||
fh.Lock()
|
||||
defer fh.Unlock()
|
||||
fh.lastUpdate = time.Now()
|
||||
if fh.rootfs != "" && rootErr == nil {
|
||||
fh.usage.InodeUsage = rootUsage.Inodes
|
||||
fh.usage.BaseUsageBytes = rootUsage.Bytes
|
||||
fh.usage.TotalUsageBytes = rootUsage.Bytes
|
||||
}
|
||||
if fh.extraDir != "" && extraErr == nil {
|
||||
if fh.rootfs != "" {
|
||||
fh.usage.TotalUsageBytes += extraUsage.Bytes
|
||||
} else {
|
||||
// rootfs is empty, totalUsageBytes use extra usage bytes
|
||||
fh.usage.TotalUsageBytes = extraUsage.Bytes
|
||||
}
|
||||
}
|
||||
|
||||
// Combine errors into a single error to return
|
||||
if rootErr != nil || extraErr != nil {
|
||||
return fmt.Errorf("rootDiskErr: %v, extraDiskErr: %v", rootErr, extraErr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fh *realFsHandler) trackUsage() {
|
||||
longOp := time.Second
|
||||
for {
|
||||
start := time.Now()
|
||||
if err := fh.update(); err != nil {
|
||||
klog.Errorf("failed to collect filesystem stats - %v", err)
|
||||
fh.period = fh.period * 2
|
||||
if fh.period > maxBackoffFactor*fh.minPeriod {
|
||||
fh.period = maxBackoffFactor * fh.minPeriod
|
||||
}
|
||||
} else {
|
||||
fh.period = fh.minPeriod
|
||||
}
|
||||
duration := time.Since(start)
|
||||
if duration > longOp {
|
||||
// adapt longOp time so that message doesn't continue to print
|
||||
// if the long duration is persistent either because of slow
|
||||
// disk or lots of containers.
|
||||
longOp = longOp + time.Second
|
||||
klog.V(2).Infof("fs: disk usage and inodes count on following dirs took %v: %v; will not log again for this container unless duration exceeds %v", duration, []string{fh.rootfs, fh.extraDir}, longOp)
|
||||
}
|
||||
select {
|
||||
case <-fh.stopChan:
|
||||
return
|
||||
case <-time.After(fh.period):
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (fh *realFsHandler) Start() {
|
||||
go fh.trackUsage()
|
||||
}
|
||||
|
||||
func (fh *realFsHandler) Stop() {
|
||||
close(fh.stopChan)
|
||||
}
|
||||
|
||||
func (fh *realFsHandler) Usage() FsUsage {
|
||||
fh.RLock()
|
||||
defer fh.RUnlock()
|
||||
return fh.usage
|
||||
}
|
459
e2e/vendor/github.com/google/cadvisor/container/common/helpers.go
generated
vendored
Normal file
459
e2e/vendor/github.com/google/cadvisor/container/common/helpers.go
generated
vendored
Normal file
@ -0,0 +1,459 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/karrick/godirwalk"
|
||||
"github.com/opencontainers/runc/libcontainer/cgroups"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/google/cadvisor/container"
|
||||
info "github.com/google/cadvisor/info/v1"
|
||||
"github.com/google/cadvisor/utils"
|
||||
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
func DebugInfo(watches map[string][]string) map[string][]string {
|
||||
out := make(map[string][]string)
|
||||
|
||||
lines := make([]string, 0, len(watches))
|
||||
for containerName, cgroupWatches := range watches {
|
||||
lines = append(lines, fmt.Sprintf("%s:", containerName))
|
||||
for _, cg := range cgroupWatches {
|
||||
lines = append(lines, fmt.Sprintf("\t%s", cg))
|
||||
}
|
||||
}
|
||||
out["Inotify watches"] = lines
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
var bootTime = func() time.Time {
|
||||
now := time.Now()
|
||||
var sysinfo unix.Sysinfo_t
|
||||
if err := unix.Sysinfo(&sysinfo); err != nil {
|
||||
return now
|
||||
}
|
||||
sinceBoot := time.Duration(sysinfo.Uptime) * time.Second
|
||||
return now.Add(-1 * sinceBoot).Truncate(time.Minute)
|
||||
}()
|
||||
|
||||
func GetSpec(cgroupPaths map[string]string, machineInfoFactory info.MachineInfoFactory, hasNetwork, hasFilesystem bool) (info.ContainerSpec, error) {
|
||||
return getSpecInternal(cgroupPaths, machineInfoFactory, hasNetwork, hasFilesystem, cgroups.IsCgroup2UnifiedMode())
|
||||
}
|
||||
|
||||
func getSpecInternal(cgroupPaths map[string]string, machineInfoFactory info.MachineInfoFactory, hasNetwork, hasFilesystem, cgroup2UnifiedMode bool) (info.ContainerSpec, error) {
|
||||
var spec info.ContainerSpec
|
||||
|
||||
// Assume unified hierarchy containers.
|
||||
// Get the lowest creation time from all hierarchies as the container creation time.
|
||||
now := time.Now()
|
||||
lowestTime := now
|
||||
for _, cgroupPathDir := range cgroupPaths {
|
||||
dir, err := os.Stat(cgroupPathDir)
|
||||
if err == nil && dir.ModTime().Before(lowestTime) {
|
||||
lowestTime = dir.ModTime()
|
||||
} else if os.IsNotExist(err) {
|
||||
// Directory does not exist, skip checking for files within.
|
||||
continue
|
||||
}
|
||||
|
||||
// The modified time of the cgroup directory sometimes changes whenever a subcontainer is created.
|
||||
// eg. /docker will have creation time matching the creation of latest docker container.
|
||||
// Use clone_children/events as a workaround as it isn't usually modified. It is only likely changed
|
||||
// immediately after creating a container. If the directory modified time is lower, we use that.
|
||||
cgroupPathFile := path.Join(cgroupPathDir, "cgroup.clone_children")
|
||||
if cgroup2UnifiedMode {
|
||||
cgroupPathFile = path.Join(cgroupPathDir, "cgroup.events")
|
||||
}
|
||||
fi, err := os.Stat(cgroupPathFile)
|
||||
if err == nil && fi.ModTime().Before(lowestTime) {
|
||||
lowestTime = fi.ModTime()
|
||||
}
|
||||
}
|
||||
if lowestTime.Before(bootTime) {
|
||||
lowestTime = bootTime
|
||||
}
|
||||
|
||||
if lowestTime != now {
|
||||
spec.CreationTime = lowestTime
|
||||
}
|
||||
|
||||
// Get machine info.
|
||||
mi, err := machineInfoFactory.GetMachineInfo()
|
||||
if err != nil {
|
||||
return spec, err
|
||||
}
|
||||
|
||||
// CPU.
|
||||
cpuRoot, ok := GetControllerPath(cgroupPaths, "cpu", cgroup2UnifiedMode)
|
||||
if ok {
|
||||
if utils.FileExists(cpuRoot) {
|
||||
if cgroup2UnifiedMode {
|
||||
spec.HasCpu = true
|
||||
|
||||
weight := readUInt64(cpuRoot, "cpu.weight")
|
||||
if weight > 0 {
|
||||
limit, err := convertCPUWeightToCPULimit(weight)
|
||||
if err != nil {
|
||||
klog.Errorf("GetSpec: Failed to read CPULimit from %q: %s", path.Join(cpuRoot, "cpu.weight"), err)
|
||||
} else {
|
||||
spec.Cpu.Limit = limit
|
||||
}
|
||||
}
|
||||
max := readString(cpuRoot, "cpu.max")
|
||||
if max != "" {
|
||||
splits := strings.SplitN(max, " ", 2)
|
||||
if len(splits) != 2 {
|
||||
klog.Errorf("GetSpec: Failed to parse CPUmax from %q", path.Join(cpuRoot, "cpu.max"))
|
||||
} else {
|
||||
if splits[0] != "max" {
|
||||
spec.Cpu.Quota = parseUint64String(splits[0])
|
||||
}
|
||||
spec.Cpu.Period = parseUint64String(splits[1])
|
||||
}
|
||||
}
|
||||
} else {
|
||||
spec.HasCpu = true
|
||||
spec.Cpu.Limit = readUInt64(cpuRoot, "cpu.shares")
|
||||
spec.Cpu.Period = readUInt64(cpuRoot, "cpu.cfs_period_us")
|
||||
quota := readString(cpuRoot, "cpu.cfs_quota_us")
|
||||
|
||||
if quota != "" && quota != "-1" {
|
||||
val, err := strconv.ParseUint(quota, 10, 64)
|
||||
if err != nil {
|
||||
klog.Errorf("GetSpec: Failed to parse CPUQuota from %q: %s", path.Join(cpuRoot, "cpu.cfs_quota_us"), err)
|
||||
} else {
|
||||
spec.Cpu.Quota = val
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Cpu Mask.
|
||||
// This will fail for non-unified hierarchies. We'll return the whole machine mask in that case.
|
||||
cpusetRoot, ok := GetControllerPath(cgroupPaths, "cpuset", cgroup2UnifiedMode)
|
||||
if ok {
|
||||
if utils.FileExists(cpusetRoot) {
|
||||
spec.HasCpu = true
|
||||
mask := ""
|
||||
if cgroup2UnifiedMode {
|
||||
mask = readString(cpusetRoot, "cpuset.cpus.effective")
|
||||
} else {
|
||||
mask = readString(cpusetRoot, "cpuset.cpus")
|
||||
}
|
||||
spec.Cpu.Mask = utils.FixCpuMask(mask, mi.NumCores)
|
||||
}
|
||||
}
|
||||
|
||||
// Memory
|
||||
memoryRoot, ok := GetControllerPath(cgroupPaths, "memory", cgroup2UnifiedMode)
|
||||
if ok {
|
||||
if cgroup2UnifiedMode {
|
||||
if utils.FileExists(path.Join(memoryRoot, "memory.max")) {
|
||||
spec.HasMemory = true
|
||||
spec.Memory.Reservation = readUInt64(memoryRoot, "memory.min")
|
||||
spec.Memory.Limit = readUInt64(memoryRoot, "memory.max")
|
||||
spec.Memory.SwapLimit = readUInt64(memoryRoot, "memory.swap.max")
|
||||
}
|
||||
} else {
|
||||
if utils.FileExists(memoryRoot) {
|
||||
spec.HasMemory = true
|
||||
spec.Memory.Limit = readUInt64(memoryRoot, "memory.limit_in_bytes")
|
||||
spec.Memory.SwapLimit = readUInt64(memoryRoot, "memory.memsw.limit_in_bytes")
|
||||
spec.Memory.Reservation = readUInt64(memoryRoot, "memory.soft_limit_in_bytes")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Hugepage
|
||||
hugepageRoot, ok := cgroupPaths["hugetlb"]
|
||||
if ok {
|
||||
if utils.FileExists(hugepageRoot) {
|
||||
spec.HasHugetlb = true
|
||||
}
|
||||
}
|
||||
|
||||
// Processes, read it's value from pids path directly
|
||||
pidsRoot, ok := GetControllerPath(cgroupPaths, "pids", cgroup2UnifiedMode)
|
||||
if ok {
|
||||
if utils.FileExists(pidsRoot) {
|
||||
spec.HasProcesses = true
|
||||
spec.Processes.Limit = readUInt64(pidsRoot, "pids.max")
|
||||
}
|
||||
}
|
||||
|
||||
spec.HasNetwork = hasNetwork
|
||||
spec.HasFilesystem = hasFilesystem
|
||||
|
||||
ioControllerName := "blkio"
|
||||
if cgroup2UnifiedMode {
|
||||
ioControllerName = "io"
|
||||
}
|
||||
|
||||
if blkioRoot, ok := GetControllerPath(cgroupPaths, ioControllerName, cgroup2UnifiedMode); ok && utils.FileExists(blkioRoot) {
|
||||
spec.HasDiskIo = true
|
||||
}
|
||||
|
||||
return spec, nil
|
||||
}
|
||||
|
||||
func GetControllerPath(cgroupPaths map[string]string, controllerName string, cgroup2UnifiedMode bool) (string, bool) {
|
||||
|
||||
ok := false
|
||||
path := ""
|
||||
|
||||
if cgroup2UnifiedMode {
|
||||
path, ok = cgroupPaths[""]
|
||||
} else {
|
||||
path, ok = cgroupPaths[controllerName]
|
||||
}
|
||||
return path, ok
|
||||
}
|
||||
|
||||
func readString(dirpath string, file string) string {
|
||||
cgroupFile := path.Join(dirpath, file)
|
||||
|
||||
// Read
|
||||
out, err := os.ReadFile(cgroupFile)
|
||||
if err != nil {
|
||||
// Ignore non-existent files
|
||||
if !os.IsNotExist(err) {
|
||||
klog.Warningf("readString: Failed to read %q: %s", cgroupFile, err)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
return strings.TrimSpace(string(out))
|
||||
}
|
||||
|
||||
// Convert from [1-10000] to [2-262144]
|
||||
func convertCPUWeightToCPULimit(weight uint64) (uint64, error) {
|
||||
const (
|
||||
// minWeight is the lowest value possible for cpu.weight
|
||||
minWeight = 1
|
||||
// maxWeight is the highest value possible for cpu.weight
|
||||
maxWeight = 10000
|
||||
)
|
||||
if weight < minWeight || weight > maxWeight {
|
||||
return 0, fmt.Errorf("convertCPUWeightToCPULimit: invalid cpu weight: %v", weight)
|
||||
}
|
||||
return 2 + ((weight-1)*262142)/9999, nil
|
||||
}
|
||||
|
||||
func parseUint64String(strValue string) uint64 {
|
||||
if strValue == "max" {
|
||||
return math.MaxUint64
|
||||
}
|
||||
if strValue == "" {
|
||||
return 0
|
||||
}
|
||||
|
||||
val, err := strconv.ParseUint(strValue, 10, 64)
|
||||
if err != nil {
|
||||
klog.Errorf("parseUint64String: Failed to parse int %q: %s", strValue, err)
|
||||
return 0
|
||||
}
|
||||
|
||||
return val
|
||||
}
|
||||
|
||||
func readUInt64(dirpath string, file string) uint64 {
|
||||
out := readString(dirpath, file)
|
||||
if out == "max" {
|
||||
return math.MaxUint64
|
||||
}
|
||||
if out == "" {
|
||||
return 0
|
||||
}
|
||||
|
||||
val, err := strconv.ParseUint(out, 10, 64)
|
||||
if err != nil {
|
||||
klog.Errorf("readUInt64: Failed to parse int %q from file %q: %s", out, path.Join(dirpath, file), err)
|
||||
return 0
|
||||
}
|
||||
|
||||
return val
|
||||
}
|
||||
|
||||
// Lists all directories under "path" and outputs the results as children of "parent".
|
||||
func ListDirectories(dirpath string, parent string, recursive bool, output map[string]struct{}) error {
|
||||
buf := make([]byte, godirwalk.MinimumScratchBufferSize)
|
||||
return listDirectories(dirpath, parent, recursive, output, buf)
|
||||
}
|
||||
|
||||
func listDirectories(dirpath string, parent string, recursive bool, output map[string]struct{}, buf []byte) error {
|
||||
dirents, err := godirwalk.ReadDirents(dirpath, buf)
|
||||
if err != nil {
|
||||
// Ignore if this hierarchy does not exist.
|
||||
if os.IsNotExist(errors.Cause(err)) {
|
||||
err = nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
for _, dirent := range dirents {
|
||||
// We only grab directories.
|
||||
if !dirent.IsDir() {
|
||||
continue
|
||||
}
|
||||
dirname := dirent.Name()
|
||||
|
||||
name := path.Join(parent, dirname)
|
||||
output[name] = struct{}{}
|
||||
|
||||
// List subcontainers if asked to.
|
||||
if recursive {
|
||||
err := listDirectories(path.Join(dirpath, dirname), name, true, output, buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func MakeCgroupPaths(mountPoints map[string]string, name string) map[string]string {
|
||||
cgroupPaths := make(map[string]string, len(mountPoints))
|
||||
for key, val := range mountPoints {
|
||||
cgroupPaths[key] = path.Join(val, name)
|
||||
}
|
||||
|
||||
return cgroupPaths
|
||||
}
|
||||
|
||||
func CgroupExists(cgroupPaths map[string]string) bool {
|
||||
// If any cgroup exists, the container is still alive.
|
||||
for _, cgroupPath := range cgroupPaths {
|
||||
if utils.FileExists(cgroupPath) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// AssignDeviceNamesToDiskStats assigns the Device field on the provided DiskIoStats by looking up
|
||||
// the device major and minor identifiers in the provided device namer.
|
||||
func AssignDeviceNamesToDiskStats(namer DeviceNamer, stats *info.DiskIoStats) {
|
||||
assignDeviceNamesToPerDiskStats(
|
||||
namer,
|
||||
stats.IoMerged,
|
||||
stats.IoQueued,
|
||||
stats.IoServiceBytes,
|
||||
stats.IoServiceTime,
|
||||
stats.IoServiced,
|
||||
stats.IoTime,
|
||||
stats.IoWaitTime,
|
||||
stats.Sectors,
|
||||
)
|
||||
}
|
||||
|
||||
// assignDeviceNamesToPerDiskStats looks up device names for the provided stats, caching names
|
||||
// if necessary.
|
||||
func assignDeviceNamesToPerDiskStats(namer DeviceNamer, diskStats ...[]info.PerDiskStats) {
|
||||
devices := make(deviceIdentifierMap)
|
||||
for _, stats := range diskStats {
|
||||
for i, stat := range stats {
|
||||
stats[i].Device = devices.Find(stat.Major, stat.Minor, namer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeviceNamer returns string names for devices by their major and minor id.
|
||||
type DeviceNamer interface {
|
||||
// DeviceName returns the name of the device by its major and minor ids, or false if no
|
||||
// such device is recognized.
|
||||
DeviceName(major, minor uint64) (string, bool)
|
||||
}
|
||||
|
||||
type MachineInfoNamer info.MachineInfo
|
||||
|
||||
func (n *MachineInfoNamer) DeviceName(major, minor uint64) (string, bool) {
|
||||
for _, info := range n.DiskMap {
|
||||
if info.Major == major && info.Minor == minor {
|
||||
return "/dev/" + info.Name, true
|
||||
}
|
||||
}
|
||||
for _, info := range n.Filesystems {
|
||||
if info.DeviceMajor == major && info.DeviceMinor == minor {
|
||||
return info.Device, true
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
type deviceIdentifier struct {
|
||||
major uint64
|
||||
minor uint64
|
||||
}
|
||||
|
||||
type deviceIdentifierMap map[deviceIdentifier]string
|
||||
|
||||
// Find locates the device name by device identifier out of from, caching the result as necessary.
|
||||
func (m deviceIdentifierMap) Find(major, minor uint64, namer DeviceNamer) string {
|
||||
d := deviceIdentifier{major, minor}
|
||||
if s, ok := m[d]; ok {
|
||||
return s
|
||||
}
|
||||
s, _ := namer.DeviceName(major, minor)
|
||||
m[d] = s
|
||||
return s
|
||||
}
|
||||
|
||||
// RemoveNetMetrics is used to remove any network metrics from the given MetricSet.
|
||||
// It returns the original set as is if remove is false, or if there are no metrics
|
||||
// to remove.
|
||||
func RemoveNetMetrics(metrics container.MetricSet, remove bool) container.MetricSet {
|
||||
if !remove {
|
||||
return metrics
|
||||
}
|
||||
|
||||
// Check if there is anything we can remove, to avoid useless copying.
|
||||
if !metrics.HasAny(container.AllNetworkMetrics) {
|
||||
return metrics
|
||||
}
|
||||
|
||||
// A copy of all metrics except for network ones.
|
||||
return metrics.Difference(container.AllNetworkMetrics)
|
||||
}
|
135
e2e/vendor/github.com/google/cadvisor/container/common/inotify_watcher.go
generated
vendored
Normal file
135
e2e/vendor/github.com/google/cadvisor/container/common/inotify_watcher.go
generated
vendored
Normal file
@ -0,0 +1,135 @@
|
||||
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package common
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
inotify "k8s.io/utils/inotify"
|
||||
)
|
||||
|
||||
// Watcher for container-related inotify events in the cgroup hierarchy.
|
||||
//
|
||||
// Implementation is thread-safe.
|
||||
type InotifyWatcher struct {
|
||||
// Underlying inotify watcher.
|
||||
watcher *inotify.Watcher
|
||||
|
||||
// Map of containers being watched to cgroup paths watched for that container.
|
||||
containersWatched map[string]map[string]bool
|
||||
|
||||
// Lock for all datastructure access.
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
func NewInotifyWatcher() (*InotifyWatcher, error) {
|
||||
w, err := inotify.NewWatcher()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &InotifyWatcher{
|
||||
watcher: w,
|
||||
containersWatched: make(map[string]map[string]bool),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Add a watch to the specified directory. Returns if the container was already being watched.
|
||||
func (iw *InotifyWatcher) AddWatch(containerName, dir string) (bool, error) {
|
||||
iw.lock.Lock()
|
||||
defer iw.lock.Unlock()
|
||||
|
||||
cgroupsWatched, alreadyWatched := iw.containersWatched[containerName]
|
||||
|
||||
// Register an inotify notification.
|
||||
if !cgroupsWatched[dir] {
|
||||
err := iw.watcher.AddWatch(dir, inotify.InCreate|inotify.InDelete|inotify.InMove)
|
||||
if err != nil {
|
||||
return alreadyWatched, err
|
||||
}
|
||||
|
||||
if cgroupsWatched == nil {
|
||||
cgroupsWatched = make(map[string]bool)
|
||||
}
|
||||
cgroupsWatched[dir] = true
|
||||
}
|
||||
|
||||
// Record our watching of the container.
|
||||
if !alreadyWatched {
|
||||
iw.containersWatched[containerName] = cgroupsWatched
|
||||
}
|
||||
return alreadyWatched, nil
|
||||
}
|
||||
|
||||
// Remove watch from the specified directory. Returns if this was the last watch on the specified container.
|
||||
func (iw *InotifyWatcher) RemoveWatch(containerName, dir string) (bool, error) {
|
||||
iw.lock.Lock()
|
||||
defer iw.lock.Unlock()
|
||||
|
||||
// If we don't have a watch registered for this, just return.
|
||||
cgroupsWatched, ok := iw.containersWatched[containerName]
|
||||
if !ok {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Remove the inotify watch if it exists.
|
||||
if cgroupsWatched[dir] {
|
||||
err := iw.watcher.RemoveWatch(dir)
|
||||
if err != nil {
|
||||
return false, nil
|
||||
}
|
||||
delete(cgroupsWatched, dir)
|
||||
}
|
||||
|
||||
// Remove the record if this is the last watch.
|
||||
if len(cgroupsWatched) == 0 {
|
||||
delete(iw.containersWatched, containerName)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Errors are returned on this channel.
|
||||
func (iw *InotifyWatcher) Error() chan error {
|
||||
return iw.watcher.Error
|
||||
}
|
||||
|
||||
// Events are returned on this channel.
|
||||
func (iw *InotifyWatcher) Event() chan *inotify.Event {
|
||||
return iw.watcher.Event
|
||||
}
|
||||
|
||||
// Closes the inotify watcher.
|
||||
func (iw *InotifyWatcher) Close() error {
|
||||
return iw.watcher.Close()
|
||||
}
|
||||
|
||||
// Returns a map of containers to the cgroup paths being watched.
|
||||
func (iw *InotifyWatcher) GetWatches() map[string][]string {
|
||||
out := make(map[string][]string, len(iw.containersWatched))
|
||||
for k, v := range iw.containersWatched {
|
||||
out[k] = mapToSlice(v)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func mapToSlice(m map[string]bool) []string {
|
||||
out := make([]string, 0, len(m))
|
||||
for k := range m {
|
||||
out = append(out, k)
|
||||
}
|
||||
return out
|
||||
}
|
79
e2e/vendor/github.com/google/cadvisor/container/container.go
generated
vendored
Normal file
79
e2e/vendor/github.com/google/cadvisor/container/container.go
generated
vendored
Normal file
@ -0,0 +1,79 @@
|
||||
// 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 container
|
||||
|
||||
import info "github.com/google/cadvisor/info/v1"
|
||||
|
||||
// ListType describes whether listing should be just for a
|
||||
// specific container or performed recursively.
|
||||
type ListType int
|
||||
|
||||
const (
|
||||
ListSelf ListType = iota
|
||||
ListRecursive
|
||||
)
|
||||
|
||||
type ContainerType int
|
||||
|
||||
const (
|
||||
ContainerTypeRaw ContainerType = iota
|
||||
ContainerTypeDocker
|
||||
ContainerTypeCrio
|
||||
ContainerTypeContainerd
|
||||
ContainerTypeMesos
|
||||
ContainerTypePodman
|
||||
)
|
||||
|
||||
// Interface for container operation handlers.
|
||||
type ContainerHandler interface {
|
||||
// Returns the ContainerReference
|
||||
ContainerReference() (info.ContainerReference, error)
|
||||
|
||||
// Returns container's isolation spec.
|
||||
GetSpec() (info.ContainerSpec, error)
|
||||
|
||||
// Returns the current stats values of the container.
|
||||
GetStats() (*info.ContainerStats, error)
|
||||
|
||||
// Returns the subcontainers of this container.
|
||||
ListContainers(listType ListType) ([]info.ContainerReference, error)
|
||||
|
||||
// Returns the processes inside this container.
|
||||
ListProcesses(listType ListType) ([]int, error)
|
||||
|
||||
// Returns absolute cgroup path for the requested resource.
|
||||
GetCgroupPath(resource string) (string, error)
|
||||
|
||||
// Returns container labels, if available.
|
||||
GetContainerLabels() map[string]string
|
||||
|
||||
// Returns the container's ip address, if available
|
||||
GetContainerIPAddress() string
|
||||
|
||||
// Returns whether the container still exists.
|
||||
Exists() bool
|
||||
|
||||
// Cleanup frees up any resources being held like fds or go routines, etc.
|
||||
Cleanup()
|
||||
|
||||
// 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
|
||||
}
|
162
e2e/vendor/github.com/google/cadvisor/container/containerd/client.go
generated
vendored
Normal file
162
e2e/vendor/github.com/google/cadvisor/container/containerd/client.go
generated
vendored
Normal file
@ -0,0 +1,162 @@
|
||||
// Copyright 2017 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 containerd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
containersapi "github.com/containerd/containerd/api/services/containers/v1"
|
||||
tasksapi "github.com/containerd/containerd/api/services/tasks/v1"
|
||||
versionapi "github.com/containerd/containerd/api/services/version/v1"
|
||||
tasktypes "github.com/containerd/containerd/api/types/task"
|
||||
"github.com/containerd/errdefs"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/backoff"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
emptypb "google.golang.org/protobuf/types/known/emptypb"
|
||||
|
||||
"github.com/google/cadvisor/container/containerd/containers"
|
||||
"github.com/google/cadvisor/container/containerd/pkg/dialer"
|
||||
)
|
||||
|
||||
type client struct {
|
||||
containerService containersapi.ContainersClient
|
||||
taskService tasksapi.TasksClient
|
||||
versionService versionapi.VersionClient
|
||||
}
|
||||
|
||||
type ContainerdClient interface {
|
||||
LoadContainer(ctx context.Context, id string) (*containers.Container, error)
|
||||
TaskPid(ctx context.Context, id string) (uint32, error)
|
||||
Version(ctx context.Context) (string, error)
|
||||
}
|
||||
|
||||
var (
|
||||
ErrTaskIsInUnknownState = errors.New("containerd task is in unknown state") // used when process reported in containerd task is in Unknown State
|
||||
)
|
||||
|
||||
var once sync.Once
|
||||
var ctrdClient ContainerdClient = nil
|
||||
|
||||
const (
|
||||
maxBackoffDelay = 3 * time.Second
|
||||
baseBackoffDelay = 100 * time.Millisecond
|
||||
connectionTimeout = 2 * time.Second
|
||||
maxMsgSize = 16 * 1024 * 1024 // 16MB
|
||||
)
|
||||
|
||||
// Client creates a containerd client
|
||||
func Client(address, namespace string) (ContainerdClient, error) {
|
||||
var retErr error
|
||||
once.Do(func() {
|
||||
tryConn, err := net.DialTimeout("unix", address, connectionTimeout)
|
||||
if err != nil {
|
||||
retErr = fmt.Errorf("containerd: cannot unix dial containerd api service: %v", err)
|
||||
return
|
||||
}
|
||||
tryConn.Close()
|
||||
|
||||
connParams := grpc.ConnectParams{
|
||||
Backoff: backoff.DefaultConfig,
|
||||
}
|
||||
connParams.Backoff.BaseDelay = baseBackoffDelay
|
||||
connParams.Backoff.MaxDelay = maxBackoffDelay
|
||||
//nolint:staticcheck // SA1019
|
||||
gopts := []grpc.DialOption{
|
||||
grpc.WithTransportCredentials(insecure.NewCredentials()),
|
||||
grpc.WithContextDialer(dialer.ContextDialer),
|
||||
grpc.WithBlock(),
|
||||
grpc.WithConnectParams(connParams),
|
||||
grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(maxMsgSize)),
|
||||
}
|
||||
unary, stream := newNSInterceptors(namespace)
|
||||
gopts = append(gopts,
|
||||
grpc.WithUnaryInterceptor(unary),
|
||||
grpc.WithStreamInterceptor(stream),
|
||||
)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), connectionTimeout)
|
||||
defer cancel()
|
||||
//nolint:staticcheck // SA1019
|
||||
conn, err := grpc.DialContext(ctx, dialer.DialAddress(address), gopts...)
|
||||
if err != nil {
|
||||
retErr = err
|
||||
return
|
||||
}
|
||||
ctrdClient = &client{
|
||||
containerService: containersapi.NewContainersClient(conn),
|
||||
taskService: tasksapi.NewTasksClient(conn),
|
||||
versionService: versionapi.NewVersionClient(conn),
|
||||
}
|
||||
})
|
||||
return ctrdClient, retErr
|
||||
}
|
||||
|
||||
func (c *client) LoadContainer(ctx context.Context, id string) (*containers.Container, error) {
|
||||
r, err := c.containerService.Get(ctx, &containersapi.GetContainerRequest{
|
||||
ID: id,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errdefs.FromGRPC(err)
|
||||
}
|
||||
return containerFromProto(r.Container), nil
|
||||
}
|
||||
|
||||
func (c *client) TaskPid(ctx context.Context, id string) (uint32, error) {
|
||||
response, err := c.taskService.Get(ctx, &tasksapi.GetRequest{
|
||||
ContainerID: id,
|
||||
})
|
||||
if err != nil {
|
||||
return 0, errdefs.FromGRPC(err)
|
||||
}
|
||||
if response.Process.Status == tasktypes.Status_UNKNOWN {
|
||||
return 0, ErrTaskIsInUnknownState
|
||||
}
|
||||
return response.Process.Pid, nil
|
||||
}
|
||||
|
||||
func (c *client) Version(ctx context.Context) (string, error) {
|
||||
response, err := c.versionService.Version(ctx, &emptypb.Empty{})
|
||||
if err != nil {
|
||||
return "", errdefs.FromGRPC(err)
|
||||
}
|
||||
return response.Version, nil
|
||||
}
|
||||
|
||||
func containerFromProto(containerpb *containersapi.Container) *containers.Container {
|
||||
var runtime containers.RuntimeInfo
|
||||
// TODO: is nil check required for containerpb
|
||||
if containerpb.Runtime != nil {
|
||||
runtime = containers.RuntimeInfo{
|
||||
Name: containerpb.Runtime.Name,
|
||||
Options: containerpb.Runtime.Options,
|
||||
}
|
||||
}
|
||||
return &containers.Container{
|
||||
ID: containerpb.ID,
|
||||
Labels: containerpb.Labels,
|
||||
Image: containerpb.Image,
|
||||
Runtime: runtime,
|
||||
Spec: containerpb.Spec,
|
||||
Snapshotter: containerpb.Snapshotter,
|
||||
SnapshotKey: containerpb.SnapshotKey,
|
||||
Extensions: containerpb.Extensions,
|
||||
}
|
||||
}
|
125
e2e/vendor/github.com/google/cadvisor/container/containerd/containers/containers.go
generated
vendored
Normal file
125
e2e/vendor/github.com/google/cadvisor/container/containerd/containers/containers.go
generated
vendored
Normal file
@ -0,0 +1,125 @@
|
||||
// Copyright 2017 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.
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
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 containers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"google.golang.org/protobuf/types/known/anypb"
|
||||
)
|
||||
|
||||
// Container represents the set of data pinned by a container. Unless otherwise
|
||||
// noted, the resources here are considered in use by the container.
|
||||
//
|
||||
// The resources specified in this object are used to create tasks from the container.
|
||||
type Container struct {
|
||||
// ID uniquely identifies the container in a namespace.
|
||||
//
|
||||
// This property is required and cannot be changed after creation.
|
||||
ID string
|
||||
|
||||
// Labels provide metadata extension for a container.
|
||||
//
|
||||
// These are optional and fully mutable.
|
||||
Labels map[string]string
|
||||
|
||||
// Image specifies the image reference used for a container.
|
||||
//
|
||||
// This property is optional and mutable.
|
||||
Image string
|
||||
|
||||
// Runtime specifies which runtime should be used when launching container
|
||||
// tasks.
|
||||
//
|
||||
// This property is required and immutable.
|
||||
Runtime RuntimeInfo
|
||||
|
||||
// Spec should carry the runtime specification used to implement the
|
||||
// container.
|
||||
//
|
||||
// This field is required but mutable.
|
||||
Spec *anypb.Any
|
||||
|
||||
// SnapshotKey specifies the snapshot key to use for the container's root
|
||||
// filesystem. When starting a task from this container, a caller should
|
||||
// look up the mounts from the snapshot service and include those on the
|
||||
// task create request.
|
||||
//
|
||||
// This field is not required but mutable.
|
||||
SnapshotKey string
|
||||
|
||||
// Snapshotter specifies the snapshotter name used for rootfs
|
||||
//
|
||||
// This field is not required but immutable.
|
||||
Snapshotter string
|
||||
|
||||
// CreatedAt is the time at which the container was created.
|
||||
CreatedAt time.Time
|
||||
|
||||
// UpdatedAt is the time at which the container was updated.
|
||||
UpdatedAt time.Time
|
||||
|
||||
// Extensions stores client-specified metadata
|
||||
Extensions map[string]*anypb.Any
|
||||
}
|
||||
|
||||
// RuntimeInfo holds runtime specific information
|
||||
type RuntimeInfo struct {
|
||||
Name string
|
||||
Options *anypb.Any
|
||||
}
|
||||
|
||||
// Store interacts with the underlying container storage
|
||||
type Store interface {
|
||||
// Get a container using the id.
|
||||
//
|
||||
// Container object is returned on success. If the id is not known to the
|
||||
// store, an error will be returned.
|
||||
Get(ctx context.Context, id string) (Container, error)
|
||||
|
||||
// List returns containers that match one or more of the provided filters.
|
||||
List(ctx context.Context, filters ...string) ([]Container, error)
|
||||
|
||||
// Create a container in the store from the provided container.
|
||||
Create(ctx context.Context, container Container) (Container, error)
|
||||
|
||||
// Update the container with the provided container object. ID must be set.
|
||||
//
|
||||
// If one or more fieldpaths are provided, only the field corresponding to
|
||||
// the fieldpaths will be mutated.
|
||||
Update(ctx context.Context, container Container, fieldpaths ...string) (Container, error)
|
||||
|
||||
// Delete a container using the id.
|
||||
//
|
||||
// nil will be returned on success. If the container is not known to the
|
||||
// store, ErrNotFound will be returned.
|
||||
Delete(ctx context.Context, id string) error
|
||||
}
|
157
e2e/vendor/github.com/google/cadvisor/container/containerd/factory.go
generated
vendored
Normal file
157
e2e/vendor/github.com/google/cadvisor/container/containerd/factory.go
generated
vendored
Normal file
@ -0,0 +1,157 @@
|
||||
// Copyright 2017 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 containerd
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
"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/watcher"
|
||||
)
|
||||
|
||||
var ArgContainerdEndpoint = flag.String("containerd", "/run/containerd/containerd.sock", "containerd endpoint")
|
||||
var ArgContainerdNamespace = flag.String("containerd-namespace", "k8s.io", "containerd namespace")
|
||||
|
||||
var containerdEnvMetadataWhiteList = flag.String("containerd_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 containerd containers")
|
||||
|
||||
// The namespace under which containerd aliases are unique.
|
||||
const k8sContainerdNamespace = "containerd"
|
||||
|
||||
// Regexp that identifies containerd cgroups, containers started with
|
||||
// --cgroup-parent have another prefix than 'containerd'
|
||||
var containerdCgroupRegexp = regexp.MustCompile(`([a-z0-9]{64})`)
|
||||
|
||||
type containerdFactory struct {
|
||||
machineInfoFactory info.MachineInfoFactory
|
||||
client ContainerdClient
|
||||
version string
|
||||
// Information about the mounted cgroup subsystems.
|
||||
cgroupSubsystems map[string]string
|
||||
// Information about mounted filesystems.
|
||||
fsInfo fs.FsInfo
|
||||
includedMetrics container.MetricSet
|
||||
}
|
||||
|
||||
func (f *containerdFactory) String() string {
|
||||
return k8sContainerdNamespace
|
||||
}
|
||||
|
||||
func (f *containerdFactory) NewContainerHandler(name string, metadataEnvAllowList []string, inHostNamespace bool) (handler container.ContainerHandler, err error) {
|
||||
client, err := Client(*ArgContainerdEndpoint, *ArgContainerdNamespace)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
containerdMetadataEnvAllowList := strings.Split(*containerdEnvMetadataWhiteList, ",")
|
||||
|
||||
// prefer using the unified metadataEnvAllowList
|
||||
if len(metadataEnvAllowList) != 0 {
|
||||
containerdMetadataEnvAllowList = metadataEnvAllowList
|
||||
}
|
||||
|
||||
return newContainerdContainerHandler(
|
||||
client,
|
||||
name,
|
||||
f.machineInfoFactory,
|
||||
f.fsInfo,
|
||||
f.cgroupSubsystems,
|
||||
inHostNamespace,
|
||||
containerdMetadataEnvAllowList,
|
||||
f.includedMetrics,
|
||||
)
|
||||
}
|
||||
|
||||
// Returns the containerd ID from the full container name.
|
||||
func ContainerNameToContainerdID(name string) string {
|
||||
id := path.Base(name)
|
||||
if matches := containerdCgroupRegexp.FindStringSubmatch(id); matches != nil {
|
||||
return matches[1]
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
// isContainerName returns true if the cgroup with associated name
|
||||
// corresponds to a containerd container.
|
||||
func isContainerName(name string) bool {
|
||||
// TODO: May be check with HasPrefix ContainerdNamespace
|
||||
if strings.HasSuffix(name, ".mount") {
|
||||
return false
|
||||
}
|
||||
return containerdCgroupRegexp.MatchString(path.Base(name))
|
||||
}
|
||||
|
||||
// Containerd can handle and accept all containerd created containers
|
||||
func (f *containerdFactory) CanHandleAndAccept(name string) (bool, bool, error) {
|
||||
// if the container is not associated with containerd, we can't handle it or accept it.
|
||||
if !isContainerName(name) {
|
||||
return false, false, nil
|
||||
}
|
||||
// Check if the container is known to containerd and it is running.
|
||||
id := ContainerNameToContainerdID(name)
|
||||
// If container and task lookup in containerd fails then we assume
|
||||
// that the container state is not known to containerd
|
||||
ctx := context.Background()
|
||||
_, err := f.client.LoadContainer(ctx, id)
|
||||
if err != nil {
|
||||
return false, false, fmt.Errorf("failed to load container: %v", err)
|
||||
}
|
||||
|
||||
return true, true, nil
|
||||
}
|
||||
|
||||
func (f *containerdFactory) DebugInfo() map[string][]string {
|
||||
return map[string][]string{}
|
||||
}
|
||||
|
||||
// Register root container before running this function!
|
||||
func Register(factory info.MachineInfoFactory, fsInfo fs.FsInfo, includedMetrics container.MetricSet) error {
|
||||
client, err := Client(*ArgContainerdEndpoint, *ArgContainerdNamespace)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to create containerd client: %v", err)
|
||||
}
|
||||
|
||||
containerdVersion, err := client.Version(context.Background())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to fetch containerd client version: %v", err)
|
||||
}
|
||||
|
||||
cgroupSubsystems, err := libcontainer.GetCgroupSubsystems(includedMetrics)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get cgroup subsystems: %v", err)
|
||||
}
|
||||
|
||||
klog.V(1).Infof("Registering containerd factory")
|
||||
f := &containerdFactory{
|
||||
cgroupSubsystems: cgroupSubsystems,
|
||||
client: client,
|
||||
fsInfo: fsInfo,
|
||||
machineInfoFactory: factory,
|
||||
version: containerdVersion,
|
||||
includedMetrics: includedMetrics,
|
||||
}
|
||||
|
||||
container.RegisterContainerHandlerFactory(f, []watcher.ContainerWatchSource{watcher.Raw})
|
||||
return nil
|
||||
}
|
49
e2e/vendor/github.com/google/cadvisor/container/containerd/grpc.go
generated
vendored
Normal file
49
e2e/vendor/github.com/google/cadvisor/container/containerd/grpc.go
generated
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
// Copyright 2017 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.
|
||||
|
||||
// This code has been taken from containerd repo to avoid large library import
|
||||
package containerd
|
||||
|
||||
import (
|
||||
"github.com/google/cadvisor/container/containerd/namespaces"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
type namespaceInterceptor struct {
|
||||
namespace string
|
||||
}
|
||||
|
||||
func (ni namespaceInterceptor) unary(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
|
||||
_, ok := namespaces.Namespace(ctx)
|
||||
if !ok {
|
||||
ctx = namespaces.WithNamespace(ctx, ni.namespace)
|
||||
}
|
||||
return invoker(ctx, method, req, reply, cc, opts...)
|
||||
}
|
||||
|
||||
func (ni namespaceInterceptor) stream(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {
|
||||
_, ok := namespaces.Namespace(ctx)
|
||||
if !ok {
|
||||
ctx = namespaces.WithNamespace(ctx, ni.namespace)
|
||||
}
|
||||
return streamer(ctx, desc, cc, method, opts...)
|
||||
}
|
||||
|
||||
func newNSInterceptors(ns string) (grpc.UnaryClientInterceptor, grpc.StreamClientInterceptor) {
|
||||
ni := namespaceInterceptor{
|
||||
namespace: ns,
|
||||
}
|
||||
return grpc.UnaryClientInterceptor(ni.unary), grpc.StreamClientInterceptor(ni.stream)
|
||||
}
|
250
e2e/vendor/github.com/google/cadvisor/container/containerd/handler.go
generated
vendored
Normal file
250
e2e/vendor/github.com/google/cadvisor/container/containerd/handler.go
generated
vendored
Normal file
@ -0,0 +1,250 @@
|
||||
// Copyright 2017 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 containerd containers.
|
||||
package containerd
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/errdefs"
|
||||
"github.com/opencontainers/runc/libcontainer/cgroups"
|
||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/google/cadvisor/container"
|
||||
"github.com/google/cadvisor/container/common"
|
||||
containerlibcontainer "github.com/google/cadvisor/container/libcontainer"
|
||||
"github.com/google/cadvisor/fs"
|
||||
info "github.com/google/cadvisor/info/v1"
|
||||
)
|
||||
|
||||
type containerdContainerHandler struct {
|
||||
machineInfoFactory info.MachineInfoFactory
|
||||
// Absolute path to the cgroup hierarchies of this container.
|
||||
// (e.g.: "cpu" -> "/sys/fs/cgroup/cpu/test")
|
||||
cgroupPaths map[string]string
|
||||
fsInfo fs.FsInfo
|
||||
// Metadata associated with the container.
|
||||
reference info.ContainerReference
|
||||
envs map[string]string
|
||||
labels map[string]string
|
||||
// Image name used for this container.
|
||||
image string
|
||||
// Filesystem handler.
|
||||
includedMetrics container.MetricSet
|
||||
|
||||
libcontainerHandler *containerlibcontainer.Handler
|
||||
}
|
||||
|
||||
var _ container.ContainerHandler = &containerdContainerHandler{}
|
||||
|
||||
// newContainerdContainerHandler returns a new container.ContainerHandler
|
||||
func newContainerdContainerHandler(
|
||||
client ContainerdClient,
|
||||
name string,
|
||||
machineInfoFactory info.MachineInfoFactory,
|
||||
fsInfo fs.FsInfo,
|
||||
cgroupSubsystems map[string]string,
|
||||
inHostNamespace bool,
|
||||
metadataEnvAllowList []string,
|
||||
includedMetrics container.MetricSet,
|
||||
) (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
|
||||
}
|
||||
|
||||
id := ContainerNameToContainerdID(name)
|
||||
// We assume that if load fails then the container is not known to containerd.
|
||||
ctx := context.Background()
|
||||
cntr, err := client.LoadContainer(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var spec specs.Spec
|
||||
if err := json.Unmarshal(cntr.Spec.Value, &spec); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Cgroup is created during task creation. When cadvisor sees the cgroup,
|
||||
// task may not be fully created yet. Use a retry+backoff to tolerant the
|
||||
// race condition.
|
||||
// TODO(random-liu): Use cri-containerd client to talk with cri-containerd
|
||||
// instead. cri-containerd has some internal synchronization to make sure
|
||||
// `ContainerStatus` only returns result after `StartContainer` finishes.
|
||||
var taskPid uint32
|
||||
backoff := 100 * time.Millisecond
|
||||
retry := 5
|
||||
for {
|
||||
taskPid, err = client.TaskPid(ctx, id)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
|
||||
// Retry when task is not created yet or task is in unknown state (likely in process of initializing)
|
||||
isRetriableError := errdefs.IsNotFound(err) || errors.Is(err, ErrTaskIsInUnknownState)
|
||||
if !isRetriableError || retry == 0 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
retry--
|
||||
time.Sleep(backoff)
|
||||
backoff *= 2
|
||||
}
|
||||
|
||||
rootfs := "/"
|
||||
if !inHostNamespace {
|
||||
rootfs = "/rootfs"
|
||||
}
|
||||
|
||||
containerReference := info.ContainerReference{
|
||||
Id: id,
|
||||
Name: name,
|
||||
Namespace: k8sContainerdNamespace,
|
||||
Aliases: []string{id, name},
|
||||
}
|
||||
|
||||
// Containers that don't have their own network -- this includes
|
||||
// containers running in Kubernetes pods that use the network of the
|
||||
// infrastructure container -- does not need their stats to be
|
||||
// reported. This stops metrics being reported multiple times for each
|
||||
// container in a pod.
|
||||
metrics := common.RemoveNetMetrics(includedMetrics, cntr.Labels["io.cri-containerd.kind"] != "sandbox")
|
||||
|
||||
libcontainerHandler := containerlibcontainer.NewHandler(cgroupManager, rootfs, int(taskPid), metrics)
|
||||
|
||||
handler := &containerdContainerHandler{
|
||||
machineInfoFactory: machineInfoFactory,
|
||||
cgroupPaths: cgroupPaths,
|
||||
fsInfo: fsInfo,
|
||||
envs: make(map[string]string),
|
||||
labels: cntr.Labels,
|
||||
includedMetrics: metrics,
|
||||
reference: containerReference,
|
||||
libcontainerHandler: libcontainerHandler,
|
||||
}
|
||||
// Add the name and bare ID as aliases of the container.
|
||||
handler.image = cntr.Image
|
||||
|
||||
for _, exposedEnv := range metadataEnvAllowList {
|
||||
if exposedEnv == "" {
|
||||
// if no containerdEnvWhitelist provided, len(metadataEnvAllowList) == 1, metadataEnvAllowList[0] == ""
|
||||
continue
|
||||
}
|
||||
|
||||
for _, envVar := range spec.Process.Env {
|
||||
if envVar != "" {
|
||||
splits := strings.SplitN(envVar, "=", 2)
|
||||
if len(splits) == 2 && strings.HasPrefix(splits[0], exposedEnv) {
|
||||
handler.envs[splits[0]] = splits[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return handler, nil
|
||||
}
|
||||
|
||||
func (h *containerdContainerHandler) ContainerReference() (info.ContainerReference, error) {
|
||||
return h.reference, nil
|
||||
}
|
||||
|
||||
func (h *containerdContainerHandler) GetSpec() (info.ContainerSpec, error) {
|
||||
// TODO: Since we dont collect disk usage stats for containerd, we set hasFilesystem
|
||||
// to false. Revisit when we support disk usage stats for containerd
|
||||
hasFilesystem := false
|
||||
hasNet := h.includedMetrics.Has(container.NetworkUsageMetrics)
|
||||
spec, err := common.GetSpec(h.cgroupPaths, h.machineInfoFactory, hasNet, hasFilesystem)
|
||||
spec.Labels = h.labels
|
||||
spec.Envs = h.envs
|
||||
spec.Image = h.image
|
||||
|
||||
return spec, err
|
||||
}
|
||||
|
||||
func (h *containerdContainerHandler) 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)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *containerdContainerHandler) GetStats() (*info.ContainerStats, error) {
|
||||
stats, err := h.libcontainerHandler.GetStats()
|
||||
if err != nil {
|
||||
return stats, err
|
||||
}
|
||||
|
||||
// Get filesystem stats.
|
||||
err = h.getFsStats(stats)
|
||||
return stats, err
|
||||
}
|
||||
|
||||
func (h *containerdContainerHandler) ListContainers(listType container.ListType) ([]info.ContainerReference, error) {
|
||||
return []info.ContainerReference{}, nil
|
||||
}
|
||||
|
||||
func (h *containerdContainerHandler) 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 *containerdContainerHandler) GetContainerLabels() map[string]string {
|
||||
return h.labels
|
||||
}
|
||||
|
||||
func (h *containerdContainerHandler) ListProcesses(listType container.ListType) ([]int, error) {
|
||||
return h.libcontainerHandler.GetProcesses()
|
||||
}
|
||||
|
||||
func (h *containerdContainerHandler) Exists() bool {
|
||||
return common.CgroupExists(h.cgroupPaths)
|
||||
}
|
||||
|
||||
func (h *containerdContainerHandler) Type() container.ContainerType {
|
||||
return container.ContainerTypeContainerd
|
||||
}
|
||||
|
||||
func (h *containerdContainerHandler) Start() {
|
||||
}
|
||||
|
||||
func (h *containerdContainerHandler) Cleanup() {
|
||||
}
|
||||
|
||||
func (h *containerdContainerHandler) GetContainerIPAddress() string {
|
||||
// containerd doesnt take care of networking.So it doesnt maintain networking states
|
||||
return ""
|
||||
}
|
86
e2e/vendor/github.com/google/cadvisor/container/containerd/identifiers/validate.go
generated
vendored
Normal file
86
e2e/vendor/github.com/google/cadvisor/container/containerd/identifiers/validate.go
generated
vendored
Normal file
@ -0,0 +1,86 @@
|
||||
// Copyright 2017 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.
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
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 identifiers provides common validation for identifiers and keys
|
||||
// across containerd.
|
||||
//
|
||||
// Identifiers in containerd must be a alphanumeric, allowing limited
|
||||
// underscores, dashes and dots.
|
||||
//
|
||||
// While the character set may be expanded in the future, identifiers
|
||||
// are guaranteed to be safely used as filesystem path components.
|
||||
package identifiers
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
|
||||
"github.com/containerd/errdefs"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
maxLength = 76
|
||||
alphanum = `[A-Za-z0-9]+`
|
||||
separators = `[._-]`
|
||||
)
|
||||
|
||||
var (
|
||||
// identifierRe defines the pattern for valid identifiers.
|
||||
identifierRe = regexp.MustCompile(reAnchor(alphanum + reGroup(separators+reGroup(alphanum)) + "*"))
|
||||
)
|
||||
|
||||
// Validate returns nil if the string s is a valid identifier.
|
||||
//
|
||||
// identifiers are similar to the domain name rules according to RFC 1035, section 2.3.1. However
|
||||
// rules in this package are relaxed to allow numerals to follow period (".") and mixed case is
|
||||
// allowed.
|
||||
//
|
||||
// In general identifiers that pass this validation should be safe for use as filesystem path components.
|
||||
func Validate(s string) error {
|
||||
if len(s) == 0 {
|
||||
return errors.Wrapf(errdefs.ErrInvalidArgument, "identifier must not be empty")
|
||||
}
|
||||
|
||||
if len(s) > maxLength {
|
||||
return errors.Wrapf(errdefs.ErrInvalidArgument, "identifier %q greater than maximum length (%d characters)", s, maxLength)
|
||||
}
|
||||
|
||||
if !identifierRe.MatchString(s) {
|
||||
return errors.Wrapf(errdefs.ErrInvalidArgument, "identifier %q must match %v", s, identifierRe)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func reGroup(s string) string {
|
||||
return `(?:` + s + `)`
|
||||
}
|
||||
|
||||
func reAnchor(s string) string {
|
||||
return `^` + s + `$`
|
||||
}
|
30
e2e/vendor/github.com/google/cadvisor/container/containerd/install/install.go
generated
vendored
Normal file
30
e2e/vendor/github.com/google/cadvisor/container/containerd/install/install.go
generated
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
// 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 containerd.NewPlugin() as the "containerd" container provider when imported
|
||||
package install
|
||||
|
||||
import (
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
"github.com/google/cadvisor/container"
|
||||
"github.com/google/cadvisor/container/containerd"
|
||||
)
|
||||
|
||||
func init() {
|
||||
err := container.RegisterPlugin("containerd", containerd.NewPlugin())
|
||||
if err != nil {
|
||||
klog.Fatalf("Failed to register containerd plugin: %v", err)
|
||||
}
|
||||
}
|
92
e2e/vendor/github.com/google/cadvisor/container/containerd/namespaces/context.go
generated
vendored
Normal file
92
e2e/vendor/github.com/google/cadvisor/container/containerd/namespaces/context.go
generated
vendored
Normal file
@ -0,0 +1,92 @@
|
||||
// Copyright 2017 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.
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
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 namespaces
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
|
||||
"github.com/containerd/errdefs"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/google/cadvisor/container/containerd/identifiers"
|
||||
)
|
||||
|
||||
const (
|
||||
// NamespaceEnvVar is the environment variable key name
|
||||
NamespaceEnvVar = "CONTAINERD_NAMESPACE"
|
||||
// Default is the name of the default namespace
|
||||
Default = "default"
|
||||
)
|
||||
|
||||
type namespaceKey struct{}
|
||||
|
||||
// WithNamespace sets a given namespace on the context
|
||||
func WithNamespace(ctx context.Context, namespace string) context.Context {
|
||||
ctx = context.WithValue(ctx, namespaceKey{}, namespace) // set our key for namespace
|
||||
// also store on the grpc and ttrpc headers so it gets picked up by any clients that
|
||||
// are using this.
|
||||
return withTTRPCNamespaceHeader(withGRPCNamespaceHeader(ctx, namespace), namespace)
|
||||
}
|
||||
|
||||
// NamespaceFromEnv uses the namespace defined in CONTAINERD_NAMESPACE or
|
||||
// default
|
||||
func NamespaceFromEnv(ctx context.Context) context.Context {
|
||||
namespace := os.Getenv(NamespaceEnvVar)
|
||||
if namespace == "" {
|
||||
namespace = Default
|
||||
}
|
||||
return WithNamespace(ctx, namespace)
|
||||
}
|
||||
|
||||
// Namespace returns the namespace from the context.
|
||||
//
|
||||
// The namespace is not guaranteed to be valid.
|
||||
func Namespace(ctx context.Context) (string, bool) {
|
||||
namespace, ok := ctx.Value(namespaceKey{}).(string)
|
||||
if !ok {
|
||||
if namespace, ok = fromGRPCHeader(ctx); !ok {
|
||||
return fromTTRPCHeader(ctx)
|
||||
}
|
||||
}
|
||||
return namespace, ok
|
||||
}
|
||||
|
||||
// NamespaceRequired returns the valid namespace from the context or an error.
|
||||
func NamespaceRequired(ctx context.Context) (string, error) {
|
||||
namespace, ok := Namespace(ctx)
|
||||
if !ok || namespace == "" {
|
||||
return "", errors.Wrapf(errdefs.ErrFailedPrecondition, "namespace is required")
|
||||
}
|
||||
if err := identifiers.Validate(namespace); err != nil {
|
||||
return "", errors.Wrap(err, "namespace validation")
|
||||
}
|
||||
return namespace, nil
|
||||
}
|
74
e2e/vendor/github.com/google/cadvisor/container/containerd/namespaces/grpc.go
generated
vendored
Normal file
74
e2e/vendor/github.com/google/cadvisor/container/containerd/namespaces/grpc.go
generated
vendored
Normal file
@ -0,0 +1,74 @@
|
||||
// Copyright 2017 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.
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
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 namespaces
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"google.golang.org/grpc/metadata"
|
||||
)
|
||||
|
||||
const (
|
||||
// GRPCHeader defines the header name for specifying a containerd namespace.
|
||||
GRPCHeader = "containerd-namespace"
|
||||
)
|
||||
|
||||
// NOTE(stevvooe): We can stub this file out if we don't want a grpc dependency here.
|
||||
|
||||
func withGRPCNamespaceHeader(ctx context.Context, namespace string) context.Context {
|
||||
// also store on the grpc headers so it gets picked up by any clients that
|
||||
// are using this.
|
||||
nsheader := metadata.Pairs(GRPCHeader, namespace)
|
||||
md, ok := metadata.FromOutgoingContext(ctx) // merge with outgoing context.
|
||||
if !ok {
|
||||
md = nsheader
|
||||
} else {
|
||||
// order ensures the latest is first in this list.
|
||||
md = metadata.Join(nsheader, md)
|
||||
}
|
||||
|
||||
return metadata.NewOutgoingContext(ctx, md)
|
||||
}
|
||||
|
||||
func fromGRPCHeader(ctx context.Context) (string, bool) {
|
||||
// try to extract for use in grpc servers.
|
||||
md, ok := metadata.FromIncomingContext(ctx)
|
||||
if !ok {
|
||||
// TODO(stevvooe): Check outgoing context?
|
||||
return "", false
|
||||
}
|
||||
|
||||
values := md[GRPCHeader]
|
||||
if len(values) == 0 {
|
||||
return "", false
|
||||
}
|
||||
|
||||
return values[0], true
|
||||
}
|
57
e2e/vendor/github.com/google/cadvisor/container/containerd/namespaces/store.go
generated
vendored
Normal file
57
e2e/vendor/github.com/google/cadvisor/container/containerd/namespaces/store.go
generated
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
// Copyright 2017 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.
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
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 namespaces
|
||||
|
||||
import "context"
|
||||
|
||||
// Store provides introspection about namespaces.
|
||||
//
|
||||
// Note that these are slightly different than other objects, which are record
|
||||
// oriented. A namespace is really just a name and a set of labels. Objects
|
||||
// that belong to a namespace are returned when the namespace is assigned to a
|
||||
// given context.
|
||||
type Store interface {
|
||||
Create(ctx context.Context, namespace string, labels map[string]string) error
|
||||
Labels(ctx context.Context, namespace string) (map[string]string, error)
|
||||
SetLabel(ctx context.Context, namespace, key, value string) error
|
||||
List(ctx context.Context) ([]string, error)
|
||||
|
||||
// Delete removes the namespace. The namespace must be empty to be deleted.
|
||||
Delete(ctx context.Context, namespace string, opts ...DeleteOpts) error
|
||||
}
|
||||
|
||||
// DeleteInfo specifies information for the deletion of a namespace
|
||||
type DeleteInfo struct {
|
||||
// Name of the namespace
|
||||
Name string
|
||||
}
|
||||
|
||||
// DeleteOpts allows the caller to set options for namespace deletion
|
||||
type DeleteOpts func(context.Context, *DeleteInfo) error
|
64
e2e/vendor/github.com/google/cadvisor/container/containerd/namespaces/ttrpc.go
generated
vendored
Normal file
64
e2e/vendor/github.com/google/cadvisor/container/containerd/namespaces/ttrpc.go
generated
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
// Copyright 2017 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.
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
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 namespaces
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/containerd/ttrpc"
|
||||
)
|
||||
|
||||
const (
|
||||
// TTRPCHeader defines the header name for specifying a containerd namespace
|
||||
TTRPCHeader = "containerd-namespace-ttrpc"
|
||||
)
|
||||
|
||||
func copyMetadata(src ttrpc.MD) ttrpc.MD {
|
||||
md := ttrpc.MD{}
|
||||
for k, v := range src {
|
||||
md[k] = append(md[k], v...)
|
||||
}
|
||||
return md
|
||||
}
|
||||
|
||||
func withTTRPCNamespaceHeader(ctx context.Context, namespace string) context.Context {
|
||||
md, ok := ttrpc.GetMetadata(ctx)
|
||||
if !ok {
|
||||
md = ttrpc.MD{}
|
||||
} else {
|
||||
md = copyMetadata(md)
|
||||
}
|
||||
md.Set(TTRPCHeader, namespace)
|
||||
return ttrpc.WithMetadata(ctx, md)
|
||||
}
|
||||
|
||||
func fromTTRPCHeader(ctx context.Context) (string, bool) {
|
||||
return ttrpc.GetMetadataValue(ctx, TTRPCHeader)
|
||||
}
|
92
e2e/vendor/github.com/google/cadvisor/container/containerd/pkg/dialer/dialer.go
generated
vendored
Normal file
92
e2e/vendor/github.com/google/cadvisor/container/containerd/pkg/dialer/dialer.go
generated
vendored
Normal file
@ -0,0 +1,92 @@
|
||||
// Copyright 2017 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.
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
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 dialer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type dialResult struct {
|
||||
c net.Conn
|
||||
err error
|
||||
}
|
||||
|
||||
// ContextDialer returns a GRPC net.Conn connected to the provided address
|
||||
func ContextDialer(ctx context.Context, address string) (net.Conn, error) {
|
||||
if deadline, ok := ctx.Deadline(); ok {
|
||||
return timeoutDialer(address, time.Until(deadline))
|
||||
}
|
||||
return timeoutDialer(address, 0)
|
||||
}
|
||||
|
||||
// Dialer returns a GRPC net.Conn connected to the provided address
|
||||
// Deprecated: use ContextDialer and grpc.WithContextDialer.
|
||||
var Dialer = timeoutDialer
|
||||
|
||||
func timeoutDialer(address string, timeout time.Duration) (net.Conn, error) {
|
||||
var (
|
||||
stopC = make(chan struct{})
|
||||
synC = make(chan *dialResult)
|
||||
)
|
||||
go func() {
|
||||
defer close(synC)
|
||||
for {
|
||||
select {
|
||||
case <-stopC:
|
||||
return
|
||||
default:
|
||||
c, err := dialer(address, timeout)
|
||||
if isNoent(err) {
|
||||
<-time.After(10 * time.Millisecond)
|
||||
continue
|
||||
}
|
||||
synC <- &dialResult{c, err}
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
select {
|
||||
case dr := <-synC:
|
||||
return dr.c, dr.err
|
||||
case <-time.After(timeout):
|
||||
close(stopC)
|
||||
go func() {
|
||||
dr := <-synC
|
||||
if dr != nil && dr.c != nil {
|
||||
dr.c.Close()
|
||||
}
|
||||
}()
|
||||
return nil, errors.Errorf("dial %s: timeout", address)
|
||||
}
|
||||
}
|
66
e2e/vendor/github.com/google/cadvisor/container/containerd/pkg/dialer/dialer_unix.go
generated
vendored
Normal file
66
e2e/vendor/github.com/google/cadvisor/container/containerd/pkg/dialer/dialer_unix.go
generated
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
// Copyright 2017 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.
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
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 dialer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
// DialAddress returns the address with unix:// prepended to the
|
||||
// provided address
|
||||
func DialAddress(address string) string {
|
||||
return fmt.Sprintf("unix://%s", address)
|
||||
}
|
||||
|
||||
func isNoent(err error) bool {
|
||||
if err != nil {
|
||||
if nerr, ok := err.(*net.OpError); ok {
|
||||
if serr, ok := nerr.Err.(*os.SyscallError); ok {
|
||||
if serr.Err == syscall.ENOENT {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func dialer(address string, timeout time.Duration) (net.Conn, error) {
|
||||
address = strings.TrimPrefix(address, "unix://")
|
||||
return net.DialTimeout("unix", address, timeout)
|
||||
}
|
51
e2e/vendor/github.com/google/cadvisor/container/containerd/pkg/dialer/dialer_windows.go
generated
vendored
Normal file
51
e2e/vendor/github.com/google/cadvisor/container/containerd/pkg/dialer/dialer_windows.go
generated
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
// Copyright 2017 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.
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
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 dialer
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
winio "github.com/Microsoft/go-winio"
|
||||
)
|
||||
|
||||
func isNoent(err error) bool {
|
||||
return os.IsNotExist(err)
|
||||
}
|
||||
|
||||
func dialer(address string, timeout time.Duration) (net.Conn, error) {
|
||||
return winio.DialPipe(address, &timeout)
|
||||
}
|
||||
|
||||
// DialAddress returns the dial address
|
||||
func DialAddress(address string) string {
|
||||
return address
|
||||
}
|
38
e2e/vendor/github.com/google/cadvisor/container/containerd/plugin.go
generated
vendored
Normal file
38
e2e/vendor/github.com/google/cadvisor/container/containerd/plugin.go
generated
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
// 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 containerd
|
||||
|
||||
import (
|
||||
"github.com/google/cadvisor/container"
|
||||
"github.com/google/cadvisor/fs"
|
||||
info "github.com/google/cadvisor/info/v1"
|
||||
"github.com/google/cadvisor/watcher"
|
||||
)
|
||||
|
||||
// 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 {
|
||||
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
|
||||
}
|
167
e2e/vendor/github.com/google/cadvisor/container/crio/client.go
generated
vendored
Normal file
167
e2e/vendor/github.com/google/cadvisor/container/crio/client.go
generated
vendored
Normal file
@ -0,0 +1,167 @@
|
||||
// Copyright 2017 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 crio
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
var crioClientTimeout = flag.Duration("crio_client_timeout", time.Duration(0), "CRI-O client timeout. Default is no timeout.")
|
||||
|
||||
const (
|
||||
CrioSocket = "/var/run/crio/crio.sock"
|
||||
maxUnixSocketPathSize = len(syscall.RawSockaddrUnix{}.Path)
|
||||
)
|
||||
|
||||
var (
|
||||
theClient CrioClient
|
||||
clientErr error
|
||||
crioClientOnce sync.Once
|
||||
)
|
||||
|
||||
// Info represents CRI-O information as sent by the CRI-O server
|
||||
type Info struct {
|
||||
StorageDriver string `json:"storage_driver"`
|
||||
StorageRoot string `json:"storage_root"`
|
||||
StorageImage string `json:"storage_image"`
|
||||
}
|
||||
|
||||
// ContainerInfo represents a given container information
|
||||
type ContainerInfo struct {
|
||||
Name string `json:"name"`
|
||||
Pid int `json:"pid"`
|
||||
Image string `json:"image"`
|
||||
CreatedTime int64 `json:"created_time"`
|
||||
Labels map[string]string `json:"labels"`
|
||||
Annotations map[string]string `json:"annotations"`
|
||||
LogPath string `json:"log_path"`
|
||||
Root string `json:"root"`
|
||||
IP string `json:"ip_address"`
|
||||
IPs []string `json:"ip_addresses"`
|
||||
}
|
||||
|
||||
type CrioClient interface {
|
||||
Info() (Info, error)
|
||||
ContainerInfo(string) (*ContainerInfo, error)
|
||||
}
|
||||
|
||||
type crioClientImpl struct {
|
||||
client *http.Client
|
||||
}
|
||||
|
||||
func configureUnixTransport(tr *http.Transport, proto, addr string) error {
|
||||
if len(addr) > maxUnixSocketPathSize {
|
||||
return fmt.Errorf("Unix socket path %q is too long", addr)
|
||||
}
|
||||
// No need for compression in local communications.
|
||||
tr.DisableCompression = true
|
||||
tr.DialContext = func(_ context.Context, _, _ string) (net.Conn, error) {
|
||||
return net.DialTimeout(proto, addr, 32*time.Second)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Client returns a new configured CRI-O client
|
||||
func Client() (CrioClient, error) {
|
||||
crioClientOnce.Do(func() {
|
||||
tr := new(http.Transport)
|
||||
theClient = nil
|
||||
if clientErr = configureUnixTransport(tr, "unix", CrioSocket); clientErr != nil {
|
||||
return
|
||||
}
|
||||
theClient = &crioClientImpl{
|
||||
client: &http.Client{
|
||||
Transport: tr,
|
||||
Timeout: *crioClientTimeout,
|
||||
},
|
||||
}
|
||||
})
|
||||
return theClient, clientErr
|
||||
}
|
||||
|
||||
func getRequest(path string) (*http.Request, error) {
|
||||
req, err := http.NewRequest("GET", path, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// For local communications over a unix socket, it doesn't matter what
|
||||
// the host is. We just need a valid and meaningful host name.
|
||||
req.Host = "crio"
|
||||
req.URL.Host = CrioSocket
|
||||
req.URL.Scheme = "http"
|
||||
return req, nil
|
||||
}
|
||||
|
||||
// Info returns generic info from the CRI-O server
|
||||
func (c *crioClientImpl) Info() (Info, error) {
|
||||
info := Info{}
|
||||
req, err := getRequest("/info")
|
||||
if err != nil {
|
||||
return info, err
|
||||
}
|
||||
resp, err := c.client.Do(req)
|
||||
if err != nil {
|
||||
return info, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if err := json.NewDecoder(resp.Body).Decode(&info); err != nil {
|
||||
return info, err
|
||||
}
|
||||
return info, nil
|
||||
}
|
||||
|
||||
// ContainerInfo returns information about a given container
|
||||
func (c *crioClientImpl) ContainerInfo(id string) (*ContainerInfo, error) {
|
||||
req, err := getRequest("/containers/" + id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cInfo := ContainerInfo{}
|
||||
resp, err := c.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// golang's http.Do doesn't return an error if non 200 response code is returned
|
||||
// handle this case here, rather than failing to decode the body
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
respBody, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error finding container %s: Status %d", id, resp.StatusCode)
|
||||
}
|
||||
return nil, fmt.Errorf("Error finding container %s: Status %d returned error %s", id, resp.StatusCode, string(respBody))
|
||||
}
|
||||
|
||||
if err := json.NewDecoder(resp.Body).Decode(&cInfo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(cInfo.IP) > 0 {
|
||||
return &cInfo, nil
|
||||
}
|
||||
if len(cInfo.IPs) > 0 {
|
||||
cInfo.IP = cInfo.IPs[0]
|
||||
}
|
||||
return &cInfo, nil
|
||||
}
|
166
e2e/vendor/github.com/google/cadvisor/container/crio/factory.go
generated
vendored
Normal file
166
e2e/vendor/github.com/google/cadvisor/container/crio/factory.go
generated
vendored
Normal file
@ -0,0 +1,166 @@
|
||||
// Copyright 2017 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 crio
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
"regexp"
|
||||
"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/watcher"
|
||||
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
// The namespace under which crio aliases are unique.
|
||||
const CrioNamespace = "crio"
|
||||
|
||||
// The namespace systemd runs components under.
|
||||
const SystemdNamespace = "system-systemd"
|
||||
|
||||
// Regexp that identifies CRI-O cgroups
|
||||
var crioCgroupRegexp = regexp.MustCompile(`([a-z0-9]{64})`)
|
||||
|
||||
type storageDriver string
|
||||
|
||||
const (
|
||||
// TODO add full set of supported drivers in future..
|
||||
overlayStorageDriver storageDriver = "overlay"
|
||||
overlay2StorageDriver storageDriver = "overlay2"
|
||||
)
|
||||
|
||||
type crioFactory struct {
|
||||
machineInfoFactory info.MachineInfoFactory
|
||||
|
||||
storageDriver storageDriver
|
||||
storageDir string
|
||||
|
||||
// Information about the mounted cgroup subsystems.
|
||||
cgroupSubsystems map[string]string
|
||||
|
||||
// Information about mounted filesystems.
|
||||
fsInfo fs.FsInfo
|
||||
|
||||
includedMetrics container.MetricSet
|
||||
|
||||
client CrioClient
|
||||
}
|
||||
|
||||
func (f *crioFactory) String() string {
|
||||
return CrioNamespace
|
||||
}
|
||||
|
||||
func (f *crioFactory) NewContainerHandler(name string, metadataEnvAllowList []string, inHostNamespace bool) (handler container.ContainerHandler, err error) {
|
||||
client, err := Client()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
handler, err = newCrioContainerHandler(
|
||||
client,
|
||||
name,
|
||||
f.machineInfoFactory,
|
||||
f.fsInfo,
|
||||
f.storageDriver,
|
||||
f.storageDir,
|
||||
f.cgroupSubsystems,
|
||||
inHostNamespace,
|
||||
metadataEnvAllowList,
|
||||
f.includedMetrics,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// Returns the CRIO ID from the full container name.
|
||||
func ContainerNameToCrioId(name string) string {
|
||||
id := path.Base(name)
|
||||
|
||||
if matches := crioCgroupRegexp.FindStringSubmatch(id); matches != nil {
|
||||
return matches[1]
|
||||
}
|
||||
|
||||
return id
|
||||
}
|
||||
|
||||
// isContainerName returns true if the cgroup with associated name
|
||||
// corresponds to a crio container.
|
||||
func isContainerName(name string) bool {
|
||||
// always ignore .mount cgroup even if associated with crio and delegate to systemd
|
||||
if strings.HasSuffix(name, ".mount") {
|
||||
return false
|
||||
}
|
||||
return crioCgroupRegexp.MatchString(path.Base(name))
|
||||
}
|
||||
|
||||
// crio handles all containers under /crio
|
||||
func (f *crioFactory) CanHandleAndAccept(name string) (bool, bool, error) {
|
||||
if strings.HasPrefix(path.Base(name), "crio-conmon") {
|
||||
// TODO(runcom): should we include crio-conmon cgroups?
|
||||
return false, false, nil
|
||||
}
|
||||
if !strings.HasPrefix(path.Base(name), CrioNamespace) {
|
||||
return false, false, nil
|
||||
}
|
||||
if strings.HasPrefix(path.Base(name), SystemdNamespace) {
|
||||
return true, false, nil
|
||||
}
|
||||
// if the container is not associated with CRI-O, we can't handle it or accept it.
|
||||
if !isContainerName(name) {
|
||||
return false, false, nil
|
||||
}
|
||||
return true, true, nil
|
||||
}
|
||||
|
||||
func (f *crioFactory) DebugInfo() map[string][]string {
|
||||
return map[string][]string{}
|
||||
}
|
||||
|
||||
// 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 err
|
||||
}
|
||||
|
||||
info, err := client.Info()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO determine crio version so we can work differently w/ future versions if needed
|
||||
|
||||
cgroupSubsystems, err := libcontainer.GetCgroupSubsystems(includedMetrics)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get cgroup subsystems: %v", err)
|
||||
}
|
||||
|
||||
klog.V(1).Infof("Registering CRI-O factory")
|
||||
f := &crioFactory{
|
||||
client: client,
|
||||
cgroupSubsystems: cgroupSubsystems,
|
||||
fsInfo: fsInfo,
|
||||
machineInfoFactory: factory,
|
||||
storageDriver: storageDriver(info.StorageDriver),
|
||||
storageDir: info.StorageRoot,
|
||||
includedMetrics: includedMetrics,
|
||||
}
|
||||
|
||||
container.RegisterContainerHandlerFactory(f, []watcher.ContainerWatchSource{watcher.Raw})
|
||||
return nil
|
||||
}
|
362
e2e/vendor/github.com/google/cadvisor/container/crio/handler.go
generated
vendored
Normal file
362
e2e/vendor/github.com/google/cadvisor/container/crio/handler.go
generated
vendored
Normal file
@ -0,0 +1,362 @@
|
||||
// Copyright 2017 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 CRI-O containers.
|
||||
package crio
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/opencontainers/runc/libcontainer/cgroups"
|
||||
|
||||
"github.com/google/cadvisor/container"
|
||||
"github.com/google/cadvisor/container/common"
|
||||
containerlibcontainer "github.com/google/cadvisor/container/libcontainer"
|
||||
"github.com/google/cadvisor/fs"
|
||||
info "github.com/google/cadvisor/info/v1"
|
||||
)
|
||||
|
||||
type crioContainerHandler struct {
|
||||
client CrioClient
|
||||
name string
|
||||
|
||||
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 CRI-O storage driver
|
||||
storageDriver storageDriver
|
||||
fsInfo fs.FsInfo
|
||||
rootfsStorageDir string
|
||||
|
||||
// Metadata associated with the container.
|
||||
envs map[string]string
|
||||
labels map[string]string
|
||||
|
||||
// TODO
|
||||
// crio version handling...
|
||||
|
||||
// Image name used for this container.
|
||||
image string
|
||||
|
||||
// The network mode of the container
|
||||
// TODO
|
||||
|
||||
// Filesystem handler.
|
||||
fsHandler common.FsHandler
|
||||
|
||||
// The IP address of the container
|
||||
ipAddress string
|
||||
|
||||
includedMetrics container.MetricSet
|
||||
|
||||
reference info.ContainerReference
|
||||
|
||||
libcontainerHandler *containerlibcontainer.Handler
|
||||
cgroupManager cgroups.Manager
|
||||
rootFs string
|
||||
pidKnown bool
|
||||
}
|
||||
|
||||
var _ container.ContainerHandler = &crioContainerHandler{}
|
||||
|
||||
// newCrioContainerHandler returns a new container.ContainerHandler
|
||||
func newCrioContainerHandler(
|
||||
client CrioClient,
|
||||
name string,
|
||||
machineInfoFactory info.MachineInfoFactory,
|
||||
fsInfo fs.FsInfo,
|
||||
storageDriver storageDriver,
|
||||
storageDir string,
|
||||
cgroupSubsystems map[string]string,
|
||||
inHostNamespace bool,
|
||||
metadataEnvAllowList []string,
|
||||
includedMetrics container.MetricSet,
|
||||
) (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"
|
||||
}
|
||||
|
||||
id := ContainerNameToCrioId(name)
|
||||
pidKnown := true
|
||||
|
||||
cInfo, err := client.ContainerInfo(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if cInfo.Pid == 0 {
|
||||
// If pid is not known yet, network related stats can not be retrieved by the
|
||||
// libcontainer handler GetStats(). In this case, the crio handler GetStats()
|
||||
// will reattempt to get the pid and, if now known, will construct the libcontainer
|
||||
// handler. This libcontainer handler is then cached and reused without additional
|
||||
// calls to crio.
|
||||
pidKnown = false
|
||||
}
|
||||
|
||||
// passed to fs handler below ...
|
||||
// XXX: this is using the full container logpath, as constructed by the CRI
|
||||
// /var/log/pods/<pod_uuid>/container_instance.log
|
||||
// It's not actually a log dir, as the CRI doesn't have per-container dirs
|
||||
// under /var/log/pods/<pod_uuid>/
|
||||
// We can't use /var/log/pods/<pod_uuid>/ to count per-container log usage.
|
||||
// We use the container log file directly.
|
||||
storageLogDir := cInfo.LogPath
|
||||
|
||||
// Determine the rootfs storage dir
|
||||
rootfsStorageDir := cInfo.Root
|
||||
// TODO(runcom): CRI-O doesn't strip /merged but we need to in order to
|
||||
// get device ID from root, otherwise, it's going to error out as overlay
|
||||
// mounts doesn't have fixed dev ids.
|
||||
rootfsStorageDir = strings.TrimSuffix(rootfsStorageDir, "/merged")
|
||||
switch storageDriver {
|
||||
case overlayStorageDriver, overlay2StorageDriver:
|
||||
// overlay and overlay2 driver are the same "overlay2" driver so treat
|
||||
// them the same.
|
||||
rootfsStorageDir = filepath.Join(rootfsStorageDir, "diff")
|
||||
}
|
||||
|
||||
containerReference := info.ContainerReference{
|
||||
Id: id,
|
||||
Name: name,
|
||||
Aliases: []string{cInfo.Name, id},
|
||||
Namespace: CrioNamespace,
|
||||
}
|
||||
|
||||
// Find out if we need network metrics reported for this container.
|
||||
// Containers that don't have their own network -- this includes
|
||||
// containers running in Kubernetes pods that use the network of the
|
||||
// infrastructure container -- does not need their stats to be
|
||||
// reported. This stops metrics being reported multiple times for each
|
||||
// container in a pod.
|
||||
metrics := common.RemoveNetMetrics(includedMetrics, cInfo.Labels["io.kubernetes.container.name"] != "POD")
|
||||
|
||||
libcontainerHandler := containerlibcontainer.NewHandler(cgroupManager, rootFs, cInfo.Pid, metrics)
|
||||
|
||||
// TODO: extract object mother method
|
||||
handler := &crioContainerHandler{
|
||||
client: client,
|
||||
name: name,
|
||||
machineInfoFactory: machineInfoFactory,
|
||||
cgroupPaths: cgroupPaths,
|
||||
storageDriver: storageDriver,
|
||||
fsInfo: fsInfo,
|
||||
rootfsStorageDir: rootfsStorageDir,
|
||||
envs: make(map[string]string),
|
||||
labels: cInfo.Labels,
|
||||
includedMetrics: metrics,
|
||||
reference: containerReference,
|
||||
libcontainerHandler: libcontainerHandler,
|
||||
cgroupManager: cgroupManager,
|
||||
rootFs: rootFs,
|
||||
pidKnown: pidKnown,
|
||||
}
|
||||
|
||||
handler.image = cInfo.Image
|
||||
// TODO: we wantd to know graph driver DeviceId (dont think this is needed now)
|
||||
|
||||
// ignore err and get zero as default, this happens with sandboxes, not sure why...
|
||||
// kube isn't sending restart count in labels for sandboxes.
|
||||
restartCount, _ := strconv.Atoi(cInfo.Annotations["io.kubernetes.container.restartCount"])
|
||||
// Only adds restartcount label if it's greater than 0
|
||||
if restartCount > 0 {
|
||||
handler.labels["restartcount"] = strconv.Itoa(restartCount)
|
||||
}
|
||||
|
||||
handler.ipAddress = cInfo.IP
|
||||
|
||||
// we optionally collect disk usage metrics
|
||||
if includedMetrics.Has(container.DiskUsageMetrics) {
|
||||
handler.fsHandler = common.NewFsHandler(common.DefaultPeriod, rootfsStorageDir, storageLogDir, fsInfo)
|
||||
}
|
||||
// TODO for env vars we wanted to show from container.Config.Env from whitelist
|
||||
//for _, exposedEnv := range metadataEnvAllowList {
|
||||
//klog.V(4).Infof("TODO env whitelist: %v", exposedEnv)
|
||||
//}
|
||||
|
||||
return handler, nil
|
||||
}
|
||||
|
||||
func (h *crioContainerHandler) Start() {
|
||||
if h.fsHandler != nil {
|
||||
h.fsHandler.Start()
|
||||
}
|
||||
}
|
||||
|
||||
func (h *crioContainerHandler) Cleanup() {
|
||||
if h.fsHandler != nil {
|
||||
h.fsHandler.Stop()
|
||||
}
|
||||
}
|
||||
|
||||
func (h *crioContainerHandler) ContainerReference() (info.ContainerReference, error) {
|
||||
return h.reference, nil
|
||||
}
|
||||
|
||||
func (h *crioContainerHandler) GetSpec() (info.ContainerSpec, error) {
|
||||
hasFilesystem := h.includedMetrics.Has(container.DiskUsageMetrics)
|
||||
hasNet := h.includedMetrics.Has(container.NetworkUsageMetrics)
|
||||
spec, err := common.GetSpec(h.cgroupPaths, h.machineInfoFactory, hasNet, hasFilesystem)
|
||||
|
||||
spec.Labels = h.labels
|
||||
spec.Envs = h.envs
|
||||
spec.Image = h.image
|
||||
|
||||
return spec, err
|
||||
}
|
||||
|
||||
func (h *crioContainerHandler) 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 overlay2StorageDriver, overlayStorageDriver:
|
||||
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
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
limit uint64
|
||||
fsType string
|
||||
)
|
||||
|
||||
// crio 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
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if fsType == "" {
|
||||
return fmt.Errorf("unable to determine fs type for device: %v", device)
|
||||
}
|
||||
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
|
||||
|
||||
stats.Filesystem = append(stats.Filesystem, fsStat)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *crioContainerHandler) getLibcontainerHandler() *containerlibcontainer.Handler {
|
||||
if h.pidKnown {
|
||||
return h.libcontainerHandler
|
||||
}
|
||||
|
||||
id := ContainerNameToCrioId(h.name)
|
||||
|
||||
cInfo, err := h.client.ContainerInfo(id)
|
||||
if err != nil || cInfo.Pid == 0 {
|
||||
return h.libcontainerHandler
|
||||
}
|
||||
|
||||
h.pidKnown = true
|
||||
h.libcontainerHandler = containerlibcontainer.NewHandler(h.cgroupManager, h.rootFs, cInfo.Pid, h.includedMetrics)
|
||||
|
||||
return h.libcontainerHandler
|
||||
}
|
||||
|
||||
func (h *crioContainerHandler) GetStats() (*info.ContainerStats, error) {
|
||||
libcontainerHandler := h.getLibcontainerHandler()
|
||||
stats, err := libcontainerHandler.GetStats()
|
||||
if err != nil {
|
||||
return stats, err
|
||||
}
|
||||
|
||||
if h.includedMetrics.Has(container.NetworkUsageMetrics) && len(stats.Network.Interfaces) == 0 {
|
||||
// No network related information indicates that the pid of the
|
||||
// container is not longer valid and we need to ask crio to
|
||||
// provide the pid of another container from that pod
|
||||
h.pidKnown = false
|
||||
return stats, nil
|
||||
}
|
||||
// Get filesystem stats.
|
||||
err = h.getFsStats(stats)
|
||||
if err != nil {
|
||||
return stats, err
|
||||
}
|
||||
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
func (h *crioContainerHandler) ListContainers(listType container.ListType) ([]info.ContainerReference, error) {
|
||||
// No-op for Docker driver.
|
||||
return []info.ContainerReference{}, nil
|
||||
}
|
||||
|
||||
func (h *crioContainerHandler) 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 *crioContainerHandler) GetContainerLabels() map[string]string {
|
||||
return h.labels
|
||||
}
|
||||
|
||||
func (h *crioContainerHandler) GetContainerIPAddress() string {
|
||||
return h.ipAddress
|
||||
}
|
||||
|
||||
func (h *crioContainerHandler) ListProcesses(listType container.ListType) ([]int, error) {
|
||||
return h.libcontainerHandler.GetProcesses()
|
||||
}
|
||||
|
||||
func (h *crioContainerHandler) Exists() bool {
|
||||
return common.CgroupExists(h.cgroupPaths)
|
||||
}
|
||||
|
||||
func (h *crioContainerHandler) Type() container.ContainerType {
|
||||
return container.ContainerTypeCrio
|
||||
}
|
30
e2e/vendor/github.com/google/cadvisor/container/crio/install/install.go
generated
vendored
Normal file
30
e2e/vendor/github.com/google/cadvisor/container/crio/install/install.go
generated
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
// 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 crio.NewPlugin() as the "crio" container provider when imported
|
||||
package install
|
||||
|
||||
import (
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
"github.com/google/cadvisor/container"
|
||||
"github.com/google/cadvisor/container/crio"
|
||||
)
|
||||
|
||||
func init() {
|
||||
err := container.RegisterPlugin("crio", crio.NewPlugin())
|
||||
if err != nil {
|
||||
klog.Fatalf("Failed to register crio plugin: %v", err)
|
||||
}
|
||||
}
|
51
e2e/vendor/github.com/google/cadvisor/container/crio/plugin.go
generated
vendored
Normal file
51
e2e/vendor/github.com/google/cadvisor/container/crio/plugin.go
generated
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
// 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 crio
|
||||
|
||||
import (
|
||||
"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"
|
||||
)
|
||||
|
||||
// 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 {
|
||||
crioClient, err := Client()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
crioInfo, err := crioClient.Info()
|
||||
if err != nil {
|
||||
klog.V(5).Infof("CRI-O not connected: %v", err)
|
||||
} else {
|
||||
context.Crio = fs.CrioContext{Root: crioInfo.StorageRoot, ImageStore: crioInfo.StorageImage, Driver: crioInfo.StorageDriver}
|
||||
}
|
||||
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
|
||||
}
|
330
e2e/vendor/github.com/google/cadvisor/container/factory.go
generated
vendored
Normal file
330
e2e/vendor/github.com/google/cadvisor/container/factory.go
generated
vendored
Normal file
@ -0,0 +1,330 @@
|
||||
// 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
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/google/cadvisor/fs"
|
||||
info "github.com/google/cadvisor/info/v1"
|
||||
"github.com/google/cadvisor/watcher"
|
||||
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
type ContainerHandlerFactory interface {
|
||||
// Create a new ContainerHandler using this factory. CanHandleAndAccept() must have returned true.
|
||||
NewContainerHandler(name string, metadataEnvAllowList []string, inHostNamespace bool) (c ContainerHandler, err error)
|
||||
|
||||
// Returns whether this factory can handle and accept the specified container.
|
||||
CanHandleAndAccept(name string) (handle bool, accept bool, err error)
|
||||
|
||||
// Name of the factory.
|
||||
String() string
|
||||
|
||||
// Returns debugging information. Map of lines per category.
|
||||
DebugInfo() map[string][]string
|
||||
}
|
||||
|
||||
// MetricKind represents the kind of metrics that cAdvisor exposes.
|
||||
type MetricKind string
|
||||
|
||||
const (
|
||||
CpuUsageMetrics MetricKind = "cpu"
|
||||
ProcessSchedulerMetrics MetricKind = "sched"
|
||||
PerCpuUsageMetrics MetricKind = "percpu"
|
||||
MemoryUsageMetrics MetricKind = "memory"
|
||||
MemoryNumaMetrics MetricKind = "memory_numa"
|
||||
CpuLoadMetrics MetricKind = "cpuLoad"
|
||||
DiskIOMetrics MetricKind = "diskIO"
|
||||
DiskUsageMetrics MetricKind = "disk"
|
||||
NetworkUsageMetrics MetricKind = "network"
|
||||
NetworkTcpUsageMetrics MetricKind = "tcp"
|
||||
NetworkAdvancedTcpUsageMetrics MetricKind = "advtcp"
|
||||
NetworkUdpUsageMetrics MetricKind = "udp"
|
||||
AppMetrics MetricKind = "app"
|
||||
ProcessMetrics MetricKind = "process"
|
||||
HugetlbUsageMetrics MetricKind = "hugetlb"
|
||||
PerfMetrics MetricKind = "perf_event"
|
||||
ReferencedMemoryMetrics MetricKind = "referenced_memory"
|
||||
CPUTopologyMetrics MetricKind = "cpu_topology"
|
||||
ResctrlMetrics MetricKind = "resctrl"
|
||||
CPUSetMetrics MetricKind = "cpuset"
|
||||
OOMMetrics MetricKind = "oom_event"
|
||||
)
|
||||
|
||||
// AllMetrics represents all kinds of metrics that cAdvisor supported.
|
||||
var AllMetrics = MetricSet{
|
||||
CpuUsageMetrics: struct{}{},
|
||||
ProcessSchedulerMetrics: struct{}{},
|
||||
PerCpuUsageMetrics: struct{}{},
|
||||
MemoryUsageMetrics: struct{}{},
|
||||
MemoryNumaMetrics: struct{}{},
|
||||
CpuLoadMetrics: struct{}{},
|
||||
DiskIOMetrics: struct{}{},
|
||||
DiskUsageMetrics: struct{}{},
|
||||
NetworkUsageMetrics: struct{}{},
|
||||
NetworkTcpUsageMetrics: struct{}{},
|
||||
NetworkAdvancedTcpUsageMetrics: struct{}{},
|
||||
NetworkUdpUsageMetrics: struct{}{},
|
||||
ProcessMetrics: struct{}{},
|
||||
AppMetrics: struct{}{},
|
||||
HugetlbUsageMetrics: struct{}{},
|
||||
PerfMetrics: struct{}{},
|
||||
ReferencedMemoryMetrics: struct{}{},
|
||||
CPUTopologyMetrics: struct{}{},
|
||||
ResctrlMetrics: struct{}{},
|
||||
CPUSetMetrics: struct{}{},
|
||||
OOMMetrics: struct{}{},
|
||||
}
|
||||
|
||||
// AllNetworkMetrics represents all network metrics that cAdvisor supports.
|
||||
var AllNetworkMetrics = MetricSet{
|
||||
NetworkUsageMetrics: struct{}{},
|
||||
NetworkTcpUsageMetrics: struct{}{},
|
||||
NetworkAdvancedTcpUsageMetrics: struct{}{},
|
||||
NetworkUdpUsageMetrics: struct{}{},
|
||||
}
|
||||
|
||||
func (mk MetricKind) String() string {
|
||||
return string(mk)
|
||||
}
|
||||
|
||||
type MetricSet map[MetricKind]struct{}
|
||||
|
||||
func (ms MetricSet) Has(mk MetricKind) bool {
|
||||
_, exists := ms[mk]
|
||||
return exists
|
||||
}
|
||||
|
||||
func (ms MetricSet) HasAny(ms1 MetricSet) bool {
|
||||
for m := range ms1 {
|
||||
if _, ok := ms[m]; ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (ms MetricSet) add(mk MetricKind) {
|
||||
ms[mk] = struct{}{}
|
||||
}
|
||||
|
||||
func (ms MetricSet) String() string {
|
||||
values := make([]string, 0, len(ms))
|
||||
for metric := range ms {
|
||||
values = append(values, string(metric))
|
||||
}
|
||||
sort.Strings(values)
|
||||
return strings.Join(values, ",")
|
||||
}
|
||||
|
||||
// Not thread-safe, exported only for https://pkg.go.dev/flag#Value
|
||||
func (ms *MetricSet) Set(value string) error {
|
||||
*ms = MetricSet{}
|
||||
if value == "" {
|
||||
return nil
|
||||
}
|
||||
for _, metric := range strings.Split(value, ",") {
|
||||
if AllMetrics.Has(MetricKind(metric)) {
|
||||
(*ms).add(MetricKind(metric))
|
||||
} else {
|
||||
return fmt.Errorf("unsupported metric %q specified", metric)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ms MetricSet) Difference(ms1 MetricSet) MetricSet {
|
||||
result := MetricSet{}
|
||||
for kind := range ms {
|
||||
if !ms1.Has(kind) {
|
||||
result.add(kind)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (ms MetricSet) Append(ms1 MetricSet) MetricSet {
|
||||
result := ms
|
||||
for kind := range ms1 {
|
||||
if !ms.Has(kind) {
|
||||
result.add(kind)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// All registered auth provider plugins.
|
||||
var pluginsLock sync.Mutex
|
||||
var plugins = make(map[string]Plugin)
|
||||
|
||||
type Plugin interface {
|
||||
// InitializeFSContext is invoked when populating an fs.Context object for a new manager.
|
||||
// A returned error here is fatal.
|
||||
InitializeFSContext(context *fs.Context) error
|
||||
|
||||
// Register is invoked when starting a manager. It can optionally return a container watcher.
|
||||
// A returned error is logged, but is not fatal.
|
||||
Register(factory info.MachineInfoFactory, fsInfo fs.FsInfo, includedMetrics MetricSet) (watcher.ContainerWatcher, error)
|
||||
}
|
||||
|
||||
func RegisterPlugin(name string, plugin Plugin) error {
|
||||
pluginsLock.Lock()
|
||||
defer pluginsLock.Unlock()
|
||||
if _, found := plugins[name]; found {
|
||||
return fmt.Errorf("Plugin %q was registered twice", name)
|
||||
}
|
||||
klog.V(4).Infof("Registered Plugin %q", name)
|
||||
plugins[name] = plugin
|
||||
return nil
|
||||
}
|
||||
|
||||
func InitializeFSContext(context *fs.Context) error {
|
||||
pluginsLock.Lock()
|
||||
defer pluginsLock.Unlock()
|
||||
for name, plugin := range plugins {
|
||||
err := plugin.InitializeFSContext(context)
|
||||
if err != nil {
|
||||
klog.V(5).Infof("Initialization of the %s context failed: %v", name, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func InitializePlugins(factory info.MachineInfoFactory, fsInfo fs.FsInfo, includedMetrics MetricSet) []watcher.ContainerWatcher {
|
||||
pluginsLock.Lock()
|
||||
defer pluginsLock.Unlock()
|
||||
|
||||
containerWatchers := []watcher.ContainerWatcher{}
|
||||
for name, plugin := range plugins {
|
||||
watcher, err := plugin.Register(factory, fsInfo, includedMetrics)
|
||||
if err != nil {
|
||||
klog.Infof("Registration of the %s container factory failed: %v", name, err)
|
||||
} else {
|
||||
klog.Infof("Registration of the %s container factory successfully", name)
|
||||
}
|
||||
if watcher != nil {
|
||||
containerWatchers = append(containerWatchers, watcher)
|
||||
}
|
||||
}
|
||||
return containerWatchers
|
||||
}
|
||||
|
||||
// TODO(vmarmol): Consider not making this global.
|
||||
// Global list of factories.
|
||||
var (
|
||||
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, watchTypes []watcher.ContainerWatchSource) {
|
||||
factoriesLock.Lock()
|
||||
defer factoriesLock.Unlock()
|
||||
|
||||
for _, watchType := range watchTypes {
|
||||
factories[watchType] = append(factories[watchType], factory)
|
||||
}
|
||||
}
|
||||
|
||||
// Returns whether there are any container handler factories registered.
|
||||
func HasFactories() bool {
|
||||
factoriesLock.Lock()
|
||||
defer factoriesLock.Unlock()
|
||||
|
||||
return len(factories) != 0
|
||||
}
|
||||
|
||||
// Create a new ContainerHandler for the specified container.
|
||||
func NewContainerHandler(name string, watchType watcher.ContainerWatchSource, metadataEnvAllowList []string, inHostNamespace bool) (ContainerHandler, bool, error) {
|
||||
factoriesLock.RLock()
|
||||
defer factoriesLock.RUnlock()
|
||||
|
||||
// Create the ContainerHandler with the first factory that supports it.
|
||||
// Note that since RawContainerHandler can support a wide range of paths,
|
||||
// it's evaluated last just to make sure if any other ContainerHandler
|
||||
// can support it.
|
||||
for _, factory := range GetReorderedFactoryList(watchType) {
|
||||
canHandle, canAccept, err := factory.CanHandleAndAccept(name)
|
||||
if err != nil {
|
||||
klog.V(4).Infof("Error trying to work out if we can handle %s: %v", name, err)
|
||||
}
|
||||
if canHandle {
|
||||
if !canAccept {
|
||||
klog.V(3).Infof("Factory %q can handle container %q, but ignoring.", factory, name)
|
||||
return nil, false, nil
|
||||
}
|
||||
klog.V(3).Infof("Using factory %q for container %q", factory, name)
|
||||
handle, err := factory.NewContainerHandler(name, metadataEnvAllowList, inHostNamespace)
|
||||
return handle, canAccept, err
|
||||
}
|
||||
klog.V(4).Infof("Factory %q was unable to handle container %q", factory, name)
|
||||
}
|
||||
|
||||
return nil, false, fmt.Errorf("no known factory can handle creation of container")
|
||||
}
|
||||
|
||||
// Clear the known factories.
|
||||
func ClearContainerHandlerFactories() {
|
||||
factoriesLock.Lock()
|
||||
defer factoriesLock.Unlock()
|
||||
|
||||
factories = map[watcher.ContainerWatchSource][]ContainerHandlerFactory{}
|
||||
}
|
||||
|
||||
func DebugInfo() map[string][]string {
|
||||
factoriesLock.RLock()
|
||||
defer factoriesLock.RUnlock()
|
||||
|
||||
// Get debug information for all factories.
|
||||
out := make(map[string][]string)
|
||||
for _, factoriesSlice := range factories {
|
||||
for _, factory := range factoriesSlice {
|
||||
for k, v := range factory.DebugInfo() {
|
||||
out[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// GetReorderedFactoryList returns the list of ContainerHandlerFactory where the
|
||||
// RawContainerHandler is always the last element.
|
||||
func GetReorderedFactoryList(watchType watcher.ContainerWatchSource) []ContainerHandlerFactory {
|
||||
ContainerHandlerFactoryList := make([]ContainerHandlerFactory, 0, len(factories))
|
||||
|
||||
var rawFactory ContainerHandlerFactory
|
||||
for _, v := range factories[watchType] {
|
||||
if v != nil {
|
||||
if v.String() == "raw" {
|
||||
rawFactory = v
|
||||
continue
|
||||
}
|
||||
ContainerHandlerFactoryList = append(ContainerHandlerFactoryList, v)
|
||||
}
|
||||
}
|
||||
|
||||
if rawFactory != nil {
|
||||
ContainerHandlerFactoryList = append(ContainerHandlerFactoryList, rawFactory)
|
||||
}
|
||||
|
||||
return ContainerHandlerFactoryList
|
||||
}
|
917
e2e/vendor/github.com/google/cadvisor/container/libcontainer/handler.go
generated
vendored
Normal file
917
e2e/vendor/github.com/google/cadvisor/container/libcontainer/handler.go
generated
vendored
Normal file
@ -0,0 +1,917 @@
|
||||
// Copyright 2018 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package libcontainer
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/opencontainers/runc/libcontainer/cgroups"
|
||||
"github.com/opencontainers/runc/libcontainer/cgroups/fs2"
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
"github.com/google/cadvisor/container"
|
||||
"github.com/google/cadvisor/container/common"
|
||||
info "github.com/google/cadvisor/info/v1"
|
||||
)
|
||||
|
||||
var (
|
||||
referencedResetInterval = flag.Uint64("referenced_reset_interval", 0,
|
||||
"Reset interval for referenced bytes (container_referenced_bytes metric), number of measurement cycles after which referenced bytes are cleared, if set to 0 referenced bytes are never cleared (default: 0)")
|
||||
|
||||
smapsFilePathPattern = "/proc/%d/smaps"
|
||||
clearRefsFilePathPattern = "/proc/%d/clear_refs"
|
||||
|
||||
referencedRegexp = regexp.MustCompile(`Referenced:\s*([0-9]+)\s*kB`)
|
||||
)
|
||||
|
||||
type Handler struct {
|
||||
cgroupManager cgroups.Manager
|
||||
rootFs string
|
||||
pid int
|
||||
includedMetrics container.MetricSet
|
||||
// pidMetricsCache holds CPU scheduler stats for existing processes (map key is PID) between calls to schedulerStatsFromProcs.
|
||||
pidMetricsCache map[int]*info.CpuSchedstat
|
||||
// pidMetricsSaved holds accumulated CPU scheduler stats for processes that no longer exist.
|
||||
pidMetricsSaved info.CpuSchedstat
|
||||
cycles uint64
|
||||
}
|
||||
|
||||
func NewHandler(cgroupManager cgroups.Manager, rootFs string, pid int, includedMetrics container.MetricSet) *Handler {
|
||||
return &Handler{
|
||||
cgroupManager: cgroupManager,
|
||||
rootFs: rootFs,
|
||||
pid: pid,
|
||||
includedMetrics: includedMetrics,
|
||||
pidMetricsCache: make(map[int]*info.CpuSchedstat),
|
||||
}
|
||||
}
|
||||
|
||||
// Get cgroup and networking stats of the specified container
|
||||
func (h *Handler) GetStats() (*info.ContainerStats, error) {
|
||||
ignoreStatsError := false
|
||||
if cgroups.IsCgroup2UnifiedMode() {
|
||||
// On cgroup v2 the root cgroup stats have been introduced in recent kernel versions,
|
||||
// so not all kernel versions have all the data. This means that stat fetching can fail
|
||||
// due to lacking cgroup stat files, but that some data is provided.
|
||||
if h.cgroupManager.Path("") == fs2.UnifiedMountpoint {
|
||||
ignoreStatsError = true
|
||||
}
|
||||
}
|
||||
|
||||
cgroupStats, err := h.cgroupManager.GetStats()
|
||||
if err != nil {
|
||||
if !ignoreStatsError {
|
||||
return nil, err
|
||||
}
|
||||
klog.V(4).Infof("Ignoring errors when gathering stats for root cgroup since some controllers don't have stats on the root cgroup: %v", err)
|
||||
}
|
||||
stats := newContainerStats(cgroupStats, h.includedMetrics)
|
||||
|
||||
if h.includedMetrics.Has(container.ProcessSchedulerMetrics) {
|
||||
stats.Cpu.Schedstat, err = h.schedulerStatsFromProcs()
|
||||
if err != nil {
|
||||
klog.V(4).Infof("Unable to get Process Scheduler Stats: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if h.includedMetrics.Has(container.ReferencedMemoryMetrics) {
|
||||
h.cycles++
|
||||
pids, err := h.cgroupManager.GetPids()
|
||||
if err != nil {
|
||||
klog.V(4).Infof("Could not get PIDs for container %d: %v", h.pid, err)
|
||||
} else {
|
||||
stats.ReferencedMemory, err = referencedBytesStat(pids, h.cycles, *referencedResetInterval)
|
||||
if err != nil {
|
||||
klog.V(4).Infof("Unable to get referenced bytes: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we know the pid then get network stats from /proc/<pid>/net/dev
|
||||
if h.pid > 0 {
|
||||
if h.includedMetrics.Has(container.NetworkUsageMetrics) {
|
||||
netStats, err := networkStatsFromProc(h.rootFs, h.pid)
|
||||
if err != nil {
|
||||
klog.V(4).Infof("Unable to get network stats from pid %d: %v", h.pid, err)
|
||||
} else {
|
||||
stats.Network.Interfaces = append(stats.Network.Interfaces, netStats...)
|
||||
}
|
||||
}
|
||||
if h.includedMetrics.Has(container.NetworkTcpUsageMetrics) {
|
||||
t, err := tcpStatsFromProc(h.rootFs, h.pid, "net/tcp")
|
||||
if err != nil {
|
||||
klog.V(4).Infof("Unable to get tcp stats from pid %d: %v", h.pid, err)
|
||||
} else {
|
||||
stats.Network.Tcp = t
|
||||
}
|
||||
|
||||
t6, err := tcpStatsFromProc(h.rootFs, h.pid, "net/tcp6")
|
||||
if err != nil {
|
||||
klog.V(4).Infof("Unable to get tcp6 stats from pid %d: %v", h.pid, err)
|
||||
} else {
|
||||
stats.Network.Tcp6 = t6
|
||||
}
|
||||
|
||||
}
|
||||
if h.includedMetrics.Has(container.NetworkAdvancedTcpUsageMetrics) {
|
||||
ta, err := advancedTCPStatsFromProc(h.rootFs, h.pid, "net/netstat", "net/snmp")
|
||||
if err != nil {
|
||||
klog.V(4).Infof("Unable to get advanced tcp stats from pid %d: %v", h.pid, err)
|
||||
} else {
|
||||
stats.Network.TcpAdvanced = ta
|
||||
}
|
||||
}
|
||||
if h.includedMetrics.Has(container.NetworkUdpUsageMetrics) {
|
||||
u, err := udpStatsFromProc(h.rootFs, h.pid, "net/udp")
|
||||
if err != nil {
|
||||
klog.V(4).Infof("Unable to get udp stats from pid %d: %v", h.pid, err)
|
||||
} else {
|
||||
stats.Network.Udp = u
|
||||
}
|
||||
|
||||
u6, err := udpStatsFromProc(h.rootFs, h.pid, "net/udp6")
|
||||
if err != nil {
|
||||
klog.V(4).Infof("Unable to get udp6 stats from pid %d: %v", h.pid, err)
|
||||
} else {
|
||||
stats.Network.Udp6 = u6
|
||||
}
|
||||
}
|
||||
}
|
||||
// some process metrics are per container ( number of processes, number of
|
||||
// file descriptors etc.) and not required a proper container's
|
||||
// root PID (systemd services don't have the root PID atm)
|
||||
if h.includedMetrics.Has(container.ProcessMetrics) {
|
||||
path, ok := common.GetControllerPath(h.cgroupManager.GetPaths(), "cpu", cgroups.IsCgroup2UnifiedMode())
|
||||
if !ok {
|
||||
klog.V(4).Infof("Could not find cgroups CPU for container %d", h.pid)
|
||||
} else {
|
||||
stats.Processes, err = processStatsFromProcs(h.rootFs, path, h.pid)
|
||||
if err != nil {
|
||||
klog.V(4).Infof("Unable to get Process Stats: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// if include processes metrics, just set threads metrics if exist, and has no relationship with cpu path
|
||||
setThreadsStats(cgroupStats, stats)
|
||||
}
|
||||
|
||||
// For backwards compatibility.
|
||||
if len(stats.Network.Interfaces) > 0 {
|
||||
stats.Network.InterfaceStats = stats.Network.Interfaces[0]
|
||||
}
|
||||
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
func parseUlimit(value string) (int64, error) {
|
||||
num, err := strconv.ParseInt(value, 10, 64)
|
||||
if err != nil {
|
||||
if strings.EqualFold(value, "unlimited") {
|
||||
// -1 implies unlimited except for priority and nice; man limits.conf
|
||||
num = -1
|
||||
} else {
|
||||
// Value is not a number or "unlimited"; return an error
|
||||
return 0, fmt.Errorf("unable to parse limit: %s", value)
|
||||
}
|
||||
}
|
||||
return num, nil
|
||||
}
|
||||
|
||||
func processLimitsFile(fileData string) []info.UlimitSpec {
|
||||
const maxOpenFilesLinePrefix = "Max open files"
|
||||
|
||||
limits := strings.Split(fileData, "\n")
|
||||
ulimits := make([]info.UlimitSpec, 0, len(limits))
|
||||
for _, lim := range limits {
|
||||
// Skip any headers/footers
|
||||
if strings.HasPrefix(lim, "Max open files") {
|
||||
// Remove line prefix
|
||||
ulimit, err := processMaxOpenFileLimitLine(
|
||||
"max_open_files",
|
||||
lim[len(maxOpenFilesLinePrefix):],
|
||||
)
|
||||
if err == nil {
|
||||
ulimits = append(ulimits, ulimit)
|
||||
}
|
||||
}
|
||||
}
|
||||
return ulimits
|
||||
}
|
||||
|
||||
// Any caller of processMaxOpenFileLimitLine must ensure that the name prefix is already removed from the limit line.
|
||||
// with the "Max open files" prefix.
|
||||
func processMaxOpenFileLimitLine(name, line string) (info.UlimitSpec, error) {
|
||||
// Remove any leading whitespace
|
||||
line = strings.TrimSpace(line)
|
||||
// Split on whitespace
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) != 3 {
|
||||
return info.UlimitSpec{}, fmt.Errorf("unable to parse max open files line: %s", line)
|
||||
}
|
||||
// The first field is the soft limit, the second is the hard limit
|
||||
soft, err := parseUlimit(fields[0])
|
||||
if err != nil {
|
||||
return info.UlimitSpec{}, err
|
||||
}
|
||||
hard, err := parseUlimit(fields[1])
|
||||
if err != nil {
|
||||
return info.UlimitSpec{}, err
|
||||
}
|
||||
return info.UlimitSpec{
|
||||
Name: name,
|
||||
SoftLimit: soft,
|
||||
HardLimit: hard,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func processRootProcUlimits(rootFs string, rootPid int) []info.UlimitSpec {
|
||||
filePath := path.Join(rootFs, "/proc", strconv.Itoa(rootPid), "limits")
|
||||
out, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
klog.V(4).Infof("error while listing directory %q to read ulimits: %v", filePath, err)
|
||||
return []info.UlimitSpec{}
|
||||
}
|
||||
return processLimitsFile(string(out))
|
||||
}
|
||||
|
||||
func processStatsFromProcs(rootFs string, cgroupPath string, rootPid int) (info.ProcessStats, error) {
|
||||
var fdCount, socketCount uint64
|
||||
filePath := path.Join(cgroupPath, "cgroup.procs")
|
||||
out, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return info.ProcessStats{}, fmt.Errorf("couldn't open cpu cgroup procs file %v : %v", filePath, err)
|
||||
}
|
||||
|
||||
pids := strings.Split(string(out), "\n")
|
||||
|
||||
// EOL is also treated as a new line while reading "cgroup.procs" file with os.ReadFile.
|
||||
// The last value is an empty string "". Ex: pids = ["22", "1223", ""]
|
||||
// Trim the last value
|
||||
if len(pids) != 0 && pids[len(pids)-1] == "" {
|
||||
pids = pids[:len(pids)-1]
|
||||
}
|
||||
|
||||
for _, pid := range pids {
|
||||
dirPath := path.Join(rootFs, "/proc", pid, "fd")
|
||||
fds, err := os.ReadDir(dirPath)
|
||||
if err != nil {
|
||||
klog.V(4).Infof("error while listing directory %q to measure fd count: %v", dirPath, err)
|
||||
continue
|
||||
}
|
||||
fdCount += uint64(len(fds))
|
||||
for _, fd := range fds {
|
||||
fdPath := path.Join(dirPath, fd.Name())
|
||||
linkName, err := os.Readlink(fdPath)
|
||||
if err != nil {
|
||||
klog.V(4).Infof("error while reading %q link: %v", fdPath, err)
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(linkName, "socket") {
|
||||
socketCount++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
processStats := info.ProcessStats{
|
||||
ProcessCount: uint64(len(pids)),
|
||||
FdCount: fdCount,
|
||||
SocketCount: socketCount,
|
||||
}
|
||||
|
||||
if rootPid > 0 {
|
||||
processStats.Ulimits = processRootProcUlimits(rootFs, rootPid)
|
||||
}
|
||||
|
||||
return processStats, nil
|
||||
}
|
||||
|
||||
func (h *Handler) schedulerStatsFromProcs() (info.CpuSchedstat, error) {
|
||||
pids, err := h.cgroupManager.GetAllPids()
|
||||
if err != nil {
|
||||
return info.CpuSchedstat{}, fmt.Errorf("Could not get PIDs for container %d: %w", h.pid, err)
|
||||
}
|
||||
alivePids := make(map[int]struct{}, len(pids))
|
||||
for _, pid := range pids {
|
||||
f, err := os.Open(path.Join(h.rootFs, "proc", strconv.Itoa(pid), "schedstat"))
|
||||
if err != nil {
|
||||
return info.CpuSchedstat{}, fmt.Errorf("couldn't open scheduler statistics for process %d: %v", pid, err)
|
||||
}
|
||||
defer f.Close()
|
||||
contents, err := io.ReadAll(f)
|
||||
if err != nil {
|
||||
return info.CpuSchedstat{}, fmt.Errorf("couldn't read scheduler statistics for process %d: %v", pid, err)
|
||||
}
|
||||
alivePids[pid] = struct{}{}
|
||||
rawMetrics := bytes.Split(bytes.TrimRight(contents, "\n"), []byte(" "))
|
||||
if len(rawMetrics) != 3 {
|
||||
return info.CpuSchedstat{}, fmt.Errorf("unexpected number of metrics in schedstat file for process %d", pid)
|
||||
}
|
||||
cacheEntry, ok := h.pidMetricsCache[pid]
|
||||
if !ok {
|
||||
cacheEntry = &info.CpuSchedstat{}
|
||||
h.pidMetricsCache[pid] = cacheEntry
|
||||
}
|
||||
for i, rawMetric := range rawMetrics {
|
||||
metric, err := strconv.ParseUint(string(rawMetric), 10, 64)
|
||||
if err != nil {
|
||||
return info.CpuSchedstat{}, fmt.Errorf("parsing error while reading scheduler statistics for process: %d: %v", pid, err)
|
||||
}
|
||||
switch i {
|
||||
case 0:
|
||||
cacheEntry.RunTime = metric
|
||||
case 1:
|
||||
cacheEntry.RunqueueTime = metric
|
||||
case 2:
|
||||
cacheEntry.RunPeriods = metric
|
||||
}
|
||||
}
|
||||
}
|
||||
schedstats := h.pidMetricsSaved // copy
|
||||
for p, v := range h.pidMetricsCache {
|
||||
schedstats.RunPeriods += v.RunPeriods
|
||||
schedstats.RunqueueTime += v.RunqueueTime
|
||||
schedstats.RunTime += v.RunTime
|
||||
if _, alive := alivePids[p]; !alive {
|
||||
// PID p is gone: accumulate its stats ...
|
||||
h.pidMetricsSaved.RunPeriods += v.RunPeriods
|
||||
h.pidMetricsSaved.RunqueueTime += v.RunqueueTime
|
||||
h.pidMetricsSaved.RunTime += v.RunTime
|
||||
// ... and remove its cache entry, to prevent
|
||||
// pidMetricsCache from growing.
|
||||
delete(h.pidMetricsCache, p)
|
||||
}
|
||||
}
|
||||
return schedstats, nil
|
||||
}
|
||||
|
||||
// referencedBytesStat gets and clears referenced bytes
|
||||
// see: https://github.com/brendangregg/wss#wsspl-referenced-page-flag
|
||||
func referencedBytesStat(pids []int, cycles uint64, resetInterval uint64) (uint64, error) {
|
||||
referencedKBytes, err := getReferencedKBytes(pids)
|
||||
if err != nil {
|
||||
return uint64(0), err
|
||||
}
|
||||
|
||||
err = clearReferencedBytes(pids, cycles, resetInterval)
|
||||
if err != nil {
|
||||
return uint64(0), err
|
||||
}
|
||||
return referencedKBytes * 1024, nil
|
||||
}
|
||||
|
||||
func getReferencedKBytes(pids []int) (uint64, error) {
|
||||
referencedKBytes := uint64(0)
|
||||
readSmapsContent := false
|
||||
foundMatch := false
|
||||
for _, pid := range pids {
|
||||
smapsFilePath := fmt.Sprintf(smapsFilePathPattern, pid)
|
||||
smapsContent, err := os.ReadFile(smapsFilePath)
|
||||
if err != nil {
|
||||
klog.V(5).Infof("Cannot read %s file, err: %s", smapsFilePath, err)
|
||||
if os.IsNotExist(err) {
|
||||
continue // smaps file does not exists for all PIDs
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
readSmapsContent = true
|
||||
|
||||
allMatches := referencedRegexp.FindAllSubmatch(smapsContent, -1)
|
||||
if len(allMatches) == 0 {
|
||||
klog.V(5).Infof("Not found any information about referenced bytes in %s file", smapsFilePath)
|
||||
continue // referenced bytes may not exist in smaps file
|
||||
}
|
||||
|
||||
for _, matches := range allMatches {
|
||||
if len(matches) != 2 {
|
||||
return 0, fmt.Errorf("failed to match regexp in output: %s", string(smapsContent))
|
||||
}
|
||||
foundMatch = true
|
||||
referenced, err := strconv.ParseUint(string(matches[1]), 10, 64)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
referencedKBytes += referenced
|
||||
}
|
||||
}
|
||||
|
||||
if len(pids) != 0 {
|
||||
if !readSmapsContent {
|
||||
klog.Warningf("Cannot read smaps files for any PID from %s", "CONTAINER")
|
||||
} else if !foundMatch {
|
||||
klog.Warningf("Not found any information about referenced bytes in smaps files for any PID from %s", "CONTAINER")
|
||||
}
|
||||
}
|
||||
return referencedKBytes, nil
|
||||
}
|
||||
|
||||
func clearReferencedBytes(pids []int, cycles uint64, resetInterval uint64) error {
|
||||
if resetInterval == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if cycles%resetInterval == 0 {
|
||||
for _, pid := range pids {
|
||||
clearRefsFilePath := fmt.Sprintf(clearRefsFilePathPattern, pid)
|
||||
clerRefsFile, err := os.OpenFile(clearRefsFilePath, os.O_WRONLY, 0o644)
|
||||
if err != nil {
|
||||
// clear_refs file may not exist for all PIDs
|
||||
continue
|
||||
}
|
||||
_, err = clerRefsFile.WriteString("1\n")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = clerRefsFile.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func networkStatsFromProc(rootFs string, pid int) ([]info.InterfaceStats, error) {
|
||||
netStatsFile := path.Join(rootFs, "proc", strconv.Itoa(pid), "/net/dev")
|
||||
|
||||
ifaceStats, err := scanInterfaceStats(netStatsFile)
|
||||
if err != nil {
|
||||
return []info.InterfaceStats{}, fmt.Errorf("couldn't read network stats: %v", err)
|
||||
}
|
||||
|
||||
return ifaceStats, nil
|
||||
}
|
||||
|
||||
var ignoredDevicePrefixes = []string{"lo", "veth", "docker", "nerdctl"}
|
||||
|
||||
func isIgnoredDevice(ifName string) bool {
|
||||
for _, prefix := range ignoredDevicePrefixes {
|
||||
if strings.HasPrefix(strings.ToLower(ifName), prefix) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func scanInterfaceStats(netStatsFile string) ([]info.InterfaceStats, error) {
|
||||
file, err := os.Open(netStatsFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failure opening %s: %v", netStatsFile, err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
|
||||
// Discard header lines
|
||||
for i := 0; i < 2; i++ {
|
||||
if b := scanner.Scan(); !b {
|
||||
return nil, scanner.Err()
|
||||
}
|
||||
}
|
||||
|
||||
stats := []info.InterfaceStats{}
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
line = strings.Replace(line, ":", "", -1)
|
||||
|
||||
fields := strings.Fields(line)
|
||||
// If the format of the line is invalid then don't trust any of the stats
|
||||
// in this file.
|
||||
if len(fields) != 17 {
|
||||
return nil, fmt.Errorf("invalid interface stats line: %v", line)
|
||||
}
|
||||
|
||||
devName := fields[0]
|
||||
if isIgnoredDevice(devName) {
|
||||
continue
|
||||
}
|
||||
|
||||
i := info.InterfaceStats{
|
||||
Name: devName,
|
||||
}
|
||||
|
||||
statFields := append(fields[1:5], fields[9:13]...)
|
||||
statPointers := []*uint64{
|
||||
&i.RxBytes, &i.RxPackets, &i.RxErrors, &i.RxDropped,
|
||||
&i.TxBytes, &i.TxPackets, &i.TxErrors, &i.TxDropped,
|
||||
}
|
||||
|
||||
err := setInterfaceStatValues(statFields, statPointers)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot parse interface stats (%v): %v", err, line)
|
||||
}
|
||||
|
||||
stats = append(stats, i)
|
||||
}
|
||||
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
func setInterfaceStatValues(fields []string, pointers []*uint64) error {
|
||||
for i, v := range fields {
|
||||
val, err := strconv.ParseUint(v, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*pointers[i] = val
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func tcpStatsFromProc(rootFs string, pid int, file string) (info.TcpStat, error) {
|
||||
tcpStatsFile := path.Join(rootFs, "proc", strconv.Itoa(pid), file)
|
||||
|
||||
tcpStats, err := scanTCPStats(tcpStatsFile)
|
||||
if err != nil {
|
||||
return tcpStats, fmt.Errorf("couldn't read tcp stats: %v", err)
|
||||
}
|
||||
|
||||
return tcpStats, nil
|
||||
}
|
||||
|
||||
func advancedTCPStatsFromProc(rootFs string, pid int, file1, file2 string) (info.TcpAdvancedStat, error) {
|
||||
var advancedStats info.TcpAdvancedStat
|
||||
var err error
|
||||
|
||||
netstatFile := path.Join(rootFs, "proc", strconv.Itoa(pid), file1)
|
||||
err = scanAdvancedTCPStats(&advancedStats, netstatFile)
|
||||
if err != nil {
|
||||
return advancedStats, err
|
||||
}
|
||||
|
||||
snmpFile := path.Join(rootFs, "proc", strconv.Itoa(pid), file2)
|
||||
err = scanAdvancedTCPStats(&advancedStats, snmpFile)
|
||||
if err != nil {
|
||||
return advancedStats, err
|
||||
}
|
||||
|
||||
return advancedStats, nil
|
||||
}
|
||||
|
||||
func scanAdvancedTCPStats(advancedStats *info.TcpAdvancedStat, advancedTCPStatsFile string) error {
|
||||
data, err := os.ReadFile(advancedTCPStatsFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failure opening %s: %v", advancedTCPStatsFile, err)
|
||||
}
|
||||
|
||||
reader := strings.NewReader(string(data))
|
||||
scanner := bufio.NewScanner(reader)
|
||||
scanner.Split(bufio.ScanLines)
|
||||
|
||||
advancedTCPStats := make(map[string]interface{})
|
||||
for scanner.Scan() {
|
||||
nameParts := strings.Split(scanner.Text(), " ")
|
||||
scanner.Scan()
|
||||
valueParts := strings.Split(scanner.Text(), " ")
|
||||
// Remove trailing :. and ignore non-tcp
|
||||
protocol := nameParts[0][:len(nameParts[0])-1]
|
||||
if protocol != "TcpExt" && protocol != "Tcp" {
|
||||
continue
|
||||
}
|
||||
if len(nameParts) != len(valueParts) {
|
||||
return fmt.Errorf("mismatch field count mismatch in %s: %s",
|
||||
advancedTCPStatsFile, protocol)
|
||||
}
|
||||
for i := 1; i < len(nameParts); i++ {
|
||||
if strings.Contains(valueParts[i], "-") {
|
||||
vInt64, err := strconv.ParseInt(valueParts[i], 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("decode value: %s to int64 error: %s", valueParts[i], err)
|
||||
}
|
||||
advancedTCPStats[nameParts[i]] = vInt64
|
||||
} else {
|
||||
vUint64, err := strconv.ParseUint(valueParts[i], 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("decode value: %s to uint64 error: %s", valueParts[i], err)
|
||||
}
|
||||
advancedTCPStats[nameParts[i]] = vUint64
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
b, err := json.Marshal(advancedTCPStats)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(b, advancedStats)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return scanner.Err()
|
||||
}
|
||||
|
||||
func scanTCPStats(tcpStatsFile string) (info.TcpStat, error) {
|
||||
var stats info.TcpStat
|
||||
|
||||
data, err := os.ReadFile(tcpStatsFile)
|
||||
if err != nil {
|
||||
return stats, fmt.Errorf("failure opening %s: %v", tcpStatsFile, err)
|
||||
}
|
||||
|
||||
tcpStateMap := map[string]uint64{
|
||||
"01": 0, // ESTABLISHED
|
||||
"02": 0, // SYN_SENT
|
||||
"03": 0, // SYN_RECV
|
||||
"04": 0, // FIN_WAIT1
|
||||
"05": 0, // FIN_WAIT2
|
||||
"06": 0, // TIME_WAIT
|
||||
"07": 0, // CLOSE
|
||||
"08": 0, // CLOSE_WAIT
|
||||
"09": 0, // LAST_ACK
|
||||
"0A": 0, // LISTEN
|
||||
"0B": 0, // CLOSING
|
||||
}
|
||||
|
||||
reader := strings.NewReader(string(data))
|
||||
scanner := bufio.NewScanner(reader)
|
||||
|
||||
scanner.Split(bufio.ScanLines)
|
||||
|
||||
// Discard header line
|
||||
if b := scanner.Scan(); !b {
|
||||
return stats, scanner.Err()
|
||||
}
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
|
||||
state := strings.Fields(line)
|
||||
// TCP state is the 4th field.
|
||||
// Format: sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode
|
||||
tcpState := state[3]
|
||||
_, ok := tcpStateMap[tcpState]
|
||||
if !ok {
|
||||
return stats, fmt.Errorf("invalid TCP stats line: %v", line)
|
||||
}
|
||||
tcpStateMap[tcpState]++
|
||||
}
|
||||
|
||||
stats = info.TcpStat{
|
||||
Established: tcpStateMap["01"],
|
||||
SynSent: tcpStateMap["02"],
|
||||
SynRecv: tcpStateMap["03"],
|
||||
FinWait1: tcpStateMap["04"],
|
||||
FinWait2: tcpStateMap["05"],
|
||||
TimeWait: tcpStateMap["06"],
|
||||
Close: tcpStateMap["07"],
|
||||
CloseWait: tcpStateMap["08"],
|
||||
LastAck: tcpStateMap["09"],
|
||||
Listen: tcpStateMap["0A"],
|
||||
Closing: tcpStateMap["0B"],
|
||||
}
|
||||
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
func udpStatsFromProc(rootFs string, pid int, file string) (info.UdpStat, error) {
|
||||
var err error
|
||||
var udpStats info.UdpStat
|
||||
|
||||
udpStatsFile := path.Join(rootFs, "proc", strconv.Itoa(pid), file)
|
||||
|
||||
r, err := os.Open(udpStatsFile)
|
||||
if err != nil {
|
||||
return udpStats, fmt.Errorf("failure opening %s: %v", udpStatsFile, err)
|
||||
}
|
||||
|
||||
udpStats, err = scanUDPStats(r)
|
||||
if err != nil {
|
||||
return udpStats, fmt.Errorf("couldn't read udp stats: %v", err)
|
||||
}
|
||||
|
||||
return udpStats, nil
|
||||
}
|
||||
|
||||
func scanUDPStats(r io.Reader) (info.UdpStat, error) {
|
||||
var stats info.UdpStat
|
||||
|
||||
scanner := bufio.NewScanner(r)
|
||||
scanner.Split(bufio.ScanLines)
|
||||
|
||||
// Discard header line
|
||||
if b := scanner.Scan(); !b {
|
||||
return stats, scanner.Err()
|
||||
}
|
||||
|
||||
listening := uint64(0)
|
||||
dropped := uint64(0)
|
||||
rxQueued := uint64(0)
|
||||
txQueued := uint64(0)
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
// Format: sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode ref pointer drops
|
||||
|
||||
listening++
|
||||
|
||||
fs := strings.Fields(line)
|
||||
if len(fs) != 13 {
|
||||
continue
|
||||
}
|
||||
|
||||
rx, tx := uint64(0), uint64(0)
|
||||
fmt.Sscanf(fs[4], "%X:%X", &rx, &tx)
|
||||
rxQueued += rx
|
||||
txQueued += tx
|
||||
|
||||
d, err := strconv.Atoi(string(fs[12]))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
dropped += uint64(d)
|
||||
}
|
||||
|
||||
stats = info.UdpStat{
|
||||
Listen: listening,
|
||||
Dropped: dropped,
|
||||
RxQueued: rxQueued,
|
||||
TxQueued: txQueued,
|
||||
}
|
||||
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
func (h *Handler) GetProcesses() ([]int, error) {
|
||||
pids, err := h.cgroupManager.GetPids()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pids, nil
|
||||
}
|
||||
|
||||
// Convert libcontainer stats to info.ContainerStats.
|
||||
func setCPUStats(s *cgroups.Stats, ret *info.ContainerStats, withPerCPU bool) {
|
||||
ret.Cpu.Usage.User = s.CpuStats.CpuUsage.UsageInUsermode
|
||||
ret.Cpu.Usage.System = s.CpuStats.CpuUsage.UsageInKernelmode
|
||||
ret.Cpu.Usage.Total = s.CpuStats.CpuUsage.TotalUsage
|
||||
ret.Cpu.CFS.Periods = s.CpuStats.ThrottlingData.Periods
|
||||
ret.Cpu.CFS.ThrottledPeriods = s.CpuStats.ThrottlingData.ThrottledPeriods
|
||||
ret.Cpu.CFS.ThrottledTime = s.CpuStats.ThrottlingData.ThrottledTime
|
||||
|
||||
if !withPerCPU {
|
||||
return
|
||||
}
|
||||
if len(s.CpuStats.CpuUsage.PercpuUsage) == 0 {
|
||||
// libcontainer's 'GetStats' can leave 'PercpuUsage' nil if it skipped the
|
||||
// cpuacct subsystem.
|
||||
return
|
||||
}
|
||||
ret.Cpu.Usage.PerCpu = s.CpuStats.CpuUsage.PercpuUsage
|
||||
}
|
||||
|
||||
func setDiskIoStats(s *cgroups.Stats, ret *info.ContainerStats) {
|
||||
ret.DiskIo.IoServiceBytes = diskStatsCopy(s.BlkioStats.IoServiceBytesRecursive)
|
||||
ret.DiskIo.IoServiced = diskStatsCopy(s.BlkioStats.IoServicedRecursive)
|
||||
ret.DiskIo.IoQueued = diskStatsCopy(s.BlkioStats.IoQueuedRecursive)
|
||||
ret.DiskIo.Sectors = diskStatsCopy(s.BlkioStats.SectorsRecursive)
|
||||
ret.DiskIo.IoServiceTime = diskStatsCopy(s.BlkioStats.IoServiceTimeRecursive)
|
||||
ret.DiskIo.IoWaitTime = diskStatsCopy(s.BlkioStats.IoWaitTimeRecursive)
|
||||
ret.DiskIo.IoMerged = diskStatsCopy(s.BlkioStats.IoMergedRecursive)
|
||||
ret.DiskIo.IoTime = diskStatsCopy(s.BlkioStats.IoTimeRecursive)
|
||||
}
|
||||
|
||||
func setMemoryStats(s *cgroups.Stats, ret *info.ContainerStats) {
|
||||
ret.Memory.Usage = s.MemoryStats.Usage.Usage
|
||||
ret.Memory.MaxUsage = s.MemoryStats.Usage.MaxUsage
|
||||
ret.Memory.Failcnt = s.MemoryStats.Usage.Failcnt
|
||||
ret.Memory.KernelUsage = s.MemoryStats.KernelUsage.Usage
|
||||
|
||||
if cgroups.IsCgroup2UnifiedMode() {
|
||||
ret.Memory.Cache = s.MemoryStats.Stats["file"]
|
||||
ret.Memory.RSS = s.MemoryStats.Stats["anon"]
|
||||
ret.Memory.Swap = s.MemoryStats.SwapUsage.Usage - s.MemoryStats.Usage.Usage
|
||||
ret.Memory.MappedFile = s.MemoryStats.Stats["file_mapped"]
|
||||
} else if s.MemoryStats.UseHierarchy {
|
||||
ret.Memory.Cache = s.MemoryStats.Stats["total_cache"]
|
||||
ret.Memory.RSS = s.MemoryStats.Stats["total_rss"]
|
||||
ret.Memory.Swap = s.MemoryStats.Stats["total_swap"]
|
||||
ret.Memory.MappedFile = s.MemoryStats.Stats["total_mapped_file"]
|
||||
} else {
|
||||
ret.Memory.Cache = s.MemoryStats.Stats["cache"]
|
||||
ret.Memory.RSS = s.MemoryStats.Stats["rss"]
|
||||
ret.Memory.Swap = s.MemoryStats.Stats["swap"]
|
||||
ret.Memory.MappedFile = s.MemoryStats.Stats["mapped_file"]
|
||||
}
|
||||
if v, ok := s.MemoryStats.Stats["pgfault"]; ok {
|
||||
ret.Memory.ContainerData.Pgfault = v
|
||||
ret.Memory.HierarchicalData.Pgfault = v
|
||||
}
|
||||
if v, ok := s.MemoryStats.Stats["pgmajfault"]; ok {
|
||||
ret.Memory.ContainerData.Pgmajfault = v
|
||||
ret.Memory.HierarchicalData.Pgmajfault = v
|
||||
}
|
||||
|
||||
inactiveFileKeyName := "total_inactive_file"
|
||||
if cgroups.IsCgroup2UnifiedMode() {
|
||||
inactiveFileKeyName = "inactive_file"
|
||||
}
|
||||
|
||||
activeFileKeyName := "total_active_file"
|
||||
if cgroups.IsCgroup2UnifiedMode() {
|
||||
activeFileKeyName = "active_file"
|
||||
}
|
||||
|
||||
if v, ok := s.MemoryStats.Stats[activeFileKeyName]; ok {
|
||||
ret.Memory.TotalActiveFile = v
|
||||
}
|
||||
|
||||
workingSet := ret.Memory.Usage
|
||||
if v, ok := s.MemoryStats.Stats[inactiveFileKeyName]; ok {
|
||||
ret.Memory.TotalInactiveFile = v
|
||||
if workingSet < v {
|
||||
workingSet = 0
|
||||
} else {
|
||||
workingSet -= v
|
||||
}
|
||||
}
|
||||
ret.Memory.WorkingSet = workingSet
|
||||
}
|
||||
|
||||
func setCPUSetStats(s *cgroups.Stats, ret *info.ContainerStats) {
|
||||
ret.CpuSet.MemoryMigrate = s.CPUSetStats.MemoryMigrate
|
||||
}
|
||||
|
||||
func getNumaStats(memoryStats map[uint8]uint64) map[uint8]uint64 {
|
||||
stats := make(map[uint8]uint64, len(memoryStats))
|
||||
for node, usage := range memoryStats {
|
||||
stats[node] = usage
|
||||
}
|
||||
return stats
|
||||
}
|
||||
|
||||
func setMemoryNumaStats(s *cgroups.Stats, ret *info.ContainerStats) {
|
||||
ret.Memory.ContainerData.NumaStats.File = getNumaStats(s.MemoryStats.PageUsageByNUMA.File.Nodes)
|
||||
ret.Memory.ContainerData.NumaStats.Anon = getNumaStats(s.MemoryStats.PageUsageByNUMA.Anon.Nodes)
|
||||
ret.Memory.ContainerData.NumaStats.Unevictable = getNumaStats(s.MemoryStats.PageUsageByNUMA.Unevictable.Nodes)
|
||||
|
||||
ret.Memory.HierarchicalData.NumaStats.File = getNumaStats(s.MemoryStats.PageUsageByNUMA.Hierarchical.File.Nodes)
|
||||
ret.Memory.HierarchicalData.NumaStats.Anon = getNumaStats(s.MemoryStats.PageUsageByNUMA.Hierarchical.Anon.Nodes)
|
||||
ret.Memory.HierarchicalData.NumaStats.Unevictable = getNumaStats(s.MemoryStats.PageUsageByNUMA.Hierarchical.Unevictable.Nodes)
|
||||
}
|
||||
|
||||
func setHugepageStats(s *cgroups.Stats, ret *info.ContainerStats) {
|
||||
ret.Hugetlb = make(map[string]info.HugetlbStats)
|
||||
for k, v := range s.HugetlbStats {
|
||||
ret.Hugetlb[k] = info.HugetlbStats{
|
||||
Usage: v.Usage,
|
||||
MaxUsage: v.MaxUsage,
|
||||
Failcnt: v.Failcnt,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// read from pids path not cpu
|
||||
func setThreadsStats(s *cgroups.Stats, ret *info.ContainerStats) {
|
||||
if s != nil {
|
||||
ret.Processes.ThreadsCurrent = s.PidsStats.Current
|
||||
ret.Processes.ThreadsMax = s.PidsStats.Limit
|
||||
}
|
||||
}
|
||||
|
||||
func newContainerStats(cgroupStats *cgroups.Stats, includedMetrics container.MetricSet) *info.ContainerStats {
|
||||
ret := &info.ContainerStats{
|
||||
Timestamp: time.Now(),
|
||||
}
|
||||
|
||||
if s := cgroupStats; s != nil {
|
||||
setCPUStats(s, ret, includedMetrics.Has(container.PerCpuUsageMetrics))
|
||||
if includedMetrics.Has(container.DiskIOMetrics) {
|
||||
setDiskIoStats(s, ret)
|
||||
}
|
||||
setMemoryStats(s, ret)
|
||||
if includedMetrics.Has(container.MemoryNumaMetrics) {
|
||||
setMemoryNumaStats(s, ret)
|
||||
}
|
||||
if includedMetrics.Has(container.HugetlbUsageMetrics) {
|
||||
setHugepageStats(s, ret)
|
||||
}
|
||||
if includedMetrics.Has(container.CPUSetMetrics) {
|
||||
setCPUSetStats(s, ret)
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
170
e2e/vendor/github.com/google/cadvisor/container/libcontainer/helpers.go
generated
vendored
Normal file
170
e2e/vendor/github.com/google/cadvisor/container/libcontainer/helpers.go
generated
vendored
Normal file
@ -0,0 +1,170 @@
|
||||
// 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 libcontainer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
info "github.com/google/cadvisor/info/v1"
|
||||
|
||||
"github.com/opencontainers/runc/libcontainer/cgroups"
|
||||
|
||||
"github.com/google/cadvisor/container"
|
||||
|
||||
fs "github.com/opencontainers/runc/libcontainer/cgroups/fs"
|
||||
fs2 "github.com/opencontainers/runc/libcontainer/cgroups/fs2"
|
||||
configs "github.com/opencontainers/runc/libcontainer/configs"
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
// GetCgroupSubsystems returns information about the cgroup subsystems that are
|
||||
// of interest as a map of cgroup controllers to their mount points.
|
||||
// For example, "cpu" -> "/sys/fs/cgroup/cpu".
|
||||
//
|
||||
// The incudeMetrics arguments specifies which metrics are requested,
|
||||
// and is used to filter out some cgroups and their mounts. If nil,
|
||||
// all supported cgroup subsystems are included.
|
||||
//
|
||||
// For cgroup v2, includedMetrics argument is unused, the only map key is ""
|
||||
// (empty string), and the value is the unified cgroup mount point.
|
||||
func GetCgroupSubsystems(includedMetrics container.MetricSet) (map[string]string, error) {
|
||||
if cgroups.IsCgroup2UnifiedMode() {
|
||||
return map[string]string{"": fs2.UnifiedMountpoint}, nil
|
||||
}
|
||||
// Get all cgroup mounts.
|
||||
allCgroups, err := cgroups.GetCgroupMounts(true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return getCgroupSubsystemsHelper(allCgroups, includedMetrics)
|
||||
}
|
||||
|
||||
func getCgroupSubsystemsHelper(allCgroups []cgroups.Mount, includedMetrics container.MetricSet) (map[string]string, error) {
|
||||
if len(allCgroups) == 0 {
|
||||
return nil, fmt.Errorf("failed to find cgroup mounts")
|
||||
}
|
||||
|
||||
// Trim the mounts to only the subsystems we care about.
|
||||
mountPoints := make(map[string]string, len(allCgroups))
|
||||
for _, mount := range allCgroups {
|
||||
for _, subsystem := range mount.Subsystems {
|
||||
if !needSubsys(subsystem, includedMetrics) {
|
||||
continue
|
||||
}
|
||||
if _, ok := mountPoints[subsystem]; ok {
|
||||
// duplicate mount for this subsystem; use the first one we saw
|
||||
klog.V(5).Infof("skipping %s, already using mount at %s", mount.Mountpoint, mountPoints[subsystem])
|
||||
continue
|
||||
}
|
||||
mountPoints[subsystem] = mount.Mountpoint
|
||||
}
|
||||
}
|
||||
|
||||
return mountPoints, nil
|
||||
}
|
||||
|
||||
// A map of cgroup subsystems we support listing (should be the minimal set
|
||||
// we need stats from) to a respective MetricKind.
|
||||
var supportedSubsystems = map[string]container.MetricKind{
|
||||
"cpu": container.CpuUsageMetrics,
|
||||
"cpuacct": container.CpuUsageMetrics,
|
||||
"memory": container.MemoryUsageMetrics,
|
||||
"hugetlb": container.HugetlbUsageMetrics,
|
||||
"pids": container.ProcessMetrics,
|
||||
"cpuset": container.CPUSetMetrics,
|
||||
"blkio": container.DiskIOMetrics,
|
||||
"io": container.DiskIOMetrics,
|
||||
"devices": "",
|
||||
"perf_event": container.PerfMetrics,
|
||||
}
|
||||
|
||||
// Check if this cgroup subsystem/controller is of use.
|
||||
func needSubsys(name string, metrics container.MetricSet) bool {
|
||||
// Check if supported.
|
||||
metric, supported := supportedSubsystems[name]
|
||||
if !supported {
|
||||
return false
|
||||
}
|
||||
// Check if needed.
|
||||
if metrics == nil || metric == "" {
|
||||
return true
|
||||
}
|
||||
|
||||
return metrics.Has(metric)
|
||||
}
|
||||
|
||||
func diskStatsCopy0(major, minor uint64) *info.PerDiskStats {
|
||||
disk := info.PerDiskStats{
|
||||
Major: major,
|
||||
Minor: minor,
|
||||
}
|
||||
disk.Stats = make(map[string]uint64)
|
||||
return &disk
|
||||
}
|
||||
|
||||
type diskKey struct {
|
||||
Major uint64
|
||||
Minor uint64
|
||||
}
|
||||
|
||||
func diskStatsCopy1(diskStat map[diskKey]*info.PerDiskStats) []info.PerDiskStats {
|
||||
i := 0
|
||||
stat := make([]info.PerDiskStats, len(diskStat))
|
||||
for _, disk := range diskStat {
|
||||
stat[i] = *disk
|
||||
i++
|
||||
}
|
||||
return stat
|
||||
}
|
||||
|
||||
func diskStatsCopy(blkioStats []cgroups.BlkioStatEntry) (stat []info.PerDiskStats) {
|
||||
if len(blkioStats) == 0 {
|
||||
return
|
||||
}
|
||||
diskStat := make(map[diskKey]*info.PerDiskStats)
|
||||
for i := range blkioStats {
|
||||
major := blkioStats[i].Major
|
||||
minor := blkioStats[i].Minor
|
||||
key := diskKey{
|
||||
Major: major,
|
||||
Minor: minor,
|
||||
}
|
||||
diskp, ok := diskStat[key]
|
||||
if !ok {
|
||||
diskp = diskStatsCopy0(major, minor)
|
||||
diskStat[key] = diskp
|
||||
}
|
||||
op := blkioStats[i].Op
|
||||
if op == "" {
|
||||
op = "Count"
|
||||
}
|
||||
diskp.Stats[op] = blkioStats[i].Value
|
||||
}
|
||||
return diskStatsCopy1(diskStat)
|
||||
}
|
||||
|
||||
func NewCgroupManager(name string, paths map[string]string) (cgroups.Manager, error) {
|
||||
config := &configs.Cgroup{
|
||||
Name: name,
|
||||
Resources: &configs.Resources{},
|
||||
}
|
||||
if cgroups.IsCgroup2UnifiedMode() {
|
||||
path := paths[""]
|
||||
return fs2.NewManager(config, path)
|
||||
}
|
||||
|
||||
return fs.NewManager(config, paths)
|
||||
}
|
114
e2e/vendor/github.com/google/cadvisor/container/raw/factory.go
generated
vendored
Normal file
114
e2e/vendor/github.com/google/cadvisor/container/raw/factory.go
generated
vendored
Normal file
@ -0,0 +1,114 @@
|
||||
// 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 raw
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"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"
|
||||
watch "github.com/google/cadvisor/watcher"
|
||||
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
var (
|
||||
DockerOnly = flag.Bool("docker_only", false, "Only report docker containers in addition to root stats")
|
||||
disableRootCgroupStats = flag.Bool("disable_root_cgroup_stats", false, "Disable collecting root Cgroup stats")
|
||||
)
|
||||
|
||||
type rawFactory struct {
|
||||
// Factory for machine information.
|
||||
machineInfoFactory info.MachineInfoFactory
|
||||
|
||||
// Information about the cgroup subsystems.
|
||||
cgroupSubsystems map[string]string
|
||||
|
||||
// Information about mounted filesystems.
|
||||
fsInfo fs.FsInfo
|
||||
|
||||
// Watcher for inotify events.
|
||||
watcher *common.InotifyWatcher
|
||||
|
||||
// List of metrics to be included.
|
||||
includedMetrics map[container.MetricKind]struct{}
|
||||
|
||||
// List of raw container cgroup path prefix whitelist.
|
||||
rawPrefixWhiteList []string
|
||||
}
|
||||
|
||||
func (f *rawFactory) String() string {
|
||||
return "raw"
|
||||
}
|
||||
|
||||
func (f *rawFactory) NewContainerHandler(name string, metadataEnvAllowList []string, inHostNamespace bool) (container.ContainerHandler, error) {
|
||||
rootFs := "/"
|
||||
if !inHostNamespace {
|
||||
rootFs = "/rootfs"
|
||||
}
|
||||
return newRawContainerHandler(name, f.cgroupSubsystems, f.machineInfoFactory, f.fsInfo, f.watcher, rootFs, f.includedMetrics)
|
||||
}
|
||||
|
||||
// The raw factory can handle any container. If --docker_only is set to true, non-docker containers are ignored except for "/" and those whitelisted by raw_cgroup_prefix_whitelist flag.
|
||||
func (f *rawFactory) CanHandleAndAccept(name string) (bool, bool, error) {
|
||||
if name == "/" {
|
||||
return true, true, nil
|
||||
}
|
||||
if *DockerOnly && f.rawPrefixWhiteList[0] == "" {
|
||||
return true, false, nil
|
||||
}
|
||||
for _, prefix := range f.rawPrefixWhiteList {
|
||||
if strings.HasPrefix(name, prefix) {
|
||||
return true, true, nil
|
||||
}
|
||||
}
|
||||
return true, false, nil
|
||||
}
|
||||
|
||||
func (f *rawFactory) DebugInfo() map[string][]string {
|
||||
return common.DebugInfo(f.watcher.GetWatches())
|
||||
}
|
||||
|
||||
func Register(machineInfoFactory info.MachineInfoFactory, fsInfo fs.FsInfo, includedMetrics map[container.MetricKind]struct{}, rawPrefixWhiteList []string) error {
|
||||
cgroupSubsystems, err := libcontainer.GetCgroupSubsystems(includedMetrics)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get cgroup subsystems: %v", err)
|
||||
}
|
||||
if len(cgroupSubsystems) == 0 {
|
||||
return fmt.Errorf("failed to find supported cgroup mounts for the raw factory")
|
||||
}
|
||||
|
||||
watcher, err := common.NewInotifyWatcher()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
klog.V(1).Infof("Registering Raw factory")
|
||||
factory := &rawFactory{
|
||||
machineInfoFactory: machineInfoFactory,
|
||||
fsInfo: fsInfo,
|
||||
cgroupSubsystems: cgroupSubsystems,
|
||||
watcher: watcher,
|
||||
includedMetrics: includedMetrics,
|
||||
rawPrefixWhiteList: rawPrefixWhiteList,
|
||||
}
|
||||
container.RegisterContainerHandlerFactory(factory, []watch.ContainerWatchSource{watch.Raw})
|
||||
return nil
|
||||
}
|
304
e2e/vendor/github.com/google/cadvisor/container/raw/handler.go
generated
vendored
Normal file
304
e2e/vendor/github.com/google/cadvisor/container/raw/handler.go
generated
vendored
Normal file
@ -0,0 +1,304 @@
|
||||
// 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 "raw" containers.
|
||||
package raw
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/google/cadvisor/container"
|
||||
"github.com/google/cadvisor/container/common"
|
||||
"github.com/google/cadvisor/container/libcontainer"
|
||||
"github.com/google/cadvisor/fs"
|
||||
info "github.com/google/cadvisor/info/v1"
|
||||
"github.com/google/cadvisor/machine"
|
||||
"github.com/opencontainers/runc/libcontainer/cgroups"
|
||||
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
type rawContainerHandler struct {
|
||||
// Name of the container for this handler.
|
||||
name string
|
||||
machineInfoFactory info.MachineInfoFactory
|
||||
|
||||
// Absolute path to the cgroup hierarchies of this container.
|
||||
// (e.g.: "cpu" -> "/sys/fs/cgroup/cpu/test")
|
||||
cgroupPaths map[string]string
|
||||
|
||||
fsInfo fs.FsInfo
|
||||
externalMounts []common.Mount
|
||||
includedMetrics container.MetricSet
|
||||
|
||||
libcontainerHandler *libcontainer.Handler
|
||||
}
|
||||
|
||||
func isRootCgroup(name string) bool {
|
||||
return name == "/"
|
||||
}
|
||||
|
||||
func newRawContainerHandler(name string, cgroupSubsystems map[string]string, machineInfoFactory info.MachineInfoFactory, fsInfo fs.FsInfo, watcher *common.InotifyWatcher, rootFs string, includedMetrics container.MetricSet) (container.ContainerHandler, error) {
|
||||
cHints, err := common.GetContainerHintsFromFile(*common.ArgContainerHints)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cgroupPaths := common.MakeCgroupPaths(cgroupSubsystems, name)
|
||||
|
||||
cgroupManager, err := libcontainer.NewCgroupManager(name, cgroupPaths)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var externalMounts []common.Mount
|
||||
for _, container := range cHints.AllHosts {
|
||||
if name == container.FullName {
|
||||
externalMounts = container.Mounts
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
pid := 0
|
||||
if isRootCgroup(name) {
|
||||
pid = 1
|
||||
|
||||
// delete pids from cgroup paths because /sys/fs/cgroup/pids/pids.current not exist
|
||||
delete(cgroupPaths, "pids")
|
||||
}
|
||||
|
||||
handler := libcontainer.NewHandler(cgroupManager, rootFs, pid, includedMetrics)
|
||||
|
||||
return &rawContainerHandler{
|
||||
name: name,
|
||||
machineInfoFactory: machineInfoFactory,
|
||||
cgroupPaths: cgroupPaths,
|
||||
fsInfo: fsInfo,
|
||||
externalMounts: externalMounts,
|
||||
includedMetrics: includedMetrics,
|
||||
libcontainerHandler: handler,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (h *rawContainerHandler) ContainerReference() (info.ContainerReference, error) {
|
||||
// We only know the container by its one name.
|
||||
return info.ContainerReference{
|
||||
Name: h.name,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (h *rawContainerHandler) GetRootNetworkDevices() ([]info.NetInfo, error) {
|
||||
nd := []info.NetInfo{}
|
||||
if isRootCgroup(h.name) {
|
||||
mi, err := h.machineInfoFactory.GetMachineInfo()
|
||||
if err != nil {
|
||||
return nd, err
|
||||
}
|
||||
return mi.NetworkDevices, nil
|
||||
}
|
||||
return nd, nil
|
||||
}
|
||||
|
||||
// Nothing to start up.
|
||||
func (h *rawContainerHandler) Start() {}
|
||||
|
||||
// Nothing to clean up.
|
||||
func (h *rawContainerHandler) Cleanup() {}
|
||||
|
||||
func (h *rawContainerHandler) GetSpec() (info.ContainerSpec, error) {
|
||||
const hasNetwork = false
|
||||
hasFilesystem := isRootCgroup(h.name) || len(h.externalMounts) > 0
|
||||
spec, err := common.GetSpec(h.cgroupPaths, h.machineInfoFactory, hasNetwork, hasFilesystem)
|
||||
if err != nil {
|
||||
return spec, err
|
||||
}
|
||||
|
||||
if isRootCgroup(h.name) {
|
||||
// Check physical network devices for root container.
|
||||
nd, err := h.GetRootNetworkDevices()
|
||||
if err != nil {
|
||||
return spec, err
|
||||
}
|
||||
spec.HasNetwork = spec.HasNetwork || len(nd) != 0
|
||||
|
||||
// Get memory and swap limits of the running machine
|
||||
memLimit, err := machine.GetMachineMemoryCapacity()
|
||||
if err != nil {
|
||||
klog.Warningf("failed to obtain memory limit for machine container")
|
||||
spec.HasMemory = false
|
||||
} else {
|
||||
spec.Memory.Limit = uint64(memLimit)
|
||||
// Spec is marked to have memory only if the memory limit is set
|
||||
spec.HasMemory = true
|
||||
}
|
||||
|
||||
swapLimit, err := machine.GetMachineSwapCapacity()
|
||||
if err != nil {
|
||||
klog.Warningf("failed to obtain swap limit for machine container")
|
||||
} else {
|
||||
spec.Memory.SwapLimit = uint64(swapLimit)
|
||||
}
|
||||
}
|
||||
|
||||
return spec, nil
|
||||
}
|
||||
|
||||
func fsToFsStats(fs *fs.Fs) info.FsStats {
|
||||
inodes := uint64(0)
|
||||
inodesFree := uint64(0)
|
||||
hasInodes := fs.InodesFree != nil
|
||||
if hasInodes {
|
||||
inodes = *fs.Inodes
|
||||
inodesFree = *fs.InodesFree
|
||||
}
|
||||
return info.FsStats{
|
||||
Device: fs.Device,
|
||||
Type: fs.Type.String(),
|
||||
Limit: fs.Capacity,
|
||||
Usage: fs.Capacity - fs.Free,
|
||||
HasInodes: hasInodes,
|
||||
Inodes: inodes,
|
||||
InodesFree: inodesFree,
|
||||
Available: fs.Available,
|
||||
ReadsCompleted: fs.DiskStats.ReadsCompleted,
|
||||
ReadsMerged: fs.DiskStats.ReadsMerged,
|
||||
SectorsRead: fs.DiskStats.SectorsRead,
|
||||
ReadTime: fs.DiskStats.ReadTime,
|
||||
WritesCompleted: fs.DiskStats.WritesCompleted,
|
||||
WritesMerged: fs.DiskStats.WritesMerged,
|
||||
SectorsWritten: fs.DiskStats.SectorsWritten,
|
||||
WriteTime: fs.DiskStats.WriteTime,
|
||||
IoInProgress: fs.DiskStats.IoInProgress,
|
||||
IoTime: fs.DiskStats.IoTime,
|
||||
WeightedIoTime: fs.DiskStats.WeightedIoTime,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *rawContainerHandler) getFsStats(stats *info.ContainerStats) error {
|
||||
var filesystems []fs.Fs
|
||||
var err error
|
||||
// Early exist if no disk metrics are to be collected.
|
||||
if !h.includedMetrics.Has(container.DiskUsageMetrics) && !h.includedMetrics.Has(container.DiskIOMetrics) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get Filesystem information only for the root cgroup.
|
||||
if isRootCgroup(h.name) {
|
||||
filesystems, err = h.fsInfo.GetGlobalFsInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if len(h.externalMounts) > 0 {
|
||||
mountSet := make(map[string]struct{})
|
||||
for _, mount := range h.externalMounts {
|
||||
mountSet[mount.HostDir] = struct{}{}
|
||||
}
|
||||
filesystems, err = h.fsInfo.GetFsInfoForPath(mountSet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if h.includedMetrics.Has(container.DiskUsageMetrics) {
|
||||
for i := range filesystems {
|
||||
fs := filesystems[i]
|
||||
stats.Filesystem = append(stats.Filesystem, fsToFsStats(&fs))
|
||||
}
|
||||
}
|
||||
|
||||
if h.includedMetrics.Has(container.DiskIOMetrics) {
|
||||
common.AssignDeviceNamesToDiskStats(&fsNamer{fs: filesystems, factory: h.machineInfoFactory}, &stats.DiskIo)
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *rawContainerHandler) GetStats() (*info.ContainerStats, error) {
|
||||
if *disableRootCgroupStats && isRootCgroup(h.name) {
|
||||
return nil, nil
|
||||
}
|
||||
stats, err := h.libcontainerHandler.GetStats()
|
||||
if err != nil {
|
||||
return stats, err
|
||||
}
|
||||
|
||||
// Get filesystem stats.
|
||||
err = h.getFsStats(stats)
|
||||
if err != nil {
|
||||
return stats, err
|
||||
}
|
||||
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
func (h *rawContainerHandler) 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.name)
|
||||
}
|
||||
return path, nil
|
||||
}
|
||||
|
||||
func (h *rawContainerHandler) GetContainerLabels() map[string]string {
|
||||
return map[string]string{}
|
||||
}
|
||||
|
||||
func (h *rawContainerHandler) GetContainerIPAddress() string {
|
||||
// the IP address for the raw container corresponds to the system ip address.
|
||||
return "127.0.0.1"
|
||||
}
|
||||
|
||||
func (h *rawContainerHandler) ListContainers(listType container.ListType) ([]info.ContainerReference, error) {
|
||||
return common.ListContainers(h.name, h.cgroupPaths, listType)
|
||||
}
|
||||
|
||||
func (h *rawContainerHandler) ListProcesses(listType container.ListType) ([]int, error) {
|
||||
return h.libcontainerHandler.GetProcesses()
|
||||
}
|
||||
|
||||
func (h *rawContainerHandler) Exists() bool {
|
||||
return common.CgroupExists(h.cgroupPaths)
|
||||
}
|
||||
|
||||
func (h *rawContainerHandler) Type() container.ContainerType {
|
||||
return container.ContainerTypeRaw
|
||||
}
|
||||
|
||||
type fsNamer struct {
|
||||
fs []fs.Fs
|
||||
factory info.MachineInfoFactory
|
||||
info common.DeviceNamer
|
||||
}
|
||||
|
||||
func (n *fsNamer) DeviceName(major, minor uint64) (string, bool) {
|
||||
for _, info := range n.fs {
|
||||
if uint64(info.Major) == major && uint64(info.Minor) == minor {
|
||||
return info.Device, true
|
||||
}
|
||||
}
|
||||
if n.info == nil {
|
||||
mi, err := n.factory.GetMachineInfo()
|
||||
if err != nil {
|
||||
return "", false
|
||||
}
|
||||
n.info = (*common.MachineInfoNamer)(mi)
|
||||
}
|
||||
return n.info.DeviceName(major, minor)
|
||||
}
|
243
e2e/vendor/github.com/google/cadvisor/container/raw/watcher.go
generated
vendored
Normal file
243
e2e/vendor/github.com/google/cadvisor/container/raw/watcher.go
generated
vendored
Normal file
@ -0,0 +1,243 @@
|
||||
// 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"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
inotify "k8s.io/utils/inotify"
|
||||
|
||||
"github.com/google/cadvisor/container"
|
||||
"github.com/google/cadvisor/container/common"
|
||||
"github.com/google/cadvisor/container/libcontainer"
|
||||
"github.com/google/cadvisor/watcher"
|
||||
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
type rawContainerWatcher struct {
|
||||
// Absolute path to the root of the cgroup hierarchies
|
||||
cgroupPaths map[string]string
|
||||
|
||||
// Inotify event watcher.
|
||||
watcher *common.InotifyWatcher
|
||||
|
||||
// Signal for watcher thread to stop.
|
||||
stopWatcher chan error
|
||||
}
|
||||
|
||||
func NewRawContainerWatcher(includedMetrics container.MetricSet) (watcher.ContainerWatcher, error) {
|
||||
cgroupSubsystems, err := libcontainer.GetCgroupSubsystems(includedMetrics)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get cgroup subsystems: %v", err)
|
||||
}
|
||||
if len(cgroupSubsystems) == 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: cgroupSubsystems,
|
||||
watcher: watcher,
|
||||
stopWatcher: make(chan error),
|
||||
}
|
||||
|
||||
return rawWatcher, nil
|
||||
}
|
||||
|
||||
func (w *rawContainerWatcher) Start(events chan watcher.ContainerEvent) error {
|
||||
// Watch this container (all its cgroups) and all subdirectories.
|
||||
watched := make([]string, 0)
|
||||
for _, cgroupPath := range w.cgroupPaths {
|
||||
_, err := w.watchDirectory(events, cgroupPath, "/")
|
||||
if err != nil {
|
||||
for _, watchedCgroupPath := range watched {
|
||||
_, removeErr := w.watcher.RemoveWatch("/", watchedCgroupPath)
|
||||
if removeErr != nil {
|
||||
klog.Warningf("Failed to remove inotify watch for %q with error: %v", watchedCgroupPath, removeErr)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
watched = append(watched, cgroupPath)
|
||||
}
|
||||
|
||||
// Process the events received from the kernel.
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case event := <-w.watcher.Event():
|
||||
err := w.processEvent(event, events)
|
||||
if err != nil {
|
||||
klog.Warningf("Error while processing event (%+v): %v", event, err)
|
||||
}
|
||||
case err := <-w.watcher.Error():
|
||||
klog.Warningf("Error while watching %q: %v", "/", err)
|
||||
case <-w.stopWatcher:
|
||||
err := w.watcher.Close()
|
||||
if err == nil {
|
||||
w.stopWatcher <- err
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *rawContainerWatcher) Stop() error {
|
||||
// Rendezvous with the watcher thread.
|
||||
w.stopWatcher <- nil
|
||||
return <-w.stopWatcher
|
||||
}
|
||||
|
||||
// Watches the specified directory and all subdirectories. Returns whether the path was
|
||||
// already being watched and an error (if any).
|
||||
func (w *rawContainerWatcher) watchDirectory(events chan watcher.ContainerEvent, dir string, containerName string) (bool, error) {
|
||||
// Don't watch .mount cgroups because they never have containers as sub-cgroups. A single container
|
||||
// can have many .mount cgroups associated with it which can quickly exhaust the inotify watches on a node.
|
||||
if strings.HasSuffix(containerName, ".mount") {
|
||||
return false, nil
|
||||
}
|
||||
alreadyWatching, err := w.watcher.AddWatch(containerName, dir)
|
||||
if err != nil {
|
||||
return alreadyWatching, err
|
||||
}
|
||||
|
||||
// Remove the watch if further operations failed.
|
||||
cleanup := true
|
||||
defer func() {
|
||||
if cleanup {
|
||||
_, err := w.watcher.RemoveWatch(containerName, dir)
|
||||
if err != nil {
|
||||
klog.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 := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
return alreadyWatching, err
|
||||
}
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() {
|
||||
entryPath := path.Join(dir, entry.Name())
|
||||
subcontainerName := path.Join(containerName, entry.Name())
|
||||
alreadyWatchingSubDir, err := w.watchDirectory(events, entryPath, subcontainerName)
|
||||
if err != nil {
|
||||
klog.Errorf("Failed to watch directory %q: %v", entryPath, err)
|
||||
if os.IsNotExist(err) {
|
||||
// The directory may have been removed before watching. Try to watch the other
|
||||
// subdirectories. (https://github.com/kubernetes/kubernetes/issues/28997)
|
||||
continue
|
||||
}
|
||||
return alreadyWatching, err
|
||||
}
|
||||
// since we already missed the creation event for this directory, publish an event here.
|
||||
if !alreadyWatchingSubDir {
|
||||
go func() {
|
||||
events <- watcher.ContainerEvent{
|
||||
EventType: watcher.ContainerAdd,
|
||||
Name: subcontainerName,
|
||||
WatchSource: watcher.Raw,
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cleanup = false
|
||||
return alreadyWatching, nil
|
||||
}
|
||||
|
||||
func (w *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.InCreate) > 0:
|
||||
eventType = watcher.ContainerAdd
|
||||
case (event.Mask & inotify.InDelete) > 0:
|
||||
eventType = watcher.ContainerDelete
|
||||
case (event.Mask & inotify.InMovedFrom) > 0:
|
||||
eventType = watcher.ContainerDelete
|
||||
case (event.Mask & inotify.InMovedTo) > 0:
|
||||
eventType = watcher.ContainerAdd
|
||||
default:
|
||||
// Ignore other events.
|
||||
return nil
|
||||
}
|
||||
|
||||
// Derive the container name from the path name.
|
||||
var containerName string
|
||||
for _, mount := range w.cgroupPaths {
|
||||
mountLocation := path.Clean(mount) + "/"
|
||||
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 := w.watchDirectory(events, 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 := w.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
|
||||
}
|
59
e2e/vendor/github.com/google/cadvisor/container/systemd/factory.go
generated
vendored
Normal file
59
e2e/vendor/github.com/google/cadvisor/container/systemd/factory.go
generated
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package systemd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/google/cadvisor/container"
|
||||
"github.com/google/cadvisor/fs"
|
||||
info "github.com/google/cadvisor/info/v1"
|
||||
"github.com/google/cadvisor/watcher"
|
||||
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
type systemdFactory struct{}
|
||||
|
||||
func (f *systemdFactory) String() string {
|
||||
return "systemd"
|
||||
}
|
||||
|
||||
func (f *systemdFactory) NewContainerHandler(name string, metadataEnvAllowList []string, inHostNamespace bool) (container.ContainerHandler, error) {
|
||||
return nil, fmt.Errorf("Not yet supported")
|
||||
}
|
||||
|
||||
func (f *systemdFactory) CanHandleAndAccept(name string) (bool, bool, error) {
|
||||
// on systemd using devicemapper each mount into the container has an associated cgroup that we ignore.
|
||||
// for details on .mount units: http://man7.org/linux/man-pages/man5/systemd.mount.5.html
|
||||
if strings.HasSuffix(name, ".mount") {
|
||||
return true, false, nil
|
||||
}
|
||||
klog.V(5).Infof("%s not handled by systemd handler", name)
|
||||
return false, false, nil
|
||||
}
|
||||
|
||||
func (f *systemdFactory) DebugInfo() map[string][]string {
|
||||
return map[string][]string{}
|
||||
}
|
||||
|
||||
// Register registers the systemd container factory.
|
||||
func Register(machineInfoFactory info.MachineInfoFactory, fsInfo fs.FsInfo, includedMetrics container.MetricSet) error {
|
||||
klog.V(1).Infof("Registering systemd factory")
|
||||
factory := &systemdFactory{}
|
||||
container.RegisterContainerHandlerFactory(factory, []watcher.ContainerWatchSource{watcher.Raw})
|
||||
return nil
|
||||
}
|
30
e2e/vendor/github.com/google/cadvisor/container/systemd/install/install.go
generated
vendored
Normal file
30
e2e/vendor/github.com/google/cadvisor/container/systemd/install/install.go
generated
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
// 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 systemd.NewPlugin() as the "systemd" container provider when imported
|
||||
package install
|
||||
|
||||
import (
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
"github.com/google/cadvisor/container"
|
||||
"github.com/google/cadvisor/container/systemd"
|
||||
)
|
||||
|
||||
func init() {
|
||||
err := container.RegisterPlugin("systemd", systemd.NewPlugin())
|
||||
if err != nil {
|
||||
klog.Fatalf("Failed to register systemd plugin: %v", err)
|
||||
}
|
||||
}
|
38
e2e/vendor/github.com/google/cadvisor/container/systemd/plugin.go
generated
vendored
Normal file
38
e2e/vendor/github.com/google/cadvisor/container/systemd/plugin.go
generated
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
// 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 systemd
|
||||
|
||||
import (
|
||||
"github.com/google/cadvisor/container"
|
||||
"github.com/google/cadvisor/fs"
|
||||
info "github.com/google/cadvisor/info/v1"
|
||||
"github.com/google/cadvisor/watcher"
|
||||
)
|
||||
|
||||
// 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 {
|
||||
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
|
||||
}
|
Reference in New Issue
Block a user