- Create MigrationTool console app for exporting DB config to K8s YAML - Support dry-run mode and validation - Add Npgsql and YamlDotNet dependencies
319 lines
9.1 KiB
C#
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
|
|
}
|