Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions packages/mongodb-runner/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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', {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

    .option('verbose', { type: 'boolean', describe: 'Enable verbose output' })
    .command('start', 'Start a MongoDB instance')
    .command('stop', 'Stop a MongoDB instance')
    .command('prune', 'Clean up metadata for any dead MongoDB instances')
    .command('ls', 'List currently running MongoDB instances', (yargs) => {
      return yargs.option('json', {
        type: 'boolean',
        describe: 'Output machine-readable JSON',
      });
    })

just a suggestion, you could nest it under ls, it's still top-level on argv so no code change below needed.

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')
Expand Down Expand Up @@ -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}`);
}
}
}

Expand Down
2 changes: 2 additions & 0 deletions packages/mongodb-runner/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export {
MongoServer,
type MongoServerEvents,
MongoServerOptions,
SerializedServerProperties as MongoServerSerializedProperties,
} from './mongoserver';
export {
MongoCluster,
Expand All @@ -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';
Expand Down
27 changes: 24 additions & 3 deletions packages/mongodb-runner/src/mongocluster.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -162,6 +166,17 @@ export type MongoClusterEvents = {
removeListener: [keyof MongoClusterEvents];
};

export interface SerializedClusterProperties {
topology: MongoClusterOptions['topology'];
replSetName?: string;
servers: SerializedServerProperties[];
shards: SerializedClusterProperties[];
oidcMockProviderProcess?: ReturnType<OIDCMockProviderProcess['serialize']>;
defaultConnectionOptions: Partial<MongoClientOptions>;
users: MongoDBUserDoc[];
options?: MongoClusterOptions;
}

function removePortArg([...args]: string[]): string[] {
let portArgIndex = -1;
if ((portArgIndex = args.indexOf('--port')) !== -1) {
Expand Down Expand Up @@ -278,6 +293,7 @@ export class MongoCluster extends EventEmitter<MongoClusterEvents> {
private oidcMockProviderProcess?: OIDCMockProviderProcess;
private defaultConnectionOptions: Partial<MongoClientOptions> = {};
private users: MongoDBUserDoc[] = [];
private originalOptions?: MongoClusterOptions;

private constructor() {
super();
Expand Down Expand Up @@ -309,7 +325,7 @@ export class MongoCluster extends EventEmitter<MongoClusterEvents> {
});
}

serialize(): unknown /* JSON-serializable */ {
serialize(): SerializedClusterProperties {
return {
topology: this.topology,
replSetName: this.replSetName,
Expand All @@ -318,6 +334,7 @@ export class MongoCluster extends EventEmitter<MongoClusterEvents> {
oidcMockProviderProcess: this.oidcMockProviderProcess?.serialize(),
defaultConnectionOptions: jsonClone(this.defaultConnectionOptions ?? {}),
users: jsonClone(this.users),
options: jsonClone(this.originalOptions),
};
}

Expand All @@ -328,7 +345,9 @@ export class MongoCluster extends EventEmitter<MongoClusterEvents> {
return true;
}

static async deserialize(serialized: any): Promise<MongoCluster> {
static async deserialize(
serialized: SerializedClusterProperties,
): Promise<MongoCluster> {
const cluster = new MongoCluster();
cluster.topology = serialized.topology;
cluster.replSetName = serialized.replSetName;
Expand All @@ -343,6 +362,7 @@ export class MongoCluster extends EventEmitter<MongoClusterEvents> {
cluster.oidcMockProviderProcess = serialized.oidcMockProviderProcess
? OIDCMockProviderProcess.deserialize(serialized.oidcMockProviderProcess)
: undefined;
cluster.originalOptions = serialized.options;
return cluster;
}

Expand Down Expand Up @@ -380,6 +400,7 @@ export class MongoCluster extends EventEmitter<MongoClusterEvents> {
options = { ...options, ...(await handleTLSClientKeyOptions(options)) };

const cluster = new MongoCluster();
cluster.originalOptions = options;
cluster.topology = options.topology;
cluster.users = options.users ?? [];
cluster.defaultConnectionOptions = { ...options.internalClientOptions };
Expand Down
7 changes: 6 additions & 1 deletion packages/mongodb-runner/src/mongoserver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export interface MongoServerOptions {
keyFileContents?: string;
}

interface SerializedServerProperties {
export interface SerializedServerProperties {
_id: string;
pid?: number;
port?: number;
Expand All @@ -66,6 +66,7 @@ interface SerializedServerProperties {
isMongos?: boolean;
isConfigSvr?: boolean;
keyFileContents?: string;
commandline?: string[];
}

export interface MongoServerEvents {
Expand Down Expand Up @@ -98,6 +99,7 @@ export class MongoServer extends EventEmitter<MongoServerEvents> {
private isConfigSvr = false;
private keyFileContents?: string;
private defaultConnectionOptions?: Partial<MongoClientOptions>;
private commandline: string[] = [];

get id(): string {
return this.uuid;
Expand All @@ -122,6 +124,7 @@ export class MongoServer extends EventEmitter<MongoServerEvents> {
isMongos: this.isMongos,
isConfigSvr: this.isConfigSvr,
keyFileContents: this.keyFileContents,
commandline: this.commandline,
};
}

Expand All @@ -140,6 +143,7 @@ export class MongoServer extends EventEmitter<MongoServerEvents> {
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;
Expand Down Expand Up @@ -261,6 +265,7 @@ export class MongoServer extends EventEmitter<MongoServerEvents> {

debug('starting server', commandline);
const [executable, ...args] = commandline;
srv.commandline = commandline;
const proc = spawn(executable, args, {
stdio: ['inherit', 'pipe', 'pipe'],
cwd: options.tmpDir,
Expand Down
9 changes: 6 additions & 3 deletions packages/mongodb-runner/src/runner-helpers.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -10,7 +13,7 @@ import { once } from 'events';
interface StoredInstance {
id: string;
filepath: string;
serialized: string;
serialized: SerializedClusterProperties;
connectionString: string;
}

Expand All @@ -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(
Expand Down
Loading