[e2e] Add container-based agent-network e2e harness (Pillar 1)

Introduce a self-contained, OIDC-free e2e harness that stands up NetBird
in containers, so suites no longer depend on the hand-maintained Tilt
stack or a real IdP.

- harness brings up the combined server (management + signal + relay +
  STUN + embedded IdP) in a single container built from
  combined/Dockerfile.multistage, and mints an admin PAT through the
  unauthenticated /api/setup bootstrap (NB_SETUP_PAT_ENABLED). API access
  goes through the existing shared/management/client/rest typed client.
- the image is built via the docker CLI (BuildKit) so the Dockerfile's
  cache mounts are honored; testcontainers then runs the tagged image.
- everything is behind the `e2e` build tag so normal builds and unit
  tests never pull in testcontainers.

Adds BuildKit cache mounts to combined/Dockerfile.multistage so source
changes recompile incrementally rather than from scratch.

Pillar 1 proven by TestCombinedBootstrap: server builds, boots, mints a
PAT, and the PAT authenticates a real management API call.
This commit is contained in:
mlsmaycon
2026-06-28 18:39:20 +02:00
parent 6e458d2afe
commit 4a296c5338
9 changed files with 581 additions and 3 deletions
+47
View File
@@ -0,0 +1,47 @@
//go:build e2e
package harness
import (
"context"
"fmt"
"github.com/netbirdio/netbird/shared/management/client/rest"
"github.com/netbirdio/netbird/shared/management/http/api"
)
// Bootstrap creates the initial admin owner through the unauthenticated
// /api/setup endpoint and returns the plaintext admin PAT. It also wires an
// authenticated REST client on the Combined (see API). create_pat requires the
// server to run with NB_SETUP_PAT_ENABLED=true, which the harness sets. A
// second call returns an error (the server reports setup already completed).
func (c *Combined) Bootstrap(ctx context.Context) (string, error) {
// The setup endpoint is unauthenticated; use a tokenless client.
setupClient := rest.NewWithOptions(rest.WithManagementURL(c.BaseURL))
createPAT := true
expireDays := 1
resp, err := setupClient.Instance.Setup(ctx, api.PostApiSetupJSONRequestBody{
Email: "admin@netbird.test",
Password: "Netbird-e2e-Passw0rd!",
Name: "E2E Admin",
CreatePat: &createPAT,
PatExpireIn: &expireDays,
})
if err != nil {
return "", fmt.Errorf("instance setup: %w", err)
}
if resp.PersonalAccessToken == nil || *resp.PersonalAccessToken == "" {
return "", fmt.Errorf("setup succeeded but no PAT returned (is NB_SETUP_PAT_ENABLED set?)")
}
c.PAT = *resp.PersonalAccessToken
c.api = rest.New(c.BaseURL, c.PAT)
return c.PAT, nil
}
// API returns the PAT-authenticated management REST client. It is nil until
// Bootstrap runs.
func (c *Combined) API() *rest.Client {
return c.api
}