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  }