tetragon
362 строки · 10.7 Кб
1// SPDX-License-Identifier: Apache-2.0
2// Copyright Authors of Tetragon
3
4package common
5
6import (
7"fmt"
8"path/filepath"
9"strings"
10
11"github.com/cilium/tetragon/pkg/logger"
12"google.golang.org/protobuf/compiler/protogen"
13"google.golang.org/protobuf/reflect/protoreflect"
14)
15
16// TetragonPackageName is the import path for the Tetragon package
17var TetragonPackageName = "github.com/cilium/tetragon"
18
19// TetragonApiPackageName is the import path for the code generated package
20var TetragonApiPackageName = "api/v1/tetragon"
21
22// TetragonCopyrightHeader is the license header to prepend to all generated files
23var TetragonCopyrightHeader = `// SPDX-License-Identifier: Apache-2.0
24// Copyright Authors of Tetragon`
25
26// NewFile creates a new pakage and file in the project
27func NewFile(gen *protogen.Plugin, file *protogen.File, pkg string, pkgName string, fileName string) *protogen.GeneratedFile {
28importPath := filepath.Join(string(file.GoImportPath), pkg)
29pathSuffix := filepath.Base(file.GeneratedFilenamePrefix)
30fileName = filepath.Join(strings.TrimSuffix(file.GeneratedFilenamePrefix, pathSuffix), pkg, fmt.Sprintf("%s.pb.go", fileName))
31logger.GetLogger().Infof("%s", fileName)
32
33g := gen.NewGeneratedFile(fileName, protogen.GoImportPath(importPath))
34g.P(TetragonCopyrightHeader)
35g.P()
36
37g.P("// Code generated by protoc-gen-go-tetragon. DO NOT EDIT")
38g.P()
39
40g.P("package ", pkgName)
41g.P()
42
43return g
44}
45
46// NewCodegenFile creates a new codegen pakage and file in the project
47func NewCodegenFile(gen *protogen.Plugin, file *protogen.File, pkg string) *protogen.GeneratedFile {
48pkgName := filepath.Base(pkg)
49pkg = filepath.Join("codegen", pkg)
50
51return NewFile(gen, file, pkg, pkgName, pkgName)
52}
53
54// GoIdent is a convenience helper that returns a qualified go ident as a string for
55// a given import package and name
56func GoIdent(g *protogen.GeneratedFile, importPath string, name string) string {
57return g.QualifiedGoIdent(protogen.GoIdent{
58GoName: name,
59GoImportPath: protogen.GoImportPath(importPath),
60})
61}
62
63// TetragonApiIdent is a convenience helper that calls GoIdent with the path to the
64// Tetragon API package.
65func TetragonApiIdent(g *protogen.GeneratedFile, name string) string {
66return TetragonIdent(g, TetragonApiPackageName, name)
67}
68
69// TetragonIdent is a convenience helper that calls GoIdent with the path to the
70// Tetragon package.
71func TetragonIdent(g *protogen.GeneratedFile, importPath string, name string) string {
72importPath = filepath.Join(TetragonPackageName, importPath)
73return GoIdent(g, importPath, name)
74}
75
76// GeneratedIdent is a convenience helper that returns a qualified go ident as a string for
77// a given import package and name within the codegen package
78func GeneratedIdent(g *protogen.GeneratedFile, importPath string, name string) string {
79importPath = filepath.Join(TetragonPackageName, TetragonApiPackageName, "codegen", importPath)
80return GoIdent(g, importPath, name)
81}
82
83// Logger is a convenience helper that generates a call to logger.GetLogger()
84func Logger(g *protogen.GeneratedFile) string {
85return fmt.Sprintf("%s()", GoIdent(g, "github.com/cilium/tetragon/pkg/logger", "GetLogger"))
86}
87
88func ProcessIdent(g *protogen.GeneratedFile) string {
89importPath := filepath.Join("github.com/cilium/tetragon/api/v1/tetragon")
90return GoIdent(g, importPath, "Process")
91}
92
93func ProcessKprobeIdent(g *protogen.GeneratedFile) string {
94importPath := filepath.Join("github.com/cilium/tetragon/api/v1/tetragon")
95return GoIdent(g, importPath, "ProcessKprobe")
96}
97
98func ListMatcherIdent(g *protogen.GeneratedFile, name string) string {
99importPath := filepath.Join("github.com/cilium/tetragon/pkg/matchers/listmatcher")
100return GoIdent(g, importPath, name)
101}
102
103func StringMatcherIdent(g *protogen.GeneratedFile, name string) string {
104importPath := filepath.Join("github.com/cilium/tetragon/pkg/matchers/stringmatcher")
105return GoIdent(g, importPath, name)
106}
107
108func BytesMatcherIdent(g *protogen.GeneratedFile, name string) string {
109importPath := filepath.Join("github.com/cilium/tetragon/pkg/matchers/bytesmatcher")
110return GoIdent(g, importPath, name)
111}
112
113func DurationMatcherIdent(g *protogen.GeneratedFile, name string) string {
114importPath := filepath.Join("github.com/cilium/tetragon/pkg/matchers/durationmatcher")
115return GoIdent(g, importPath, name)
116}
117
118func TimestampMatcherIdent(g *protogen.GeneratedFile, name string) string {
119importPath := filepath.Join("github.com/cilium/tetragon/pkg/matchers/timestampmatcher")
120return GoIdent(g, importPath, name)
121}
122
123func PkgProcessIdent(g *protogen.GeneratedFile, name string) string {
124importPath := filepath.Join("github.com/cilium/tetragon/pkg/process")
125return GoIdent(g, importPath, name)
126}
127
128// FmtErrorf is a convenience helper that generates a call to fmt.Errorf
129func FmtErrorf(g *protogen.GeneratedFile, fmt_ string, args ...string) string {
130args = append([]string{fmt.Sprintf("\"%s\"", fmt_)}, args...)
131return fmt.Sprintf("%s(%s)", GoIdent(g, "fmt", "Errorf"), strings.Join(args, ", "))
132}
133
134// FmtSprintf is a convenience helper that generates a call to fmt.Sprintf
135func FmtSprintf(g *protogen.GeneratedFile, fmt_ string, args ...string) string {
136args = append([]string{fmt.Sprintf("\"%s\"", fmt_)}, args...)
137return fmt.Sprintf("%s(%s)", GoIdent(g, "fmt", "Sprintf"), strings.Join(args, ", "))
138}
139
140// EventFieldCheck returns true if the event has the field
141func EventFieldCheck(msg *protogen.Message, field string) bool {
142return msg.Desc.Fields().ByName(protoreflect.Name(field)) != nil
143}
144
145// IsProcessEvent returns true if the message is an Tetragon event that has a process field
146func IsProcessEvent(msg *protogen.Message) bool {
147return EventFieldCheck(msg, "process")
148}
149
150// IsParentEvent returns true if the message is an Tetragon event that has a parent field
151func IsParentEvent(msg *protogen.Message) bool {
152return EventFieldCheck(msg, "parent")
153}
154
155// StructTag is a convenience helper that formats a struct tag
156func StructTag(tag string) string {
157return fmt.Sprintf("`%s`", tag)
158}
159
160var eventsCache []*protogen.Message
161
162type GetEventsResponseOneofInfo struct {
163TypeName string
164FieldName string
165}
166
167func GetEventsResponseOneofs(files []*protogen.File) ([]GetEventsResponseOneofInfo, error) {
168// find the GetEventsResponse type
169var getEventsResponse *protogen.Message
170for _, f := range files {
171for _, msg := range f.Messages {
172if msg.GoIdent.GoName == "GetEventsResponse" {
173getEventsResponse = msg
174break
175}
176}
177}
178if getEventsResponse == nil {
179return nil, fmt.Errorf("Unable to find GetEventsResponse message")
180}
181
182var eventOneof *protogen.Oneof
183for _, oneof := range getEventsResponse.Oneofs {
184if oneof.Desc.Name() == "event" {
185eventOneof = oneof
186break
187}
188}
189if eventOneof == nil {
190return nil, fmt.Errorf("Unable to find GetEventsResponse.event")
191}
192
193var info []GetEventsResponseOneofInfo
194for _, oneof := range eventOneof.Fields {
195info = append(info, GetEventsResponseOneofInfo{
196TypeName: strings.TrimPrefix(oneof.GoIdent.GoName, "GetEventsResponse_"),
197FieldName: oneof.Desc.TextName(),
198})
199}
200
201return info, nil
202}
203
204// GetEvents returns a list of all messages that are events
205func GetEvents(files []*protogen.File) ([]*protogen.Message, error) {
206if len(eventsCache) != 0 {
207return eventsCache, nil
208}
209
210var getEventsResponse *protogen.Message
211for _, f := range files {
212for _, msg := range f.Messages {
213if msg.GoIdent.GoName == "GetEventsResponse" {
214getEventsResponse = msg
215break
216}
217}
218}
219if getEventsResponse == nil {
220return nil, fmt.Errorf("Unable to find GetEventsResponse message")
221}
222
223var eventOneof *protogen.Oneof
224for _, oneof := range getEventsResponse.Oneofs {
225if oneof.Desc.Name() == "event" {
226eventOneof = oneof
227break
228}
229}
230if eventOneof == nil {
231return nil, fmt.Errorf("Unable to find GetEventsResponse.event")
232}
233
234validNames := make(map[string]struct{})
235for _, type_ := range eventOneof.Fields {
236name := strings.TrimPrefix(type_.GoIdent.GoName, "GetEventsResponse_")
237validNames[name] = struct{}{}
238}
239
240for _, f := range files {
241for _, msg := range f.Messages {
242if _, ok := validNames[string(msg.Desc.Name())]; ok {
243eventsCache = append(eventsCache, msg)
244}
245}
246}
247
248return eventsCache, nil
249}
250
251var fieldsCache []*protogen.Message
252
253// isReservedField marks message types that we don't want to generate event checkers for
254var isReservedField = map[string]struct{}{
255"Timestamp": {},
256"UInt32Value": {},
257"Int32Value": {},
258"UInt64Value": {},
259"Int64Value": {},
260"StringValue": {},
261"Duration": {},
262"BoolValue": {},
263}
264
265// GetFields returns a list of all messages that are fields
266func GetFields(files []*protogen.File) ([]*protogen.Message, error) {
267if len(fieldsCache) == 0 {
268events, err := GetEvents(files)
269if err != nil {
270return nil, err
271}
272
273validFields := make(map[string]struct{})
274for _, event := range events {
275fields := getFieldsForMessage(event)
276for _, field := range fields {
277if field.Message == nil {
278continue
279}
280if _, reserved := isReservedField[field.Message.GoIdent.GoName]; !reserved {
281validFields[field.Message.GoIdent.GoName] = struct{}{}
282}
283}
284}
285
286for _, f := range files {
287for _, msg := range f.Messages {
288if _, ok := validFields[string(msg.Desc.Name())]; ok {
289fieldsCache = append(fieldsCache, msg)
290}
291}
292}
293}
294
295return fieldsCache, nil
296}
297
298// getFieldsForMessage recursively looks up all the fields for a given message
299func getFieldsForMessage(msg *protogen.Message) []*protogen.Field {
300seen := make(map[string]struct{})
301return __getFieldsForMessage(msg, seen)
302}
303
304// __getFieldsForMessage is the underlying recusion logic of getFieldsForMessage
305func __getFieldsForMessage(msg *protogen.Message, seen map[string]struct{}) []*protogen.Field {
306var fields []*protogen.Field
307
308for _, field := range msg.Fields {
309if field.Message == nil {
310continue
311}
312fieldType := field.Message.GoIdent.GoName
313if _, ok := seen[fieldType]; ok {
314continue
315}
316seen[fieldType] = struct{}{}
317fields = append(fields, field)
318fields = append(fields, __getFieldsForMessage(field.Message, seen)...)
319}
320
321return fields
322}
323
324var enumsCache []*protogen.Enum
325
326// GetEnums returns a list of all enums that are message fields
327func GetEnums(files []*protogen.File) ([]*protogen.Enum, error) {
328if len(enumsCache) == 0 {
329events, err := GetEvents(files)
330if err != nil {
331return nil, err
332}
333
334fields, err := GetFields(files)
335if err != nil {
336return nil, err
337}
338
339eventsAndFields := append(events, fields...)
340
341validNames := make(map[string]struct{})
342for _, msg := range eventsAndFields {
343for _, field := range msg.Fields {
344enum := field.Enum
345if enum == nil {
346continue
347}
348validNames[string(enum.Desc.Name())] = struct{}{}
349}
350}
351
352for _, f := range files {
353for _, enum := range f.Enums {
354if _, ok := validNames[string(enum.Desc.Name())]; ok {
355enumsCache = append(enumsCache, enum)
356}
357}
358}
359}
360
361return enumsCache, nil
362}
363