Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 54 additions & 1 deletion .github/workflows/nodeagent-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
test:
needs: changes
if: needs.changes.outputs.relevant == 'true'
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
timeout-minutes: 20
steps:
- uses: actions/checkout@v6
Expand All @@ -37,6 +37,12 @@ jobs:
- name: Check formatting
working-directory: components/nodeagent
run: make fmt-check
- name: Verify syscall BPF bytecode
working-directory: components/nodeagent
run: |
sudo apt-get update
sudo apt-get install -y clang-18 libbpf-dev=1:1.3.0-2build2
make verify-syscalls-bpf CLANG=clang-18
- name: Vet, test, and build
working-directory: components/nodeagent
run: |
Expand All @@ -56,6 +62,35 @@ jobs:
helm template nodeagent kubernetes/charts/opensandbox-node-agent \
--set 'config.sources={source-a,source-b}' \
> /tmp/nodeagent-non-log-sources.yaml
helm template nodeagent kubernetes/charts/opensandbox-node-agent \
--set 'config.sources={syscalls}' \
> /tmp/nodeagent-syscalls.yaml
grep -A8 'capabilities:' /tmp/nodeagent-syscalls.yaml | grep -q -- '- BPF'
grep -A8 'capabilities:' /tmp/nodeagent-syscalls.yaml | grep -q -- '- PERFMON'
grep -A1 'name: NODEAGENT_SYSCALL_CGROUP_ROOT' /tmp/nodeagent-syscalls.yaml \
| grep -q 'value: /host/sys/fs/cgroup'
grep -A2 'mountPath: /host/sys/fs/cgroup' /tmp/nodeagent-syscalls.yaml | grep -q 'readOnly: true'
grep -A2 'mountPath: /sys/kernel/tracing' /tmp/nodeagent-syscalls.yaml | grep -q 'readOnly: true'
grep -q 'name: host-cgroup' /tmp/nodeagent-syscalls.yaml
grep -q 'name: host-tracing' /tmp/nodeagent-syscalls.yaml
grep -q 'path: "/sys/fs/cgroup"' /tmp/nodeagent-syscalls.yaml
grep -q 'path: "/sys/kernel/tracing"' /tmp/nodeagent-syscalls.yaml
helm template nodeagent kubernetes/charts/opensandbox-node-agent \
--set 'config.sources={syscalls}' \
--set containerSecurityContext=null \
> /tmp/nodeagent-syscalls-no-secctx.yaml
grep -A8 'capabilities:' /tmp/nodeagent-syscalls-no-secctx.yaml | grep -q -- '- BPF'
grep -A8 'capabilities:' /tmp/nodeagent-syscalls-no-secctx.yaml | grep -q -- '- PERFMON'
if grep -Eq -- '- (BPF|PERFMON)' /tmp/nodeagent-default-sources.yaml; then
echo "eBPF capabilities rendered while the syscalls Source is disabled" >&2
exit 1
fi
for nodeagent_syscall_marker in NODEAGENT_SYSCALL_CGROUP_ROOT 'name: host-cgroup' 'name: host-tracing'; do
if grep -q "${nodeagent_syscall_marker}" /tmp/nodeagent-default-sources.yaml; then
echo "syscalls host access rendered while the Source is disabled: ${nodeagent_syscall_marker}" >&2
exit 1
fi
done
helm template nodeagent kubernetes/charts/opensandbox-node-agent \
--set 'config.sources={container-logs,source-a}' \
> /tmp/nodeagent-mixed-sources.yaml
Expand Down Expand Up @@ -98,6 +133,24 @@ jobs:
echo "container-logs configuration failed for an unexpected reason: $nodeagent_source_schema_error" >&2
exit 1
fi
for nodeagent_syscall_host_path in cgroup tracing; do
nodeagent_source_schema_error=""
if nodeagent_source_schema_error="$(helm template nodeagent kubernetes/charts/opensandbox-node-agent \
--set 'config.sources={syscalls}' \
--set "hostPaths.${nodeagent_syscall_host_path}=null" 2>&1)"; then
echo "syscalls configuration without hostPaths.${nodeagent_syscall_host_path} passed schema validation" >&2
exit 1
fi
if ! grep -Eiq "(don't meet the specifications of the schema|values don't meet)" <<<"$nodeagent_source_schema_error" \
|| ! grep -Eiq "hostPaths.*${nodeagent_syscall_host_path}|missing property.*${nodeagent_syscall_host_path}" <<<"$nodeagent_source_schema_error"; then
echo "syscalls configuration failed for an unexpected reason: $nodeagent_source_schema_error" >&2
exit 1
fi
done
helm template nodeagent kubernetes/charts/opensandbox-node-agent \
--set 'config.sources={source-a,source-b}' \
--set hostPaths.cgroup=null \
--set hostPaths.tracing=null >/dev/null
for nodeagent_required_setting in maxLineBytes partialTimeout endedStateRetention; do
nodeagent_source_schema_error=""
if nodeagent_source_schema_error="$(helm template nodeagent kubernetes/charts/opensandbox-node-agent \
Expand Down
20 changes: 20 additions & 0 deletions components/nodeagent/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo dev)
GIT_COMMIT ?= $(shell git rev-parse HEAD 2>/dev/null || echo unknown)
BUILD_TIME ?= $(shell if [ -n "$$SOURCE_DATE_EPOCH" ]; then date -u -d "@$$SOURCE_DATE_EPOCH" +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || date -u -r "$$SOURCE_DATE_EPOCH" +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null; else date -u +"%Y-%m-%dT%H:%M:%SZ"; fi)
GO ?= go
CLANG_DEFAULT := $(shell command -v clang-18 2>/dev/null || command -v clang 2>/dev/null)
CLANG ?= $(CLANG_DEFAULT)
GOFMT_DEFAULT := $(shell command -v "$$($(GO) env GOROOT 2>/dev/null)/bin/gofmt" 2>/dev/null || command -v gofmt 2>/dev/null)
GOFMT ?= $(GOFMT_DEFAULT)
PROJECT_GOFLAGS := -trimpath -buildvcs=false
Expand Down Expand Up @@ -42,6 +44,24 @@ build:
CGO_ENABLED=0 $(GO) build $(GO_BUILD_FLAGS) -ldflags "$(GO_LDFLAGS)" -o bin/nodeagent .
CGO_ENABLED=0 $(GO) build $(GO_BUILD_FLAGS) -ldflags "$(GO_LDFLAGS)" -o bin/nodeagent-oss-cleanup ./cmd/oss-cleanup

.PHONY: generate-syscalls-bpf
generate-syscalls-bpf:
@case "$$(uname -s 2>/dev/null)" in Linux) ;; *) echo "syscall BPF generation requires Linux" >&2; exit 1;; esac
@case "$$(uname -m 2>/dev/null)" in x86_64) ;; *) echo "syscall BPF generation requires Linux x86_64" >&2; exit 1;; esac
@command -v "$(CLANG)" >/dev/null 2>&1 || { echo "clang not found: $(CLANG)" >&2; exit 1; }
@"$(CLANG)" --version | head -n 1 | grep -Eq 'clang version 18\.' || { echo "syscall BPF generation requires clang 18: $(CLANG)" >&2; exit 1; }
$(CLANG) -target bpfel -O2 -g -fdebug-prefix-map="$(CURDIR)"=. \
-I/usr/include/$$(uname -m)-linux-gnu \
-c pkg/source/syscalls/bpf/syscalls.bpf.c \
-o "$(or $(BPF_OUTPUT),pkg/source/syscalls/syscalls_bpfel.o)"

.PHONY: verify-syscalls-bpf
verify-syscalls-bpf:
@set -e; output="$$(mktemp)"; trap 'rm -f "$$output"' EXIT; \
$(MAKE) --no-print-directory generate-syscalls-bpf BPF_OUTPUT="$$output"; \
cmp pkg/source/syscalls/syscalls_bpfel.o "$$output" \
|| { echo "pkg/source/syscalls/syscalls_bpfel.o is stale; run 'make generate-syscalls-bpf'" >&2; exit 1; }

.PHONY: check
check: fmt-check vet test integration-compile build

Expand Down
25 changes: 23 additions & 2 deletions components/nodeagent/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

Node Agent runs once per Linux Kubernetes node. It merges one or more Sources
into a common pipeline, preserves order within each stream, and writes sandbox
records to a file or Alibaba Cloud OSS Sink. The stock binary currently ships
the `container-logs` Source for CRI stdout/stderr.
records to a file or Alibaba Cloud OSS Sink. The stock binary ships the
`container-logs` Source for CRI stdout/stderr and the opt-in `syscalls` Source
for cgroup-scoped Linux system-call records.

## Status

Expand All @@ -22,6 +23,26 @@ binary and defaults to `container-logs`. Every Source owns its StreamRef
namespace and private state handle. Each emitted RecordKind has a registered
storage format that defines its encoding and object layout.

The `syscalls` Source attaches one eBPF program to
`raw_syscalls/sys_enter`, filters events by sandbox-container cgroup, and emits
NDJSON. It requires Linux kernel 5.11 or newer, cgroup v2, tracefs, and the `BPF` and `PERFMON`
capabilities. The Helm chart adds these mounts and capabilities only when
`syscalls` is enabled. The Source persists active stream identity and outcome
metadata so a restart can reattach a live container or finalize a stream whose
Pod disappeared while the Agent was down. It does not persist eBPF event
payloads, so the restart interval remains an unobservable gap and is reported
as `syscall-agent-restart`. Its bounded Source data queue continues processing
lifecycle events under output backpressure and reports discarded events as
`syscall-source-backpressure`. It also attaches only after Kubernetes reports
the container ID, so its finalization marker is `incomplete` with
`syscall-attach-after-container-start` rather than claiming full-lifecycle
coverage. The filter tracks the runtime's container cgroup itself; processes
moved into delegated descendant cgroups are outside this first implementation.

The checked-in eBPF object is generated on Linux x86-64 with clang 18 and the
Ubuntu 24.04 libbpf headers. Run `make generate-syscalls-bpf CLANG=clang-18`
in this directory after changing the BPF C source.

Every Source also receives an isolated view of the node-local sandbox Pod
store. It must call `Store.Forget` after it no longer needs a terminated Pod;
the Store keeps that identity until every enabled Source has released it.
Expand Down
2 changes: 2 additions & 0 deletions components/nodeagent/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.25.0
require (
github.com/alibaba/opensandbox/internal v0.0.0
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible
github.com/cilium/ebpf v0.16.0
github.com/fsnotify/fsnotify v1.10.1
github.com/google/uuid v1.6.0
go.etcd.io/bbolt v1.5.0
Expand Down Expand Up @@ -51,6 +52,7 @@ require (
go.uber.org/zap v1.27.1 // indirect
go.yaml.in/yaml/v2 v2.4.2 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 // indirect
golang.org/x/net v0.55.0 // indirect
golang.org/x/oauth2 v0.36.0 // indirect
golang.org/x/term v0.43.0 // indirect
Expand Down
14 changes: 14 additions & 0 deletions components/nodeagent/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1x
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cilium/ebpf v0.16.0 h1:+BiEnHL6Z7lXnlGUsXQPPAE7+kenAd4ES8MQ5min0Ok=
github.com/cilium/ebpf v0.16.0/go.mod h1:L7u2Blt2jMM/vLAVgjxluxtBKlz3/GWjB0dMOEngfwE=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
Expand All @@ -27,6 +29,8 @@ github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
Expand All @@ -46,6 +50,10 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/jsimonetti/rtnetlink/v2 v2.0.1 h1:xda7qaHDSVOsADNouv7ukSuicKZO7GgVUCXxpaIEIlM=
github.com/jsimonetti/rtnetlink/v2 v2.0.1/go.mod h1:7MoNYNbb3UaDHtF8udiJo/RH6VsTKP1pqKLUTVCvToE=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
Expand All @@ -59,6 +67,10 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=
github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw=
github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U=
github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
Expand Down Expand Up @@ -125,6 +137,8 @@ go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 h1:Jvc7gsqn21cJHCmAWx0LiimpP18LZmUxkT5Mp7EZ1mI=
golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
Expand Down
1 change: 1 addition & 0 deletions components/nodeagent/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import (
_ "github.com/alibaba/opensandbox/nodeagent/pkg/sink/oss"
sourcegroup "github.com/alibaba/opensandbox/nodeagent/pkg/source"
_ "github.com/alibaba/opensandbox/nodeagent/pkg/source/containerlogs"
_ "github.com/alibaba/opensandbox/nodeagent/pkg/source/syscalls"
"github.com/alibaba/opensandbox/nodeagent/pkg/state"
"github.com/alibaba/opensandbox/nodeagent/pkg/store"
"k8s.io/client-go/kubernetes"
Expand Down
2 changes: 2 additions & 0 deletions components/nodeagent/pkg/api/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ type RecordKind string

const (
SourceNameContainerLogs = "container-logs"
SourceNameSyscalls = "syscalls"
RecordKindContainerLog RecordKind = "container-log"
RecordKindSyscall RecordKind = "syscall"
)

type Capabilities struct {
Expand Down
12 changes: 11 additions & 1 deletion components/nodeagent/pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ type Config struct {
Sources []string
Sink string
LogRoot string
SyscallCgroupRoot string
StateDir string
StateMaxBytes int64
FilePath string
Expand Down Expand Up @@ -86,6 +87,7 @@ func Load() (Config, error) {
Sources: parseSources(envDefault("NODEAGENT_SOURCES", api.SourceNameContainerLogs), &errs),
Sink: envDefault("NODEAGENT_SINKS", SinkFile),
LogRoot: envDefault("NODEAGENT_LOG_ROOT", "/var/log/pods"),
SyscallCgroupRoot: envDefault("NODEAGENT_SYSCALL_CGROUP_ROOT", "/host/sys/fs/cgroup"),
StateDir: envDefault("NODEAGENT_STATE_DIR", "/var/lib/opensandbox/nodeagent"),
FilePath: strings.TrimSpace(os.Getenv("NODEAGENT_FILE_PATH")),
OSSEndpoint: strings.TrimSpace(os.Getenv("NODEAGENT_OSS_ENDPOINT")),
Expand Down Expand Up @@ -154,13 +156,21 @@ func (c Config) validate() []error {
errs = append(errs, errors.New("NODEAGENT_STATE_DIR must not overlap NODEAGENT_LOG_ROOT"))
}
}
if c.HasSource(api.SourceNameSyscalls) {
if err := validateAbsolutePath(c.SyscallCgroupRoot); err != nil {
errs = append(errs, fmt.Errorf("NODEAGENT_SYSCALL_CGROUP_ROOT: %w", err))
}
if pathsOverlap(c.StateDir, c.SyscallCgroupRoot) {
errs = append(errs, errors.New("NODEAGENT_STATE_DIR must not overlap NODEAGENT_SYSCALL_CGROUP_ROOT"))
}
}
switch c.Sink {
case SinkFile:
if c.FilePath != "" {
if err := validateAbsolutePath(c.FilePath); err != nil {
errs = append(errs, fmt.Errorf("NODEAGENT_FILE_PATH: %w", err))
}
if pathsOverlap(c.FilePath, c.StateDir) || containerLogsEnabled && pathsOverlap(c.FilePath, c.LogRoot) {
if pathsOverlap(c.FilePath, c.StateDir) || containerLogsEnabled && pathsOverlap(c.FilePath, c.LogRoot) || c.HasSource(api.SourceNameSyscalls) && pathsOverlap(c.FilePath, c.SyscallCgroupRoot) {
errs = append(errs, errors.New("NODEAGENT_FILE_PATH must not overlap active state or source paths"))
}
if c.FileMaxTotalBytes < c.FileMaxBytes {
Expand Down
28 changes: 28 additions & 0 deletions components/nodeagent/pkg/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ package config
import (
"strings"
"testing"

"github.com/alibaba/opensandbox/nodeagent/pkg/api"
)

func TestLoadFileConfig(t *testing.T) {
Expand Down Expand Up @@ -86,6 +88,32 @@ func TestLoadDoesNotValidateDisabledContainerLogSettings(t *testing.T) {
}
}

func TestLoadValidatesOnlyEnabledSyscallSettings(t *testing.T) {
t.Setenv("NODE_NAME", "node-1")
t.Setenv("NODEAGENT_CLUSTER_ID", "prod-a")
t.Setenv("NODEAGENT_SOURCES", "custom-source")
t.Setenv("NODEAGENT_STATE_DIR", t.TempDir())
t.Setenv("NODEAGENT_SYSCALL_CGROUP_ROOT", "relative")
if _, err := Load(); err != nil {
t.Fatalf("Load() validated a disabled syscalls Source: %v", err)
}

t.Setenv("NODEAGENT_SOURCES", api.SourceNameSyscalls)
if _, err := Load(); err == nil || !strings.Contains(err.Error(), "NODEAGENT_SYSCALL_CGROUP_ROOT") {
t.Fatalf("Load() error = %v, want syscall cgroup root error", err)
}
}

func TestValidateRejectsFileSinkInsideSyscallCgroupRoot(t *testing.T) {
cfg := validConfig()
cfg.Sources = []string{api.SourceNameSyscalls}
cfg.SyscallCgroupRoot = "/host/sys/fs/cgroup"
cfg.FilePath = "/host/sys/fs/cgroup/output"
if err := errorsContaining(cfg.validate(), "must not overlap active state or source paths"); err == "" {
t.Fatal("validate() accepted a file sink inside the cgroup source root")
}
}

func TestLoadRejectsInvalidIdentityAndBudget(t *testing.T) {
t.Setenv("NODE_NAME", "node-1")
t.Setenv("NODEAGENT_CLUSTER_ID", "INVALID")
Expand Down
Loading
Loading