Bump cAdvisor to v0.43.0

Bumping cAdvisor from v0.39.2 -> v0.43.0

* Also pin transitive dependencies
  * containerd v1.4.9 -> v1.4.11
  * docker v20.10.2+incompatible> v20.10.7+incompatible

Signed-off-by: David Porter <david@porter.me>
This commit is contained in:
David Porter
2021-11-09 14:23:06 -08:00
parent e4adf7f31c
commit c6452be958
80 changed files with 1637 additions and 465 deletions

View File

@@ -96,7 +96,12 @@ func (fh *realFsHandler) update() error {
fh.usage.TotalUsageBytes = rootUsage.Bytes
}
if fh.extraDir != "" && extraErr == nil {
fh.usage.TotalUsageBytes += extraUsage.Bytes
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

View File

@@ -24,14 +24,15 @@ import (
"strings"
"time"
"github.com/google/cadvisor/container"
info "github.com/google/cadvisor/info/v1"
"github.com/google/cadvisor/utils"
"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"
)
@@ -104,7 +105,7 @@ func getSpecInternal(cgroupPaths map[string]string, machineInfoFactory info.Mach
}
// CPU.
cpuRoot, ok := cgroupPaths["cpu"]
cpuRoot, ok := getControllerPath(cgroupPaths, "cpu", cgroup2UnifiedMode)
if ok {
if utils.FileExists(cpuRoot) {
if cgroup2UnifiedMode {
@@ -151,7 +152,7 @@ func getSpecInternal(cgroupPaths map[string]string, machineInfoFactory info.Mach
// Cpu Mask.
// This will fail for non-unified hierarchies. We'll return the whole machine mask in that case.
cpusetRoot, ok := cgroupPaths["cpuset"]
cpusetRoot, ok := getControllerPath(cgroupPaths, "cpuset", cgroup2UnifiedMode)
if ok {
if utils.FileExists(cpusetRoot) {
spec.HasCpu = true
@@ -166,7 +167,7 @@ func getSpecInternal(cgroupPaths map[string]string, machineInfoFactory info.Mach
}
// Memory
memoryRoot, ok := cgroupPaths["memory"]
memoryRoot, ok := getControllerPath(cgroupPaths, "memory", cgroup2UnifiedMode)
if ok {
if cgroup2UnifiedMode {
if utils.FileExists(path.Join(memoryRoot, "memory.max")) {
@@ -194,7 +195,7 @@ func getSpecInternal(cgroupPaths map[string]string, machineInfoFactory info.Mach
}
// Processes, read it's value from pids path directly
pidsRoot, ok := cgroupPaths["pids"]
pidsRoot, ok := getControllerPath(cgroupPaths, "pids", cgroup2UnifiedMode)
if ok {
if utils.FileExists(pidsRoot) {
spec.HasProcesses = true
@@ -216,6 +217,19 @@ func getSpecInternal(cgroupPaths map[string]string, machineInfoFactory info.Mach
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)

View File

@@ -24,10 +24,10 @@ import (
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"
"github.com/containerd/containerd/containers"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/pkg/dialer"
ptypes "github.com/gogo/protobuf/types"
"github.com/google/cadvisor/container/containerd/containers"
"github.com/google/cadvisor/container/containerd/errdefs"
"github.com/google/cadvisor/container/containerd/pkg/dialer"
"google.golang.org/grpc"
"google.golang.org/grpc/backoff"
)

View 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"
"github.com/gogo/protobuf/types"
)
// 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 *types.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]types.Any
}
// RuntimeInfo holds runtime specific information
type RuntimeInfo struct {
Name string
Options *types.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
}

View File

@@ -0,0 +1,106 @@
// 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 errdefs defines the common errors used throughout containerd
// packages.
//
// Use with errors.Wrap and error.Wrapf to add context to an error.
//
// To detect an error class, use the IsXXX functions to tell whether an error
// is of a certain type.
//
// The functions ToGRPC and FromGRPC can be used to map server-side and
// client-side errors to the correct types.
package errdefs
import (
"context"
"github.com/pkg/errors"
)
// Definitions of common error types used throughout containerd. All containerd
// errors returned by most packages will map into one of these errors classes.
// Packages should return errors of these types when they want to instruct a
// client to take a particular action.
//
// For the most part, we just try to provide local grpc errors. Most conditions
// map very well to those defined by grpc.
var (
ErrUnknown = errors.New("unknown") // used internally to represent a missed mapping.
ErrInvalidArgument = errors.New("invalid argument")
ErrNotFound = errors.New("not found")
ErrAlreadyExists = errors.New("already exists")
ErrFailedPrecondition = errors.New("failed precondition")
ErrUnavailable = errors.New("unavailable")
ErrNotImplemented = errors.New("not implemented") // represents not supported and unimplemented
)
// IsInvalidArgument returns true if the error is due to an invalid argument
func IsInvalidArgument(err error) bool {
return errors.Is(err, ErrInvalidArgument)
}
// IsNotFound returns true if the error is due to a missing object
func IsNotFound(err error) bool {
return errors.Is(err, ErrNotFound)
}
// IsAlreadyExists returns true if the error is due to an already existing
// metadata item
func IsAlreadyExists(err error) bool {
return errors.Is(err, ErrAlreadyExists)
}
// IsFailedPrecondition returns true if an operation could not proceed to the
// lack of a particular condition
func IsFailedPrecondition(err error) bool {
return errors.Is(err, ErrFailedPrecondition)
}
// IsUnavailable returns true if the error is due to a resource being unavailable
func IsUnavailable(err error) bool {
return errors.Is(err, ErrUnavailable)
}
// IsNotImplemented returns true if the error is due to not being implemented
func IsNotImplemented(err error) bool {
return errors.Is(err, ErrNotImplemented)
}
// IsCanceled returns true if the error is due to `context.Canceled`.
func IsCanceled(err error) bool {
return errors.Is(err, context.Canceled)
}
// IsDeadlineExceeded returns true if the error is due to
// `context.DeadlineExceeded`.
func IsDeadlineExceeded(err error) bool {
return errors.Is(err, context.DeadlineExceeded)
}

View File

@@ -0,0 +1,160 @@
// 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 errdefs
import (
"context"
"strings"
"github.com/pkg/errors"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// ToGRPC will attempt to map the backend containerd error into a grpc error,
// using the original error message as a description.
//
// Further information may be extracted from certain errors depending on their
// type.
//
// If the error is unmapped, the original error will be returned to be handled
// by the regular grpc error handling stack.
func ToGRPC(err error) error {
if err == nil {
return nil
}
if isGRPCError(err) {
// error has already been mapped to grpc
return err
}
switch {
case IsInvalidArgument(err):
return status.Errorf(codes.InvalidArgument, err.Error())
case IsNotFound(err):
return status.Errorf(codes.NotFound, err.Error())
case IsAlreadyExists(err):
return status.Errorf(codes.AlreadyExists, err.Error())
case IsFailedPrecondition(err):
return status.Errorf(codes.FailedPrecondition, err.Error())
case IsUnavailable(err):
return status.Errorf(codes.Unavailable, err.Error())
case IsNotImplemented(err):
return status.Errorf(codes.Unimplemented, err.Error())
case IsCanceled(err):
return status.Errorf(codes.Canceled, err.Error())
case IsDeadlineExceeded(err):
return status.Errorf(codes.DeadlineExceeded, err.Error())
}
return err
}
// ToGRPCf maps the error to grpc error codes, assembling the formatting string
// and combining it with the target error string.
//
// This is equivalent to errors.ToGRPC(errors.Wrapf(err, format, args...))
func ToGRPCf(err error, format string, args ...interface{}) error {
return ToGRPC(errors.Wrapf(err, format, args...))
}
// FromGRPC returns the underlying error from a grpc service based on the grpc error code
func FromGRPC(err error) error {
if err == nil {
return nil
}
var cls error // divide these into error classes, becomes the cause
switch code(err) {
case codes.InvalidArgument:
cls = ErrInvalidArgument
case codes.AlreadyExists:
cls = ErrAlreadyExists
case codes.NotFound:
cls = ErrNotFound
case codes.Unavailable:
cls = ErrUnavailable
case codes.FailedPrecondition:
cls = ErrFailedPrecondition
case codes.Unimplemented:
cls = ErrNotImplemented
case codes.Canceled:
cls = context.Canceled
case codes.DeadlineExceeded:
cls = context.DeadlineExceeded
default:
cls = ErrUnknown
}
msg := rebaseMessage(cls, err)
if msg != "" {
err = errors.Wrap(cls, msg)
} else {
err = errors.WithStack(cls)
}
return err
}
// rebaseMessage removes the repeats for an error at the end of an error
// string. This will happen when taking an error over grpc then remapping it.
//
// Effectively, we just remove the string of cls from the end of err if it
// appears there.
func rebaseMessage(cls error, err error) string {
desc := errDesc(err)
clss := cls.Error()
if desc == clss {
return ""
}
return strings.TrimSuffix(desc, ": "+clss)
}
func isGRPCError(err error) bool {
_, ok := status.FromError(err)
return ok
}
func code(err error) codes.Code {
if s, ok := status.FromError(err); ok {
return s.Code()
}
return codes.Unknown
}
func errDesc(err error) string {
if s, ok := status.FromError(err); ok {
return s.Message()
}
return err.Error()
}

View File

@@ -34,6 +34,8 @@ import (
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"
@@ -46,7 +48,7 @@ type containerdFactory struct {
client ContainerdClient
version string
// Information about the mounted cgroup subsystems.
cgroupSubsystems libcontainer.CgroupSubsystems
cgroupSubsystems map[string]string
// Information about mounted filesystems.
fsInfo fs.FsInfo
includedMetrics container.MetricSet
@@ -56,21 +58,27 @@ func (f *containerdFactory) String() string {
return k8sContainerdNamespace
}
func (f *containerdFactory) NewContainerHandler(name string, inHostNamespace bool) (handler container.ContainerHandler, err error) {
func (f *containerdFactory) NewContainerHandler(name string, metadataEnvAllowList []string, inHostNamespace bool) (handler container.ContainerHandler, err error) {
client, err := Client(*ArgContainerdEndpoint, *ArgContainerdNamespace)
if err != nil {
return
}
metadataEnvs := []string{}
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,
f.cgroupSubsystems,
inHostNamespace,
metadataEnvs,
containerdMetadataEnvAllowList,
f.includedMetrics,
)
}

View File

@@ -16,7 +16,7 @@
package containerd
import (
"github.com/containerd/containerd/namespaces"
"github.com/google/cadvisor/container/containerd/namespaces"
"golang.org/x/net/context"
"google.golang.org/grpc"
)

View File

@@ -21,15 +21,17 @@ import (
"strings"
"time"
"github.com/containerd/containerd/errdefs"
"github.com/google/cadvisor/container/containerd/errdefs"
"github.com/opencontainers/runc/libcontainer/cgroups"
"golang.org/x/net/context"
specs "github.com/opencontainers/runtime-spec/specs-go"
"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"
specs "github.com/opencontainers/runtime-spec/specs-go"
)
type containerdContainerHandler struct {
@@ -58,13 +60,13 @@ func newContainerdContainerHandler(
name string,
machineInfoFactory info.MachineInfoFactory,
fsInfo fs.FsInfo,
cgroupSubsystems *containerlibcontainer.CgroupSubsystems,
cgroupSubsystems map[string]string,
inHostNamespace bool,
metadataEnvs []string,
metadataEnvAllowList []string,
includedMetrics container.MetricSet,
) (container.ContainerHandler, error) {
// Create the cgroup paths.
cgroupPaths := common.MakeCgroupPaths(cgroupSubsystems.MountPoints, name)
cgroupPaths := common.MakeCgroupPaths(cgroupSubsystems, name)
// Generate the equivalent cgroup manager for this container.
cgroupManager, err := containerlibcontainer.NewCgroupManager(name, cgroupPaths)
@@ -133,11 +135,19 @@ func newContainerdContainerHandler(
}
// Add the name and bare ID as aliases of the container.
handler.image = cntr.Image
for _, envVar := range spec.Process.Env {
if envVar != "" {
splits := strings.SplitN(envVar, "=", 2)
if len(splits) == 2 {
handler.envs[splits[0]] = splits[1]
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]
}
}
}
}
@@ -207,7 +217,11 @@ func (h *containerdContainerHandler) ListContainers(listType container.ListType)
}
func (h *containerdContainerHandler) GetCgroupPath(resource string) (string, error) {
path, ok := h.cgroupPaths[resource]
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)
}

View 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/google/cadvisor/container/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 + `$`
}

View File

@@ -16,9 +16,10 @@
package install
import (
"k8s.io/klog/v2"
"github.com/google/cadvisor/container"
"github.com/google/cadvisor/container/containerd"
"k8s.io/klog/v2"
)
func init() {

View File

@@ -0,0 +1,91 @@
// 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/google/cadvisor/container/containerd/errdefs"
"github.com/google/cadvisor/container/containerd/identifiers"
"github.com/pkg/errors"
)
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
}

View 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
}

View File

@@ -0,0 +1,59 @@
// 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

View 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)
}

View 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)
}
}

View 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)
}

View 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
}

View File

@@ -32,6 +32,9 @@ import (
// 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})`)
@@ -50,7 +53,7 @@ type crioFactory struct {
storageDir string
// Information about the mounted cgroup subsystems.
cgroupSubsystems libcontainer.CgroupSubsystems
cgroupSubsystems map[string]string
// Information about mounted filesystems.
fsInfo fs.FsInfo
@@ -64,13 +67,11 @@ func (f *crioFactory) String() string {
return CrioNamespace
}
func (f *crioFactory) NewContainerHandler(name string, inHostNamespace bool) (handler container.ContainerHandler, err error) {
func (f *crioFactory) NewContainerHandler(name string, metadataEnvAllowList []string, inHostNamespace bool) (handler container.ContainerHandler, err error) {
client, err := Client()
if err != nil {
return
}
// TODO are there any env vars we need to white list, if so, do it here...
metadataEnvs := []string{}
handler, err = newCrioContainerHandler(
client,
name,
@@ -78,9 +79,9 @@ func (f *crioFactory) NewContainerHandler(name string, inHostNamespace bool) (ha
f.fsInfo,
f.storageDriver,
f.storageDir,
&f.cgroupSubsystems,
f.cgroupSubsystems,
inHostNamespace,
metadataEnvs,
metadataEnvAllowList,
f.includedMetrics,
)
return
@@ -116,6 +117,9 @@ func (f *crioFactory) CanHandleAndAccept(name string) (bool, bool, error) {
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

View File

@@ -21,12 +21,13 @@ import (
"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"
"github.com/opencontainers/runc/libcontainer/cgroups"
)
type crioContainerHandler struct {
@@ -83,13 +84,13 @@ func newCrioContainerHandler(
fsInfo fs.FsInfo,
storageDriver storageDriver,
storageDir string,
cgroupSubsystems *containerlibcontainer.CgroupSubsystems,
cgroupSubsystems map[string]string,
inHostNamespace bool,
metadataEnvs []string,
metadataEnvAllowList []string,
includedMetrics container.MetricSet,
) (container.ContainerHandler, error) {
// Create the cgroup paths.
cgroupPaths := common.MakeCgroupPaths(cgroupSubsystems.MountPoints, name)
cgroupPaths := common.MakeCgroupPaths(cgroupSubsystems, name)
// Generate the equivalent cgroup manager for this container.
cgroupManager, err := containerlibcontainer.NewCgroupManager(name, cgroupPaths)
@@ -186,7 +187,7 @@ func newCrioContainerHandler(
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 metadataEnvs {
//for _, exposedEnv := range metadataEnvAllowList {
//klog.V(4).Infof("TODO env whitelist: %v", exposedEnv)
//}
@@ -327,7 +328,11 @@ func (h *crioContainerHandler) ListContainers(listType container.ListType) ([]in
}
func (h *crioContainerHandler) GetCgroupPath(resource string) (string, error) {
path, ok := h.cgroupPaths[resource]
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)
}

View File

@@ -16,9 +16,10 @@
package install
import (
"k8s.io/klog/v2"
"github.com/google/cadvisor/container"
"github.com/google/cadvisor/container/crio"
"k8s.io/klog/v2"
)
func init() {

View File

@@ -15,11 +15,12 @@
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"
"k8s.io/klog/v2"
)
// NewPlugin returns an implementation of container.Plugin suitable for passing to container.RegisterPlugin()

View File

@@ -25,7 +25,7 @@ import (
"time"
"github.com/google/cadvisor/info/v1"
v1 "github.com/google/cadvisor/info/v1"
"github.com/google/cadvisor/machine"
)

View File

@@ -26,6 +26,7 @@ import (
"github.com/blang/semver"
dockertypes "github.com/docker/docker/api/types"
"github.com/google/cadvisor/container"
dockerutil "github.com/google/cadvisor/container/docker/utils"
"github.com/google/cadvisor/container/libcontainer"
@@ -47,21 +48,21 @@ var ArgDockerCert = flag.String("docker-tls-cert", "cert.pem", "path to client c
var ArgDockerKey = flag.String("docker-tls-key", "key.pem", "path to private key")
var ArgDockerCA = flag.String("docker-tls-ca", "ca.pem", "path to trusted CA")
var dockerEnvMetadataWhiteList = flag.String("docker_env_metadata_whitelist", "", "DEPRECATED: this flag will be removed, please use `env_metadata_whitelist`. A comma-separated list of environment variable keys matched with specified prefix that needs to be collected for docker containers")
// The namespace under which Docker aliases are unique.
const DockerNamespace = "docker"
// The retry times for getting docker root dir
const rootDirRetries = 5
//The retry period for getting docker root dir, Millisecond
// The retry period for getting docker root dir, Millisecond
const rootDirRetryPeriod time.Duration = 1000 * time.Millisecond
// Regexp that identifies docker cgroups, containers started with
// --cgroup-parent have another prefix than 'docker'
var dockerCgroupRegexp = regexp.MustCompile(`([a-z0-9]{64})`)
var dockerEnvWhitelist = flag.String("docker_env_metadata_whitelist", "", "a comma-separated list of environment variable keys matched with specified prefix that needs to be collected for docker containers")
var (
// Basepath to all container specific information that libcontainer stores.
dockerRootDir string
@@ -115,7 +116,7 @@ type dockerFactory struct {
client *docker.Client
// Information about the mounted cgroup subsystems.
cgroupSubsystems libcontainer.CgroupSubsystems
cgroupSubsystems map[string]string
// Information about mounted filesystems.
fsInfo fs.FsInfo
@@ -136,13 +137,18 @@ func (f *dockerFactory) String() string {
return DockerNamespace
}
func (f *dockerFactory) NewContainerHandler(name string, inHostNamespace bool) (handler container.ContainerHandler, err error) {
func (f *dockerFactory) NewContainerHandler(name string, metadataEnvAllowList []string, inHostNamespace bool) (handler container.ContainerHandler, err error) {
client, err := Client()
if err != nil {
return
}
metadataEnvs := strings.Split(*dockerEnvWhitelist, ",")
dockerMetadataEnvAllowList := strings.Split(*dockerEnvMetadataWhiteList, ",")
// prefer using the unified metadataEnvAllowList
if len(metadataEnvAllowList) != 0 {
dockerMetadataEnvAllowList = metadataEnvAllowList
}
handler, err = newDockerContainerHandler(
client,
@@ -151,9 +157,9 @@ func (f *dockerFactory) NewContainerHandler(name string, inHostNamespace bool) (
f.fsInfo,
f.storageDriver,
f.storageDir,
&f.cgroupSubsystems,
f.cgroupSubsystems,
inHostNamespace,
metadataEnvs,
dockerMetadataEnvAllowList,
f.dockerVersion,
f.includedMetrics,
f.thinPoolName,

View File

@@ -31,6 +31,7 @@ import (
"github.com/google/cadvisor/fs"
info "github.com/google/cadvisor/info/v1"
"github.com/google/cadvisor/zfs"
"github.com/opencontainers/runc/libcontainer/cgroups"
dockercontainer "github.com/docker/docker/api/types/container"
docker "github.com/docker/docker/client"
@@ -121,9 +122,9 @@ func newDockerContainerHandler(
fsInfo fs.FsInfo,
storageDriver storageDriver,
storageDir string,
cgroupSubsystems *containerlibcontainer.CgroupSubsystems,
cgroupSubsystems map[string]string,
inHostNamespace bool,
metadataEnvs []string,
metadataEnvAllowList []string,
dockerVersion []int,
includedMetrics container.MetricSet,
thinPoolName string,
@@ -131,7 +132,7 @@ func newDockerContainerHandler(
zfsWatcher *zfs.ZfsWatcher,
) (container.ContainerHandler, error) {
// Create the cgroup paths.
cgroupPaths := common.MakeCgroupPaths(cgroupSubsystems.MountPoints, name)
cgroupPaths := common.MakeCgroupPaths(cgroupSubsystems, name)
// Generate the equivalent cgroup manager for this container.
cgroupManager, err := containerlibcontainer.NewCgroupManager(name, cgroupPaths)
@@ -249,9 +250,9 @@ func newDockerContainerHandler(
}
// split env vars to get metadata map.
for _, exposedEnv := range metadataEnvs {
for _, exposedEnv := range metadataEnvAllowList {
if exposedEnv == "" {
// if no dockerEnvWhitelist provided, len(metadataEnvs) == 1, metadataEnvs[0] == ""
// if no dockerEnvWhitelist provided, len(metadataEnvAllowList) == 1, metadataEnvAllowList[0] == ""
continue
}
@@ -484,7 +485,11 @@ func (h *dockerContainerHandler) ListContainers(listType container.ListType) ([]
}
func (h *dockerContainerHandler) GetCgroupPath(resource string) (string, error) {
path, ok := h.cgroupPaths[resource]
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)
}

View File

@@ -16,9 +16,10 @@
package install
import (
"k8s.io/klog/v2"
"github.com/google/cadvisor/container"
"github.com/google/cadvisor/container/docker"
"k8s.io/klog/v2"
)
func init() {

View File

@@ -17,12 +17,13 @@ package docker
import (
"time"
"golang.org/x/net/context"
"k8s.io/klog/v2"
"github.com/google/cadvisor/container"
"github.com/google/cadvisor/fs"
info "github.com/google/cadvisor/info/v1"
"github.com/google/cadvisor/watcher"
"golang.org/x/net/context"
"k8s.io/klog/v2"
)
const dockerClientTimeout = 10 * time.Second

View File

@@ -16,6 +16,8 @@ package container
import (
"fmt"
"sort"
"strings"
"sync"
"github.com/google/cadvisor/fs"
@@ -27,7 +29,7 @@ import (
type ContainerHandlerFactory interface {
// Create a new ContainerHandler using this factory. CanHandleAndAccept() must have returned true.
NewContainerHandler(name string, inHostNamespace bool) (c ContainerHandler, err error)
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)
@@ -64,6 +66,7 @@ const (
CPUTopologyMetrics MetricKind = "cpu_topology"
ResctrlMetrics MetricKind = "resctrl"
CPUSetMetrics MetricKind = "cpuset"
OOMMetrics MetricKind = "oom_event"
)
// AllMetrics represents all kinds of metrics that cAdvisor supported.
@@ -89,6 +92,7 @@ var AllMetrics = MetricSet{
CPUTopologyMetrics: struct{}{},
ResctrlMetrics: struct{}{},
CPUSetMetrics: struct{}{},
OOMMetrics: struct{}{},
}
func (mk MetricKind) String() string {
@@ -102,15 +106,50 @@ func (ms MetricSet) Has(mk MetricKind) bool {
return exists
}
func (ms MetricSet) Add(mk MetricKind) {
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)
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
@@ -198,12 +237,15 @@ func HasFactories() bool {
}
// Create a new ContainerHandler for the specified container.
func NewContainerHandler(name string, watchType watcher.ContainerWatchSource, inHostNamespace bool) (ContainerHandler, bool, error) {
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.
for _, factory := range factories[watchType] {
// 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)
@@ -214,7 +256,7 @@ func NewContainerHandler(name string, watchType watcher.ContainerWatchSource, in
return nil, false, nil
}
klog.V(3).Infof("Using factory %q for container %q", factory, name)
handle, err := factory.NewContainerHandler(name, inHostNamespace)
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)
@@ -246,3 +288,26 @@ func DebugInfo() map[string][]string {
}
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
}

View File

@@ -54,7 +54,10 @@ type Handler struct {
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
}
@@ -93,14 +96,9 @@ func (h *Handler) GetStats() (*info.ContainerStats, error) {
stats := newContainerStats(libcontainerStats, h.includedMetrics)
if h.includedMetrics.Has(container.ProcessSchedulerMetrics) {
pids, err := h.cgroupManager.GetAllPids()
stats.Cpu.Schedstat, err = h.schedulerStatsFromProcs()
if err != nil {
klog.V(4).Infof("Could not get PIDs for container %d: %v", h.pid, err)
} else {
stats.Cpu.Schedstat, err = schedulerStatsFromProcs(h.rootFs, pids, h.pidMetricsCache)
if err != nil {
klog.V(4).Infof("Unable to get Process Scheduler Stats: %v", err)
}
klog.V(4).Infof("Unable to get Process Scheduler Stats: %v", err)
}
}
@@ -314,9 +312,14 @@ func processStatsFromProcs(rootFs string, cgroupPath string, rootPid int) (info.
return processStats, nil
}
func schedulerStatsFromProcs(rootFs string, pids []int, pidMetricsCache map[int]*info.CpuSchedstat) (info.CpuSchedstat, error) {
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(rootFs, "proc", strconv.Itoa(pid), "schedstat"))
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)
}
@@ -325,14 +328,15 @@ func schedulerStatsFromProcs(rootFs string, pids []int, pidMetricsCache map[int]
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 := pidMetricsCache[pid]
cacheEntry, ok := h.pidMetricsCache[pid]
if !ok {
cacheEntry = &info.CpuSchedstat{}
pidMetricsCache[pid] = cacheEntry
h.pidMetricsCache[pid] = cacheEntry
}
for i, rawMetric := range rawMetrics {
metric, err := strconv.ParseUint(string(rawMetric), 10, 64)
@@ -349,11 +353,20 @@ func schedulerStatsFromProcs(rootFs string, pids []int, pidMetricsCache map[int]
}
}
}
schedstats := info.CpuSchedstat{}
for _, v := range pidMetricsCache {
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
}
@@ -383,7 +396,7 @@ func getReferencedKBytes(pids []int) (uint64, error) {
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
continue // smaps file does not exists for all PIDs
}
return 0, err
}
@@ -426,7 +439,7 @@ func clearReferencedBytes(pids []int, cycles uint64, resetInterval uint64) error
if cycles%resetInterval == 0 {
for _, pid := range pids {
clearRefsFilePath := fmt.Sprintf(clearRefsFilePathPattern, pid)
clerRefsFile, err := os.OpenFile(clearRefsFilePath, os.O_WRONLY, 0644)
clerRefsFile, err := os.OpenFile(clearRefsFilePath, os.O_WRONLY, 0o644)
if err != nil {
// clear_refs file may not exist for all PIDs
continue
@@ -455,9 +468,7 @@ func networkStatsFromProc(rootFs string, pid int) ([]info.InterfaceStats, error)
return ifaceStats, nil
}
var (
ignoredDevicePrefixes = []string{"lo", "veth", "docker"}
)
var ignoredDevicePrefixes = []string{"lo", "veth", "docker"}
func isIgnoredDevice(ifName string) bool {
for _, prefix := range ignoredDevicePrefixes {
@@ -615,11 +626,9 @@ func scanAdvancedTCPStats(advancedStats *info.TcpAdvancedStat, advancedTCPStatsF
}
return scanner.Err()
}
func scanTCPStats(tcpStatsFile string) (info.TcpStat, error) {
var stats info.TcpStat
data, err := ioutil.ReadFile(tcpStatsFile)
@@ -628,17 +637,17 @@ func scanTCPStats(tcpStatsFile string) (info.TcpStat, error) {
}
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
"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))
@@ -779,14 +788,14 @@ func setCPUStats(s *cgroups.Stats, ret *info.ContainerStats, withPerCPU bool) {
}
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)
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) {
@@ -797,7 +806,7 @@ func setMemoryStats(s *cgroups.Stats, ret *info.ContainerStats) {
if cgroups.IsCgroup2UnifiedMode() {
ret.Memory.Cache = s.MemoryStats.Stats["file"]
ret.Memory.RSS = s.MemoryStats.Stats["anon"]
ret.Memory.Swap = s.MemoryStats.SwapUsage.Usage
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"]
@@ -896,7 +905,6 @@ func setThreadsStats(s *cgroups.Stats, ret *info.ContainerStats) {
ret.Processes.ThreadsCurrent = s.PidsStats.Current
ret.Processes.ThreadsMax = s.PidsStats.Limit
}
}
func newContainerStats(libcontainerStats *libcontainer.Stats, includedMetrics container.MetricSet) *info.ContainerStats {

View File

@@ -19,71 +19,49 @@ import (
info "github.com/google/cadvisor/info/v1"
"github.com/google/cadvisor/container"
"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"
)
type CgroupSubsystems struct {
// Cgroup subsystem mounts.
// e.g.: "/sys/fs/cgroup/cpu" -> ["cpu", "cpuacct"]
Mounts []cgroups.Mount
// Cgroup subsystem to their mount location.
// e.g.: "cpu" -> "/sys/fs/cgroup/cpu"
MountPoints map[string]string
}
// Get information about the cgroup subsystems those we want
func GetCgroupSubsystems(includedMetrics container.MetricSet) (CgroupSubsystems, error) {
// 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 CgroupSubsystems{}, err
return nil, err
}
disableCgroups := map[string]struct{}{}
//currently we only support disable blkio subsystem
if !includedMetrics.Has(container.DiskIOMetrics) {
disableCgroups["blkio"] = struct{}{}
disableCgroups["io"] = struct{}{}
}
return getCgroupSubsystemsHelper(allCgroups, disableCgroups)
return getCgroupSubsystemsHelper(allCgroups, includedMetrics)
}
// Get information about all the cgroup subsystems.
func GetAllCgroupSubsystems() (CgroupSubsystems, error) {
// Get all cgroup mounts.
allCgroups, err := cgroups.GetCgroupMounts(true)
if err != nil {
return CgroupSubsystems{}, err
}
emptyDisableCgroups := map[string]struct{}{}
return getCgroupSubsystemsHelper(allCgroups, emptyDisableCgroups)
}
func getCgroupSubsystemsHelper(allCgroups []cgroups.Mount, disableCgroups map[string]struct{}) (CgroupSubsystems, error) {
func getCgroupSubsystemsHelper(allCgroups []cgroups.Mount, includedMetrics container.MetricSet) (map[string]string, error) {
if len(allCgroups) == 0 {
return CgroupSubsystems{}, fmt.Errorf("failed to find cgroup mounts")
return nil, fmt.Errorf("failed to find cgroup mounts")
}
// Trim the mounts to only the subsystems we care about.
supportedCgroups := make([]cgroups.Mount, 0, len(allCgroups))
recordedMountpoints := make(map[string]struct{}, len(allCgroups))
mountPoints := make(map[string]string, len(allCgroups))
for _, mount := range allCgroups {
for _, subsystem := range mount.Subsystems {
if _, exists := disableCgroups[subsystem]; exists {
continue
}
if _, ok := supportedSubsystems[subsystem]; !ok {
// Unsupported subsystem
if !needSubsys(subsystem, includedMetrics) {
continue
}
if _, ok := mountPoints[subsystem]; ok {
@@ -91,36 +69,44 @@ func getCgroupSubsystemsHelper(allCgroups []cgroups.Mount, disableCgroups map[st
klog.V(5).Infof("skipping %s, already using mount at %s", mount.Mountpoint, mountPoints[subsystem])
continue
}
if _, ok := recordedMountpoints[mount.Mountpoint]; !ok {
// avoid appending the same mount twice in e.g. `cpu,cpuacct` case
supportedCgroups = append(supportedCgroups, mount)
recordedMountpoints[mount.Mountpoint] = struct{}{}
}
mountPoints[subsystem] = mount.Mountpoint
}
}
return CgroupSubsystems{
Mounts: supportedCgroups,
MountPoints: mountPoints,
}, nil
return mountPoints, nil
}
// Cgroup subsystems we support listing (should be the minimal set we need stats from).
var supportedSubsystems map[string]struct{} = map[string]struct{}{
"cpu": {},
"cpuacct": {},
"memory": {},
"hugetlb": {},
"pids": {},
"cpuset": {},
"blkio": {},
"io": {},
"devices": {},
"perf_event": {},
// 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,
}
func DiskStatsCopy0(major, minor uint64) *info.PerDiskStats {
// 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,
@@ -129,12 +115,12 @@ func DiskStatsCopy0(major, minor uint64) *info.PerDiskStats {
return &disk
}
type DiskKey struct {
type diskKey struct {
Major uint64
Minor uint64
}
func DiskStatsCopy1(diskStat map[DiskKey]*info.PerDiskStats) []info.PerDiskStats {
func diskStatsCopy1(diskStat map[diskKey]*info.PerDiskStats) []info.PerDiskStats {
i := 0
stat := make([]info.PerDiskStats, len(diskStat))
for _, disk := range diskStat {
@@ -144,21 +130,21 @@ func DiskStatsCopy1(diskStat map[DiskKey]*info.PerDiskStats) []info.PerDiskStats
return stat
}
func DiskStatsCopy(blkioStats []cgroups.BlkioStatEntry) (stat []info.PerDiskStats) {
func diskStatsCopy(blkioStats []cgroups.BlkioStatEntry) (stat []info.PerDiskStats) {
if len(blkioStats) == 0 {
return
}
diskStat := make(map[DiskKey]*info.PerDiskStats)
diskStat := make(map[diskKey]*info.PerDiskStats)
for i := range blkioStats {
major := blkioStats[i].Major
minor := blkioStats[i].Minor
key := DiskKey{
key := diskKey{
Major: major,
Minor: minor,
}
diskp, ok := diskStat[key]
if !ok {
diskp = DiskStatsCopy0(major, minor)
diskp = diskStatsCopy0(major, minor)
diskStat[key] = diskp
}
op := blkioStats[i].Op
@@ -167,12 +153,12 @@ func DiskStatsCopy(blkioStats []cgroups.BlkioStatEntry) (stat []info.PerDiskStat
}
diskp.Stats[op] = blkioStats[i].Value
}
return DiskStatsCopy1(diskStat)
return diskStatsCopy1(diskStat)
}
func NewCgroupManager(name string, paths map[string]string) (cgroups.Manager, error) {
if cgroups.IsCgroup2UnifiedMode() {
path := paths["cpu"]
path := paths[""]
return fs2.NewManager(nil, path, false)
}
@@ -180,5 +166,4 @@ func NewCgroupManager(name string, paths map[string]string) (cgroups.Manager, er
Name: name,
}
return fs.NewManager(&config, paths, false), nil
}

View File

@@ -29,15 +29,17 @@ import (
"k8s.io/klog/v2"
)
var dockerOnly = flag.Bool("docker_only", false, "Only report docker containers in addition to root stats")
var disableRootCgroupStats = flag.Bool("disable_root_cgroup_stats", false, "Disable collecting root Cgroup stats")
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 *libcontainer.CgroupSubsystems
cgroupSubsystems map[string]string
// Information about mounted filesystems.
fsInfo fs.FsInfo
@@ -56,7 +58,7 @@ func (f *rawFactory) String() string {
return "raw"
}
func (f *rawFactory) NewContainerHandler(name string, inHostNamespace bool) (container.ContainerHandler, error) {
func (f *rawFactory) NewContainerHandler(name string, metadataEnvAllowList []string, inHostNamespace bool) (container.ContainerHandler, error) {
rootFs := "/"
if !inHostNamespace {
rootFs = "/rootfs"
@@ -69,7 +71,7 @@ func (f *rawFactory) CanHandleAndAccept(name string) (bool, bool, error) {
if name == "/" {
return true, true, nil
}
if *dockerOnly && f.rawPrefixWhiteList[0] == "" {
if *DockerOnly && f.rawPrefixWhiteList[0] == "" {
return true, false, nil
}
for _, prefix := range f.rawPrefixWhiteList {
@@ -89,7 +91,7 @@ func Register(machineInfoFactory info.MachineInfoFactory, fsInfo fs.FsInfo, incl
if err != nil {
return fmt.Errorf("failed to get cgroup subsystems: %v", err)
}
if len(cgroupSubsystems.Mounts) == 0 {
if len(cgroupSubsystems) == 0 {
return fmt.Errorf("failed to find supported cgroup mounts for the raw factory")
}
@@ -102,7 +104,7 @@ func Register(machineInfoFactory info.MachineInfoFactory, fsInfo fs.FsInfo, incl
factory := &rawFactory{
machineInfoFactory: machineInfoFactory,
fsInfo: fsInfo,
cgroupSubsystems: &cgroupSubsystems,
cgroupSubsystems: cgroupSubsystems,
watcher: watcher,
includedMetrics: includedMetrics,
rawPrefixWhiteList: rawPrefixWhiteList,

View File

@@ -24,6 +24,7 @@ import (
"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"
)
@@ -48,13 +49,13 @@ func isRootCgroup(name string) bool {
return name == "/"
}
func newRawContainerHandler(name string, cgroupSubsystems *libcontainer.CgroupSubsystems, machineInfoFactory info.MachineInfoFactory, fsInfo fs.FsInfo, watcher *common.InotifyWatcher, rootFs string, includedMetrics container.MetricSet) (container.ContainerHandler, error) {
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.MountPoints, name)
cgroupPaths := common.MakeCgroupPaths(cgroupSubsystems, name)
cgroupManager, err := libcontainer.NewCgroupManager(name, cgroupPaths)
if err != nil {
@@ -244,7 +245,11 @@ func (h *rawContainerHandler) GetStats() (*info.ContainerStats, error) {
}
func (h *rawContainerHandler) GetCgroupPath(resource string) (string, error) {
path, ok := h.cgroupPaths[resource]
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)
}

View File

@@ -23,10 +23,11 @@ import (
"path"
"strings"
inotify "k8s.io/utils/inotify"
"github.com/google/cadvisor/container/common"
"github.com/google/cadvisor/container/libcontainer"
"github.com/google/cadvisor/watcher"
inotify "k8s.io/utils/inotify"
"k8s.io/klog/v2"
)
@@ -35,8 +36,6 @@ type rawContainerWatcher struct {
// Absolute path to the root of the cgroup hierarchies
cgroupPaths map[string]string
cgroupSubsystems *libcontainer.CgroupSubsystems
// Inotify event watcher.
watcher *common.InotifyWatcher
@@ -45,11 +44,11 @@ type rawContainerWatcher struct {
}
func NewRawContainerWatcher() (watcher.ContainerWatcher, error) {
cgroupSubsystems, err := libcontainer.GetAllCgroupSubsystems()
cgroupSubsystems, err := libcontainer.GetCgroupSubsystems(nil)
if err != nil {
return nil, fmt.Errorf("failed to get cgroup subsystems: %v", err)
}
if len(cgroupSubsystems.Mounts) == 0 {
if len(cgroupSubsystems) == 0 {
return nil, fmt.Errorf("failed to find supported cgroup mounts for the raw factory")
}
@@ -59,10 +58,9 @@ func NewRawContainerWatcher() (watcher.ContainerWatcher, error) {
}
rawWatcher := &rawContainerWatcher{
cgroupPaths: common.MakeCgroupPaths(cgroupSubsystems.MountPoints, "/"),
cgroupSubsystems: &cgroupSubsystems,
watcher: watcher,
stopWatcher: make(chan error),
cgroupPaths: cgroupSubsystems,
watcher: watcher,
stopWatcher: make(chan error),
}
return rawWatcher, nil
@@ -195,8 +193,8 @@ func (w *rawContainerWatcher) processEvent(event *inotify.Event, events chan wat
// Derive the container name from the path name.
var containerName string
for _, mount := range w.cgroupSubsystems.Mounts {
mountLocation := path.Clean(mount.Mountpoint) + "/"
for _, mount := range w.cgroupPaths {
mountLocation := path.Clean(mount) + "/"
if strings.HasPrefix(event.Name, mountLocation) {
containerName = event.Name[len(mountLocation)-1:]
break

View File

@@ -32,7 +32,7 @@ func (f *systemdFactory) String() string {
return "systemd"
}
func (f *systemdFactory) NewContainerHandler(name string, inHostNamespace bool) (container.ContainerHandler, error) {
func (f *systemdFactory) NewContainerHandler(name string, metadataEnvAllowList []string, inHostNamespace bool) (container.ContainerHandler, error) {
return nil, fmt.Errorf("Not yet supported")
}

View File

@@ -16,9 +16,10 @@
package install
import (
"k8s.io/klog/v2"
"github.com/google/cadvisor/container"
"github.com/google/cadvisor/container/systemd"
"k8s.io/klog/v2"
)
func init() {