Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions typescript/infra/config/docker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ export const mainnetDockerTags: MainnetDockerTags = {
kathy: '74d999b-20260108-145131',
checkWarpDeploy: '74d999b-20260108-145131',
// standalone services
warpMonitor: '74d999b-20260108-145128',
rebalancer: '74d999b-20260108-145129',
warpMonitor: '6b6fd0b-20260123-121413',
rebalancer: '6b6fd0b-20260123-121418',
};

export const testnetDockerTags: BaseDockerTags = {
Expand Down
4 changes: 4 additions & 0 deletions typescript/infra/config/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ export function getChainAddresses(): ChainMap<ChainAddresses> {
return getRegistry().getAddresses();
}

export function warpRouteExistsInRegistry(warpRouteId: string): boolean {
return !!getRegistry().getWarpRoute(warpRouteId);
}

export function getWarpCoreConfig(warpRouteId: string): WarpCoreConfig {
const registry = getRegistry();
const warpRouteConfig = registry.getWarpRoute(warpRouteId);
Expand Down
4 changes: 4 additions & 0 deletions typescript/infra/helm/rebalancer/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ The rebalancer container
value: json
- name: LOG_LEVEL
value: info
{{- if .Values.warpRouteId }}
- name: WARP_ROUTE_ID
value: {{ .Values.warpRouteId }}
{{- end }}
{{- if .Values.hyperlane.registryUri }}
- name: REGISTRY_URI
value: {{ .Values.hyperlane.registryUri }}
Expand Down
1 change: 1 addition & 0 deletions typescript/infra/helm/rebalancer/values.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
image:
repository: gcr.io/abacus-labs-dev/hyperlane-rebalancer
tag:
warpRouteId: ''
hyperlane:
runEnv:
context: hyperlane
Expand Down
27 changes: 27 additions & 0 deletions typescript/infra/scripts/agent-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {
getChains,
getEnvChains,
getRegistry,
warpRouteExistsInRegistry,
} from '../config/registry.js';
import { getCurrentKubernetesContext } from '../src/agents/index.js';
import { getCloudAgentKey } from '../src/agents/key-utils.js';
Expand Down Expand Up @@ -249,6 +250,14 @@ export function withDryRun<T>(args: Argv<T>) {
.default('dryRun', false);
}

export function withYes<T>(args: Argv<T>) {
return args
.describe('yes', 'Skip confirmations and use defaults')
.boolean('yes')
.alias('y', 'yes')
.default('yes', false);
}

export function withKnownWarpRouteIdRequired<T>(args: Argv<T>) {
return withKnownWarpRouteId(args).demandOption('warpRouteId');
}
Expand Down Expand Up @@ -441,6 +450,24 @@ export async function getWarpRouteIdsInteractive(
return selection;
}

export function filterOrphanedWarpRouteIds(warpRouteIds: string[]): {
validIds: string[];
orphanedIds: string[];
} {
const validIds: string[] = [];
const orphanedIds: string[] = [];

for (const id of warpRouteIds) {
if (warpRouteExistsInRegistry(id)) {
validIds.push(id);
} else {
orphanedIds.push(id);
}
}

return { validIds, orphanedIds };
}

// not requiring to build coreConfig to get agentConfig
export async function getAgentConfigsBasedOnArgs(argv?: {
environment: DeployEnvironment;
Expand Down
107 changes: 92 additions & 15 deletions typescript/infra/scripts/rebalancer/deploy-rebalancer.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { input } from '@inquirer/prompts';
import { checkbox, input } from '@inquirer/prompts';
import path from 'path';

import {
Expand All @@ -9,16 +9,21 @@ import {
} from '@hyperlane-xyz/utils';

import { DeployEnvironment } from '../../src/config/environment.js';
import { RebalancerHelmManager } from '../../src/rebalancer/helm.js';
import {
RebalancerHelmManager,
getDeployedRebalancerWarpRouteIds,
} from '../../src/rebalancer/helm.js';
import { REBALANCER_HELM_RELEASE_PREFIX } from '../../src/utils/consts.js';
import { validateRegistryCommit } from '../../src/utils/git.js';
import { HelmCommand } from '../../src/utils/helm.js';
import {
assertCorrectKubeContext,
filterOrphanedWarpRouteIds,
getArgs,
getWarpRouteIdsInteractive,
withMetrics,
withRegistryCommit,
withWarpRouteId,
withYes,
} from '../agent-utils.js';
import { getEnvironmentConfig } from '../core-utils.js';

Expand All @@ -33,30 +38,102 @@ async function main() {
warpRouteId,
metrics,
registryCommit: registryCommitArg,
} = await withMetrics(withRegistryCommit(withWarpRouteId(getArgs()))).parse();
yes: skipConfirmation,
} = await withYes(
withMetrics(withRegistryCommit(withWarpRouteId(getArgs()))),
).parse();

await assertCorrectKubeContext(getEnvironmentConfig(environment));

let warpRouteIds;
let warpRouteIds: string[];
if (warpRouteId) {
warpRouteIds = [warpRouteId];
} else {
warpRouteIds = await getWarpRouteIdsInteractive(environment);
const deployedPods = await getDeployedRebalancerWarpRouteIds(
environment,
REBALANCER_HELM_RELEASE_PREFIX,
);
const deployedIds = [
...new Set(
deployedPods
.map((p) => p.warpRouteId)
.filter((id): id is string => !!id),
),
].sort();

if (deployedIds.length === 0) {
rootLogger.error(
'No deployed rebalancers found. Use --warp-route-id to deploy a new one.',
);
process.exit(1);
}

warpRouteIds = await checkbox({
message: 'Select rebalancers to redeploy',
choices: deployedIds.map((id) => ({ value: id })),
pageSize: 30,
});

if (warpRouteIds.length === 0) {
rootLogger.info('No rebalancers selected');
process.exit(0);
}
}

const { validIds: validWarpRouteIds, orphanedIds } =
filterOrphanedWarpRouteIds(warpRouteIds);

if (orphanedIds.length > 0) {
rootLogger.warn(
`Skipping ${orphanedIds.length} orphaned rebalancers (warp route no longer in registry):\n${orphanedIds.map((id) => ` - ${id}`).join('\n')}`,
);
rootLogger.warn('Run helm uninstall manually to remove these rebalancers');
}

const registryCommit =
registryCommitArg ??
(await input({
message:
'Enter the registry version to use (can be a commit, branch or tag):',
}));
await validateRegistryCommit(registryCommit);
if (validWarpRouteIds.length === 0) {
if (warpRouteId && orphanedIds.includes(warpRouteId)) {
rootLogger.error(
`Warp route "${warpRouteId}" not found in registry. Verify the warp route ID is correct.`,
);
process.exit(1);
}
rootLogger.info('No valid warp routes to deploy');
process.exit(0);
}

rootLogger.info(
`Deploying Rebalancer for the following Route IDs:\n${warpRouteIds.map((id) => ` - ${id}`).join('\n')}`,
`Deploying Rebalancer for the following Route IDs:\n${validWarpRouteIds.map((id) => ` - ${id}`).join('\n')}`,
);

// Cache validated commits to avoid re-validating the same commit
const validatedCommits = new Set<string>();

const deployRebalancer = async (warpRouteId: string) => {
let registryCommit: string;
if (registryCommitArg) {
registryCommit = registryCommitArg;
} else {
const defaultRegistryCommit =
await RebalancerHelmManager.getDeployedRegistryCommit(
warpRouteId,
environment,
);

if (skipConfirmation) {
registryCommit = defaultRegistryCommit ?? 'main';
} else {
registryCommit = await input({
message: `[${warpRouteId}] Enter registry version (commit, branch or tag):`,
default: defaultRegistryCommit,
});
}
}

if (!validatedCommits.has(registryCommit)) {
await validateRegistryCommit(registryCommit);
validatedCommits.add(registryCommit);
}

// Build path for config file - relative for local checks
const configFileName = `${warpRouteId}-config.yaml`;
const relativeConfigPath = path.join(
Expand All @@ -83,7 +160,7 @@ async function main() {

// TODO: Uninstall any stale rebalancer releases.

for (const id of warpRouteIds) {
for (const id of validWarpRouteIds) {
rootLogger.info(`Deploying Rebalancer for Route ID: ${id}`);
await deployRebalancer(id);
}
Expand Down
Loading
Loading