-
Notifications
You must be signed in to change notification settings - Fork 186
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement image-based gadgets examples #2753
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some questions I found while implementing the examples.
pkg/operators/localmanager/params.go
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you folks think about providing this structure for different operators?
/cc @flyth
examples/gadgets/fields/main.go
Outdated
fnameF := d.GetField("fname") | ||
|
||
d.Subscribe(func(source datasource.DataSource, data datasource.Data) error { | ||
pid := pidF.Uint32(data) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we should add a generic .Interface()
or .Any()
(although I find that more confusing than .Interface()
) to cast to whatever the underlying type is and return it as any
in case you don't care about the type (e.g. when printing with %v
).
9eb8702
to
ebca4d5
Compare
93c0222
to
ec97beb
Compare
cb05414
to
f253049
Compare
I updated the PR by reorganizing the examples and adding a few more. My proposal is to start the discussion on these examples, and create a wish-list of other examples that we can add later on. Any thoughts? |
f253049
to
36db15e
Compare
36db15e
to
e41b66d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome! 🚀
I started reviewing the documentation and the simple/trace_dns. I'll continue tomorrow with the rest. Some discussions are long, so maybe they can become a separate issue?
) | ||
|
||
func do() error { | ||
const opPriority = 50000 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How do people know the priority they need to set so that their operator is called after the other operators?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good question. What I did was to check the priority used by other operators.
Currently it's the operator that tells that's the priority for the Subscribe
, it requires operators to know the priorities of other operators (the CLI needs to be sure it has higher priority than the localmanager, formatter, etc). I'm really wondering if this is the right approach, perhaps we can move this responsibility to the caller? It could have a way to define the order of the operators and it should be sure they're called in the right order satisfying the dependencies.
I know @flyth put a lot of thinking into this. Do you have strong opinions for one or another approach?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As #2709 introduces the difference between Packet and Data (with it's own priority handling), I suggest we define only three priorities as constants for now:
PriorityDefault
as 0, PriorityHigh
as something like -50000 and PriorityLow
as 50000 (or -PriorityHigh). If a dev/user needs something in between, this allows a lot of room. But for normal cases the default priority should work fine. Same can be used for Packets, as they're always handled after the Data subscriptions.
e41b66d
to
0e8e0a3
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added some comments but LGTM already. Thanks for working on this.
|
||
Those will printed in the gadget's terminal: | ||
|
||
TODO: check why this gadget is lacking so much information compared to the built-in one. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After igjson.WithShowAll(true)
in the formatter initialization, I got most of the missing fields.
defer runtime.Close() | ||
|
||
params := map[string]string{ | ||
// columns, json, jsonpretty and yaml are supported |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO: Link to operator documentation
0e8e0a3
to
8f7009b
Compare
8f7009b
to
bf73d38
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi!
These examples are welcomed!
I am wondering nonetheless if we should not put them in the existing folder.
I will take a deeper look later, as I need to get familiar with the API first.
Best regards.
bf73d38
to
73b2e64
Compare
Where to you mean? |
In |
73b2e64
to
18aee98
Compare
Signed-off-by: Mauricio Vásquez <mauriciov@microsoft.com>
Show how to: - Create a simple operator to subscribe to events - Marshall events to json - Set parameters for the gadget Signed-off-by: Mauricio Vásquez <mauriciov@microsoft.com>
Signed-off-by: Mauricio Vásquez <mauriciov@microsoft.com>
Show how to use the localmanager to filter and enrich events with container data. Signed-off-by: Mauricio Vásquez <mauriciov@microsoft.com>
Show how to use the data source to get some specific fields from the event. Signed-off-by: Mauricio Vásquez <mauriciov@microsoft.com>
Signed-off-by: Mauricio Vásquez <mauriciov@microsoft.com>
Example showing how to add and mutate fields from a datasource. Signed-off-by: Mauricio Vásquez <mauriciov@microsoft.com>
Signed-off-by: Mauricio Vásquez <mauriciov@microsoft.com>
Signed-off-by: Mauricio Vásquez <mauriciov@microsoft.com>
18aee98
to
43bb599
Compare
// TODO: documentation | ||
// Our operator should be the last of the chain to print information after | ||
// all operators have been run. | ||
const opPriority = 50000 | ||
myOperator := simple.New("myHandler", simple.OnInit(func(gadgetCtx operators.GadgetContext) error { | ||
// Subscribe to all datasources and print their output as json the terminal | ||
// Check the datasources documentation for more information | ||
// TODO: link to documentation | ||
for _, d := range gadgetCtx.GetDataSources() { | ||
jsonFormatter, _ := igjson.New(d, | ||
// Show all fields | ||
igjson.WithShowAll(true), | ||
|
||
// Print json in a pretty format | ||
igjson.WithPretty(true, " "), | ||
) | ||
|
||
d.Subscribe(func(source datasource.DataSource, data datasource.Data) error { | ||
jsonOutput := jsonFormatter.Marshal(data) | ||
fmt.Printf("%s\n", jsonOutput) | ||
return nil | ||
}, opPriority) | ||
} | ||
return nil | ||
})) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that I'm working on the combiner operator, I noticed that by subscribing to a data source in the OnInit
phase there's a risk that a new operator registers a new data source afte us and we miss it. Of course, you can rely on the priority at the moment you add your operator, but what about new operator created later on by other people?
One solution is to never call GetDataSources
on the OnInit
phase as other data sources could be registered in operators with lower priority and instead, do it in the OnStart
phase where all operators are supposed to have already registered their data sources. But again, there's no guarantee.
We need to describe in the documentation to use high priority for operators registering datasource and maybe also tell that it's a good practice to use the OnInit
phase is for datasource registration and the OnStart
phase for subscribing to datasources. WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I drafted this earlier which contains some information on the lifecycle hooks.
Basically:
- OnInit() / Instantiate(): Add & manipulate DataSources - priority of operator handles initialization order; this is what GetGadgetInfo() returns
- PreStart() (optional): Subscribe to DataSources (to prevent missing some data) - priority determines handling order
- Start(): Emit data, Subscriptions will be evaluated
- Stop(): Stop everything
Regarding subscription priorities: I think filtering should be done before sorting on the server side. Combining should be done (on the client side only) before sorting but after filtering. So sort/filter would run on both client+server.
I hope this will work for 90% of the use cases 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I messed up the examples because there are two priorities:
- operator priority: defines the order on which operators are initialized, started and stopped.
- subscription priority: order in which subscriptions are called
Those two are independent, an operator with a low priority (is initialized last), could call subscribe with a high priority (its callback will be called first.
@flyth what's the design decision to have a priority for the operator and not to rely on the order used in WithDataOperators()?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what's the design decision to have a priority for the operator and not to rely on the order used in WithDataOperators()?
Explicitly adding operators using WithDataOperators()
was an option added later on; most (default) operators register themselves with the registry, so at least for that I think it makes sense to have the priority mentioned inside the operator (although we could make it overridable later on - e.g. the gadget.yaml could specify the order, or we could have a WithDataOperatorsOrdered()
that would honor the order in which operators are given, overriding their preset priorities).
This PR provides some examples of running image-based gadgets through the Golang API
Examples included: (readme added on this PR)
Gadgets Examples
This folder contains different examples of how gadgets can be run from a Golang
application.
TODO: link to API and concepts documentation before
Simple Gadgets
Examples showing how to use some gadgets in their simplest configuration.
trace_open
gadget and print theevents to the terminal in json format.
trace_dns
gadget with some of theoperators it requires.
Operators
Examples showing how to use some of the operators.
to filter and enrich events with container data.
a pretty way.
Datasource
Examples showing how to use
datasource
Executing Gadgets on remove Inspektor Gadget instances
Examples showing how to execute gadgets in remove instances of Inspektor Gadget
(either
ig
oriìg-k8s
) by using the grpc runtime.output to the terminal in json format by using a custom operator.
The idea of this PR is to discuss some of the pain points I found while implementing the examples and possible solutions to them. However I shouldn't be blocked on the solutions to all of them. I think the examples are valuable in their current status.
TODO
Future TODOs