using Amazon.CloudWatchLogs; using Amazon.ECS; using Amazon.ECS.Model; using Microsoft.Extensions.Logging; using StellaOps.Agent.Core.Capability; using StellaOps.Agent.Core.Models; using StellaOps.Agent.Ecs.Tasks; using System.Text.Json; namespace StellaOps.Agent.Ecs; /// /// Agent capability for managing AWS ECS services and tasks. /// public sealed class EcsCapability : IAgentCapability, IAsyncDisposable { private readonly IAmazonECS _ecsClient; private readonly IAmazonCloudWatchLogs _logsClient; private readonly TimeProvider _timeProvider; private readonly ILoggerFactory _loggerFactory; private readonly ILogger _logger; private readonly Dictionary>> _taskHandlers; /// /// Gets the capability name. /// public string Name => "ecs"; /// /// Gets the capability version. /// public string Version => "1.0.0"; /// /// Gets the supported task types. /// public IReadOnlyList SupportedTaskTypes { get; } = new[] { "ecs.deploy", "ecs.run", "ecs.stop", "ecs.scale", "ecs.register", "ecs.health", "ecs.describe" }; /// /// Creates a new ECS capability. /// /// The ECS client. /// The CloudWatch Logs client. /// Time provider for timestamps. /// Logger factory. public EcsCapability( IAmazonECS ecsClient, IAmazonCloudWatchLogs logsClient, TimeProvider timeProvider, ILoggerFactory loggerFactory) { _ecsClient = ecsClient ?? throw new ArgumentNullException(nameof(ecsClient)); _logsClient = logsClient ?? throw new ArgumentNullException(nameof(logsClient)); _timeProvider = timeProvider ?? throw new ArgumentNullException(nameof(timeProvider)); _loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory)); _logger = loggerFactory.CreateLogger(); _taskHandlers = new Dictionary>> { ["ecs.deploy"] = ExecuteDeployAsync, ["ecs.run"] = ExecuteRunTaskAsync, ["ecs.stop"] = ExecuteStopTaskAsync, ["ecs.scale"] = ExecuteScaleAsync, ["ecs.register"] = ExecuteRegisterAsync, ["ecs.health"] = ExecuteHealthCheckAsync, ["ecs.describe"] = ExecuteDescribeAsync }; } /// public async Task InitializeAsync(CancellationToken ct = default) { try { // Verify AWS credentials and ECS access by listing clusters var response = await _ecsClient.ListClustersAsync(new ListClustersRequest { MaxResults = 1 }, ct); _logger.LogInformation( "ECS capability initialized, AWS API accessible"); return true; } catch (Exception ex) { _logger.LogError(ex, "Failed to initialize ECS capability - AWS API not accessible"); return false; } } /// public async Task ExecuteAsync(AgentTaskInfo task, CancellationToken ct = default) { if (!_taskHandlers.TryGetValue(task.TaskType, out var handler)) { throw new InvalidEcsPayloadException(task.TaskType, "Unsupported task type"); } var startTime = _timeProvider.GetUtcNow(); try { var result = await handler(task, ct); return result with { Duration = _timeProvider.GetUtcNow() - startTime }; } catch (InvalidEcsPayloadException) { throw; } catch (Exception ex) { _logger.LogError(ex, "ECS task {TaskType} failed", task.TaskType); return new AgentTaskResult { TaskId = task.Id, Success = false, Error = ex.Message, CompletedAt = _timeProvider.GetUtcNow(), Duration = _timeProvider.GetUtcNow() - startTime }; } } /// public async Task CheckHealthAsync(CancellationToken ct = default) { try { await _ecsClient.ListClustersAsync(new ListClustersRequest { MaxResults = 1 }, ct); return new CapabilityHealthStatus(true, "ECS capability ready"); } catch (Exception ex) { return new CapabilityHealthStatus(false, $"ECS API not accessible: {ex.Message}"); } } private async Task ExecuteDeployAsync(AgentTaskInfo task, CancellationToken ct) { var taskHandler = new EcsDeployServiceTask( _ecsClient, _timeProvider, _loggerFactory.CreateLogger()); return await taskHandler.ExecuteAsync(task, ct); } private async Task ExecuteRunTaskAsync(AgentTaskInfo task, CancellationToken ct) { var taskHandler = new EcsRunTaskTask( _ecsClient, _timeProvider, _loggerFactory.CreateLogger()); return await taskHandler.ExecuteAsync(task, ct); } private async Task ExecuteStopTaskAsync(AgentTaskInfo task, CancellationToken ct) { var taskHandler = new EcsStopTaskTask( _ecsClient, _timeProvider, _loggerFactory.CreateLogger()); return await taskHandler.ExecuteAsync(task, ct); } private async Task ExecuteScaleAsync(AgentTaskInfo task, CancellationToken ct) { var taskHandler = new EcsScaleServiceTask( _ecsClient, _timeProvider, _loggerFactory.CreateLogger()); return await taskHandler.ExecuteAsync(task, ct); } private async Task ExecuteRegisterAsync(AgentTaskInfo task, CancellationToken ct) { var taskHandler = new EcsRegisterTaskDefinitionTask( _ecsClient, _timeProvider, _loggerFactory.CreateLogger()); return await taskHandler.ExecuteAsync(task, ct); } private async Task ExecuteHealthCheckAsync(AgentTaskInfo task, CancellationToken ct) { var taskHandler = new EcsHealthCheckTask( _ecsClient, _timeProvider, _loggerFactory.CreateLogger()); return await taskHandler.ExecuteAsync(task, ct); } private async Task ExecuteDescribeAsync(AgentTaskInfo task, CancellationToken ct) { var taskHandler = new EcsDescribeServiceTask( _ecsClient, _timeProvider, _loggerFactory.CreateLogger()); return await taskHandler.ExecuteAsync(task, ct); } /// public ValueTask DisposeAsync() { _ecsClient.Dispose(); _logsClient.Dispose(); return ValueTask.CompletedTask; } }