github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/operators/kubemanager/kubemanager.go (about) 1 // Copyright 2023-2024 The Inspektor Gadget authors 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 kubemanager 16 17 import ( 18 "errors" 19 "fmt" 20 "strings" 21 22 "github.com/cilium/ebpf" 23 "github.com/google/uuid" 24 log "github.com/sirupsen/logrus" 25 26 containercollection "github.com/inspektor-gadget/inspektor-gadget/pkg/container-collection" 27 "github.com/inspektor-gadget/inspektor-gadget/pkg/datasource" 28 "github.com/inspektor-gadget/inspektor-gadget/pkg/datasource/compat" 29 "github.com/inspektor-gadget/inspektor-gadget/pkg/gadget-service/api" 30 apihelpers "github.com/inspektor-gadget/inspektor-gadget/pkg/gadget-service/api-helpers" 31 "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets" 32 "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgettracermanager" 33 "github.com/inspektor-gadget/inspektor-gadget/pkg/operators" 34 "github.com/inspektor-gadget/inspektor-gadget/pkg/params" 35 "github.com/inspektor-gadget/inspektor-gadget/pkg/types" 36 ) 37 38 const ( 39 OperatorName = "KubeManager" 40 ParamContainerName = "containername" 41 ParamSelector = "selector" 42 ParamAllNamespaces = "all-namespaces" 43 ParamPodName = "podname" 44 ParamNamespace = "namespace" 45 ) 46 47 type MountNsMapSetter interface { 48 SetMountNsMap(*ebpf.Map) 49 } 50 51 type Attacher interface { 52 AttachContainer(container *containercollection.Container) error 53 DetachContainer(*containercollection.Container) error 54 } 55 56 type KubeManager struct { 57 gadgetTracerManager *gadgettracermanager.GadgetTracerManager 58 } 59 60 func (k *KubeManager) SetGadgetTracerMgr(g *gadgettracermanager.GadgetTracerManager) { 61 log.Infof("gadget tracermgr set in kubemanager") 62 k.gadgetTracerManager = g 63 } 64 65 func (k *KubeManager) Name() string { 66 return OperatorName 67 } 68 69 func (k *KubeManager) Description() string { 70 return "KubeManager handles container/pod/namespace information using Container-Collection and GadgetTracerMgr" 71 } 72 73 func (k *KubeManager) GlobalParamDescs() params.ParamDescs { 74 return nil 75 } 76 77 func (k *KubeManager) ParamDescs() params.ParamDescs { 78 return params.ParamDescs{ 79 { 80 Key: ParamContainerName, 81 Alias: "c", 82 Description: "Show only data from containers with that name", 83 ValueHint: gadgets.K8SContainerName, 84 }, 85 { 86 Key: ParamSelector, 87 Alias: "l", 88 Description: "Labels selector to filter on. Only '=' is supported (e.g. key1=value1,key2=value2).", 89 ValueHint: gadgets.K8SLabels, 90 Validator: func(value string) error { 91 if value == "" { 92 return nil 93 } 94 95 pairs := strings.Split(value, ",") 96 for _, pair := range pairs { 97 kv := strings.Split(pair, "=") 98 if len(kv) != 2 { 99 return fmt.Errorf("should be a comma-separated list of key-value pairs (key=value[,key=value,...])") 100 } 101 } 102 103 return nil 104 }, 105 }, 106 { 107 Key: ParamPodName, 108 Alias: "p", 109 Description: "Show only data from pods with that name", 110 ValueHint: gadgets.K8SPodName, 111 }, 112 { 113 Key: ParamAllNamespaces, 114 Alias: "A", 115 Description: "Show data from pods in all namespaces", 116 TypeHint: params.TypeBool, 117 DefaultValue: "false", 118 }, 119 { 120 Key: ParamNamespace, 121 Alias: "n", 122 Description: "Show only data from pods in a given namespace", 123 ValueHint: gadgets.K8SNamespace, 124 }, 125 } 126 } 127 128 func (k *KubeManager) Dependencies() []string { 129 return nil 130 } 131 132 func (k *KubeManager) CanOperateOn(gadget gadgets.GadgetDesc) bool { 133 // We need to be able to get MountNSID or NetNSID, and set ContainerInfo, so 134 // check for that first 135 _, canEnrichEventFromMountNs := gadget.EventPrototype().(operators.ContainerInfoFromMountNSID) 136 _, canEnrichEventFromNetNs := gadget.EventPrototype().(operators.ContainerInfoFromNetNSID) 137 canEnrichEvent := canEnrichEventFromMountNs || canEnrichEventFromNetNs 138 139 // Secondly, we need to be able to inject the ebpf map onto the tracer 140 gi, ok := gadget.(gadgets.GadgetInstantiate) 141 if !ok { 142 return false 143 } 144 145 instance, err := gi.NewInstance() 146 if err != nil { 147 log.Warnf("failed to create dummy %s instance: %s", OperatorName, err) 148 return false 149 } 150 _, isMountNsMapSetter := instance.(MountNsMapSetter) 151 _, isAttacher := instance.(Attacher) 152 153 log.Debugf("> canEnrichEvent: %v", canEnrichEvent) 154 log.Debugf(" > canEnrichEventFromMountNs: %v", canEnrichEventFromMountNs) 155 log.Debugf(" > canEnrichEventFromNetNs: %v", canEnrichEventFromNetNs) 156 log.Debugf("> isMountNsMapSetter: %v", isMountNsMapSetter) 157 log.Debugf("> isAttacher: %v", isAttacher) 158 159 return isMountNsMapSetter || canEnrichEvent || isAttacher 160 } 161 162 func (k *KubeManager) Init(params *params.Params) error { 163 return nil 164 } 165 166 func (k *KubeManager) Close() error { 167 return nil 168 } 169 170 func (k *KubeManager) Instantiate(gadgetContext operators.GadgetContext, gadgetInstance any, params *params.Params) (operators.OperatorInstance, error) { 171 _, canEnrichEventFromMountNs := gadgetContext.GadgetDesc().EventPrototype().(operators.ContainerInfoFromMountNSID) 172 _, canEnrichEventFromNetNs := gadgetContext.GadgetDesc().EventPrototype().(operators.ContainerInfoFromNetNSID) 173 canEnrichEvent := canEnrichEventFromMountNs || canEnrichEventFromNetNs 174 175 traceInstance := &KubeManagerInstance{ 176 id: uuid.New().String(), 177 manager: k, 178 enrichEvents: canEnrichEvent, 179 params: params, 180 gadgetInstance: gadgetInstance, 181 gadgetCtx: gadgetContext, 182 } 183 184 return traceInstance, nil 185 } 186 187 type KubeManagerInstance struct { 188 id string 189 manager *KubeManager 190 enrichEvents bool 191 mountnsmap *ebpf.Map 192 subscribed bool 193 194 attachedContainers map[string]*containercollection.Container 195 attacher Attacher 196 params *params.Params 197 gadgetInstance any 198 gadgetCtx operators.GadgetContext 199 200 eventWrappers map[datasource.DataSource]*compat.EventWrapperBase 201 } 202 203 func (m *KubeManagerInstance) Name() string { 204 return OperatorName 205 } 206 207 func (m *KubeManagerInstance) PreGadgetRun() error { 208 log := m.gadgetCtx.Logger() 209 210 labels := make(map[string]string) 211 selectorSlice := m.params.Get(ParamSelector).AsStringSlice() 212 for _, pair := range selectorSlice { 213 kv := strings.Split(pair, "=") 214 labels[kv[0]] = kv[1] 215 } 216 217 containerSelector := containercollection.ContainerSelector{ 218 K8s: containercollection.K8sSelector{ 219 BasicK8sMetadata: types.BasicK8sMetadata{ 220 Namespace: m.params.Get(ParamNamespace).AsString(), 221 PodName: m.params.Get(ParamPodName).AsString(), 222 ContainerName: m.params.Get(ParamContainerName).AsString(), 223 PodLabels: labels, 224 }, 225 }, 226 } 227 228 if m.params.Get(ParamAllNamespaces).AsBool() { 229 containerSelector.K8s.Namespace = "" 230 } 231 232 if setter, ok := m.gadgetInstance.(MountNsMapSetter); ok { 233 err := m.manager.gadgetTracerManager.AddTracer(m.id, containerSelector) 234 if err != nil { 235 return fmt.Errorf("adding tracer: %w", err) 236 } 237 238 // Create mount namespace map to filter by containers 239 mountnsmap, err := m.manager.gadgetTracerManager.TracerMountNsMap(m.id) 240 if err != nil { 241 m.manager.gadgetTracerManager.RemoveTracer(m.id) 242 return fmt.Errorf("creating mountns map: %w", err) 243 } 244 245 log.Debugf("set mountnsmap for gadget") 246 setter.SetMountNsMap(mountnsmap) 247 248 m.mountnsmap = mountnsmap 249 } 250 251 if attacher, ok := m.gadgetInstance.(Attacher); ok { 252 m.attacher = attacher 253 m.attachedContainers = make(map[string]*containercollection.Container) 254 255 attachContainerFunc := func(container *containercollection.Container) { 256 log.Debugf("calling gadget.AttachContainer()") 257 err := attacher.AttachContainer(container) 258 if err != nil { 259 var ve *ebpf.VerifierError 260 if errors.As(err, &ve) { 261 m.gadgetCtx.Logger().Debugf("start tracing container %q: verifier error: %+v\n", container.K8s.ContainerName, ve) 262 } 263 264 log.Warnf("start tracing container %q: %s", container.K8s.ContainerName, err) 265 return 266 } 267 268 m.attachedContainers[container.Runtime.ContainerID] = container 269 270 log.Debugf("tracer attached: container %q pid %d mntns %d netns %d", 271 container.K8s.ContainerName, container.Pid, container.Mntns, container.Netns) 272 } 273 274 detachContainerFunc := func(container *containercollection.Container) { 275 log.Debugf("calling gadget.Detach()") 276 delete(m.attachedContainers, container.Runtime.ContainerID) 277 278 err := attacher.DetachContainer(container) 279 if err != nil { 280 log.Warnf("stop tracing container %q: %s", container.K8s.ContainerName, err) 281 return 282 } 283 log.Debugf("tracer detached: container %q pid %d mntns %d netns %d", 284 container.K8s.ContainerName, container.Pid, container.Mntns, container.Netns) 285 } 286 287 m.subscribed = true 288 289 log.Debugf("add subscription to gadgetTracerManager") 290 containers := m.manager.gadgetTracerManager.Subscribe( 291 m.id, 292 containerSelector, 293 func(event containercollection.PubSubEvent) { 294 log.Debugf("%s: %s", event.Type.String(), event.Container.Runtime.ContainerID) 295 switch event.Type { 296 case containercollection.EventTypeAddContainer: 297 attachContainerFunc(event.Container) 298 case containercollection.EventTypeRemoveContainer: 299 detachContainerFunc(event.Container) 300 } 301 }, 302 ) 303 304 for _, container := range containers { 305 attachContainerFunc(container) 306 } 307 } 308 309 return nil 310 } 311 312 func (m *KubeManagerInstance) PostGadgetRun() error { 313 if m.mountnsmap != nil { 314 m.gadgetCtx.Logger().Debugf("calling RemoveTracer()") 315 m.manager.gadgetTracerManager.RemoveTracer(m.id) 316 } 317 if m.subscribed { 318 m.gadgetCtx.Logger().Debugf("calling Unsubscribe()") 319 m.manager.gadgetTracerManager.Unsubscribe(m.id) 320 321 // emit detach for all remaining containers 322 for _, container := range m.attachedContainers { 323 m.attacher.DetachContainer(container) 324 } 325 } 326 return nil 327 } 328 329 func (m *KubeManagerInstance) enrich(ev any) { 330 if event, canEnrichEventFromMountNs := ev.(operators.ContainerInfoFromMountNSID); canEnrichEventFromMountNs { 331 m.manager.gadgetTracerManager.ContainerCollection.EnrichEventByMntNs(event) 332 } 333 if event, canEnrichEventFromNetNs := ev.(operators.ContainerInfoFromNetNSID); canEnrichEventFromNetNs { 334 m.manager.gadgetTracerManager.ContainerCollection.EnrichEventByNetNs(event) 335 } 336 } 337 338 func (m *KubeManagerInstance) EnrichEvent(ev any) error { 339 if !m.enrichEvents { 340 return nil 341 } 342 m.enrich(ev) 343 return nil 344 } 345 346 func (k *KubeManager) GlobalParams() api.Params { 347 return apihelpers.ParamDescsToParams(k.GlobalParamDescs()) 348 } 349 350 func (k *KubeManager) InstanceParams() api.Params { 351 return apihelpers.ParamDescsToParams(k.ParamDescs()) 352 } 353 354 func (k *KubeManager) InstantiateDataOperator(gadgetCtx operators.GadgetContext, paramValues api.ParamValues) ( 355 operators.DataOperatorInstance, error, 356 ) { 357 params := k.ParamDescs().ToParams() 358 err := params.CopyFromMap(paramValues, "") 359 if err != nil { 360 return nil, err 361 } 362 363 traceInstance := &KubeManagerInstance{ 364 manager: k, 365 enrichEvents: false, 366 attachedContainers: make(map[string]*containercollection.Container), 367 params: params, 368 gadgetCtx: gadgetCtx, 369 id: uuid.New().String(), 370 371 eventWrappers: make(map[datasource.DataSource]*compat.EventWrapperBase), 372 } 373 374 // hack - this makes it possible to use the Attacher interface 375 var ok bool 376 traceInstance.gadgetInstance, ok = gadgetCtx.GetVar("ebpfInstance") 377 if !ok { 378 return nil, fmt.Errorf("getting ebpfInstance") 379 } 380 381 activate := false 382 383 // Check, whether the gadget requested a map from us 384 if t, ok := gadgetCtx.GetVar(gadgets.MntNsFilterMapName); ok { 385 if _, ok := t.(*ebpf.Map); ok { 386 gadgetCtx.Logger().Debugf("gadget requested map %s", gadgets.MntNsFilterMapName) 387 activate = true 388 } 389 } 390 391 // Check for NeedContainerEvents; this is set for example for tchandlers, as they 392 // require the Attacher interface to be aware of containers 393 if val, ok := gadgetCtx.GetVar("NeedContainerEvents"); ok { 394 if b, ok := val.(bool); ok && b { 395 activate = true 396 } 397 } 398 399 wrappers, err := compat.GetEventWrappers(gadgetCtx) 400 if err != nil { 401 return nil, fmt.Errorf("getting event wrappers: %w", err) 402 } 403 traceInstance.eventWrappers = wrappers 404 if len(wrappers) > 0 { 405 activate = true 406 } 407 408 if !activate { 409 return nil, nil 410 } 411 412 return traceInstance, nil 413 } 414 415 func (k *KubeManager) Priority() int { 416 return -1 417 } 418 419 func (m *KubeManagerInstance) ParamDescs(gadgetCtx operators.GadgetContext) params.ParamDescs { 420 return m.manager.ParamDescs() 421 } 422 423 func (m *KubeManagerInstance) PreStart(gadgetCtx operators.GadgetContext) error { 424 compat.Subscribe( 425 m.eventWrappers, 426 m.manager.gadgetTracerManager.ContainerCollection.EnrichEventByMntNs, 427 m.manager.gadgetTracerManager.ContainerCollection.EnrichEventByNetNs, 428 0, 429 ) 430 431 labels := make(map[string]string) 432 selectorSlice := m.params.Get(ParamSelector).AsStringSlice() 433 for _, pair := range selectorSlice { 434 kv := strings.Split(pair, "=") 435 labels[kv[0]] = kv[1] 436 } 437 438 containerSelector := containercollection.ContainerSelector{ 439 K8s: containercollection.K8sSelector{ 440 BasicK8sMetadata: types.BasicK8sMetadata{ 441 Namespace: m.params.Get(ParamNamespace).AsString(), 442 PodName: m.params.Get(ParamPodName).AsString(), 443 ContainerName: m.params.Get(ParamContainerName).AsString(), 444 PodLabels: labels, 445 }, 446 }, 447 } 448 449 if m.manager.gadgetTracerManager == nil { 450 return fmt.Errorf("container-collection isn't available") 451 } 452 453 // Create mount namespace map to filter by containers 454 err := m.manager.gadgetTracerManager.AddTracer(m.id, containerSelector) 455 if err != nil { 456 return fmt.Errorf("adding tracer: %w", err) 457 } 458 459 mountnsmap, err := m.manager.gadgetTracerManager.TracerMountNsMap(m.id) 460 if err != nil { 461 m.manager.gadgetTracerManager.RemoveTracer(m.id) 462 return fmt.Errorf("creating mountnsmap: %w", err) 463 } 464 465 gadgetCtx.Logger().Debugf("set mountnsmap for gadget") 466 gadgetCtx.SetVar(gadgets.MntNsFilterMapName, mountnsmap) 467 gadgetCtx.SetVar(gadgets.FilterByMntNsName, true) 468 469 m.mountnsmap = mountnsmap 470 // using PreGadgetRun() for the time being to register attacher funcs 471 return m.PreGadgetRun() 472 } 473 474 func (m *KubeManagerInstance) Start(gadgetCtx operators.GadgetContext) error { 475 return nil 476 } 477 478 func (m *KubeManagerInstance) Stop(gadgetCtx operators.GadgetContext) error { 479 m.manager.gadgetTracerManager.RemoveTracer(m.id) 480 return nil 481 } 482 483 func init() { 484 km := &KubeManager{} 485 operators.Register(km) 486 operators.RegisterDataOperator(km) 487 }