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