diff --git a/packages/mongodb-runner/src/cli.ts b/packages/mongodb-runner/src/cli.ts index 633eb19a..ca6c4194 100644 --- a/packages/mongodb-runner/src/cli.ts +++ b/packages/mongodb-runner/src/cli.ts @@ -79,6 +79,10 @@ import type { MongoClientOptions } from 'mongodb'; }) .option('debug', { type: 'boolean', describe: 'Enable debug output' }) .option('verbose', { type: 'boolean', describe: 'Enable verbose output' }) + .option('json', { + type: 'boolean', + describe: 'Output machine-readable JSON ("ls" only)', + }) .command('start', 'Start a MongoDB instance') .command('stop', 'Stop a MongoDB instance') .command('prune', 'Clean up metadata for any dead MongoDB instances') @@ -149,8 +153,18 @@ import type { MongoClientOptions } from 'mongodb'; } async function ls() { - for await (const { id, connectionString } of utilities.instances(argv)) { - console.log(`${id}: ${connectionString}`); + if (argv.json) { + let first = true; + for await (const { serialized, ...rest } of utilities.instances(argv)) { + console.log(first ? '[' : ','); + first = false; + console.log(JSON.stringify({ ...rest, ...serialized }, null, 2)); + } + console.log(']'); + } else { + for await (const { id, connectionString } of utilities.instances(argv)) { + console.log(`${id}: ${connectionString}`); + } } } diff --git a/packages/mongodb-runner/src/index.ts b/packages/mongodb-runner/src/index.ts index d59500c9..8de43d89 100644 --- a/packages/mongodb-runner/src/index.ts +++ b/packages/mongodb-runner/src/index.ts @@ -2,6 +2,7 @@ export { MongoServer, type MongoServerEvents, MongoServerOptions, + SerializedServerProperties as MongoServerSerializedProperties, } from './mongoserver'; export { MongoCluster, @@ -12,6 +13,7 @@ export { RSOptions as MongoClusterRSOptions, CommonOptions as MongoClusterCommonOptions, ShardedOptions as MongoClusterShardedOptions, + SerializedClusterProperties as MongoClusterSerializedProperties, } from './mongocluster'; export type { LogEntry } from './mongologreader'; export type { ConnectionString } from 'mongodb-connection-string-url'; diff --git a/packages/mongodb-runner/src/mongocluster.ts b/packages/mongodb-runner/src/mongocluster.ts index d8d97388..df239fb6 100644 --- a/packages/mongodb-runner/src/mongocluster.ts +++ b/packages/mongodb-runner/src/mongocluster.ts @@ -1,4 +1,8 @@ -import type { MongoServerEvents, MongoServerOptions } from './mongoserver'; +import type { + MongoServerEvents, + MongoServerOptions, + SerializedServerProperties, +} from './mongoserver'; import { MongoServer } from './mongoserver'; import { ConnectionString } from 'mongodb-connection-string-url'; import type { DownloadOptions } from '@mongodb-js/mongodb-downloader'; @@ -162,6 +166,17 @@ export type MongoClusterEvents = { removeListener: [keyof MongoClusterEvents]; }; +export interface SerializedClusterProperties { + topology: MongoClusterOptions['topology']; + replSetName?: string; + servers: SerializedServerProperties[]; + shards: SerializedClusterProperties[]; + oidcMockProviderProcess?: ReturnType; + defaultConnectionOptions: Partial; + users: MongoDBUserDoc[]; + options?: MongoClusterOptions; +} + function removePortArg([...args]: string[]): string[] { let portArgIndex = -1; if ((portArgIndex = args.indexOf('--port')) !== -1) { @@ -278,6 +293,7 @@ export class MongoCluster extends EventEmitter { private oidcMockProviderProcess?: OIDCMockProviderProcess; private defaultConnectionOptions: Partial = {}; private users: MongoDBUserDoc[] = []; + private originalOptions?: MongoClusterOptions; private constructor() { super(); @@ -309,7 +325,7 @@ export class MongoCluster extends EventEmitter { }); } - serialize(): unknown /* JSON-serializable */ { + serialize(): SerializedClusterProperties { return { topology: this.topology, replSetName: this.replSetName, @@ -318,6 +334,7 @@ export class MongoCluster extends EventEmitter { oidcMockProviderProcess: this.oidcMockProviderProcess?.serialize(), defaultConnectionOptions: jsonClone(this.defaultConnectionOptions ?? {}), users: jsonClone(this.users), + options: jsonClone(this.originalOptions), }; } @@ -328,7 +345,9 @@ export class MongoCluster extends EventEmitter { return true; } - static async deserialize(serialized: any): Promise { + static async deserialize( + serialized: SerializedClusterProperties, + ): Promise { const cluster = new MongoCluster(); cluster.topology = serialized.topology; cluster.replSetName = serialized.replSetName; @@ -343,6 +362,7 @@ export class MongoCluster extends EventEmitter { cluster.oidcMockProviderProcess = serialized.oidcMockProviderProcess ? OIDCMockProviderProcess.deserialize(serialized.oidcMockProviderProcess) : undefined; + cluster.originalOptions = serialized.options; return cluster; } @@ -380,6 +400,7 @@ export class MongoCluster extends EventEmitter { options = { ...options, ...(await handleTLSClientKeyOptions(options)) }; const cluster = new MongoCluster(); + cluster.originalOptions = options; cluster.topology = options.topology; cluster.users = options.users ?? []; cluster.defaultConnectionOptions = { ...options.internalClientOptions }; diff --git a/packages/mongodb-runner/src/mongoserver.ts b/packages/mongodb-runner/src/mongoserver.ts index 99dd9abb..a57a9c84 100644 --- a/packages/mongodb-runner/src/mongoserver.ts +++ b/packages/mongodb-runner/src/mongoserver.ts @@ -53,7 +53,7 @@ export interface MongoServerOptions { keyFileContents?: string; } -interface SerializedServerProperties { +export interface SerializedServerProperties { _id: string; pid?: number; port?: number; @@ -66,6 +66,7 @@ interface SerializedServerProperties { isMongos?: boolean; isConfigSvr?: boolean; keyFileContents?: string; + commandline?: string[]; } export interface MongoServerEvents { @@ -98,6 +99,7 @@ export class MongoServer extends EventEmitter { private isConfigSvr = false; private keyFileContents?: string; private defaultConnectionOptions?: Partial; + private commandline: string[] = []; get id(): string { return this.uuid; @@ -122,6 +124,7 @@ export class MongoServer extends EventEmitter { isMongos: this.isMongos, isConfigSvr: this.isConfigSvr, keyFileContents: this.keyFileContents, + commandline: this.commandline, }; } @@ -140,6 +143,7 @@ export class MongoServer extends EventEmitter { srv.isMongos = !!serialized.isMongos; srv.isConfigSvr = !!serialized.isConfigSvr; srv.keyFileContents = serialized.keyFileContents; + srv.commandline = serialized.commandline ?? []; if (!srv.closing) { srv.pid = serialized.pid; srv.dbPath = serialized.dbPath; @@ -261,6 +265,7 @@ export class MongoServer extends EventEmitter { debug('starting server', commandline); const [executable, ...args] = commandline; + srv.commandline = commandline; const proc = spawn(executable, args, { stdio: ['inherit', 'pipe', 'pipe'], cwd: options.tmpDir, diff --git a/packages/mongodb-runner/src/runner-helpers.ts b/packages/mongodb-runner/src/runner-helpers.ts index 367aebb3..11285253 100644 --- a/packages/mongodb-runner/src/runner-helpers.ts +++ b/packages/mongodb-runner/src/runner-helpers.ts @@ -1,6 +1,9 @@ import { BSON } from 'mongodb'; import path from 'path'; -import type { MongoClusterOptions } from './mongocluster'; +import type { + MongoClusterOptions, + SerializedClusterProperties, +} from './mongocluster'; import { MongoCluster } from './mongocluster'; import { parallelForEach } from './util'; import * as fs from 'fs/promises'; @@ -10,7 +13,7 @@ import { once } from 'events'; interface StoredInstance { id: string; filepath: string; - serialized: string; + serialized: SerializedClusterProperties; connectionString: string; } @@ -31,7 +34,7 @@ export async function start( ...argv, args, }); - const serialized = await cluster.serialize(); + const serialized = cluster.serialize(); const { connectionString } = cluster; await fs.writeFile(