Files
123123/ZA.CoreService.ESBCertificateManager/Services/SonicContainerDiscovery.cs
T
2026-07-24 08:10:13 +02:00

140 lines
5.4 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using ZA.CoreService.ESBCertificateManager.Models;
namespace ZA.CoreService.ESBCertificateManager.Services;
/// <summary>
/// Fragt alle konfigurierten Sonic-Management-Verbindungen nach ihren Containern
/// und gleicht die gefundenen Container mit den bereits konfigurierten Deployment-Zielen ab.
/// </summary>
public sealed class SonicContainerDiscovery
{
private readonly IReadOnlyList<SonicConnection> _connections;
public SonicContainerDiscovery(IReadOnlyList<SonicConnection> connections)
{
_connections = connections;
}
public bool HasConnections => _connections.Count > 0;
public IReadOnlyList<SonicConnection> Connections => _connections;
/// <summary>
/// Verbindet sich mit der angegebenen Sonic-Instanz, liest die Container-Liste
/// und reichert sie mit konfigurierten Ziel-Metadaten an.
/// </summary>
public async Task<SonicDiscoveryResult> DiscoverAsync(
string connectionName,
IReadOnlyList<DeploymentTarget> 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<string> 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<DeploymentTarget> 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<DeploymentTarget> BuildTargets(
SonicConnection connection,
IReadOnlyList<string> containerNames,
IReadOnlyList<DeploymentTarget> knownTargets)
{
List<DeploymentTarget> 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<DeploymentTarget> DiscoveredTargets { get; init; } = [];
public IReadOnlyList<string> RawContainerNames { get; init; } = [];
public static SonicDiscoveryResult Failed(string connectionName, string error)
=> new() { ConnectionName = connectionName, Success = false, ErrorMessage = error };
}