Skip to content

Commit 2ef9365

Browse files
committed
chore: make dependencies manager a singleton
This tidies up the dependencies manager by making it a singleton. It's a necessary step to decouple the manager from the Tracee object allowing it to be accessed from other parts of the codebase and be detached from other logic. It introduces the temporary ResetManagerFromTests() to reset the manager state between tests. NOTE: It continues being not thread-safe. This will be addressed in a future PR.
1 parent ee4376d commit 2ef9365

File tree

6 files changed

+94
-28
lines changed

6 files changed

+94
-28
lines changed

pkg/ebpf/ksymbols.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55

66
"github.com/aquasecurity/tracee/pkg/errfmt"
77
"github.com/aquasecurity/tracee/pkg/events"
8+
"github.com/aquasecurity/tracee/pkg/events/dependencies"
89
"github.com/aquasecurity/tracee/pkg/logger"
910
)
1011

@@ -26,7 +27,7 @@ func (t *Tracee) UpdateKallsyms() error {
2627

2728
// Wrap long method names.
2829
evtDefSymDeps := func(id events.ID) []events.KSymbol {
29-
depsNode, _ := t.eventsDependencies.GetEvent(id)
30+
depsNode, _ := dependencies.GetManagerInstance().GetEvent(id)
3031
deps := depsNode.GetDependencies()
3132
return deps.GetKSymbols()
3233
}

pkg/ebpf/tracee.go

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,6 @@ type Tracee struct {
119119
streamsManager *streams.StreamsManager
120120
// policyManager manages policy state
121121
policyManager *policyManager
122-
// The dependencies of events used by Tracee
123-
eventsDependencies *dependencies.Manager
124122

125123
// Ksymbols needed to be kept alive in table.
126124
// This does not mean they are required for tracee to function.
@@ -180,7 +178,7 @@ func (t *Tracee) addDependenciesToStateRecursive(eventNode *dependencies.EventNo
180178
eventID := eventNode.GetID()
181179
for _, dependencyEventID := range eventNode.GetDependencies().GetIDs() {
182180
t.addDependencyEventToState(dependencyEventID, []events.ID{eventID})
183-
dependencyNode, err := t.eventsDependencies.GetEvent(dependencyEventID)
181+
dependencyNode, err := dependencies.GetManagerInstance().GetEvent(dependencyEventID)
184182
if err == nil {
185183
t.addDependenciesToStateRecursive(dependencyNode)
186184
}
@@ -189,7 +187,7 @@ func (t *Tracee) addDependenciesToStateRecursive(eventNode *dependencies.EventNo
189187

190188
func (t *Tracee) selectEvent(eventID events.ID, chosenState events.EventState) {
191189
t.addEventState(eventID, chosenState)
192-
eventNode, err := t.eventsDependencies.SelectEvent(eventID)
190+
eventNode, err := dependencies.GetManagerInstance().SelectEvent(eventID)
193191
if err != nil {
194192
logger.Errorw("Event selection failed",
195193
"event", events.Core.GetDefinitionByID(eventID).GetName())
@@ -239,16 +237,17 @@ func New(cfg config.Config) (*Tracee, error) {
239237
eventSignatures: make(map[events.ID]bool),
240238
streamsManager: streams.NewStreamsManager(),
241239
policyManager: policyManager,
242-
eventsDependencies: dependencies.NewDependenciesManager(
243-
func(id events.ID) events.Dependencies {
244-
return events.Core.GetDefinitionByID(id).GetDependencies()
245-
}),
246-
requiredKsyms: []string{},
240+
requiredKsyms: []string{},
247241
}
248242

243+
eventsDependencies := dependencies.NewDependenciesManager(
244+
func(id events.ID) events.Dependencies {
245+
return events.Core.GetDefinitionByID(id).GetDependencies()
246+
})
247+
249248
// TODO: As dynamic event addition or removal becomes a thing, we should subscribe all the watchers
250249
// before selecting them. There is no reason to select the event in the New function anyhow.
251-
t.eventsDependencies.SubscribeAdd(
250+
eventsDependencies.SubscribeAdd(
252251
dependencies.EventNodeType,
253252
func(node interface{}) []dependencies.Action {
254253
eventNode, ok := node.(*dependencies.EventNode)
@@ -259,7 +258,7 @@ func New(cfg config.Config) (*Tracee, error) {
259258
t.addDependencyEventToState(eventNode.GetID(), eventNode.GetDependents())
260259
return nil
261260
})
262-
t.eventsDependencies.SubscribeRemove(
261+
eventsDependencies.SubscribeRemove(
263262
dependencies.EventNodeType,
264263
func(node interface{}) []dependencies.Action {
265264
eventNode, ok := node.(*dependencies.EventNode)
@@ -353,7 +352,7 @@ func New(cfg config.Config) (*Tracee, error) {
353352
if !events.Core.IsDefined(id) {
354353
return t, errfmt.Errorf("event %d is not defined", id)
355354
}
356-
depsNode, err := t.eventsDependencies.GetEvent(id)
355+
depsNode, err := eventsDependencies.GetEvent(id)
357356
if err == nil {
358357
deps := depsNode.GetDependencies()
359358
evtCaps := deps.GetCapabilities()
@@ -915,7 +914,7 @@ func (t *Tracee) initKsymTableRequiredSyms() error {
915914
return errfmt.Errorf("event %d is not defined", id)
916915
}
917916

918-
depsNode, err := t.eventsDependencies.GetEvent(id)
917+
depsNode, err := dependencies.GetManagerInstance().GetEvent(id)
919918
if err != nil {
920919
logger.Warnw("failed to extract required ksymbols from event", "event_id", id, "error", err)
921920
continue
@@ -1029,8 +1028,10 @@ func getUnavailbaleKsymbols(ksymbols []events.KSymbol, kernelSymbols *environmen
10291028
// from the kallsyms file to check for missing symbols. If some symbols are
10301029
// missing, it will cancel their event with informative error message.
10311030
func (t *Tracee) validateKallsymsDependencies() {
1031+
depsInstance := dependencies.GetManagerInstance()
1032+
10321033
evtDefSymDeps := func(id events.ID) []events.KSymbol {
1033-
depsNode, _ := t.eventsDependencies.GetEvent(id)
1034+
depsNode, _ := depsInstance.GetEvent(id)
10341035
deps := depsNode.GetDependencies()
10351036
return deps.GetKSymbols()
10361037
}
@@ -1059,7 +1060,7 @@ func (t *Tracee) validateKallsymsDependencies() {
10591060
return true
10601061
}
10611062

1062-
t.eventsDependencies.SubscribeAdd(
1063+
depsInstance.SubscribeAdd(
10631064
dependencies.EventNodeType,
10641065
func(node interface{}) []dependencies.Action {
10651066
eventNode, ok := node.(*dependencies.EventNode)
@@ -1076,7 +1077,7 @@ func (t *Tracee) validateKallsymsDependencies() {
10761077
for eventId := range t.eventsState {
10771078
if !validateEvent(eventId) {
10781079
// Cancel the event, its dependencies and its dependent events
1079-
err := t.eventsDependencies.RemoveEvent(eventId)
1080+
err := depsInstance.RemoveEvent(eventId)
10801081
if err != nil {
10811082
logger.Warnw("Failed to remove event from dependencies manager", "remove reason", "missing ksymbols", "error", err)
10821083
}
@@ -1245,7 +1246,7 @@ func (t *Tracee) populateFilterMaps(newPolicies *policy.Policies, updateProcTree
12451246
// Return whether the event was successfully attached or not.
12461247
func (t *Tracee) attachEvent(id events.ID) error {
12471248
evtName := events.Core.GetDefinitionByID(id).GetName()
1248-
depsNode, err := t.eventsDependencies.GetEvent(id)
1249+
depsNode, err := dependencies.GetManagerInstance().GetEvent(id)
12491250
if err != nil {
12501251
logger.Errorw("Missing event in dependencies", "event", evtName)
12511252
return err
@@ -1274,9 +1275,11 @@ func (t *Tracee) attachEvent(id events.ID) error {
12741275

12751276
// attachProbes attaches selected events probes to their respective eBPF programs.
12761277
func (t *Tracee) attachProbes() error {
1278+
depsInstance := dependencies.GetManagerInstance()
1279+
12771280
// Subscribe to all watchers on the dependencies to attach and/or detach
12781281
// probes upon changes
1279-
t.eventsDependencies.SubscribeAdd(
1282+
depsInstance.SubscribeAdd(
12801283
dependencies.ProbeNodeType,
12811284
func(node interface{}) []dependencies.Action {
12821285
probeNode, ok := node.(*dependencies.ProbeNode)
@@ -1291,7 +1294,7 @@ func (t *Tracee) attachProbes() error {
12911294
return nil
12921295
})
12931296

1294-
t.eventsDependencies.SubscribeRemove(
1297+
depsInstance.SubscribeRemove(
12951298
dependencies.ProbeNodeType,
12961299
func(node interface{}) []dependencies.Action {
12971300
probeNode, ok := node.(*dependencies.ProbeNode)
@@ -1312,7 +1315,7 @@ func (t *Tracee) attachProbes() error {
13121315
for eventID := range t.eventsState {
13131316
err := t.attachEvent(eventID)
13141317
if err != nil {
1315-
err := t.eventsDependencies.RemoveEvent(eventID)
1318+
err := depsInstance.RemoveEvent(eventID)
13161319
if err != nil {
13171320
logger.Warnw("Failed to remove event from dependencies manager", "remove reason", "failed probes attachment", "error", err)
13181321
}

pkg/events/dependencies/manager.go

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package dependencies
33
import (
44
"fmt"
55
"reflect"
6+
"sync"
67

78
"github.com/aquasecurity/tracee/pkg/ebpf/probes"
89
"github.com/aquasecurity/tracee/pkg/events"
@@ -18,11 +19,17 @@ const (
1819
IllegalNodeType NodeType = "illegal"
1920
)
2021

22+
var (
23+
managerInstance *Manager
24+
once sync.Once
25+
mu sync.Mutex // workaround mutex to protect the singleton instance in the integration tests
26+
)
27+
2128
// Manager is a management tree for the current dependencies of events.
2229
// As events can depend on multiple things (e.g events, probes), it manages their connections in the form of a tree.
2330
// The tree supports watcher functions for adding and removing nodes.
2431
// The watchers should be used as the way to handle changes in events, probes or any other node type in Tracee.
25-
// The manager is *not* thread-safe.
32+
// The manager is *NOT* thread-safe.
2633
type Manager struct {
2734
events map[events.ID]*EventNode
2835
probes map[probes.Handle]*ProbeNode
@@ -32,13 +39,40 @@ type Manager struct {
3239
}
3340

3441
func NewDependenciesManager(dependenciesGetter func(events.ID) events.Dependencies) *Manager {
35-
return &Manager{
36-
events: make(map[events.ID]*EventNode),
37-
probes: make(map[probes.Handle]*ProbeNode),
38-
onAdd: make(map[NodeType][]func(node interface{}) []Action),
39-
onRemove: make(map[NodeType][]func(node interface{}) []Action),
40-
dependenciesGetter: dependenciesGetter,
42+
mu.Lock()
43+
defer mu.Unlock()
44+
45+
once.Do(func() {
46+
managerInstance = &Manager{
47+
events: make(map[events.ID]*EventNode),
48+
probes: make(map[probes.Handle]*ProbeNode),
49+
onAdd: make(map[NodeType][]func(node interface{}) []Action),
50+
onRemove: make(map[NodeType][]func(node interface{}) []Action),
51+
dependenciesGetter: dependenciesGetter,
52+
}
53+
})
54+
55+
return managerInstance
56+
}
57+
58+
func GetManagerInstance() *Manager {
59+
mu.Lock()
60+
defer mu.Unlock()
61+
62+
if managerInstance == nil {
63+
panic("Manager instance not initialized. Call NewDependenciesManager first.")
4164
}
65+
66+
return managerInstance
67+
}
68+
69+
// ResetManagerFromTests is a workaround for tests to reset the manager instance.
70+
// TODO: remove this when the manager is refactored to be thread-safe.
71+
func ResetManagerFromTests() {
72+
mu.Lock()
73+
defer mu.Unlock()
74+
75+
once = sync.Once{}
4276
}
4377

4478
// SubscribeAdd adds a watcher function called upon the addition of an event to the tree.

pkg/events/dependencies/manager_test.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,11 @@ func TestManager_AddEvent(t *testing.T) {
8181
t.Run(testCase.name, func(t *testing.T) {
8282
// Create a new Manager instance
8383
m := dependencies.NewDependenciesManager(getTestDependenciesFunc(testCase.deps))
84+
defer func() {
85+
dependencies.ResetManagerFromTests()
86+
t.Logf(" --- reset dependencies ---")
87+
}()
88+
8489
var eventsAdditions []events.ID
8590
m.SubscribeAdd(
8691
dependencies.EventNodeType,
@@ -136,6 +141,11 @@ func TestManager_AddEvent(t *testing.T) {
136141
t.Run(testCase.name, func(t *testing.T) {
137142
// Create a new Manager instance
138143
m := dependencies.NewDependenciesManager(getTestDependenciesFunc(testCase.deps))
144+
defer func() {
145+
dependencies.ResetManagerFromTests()
146+
t.Logf(" --- reset dependencies ---")
147+
}()
148+
139149
var eventsAdditions, eventsRemove []events.ID
140150
// Count additions
141151
m.SubscribeAdd(
@@ -331,6 +341,10 @@ func TestManager_RemoveEvent(t *testing.T) {
331341
testCase.name, func(t *testing.T) {
332342
// Create a new Manager instance
333343
m := dependencies.NewDependenciesManager(getTestDependenciesFunc(testCase.deps))
344+
defer func() {
345+
dependencies.ResetManagerFromTests()
346+
t.Logf(" --- reset dependencies ---")
347+
}()
334348

335349
var eventsRemoved []events.ID
336350
m.SubscribeRemove(
@@ -505,6 +519,10 @@ func TestManager_UnselectEvent(t *testing.T) {
505519
testCase.name, func(t *testing.T) {
506520
// Create a new Manager instance
507521
m := dependencies.NewDependenciesManager(getTestDependenciesFunc(testCase.deps))
522+
defer func() {
523+
dependencies.ResetManagerFromTests()
524+
t.Logf(" --- reset dependencies ---")
525+
}()
508526

509527
var eventsRemoved []events.ID
510528
m.SubscribeRemove(

tests/integration/dependencies_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313

1414
"github.com/aquasecurity/tracee/pkg/config"
1515
"github.com/aquasecurity/tracee/pkg/events"
16+
"github.com/aquasecurity/tracee/pkg/events/dependencies"
1617
"github.com/aquasecurity/tracee/pkg/logger"
1718
"github.com/aquasecurity/tracee/pkg/policy"
1819
"github.com/aquasecurity/tracee/tests/testutils"
@@ -147,6 +148,10 @@ func Test_EventsDependencies(t *testing.T) {
147148
cancel()
148149
t.Fatal(err)
149150
}
151+
defer func() {
152+
dependencies.ResetManagerFromTests()
153+
t.Logf(" --- reset dependencies ---")
154+
}()
150155

151156
stream := trc.SubscribeAll()
152157
defer trc.Unsubscribe(stream)

tests/integration/event_filters_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414

1515
"github.com/aquasecurity/tracee/pkg/config"
1616
"github.com/aquasecurity/tracee/pkg/events"
17+
"github.com/aquasecurity/tracee/pkg/events/dependencies"
1718
k8s "github.com/aquasecurity/tracee/pkg/k8s/apis/tracee.aquasec.com/v1beta1"
1819
"github.com/aquasecurity/tracee/pkg/policy"
1920
"github.com/aquasecurity/tracee/pkg/policy/v1beta1"
@@ -1733,6 +1734,10 @@ func Test_EventFilters(t *testing.T) {
17331734
cancel()
17341735
t.Fatal(err)
17351736
}
1737+
defer func() {
1738+
dependencies.ResetManagerFromTests()
1739+
t.Logf(" --- reset dependencies ---")
1740+
}()
17361741

17371742
stream := trc.SubscribeAll()
17381743
defer trc.Unsubscribe(stream)

0 commit comments

Comments
 (0)