From 569b9aa169c01ddb89885f188811da2f21f085d0 Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Tue, 20 Oct 2026 14:21:05 +0200 Subject: [PATCH] [misc] Let suites outside the module reach the e2e harness network (#8260) The harness hands peers the management address netbird.local:8080, which only resolves on the deployment's own Docker network, and keeps that network, the client image and the client container private. A suite that runs agents somewhere else, such as the Kubernetes operator's Kind cluster, has no way to attach to the deployment, run the same client build, or check routed access from a harness peer. Expose the network name, resolve the client image on its own, and let a caller exec into a client container. --- e2e/harness/client.go | 34 +++++++++++++++++++++++++++++----- e2e/harness/combined.go | 8 ++++++++ 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/e2e/harness/client.go b/e2e/harness/client.go index 9843c25b2..da27d3bc5 100644 --- a/e2e/harness/client.go +++ b/e2e/harness/client.go @@ -65,11 +65,7 @@ func StartClient(ctx context.Context, c *Combined, setupKey string, opts ...Clie opt(&o) } - root, err := repoRoot(ctx) - if err != nil { - return nil, err - } - clientImage, err := resolveImage(ctx, root, "NB_E2E_CLIENT_IMAGE", defaultClientImage, clientDockerfile) + clientImage, err := ClientImage(ctx) if err != nil { return nil, err } @@ -105,6 +101,34 @@ func StartClient(ctx context.Context, c *Combined, setupKey string, opts ...Clie return &Client{container: ctr, name: o.name}, nil } +// ClientImage returns the client image StartClient runs, building it from this +// module's source unless NB_E2E_CLIENT_IMAGE names a published one. A suite that +// runs the agent outside a harness container, such as in a Kubernetes cluster, +// uses it to get the same build. +func ClientImage(ctx context.Context) (string, error) { + root, err := repoRoot(ctx) + if err != nil { + return "", err + } + return resolveImage(ctx, root, "NB_E2E_CLIENT_IMAGE", defaultClientImage, clientDockerfile) +} + +// Exec runs a command inside the client container and returns its exit code and +// combined output. It reaches whatever the agent routes, so a suite can check +// access the request helpers here do not model, such as plain HTTP to a routed +// address. +func (cl *Client) Exec(ctx context.Context, cmd ...string) (int, string, error) { + code, reader, err := cl.container.Exec(ctx, cmd, tcexec.Multiplexed()) + if err != nil { + return 0, "", fmt.Errorf("exec %v: %w", cmd, err) + } + out, err := io.ReadAll(reader) + if err != nil { + return code, string(out), fmt.Errorf("read output of %v: %w", cmd, err) + } + return code, string(out), nil +} + // Hostname returns the container hostname the agent reports to management — // the name the registered peer appears under in the peers API. func (cl *Client) Hostname() string { diff --git a/e2e/harness/combined.go b/e2e/harness/combined.go index ea451e3ad..d08db8bf9 100644 --- a/e2e/harness/combined.go +++ b/e2e/harness/combined.go @@ -332,6 +332,14 @@ func (c *Combined) Restart(ctx context.Context) error { return nil } +// NetworkName returns the Docker network the deployment runs on. Peers reach +// management, signal and relay at the combined server's alias on this network +// only, so a suite running agents outside the harness, such as in a Kind +// cluster, attaches them here. +func (c *Combined) NetworkName() string { + return c.network.Name +} + // Logs returns the combined server container logs, for diagnostics. func (c *Combined) Logs(ctx context.Context) string { return containerLogs(ctx, c.container)