using System.Text; using RackPeek.Domain.Resources; using RackPeek.Domain.Resources.SystemResources; namespace RackPeek.Domain.UseCases.SSH; public static class SshConfigGenerator { public static SshExportResult ToSshConfig( this IReadOnlyList resources, SshExportOptions? options = null) { options ??= new SshExportOptions(); var sb = new StringBuilder(); var warnings = new List(); foreach (Resource r in resources.OrderBy(x => x.Name)) { if (options.IncludeTags.Any()) { var tags = r.Tags ?? []; if (!options.IncludeTags.Any(t => tags.Contains(t, StringComparer.OrdinalIgnoreCase))) continue; } var address = GetAddress(r); if (string.IsNullOrWhiteSpace(address)) continue; var alias = options.UseResourceNameAsAlias ? SanitizeAlias(r.Name) : address; var user = GetLabel(r, "ssh_user") ?? GetLabel(r, "ansible_user") ?? options.DefaultUser; var port = GetPort(r) ?? options.DefaultPort; var identity = GetLabel(r, "ssh_identity_file") ?? options.DefaultIdentityFile; sb.AppendLine($"Host {alias}"); sb.AppendLine($" HostName {address}"); if (!string.IsNullOrWhiteSpace(user)) sb.AppendLine($" User {user}"); if (port != 22) sb.AppendLine($" Port {port}"); if (!string.IsNullOrWhiteSpace(identity)) sb.AppendLine($" IdentityFile {identity}"); sb.AppendLine(); } if (sb.Length == 0) warnings.Add("No SSH entries generated."); return new SshExportResult(sb.ToString().TrimEnd(), warnings); } private static string? GetAddress(Resource r) { if (r is SystemResource { Ip: not null } system && !string.IsNullOrWhiteSpace(system!.Ip)) { return system.Ip; } if (r.Labels.TryGetValue("ip", out var ip) && !string.IsNullOrWhiteSpace(ip)) return ip; if (r.Labels.TryGetValue("hostname", out var hn) && !string.IsNullOrWhiteSpace(hn)) return hn; if (r.Labels.TryGetValue("ansible_host", out var ah) && !string.IsNullOrWhiteSpace(ah)) return ah; return null; } private static string? GetLabel(Resource r, string key) { if (r.Labels.TryGetValue(key, out var val) && !string.IsNullOrWhiteSpace(val)) return val; return null; } private static int? GetPort(Resource r) { if (r.Labels.TryGetValue("ssh_port", out var portStr) && int.TryParse(portStr, out var port)) return port; if (r.Labels.TryGetValue("ansible_port", out var ansiblePort) && int.TryParse(ansiblePort, out var aPort)) return aPort; return null; } private static string SanitizeAlias(string name) { var sb = new StringBuilder(); foreach (var ch in name.Trim().ToLowerInvariant()) if (char.IsLetterOrDigit(ch) || ch == '_' || ch == '-') sb.Append(ch); else if (ch == '.' || ch == ' ') sb.Append('-'); return sb.Length == 0 ? "host" : sb.ToString(); } }