github.com/google/cadvisor@v0.49.1/container/factory.go (about)

     1  // Copyright 2014 Google Inc. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package container
    16  
    17  import (
    18  	"fmt"
    19  	"sort"
    20  	"strings"
    21  	"sync"
    22  
    23  	"github.com/google/cadvisor/fs"
    24  	info "github.com/google/cadvisor/info/v1"
    25  	"github.com/google/cadvisor/watcher"
    26  
    27  	"k8s.io/klog/v2"
    28  )
    29  
    30  type ContainerHandlerFactory interface {
    31  	// Create a new ContainerHandler using this factory. CanHandleAndAccept() must have returned true.
    32  	NewContainerHandler(name string, metadataEnvAllowList []string, inHostNamespace bool) (c ContainerHandler, err error)
    33  
    34  	// Returns whether this factory can handle and accept the specified container.
    35  	CanHandleAndAccept(name string) (handle bool, accept bool, err error)
    36  
    37  	// Name of the factory.
    38  	String() string
    39  
    40  	// Returns debugging information. Map of lines per category.
    41  	DebugInfo() map[string][]string
    42  }
    43  
    44  // MetricKind represents the kind of metrics that cAdvisor exposes.
    45  type MetricKind string
    46  
    47  const (
    48  	CpuUsageMetrics                MetricKind = "cpu"
    49  	ProcessSchedulerMetrics        MetricKind = "sched"
    50  	PerCpuUsageMetrics             MetricKind = "percpu"
    51  	MemoryUsageMetrics             MetricKind = "memory"
    52  	MemoryNumaMetrics              MetricKind = "memory_numa"
    53  	CpuLoadMetrics                 MetricKind = "cpuLoad"
    54  	DiskIOMetrics                  MetricKind = "diskIO"
    55  	DiskUsageMetrics               MetricKind = "disk"
    56  	NetworkUsageMetrics            MetricKind = "network"
    57  	NetworkTcpUsageMetrics         MetricKind = "tcp"
    58  	NetworkAdvancedTcpUsageMetrics MetricKind = "advtcp"
    59  	NetworkUdpUsageMetrics         MetricKind = "udp"
    60  	AppMetrics                     MetricKind = "app"
    61  	ProcessMetrics                 MetricKind = "process"
    62  	HugetlbUsageMetrics            MetricKind = "hugetlb"
    63  	PerfMetrics                    MetricKind = "perf_event"
    64  	ReferencedMemoryMetrics        MetricKind = "referenced_memory"
    65  	CPUTopologyMetrics             MetricKind = "cpu_topology"
    66  	ResctrlMetrics                 MetricKind = "resctrl"
    67  	CPUSetMetrics                  MetricKind = "cpuset"
    68  	OOMMetrics                     MetricKind = "oom_event"
    69  )
    70  
    71  // AllMetrics represents all kinds of metrics that cAdvisor supported.
    72  var AllMetrics = MetricSet{
    73  	CpuUsageMetrics:                struct{}{},
    74  	ProcessSchedulerMetrics:        struct{}{},
    75  	PerCpuUsageMetrics:             struct{}{},
    76  	MemoryUsageMetrics:             struct{}{},
    77  	MemoryNumaMetrics:              struct{}{},
    78  	CpuLoadMetrics:                 struct{}{},
    79  	DiskIOMetrics:                  struct{}{},
    80  	DiskUsageMetrics:               struct{}{},
    81  	NetworkUsageMetrics:            struct{}{},
    82  	NetworkTcpUsageMetrics:         struct{}{},
    83  	NetworkAdvancedTcpUsageMetrics: struct{}{},
    84  	NetworkUdpUsageMetrics:         struct{}{},
    85  	ProcessMetrics:                 struct{}{},
    86  	AppMetrics:                     struct{}{},
    87  	HugetlbUsageMetrics:            struct{}{},
    88  	PerfMetrics:                    struct{}{},
    89  	ReferencedMemoryMetrics:        struct{}{},
    90  	CPUTopologyMetrics:             struct{}{},
    91  	ResctrlMetrics:                 struct{}{},
    92  	CPUSetMetrics:                  struct{}{},
    93  	OOMMetrics:                     struct{}{},
    94  }
    95  
    96  // AllNetworkMetrics represents all network metrics that cAdvisor supports.
    97  var AllNetworkMetrics = MetricSet{
    98  	NetworkUsageMetrics:            struct{}{},
    99  	NetworkTcpUsageMetrics:         struct{}{},
   100  	NetworkAdvancedTcpUsageMetrics: struct{}{},
   101  	NetworkUdpUsageMetrics:         struct{}{},
   102  }
   103  
   104  func (mk MetricKind) String() string {
   105  	return string(mk)
   106  }
   107  
   108  type MetricSet map[MetricKind]struct{}
   109  
   110  func (ms MetricSet) Has(mk MetricKind) bool {
   111  	_, exists := ms[mk]
   112  	return exists
   113  }
   114  
   115  func (ms MetricSet) HasAny(ms1 MetricSet) bool {
   116  	for m := range ms1 {
   117  		if _, ok := ms[m]; ok {
   118  			return true
   119  		}
   120  	}
   121  	return false
   122  }
   123  
   124  func (ms MetricSet) add(mk MetricKind) {
   125  	ms[mk] = struct{}{}
   126  }
   127  
   128  func (ms MetricSet) String() string {
   129  	values := make([]string, 0, len(ms))
   130  	for metric := range ms {
   131  		values = append(values, string(metric))
   132  	}
   133  	sort.Strings(values)
   134  	return strings.Join(values, ",")
   135  }
   136  
   137  // Not thread-safe, exported only for https://pkg.go.dev/flag#Value
   138  func (ms *MetricSet) Set(value string) error {
   139  	*ms = MetricSet{}
   140  	if value == "" {
   141  		return nil
   142  	}
   143  	for _, metric := range strings.Split(value, ",") {
   144  		if AllMetrics.Has(MetricKind(metric)) {
   145  			(*ms).add(MetricKind(metric))
   146  		} else {
   147  			return fmt.Errorf("unsupported metric %q specified", metric)
   148  		}
   149  	}
   150  	return nil
   151  }
   152  
   153  func (ms MetricSet) Difference(ms1 MetricSet) MetricSet {
   154  	result := MetricSet{}
   155  	for kind := range ms {
   156  		if !ms1.Has(kind) {
   157  			result.add(kind)
   158  		}
   159  	}
   160  	return result
   161  }
   162  
   163  func (ms MetricSet) Append(ms1 MetricSet) MetricSet {
   164  	result := ms
   165  	for kind := range ms1 {
   166  		if !ms.Has(kind) {
   167  			result.add(kind)
   168  		}
   169  	}
   170  	return result
   171  }
   172  
   173  // All registered auth provider plugins.
   174  var pluginsLock sync.Mutex
   175  var plugins = make(map[string]Plugin)
   176  
   177  type Plugin interface {
   178  	// InitializeFSContext is invoked when populating an fs.Context object for a new manager.
   179  	// A returned error here is fatal.
   180  	InitializeFSContext(context *fs.Context) error
   181  
   182  	// Register is invoked when starting a manager. It can optionally return a container watcher.
   183  	// A returned error is logged, but is not fatal.
   184  	Register(factory info.MachineInfoFactory, fsInfo fs.FsInfo, includedMetrics MetricSet) (watcher.ContainerWatcher, error)
   185  }
   186  
   187  func RegisterPlugin(name string, plugin Plugin) error {
   188  	pluginsLock.Lock()
   189  	defer pluginsLock.Unlock()
   190  	if _, found := plugins[name]; found {
   191  		return fmt.Errorf("Plugin %q was registered twice", name)
   192  	}
   193  	klog.V(4).Infof("Registered Plugin %q", name)
   194  	plugins[name] = plugin
   195  	return nil
   196  }
   197  
   198  func InitializeFSContext(context *fs.Context) error {
   199  	pluginsLock.Lock()
   200  	defer pluginsLock.Unlock()
   201  	for name, plugin := range plugins {
   202  		err := plugin.InitializeFSContext(context)
   203  		if err != nil {
   204  			klog.V(5).Infof("Initialization of the %s context failed: %v", name, err)
   205  			return err
   206  		}
   207  	}
   208  	return nil
   209  }
   210  
   211  func InitializePlugins(factory info.MachineInfoFactory, fsInfo fs.FsInfo, includedMetrics MetricSet) []watcher.ContainerWatcher {
   212  	pluginsLock.Lock()
   213  	defer pluginsLock.Unlock()
   214  
   215  	containerWatchers := []watcher.ContainerWatcher{}
   216  	for name, plugin := range plugins {
   217  		watcher, err := plugin.Register(factory, fsInfo, includedMetrics)
   218  		if err != nil {
   219  			klog.Infof("Registration of the %s container factory failed: %v", name, err)
   220  		} else {
   221  			klog.Infof("Registration of the %s container factory successfully", name)
   222  		}
   223  		if watcher != nil {
   224  			containerWatchers = append(containerWatchers, watcher)
   225  		}
   226  	}
   227  	return containerWatchers
   228  }
   229  
   230  // TODO(vmarmol): Consider not making this global.
   231  // Global list of factories.
   232  var (
   233  	factories     = map[watcher.ContainerWatchSource][]ContainerHandlerFactory{}
   234  	factoriesLock sync.RWMutex
   235  )
   236  
   237  // Register a ContainerHandlerFactory. These should be registered from least general to most general
   238  // as they will be asked in order whether they can handle a particular container.
   239  func RegisterContainerHandlerFactory(factory ContainerHandlerFactory, watchTypes []watcher.ContainerWatchSource) {
   240  	factoriesLock.Lock()
   241  	defer factoriesLock.Unlock()
   242  
   243  	for _, watchType := range watchTypes {
   244  		factories[watchType] = append(factories[watchType], factory)
   245  	}
   246  }
   247  
   248  // Returns whether there are any container handler factories registered.
   249  func HasFactories() bool {
   250  	factoriesLock.Lock()
   251  	defer factoriesLock.Unlock()
   252  
   253  	return len(factories) != 0
   254  }
   255  
   256  // Create a new ContainerHandler for the specified container.
   257  func NewContainerHandler(name string, watchType watcher.ContainerWatchSource, metadataEnvAllowList []string, inHostNamespace bool) (ContainerHandler, bool, error) {
   258  	factoriesLock.RLock()
   259  	defer factoriesLock.RUnlock()
   260  
   261  	// Create the ContainerHandler with the first factory that supports it.
   262  	// Note that since RawContainerHandler can support a wide range of paths,
   263  	// it's evaluated last just to make sure if any other ContainerHandler
   264  	// can support it.
   265  	for _, factory := range GetReorderedFactoryList(watchType) {
   266  		canHandle, canAccept, err := factory.CanHandleAndAccept(name)
   267  		if err != nil {
   268  			klog.V(4).Infof("Error trying to work out if we can handle %s: %v", name, err)
   269  		}
   270  		if canHandle {
   271  			if !canAccept {
   272  				klog.V(3).Infof("Factory %q can handle container %q, but ignoring.", factory, name)
   273  				return nil, false, nil
   274  			}
   275  			klog.V(3).Infof("Using factory %q for container %q", factory, name)
   276  			handle, err := factory.NewContainerHandler(name, metadataEnvAllowList, inHostNamespace)
   277  			return handle, canAccept, err
   278  		}
   279  		klog.V(4).Infof("Factory %q was unable to handle container %q", factory, name)
   280  	}
   281  
   282  	return nil, false, fmt.Errorf("no known factory can handle creation of container")
   283  }
   284  
   285  // Clear the known factories.
   286  func ClearContainerHandlerFactories() {
   287  	factoriesLock.Lock()
   288  	defer factoriesLock.Unlock()
   289  
   290  	factories = map[watcher.ContainerWatchSource][]ContainerHandlerFactory{}
   291  }
   292  
   293  func DebugInfo() map[string][]string {
   294  	factoriesLock.RLock()
   295  	defer factoriesLock.RUnlock()
   296  
   297  	// Get debug information for all factories.
   298  	out := make(map[string][]string)
   299  	for _, factoriesSlice := range factories {
   300  		for _, factory := range factoriesSlice {
   301  			for k, v := range factory.DebugInfo() {
   302  				out[k] = v
   303  			}
   304  		}
   305  	}
   306  	return out
   307  }
   308  
   309  // GetReorderedFactoryList returns the list of ContainerHandlerFactory where the
   310  // RawContainerHandler is always the last element.
   311  func GetReorderedFactoryList(watchType watcher.ContainerWatchSource) []ContainerHandlerFactory {
   312  	ContainerHandlerFactoryList := make([]ContainerHandlerFactory, 0, len(factories))
   313  
   314  	var rawFactory ContainerHandlerFactory
   315  	for _, v := range factories[watchType] {
   316  		if v != nil {
   317  			if v.String() == "raw" {
   318  				rawFactory = v
   319  				continue
   320  			}
   321  			ContainerHandlerFactoryList = append(ContainerHandlerFactoryList, v)
   322  		}
   323  	}
   324  
   325  	if rawFactory != nil {
   326  		ContainerHandlerFactoryList = append(ContainerHandlerFactoryList, rawFactory)
   327  	}
   328  
   329  	return ContainerHandlerFactoryList
   330  }