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 }