ecs-pf
Version:
CLI for port-forwarding to RDS via AWS ECS
152 lines (151 loc) • 6.44 kB
JavaScript
import { isDefined } from "remeda";
import { getECSClustersWithExecCapability, getECSTasksWithExecCapability, } from "../aws-services.js";
import { parseClusterName } from "../types.js";
import { messages } from "../utils/messages.js";
import { inferClustersFromRDSName } from "./cluster-inference.js";
import { PerformanceTracker } from "./performance-tracker.js";
import { scoreTasksAgainstRDS, scoreTasksByNaming } from "./task-scoring.js";
export async function inferECSTargets(params) {
const { ecsClient, selectedRDS: rdsInstance, enablePerformanceTracking = false, } = params;
const tracker = new PerformanceTracker();
const results = [];
try {
tracker.startStep("Load analysis results");
const analysisResults = {
environment: [],
naming: [],
network: [],
};
tracker.endStep();
tracker.startStep("Get ECS clusters with exec capability");
const clustersResult = await getECSClustersWithExecCapability(ecsClient);
if (!clustersResult.success)
throw new Error(clustersResult.error);
const allClusters = clustersResult.data;
const clusterMap = new Map(allClusters.map((c) => [c.clusterName, c]));
tracker.endStep();
tracker.startStep("RDS name-based cluster inference");
const likelyClusterNames = inferClustersFromRDSName({
rdsName: rdsInstance.dbInstanceIdentifier,
allClusters,
});
const likelyClusters = likelyClusterNames
.map((name) => {
const clusterNameResult = parseClusterName(name);
if (!clusterNameResult.success)
return undefined;
return clusterMap.get(clusterNameResult.data);
})
.filter((cluster) => isDefined(cluster));
tracker.endStep();
tracker.startStep("Search tasks in high-scoring clusters");
const highScoreClusters = likelyClusters.slice(0, 4);
const highScoreResults = await Promise.all(highScoreClusters.map(async (cluster) => {
try {
const tasksResult = await getECSTasksWithExecCapability(ecsClient, cluster);
if (!tasksResult.success)
return [];
const tasks = tasksResult.data;
if (tasks.length > 0) {
const scored = await scoreTasksAgainstRDS({
ecsClient,
tasks,
cluster,
rdsInstance,
analysisResults,
});
return scored.map((result) => ({
...result,
reasons: [result.reason],
}));
}
else {
return [];
}
}
catch {
return [];
}
}));
results.push(...highScoreResults.flat());
tracker.endStep();
tracker.startStep("Search tasks in remaining clusters");
const remainingInferredClusters = likelyClusters.slice(4);
const nonInferredClusters = allClusters.filter((cluster) => !likelyClusterNames.includes(cluster.clusterName));
const remainingResults = await Promise.all([...remainingInferredClusters, ...nonInferredClusters].map(async (cluster) => {
try {
const tasksResult = await getECSTasksWithExecCapability(ecsClient, cluster);
if (!tasksResult.success)
return [];
const tasks = tasksResult.data;
if (tasks.length > 0) {
const scored = await scoreTasksByNaming({
tasks,
cluster,
rdsInstance,
});
return scored.map((result) => ({
...result,
confidence: "low",
reasons: [result.reason],
}));
}
else {
return [];
}
}
catch {
return [];
}
}));
results.push(...remainingResults.flat());
tracker.endStep();
const validResults = results.filter((result) => {
return (result.task.taskStatus === "RUNNING" ||
result.task.taskStatus === "PENDING");
});
const invalidResults = results.filter((result) => {
return (result.task.taskStatus !== "RUNNING" &&
result.task.taskStatus !== "PENDING");
});
const sortedValidResults = validResults.sort((a, b) => {
const confidenceOrder = { high: 3, medium: 2, low: 1 };
const confidenceDiff = confidenceOrder[b.confidence] - confidenceOrder[a.confidence];
if (confidenceDiff !== 0)
return confidenceDiff;
return b.score - a.score;
});
const finalResults = [
...sortedValidResults,
...invalidResults.map((result) => ({
...result,
confidence: "low",
score: 0,
reason: `${result.reason} (タスク停止中 - 接続不可)`,
})),
];
if (enablePerformanceTracking) {
messages.debug(`推論結果サマリー:`);
messages.debug(` - 総クラスター数: ${allClusters.length}個`);
messages.debug(` - 高スコアクラスター: ${highScoreClusters.length}個`);
messages.debug(` - 推論クラスター: ${likelyClusterNames.length}個`);
messages.debug(` - 検索済みクラスター: ${allClusters.length}個`);
messages.debug(` - 検索済みタスク: ${results.length}個`);
messages.debug(` - 接続可能: ${validResults.length}個`);
messages.debug(` - 接続不可: ${invalidResults.length}個`);
}
return finalResults;
}
catch (error) {
messages.error(`Error during ECS target inference: ${String(error)}`);
throw error;
}
finally {
if (enablePerformanceTracking) {
messages.debug(tracker.getReport());
}
}
}
export function formatInferenceResult(result) {
return result.task.serviceName;
}