Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
<PackageVersion Include="HdrHistogram" Version="2.5.0" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.17" />
<PackageVersion Include="Microsoft.AspNetCore.SignalR.Client" Version="8.0.17" />
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
<PackageVersion Include="Microsoft.Extensions.DependencyModel" Version="8.0.2" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="8.0.1" />
Expand Down
25 changes: 25 additions & 0 deletions src/ServiceControl.Configuration/AppConfigConfigurationProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#nullable enable

namespace ServiceControl.Configuration;

using System.Collections.Generic;
using Microsoft.Extensions.Configuration;

public class AppConfigConfigurationProvider : ConfigurationProvider
{
public AppConfigConfigurationProvider(Dictionary<string, string[]> mappings)
{
foreach (var (msConfigurationExtensionKey, appConfigKeys) in mappings)
{
foreach (var appConfigKey in appConfigKeys)
{
var appConfigValue = System.Configuration.ConfigurationManager.AppSettings[appConfigKey];

if (appConfigValue is not null)
{
Data[msConfigurationExtensionKey] = appConfigValue;
}
}
}
}
}
32 changes: 32 additions & 0 deletions src/ServiceControl.Configuration/AppConfigConfigurationSource.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#nullable enable

namespace ServiceControl.Configuration;

using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Linq;

public class AppConfigConfigurationSource : IConfigurationSource
{
public IConfigurationProvider Build(IConfigurationBuilder builder)
{
var propertiesWithAttribute = from a in AppDomain.CurrentDomain.GetAssemblies()
from t in a.GetTypes()
from p in t.GetProperties()
let attributes = p.GetCustomAttributes(typeof(AppConfigSettingAttribute), true)
where attributes != null && attributes.Length > 0
select new { Type = p, Attribute = attributes.Cast<AppConfigSettingAttribute>().Single() };

var mappings = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase);

foreach (var property in propertiesWithAttribute)
{
var section = property.Type.DeclaringType!.Name.Replace("Options", "");
var name = property.Type.Name;
mappings[$"{section}:{name}"] = property.Attribute.Keys;
}

return new AppConfigConfigurationProvider(mappings);
}
}
11 changes: 11 additions & 0 deletions src/ServiceControl.Configuration/AppConfigSettingAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#nullable enable

namespace ServiceControl.Configuration;

using System;

[AttributeUsage(AttributeTargets.All)]
public class AppConfigSettingAttribute(params string[] keys) : Attribute
{
public string[] Keys { get; } = keys;
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration" />
<PackageReference Include="System.Configuration.ConfigurationManager" />
</ItemGroup>

Expand Down
4 changes: 4 additions & 0 deletions src/ServiceControl.UnitTests/API/APIApprovals.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using NServiceBus.CustomChecks;
using NUnit.Framework;
using Particular.Approvals;
using Particular.ServiceControl.Licensing;
using ServiceBus.Management.Infrastructure.Settings;
using ServiceControl.Infrastructure.Api;
using ServiceControl.Infrastructure.Settings;
using ServiceControl.Infrastructure.WebApi;
using ServiceControl.Monitoring.HeartbeatMonitoring;

Expand All @@ -31,9 +33,11 @@ public async Task RootPathValue()
var httpContext = new DefaultHttpContext { Request = { Scheme = "http", Host = new HostString("localhost") } };
var actionContext = new ActionContext { HttpContext = httpContext, RouteData = new RouteData(), ActionDescriptor = new ControllerActionDescriptor() };
var controllerContext = new ControllerContext(actionContext);

var configurationApi = new ConfigurationApi(
new ActiveLicense(null, NullLogger<ActiveLicense>.Instance) { IsValid = true },
new Settings(),
Options.Create(new ServiceControlOptions()),
null,
new MassTransitConnectorHeartbeatStatus());

Expand Down
5 changes: 4 additions & 1 deletion src/ServiceControl/HostApplicationBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ namespace Particular.ServiceControl
using global::ServiceControl.Infrastructure.BackgroundTasks;
using global::ServiceControl.Infrastructure.DomainEvents;
using global::ServiceControl.Infrastructure.Metrics;
using global::ServiceControl.Infrastructure.Settings;
using global::ServiceControl.Infrastructure.SignalR;
using global::ServiceControl.Infrastructure.WebApi;
using global::ServiceControl.Notifications.Email;
using global::ServiceControl.Persistence;
using global::ServiceControl.Transports;
using Licensing;
using Microsoft.AspNetCore.HttpLogging;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Hosting.WindowsServices;
Expand Down Expand Up @@ -76,7 +78,8 @@ public static void AddServiceControl(this IHostApplicationBuilder hostBuilder, S
services.AddPersistence(settings);
services.AddMetrics(settings.PrintMetrics);

NServiceBusFactory.Configure(settings, transportCustomization, transportSettings, configuration);
var scOptions = hostBuilder.Configuration.GetSection("ServiceControl").Get<ServiceControlOptions>();
NServiceBusFactory.Configure(scOptions, transportCustomization, transportSettings, configuration);
hostBuilder.UseNServiceBus(configuration);

if (!settings.DisableExternalIntegrationsPublishing)
Expand Down
8 changes: 8 additions & 0 deletions src/ServiceControl/Hosting/Commands/RunCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@
using System.Threading.Tasks;
using Infrastructure.WebApi;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using NServiceBus;
using Particular.ServiceControl;
using Particular.ServiceControl.Hosting;
using ServiceBus.Management.Infrastructure.Settings;
using ServiceControl;
using ServiceControl.Configuration;
using ServiceControl.Infrastructure.Settings;

class RunCommand : AbstractCommand
{
Expand All @@ -20,6 +24,10 @@ public override async Task Execute(HostArguments args, Settings settings)
settings.RunCleanupBundle = true;

var hostBuilder = WebApplication.CreateBuilder();

hostBuilder.Configuration.Add<AppConfigConfigurationSource>(source => { });
hostBuilder.Services.Configure<ServiceControlOptions>(hostBuilder.Configuration.GetSection("ServiceControl"));

hostBuilder.AddServiceControl(settings, endpointConfiguration);
hostBuilder.AddServiceControlApi();

Expand Down
33 changes: 20 additions & 13 deletions src/ServiceControl/Infrastructure/Api/ConfigurationApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,23 @@
using System.Threading;
using System.Threading.Tasks;
using Configuration;
using Microsoft.Extensions.Options;
using Monitoring.HeartbeatMonitoring;
using Particular.ServiceControl.Licensing;
using ServiceBus.Management.Infrastructure.Settings;
using ServiceControl.Api;
using ServiceControl.Api.Contracts;
using ServiceControl.Infrastructure.Settings;

class ConfigurationApi(ActiveLicense license,
class ConfigurationApi(
ActiveLicense license,
Settings settings,
IHttpClientFactory httpClientFactory, MassTransitConnectorHeartbeatStatus connectorHeartbeatStatus) : IConfigurationApi
IOptions<ServiceControlOptions> scOptions,
IHttpClientFactory httpClientFactory,
MassTransitConnectorHeartbeatStatus connectorHeartbeatStatus) : IConfigurationApi
{
readonly ServiceControlOptions scOptions = scOptions.Value;

public Task<RootUrls> GetUrls(string baseUrl, CancellationToken cancellationToken)
{
var model = new RootUrls
Expand Down Expand Up @@ -56,33 +63,33 @@ public Task<object> GetConfig(CancellationToken cancellationToken)
{
Host = new
{
settings.InstanceName,
scOptions.InstanceName,
Logging = new
{
settings.LoggingSettings.LogPath,
LoggingLevel = settings.LoggingSettings.LogLevel
scOptions.LogPath,
LoggingLevel = scOptions.LogLevel
}
},
DataRetention = new
{
settings.AuditRetentionPeriod,
settings.ErrorRetentionPeriod
scOptions.AuditRetentionPeriod,
scOptions.ErrorRetentionPeriod
},
PerformanceTunning = new
{
settings.ExternalIntegrationsDispatchingBatchSize
scOptions.ExternalIntegrationsDispatchingBatchSize
},
PersistenceSettings = settings.PersisterSpecificSettings,
Transport = new
{
settings.TransportType,
settings.ErrorLogQueue,
settings.ErrorQueue,
settings.ForwardErrorMessages
scOptions.TransportType,
scOptions.ErrorLogQueue,
scOptions.ErrorQueue,
scOptions.ForwardErrorMessages
},
Plugins = new
{
settings.HeartbeatGracePeriod
scOptions.HeartbeatGracePeriod
},
MassTransitConnector = connectorHeartbeatStatus.LastHeartbeat
};
Expand Down
16 changes: 9 additions & 7 deletions src/ServiceControl/Infrastructure/NServiceBusFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ namespace ServiceBus.Management.Infrastructure
using ServiceControl.Configuration;
using ServiceControl.ExternalIntegrations;
using ServiceControl.Infrastructure;
using ServiceControl.Infrastructure.Settings;
using ServiceControl.Infrastructure.Subscriptions;
using ServiceControl.Monitoring.HeartbeatMonitoring;
using ServiceControl.Notifications.Email;
Expand All @@ -18,8 +19,11 @@ namespace ServiceBus.Management.Infrastructure

static class NServiceBusFactory
{
public static void Configure(Settings.Settings settings, ITransportCustomization transportCustomization,
TransportSettings transportSettings, EndpointConfiguration configuration)
public static void Configure(
ServiceControlOptions scOptions,
ITransportCustomization transportCustomization,
TransportSettings transportSettings,
EndpointConfiguration configuration)
{
if (configuration == null)
{
Expand All @@ -28,14 +32,12 @@ public static void Configure(Settings.Settings settings, ITransportCustomization
assemblyScanner.ExcludeAssemblies("ServiceControl.Plugin");
}

configuration.GetSettings().Set("ServiceControl.Settings", settings);

transportCustomization.CustomizePrimaryEndpoint(configuration, transportSettings);

configuration.GetSettings().Set(settings.LoggingSettings);
configuration.SetDiagnosticsPath(settings.LoggingSettings.LogPath);
configuration.GetSettings().Set(scOptions);
configuration.SetDiagnosticsPath(scOptions.LogPath);

if (settings.DisableExternalIntegrationsPublishing)
if (scOptions.DisableExternalIntegrationsPublishing)
{
configuration.DisableFeature<ExternalIntegrationsFeature>();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
namespace ServiceControl.Infrastructure.Settings;

using System;
using System.IO;
using ServiceControl.Configuration;

public class ServiceControlOptions
{
const string DefaultInstanceName = "Particular.ServiceControl";
const int DefaultExternalIntegrationsDispatchingBatchSize = 100;
static readonly string DefaultLogPath = Path.Combine(AppContext.BaseDirectory, ".logs");

[AppConfigSetting(
"ServiceControl/InternalQueueName", // LEGACY SETTING NAME
"ServiceControl/InstanceName")]
public string InstanceName { get; set; } = DefaultInstanceName;

[AppConfigSetting("ServiceControl/LogLevel")]
public string LogLevel { get; set; }

// SC installer always populates LogPath in app.config on installation/change/upgrade so this will only be used when
// debugging or if the entry is removed manually. In those circumstances default to the folder containing the exe
[AppConfigSetting("ServiceControl/LogPath")]
public string LogPath { get; set; } = DefaultLogPath;

[AppConfigSetting("ServiceControl/TransportType")]
public string TransportType { get; set; }

[AppConfigSetting("ServiceBus/ErrorQueue")]
public string ErrorQueue { get; set; }

[AppConfigSetting("ServiceBus/ErrorLogQueue")]
public string ErrorLogQueue { get; set; }

public bool ForwardErrorMessages { get; set; }

[AppConfigSetting("ServiceControl/ErrorRetentionPeriod")]
public TimeSpan ErrorRetentionPeriod { get; set; }

[AppConfigSetting("ServiceControl/AuditRetentionPeriod")]
public TimeSpan? AuditRetentionPeriod { get; set; }

[AppConfigSetting("ServiceControl/HeartbeatGracePeriod")]
public TimeSpan HeartbeatGracePeriod { get; set; } = TimeSpan.FromSeconds(40);

public string StagingQueue => $"{InstanceName}.staging";

[AppConfigSetting("ServiceControl/DisableExternalIntegrationsPublishing")]
public bool DisableExternalIntegrationsPublishing { get; set; } = false;

[AppConfigSetting("ServiceControl/ExternalIntegrationsDispatchingBatchSize")]
public int ExternalIntegrationsDispatchingBatchSize { get; set; } = DefaultExternalIntegrationsDispatchingBatchSize;
}
11 changes: 8 additions & 3 deletions src/ServiceControl/Operations/ErrorIngestion.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@
using Infrastructure.Metrics;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using NServiceBus;
using NServiceBus.Transport;
using Persistence;
using Persistence.UnitOfWork;
using ServiceBus.Management.Infrastructure.Settings;
using ServiceControl.Infrastructure.Settings;
using Transports;

class ErrorIngestion : BackgroundService
Expand All @@ -23,6 +25,7 @@ class ErrorIngestion : BackgroundService

public ErrorIngestion(
Settings settings,
IOptions<ServiceControlOptions> scOptions,
ITransportCustomization transportCustomization,
TransportSettings transportSettings,
Metrics metrics,
Expand All @@ -34,9 +37,10 @@ public ErrorIngestion(
ILogger<ErrorIngestion> logger)
{
this.settings = settings;
this.scOptions = scOptions.Value;
this.transportCustomization = transportCustomization;
this.transportSettings = transportSettings;
errorQueue = settings.ErrorQueue;
errorQueue = this.scOptions.ErrorQueue;
this.ingestor = ingestor;
this.unitOfWorkFactory = unitOfWorkFactory;
this.applicationLifetime = applicationLifetime;
Expand All @@ -58,7 +62,7 @@ public ErrorIngestion(
FullMode = BoundedChannelFullMode.Wait
});

errorHandlingPolicy = new ErrorIngestionFaultPolicy(dataStore, settings.LoggingSettings, OnCriticalError, logger);
errorHandlingPolicy = new ErrorIngestionFaultPolicy(dataStore, this.scOptions, OnCriticalError, logger);

watchdog = new Watchdog(
"failed message ingestion",
Expand Down Expand Up @@ -213,7 +217,7 @@ async Task SetUpAndStartInfrastructure(CancellationToken cancellationToken)

messageReceiver = transportInfrastructure.Receivers[errorQueue];

if (settings.ForwardErrorMessages)
if (scOptions.ForwardErrorMessages)
{
await ingestor.VerifyCanReachForwardingAddress(cancellationToken);
}
Expand Down Expand Up @@ -312,6 +316,7 @@ async Task EnsureStopped(CancellationToken cancellationToken = default)
IMessageReceiver messageReceiver;

readonly Settings settings;
readonly ServiceControlOptions scOptions;
readonly ITransportCustomization transportCustomization;
readonly TransportSettings transportSettings;
readonly Watchdog watchdog;
Expand Down
Loading
Loading