Skip to content

Commit a679662

Browse files
committed
feat(infra): simplify RPC rotation k8s refresh with 2-stage UX
- Split refresh prompt into core infra (agents, key-funder, check-warp-deploy) and warp monitors - Add 'refresh all/select/skip' options for warp monitors to reduce prompt clutter - Remove Kathy and filter to Hyperlane+RC relayers, Hyperlane-only validators/scrapers - Fix warp route ID parsing to check both container.command and container.args
1 parent 0dc9fb0 commit a679662

2 files changed

Lines changed: 111 additions & 65 deletions

File tree

typescript/infra/src/utils/rpcUrls.ts

Lines changed: 102 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,7 @@ import {
1818
secretRpcEndpointsExist,
1919
setSecretRpcEndpoints,
2020
} from '../agents/index.js';
21-
import { CheckWarpDeployHelmManager } from '../check-warp-deploy/helm.js';
2221
import { DeployEnvironment } from '../config/environment.js';
23-
import { KeyFunderHelmManager } from '../funding/key-funder.js';
24-
import { KathyHelmManager } from '../helloworld/kathy.js';
2522
import { WarpRouteMonitorHelmManager } from '../warp-monitor/helm.js';
2623

2724
import { disableGCPSecretVersion } from './gcloud.js';
@@ -274,9 +271,7 @@ async function updateSecretAndDisablePrevious(
274271

275272
/**
276273
* Interactively refreshes dependent k8s resources for the given chain in the given environment.
277-
* Allows for helm releases to be selected for refreshing. Refreshing involves first deleting
278-
* secrets, expecting them to be recreated by external-secrets, and then deleting pods to restart
279-
* them with the new secrets.
274+
* Prompts for core infrastructure and warp monitors separately, then executes all refreshes together.
280275
* @param environment The environment to refresh resources in
281276
* @param chain The chain to refresh resources for
282277
*/
@@ -292,87 +287,134 @@ async function refreshDependentK8sResourcesInteractive(
292287
return;
293288
}
294289

290+
// Collect selections from both prompts
291+
const coreManagers = await selectCoreInfrastructure(environment, chain);
292+
const warpManagers = await selectWarpMonitors(environment, chain);
293+
294+
// Execute all refreshes together
295+
const allManagers = [...coreManagers, ...warpManagers];
296+
if (allManagers.length > 0) {
297+
await refreshK8sResources(allManagers, K8sResourceType.SECRET, environment);
298+
await refreshK8sResources(allManagers, K8sResourceType.POD, environment);
299+
}
300+
}
301+
302+
async function selectCoreInfrastructure(
303+
environment: DeployEnvironment,
304+
chain: string,
305+
): Promise<HelmManager<any>[]> {
295306
const envConfig = getEnvironmentConfig(environment);
296-
const contextHelmManagers: [string, HelmManager<any>][] = [];
297-
const pushContextHelmManager = (
298-
context: string,
299-
manager: HelmManager<any>,
300-
) => {
301-
contextHelmManagers.push([context, manager]);
302-
};
307+
const coreHelmManagers: [string, HelmManager<any>][] = [];
308+
303309
for (const [context, agentConfig] of Object.entries(envConfig.agents)) {
304-
if (agentConfig.relayer) {
305-
pushContextHelmManager(context, new RelayerHelmManager(agentConfig));
310+
// Relayer: Hyperlane + RC only
311+
if (
312+
agentConfig.relayer &&
313+
(context === Contexts.Hyperlane || context === Contexts.ReleaseCandidate)
314+
) {
315+
coreHelmManagers.push([context, new RelayerHelmManager(agentConfig)]);
306316
}
317+
318+
// Validator: Hyperlane only, for the affected chain
307319
if (
320+
context === Contexts.Hyperlane &&
308321
agentConfig.validators &&
309322
agentConfig.contextChainNames.validator?.includes(chain)
310323
) {
311-
pushContextHelmManager(
324+
coreHelmManagers.push([
312325
context,
313326
new ValidatorHelmManager(agentConfig, chain),
314-
);
315-
}
316-
if (agentConfig.scraper) {
317-
pushContextHelmManager(context, new ScraperHelmManager(agentConfig));
327+
]);
318328
}
319329

320-
if (context == Contexts.Hyperlane) {
321-
// Key funder
322-
pushContextHelmManager(
323-
context,
324-
KeyFunderHelmManager.forEnvironment(environment),
325-
);
326-
327-
// Kathy - only expected to be running as a long-running service in the
328-
// Hyperlane context
329-
if (envConfig.helloWorld?.hyperlane?.addresses[chain]) {
330-
pushContextHelmManager(
331-
context,
332-
KathyHelmManager.forEnvironment(environment, context),
333-
);
334-
}
330+
// Scraper: Hyperlane only
331+
if (context === Contexts.Hyperlane && agentConfig.scraper) {
332+
coreHelmManagers.push([context, new ScraperHelmManager(agentConfig)]);
335333
}
336334
}
337335

338-
// Warp route monitors that include the affected chain
336+
if (coreHelmManagers.length === 0) {
337+
console.log('No core infrastructure to refresh');
338+
return [];
339+
}
340+
341+
const selection = await checkbox({
342+
message:
343+
'Select core infrastructure to refresh (update secrets & restart pods)',
344+
choices: coreHelmManagers.map(([context, helmManager], i) => ({
345+
name: `${helmManager.helmReleaseName} (context: ${context})`,
346+
value: i,
347+
checked: true,
348+
})),
349+
});
350+
351+
return coreHelmManagers
352+
.map(([_, m]) => m)
353+
.filter((_, i) => selection.includes(i));
354+
}
355+
356+
enum WarpMonitorRefreshChoice {
357+
ALL = 'all',
358+
SELECT = 'select',
359+
SKIP = 'skip',
360+
}
361+
362+
async function selectWarpMonitors(
363+
environment: DeployEnvironment,
364+
chain: string,
365+
): Promise<WarpRouteMonitorHelmManager[]> {
339366
const warpMonitorManagers =
340367
await WarpRouteMonitorHelmManager.getManagersForChain(environment, chain);
368+
369+
if (warpMonitorManagers.length === 0) {
370+
console.log(`No warp route monitors found for ${chain}`);
371+
return [];
372+
}
373+
374+
console.log(
375+
`Found ${warpMonitorManagers.length} warp route monitors that include ${chain}:`,
376+
);
341377
for (const manager of warpMonitorManagers) {
342-
pushContextHelmManager(Contexts.Hyperlane, manager);
378+
console.log(` - ${manager.helmReleaseName} (${manager.warpRouteId})`);
379+
}
380+
381+
const choice = await select({
382+
message: `Refresh warp route monitors?`,
383+
choices: [
384+
{
385+
name: `Yes, refresh all ${warpMonitorManagers.length} monitors`,
386+
value: WarpMonitorRefreshChoice.ALL,
387+
},
388+
{
389+
name: 'Yes, let me select which ones',
390+
value: WarpMonitorRefreshChoice.SELECT,
391+
},
392+
{
393+
name: 'No, skip warp monitors',
394+
value: WarpMonitorRefreshChoice.SKIP,
395+
},
396+
],
397+
});
398+
399+
if (choice === WarpMonitorRefreshChoice.SKIP) {
400+
console.log('Skipping warp monitor refresh');
401+
return [];
343402
}
344403

345-
// Check warp deploy cron job
346-
const checkWarpDeployManager =
347-
CheckWarpDeployHelmManager.forEnvironment(environment);
348-
if (checkWarpDeployManager) {
349-
pushContextHelmManager(Contexts.Hyperlane, checkWarpDeployManager);
404+
if (choice === WarpMonitorRefreshChoice.ALL) {
405+
return warpMonitorManagers;
350406
}
351407

352408
const selection = await checkbox({
353-
message:
354-
'Select deployments to refresh (update secrets & restart any pods)',
355-
choices: contextHelmManagers.map(([context, helmManager], i) => ({
356-
name: `${helmManager.helmReleaseName} (context: ${context})`,
409+
message: 'Select warp monitors to refresh',
410+
choices: warpMonitorManagers.map((manager, i) => ({
411+
name: manager.helmReleaseName,
357412
value: i,
358-
// By default, all deployments are selected
359413
checked: true,
360414
})),
361415
});
362-
const selectedHelmManagers = contextHelmManagers
363-
.map(([_, m]) => m)
364-
.filter((_, m) => selection.includes(m));
365416

366-
await refreshK8sResources(
367-
selectedHelmManagers,
368-
K8sResourceType.SECRET,
369-
environment,
370-
);
371-
await refreshK8sResources(
372-
selectedHelmManagers,
373-
K8sResourceType.POD,
374-
environment,
375-
);
417+
return warpMonitorManagers.filter((_, i) => selection.includes(i));
376418
}
377419

378420
/**

typescript/infra/src/warp-monitor/helm.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -296,11 +296,15 @@ export async function getDeployedWarpMonitorWarpRouteIds(
296296
break;
297297
}
298298

299-
// Legacy monorepo image: --warpRouteId arg
300-
const args: string[] = container.args || [];
301-
const warpRouteIdArgIndex = args.indexOf('--warpRouteId');
302-
if (warpRouteIdArgIndex !== -1 && args[warpRouteIdArgIndex + 1]) {
303-
warpRouteId = args[warpRouteIdArgIndex + 1];
299+
// Legacy monorepo image: --warpRouteId in command or args
300+
// Some pods use container.command, others use container.args
301+
const allArgs: string[] = [
302+
...(container.command || []),
303+
...(container.args || []),
304+
];
305+
const warpRouteIdArgIndex = allArgs.indexOf('--warpRouteId');
306+
if (warpRouteIdArgIndex !== -1 && allArgs[warpRouteIdArgIndex + 1]) {
307+
warpRouteId = allArgs[warpRouteIdArgIndex + 1];
304308
break;
305309
}
306310
}

0 commit comments

Comments
 (0)