UNPKG

ecs-pf

Version:

CLI for port-forwarding to RDS via AWS ECS

152 lines (151 loc) 6.44 kB
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; }