init
This commit is contained in:
commit
44825c4c31
11 changed files with 247 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
/socket.socket
|
||||||
|
/deno.lock
|
31
control.ts
Executable file
31
control.ts
Executable file
|
@ -0,0 +1,31 @@
|
||||||
|
#!/bin/env -S deno run -A --unstable
|
||||||
|
|
||||||
|
import { daemon_send, new_cmd_disable, new_cmd_enable, new_cmd_status } from "./src/lib.ts";
|
||||||
|
import { socket_path } from "./src/lib/paths.ts";
|
||||||
|
|
||||||
|
await main();
|
||||||
|
async function main() {
|
||||||
|
const [kind, ...rest] = Deno.args;
|
||||||
|
|
||||||
|
if (kind === "status") {
|
||||||
|
const res = await daemon_send(socket_path(), new_cmd_status());
|
||||||
|
console.log({ res });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kind === "enable") {
|
||||||
|
const [name] = rest;
|
||||||
|
const res = await daemon_send(socket_path(), new_cmd_enable(name));
|
||||||
|
console.log({ res });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kind === "disable") {
|
||||||
|
const [name] = rest;
|
||||||
|
const res = await daemon_send(socket_path(), new_cmd_disable(name));
|
||||||
|
console.log({ res });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kind === "stop") {
|
||||||
|
const res = await daemon_send(socket_path(), new_cmd_status());
|
||||||
|
console.log({ res });
|
||||||
|
}
|
||||||
|
}
|
42
daemon.ts
Executable file
42
daemon.ts
Executable file
|
@ -0,0 +1,42 @@
|
||||||
|
#!/bin/env -S deno run -A --unstable
|
||||||
|
|
||||||
|
import { daemon_listen, Runner, start_runner } from "./src/lib.ts";
|
||||||
|
import { new_container_config } from "./src/lib/config.ts";
|
||||||
|
import { socket_path } from "./src/lib/paths.ts";
|
||||||
|
|
||||||
|
await main();
|
||||||
|
async function main() {
|
||||||
|
const enabled = new Map<string, Runner>();
|
||||||
|
|
||||||
|
for await (const { cmd, respond } of daemon_listen(socket_path())) {
|
||||||
|
console.log("received", { cmd });
|
||||||
|
|
||||||
|
if (cmd.kind === "status") {
|
||||||
|
await respond(JSON.stringify(Array.from(enabled.keys())));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd.kind === "enable") {
|
||||||
|
const config = new_container_config(cmd.name); // TODO
|
||||||
|
const runner = start_runner(config);
|
||||||
|
enabled.set(cmd.name, runner);
|
||||||
|
await respond("enabled");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd.kind === "disable") {
|
||||||
|
await enabled.get(cmd.name)?.stop();
|
||||||
|
enabled.delete(cmd.name);
|
||||||
|
await respond("disabled");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd.kind === "stop") {
|
||||||
|
await respond(JSON.stringify("stopping, ++"));
|
||||||
|
for (const runner of enabled.values()) await runner.stop();
|
||||||
|
Deno.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
await respond("unknown");
|
||||||
|
}
|
||||||
|
}
|
6
deno.json
Normal file
6
deno.json
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"fmt": {
|
||||||
|
"useTabs": true,
|
||||||
|
"lineWidth": 120
|
||||||
|
}
|
||||||
|
}
|
2
local/.gitignore
vendored
Normal file
2
local/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
/base/*
|
||||||
|
/container/*
|
18
src/bin/proxy.ts
Normal file
18
src/bin/proxy.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#!/bin/env -S deno run -A --unstable
|
||||||
|
|
||||||
|
await main();
|
||||||
|
async function main() {
|
||||||
|
const [from_port, to_ip, to_port] = Deno.args;
|
||||||
|
if (to_port === undefined) Deno.exit(2);
|
||||||
|
|
||||||
|
const server = Deno.listen({ transport: "tcp", port: parseInt(from_port) });
|
||||||
|
|
||||||
|
for await (const connection of server) {
|
||||||
|
try {
|
||||||
|
const client = await Deno.connect({ transport: "tcp", hostname: to_ip, port: parseInt(to_port) });
|
||||||
|
const promise_connection_done = connection.readable.pipeTo(client.writable);
|
||||||
|
const promise_client_done = client.readable.pipeTo(connection.writable);
|
||||||
|
await Promise.all([promise_client_done, promise_connection_done]);
|
||||||
|
} catch (_) { /* isok */ }
|
||||||
|
}
|
||||||
|
}
|
59
src/lib.ts
Normal file
59
src/lib.ts
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
import { toText } from "https://deno.land/std@0.208.0/streams/to_text.ts";
|
||||||
|
import { lines } from "./lib/utils.ts";
|
||||||
|
import { ContainerConfig } from "./lib/config.ts";
|
||||||
|
|
||||||
|
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 CmdStop = ReturnType<typeof new_cmd_stop>;
|
||||||
|
export function new_cmd_stop() {
|
||||||
|
return { kind: "stop" as const };
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Cmd = CmdStatus | CmdEnable | CmdDisable | CmdStop;
|
||||||
|
|
||||||
|
export async function* daemon_listen(sock_path: string) {
|
||||||
|
const server = Deno.listen({ transport: "unix", path: sock_path });
|
||||||
|
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 */ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 type Runner = ReturnType<typeof start_runner>;
|
||||||
|
export function start_runner(config: ContainerConfig) {
|
||||||
|
return {
|
||||||
|
name: config.name,
|
||||||
|
stop: async () => {
|
||||||
|
// TODO
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
21
src/lib/config.ts
Normal file
21
src/lib/config.ts
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import { container_paths } from "./paths.ts";
|
||||||
|
import { exists } from "./utils.ts";
|
||||||
|
|
||||||
|
export type ContainerConfig = ReturnType<typeof new_container_config>;
|
||||||
|
export function new_container_config(name: string) {
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
version: 0,
|
||||||
|
redirects: [] as [number, number][],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function load_container_config(name: string) {
|
||||||
|
const config_path = container_paths(name).config;
|
||||||
|
if (!exists(config_path)) return null;
|
||||||
|
const content = await Deno.readTextFile(config_path);
|
||||||
|
const read = JSON.parse(content);
|
||||||
|
const default_ = new_container_config(name);
|
||||||
|
if (read.version < default_.version) throw new Error("read conf version is outdated");
|
||||||
|
return { ...default_, ...read };
|
||||||
|
}
|
1
src/lib/nspawn.ts
Normal file
1
src/lib/nspawn.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
// wrapper
|
22
src/lib/paths.ts
Normal file
22
src/lib/paths.ts
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import { dirname } from "https://deno.land/std@0.208.0/path/mod.ts";
|
||||||
|
|
||||||
|
// '/bdmgrdts'
|
||||||
|
export function instance_root_path() {
|
||||||
|
const file_path = new URL("", import.meta.url).pathname;
|
||||||
|
return dirname(dirname(dirname(file_path)));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function socket_path() {
|
||||||
|
return instance_root_path() + "/socket.socket";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function containers_path() {
|
||||||
|
return instance_root_path() + "/local/containers";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function container_paths(name: string) {
|
||||||
|
const base = containers_path() + "/" + name;
|
||||||
|
const conf = base + "/config.json";
|
||||||
|
const root = base + "/root";
|
||||||
|
return { base, configuration: conf, root };
|
||||||
|
}
|
43
src/lib/utils.ts
Normal file
43
src/lib/utils.ts
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
import { TextLineStream } from "https://deno.land/std@0.208.0/streams/mod.ts";
|
||||||
|
|
||||||
|
export function channel<T>() {
|
||||||
|
const inner = {
|
||||||
|
queue: [] as T[],
|
||||||
|
resolver: null as (() => void) | null,
|
||||||
|
};
|
||||||
|
const send = (item: T) => {
|
||||||
|
inner.queue.push(item);
|
||||||
|
if (inner.resolver === null) return;
|
||||||
|
inner.resolver();
|
||||||
|
inner.resolver = null;
|
||||||
|
};
|
||||||
|
const receive = async () => {
|
||||||
|
if (inner.queue.length < 1) await new Promise<void>((res) => inner.resolver = res);
|
||||||
|
const [head] = inner.queue.splice(0, 1);
|
||||||
|
return head;
|
||||||
|
};
|
||||||
|
return { send, receive };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function* range(from: number, to: number) {
|
||||||
|
while (from < to) yield from++;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function exists(path: string) {
|
||||||
|
try {
|
||||||
|
await Deno.stat(path);
|
||||||
|
return true;
|
||||||
|
} catch (_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function next<T>(readable: ReadableStream<T>) {
|
||||||
|
for await (const item of readable) return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function lines(readable: ReadableStream<Uint8Array>) {
|
||||||
|
return readable
|
||||||
|
.pipeThrough(new TextDecoderStream())
|
||||||
|
.pipeThrough(new TextLineStream());
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue