Initial commit: ESB Certificate Manager.
EOF Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
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 };
|
||||
}
|
||||
Reference in New Issue
Block a user