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
39 changes: 39 additions & 0 deletions packages/mongodb-runner/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ Options:
--docker Docker image name to run server instances under [string]
--id ID to save the cluster metadata under [string]
--all for `stop`: stop all clusters [boolean]
--json for `start` and `ls`: print machine-readable JSON
on stdout instead of plain text [boolean]
--debug Enable debug output [boolean]
--help Show help [boolean]
```
Expand Down Expand Up @@ -109,6 +111,43 @@ You can specify a config file for the CLI using `--config`:
$ npx @mongodb-js/mongodb-runner start --config <path/to/config.json>
```

## Machine-readable output

By default `start` prints diagnostics to stderr and the bare connection string
to stdout, and `ls` prints one `id: uri` line per running instance. Pass
`--json` to get structured output on stdout instead (diagnostics still go to
stderr, and are not part of the JSON):

```sh
$ npx mongodb-runner start -t replset --json
{
"id": "2b56a4d3-...",
"connectionString": "mongodb://localhost:27017,localhost:27018/?replicaSet=repl0"
}
```

For OIDC clusters the result also carries `oidcIssuer` and
`connectionStringWithOidc` (the connection string with
`authMechanism=MONGODB-OIDC` appended). For DSC clusters it carries an `sls`
object with the allocated `ports` and `services`. Capture it to a file rather
than scraping stdout:

```sh
$ npx mongodb-runner start -t replset --json > cluster.json
```

`ls --json` prints a JSON array of the running instances:

```sh
$ npx mongodb-runner ls --json
[
{
"id": "2b56a4d3-...",
"connectionString": "mongodb://localhost:27017/"
}
]
```

## DSC clusters

mongodb-runner can launch clusters that use DSC, backed by an SLS storage
Expand Down
20 changes: 16 additions & 4 deletions packages/mongodb-runner/docs/disaggregated-storage.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,22 @@ Full sequence, assuming a mongodb server checkout at `$MONGO_REPO`:
# --downloadUrl=https://.../dsc-mongod.tgz
```

This prints the connection string once the cluster is up. The first run pulls
all SLS images, which can take several minutes (`--debug` shows the
progress). `mongodb-runner stop --id=...` (printed by `start`) tears down the
mongod processes and the compose project together.
This prints the connection string once the cluster is up. To get the allocated
SLS service ports/URIs as structured output, add `--json` and capture stdout to
a file instead of scraping it:

```bash
mongodb-runner start -t replset \
--slsCompose=$MONGO_REPO/buildscripts/modules/atlas/sls-multicell-docker-compose.yml \
--binDir=/path/to/dsc-mongod/bin \
--json > cluster.json
```

The JSON object carries `id` and `connectionString` plus an `sls` field with
the `ports` and `services` (the same info printed to stderr without `--json`).
The first run pulls all SLS images, which can take several minutes (`--debug`
shows the progress). `mongodb-runner stop --id=...` (printed by `start`) tears
down the mongod processes and the compose project together.

The same works for `-t standalone` and `-t sharded`. A DSC-capable
`mongod` is required (see Prerequisites) — with a stock binary, startup fails
Expand Down
57 changes: 57 additions & 0 deletions packages/mongodb-runner/src/cli.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,4 +140,61 @@ describe('cli', function () {

await runCli(['stop', '--all']);
});

it('emits structured output for start with --json', async function () {
const stdout = await runCli([
'start',
'--topology',
'standalone',
'--json',
]);

let parsed: any;
expect(() => {
parsed = JSON.parse(stdout);
}, 'stdout must be parseable as JSON, with diagnostics on stderr').to.not.throw();

expect(parsed.id, 'result should carry the cluster id').to.be.a('string');
expect(
parsed.connectionString,
'result should carry the connection string',
).to.match(/^mongodb:\/\//);

const client = new MongoClient(parsed.connectionString);
const result = await client.db('admin').command({ ping: 1 });
await client.close();
expect(result.ok, 'reported connection string should be usable').to.eq(1);

await runCli(['stop', '--all']);
await runCli(['prune']);
});

it('emits structured output for ls with --json', async function () {
const startStdout = await runCli([
'start',
'--topology',
'standalone',
'--json',
]);
const started = JSON.parse(startStdout);

const lsStdout = await runCli(['ls', '--json']);
let parsed: any;
expect(() => {
parsed = JSON.parse(lsStdout);
}, 'ls --json stdout must be parseable as JSON').to.not.throw();

expect(parsed, 'ls --json should yield an array').to.be.an('array');
const entry = parsed.find((e: any) => e.id === started.id);
expect(entry, 'the started cluster should be listed').to.not.equal(
undefined,
);
expect(
entry.connectionString,
'listed connection string should match the one start reported',
).to.equal(started.connectionString);

await runCli(['stop', '--all']);
await runCli(['prune']);
});
});
37 changes: 35 additions & 2 deletions packages/mongodb-runner/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,12 @@ import type { MongoClientOptions } from 'mongodb';
describe:
'Skip authenticating to the SLS image registry (use if you have already run docker login)',
})
.option('json', {
type: 'boolean',
default: false,
describe:
'For `start` and `ls`: print machine-readable JSON on stdout instead of plain text',
})
.option('debug', { type: 'boolean', describe: 'Enable debug output' })
.option('verbose', { type: 'boolean', describe: 'Enable verbose output' })
.command('start', 'Start a MongoDB instance')
Expand Down Expand Up @@ -177,7 +183,26 @@ import type { MongoClientOptions } from 'mongodb';
? `--runnerDir=${argv.runnerDir}`
: ''),
);
console.log(cs.toString());
if (argv.json) {
const result: Record<string, unknown> = {
__proto__: null,
id,
connectionString: cluster.connectionString,
};
if (cluster.oidcIssuer) {
result.oidcIssuer = cluster.oidcIssuer;
result.connectionStringWithOidc = cs.toString();
}
if (disaggregatedStorage && 'sls' in disaggregatedStorage) {
result.sls = {
ports: disaggregatedStorage.sls.ports,
services: disaggregatedStorage.sls.services,
};
}
console.log(JSON.stringify(result, null, 2));
} else {
console.log(cs.toString());
}
cluster.unref();
}

Expand All @@ -189,8 +214,16 @@ import type { MongoClientOptions } from 'mongodb';
}

async function ls() {
const entries: { id: string; connectionString: string }[] = [];
for await (const { id, connectionString } of utilities.instances(argv)) {
console.log(`${id}: ${connectionString}`);
if (argv.json) {
entries.push({ id, connectionString });
} else {
console.log(`${id}: ${connectionString}`);
}
}
if (argv.json) {
console.log(JSON.stringify(entries, null, 2));
}
}

Expand Down