mirror of
https://github.com/tale/headplane.git
synced 2026-08-17 16:47:51 +00:00
chore: format everything with oxfmt
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
import type { OpenAPIV2 } from "openapi-types";
|
||||
|
||||
import { writeFile } from "node:fs/promises";
|
||||
import { resolve } from "node:path";
|
||||
import { cwd } from "node:process";
|
||||
|
||||
import type { OpenAPIV2 } from "openapi-types";
|
||||
import { request } from "undici";
|
||||
|
||||
import { hashOpenApiDocument } from "~/server/headscale/api/hasher";
|
||||
|
||||
@@ -1,73 +1,68 @@
|
||||
import hashes from '~/openapi-operation-hashes.json';
|
||||
import {
|
||||
createHeadscaleInterface,
|
||||
type HeadscaleApiInterface,
|
||||
} from '~/server/headscale/api';
|
||||
import { type HeadscaleEnv, startHeadscale } from './start-headscale';
|
||||
import { startTailscaleNode, TailscaleNodeEnv } from './start-tailscale';
|
||||
import hashes from "~/openapi-operation-hashes.json";
|
||||
import { createHeadscaleInterface, type HeadscaleApiInterface } from "~/server/headscale/api";
|
||||
|
||||
import { type HeadscaleEnv, startHeadscale } from "./start-headscale";
|
||||
import { startTailscaleNode, TailscaleNodeEnv } from "./start-tailscale";
|
||||
|
||||
export type Version = keyof typeof hashes;
|
||||
export const HS_VERSIONS = Object.keys(hashes) as Version[];
|
||||
|
||||
interface VersionStateEntry {
|
||||
env: HeadscaleEnv;
|
||||
tailscaleNode: TailscaleNodeEnv;
|
||||
bootstrap: HeadscaleApiInterface;
|
||||
env: HeadscaleEnv;
|
||||
tailscaleNode: TailscaleNodeEnv;
|
||||
bootstrap: HeadscaleApiInterface;
|
||||
}
|
||||
|
||||
const versionState = new Map<Version, VersionStateEntry>();
|
||||
async function ensureVersion(version: Version) {
|
||||
if (versionState.has(version)) {
|
||||
return versionState.get(version)!;
|
||||
}
|
||||
if (versionState.has(version)) {
|
||||
return versionState.get(version)!;
|
||||
}
|
||||
|
||||
const env = await startHeadscale(version);
|
||||
const tailscaleNode = await startTailscaleNode(
|
||||
version,
|
||||
env.container.getMappedPort(8080),
|
||||
);
|
||||
const bootstrap = await createHeadscaleInterface(env.apiUrl);
|
||||
const env = await startHeadscale(version);
|
||||
const tailscaleNode = await startTailscaleNode(version, env.container.getMappedPort(8080));
|
||||
const bootstrap = await createHeadscaleInterface(env.apiUrl);
|
||||
|
||||
const entry = { env, tailscaleNode, bootstrap };
|
||||
versionState.set(version, entry);
|
||||
return entry;
|
||||
const entry = { env, tailscaleNode, bootstrap };
|
||||
versionState.set(version, entry);
|
||||
return entry;
|
||||
}
|
||||
|
||||
export async function getBootstrapClient(version: Version) {
|
||||
const { bootstrap } = await ensureVersion(version);
|
||||
return bootstrap;
|
||||
const { bootstrap } = await ensureVersion(version);
|
||||
return bootstrap;
|
||||
}
|
||||
|
||||
export async function getRuntimeClient(version: Version) {
|
||||
const { env, bootstrap } = await ensureVersion(version);
|
||||
return bootstrap.getRuntimeClient(env.apiKey);
|
||||
const { env, bootstrap } = await ensureVersion(version);
|
||||
return bootstrap.getRuntimeClient(env.apiKey);
|
||||
}
|
||||
|
||||
export async function getIsAtLeast(version: Version) {
|
||||
const { env, bootstrap } = await ensureVersion(version);
|
||||
return bootstrap.clientHelpers.isAtleast;
|
||||
const { env, bootstrap } = await ensureVersion(version);
|
||||
return bootstrap.clientHelpers.isAtleast;
|
||||
}
|
||||
|
||||
export async function getNode(version: Version) {
|
||||
const { tailscaleNode } = await ensureVersion(version);
|
||||
return {
|
||||
authCode: tailscaleNode.authCode,
|
||||
nodeName: tailscaleNode.nodeName,
|
||||
};
|
||||
const { tailscaleNode } = await ensureVersion(version);
|
||||
return {
|
||||
authCode: tailscaleNode.authCode,
|
||||
nodeName: tailscaleNode.nodeName,
|
||||
};
|
||||
}
|
||||
|
||||
export async function stopAllVersions() {
|
||||
for (const { env, tailscaleNode } of versionState.values()) {
|
||||
await env.container.stop({
|
||||
remove: true,
|
||||
removeVolumes: true,
|
||||
});
|
||||
for (const { env, tailscaleNode } of versionState.values()) {
|
||||
await env.container.stop({
|
||||
remove: true,
|
||||
removeVolumes: true,
|
||||
});
|
||||
|
||||
await tailscaleNode.container.stop({
|
||||
remove: true,
|
||||
removeVolumes: true,
|
||||
});
|
||||
}
|
||||
await tailscaleNode.container.stop({
|
||||
remove: true,
|
||||
removeVolumes: true,
|
||||
});
|
||||
}
|
||||
|
||||
versionState.clear();
|
||||
versionState.clear();
|
||||
}
|
||||
|
||||
@@ -1,85 +1,82 @@
|
||||
import { createInterface } from 'node:readline';
|
||||
import tc from 'testcontainers';
|
||||
import hashes from '~/openapi-operation-hashes.json';
|
||||
import { createInterface } from "node:readline";
|
||||
|
||||
import tc from "testcontainers";
|
||||
|
||||
import hashes from "~/openapi-operation-hashes.json";
|
||||
|
||||
export type Version = keyof typeof hashes;
|
||||
|
||||
export interface TailscaleNodeEnv {
|
||||
container: tc.StartedTestContainer;
|
||||
authCode: string;
|
||||
nodeName: string;
|
||||
container: tc.StartedTestContainer;
|
||||
authCode: string;
|
||||
nodeName: string;
|
||||
}
|
||||
|
||||
export async function startTailscaleNode(
|
||||
version: Version,
|
||||
headscalePort: number,
|
||||
version: Version,
|
||||
headscalePort: number,
|
||||
): Promise<TailscaleNodeEnv> {
|
||||
let resolveAuthCode!: (code: string) => void;
|
||||
let rejectAuthCode!: (err: Error) => void;
|
||||
let authCodeResolved = false;
|
||||
let resolveAuthCode!: (code: string) => void;
|
||||
let rejectAuthCode!: (err: Error) => void;
|
||||
let authCodeResolved = false;
|
||||
|
||||
const authCodePromise = new Promise<string>((resolve, reject) => {
|
||||
resolveAuthCode = resolve;
|
||||
rejectAuthCode = reject;
|
||||
});
|
||||
const authCodePromise = new Promise<string>((resolve, reject) => {
|
||||
resolveAuthCode = resolve;
|
||||
rejectAuthCode = reject;
|
||||
});
|
||||
|
||||
const nodeName = `test-node-${version.replace(/\./g, '-')}`;
|
||||
const prefix = `http://localhost:8080/register/`;
|
||||
const container = await new tc.GenericContainer('tailscale/tailscale:latest')
|
||||
.withNetworkMode('host')
|
||||
.withEnvironment({
|
||||
TS_STATE_DIR: '/tailscale-state',
|
||||
TS_EXTRA_ARGS: [
|
||||
`--login-server=http://localhost:${headscalePort}`,
|
||||
`--hostname=${nodeName}`,
|
||||
'--accept-dns=false',
|
||||
'--accept-routes=false',
|
||||
].join(' '),
|
||||
})
|
||||
.withLogConsumer((stream) => {
|
||||
const rl = createInterface({ input: stream });
|
||||
const nodeName = `test-node-${version.replace(/\./g, "-")}`;
|
||||
const prefix = `http://localhost:8080/register/`;
|
||||
const container = await new tc.GenericContainer("tailscale/tailscale:latest")
|
||||
.withNetworkMode("host")
|
||||
.withEnvironment({
|
||||
TS_STATE_DIR: "/tailscale-state",
|
||||
TS_EXTRA_ARGS: [
|
||||
`--login-server=http://localhost:${headscalePort}`,
|
||||
`--hostname=${nodeName}`,
|
||||
"--accept-dns=false",
|
||||
"--accept-routes=false",
|
||||
].join(" "),
|
||||
})
|
||||
.withLogConsumer((stream) => {
|
||||
const rl = createInterface({ input: stream });
|
||||
|
||||
rl.on('line', (line: string) => {
|
||||
if (authCodeResolved) return;
|
||||
const idx = line.indexOf(prefix);
|
||||
if (idx === -1) return;
|
||||
rl.on("line", (line: string) => {
|
||||
if (authCodeResolved) return;
|
||||
const idx = line.indexOf(prefix);
|
||||
if (idx === -1) return;
|
||||
|
||||
const after = line.slice(idx + prefix.length).trim();
|
||||
if (!after) return;
|
||||
const after = line.slice(idx + prefix.length).trim();
|
||||
if (!after) return;
|
||||
|
||||
const token = after.split(/\s+/)[0];
|
||||
if (!token) return;
|
||||
const token = after.split(/\s+/)[0];
|
||||
if (!token) return;
|
||||
|
||||
authCodeResolved = true;
|
||||
resolveAuthCode(token);
|
||||
rl.close();
|
||||
});
|
||||
authCodeResolved = true;
|
||||
resolveAuthCode(token);
|
||||
rl.close();
|
||||
});
|
||||
|
||||
rl.on('close', () => {
|
||||
if (!authCodeResolved) {
|
||||
rejectAuthCode(
|
||||
new Error(
|
||||
'Tailscale container log stream closed before auth code was found',
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
})
|
||||
.withWaitStrategy(tc.Wait.forLogMessage(prefix).withStartupTimeout(30_000))
|
||||
.start();
|
||||
rl.on("close", () => {
|
||||
if (!authCodeResolved) {
|
||||
rejectAuthCode(
|
||||
new Error("Tailscale container log stream closed before auth code was found"),
|
||||
);
|
||||
}
|
||||
});
|
||||
})
|
||||
.withWaitStrategy(tc.Wait.forLogMessage(prefix).withStartupTimeout(30_000))
|
||||
.start();
|
||||
|
||||
const authCode = await Promise.race<string>([
|
||||
authCodePromise,
|
||||
new Promise((_, reject) =>
|
||||
setTimeout(
|
||||
() =>
|
||||
reject(
|
||||
new Error(`Timed out waiting for Tailscale auth URL on ${prefix}`),
|
||||
),
|
||||
25_000,
|
||||
),
|
||||
),
|
||||
]);
|
||||
const authCode = await Promise.race<string>([
|
||||
authCodePromise,
|
||||
new Promise((_, reject) =>
|
||||
setTimeout(
|
||||
() => reject(new Error(`Timed out waiting for Tailscale auth URL on ${prefix}`)),
|
||||
25_000,
|
||||
),
|
||||
),
|
||||
]);
|
||||
|
||||
return { container, authCode, nodeName };
|
||||
return { container, authCode, nodeName };
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { getBootstrapClient, HS_VERSIONS, stopAllVersions } from './env';
|
||||
import { getBootstrapClient, HS_VERSIONS, stopAllVersions } from "./env";
|
||||
|
||||
export async function setup() {
|
||||
for (const version of HS_VERSIONS) {
|
||||
await getBootstrapClient(version);
|
||||
}
|
||||
for (const version of HS_VERSIONS) {
|
||||
await getBootstrapClient(version);
|
||||
}
|
||||
}
|
||||
|
||||
export async function teardown() {
|
||||
await stopAllVersions();
|
||||
await stopAllVersions();
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { describe, expect, test } from "vitest";
|
||||
|
||||
import type { PartialHeadplaneConfigWithPaths } from "~/server/config/config-schema";
|
||||
|
||||
import { ConfigError } from "~/server/config/error";
|
||||
import { loadConfigKeyPaths } from "~/server/config/load";
|
||||
|
||||
|
||||
@@ -1,40 +1,40 @@
|
||||
import { vi } from 'vitest';
|
||||
import { vi } from "vitest";
|
||||
|
||||
const fakeFs = new Map<string, string>();
|
||||
export function createFakeFile(path: string, content: string) {
|
||||
fakeFs.set(path, content);
|
||||
fakeFs.set(path, content);
|
||||
}
|
||||
|
||||
export function clearFakeFiles() {
|
||||
fakeFs.clear();
|
||||
fakeFs.clear();
|
||||
}
|
||||
|
||||
// @ts-expect-error: I have no clue why vitest's types are wrong here
|
||||
vi.mock(import('node:fs/promises'), async (importOrig) => {
|
||||
const orig = await importOrig();
|
||||
return {
|
||||
...orig,
|
||||
readFile: (path, options) => {
|
||||
const p = path.toString();
|
||||
if (fakeFs.has(p)) {
|
||||
const content = fakeFs.get(p)!;
|
||||
if (typeof options === 'string' || options?.encoding) {
|
||||
return Promise.resolve(content);
|
||||
}
|
||||
vi.mock(import("node:fs/promises"), async (importOrig) => {
|
||||
const orig = await importOrig();
|
||||
return {
|
||||
...orig,
|
||||
readFile: (path, options) => {
|
||||
const p = path.toString();
|
||||
if (fakeFs.has(p)) {
|
||||
const content = fakeFs.get(p)!;
|
||||
if (typeof options === "string" || options?.encoding) {
|
||||
return Promise.resolve(content);
|
||||
}
|
||||
|
||||
return Promise.resolve(Buffer.from(content));
|
||||
}
|
||||
return Promise.resolve(Buffer.from(content));
|
||||
}
|
||||
|
||||
return orig.readFile.call(this, path, options);
|
||||
},
|
||||
return orig.readFile.call(this, path, options);
|
||||
},
|
||||
|
||||
access: (path, mode) => {
|
||||
const p = path.toString();
|
||||
if (fakeFs.has(p)) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
access: (path, mode) => {
|
||||
const p = path.toString();
|
||||
if (fakeFs.has(p)) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return orig.access.call(this, path, mode);
|
||||
},
|
||||
};
|
||||
return orig.access.call(this, path, mode);
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user