tetragon

Форк
0
/
checker.go 
144 строки · 4.0 Кб
1
// SPDX-License-Identifier: Apache-2.0
2
// Copyright Authors of Tetragon
3

4
package test
5

6
import (
7
	"os/exec"
8
	"runtime"
9
	"testing"
10

11
	"github.com/cilium/tetragon/api/v1/tetragon"
12
	"github.com/cilium/tetragon/pkg/testutils"
13
	"github.com/sirupsen/logrus"
14

15
	ec "github.com/cilium/tetragon/api/v1/tetragon/codegen/eventchecker"
16
)
17

18
//revive:disable
19

20
// TestEventChecker is a checker that relies on:
21
//   - the test sensor being loaded
22
//   - user-space executing hooks that trigger the test sensor on all cores
23
//     (see contrib/tester-progs/trigger-test-events).
24
//
25
// The typical structure of a test is:
26
//  1. start observer
27
//  2. do some things on user-space
28
//  3. check that we get the expected events from tetragon
29
//
30
// In such a test there is no way to determine when to stop looking for events.  Hence, we retry
31
// step 3 a number of times to gain confidence that all events from step 2 have been processed.
32
// These retries induce a significant time cost in failing tests or in tests that check the absence
33
// of events (e.g., when doing filtering).
34
//
35
// TestEventChecker enables testing without timeouts. Timeouts are still used for robustness, but
36
// assuming TestEventChecker works correctly they are not needed.
37
//
38
// After step 2, we trigger the hook of the test sensor (a simple sensor that generates test events)
39
// on all CPUs. Once we have seen all the test events (on all CPUs), then we know that if we expect
40
// any events, they are not there because events cannot be reordered on the same CPU.
41
type TestEventChecker struct {
42
	// eventChecker is the underlying event checker
43
	eventChecker      ec.MultiEventChecker
44
	completionChecker *CompletionChecker
45
}
46

47
// TestCheckerMarkEnd executes the necessary operations to mark the end of event stream on all CPUs
48
func TestCheckerMarkEnd(t *testing.T) {
49
	testBin := testutils.RepoRootPath("contrib/tester-progs/trigger-test-events")
50
	testCmd := exec.Command(testBin)
51
	err := testCmd.Run()
52
	if err != nil {
53
		t.Fatalf("error executing command: %v", err)
54
	}
55
}
56

57
//revive:enable
58

59
func NewTestChecker(c ec.MultiEventChecker) *TestEventChecker {
60
	ret := TestEventChecker{
61
		eventChecker:      c,
62
		completionChecker: NewCompletionChecker(),
63
	}
64

65
	return &ret
66
}
67

68
// update updates the state bsaed on the given event
69
func (tc *TestEventChecker) update(ev ec.Event) {
70
	switch ev := ev.(type) {
71
	case *tetragon.Test:
72
		cpu := ev.Arg0
73
		tc.completionChecker.Update(cpu)
74
	default:
75
	}
76
}
77

78
func (tc *TestEventChecker) NextEventCheck(ev ec.Event, l *logrus.Logger) (bool, error) {
79
	if tc.completionChecker.Done() {
80
		l.Info("seen events on all CPUs, finalizing test")
81
		return true, tc.eventChecker.FinalCheck(l)
82
	}
83

84
	done, err := tc.eventChecker.NextEventCheck(ev, l)
85
	if done {
86
		// underlying checker done, just return its values
87
		return true, err
88
	}
89

90
	// just update the state. In the next event, we wil check
91
	// whether it's time to terminate or not.
92
	tc.update(ev)
93

94
	return false, err
95
}
96

97
func (tc *TestEventChecker) FinalCheck(l *logrus.Logger) error {
98
	// this means that we run out of events before seeing all test events.
99
	// Just return what the underlying checker returns
100
	tc.completionChecker.Reset()
101
	return tc.eventChecker.FinalCheck(l)
102
}
103

104
type CompletionChecker struct {
105
	cpuDone  map[uint64]bool
106
	remCount int
107
}
108

109
func NewCompletionChecker() *CompletionChecker {
110
	ncpus := runtime.NumCPU()
111
	ret := CompletionChecker{
112
		cpuDone:  make(map[uint64]bool, ncpus),
113
		remCount: 0,
114
	}
115

116
	// NB: We assume CPU ids are consecutive. There are systems where this
117
	// is not the caes (e.g., cores getting offline), but we ignore them
118
	// for now.
119
	ret.remCount = ncpus
120
	for i := 0; i < ncpus; i++ {
121
		ret.cpuDone[uint64(i)] = false
122
	}
123

124
	return &ret
125
}
126

127
func (cc *CompletionChecker) Update(cpu uint64) {
128
	prev := cc.cpuDone[cpu]
129
	cc.cpuDone[cpu] = true
130
	if !prev && cc.remCount > 0 {
131
		cc.remCount--
132
	}
133
}
134

135
func (cc *CompletionChecker) Reset() {
136
	for i := range cc.cpuDone {
137
		cc.cpuDone[i] = false
138
	}
139
	cc.remCount = len(cc.cpuDone)
140
}
141

142
func (cc *CompletionChecker) Done() bool {
143
	return cc.remCount == 0
144
}
145

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

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

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

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