inspektor-gadget

Форк
0
229 строк · 7.2 Кб
1
// Copyright 2022-2023 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
//go:build linux
16
// +build linux
17

18
package tracer
19

20
import (
21
	"fmt"
22
	"os"
23
	"os/exec"
24
	"reflect"
25
	"testing"
26

27
	utilstest "github.com/inspektor-gadget/inspektor-gadget/internal/test"
28
	containerutils "github.com/inspektor-gadget/inspektor-gadget/pkg/container-utils"
29
	"github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets"
30
	snapshotProcessTypes "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/snapshot/process/types"
31
	eventtypes "github.com/inspektor-gadget/inspektor-gadget/pkg/types"
32
)
33

34
type collectorFunc func(config *Config, enricher gadgets.DataEnricherByMntNs) ([]*snapshotProcessTypes.Event, error)
35

36
func BenchmarkSnapshotProcessEBPFTracer(b *testing.B) {
37
	benchmarkTracer(b, runeBPFCollector)
38
}
39

40
func BenchmarkSnapshotProcessProcfsTracer(b *testing.B) {
41
	benchmarkTracer(b, runProcfsCollector)
42
}
43

44
func benchmarkTracer(b *testing.B, runCollector collectorFunc) {
45
	utilstest.RequireRoot(b)
46

47
	for n := 0; n < b.N; n++ {
48
		_, err := runCollector(&Config{}, nil)
49
		if err != nil {
50
			b.Fatalf("benchmarking collector: %s", err)
51
		}
52
	}
53
}
54

55
func TestSnapshotProcessEBPFTracer(t *testing.T) {
56
	testTracer(t, runeBPFCollector)
57
}
58

59
func TestSnapshotProcessProcfsTracer(t *testing.T) {
60
	testTracer(t, runProcfsCollector)
61
}
62

63
func testTracer(t *testing.T, runCollector collectorFunc) {
64
	t.Parallel()
65

66
	utilstest.RequireRoot(t)
67
	utilstest.HostInit(t)
68

69
	type testDefinition struct {
70
		getTracerConfig func(info *utilstest.RunnerInfo) *Config
71
		runnerConfig    *utilstest.RunnerConfig
72
		generateEvent   func() (int, error)
73
		validateEvent   func(t *testing.T, info *utilstest.RunnerInfo, sleepPid int, events []snapshotProcessTypes.Event)
74
	}
75

76
	for name, test := range map[string]testDefinition{
77
		"captures_all_events_with_no_filters_configured": {
78
			getTracerConfig: func(info *utilstest.RunnerInfo) *Config {
79
				return &Config{}
80
			},
81
			generateEvent: generateEvent,
82
			validateEvent: utilstest.ExpectAtLeastOneEvent(func(info *utilstest.RunnerInfo, sleepPid int) *snapshotProcessTypes.Event {
83
				return &snapshotProcessTypes.Event{
84
					Event: eventtypes.Event{
85
						Type: eventtypes.NORMAL,
86
					},
87
					Command:       "sleep",
88
					Pid:           sleepPid,
89
					Tid:           sleepPid,
90
					ParentPid:     0,
91
					WithMountNsID: eventtypes.WithMountNsID{MountNsID: info.MountNsID},
92
				}
93
			}),
94
		},
95
		"captures_no_events_with_no_matching_filter": {
96
			getTracerConfig: func(info *utilstest.RunnerInfo) *Config {
97
				// We can't use 0 as the mntnsid because the tracer will collect
98
				// some defunct processes that don't have any mntnsid defined
99
				// anymore. Then, we set the network namespace inode id to be sure
100
				// there is not any mount namespace using that same inode.
101
				mntns, _ := containerutils.GetNetNs(os.Getpid())
102
				return &Config{
103
					MountnsMap: utilstest.CreateMntNsFilterMap(t, mntns),
104
				}
105
			},
106
			generateEvent: generateEvent,
107
			validateEvent: utilstest.ExpectNoEvent[snapshotProcessTypes.Event, int],
108
		},
109
		"captures_events_with_matching_filter": {
110
			getTracerConfig: func(info *utilstest.RunnerInfo) *Config {
111
				return &Config{
112
					MountnsMap: utilstest.CreateMntNsFilterMap(t, info.MountNsID),
113
				}
114
			},
115
			generateEvent: generateEvent,
116
			// We have to use ExpectAtLeastOneEvent because it's possible that the
117
			// golang thread that executes this test is also captured
118
			validateEvent: utilstest.ExpectAtLeastOneEvent(func(info *utilstest.RunnerInfo, sleepPid int) *snapshotProcessTypes.Event {
119
				return &snapshotProcessTypes.Event{
120
					Event: eventtypes.Event{
121
						Type: eventtypes.NORMAL,
122
					},
123
					Command:       "sleep",
124
					Pid:           sleepPid,
125
					Tid:           sleepPid,
126
					WithMountNsID: eventtypes.WithMountNsID{MountNsID: info.MountNsID},
127
				}
128
			}),
129
		},
130
		// This is a hacky way to test this: one of the threads of the goroutine is moved to
131
		// the mount namespace created for testing, also the sleep process we execute is
132
		// there. That's why 2 events are expected. A better way would be to execute a
133
		// command that creates multiple threads and check if we capture all of them, but so
134
		// far I haven't found an easy way to do so. One idea is to use python but it seems
135
		// too complicated and will introduce another dependency for testing.
136
		"captures_events_with_matching_filter_threads": {
137
			getTracerConfig: func(info *utilstest.RunnerInfo) *Config {
138
				return &Config{
139
					MountnsMap:  utilstest.CreateMntNsFilterMap(t, info.MountNsID),
140
					ShowThreads: true,
141
				}
142
			},
143
			generateEvent: generateEvent,
144
			validateEvent: func(t *testing.T, info *utilstest.RunnerInfo, sleepPid int, events []snapshotProcessTypes.Event) {
145
				if len(events) != 2 {
146
					t.Fatalf("%d events expected, found: %d", 2, len(events))
147
				}
148

149
				expectedEvent := &snapshotProcessTypes.Event{
150
					Event: eventtypes.Event{
151
						Type: eventtypes.NORMAL,
152
					},
153
					Command:       "sleep",
154
					Pid:           sleepPid,
155
					Tid:           sleepPid,
156
					ParentPid:     0,
157
					WithMountNsID: eventtypes.WithMountNsID{MountNsID: info.MountNsID},
158
				}
159

160
				for _, event := range events {
161
					if reflect.DeepEqual(expectedEvent, &event) {
162
						return
163
					}
164
				}
165

166
				t.Fatalf("Event wasn't captured")
167
			},
168
		},
169
		"no_threads_are_captured": {
170
			getTracerConfig: func(info *utilstest.RunnerInfo) *Config {
171
				return &Config{}
172
			},
173
			generateEvent: generateEvent,
174
			validateEvent: func(t *testing.T, info *utilstest.RunnerInfo, sleepPid int, events []snapshotProcessTypes.Event) {
175
				if len(events) == 0 {
176
					t.Fatalf("no events were captured")
177
				}
178
				for _, event := range events {
179
					if event.Pid != event.Tid {
180
						t.Fatalf("thread %d was captured", event.Tid)
181
					}
182
				}
183
			},
184
		},
185
	} {
186
		test := test
187

188
		t.Run(name, func(t *testing.T) {
189
			t.Parallel()
190

191
			runner := utilstest.NewRunnerWithTest(t, test.runnerConfig)
192

193
			var sleepPid int
194

195
			utilstest.RunWithRunner(t, runner, func() error {
196
				var err error
197
				sleepPid, err = test.generateEvent()
198
				return err
199
			})
200

201
			events, err := runCollector(test.getTracerConfig(runner.Info), nil)
202
			if err != nil {
203
				t.Fatalf("running collector: %s", err)
204
			}
205

206
			// TODO: This won't be required once we pass pointers everywhere
207
			validateEvents := []snapshotProcessTypes.Event{}
208
			for _, event := range events {
209
				// Normalize parent PID to avoid failing tests as this is not trivial to
210
				// guess the parent PID.
211
				event.ParentPid = 0
212

213
				validateEvents = append(validateEvents, *event)
214
			}
215

216
			test.validateEvent(t, runner.Info, sleepPid, validateEvents)
217
		})
218
	}
219
}
220

221
// Function that runs a "sleep" process.
222
func generateEvent() (int, error) {
223
	cmd := exec.Command("/bin/sleep", "5")
224
	if err := cmd.Start(); err != nil {
225
		return 0, fmt.Errorf("running command: %w", err)
226
	}
227

228
	return cmd.Process.Pid, nil
229
}
230

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.