using ZA.CoreService.ESBCertificateManager.Models;
namespace ZA.CoreService.ESBCertificateManager.Services;
///
/// Fragt alle konfigurierten Sonic-Management-Verbindungen nach ihren Containern
/// und gleicht die gefundenen Container mit den bereits konfigurierten Deployment-Zielen ab.
///
public sealed class SonicContainerDiscovery
{
private readonly IReadOnlyList _connections;
public SonicContainerDiscovery(IReadOnlyList connections)
{
_connections = connections;
}
public bool HasConnections => _connections.Count > 0;
public IReadOnlyList Connections => _connections;
///
/// Verbindet sich mit der angegebenen Sonic-Instanz, liest die Container-Liste
/// und reichert sie mit konfigurierten Ziel-Metadaten an.
///
public async Task DiscoverAsync(
string connectionName,
IReadOnlyList knownTargets,
CancellationToken cancellationToken = default)
{
SonicConnection? connection = _connections
.FirstOrDefault(c => string.Equals(c.Name, connectionName, StringComparison.OrdinalIgnoreCase));
if (connection is null)
{
return SonicDiscoveryResult.Failed(connectionName,
$"Sonic-Verbindung '{connectionName}' ist nicht in AppSettings konfiguriert.");
}
using SonicManagementClient client = new(connection);
(bool reachable, string? pingError, string? resolvedPath) = await client.CheckConnectionAsync(cancellationToken);
if (!reachable)
{
return SonicDiscoveryResult.Failed(connectionName,
$"Management-Konsole nicht erreichbar: {pingError}");
}
(bool listOk, IReadOnlyList containerNames, string? listError) =
await client.GetContainersAsync(cancellationToken);
if (!listOk)
{
// Fallback: CheckConnection erfolgreich, aber /containers nicht gefunden –
// dies kann passieren wenn die API-Pfade abweichen. Container-Namen sind dann leer.
containerNames = [];
}
List discovered = BuildTargets(connection, containerNames, knownTargets);
return new SonicDiscoveryResult
{
ConnectionName = connectionName,
Success = true,
ErrorMessage = listOk ? null : $"Container-Liste konnte nicht geladen werden: {listError}",
DiscoveredTargets = discovered,
RawContainerNames = containerNames
};
}
private static List BuildTargets(
SonicConnection connection,
IReadOnlyList containerNames,
IReadOnlyList knownTargets)
{
List result = [];
int syntheticId = -1;
foreach (string containerName in containerNames)
{
// Bekanntes, voll-konfiguriertes Ziel suchen (nach ContainerName + SonicConnectionName)
DeploymentTarget? existing = knownTargets.FirstOrDefault(t =>
string.Equals(t.ContainerName, containerName, StringComparison.OrdinalIgnoreCase) &&
string.Equals(t.SonicConnectionName, connection.Name, StringComparison.OrdinalIgnoreCase));
if (existing is not null)
{
result.Add(existing);
}
else
{
// Minimales Ziel aus der Discovery erzeugen.
// TargetDirectory/CertificateFileName sind leer → Preflight-Validator zeigt Warnung.
result.Add(new DeploymentTarget
{
Id = syntheticId--,
Name = $"{connection.Name} / {containerName}",
Environment = connection.DomainName,
IsActive = true,
TargetDirectory = string.Empty,
CertificateFileName = string.Empty,
ContainerName = containerName,
RestartType = RestartType.SonicContainer,
SonicConnectionName = connection.Name,
SortOrder = result.Count * 10
});
}
}
// Konfigurierte Ziele dieser Verbindung, die NICHT in der Discovery aufgetaucht sind,
// trotzdem anzeigen (könnten offline / gestoppt sein).
foreach (DeploymentTarget known in knownTargets)
{
if (!string.Equals(known.SonicConnectionName, connection.Name, StringComparison.OrdinalIgnoreCase))
continue;
bool alreadyAdded = result.Any(r =>
string.Equals(r.ContainerName, known.ContainerName, StringComparison.OrdinalIgnoreCase));
if (!alreadyAdded)
{
result.Add(known);
}
}
return result;
}
}
public sealed class SonicDiscoveryResult
{
public required string ConnectionName { get; init; }
public bool Success { get; init; }
public string? ErrorMessage { get; init; }
public IReadOnlyList DiscoveredTargets { get; init; } = [];
public IReadOnlyList RawContainerNames { get; init; } = [];
public static SonicDiscoveryResult Failed(string connectionName, string error)
=> new() { ConnectionName = connectionName, Success = false, ErrorMessage = error };
}