Skip to content

Commit

Permalink
tests: write unit tests for in_init_tree
Browse files Browse the repository at this point in the history
Write a unit test for in_init_tree. The test makes sure that processes descending from the
entrypoint are in_init_tree and that a docker exec'd process is not in_init_tree.

Signed-off-by: William Findlay <[email protected]>
  • Loading branch information
will-isovalent committed Dec 10, 2024
1 parent 9c4ff53 commit 0d103e6
Show file tree
Hide file tree
Showing 2 changed files with 129 additions and 0 deletions.
49 changes: 49 additions & 0 deletions pkg/observer/observertesthelper/observer_test_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,15 @@ func WithMyPid() TestOption {
}
}

// Filter for a container id in event export
func WithContainerId(id string) TestOption {
return func(o *TestOptions) {
o.exporter.allowList = append(o.exporter.allowList, &tetragon.Filter{
ContainerId: []string{id},
})
}
}

func WithAllowList(allowList *tetragon.Filter) TestOption {
return func(o *TestOptions) {
o.exporter.allowList = append(o.exporter.allowList, allowList)
Expand Down Expand Up @@ -511,6 +520,37 @@ func ExecWGCurl(readyWG *sync.WaitGroup, retries uint, args ...string) error {
return err
}

// DockerCreate creates a new docker container in the background. The container will
// be killed and removed on test cleanup.
// It returns the containerId on success, or an error if spawning the container failed.
func DockerCreate(tb testing.TB, args ...string) (containerId string) {
// note: we are not using `--rm` so we can choose to wait on the container
// with `docker wait`. We remove it manually below in t.Cleanup instead
args = append([]string{"create"}, args...)
id, err := exec.Command("docker", args...).Output()
if err != nil {
tb.Fatalf("failed to spawn docker container %v: %s", args, err)
}

containerId = strings.TrimSpace(string(id))
tb.Cleanup(func() {
err := exec.Command("docker", "rm", "--force", containerId).Run()
if err != nil {
tb.Logf("failed to remove container %s: %s", containerId, err)
}
})

return containerId
}

// DockerStart starts a new docker container with a given ID.
func DockerStart(tb testing.TB, id string) {
err := exec.Command("docker", "start", id).Run()
if err != nil {
tb.Fatalf("failed to start docker container %s: %s", id, err)
}
}

// dockerRun starts a new docker container in the background. The container will
// be killed and removed on test cleanup.
// It returns the containerId on success, or an error if spawning the container failed.
Expand All @@ -534,6 +574,15 @@ func DockerRun(tb testing.TB, args ...string) (containerId string) {
return containerId
}

// dockerExec executes a command in a container.
func DockerExec(tb testing.TB, id string, args ...string) {
args = append([]string{"exec", id}, args...)
err := exec.Command("docker", args...).Run()
if err != nil {
tb.Fatalf("failed to exec in docker container %v: %s", args, err)
}
}

type fakeK8sWatcher struct {
fakePod, fakeNamespace string
}
Expand Down
80 changes: 80 additions & 0 deletions pkg/sensors/exec/exec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,86 @@ func TestDocker(t *testing.T) {
assert.NoError(t, err)
}

func TestInInitTree(t *testing.T) {
if err := exec.Command("docker", "version").Run(); err != nil {
t.Skipf("docker not available. skipping test: %s", err)
}

var doneWG, readyWG sync.WaitGroup
defer doneWG.Wait()

ctx, cancel := context.WithTimeout(context.Background(), tus.Conf().CmdWaitTime)
defer cancel()

containerID := observertesthelper.DockerCreate(t, "--name", "in-init-tree-test", "bash", "bash", "-c", "sleep 1 & sleep infinity")
// Tetragon sends 31 bytes + \0 to user-space. Since it might have an arbitrary prefix,
// match only on the first 24 bytes.
trimmedContainerID := sm.Prefix(containerID[:24])

obs, err := observertesthelper.GetDefaultObserver(t, ctx, tus.Conf().TetragonLib, observertesthelper.WithContainerId(containerID[:24]))
if err != nil {
t.Fatalf("GetDefaultObserver error: %s", err)
}

observertesthelper.LoopEvents(ctx, t, &doneWG, &readyWG, obs)

readyWG.Wait()
observertesthelper.DockerStart(t, "in-init-tree-test")
time.Sleep(1 * time.Second)
observertesthelper.DockerExec(t, "in-init-tree-test", "ls")

// This is the initial cmd, so inInitTree should be true
entrypointChecker := ec.NewProcessChecker().
WithBinary(sm.Suffix("/docker-entrypoint.sh")).
WithCwd(sm.Full("/")).
WithUid(0).
WithDocker(trimmedContainerID).
WithInInitTree(true)

// This is forked from the initial cmd, so inInitTree should be true
bashChecker := ec.NewProcessChecker().
WithBinary(sm.Suffix("/bash")).
WithCwd(sm.Full("/")).
WithUid(0).
WithDocker(trimmedContainerID).
WithInInitTree(true)

// This is forked from the initial cmd, so inInitTree should be true
sleepChecker := ec.NewProcessChecker().
WithBinary(sm.Suffix("/sleep")).
WithArguments(sm.Full("infinity")).
WithCwd(sm.Full("/")).
WithUid(0).
WithDocker(trimmedContainerID).
WithInInitTree(true)

// This is run via docker exec, so inInitTree should be false
lsChecker := ec.NewProcessChecker().
WithBinary(sm.Suffix("/ls")).
WithCwd(sm.Full("/")).
WithUid(0).
WithDocker(trimmedContainerID).
WithInInitTree(false)

checker := ec.NewUnorderedEventChecker(
ec.NewProcessExecChecker("entrypoint").
WithProcess(entrypointChecker).
WithParent(ec.NewProcessChecker().WithInInitTree(false)),
ec.NewProcessExecChecker("bash").
WithProcess(bashChecker).
WithParent(entrypointChecker),
ec.NewProcessExecChecker("sleep").
WithProcess(sleepChecker).
WithParent(bashChecker),
ec.NewProcessExecChecker("ls").
WithProcess(lsChecker).
WithParent(ec.NewProcessChecker().WithInInitTree(false)),
)

err = jsonchecker.JsonTestCheck(t, checker)
assert.NoError(t, err)
}

func TestUpdateStatsMap(t *testing.T) {
m, err := ebpf.NewMap(&ebpf.MapSpec{
Type: ebpf.PerCPUArray,
Expand Down

0 comments on commit 0d103e6

Please sign in to comment.