@aztec/end-to-end 0.0.1-commit.e6bd8901 → 0.0.1-commit.f146247c

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.
Files changed (43) hide show
  1. package/dest/e2e_epochs/epochs_test.d.ts +7 -1
  2. package/dest/e2e_epochs/epochs_test.d.ts.map +1 -1
  3. package/dest/e2e_epochs/epochs_test.js +28 -9
  4. package/dest/e2e_l1_publisher/write_json.d.ts +3 -2
  5. package/dest/e2e_l1_publisher/write_json.d.ts.map +1 -1
  6. package/dest/e2e_l1_publisher/write_json.js +1 -7
  7. package/dest/e2e_p2p/reqresp/utils.d.ts +22 -0
  8. package/dest/e2e_p2p/reqresp/utils.d.ts.map +1 -0
  9. package/dest/e2e_p2p/reqresp/utils.js +153 -0
  10. package/dest/e2e_p2p/shared.d.ts +1 -1
  11. package/dest/e2e_p2p/shared.d.ts.map +1 -1
  12. package/dest/e2e_p2p/shared.js +2 -2
  13. package/dest/fixtures/e2e_prover_test.js +1 -1
  14. package/dest/fixtures/setup.d.ts +3 -3
  15. package/dest/fixtures/setup.d.ts.map +1 -1
  16. package/dest/fixtures/setup.js +20 -15
  17. package/dest/fixtures/setup_p2p_test.d.ts +4 -5
  18. package/dest/fixtures/setup_p2p_test.d.ts.map +1 -1
  19. package/dest/fixtures/setup_p2p_test.js +24 -19
  20. package/dest/spartan/tx_metrics.d.ts +35 -1
  21. package/dest/spartan/tx_metrics.d.ts.map +1 -1
  22. package/dest/spartan/tx_metrics.js +150 -0
  23. package/dest/spartan/utils/index.d.ts +3 -3
  24. package/dest/spartan/utils/index.d.ts.map +1 -1
  25. package/dest/spartan/utils/index.js +2 -2
  26. package/dest/spartan/utils/k8s.d.ts +29 -1
  27. package/dest/spartan/utils/k8s.d.ts.map +1 -1
  28. package/dest/spartan/utils/k8s.js +118 -0
  29. package/dest/spartan/utils/nodes.d.ts +11 -1
  30. package/dest/spartan/utils/nodes.d.ts.map +1 -1
  31. package/dest/spartan/utils/nodes.js +198 -27
  32. package/package.json +39 -39
  33. package/src/e2e_epochs/epochs_test.ts +31 -10
  34. package/src/e2e_l1_publisher/write_json.ts +1 -6
  35. package/src/e2e_p2p/reqresp/utils.ts +207 -0
  36. package/src/e2e_p2p/shared.ts +10 -2
  37. package/src/fixtures/e2e_prover_test.ts +1 -1
  38. package/src/fixtures/setup.ts +13 -13
  39. package/src/fixtures/setup_p2p_test.ts +15 -20
  40. package/src/spartan/tx_metrics.ts +126 -0
  41. package/src/spartan/utils/index.ts +2 -0
  42. package/src/spartan/utils/k8s.ts +152 -0
  43. package/src/spartan/utils/nodes.ts +239 -24
@@ -4,6 +4,7 @@ import { retryUntil } from '@aztec/foundation/retry';
4
4
  import { exec, spawn } from 'child_process';
5
5
  import path from 'path';
6
6
  import { promisify } from 'util';
7
+ import { AlertTriggeredError, GrafanaClient } from '../../quality_of_service/grafana_client.js';
7
8
  const execAsync = promisify(exec);
8
9
  const logger = createLogger('e2e:k8s-utils');
9
10
  export async function startPortForward({ resource, namespace, containerPort, hostPort }) {
@@ -252,6 +253,123 @@ export async function waitForResourcesByName({ resource, names, namespace, condi
252
253
  }
253
254
  }));
254
255
  }
256
+ /**
257
+ * Waits for all StatefulSets matching a label to have all their replicas ready.
258
+ *
259
+ * @param namespace - Kubernetes namespace
260
+ * @param label - Label selector for StatefulSets (e.g., "app.kubernetes.io/component=sequencer-node")
261
+ * @param timeoutSeconds - Maximum time to wait in seconds
262
+ * @param pollIntervalSeconds - How often to check status
263
+ */ export async function waitForStatefulSetsReady({ namespace, label, timeoutSeconds = 600, pollIntervalSeconds = 5 }) {
264
+ logger.info(`Waiting for StatefulSets with label ${label} to have all replicas ready (timeout: ${timeoutSeconds}s)`);
265
+ await retryUntil(async ()=>{
266
+ // Get all StatefulSets matching the label
267
+ const getCmd = `kubectl get statefulset -l ${label} -n ${namespace} -o json`;
268
+ const { stdout } = await execAsync(getCmd);
269
+ const result = JSON.parse(stdout);
270
+ if (!result.items || result.items.length === 0) {
271
+ logger.verbose(`No StatefulSets found with label ${label}`);
272
+ return false;
273
+ }
274
+ // Check each StatefulSet
275
+ for (const sts of result.items){
276
+ const name = sts.metadata.name;
277
+ const desired = sts.spec.replicas ?? 0;
278
+ const ready = sts.status.readyReplicas ?? 0;
279
+ const updated = sts.status.updatedReplicas ?? 0;
280
+ if (ready < desired || updated < desired) {
281
+ logger.verbose(`StatefulSet ${name}: ${ready}/${desired} ready, ${updated}/${desired} updated`);
282
+ return false;
283
+ }
284
+ }
285
+ logger.info(`All StatefulSets with label ${label} are ready`);
286
+ return true;
287
+ }, `StatefulSets with label ${label} to be ready`, timeoutSeconds, pollIntervalSeconds);
288
+ }
289
+ /**
290
+ * Creates a Prometheus connection that can re-establish port-forward on failure.
291
+ * Returns functions to connect and run alert checks that automatically reconnect if needed.
292
+ *
293
+ * @param namespace - K8s namespace to fall back to if metrics namespace doesn't have Prometheus
294
+ * @param endpoints - Array to track created endpoints for cleanup
295
+ * @param log - Logger instance
296
+ */ export function createResilientPrometheusConnection(namespace, endpoints, log) {
297
+ let alertChecker;
298
+ let currentEndpoint;
299
+ const connect = async ()=>{
300
+ // Kill existing connection if any
301
+ if (currentEndpoint?.process) {
302
+ currentEndpoint.process.kill();
303
+ }
304
+ // Try metrics namespace first, then network namespace
305
+ let promPort = 0;
306
+ let promUrl = '';
307
+ let promProc;
308
+ try {
309
+ const metricsResult = await startPortForward({
310
+ resource: `svc/metrics-prometheus-server`,
311
+ namespace: 'metrics',
312
+ containerPort: 80
313
+ });
314
+ promProc = metricsResult.process;
315
+ promPort = metricsResult.port;
316
+ promUrl = `http://127.0.0.1:${promPort}/api/v1`;
317
+ } catch {
318
+ // Metrics namespace might not have Prometheus, try network namespace
319
+ log.verbose('Metrics namespace Prometheus not available, trying network namespace');
320
+ }
321
+ if (promPort === 0) {
322
+ const nsResult = await startPortForward({
323
+ resource: `svc/prometheus-server`,
324
+ namespace,
325
+ containerPort: 80
326
+ });
327
+ promProc = nsResult.process;
328
+ promPort = nsResult.port;
329
+ promUrl = `http://127.0.0.1:${promPort}/api/v1`;
330
+ }
331
+ if (!promProc || promPort === 0) {
332
+ throw new Error('Unable to port-forward to Prometheus');
333
+ }
334
+ currentEndpoint = {
335
+ url: promUrl,
336
+ process: promProc
337
+ };
338
+ endpoints.push(currentEndpoint);
339
+ alertChecker = new GrafanaClient(log, {
340
+ grafanaEndpoint: promUrl,
341
+ grafanaCredentials: ''
342
+ });
343
+ log.info(`Established Prometheus connection at ${promUrl}`);
344
+ return alertChecker;
345
+ };
346
+ const runAlertCheck = async (alerts)=>{
347
+ if (!alertChecker) {
348
+ alertChecker = await connect();
349
+ }
350
+ try {
351
+ await alertChecker.runAlertCheck(alerts);
352
+ } catch (err) {
353
+ // If it's an AlertTriggeredError (expected behavior)
354
+ if (err instanceof AlertTriggeredError) {
355
+ throw err;
356
+ }
357
+ // Check if it's a connection error (port-forward died)
358
+ const errorStr = String(err);
359
+ if (errorStr.includes('fetch failed') || errorStr.includes('ECONNREFUSED') || errorStr.includes('ECONNRESET')) {
360
+ log.warn(`Prometheus connection lost, re-establishing port-forward...`);
361
+ alertChecker = await connect();
362
+ await alertChecker.runAlertCheck(alerts);
363
+ } else {
364
+ throw err;
365
+ }
366
+ }
367
+ };
368
+ return {
369
+ connect,
370
+ runAlertCheck
371
+ };
372
+ }
255
373
  export function getChartDir(spartanDir, chartName) {
256
374
  return path.join(spartanDir.trim(), chartName);
257
375
  }
@@ -4,10 +4,20 @@ import type { Logger } from '@aztec/foundation/log';
4
4
  import { type AztecNodeAdmin, type AztecNodeAdminConfig } from '@aztec/stdlib/interfaces/client';
5
5
  import type { TestConfig } from './config.js';
6
6
  export declare function awaitCheckpointNumber(rollupCheatCodes: RollupCheatCodes, checkpointNumber: CheckpointNumber, timeoutSeconds: number, log: Logger): Promise<void>;
7
+ /**
8
+ * Waits until the proven block number increases.
9
+ *
10
+ * @param rpcUrl - URL of an Aztec RPC node to query
11
+ * @param log - Logger instance
12
+ * @param timeoutSeconds - Maximum time to wait
13
+ * @param pollIntervalSeconds - How often to check
14
+ */
15
+ export declare function waitForProvenToAdvance(rpcUrl: string, log: Logger, timeoutSeconds?: number, pollIntervalSeconds?: number): Promise<void>;
7
16
  export declare function getSequencers(namespace: string): Promise<string[]>;
8
17
  export declare function updateSequencersConfig(env: TestConfig, config: Partial<AztecNodeAdminConfig>): Promise<AztecNodeAdminConfig[]>;
9
18
  export declare function getSequencersConfig(env: TestConfig): Promise<AztecNodeAdminConfig[]>;
10
19
  export declare function withSequencersAdmin<T>(env: TestConfig, fn: (node: AztecNodeAdmin) => Promise<T>): Promise<T[]>;
20
+ export declare function initHADb(namespace: string): Promise<void>;
11
21
  /**
12
22
  * Enables or disables probabilistic transaction dropping on validators and waits for rollout.
13
23
  * Wired to env vars P2P_DROP_TX and P2P_DROP_TX_CHANCE via Helm values.
@@ -28,4 +38,4 @@ export declare function enableValidatorDynamicBootNode(instanceName: string, nam
28
38
  * Defaults to false, which preserves the existing storage.
29
39
  */
30
40
  export declare function rollAztecPods(namespace: string, clearState?: boolean): Promise<void>;
31
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibm9kZXMuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9zcGFydGFuL3V0aWxzL25vZGVzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUNBLE9BQU8sS0FBSyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDN0QsT0FBTyxLQUFLLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUN4RSxPQUFPLEtBQUssRUFBRSxNQUFNLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUdwRCxPQUFPLEVBQ0wsS0FBSyxjQUFjLEVBQ25CLEtBQUssb0JBQW9CLEVBRTFCLE1BQU0saUNBQWlDLENBQUM7QUFLekMsT0FBTyxLQUFLLEVBQUUsVUFBVSxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBUTlDLHdCQUFzQixxQkFBcUIsQ0FDekMsZ0JBQWdCLEVBQUUsZ0JBQWdCLEVBQ2xDLGdCQUFnQixFQUFFLGdCQUFnQixFQUNsQyxjQUFjLEVBQUUsTUFBTSxFQUN0QixHQUFHLEVBQUUsTUFBTSxpQkFlWjtBQUVELHdCQUFzQixhQUFhLENBQUMsU0FBUyxFQUFFLE1BQU0scUJBNEJwRDtBQUVELHdCQUFnQixzQkFBc0IsQ0FBQyxHQUFHLEVBQUUsVUFBVSxFQUFFLE1BQU0sRUFBRSxPQUFPLENBQUMsb0JBQW9CLENBQUMsbUNBSzVGO0FBRUQsd0JBQWdCLG1CQUFtQixDQUFDLEdBQUcsRUFBRSxVQUFVLG1DQUVsRDtBQUVELHdCQUFzQixtQkFBbUIsQ0FBQyxDQUFDLEVBQUUsR0FBRyxFQUFFLFVBQVUsRUFBRSxFQUFFLEVBQUUsQ0FBQyxJQUFJLEVBQUUsY0FBYyxLQUFLLE9BQU8sQ0FBQyxDQUFDLENBQUMsR0FBRyxPQUFPLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0EwQ3BIO0FBRUQ7OztHQUdHO0FBQ0gsd0JBQXNCLGtCQUFrQixDQUFDLEVBQ3ZDLFNBQVMsRUFDVCxPQUFPLEVBQ1AsV0FBVyxFQUNYLE1BQU0sRUFBRSxHQUFHLEVBQ1osRUFBRTtJQUNELFNBQVMsRUFBRSxNQUFNLENBQUM7SUFDbEIsT0FBTyxFQUFFLE9BQU8sQ0FBQztJQUNqQixXQUFXLEVBQUUsTUFBTSxDQUFDO0lBQ3BCLE1BQU0sRUFBRSxNQUFNLENBQUM7Q0FDaEIsaUJBZ0NBO0FBRUQsd0JBQXNCLGlCQUFpQixDQUFDLFNBQVMsRUFBRSxNQUFNLEVBQUUsR0FBRyxFQUFFLE1BQU0saUJBK0JyRTtBQUVELHdCQUFzQiw4QkFBOEIsQ0FDbEQsWUFBWSxFQUFFLE1BQU0sRUFDcEIsU0FBUyxFQUFFLE1BQU0sRUFDakIsVUFBVSxFQUFFLE1BQU0sRUFDbEIsR0FBRyxFQUFFLE1BQU0saUJBZ0JaO0FBRUQ7Ozs7OztHQU1HO0FBQ0gsd0JBQXNCLGFBQWEsQ0FBQyxTQUFTLEVBQUUsTUFBTSxFQUFFLFVBQVUsR0FBRSxPQUFlLGlCQW1GakYifQ==
41
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibm9kZXMuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9zcGFydGFuL3V0aWxzL25vZGVzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUVBLE9BQU8sS0FBSyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDN0QsT0FBTyxLQUFLLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUN4RSxPQUFPLEtBQUssRUFBRSxNQUFNLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUdwRCxPQUFPLEVBQ0wsS0FBSyxjQUFjLEVBQ25CLEtBQUssb0JBQW9CLEVBRTFCLE1BQU0saUNBQWlDLENBQUM7QUFLekMsT0FBTyxLQUFLLEVBQUUsVUFBVSxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBZTlDLHdCQUFzQixxQkFBcUIsQ0FDekMsZ0JBQWdCLEVBQUUsZ0JBQWdCLEVBQ2xDLGdCQUFnQixFQUFFLGdCQUFnQixFQUNsQyxjQUFjLEVBQUUsTUFBTSxFQUN0QixHQUFHLEVBQUUsTUFBTSxpQkFlWjtBQUVEOzs7Ozs7O0dBT0c7QUFDSCx3QkFBc0Isc0JBQXNCLENBQzFDLE1BQU0sRUFBRSxNQUFNLEVBQ2QsR0FBRyxFQUFFLE1BQU0sRUFDWCxjQUFjLEdBQUUsTUFBWSxFQUM1QixtQkFBbUIsR0FBRSxNQUFXLEdBQy9CLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0EwQ2Y7QUFFRCx3QkFBc0IsYUFBYSxDQUFDLFNBQVMsRUFBRSxNQUFNLHFCQTRCcEQ7QUFFRCx3QkFBZ0Isc0JBQXNCLENBQUMsR0FBRyxFQUFFLFVBQVUsRUFBRSxNQUFNLEVBQUUsT0FBTyxDQUFDLG9CQUFvQixDQUFDLG1DQUs1RjtBQUVELHdCQUFnQixtQkFBbUIsQ0FBQyxHQUFHLEVBQUUsVUFBVSxtQ0FFbEQ7QUFFRCx3QkFBc0IsbUJBQW1CLENBQUMsQ0FBQyxFQUFFLEdBQUcsRUFBRSxVQUFVLEVBQUUsRUFBRSxFQUFFLENBQUMsSUFBSSxFQUFFLGNBQWMsS0FBSyxPQUFPLENBQUMsQ0FBQyxDQUFDLEdBQUcsT0FBTyxDQUFDLENBQUMsRUFBRSxDQUFDLENBNENwSDtBQWtDRCx3QkFBc0IsUUFBUSxDQUFDLFNBQVMsRUFBRSxNQUFNLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxDQTBCL0Q7QUFFRDs7O0dBR0c7QUFDSCx3QkFBc0Isa0JBQWtCLENBQUMsRUFDdkMsU0FBUyxFQUNULE9BQU8sRUFDUCxXQUFXLEVBQ1gsTUFBTSxFQUFFLEdBQUcsRUFDWixFQUFFO0lBQ0QsU0FBUyxFQUFFLE1BQU0sQ0FBQztJQUNsQixPQUFPLEVBQUUsT0FBTyxDQUFDO0lBQ2pCLFdBQVcsRUFBRSxNQUFNLENBQUM7SUFDcEIsTUFBTSxFQUFFLE1BQU0sQ0FBQztDQUNoQixpQkFnQ0E7QUFFRCx3QkFBc0IsaUJBQWlCLENBQUMsU0FBUyxFQUFFLE1BQU0sRUFBRSxHQUFHLEVBQUUsTUFBTSxpQkErQnJFO0FBRUQsd0JBQXNCLDhCQUE4QixDQUNsRCxZQUFZLEVBQUUsTUFBTSxFQUNwQixTQUFTLEVBQUUsTUFBTSxFQUNqQixVQUFVLEVBQUUsTUFBTSxFQUNsQixHQUFHLEVBQUUsTUFBTSxpQkFnQlo7QUFFRDs7Ozs7O0dBTUc7QUFDSCx3QkFBc0IsYUFBYSxDQUFDLFNBQVMsRUFBRSxNQUFNLEVBQUUsVUFBVSxHQUFFLE9BQWUsaUJBMktqRiJ9
@@ -1 +1 @@
1
- {"version":3,"file":"nodes.d.ts","sourceRoot":"","sources":["../../../src/spartan/utils/nodes.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAGpD,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,oBAAoB,EAE1B,MAAM,iCAAiC,CAAC;AAKzC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAQ9C,wBAAsB,qBAAqB,CACzC,gBAAgB,EAAE,gBAAgB,EAClC,gBAAgB,EAAE,gBAAgB,EAClC,cAAc,EAAE,MAAM,EACtB,GAAG,EAAE,MAAM,iBAeZ;AAED,wBAAsB,aAAa,CAAC,SAAS,EAAE,MAAM,qBA4BpD;AAED,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,oBAAoB,CAAC,mCAK5F;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,UAAU,mCAElD;AAED,wBAAsB,mBAAmB,CAAC,CAAC,EAAE,GAAG,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC,CA0CpH;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,EACvC,SAAS,EACT,OAAO,EACP,WAAW,EACX,MAAM,EAAE,GAAG,EACZ,EAAE;IACD,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB,iBAgCA;AAED,wBAAsB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,iBA+BrE;AAED,wBAAsB,8BAA8B,CAClD,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,MAAM,iBAgBZ;AAED;;;;;;GAMG;AACH,wBAAsB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,GAAE,OAAe,iBAmFjF"}
1
+ {"version":3,"file":"nodes.d.ts","sourceRoot":"","sources":["../../../src/spartan/utils/nodes.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAGpD,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,oBAAoB,EAE1B,MAAM,iCAAiC,CAAC;AAKzC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAe9C,wBAAsB,qBAAqB,CACzC,gBAAgB,EAAE,gBAAgB,EAClC,gBAAgB,EAAE,gBAAgB,EAClC,cAAc,EAAE,MAAM,EACtB,GAAG,EAAE,MAAM,iBAeZ;AAED;;;;;;;GAOG;AACH,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACX,cAAc,GAAE,MAAY,EAC5B,mBAAmB,GAAE,MAAW,GAC/B,OAAO,CAAC,IAAI,CAAC,CA0Cf;AAED,wBAAsB,aAAa,CAAC,SAAS,EAAE,MAAM,qBA4BpD;AAED,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,oBAAoB,CAAC,mCAK5F;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,UAAU,mCAElD;AAED,wBAAsB,mBAAmB,CAAC,CAAC,EAAE,GAAG,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC,CA4CpH;AAkCD,wBAAsB,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA0B/D;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,EACvC,SAAS,EACT,OAAO,EACP,WAAW,EACX,MAAM,EAAE,GAAG,EACZ,EAAE;IACD,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB,iBAgCA;AAED,wBAAsB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,iBA+BrE;AAED,wBAAsB,8BAA8B,CAClD,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,MAAM,iBAgBZ;AAED;;;;;;GAMG;AACH,wBAAsB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,GAAE,OAAe,iBA2KjF"}
@@ -1,11 +1,12 @@
1
1
  import { createLogger } from '@aztec/aztec.js/log';
2
- import { makeBackoff, retry } from '@aztec/foundation/retry';
2
+ import { createAztecNodeClient } from '@aztec/aztec.js/node';
3
+ import { makeBackoff, retry, retryUntil } from '@aztec/foundation/retry';
3
4
  import { sleep } from '@aztec/foundation/sleep';
4
5
  import { createAztecNodeAdminClient } from '@aztec/stdlib/interfaces/client';
5
6
  import { exec } from 'child_process';
6
7
  import { promisify } from 'util';
7
8
  import { execHelmCommand } from './helm.js';
8
- import { deleteResourceByLabel, getChartDir, startPortForward, waitForResourceByLabel } from './k8s.js';
9
+ import { deleteResourceByLabel, getChartDir, startPortForward, waitForResourceByLabel, waitForResourceByName, waitForStatefulSetsReady } from './k8s.js';
9
10
  const execAsync = promisify(exec);
10
11
  const logger = createLogger('e2e:k8s-utils');
11
12
  export async function awaitCheckpointNumber(rollupCheatCodes, checkpointNumber, timeoutSeconds, log) {
@@ -23,6 +24,43 @@ export async function awaitCheckpointNumber(rollupCheatCodes, checkpointNumber,
23
24
  log.info(`Reached checkpoint ${tips.pending}`);
24
25
  }
25
26
  }
27
+ /**
28
+ * Waits until the proven block number increases.
29
+ *
30
+ * @param rpcUrl - URL of an Aztec RPC node to query
31
+ * @param log - Logger instance
32
+ * @param timeoutSeconds - Maximum time to wait
33
+ * @param pollIntervalSeconds - How often to check
34
+ */ export async function waitForProvenToAdvance(rpcUrl, log, timeoutSeconds = 300, pollIntervalSeconds = 12) {
35
+ const node = createAztecNodeClient(rpcUrl);
36
+ log.info('Waiting for proven block to advance (indicating epoch proof just submitted)...');
37
+ // Get current proven block number
38
+ let initialProvenBlock;
39
+ try {
40
+ const tips = await node.getL2Tips();
41
+ initialProvenBlock = Number(tips.proven.block.number);
42
+ log.info(`Current proven block: ${initialProvenBlock}. Waiting for it to increase...`);
43
+ } catch (err) {
44
+ log.warn(`Error getting initial tips: ${err}. Will poll until successful.`);
45
+ initialProvenBlock = 0;
46
+ }
47
+ await retryUntil(async ()=>{
48
+ try {
49
+ const tips = await node.getL2Tips();
50
+ const currentProvenBlock = Number(tips.proven.block.number);
51
+ const proposedBlock = Number(tips.proposed.number);
52
+ log.verbose(`Chain state: proposed=${proposedBlock}, proven=${currentProvenBlock} (waiting for > ${initialProvenBlock})`);
53
+ if (currentProvenBlock > initialProvenBlock) {
54
+ log.info(`Proven block advanced from ${initialProvenBlock} to ${currentProvenBlock}.`);
55
+ return true;
56
+ }
57
+ return false;
58
+ } catch (err) {
59
+ log.verbose(`Error checking tips: ${err}`);
60
+ return false;
61
+ }
62
+ }, 'proven block to advance', timeoutSeconds, pollIntervalSeconds);
63
+ }
26
64
  export async function getSequencers(namespace) {
27
65
  const selectors = [
28
66
  'app.kubernetes.io/name=validator',
@@ -61,9 +99,15 @@ export async function withSequencersAdmin(env, fn) {
61
99
  const sequencers = await getSequencers(namespace);
62
100
  const results = [];
63
101
  for (const sequencer of sequencers){
102
+ // Ensure pod is Ready before attempting port-forward.
103
+ await waitForResourceByName({
104
+ resource: 'pods',
105
+ name: sequencer,
106
+ namespace
107
+ });
64
108
  // Wrap port-forward + fetch in a retry to handle flaky port-forwards
65
109
  const result = await retry(async ()=>{
66
- const { process, port } = await startPortForward({
110
+ const { process: process1, port } = await startPortForward({
67
111
  resource: `pod/${sequencer}`,
68
112
  namespace,
69
113
  containerPort: adminContainerPort
@@ -78,11 +122,11 @@ export async function withSequencersAdmin(env, fn) {
78
122
  const client = createAztecNodeAdminClient(url);
79
123
  return {
80
124
  result: await fn(client),
81
- process
125
+ process: process1
82
126
  };
83
127
  } catch (err) {
84
128
  // Kill the port-forward before retrying
85
- process.kill();
129
+ process1.kill();
86
130
  throw err;
87
131
  }
88
132
  }, 'connect to node admin', makeBackoff([
@@ -96,6 +140,58 @@ export async function withSequencersAdmin(env, fn) {
96
140
  }
97
141
  return results;
98
142
  }
143
+ async function getAztecImageForMigrations(namespace) {
144
+ const aztecDockerImage = process.env.AZTEC_DOCKER_IMAGE;
145
+ if (aztecDockerImage) {
146
+ return aztecDockerImage;
147
+ }
148
+ const { stdout } = await execAsync(`kubectl get pods -l app.kubernetes.io/name=validator -n ${namespace} -o jsonpath='{.items[0].spec.containers[?(@.name=="aztec")].image}' | cat`);
149
+ const image = stdout.trim().replace(/^'|'$/g, '');
150
+ if (!image) {
151
+ throw new Error(`Could not detect aztec image from validator pod in namespace ${namespace}`);
152
+ }
153
+ return image;
154
+ }
155
+ async function getHaDbConnectionUrl(namespace) {
156
+ const secretName = `${namespace}-validator-ha-db-postgres`;
157
+ const { stdout } = await execAsync(`kubectl get secret ${secretName} -n ${namespace} -o json`);
158
+ const secret = JSON.parse(stdout);
159
+ const data = secret?.data ?? {};
160
+ const decode = (value)=>value ? Buffer.from(value, 'base64').toString('utf8') : '';
161
+ const user = decode(data.POSTGRES_USER);
162
+ const password = decode(data.POSTGRES_PASSWORD);
163
+ const database = decode(data.POSTGRES_DB);
164
+ if (!user || !password || !database) {
165
+ throw new Error(`Missing HA DB credentials in secret ${secretName}`);
166
+ }
167
+ const host = `${namespace}-validator-ha-db-postgres.${namespace}.svc.cluster.local`;
168
+ return `postgresql://${encodeURIComponent(user)}:${encodeURIComponent(password)}@${host}:5432/${database}`;
169
+ }
170
+ export async function initHADb(namespace) {
171
+ const databaseUrl = await getHaDbConnectionUrl(namespace);
172
+ const image = await getAztecImageForMigrations(namespace);
173
+ const jobName = `${namespace}-validator-ha-db-migrate`;
174
+ await execAsync(`kubectl delete pod ${jobName} -n ${namespace} --ignore-not-found=true`).catch(()=>undefined);
175
+ const migrateCmd = [
176
+ `kubectl run ${jobName} -n ${namespace}`,
177
+ '--rm -i',
178
+ '--restart=Never',
179
+ `--image=${image}`,
180
+ `--env=DATABASE_URL=${databaseUrl}`,
181
+ '--command -- node --no-warnings /usr/src/yarn-project/aztec/dest/bin/index.js migrate-ha-db up'
182
+ ].join(' ');
183
+ const migrateCmdForLog = migrateCmd.replace(/--env=DATABASE_URL=\S+/, '--env=DATABASE_URL=<redacted>');
184
+ await retry(async ()=>{
185
+ logger.info(`command: ${migrateCmdForLog}`);
186
+ await execAsync(migrateCmd);
187
+ }, 'run HA DB migrations', makeBackoff([
188
+ 1,
189
+ 2,
190
+ 4,
191
+ 8,
192
+ 16
193
+ ]), logger, true);
194
+ }
99
195
  /**
100
196
  * Enables or disables probabilistic transaction dropping on validators and waits for rollout.
101
197
  * Wired to env vars P2P_DROP_TX and P2P_DROP_TX_CHANCE via Helm values.
@@ -201,14 +297,16 @@ export async function enableValidatorDynamicBootNode(instanceName, namespace, sp
201
297
  'prover-broker',
202
298
  'prover-agent',
203
299
  'sequencer-node',
204
- 'rpc'
300
+ 'rpc',
301
+ 'validator-ha-db'
205
302
  ];
206
303
  const pvcComponents = [
207
304
  'p2p-bootstrap',
208
305
  'prover-node',
209
306
  'prover-broker',
210
307
  'sequencer-node',
211
- 'rpc'
308
+ 'rpc',
309
+ 'validator-ha-db'
212
310
  ];
213
311
  // StatefulSet components that need to be scaled down before PVC deletion
214
312
  // Note: validators use 'sequencer-node' as component label, not 'validator'
@@ -217,20 +315,27 @@ export async function enableValidatorDynamicBootNode(instanceName, namespace, sp
217
315
  'prover-node',
218
316
  'prover-broker',
219
317
  'sequencer-node',
220
- 'rpc'
318
+ 'rpc',
319
+ 'validator-ha-db'
221
320
  ];
222
321
  if (clearState) {
223
322
  // To delete PVCs, we must first scale down StatefulSets so pods release the volumes
224
323
  // Otherwise PVC deletion will hang waiting for pods to terminate
225
- // First, save original replica counts
324
+ // Save original replica counts for all StatefulSets
226
325
  const originalReplicas = new Map();
227
326
  for (const component of statefulSetComponents){
228
327
  try {
229
- const getCmd = `kubectl get statefulset -l app.kubernetes.io/component=${component} -n ${namespace} -o jsonpath='{.items[0].spec.replicas}'`;
328
+ // Get all StatefulSets that match the component label
329
+ const getCmd = `kubectl get statefulset -l app.kubernetes.io/component=${component} -n ${namespace} -o json`;
230
330
  const { stdout } = await execAsync(getCmd);
231
- const replicas = parseInt(stdout.replace(/'/g, '').trim(), 10);
232
- if (!isNaN(replicas) && replicas > 0) {
233
- originalReplicas.set(component, replicas);
331
+ const result = JSON.parse(stdout);
332
+ for (const sts of result.items || []){
333
+ const name = sts.metadata.name;
334
+ const replicas = sts.spec.replicas ?? 1;
335
+ if (replicas > 0) {
336
+ originalReplicas.set(name, replicas);
337
+ logger.debug(`Saved replica count for StatefulSet ${name}: ${replicas}`);
338
+ }
234
339
  }
235
340
  } catch {
236
341
  // Component might not exist, continue
@@ -247,25 +352,79 @@ export async function enableValidatorDynamicBootNode(instanceName, namespace, sp
247
352
  logger.verbose(`Scale down ${component} skipped: ${e}`);
248
353
  }
249
354
  }
250
- // Wait for pods to terminate
251
- await sleep(15 * 1000);
355
+ // Wait for all pods to fully terminate before deleting PVCs.
356
+ // terminationGracePeriodSeconds default is 30s.
357
+ logger.info('Waiting for pods to fully terminate before deleting PVCs...');
358
+ for (const component of statefulSetComponents){
359
+ try {
360
+ // Wait for all pods with this component label to be deleted
361
+ const waitCmd = `kubectl wait pods -l app.kubernetes.io/component=${component} --for=delete -n ${namespace} --timeout=2m`;
362
+ logger.info(`command: ${waitCmd}`);
363
+ await execAsync(waitCmd);
364
+ } catch (e) {
365
+ logger.verbose(`Wait for pod deletion ${component} skipped: ${e}`);
366
+ }
367
+ }
368
+ // Extra buffer to ensure PVC protection finalizers are cleared
369
+ await sleep(5 * 1000);
252
370
  // Now delete PVCs (they should no longer be in use)
253
371
  for (const component of pvcComponents){
254
- await deleteResourceByLabel({
255
- resource: 'persistentvolumeclaims',
256
- namespace: namespace,
257
- label: `app.kubernetes.io/component=${component}`
258
- });
372
+ try {
373
+ await deleteResourceByLabel({
374
+ resource: 'persistentvolumeclaims',
375
+ namespace: namespace,
376
+ label: `app.kubernetes.io/component=${component}`
377
+ });
378
+ } catch (e) {
379
+ logger.warn(`Failed to delete PVCs for ${component}: ${e}`);
380
+ }
259
381
  }
260
- // Scale StatefulSets back up to original replica counts
261
- for (const component of statefulSetComponents){
262
- const replicas = originalReplicas.get(component) ?? 1;
382
+ // Verify PVCs are deleted
383
+ for (const component of pvcComponents){
384
+ try {
385
+ const waitCmd = `kubectl wait pvc -l app.kubernetes.io/component=${component} --for=delete -n ${namespace} --timeout=2m`;
386
+ logger.info(`command: ${waitCmd}`);
387
+ await execAsync(waitCmd);
388
+ } catch (e) {
389
+ logger.verbose(`Wait for PVC deletion ${component} skipped: ${e}`);
390
+ }
391
+ }
392
+ const haDbStatefulSets = [
393
+ ...originalReplicas.entries()
394
+ ].filter(([name])=>name.includes('validator-ha-db'));
395
+ const otherStatefulSets = [
396
+ ...originalReplicas.entries()
397
+ ].filter(([name])=>!name.includes('validator-ha-db'));
398
+ // Bring up HA DB first so we can run migrations before validators start
399
+ for (const [stsName, replicas] of haDbStatefulSets){
400
+ try {
401
+ const scaleCmd = `kubectl scale statefulset ${stsName} -n ${namespace} --replicas=${replicas} --timeout=2m`;
402
+ logger.info(`command: ${scaleCmd}`);
403
+ await execAsync(scaleCmd);
404
+ } catch (e) {
405
+ logger.verbose(`Scale up ${stsName} skipped: ${e}`);
406
+ }
407
+ }
408
+ if (haDbStatefulSets.length > 0) {
409
+ try {
410
+ await waitForStatefulSetsReady({
411
+ namespace,
412
+ label: 'app.kubernetes.io/component=validator-ha-db',
413
+ timeoutSeconds: 600
414
+ });
415
+ await initHADb(namespace);
416
+ } catch (e) {
417
+ logger.warn(`HA DB migration step skipped or failed: ${e}`);
418
+ }
419
+ }
420
+ // Scale remaining StatefulSets back up to original replica counts (by name, not label)
421
+ for (const [stsName, replicas] of otherStatefulSets){
263
422
  try {
264
- const scaleCmd = `kubectl scale statefulset -l app.kubernetes.io/component=${component} -n ${namespace} --replicas=${replicas} --timeout=2m`;
423
+ const scaleCmd = `kubectl scale statefulset ${stsName} -n ${namespace} --replicas=${replicas} --timeout=2m`;
265
424
  logger.info(`command: ${scaleCmd}`);
266
425
  await execAsync(scaleCmd);
267
426
  } catch (e) {
268
- logger.verbose(`Scale up ${component} skipped: ${e}`);
427
+ logger.verbose(`Scale up ${stsName} skipped: ${e}`);
269
428
  }
270
429
  }
271
430
  } else {
@@ -279,8 +438,20 @@ export async function enableValidatorDynamicBootNode(instanceName, namespace, sp
279
438
  }
280
439
  }
281
440
  await sleep(10 * 1000);
282
- // Wait for pods to come back
283
- for (const component of podComponents){
441
+ // Wait for StatefulSets to have all replicas ready.
442
+ for (const component of statefulSetComponents){
443
+ try {
444
+ await waitForStatefulSetsReady({
445
+ namespace,
446
+ label: `app.kubernetes.io/component=${component}`,
447
+ timeoutSeconds: 600
448
+ });
449
+ } catch (e) {
450
+ logger.warn(`StatefulSet component ${component} may not be fully ready: ${e}`);
451
+ }
452
+ }
453
+ const nonStatefulSetComponents = podComponents.filter((c)=>!statefulSetComponents.includes(c));
454
+ for (const component of nonStatefulSetComponents){
284
455
  await waitForResourceByLabel({
285
456
  resource: 'pods',
286
457
  namespace: namespace,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aztec/end-to-end",
3
- "version": "0.0.1-commit.e6bd8901",
3
+ "version": "0.0.1-commit.f146247c",
4
4
  "type": "module",
5
5
  "exports": "./dest/index.js",
6
6
  "inherits": [
@@ -25,44 +25,44 @@
25
25
  "formatting": "run -T prettier --check ./src && run -T eslint ./src"
26
26
  },
27
27
  "dependencies": {
28
- "@aztec/accounts": "0.0.1-commit.e6bd8901",
29
- "@aztec/archiver": "0.0.1-commit.e6bd8901",
30
- "@aztec/aztec": "0.0.1-commit.e6bd8901",
31
- "@aztec/aztec-node": "0.0.1-commit.e6bd8901",
32
- "@aztec/aztec.js": "0.0.1-commit.e6bd8901",
33
- "@aztec/bb-prover": "0.0.1-commit.e6bd8901",
34
- "@aztec/bb.js": "0.0.1-commit.e6bd8901",
35
- "@aztec/blob-client": "0.0.1-commit.e6bd8901",
36
- "@aztec/blob-lib": "0.0.1-commit.e6bd8901",
37
- "@aztec/bot": "0.0.1-commit.e6bd8901",
38
- "@aztec/cli": "0.0.1-commit.e6bd8901",
39
- "@aztec/constants": "0.0.1-commit.e6bd8901",
40
- "@aztec/entrypoints": "0.0.1-commit.e6bd8901",
41
- "@aztec/epoch-cache": "0.0.1-commit.e6bd8901",
42
- "@aztec/ethereum": "0.0.1-commit.e6bd8901",
43
- "@aztec/foundation": "0.0.1-commit.e6bd8901",
44
- "@aztec/kv-store": "0.0.1-commit.e6bd8901",
45
- "@aztec/l1-artifacts": "0.0.1-commit.e6bd8901",
46
- "@aztec/merkle-tree": "0.0.1-commit.e6bd8901",
47
- "@aztec/node-keystore": "0.0.1-commit.e6bd8901",
48
- "@aztec/noir-contracts.js": "0.0.1-commit.e6bd8901",
49
- "@aztec/noir-noirc_abi": "0.0.1-commit.e6bd8901",
50
- "@aztec/noir-protocol-circuits-types": "0.0.1-commit.e6bd8901",
51
- "@aztec/noir-test-contracts.js": "0.0.1-commit.e6bd8901",
52
- "@aztec/p2p": "0.0.1-commit.e6bd8901",
53
- "@aztec/protocol-contracts": "0.0.1-commit.e6bd8901",
54
- "@aztec/prover-client": "0.0.1-commit.e6bd8901",
55
- "@aztec/prover-node": "0.0.1-commit.e6bd8901",
56
- "@aztec/pxe": "0.0.1-commit.e6bd8901",
57
- "@aztec/sequencer-client": "0.0.1-commit.e6bd8901",
58
- "@aztec/simulator": "0.0.1-commit.e6bd8901",
59
- "@aztec/slasher": "0.0.1-commit.e6bd8901",
60
- "@aztec/stdlib": "0.0.1-commit.e6bd8901",
61
- "@aztec/telemetry-client": "0.0.1-commit.e6bd8901",
62
- "@aztec/test-wallet": "0.0.1-commit.e6bd8901",
63
- "@aztec/validator-client": "0.0.1-commit.e6bd8901",
64
- "@aztec/validator-ha-signer": "0.0.1-commit.e6bd8901",
65
- "@aztec/world-state": "0.0.1-commit.e6bd8901",
28
+ "@aztec/accounts": "0.0.1-commit.f146247c",
29
+ "@aztec/archiver": "0.0.1-commit.f146247c",
30
+ "@aztec/aztec": "0.0.1-commit.f146247c",
31
+ "@aztec/aztec-node": "0.0.1-commit.f146247c",
32
+ "@aztec/aztec.js": "0.0.1-commit.f146247c",
33
+ "@aztec/bb-prover": "0.0.1-commit.f146247c",
34
+ "@aztec/bb.js": "0.0.1-commit.f146247c",
35
+ "@aztec/blob-client": "0.0.1-commit.f146247c",
36
+ "@aztec/blob-lib": "0.0.1-commit.f146247c",
37
+ "@aztec/bot": "0.0.1-commit.f146247c",
38
+ "@aztec/cli": "0.0.1-commit.f146247c",
39
+ "@aztec/constants": "0.0.1-commit.f146247c",
40
+ "@aztec/entrypoints": "0.0.1-commit.f146247c",
41
+ "@aztec/epoch-cache": "0.0.1-commit.f146247c",
42
+ "@aztec/ethereum": "0.0.1-commit.f146247c",
43
+ "@aztec/foundation": "0.0.1-commit.f146247c",
44
+ "@aztec/kv-store": "0.0.1-commit.f146247c",
45
+ "@aztec/l1-artifacts": "0.0.1-commit.f146247c",
46
+ "@aztec/merkle-tree": "0.0.1-commit.f146247c",
47
+ "@aztec/node-keystore": "0.0.1-commit.f146247c",
48
+ "@aztec/noir-contracts.js": "0.0.1-commit.f146247c",
49
+ "@aztec/noir-noirc_abi": "0.0.1-commit.f146247c",
50
+ "@aztec/noir-protocol-circuits-types": "0.0.1-commit.f146247c",
51
+ "@aztec/noir-test-contracts.js": "0.0.1-commit.f146247c",
52
+ "@aztec/p2p": "0.0.1-commit.f146247c",
53
+ "@aztec/protocol-contracts": "0.0.1-commit.f146247c",
54
+ "@aztec/prover-client": "0.0.1-commit.f146247c",
55
+ "@aztec/prover-node": "0.0.1-commit.f146247c",
56
+ "@aztec/pxe": "0.0.1-commit.f146247c",
57
+ "@aztec/sequencer-client": "0.0.1-commit.f146247c",
58
+ "@aztec/simulator": "0.0.1-commit.f146247c",
59
+ "@aztec/slasher": "0.0.1-commit.f146247c",
60
+ "@aztec/stdlib": "0.0.1-commit.f146247c",
61
+ "@aztec/telemetry-client": "0.0.1-commit.f146247c",
62
+ "@aztec/test-wallet": "0.0.1-commit.f146247c",
63
+ "@aztec/validator-client": "0.0.1-commit.f146247c",
64
+ "@aztec/validator-ha-signer": "0.0.1-commit.f146247c",
65
+ "@aztec/world-state": "0.0.1-commit.f146247c",
66
66
  "@iarna/toml": "^2.2.5",
67
67
  "@jest/globals": "^30.0.0",
68
68
  "@noble/curves": "=1.0.0",