Merge branch 'codex/jetstream-full-parity-executeplan' into main
# Conflicts: # differences.md # docs/plans/2026-02-23-jetstream-full-parity-plan.md # src/NATS.Server/Auth/Account.cs # src/NATS.Server/Configuration/ConfigProcessor.cs # src/NATS.Server/Monitoring/VarzHandler.cs # src/NATS.Server/NatsClient.cs # src/NATS.Server/NatsOptions.cs # src/NATS.Server/NatsServer.cs
This commit is contained in:
@@ -217,6 +217,26 @@ public static class ConfigProcessor
|
||||
opts.AllowNonTls = ToBool(value);
|
||||
break;
|
||||
|
||||
// Cluster / inter-server / JetStream
|
||||
case "cluster":
|
||||
if (value is Dictionary<string, object?> clusterDict)
|
||||
opts.Cluster = ParseCluster(clusterDict, errors);
|
||||
break;
|
||||
case "gateway":
|
||||
if (value is Dictionary<string, object?> gatewayDict)
|
||||
opts.Gateway = ParseGateway(gatewayDict, errors);
|
||||
break;
|
||||
case "leaf":
|
||||
case "leafnode":
|
||||
case "leafnodes":
|
||||
if (value is Dictionary<string, object?> leafDict)
|
||||
opts.LeafNode = ParseLeafNode(leafDict, errors);
|
||||
break;
|
||||
case "jetstream":
|
||||
if (value is Dictionary<string, object?> jsDict)
|
||||
opts.JetStream = ParseJetStream(jsDict, errors);
|
||||
break;
|
||||
|
||||
// Tags
|
||||
case "server_tags":
|
||||
if (value is Dictionary<string, object?> tagsDict)
|
||||
@@ -348,6 +368,9 @@ public static class ConfigProcessor
|
||||
private static readonly Regex DurationPattern = new(
|
||||
@"^(-?\d+(?:\.\d+)?)\s*(ms|s|m|h)$",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
private static readonly Regex ByteSizePattern = new(
|
||||
@"^(\d+)\s*(b|kb|mb|gb|tb)?$",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
private static TimeSpan ParseDurationString(string s)
|
||||
{
|
||||
@@ -368,6 +391,133 @@ public static class ConfigProcessor
|
||||
};
|
||||
}
|
||||
|
||||
// ─── Cluster / gateway / leafnode / JetStream parsing ────────
|
||||
|
||||
private static ClusterOptions ParseCluster(Dictionary<string, object?> dict, List<string> errors)
|
||||
{
|
||||
var options = new ClusterOptions();
|
||||
foreach (var (key, value) in dict)
|
||||
{
|
||||
switch (key.ToLowerInvariant())
|
||||
{
|
||||
case "name":
|
||||
options.Name = ToString(value);
|
||||
break;
|
||||
case "listen":
|
||||
try
|
||||
{
|
||||
var (host, port) = ParseHostPort(value);
|
||||
if (host is not null)
|
||||
options.Host = host;
|
||||
if (port is not null)
|
||||
options.Port = port.Value;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
errors.Add($"Invalid cluster.listen: {ex.Message}");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
private static GatewayOptions ParseGateway(Dictionary<string, object?> dict, List<string> errors)
|
||||
{
|
||||
var options = new GatewayOptions();
|
||||
foreach (var (key, value) in dict)
|
||||
{
|
||||
switch (key.ToLowerInvariant())
|
||||
{
|
||||
case "name":
|
||||
options.Name = ToString(value);
|
||||
break;
|
||||
case "listen":
|
||||
try
|
||||
{
|
||||
var (host, port) = ParseHostPort(value);
|
||||
if (host is not null)
|
||||
options.Host = host;
|
||||
if (port is not null)
|
||||
options.Port = port.Value;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
errors.Add($"Invalid gateway.listen: {ex.Message}");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
private static LeafNodeOptions ParseLeafNode(Dictionary<string, object?> dict, List<string> errors)
|
||||
{
|
||||
var options = new LeafNodeOptions();
|
||||
foreach (var (key, value) in dict)
|
||||
{
|
||||
if (key.Equals("listen", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
try
|
||||
{
|
||||
var (host, port) = ParseHostPort(value);
|
||||
if (host is not null)
|
||||
options.Host = host;
|
||||
if (port is not null)
|
||||
options.Port = port.Value;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
errors.Add($"Invalid leafnode.listen: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
private static JetStreamOptions ParseJetStream(Dictionary<string, object?> dict, List<string> errors)
|
||||
{
|
||||
var options = new JetStreamOptions();
|
||||
foreach (var (key, value) in dict)
|
||||
{
|
||||
switch (key.ToLowerInvariant())
|
||||
{
|
||||
case "store_dir":
|
||||
options.StoreDir = ToString(value);
|
||||
break;
|
||||
case "max_mem_store":
|
||||
try
|
||||
{
|
||||
options.MaxMemoryStore = ParseByteSize(value);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
errors.Add($"Invalid jetstream.max_mem_store: {ex.Message}");
|
||||
}
|
||||
|
||||
break;
|
||||
case "max_file_store":
|
||||
try
|
||||
{
|
||||
options.MaxFileStore = ParseByteSize(value);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
errors.Add($"Invalid jetstream.max_file_store: {ex.Message}");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
// ─── Authorization parsing ─────────────────────────────────────
|
||||
|
||||
private static void ParseAuthorization(Dictionary<string, object?> dict, NatsOptions opts, List<string> errors)
|
||||
@@ -785,6 +935,40 @@ public static class ConfigProcessor
|
||||
_ => throw new FormatException($"Cannot convert {value?.GetType().Name ?? "null"} to long"),
|
||||
};
|
||||
|
||||
private static long ParseByteSize(object? value)
|
||||
{
|
||||
if (value is long l)
|
||||
return l;
|
||||
if (value is int i)
|
||||
return i;
|
||||
if (value is double d)
|
||||
return (long)d;
|
||||
if (value is not string s)
|
||||
throw new FormatException($"Cannot parse byte size from {value?.GetType().Name ?? "null"}");
|
||||
|
||||
var trimmed = s.Trim();
|
||||
var match = ByteSizePattern.Match(trimmed);
|
||||
if (!match.Success)
|
||||
throw new FormatException($"Cannot parse byte size: '{s}'");
|
||||
|
||||
var amount = long.Parse(match.Groups[1].Value, CultureInfo.InvariantCulture);
|
||||
var unit = match.Groups[2].Value.ToLowerInvariant();
|
||||
var multiplier = unit switch
|
||||
{
|
||||
"" or "b" => 1L,
|
||||
"kb" => 1024L,
|
||||
"mb" => 1024L * 1024L,
|
||||
"gb" => 1024L * 1024L * 1024L,
|
||||
"tb" => 1024L * 1024L * 1024L * 1024L,
|
||||
_ => throw new FormatException($"Unknown byte-size unit: '{unit}'"),
|
||||
};
|
||||
|
||||
checked
|
||||
{
|
||||
return amount * multiplier;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool ToBool(object? value) => value switch
|
||||
{
|
||||
bool b => b,
|
||||
|
||||
Reference in New Issue
Block a user