Add unit tests for Router configuration and transport layers
- Implemented tests for RouterConfig, RoutingOptions, StaticInstanceConfig, and RouterConfigOptions to ensure default values are set correctly. - Added tests for RouterConfigProvider to validate configurations and ensure defaults are returned when no file is specified. - Created tests for ConfigValidationResult to check success and error scenarios. - Developed tests for ServiceCollectionExtensions to verify service registration for RouterConfig. - Introduced UdpTransportTests to validate serialization, connection, request-response, and error handling in UDP transport. - Added scripts for signing authority gaps and hashing DevPortal SDK snippets.
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
namespace StellaOps.Router.Config;
|
||||
|
||||
/// <summary>
|
||||
/// Provides access to router configuration with hot-reload support.
|
||||
/// </summary>
|
||||
public interface IRouterConfigProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the current router configuration.
|
||||
/// </summary>
|
||||
RouterConfig Current { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current router configuration options.
|
||||
/// </summary>
|
||||
RouterConfigOptions Options { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Raised when the configuration is reloaded.
|
||||
/// </summary>
|
||||
event EventHandler<ConfigChangedEventArgs>? ConfigurationChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Reloads the configuration from the source.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task representing the reload operation.</returns>
|
||||
Task ReloadAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Validates the current configuration.
|
||||
/// </summary>
|
||||
/// <returns>Validation result.</returns>
|
||||
ConfigValidationResult Validate();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event arguments for configuration changes.
|
||||
/// </summary>
|
||||
public sealed class ConfigChangedEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ConfigChangedEventArgs"/> class.
|
||||
/// </summary>
|
||||
/// <param name="previous">The previous configuration.</param>
|
||||
/// <param name="current">The current configuration.</param>
|
||||
public ConfigChangedEventArgs(RouterConfig previous, RouterConfig current)
|
||||
{
|
||||
Previous = previous;
|
||||
Current = current;
|
||||
ChangedAt = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the previous configuration.
|
||||
/// </summary>
|
||||
public RouterConfig Previous { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current configuration.
|
||||
/// </summary>
|
||||
public RouterConfig Current { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the time the configuration was changed.
|
||||
/// </summary>
|
||||
public DateTime ChangedAt { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Result of configuration validation.
|
||||
/// </summary>
|
||||
public sealed class ConfigValidationResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets whether the configuration is valid.
|
||||
/// </summary>
|
||||
public bool IsValid => Errors.Count == 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the validation errors.
|
||||
/// </summary>
|
||||
public List<string> Errors { get; init; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the validation warnings.
|
||||
/// </summary>
|
||||
public List<string> Warnings { get; init; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// A successful validation result.
|
||||
/// </summary>
|
||||
public static ConfigValidationResult Success => new();
|
||||
}
|
||||
@@ -12,8 +12,18 @@ public sealed class RouterConfig
|
||||
/// </summary>
|
||||
public PayloadLimits PayloadLimits { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the routing options.
|
||||
/// </summary>
|
||||
public RoutingOptions Routing { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the service configurations.
|
||||
/// </summary>
|
||||
public List<ServiceConfig> Services { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the static instance configurations.
|
||||
/// </summary>
|
||||
public List<StaticInstanceConfig> StaticInstances { get; set; } = [];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
namespace StellaOps.Router.Config;
|
||||
|
||||
/// <summary>
|
||||
/// Options for the router configuration provider.
|
||||
/// </summary>
|
||||
public sealed class RouterConfigOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the path to the router configuration file (YAML or JSON).
|
||||
/// </summary>
|
||||
public string? ConfigPath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the environment variable prefix for overrides.
|
||||
/// Default: "STELLAOPS_ROUTER_".
|
||||
/// </summary>
|
||||
public string EnvironmentVariablePrefix { get; set; } = "STELLAOPS_ROUTER_";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether to enable hot-reload of configuration.
|
||||
/// </summary>
|
||||
public bool EnableHotReload { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the debounce interval for file change notifications.
|
||||
/// </summary>
|
||||
public TimeSpan DebounceInterval { get; set; } = TimeSpan.FromMilliseconds(500);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether to throw on configuration validation errors.
|
||||
/// If false, keeps the previous valid configuration.
|
||||
/// </summary>
|
||||
public bool ThrowOnValidationError { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the configuration section name in appsettings.json.
|
||||
/// </summary>
|
||||
public string ConfigurationSection { get; set; } = "Router";
|
||||
}
|
||||
321
src/__Libraries/StellaOps.Router.Config/RouterConfigProvider.cs
Normal file
321
src/__Libraries/StellaOps.Router.Config/RouterConfigProvider.cs
Normal file
@@ -0,0 +1,321 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace StellaOps.Router.Config;
|
||||
|
||||
/// <summary>
|
||||
/// Provides router configuration with hot-reload support.
|
||||
/// </summary>
|
||||
public sealed class RouterConfigProvider : IRouterConfigProvider, IDisposable
|
||||
{
|
||||
private readonly RouterConfigOptions _options;
|
||||
private readonly ILogger<RouterConfigProvider> _logger;
|
||||
private readonly FileSystemWatcher? _watcher;
|
||||
private readonly SemaphoreSlim _reloadLock = new(1, 1);
|
||||
private readonly Timer? _debounceTimer;
|
||||
private RouterConfig _current;
|
||||
private bool _disposed;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<ConfigChangedEventArgs>? ConfigurationChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RouterConfigProvider"/> class.
|
||||
/// </summary>
|
||||
public RouterConfigProvider(
|
||||
IOptions<RouterConfigOptions> options,
|
||||
ILogger<RouterConfigProvider> logger)
|
||||
{
|
||||
_options = options.Value;
|
||||
_logger = logger;
|
||||
_current = LoadConfiguration();
|
||||
|
||||
if (_options.EnableHotReload && !string.IsNullOrEmpty(_options.ConfigPath) && File.Exists(_options.ConfigPath))
|
||||
{
|
||||
var directory = Path.GetDirectoryName(Path.GetFullPath(_options.ConfigPath))!;
|
||||
var fileName = Path.GetFileName(_options.ConfigPath);
|
||||
|
||||
_watcher = new FileSystemWatcher(directory)
|
||||
{
|
||||
Filter = fileName,
|
||||
NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size
|
||||
};
|
||||
|
||||
_debounceTimer = new Timer(OnDebounceElapsed, null, Timeout.Infinite, Timeout.Infinite);
|
||||
|
||||
_watcher.Changed += OnFileChanged;
|
||||
_watcher.EnableRaisingEvents = true;
|
||||
|
||||
_logger.LogInformation("Hot-reload enabled for configuration file: {Path}", _options.ConfigPath);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public RouterConfig Current => _current;
|
||||
|
||||
/// <inheritdoc />
|
||||
public RouterConfigOptions Options => _options;
|
||||
|
||||
private void OnFileChanged(object sender, FileSystemEventArgs e)
|
||||
{
|
||||
// Debounce rapid file changes (e.g., from editors saving multiple times)
|
||||
_debounceTimer?.Change(_options.DebounceInterval, Timeout.InfiniteTimeSpan);
|
||||
}
|
||||
|
||||
private void OnDebounceElapsed(object? state)
|
||||
{
|
||||
_ = ReloadAsyncInternal();
|
||||
}
|
||||
|
||||
private async Task ReloadAsyncInternal()
|
||||
{
|
||||
if (!await _reloadLock.WaitAsync(TimeSpan.Zero))
|
||||
{
|
||||
// Another reload is in progress
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var previous = _current;
|
||||
var newConfig = LoadConfiguration();
|
||||
var validation = ValidateConfig(newConfig);
|
||||
|
||||
if (!validation.IsValid)
|
||||
{
|
||||
if (_options.ThrowOnValidationError)
|
||||
{
|
||||
throw new ConfigurationException(
|
||||
$"Configuration validation failed: {string.Join("; ", validation.Errors)}");
|
||||
}
|
||||
|
||||
_logger.LogError(
|
||||
"Configuration validation failed, keeping previous: {Errors}",
|
||||
string.Join("; ", validation.Errors));
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var warning in validation.Warnings)
|
||||
{
|
||||
_logger.LogWarning("Configuration warning: {Warning}", warning);
|
||||
}
|
||||
|
||||
_current = newConfig;
|
||||
_logger.LogInformation("Router configuration reloaded successfully");
|
||||
|
||||
ConfigurationChanged?.Invoke(this, new ConfigChangedEventArgs(previous, newConfig));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to reload configuration, keeping previous");
|
||||
|
||||
if (_options.ThrowOnValidationError)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_reloadLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task ReloadAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
await _reloadLock.WaitAsync(cancellationToken);
|
||||
|
||||
try
|
||||
{
|
||||
var previous = _current;
|
||||
var newConfig = LoadConfiguration();
|
||||
var validation = ValidateConfig(newConfig);
|
||||
|
||||
if (!validation.IsValid)
|
||||
{
|
||||
throw new ConfigurationException(
|
||||
$"Configuration validation failed: {string.Join("; ", validation.Errors)}");
|
||||
}
|
||||
|
||||
_current = newConfig;
|
||||
_logger.LogInformation("Router configuration reloaded successfully");
|
||||
|
||||
ConfigurationChanged?.Invoke(this, new ConfigChangedEventArgs(previous, newConfig));
|
||||
}
|
||||
finally
|
||||
{
|
||||
_reloadLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ConfigValidationResult Validate() => ValidateConfig(_current);
|
||||
|
||||
private RouterConfig LoadConfiguration()
|
||||
{
|
||||
var builder = new ConfigurationBuilder();
|
||||
|
||||
// Load from YAML file if specified
|
||||
if (!string.IsNullOrEmpty(_options.ConfigPath))
|
||||
{
|
||||
var extension = Path.GetExtension(_options.ConfigPath).ToLowerInvariant();
|
||||
var fullPath = Path.GetFullPath(_options.ConfigPath);
|
||||
|
||||
if (File.Exists(fullPath))
|
||||
{
|
||||
switch (extension)
|
||||
{
|
||||
case ".yaml":
|
||||
case ".yml":
|
||||
builder.AddYamlFile(fullPath, optional: true, reloadOnChange: false);
|
||||
break;
|
||||
case ".json":
|
||||
builder.AddJsonFile(fullPath, optional: true, reloadOnChange: false);
|
||||
break;
|
||||
default:
|
||||
_logger.LogWarning("Unknown configuration file extension: {Extension}", extension);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning("Configuration file not found: {Path}", fullPath);
|
||||
}
|
||||
}
|
||||
|
||||
// Add environment variable overrides
|
||||
builder.AddEnvironmentVariables(prefix: _options.EnvironmentVariablePrefix);
|
||||
|
||||
var configuration = builder.Build();
|
||||
|
||||
var config = new RouterConfig();
|
||||
configuration.Bind(config);
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
private static ConfigValidationResult ValidateConfig(RouterConfig config)
|
||||
{
|
||||
var result = new ConfigValidationResult();
|
||||
|
||||
// Validate payload limits
|
||||
if (config.PayloadLimits.MaxRequestBytesPerCall <= 0)
|
||||
{
|
||||
result.Errors.Add("PayloadLimits.MaxRequestBytesPerCall must be positive");
|
||||
}
|
||||
|
||||
if (config.PayloadLimits.MaxRequestBytesPerConnection <= 0)
|
||||
{
|
||||
result.Errors.Add("PayloadLimits.MaxRequestBytesPerConnection must be positive");
|
||||
}
|
||||
|
||||
if (config.PayloadLimits.MaxAggregateInflightBytes <= 0)
|
||||
{
|
||||
result.Errors.Add("PayloadLimits.MaxAggregateInflightBytes must be positive");
|
||||
}
|
||||
|
||||
if (config.PayloadLimits.MaxRequestBytesPerCall > config.PayloadLimits.MaxRequestBytesPerConnection)
|
||||
{
|
||||
result.Warnings.Add("MaxRequestBytesPerCall is larger than MaxRequestBytesPerConnection");
|
||||
}
|
||||
|
||||
// Validate routing options
|
||||
if (config.Routing.DefaultTimeout <= TimeSpan.Zero)
|
||||
{
|
||||
result.Errors.Add("Routing.DefaultTimeout must be positive");
|
||||
}
|
||||
|
||||
// Validate services
|
||||
var serviceNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
foreach (var service in config.Services)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(service.ServiceName))
|
||||
{
|
||||
result.Errors.Add("Service name cannot be empty");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!serviceNames.Add(service.ServiceName))
|
||||
{
|
||||
result.Errors.Add($"Duplicate service name: {service.ServiceName}");
|
||||
}
|
||||
|
||||
foreach (var endpoint in service.Endpoints)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(endpoint.Method))
|
||||
{
|
||||
result.Errors.Add($"Service {service.ServiceName}: endpoint method cannot be empty");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(endpoint.Path))
|
||||
{
|
||||
result.Errors.Add($"Service {service.ServiceName}: endpoint path cannot be empty");
|
||||
}
|
||||
|
||||
if (endpoint.DefaultTimeout.HasValue && endpoint.DefaultTimeout.Value <= TimeSpan.Zero)
|
||||
{
|
||||
result.Warnings.Add(
|
||||
$"Service {service.ServiceName}: endpoint {endpoint.Method} {endpoint.Path} has non-positive timeout");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validate static instances
|
||||
foreach (var instance in config.StaticInstances)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(instance.ServiceName))
|
||||
{
|
||||
result.Errors.Add("Static instance service name cannot be empty");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(instance.Host))
|
||||
{
|
||||
result.Errors.Add($"Static instance {instance.ServiceName}: host cannot be empty");
|
||||
}
|
||||
|
||||
if (instance.Port <= 0 || instance.Port > 65535)
|
||||
{
|
||||
result.Errors.Add($"Static instance {instance.ServiceName}: port must be between 1 and 65535");
|
||||
}
|
||||
|
||||
if (instance.Weight <= 0)
|
||||
{
|
||||
result.Warnings.Add($"Static instance {instance.ServiceName}: weight should be positive");
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed) return;
|
||||
_disposed = true;
|
||||
|
||||
_watcher?.Dispose();
|
||||
_debounceTimer?.Dispose();
|
||||
_reloadLock.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exception thrown when configuration is invalid.
|
||||
/// </summary>
|
||||
public sealed class ConfigurationException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ConfigurationException"/> class.
|
||||
/// </summary>
|
||||
public ConfigurationException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ConfigurationException"/> class.
|
||||
/// </summary>
|
||||
public ConfigurationException(string message, Exception innerException) : base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
58
src/__Libraries/StellaOps.Router.Config/RoutingOptions.cs
Normal file
58
src/__Libraries/StellaOps.Router.Config/RoutingOptions.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
namespace StellaOps.Router.Config;
|
||||
|
||||
/// <summary>
|
||||
/// Routing behavior options.
|
||||
/// </summary>
|
||||
public sealed class RoutingOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the local region for routing preferences.
|
||||
/// </summary>
|
||||
public string LocalRegion { get; set; } = "default";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the neighbor regions for fallback routing.
|
||||
/// </summary>
|
||||
public List<string> NeighborRegions { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the tie-breaker strategy for equal-weight instances.
|
||||
/// </summary>
|
||||
public TieBreakerStrategy TieBreaker { get; set; } = TieBreakerStrategy.RoundRobin;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether to prefer local region instances.
|
||||
/// </summary>
|
||||
public bool PreferLocalRegion { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default request timeout.
|
||||
/// </summary>
|
||||
public TimeSpan DefaultTimeout { get; set; } = TimeSpan.FromSeconds(30);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tie-breaker strategy for routing decisions.
|
||||
/// </summary>
|
||||
public enum TieBreakerStrategy
|
||||
{
|
||||
/// <summary>
|
||||
/// Round-robin between equal-weight instances.
|
||||
/// </summary>
|
||||
RoundRobin,
|
||||
|
||||
/// <summary>
|
||||
/// Random selection between equal-weight instances.
|
||||
/// </summary>
|
||||
Random,
|
||||
|
||||
/// <summary>
|
||||
/// Select the least-loaded instance.
|
||||
/// </summary>
|
||||
LeastLoaded,
|
||||
|
||||
/// <summary>
|
||||
/// Consistent hashing based on request attributes.
|
||||
/// </summary>
|
||||
ConsistentHash
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace StellaOps.Router.Config;
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for registering router configuration services.
|
||||
/// </summary>
|
||||
public static class ServiceCollectionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds router configuration services to the service collection.
|
||||
/// </summary>
|
||||
/// <param name="services">The service collection.</param>
|
||||
/// <param name="configPath">Optional path to the configuration file.</param>
|
||||
/// <returns>The service collection.</returns>
|
||||
public static IServiceCollection AddRouterConfig(
|
||||
this IServiceCollection services,
|
||||
string? configPath = null)
|
||||
{
|
||||
return services.AddRouterConfig(options =>
|
||||
{
|
||||
if (!string.IsNullOrEmpty(configPath))
|
||||
{
|
||||
options.ConfigPath = configPath;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds router configuration services to the service collection.
|
||||
/// </summary>
|
||||
/// <param name="services">The service collection.</param>
|
||||
/// <param name="configure">Configuration action.</param>
|
||||
/// <returns>The service collection.</returns>
|
||||
public static IServiceCollection AddRouterConfig(
|
||||
this IServiceCollection services,
|
||||
Action<RouterConfigOptions> configure)
|
||||
{
|
||||
services.Configure(configure);
|
||||
services.AddSingleton<IRouterConfigProvider, RouterConfigProvider>();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds router configuration services to the service collection, binding from IConfiguration.
|
||||
/// </summary>
|
||||
/// <param name="services">The service collection.</param>
|
||||
/// <param name="configuration">The configuration.</param>
|
||||
/// <param name="sectionName">The configuration section name.</param>
|
||||
/// <returns>The service collection.</returns>
|
||||
public static IServiceCollection AddRouterConfig(
|
||||
this IServiceCollection services,
|
||||
IConfiguration configuration,
|
||||
string sectionName = "Router")
|
||||
{
|
||||
var section = configuration.GetSection(sectionName);
|
||||
|
||||
services.Configure<RouterConfigOptions>(options =>
|
||||
{
|
||||
options.ConfigurationSection = sectionName;
|
||||
});
|
||||
|
||||
services.Configure<RouterConfig>(section);
|
||||
services.AddSingleton<IRouterConfigProvider, RouterConfigProvider>();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds router configuration from a YAML file.
|
||||
/// </summary>
|
||||
/// <param name="services">The service collection.</param>
|
||||
/// <param name="yamlPath">Path to the YAML configuration file.</param>
|
||||
/// <param name="enableHotReload">Whether to enable hot-reload.</param>
|
||||
/// <returns>The service collection.</returns>
|
||||
public static IServiceCollection AddRouterConfigFromYaml(
|
||||
this IServiceCollection services,
|
||||
string yamlPath,
|
||||
bool enableHotReload = true)
|
||||
{
|
||||
return services.AddRouterConfig(options =>
|
||||
{
|
||||
options.ConfigPath = yamlPath;
|
||||
options.EnableHotReload = enableHotReload;
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds router configuration from a JSON file.
|
||||
/// </summary>
|
||||
/// <param name="services">The service collection.</param>
|
||||
/// <param name="jsonPath">Path to the JSON configuration file.</param>
|
||||
/// <param name="enableHotReload">Whether to enable hot-reload.</param>
|
||||
/// <returns>The service collection.</returns>
|
||||
public static IServiceCollection AddRouterConfigFromJson(
|
||||
this IServiceCollection services,
|
||||
string jsonPath,
|
||||
bool enableHotReload = true)
|
||||
{
|
||||
return services.AddRouterConfig(options =>
|
||||
{
|
||||
options.ConfigPath = jsonPath;
|
||||
options.EnableHotReload = enableHotReload;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
using StellaOps.Router.Common.Enums;
|
||||
|
||||
namespace StellaOps.Router.Config;
|
||||
|
||||
/// <summary>
|
||||
/// Configuration for a statically-defined microservice instance.
|
||||
/// </summary>
|
||||
public sealed class StaticInstanceConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the service name.
|
||||
/// </summary>
|
||||
public required string ServiceName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the service version.
|
||||
/// </summary>
|
||||
public required string Version { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the region.
|
||||
/// </summary>
|
||||
public string Region { get; set; } = "default";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the host name or IP address.
|
||||
/// </summary>
|
||||
public required string Host { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the port.
|
||||
/// </summary>
|
||||
public required int Port { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the transport type.
|
||||
/// </summary>
|
||||
public TransportType Transport { get; set; } = TransportType.Tcp;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the instance weight for load balancing.
|
||||
/// </summary>
|
||||
public int Weight { get; set; } = 100;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the instance metadata.
|
||||
/// </summary>
|
||||
public Dictionary<string, string> Metadata { get; set; } = [];
|
||||
}
|
||||
@@ -5,8 +5,23 @@
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<RootNamespace>StellaOps.Router.Config</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\StellaOps.Router.Common\StellaOps.Router.Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="10.0.0-rc.2.25502.107" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="10.0.0-rc.2.25502.107" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="10.0.0-rc.2.25502.107" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="10.0.0-rc.2.25502.107" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.0-rc.2.25502.107" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.0-rc.2.25502.107" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="10.0.0-rc.2.25502.107" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="10.0.0-rc.2.25502.107" />
|
||||
<PackageReference Include="NetEscapades.Configuration.Yaml" Version="2.1.0" />
|
||||
<PackageReference Include="YamlDotNet" Version="13.7.1" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
Reference in New Issue
Block a user