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;
}
}