diff --git a/pkg/gadgets/run/tracer/helpers.go b/pkg/gadgets/run/tracer/helpers.go index d977d41ad84..341a17525ae 100644 --- a/pkg/gadgets/run/tracer/helpers.go +++ b/pkg/gadgets/run/tracer/helpers.go @@ -1,4 +1,4 @@ -// Copyright 2023 The Inspektor Gadget authors +// Copyright 2023-2024 The Inspektor Gadget authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -22,8 +22,8 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/run/types" "github.com/inspektor-gadget/inspektor-gadget/pkg/k8sutil" + metadatav1 "github.com/inspektor-gadget/inspektor-gadget/pkg/metadata/v1" ) // getAnyMapElem returns any element of a map. If the map is empty, it returns nil, nil. @@ -34,7 +34,7 @@ func getAnyMapElem[K comparable, V any](m map[K]V) (*K, *V) { return nil, nil } -func getEventTypeBTF(progContent []byte, metadata *types.GadgetMetadata) (*btf.Struct, error) { +func getEventTypeBTF(progContent []byte, metadata *metadatav1.GadgetMetadata) (*btf.Struct, error) { spec, err := loadSpec(progContent) if err != nil { return nil, err diff --git a/pkg/gadgets/run/tracer/run.go b/pkg/gadgets/run/tracer/run.go index 738128f2e4a..af63be5fa25 100644 --- a/pkg/gadgets/run/tracer/run.go +++ b/pkg/gadgets/run/tracer/run.go @@ -37,6 +37,7 @@ import ( "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets" "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/run/types" "github.com/inspektor-gadget/inspektor-gadget/pkg/logger" + metadatav1 "github.com/inspektor-gadget/inspektor-gadget/pkg/metadata/v1" "github.com/inspektor-gadget/inspektor-gadget/pkg/oci" "github.com/inspektor-gadget/inspektor-gadget/pkg/params" "github.com/inspektor-gadget/inspektor-gadget/pkg/parser" @@ -127,7 +128,7 @@ func (g *GadgetDesc) Experimental() bool { // getGadgetType returns the type of the gadget according to the gadget being run. func getGadgetType(spec *ebpf.CollectionSpec, - gadgetMetadata *types.GadgetMetadata, + gadgetMetadata *metadatav1.GadgetMetadata, ) (gadgets.GadgetType, error) { switch { case len(gadgetMetadata.Structs) == 0: @@ -156,7 +157,7 @@ func getGadgetInfo(params *params.Params, args []string, secretBytes []byte, log ret := &types.GadgetInfo{ ProgContent: gadget.EbpfObject, - GadgetMetadata: &types.GadgetMetadata{}, + GadgetMetadata: &metadatav1.GadgetMetadata{}, } spec, err := loadSpec(ret.ProgContent) @@ -166,7 +167,7 @@ func getGadgetInfo(params *params.Params, args []string, secretBytes []byte, log if bytes.Equal(gadget.Metadata, ocispec.DescriptorEmptyJSON.Data) { // metadata is not present. synthesize something on the fly from the spec - if err := ret.GadgetMetadata.Populate(spec); err != nil { + if err := types.Populate(ret.GadgetMetadata, spec); err != nil { return nil, err } } else { @@ -176,7 +177,7 @@ func getGadgetInfo(params *params.Params, args []string, secretBytes []byte, log return nil, fmt.Errorf("unmarshaling metadata: %w", err) } - if err := ret.GadgetMetadata.Validate(spec); err != nil { + if err := types.Validate(ret.GadgetMetadata, spec); err != nil { if !validate { logger.Warnf("gadget metadata is not valid: %v", err) } else { @@ -258,7 +259,7 @@ func getTypeHint(typ btf.Type) params.TypeHint { // fillTypeHints fills the TypeHint field in the ebpf parameters according to the BTF information // about those constants. -func fillTypeHints(spec *ebpf.CollectionSpec, params map[string]types.EBPFParam) error { +func fillTypeHints(spec *ebpf.CollectionSpec, params map[string]metadatav1.EBPFParam) error { for varName, p := range params { var btfVar *btf.Var err := spec.Types.TypeByName(varName, &btfVar) @@ -414,7 +415,7 @@ func addL4EndpointColumns( }) } -func field2ColumnAttrs(field *types.Field) columns.Attributes { +func field2ColumnAttrs(field *metadatav1.Field) columns.Attributes { fieldAttrs := field.Attributes defaultOpts := columns.GetDefault() @@ -441,18 +442,18 @@ func field2ColumnAttrs(field *types.Field) columns.Attributes { } switch fieldAttrs.Alignment { - case types.AlignmentLeft: + case metadatav1.AlignmentLeft: attrs.Alignment = columns.AlignLeft - case types.AlignmentRight: + case metadatav1.AlignmentRight: attrs.Alignment = columns.AlignRight } switch fieldAttrs.Ellipsis { - case types.EllipsisStart: + case metadatav1.EllipsisStart: attrs.EllipsisType = ellipsis.Start - case types.EllipsisMiddle: + case metadatav1.EllipsisMiddle: attrs.EllipsisType = ellipsis.Middle - case types.EllipsisEnd: + case metadatav1.EllipsisEnd: attrs.EllipsisType = ellipsis.End } @@ -474,7 +475,7 @@ func (g *GadgetDesc) getColumns(info *types.GadgetInfo) (*columns.Columns[types. l4endpointCounter := 0 timestampsCounter := 0 - fields := map[string]types.Field{} + fields := map[string]metadatav1.Field{} for _, field := range eventStruct.Fields { fields[field.Name] = field } @@ -778,7 +779,7 @@ func simpleTypeFromBTF(typ btf.Type) *types.Type { func calculateColumnsForClient( eventFactory *types.EventFactory, - gadgetMetadata *types.GadgetMetadata, + gadgetMetadata *metadatav1.GadgetMetadata, progContent []byte, logger logger.Logger, ) ([]types.ColumnDesc, error) { diff --git a/pkg/gadgets/run/tracer/tracer.go b/pkg/gadgets/run/tracer/tracer.go index a6239c7cfdd..668c00c3155 100644 --- a/pkg/gadgets/run/tracer/tracer.go +++ b/pkg/gadgets/run/tracer/tracer.go @@ -43,6 +43,7 @@ import ( gadgetcontext "github.com/inspektor-gadget/inspektor-gadget/pkg/gadget-context" "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets" "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/run/types" + metadatav1 "github.com/inspektor-gadget/inspektor-gadget/pkg/metadata/v1" "github.com/inspektor-gadget/inspektor-gadget/pkg/netnsenter" "github.com/inspektor-gadget/inspektor-gadget/pkg/networktracer" "github.com/inspektor-gadget/inspektor-gadget/pkg/params" @@ -69,7 +70,7 @@ type l4EndpointT struct { type Config struct { ProgContent []byte - Metadata *types.GadgetMetadata + Metadata *metadatav1.GadgetMetadata MountnsMap *ebpf.Map // constants to replace in the ebpf program @@ -854,7 +855,7 @@ func (t *Tracer) runTracers(gadgetCtx gadgets.GadgetContext) { } } -func (t *Tracer) setEBPFParameters(ebpfParams map[string]types.EBPFParam, gadgetParams *params.Params) { +func (t *Tracer) setEBPFParameters(ebpfParams map[string]metadatav1.EBPFParam, gadgetParams *params.Params) { t.config.Consts = make(map[string]interface{}) for varName, paramDef := range ebpfParams { p := gadgetParams.Get(paramDef.Key) diff --git a/pkg/gadgets/run/types/metadata.go b/pkg/gadgets/run/types/metadata.go index 28823c3c123..691297e91a5 100644 --- a/pkg/gadgets/run/types/metadata.go +++ b/pkg/gadgets/run/types/metadata.go @@ -26,6 +26,7 @@ import ( "github.com/inspektor-gadget/inspektor-gadget/pkg/btfhelpers" "github.com/inspektor-gadget/inspektor-gadget/pkg/columns" + metadatav1 "github.com/inspektor-gadget/inspektor-gadget/pkg/metadata/v1" "github.com/inspektor-gadget/inspektor-gadget/pkg/params" ) @@ -73,125 +74,9 @@ const ( IfaceParam = "iface" ) -type EBPFParam struct { - params.ParamDesc `yaml:",inline"` -} - -const ( - DefaultColumnWidth = 16 -) - -type Alignment string - -const ( - AlignmenNone Alignment = "" - AlignmentLeft Alignment = "left" - AlignmentRight Alignment = "right" -) - -type EllipsisType string - -const ( - EllipsisNone EllipsisType = "" - EllipsisStart EllipsisType = "start" - EllipsisMiddle EllipsisType = "middle" - EllipsisEnd EllipsisType = "end" -) - -// FieldAttributes describes how to format a field. It's almost 1:1 mapping with columns.Attributes, -// however we are keeping this separated because we don't want to create a strong coupling with the -// columns library now. Later on we can consider merging both of them. -type FieldAttributes struct { - // Width to reserve for this field - Width uint `yaml:"width,omitempty"` - // MinWidth is the minimum width for this field - MinWidth uint `yaml:"minWidth,omitempty"` - // MaxWidth is the maximum width for this field - MaxWidth uint `yaml:"maxWidth,omitempty"` - // Alignment of this column (left or right) - Alignment Alignment `yaml:"alignment,omitempty"` - // Hidden defines whether a column is to be hid by default - Hidden bool `yaml:"hidden,omitempty"` - // EllipsisType defines how to abbreviate this column if the value needs more space than is - // available. (start, middle or end) - Ellipsis EllipsisType `yaml:"ellipsis,omitempty"` - // Template defines the template that will be used. - // TODO: add a link to existing templates - Template string `yaml:"template,omitempty"` -} - -type Field struct { - // Field name - Name string `yaml:"name"` - // Field description - Description string `yaml:"description,omitempty"` - // Attributes defines how the field should be formatted - Attributes FieldAttributes `yaml:"attributes"` - // Annotations represents extra information that is not relevant to Inspektor Gadget, but - // for other applications, like color font for instance. - Annotations map[string]interface{} `yaml:"annotations,omitempty"` -} - -// Struct describes a type generated by the gadget -type Struct struct { - Fields []Field `yaml:"fields"` -} - -// Tracer describes the behavior of a gadget that collects and sends events to user space -// TODO: We need to rename this concept not to collide with the opentelemetry concept -type Tracer struct { - // Name of the perf event array or ring buffer that the gadget uses to send events - MapName string `yaml:"mapName"` - // Name of the structure generated by this tracer - StructName string `yaml:"structName"` -} - -// Topper describes the behavior of a gadget that shows the current activity -// sorted by the highest to the lowest in the resource being observed. -type Topper struct { - // Name of the hash map that the gadget uses to send statistics - MapName string `yaml:"mapName"` - // Name of the structure generated by this topper - StructName string `yaml:"structName"` -} - -// Snapshotter describes the behavior of a gadget that collects the state of a subsystem -type Snapshotter struct { - StructName string `yaml:"structName"` -} - -type GadgetMetadata struct { - // Gadget name - Name string `yaml:"name"` - // Gadget description - Description string `yaml:"description,omitempty"` - // HomepageURL is the URL to the gadget's homepage - HomepageURL string `yaml:"homepageURL,omitempty"` - // DocumentationURL is the URL to the gadget's documentation - DocumentationURL string `yaml:"documentationURL,omitempty"` - // SourceURL is the URL to the gadget's source code repository - SourceURL string `yaml:"sourceURL,omitempty"` - // Annotations is a map of key-value pairs that provide additional information about the gadget - Annotations map[string]string `yaml:"annotations,omitempty"` - - // Tracers implemented by the gadget - // TODO: Rename this field to something that doesn't collide with the opentelemetry concept - Tracers map[string]Tracer `yaml:"tracers,omitempty"` - // Toppers implemented by the gadget - Toppers map[string]Topper `yaml:"toppers,omitempty"` - // Snapshotters implemented by the gadget - Snapshotters map[string]Snapshotter `yaml:"snapshotters,omitempty"` - // Types generated by the gadget - Structs map[string]Struct `yaml:"structs,omitempty"` - // Params exposed by the gadget through eBPF constants - EBPFParams map[string]EBPFParam `yaml:"ebpfParams,omitempty"` - // Other params exposed by the gadget - GadgetParams map[string]params.ParamDesc `yaml:"gadgetParams,omitempty"` -} - // countDistImp returns the number of distinct implementations of tracers, // snapshotters and toppers that the gadget has. -func (m *GadgetMetadata) countDistImp() int { +func countDistImp(m *metadatav1.GadgetMetadata) int { count := 0 if len(m.Tracers) > 0 { count++ @@ -205,7 +90,7 @@ func (m *GadgetMetadata) countDistImp() int { return count } -func (m *GadgetMetadata) Validate(spec *ebpf.CollectionSpec) error { +func Validate(m *metadatav1.GadgetMetadata, spec *ebpf.CollectionSpec) error { var result error if m.Name == "" { @@ -213,41 +98,41 @@ func (m *GadgetMetadata) Validate(spec *ebpf.CollectionSpec) error { } // Temporary limitation - if count := m.countDistImp(); count > 1 { + if count := countDistImp(m); count > 1 { result = multierror.Append( result, fmt.Errorf("gadget can implement only one tracer or snapshotter or topper, found %d", count), ) } - if err := m.validateEbpfParams(spec); err != nil { + if err := validateEbpfParams(m, spec); err != nil { result = multierror.Append(result, err) } - if err := m.validateTracers(spec); err != nil { + if err := validateTracers(m, spec); err != nil { result = multierror.Append(result, err) } - if err := m.validateToppers(spec); err != nil { + if err := validateToppers(m, spec); err != nil { result = multierror.Append(result, err) } - if err := m.validateSnapshotters(spec); err != nil { + if err := validateSnapshotters(m, spec); err != nil { result = multierror.Append(result, err) } - if err := m.validateStructs(spec); err != nil { + if err := validateStructs(m, spec); err != nil { result = multierror.Append(result, err) } - if err := m.validateGadgetParams(spec); err != nil { + if err := validateGadgetParams(m, spec); err != nil { result = multierror.Append(result, err) } return result } -func (m *GadgetMetadata) validateTracers(spec *ebpf.CollectionSpec) error { +func validateTracers(m *metadatav1.GadgetMetadata, spec *ebpf.CollectionSpec) error { var result error // Temporary limitation @@ -276,7 +161,7 @@ func validateTracerMap(tracerMap *ebpf.MapSpec, _ string) error { return nil } -func (m *GadgetMetadata) validateToppers(spec *ebpf.CollectionSpec) error { +func validateToppers(m *metadatav1.GadgetMetadata, spec *ebpf.CollectionSpec) error { var result error // Temporary limitation @@ -318,7 +203,7 @@ func validateTopperMap(topperMap *ebpf.MapSpec, expectedStructName string) error return nil } -func (m *GadgetMetadata) validateSnapshotters(spec *ebpf.CollectionSpec) error { +func validateSnapshotters(m *metadatav1.GadgetMetadata, spec *ebpf.CollectionSpec) error { var result error // Temporary limitation @@ -345,7 +230,7 @@ func (m *GadgetMetadata) validateSnapshotters(spec *ebpf.CollectionSpec) error { // validated with the rest of the structs. func validateMapAndStruct(mapName, structName string, spec *ebpf.CollectionSpec, - m *GadgetMetadata, + m *metadatav1.GadgetMetadata, validateMap func(*ebpf.MapSpec, string) error, ) (result error) { if mapName == "" { @@ -370,7 +255,7 @@ func validateMapAndStruct(mapName, structName string, return } -func (m *GadgetMetadata) validateStructs(spec *ebpf.CollectionSpec) error { +func validateStructs(m *metadatav1.GadgetMetadata, spec *ebpf.CollectionSpec) error { var result error for name, mapStruct := range m.Structs { @@ -380,7 +265,7 @@ func (m *GadgetMetadata) validateStructs(spec *ebpf.CollectionSpec) error { continue } - mapStructFields := make(map[string]Field, len(mapStruct.Fields)) + mapStructFields := make(map[string]metadatav1.Field, len(mapStruct.Fields)) for _, f := range mapStruct.Fields { mapStructFields[f.Name] = f } @@ -400,7 +285,7 @@ func (m *GadgetMetadata) validateStructs(spec *ebpf.CollectionSpec) error { return result } -func (m *GadgetMetadata) validateEbpfParams(spec *ebpf.CollectionSpec) error { +func validateEbpfParams(m *metadatav1.GadgetMetadata, spec *ebpf.CollectionSpec) error { var result error for varName := range m.EBPFParams { if err := checkParamVar(spec, varName); err != nil { @@ -413,7 +298,7 @@ func (m *GadgetMetadata) validateEbpfParams(spec *ebpf.CollectionSpec) error { return result } -func (m *GadgetMetadata) validateGadgetParams(spec *ebpf.CollectionSpec) error { +func validateGadgetParams(m *metadatav1.GadgetMetadata, spec *ebpf.CollectionSpec) error { var result error for _, p := range spec.Programs { switch p.Type { @@ -432,7 +317,7 @@ func (m *GadgetMetadata) validateGadgetParams(spec *ebpf.CollectionSpec) error { } // Populate fills the metadata from its ebpf spec -func (m *GadgetMetadata) Populate(spec *ebpf.CollectionSpec) error { +func Populate(m *metadatav1.GadgetMetadata, spec *ebpf.CollectionSpec) error { if m.Name == "" { m.Name = "TODO: Fill the gadget name" } @@ -453,23 +338,23 @@ func (m *GadgetMetadata) Populate(spec *ebpf.CollectionSpec) error { m.SourceURL = "TODO: Fill the gadget source code URL" } - if err := m.populateTracers(spec); err != nil { + if err := populateTracers(m, spec); err != nil { return fmt.Errorf("handling trace maps: %w", err) } - if err := m.populateToppers(spec); err != nil { - return fmt.Errorf("handling toppers: %w", err) + if err := populateToppers(m, spec); err != nil { + return fmt.Errorf("handling snapshotters: %w", err) } - if err := m.populateSnapshotters(spec); err != nil { + if err := populateSnapshotters(m, spec); err != nil { return fmt.Errorf("handling snapshotters: %w", err) } - if err := m.populateEbpfParams(spec); err != nil { + if err := populateEbpfParams(m, spec); err != nil { return fmt.Errorf("handling params: %w", err) } - if err := m.populateGadgetParams(spec); err != nil { + if err := populateGadgetParams(m, spec); err != nil { return fmt.Errorf("handling gadget params: %w", err) } @@ -513,10 +398,10 @@ func getColumnSize(typ btf.Type) uint { return getColumnSize(typ) } - return DefaultColumnWidth + return metadatav1.DefaultColumnWidth } -func (m *GadgetMetadata) populateTracers(spec *ebpf.CollectionSpec) error { +func populateTracers(m *metadatav1.GadgetMetadata, spec *ebpf.CollectionSpec) error { tracerInfo, err := getTracerInfo(spec) if err != nil { return err @@ -527,7 +412,7 @@ func (m *GadgetMetadata) populateTracers(spec *ebpf.CollectionSpec) error { } if m.Tracers == nil { - m.Tracers = make(map[string]Tracer) + m.Tracers = make(map[string]metadatav1.Tracer) } tracerMap := spec.Maps[tracerInfo.mapName] @@ -548,7 +433,7 @@ func (m *GadgetMetadata) populateTracers(spec *ebpf.CollectionSpec) error { log.Debugf("Adding tracer %q with map %q and struct %q", tracerInfo.name, tracerMap.Name, tracerMapStruct.Name) - m.Tracers[tracerInfo.name] = Tracer{ + m.Tracers[tracerInfo.name] = metadatav1.Tracer{ MapName: tracerMap.Name, StructName: tracerMapStruct.Name, } @@ -556,14 +441,14 @@ func (m *GadgetMetadata) populateTracers(spec *ebpf.CollectionSpec) error { log.Debugf("Tracer %q already defined, skipping", tracerInfo.name) } - if err := m.populateStruct(tracerMapStruct); err != nil { + if err := populateStruct(m, tracerMapStruct); err != nil { return fmt.Errorf("populating struct: %w", err) } return nil } -func (m *GadgetMetadata) populateToppers(spec *ebpf.CollectionSpec) error { +func populateToppers(m *metadatav1.GadgetMetadata, spec *ebpf.CollectionSpec) error { topperInfo, err := getTopperInfo(spec) if err != nil { return err @@ -574,7 +459,7 @@ func (m *GadgetMetadata) populateToppers(spec *ebpf.CollectionSpec) error { } if m.Toppers == nil { - m.Toppers = make(map[string]Topper) + m.Toppers = make(map[string]metadatav1.Topper) } topperMap := spec.Maps[topperInfo.mapName] @@ -596,7 +481,7 @@ func (m *GadgetMetadata) populateToppers(spec *ebpf.CollectionSpec) error { log.Debugf("Adding topper %q with map %q and struct %q", topperInfo.name, topperMap.Name, topperMapStruct.Name) - m.Toppers[topperInfo.name] = Topper{ + m.Toppers[topperInfo.name] = metadatav1.Topper{ MapName: topperMap.Name, StructName: topperMapStruct.Name, } @@ -604,7 +489,7 @@ func (m *GadgetMetadata) populateToppers(spec *ebpf.CollectionSpec) error { log.Debugf("Topper %q already defined, skipping", topperInfo.name) } - if err := m.populateStruct(topperMapStruct); err != nil { + if err := populateStruct(m, topperMapStruct); err != nil { return fmt.Errorf("populating struct: %w", err) } @@ -714,9 +599,9 @@ func getTopperInfo(spec *ebpf.CollectionSpec) (*topperInfo, error) { }, nil } -func (m *GadgetMetadata) populateStruct(btfStruct *btf.Struct) error { +func populateStruct(m *metadatav1.GadgetMetadata, btfStruct *btf.Struct) error { if m.Structs == nil { - m.Structs = make(map[string]Struct) + m.Structs = make(map[string]metadatav1.Struct) } gadgetStruct := m.Structs[btfStruct.Name] @@ -733,13 +618,13 @@ func (m *GadgetMetadata) populateStruct(btfStruct *btf.Struct) error { } log.Debugf("Adding field %q", member.Name) - field := Field{ + field := metadatav1.Field{ Name: member.Name, Description: "TODO: Fill field description", - Attributes: FieldAttributes{ + Attributes: metadatav1.FieldAttributes{ Width: getColumnSize(member.Type), - Alignment: AlignmentLeft, - Ellipsis: EllipsisEnd, + Alignment: metadatav1.AlignmentLeft, + Ellipsis: metadatav1.EllipsisEnd, }, } @@ -751,7 +636,7 @@ func (m *GadgetMetadata) populateStruct(btfStruct *btf.Struct) error { return nil } -func (m *GadgetMetadata) populateEbpfParams(spec *ebpf.CollectionSpec) error { +func populateEbpfParams(m *metadatav1.GadgetMetadata, spec *ebpf.CollectionSpec) error { var result error paramNames, err := GetGadgetIdentByPrefix(spec, paramPrefix) @@ -774,7 +659,7 @@ func (m *GadgetMetadata) populateEbpfParams(spec *ebpf.CollectionSpec) error { } if m.EBPFParams == nil { - m.EBPFParams = make(map[string]EBPFParam) + m.EBPFParams = make(map[string]metadatav1.EBPFParam) } if _, found := m.EBPFParams[name]; found { @@ -783,7 +668,7 @@ func (m *GadgetMetadata) populateEbpfParams(spec *ebpf.CollectionSpec) error { } log.Debugf("Adding param %q", name) - m.EBPFParams[name] = EBPFParam{ + m.EBPFParams[name] = metadatav1.EBPFParam{ ParamDesc: params.ParamDesc{ Key: name, Description: "TODO: Fill parameter description", @@ -794,7 +679,7 @@ func (m *GadgetMetadata) populateEbpfParams(spec *ebpf.CollectionSpec) error { return result } -func (m *GadgetMetadata) populateGadgetParams(spec *ebpf.CollectionSpec) error { +func populateGadgetParams(m *metadatav1.GadgetMetadata, spec *ebpf.CollectionSpec) error { for _, p := range spec.Programs { switch p.Type { // Networking programs provide an interface name to attach to @@ -839,7 +724,7 @@ func checkParamVar(spec *ebpf.CollectionSpec, name string) error { return result } -func (m *GadgetMetadata) populateSnapshotters(spec *ebpf.CollectionSpec) error { +func populateSnapshotters(m *metadatav1.GadgetMetadata, spec *ebpf.CollectionSpec) error { snapshottersNameAndType, _ := GetGadgetIdentByPrefix(spec, snapshottersPrefix) if len(snapshottersNameAndType) == 0 { log.Debug("No snapshotters found") @@ -853,7 +738,7 @@ func (m *GadgetMetadata) populateSnapshotters(spec *ebpf.CollectionSpec) error { snapshotterNameAndType := snapshottersNameAndType[0] if m.Snapshotters == nil { - m.Snapshotters = make(map[string]Snapshotter) + m.Snapshotters = make(map[string]metadatav1.Snapshotter) } parts := strings.Split(snapshotterNameAndType, "___") @@ -873,14 +758,14 @@ func (m *GadgetMetadata) populateSnapshotters(spec *ebpf.CollectionSpec) error { _, ok := m.Snapshotters[sname] if !ok { log.Debugf("Adding snapshotter %q", sname) - m.Snapshotters[sname] = Snapshotter{ + m.Snapshotters[sname] = metadatav1.Snapshotter{ StructName: btfStruct.Name, } } else { log.Debugf("Snapshotter %q already defined, skipping", sname) } - if err := m.populateStruct(btfStruct); err != nil { + if err := populateStruct(m, btfStruct); err != nil { return fmt.Errorf("populating struct: %w", err) } diff --git a/pkg/gadgets/run/types/metadata_test.go b/pkg/gadgets/run/types/metadata_test.go index a15d423e2ed..9afc60dcece 100644 --- a/pkg/gadgets/run/types/metadata_test.go +++ b/pkg/gadgets/run/types/metadata_test.go @@ -1,4 +1,4 @@ -// Copyright 2023 The Inspektor Gadget authors +// Copyright 2023-2024 The Inspektor Gadget authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -20,30 +20,31 @@ import ( "github.com/cilium/ebpf" "github.com/stretchr/testify/require" + metadatav1 "github.com/inspektor-gadget/inspektor-gadget/pkg/metadata/v1" "github.com/inspektor-gadget/inspektor-gadget/pkg/params" ) func TestValidate(t *testing.T) { type testCase struct { objectPath string - metadata *GadgetMetadata + metadata *metadatav1.GadgetMetadata expectedErrString string } tests := map[string]testCase{ "missing_name": { objectPath: "../../../../testdata/validate_metadata1.o", - metadata: &GadgetMetadata{}, + metadata: &metadatav1.GadgetMetadata{}, expectedErrString: "gadget name is required", }, "multiple_types": { objectPath: "../../../../testdata/validate_metadata1.o", - metadata: &GadgetMetadata{ + metadata: &metadatav1.GadgetMetadata{ Name: "foo", - Tracers: map[string]Tracer{ + Tracers: map[string]metadatav1.Tracer{ "foo": {}, }, - Snapshotters: map[string]Snapshotter{ + Snapshotters: map[string]metadatav1.Snapshotter{ "bar": {}, }, }, @@ -51,9 +52,9 @@ func TestValidate(t *testing.T) { }, "tracers_more_than_one": { objectPath: "../../../../testdata/validate_metadata1.o", - metadata: &GadgetMetadata{ + metadata: &metadatav1.GadgetMetadata{ Name: "foo", - Tracers: map[string]Tracer{ + Tracers: map[string]metadatav1.Tracer{ "foo": {}, "bar": {}, }, @@ -62,9 +63,9 @@ func TestValidate(t *testing.T) { }, "tracers_missing_map_name": { objectPath: "../../../../testdata/validate_metadata1.o", - metadata: &GadgetMetadata{ + metadata: &metadatav1.GadgetMetadata{ Name: "foo", - Tracers: map[string]Tracer{ + Tracers: map[string]metadatav1.Tracer{ "foo": { StructName: "event", }, @@ -74,9 +75,9 @@ func TestValidate(t *testing.T) { }, "tracers_missing_struct_name": { objectPath: "../../../../testdata/validate_metadata1.o", - metadata: &GadgetMetadata{ + metadata: &metadatav1.GadgetMetadata{ Name: "foo", - Tracers: map[string]Tracer{ + Tracers: map[string]metadatav1.Tracer{ "foo": { MapName: "events", }, @@ -86,9 +87,9 @@ func TestValidate(t *testing.T) { }, "tracers_references_unknown_struct": { objectPath: "../../../../testdata/validate_metadata1.o", - metadata: &GadgetMetadata{ + metadata: &metadatav1.GadgetMetadata{ Name: "foo", - Tracers: map[string]Tracer{ + Tracers: map[string]metadatav1.Tracer{ "foo": { MapName: "events", StructName: "nonexistent", @@ -99,15 +100,15 @@ func TestValidate(t *testing.T) { }, "tracers_map_not_found": { objectPath: "../../../../testdata/validate_metadata1.o", - metadata: &GadgetMetadata{ + metadata: &metadatav1.GadgetMetadata{ Name: "foo", - Tracers: map[string]Tracer{ + Tracers: map[string]metadatav1.Tracer{ "foo": { MapName: "nonexistent", StructName: "event", }, }, - Structs: map[string]Struct{ + Structs: map[string]metadatav1.Struct{ "event": {}, }, }, @@ -115,15 +116,15 @@ func TestValidate(t *testing.T) { }, "tracers_bad_map_type": { objectPath: "../../../../testdata/validate_metadata1.o", - metadata: &GadgetMetadata{ + metadata: &metadatav1.GadgetMetadata{ Name: "foo", - Tracers: map[string]Tracer{ + Tracers: map[string]metadatav1.Tracer{ "foo": { MapName: "myhashmap", StructName: "event", }, }, - Structs: map[string]Struct{ + Structs: map[string]metadatav1.Struct{ "event": {}, }, }, @@ -131,39 +132,39 @@ func TestValidate(t *testing.T) { }, "tracers_map_without_btf": { objectPath: "../../../../testdata/validate_metadata1.o", - metadata: &GadgetMetadata{ + metadata: &metadatav1.GadgetMetadata{ Name: "foo", - Tracers: map[string]Tracer{ + Tracers: map[string]metadatav1.Tracer{ "foo": { MapName: "map_without_btf", StructName: "event", }, }, - Structs: map[string]Struct{ + Structs: map[string]metadatav1.Struct{ "event": {}, }, }, }, "tracers_good": { objectPath: "../../../../testdata/validate_metadata1.o", - metadata: &GadgetMetadata{ + metadata: &metadatav1.GadgetMetadata{ Name: "foo", - Tracers: map[string]Tracer{ + Tracers: map[string]metadatav1.Tracer{ "foo": { MapName: "events", StructName: "event", }, }, - Structs: map[string]Struct{ + Structs: map[string]metadatav1.Struct{ "event": {}, }, }, }, "toppers_more_than_one": { objectPath: "../../../../testdata/validate_metadata_topper.o", - metadata: &GadgetMetadata{ + metadata: &metadatav1.GadgetMetadata{ Name: "foo", - Toppers: map[string]Topper{ + Toppers: map[string]metadatav1.Topper{ "foo": {}, "bar": {}, }, @@ -172,15 +173,15 @@ func TestValidate(t *testing.T) { }, "toppers_bad_map_type": { objectPath: "../../../../testdata/validate_metadata_topper.o", - metadata: &GadgetMetadata{ + metadata: &metadatav1.GadgetMetadata{ Name: "foo", - Toppers: map[string]Topper{ + Toppers: map[string]metadatav1.Topper{ "foo": { MapName: "events", StructName: "event", }, }, - Structs: map[string]Struct{ + Structs: map[string]metadatav1.Struct{ "event": {}, }, }, @@ -188,15 +189,15 @@ func TestValidate(t *testing.T) { }, "toppers_bad_structure_name": { objectPath: "../../../../testdata/validate_metadata_topper.o", - metadata: &GadgetMetadata{ + metadata: &metadatav1.GadgetMetadata{ Name: "foo", - Toppers: map[string]Topper{ + Toppers: map[string]metadatav1.Topper{ "foo": { MapName: "myhashmap", StructName: "event2", }, }, - Structs: map[string]Struct{ + Structs: map[string]metadatav1.Struct{ "event2": {}, }, }, @@ -204,15 +205,15 @@ func TestValidate(t *testing.T) { }, "toppers_wrong_value_type": { objectPath: "../../../../testdata/validate_metadata_topper.o", - metadata: &GadgetMetadata{ + metadata: &metadatav1.GadgetMetadata{ Name: "foo", - Toppers: map[string]Topper{ + Toppers: map[string]metadatav1.Topper{ "foo": { MapName: "hash_wrong_value_map", StructName: "event", }, }, - Structs: map[string]Struct{ + Structs: map[string]metadatav1.Struct{ "event": {}, }, }, @@ -220,15 +221,15 @@ func TestValidate(t *testing.T) { }, "toppers_without_btf": { objectPath: "../../../../testdata/validate_metadata_topper.o", - metadata: &GadgetMetadata{ + metadata: &metadatav1.GadgetMetadata{ Name: "foo", - Toppers: map[string]Topper{ + Toppers: map[string]metadatav1.Topper{ "foo": { MapName: "hash_without_btf", StructName: "event", }, }, - Structs: map[string]Struct{ + Structs: map[string]metadatav1.Struct{ "event": {}, }, }, @@ -236,24 +237,24 @@ func TestValidate(t *testing.T) { }, "toppers_good": { objectPath: "../../../../testdata/validate_metadata_topper.o", - metadata: &GadgetMetadata{ + metadata: &metadatav1.GadgetMetadata{ Name: "foo", - Toppers: map[string]Topper{ + Toppers: map[string]metadatav1.Topper{ "foo": { MapName: "myhashmap", StructName: "event", }, }, - Structs: map[string]Struct{ + Structs: map[string]metadatav1.Struct{ "event": {}, }, }, }, "structs_nonexistent": { objectPath: "../../../../testdata/validate_metadata1.o", - metadata: &GadgetMetadata{ + metadata: &metadatav1.GadgetMetadata{ Name: "foo", - Structs: map[string]Struct{ + Structs: map[string]metadatav1.Struct{ "nonexistent": {}, }, }, @@ -261,11 +262,11 @@ func TestValidate(t *testing.T) { }, "structs_field_nonexistent": { objectPath: "../../../../testdata/validate_metadata1.o", - metadata: &GadgetMetadata{ + metadata: &metadatav1.GadgetMetadata{ Name: "foo", - Structs: map[string]Struct{ + Structs: map[string]metadatav1.Struct{ "event": { - Fields: []Field{ + Fields: []metadatav1.Field{ { Name: "nonexistent", }, @@ -277,11 +278,11 @@ func TestValidate(t *testing.T) { }, "structs_good": { objectPath: "../../../../testdata/validate_metadata1.o", - metadata: &GadgetMetadata{ + metadata: &metadatav1.GadgetMetadata{ Name: "foo", - Structs: map[string]Struct{ + Structs: map[string]metadatav1.Struct{ "event": { - Fields: []Field{ + Fields: []metadatav1.Field{ { Name: "pid", }, @@ -292,9 +293,9 @@ func TestValidate(t *testing.T) { }, "param_nonexistent": { objectPath: "../../../../testdata/validate_metadata1.o", - metadata: &GadgetMetadata{ + metadata: &metadatav1.GadgetMetadata{ Name: "foo", - EBPFParams: map[string]EBPFParam{ + EBPFParams: map[string]metadatav1.EBPFParam{ "bar": {}, }, }, @@ -302,9 +303,9 @@ func TestValidate(t *testing.T) { }, "param_nokey": { objectPath: "../../../../testdata/validate_metadata1.o", - metadata: &GadgetMetadata{ + metadata: &metadatav1.GadgetMetadata{ Name: "foo", - EBPFParams: map[string]EBPFParam{ + EBPFParams: map[string]metadatav1.EBPFParam{ "bar": {}, }, }, @@ -312,9 +313,9 @@ func TestValidate(t *testing.T) { }, "param_good": { objectPath: "../../../../testdata/validate_metadata1.o", - metadata: &GadgetMetadata{ + metadata: &metadatav1.GadgetMetadata{ Name: "foo", - EBPFParams: map[string]EBPFParam{ + EBPFParams: map[string]metadatav1.EBPFParam{ "param": { ParamDesc: params.ParamDesc{ Key: "param", @@ -325,9 +326,9 @@ func TestValidate(t *testing.T) { }, "param2_not_volatile": { objectPath: "../../../../testdata/validate_metadata1.o", - metadata: &GadgetMetadata{ + metadata: &metadatav1.GadgetMetadata{ Name: "foo", - EBPFParams: map[string]EBPFParam{ + EBPFParams: map[string]metadatav1.EBPFParam{ "param2": {}, }, }, @@ -335,9 +336,9 @@ func TestValidate(t *testing.T) { }, "param3_not_const": { objectPath: "../../../../testdata/validate_metadata1.o", - metadata: &GadgetMetadata{ + metadata: &metadatav1.GadgetMetadata{ Name: "foo", - EBPFParams: map[string]EBPFParam{ + EBPFParams: map[string]metadatav1.EBPFParam{ "param3": {}, }, }, @@ -345,9 +346,9 @@ func TestValidate(t *testing.T) { }, "snapshotters_more_than_one": { objectPath: "../../../../testdata/validate_metadata1.o", - metadata: &GadgetMetadata{ + metadata: &metadatav1.GadgetMetadata{ Name: "foo", - Snapshotters: map[string]Snapshotter{ + Snapshotters: map[string]metadatav1.Snapshotter{ "foo": {}, "bar": {}, }, @@ -356,9 +357,9 @@ func TestValidate(t *testing.T) { }, "snapshotters_missing_struct_name": { objectPath: "../../../../testdata/validate_metadata1.o", - metadata: &GadgetMetadata{ + metadata: &metadatav1.GadgetMetadata{ Name: "foo", - Snapshotters: map[string]Snapshotter{ + Snapshotters: map[string]metadatav1.Snapshotter{ "foo": {}, }, }, @@ -366,21 +367,21 @@ func TestValidate(t *testing.T) { }, "snapshotters_good": { objectPath: "../../../../testdata/validate_metadata1.o", - metadata: &GadgetMetadata{ + metadata: &metadatav1.GadgetMetadata{ Name: "foo", - Snapshotters: map[string]Snapshotter{ + Snapshotters: map[string]metadatav1.Snapshotter{ "foo": { StructName: "event", }, }, - Structs: map[string]Struct{ + Structs: map[string]metadatav1.Struct{ "event": {}, }, }, }, "sched_cls": { objectPath: "../../../../testdata/validate_metadata_sched_cls.o", - metadata: &GadgetMetadata{ + metadata: &metadatav1.GadgetMetadata{ Name: "foo", GadgetParams: map[string]params.ParamDesc{ "iface": { @@ -399,7 +400,7 @@ func TestValidate(t *testing.T) { spec, err := ebpf.LoadCollectionSpec(test.objectPath) require.NoError(t, err) - err = test.metadata.Validate(spec) + err = Validate(test.metadata, spec) if test.expectedErrString == "" { require.NoError(t, err) } else { @@ -410,46 +411,46 @@ func TestValidate(t *testing.T) { } func TestPopulate(t *testing.T) { - expectedTopperMetadataFromScratch := &GadgetMetadata{ + expectedTopperMetadataFromScratch := &metadatav1.GadgetMetadata{ Name: "TODO: Fill the gadget name", Description: "TODO: Fill the gadget description", HomepageURL: "TODO: Fill the gadget homepage URL", DocumentationURL: "TODO: Fill the gadget documentation URL", SourceURL: "TODO: Fill the gadget source code URL", - Toppers: map[string]Topper{ + Toppers: map[string]metadatav1.Topper{ "my_topper": { MapName: "events", StructName: "event", }, }, - Structs: map[string]Struct{ + Structs: map[string]metadatav1.Struct{ "event": { - Fields: []Field{ + Fields: []metadatav1.Field{ { Name: "pid", Description: "TODO: Fill field description", - Attributes: FieldAttributes{ + Attributes: metadatav1.FieldAttributes{ Width: 10, - Alignment: AlignmentLeft, - Ellipsis: EllipsisEnd, + Alignment: metadatav1.AlignmentLeft, + Ellipsis: metadatav1.EllipsisEnd, }, }, { Name: "comm", Description: "TODO: Fill field description", - Attributes: FieldAttributes{ + Attributes: metadatav1.FieldAttributes{ Width: 16, - Alignment: AlignmentLeft, - Ellipsis: EllipsisEnd, + Alignment: metadatav1.AlignmentLeft, + Ellipsis: metadatav1.EllipsisEnd, }, }, { Name: "filename", Description: "TODO: Fill field description", - Attributes: FieldAttributes{ + Attributes: metadatav1.FieldAttributes{ Width: 16, - Alignment: AlignmentLeft, - Ellipsis: EllipsisEnd, + Alignment: metadatav1.AlignmentLeft, + Ellipsis: metadatav1.EllipsisEnd, }, }, }, @@ -458,8 +459,8 @@ func TestPopulate(t *testing.T) { } type testCase struct { - initialMetadata *GadgetMetadata - expectedMetadata *GadgetMetadata + initialMetadata *metadatav1.GadgetMetadata + expectedMetadata *metadatav1.GadgetMetadata objectPath string expectedErrString string } @@ -467,46 +468,46 @@ func TestPopulate(t *testing.T) { tests := map[string]testCase{ "1_tracer_1_struct_from_scratch": { objectPath: "../../../../testdata/populate_metadata_1_tracer_1_struct_from_scratch.o", - expectedMetadata: &GadgetMetadata{ + expectedMetadata: &metadatav1.GadgetMetadata{ Name: "TODO: Fill the gadget name", Description: "TODO: Fill the gadget description", HomepageURL: "TODO: Fill the gadget homepage URL", DocumentationURL: "TODO: Fill the gadget documentation URL", SourceURL: "TODO: Fill the gadget source code URL", - Tracers: map[string]Tracer{ + Tracers: map[string]metadatav1.Tracer{ "test": { MapName: "events", StructName: "event", }, }, - Structs: map[string]Struct{ + Structs: map[string]metadatav1.Struct{ "event": { - Fields: []Field{ + Fields: []metadatav1.Field{ { Name: "pid", Description: "TODO: Fill field description", - Attributes: FieldAttributes{ + Attributes: metadatav1.FieldAttributes{ Width: 10, - Alignment: AlignmentLeft, - Ellipsis: EllipsisEnd, + Alignment: metadatav1.AlignmentLeft, + Ellipsis: metadatav1.EllipsisEnd, }, }, { Name: "comm", Description: "TODO: Fill field description", - Attributes: FieldAttributes{ + Attributes: metadatav1.FieldAttributes{ Width: 16, - Alignment: AlignmentLeft, - Ellipsis: EllipsisEnd, + Alignment: metadatav1.AlignmentLeft, + Ellipsis: metadatav1.EllipsisEnd, }, }, { Name: "filename", Description: "TODO: Fill field description", - Attributes: FieldAttributes{ + Attributes: metadatav1.FieldAttributes{ Width: 16, - Alignment: AlignmentLeft, - Ellipsis: EllipsisEnd, + Alignment: metadatav1.AlignmentLeft, + Ellipsis: metadatav1.EllipsisEnd, }, }, }, @@ -516,7 +517,7 @@ func TestPopulate(t *testing.T) { }, "tracer_add_missing_field": { objectPath: "../../../../testdata/populate_metadata_tracer_add_missing_field.o", - initialMetadata: &GadgetMetadata{ + initialMetadata: &metadatav1.GadgetMetadata{ Name: "foo", Description: "bar", HomepageURL: "url1", @@ -525,32 +526,32 @@ func TestPopulate(t *testing.T) { Annotations: map[string]string{ "io.inspektor-gadget.test": "test", }, - Tracers: map[string]Tracer{ + Tracers: map[string]metadatav1.Tracer{ "test": { MapName: "events", StructName: "event", }, }, - Structs: map[string]Struct{ + Structs: map[string]metadatav1.Struct{ "event": { // Set desc and some attributes to be sure they aren't overwritten - Fields: []Field{ + Fields: []metadatav1.Field{ { Name: "pid", Description: "foo-pid", - Attributes: FieldAttributes{ + Attributes: metadatav1.FieldAttributes{ Width: 4747, - Alignment: AlignmentRight, - Ellipsis: EllipsisStart, + Alignment: metadatav1.AlignmentRight, + Ellipsis: metadatav1.EllipsisStart, }, }, { Name: "comm", Description: "bar-comm", - Attributes: FieldAttributes{ + Attributes: metadatav1.FieldAttributes{ Width: 1313, - Alignment: AlignmentRight, - Ellipsis: EllipsisStart, + Alignment: metadatav1.AlignmentRight, + Ellipsis: metadatav1.EllipsisStart, }, }, // missing filename field on purpose to check if it's added @@ -558,7 +559,7 @@ func TestPopulate(t *testing.T) { }, }, }, - expectedMetadata: &GadgetMetadata{ + expectedMetadata: &metadatav1.GadgetMetadata{ Name: "foo", Description: "bar", HomepageURL: "url1", @@ -567,40 +568,40 @@ func TestPopulate(t *testing.T) { Annotations: map[string]string{ "io.inspektor-gadget.test": "test", }, - Tracers: map[string]Tracer{ + Tracers: map[string]metadatav1.Tracer{ "test": { MapName: "events", StructName: "event", }, }, - Structs: map[string]Struct{ + Structs: map[string]metadatav1.Struct{ "event": { - Fields: []Field{ + Fields: []metadatav1.Field{ { Name: "pid", Description: "foo-pid", - Attributes: FieldAttributes{ + Attributes: metadatav1.FieldAttributes{ Width: 4747, - Alignment: AlignmentRight, - Ellipsis: EllipsisStart, + Alignment: metadatav1.AlignmentRight, + Ellipsis: metadatav1.EllipsisStart, }, }, { Name: "comm", Description: "bar-comm", - Attributes: FieldAttributes{ + Attributes: metadatav1.FieldAttributes{ Width: 1313, - Alignment: AlignmentRight, - Ellipsis: EllipsisStart, + Alignment: metadatav1.AlignmentRight, + Ellipsis: metadatav1.EllipsisStart, }, }, { Name: "filename", Description: "TODO: Fill field description", - Attributes: FieldAttributes{ + Attributes: metadatav1.FieldAttributes{ Width: 16, - Alignment: AlignmentLeft, - Ellipsis: EllipsisEnd, + Alignment: metadatav1.AlignmentLeft, + Ellipsis: metadatav1.EllipsisEnd, }, }, }, @@ -610,7 +611,7 @@ func TestPopulate(t *testing.T) { }, "no_tracers_from_scratch": { objectPath: "../../../../testdata/populate_metadata_no_tracers_from_scratch.o", - expectedMetadata: &GadgetMetadata{ + expectedMetadata: &metadatav1.GadgetMetadata{ Name: "TODO: Fill the gadget name", Description: "TODO: Fill the gadget description", HomepageURL: "TODO: Fill the gadget homepage URL", @@ -628,46 +629,46 @@ func TestPopulate(t *testing.T) { }, "tracer_map_without_btf": { objectPath: "../../../../testdata/populate_metadata_tracer_map_without_btf.o", - expectedMetadata: &GadgetMetadata{ + expectedMetadata: &metadatav1.GadgetMetadata{ Name: "TODO: Fill the gadget name", Description: "TODO: Fill the gadget description", HomepageURL: "TODO: Fill the gadget homepage URL", DocumentationURL: "TODO: Fill the gadget documentation URL", SourceURL: "TODO: Fill the gadget source code URL", - Tracers: map[string]Tracer{ + Tracers: map[string]metadatav1.Tracer{ "test": { MapName: "events", StructName: "event", }, }, - Structs: map[string]Struct{ + Structs: map[string]metadatav1.Struct{ "event": { - Fields: []Field{ + Fields: []metadatav1.Field{ { Name: "pid", Description: "TODO: Fill field description", - Attributes: FieldAttributes{ + Attributes: metadatav1.FieldAttributes{ Width: 10, - Alignment: AlignmentLeft, - Ellipsis: EllipsisEnd, + Alignment: metadatav1.AlignmentLeft, + Ellipsis: metadatav1.EllipsisEnd, }, }, { Name: "comm", Description: "TODO: Fill field description", - Attributes: FieldAttributes{ + Attributes: metadatav1.FieldAttributes{ Width: 16, - Alignment: AlignmentLeft, - Ellipsis: EllipsisEnd, + Alignment: metadatav1.AlignmentLeft, + Ellipsis: metadatav1.EllipsisEnd, }, }, { Name: "filename", Description: "TODO: Fill field description", - Attributes: FieldAttributes{ + Attributes: metadatav1.FieldAttributes{ Width: 16, - Alignment: AlignmentLeft, - Ellipsis: EllipsisEnd, + Alignment: metadatav1.AlignmentLeft, + Ellipsis: metadatav1.EllipsisEnd, }, }, }, @@ -677,13 +678,13 @@ func TestPopulate(t *testing.T) { }, "param_populate_from_scratch": { objectPath: "../../../../testdata/populate_metadata_1_param_from_scratch.o", - expectedMetadata: &GadgetMetadata{ + expectedMetadata: &metadatav1.GadgetMetadata{ Name: "TODO: Fill the gadget name", Description: "TODO: Fill the gadget description", HomepageURL: "TODO: Fill the gadget homepage URL", DocumentationURL: "TODO: Fill the gadget documentation URL", SourceURL: "TODO: Fill the gadget source code URL", - EBPFParams: map[string]EBPFParam{ + EBPFParams: map[string]metadatav1.EBPFParam{ // This also makes sure that param2 won't get picked up // since GADGET_PARAM(param2) is missing "param": { @@ -697,7 +698,7 @@ func TestPopulate(t *testing.T) { }, "param_dont_modify_values": { objectPath: "../../../../testdata/populate_metadata_1_param_from_scratch.o", - initialMetadata: &GadgetMetadata{ + initialMetadata: &metadatav1.GadgetMetadata{ Name: "foo", Description: "bar", HomepageURL: "url1", @@ -706,7 +707,7 @@ func TestPopulate(t *testing.T) { Annotations: map[string]string{ "io.inspektor-gadget.test": "test", }, - EBPFParams: map[string]EBPFParam{ + EBPFParams: map[string]metadatav1.EBPFParam{ "param": { // Set desc and some attributes to be sure they aren't overwritten ParamDesc: params.ParamDesc{ @@ -717,7 +718,7 @@ func TestPopulate(t *testing.T) { }, }, }, - expectedMetadata: &GadgetMetadata{ + expectedMetadata: &metadatav1.GadgetMetadata{ Name: "foo", Description: "bar", HomepageURL: "url1", @@ -726,7 +727,7 @@ func TestPopulate(t *testing.T) { Annotations: map[string]string{ "io.inspektor-gadget.test": "test", }, - EBPFParams: map[string]EBPFParam{ + EBPFParams: map[string]metadatav1.EBPFParam{ // This also makes sure that param2 won't get picked up // since GADGET_PARAM(param2) is missing "param": { @@ -742,45 +743,45 @@ func TestPopulate(t *testing.T) { }, "snapshotter_struct": { objectPath: "../../../../testdata/populate_metadata_snapshotter_struct.o", - expectedMetadata: &GadgetMetadata{ + expectedMetadata: &metadatav1.GadgetMetadata{ Name: "TODO: Fill the gadget name", Description: "TODO: Fill the gadget description", HomepageURL: "TODO: Fill the gadget homepage URL", DocumentationURL: "TODO: Fill the gadget documentation URL", SourceURL: "TODO: Fill the gadget source code URL", - Snapshotters: map[string]Snapshotter{ + Snapshotters: map[string]metadatav1.Snapshotter{ "events": { StructName: "event", }, }, - Structs: map[string]Struct{ + Structs: map[string]metadatav1.Struct{ "event": { - Fields: []Field{ + Fields: []metadatav1.Field{ { Name: "pid", Description: "TODO: Fill field description", - Attributes: FieldAttributes{ + Attributes: metadatav1.FieldAttributes{ Width: 10, - Alignment: AlignmentLeft, - Ellipsis: EllipsisEnd, + Alignment: metadatav1.AlignmentLeft, + Ellipsis: metadatav1.EllipsisEnd, }, }, { Name: "comm", Description: "TODO: Fill field description", - Attributes: FieldAttributes{ + Attributes: metadatav1.FieldAttributes{ Width: 16, - Alignment: AlignmentLeft, - Ellipsis: EllipsisEnd, + Alignment: metadatav1.AlignmentLeft, + Ellipsis: metadatav1.EllipsisEnd, }, }, { Name: "filename", Description: "TODO: Fill field description", - Attributes: FieldAttributes{ + Attributes: metadatav1.FieldAttributes{ Width: 16, - Alignment: AlignmentLeft, - Ellipsis: EllipsisEnd, + Alignment: metadatav1.AlignmentLeft, + Ellipsis: metadatav1.EllipsisEnd, }, }, }, @@ -806,38 +807,38 @@ func TestPopulate(t *testing.T) { }, "topper_add_missing_field": { objectPath: "../../../../testdata/populate_metadata_topper_add_missing_field.o", - initialMetadata: &GadgetMetadata{ + initialMetadata: &metadatav1.GadgetMetadata{ Name: "foo", Description: "bar", HomepageURL: "url1", DocumentationURL: "url2", SourceURL: "url3", - Toppers: map[string]Topper{ + Toppers: map[string]metadatav1.Topper{ "my_topper": { MapName: "events", StructName: "event", }, }, - Structs: map[string]Struct{ + Structs: map[string]metadatav1.Struct{ "event": { // Set desc and some attributes to be sure they aren't overwritten - Fields: []Field{ + Fields: []metadatav1.Field{ { Name: "pid", Description: "foo-pid", - Attributes: FieldAttributes{ + Attributes: metadatav1.FieldAttributes{ Width: 4747, - Alignment: AlignmentRight, - Ellipsis: EllipsisStart, + Alignment: metadatav1.AlignmentRight, + Ellipsis: metadatav1.EllipsisStart, }, }, { Name: "comm", Description: "bar-comm", - Attributes: FieldAttributes{ + Attributes: metadatav1.FieldAttributes{ Width: 1313, - Alignment: AlignmentRight, - Ellipsis: EllipsisStart, + Alignment: metadatav1.AlignmentRight, + Ellipsis: metadatav1.EllipsisStart, }, }, // missing filename field on purpose to check if it's added @@ -845,46 +846,46 @@ func TestPopulate(t *testing.T) { }, }, }, - expectedMetadata: &GadgetMetadata{ + expectedMetadata: &metadatav1.GadgetMetadata{ Name: "foo", Description: "bar", HomepageURL: "url1", DocumentationURL: "url2", SourceURL: "url3", - Toppers: map[string]Topper{ + Toppers: map[string]metadatav1.Topper{ "my_topper": { MapName: "events", StructName: "event", }, }, - Structs: map[string]Struct{ + Structs: map[string]metadatav1.Struct{ "event": { - Fields: []Field{ + Fields: []metadatav1.Field{ { Name: "pid", Description: "foo-pid", - Attributes: FieldAttributes{ + Attributes: metadatav1.FieldAttributes{ Width: 4747, - Alignment: AlignmentRight, - Ellipsis: EllipsisStart, + Alignment: metadatav1.AlignmentRight, + Ellipsis: metadatav1.EllipsisStart, }, }, { Name: "comm", Description: "bar-comm", - Attributes: FieldAttributes{ + Attributes: metadatav1.FieldAttributes{ Width: 1313, - Alignment: AlignmentRight, - Ellipsis: EllipsisStart, + Alignment: metadatav1.AlignmentRight, + Ellipsis: metadatav1.EllipsisStart, }, }, { Name: "filename", Description: "TODO: Fill field description", - Attributes: FieldAttributes{ + Attributes: metadatav1.FieldAttributes{ Width: 16, - Alignment: AlignmentLeft, - Ellipsis: EllipsisEnd, + Alignment: metadatav1.AlignmentLeft, + Ellipsis: metadatav1.EllipsisEnd, }, }, }, @@ -894,10 +895,10 @@ func TestPopulate(t *testing.T) { }, "topper_invalid_struct_name": { objectPath: "../../../../testdata/populate_metadata_1_topper_1_struct_from_scratch.o", - initialMetadata: &GadgetMetadata{ + initialMetadata: &metadatav1.GadgetMetadata{ Name: "foo", Description: "bar", - Toppers: map[string]Topper{ + Toppers: map[string]metadatav1.Topper{ "my_topper": { MapName: "events", StructName: "event2", @@ -938,10 +939,10 @@ func TestPopulate(t *testing.T) { metadata := test.initialMetadata if metadata == nil { - metadata = &GadgetMetadata{} + metadata = &metadatav1.GadgetMetadata{} } - err = metadata.Populate(spec) + err = Populate(metadata, spec) if test.expectedErrString != "" { require.ErrorContains(t, err, test.expectedErrString) return diff --git a/pkg/gadgets/run/types/types.go b/pkg/gadgets/run/types/types.go index cf00e808e7c..6cab9c10d00 100644 --- a/pkg/gadgets/run/types/types.go +++ b/pkg/gadgets/run/types/types.go @@ -1,4 +1,4 @@ -// Copyright 2023 The Inspektor Gadget authors +// Copyright 2023-2024 The Inspektor Gadget authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ import ( "github.com/inspektor-gadget/inspektor-gadget/pkg/columns" "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets" "github.com/inspektor-gadget/inspektor-gadget/pkg/logger" + metadatav1 "github.com/inspektor-gadget/inspektor-gadget/pkg/metadata/v1" "github.com/inspektor-gadget/inspektor-gadget/pkg/params" "github.com/inspektor-gadget/inspektor-gadget/pkg/parser" eventtypes "github.com/inspektor-gadget/inspektor-gadget/pkg/types" @@ -137,7 +138,7 @@ type ColumnDesc struct { } type GadgetInfo struct { - GadgetMetadata *GadgetMetadata + GadgetMetadata *metadatav1.GadgetMetadata Columns []ColumnDesc ProgContent []byte GadgetType gadgets.GadgetType diff --git a/pkg/metadata/v1/metadata.go b/pkg/metadata/v1/metadata.go new file mode 100644 index 00000000000..8511b348390 --- /dev/null +++ b/pkg/metadata/v1/metadata.go @@ -0,0 +1,133 @@ +// Copyright 2024 The Inspektor Gadget authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package metadatav1 + +import "github.com/inspektor-gadget/inspektor-gadget/pkg/params" + +// Tracer describes the behavior of a gadget that collects and sends events to user space +// TODO: We need to rename this concept not to collide with the opentelemetry concept +type Tracer struct { + // Name of the perf event array or ring buffer that the gadget uses to send events + MapName string `yaml:"mapName"` + // Name of the structure generated by this tracer + StructName string `yaml:"structName"` +} + +// Topper describes the behavior of a gadget that shows the current activity +// sorted by the highest to the lowest in the resource being observed. +type Topper struct { + // Name of the hash map that the gadget uses to send statistics + MapName string `yaml:"mapName"` + // Name of the structure generated by this topper + StructName string `yaml:"structName"` +} + +// Snapshotter describes the behavior of a gadget that collects the state of a subsystem +type Snapshotter struct { + StructName string `yaml:"structName"` +} + +const ( + DefaultColumnWidth = 16 +) + +type Alignment string + +const ( + AlignmenNone Alignment = "" + AlignmentLeft Alignment = "left" + AlignmentRight Alignment = "right" +) + +type EllipsisType string + +const ( + EllipsisNone EllipsisType = "" + EllipsisStart EllipsisType = "start" + EllipsisMiddle EllipsisType = "middle" + EllipsisEnd EllipsisType = "end" +) + +// FieldAttributes describes how to format a field. It's almost 1:1 mapping with columns.Attributes, +// however we are keeping this separated because we don't want to create a strong coupling with the +// columns library now. Later on we can consider merging both of them. +type FieldAttributes struct { + // Width to reserve for this field + Width uint `yaml:"width,omitempty"` + // MinWidth is the minimum width for this field + MinWidth uint `yaml:"minWidth,omitempty"` + // MaxWidth is the maximum width for this field + MaxWidth uint `yaml:"maxWidth,omitempty"` + // Alignment of this column (left or right) + Alignment Alignment `yaml:"alignment,omitempty"` + // Hidden defines whether a column is to be hid by default + Hidden bool `yaml:"hidden,omitempty"` + // EllipsisType defines how to abbreviate this column if the value needs more space than is + // available. (start, middle or end) + Ellipsis EllipsisType `yaml:"ellipsis,omitempty"` + // Template defines the template that will be used. + // TODO: add a link to existing templates + Template string `yaml:"template,omitempty"` +} + +type Field struct { + // Field name + Name string `yaml:"name"` + // Field description + Description string `yaml:"description,omitempty"` + // Attributes defines how the field should be formatted + Attributes FieldAttributes `yaml:"attributes"` + // Annotations represents extra information that is not relevant to Inspektor Gadget, but + // for other applications, like color font for instance. + Annotations map[string]interface{} `yaml:"annotations,omitempty"` +} + +// Struct describes a type generated by the gadget +type Struct struct { + Fields []Field `yaml:"fields"` +} + +type EBPFParam struct { + params.ParamDesc `yaml:",inline"` +} + +type GadgetMetadata struct { + // Gadget name + Name string `yaml:"name"` + // Gadget description + Description string `yaml:"description,omitempty"` + // HomepageURL is the URL to the gadget's homepage + HomepageURL string `yaml:"homepageURL,omitempty"` + // DocumentationURL is the URL to the gadget's documentation + DocumentationURL string `yaml:"documentationURL,omitempty"` + // SourceURL is the URL to the gadget's source code repository + SourceURL string `yaml:"sourceURL,omitempty"` + // Annotations is a map of key-value pairs that provide additional information about the gadget + Annotations map[string]string `yaml:"annotations,omitempty"` + + // Tracers implemented by the gadget + // TODO: Rename this field to something that doesn't collide with the opentelemetry concept + Tracers map[string]Tracer `yaml:"tracers,omitempty"` + // Toppers implemented by the gadget + Toppers map[string]Topper `yaml:"toppers,omitempty"` + // Snapshotters implemented by the gadget + Snapshotters map[string]Snapshotter `yaml:"snapshotters,omitempty"` + // Types generated by the gadget + Structs map[string]Struct `yaml:"structs,omitempty"` + // Params exposed by the gadget through eBPF constants + EBPFParams map[string]EBPFParam `yaml:"ebpfParams,omitempty"` + // Other params exposed by the gadget + GadgetParams map[string]params.ParamDesc `yaml:"gadgetParams,omitempty"` +} diff --git a/pkg/oci/build.go b/pkg/oci/build.go index 4a737673814..cc5528fa6df 100644 --- a/pkg/oci/build.go +++ b/pkg/oci/build.go @@ -1,4 +1,4 @@ -// Copyright 2023 The Inspektor Gadget authors +// Copyright 2023-2024 The Inspektor Gadget authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -31,7 +31,7 @@ import ( "oras.land/oras-go/v2/content" "oras.land/oras-go/v2/errdef" - "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/run/types" + metadatav1 "github.com/inspektor-gadget/inspektor-gadget/pkg/metadata/v1" ) const ( @@ -138,7 +138,7 @@ func createLayerDesc(ctx context.Context, target oras.Target, progFilePath, medi } func annotationsFromMetadata(metadataBytes []byte) (map[string]string, error) { - metadata := &types.GadgetMetadata{} + metadata := &metadatav1.GadgetMetadata{} if err := yaml.NewDecoder(bytes.NewReader(metadataBytes)).Decode(&metadata); err != nil { return nil, fmt.Errorf("decoding metadata file: %w", err) } diff --git a/pkg/oci/build_metadata.go b/pkg/oci/build_metadata.go index cd2b8633f9d..0c557fdd3b4 100644 --- a/pkg/oci/build_metadata.go +++ b/pkg/oci/build_metadata.go @@ -1,4 +1,4 @@ -// Copyright 2023 The Inspektor Gadget authors +// Copyright 2023-2024 The Inspektor Gadget authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -25,6 +25,7 @@ import ( "gopkg.in/yaml.v2" "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/run/types" + metadatav1 "github.com/inspektor-gadget/inspektor-gadget/pkg/metadata/v1" ) func loadSpec(progContent []byte) (*ebpf.CollectionSpec, error) { @@ -68,7 +69,7 @@ func validateMetadataFile(ctx context.Context, opts *BuildGadgetImageOpts) error } defer metadataFile.Close() - metadata := &types.GadgetMetadata{} + metadata := &metadatav1.GadgetMetadata{} if err := yaml.NewDecoder(metadataFile).Decode(metadata); err != nil { return fmt.Errorf("decoding metadata file: %w", err) } @@ -78,7 +79,7 @@ func validateMetadataFile(ctx context.Context, opts *BuildGadgetImageOpts) error return fmt.Errorf("loading spec: %w", err) } - return metadata.Validate(spec) + return types.Validate(metadata, spec) } func createOrUpdateMetadataFile(ctx context.Context, opts *BuildGadgetImageOpts) error { @@ -90,7 +91,7 @@ func createOrUpdateMetadataFile(ctx context.Context, opts *BuildGadgetImageOpts) _, statErr := os.Stat(opts.MetadataPath) update := statErr == nil - metadata := &types.GadgetMetadata{} + metadata := &metadatav1.GadgetMetadata{} if update { // load metadata file @@ -107,14 +108,14 @@ func createOrUpdateMetadataFile(ctx context.Context, opts *BuildGadgetImageOpts) log.Debugf("Metadata file found, updating it") // TODO: this validation could be softer, just printing warnings - if err := metadata.Validate(spec); err != nil { + if err := types.Validate(metadata, spec); err != nil { return fmt.Errorf("metadata file is wrong, fix it before continuing: %w", err) } } else { log.Debug("Metadata file not found, generating it") } - if err := metadata.Populate(spec); err != nil { + if err := types.Populate(metadata, spec); err != nil { return fmt.Errorf("handling trace maps: %w", err) }