diff --git a/packages/pockethost/package.json b/packages/pockethost/package.json index bffb4ec6..adb03f97 100644 --- a/packages/pockethost/package.json +++ b/packages/pockethost/package.json @@ -41,7 +41,6 @@ "express-async-errors": "^3.1.1", "express-sslify": "^1.2.0", "ftp-srv": "github:pockethost/ftp-srv#0fc708bae0d5d7a55ce948767f082d6fcfb2af59", - "get-port": "^6.1.2", "glob": "^10.3.10", "gobot": "1.0.0-alpha.41", "gobot-pocketbase": "0.22.8-alpha.22", diff --git a/packages/pockethost/src/services/InstanceService/index.ts b/packages/pockethost/src/services/InstanceService/index.ts index 35266dd0..3f9c2c8b 100644 --- a/packages/pockethost/src/services/InstanceService/index.ts +++ b/packages/pockethost/src/services/InstanceService/index.ts @@ -19,7 +19,6 @@ import { mkContainerHomePath, mkDocUrl, mkInstanceUrl, - mkInternalUrl, mkSingleton, now, stringify, @@ -29,7 +28,6 @@ import { InstanceLogger, MothershipAdminClientService, PocketbaseService, - PortService, SpawnConfig, proxyService, } from '../../services' @@ -121,24 +119,10 @@ export const instanceService = mkSingleton( updateInstanceStatus(id, InstanceStatus.Idle) }) - /** Obtain empty port */ - dbg(`Obtaining port`) - const [newPortPromise, releasePort] = PortService().alloc() - const newPort = await newPortPromise - shutdownManager.push(() => { - dbg(`shut down: releasing port`) - releasePort() - }) - systemInstanceLogger.breadcrumb({ port: newPort }) - dbg(`Found port`) - const internalUrl = mkInternalUrl(newPort) - dbg(`internalUrl`, internalUrl) - /** Create spawn config */ const spawnArgs: SpawnConfig = { subdomain: instance.subdomain, instanceId: instance.id, - port: newPort, dev: instance.dev, extraBinds: flatten([ globSync(join(INSTANCE_APP_MIGRATIONS_DIR(), '*.js')).map( @@ -178,7 +162,7 @@ export const instanceService = mkSingleton( /** Spawn the child process */ const childProcess = await pbService.spawn(spawnArgs) - const { exitCode, stopped, started } = childProcess + const { exitCode, stopped, started, url: internalUrl } = childProcess shutdownManager.push(() => { dbg(`killing ${id}`) diff --git a/packages/pockethost/src/services/PocketBaseService/index.ts b/packages/pockethost/src/services/PocketBaseService/index.ts index e9bb351c..22c7e248 100644 --- a/packages/pockethost/src/services/PocketBaseService/index.ts +++ b/packages/pockethost/src/services/PocketBaseService/index.ts @@ -18,14 +18,12 @@ import { } from '../../../core' import { GobotService } from '../GobotService' import { InstanceLogger } from '../InstanceLoggerService' -import { PortService } from '../PortService' export type Env = { [_: string]: string } export type SpawnConfig = { subdomain: string instanceId: string version?: string - port?: number extraBinds?: string[] env?: Env stdout?: MemoryStream @@ -65,16 +63,9 @@ export const createPocketbaseService = async ( const cm = createCleanupManager() const logger = LoggerService().create('spawn') const { dbg, warn, error } = logger - const port = - cfg.port || - (await (async () => { - const [defaultPort, freeDefaultPort] = await PortService().alloc() - cm.add(freeDefaultPort) - return defaultPort - })()) + const _cfg: Required = { version: maxVersion, - port, extraBinds: [], env: {}, stderr: new MemoryStream(), @@ -119,6 +110,7 @@ export const createPocketbaseService = async ( const container = await new Promise<{ on: EventEmitter['on'] kill: () => Promise + portBinding: number }>((resolve, reject) => { const docker = new Docker() iLogger.info(`Starting instance`) @@ -153,7 +145,7 @@ export const createPocketbaseService = async ( HostConfig: { AutoRemove: true, PortBindings: { - '8090/tcp': [{ HostPort: `${port}` }], + '8090/tcp': [{ HostPort: `0` }], }, Binds, Ulimits: [ @@ -225,17 +217,44 @@ export const createPocketbaseService = async ( } }, ) - .on('start', (container: Container) => { + .on('start', async (container: Container) => { dbg(`Got started container`, container) started = true - resolve({ - on: emitter.on.bind(emitter), - kill: () => - container.stop({ signal: `SIGINT` }).catch((e) => { - error(e) - return container.stop({ signal: `SIGKILL` }).catch(error) - }), - }) + + try { + // Get container info to retrieve the assigned port + const containerInfo = await container.inspect() + const ports = containerInfo.NetworkSettings?.Ports?.['8090/tcp'] + + if (!ports || !ports[0] || !ports[0].HostPort) { + throw new Error('Could not get port binding from container') + } + + const portBinding = parseInt(ports[0].HostPort, 10) + if (isNaN(portBinding)) { + throw new Error(`Invalid port binding: ${ports[0].HostPort}`) + } + + resolve({ + on: emitter.on.bind(emitter), + kill: () => + container.stop({ signal: `SIGINT` }).catch((e) => { + error(e) + return container.stop({ signal: `SIGKILL` }).catch(error) + }), + portBinding, + }) + } catch (e) { + error(`Failed to get port binding: ${e}`) + reject(e) + try { + await container.stop() + } catch (stopError) { + error( + `Failed to stop container after port binding error: ${stopError}`, + ) + } + } }) }).catch((e) => { error(`Error starting container: ${e}`) @@ -255,7 +274,8 @@ export const createPocketbaseService = async ( dbg(`Instance exited with ${code}`) cm.shutdown().catch(error) }) - const url = mkInternalUrl(port) + + const url = mkInternalUrl(container.portBinding) logger.breadcrumb({ url }) dbg(`Making exit hook for ${url}`) const unsub = asyncExitHook(async () => { diff --git a/packages/pockethost/src/services/PortService.ts b/packages/pockethost/src/services/PortService.ts deleted file mode 100644 index 7e61473c..00000000 --- a/packages/pockethost/src/services/PortService.ts +++ /dev/null @@ -1,16 +0,0 @@ -import getPort from 'get-port' -import { INITIAL_PORT_POOL_SIZE } from '../../core' -import { Allocator, ResourceAllocator, ioc } from '../common' - -export type Config = { maxPorts: number } - -export const PortService = () => { - try { - return ioc>('portAllocator') - } catch (e) { - return ioc( - 'portAllocator', - ResourceAllocator(INITIAL_PORT_POOL_SIZE(), getPort), - ) - } -} diff --git a/packages/pockethost/src/services/index.ts b/packages/pockethost/src/services/index.ts index 8c11c7ed..25837147 100644 --- a/packages/pockethost/src/services/index.ts +++ b/packages/pockethost/src/services/index.ts @@ -2,6 +2,5 @@ export * from './InstanceLoggerService' export * from './InstanceService' export * from './MothershipAdminClientService' export * from './PocketBaseService' -export * from './PortService' export * from './ProxyService' export * from './RealtimeLog' diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f74e7dc9..8e748880 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -370,9 +370,6 @@ importers: ftp-srv: specifier: github:pockethost/ftp-srv#0fc708bae0d5d7a55ce948767f082d6fcfb2af59 version: https://codeload.github.com/pockethost/ftp-srv/tar.gz/0fc708bae0d5d7a55ce948767f082d6fcfb2af59 - get-port: - specifier: ^6.1.2 - version: 6.1.2 glob: specifier: ^10.3.10 version: 10.4.5 @@ -2968,10 +2965,6 @@ packages: resolution: {integrity: sha512-x5UJKlgeUiNT8nyo/AcnwLnZuZNcSjSw0kogRB+Whd1fjjFq4B1hySFxSFWWSn4mIBzg3sRNUDFYc4g5gjPoLg==} engines: {node: '>=4'} - get-port@6.1.2: - resolution: {integrity: sha512-BrGGraKm2uPqurfGVj/z97/zv8dPleC6x9JBNRTrDNtCkkRF4rPwrQXFgL7+I+q8QSdU4ntLQX2D7KIxSy8nGw==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - get-stream@2.3.1: resolution: {integrity: sha512-AUGhbbemXxrZJRD5cDvKtQxLuYaIbNtDTK8YqupCI393Q2KSTreEsLUN3ZxAWFGiKTzL6nKuzfcIvieflUX9qA==} engines: {node: '>=0.10.0'} @@ -7778,8 +7771,6 @@ snapshots: get-port@3.2.0: {} - get-port@6.1.2: {} - get-stream@2.3.1: dependencies: object-assign: 4.1.1