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 }