@zincapp/znvault-migrate 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +74 -0
- package/dist/adapters/engine-adapter.d.ts +91 -0
- package/dist/adapters/engine-adapter.d.ts.map +1 -0
- package/dist/adapters/engine-adapter.js +1 -0
- package/dist/adapters/engine-adapter.js.map +1 -0
- package/dist/adapters/mysql/connection.d.ts +30 -0
- package/dist/adapters/mysql/connection.d.ts.map +1 -0
- package/dist/adapters/mysql/connection.js +132 -0
- package/dist/adapters/mysql/connection.js.map +1 -0
- package/dist/adapters/mysql/lock.d.ts +41 -0
- package/dist/adapters/mysql/lock.d.ts.map +1 -0
- package/dist/adapters/mysql/lock.js +77 -0
- package/dist/adapters/mysql/lock.js.map +1 -0
- package/dist/adapters/mysql/mysql-adapter.d.ts +11 -0
- package/dist/adapters/mysql/mysql-adapter.d.ts.map +1 -0
- package/dist/adapters/mysql/mysql-adapter.js +130 -0
- package/dist/adapters/mysql/mysql-adapter.js.map +1 -0
- package/dist/adapters/mysql/mysql-conn.d.ts +22 -0
- package/dist/adapters/mysql/mysql-conn.d.ts.map +1 -0
- package/dist/adapters/mysql/mysql-conn.js +1 -0
- package/dist/adapters/mysql/mysql-conn.js.map +1 -0
- package/dist/adapters/mysql/scaffolding.d.ts +49 -0
- package/dist/adapters/mysql/scaffolding.d.ts.map +1 -0
- package/dist/adapters/mysql/scaffolding.js +70 -0
- package/dist/adapters/mysql/scaffolding.js.map +1 -0
- package/dist/adapters/mysql/schema-migrations-repo.d.ts +42 -0
- package/dist/adapters/mysql/schema-migrations-repo.d.ts.map +1 -0
- package/dist/adapters/mysql/schema-migrations-repo.js +77 -0
- package/dist/adapters/mysql/schema-migrations-repo.js.map +1 -0
- package/dist/adapters/mysql/sql-splitter.d.ts +15 -0
- package/dist/adapters/mysql/sql-splitter.d.ts.map +1 -0
- package/dist/adapters/mysql/sql-splitter.js +160 -0
- package/dist/adapters/mysql/sql-splitter.js.map +1 -0
- package/dist/config.d.ts +35 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +36 -0
- package/dist/config.js.map +1 -0
- package/dist/core/baseline-marker.d.ts +2 -0
- package/dist/core/baseline-marker.d.ts.map +1 -0
- package/dist/core/baseline-marker.js +10 -0
- package/dist/core/baseline-marker.js.map +1 -0
- package/dist/core/checksum.d.ts +18 -0
- package/dist/core/checksum.d.ts.map +1 -0
- package/dist/core/checksum.js +30 -0
- package/dist/core/checksum.js.map +1 -0
- package/dist/core/migration-files.d.ts +8 -0
- package/dist/core/migration-files.d.ts.map +1 -0
- package/dist/core/migration-files.js +32 -0
- package/dist/core/migration-files.js.map +1 -0
- package/dist/core/planner.d.ts +36 -0
- package/dist/core/planner.d.ts.map +1 -0
- package/dist/core/planner.js +81 -0
- package/dist/core/planner.js.map +1 -0
- package/dist/core/runner.d.ts +159 -0
- package/dist/core/runner.d.ts.map +1 -0
- package/dist/core/runner.js +301 -0
- package/dist/core/runner.js.map +1 -0
- package/dist/core/types.d.ts +24 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +1 -0
- package/dist/core/types.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/lease/dynamic-secrets-client.d.ts +43 -0
- package/dist/lease/dynamic-secrets-client.d.ts.map +1 -0
- package/dist/lease/dynamic-secrets-client.js +110 -0
- package/dist/lease/dynamic-secrets-client.js.map +1 -0
- package/dist/lease/run-migrations.d.ts +169 -0
- package/dist/lease/run-migrations.d.ts.map +1 -0
- package/dist/lease/run-migrations.js +302 -0
- package/dist/lease/run-migrations.js.map +1 -0
- package/package.json +65 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* run-migrations.ts — the deploy migration phase.
|
|
3
|
+
*
|
|
4
|
+
* Mints a dynamic-secrets lease (4h TTL), opens a connection via the selected
|
|
5
|
+
* engine adapter with the ephemeral credentials, runs the migration engine,
|
|
6
|
+
* and revokes the lease in the finally block and on SIGINT/SIGTERM.
|
|
7
|
+
*
|
|
8
|
+
* Design decisions (spec §run-migrations.ts + Codex):
|
|
9
|
+
* - 4h TTL (not 600s): Vault's cleanup job KILLs active DB sessions when a
|
|
10
|
+
* lease expires mid-DDL. A generous TTL + explicit revoke-on-exit is safer.
|
|
11
|
+
* - Migration-helper scaffolding (if `opts.scaffoldingFile` configured) is
|
|
12
|
+
* applied by the runner at the start of the phase, against the just-minted
|
|
13
|
+
* lease's own ephemeral user, and cleaned up (definer objects dropped)
|
|
14
|
+
* after reconcile — so the ephemeral migrate user never owns a routine
|
|
15
|
+
* past the phase and revokes cleanly.
|
|
16
|
+
* - ALWAYS call runner.run() — never short-circuit on status (CRITICAL F4.1):
|
|
17
|
+
* run() unconditionally executes pending migrations on every invocation; a
|
|
18
|
+
* status check short-circuit would leave pending migrations unapplied while
|
|
19
|
+
* reporting "up to date".
|
|
20
|
+
* - revokeOnce: single guarded revoke with 3× retry + backoff; 404/non-ACTIVE
|
|
21
|
+
* treated as success (Codex F6.1). Signal handlers await the revoke (5s cap)
|
|
22
|
+
* before exiting (Codex F6.4) so the in-flight HTTP request isn't aborted.
|
|
23
|
+
* - Lease mint failure aborts the deploy before any DB/host change.
|
|
24
|
+
* - Migration failure propagates; revokeOnce still runs in finally.
|
|
25
|
+
* - Credentials held only in memory; never logged (Codex F6.3).
|
|
26
|
+
*
|
|
27
|
+
* PACKAGE SEAM (znvault-migrate): the source (znvault-plugin-payara's
|
|
28
|
+
* src/run-migrations.ts) hard-imports a MySQL-only `openDb` + `MigrationRunner`.
|
|
29
|
+
* Here the DB-open + runner-construction are parameterized by an `EngineAdapter`
|
|
30
|
+
* selected from `opts.engine` (mysql only — postgres is a deferred adapter; see
|
|
31
|
+
* `selectAdapter` below). Everything else — lease mint, settle, revoke-retry,
|
|
32
|
+
* signal handlers, password redaction — is byte-identical to the source.
|
|
33
|
+
*/
|
|
34
|
+
import type { Lease } from './dynamic-secrets-client.js';
|
|
35
|
+
import type { EngineAdapter, Conn } from '../adapters/engine-adapter.js';
|
|
36
|
+
export interface RunMigrationsOpts {
|
|
37
|
+
/**
|
|
38
|
+
* The engine adapter selector. Only 'mysql' is implemented — 'postgres' is a
|
|
39
|
+
* valid literal (future-ready) but rejected at runtime by `selectAdapter`
|
|
40
|
+
* (defense-in-depth; mirrors the deferred-adapter guard in `config.ts`'s
|
|
41
|
+
* `validateMigrationConfig`).
|
|
42
|
+
*/
|
|
43
|
+
engine: 'mysql' | 'postgres';
|
|
44
|
+
env: string;
|
|
45
|
+
roleId: string;
|
|
46
|
+
/**
|
|
47
|
+
* Optional database name override.
|
|
48
|
+
*
|
|
49
|
+
* host/port/database are provided by the Vault dynamic-secrets connection
|
|
50
|
+
* (referenced by roleId) and returned with the lease — the deploy config
|
|
51
|
+
* only names the role + the migrations dir. This field lets the caller
|
|
52
|
+
* override the database name when the lease does not pin one.
|
|
53
|
+
*/
|
|
54
|
+
database?: string;
|
|
55
|
+
migrationsDir: string;
|
|
56
|
+
/**
|
|
57
|
+
* Additional migration directories that share this deploy's schema_migrations
|
|
58
|
+
* history table (the OTHER phase's dir — e.g. the pre-deploy dir when running the
|
|
59
|
+
* post-deploy phase). Used ONLY to widen the planner's orphan/checksum integrity
|
|
60
|
+
* lookup so a migration applied by a sibling phase is not mistaken for a
|
|
61
|
+
* renamed/deleted file. Absent/empty = single-directory behavior (unchanged).
|
|
62
|
+
*/
|
|
63
|
+
integrityDirs?: string[];
|
|
64
|
+
/**
|
|
65
|
+
* Optional migration-helper scaffolding SQL file, applied at the start of the
|
|
66
|
+
* migration phase and cleaned up (definer objects dropped) after reconcile.
|
|
67
|
+
* The cleanup target is the lease's own minted username — see where this is
|
|
68
|
+
* consumed below for the {filename, leaseUser} construction. Absent = no
|
|
69
|
+
* scaffolding (today's behavior, byte-identical).
|
|
70
|
+
*/
|
|
71
|
+
scaffoldingFile?: string;
|
|
72
|
+
}
|
|
73
|
+
/** Shape of a minimal dynamic-secrets client (for injection / testing). */
|
|
74
|
+
export interface DynamicSecretsClient {
|
|
75
|
+
issueCredential(roleId: string, opts: {
|
|
76
|
+
ttlSeconds: number;
|
|
77
|
+
}): Promise<Lease>;
|
|
78
|
+
revokeCredential(leaseId: string, opts: {
|
|
79
|
+
reason: string;
|
|
80
|
+
}): Promise<void>;
|
|
81
|
+
}
|
|
82
|
+
/** Injectable dependencies (real in production; mocked in tests). */
|
|
83
|
+
export interface RunMigrationsDeps {
|
|
84
|
+
client: DynamicSecretsClient;
|
|
85
|
+
openConnection(cfg: {
|
|
86
|
+
host: string;
|
|
87
|
+
port: number;
|
|
88
|
+
database: string;
|
|
89
|
+
user: string;
|
|
90
|
+
password: string;
|
|
91
|
+
ssl: boolean;
|
|
92
|
+
}): Promise<Conn>;
|
|
93
|
+
makeRunner(conn: Conn, dir: string, appliedBy: string, integrityDirs?: string[], scaffolding?: {
|
|
94
|
+
filename: string;
|
|
95
|
+
leaseUser: string;
|
|
96
|
+
}): {
|
|
97
|
+
run(): Promise<unknown>;
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* Override the settle delay applied after conn.end() and before revokeOnce().
|
|
101
|
+
* Set to 0 in tests to avoid real waiting. Production always uses REVOKE_SETTLE_MS.
|
|
102
|
+
*/
|
|
103
|
+
settleMs?: number;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Adapt a CLIPluginContext-style client (returns T directly) to the VaultHttp
|
|
107
|
+
* shape expected by makeDynamicSecretsClient ({ status, body }).
|
|
108
|
+
*
|
|
109
|
+
* The CLI client throws on non-2xx (see agentPost); on success it returns the
|
|
110
|
+
* parsed body directly. We wrap it so revokeCredential's isAlreadyGone() logic
|
|
111
|
+
* can inspect the status on thrown errors.
|
|
112
|
+
*
|
|
113
|
+
* The adapter is intentionally narrow — only `post` is needed.
|
|
114
|
+
*/
|
|
115
|
+
export declare function makeVaultHttpAdapter(client: {
|
|
116
|
+
post<T>(path: string, body: unknown): Promise<T>;
|
|
117
|
+
}): {
|
|
118
|
+
post(path: string, body: unknown): Promise<{
|
|
119
|
+
status: number;
|
|
120
|
+
body: unknown;
|
|
121
|
+
}>;
|
|
122
|
+
};
|
|
123
|
+
/**
|
|
124
|
+
* Build the real production deps.
|
|
125
|
+
*
|
|
126
|
+
* @param client - the vault CLI client (CLIPluginContext.client), used to mint/revoke leases.
|
|
127
|
+
* @param adapter - the selected EngineAdapter (see `selectAdapter` / `runMigrations`).
|
|
128
|
+
*/
|
|
129
|
+
export declare function defaultDeps(client: {
|
|
130
|
+
post<T>(path: string, body: unknown): Promise<T>;
|
|
131
|
+
}, adapter: EngineAdapter): RunMigrationsDeps;
|
|
132
|
+
/**
|
|
133
|
+
* How long to wait after conn.end() before calling revokeOnce() in the normal
|
|
134
|
+
* teardown path.
|
|
135
|
+
*
|
|
136
|
+
* WHY: conn.end() sends COM_QUIT to MySQL, but the server-side session lingers
|
|
137
|
+
* for a short window. The vault revoke path does:
|
|
138
|
+
* SELECT ID FROM processlist WHERE user=<ephemeral> → KILL sessions → DROP USER
|
|
139
|
+
* If the KILL races a just-closed session, the DROP USER fails transiently and
|
|
140
|
+
* vault marks the lease FAILED — subsequent retry attempts then hit
|
|
141
|
+
* "Cannot revoke failed lease" (vault's failed-lease guard), making all 3
|
|
142
|
+
* attempts fail. Waiting ~1.5s gives the server time to tear down the session
|
|
143
|
+
* so the revoke's KILL/DROP runs against a clean slate.
|
|
144
|
+
*
|
|
145
|
+
* This delay is ONLY in the normal finally teardown. The signal handler path
|
|
146
|
+
* calls revokeOnce() directly (inside withTimeout) and must stay fast.
|
|
147
|
+
*/
|
|
148
|
+
export declare const REVOKE_SETTLE_MS = 1500;
|
|
149
|
+
/**
|
|
150
|
+
* Run schema migrations as a deploy phase.
|
|
151
|
+
*
|
|
152
|
+
* 1. Mint a dynamic-secrets lease (TTL 4h).
|
|
153
|
+
* 2. Install signal handlers that await revoke (5s cap) before exiting.
|
|
154
|
+
* 3. Open a connection (via the engine adapter) with the ephemeral credentials.
|
|
155
|
+
* 4. ALWAYS call runner.run() — never skip (CRITICAL F4.1).
|
|
156
|
+
* 5. In finally: close the connection + revokeOnce + remove signal handlers.
|
|
157
|
+
*
|
|
158
|
+
* @param ctx - CLI plugin context (used for logging; pass {} as `any` in tests).
|
|
159
|
+
* @param opts - engine, env, roleId, migrationsDir, and an optional database override.
|
|
160
|
+
* host/port/database come from the Vault dynamic-secrets lease.
|
|
161
|
+
* @param deps - Injectable deps (real = defaultDeps(ctx.client, adapter); tests pass mocks).
|
|
162
|
+
*/
|
|
163
|
+
export declare function runMigrations(ctx: {
|
|
164
|
+
output?: {
|
|
165
|
+
info(msg: string): void;
|
|
166
|
+
warn(msg: string): void;
|
|
167
|
+
};
|
|
168
|
+
}, opts: RunMigrationsOpts, deps: RunMigrationsDeps): Promise<void>;
|
|
169
|
+
//# sourceMappingURL=run-migrations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-migrations.d.ts","sourceRoot":"","sources":["../../src/lease/run-migrations.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* run-migrations.ts — the deploy migration phase.
|
|
3
|
+
*
|
|
4
|
+
* Mints a dynamic-secrets lease (4h TTL), opens a connection via the selected
|
|
5
|
+
* engine adapter with the ephemeral credentials, runs the migration engine,
|
|
6
|
+
* and revokes the lease in the finally block and on SIGINT/SIGTERM.
|
|
7
|
+
*
|
|
8
|
+
* Design decisions (spec §run-migrations.ts + Codex):
|
|
9
|
+
* - 4h TTL (not 600s): Vault's cleanup job KILLs active DB sessions when a
|
|
10
|
+
* lease expires mid-DDL. A generous TTL + explicit revoke-on-exit is safer.
|
|
11
|
+
* - Migration-helper scaffolding (if `opts.scaffoldingFile` configured) is
|
|
12
|
+
* applied by the runner at the start of the phase, against the just-minted
|
|
13
|
+
* lease's own ephemeral user, and cleaned up (definer objects dropped)
|
|
14
|
+
* after reconcile — so the ephemeral migrate user never owns a routine
|
|
15
|
+
* past the phase and revokes cleanly.
|
|
16
|
+
* - ALWAYS call runner.run() — never short-circuit on status (CRITICAL F4.1):
|
|
17
|
+
* run() unconditionally executes pending migrations on every invocation; a
|
|
18
|
+
* status check short-circuit would leave pending migrations unapplied while
|
|
19
|
+
* reporting "up to date".
|
|
20
|
+
* - revokeOnce: single guarded revoke with 3× retry + backoff; 404/non-ACTIVE
|
|
21
|
+
* treated as success (Codex F6.1). Signal handlers await the revoke (5s cap)
|
|
22
|
+
* before exiting (Codex F6.4) so the in-flight HTTP request isn't aborted.
|
|
23
|
+
* - Lease mint failure aborts the deploy before any DB/host change.
|
|
24
|
+
* - Migration failure propagates; revokeOnce still runs in finally.
|
|
25
|
+
* - Credentials held only in memory; never logged (Codex F6.3).
|
|
26
|
+
*
|
|
27
|
+
* PACKAGE SEAM (znvault-migrate): the source (znvault-plugin-payara's
|
|
28
|
+
* src/run-migrations.ts) hard-imports a MySQL-only `openDb` + `MigrationRunner`.
|
|
29
|
+
* Here the DB-open + runner-construction are parameterized by an `EngineAdapter`
|
|
30
|
+
* selected from `opts.engine` (mysql only — postgres is a deferred adapter; see
|
|
31
|
+
* `selectAdapter` below). Everything else — lease mint, settle, revoke-retry,
|
|
32
|
+
* signal handlers, password redaction — is byte-identical to the source.
|
|
33
|
+
*/
|
|
34
|
+
import os from 'node:os';
|
|
35
|
+
import { makeDynamicSecretsClient } from './dynamic-secrets-client.js';
|
|
36
|
+
import { mysqlAdapter } from '../adapters/mysql/mysql-adapter.js';
|
|
37
|
+
import { MigrationRunner } from '../core/runner.js';
|
|
38
|
+
// ─── VaultHttp adapter ────────────────────────────────────────────────────────
|
|
39
|
+
/**
|
|
40
|
+
* Adapt a CLIPluginContext-style client (returns T directly) to the VaultHttp
|
|
41
|
+
* shape expected by makeDynamicSecretsClient ({ status, body }).
|
|
42
|
+
*
|
|
43
|
+
* The CLI client throws on non-2xx (see agentPost); on success it returns the
|
|
44
|
+
* parsed body directly. We wrap it so revokeCredential's isAlreadyGone() logic
|
|
45
|
+
* can inspect the status on thrown errors.
|
|
46
|
+
*
|
|
47
|
+
* The adapter is intentionally narrow — only `post` is needed.
|
|
48
|
+
*/
|
|
49
|
+
export function makeVaultHttpAdapter(client) {
|
|
50
|
+
return {
|
|
51
|
+
async post(path, body) {
|
|
52
|
+
// ctx.client.post<T> returns T on success, throws on non-2xx.
|
|
53
|
+
const result = await client.post(path, body);
|
|
54
|
+
return { status: 200, body: result };
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
// ─── Engine adapter selection ─────────────────────────────────────────────────
|
|
59
|
+
/**
|
|
60
|
+
* Select the concrete EngineAdapter for `opts.engine`. Only 'mysql' is
|
|
61
|
+
* implemented; 'postgres' throws the SAME deferred-error string used by
|
|
62
|
+
* `config.ts`'s `validateMigrationConfig` (em-dash U+2014 included), so a
|
|
63
|
+
* postgres engine is rejected consistently whether caught at config-validation
|
|
64
|
+
* time or here (defense-in-depth — see the doc comment on `RunMigrationsOpts.engine`).
|
|
65
|
+
*/
|
|
66
|
+
function selectAdapter(engine) {
|
|
67
|
+
if (engine === 'mysql')
|
|
68
|
+
return mysqlAdapter;
|
|
69
|
+
// postgres is a valid type literal but not implemented — mirror the config-validation deferral.
|
|
70
|
+
throw new Error('postgres migrations are not yet supported (the PostgreSQL adapter is a deferred project — see the znvault-migrate PostgreSQL design)');
|
|
71
|
+
}
|
|
72
|
+
// ─── Default production deps factory ─────────────────────────────────────────
|
|
73
|
+
/**
|
|
74
|
+
* Build the real production deps.
|
|
75
|
+
*
|
|
76
|
+
* @param client - the vault CLI client (CLIPluginContext.client), used to mint/revoke leases.
|
|
77
|
+
* @param adapter - the selected EngineAdapter (see `selectAdapter` / `runMigrations`).
|
|
78
|
+
*/
|
|
79
|
+
export function defaultDeps(client, adapter) {
|
|
80
|
+
const http = makeVaultHttpAdapter(client);
|
|
81
|
+
const dynamicClient = makeDynamicSecretsClient(http);
|
|
82
|
+
return {
|
|
83
|
+
client: dynamicClient,
|
|
84
|
+
openConnection: (cfg) => adapter.openConnection(cfg),
|
|
85
|
+
makeRunner(conn, dir, appliedBy, integrityDirs, scaffolding) {
|
|
86
|
+
return new MigrationRunner(adapter, conn, dir, appliedBy, integrityDirs ?? [], scaffolding);
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
// ─── revokeOnce with retry helper ────────────────────────────────────────────
|
|
91
|
+
const REVOKE_RETRY_DELAYS_MS = [200, 600]; // 3 attempts: immediate + 2 retries
|
|
92
|
+
/**
|
|
93
|
+
* Return true when the caught error indicates the lease is permanently in a
|
|
94
|
+
* non-revocable state (e.g. vault already marked it FAILED after a previous
|
|
95
|
+
* failed attempt). Retrying these is futile — bail out immediately.
|
|
96
|
+
*
|
|
97
|
+
* Examples from the vault server:
|
|
98
|
+
* "Cannot revoke failed lease"
|
|
99
|
+
* "Cannot revoke a FAILED lease"
|
|
100
|
+
*/
|
|
101
|
+
function isNonRevocable(e) {
|
|
102
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
103
|
+
return /cannot revoke .* lease|failed lease/i.test(msg);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Build a guarded single-shot revoke function with 3× retry + backoff.
|
|
107
|
+
*
|
|
108
|
+
* Idempotent: subsequent calls after the first are no-ops (the `revoked` flag
|
|
109
|
+
* is captured in closure so parallel signal + finally can't double-revoke).
|
|
110
|
+
*
|
|
111
|
+
* 404/410/non-ACTIVE response → already gone → resolved as success (F6.1 — handled
|
|
112
|
+
* inside revokeCredential, never reaches the catch here).
|
|
113
|
+
*
|
|
114
|
+
* Non-revocable lease (vault marked FAILED) → log WARN with real error, stop
|
|
115
|
+
* immediately without retrying — retrying cannot fix a FAILED-lease state.
|
|
116
|
+
*
|
|
117
|
+
* Other transient failures → log DEBUG + retry up to 3 times; on final failure
|
|
118
|
+
* log WARN with the real error and give up without throwing (cleanup job + 4h
|
|
119
|
+
* TTL will drop the user eventually).
|
|
120
|
+
*/
|
|
121
|
+
function makeRevokeOnce(client, lease, log) {
|
|
122
|
+
let revoked = false;
|
|
123
|
+
return async function revokeOnce() {
|
|
124
|
+
if (revoked)
|
|
125
|
+
return;
|
|
126
|
+
revoked = true;
|
|
127
|
+
const attempts = 1 + REVOKE_RETRY_DELAYS_MS.length; // 3 total
|
|
128
|
+
for (let attempt = 1; attempt <= attempts; attempt++) {
|
|
129
|
+
try {
|
|
130
|
+
await client.revokeCredential(lease.leaseId, { reason: 'migration complete' });
|
|
131
|
+
return; // success
|
|
132
|
+
}
|
|
133
|
+
catch (e) {
|
|
134
|
+
const errMsg = e instanceof Error ? e.message : String(e);
|
|
135
|
+
// Part C: if the lease is permanently non-revocable (vault already
|
|
136
|
+
// marked it FAILED), retrying is futile — stop immediately.
|
|
137
|
+
if (isNonRevocable(e)) {
|
|
138
|
+
log(`[run-migrations] WARN: revoke failed after ${attempt} attempt(s): ${errMsg}; ` +
|
|
139
|
+
`lease is in a non-revocable state — cleanup job + 4h TTL will drop the user. leaseId=${lease.leaseId}`);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
const isLast = attempt === attempts;
|
|
143
|
+
if (isLast) {
|
|
144
|
+
// All retries exhausted — log real error and give up; do NOT throw.
|
|
145
|
+
log(`[run-migrations] WARN: revoke failed after ${attempts} attempt(s): ${errMsg}; ` +
|
|
146
|
+
`cleanup job + 4h TTL will drop the user. leaseId=${lease.leaseId}`);
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
// Non-last transient failure: log attempt details then wait before retry.
|
|
150
|
+
const delayMs = REVOKE_RETRY_DELAYS_MS[attempt - 1] ?? 200;
|
|
151
|
+
log(`[run-migrations] revoke attempt ${attempt} failed: ${errMsg}; retrying in ${delayMs}ms`);
|
|
152
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
// ─── Settle delay constant ────────────────────────────────────────────────────
|
|
158
|
+
/**
|
|
159
|
+
* How long to wait after conn.end() before calling revokeOnce() in the normal
|
|
160
|
+
* teardown path.
|
|
161
|
+
*
|
|
162
|
+
* WHY: conn.end() sends COM_QUIT to MySQL, but the server-side session lingers
|
|
163
|
+
* for a short window. The vault revoke path does:
|
|
164
|
+
* SELECT ID FROM processlist WHERE user=<ephemeral> → KILL sessions → DROP USER
|
|
165
|
+
* If the KILL races a just-closed session, the DROP USER fails transiently and
|
|
166
|
+
* vault marks the lease FAILED — subsequent retry attempts then hit
|
|
167
|
+
* "Cannot revoke failed lease" (vault's failed-lease guard), making all 3
|
|
168
|
+
* attempts fail. Waiting ~1.5s gives the server time to tear down the session
|
|
169
|
+
* so the revoke's KILL/DROP runs against a clean slate.
|
|
170
|
+
*
|
|
171
|
+
* This delay is ONLY in the normal finally teardown. The signal handler path
|
|
172
|
+
* calls revokeOnce() directly (inside withTimeout) and must stay fast.
|
|
173
|
+
*/
|
|
174
|
+
export const REVOKE_SETTLE_MS = 1500;
|
|
175
|
+
// ─── withTimeout helper ───────────────────────────────────────────────────────
|
|
176
|
+
/**
|
|
177
|
+
* Race a promise against a timeout. Resolves (possibly undefined) on timeout
|
|
178
|
+
* so that a stuck revoke doesn't block process.exit indefinitely.
|
|
179
|
+
*/
|
|
180
|
+
function withTimeout(p, ms) {
|
|
181
|
+
return new Promise((resolve) => {
|
|
182
|
+
const timer = setTimeout(resolve, ms);
|
|
183
|
+
p.then(() => { clearTimeout(timer); resolve(); }, () => { clearTimeout(timer); resolve(); });
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
// ─── Core function ────────────────────────────────────────────────────────────
|
|
187
|
+
/**
|
|
188
|
+
* Run schema migrations as a deploy phase.
|
|
189
|
+
*
|
|
190
|
+
* 1. Mint a dynamic-secrets lease (TTL 4h).
|
|
191
|
+
* 2. Install signal handlers that await revoke (5s cap) before exiting.
|
|
192
|
+
* 3. Open a connection (via the engine adapter) with the ephemeral credentials.
|
|
193
|
+
* 4. ALWAYS call runner.run() — never skip (CRITICAL F4.1).
|
|
194
|
+
* 5. In finally: close the connection + revokeOnce + remove signal handlers.
|
|
195
|
+
*
|
|
196
|
+
* @param ctx - CLI plugin context (used for logging; pass {} as `any` in tests).
|
|
197
|
+
* @param opts - engine, env, roleId, migrationsDir, and an optional database override.
|
|
198
|
+
* host/port/database come from the Vault dynamic-secrets lease.
|
|
199
|
+
* @param deps - Injectable deps (real = defaultDeps(ctx.client, adapter); tests pass mocks).
|
|
200
|
+
*/
|
|
201
|
+
export async function runMigrations(ctx, opts, deps) {
|
|
202
|
+
// Validate the engine selector up front (defense-in-depth — see selectAdapter's
|
|
203
|
+
// doc comment). The real adapter used for the DB open + runner construction is
|
|
204
|
+
// the one already baked into `deps` by `defaultDeps`; this call exists solely
|
|
205
|
+
// so a postgres `opts.engine` is rejected even if deps were somehow built for
|
|
206
|
+
// an unsupported engine, mirroring config.ts's validation-time deferral.
|
|
207
|
+
selectAdapter(opts.engine);
|
|
208
|
+
const log = (msg) => {
|
|
209
|
+
if (ctx.output?.warn) {
|
|
210
|
+
ctx.output.warn(msg);
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
// eslint-disable-next-line no-console
|
|
214
|
+
console.warn(msg);
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
const info = (msg) => {
|
|
218
|
+
if (ctx.output?.info) {
|
|
219
|
+
ctx.output.info(msg);
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
// eslint-disable-next-line no-console
|
|
223
|
+
console.info(msg);
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
// ── Step 1: Mint lease (4h TTL). Failure aborts deploy before any host change. ──
|
|
227
|
+
const LEASE_TTL_SECONDS = 4 * 3600; // 14400 — NOT 600; see spec §run-migrations.ts
|
|
228
|
+
const lease = await deps.client.issueCredential(opts.roleId, { ttlSeconds: LEASE_TTL_SECONDS });
|
|
229
|
+
info(`[run-migrations] Lease minted: ${lease.leaseId} (TTL ${LEASE_TTL_SECONDS}s)`);
|
|
230
|
+
// ── Step 2: Build the guarded revoke function ──────────────────────────────
|
|
231
|
+
const revokeOnce = makeRevokeOnce(deps.client, lease, log);
|
|
232
|
+
// ── Step 3: Signal handlers — await revoke before exit (Codex F6.4) ───────
|
|
233
|
+
const onSignal = async () => {
|
|
234
|
+
log('[run-migrations] Signal received — revoking lease before exit...');
|
|
235
|
+
await withTimeout(revokeOnce(), 5000);
|
|
236
|
+
process.exit(1);
|
|
237
|
+
};
|
|
238
|
+
const sigintHandler = () => { void onSignal(); };
|
|
239
|
+
const sigtermHandler = () => { void onSignal(); };
|
|
240
|
+
process.on('SIGINT', sigintHandler);
|
|
241
|
+
process.on('SIGTERM', sigtermHandler);
|
|
242
|
+
// ── Step 4: Open connection + run engine ──────────────────────────────────
|
|
243
|
+
let conn;
|
|
244
|
+
try {
|
|
245
|
+
// Resolve the database name: prefer the lease's value (from the Vault connection),
|
|
246
|
+
// fall back to the opts override, and error if neither is available.
|
|
247
|
+
// This check is INSIDE the try so that revokeOnce fires in finally even on validation failure.
|
|
248
|
+
const database = lease.database ?? opts.database;
|
|
249
|
+
if (!database) {
|
|
250
|
+
throw new Error('[run-migrations] No database name: the Vault dynamic-secrets connection did not return a ' +
|
|
251
|
+
'database name and no database override was provided in the migration config.');
|
|
252
|
+
}
|
|
253
|
+
conn = await deps.openConnection({
|
|
254
|
+
host: lease.host,
|
|
255
|
+
port: lease.port,
|
|
256
|
+
database,
|
|
257
|
+
user: lease.username,
|
|
258
|
+
password: lease.password,
|
|
259
|
+
ssl: true,
|
|
260
|
+
});
|
|
261
|
+
const appliedBy = `${os.userInfo().username}@${os.hostname()}`;
|
|
262
|
+
// Scaffolding cleanup must target the lease's OWN minted username — that is
|
|
263
|
+
// the ephemeral identity that will own any definer objects created while the
|
|
264
|
+
// scaffolding file is applied, and the one whose leftover objects need
|
|
265
|
+
// dropping after reconcile. Absent opts.scaffoldingFile = no scaffolding
|
|
266
|
+
// (byte-identical to pre-scaffolding behavior).
|
|
267
|
+
const scaffolding = opts.scaffoldingFile
|
|
268
|
+
? { filename: opts.scaffoldingFile, leaseUser: lease.username }
|
|
269
|
+
: undefined;
|
|
270
|
+
// ALWAYS run() — NEVER short-circuit on status (CRITICAL F4.1).
|
|
271
|
+
// run() applies the scaffolding (if configured) and pending migrations;
|
|
272
|
+
// a status-check short-circuit would leave pending migrations unapplied
|
|
273
|
+
// while reporting "up to date".
|
|
274
|
+
const result = await deps
|
|
275
|
+
.makeRunner(conn, opts.migrationsDir, appliedBy, opts.integrityDirs, scaffolding)
|
|
276
|
+
.run();
|
|
277
|
+
info(`[run-migrations] Migrations complete: ${JSON.stringify(result)}`);
|
|
278
|
+
}
|
|
279
|
+
finally {
|
|
280
|
+
// ── Step 5: Teardown — close connection (best-effort) then revoke ────────
|
|
281
|
+
if (conn) {
|
|
282
|
+
await conn.end().catch(() => {
|
|
283
|
+
// best-effort; never mask the primary error
|
|
284
|
+
});
|
|
285
|
+
// Settle delay: give the server time to tear down the just-closed
|
|
286
|
+
// ephemeral session before the vault revoke's KILL/DROP runs.
|
|
287
|
+
// Without this, conn.end() (COM_QUIT) may still linger server-side,
|
|
288
|
+
// causing the revoke's KILL to race it → vault marks the lease FAILED
|
|
289
|
+
// → subsequent retry attempts hit "Cannot revoke failed lease".
|
|
290
|
+
// The signal handler path does NOT include this delay (stays fast).
|
|
291
|
+
// deps.settleMs overrides REVOKE_SETTLE_MS (set to 0 in tests to skip).
|
|
292
|
+
const settleMs = deps.settleMs ?? REVOKE_SETTLE_MS;
|
|
293
|
+
if (settleMs > 0) {
|
|
294
|
+
await new Promise((resolve) => setTimeout(resolve, settleMs));
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
await revokeOnce();
|
|
298
|
+
// Remove signal handlers to avoid memory leaks and double-handling.
|
|
299
|
+
process.removeListener('SIGINT', sigintHandler);
|
|
300
|
+
process.removeListener('SIGTERM', sigtermHandler);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-migrations.js","sourceRoot":"","sources":["../../src/lease/run-migrations.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@zincapp/znvault-migrate",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Shared MySQL schema-migration library for ZnVault deployer tooling",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"import": "./dist/index.js",
|
|
14
|
+
"types": "./dist/index.d.ts"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsc",
|
|
19
|
+
"build:prod": "tsc --sourceMap false",
|
|
20
|
+
"dev": "tsc --watch",
|
|
21
|
+
"clean": "rm -rf dist",
|
|
22
|
+
"test": "vitest run",
|
|
23
|
+
"test:watch": "vitest",
|
|
24
|
+
"test:coverage": "vitest run --coverage",
|
|
25
|
+
"lint": "ESLINT_USE_FLAT_CONFIG=false eslint 'src/**/*.ts' 'test/**/*.ts'",
|
|
26
|
+
"lint:fix": "ESLINT_USE_FLAT_CONFIG=false eslint 'src/**/*.ts' 'test/**/*.ts' --fix",
|
|
27
|
+
"typecheck": "tsc --noEmit",
|
|
28
|
+
"prepublishOnly": "npm run build:prod"
|
|
29
|
+
},
|
|
30
|
+
"keywords": [
|
|
31
|
+
"znvault",
|
|
32
|
+
"mysql",
|
|
33
|
+
"migration",
|
|
34
|
+
"schema",
|
|
35
|
+
"database"
|
|
36
|
+
],
|
|
37
|
+
"author": "ZincApp",
|
|
38
|
+
"license": "MIT",
|
|
39
|
+
"homepage": "https://github.com/vidaldiego/znvault-migrate#readme",
|
|
40
|
+
"bugs": {
|
|
41
|
+
"url": "https://github.com/vidaldiego/znvault-migrate/issues"
|
|
42
|
+
},
|
|
43
|
+
"repository": {
|
|
44
|
+
"type": "git",
|
|
45
|
+
"url": "git+https://github.com/vidaldiego/znvault-migrate.git"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"mysql2": "^3.16.1"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@types/node": "^25.0.10",
|
|
52
|
+
"@typescript-eslint/eslint-plugin": "^8.53.1",
|
|
53
|
+
"@typescript-eslint/parser": "^8.53.1",
|
|
54
|
+
"@vitest/coverage-v8": "^4.0.17",
|
|
55
|
+
"eslint": "^9.39.2",
|
|
56
|
+
"typescript": "^5.9.3",
|
|
57
|
+
"vitest": "^4.0.17"
|
|
58
|
+
},
|
|
59
|
+
"engines": {
|
|
60
|
+
"node": ">=18.0.0"
|
|
61
|
+
},
|
|
62
|
+
"publishConfig": {
|
|
63
|
+
"access": "public"
|
|
64
|
+
}
|
|
65
|
+
}
|