From 47690ce24ec7a90a4fdf32dab913fef9aa602b7f Mon Sep 17 00:00:00 2001 From: Kornilios Kourtis Date: Tue, 4 Jun 2024 14:27:07 +0200 Subject: [PATCH] sensors: test intermediate states ({,un}loading) This patch tests the intermediate states that were introduced in the previous patches. Specifically, it tests whether ListTracingPolicies call observes loading/unloading states for the corresponding sensors. It does so by a TestDelayedSensor that implements the SensorIface interface. Signed-off-by: Kornilios Kourtis --- pkg/sensors/delayed_sensor_test.go | 70 +++++++++++++++++++ pkg/sensors/manager_test.go | 105 +++++++++++++++++++++++++++++ 2 files changed, 175 insertions(+) create mode 100644 pkg/sensors/delayed_sensor_test.go diff --git a/pkg/sensors/delayed_sensor_test.go b/pkg/sensors/delayed_sensor_test.go new file mode 100644 index 00000000000..375ff0709ff --- /dev/null +++ b/pkg/sensors/delayed_sensor_test.go @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Authors of Tetragon + +package sensors + +import ( + "fmt" + "testing" + "time" +) + +// A sensor to test the intermediate policy states (loading / unloading) + +func makeTestDelayedSensor(t *testing.T) *TestDelayedSensor { + s := &TestDelayedSensor{ + name: "test-delayed-sensor", + loaded: false, + ch: make(chan struct{}), + } + RegisterPolicyHandlerAtInit("dummy-policyhandler", &dummyHandler{s: s}) + t.Cleanup(func() { + delete(registeredPolicyHandlers, "dummy-policyhandler") + }) + + return s +} + +type TestDelayedSensor struct { + name string + loaded bool + ch chan struct{} +} + +func (tds *TestDelayedSensor) GetName() string { + return tds.name +} + +func (tds *TestDelayedSensor) IsLoaded() bool { + return tds.loaded +} + +func (tds *TestDelayedSensor) Load(_ string) error { + select { + case <-tds.ch: + + case <-time.After(1 * time.Second): + return fmt.Errorf("TestDelayedSensor/Load timeout when waiting for unblocking") + } + tds.loaded = true + return nil +} + +func (tds *TestDelayedSensor) Unload() error { + select { + case <-tds.ch: + + case <-time.After(1 * time.Second): + return fmt.Errorf("TestDelayedSensor/Unload timeout when waiting for unblocking") + } + tds.loaded = false + return nil +} + +func (tds *TestDelayedSensor) Destroy() { + tds.loaded = false +} + +func (tds *TestDelayedSensor) unblock(_ *testing.T) { + tds.ch <- struct{}{} +} diff --git a/pkg/sensors/manager_test.go b/pkg/sensors/manager_test.go index cc6e1979623..30f413694d0 100644 --- a/pkg/sensors/manager_test.go +++ b/pkg/sensors/manager_test.go @@ -10,6 +10,7 @@ import ( "testing" "time" + "github.com/cilium/tetragon/api/v1/tetragon" "github.com/cilium/tetragon/pkg/k8s/apis/cilium.io/v1alpha1" "github.com/cilium/tetragon/pkg/policyfilter" "github.com/cilium/tetragon/pkg/sensors/program" @@ -304,3 +305,107 @@ func TestPolicyLoadErrorOverride(t *testing.T) { assert.Len(t, l.Policies, 1) assert.Equal(t, EnabledState.ToTetragonState(), l.Policies[0].State) } + +func TestPolicyListingWhileLoadUnload(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + polName := "test-policy" + testSensor := makeTestDelayedSensor(t) + + mgr, err := StartSensorManager("", nil) + require.NoError(t, err) + t.Cleanup(func() { + if err := mgr.StopSensorManager(ctx); err != nil { + panic("failed to stop sensor manager") + } + }) + + checkPolicy := func(t *testing.T, statuses []*tetragon.TracingPolicyStatus, state tetragon.TracingPolicyState) { + require.Equal(t, 1, len(statuses)) + pol := statuses[0] + require.Equal(t, pol.Name, polName) + require.Equal(t, pol.State, state) + } + + go func() { + // wait until at least one policy shows up, verify that it's in loading state and + // unblock the loading of the policy + for { + l, err := mgr.ListTracingPolicies(ctx) + require.NoError(t, err) + if len(l.Policies) > 0 { + checkPolicy(t, l.Policies, tetragon.TracingPolicyState_TP_STATE_LOADING) + testSensor.unblock(t) + break + } + time.Sleep(1 * time.Millisecond) + } + }() + + t.Log("adding policy") + policy := v1alpha1.TracingPolicy{} + policy.ObjectMeta.Name = polName + err = mgr.AddTracingPolicy(ctx, &policy) + require.NoError(t, err) + + // check that policy is now enabled + l, err := mgr.ListTracingPolicies(ctx) + require.NoError(t, err) + checkPolicy(t, l.Policies, tetragon.TracingPolicyState_TP_STATE_ENABLED) + + go func() { + // wait until at least one policy shows up, verify that it's in unloading state and + // unblock the unloading of the policy + for { + l, err := mgr.ListTracingPolicies(ctx) + require.NoError(t, err) + require.Equal(t, len(l.Policies), 1) + if l.Policies[0].State == tetragon.TracingPolicyState_TP_STATE_UNLOADING { + testSensor.unblock(t) + break + } + time.Sleep(1 * time.Millisecond) + } + }() + + t.Log("disabling policy") + err = mgr.DisableTracingPolicy(ctx, polName, "") + require.NoError(t, err) + + // check that policy is now diabled + l, err = mgr.ListTracingPolicies(ctx) + require.NoError(t, err) + checkPolicy(t, l.Policies, tetragon.TracingPolicyState_TP_STATE_DISABLED) + + go func() { + // wait until at least one policy shows up, verify that it's in loading state and + // unblock the loading of the policy + for { + l, err := mgr.ListTracingPolicies(ctx) + require.NoError(t, err) + require.Equal(t, len(l.Policies), 1) + if l.Policies[0].State == tetragon.TracingPolicyState_TP_STATE_LOADING { + testSensor.unblock(t) + break + } + time.Sleep(1 * time.Millisecond) + } + }() + + t.Log("re-enabling policy") + err = mgr.EnableTracingPolicy(ctx, polName, "") + require.NoError(t, err) + + // check that policy is now diabled + l, err = mgr.ListTracingPolicies(ctx) + require.NoError(t, err) + checkPolicy(t, l.Policies, tetragon.TracingPolicyState_TP_STATE_ENABLED) + + t.Log("deleting policy") + err = mgr.DeleteTracingPolicy(ctx, polName, "") + require.NoError(t, err) + l, err = mgr.ListTracingPolicies(ctx) + require.NoError(t, err) + require.Equal(t, 0, len(l.Policies)) +}