import { join } from "node:path"; import { fileURLToPath } from "node:url"; import tc from "testcontainers"; export interface HeadscaleEnv { container: tc.StartedTestContainer; apiUrl: string; apiKey: string; } export interface HeadscaleOptions { labels?: Record; } const cwd = fileURLToPath(import.meta.url); const config = join(cwd, "..", "config.yaml"); export async function startHeadscale( version: string, options?: HeadscaleOptions, ): Promise { let builder = new tc.GenericContainer(`headscale/headscale:${version}`) .withExposedPorts(8080) .withWaitStrategy( tc.Wait.forHttp("/health", 8080).withStartupTimeout(30_000).forStatusCode(200), ) .withCopyFilesToContainer([ { source: config, target: "/etc/headscale/config.yaml", }, ]) .withCommand(["serve"]); if (options?.labels) { builder = builder.withLabels(options.labels); } const container = await builder.start(); const host = container.getHost(); const port = container.getMappedPort(8080); const apiUrl = `http://${host}:${port}`; const exec = await container.exec(["headscale", "apikeys", "create", "-o", "json"]); if (exec.exitCode !== 0) { throw new Error(`headscale apikeys create failed:\n${exec.stderr}`); } const apiKey = JSON.parse(exec.stdout.toString()); return { container, apiUrl, apiKey }; }