github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/operators/operators.go (about)

     1  // Copyright 2022-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 operators
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"sync"
    21  
    22  	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
    23  	log "github.com/sirupsen/logrus"
    24  
    25  	"github.com/inspektor-gadget/inspektor-gadget/pkg/datasource"
    26  	"github.com/inspektor-gadget/inspektor-gadget/pkg/gadget-service/api"
    27  	"github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets"
    28  	"github.com/inspektor-gadget/inspektor-gadget/pkg/logger"
    29  	"github.com/inspektor-gadget/inspektor-gadget/pkg/params"
    30  	"github.com/inspektor-gadget/inspektor-gadget/pkg/types"
    31  )
    32  
    33  type GadgetContext interface {
    34  	ID() string
    35  	Context() context.Context
    36  	GadgetDesc() gadgets.GadgetDesc
    37  	Logger() logger.Logger
    38  
    39  	Cancel()
    40  	SerializeGadgetInfo() (*api.GadgetInfo, error)
    41  	ImageName() string
    42  	RegisterDataSource(datasource.Type, string) (datasource.DataSource, error)
    43  	GetDataSources() map[string]datasource.DataSource
    44  	SetVar(string, any)
    45  	GetVar(string) (any, bool)
    46  	Params() []*api.Param
    47  	SetParams([]*api.Param)
    48  	SetMetadata([]byte)
    49  }
    50  
    51  type (
    52  	EnricherFunc func(any) error
    53  )
    54  
    55  type Operator interface {
    56  	// Name must return a unique name for the operator
    57  	Name() string
    58  
    59  	// Description is an optional description to show to the user
    60  	Description() string
    61  
    62  	// GlobalParamDescs will return global params (required) for this operator
    63  	GlobalParamDescs() params.ParamDescs
    64  
    65  	// ParamDescs will return params (required) per gadget instance of the operator
    66  	ParamDescs() params.ParamDescs
    67  
    68  	// Dependencies can list other operators that this operator depends on
    69  	Dependencies() []string
    70  
    71  	// CanOperateOn should test whether the operator supports the given gadget. Init has not
    72  	// necessarily been called at this point.
    73  	CanOperateOn(gadgets.GadgetDesc) bool
    74  
    75  	// Init allows the operator to initialize itself
    76  	Init(params *params.Params) error
    77  
    78  	// Close allows the operator to clean up stuff prior to exiting
    79  	Close() error
    80  
    81  	// Instantiate is called before a gadget is run with this operator.
    82  	// This must return something that implements OperatorInstance.
    83  	// This is useful to create a context for an operator by wrapping it.
    84  	// Params given here are the ones returned by ParamDescs()
    85  	Instantiate(gadgetCtx GadgetContext, gadgetInstance any, params *params.Params) (OperatorInstance, error)
    86  }
    87  
    88  type ImageOperator interface {
    89  	Name() string
    90  
    91  	// InstantiateImageOperator will be run to load information about a gadget and also to _possibly_
    92  	// run the gadget afterward. It should only do things that are required to populate
    93  	// DataSources and Params. It could use caching to speed things up, if necessary.
    94  	InstantiateImageOperator(gadgetCtx GadgetContext, descriptor ocispec.Descriptor,
    95  		paramValues api.ParamValues) (ImageOperatorInstance, error)
    96  }
    97  
    98  type ImageOperatorInstance interface {
    99  	Name() string
   100  	Prepare(gadgetCtx GadgetContext) error
   101  	Start(gadgetCtx GadgetContext) error
   102  	Stop(gadgetCtx GadgetContext) error
   103  	ExtraParams(gadgetCtx GadgetContext) api.Params
   104  }
   105  
   106  type DataOperator interface {
   107  	Name() string
   108  
   109  	// Init allows the operator to initialize itself
   110  	Init(params *params.Params) error
   111  
   112  	// GlobalParams should return global params (required) for this operator; these are valid globally for the process
   113  	GlobalParams() api.Params
   114  
   115  	// InstanceParams should return parameters valid for a single gadget run
   116  	InstanceParams() api.Params
   117  
   118  	// InstantiateDataOperator should create a new (lightweight) instance for the operator that can read/write
   119  	// from and to DataSources, register Params and read/write Variables; instanceParamValues can contain values for
   120  	// both params defined by InstanceParams() as well as params defined by DataOperatorInstance.ExtraParams())
   121  	InstantiateDataOperator(gadgetCtx GadgetContext, instanceParamValues api.ParamValues) (DataOperatorInstance, error)
   122  
   123  	Priority() int
   124  }
   125  
   126  type DataOperatorInstance interface {
   127  	Name() string
   128  	Start(gadgetCtx GadgetContext) error
   129  	Stop(gadgetCtx GadgetContext) error
   130  }
   131  
   132  type DataOperatorExtraParams interface {
   133  	// ExtraParams can return dynamically created params; they are read after Prepare() has been called
   134  	ExtraParams(gadgetCtx GadgetContext) api.Params
   135  }
   136  
   137  type PreStart interface {
   138  	PreStart(gadgetCtx GadgetContext) error
   139  }
   140  
   141  type PostStop interface {
   142  	PostStop(gadgetCtx GadgetContext) error
   143  }
   144  
   145  type OperatorInstance interface {
   146  	// Name returns the name of the operator instance
   147  	Name() string
   148  
   149  	// PreGadgetRun in called before a gadget is run
   150  	PreGadgetRun() error
   151  
   152  	// PostGadgetRun is called after a gadget is run
   153  	PostGadgetRun() error
   154  
   155  	// EnrichEvent enriches the given event with additional data
   156  	EnrichEvent(ev any) error
   157  }
   158  
   159  type Operators []Operator
   160  
   161  // ContainerInfoFromMountNSID is a typical kubernetes operator interface that adds node, pod, namespace and container
   162  // information given the MountNSID
   163  type ContainerInfoFromMountNSID interface {
   164  	ContainerInfoSetters
   165  	GetMountNSID() uint64
   166  }
   167  
   168  type ContainerInfoFromNetNSID interface {
   169  	ContainerInfoSetters
   170  	GetNetNSID() uint64
   171  }
   172  
   173  type ContainerInfoSetters interface {
   174  	NodeSetter
   175  	SetPodMetadata(types.Container)
   176  	SetContainerMetadata(types.Container)
   177  }
   178  
   179  type NodeSetter interface {
   180  	SetNode(string)
   181  }
   182  
   183  type ContainerInfoGetters interface {
   184  	GetNode() string
   185  	GetPod() string
   186  	GetNamespace() string
   187  	GetContainer() string
   188  	GetContainerImageName() string
   189  }
   190  
   191  var allOperators = map[string]Operator{}
   192  
   193  type operatorWrapper struct {
   194  	Operator
   195  	initOnce    sync.Once
   196  	initialized bool
   197  }
   198  
   199  func (e *operatorWrapper) Init(params *params.Params) (err error) {
   200  	e.initOnce.Do(func() {
   201  		err = e.Operator.Init(params)
   202  		e.initialized = true
   203  	})
   204  	return err
   205  }
   206  
   207  func GetRaw(name string) Operator {
   208  	if op, ok := allOperators[name]; ok {
   209  		return op.(*operatorWrapper).Operator
   210  	}
   211  	return nil
   212  }
   213  
   214  // Register adds a new operator to the registry
   215  func Register(operator Operator) {
   216  	if _, ok := allOperators[operator.Name()]; ok {
   217  		panic(fmt.Errorf("operator already registered: %q", operator.Name()))
   218  	}
   219  	allOperators[operator.Name()] = &operatorWrapper{Operator: operator}
   220  }
   221  
   222  // GlobalParamsCollection returns a collection of params of all registered operators
   223  func GlobalParamsCollection() params.Collection {
   224  	pc := make(params.Collection)
   225  	for _, operator := range allOperators {
   226  		pc[operator.Name()] = operator.GlobalParamDescs().ToParams()
   227  	}
   228  	return pc
   229  }
   230  
   231  // GetAll returns all registered operators
   232  func GetAll() Operators {
   233  	operators := make(Operators, 0, len(allOperators))
   234  	for _, op := range allOperators {
   235  		operators = append(operators, op)
   236  	}
   237  	return operators
   238  }
   239  
   240  // GetOperatorsForGadget checks which operators can work with the given gadgets and returns a collection
   241  // of them
   242  func GetOperatorsForGadget(gadget gadgets.GadgetDesc) Operators {
   243  	out := make(Operators, 0)
   244  	for _, operator := range allOperators {
   245  		if operator.CanOperateOn(gadget) {
   246  			out = append(out, operator)
   247  		}
   248  	}
   249  	out, err := SortOperators(out)
   250  	if err != nil {
   251  		panic(fmt.Sprintf("sorting operators: %v", err))
   252  	}
   253  	return out
   254  }
   255  
   256  // Init initializes all operators in the collection using their respective params
   257  func (e Operators) Init(pc params.Collection) error {
   258  	for _, operator := range e {
   259  		err := operator.Init(pc[operator.Name()])
   260  		if err != nil {
   261  			return fmt.Errorf("initializing operator %q: %w", operator.Name(), err)
   262  		}
   263  	}
   264  	return nil
   265  }
   266  
   267  // Close closes all operators in the collection; errors will be written to the log
   268  func (e Operators) Close() {
   269  	for _, operator := range e {
   270  		err := operator.Close()
   271  		if err != nil {
   272  			log.Warnf("closing operator %q: %v", operator.Name(), err)
   273  		}
   274  	}
   275  }
   276  
   277  // ParamDescCollection returns a collection of parameter descriptors for all members of the operator collection
   278  func (e Operators) ParamDescCollection() params.DescCollection {
   279  	pc := make(params.DescCollection)
   280  	for _, operator := range e {
   281  		desc := operator.ParamDescs()
   282  		pc[operator.Name()] = &desc
   283  	}
   284  	return pc
   285  }
   286  
   287  // ParamCollection returns a collection of parameters for all members of the operator collection
   288  func (e Operators) ParamCollection() params.Collection {
   289  	pc := make(params.Collection)
   290  	for _, operator := range e {
   291  		pc[operator.Name()] = operator.ParamDescs().ToParams()
   292  	}
   293  	return pc
   294  }
   295  
   296  type OperatorInstances []OperatorInstance
   297  
   298  // Instantiate calls Instantiate on all operators and returns a collection of the results.
   299  // It also calls PreGadgetRun on all instances.
   300  func (e Operators) Instantiate(gadgetContext GadgetContext, trace any, perGadgetParamCollection params.Collection) (operatorInstances OperatorInstances, _ error) {
   301  	operatorInstances = make([]OperatorInstance, 0, len(e))
   302  
   303  	for _, operator := range e {
   304  		oi, err := operator.Instantiate(gadgetContext, trace, perGadgetParamCollection[operator.Name()])
   305  		if err != nil {
   306  			return nil, fmt.Errorf("start trace on operator %q: %w", operator.Name(), err)
   307  		}
   308  		if oi == nil {
   309  			// skip operators that opted out of handling the gadget
   310  			continue
   311  		}
   312  		operatorInstances = append(operatorInstances, oi)
   313  	}
   314  
   315  	return operatorInstances, nil
   316  }
   317  
   318  func (oi OperatorInstances) PreGadgetRun() error {
   319  	loadedInstances := make(OperatorInstances, 0, len(oi))
   320  	for _, instance := range oi {
   321  		if err := instance.PreGadgetRun(); err != nil {
   322  			loadedInstances.PostGadgetRun()
   323  			return fmt.Errorf("pre gadget run on operator %q: %w", instance.Name(), err)
   324  		}
   325  		loadedInstances = append(loadedInstances, instance)
   326  	}
   327  	return nil
   328  }
   329  
   330  func (oi OperatorInstances) PostGadgetRun() error {
   331  	// TODO: Handling errors?
   332  	for _, instance := range oi {
   333  		instance.PostGadgetRun()
   334  	}
   335  	return nil
   336  }
   337  
   338  // Enrich an event using all members of the operator collection
   339  func (oi OperatorInstances) Enrich(ev any) error {
   340  	var err error
   341  	for _, operator := range oi {
   342  		if err = operator.EnrichEvent(ev); err != nil {
   343  			return fmt.Errorf("operator %q failed to enrich event %+v", operator.Name(), ev)
   344  		}
   345  	}
   346  	return nil
   347  }
   348  
   349  // SortOperators builds a dependency tree of the given operator collection and sorts them by least dependencies first
   350  // Returns an error, if there are loops or missing dependencies
   351  func SortOperators(operators Operators) (Operators, error) {
   352  	// Create a map to store the incoming edge count for each element
   353  	incomingEdges := make(map[string]int)
   354  	for _, e := range operators {
   355  		// Initialize the incoming edge count for each element to zero
   356  		incomingEdges[e.Name()] = 0
   357  	}
   358  
   359  	// Build the graph by adding an incoming edge for each dependency
   360  	for _, e := range operators {
   361  		for _, d := range e.Dependencies() {
   362  			incomingEdges[d]++
   363  		}
   364  	}
   365  
   366  	// Check if all dependencies are in operators
   367  outerLoop:
   368  	for opName := range incomingEdges {
   369  		for _, e := range operators {
   370  			if opName == e.Name() {
   371  				continue outerLoop
   372  			}
   373  		}
   374  		return nil, fmt.Errorf("dependency %q is not available in operators", opName)
   375  	}
   376  
   377  	// Initialize the queue with all the elements that have zero incoming edges
   378  	var queue []string
   379  	for _, e := range operators {
   380  		if incomingEdges[e.Name()] == 0 {
   381  			queue = append(queue, e.Name())
   382  		}
   383  	}
   384  
   385  	// Initialize the result slice
   386  	var result Operators
   387  
   388  	// Initialize the visited set
   389  	visited := make(map[string]bool)
   390  
   391  	// Process the queue
   392  	for len(queue) > 0 {
   393  		// Pop an element from the queue
   394  		n := queue[0]
   395  		queue = queue[1:]
   396  
   397  		// Add the element to the visited set
   398  		visited[n] = true
   399  
   400  		// Prepend the element to the result slice
   401  		for _, s := range operators {
   402  			if s.Name() == n {
   403  				result = append(Operators{s}, result...)
   404  				break
   405  			}
   406  		}
   407  
   408  		// Decrement the incoming edge count for each of the element's dependencies
   409  		for _, d := range result[0].Dependencies() {
   410  			incomingEdges[d]--
   411  			// If a dependency's incoming edge count becomes zero, add it to the queue
   412  			if incomingEdges[d] == 0 {
   413  				queue = append(queue, d)
   414  			}
   415  			// If a dependency is already in the visited set, there is a cycle
   416  			if visited[d] {
   417  				return nil, fmt.Errorf("dependency cycle detected")
   418  			}
   419  		}
   420  	}
   421  
   422  	// Return an error if there are any unvisited elements, indicating that there is a cycle in the dependencies
   423  	for _, e := range operators {
   424  		if !visited[e.Name()] {
   425  			return nil, fmt.Errorf("dependency cycle detected")
   426  		}
   427  	}
   428  
   429  	return result, nil
   430  }