The mesh-mongodb container goes with its Dockerfile, build bases and bus credential. Its client shelled out to mongosh, which no machine's system carries, so it now speaks to the server through the official mongodb driver its package.json names, inlined into the bundle by the builder (ADR 0198 §4); the tools answer exactly as before (relaxed Extended JSON). The server is reached on loopback at the port the machine published (${port:27017}). The root secret was owned by the mongo image's user (secrets-owner 999:999), which the runtime's account cannot read; the module's own copy is now the runtime's, and the server is given its own 999-owned copy rendered from the same secret.
53 lines
2.3 KiB
TypeScript
53 lines
2.3 KiB
TypeScript
// mongodb's provisioner — the adapter that makes mongodb a provider of the mesh `mongodb-database`
|
|
// interface. The reconcile loop, the contributions file, and reading the mesh's minted password are
|
|
// the sdk harness's; this writes only the per-service half: how mongodb creates and removes a
|
|
// consumer's database + owning user (novox/hq ADR 0039/0040/0048).
|
|
//
|
|
// The `mongodb-database` interface: a consumer connects to a database it alone owns, as `as` with the
|
|
// password the mesh minted, authenticating against that same database.
|
|
//
|
|
// **The user name and password are the mesh's, not the provisioner's (ADR 0048).** The mesh derives
|
|
// the login and hands it to both ends, and mints the password. mongodb creates a user and a
|
|
// same-named database under exactly that login — a name the consumer cannot learn is a database it
|
|
// cannot reach.
|
|
//
|
|
// The commands run through MongoClient, the official driver inside this bundle (see client.ts).
|
|
|
|
import { runProvisioner, type Provision } from "@novox/mesh-sdk/provisioner";
|
|
import { emit } from "@novox/mesh-sdk/events";
|
|
import { MongoClient } from "../client.js";
|
|
|
|
const mongo = MongoClient.fromEnv();
|
|
|
|
/** Emit a lifecycle event without letting a broker hiccup fail the provisioning itself. */
|
|
async function announce(type: string, body: Record<string, string>): Promise<void> {
|
|
try {
|
|
await emit(type, body);
|
|
} catch (err) {
|
|
console.error(`[provisioner:mongodb-database] emit ${type} failed: ${err}`);
|
|
}
|
|
}
|
|
|
|
runProvisioner("mongodb-database", {
|
|
async create(p: Provision): Promise<void> {
|
|
// Database and owning user share the consumer's login, so the consumer owns exactly its own.
|
|
const database = p.as;
|
|
await mongo.createDatabaseAndUser(database, p.as, p.password);
|
|
await announce("database.provisioned", {
|
|
consumer: p.consumer ?? "",
|
|
database,
|
|
user: p.as,
|
|
});
|
|
},
|
|
|
|
async remove(p: { as: string }): Promise<void> {
|
|
await mongo.dropDatabaseAndUser(p.as, p.as);
|
|
await announce("database.deprovisioned", { database: p.as });
|
|
},
|
|
// Asked every minute by the harness: whether the backend still holds this consumer exactly as
|
|
// the mesh gave it, so a login lost behind the provisioner's back is made again (novox/hq issue 120).
|
|
async holds(p: Provision): Promise<boolean> {
|
|
return mongo.canAuthenticateAs(p.as, p.as, p.password);
|
|
},
|
|
});
|