fix: PXE boot debugging — bisect root cause, syslog logging, serial console #3
@@ -16,6 +16,8 @@ export function registerServeCommand(program: Command): void {
|
|||||||
.option("--arch <arch>", "Architecture", "x86_64")
|
.option("--arch <arch>", "Architecture", "x86_64")
|
||||||
.option("--timezone <tz>", "Timezone", "Europe/London")
|
.option("--timezone <tz>", "Timezone", "Europe/London")
|
||||||
.option("--locale <locale>", "Locale", "en_GB.UTF-8")
|
.option("--locale <locale>", "Locale", "en_GB.UTF-8")
|
||||||
|
.option("--skip-dnsmasq", "Skip starting dnsmasq (for testing)")
|
||||||
|
.option("--skip-artifacts", "Skip downloading boot artifacts (for testing)")
|
||||||
.action(async (opts: {
|
.action(async (opts: {
|
||||||
port: string;
|
port: string;
|
||||||
dir: string;
|
dir: string;
|
||||||
@@ -25,6 +27,8 @@ export function registerServeCommand(program: Command): void {
|
|||||||
arch: string;
|
arch: string;
|
||||||
timezone: string;
|
timezone: string;
|
||||||
locale: string;
|
locale: string;
|
||||||
|
skipDnsmasq?: boolean;
|
||||||
|
skipArtifacts?: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
await startBastion({
|
await startBastion({
|
||||||
httpPort: parseInt(opts.port, 10),
|
httpPort: parseInt(opts.port, 10),
|
||||||
@@ -35,6 +39,8 @@ export function registerServeCommand(program: Command): void {
|
|||||||
arch: opts.arch,
|
arch: opts.arch,
|
||||||
timezone: opts.timezone,
|
timezone: opts.timezone,
|
||||||
locale: opts.locale,
|
locale: opts.locale,
|
||||||
|
skipDnsmasq: opts.skipDnsmasq,
|
||||||
|
skipArtifacts: opts.skipArtifacts,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ export interface BastionConfig {
|
|||||||
dhcpMode: "proxy" | "full";
|
dhcpMode: "proxy" | "full";
|
||||||
dhcpRangeStart: string;
|
dhcpRangeStart: string;
|
||||||
dhcpRangeEnd: string;
|
dhcpRangeEnd: string;
|
||||||
|
// Flags
|
||||||
|
skipDnsmasq?: boolean;
|
||||||
|
skipArtifacts?: boolean;
|
||||||
// Derived at runtime
|
// Derived at runtime
|
||||||
iface: string;
|
iface: string;
|
||||||
serverIp: string;
|
serverIp: string;
|
||||||
@@ -59,6 +62,8 @@ export function loadConfig(overrides: Partial<BastionConfig> = {}): BastionConfi
|
|||||||
gateway: overrides.gateway ?? "",
|
gateway: overrides.gateway ?? "",
|
||||||
sshKeys: overrides.sshKeys ?? [],
|
sshKeys: overrides.sshKeys ?? [],
|
||||||
adminUser: overrides.adminUser ?? "",
|
adminUser: overrides.adminUser ?? "",
|
||||||
|
skipDnsmasq: overrides.skipDnsmasq,
|
||||||
|
skipArtifacts: overrides.skipArtifacts,
|
||||||
fedoraMirror,
|
fedoraMirror,
|
||||||
tftpDir,
|
tftpDir,
|
||||||
httpDir,
|
httpDir,
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ export async function startBastion(overrides: Partial<BastionConfig> = {}): Prom
|
|||||||
mkdirSync(config.httpDir, { recursive: true });
|
mkdirSync(config.httpDir, { recursive: true });
|
||||||
|
|
||||||
// Prepare boot artifacts
|
// Prepare boot artifacts
|
||||||
|
if (!config.skipArtifacts) {
|
||||||
logger.info(`Preparing boot artifacts (Fedora ${config.fedoraVersion} ${config.arch})...`);
|
logger.info(`Preparing boot artifacts (Fedora ${config.fedoraVersion} ${config.arch})...`);
|
||||||
|
|
||||||
copyIfMissing(
|
copyIfMissing(
|
||||||
@@ -95,6 +96,9 @@ export async function startBastion(overrides: Partial<BastionConfig> = {}): Prom
|
|||||||
symlinkSafe(src, dest);
|
symlinkSafe(src, dest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
logger.info("Skipping boot artifacts (--skip-artifacts)");
|
||||||
|
}
|
||||||
|
|
||||||
// Write discovery kickstart
|
// Write discovery kickstart
|
||||||
const discoverKs = generateDiscoverKickstart(config);
|
const discoverKs = generateDiscoverKickstart(config);
|
||||||
@@ -115,8 +119,25 @@ export async function startBastion(overrides: Partial<BastionConfig> = {}): Prom
|
|||||||
await app.listen({ port: config.httpPort, host: "0.0.0.0" });
|
await app.listen({ port: config.httpPort, host: "0.0.0.0" });
|
||||||
logger.info(`HTTP server listening on :${config.httpPort}`);
|
logger.info(`HTTP server listening on :${config.httpPort}`);
|
||||||
|
|
||||||
// Start dnsmasq
|
// Start dnsmasq (unless skipped)
|
||||||
const dnsmasqProc = await startDnsmasq(config);
|
if (!config.skipDnsmasq) {
|
||||||
|
const dnsmasqProc = startDnsmasq(config);
|
||||||
|
|
||||||
|
// Monitor dnsmasq
|
||||||
|
void dnsmasqProc.then(() => {
|
||||||
|
logger.error("dnsmasq exited unexpectedly");
|
||||||
|
logger.error("Check if another DHCP/TFTP service is running.");
|
||||||
|
process.exit(1);
|
||||||
|
}).catch((err: unknown) => {
|
||||||
|
const message = err instanceof Error ? err.message : String(err);
|
||||||
|
if (!message.includes("was killed")) {
|
||||||
|
logger.error(`dnsmasq error: ${message}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
logger.info("Skipping dnsmasq (--skip-dnsmasq)");
|
||||||
|
}
|
||||||
|
|
||||||
// Print banner
|
// Print banner
|
||||||
printBanner(config);
|
printBanner(config);
|
||||||
@@ -124,7 +145,7 @@ export async function startBastion(overrides: Partial<BastionConfig> = {}): Prom
|
|||||||
// Graceful shutdown
|
// Graceful shutdown
|
||||||
const shutdown = async () => {
|
const shutdown = async () => {
|
||||||
logger.info("Shutting down...");
|
logger.info("Shutting down...");
|
||||||
stopDnsmasq();
|
if (!config.skipDnsmasq) stopDnsmasq();
|
||||||
await app.close();
|
await app.close();
|
||||||
logger.info(`State preserved in ${config.stateFile}`);
|
logger.info(`State preserved in ${config.stateFile}`);
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
@@ -133,17 +154,8 @@ export async function startBastion(overrides: Partial<BastionConfig> = {}): Prom
|
|||||||
process.on("SIGINT", () => void shutdown());
|
process.on("SIGINT", () => void shutdown());
|
||||||
process.on("SIGTERM", () => void shutdown());
|
process.on("SIGTERM", () => void shutdown());
|
||||||
|
|
||||||
// Wait for dnsmasq to exit
|
// Keep process alive
|
||||||
try {
|
await new Promise(() => {});
|
||||||
await dnsmasqProc;
|
|
||||||
} catch (err) {
|
|
||||||
const message = err instanceof Error ? err.message : String(err);
|
|
||||||
if (!message.includes("was killed")) {
|
|
||||||
logger.error(`dnsmasq exited unexpectedly: ${message}`);
|
|
||||||
logger.error("Check if another DHCP/TFTP service is running.");
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function printBanner(config: BastionConfig): void {
|
function printBanner(config: BastionConfig): void {
|
||||||
|
|||||||
Reference in New Issue
Block a user