fengling-gateway/tools/MigrationTool/Program.cs
movingsam 52eba07097 feat: add MigrationTool for gateway config migration (IMPL-7)
- Create MigrationTool console app for exporting DB config to K8s YAML
- Support dry-run mode and validation
- Add Npgsql and YamlDotNet dependencies
2026-03-08 00:35:04 +08:00

319 lines
9.1 KiB
C#

using System.Text;
using MigrationTool;
using MigrationTool.Models;
// 设置控制台输出编码
Console.OutputEncoding = Encoding.UTF8;
PrintBanner();
// 解析命令行参数
var options = ParseArguments(args);
if (options == null)
{
PrintHelp();
return 1;
}
PrintOptions(options);
// 确认执行
if (!options.DryRun)
{
Console.WriteLine();
Console.Write("确认开始迁移? (y/N): ");
var confirm = Console.ReadLine()?.Trim().ToLowerInvariant();
if (confirm != "y" && confirm != "yes")
{
Console.WriteLine("已取消");
return 0;
}
}
Console.WriteLine();
// 创建日志记录器
var logger = new ConsoleLogger(options.LogLevel);
// 执行迁移
try
{
var service = new MigrationService(options, logger);
var report = await service.MigrateAsync();
// 打印报告
report.PrintSummary();
return report.FailedCount > 0 ? 1 : 0;
}
catch (Exception ex)
{
logger.LogError($"迁移失败: {ex.Message}");
logger.LogDebug(ex.StackTrace ?? "");
return 2;
}
/// <summary>
/// 解析命令行参数
/// </summary>
static MigrationOptions? ParseArguments(string[] args)
{
var options = new MigrationOptions
{
ConnectionString = GetDefaultConnectionString()
};
for (int i = 0; i < args.Length; i++)
{
var arg = args[i];
switch (arg.ToLowerInvariant())
{
case "--help":
case "-h":
case "-?":
return null;
case "--connection-string":
case "-c":
if (i + 1 < args.Length)
options.ConnectionString = args[++i];
break;
case "--output-dir":
case "-o":
if (i + 1 < args.Length)
options.OutputDir = args[++i];
break;
case "--dry-run":
case "-d":
options.DryRun = true;
break;
case "--default-host":
if (i + 1 < args.Length)
options.DefaultHost = args[++i];
break;
case "--service-port":
if (i + 1 < args.Length && int.TryParse(args[++i], out var svcPort))
options.ServicePort = svcPort;
break;
case "--target-port":
if (i + 1 < args.Length && int.TryParse(args[++i], out var tgtPort))
options.TargetPort = tgtPort;
break;
case "--no-validate":
options.Validate = false;
break;
case "--tenant":
case "-t":
if (i + 1 < args.Length)
options.TenantCode = args[++i];
break;
case "--log-level":
case "-l":
if (i + 1 < args.Length && Enum.TryParse<LogLevel>(args[++i], true, out var level))
options.LogLevel = level;
break;
case "--no-report":
options.GenerateReport = false;
break;
case "--report-path":
if (i + 1 < args.Length)
options.ReportPath = args[++i];
break;
default:
Console.WriteLine($"未知参数: {arg}");
break;
}
}
return options;
}
/// <summary>
/// 获取默认连接字符串(从环境变量)
/// </summary>
static string GetDefaultConnectionString()
{
var envConnectionString = Environment.GetEnvironmentVariable("GATEWAY_CONNECTION_STRING");
if (!string.IsNullOrEmpty(envConnectionString))
{
return envConnectionString;
}
return "Host=localhost;Database=fengling_gateway;Username=postgres;Password=postgres";
}
/// <summary>
/// 打印 Banner
/// </summary>
static void PrintBanner()
{
Console.WriteLine();
Console.WriteLine(@" __ __ _ _ _ _ _ _ ");
Console.WriteLine(@" | \/ (_) | ___ _ __ | |_(_)_ __ __ _| |_ ___ | |_ ");
Console.WriteLine(@" | |\/| | | |/ _ \ '_ \| __| | '_ \ / _` | __/ _ \ | __|");
Console.WriteLine(@" | | | | | | __/ | | | |_| | | | | (_| | || __/ | |_ ");
Console.WriteLine(@" |_| |_|_|_|\___|_| |_|\__|_|_| |_|\__,_|\__\___| \__|");
Console.WriteLine(@" ");
Console.WriteLine(@" Fengling Gateway Migration Tool v1.0.0");
Console.WriteLine();
}
/// <summary>
/// 打印帮助信息
/// </summary>
static void PrintHelp()
{
Console.WriteLine("用法: MigrationTool [选项]");
Console.WriteLine();
Console.WriteLine("选项:");
Console.WriteLine(" -h, --help 显示帮助信息");
Console.WriteLine(" -c, --connection-string 数据库连接字符串 (默认: 从 GATEWAY_CONNECTION_STRING 环境变量读取)");
Console.WriteLine(" -o, --output-dir YAML 文件输出目录 (默认: ./output)");
Console.WriteLine(" -d, --dry-run 干运行模式,只输出不写入文件");
Console.WriteLine(" --default-host 默认路由 Host (默认: api.fengling.com)");
Console.WriteLine(" --service-port Service 端口 (默认: 80)");
Console.WriteLine(" --target-port 目标端口 (默认: 8080)");
Console.WriteLine(" --no-validate 跳过数据验证");
Console.WriteLine(" -t, --tenant 仅处理指定租户");
Console.WriteLine(" -l, --log-level 日志级别 (Trace/Debug/Information/Warning/Error)");
Console.WriteLine(" --no-report 不生成报告文件");
Console.WriteLine(" --report-path 指定报告文件路径");
Console.WriteLine();
Console.WriteLine("示例:");
Console.WriteLine(" MigrationTool --dry-run");
Console.WriteLine(" MigrationTool -c \"Host=db;Database=gateway;Username=postgres;Password=secret\" -o ./yaml");
Console.WriteLine(" MigrationTool --tenant tenant1 --dry-run");
Console.WriteLine();
}
/// <summary>
/// 打印选项
/// </summary>
static void PrintOptions(MigrationOptions options)
{
Console.WriteLine("配置选项:");
Console.WriteLine($" 连接字符串: {MaskConnectionString(options.ConnectionString)}");
Console.WriteLine($" 输出目录: {Path.GetFullPath(options.OutputDir)}");
Console.WriteLine($" Dry-Run: {(options.DryRun ? "" : "")}");
Console.WriteLine($" 默认 Host: {options.DefaultHost}");
Console.WriteLine($" Service 端口: {options.ServicePort}");
Console.WriteLine($" Target 端口: {options.TargetPort}");
Console.WriteLine($" 验证数据: {(options.Validate ? "" : "")}");
Console.WriteLine($" 日志级别: {options.LogLevel}");
if (!string.IsNullOrEmpty(options.TenantCode))
{
Console.WriteLine($" 指定租户: {options.TenantCode}");
}
}
/// <summary>
/// 掩盖连接字符串中的敏感信息
/// </summary>
static string MaskConnectionString(string connectionString)
{
if (string.IsNullOrEmpty(connectionString))
return "[empty]";
try
{
var builder = new Npgsql.NpgsqlConnectionStringBuilder(connectionString);
if (!string.IsNullOrEmpty(builder.Password))
{
builder.Password = "***";
}
return builder.ToString();
}
catch
{
return "[invalid]";
}
}
/// <summary>
/// 简单的控制台日志记录器
/// </summary>
public class ConsoleLogger : ILogger
{
private readonly LogLevel _minLevel;
public ConsoleLogger(LogLevel minLevel)
{
_minLevel = minLevel;
}
public void LogTrace(string message)
{
if (_minLevel <= LogLevel.Trace)
WriteLog("TRC", ConsoleColor.Gray, message);
}
public void LogDebug(string message)
{
if (_minLevel <= LogLevel.Debug)
WriteLog("DBG", ConsoleColor.DarkGray, message);
}
public void LogInformation(string message)
{
if (_minLevel <= LogLevel.Information)
WriteLog("INF", ConsoleColor.White, message);
}
public void LogWarning(string message)
{
if (_minLevel <= LogLevel.Warning)
WriteLog("WRN", ConsoleColor.Yellow, message);
}
public void LogError(string message)
{
if (_minLevel <= LogLevel.Error)
WriteLog("ERR", ConsoleColor.Red, message);
}
private static void WriteLog(string level, ConsoleColor color, string message)
{
var timestamp = DateTime.Now.ToString("HH:mm:ss");
var originalColor = Console.ForegroundColor;
Console.ForegroundColor = color;
Console.WriteLine($"[{timestamp}] [{level}] {message}");
Console.ForegroundColor = originalColor;
}
}
/// <summary>
/// 日志记录器接口
/// </summary>
public interface ILogger
{
void LogTrace(string message);
void LogDebug(string message);
void LogInformation(string message);
void LogWarning(string message);
void LogError(string message);
}
/// <summary>
/// 日志级别
/// </summary>
public enum LogLevel
{
Trace,
Debug,
Information,
Warning,
Error
}