bdmgrdts/instance/src/lib.ts

124 lines
3.6 KiB
TypeScript

export type { Base, BaseContext } from "./lib/create.ts";
import { toText } from "https://deno.land/std@0.208.0/streams/to_text.ts";
import { lines, log_from, loop_process, LoopProcess, run } from "./lib/utils.ts";
import { ContainerConfig } from "./lib/config.ts";
import { container_paths } from "./lib/paths.ts";
import { container_command, stop_container } from "./lib/nspawn.ts";
import { NginxController } from "./lib/nginx.ts";
const log = log_from("lib");
export type CmdStatus = ReturnType<typeof new_cmd_status>;
export function new_cmd_status() {
return { kind: "status" as const };
}
export type CmdEnable = ReturnType<typeof new_cmd_enable>;
export function new_cmd_enable(name: string) {
return { kind: "enable" as const, name };
}
export type CmdDisable = ReturnType<typeof new_cmd_disable>;
export function new_cmd_disable(name: string) {
return { kind: "disable" as const, name };
}
export type CmdReload = ReturnType<typeof new_cmd_reload>;
export function new_cmd_reload(name: string) {
return { kind: "reload" as const, name };
}
export type CmdStop = ReturnType<typeof new_cmd_stop>;
export function new_cmd_stop() {
return { kind: "stop" as const };
}
export type Cmd = CmdStatus | CmdEnable | CmdDisable | CmdReload | CmdStop;
export async function daemon_listen(sock_path: string) {
const server = Deno.listen({ transport: "unix", path: sock_path });
await Deno.chmod(sock_path, 0o775);
await run("chgrp", "wheel", sock_path);
const generator = async function* () {
for await (const request of server) {
const respond = async (message: string) => {
try {
await request.write(new TextEncoder().encode(message));
} catch (_) { /* bof mais bon */ }
};
try {
for await (const line of lines(request.readable)) {
const cmd = JSON.parse(line) as Cmd;
yield { cmd, respond };
}
} catch (_) { /* ok tier */ }
}
};
const result = generator() as ReturnType<typeof generator> & { server: typeof server };
result.server = server;
return result;
}
export async function daemon_send(sock_path: string, command: Cmd) {
const request = await Deno.connect({ transport: "unix", path: sock_path });
const text = JSON.stringify(command) + "\r\n\r\n";
await request.write(new TextEncoder().encode(text));
return await toText(request.readable);
}
export class Runner {
name;
config;
nginx;
loop;
constructor(config: ContainerConfig, nginx: NginxController) {
this.name = config.name;
this.config = config;
this.nginx = nginx;
this.loop = null as LoopProcess | null;
}
async start() {
await stop_container(this.name);
await this.update_proxies();
this.start_process();
}
private start_process() {
const paths = container_paths(this.name);
const command = container_command(this.name, paths.root, {
boot: true,
veth: true,
redirections: this.config.redirections,
cmd_opts: {
stdin: "null",
stdout: "null",
},
syscall_filter: ["add_key", "keyctl", "bpf"],
});
this.loop = loop_process(command, {
on_start: () => log("container", this.name, "started"),
on_stop: () => log("container", this.name, "stopped"),
});
}
async stop() {
await this.loop?.kill();
}
async update_proxies() {
const paths = container_paths(this.name);
await Deno.mkdir(paths.sites, { recursive: true });
const sites = new Set<string>();
for (const redir of this.config.redirections) {
if (redir.kind !== "http") continue;
await this.nginx.add_proxy(redir.domain, redir.port, paths.sites, redir.tls);
sites.add(redir.domain);
}
for await (const domains of this.nginx.read_all_in_dir(paths.sites)) {
if (!sites.has(domains)) this.nginx.remove_proxy(domains, paths.sites);
}
}
}