feat: mothership isolation (Docker vs spawn)

This commit is contained in:
Ben Allfree 2024-03-01 06:29:12 -08:00
parent c491da0b9c
commit d86f96c3b4
7 changed files with 125 additions and 34 deletions

View File

@ -36,6 +36,7 @@
"oneline",
"opengraph",
"PASV",
"pbgo",
"pbincache",
"PBOUNCE",
"pexec",

View File

@ -78,7 +78,9 @@
"nanoid": "^5.0.2",
"node-fetch": "^3.3.2",
"node-os-utils": "^1.3.7",
"pbgo": "1.0.0-alpha.1",
"pocketbase": "^0.20.1",
"rimraf": "^5.0.5",
"semver": "^7.5.4",
"sqlite3": "^5.1.6",
"syslog-parse": "^2.0.0",

14
pnpm-lock.yaml generated
View File

@ -107,9 +107,15 @@ importers:
node-os-utils:
specifier: ^1.3.7
version: 1.3.7
pbgo:
specifier: 1.0.0-alpha.1
version: link:../pbgo
pocketbase:
specifier: ^0.20.1
version: 0.20.1
rimraf:
specifier: ^5.0.5
version: 5.0.5
semver:
specifier: ^7.5.4
version: 7.5.4
@ -7211,6 +7217,14 @@ packages:
dependencies:
glob: 7.2.3
/rimraf@5.0.5:
resolution: {integrity: sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==}
engines: {node: '>=14'}
hasBin: true
dependencies:
glob: 10.3.10
dev: false
/rizzdown@0.0.7:
resolution: {integrity: sha512-gL+Vz1qMNAIoEgtYzJ+PbB0rZFtI+GJ/C+vXC9/IM1O3F21AgboBWrqGM7Ls5L7GiG1o933vApjulun/hdto2g==}
engines: {node: '>=16'}

View File

@ -2,14 +2,16 @@ import { Command } from 'commander'
import { mothership } from './mothership'
type Options = {
debug: boolean
isolate: boolean
}
export const ServeCommand = () => {
const cmd = new Command(`serve`)
.description(`Run the PocketHost mothership`)
.option(`--isolate`, `Use Docker for process isolation.`, false)
.action(async (options: Options) => {
await mothership()
console.log({ options })
await mothership(options)
})
return cmd
}

View File

@ -1,9 +1,11 @@
import {
DATA_ROOT,
DEBUG,
IS_DEV,
LS_WEBHOOK_SECRET,
mkContainerHomePath,
MOTHERSHIP_APP_DIR,
MOTHERSHIP_DATA_ROOT,
MOTHERSHIP_HOOKS_DIR,
MOTHERSHIP_MIGRATIONS_DIR,
MOTHERSHIP_NAME,
@ -18,38 +20,105 @@ import {
} from '$services'
import { LoggerService } from '$shared'
import { gracefulExit } from '$util'
import copyfiles from 'copyfiles'
import { run } from 'pbgo'
import { rimraf } from 'rimraf'
export async function mothership() {
export type MothershipConfig = { isolate: boolean }
const _copy = (src: string, dst: string) => {
const { error } = LoggerService().create(`copy`)
return new Promise<void>((resolve) => {
copyfiles(
[src, dst],
{
verbose: DEBUG(),
up: true,
},
(err) => {
if (err) {
error(err)
throw err
}
resolve()
},
)
})
}
export async function mothership(cfg: MothershipConfig) {
const { isolate } = cfg
const logger = LoggerService().create(`Mothership`)
const { dbg, error, info, warn } = logger
info(`Starting`)
dbg(`Isolation mode:`, { isolate })
await PortService({})
await PocketbaseReleaseVersionService({})
const pbService = await PocketbaseService({})
/** Launch central database */
info(`Serving`)
const { url, exitCode } = await pbService.spawn({
version: MOTHERSHIP_SEMVER(),
subdomain: MOTHERSHIP_NAME(),
instanceId: MOTHERSHIP_NAME(),
port: MOTHERSHIP_PORT(),
dev: DEBUG(),
env: {
DATA_ROOT: mkContainerHomePath(`data`),
LS_WEBHOOK_SECRET: LS_WEBHOOK_SECRET(),
},
extraBinds: [
`${DATA_ROOT()}:${mkContainerHomePath(`data`)}`,
`${MOTHERSHIP_HOOKS_DIR()}:${mkContainerHomePath(`pb_hooks`)}`,
`${PH_VERSIONS()}:${mkContainerHomePath(`pb_hooks`, `versions.js`)}`,
`${MOTHERSHIP_MIGRATIONS_DIR()}:${mkContainerHomePath(`pb_migrations`)}`,
`${MOTHERSHIP_APP_DIR()}:${mkContainerHomePath(`ph_app`)}`,
],
})
info(`Mothership URL for this session is ${url}`)
exitCode.then((c) => {
gracefulExit(c)
})
if (isolate) {
await PocketbaseReleaseVersionService({})
const pbService = await PocketbaseService({})
const { url, exitCode } = await pbService.spawn({
version: MOTHERSHIP_SEMVER(),
subdomain: MOTHERSHIP_NAME(),
instanceId: MOTHERSHIP_NAME(),
port: MOTHERSHIP_PORT(),
dev: DEBUG(),
env: {
DATA_ROOT: mkContainerHomePath(`data`),
LS_WEBHOOK_SECRET: LS_WEBHOOK_SECRET(),
},
extraBinds: [
`${DATA_ROOT()}:${mkContainerHomePath(`data`)}`,
`${MOTHERSHIP_HOOKS_DIR()}:${mkContainerHomePath(`pb_hooks`)}`,
`${PH_VERSIONS()}:${mkContainerHomePath(`pb_hooks`, `versions.js`)}`,
`${MOTHERSHIP_MIGRATIONS_DIR()}:${mkContainerHomePath(
`pb_migrations`,
)}`,
`${MOTHERSHIP_APP_DIR()}:${mkContainerHomePath(`ph_app`)}`,
],
})
info(`Mothership URL for this session is ${url}`)
exitCode.then((c) => {
gracefulExit(c)
})
} else {
await rimraf(MOTHERSHIP_DATA_ROOT(`pb_hooks`))
await _copy(MOTHERSHIP_HOOKS_DIR(`**/*`), MOTHERSHIP_DATA_ROOT(`pb_hooks`))
await _copy(PH_VERSIONS(), MOTHERSHIP_DATA_ROOT(`pb_hooks`))
await rimraf(MOTHERSHIP_DATA_ROOT(`pb_migrations`))
await _copy(
MOTHERSHIP_MIGRATIONS_DIR(`**/*`),
MOTHERSHIP_DATA_ROOT(`pb_migrations`),
)
const args = [
`serve`,
`--http`,
`0.0.0.0:${MOTHERSHIP_PORT()}`,
`--dir`,
MOTHERSHIP_DATA_ROOT(`pb_data`),
`--hooksDir`,
MOTHERSHIP_DATA_ROOT(`pb_hooks`),
`--migrationsDir`,
MOTHERSHIP_DATA_ROOT(`pb_migrations`),
`--publicDir`,
MOTHERSHIP_DATA_ROOT(`pb_public`),
]
if (IS_DEV()) {
args.push(`--dev`)
}
dbg(args)
const process = run(args, {
env: {
DATA_ROOT: DATA_ROOT(),
LS_WEBHOOK_SECRET: LS_WEBHOOK_SECRET(),
},
version: MOTHERSHIP_SEMVER(),
debug: DEBUG(),
})
}
}

View File

@ -6,19 +6,20 @@ import { firewall } from '../FirewallCommand/ServeCommand/firewall/server'
import { mothership } from '../MothershipCommand/ServeCommand/mothership'
type Options = {
debug: boolean
isolate: boolean
}
export const ServeCommand = () => {
const cmd = new Command(`serve`)
.description(`Run the entire PocketHost stack`)
.option(`--isolate`, `Use Docker for process isolation.`, false)
.action(async (options: Options) => {
const logger = LoggerService().create(`ServeComand`)
const logger = LoggerService().create(`ServeCommand`)
const { dbg, error, info, warn } = logger
info(`Starting`)
await syslog()
await mothership()
await mothership(options)
await daemon()
await firewall()
})

View File

@ -216,9 +216,10 @@ export const MOTHERSHIP_ADMIN_USERNAME = () =>
settings().MOTHERSHIP_ADMIN_USERNAME
export const MOTHERSHIP_ADMIN_PASSWORD = () =>
settings().MOTHERSHIP_ADMIN_PASSWORD
export const MOTHERSHIP_MIGRATIONS_DIR = () =>
settings().MOTHERSHIP_MIGRATIONS_DIR
export const MOTHERSHIP_HOOKS_DIR = () => settings().MOTHERSHIP_HOOKS_DIR
export const MOTHERSHIP_MIGRATIONS_DIR = (...paths: string[]) =>
join(settings().MOTHERSHIP_MIGRATIONS_DIR, ...paths)
export const MOTHERSHIP_HOOKS_DIR = (...paths: string[]) =>
join(settings().MOTHERSHIP_HOOKS_DIR, ...paths)
export const MOTHERSHIP_APP_DIR = () => settings().MOTHERSHIP_APP_DIR
export const MOTHERSHIP_SEMVER = () => settings().MOTHERSHIP_SEMVER
export const MOTHERSHIP_PORT = () => settings().MOTHERSHIP_PORT
@ -264,7 +265,8 @@ export const DOCKER_CONTAINER_HOST = () => settings().DOCKER_CONTAINER_HOST
/** Helpers */
export const MOTHERSHIP_DATA_ROOT = () => INSTANCE_DATA_ROOT(MOTHERSHIP_NAME())
export const MOTHERSHIP_DATA_ROOT = (...paths: string[]) =>
join(INSTANCE_DATA_ROOT(MOTHERSHIP_NAME()), ...paths)
export const MOTHERSHIP_DATA_DB = () =>
join(MOTHERSHIP_DATA_ROOT(), `pb_data`, `data.db`)
export const MOTHERSHIP_INTERNAL_URL = (path = '') =>