57 lines
2.9 KiB
TypeScript
57 lines
2.9 KiB
TypeScript
// minio's provisioner — the adapter that makes minio a provider of the mesh `s3-bucket` interface
|
|
// (the name in module.json's `provides`). The reconcile loop, the contributions file, and reading
|
|
// the mesh's minted secret are the sdk harness's; this writes only the per-service half: how minio
|
|
// creates and removes a consumer's bucket and its scoped access key (novox/hq ADR 0039/0040/0048).
|
|
//
|
|
// The `s3-bucket` interface: a consumer connects to an S3 endpoint with an access key confined to
|
|
// its own bucket. It depends on `s3-bucket`, not on minio, so any S3-compatible provider could serve
|
|
// it.
|
|
//
|
|
// **The access key and its secret are the mesh's, not the provisioner's (ADR 0048).** The mesh
|
|
// derives the login (the access-key id) and hands it to both ends, and mints the secret key. minio
|
|
// creates the service account under exactly that access key with exactly that secret — a credential
|
|
// the provisioner invented is one the consumer could never present.
|
|
//
|
|
// **The bucket name is the mesh's too (ADR 0202).** It used to be computed here, from the login,
|
|
// and every consumer transcribed the same rule into its own definition by hand — two copies of
|
|
// one rule with nothing comparing them, and one of three was wrong for months. Now the rule is a
|
|
// line of this module's manifest (`serves.s3-bucket.bucket: ${consumer:as:dns}`), the mesh fills
|
|
// it per consumer, and the same filled value reaches this provisioner and the consumer's own
|
|
// configuration. There is no second computation to disagree with.
|
|
|
|
import { runProvisioner, type Provision } from "@novox/mesh-sdk/provisioner";
|
|
import { emit } from "@novox/mesh-sdk/events";
|
|
import { MinioClient } from "../client.js";
|
|
import { ensureLinkKey } from "../links.js";
|
|
import { handlers } from "./handlers.js";
|
|
|
|
const minio = MinioClient.fromEnv();
|
|
// minio's own link keys (issue 1594): the secrets live under the state directory, nowhere else.
|
|
const linksDir = process.env.MESH_MINIO_LINKS ?? "";
|
|
|
|
// At start every bucket that exists gets its link key too, those made before link keys were (best effort:
|
|
// a bucket that fails is said, the provisioner still serves).
|
|
void (async () => {
|
|
if (linksDir === "") { console.error("[minio] MESH_MINIO_LINKS is not set: no link keys made"); return; }
|
|
try {
|
|
for (const b of await minio.listBuckets()) {
|
|
try { await ensureLinkKey(minio, linksDir, b.name); }
|
|
catch (err) { console.error(`[minio] link key for ${b.name}: ${err}`); }
|
|
}
|
|
} catch (err) {
|
|
console.error(`[minio] could not list buckets for link keys: ${err}`);
|
|
}
|
|
})();
|
|
|
|
runProvisioner("s3-bucket", handlers({ minio, linksDir, announce }));
|
|
|
|
/** Emit best-effort: a broker hiccup is logged and dropped, never allowed to throw back and fail a
|
|
* bucket that was made. */
|
|
async function announce(type: string, body: unknown): Promise<void> {
|
|
try {
|
|
await emit(type, body);
|
|
} catch (err) {
|
|
console.error(`[minio] could not emit ${type}: ${err}`);
|
|
}
|
|
}
|