@aztec/end-to-end 4.0.0-nightly.20260121 → 4.0.0-nightly.20260123

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 (63) hide show
  1. package/dest/e2e_fees/fees_test.js +2 -2
  2. package/dest/e2e_l1_publisher/write_json.d.ts +3 -3
  3. package/dest/e2e_l1_publisher/write_json.d.ts.map +1 -1
  4. package/dest/shared/cross_chain_test_harness.d.ts +3 -4
  5. package/dest/shared/cross_chain_test_harness.d.ts.map +1 -1
  6. package/dest/shared/submit-transactions.d.ts +1 -1
  7. package/dest/shared/submit-transactions.d.ts.map +1 -1
  8. package/dest/shared/submit-transactions.js +1 -4
  9. package/dest/simulators/lending_simulator.js +2 -2
  10. package/dest/spartan/tx_metrics.d.ts +11 -1
  11. package/dest/spartan/tx_metrics.d.ts.map +1 -1
  12. package/dest/spartan/tx_metrics.js +133 -4
  13. package/dest/spartan/utils/bot.d.ts +27 -0
  14. package/dest/spartan/utils/bot.d.ts.map +1 -0
  15. package/dest/spartan/utils/bot.js +141 -0
  16. package/dest/spartan/utils/chaos.d.ts +79 -0
  17. package/dest/spartan/utils/chaos.d.ts.map +1 -0
  18. package/dest/spartan/utils/chaos.js +142 -0
  19. package/dest/spartan/utils/clients.d.ts +25 -0
  20. package/dest/spartan/utils/clients.d.ts.map +1 -0
  21. package/dest/spartan/utils/clients.js +101 -0
  22. package/dest/spartan/utils/config.d.ts +36 -0
  23. package/dest/spartan/utils/config.d.ts.map +1 -0
  24. package/dest/spartan/utils/config.js +20 -0
  25. package/dest/spartan/utils/health.d.ts +63 -0
  26. package/dest/spartan/utils/health.d.ts.map +1 -0
  27. package/dest/spartan/utils/health.js +202 -0
  28. package/dest/spartan/utils/helm.d.ts +15 -0
  29. package/dest/spartan/utils/helm.d.ts.map +1 -0
  30. package/dest/spartan/utils/helm.js +47 -0
  31. package/dest/spartan/utils/index.d.ts +9 -0
  32. package/dest/spartan/utils/index.d.ts.map +1 -0
  33. package/dest/spartan/utils/index.js +18 -0
  34. package/dest/spartan/utils/k8s.d.ts +59 -0
  35. package/dest/spartan/utils/k8s.d.ts.map +1 -0
  36. package/dest/spartan/utils/k8s.js +185 -0
  37. package/dest/spartan/utils/nodes.d.ts +31 -0
  38. package/dest/spartan/utils/nodes.d.ts.map +1 -0
  39. package/dest/spartan/utils/nodes.js +273 -0
  40. package/dest/spartan/utils/scripts.d.ts +16 -0
  41. package/dest/spartan/utils/scripts.d.ts.map +1 -0
  42. package/dest/spartan/utils/scripts.js +66 -0
  43. package/dest/spartan/utils.d.ts +2 -260
  44. package/dest/spartan/utils.d.ts.map +1 -1
  45. package/dest/spartan/utils.js +1 -942
  46. package/package.json +39 -39
  47. package/src/e2e_fees/fees_test.ts +2 -2
  48. package/src/e2e_l1_publisher/write_json.ts +2 -2
  49. package/src/shared/cross_chain_test_harness.ts +2 -3
  50. package/src/shared/submit-transactions.ts +1 -6
  51. package/src/simulators/lending_simulator.ts +2 -2
  52. package/src/spartan/tx_metrics.ts +84 -6
  53. package/src/spartan/utils/bot.ts +185 -0
  54. package/src/spartan/utils/chaos.ts +253 -0
  55. package/src/spartan/utils/clients.ts +106 -0
  56. package/src/spartan/utils/config.ts +26 -0
  57. package/src/spartan/utils/health.ts +256 -0
  58. package/src/spartan/utils/helm.ts +84 -0
  59. package/src/spartan/utils/index.ts +58 -0
  60. package/src/spartan/utils/k8s.ts +279 -0
  61. package/src/spartan/utils/nodes.ts +308 -0
  62. package/src/spartan/utils/scripts.ts +63 -0
  63. package/src/spartan/utils.ts +1 -1246
@@ -0,0 +1,47 @@
1
+ import { createLogger } from '@aztec/aztec.js/log';
2
+ import { exec } from 'child_process';
3
+ import { promisify } from 'util';
4
+ const execAsync = promisify(exec);
5
+ const logger = createLogger('e2e:k8s-utils');
6
+ function shellQuote(value) {
7
+ // Single-quote safe shell escaping: ' -> '\''
8
+ return `'${value.replace(/'/g, "'\\''")}'`;
9
+ }
10
+ function valuesToArgs(values) {
11
+ return Object.entries(values).map(([key, value])=>typeof value === 'number' || typeof value === 'boolean' ? `--set ${key}=${value}` : `--set-string ${key}=${shellQuote(String(value))}`).join(' ');
12
+ }
13
+ export function createHelmCommand({ instanceName, helmChartDir, namespace, valuesFile, timeout, values, reuseValues = false }) {
14
+ const valuesFileArgs = valuesFile ? `--values ${helmChartDir}/values/${valuesFile}` : '';
15
+ const reuseValuesArgs = reuseValues ? '--reuse-values' : '';
16
+ return `helm upgrade --install ${instanceName} ${helmChartDir} --namespace ${namespace} ${valuesFileArgs} ${reuseValuesArgs} --wait --timeout=${timeout} ${valuesToArgs(values)}`;
17
+ }
18
+ export async function execHelmCommand(args) {
19
+ const helmCommand = createHelmCommand(args);
20
+ logger.info(`helm command: ${helmCommand}`);
21
+ const { stdout } = await execAsync(helmCommand);
22
+ return stdout;
23
+ }
24
+ export async function getHelmReleaseStatus(instanceName, namespace) {
25
+ try {
26
+ const { stdout } = await execAsync(`helm list --namespace ${namespace} --all --filter '^${instanceName}$' --output json | cat`);
27
+ const parsed = JSON.parse(stdout);
28
+ const row = parsed.find((r)=>r.name === instanceName);
29
+ return row?.status;
30
+ } catch {
31
+ return undefined;
32
+ }
33
+ }
34
+ export async function forceDeleteHelmReleaseRecord(instanceName, namespace, log) {
35
+ const labelSelector = `owner=helm,name=${instanceName}`;
36
+ const cmd = `kubectl delete secret -n ${namespace} -l ${labelSelector} --ignore-not-found=true`;
37
+ (log ?? logger).warn(`Force deleting Helm release record: ${cmd}`);
38
+ await execAsync(cmd).catch(()=>undefined);
39
+ }
40
+ export async function hasDeployedHelmRelease(instanceName, namespace) {
41
+ try {
42
+ const status = await getHelmReleaseStatus(instanceName, namespace);
43
+ return status?.toLowerCase() === 'deployed';
44
+ } catch {
45
+ return false;
46
+ }
47
+ }
@@ -0,0 +1,9 @@
1
+ export { type TestConfig, setupEnvironment } from './config.js';
2
+ export { getGitProjectRoot, getAztecBin, runAztecBin, runProjectScript } from './scripts.js';
3
+ export { startPortForward, getExternalIP, startPortForwardForPrometeheus, startPortForwardForRPC, startPortForwardForEthereum, deleteResourceByName, deleteResourceByLabel, waitForResourceByLabel, waitForResourceByName, waitForResourcesByName, getChartDir, } from './k8s.js';
4
+ export { uninstallChaosMesh, installChaosMeshChart, applyProverFailure, applyValidatorFailure, applyProverKill, applyProverBrokerKill, applyBootNodeFailure, applyValidatorKill, applyNetworkShaping, } from './chaos.js';
5
+ export { restartBot, installTransferBot, uninstallTransferBot } from './bot.js';
6
+ export { awaitCheckpointNumber, getSequencers, updateSequencersConfig, getSequencersConfig, withSequencersAdmin, setValidatorTxDrop, restartValidators, enableValidatorDynamicBootNode, rollAztecPods, } from './nodes.js';
7
+ export { getPublicViemClient, getL1DeploymentAddresses, getNodeClient } from './clients.js';
8
+ export { ChainHealth, type ChainHealthSnapshot } from './health.js';
9
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9zcGFydGFuL3V0aWxzL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUlBLE9BQU8sRUFBRSxLQUFLLFVBQVUsRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUdoRSxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsV0FBVyxFQUFFLFdBQVcsRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUc3RixPQUFPLEVBQ0wsZ0JBQWdCLEVBQ2hCLGFBQWEsRUFDYiw4QkFBOEIsRUFDOUIsc0JBQXNCLEVBQ3RCLDJCQUEyQixFQUMzQixvQkFBb0IsRUFDcEIscUJBQXFCLEVBQ3JCLHNCQUFzQixFQUN0QixxQkFBcUIsRUFDckIsc0JBQXNCLEVBQ3RCLFdBQVcsR0FDWixNQUFNLFVBQVUsQ0FBQztBQUdsQixPQUFPLEVBQ0wsa0JBQWtCLEVBQ2xCLHFCQUFxQixFQUNyQixrQkFBa0IsRUFDbEIscUJBQXFCLEVBQ3JCLGVBQWUsRUFDZixxQkFBcUIsRUFDckIsb0JBQW9CLEVBQ3BCLGtCQUFrQixFQUNsQixtQkFBbUIsR0FDcEIsTUFBTSxZQUFZLENBQUM7QUFHcEIsT0FBTyxFQUFFLFVBQVUsRUFBRSxrQkFBa0IsRUFBRSxvQkFBb0IsRUFBRSxNQUFNLFVBQVUsQ0FBQztBQUdoRixPQUFPLEVBQ0wscUJBQXFCLEVBQ3JCLGFBQWEsRUFDYixzQkFBc0IsRUFDdEIsbUJBQW1CLEVBQ25CLG1CQUFtQixFQUNuQixrQkFBa0IsRUFDbEIsaUJBQWlCLEVBQ2pCLDhCQUE4QixFQUM5QixhQUFhLEdBQ2QsTUFBTSxZQUFZLENBQUM7QUFHcEIsT0FBTyxFQUFFLG1CQUFtQixFQUFFLHdCQUF3QixFQUFFLGFBQWEsRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUc1RixPQUFPLEVBQUUsV0FBVyxFQUFFLEtBQUssbUJBQW1CLEVBQUUsTUFBTSxhQUFhLENBQUMifQ==
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/spartan/utils/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,UAAU,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAGhE,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAG7F,OAAO,EACL,gBAAgB,EAChB,aAAa,EACb,8BAA8B,EAC9B,sBAAsB,EACtB,2BAA2B,EAC3B,oBAAoB,EACpB,qBAAqB,EACrB,sBAAsB,EACtB,qBAAqB,EACrB,sBAAsB,EACtB,WAAW,GACZ,MAAM,UAAU,CAAC;AAGlB,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EACrB,kBAAkB,EAClB,qBAAqB,EACrB,eAAe,EACf,qBAAqB,EACrB,oBAAoB,EACpB,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAGhF,OAAO,EACL,qBAAqB,EACrB,aAAa,EACb,sBAAsB,EACtB,mBAAmB,EACnB,mBAAmB,EACnB,kBAAkB,EAClB,iBAAiB,EACjB,8BAA8B,EAC9B,aAAa,GACd,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,mBAAmB,EAAE,wBAAwB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAG5F,OAAO,EAAE,WAAW,EAAE,KAAK,mBAAmB,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,18 @@
1
+ // Re-export all utilities from individual modules
2
+ // Note: Internal helpers (helm commands, etc.) are not re-exported to maintain the original public API
3
+ // Config
4
+ export { setupEnvironment } from './config.js';
5
+ // Scripts
6
+ export { getGitProjectRoot, getAztecBin, runAztecBin, runProjectScript } from './scripts.js';
7
+ // K8s operations
8
+ export { startPortForward, getExternalIP, startPortForwardForPrometeheus, startPortForwardForRPC, startPortForwardForEthereum, deleteResourceByName, deleteResourceByLabel, waitForResourceByLabel, waitForResourceByName, waitForResourcesByName, getChartDir } from './k8s.js';
9
+ // Chaos Mesh
10
+ export { uninstallChaosMesh, installChaosMeshChart, applyProverFailure, applyValidatorFailure, applyProverKill, applyProverBrokerKill, applyBootNodeFailure, applyValidatorKill, applyNetworkShaping } from './chaos.js';
11
+ // Bot management
12
+ export { restartBot, installTransferBot, uninstallTransferBot } from './bot.js';
13
+ // Node operations (sequencers, validators, pods)
14
+ export { awaitCheckpointNumber, getSequencers, updateSequencersConfig, getSequencersConfig, withSequencersAdmin, setValidatorTxDrop, restartValidators, enableValidatorDynamicBootNode, rollAztecPods } from './nodes.js';
15
+ // Client utilities
16
+ export { getPublicViemClient, getL1DeploymentAddresses, getNodeClient } from './clients.js';
17
+ // Health checks
18
+ export { ChainHealth } from './health.js';
@@ -0,0 +1,59 @@
1
+ import { type ChildProcess } from 'child_process';
2
+ export declare function startPortForward({ resource, namespace, containerPort, hostPort }: {
3
+ resource: string;
4
+ namespace: string;
5
+ containerPort: number;
6
+ hostPort?: number;
7
+ }): Promise<{
8
+ process: ChildProcess;
9
+ port: number;
10
+ }>;
11
+ export declare function getExternalIP(namespace: string, serviceName: string): Promise<string>;
12
+ export declare function startPortForwardForPrometeheus(namespace: string): Promise<{
13
+ process: ChildProcess;
14
+ port: number;
15
+ }>;
16
+ export declare function startPortForwardForRPC(namespace: string, index?: number): Promise<{
17
+ process: ChildProcess;
18
+ port: number;
19
+ }>;
20
+ export declare function startPortForwardForEthereum(namespace: string): Promise<{
21
+ process: ChildProcess;
22
+ port: number;
23
+ }>;
24
+ export declare function deleteResourceByName({ resource, namespace, name, force }: {
25
+ resource: string;
26
+ namespace: string;
27
+ name: string;
28
+ force?: boolean;
29
+ }): Promise<string>;
30
+ export declare function deleteResourceByLabel({ resource, namespace, label, timeout, force }: {
31
+ resource: string;
32
+ namespace: string;
33
+ label: string;
34
+ timeout?: string;
35
+ force?: boolean;
36
+ }): Promise<string>;
37
+ export declare function waitForResourceByLabel({ resource, label, namespace, condition, timeout }: {
38
+ resource: string;
39
+ label: string;
40
+ namespace: string;
41
+ condition?: string;
42
+ timeout?: string;
43
+ }): Promise<string>;
44
+ export declare function waitForResourceByName({ resource, name, namespace, condition, timeout }: {
45
+ resource: string;
46
+ name: string;
47
+ namespace: string;
48
+ condition?: string;
49
+ timeout?: string;
50
+ }): Promise<string>;
51
+ export declare function waitForResourcesByName({ resource, names, namespace, condition, timeout }: {
52
+ resource: string;
53
+ names: string[];
54
+ namespace: string;
55
+ condition?: string;
56
+ timeout?: string;
57
+ }): Promise<void>;
58
+ export declare function getChartDir(spartanDir: string, chartName: string): string;
59
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiazhzLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvc3BhcnRhbi91dGlscy9rOHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBR0EsT0FBTyxFQUFFLEtBQUssWUFBWSxFQUFlLE1BQU0sZUFBZSxDQUFDO0FBUS9ELHdCQUFzQixnQkFBZ0IsQ0FBQyxFQUNyQyxRQUFRLEVBQ1IsU0FBUyxFQUNULGFBQWEsRUFDYixRQUFRLEVBQ1QsRUFBRTtJQUNELFFBQVEsRUFBRSxNQUFNLENBQUM7SUFDakIsU0FBUyxFQUFFLE1BQU0sQ0FBQztJQUNsQixhQUFhLEVBQUUsTUFBTSxDQUFDO0lBRXRCLFFBQVEsQ0FBQyxFQUFFLE1BQU0sQ0FBQztDQUNuQixHQUFHLE9BQU8sQ0FBQztJQUNWLE9BQU8sRUFBRSxZQUFZLENBQUM7SUFDdEIsSUFBSSxFQUFFLE1BQU0sQ0FBQztDQUNkLENBQUMsQ0F1RUQ7QUFFRCx3QkFBZ0IsYUFBYSxDQUFDLFNBQVMsRUFBRSxNQUFNLEVBQUUsV0FBVyxFQUFFLE1BQU0sR0FBRyxPQUFPLENBQUMsTUFBTSxDQUFDLENBK0JyRjtBQUVELHdCQUFnQiw4QkFBOEIsQ0FBQyxTQUFTLEVBQUUsTUFBTTs7O0dBTS9EO0FBRUQsd0JBQWdCLHNCQUFzQixDQUFDLFNBQVMsRUFBRSxNQUFNLEVBQUUsS0FBSyxTQUFJOzs7R0FNbEU7QUFFRCx3QkFBZ0IsMkJBQTJCLENBQUMsU0FBUyxFQUFFLE1BQU07OztHQU01RDtBQUVELHdCQUFzQixvQkFBb0IsQ0FBQyxFQUN6QyxRQUFRLEVBQ1IsU0FBUyxFQUNULElBQUksRUFDSixLQUFhLEVBQ2QsRUFBRTtJQUNELFFBQVEsRUFBRSxNQUFNLENBQUM7SUFDakIsU0FBUyxFQUFFLE1BQU0sQ0FBQztJQUNsQixJQUFJLEVBQUUsTUFBTSxDQUFDO0lBQ2IsS0FBSyxDQUFDLEVBQUUsT0FBTyxDQUFDO0NBQ2pCLG1CQU9BO0FBRUQsd0JBQXNCLHFCQUFxQixDQUFDLEVBQzFDLFFBQVEsRUFDUixTQUFTLEVBQ1QsS0FBSyxFQUNMLE9BQWMsRUFDZCxLQUFhLEVBQ2QsRUFBRTtJQUNELFFBQVEsRUFBRSxNQUFNLENBQUM7SUFDakIsU0FBUyxFQUFFLE1BQU0sQ0FBQztJQUNsQixLQUFLLEVBQUUsTUFBTSxDQUFDO0lBQ2QsT0FBTyxDQUFDLEVBQUUsTUFBTSxDQUFDO0lBQ2pCLEtBQUssQ0FBQyxFQUFFLE9BQU8sQ0FBQztDQUNqQixtQkFpQkE7QUFFRCx3QkFBc0Isc0JBQXNCLENBQUMsRUFDM0MsUUFBUSxFQUNSLEtBQUssRUFDTCxTQUFTLEVBQ1QsU0FBbUIsRUFDbkIsT0FBZSxFQUNoQixFQUFFO0lBQ0QsUUFBUSxFQUFFLE1BQU0sQ0FBQztJQUNqQixLQUFLLEVBQUUsTUFBTSxDQUFDO0lBQ2QsU0FBUyxFQUFFLE1BQU0sQ0FBQztJQUNsQixTQUFTLENBQUMsRUFBRSxNQUFNLENBQUM7SUFDbkIsT0FBTyxDQUFDLEVBQUUsTUFBTSxDQUFDO0NBQ2xCLG1CQUtBO0FBRUQsd0JBQXNCLHFCQUFxQixDQUFDLEVBQzFDLFFBQVEsRUFDUixJQUFJLEVBQ0osU0FBUyxFQUNULFNBQW1CLEVBQ25CLE9BQWUsRUFDaEIsRUFBRTtJQUNELFFBQVEsRUFBRSxNQUFNLENBQUM7SUFDakIsSUFBSSxFQUFFLE1BQU0sQ0FBQztJQUNiLFNBQVMsRUFBRSxNQUFNLENBQUM7SUFDbEIsU0FBUyxDQUFDLEVBQUUsTUFBTSxDQUFDO0lBQ25CLE9BQU8sQ0FBQyxFQUFFLE1BQU0sQ0FBQztDQUNsQixtQkFLQTtBQUVELHdCQUFzQixzQkFBc0IsQ0FBQyxFQUMzQyxRQUFRLEVBQ1IsS0FBSyxFQUNMLFNBQVMsRUFDVCxTQUFtQixFQUNuQixPQUFlLEVBQ2hCLEVBQUU7SUFDRCxRQUFRLEVBQUUsTUFBTSxDQUFDO0lBQ2pCLEtBQUssRUFBRSxNQUFNLEVBQUUsQ0FBQztJQUNoQixTQUFTLEVBQUUsTUFBTSxDQUFDO0lBQ2xCLFNBQVMsQ0FBQyxFQUFFLE1BQU0sQ0FBQztJQUNuQixPQUFPLENBQUMsRUFBRSxNQUFNLENBQUM7Q0FDbEIsaUJBbUJBO0FBRUQsd0JBQWdCLFdBQVcsQ0FBQyxVQUFVLEVBQUUsTUFBTSxFQUFFLFNBQVMsRUFBRSxNQUFNLFVBRWhFIn0=
@@ -0,0 +1 @@
1
+ {"version":3,"file":"k8s.d.ts","sourceRoot":"","sources":["../../../src/spartan/utils/k8s.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,YAAY,EAAe,MAAM,eAAe,CAAC;AAQ/D,wBAAsB,gBAAgB,CAAC,EACrC,QAAQ,EACR,SAAS,EACT,aAAa,EACb,QAAQ,EACT,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IAEtB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC;IACV,OAAO,EAAE,YAAY,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC,CAuED;AAED,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA+BrF;AAED,wBAAgB,8BAA8B,CAAC,SAAS,EAAE,MAAM;;;GAM/D;AAED,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,SAAI;;;GAMlE;AAED,wBAAgB,2BAA2B,CAAC,SAAS,EAAE,MAAM;;;GAM5D;AAED,wBAAsB,oBAAoB,CAAC,EACzC,QAAQ,EACR,SAAS,EACT,IAAI,EACJ,KAAa,EACd,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,mBAOA;AAED,wBAAsB,qBAAqB,CAAC,EAC1C,QAAQ,EACR,SAAS,EACT,KAAK,EACL,OAAc,EACd,KAAa,EACd,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,mBAiBA;AAED,wBAAsB,sBAAsB,CAAC,EAC3C,QAAQ,EACR,KAAK,EACL,SAAS,EACT,SAAmB,EACnB,OAAe,EAChB,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,mBAKA;AAED,wBAAsB,qBAAqB,CAAC,EAC1C,QAAQ,EACR,IAAI,EACJ,SAAS,EACT,SAAmB,EACnB,OAAe,EAChB,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,mBAKA;AAED,wBAAsB,sBAAsB,CAAC,EAC3C,QAAQ,EACR,KAAK,EACL,SAAS,EACT,SAAmB,EACnB,OAAe,EAChB,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,iBAmBA;AAED,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,UAEhE"}
@@ -0,0 +1,185 @@
1
+ import { createLogger } from '@aztec/aztec.js/log';
2
+ import { promiseWithResolvers } from '@aztec/foundation/promise';
3
+ import { exec, spawn } from 'child_process';
4
+ import path from 'path';
5
+ import { promisify } from 'util';
6
+ const execAsync = promisify(exec);
7
+ const logger = createLogger('e2e:k8s-utils');
8
+ export async function startPortForward({ resource, namespace, containerPort, hostPort }) {
9
+ const hostPortAsString = hostPort ? hostPort.toString() : '';
10
+ logger.debug(`kubectl port-forward -n ${namespace} ${resource} ${hostPortAsString}:${containerPort}`);
11
+ const process = spawn('kubectl', [
12
+ 'port-forward',
13
+ '-n',
14
+ namespace,
15
+ resource,
16
+ `${hostPortAsString}:${containerPort}`
17
+ ], {
18
+ detached: true,
19
+ windowsHide: true,
20
+ stdio: [
21
+ 'ignore',
22
+ 'pipe',
23
+ 'pipe'
24
+ ]
25
+ });
26
+ let isResolved = false;
27
+ const connected = new Promise((resolve, reject)=>{
28
+ process.stdout?.on('data', (data)=>{
29
+ const str = data.toString();
30
+ if (!isResolved && str.includes('Forwarding from')) {
31
+ isResolved = true;
32
+ logger.debug(`Port forward for ${resource}: ${str}`);
33
+ const port = str.search(/:\d+/);
34
+ if (port === -1) {
35
+ reject(new Error('Port not found in port forward output'));
36
+ return;
37
+ }
38
+ const portNumber = parseInt(str.slice(port + 1));
39
+ logger.verbose(`Port forwarded for ${resource} at ${portNumber}:${containerPort}`);
40
+ resolve(portNumber);
41
+ } else {
42
+ logger.silent(str);
43
+ }
44
+ });
45
+ process.stderr?.on('data', (data)=>{
46
+ logger.verbose(`Port forward for ${resource}: ${data.toString()}`);
47
+ // It's a strange thing:
48
+ // If we don't pipe stderr, then the port forwarding does not work.
49
+ // Log to silent because this doesn't actually report errors,
50
+ // just extremely verbose debug logs.
51
+ logger.silent(data.toString());
52
+ });
53
+ process.on('close', ()=>{
54
+ if (!isResolved) {
55
+ isResolved = true;
56
+ const msg = `Port forward for ${resource} closed before connection established`;
57
+ logger.warn(msg);
58
+ reject(new Error(msg));
59
+ }
60
+ });
61
+ process.on('error', (error)=>{
62
+ if (!isResolved) {
63
+ isResolved = true;
64
+ const msg = `Port forward for ${resource} error: ${error}`;
65
+ logger.error(msg);
66
+ reject(new Error(msg));
67
+ }
68
+ });
69
+ process.on('exit', (code)=>{
70
+ if (!isResolved) {
71
+ isResolved = true;
72
+ const msg = `Port forward for ${resource} exited with code ${code}`;
73
+ logger.verbose(msg);
74
+ reject(new Error(msg));
75
+ }
76
+ });
77
+ });
78
+ const port = await connected;
79
+ return {
80
+ process,
81
+ port
82
+ };
83
+ }
84
+ export function getExternalIP(namespace, serviceName) {
85
+ const { promise, resolve, reject } = promiseWithResolvers();
86
+ const process = spawn('kubectl', [
87
+ 'get',
88
+ 'service',
89
+ '-n',
90
+ namespace,
91
+ `${namespace}-${serviceName}`,
92
+ '--output',
93
+ "jsonpath='{.status.loadBalancer.ingress[0].ip}'"
94
+ ], {
95
+ stdio: 'pipe'
96
+ });
97
+ let ip = '';
98
+ process.stdout.on('data', (data)=>{
99
+ ip += data;
100
+ });
101
+ process.on('error', (err)=>{
102
+ reject(err);
103
+ });
104
+ process.on('exit', ()=>{
105
+ // kubectl prints JSON. Remove the quotes
106
+ resolve(ip.replace(/"|'/g, ''));
107
+ });
108
+ return promise;
109
+ }
110
+ export function startPortForwardForPrometeheus(namespace) {
111
+ return startPortForward({
112
+ resource: `svc/${namespace}-prometheus-server`,
113
+ namespace,
114
+ containerPort: 80
115
+ });
116
+ }
117
+ export function startPortForwardForRPC(namespace, index = 0) {
118
+ return startPortForward({
119
+ resource: `pod/${namespace}-rpc-aztec-node-${index}`,
120
+ namespace,
121
+ containerPort: 8080
122
+ });
123
+ }
124
+ export function startPortForwardForEthereum(namespace) {
125
+ return startPortForward({
126
+ resource: `services/${namespace}-eth-execution`,
127
+ namespace,
128
+ containerPort: 8545
129
+ });
130
+ }
131
+ export async function deleteResourceByName({ resource, namespace, name, force = false }) {
132
+ const command = `kubectl delete ${resource} ${name} -n ${namespace} --ignore-not-found=true --wait=true ${force ? '--force' : ''}`;
133
+ logger.info(`command: ${command}`);
134
+ const { stdout } = await execAsync(command);
135
+ return stdout;
136
+ }
137
+ export async function deleteResourceByLabel({ resource, namespace, label, timeout = '5m', force = false }) {
138
+ try {
139
+ // Match both plain and group-qualified names (e.g., "podchaos" or "podchaos.chaos-mesh.org")
140
+ const escaped = resource.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
141
+ const regex = `(^|\\.)${escaped}(\\.|$)`;
142
+ await execAsync(`kubectl api-resources --no-headers -o name | grep -Eq '${regex}'`);
143
+ } catch (error) {
144
+ logger.warn(`Resource type '${resource}' not found in cluster, skipping deletion ${error}`);
145
+ return '';
146
+ }
147
+ const command = `kubectl delete ${resource} -l ${label} -n ${namespace} --ignore-not-found=true --wait=true --timeout=${timeout} ${force ? '--force' : ''}`;
148
+ logger.info(`command: ${command}`);
149
+ const { stdout } = await execAsync(command);
150
+ return stdout;
151
+ }
152
+ export async function waitForResourceByLabel({ resource, label, namespace, condition = 'Ready', timeout = '10m' }) {
153
+ const command = `kubectl wait ${resource} -l ${label} --for=condition=${condition} -n ${namespace} --timeout=${timeout}`;
154
+ logger.info(`command: ${command}`);
155
+ const { stdout } = await execAsync(command);
156
+ return stdout;
157
+ }
158
+ export async function waitForResourceByName({ resource, name, namespace, condition = 'Ready', timeout = '10m' }) {
159
+ const command = `kubectl wait ${resource}/${name} --for=condition=${condition} -n ${namespace} --timeout=${timeout}`;
160
+ logger.info(`command: ${command}`);
161
+ const { stdout } = await execAsync(command);
162
+ return stdout;
163
+ }
164
+ export async function waitForResourcesByName({ resource, names, namespace, condition = 'Ready', timeout = '10m' }) {
165
+ if (!names.length) {
166
+ throw new Error(`No ${resource} names provided to waitForResourcesByName`);
167
+ }
168
+ // Wait all in parallel; if any fails, surface which one.
169
+ await Promise.all(names.map(async (name)=>{
170
+ try {
171
+ await waitForResourceByName({
172
+ resource,
173
+ name,
174
+ namespace,
175
+ condition,
176
+ timeout
177
+ });
178
+ } catch (err) {
179
+ throw new Error(`Failed waiting for ${resource}/${name} condition=${condition} timeout=${timeout} namespace=${namespace}: ${String(err)}`);
180
+ }
181
+ }));
182
+ }
183
+ export function getChartDir(spartanDir, chartName) {
184
+ return path.join(spartanDir.trim(), chartName);
185
+ }
@@ -0,0 +1,31 @@
1
+ import type { RollupCheatCodes } from '@aztec/aztec/testing';
2
+ import type { CheckpointNumber } from '@aztec/foundation/branded-types';
3
+ import type { Logger } from '@aztec/foundation/log';
4
+ import { type AztecNodeAdmin, type AztecNodeAdminConfig } from '@aztec/stdlib/interfaces/client';
5
+ import type { TestConfig } from './config.js';
6
+ export declare function awaitCheckpointNumber(rollupCheatCodes: RollupCheatCodes, checkpointNumber: CheckpointNumber, timeoutSeconds: number, log: Logger): Promise<void>;
7
+ export declare function getSequencers(namespace: string): Promise<string[]>;
8
+ export declare function updateSequencersConfig(env: TestConfig, config: Partial<AztecNodeAdminConfig>): Promise<AztecNodeAdminConfig[]>;
9
+ export declare function getSequencersConfig(env: TestConfig): Promise<AztecNodeAdminConfig[]>;
10
+ export declare function withSequencersAdmin<T>(env: TestConfig, fn: (node: AztecNodeAdmin) => Promise<T>): Promise<T[]>;
11
+ /**
12
+ * Enables or disables probabilistic transaction dropping on validators and waits for rollout.
13
+ * Wired to env vars P2P_DROP_TX and P2P_DROP_TX_CHANCE via Helm values.
14
+ */
15
+ export declare function setValidatorTxDrop({ namespace, enabled, probability, logger: log }: {
16
+ namespace: string;
17
+ enabled: boolean;
18
+ probability: number;
19
+ logger: Logger;
20
+ }): Promise<void>;
21
+ export declare function restartValidators(namespace: string, log: Logger): Promise<void>;
22
+ export declare function enableValidatorDynamicBootNode(instanceName: string, namespace: string, spartanDir: string, log: Logger): Promise<void>;
23
+ /**
24
+ * Rolls the Aztec pods in the given namespace.
25
+ * @param namespace - The namespace to roll the Aztec pods in.
26
+ * @param clearState - If true, also deletes the underlying PVCs to clear persistent storage.
27
+ * This is required for rollup upgrades where the old state is incompatible with the new rollup.
28
+ * Defaults to false, which preserves the existing storage.
29
+ */
30
+ export declare function rollAztecPods(namespace: string, clearState?: boolean): Promise<void>;
31
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibm9kZXMuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9zcGFydGFuL3V0aWxzL25vZGVzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUNBLE9BQU8sS0FBSyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDN0QsT0FBTyxLQUFLLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUN4RSxPQUFPLEtBQUssRUFBRSxNQUFNLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUdwRCxPQUFPLEVBQ0wsS0FBSyxjQUFjLEVBQ25CLEtBQUssb0JBQW9CLEVBRTFCLE1BQU0saUNBQWlDLENBQUM7QUFLekMsT0FBTyxLQUFLLEVBQUUsVUFBVSxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBUTlDLHdCQUFzQixxQkFBcUIsQ0FDekMsZ0JBQWdCLEVBQUUsZ0JBQWdCLEVBQ2xDLGdCQUFnQixFQUFFLGdCQUFnQixFQUNsQyxjQUFjLEVBQUUsTUFBTSxFQUN0QixHQUFHLEVBQUUsTUFBTSxpQkFlWjtBQUVELHdCQUFzQixhQUFhLENBQUMsU0FBUyxFQUFFLE1BQU0scUJBNEJwRDtBQUVELHdCQUFnQixzQkFBc0IsQ0FBQyxHQUFHLEVBQUUsVUFBVSxFQUFFLE1BQU0sRUFBRSxPQUFPLENBQUMsb0JBQW9CLENBQUMsbUNBSzVGO0FBRUQsd0JBQWdCLG1CQUFtQixDQUFDLEdBQUcsRUFBRSxVQUFVLG1DQUVsRDtBQUVELHdCQUFzQixtQkFBbUIsQ0FBQyxDQUFDLEVBQUUsR0FBRyxFQUFFLFVBQVUsRUFBRSxFQUFFLEVBQUUsQ0FBQyxJQUFJLEVBQUUsY0FBYyxLQUFLLE9BQU8sQ0FBQyxDQUFDLENBQUMsR0FBRyxPQUFPLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0EyQnBIO0FBRUQ7OztHQUdHO0FBQ0gsd0JBQXNCLGtCQUFrQixDQUFDLEVBQ3ZDLFNBQVMsRUFDVCxPQUFPLEVBQ1AsV0FBVyxFQUNYLE1BQU0sRUFBRSxHQUFHLEVBQ1osRUFBRTtJQUNELFNBQVMsRUFBRSxNQUFNLENBQUM7SUFDbEIsT0FBTyxFQUFFLE9BQU8sQ0FBQztJQUNqQixXQUFXLEVBQUUsTUFBTSxDQUFDO0lBQ3BCLE1BQU0sRUFBRSxNQUFNLENBQUM7Q0FDaEIsaUJBZ0NBO0FBRUQsd0JBQXNCLGlCQUFpQixDQUFDLFNBQVMsRUFBRSxNQUFNLEVBQUUsR0FBRyxFQUFFLE1BQU0saUJBK0JyRTtBQUVELHdCQUFzQiw4QkFBOEIsQ0FDbEQsWUFBWSxFQUFFLE1BQU0sRUFDcEIsU0FBUyxFQUFFLE1BQU0sRUFDakIsVUFBVSxFQUFFLE1BQU0sRUFDbEIsR0FBRyxFQUFFLE1BQU0saUJBZ0JaO0FBRUQ7Ozs7OztHQU1HO0FBQ0gsd0JBQXNCLGFBQWEsQ0FBQyxTQUFTLEVBQUUsTUFBTSxFQUFFLFVBQVUsR0FBRSxPQUFlLGlCQW1GakYifQ==
@@ -0,0 +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,CA2BpH;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"}
@@ -0,0 +1,273 @@
1
+ import { createLogger } from '@aztec/aztec.js/log';
2
+ import { makeBackoff, retry } from '@aztec/foundation/retry';
3
+ import { sleep } from '@aztec/foundation/sleep';
4
+ import { createAztecNodeAdminClient } from '@aztec/stdlib/interfaces/client';
5
+ import { exec } from 'child_process';
6
+ import { promisify } from 'util';
7
+ import { execHelmCommand } from './helm.js';
8
+ import { deleteResourceByLabel, getChartDir, startPortForward, waitForResourceByLabel } from './k8s.js';
9
+ const execAsync = promisify(exec);
10
+ const logger = createLogger('e2e:k8s-utils');
11
+ export async function awaitCheckpointNumber(rollupCheatCodes, checkpointNumber, timeoutSeconds, log) {
12
+ log.info(`Waiting for checkpoint ${checkpointNumber}`);
13
+ let tips = await rollupCheatCodes.getTips();
14
+ const endTime = Date.now() + timeoutSeconds * 1000;
15
+ while(tips.pending < checkpointNumber && Date.now() < endTime){
16
+ log.info(`At checkpoint ${tips.pending}`);
17
+ await sleep(1000);
18
+ tips = await rollupCheatCodes.getTips();
19
+ }
20
+ if (tips.pending < checkpointNumber) {
21
+ throw new Error(`Timeout waiting for checkpoint ${checkpointNumber}, only reached ${tips.pending}`);
22
+ } else {
23
+ log.info(`Reached checkpoint ${tips.pending}`);
24
+ }
25
+ }
26
+ export async function getSequencers(namespace) {
27
+ const selectors = [
28
+ 'app.kubernetes.io/name=validator',
29
+ 'app.kubernetes.io/component=validator',
30
+ 'app.kubernetes.io/component=sequencer-node',
31
+ 'app=validator'
32
+ ];
33
+ for (const selector of selectors){
34
+ try {
35
+ const command = `kubectl get pods -l ${selector} -n ${namespace} -o jsonpath='{.items[*].metadata.name}'`;
36
+ const { stdout } = await execAsync(command);
37
+ const sequencers = stdout.split(' ').map((s)=>s.trim()).filter(Boolean);
38
+ if (sequencers.length > 0) {
39
+ logger.verbose(`Found sequencer pods ${sequencers.join(', ')} (selector=${selector})`);
40
+ return sequencers;
41
+ }
42
+ } catch {
43
+ // try next selector
44
+ }
45
+ }
46
+ // Fail fast instead of returning [''] which leads to attempts to port-forward `pod/`.
47
+ throw new Error(`No sequencer/validator pods found in namespace ${namespace}. Tried selectors: ${selectors.join(', ')}`);
48
+ }
49
+ export function updateSequencersConfig(env, config) {
50
+ return withSequencersAdmin(env, async (client)=>{
51
+ await client.setConfig(config);
52
+ return client.getConfig();
53
+ });
54
+ }
55
+ export function getSequencersConfig(env) {
56
+ return withSequencersAdmin(env, (client)=>client.getConfig());
57
+ }
58
+ export async function withSequencersAdmin(env, fn) {
59
+ const adminContainerPort = 8880;
60
+ const namespace = env.NAMESPACE;
61
+ const sequencers = await getSequencers(namespace);
62
+ const results = [];
63
+ for (const sequencer of sequencers){
64
+ const { process, port } = await startPortForward({
65
+ resource: `pod/${sequencer}`,
66
+ namespace,
67
+ containerPort: adminContainerPort
68
+ });
69
+ const url = `http://localhost:${port}`;
70
+ await retry(()=>fetch(`${url}/status`).then((res)=>res.status === 200), 'forward node admin port', makeBackoff([
71
+ 1,
72
+ 1,
73
+ 2,
74
+ 6
75
+ ]), logger, true);
76
+ const client = createAztecNodeAdminClient(url);
77
+ results.push(await fn(client));
78
+ process.kill();
79
+ }
80
+ return results;
81
+ }
82
+ /**
83
+ * Enables or disables probabilistic transaction dropping on validators and waits for rollout.
84
+ * Wired to env vars P2P_DROP_TX and P2P_DROP_TX_CHANCE via Helm values.
85
+ */ export async function setValidatorTxDrop({ namespace, enabled, probability, logger: log }) {
86
+ const drop = enabled ? 'true' : 'false';
87
+ const prob = String(probability);
88
+ const selectors = [
89
+ 'app.kubernetes.io/name=validator',
90
+ 'app.kubernetes.io/component=validator',
91
+ 'app=validator'
92
+ ];
93
+ let updated = false;
94
+ for (const selector of selectors){
95
+ try {
96
+ const list = await execAsync(`kubectl get statefulset -l ${selector} -n ${namespace} --no-headers -o name | cat`);
97
+ const names = list.stdout.split('\n').map((s)=>s.trim()).filter(Boolean);
98
+ if (names.length === 0) {
99
+ continue;
100
+ }
101
+ const cmd = `kubectl set env statefulset -l ${selector} -n ${namespace} P2P_DROP_TX=${drop} P2P_DROP_TX_CHANCE=${prob}`;
102
+ log.info(`command: ${cmd}`);
103
+ await execAsync(cmd);
104
+ updated = true;
105
+ } catch (e) {
106
+ log.warn(`Failed to update validators with selector ${selector}: ${String(e)}`);
107
+ }
108
+ }
109
+ if (!updated) {
110
+ log.warn(`No validator StatefulSets found in ${namespace}. Skipping tx drop toggle.`);
111
+ return;
112
+ }
113
+ // Restart validator pods to ensure env vars take effect and wait for readiness
114
+ await restartValidators(namespace, log);
115
+ }
116
+ export async function restartValidators(namespace, log) {
117
+ const selectors = [
118
+ 'app.kubernetes.io/name=validator',
119
+ 'app.kubernetes.io/component=validator',
120
+ 'app=validator'
121
+ ];
122
+ let any = false;
123
+ for (const selector of selectors){
124
+ try {
125
+ const { stdout } = await execAsync(`kubectl get pods -l ${selector} -n ${namespace} --no-headers -o name | cat`);
126
+ if (!stdout || stdout.trim().length === 0) {
127
+ continue;
128
+ }
129
+ any = true;
130
+ await deleteResourceByLabel({
131
+ resource: 'pods',
132
+ namespace,
133
+ label: selector
134
+ });
135
+ } catch (e) {
136
+ log.warn(`Error restarting validator pods with selector ${selector}: ${String(e)}`);
137
+ }
138
+ }
139
+ if (!any) {
140
+ log.warn(`No validator pods found to restart in ${namespace}.`);
141
+ return;
142
+ }
143
+ // Wait for either label to be Ready
144
+ for (const selector of selectors){
145
+ try {
146
+ await waitForResourceByLabel({
147
+ resource: 'pods',
148
+ namespace,
149
+ label: selector
150
+ });
151
+ return;
152
+ } catch {
153
+ // try next
154
+ }
155
+ }
156
+ log.warn(`Validator pods did not report Ready; continuing.`);
157
+ }
158
+ export async function enableValidatorDynamicBootNode(instanceName, namespace, spartanDir, log) {
159
+ log.info(`Enabling validator dynamic boot node`);
160
+ await execHelmCommand({
161
+ instanceName,
162
+ namespace,
163
+ helmChartDir: getChartDir(spartanDir, 'aztec-network'),
164
+ values: {
165
+ 'validator.dynamicBootNode': 'true'
166
+ },
167
+ valuesFile: undefined,
168
+ timeout: '15m',
169
+ reuseValues: true
170
+ });
171
+ log.info(`Validator dynamic boot node enabled`);
172
+ }
173
+ /**
174
+ * Rolls the Aztec pods in the given namespace.
175
+ * @param namespace - The namespace to roll the Aztec pods in.
176
+ * @param clearState - If true, also deletes the underlying PVCs to clear persistent storage.
177
+ * This is required for rollup upgrades where the old state is incompatible with the new rollup.
178
+ * Defaults to false, which preserves the existing storage.
179
+ */ export async function rollAztecPods(namespace, clearState = false) {
180
+ // Pod components use 'validator', but StatefulSets and PVCs use 'sequencer-node' for validators
181
+ const podComponents = [
182
+ 'p2p-bootstrap',
183
+ 'prover-node',
184
+ 'prover-broker',
185
+ 'prover-agent',
186
+ 'sequencer-node',
187
+ 'rpc'
188
+ ];
189
+ const pvcComponents = [
190
+ 'p2p-bootstrap',
191
+ 'prover-node',
192
+ 'prover-broker',
193
+ 'sequencer-node',
194
+ 'rpc'
195
+ ];
196
+ // StatefulSet components that need to be scaled down before PVC deletion
197
+ // Note: validators use 'sequencer-node' as component label, not 'validator'
198
+ const statefulSetComponents = [
199
+ 'p2p-bootstrap',
200
+ 'prover-node',
201
+ 'prover-broker',
202
+ 'sequencer-node',
203
+ 'rpc'
204
+ ];
205
+ if (clearState) {
206
+ // To delete PVCs, we must first scale down StatefulSets so pods release the volumes
207
+ // Otherwise PVC deletion will hang waiting for pods to terminate
208
+ // First, save original replica counts
209
+ const originalReplicas = new Map();
210
+ for (const component of statefulSetComponents){
211
+ try {
212
+ const getCmd = `kubectl get statefulset -l app.kubernetes.io/component=${component} -n ${namespace} -o jsonpath='{.items[0].spec.replicas}'`;
213
+ const { stdout } = await execAsync(getCmd);
214
+ const replicas = parseInt(stdout.replace(/'/g, '').trim(), 10);
215
+ if (!isNaN(replicas) && replicas > 0) {
216
+ originalReplicas.set(component, replicas);
217
+ }
218
+ } catch {
219
+ // Component might not exist, continue
220
+ }
221
+ }
222
+ // Scale down to 0
223
+ for (const component of statefulSetComponents){
224
+ try {
225
+ const scaleCmd = `kubectl scale statefulset -l app.kubernetes.io/component=${component} -n ${namespace} --replicas=0 --timeout=2m`;
226
+ logger.info(`command: ${scaleCmd}`);
227
+ await execAsync(scaleCmd);
228
+ } catch (e) {
229
+ // Component might not exist or might be a Deployment, continue
230
+ logger.verbose(`Scale down ${component} skipped: ${e}`);
231
+ }
232
+ }
233
+ // Wait for pods to terminate
234
+ await sleep(15 * 1000);
235
+ // Now delete PVCs (they should no longer be in use)
236
+ for (const component of pvcComponents){
237
+ await deleteResourceByLabel({
238
+ resource: 'persistentvolumeclaims',
239
+ namespace: namespace,
240
+ label: `app.kubernetes.io/component=${component}`
241
+ });
242
+ }
243
+ // Scale StatefulSets back up to original replica counts
244
+ for (const component of statefulSetComponents){
245
+ const replicas = originalReplicas.get(component) ?? 1;
246
+ try {
247
+ const scaleCmd = `kubectl scale statefulset -l app.kubernetes.io/component=${component} -n ${namespace} --replicas=${replicas} --timeout=2m`;
248
+ logger.info(`command: ${scaleCmd}`);
249
+ await execAsync(scaleCmd);
250
+ } catch (e) {
251
+ logger.verbose(`Scale up ${component} skipped: ${e}`);
252
+ }
253
+ }
254
+ } else {
255
+ // Just delete pods (no state clearing)
256
+ for (const component of podComponents){
257
+ await deleteResourceByLabel({
258
+ resource: 'pods',
259
+ namespace: namespace,
260
+ label: `app.kubernetes.io/component=${component}`
261
+ });
262
+ }
263
+ }
264
+ await sleep(10 * 1000);
265
+ // Wait for pods to come back
266
+ for (const component of podComponents){
267
+ await waitForResourceByLabel({
268
+ resource: 'pods',
269
+ namespace: namespace,
270
+ label: `app.kubernetes.io/component=${component}`
271
+ });
272
+ }
273
+ }