fix: handle registration keys on headscale 0.29+

Closes HP-555.
This commit is contained in:
Aarnav Tale
2026-07-04 00:07:53 -04:00
parent 990e1e9ff5
commit 948cfad58c
11 changed files with 206 additions and 15 deletions
+49 -5
View File
@@ -1,19 +1,63 @@
import { describe, expect, test } from "vitest";
import { RouterContextProvider } from "react-router";
import { describe, expect, test, vi } from "vitest";
import { machineAction } from "~/routes/machines/machine-actions";
import { authContext, headscaleLiveStoreContext, requestApiContext } from "~/server/context";
import { Capabilities } from "~/server/web/roles";
import { getBootstrapClient, getNode, getRuntimeClient, HS_VERSIONS } from "../setup/env";
function registerRequest(registerKey: string) {
const form = new FormData();
form.set("action_id", "register");
form.set("register_key", registerKey);
form.set("user", "node-reg@");
return new Request("http://headplane.test/machines", {
method: "POST",
body: form,
});
}
function actionContext(api: Awaited<ReturnType<typeof getRuntimeClient>>) {
const auth = { can: vi.fn(() => true) };
const liveStore = { refresh: vi.fn() };
const principal = { id: "integration-user" };
const context = new RouterContextProvider();
context.set(authContext, auth as never);
context.set(headscaleLiveStoreContext, liveStore as never);
context.set(requestApiContext, vi.fn(async () => ({ principal, api })) as never);
return { auth, context, liveStore };
}
describe.sequential.for(HS_VERSIONS)("Headscale %s: Users", (version) => {
let workingNodeId: string;
test("nodes can register via their nodekey", async () => {
test("nodes can register from a Tailscale registration URL", async () => {
const client = await getRuntimeClient(version);
const tailnetNode = await getNode(version);
const { auth, context, liveStore } = actionContext(client);
const user = await client.users.create({ name: "node-reg@" });
const node = await client.nodes.register(user.name, tailnetNode.authCode);
expect(user.name).toBe("node-reg@");
const response = await machineAction({
request: registerRequest(tailnetNode.registerUrl),
context,
params: {},
} as never);
expect(auth.can).toHaveBeenCalledWith(expect.anything(), Capabilities.write_machines);
expect(liveStore.refresh).toHaveBeenCalledOnce();
expect(response).toBeInstanceOf(Response);
expect((response as Response).status).toBe(302);
const nodes = await client.nodes.list();
const node = nodes.find((n) => n.name === tailnetNode.nodeName);
expect(node).toBeDefined();
expect(node.registerMethod).toBe("REGISTER_METHOD_CLI");
expect(node.name).toBe(tailnetNode.nodeName);
expect(node?.registerMethod).toBe("REGISTER_METHOD_CLI");
});
test("nodes can be retrieved", async () => {