From 17d97d8c1df91823da00de4274ef5fd1c30c5a77 Mon Sep 17 00:00:00 2001 From: Mike Minutillo Date: Fri, 16 Jan 2026 09:04:57 +0800 Subject: [PATCH 01/20] Embed ServicePulse --- src/Directory.Packages.props | 3 ++- .../Hosting/Commands/RunCommand.cs | 5 +++++ src/ServiceControl/ServiceControl.csproj | 17 ++++++++++++----- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index 01d42a1fe2..1a2f9a69ce 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -80,6 +80,7 @@ + @@ -92,4 +93,4 @@ - + \ No newline at end of file diff --git a/src/ServiceControl/Hosting/Commands/RunCommand.cs b/src/ServiceControl/Hosting/Commands/RunCommand.cs index ac5cd439b4..877522ccaa 100644 --- a/src/ServiceControl/Hosting/Commands/RunCommand.cs +++ b/src/ServiceControl/Hosting/Commands/RunCommand.cs @@ -10,6 +10,7 @@ using ServiceControl; using ServiceControl.Hosting.Auth; using ServiceControl.Hosting.Https; + using ServicePulse; class RunCommand : AbstractCommand { @@ -30,6 +31,10 @@ public override async Task Execute(HostArguments args, Settings settings) var app = hostBuilder.Build(); app.UseServiceControl(settings.ForwardedHeadersSettings, settings.HttpsSettings); + app.UseServicePulse(ServicePulseSettings.GetFromEnvironmentVariables() with + { + ServiceControlUrl = settings.ApiUrl + }); app.UseServiceControlAuthentication(settings.OpenIdConnectSettings.Enabled); await app.RunAsync(settings.RootUrl); diff --git a/src/ServiceControl/ServiceControl.csproj b/src/ServiceControl/ServiceControl.csproj index f4d7a787bb..b8b4df3b03 100644 --- a/src/ServiceControl/ServiceControl.csproj +++ b/src/ServiceControl/ServiceControl.csproj @@ -16,12 +16,16 @@ - + - - - + + + @@ -37,6 +41,8 @@ + + @@ -51,7 +57,8 @@ - + From f401be0c7b4a33112e7b74f98b1bfc7745d46dc8 Mon Sep 17 00:00:00 2001 From: Mike Minutillo Date: Fri, 16 Jan 2026 09:08:30 +0800 Subject: [PATCH 02/20] Add setting to enable/disable embedded ServicePulse --- src/ServiceControl/Hosting/Commands/RunCommand.cs | 9 ++++++--- src/ServiceControl/Infrastructure/Settings/Settings.cs | 3 +++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/ServiceControl/Hosting/Commands/RunCommand.cs b/src/ServiceControl/Hosting/Commands/RunCommand.cs index 877522ccaa..a670cbc08a 100644 --- a/src/ServiceControl/Hosting/Commands/RunCommand.cs +++ b/src/ServiceControl/Hosting/Commands/RunCommand.cs @@ -31,10 +31,13 @@ public override async Task Execute(HostArguments args, Settings settings) var app = hostBuilder.Build(); app.UseServiceControl(settings.ForwardedHeadersSettings, settings.HttpsSettings); - app.UseServicePulse(ServicePulseSettings.GetFromEnvironmentVariables() with + if (settings.EnableEmbeddedServicePulse) { - ServiceControlUrl = settings.ApiUrl - }); + app.UseServicePulse(ServicePulseSettings.GetFromEnvironmentVariables() with + { + ServiceControlUrl = settings.ApiUrl + }); + } app.UseServiceControlAuthentication(settings.OpenIdConnectSettings.Enabled); await app.RunAsync(settings.RootUrl); diff --git a/src/ServiceControl/Infrastructure/Settings/Settings.cs b/src/ServiceControl/Infrastructure/Settings/Settings.cs index 939494975e..5c3e1c6aa2 100644 --- a/src/ServiceControl/Infrastructure/Settings/Settings.cs +++ b/src/ServiceControl/Infrastructure/Settings/Settings.cs @@ -65,6 +65,7 @@ public Settings( MaximumConcurrencyLevel = SettingsReader.Read(SettingsRootNamespace, "MaximumConcurrencyLevel"); RetryHistoryDepth = SettingsReader.Read(SettingsRootNamespace, "RetryHistoryDepth", 10); AllowMessageEditing = SettingsReader.Read(SettingsRootNamespace, "AllowMessageEditing"); + EnableEmbeddedServicePulse = SettingsReader.Read(SettingsRootNamespace, "EnableEmbeddedServicePulse", true); NotificationsFilter = SettingsReader.Read(SettingsRootNamespace, "NotificationsFilter"); RemoteInstances = GetRemoteInstances().ToArray(); TimeToRestartErrorIngestionAfterFailure = GetTimeToRestartErrorIngestionAfterFailure(); @@ -103,6 +104,8 @@ public Settings( public bool AllowMessageEditing { get; set; } + public bool EnableEmbeddedServicePulse { get; set; } + //HINT: acceptance tests only public Func MessageFilter { get; set; } From 8d823218c506d73aa75140f3abeac5998abcb8ae Mon Sep 17 00:00:00 2001 From: Mike Minutillo Date: Fri, 16 Jan 2026 09:17:56 +0800 Subject: [PATCH 03/20] Move settings --- src/ServiceControl/Hosting/Commands/RunCommand.cs | 6 +----- src/ServiceControl/Infrastructure/Settings/Settings.cs | 6 ++++++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/ServiceControl/Hosting/Commands/RunCommand.cs b/src/ServiceControl/Hosting/Commands/RunCommand.cs index a670cbc08a..c58e456779 100644 --- a/src/ServiceControl/Hosting/Commands/RunCommand.cs +++ b/src/ServiceControl/Hosting/Commands/RunCommand.cs @@ -10,7 +10,6 @@ using ServiceControl; using ServiceControl.Hosting.Auth; using ServiceControl.Hosting.Https; - using ServicePulse; class RunCommand : AbstractCommand { @@ -33,10 +32,7 @@ public override async Task Execute(HostArguments args, Settings settings) app.UseServiceControl(settings.ForwardedHeadersSettings, settings.HttpsSettings); if (settings.EnableEmbeddedServicePulse) { - app.UseServicePulse(ServicePulseSettings.GetFromEnvironmentVariables() with - { - ServiceControlUrl = settings.ApiUrl - }); + app.UseServicePulse(settings.ServicePulseSettings); } app.UseServiceControlAuthentication(settings.OpenIdConnectSettings.Enabled); diff --git a/src/ServiceControl/Infrastructure/Settings/Settings.cs b/src/ServiceControl/Infrastructure/Settings/Settings.cs index 5c3e1c6aa2..216e81e9ed 100644 --- a/src/ServiceControl/Infrastructure/Settings/Settings.cs +++ b/src/ServiceControl/Infrastructure/Settings/Settings.cs @@ -15,6 +15,7 @@ namespace ServiceBus.Management.Infrastructure.Settings using ServiceControl.Infrastructure.WebApi; using ServiceControl.Persistence; using ServiceControl.Transports; + using ServicePulse; using JsonSerializer = System.Text.Json.JsonSerializer; public class Settings @@ -66,6 +67,10 @@ public Settings( RetryHistoryDepth = SettingsReader.Read(SettingsRootNamespace, "RetryHistoryDepth", 10); AllowMessageEditing = SettingsReader.Read(SettingsRootNamespace, "AllowMessageEditing"); EnableEmbeddedServicePulse = SettingsReader.Read(SettingsRootNamespace, "EnableEmbeddedServicePulse", true); + ServicePulseSettings = ServicePulseSettings.GetFromEnvironmentVariables() with + { + ServiceControlUrl = ApiUrl + }; NotificationsFilter = SettingsReader.Read(SettingsRootNamespace, "NotificationsFilter"); RemoteInstances = GetRemoteInstances().ToArray(); TimeToRestartErrorIngestionAfterFailure = GetTimeToRestartErrorIngestionAfterFailure(); @@ -105,6 +110,7 @@ public Settings( public bool AllowMessageEditing { get; set; } public bool EnableEmbeddedServicePulse { get; set; } + public ServicePulseSettings ServicePulseSettings { get; set; } //HINT: acceptance tests only public Func MessageFilter { get; set; } From a3d3ebb45eda20508a9c2dba20ddc60b1d217b98 Mon Sep 17 00:00:00 2001 From: Mike Minutillo Date: Fri, 16 Jan 2026 09:30:07 +0800 Subject: [PATCH 04/20] Tell ServicePulse it is running in embedded mode --- src/Directory.Packages.props | 2 +- src/ServiceControl/Infrastructure/Settings/Settings.cs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index 1a2f9a69ce..979c27dc0b 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -80,7 +80,7 @@ - + diff --git a/src/ServiceControl/Infrastructure/Settings/Settings.cs b/src/ServiceControl/Infrastructure/Settings/Settings.cs index 216e81e9ed..9c7a04c751 100644 --- a/src/ServiceControl/Infrastructure/Settings/Settings.cs +++ b/src/ServiceControl/Infrastructure/Settings/Settings.cs @@ -69,7 +69,8 @@ public Settings( EnableEmbeddedServicePulse = SettingsReader.Read(SettingsRootNamespace, "EnableEmbeddedServicePulse", true); ServicePulseSettings = ServicePulseSettings.GetFromEnvironmentVariables() with { - ServiceControlUrl = ApiUrl + ServiceControlUrl = ApiUrl, + IsEmbedded = true }; NotificationsFilter = SettingsReader.Read(SettingsRootNamespace, "NotificationsFilter"); RemoteInstances = GetRemoteInstances().ToArray(); From c5a942624b6d0d5e8b22d1eac77538938357f9ad Mon Sep 17 00:00:00 2001 From: Mike Minutillo Date: Tue, 20 Jan 2026 12:45:02 +0800 Subject: [PATCH 05/20] Show SP url in SCMU and default to enabled --- .../UI/InstanceAdd/ServiceControlAddAttachment.cs | 2 ++ .../Infrastructure/Settings/Settings.cs | 2 +- .../ServiceControl/ServiceControlAppConfig.cs | 1 + .../Configuration/ServiceControl/SettingsList.cs | 6 ++++++ .../Instances/ServiceControlBaseService.cs | 11 +++++++---- .../Instances/ServiceControlInstallableBase.cs | 2 ++ .../Instances/ServiceControlInstance.cs | 3 +++ src/ServiceControlInstaller.Engine/Interfaces.cs | 1 + 8 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/ServiceControl.Config/UI/InstanceAdd/ServiceControlAddAttachment.cs b/src/ServiceControl.Config/UI/InstanceAdd/ServiceControlAddAttachment.cs index 5e8eb5d97a..ef701cccd2 100644 --- a/src/ServiceControl.Config/UI/InstanceAdd/ServiceControlAddAttachment.cs +++ b/src/ServiceControl.Config/UI/InstanceAdd/ServiceControlAddAttachment.cs @@ -76,6 +76,8 @@ async Task Add() serviceControlNewInstance.ServiceAccount = viewModel.ServiceControl.ServiceAccount; serviceControlNewInstance.ServiceAccountPwd = viewModel.ServiceControl.Password; serviceControlNewInstance.EnableFullTextSearchOnBodies = viewModel.ServiceControl.EnableFullTextSearchOnBodies.Value; + // TODO: Make this configurable + serviceControlNewInstance.EnableEmbeddedServicePulse = true; } var auditNewInstance = viewModel.InstallAuditInstance ? ServiceControlAuditNewInstance.CreateWithDefaultPersistence() : null; diff --git a/src/ServiceControl/Infrastructure/Settings/Settings.cs b/src/ServiceControl/Infrastructure/Settings/Settings.cs index 9c7a04c751..3fb107f5fa 100644 --- a/src/ServiceControl/Infrastructure/Settings/Settings.cs +++ b/src/ServiceControl/Infrastructure/Settings/Settings.cs @@ -66,7 +66,7 @@ public Settings( MaximumConcurrencyLevel = SettingsReader.Read(SettingsRootNamespace, "MaximumConcurrencyLevel"); RetryHistoryDepth = SettingsReader.Read(SettingsRootNamespace, "RetryHistoryDepth", 10); AllowMessageEditing = SettingsReader.Read(SettingsRootNamespace, "AllowMessageEditing"); - EnableEmbeddedServicePulse = SettingsReader.Read(SettingsRootNamespace, "EnableEmbeddedServicePulse", true); + EnableEmbeddedServicePulse = SettingsReader.Read(SettingsRootNamespace, "EnableEmbeddedServicePulse", false); ServicePulseSettings = ServicePulseSettings.GetFromEnvironmentVariables() with { ServiceControlUrl = ApiUrl, diff --git a/src/ServiceControlInstaller.Engine/Configuration/ServiceControl/ServiceControlAppConfig.cs b/src/ServiceControlInstaller.Engine/Configuration/ServiceControl/ServiceControlAppConfig.cs index c3c79743bf..d3f537d149 100644 --- a/src/ServiceControlInstaller.Engine/Configuration/ServiceControl/ServiceControlAppConfig.cs +++ b/src/ServiceControlInstaller.Engine/Configuration/ServiceControl/ServiceControlAppConfig.cs @@ -38,6 +38,7 @@ protected override void UpdateSettings() settings.Set(ServiceControlSettings.ErrorRetentionPeriod, details.ErrorRetentionPeriod.ToString(), version); settings.Set(ServiceControlSettings.EnableFullTextSearchOnBodies, details.EnableFullTextSearchOnBodies.ToString(), version); settings.Set(ServiceControlSettings.RemoteInstances, RemoteInstanceConverter.ToJson(details.RemoteInstances), version); + settings.Set(ServiceControlSettings.EnableEmbeddedServicePulse, details.EnableEmbeddedServicePulse.ToString(), version); // Windows services allow a maximum of 125 seconds when stopping a service. // When shutting down or restarting the OS we have no control over the diff --git a/src/ServiceControlInstaller.Engine/Configuration/ServiceControl/SettingsList.cs b/src/ServiceControlInstaller.Engine/Configuration/ServiceControl/SettingsList.cs index 9dc2166000..1ff4425a51 100644 --- a/src/ServiceControlInstaller.Engine/Configuration/ServiceControl/SettingsList.cs +++ b/src/ServiceControlInstaller.Engine/Configuration/ServiceControl/SettingsList.cs @@ -113,5 +113,11 @@ public static class ServiceControlSettings Name = "LicensingComponent/RabbitMQ/Password", RemovedFrom = new SemanticVersion(6, 5, 0) }; + + public static readonly SettingInfo EnableEmbeddedServicePulse = new() + { + Name = "ServiceControl/EnableEmbeddedServicePulse", + SupportedFrom = new SemanticVersion(6, 9, 0) + }; } } diff --git a/src/ServiceControlInstaller.Engine/Instances/ServiceControlBaseService.cs b/src/ServiceControlInstaller.Engine/Instances/ServiceControlBaseService.cs index 5e2a3a762f..808c06ec9a 100644 --- a/src/ServiceControlInstaller.Engine/Instances/ServiceControlBaseService.cs +++ b/src/ServiceControlInstaller.Engine/Instances/ServiceControlBaseService.cs @@ -85,6 +85,7 @@ public string AclMaintenanceUrl public TimeSpan ErrorRetentionPeriod { get; set; } public bool SkipQueueCreation { get; set; } public bool EnableFullTextSearchOnBodies { get; set; } + public bool EnableEmbeddedServicePulse { get; set; } protected abstract string BaseServiceName { get; } @@ -92,12 +93,13 @@ public string Url { get { + var suffix = EnableEmbeddedServicePulse ? "" : "api/"; if (string.IsNullOrWhiteSpace(VirtualDirectory)) { - return $"http://{HostName}:{Port}/api/"; + return $"http://{HostName}:{Port}/{suffix}"; } - return $"http://{HostName}:{Port}/{VirtualDirectory}{(VirtualDirectory.EndsWith("/") ? string.Empty : "/")}api/"; + return $"http://{HostName}:{Port}/{VirtualDirectory}{(VirtualDirectory.EndsWith("/") ? string.Empty : "/")}{suffix}"; } } @@ -105,6 +107,7 @@ public string BrowsableUrl { get { + var suffix = EnableEmbeddedServicePulse ? "" : "api/"; string host = HostName switch { "*" => "localhost", @@ -113,10 +116,10 @@ public string BrowsableUrl }; if (string.IsNullOrWhiteSpace(VirtualDirectory)) { - return $"http://{host}:{Port}/api/"; + return $"http://{host}:{Port}/{suffix}"; } - return $"http://{host}:{Port}/{VirtualDirectory}{(VirtualDirectory.EndsWith("/") ? string.Empty : "/")}api/"; + return $"http://{host}:{Port}/{VirtualDirectory}{(VirtualDirectory.EndsWith("/") ? string.Empty : "/")}{suffix}/"; } } diff --git a/src/ServiceControlInstaller.Engine/Instances/ServiceControlInstallableBase.cs b/src/ServiceControlInstaller.Engine/Instances/ServiceControlInstallableBase.cs index 6f237b1803..14643a84f7 100644 --- a/src/ServiceControlInstaller.Engine/Instances/ServiceControlInstallableBase.cs +++ b/src/ServiceControlInstaller.Engine/Instances/ServiceControlInstallableBase.cs @@ -91,6 +91,8 @@ string[] FlagFiles public bool ForwardErrorMessages { get; set; } + public bool EnableEmbeddedServicePulse { get; set; } + public TransportInfo TransportPackage { get; set; } public string ConnectionString { get; set; } diff --git a/src/ServiceControlInstaller.Engine/Instances/ServiceControlInstance.cs b/src/ServiceControlInstaller.Engine/Instances/ServiceControlInstance.cs index f500a82545..047a4356b7 100644 --- a/src/ServiceControlInstaller.Engine/Instances/ServiceControlInstance.cs +++ b/src/ServiceControlInstaller.Engine/Instances/ServiceControlInstance.cs @@ -135,6 +135,8 @@ public override void Reload() AuditLogQueue = AppConfig.Read(ServiceControlSettings.AuditLogQueue, string.IsNullOrEmpty(AuditQueue) ? null : $"{AuditQueue}.log"); } + EnableEmbeddedServicePulse = AppConfig.Read(ServiceControlSettings.EnableEmbeddedServicePulse, false); + if (TimeSpan.TryParse(AppConfig.Read(ServiceControlSettings.ErrorRetentionPeriod, (string)null), out var errorRetentionPeriod)) { ErrorRetentionPeriod = errorRetentionPeriod; @@ -181,6 +183,7 @@ protected override void ApplySettingsChanges(KeyValueConfigurationCollection set settings.Set(ServiceControlSettings.ErrorLogQueue, ErrorLogQueue, Version); settings.Set(ServiceControlSettings.EnableFullTextSearchOnBodies, EnableFullTextSearchOnBodies.ToString(), Version); settings.Set(ServiceControlSettings.PersistenceType, PersistenceManifest.Name); + settings.Set(ServiceControlSettings.EnableEmbeddedServicePulse, EnableEmbeddedServicePulse.ToString(), Version); if (RemoteInstances != null) { diff --git a/src/ServiceControlInstaller.Engine/Interfaces.cs b/src/ServiceControlInstaller.Engine/Interfaces.cs index 914be2cc43..0f8200fefc 100644 --- a/src/ServiceControlInstaller.Engine/Interfaces.cs +++ b/src/ServiceControlInstaller.Engine/Interfaces.cs @@ -109,6 +109,7 @@ public interface IServiceControlInstance : IServiceControlBaseInstance, IURLInfo string ErrorLogQueue { get; } string VirtualDirectory { get; } bool ForwardErrorMessages { get; } + bool EnableEmbeddedServicePulse { get; } TimeSpan ErrorRetentionPeriod { get; } TimeSpan? AuditRetentionPeriod { get; set; } List RemoteInstances { get; } From c7655eb0809152d0dd1b31be430e32f703a465ed Mon Sep 17 00:00:00 2001 From: Mike Minutillo Date: Tue, 20 Jan 2026 13:05:39 +0800 Subject: [PATCH 06/20] Allow disabling embedded ServicePulse at creation --- .../EnableEmbeddedServicePulseOption.cs | 8 ++++++++ .../InstanceAdd/ServiceControlAddAttachment.cs | 3 +-- .../UI/InstanceAdd/ServiceControlAddView.xaml | 6 ++++++ .../InstanceAdd/ServiceControlAddViewModel.cs | 9 +++++++++ .../InstanceAdd/ServiceControlInformation.cs | 18 ++++++++++++++++++ .../Validation/QueueValidationTests.cs | 2 ++ 6 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 src/ServiceControl.Config/UI/InstanceAdd/EnableEmbeddedServicePulseOption.cs diff --git a/src/ServiceControl.Config/UI/InstanceAdd/EnableEmbeddedServicePulseOption.cs b/src/ServiceControl.Config/UI/InstanceAdd/EnableEmbeddedServicePulseOption.cs new file mode 100644 index 0000000000..77bbd17fca --- /dev/null +++ b/src/ServiceControl.Config/UI/InstanceAdd/EnableEmbeddedServicePulseOption.cs @@ -0,0 +1,8 @@ +namespace ServiceControl.Config.UI.InstanceAdd +{ + public class EnableEmbeddedServicePulseOption + { + public string Name { get; set; } + public bool Value { get; set; } + } +} \ No newline at end of file diff --git a/src/ServiceControl.Config/UI/InstanceAdd/ServiceControlAddAttachment.cs b/src/ServiceControl.Config/UI/InstanceAdd/ServiceControlAddAttachment.cs index ef701cccd2..0b104c8d20 100644 --- a/src/ServiceControl.Config/UI/InstanceAdd/ServiceControlAddAttachment.cs +++ b/src/ServiceControl.Config/UI/InstanceAdd/ServiceControlAddAttachment.cs @@ -76,8 +76,7 @@ async Task Add() serviceControlNewInstance.ServiceAccount = viewModel.ServiceControl.ServiceAccount; serviceControlNewInstance.ServiceAccountPwd = viewModel.ServiceControl.Password; serviceControlNewInstance.EnableFullTextSearchOnBodies = viewModel.ServiceControl.EnableFullTextSearchOnBodies.Value; - // TODO: Make this configurable - serviceControlNewInstance.EnableEmbeddedServicePulse = true; + serviceControlNewInstance.EnableEmbeddedServicePulse = viewModel.ServiceControl.EnableEmbeddedServicePulse.Value; } var auditNewInstance = viewModel.InstallAuditInstance ? ServiceControlAuditNewInstance.CreateWithDefaultPersistence() : null; diff --git a/src/ServiceControl.Config/UI/InstanceAdd/ServiceControlAddView.xaml b/src/ServiceControl.Config/UI/InstanceAdd/ServiceControlAddView.xaml index 4b09b4623c..e5258f6272 100644 --- a/src/ServiceControl.Config/UI/InstanceAdd/ServiceControlAddView.xaml +++ b/src/ServiceControl.Config/UI/InstanceAdd/ServiceControlAddView.xaml @@ -254,6 +254,12 @@ Header="FULL TEXT SEARCH ON MESSAGE BODIES" ItemsSource="{Binding ErrorEnableFullTextSearchOnBodiesOptions}" SelectedValue="{Binding ErrorEnableFullTextSearchOnBodies}" /> + diff --git a/src/ServiceControl.Config/UI/InstanceAdd/ServiceControlAddViewModel.cs b/src/ServiceControl.Config/UI/InstanceAdd/ServiceControlAddViewModel.cs index f514290399..6c263dc3aa 100644 --- a/src/ServiceControl.Config/UI/InstanceAdd/ServiceControlAddViewModel.cs +++ b/src/ServiceControl.Config/UI/InstanceAdd/ServiceControlAddViewModel.cs @@ -220,6 +220,15 @@ public EnableFullTextSearchOnBodiesOption ErrorEnableFullTextSearchOnBodies set => ServiceControl.EnableFullTextSearchOnBodies = value; } + public IEnumerable ErrorEnableEmbeddedServicePulseOptions => + ServiceControl.EnableEmbeddedServicePulseOptions; + + public EnableEmbeddedServicePulseOption ErrorEnableEmbeddedServicePulse + { + get => ServiceControl.EnableEmbeddedServicePulse; + set => ServiceControl.EnableEmbeddedServicePulse = value; + } + /* Add Audit Instance */ public string AuditInstanceName diff --git a/src/ServiceControl.Config/UI/InstanceAdd/ServiceControlInformation.cs b/src/ServiceControl.Config/UI/InstanceAdd/ServiceControlInformation.cs index 843dc4bbd6..b674e96c2a 100644 --- a/src/ServiceControl.Config/UI/InstanceAdd/ServiceControlInformation.cs +++ b/src/ServiceControl.Config/UI/InstanceAdd/ServiceControlInformation.cs @@ -38,6 +38,19 @@ public ServiceControlInformation(ServiceControlEditorViewModel viewModelParent) Value = false } }; + EnableEmbeddedServicePulseOptions = new[] + { + new EnableEmbeddedServicePulseOption + { + Name = "On", + Value = true + }, + new EnableEmbeddedServicePulseOption + { + Name = "Off", + Value = false + } + }; ErrorRetention = SettingConstants.ErrorRetentionPeriodDefaultInDaysForUI; Description = "ServiceControl Service"; HostName = "localhost"; @@ -48,6 +61,7 @@ public ServiceControlInformation(ServiceControlEditorViewModel viewModelParent) PortNumber = "33333"; DatabaseMaintenancePortNumber = "33334"; EnableFullTextSearchOnBodies = EnableFullTextSearchOnBodiesOptions.First(p => p.Value); //Default to On. + EnableEmbeddedServicePulse = EnableEmbeddedServicePulseOptions.First(p => p.Value); //Default to On. ViewModelParent = viewModelParent; } @@ -92,6 +106,10 @@ public ForwardingOption ErrorForwarding public EnableFullTextSearchOnBodiesOption EnableFullTextSearchOnBodies { get; set; } + public IEnumerable EnableEmbeddedServicePulseOptions { get; } + + public EnableEmbeddedServicePulseOption EnableEmbeddedServicePulse { get; set; } + protected void UpdateErrorRetention(TimeSpan value) { ErrorRetention = ErrorRetentionUnits == TimeSpanUnits.Days ? value.TotalDays : value.TotalHours; diff --git a/src/ServiceControlInstaller.Engine.UnitTests/Validation/QueueValidationTests.cs b/src/ServiceControlInstaller.Engine.UnitTests/Validation/QueueValidationTests.cs index 578ef22468..5307a02856 100644 --- a/src/ServiceControlInstaller.Engine.UnitTests/Validation/QueueValidationTests.cs +++ b/src/ServiceControlInstaller.Engine.UnitTests/Validation/QueueValidationTests.cs @@ -21,6 +21,8 @@ class FakeServiceControlInstance : IServiceControlInstance public bool ForwardErrorMessages { get; set; } + public bool EnableEmbeddedServicePulse { get; set; } + public TimeSpan ErrorRetentionPeriod { get; set; } public TimeSpan? AuditRetentionPeriod { get; set; } From 1e00df663d3f543b9c979f22d11ac1542173ed90 Mon Sep 17 00:00:00 2001 From: Mike Minutillo Date: Tue, 20 Jan 2026 13:49:54 +0800 Subject: [PATCH 07/20] Enable updating a running instance --- .../UI/InstanceAdd/ServiceControlAddViewModel.cs | 2 +- .../UI/InstanceAdd/ServiceControlInformation.cs | 1 + .../UI/InstanceEdit/ServiceControlEditAttachment.cs | 1 + .../UI/InstanceEdit/ServiceControlEditView.xaml | 6 ++++++ .../UI/InstanceEdit/ServiceControlEditViewModel.cs | 10 ++++++++++ 5 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/ServiceControl.Config/UI/InstanceAdd/ServiceControlAddViewModel.cs b/src/ServiceControl.Config/UI/InstanceAdd/ServiceControlAddViewModel.cs index 6c263dc3aa..24fda9a51a 100644 --- a/src/ServiceControl.Config/UI/InstanceAdd/ServiceControlAddViewModel.cs +++ b/src/ServiceControl.Config/UI/InstanceAdd/ServiceControlAddViewModel.cs @@ -222,7 +222,7 @@ public EnableFullTextSearchOnBodiesOption ErrorEnableFullTextSearchOnBodies public IEnumerable ErrorEnableEmbeddedServicePulseOptions => ServiceControl.EnableEmbeddedServicePulseOptions; - + public EnableEmbeddedServicePulseOption ErrorEnableEmbeddedServicePulse { get => ServiceControl.EnableEmbeddedServicePulse; diff --git a/src/ServiceControl.Config/UI/InstanceAdd/ServiceControlInformation.cs b/src/ServiceControl.Config/UI/InstanceAdd/ServiceControlInformation.cs index b674e96c2a..b21adaa93f 100644 --- a/src/ServiceControl.Config/UI/InstanceAdd/ServiceControlInformation.cs +++ b/src/ServiceControl.Config/UI/InstanceAdd/ServiceControlInformation.cs @@ -140,6 +140,7 @@ public void UpdateFromInstance(ServiceControlInstance instance) ErrorForwardingQueueName = instance.ErrorLogQueue; UpdateErrorRetention(instance.ErrorRetentionPeriod); EnableFullTextSearchOnBodies = EnableFullTextSearchOnBodiesOptions.FirstOrDefault(p => p.Value == instance.EnableFullTextSearchOnBodies); + EnableEmbeddedServicePulse = EnableEmbeddedServicePulseOptions.FirstOrDefault(p => p.Value == instance.EnableEmbeddedServicePulse); } ForwardingOption errorForwarding; diff --git a/src/ServiceControl.Config/UI/InstanceEdit/ServiceControlEditAttachment.cs b/src/ServiceControl.Config/UI/InstanceEdit/ServiceControlEditAttachment.cs index 86ccfe100a..04944dc748 100644 --- a/src/ServiceControl.Config/UI/InstanceEdit/ServiceControlEditAttachment.cs +++ b/src/ServiceControl.Config/UI/InstanceEdit/ServiceControlEditAttachment.cs @@ -70,6 +70,7 @@ async Task Save() instance.DatabaseMaintenancePort = !string.IsNullOrWhiteSpace(viewModel.ServiceControl.DatabaseMaintenancePortNumber) ? Convert.ToInt32(viewModel.ServiceControl.DatabaseMaintenancePortNumber) : null; instance.VirtualDirectory = null; instance.ForwardErrorMessages = viewModel.ServiceControl.ErrorForwarding.Value; + instance.EnableEmbeddedServicePulse = viewModel.ServiceControl.EnableEmbeddedServicePulse.Value; instance.ErrorQueue = viewModel.ServiceControl.ErrorQueueName; instance.ErrorLogQueue = viewModel.ServiceControl.ErrorForwardingQueueName; instance.ErrorRetentionPeriod = viewModel.ServiceControl.ErrorRetentionPeriod; diff --git a/src/ServiceControl.Config/UI/InstanceEdit/ServiceControlEditView.xaml b/src/ServiceControl.Config/UI/InstanceEdit/ServiceControlEditView.xaml index a81b7420e9..35e310c88a 100644 --- a/src/ServiceControl.Config/UI/InstanceEdit/ServiceControlEditView.xaml +++ b/src/ServiceControl.Config/UI/InstanceEdit/ServiceControlEditView.xaml @@ -231,6 +231,12 @@ Header="FULL TEXT SEARCH ON MESSAGE BODIES" ItemsSource="{Binding EnableFullTextSearchOnBodiesOptions}" SelectedValue="{Binding EnableFullTextSearchOnBodies}" /> + diff --git a/src/ServiceControl.Config/UI/InstanceEdit/ServiceControlEditViewModel.cs b/src/ServiceControl.Config/UI/InstanceEdit/ServiceControlEditViewModel.cs index 8ca48e5864..7510173620 100644 --- a/src/ServiceControl.Config/UI/InstanceEdit/ServiceControlEditViewModel.cs +++ b/src/ServiceControl.Config/UI/InstanceEdit/ServiceControlEditViewModel.cs @@ -42,6 +42,7 @@ public void UpdateInstanceFromViewModel(ServiceControlInstance instance) instance.ConnectionString = ConnectionString; instance.DatabaseMaintenancePort = Convert.ToInt32(ServiceControl.DatabaseMaintenancePortNumber); instance.EnableFullTextSearchOnBodies = ServiceControl.EnableFullTextSearchOnBodies.Value; + instance.EnableEmbeddedServicePulse = ServiceControl.EnableEmbeddedServicePulse.Value; } public string InstanceName => ServiceControl.InstanceName; @@ -189,6 +190,15 @@ public EnableFullTextSearchOnBodiesOption EnableFullTextSearchOnBodies set => ServiceControl.EnableFullTextSearchOnBodies = value; } + public IEnumerable EnableEmbeddedServicePulseOptions => + ServiceControl.EnableEmbeddedServicePulseOptions; + + public EnableEmbeddedServicePulseOption EnableEmbeddedServicePulse + { + get => ServiceControl.EnableEmbeddedServicePulse; + set => ServiceControl.EnableEmbeddedServicePulse = value; + } + public bool SubmitAttempted { get; set; } } } \ No newline at end of file From 5316349e1fbd201c2439bd085a1d0c1750603770 Mon Sep 17 00:00:00 2001 From: Mike Minutillo Date: Wed, 21 Jan 2026 17:20:22 +0800 Subject: [PATCH 08/20] Show ServicePulse heading if using embedded --- .../UI/InstanceDetails/InstanceDetailsView.xaml | 2 +- .../InstanceDetails/InstanceDetailsViewModel.cs | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/ServiceControl.Config/UI/InstanceDetails/InstanceDetailsView.xaml b/src/ServiceControl.Config/UI/InstanceDetails/InstanceDetailsView.xaml index 46485b62c3..dcddb94c1a 100644 --- a/src/ServiceControl.Config/UI/InstanceDetails/InstanceDetailsView.xaml +++ b/src/ServiceControl.Config/UI/InstanceDetails/InstanceDetailsView.xaml @@ -137,7 +137,7 @@ diff --git a/src/ServiceControl.Config/UI/InstanceDetails/InstanceDetailsViewModel.cs b/src/ServiceControl.Config/UI/InstanceDetails/InstanceDetailsViewModel.cs index af944eec87..a98ad0f62b 100644 --- a/src/ServiceControl.Config/UI/InstanceDetails/InstanceDetailsViewModel.cs +++ b/src/ServiceControl.Config/UI/InstanceDetails/InstanceDetailsViewModel.cs @@ -114,6 +114,22 @@ public string BrowsableUrl public bool HasBrowsableUrl => ServiceInstance is IURLInfo; + public string UrlHeading + { + get + { + if (IsServiceControlInstance) + { + if (ServiceControlInstance.EnableEmbeddedServicePulse) + { + return "SERVICEPULSE"; + } + } + + return "URL"; + } + } + public string InstallPath => ((IServicePaths)ServiceInstance).InstallPath; public string DBPath => GetDBPathIfAvailable(); @@ -291,6 +307,7 @@ public Task HandleAsync(PostRefreshInstances message, CancellationToken cancella NotifyOfPropertyChange("HasNewVersion"); NotifyOfPropertyChange("Transport"); NotifyOfPropertyChange("BrowsableUrl"); + NotifyOfPropertyChange("UrlHeading"); return Task.CompletedTask; } From bea842bd11da9d176fdd3f989934be4c728642c9 Mon Sep 17 00:00:00 2001 From: Mike Minutillo Date: Wed, 21 Jan 2026 17:20:38 +0800 Subject: [PATCH 09/20] Fix ApiUrl --- src/ServiceControl/Infrastructure/Settings/Settings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ServiceControl/Infrastructure/Settings/Settings.cs b/src/ServiceControl/Infrastructure/Settings/Settings.cs index 3fb107f5fa..723f243845 100644 --- a/src/ServiceControl/Infrastructure/Settings/Settings.cs +++ b/src/ServiceControl/Infrastructure/Settings/Settings.cs @@ -69,7 +69,7 @@ public Settings( EnableEmbeddedServicePulse = SettingsReader.Read(SettingsRootNamespace, "EnableEmbeddedServicePulse", false); ServicePulseSettings = ServicePulseSettings.GetFromEnvironmentVariables() with { - ServiceControlUrl = ApiUrl, + ServiceControlUrl = $"{ApiUrl}/", IsEmbedded = true }; NotificationsFilter = SettingsReader.Read(SettingsRootNamespace, "NotificationsFilter"); From f5e51102ab96a428e85d79b3cc68a3cd7a9068eb Mon Sep 17 00:00:00 2001 From: Mike Minutillo Date: Thu, 22 Jan 2026 11:08:33 +0800 Subject: [PATCH 10/20] Ask about embedded ServicePulse on upgrade --- .../UpgradeServiceControlInstanceCommand.cs | 18 ++++++++++++++++++ .../Instances/ServiceControlUpgradeOptions.cs | 2 ++ 2 files changed, 20 insertions(+) diff --git a/src/ServiceControl.Config/Commands/UpgradeServiceControlInstanceCommand.cs b/src/ServiceControl.Config/Commands/UpgradeServiceControlInstanceCommand.cs index 452e955f6d..fcb07e0817 100644 --- a/src/ServiceControl.Config/Commands/UpgradeServiceControlInstanceCommand.cs +++ b/src/ServiceControl.Config/Commands/UpgradeServiceControlInstanceCommand.cs @@ -119,6 +119,24 @@ public override async Task ExecuteAsync(InstanceDetailsViewModel model) } } + if (!instance.AppConfig.AppSettingExists(ServiceControlSettings.EnableEmbeddedServicePulse.Name)) + { + var result = await windowManager.ShowYesNoCancelDialog("INPUT REQUIRED - EMBEDDED SERVICEPULSE", + "ServiceControl can host an embedded version of ServicePulse which allows you to monitor your ServiceControl instance without needing to install ServicePulse separately.", + "Would you like to enable the embedded ServicePulse for this instance?", + "Enable Embedded ServicePulse", + "Do NOT enable Embedded ServicePulse"); + + if (!result.HasValue) + { + //Dialog was cancelled + await eventAggregator.PublishOnUIThreadAsync(new RefreshInstances()); + return; + } + + upgradeOptions.EnableEmbeddedServicePulse = result.Value; + } + if (await commandChecks.StopBecauseInstanceIsRunning(instance)) { return; diff --git a/src/ServiceControlInstaller.Engine/Instances/ServiceControlUpgradeOptions.cs b/src/ServiceControlInstaller.Engine/Instances/ServiceControlUpgradeOptions.cs index d670ec50c7..8c27a1a533 100644 --- a/src/ServiceControlInstaller.Engine/Instances/ServiceControlUpgradeOptions.cs +++ b/src/ServiceControlInstaller.Engine/Instances/ServiceControlUpgradeOptions.cs @@ -10,6 +10,7 @@ public class ServiceControlUpgradeOptions public int? MaintenancePort { get; set; } public bool SkipQueueCreation { get; set; } public string RemoteUrl { get; set; } + public bool EnableEmbeddedServicePulse { get; set; } public bool Force { get; set; } public void ApplyChangesToInstance(ServiceControlBaseService instance) @@ -53,6 +54,7 @@ void ApplyChangesTo(ServiceControlInstance instance) } instance.SkipQueueCreation = SkipQueueCreation; + instance.EnableEmbeddedServicePulse = EnableEmbeddedServicePulse; } void ApplyChangesTo(ServiceControlAuditInstance instance) From 2aebd06f62b85c4641e73ddb7a6def063262efa2 Mon Sep 17 00:00:00 2001 From: Mike Minutillo Date: Fri, 23 Jan 2026 09:55:54 +0800 Subject: [PATCH 11/20] Enable configuration of embedded SP in PWSH --- .../NewServiceControlInstance.cs | 4 ++++ ...Control.Management.PowerShell.dll-help.xml | 22 ++++++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/ServiceControl.Management.PowerShell/Cmdlets/ServiceControlInstances/NewServiceControlInstance.cs b/src/ServiceControl.Management.PowerShell/Cmdlets/ServiceControlInstances/NewServiceControlInstance.cs index c7e2d6cfee..632fc48774 100644 --- a/src/ServiceControl.Management.PowerShell/Cmdlets/ServiceControlInstances/NewServiceControlInstance.cs +++ b/src/ServiceControl.Management.PowerShell/Cmdlets/ServiceControlInstances/NewServiceControlInstance.cs @@ -94,6 +94,9 @@ public class NewServiceControlInstance : PSCmdlet [Parameter(Mandatory = false, HelpMessage = "Specify whether to enable full text search on error messages.")] public SwitchParameter EnableFullTextSearchOnBodies { get; set; } = true; + [Parameter(Mandatory = false, HelpMessage = "Specify whether to enable embedded ServicePulse instance.")] + public SwitchParameter EnableEmbeddedServicePulse { get; set; } + [Parameter(Mandatory = false, HelpMessage = "Reuse the specified log, db, and install paths even if they are not empty")] public SwitchParameter Force { get; set; } @@ -172,6 +175,7 @@ protected override void ProcessRecord() details.TransportPackage = ServiceControlCoreTransports.Find(Transport); details.SkipQueueCreation = SkipQueueCreation; details.EnableFullTextSearchOnBodies = EnableFullTextSearchOnBodies; + details.EnableEmbeddedServicePulse = EnableEmbeddedServicePulse; var modulePath = Path.GetDirectoryName(MyInvocation.MyCommand.Module.Path); diff --git a/src/ServiceControl.Management.PowerShell/ServiceControl.Management.PowerShell.dll-help.xml b/src/ServiceControl.Management.PowerShell/ServiceControl.Management.PowerShell.dll-help.xml index 586dc08540..e32df137fb 100644 --- a/src/ServiceControl.Management.PowerShell/ServiceControl.Management.PowerShell.dll-help.xml +++ b/src/ServiceControl.Management.PowerShell/ServiceControl.Management.PowerShell.dll-help.xml @@ -2655,6 +2655,13 @@ SwitchParameter + + EnableEmbeddedServicePulse + + Enable the embedded version of ServicePulse that ships with ServiceControl. + + SwitchParameter + Force @@ -2942,6 +2949,18 @@ + + EnableEmbeddedServicePulse + + Enable the embedded version of ServicePulse that ships with ServiceControl + + SwitchParameter + + SwitchParameter + + + + @@ -2998,7 +3017,8 @@ -DisplayName 'ServiceControl Test' ` -AuditRetentionPeriod $AuditRetention ` -ErrorRetentionPeriod $ErrorRetention ` - -ForwardErrorMessages:$false + -ForwardErrorMessages:$false ` + -EnableEmbeddedServicePulse Add a servicecontrol instance From a17817f484fde73b2ea36470359b2a6366e63891 Mon Sep 17 00:00:00 2001 From: Mike Minutillo Date: Wed, 28 Jan 2026 14:43:13 +0800 Subject: [PATCH 12/20] Update SCMU to use https if enabled --- .../Configuration/ServiceControl/SettingsList.cs | 6 ++++++ .../Instances/ServiceControlBaseService.cs | 11 +++++++---- .../Instances/ServiceControlInstance.cs | 1 + 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/ServiceControlInstaller.Engine/Configuration/ServiceControl/SettingsList.cs b/src/ServiceControlInstaller.Engine/Configuration/ServiceControl/SettingsList.cs index 1ff4425a51..ed8a142431 100644 --- a/src/ServiceControlInstaller.Engine/Configuration/ServiceControl/SettingsList.cs +++ b/src/ServiceControlInstaller.Engine/Configuration/ServiceControl/SettingsList.cs @@ -119,5 +119,11 @@ public static class ServiceControlSettings Name = "ServiceControl/EnableEmbeddedServicePulse", SupportedFrom = new SemanticVersion(6, 9, 0) }; + + public static readonly SettingInfo HttpsEnabled = new() + { + Name = "ServiceControl/Https.Enabled", + SupportedFrom = new SemanticVersion(6, 9, 0) + }; } } diff --git a/src/ServiceControlInstaller.Engine/Instances/ServiceControlBaseService.cs b/src/ServiceControlInstaller.Engine/Instances/ServiceControlBaseService.cs index 808c06ec9a..23b8035f9b 100644 --- a/src/ServiceControlInstaller.Engine/Instances/ServiceControlBaseService.cs +++ b/src/ServiceControlInstaller.Engine/Instances/ServiceControlBaseService.cs @@ -86,9 +86,12 @@ public string AclMaintenanceUrl public bool SkipQueueCreation { get; set; } public bool EnableFullTextSearchOnBodies { get; set; } public bool EnableEmbeddedServicePulse { get; set; } + public bool HttpsEnabled { get; set; } protected abstract string BaseServiceName { get; } + public string UrlScheme => HttpsEnabled ? "https" : "http"; + public string Url { get @@ -96,10 +99,10 @@ public string Url var suffix = EnableEmbeddedServicePulse ? "" : "api/"; if (string.IsNullOrWhiteSpace(VirtualDirectory)) { - return $"http://{HostName}:{Port}/{suffix}"; + return $"{UrlScheme}://{HostName}:{Port}/{suffix}"; } - return $"http://{HostName}:{Port}/{VirtualDirectory}{(VirtualDirectory.EndsWith("/") ? string.Empty : "/")}{suffix}"; + return $"{UrlScheme}://{HostName}:{Port}/{VirtualDirectory}{(VirtualDirectory.EndsWith("/") ? string.Empty : "/")}{suffix}"; } } @@ -116,10 +119,10 @@ public string BrowsableUrl }; if (string.IsNullOrWhiteSpace(VirtualDirectory)) { - return $"http://{host}:{Port}/{suffix}"; + return $"{UrlScheme}://{host}:{Port}/{suffix}"; } - return $"http://{host}:{Port}/{VirtualDirectory}{(VirtualDirectory.EndsWith("/") ? string.Empty : "/")}{suffix}/"; + return $"{UrlScheme}://{host}:{Port}/{VirtualDirectory}{(VirtualDirectory.EndsWith("/") ? string.Empty : "/")}{suffix}/"; } } diff --git a/src/ServiceControlInstaller.Engine/Instances/ServiceControlInstance.cs b/src/ServiceControlInstaller.Engine/Instances/ServiceControlInstance.cs index 047a4356b7..01e420c77d 100644 --- a/src/ServiceControlInstaller.Engine/Instances/ServiceControlInstance.cs +++ b/src/ServiceControlInstaller.Engine/Instances/ServiceControlInstance.cs @@ -136,6 +136,7 @@ public override void Reload() } EnableEmbeddedServicePulse = AppConfig.Read(ServiceControlSettings.EnableEmbeddedServicePulse, false); + HttpsEnabled = AppConfig.Read(ServiceControlSettings.HttpsEnabled, false); if (TimeSpan.TryParse(AppConfig.Read(ServiceControlSettings.ErrorRetentionPeriod, (string)null), out var errorRetentionPeriod)) { From 66ee7c6e9c780b4e55314c199f92a4f7317b4ac8 Mon Sep 17 00:00:00 2001 From: Mike Minutillo Date: Thu, 29 Jan 2026 15:39:58 +0800 Subject: [PATCH 13/20] Change URL for monitoring instance when https is enabled --- .../Configuration/Monitoring/SettingsList.cs | 7 +++++++ .../Instances/MonitoringInstance.cs | 9 +++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/ServiceControlInstaller.Engine/Configuration/Monitoring/SettingsList.cs b/src/ServiceControlInstaller.Engine/Configuration/Monitoring/SettingsList.cs index e7651cd20e..42f0cb980c 100644 --- a/src/ServiceControlInstaller.Engine/Configuration/Monitoring/SettingsList.cs +++ b/src/ServiceControlInstaller.Engine/Configuration/Monitoring/SettingsList.cs @@ -26,5 +26,12 @@ public static class SettingsList Name = "Monitoring/ShutdownTimeout", SupportedFrom = new SemanticVersion(6, 4, 1) }; + + public static readonly SettingInfo HttpsEnabled = new() + { + Name = "Monitoring/Https.Enabled", + SupportedFrom = new SemanticVersion(6, 9, 0) + }; + } } \ No newline at end of file diff --git a/src/ServiceControlInstaller.Engine/Instances/MonitoringInstance.cs b/src/ServiceControlInstaller.Engine/Instances/MonitoringInstance.cs index 7770296479..2b3f93c4ca 100644 --- a/src/ServiceControlInstaller.Engine/Instances/MonitoringInstance.cs +++ b/src/ServiceControlInstaller.Engine/Instances/MonitoringInstance.cs @@ -40,7 +40,10 @@ public MonitoringInstance(WindowsServiceController service) public bool SkipQueueCreation { get; set; } - public string Url => $"http://{HostName}:{Port}/"; + public string Url => $"{UrlScheme}://{HostName}:{Port}/"; + + public bool HttpsEnabled { get; set; } + string UrlScheme => HttpsEnabled ? "https" : "http"; public string BrowsableUrl { @@ -52,7 +55,7 @@ public string BrowsableUrl "+" => Environment.MachineName.ToLower(), _ => HostName, }; - return $"http://{host}:{Port}/"; + return $"{UrlScheme}://{host}:{Port}/"; } } @@ -71,6 +74,8 @@ public override void Reload() ConnectionString = ReadConnectionString(); Description = GetDescription(); ServiceAccount = Service.Account; + HttpsEnabled = AppConfig.Read(SettingsList.HttpsEnabled, false); + } string DefaultLogPath() From 10e9a1d61ee60c9c554454eaed7cd18f1d95b6f3 Mon Sep 17 00:00:00 2001 From: Mike Minutillo Date: Fri, 6 Feb 2026 14:23:54 +0800 Subject: [PATCH 14/20] Use new package --- src/Directory.Packages.props | 2 +- .../UpgradeServiceControlInstanceCommand.cs | 14 +++++++------- ...on.cs => EnableIntegratedServicePulseOption.cs} | 2 +- .../UI/InstanceAdd/ServiceControlAddAttachment.cs | 2 +- .../UI/InstanceAdd/ServiceControlAddView.xaml | 6 +++--- .../UI/InstanceAdd/ServiceControlAddViewModel.cs | 10 +++++----- .../UI/InstanceAdd/ServiceControlInformation.cs | 14 +++++++------- .../UI/InstanceDetails/InstanceDetailsViewModel.cs | 2 +- .../InstanceEdit/ServiceControlEditAttachment.cs | 2 +- .../UI/InstanceEdit/ServiceControlEditView.xaml | 6 +++--- .../UI/InstanceEdit/ServiceControlEditViewModel.cs | 12 ++++++------ .../NewServiceControlInstance.cs | 6 +++--- ...rviceControl.Management.PowerShell.dll-help.xml | 10 +++++----- src/ServiceControl/Hosting/Commands/RunCommand.cs | 3 ++- .../Infrastructure/Settings/Settings.cs | 6 +++--- src/ServiceControl/ServiceControl.csproj | 3 +-- .../Validation/QueueValidationTests.cs | 2 +- .../ServiceControl/ServiceControlAppConfig.cs | 2 +- .../Configuration/ServiceControl/SettingsList.cs | 8 ++++---- .../Instances/ServiceControlBaseService.cs | 6 +++--- .../Instances/ServiceControlInstallableBase.cs | 2 +- .../Instances/ServiceControlInstance.cs | 4 ++-- .../Instances/ServiceControlUpgradeOptions.cs | 4 ++-- src/ServiceControlInstaller.Engine/Interfaces.cs | 2 +- 24 files changed, 65 insertions(+), 65 deletions(-) rename src/ServiceControl.Config/UI/InstanceAdd/{EnableEmbeddedServicePulseOption.cs => EnableIntegratedServicePulseOption.cs} (73%) diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index 979c27dc0b..c8e20956ce 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -63,6 +63,7 @@ + @@ -80,7 +81,6 @@ - diff --git a/src/ServiceControl.Config/Commands/UpgradeServiceControlInstanceCommand.cs b/src/ServiceControl.Config/Commands/UpgradeServiceControlInstanceCommand.cs index fcb07e0817..5c7b261bfd 100644 --- a/src/ServiceControl.Config/Commands/UpgradeServiceControlInstanceCommand.cs +++ b/src/ServiceControl.Config/Commands/UpgradeServiceControlInstanceCommand.cs @@ -119,13 +119,13 @@ public override async Task ExecuteAsync(InstanceDetailsViewModel model) } } - if (!instance.AppConfig.AppSettingExists(ServiceControlSettings.EnableEmbeddedServicePulse.Name)) + if (!instance.AppConfig.AppSettingExists(ServiceControlSettings.EnableIntegratedServicePulse.Name)) { - var result = await windowManager.ShowYesNoCancelDialog("INPUT REQUIRED - EMBEDDED SERVICEPULSE", - "ServiceControl can host an embedded version of ServicePulse which allows you to monitor your ServiceControl instance without needing to install ServicePulse separately.", - "Would you like to enable the embedded ServicePulse for this instance?", - "Enable Embedded ServicePulse", - "Do NOT enable Embedded ServicePulse"); + var result = await windowManager.ShowYesNoCancelDialog("INPUT REQUIRED - INTEGRATED SERVICEPULSE", + "ServiceControl can host an integrated version of ServicePulse which allows you to monitor your ServiceControl instance without needing to install ServicePulse separately.", + "Would you like to enable the integrated ServicePulse for this instance?", + "Enable Integrated ServicePulse", + "Do NOT enable Integrated ServicePulse"); if (!result.HasValue) { @@ -134,7 +134,7 @@ public override async Task ExecuteAsync(InstanceDetailsViewModel model) return; } - upgradeOptions.EnableEmbeddedServicePulse = result.Value; + upgradeOptions.EnableIntegratedServicePulse = result.Value; } if (await commandChecks.StopBecauseInstanceIsRunning(instance)) diff --git a/src/ServiceControl.Config/UI/InstanceAdd/EnableEmbeddedServicePulseOption.cs b/src/ServiceControl.Config/UI/InstanceAdd/EnableIntegratedServicePulseOption.cs similarity index 73% rename from src/ServiceControl.Config/UI/InstanceAdd/EnableEmbeddedServicePulseOption.cs rename to src/ServiceControl.Config/UI/InstanceAdd/EnableIntegratedServicePulseOption.cs index 77bbd17fca..9d16dc9942 100644 --- a/src/ServiceControl.Config/UI/InstanceAdd/EnableEmbeddedServicePulseOption.cs +++ b/src/ServiceControl.Config/UI/InstanceAdd/EnableIntegratedServicePulseOption.cs @@ -1,6 +1,6 @@ namespace ServiceControl.Config.UI.InstanceAdd { - public class EnableEmbeddedServicePulseOption + public class EnableIntegratedServicePulseOption { public string Name { get; set; } public bool Value { get; set; } diff --git a/src/ServiceControl.Config/UI/InstanceAdd/ServiceControlAddAttachment.cs b/src/ServiceControl.Config/UI/InstanceAdd/ServiceControlAddAttachment.cs index 0b104c8d20..1d3c08009d 100644 --- a/src/ServiceControl.Config/UI/InstanceAdd/ServiceControlAddAttachment.cs +++ b/src/ServiceControl.Config/UI/InstanceAdd/ServiceControlAddAttachment.cs @@ -76,7 +76,7 @@ async Task Add() serviceControlNewInstance.ServiceAccount = viewModel.ServiceControl.ServiceAccount; serviceControlNewInstance.ServiceAccountPwd = viewModel.ServiceControl.Password; serviceControlNewInstance.EnableFullTextSearchOnBodies = viewModel.ServiceControl.EnableFullTextSearchOnBodies.Value; - serviceControlNewInstance.EnableEmbeddedServicePulse = viewModel.ServiceControl.EnableEmbeddedServicePulse.Value; + serviceControlNewInstance.EnableIntegratedServicePulse = viewModel.ServiceControl.EnableIntegratedServicePulse.Value; } var auditNewInstance = viewModel.InstallAuditInstance ? ServiceControlAuditNewInstance.CreateWithDefaultPersistence() : null; diff --git a/src/ServiceControl.Config/UI/InstanceAdd/ServiceControlAddView.xaml b/src/ServiceControl.Config/UI/InstanceAdd/ServiceControlAddView.xaml index e5258f6272..9ace3fbd7a 100644 --- a/src/ServiceControl.Config/UI/InstanceAdd/ServiceControlAddView.xaml +++ b/src/ServiceControl.Config/UI/InstanceAdd/ServiceControlAddView.xaml @@ -257,9 +257,9 @@ + Header="ENABLE INTEGRATED SERVICEPULSE" + ItemsSource="{Binding ErrorEnableIntegratedServicePulseOptions}" + SelectedValue="{Binding ErrorEnableIntegratedServicePulse}" /> diff --git a/src/ServiceControl.Config/UI/InstanceAdd/ServiceControlAddViewModel.cs b/src/ServiceControl.Config/UI/InstanceAdd/ServiceControlAddViewModel.cs index 24fda9a51a..cc8bdb3d2a 100644 --- a/src/ServiceControl.Config/UI/InstanceAdd/ServiceControlAddViewModel.cs +++ b/src/ServiceControl.Config/UI/InstanceAdd/ServiceControlAddViewModel.cs @@ -220,13 +220,13 @@ public EnableFullTextSearchOnBodiesOption ErrorEnableFullTextSearchOnBodies set => ServiceControl.EnableFullTextSearchOnBodies = value; } - public IEnumerable ErrorEnableEmbeddedServicePulseOptions => - ServiceControl.EnableEmbeddedServicePulseOptions; + public IEnumerable ErrorEnableIntegratedServicePulseOptions => + ServiceControl.EnableIntegratedServicePulseOptions; - public EnableEmbeddedServicePulseOption ErrorEnableEmbeddedServicePulse + public EnableIntegratedServicePulseOption ErrorEnableIntegratedServicePulse { - get => ServiceControl.EnableEmbeddedServicePulse; - set => ServiceControl.EnableEmbeddedServicePulse = value; + get => ServiceControl.EnableIntegratedServicePulse; + set => ServiceControl.EnableIntegratedServicePulse = value; } /* Add Audit Instance */ diff --git a/src/ServiceControl.Config/UI/InstanceAdd/ServiceControlInformation.cs b/src/ServiceControl.Config/UI/InstanceAdd/ServiceControlInformation.cs index b21adaa93f..13e6d1289c 100644 --- a/src/ServiceControl.Config/UI/InstanceAdd/ServiceControlInformation.cs +++ b/src/ServiceControl.Config/UI/InstanceAdd/ServiceControlInformation.cs @@ -38,14 +38,14 @@ public ServiceControlInformation(ServiceControlEditorViewModel viewModelParent) Value = false } }; - EnableEmbeddedServicePulseOptions = new[] + EnableIntegratedServicePulseOptions = new[] { - new EnableEmbeddedServicePulseOption + new EnableIntegratedServicePulseOption { Name = "On", Value = true }, - new EnableEmbeddedServicePulseOption + new EnableIntegratedServicePulseOption { Name = "Off", Value = false @@ -61,7 +61,7 @@ public ServiceControlInformation(ServiceControlEditorViewModel viewModelParent) PortNumber = "33333"; DatabaseMaintenancePortNumber = "33334"; EnableFullTextSearchOnBodies = EnableFullTextSearchOnBodiesOptions.First(p => p.Value); //Default to On. - EnableEmbeddedServicePulse = EnableEmbeddedServicePulseOptions.First(p => p.Value); //Default to On. + EnableIntegratedServicePulse = EnableIntegratedServicePulseOptions.First(p => p.Value); //Default to On. ViewModelParent = viewModelParent; } @@ -106,9 +106,9 @@ public ForwardingOption ErrorForwarding public EnableFullTextSearchOnBodiesOption EnableFullTextSearchOnBodies { get; set; } - public IEnumerable EnableEmbeddedServicePulseOptions { get; } + public IEnumerable EnableIntegratedServicePulseOptions { get; } - public EnableEmbeddedServicePulseOption EnableEmbeddedServicePulse { get; set; } + public EnableIntegratedServicePulseOption EnableIntegratedServicePulse { get; set; } protected void UpdateErrorRetention(TimeSpan value) { @@ -140,7 +140,7 @@ public void UpdateFromInstance(ServiceControlInstance instance) ErrorForwardingQueueName = instance.ErrorLogQueue; UpdateErrorRetention(instance.ErrorRetentionPeriod); EnableFullTextSearchOnBodies = EnableFullTextSearchOnBodiesOptions.FirstOrDefault(p => p.Value == instance.EnableFullTextSearchOnBodies); - EnableEmbeddedServicePulse = EnableEmbeddedServicePulseOptions.FirstOrDefault(p => p.Value == instance.EnableEmbeddedServicePulse); + EnableIntegratedServicePulse = EnableIntegratedServicePulseOptions.FirstOrDefault(p => p.Value == instance.EnableIntegratedServicePulse); } ForwardingOption errorForwarding; diff --git a/src/ServiceControl.Config/UI/InstanceDetails/InstanceDetailsViewModel.cs b/src/ServiceControl.Config/UI/InstanceDetails/InstanceDetailsViewModel.cs index a98ad0f62b..57ad612610 100644 --- a/src/ServiceControl.Config/UI/InstanceDetails/InstanceDetailsViewModel.cs +++ b/src/ServiceControl.Config/UI/InstanceDetails/InstanceDetailsViewModel.cs @@ -120,7 +120,7 @@ public string UrlHeading { if (IsServiceControlInstance) { - if (ServiceControlInstance.EnableEmbeddedServicePulse) + if (ServiceControlInstance.EnableIntegratedServicePulse) { return "SERVICEPULSE"; } diff --git a/src/ServiceControl.Config/UI/InstanceEdit/ServiceControlEditAttachment.cs b/src/ServiceControl.Config/UI/InstanceEdit/ServiceControlEditAttachment.cs index 04944dc748..f978f2f82c 100644 --- a/src/ServiceControl.Config/UI/InstanceEdit/ServiceControlEditAttachment.cs +++ b/src/ServiceControl.Config/UI/InstanceEdit/ServiceControlEditAttachment.cs @@ -70,7 +70,7 @@ async Task Save() instance.DatabaseMaintenancePort = !string.IsNullOrWhiteSpace(viewModel.ServiceControl.DatabaseMaintenancePortNumber) ? Convert.ToInt32(viewModel.ServiceControl.DatabaseMaintenancePortNumber) : null; instance.VirtualDirectory = null; instance.ForwardErrorMessages = viewModel.ServiceControl.ErrorForwarding.Value; - instance.EnableEmbeddedServicePulse = viewModel.ServiceControl.EnableEmbeddedServicePulse.Value; + instance.EnableIntegratedServicePulse = viewModel.ServiceControl.EnableIntegratedServicePulse.Value; instance.ErrorQueue = viewModel.ServiceControl.ErrorQueueName; instance.ErrorLogQueue = viewModel.ServiceControl.ErrorForwardingQueueName; instance.ErrorRetentionPeriod = viewModel.ServiceControl.ErrorRetentionPeriod; diff --git a/src/ServiceControl.Config/UI/InstanceEdit/ServiceControlEditView.xaml b/src/ServiceControl.Config/UI/InstanceEdit/ServiceControlEditView.xaml index 35e310c88a..7565b9c214 100644 --- a/src/ServiceControl.Config/UI/InstanceEdit/ServiceControlEditView.xaml +++ b/src/ServiceControl.Config/UI/InstanceEdit/ServiceControlEditView.xaml @@ -234,9 +234,9 @@ + Header="ENABLE INTEGRATED SERVICEPULSE" + ItemsSource="{Binding EnableIntegratedServicePulseOptions}" + SelectedValue="{Binding EnableIntegratedServicePulse}" /> diff --git a/src/ServiceControl.Config/UI/InstanceEdit/ServiceControlEditViewModel.cs b/src/ServiceControl.Config/UI/InstanceEdit/ServiceControlEditViewModel.cs index 7510173620..dcbeb0fd46 100644 --- a/src/ServiceControl.Config/UI/InstanceEdit/ServiceControlEditViewModel.cs +++ b/src/ServiceControl.Config/UI/InstanceEdit/ServiceControlEditViewModel.cs @@ -42,7 +42,7 @@ public void UpdateInstanceFromViewModel(ServiceControlInstance instance) instance.ConnectionString = ConnectionString; instance.DatabaseMaintenancePort = Convert.ToInt32(ServiceControl.DatabaseMaintenancePortNumber); instance.EnableFullTextSearchOnBodies = ServiceControl.EnableFullTextSearchOnBodies.Value; - instance.EnableEmbeddedServicePulse = ServiceControl.EnableEmbeddedServicePulse.Value; + instance.EnableIntegratedServicePulse = ServiceControl.EnableIntegratedServicePulse.Value; } public string InstanceName => ServiceControl.InstanceName; @@ -190,13 +190,13 @@ public EnableFullTextSearchOnBodiesOption EnableFullTextSearchOnBodies set => ServiceControl.EnableFullTextSearchOnBodies = value; } - public IEnumerable EnableEmbeddedServicePulseOptions => - ServiceControl.EnableEmbeddedServicePulseOptions; + public IEnumerable EnableIntegratedServicePulseOptions => + ServiceControl.EnableIntegratedServicePulseOptions; - public EnableEmbeddedServicePulseOption EnableEmbeddedServicePulse + public EnableIntegratedServicePulseOption EnableIntegratedServicePulse { - get => ServiceControl.EnableEmbeddedServicePulse; - set => ServiceControl.EnableEmbeddedServicePulse = value; + get => ServiceControl.EnableIntegratedServicePulse; + set => ServiceControl.EnableIntegratedServicePulse = value; } public bool SubmitAttempted { get; set; } diff --git a/src/ServiceControl.Management.PowerShell/Cmdlets/ServiceControlInstances/NewServiceControlInstance.cs b/src/ServiceControl.Management.PowerShell/Cmdlets/ServiceControlInstances/NewServiceControlInstance.cs index 632fc48774..66bd8e8730 100644 --- a/src/ServiceControl.Management.PowerShell/Cmdlets/ServiceControlInstances/NewServiceControlInstance.cs +++ b/src/ServiceControl.Management.PowerShell/Cmdlets/ServiceControlInstances/NewServiceControlInstance.cs @@ -94,8 +94,8 @@ public class NewServiceControlInstance : PSCmdlet [Parameter(Mandatory = false, HelpMessage = "Specify whether to enable full text search on error messages.")] public SwitchParameter EnableFullTextSearchOnBodies { get; set; } = true; - [Parameter(Mandatory = false, HelpMessage = "Specify whether to enable embedded ServicePulse instance.")] - public SwitchParameter EnableEmbeddedServicePulse { get; set; } + [Parameter(Mandatory = false, HelpMessage = "Specify whether to enable integrated ServicePulse instance.")] + public SwitchParameter EnableIntegratedServicePulse { get; set; } [Parameter(Mandatory = false, HelpMessage = "Reuse the specified log, db, and install paths even if they are not empty")] public SwitchParameter Force { get; set; } @@ -175,7 +175,7 @@ protected override void ProcessRecord() details.TransportPackage = ServiceControlCoreTransports.Find(Transport); details.SkipQueueCreation = SkipQueueCreation; details.EnableFullTextSearchOnBodies = EnableFullTextSearchOnBodies; - details.EnableEmbeddedServicePulse = EnableEmbeddedServicePulse; + details.EnableIntegratedServicePulse = EnableIntegratedServicePulse; var modulePath = Path.GetDirectoryName(MyInvocation.MyCommand.Module.Path); diff --git a/src/ServiceControl.Management.PowerShell/ServiceControl.Management.PowerShell.dll-help.xml b/src/ServiceControl.Management.PowerShell/ServiceControl.Management.PowerShell.dll-help.xml index e32df137fb..42ad697714 100644 --- a/src/ServiceControl.Management.PowerShell/ServiceControl.Management.PowerShell.dll-help.xml +++ b/src/ServiceControl.Management.PowerShell/ServiceControl.Management.PowerShell.dll-help.xml @@ -2656,9 +2656,9 @@ SwitchParameter - EnableEmbeddedServicePulse + EnableIntegratedServicePulse - Enable the embedded version of ServicePulse that ships with ServiceControl. + Enable the integrated version of ServicePulse that ships with ServiceControl. SwitchParameter @@ -2950,9 +2950,9 @@ - EnableEmbeddedServicePulse + EnableIntegratedServicePulse - Enable the embedded version of ServicePulse that ships with ServiceControl + Enable the integrated version of ServicePulse that ships with ServiceControl SwitchParameter @@ -3018,7 +3018,7 @@ -AuditRetentionPeriod $AuditRetention ` -ErrorRetentionPeriod $ErrorRetention ` -ForwardErrorMessages:$false ` - -EnableEmbeddedServicePulse + -EnableIntegratedServicePulse Add a servicecontrol instance diff --git a/src/ServiceControl/Hosting/Commands/RunCommand.cs b/src/ServiceControl/Hosting/Commands/RunCommand.cs index c58e456779..ebc08958cf 100644 --- a/src/ServiceControl/Hosting/Commands/RunCommand.cs +++ b/src/ServiceControl/Hosting/Commands/RunCommand.cs @@ -10,6 +10,7 @@ using ServiceControl; using ServiceControl.Hosting.Auth; using ServiceControl.Hosting.Https; + using ServicePulse; class RunCommand : AbstractCommand { @@ -30,7 +31,7 @@ public override async Task Execute(HostArguments args, Settings settings) var app = hostBuilder.Build(); app.UseServiceControl(settings.ForwardedHeadersSettings, settings.HttpsSettings); - if (settings.EnableEmbeddedServicePulse) + if (settings.EnableIntegratedServicePulse) { app.UseServicePulse(settings.ServicePulseSettings); } diff --git a/src/ServiceControl/Infrastructure/Settings/Settings.cs b/src/ServiceControl/Infrastructure/Settings/Settings.cs index 723f243845..1fcdee1245 100644 --- a/src/ServiceControl/Infrastructure/Settings/Settings.cs +++ b/src/ServiceControl/Infrastructure/Settings/Settings.cs @@ -66,11 +66,11 @@ public Settings( MaximumConcurrencyLevel = SettingsReader.Read(SettingsRootNamespace, "MaximumConcurrencyLevel"); RetryHistoryDepth = SettingsReader.Read(SettingsRootNamespace, "RetryHistoryDepth", 10); AllowMessageEditing = SettingsReader.Read(SettingsRootNamespace, "AllowMessageEditing"); - EnableEmbeddedServicePulse = SettingsReader.Read(SettingsRootNamespace, "EnableEmbeddedServicePulse", false); + EnableIntegratedServicePulse = SettingsReader.Read(SettingsRootNamespace, "EnableIntegratedServicePulse", false); ServicePulseSettings = ServicePulseSettings.GetFromEnvironmentVariables() with { ServiceControlUrl = $"{ApiUrl}/", - IsEmbedded = true + IsIntegrated = true }; NotificationsFilter = SettingsReader.Read(SettingsRootNamespace, "NotificationsFilter"); RemoteInstances = GetRemoteInstances().ToArray(); @@ -110,7 +110,7 @@ public Settings( public bool AllowMessageEditing { get; set; } - public bool EnableEmbeddedServicePulse { get; set; } + public bool EnableIntegratedServicePulse { get; set; } public ServicePulseSettings ServicePulseSettings { get; set; } //HINT: acceptance tests only diff --git a/src/ServiceControl/ServiceControl.csproj b/src/ServiceControl/ServiceControl.csproj index b8b4df3b03..dd8ef9aa2e 100644 --- a/src/ServiceControl/ServiceControl.csproj +++ b/src/ServiceControl/ServiceControl.csproj @@ -40,9 +40,8 @@ + - - diff --git a/src/ServiceControlInstaller.Engine.UnitTests/Validation/QueueValidationTests.cs b/src/ServiceControlInstaller.Engine.UnitTests/Validation/QueueValidationTests.cs index 5307a02856..aaf6fdf138 100644 --- a/src/ServiceControlInstaller.Engine.UnitTests/Validation/QueueValidationTests.cs +++ b/src/ServiceControlInstaller.Engine.UnitTests/Validation/QueueValidationTests.cs @@ -21,7 +21,7 @@ class FakeServiceControlInstance : IServiceControlInstance public bool ForwardErrorMessages { get; set; } - public bool EnableEmbeddedServicePulse { get; set; } + public bool EnableIntegratedServicePulse { get; set; } public TimeSpan ErrorRetentionPeriod { get; set; } diff --git a/src/ServiceControlInstaller.Engine/Configuration/ServiceControl/ServiceControlAppConfig.cs b/src/ServiceControlInstaller.Engine/Configuration/ServiceControl/ServiceControlAppConfig.cs index d3f537d149..3f2ef78627 100644 --- a/src/ServiceControlInstaller.Engine/Configuration/ServiceControl/ServiceControlAppConfig.cs +++ b/src/ServiceControlInstaller.Engine/Configuration/ServiceControl/ServiceControlAppConfig.cs @@ -38,7 +38,7 @@ protected override void UpdateSettings() settings.Set(ServiceControlSettings.ErrorRetentionPeriod, details.ErrorRetentionPeriod.ToString(), version); settings.Set(ServiceControlSettings.EnableFullTextSearchOnBodies, details.EnableFullTextSearchOnBodies.ToString(), version); settings.Set(ServiceControlSettings.RemoteInstances, RemoteInstanceConverter.ToJson(details.RemoteInstances), version); - settings.Set(ServiceControlSettings.EnableEmbeddedServicePulse, details.EnableEmbeddedServicePulse.ToString(), version); + settings.Set(ServiceControlSettings.EnableIntegratedServicePulse, details.EnableIntegratedServicePulse.ToString(), version); // Windows services allow a maximum of 125 seconds when stopping a service. // When shutting down or restarting the OS we have no control over the diff --git a/src/ServiceControlInstaller.Engine/Configuration/ServiceControl/SettingsList.cs b/src/ServiceControlInstaller.Engine/Configuration/ServiceControl/SettingsList.cs index ed8a142431..841f850f71 100644 --- a/src/ServiceControlInstaller.Engine/Configuration/ServiceControl/SettingsList.cs +++ b/src/ServiceControlInstaller.Engine/Configuration/ServiceControl/SettingsList.cs @@ -114,16 +114,16 @@ public static class ServiceControlSettings RemovedFrom = new SemanticVersion(6, 5, 0) }; - public static readonly SettingInfo EnableEmbeddedServicePulse = new() + public static readonly SettingInfo EnableIntegratedServicePulse = new() { - Name = "ServiceControl/EnableEmbeddedServicePulse", - SupportedFrom = new SemanticVersion(6, 9, 0) + Name = "ServiceControl/EnableIntegratedServicePulse", + SupportedFrom = new SemanticVersion(6, 12, 0) }; public static readonly SettingInfo HttpsEnabled = new() { Name = "ServiceControl/Https.Enabled", - SupportedFrom = new SemanticVersion(6, 9, 0) + SupportedFrom = new SemanticVersion(6, 11, 0) }; } } diff --git a/src/ServiceControlInstaller.Engine/Instances/ServiceControlBaseService.cs b/src/ServiceControlInstaller.Engine/Instances/ServiceControlBaseService.cs index 23b8035f9b..a5e2294538 100644 --- a/src/ServiceControlInstaller.Engine/Instances/ServiceControlBaseService.cs +++ b/src/ServiceControlInstaller.Engine/Instances/ServiceControlBaseService.cs @@ -85,7 +85,7 @@ public string AclMaintenanceUrl public TimeSpan ErrorRetentionPeriod { get; set; } public bool SkipQueueCreation { get; set; } public bool EnableFullTextSearchOnBodies { get; set; } - public bool EnableEmbeddedServicePulse { get; set; } + public bool EnableIntegratedServicePulse { get; set; } public bool HttpsEnabled { get; set; } protected abstract string BaseServiceName { get; } @@ -96,7 +96,7 @@ public string Url { get { - var suffix = EnableEmbeddedServicePulse ? "" : "api/"; + var suffix = EnableIntegratedServicePulse ? "" : "api/"; if (string.IsNullOrWhiteSpace(VirtualDirectory)) { return $"{UrlScheme}://{HostName}:{Port}/{suffix}"; @@ -110,7 +110,7 @@ public string BrowsableUrl { get { - var suffix = EnableEmbeddedServicePulse ? "" : "api/"; + var suffix = EnableIntegratedServicePulse ? "" : "api/"; string host = HostName switch { "*" => "localhost", diff --git a/src/ServiceControlInstaller.Engine/Instances/ServiceControlInstallableBase.cs b/src/ServiceControlInstaller.Engine/Instances/ServiceControlInstallableBase.cs index 14643a84f7..49d7b73ca9 100644 --- a/src/ServiceControlInstaller.Engine/Instances/ServiceControlInstallableBase.cs +++ b/src/ServiceControlInstaller.Engine/Instances/ServiceControlInstallableBase.cs @@ -91,7 +91,7 @@ string[] FlagFiles public bool ForwardErrorMessages { get; set; } - public bool EnableEmbeddedServicePulse { get; set; } + public bool EnableIntegratedServicePulse { get; set; } public TransportInfo TransportPackage { get; set; } diff --git a/src/ServiceControlInstaller.Engine/Instances/ServiceControlInstance.cs b/src/ServiceControlInstaller.Engine/Instances/ServiceControlInstance.cs index 01e420c77d..d010a64a20 100644 --- a/src/ServiceControlInstaller.Engine/Instances/ServiceControlInstance.cs +++ b/src/ServiceControlInstaller.Engine/Instances/ServiceControlInstance.cs @@ -135,7 +135,7 @@ public override void Reload() AuditLogQueue = AppConfig.Read(ServiceControlSettings.AuditLogQueue, string.IsNullOrEmpty(AuditQueue) ? null : $"{AuditQueue}.log"); } - EnableEmbeddedServicePulse = AppConfig.Read(ServiceControlSettings.EnableEmbeddedServicePulse, false); + EnableIntegratedServicePulse = AppConfig.Read(ServiceControlSettings.EnableIntegratedServicePulse, false); HttpsEnabled = AppConfig.Read(ServiceControlSettings.HttpsEnabled, false); if (TimeSpan.TryParse(AppConfig.Read(ServiceControlSettings.ErrorRetentionPeriod, (string)null), out var errorRetentionPeriod)) @@ -184,7 +184,7 @@ protected override void ApplySettingsChanges(KeyValueConfigurationCollection set settings.Set(ServiceControlSettings.ErrorLogQueue, ErrorLogQueue, Version); settings.Set(ServiceControlSettings.EnableFullTextSearchOnBodies, EnableFullTextSearchOnBodies.ToString(), Version); settings.Set(ServiceControlSettings.PersistenceType, PersistenceManifest.Name); - settings.Set(ServiceControlSettings.EnableEmbeddedServicePulse, EnableEmbeddedServicePulse.ToString(), Version); + settings.Set(ServiceControlSettings.EnableIntegratedServicePulse, EnableIntegratedServicePulse.ToString(), Version); if (RemoteInstances != null) { diff --git a/src/ServiceControlInstaller.Engine/Instances/ServiceControlUpgradeOptions.cs b/src/ServiceControlInstaller.Engine/Instances/ServiceControlUpgradeOptions.cs index 8c27a1a533..5e74501c4a 100644 --- a/src/ServiceControlInstaller.Engine/Instances/ServiceControlUpgradeOptions.cs +++ b/src/ServiceControlInstaller.Engine/Instances/ServiceControlUpgradeOptions.cs @@ -10,7 +10,7 @@ public class ServiceControlUpgradeOptions public int? MaintenancePort { get; set; } public bool SkipQueueCreation { get; set; } public string RemoteUrl { get; set; } - public bool EnableEmbeddedServicePulse { get; set; } + public bool EnableIntegratedServicePulse { get; set; } public bool Force { get; set; } public void ApplyChangesToInstance(ServiceControlBaseService instance) @@ -54,7 +54,7 @@ void ApplyChangesTo(ServiceControlInstance instance) } instance.SkipQueueCreation = SkipQueueCreation; - instance.EnableEmbeddedServicePulse = EnableEmbeddedServicePulse; + instance.EnableIntegratedServicePulse = EnableIntegratedServicePulse; } void ApplyChangesTo(ServiceControlAuditInstance instance) diff --git a/src/ServiceControlInstaller.Engine/Interfaces.cs b/src/ServiceControlInstaller.Engine/Interfaces.cs index 0f8200fefc..5d5968b7a5 100644 --- a/src/ServiceControlInstaller.Engine/Interfaces.cs +++ b/src/ServiceControlInstaller.Engine/Interfaces.cs @@ -109,7 +109,7 @@ public interface IServiceControlInstance : IServiceControlBaseInstance, IURLInfo string ErrorLogQueue { get; } string VirtualDirectory { get; } bool ForwardErrorMessages { get; } - bool EnableEmbeddedServicePulse { get; } + bool EnableIntegratedServicePulse { get; } TimeSpan ErrorRetentionPeriod { get; } TimeSpan? AuditRetentionPeriod { get; set; } List RemoteInstances { get; } From a4fb9ea9340c0f573e33e307fef1e5fdb7f18c09 Mon Sep 17 00:00:00 2001 From: Mike Minutillo Date: Fri, 6 Feb 2026 14:26:27 +0800 Subject: [PATCH 15/20] Improvements to upgrade questions based on review --- .../Commands/UpgradeServiceControlInstanceCommand.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ServiceControl.Config/Commands/UpgradeServiceControlInstanceCommand.cs b/src/ServiceControl.Config/Commands/UpgradeServiceControlInstanceCommand.cs index 5c7b261bfd..de12a681b9 100644 --- a/src/ServiceControl.Config/Commands/UpgradeServiceControlInstanceCommand.cs +++ b/src/ServiceControl.Config/Commands/UpgradeServiceControlInstanceCommand.cs @@ -123,9 +123,9 @@ public override async Task ExecuteAsync(InstanceDetailsViewModel model) { var result = await windowManager.ShowYesNoCancelDialog("INPUT REQUIRED - INTEGRATED SERVICEPULSE", "ServiceControl can host an integrated version of ServicePulse which allows you to monitor your ServiceControl instance without needing to install ServicePulse separately.", - "Would you like to enable the integrated ServicePulse for this instance?", - "Enable Integrated ServicePulse", - "Do NOT enable Integrated ServicePulse"); + "Should an integrated ServicePulse be enabled for this ServiceControl instance?", + "Enable integrated ServicePulse", + "Do NOT enable integrated ServicePulse"); if (!result.HasValue) { From c674d35eb529cbb69c7b0ba5617a6a000fbfa345 Mon Sep 17 00:00:00 2001 From: Mike Minutillo Date: Fri, 6 Feb 2026 15:08:11 +0800 Subject: [PATCH 16/20] Add abstraction for environment data providers --- .../IEnvironmentDataProvider.cs | 9 ++++ ...AdditionalEnvironmentDataProvider_Tests.cs | 42 +++++++++++++++++++ ...utCollector_SanitizedNameGrouping_Tests.cs | 8 ++-- .../ThroughputCollector.cs | 10 ++++- 4 files changed, 64 insertions(+), 5 deletions(-) create mode 100644 src/Particular.LicensingComponent.Contracts/IEnvironmentDataProvider.cs create mode 100644 src/Particular.LicensingComponent.UnitTests/ThroughputCollector/ThroughputCollector_AdditionalEnvironmentDataProvider_Tests.cs diff --git a/src/Particular.LicensingComponent.Contracts/IEnvironmentDataProvider.cs b/src/Particular.LicensingComponent.Contracts/IEnvironmentDataProvider.cs new file mode 100644 index 0000000000..e7bb79f9c7 --- /dev/null +++ b/src/Particular.LicensingComponent.Contracts/IEnvironmentDataProvider.cs @@ -0,0 +1,9 @@ +namespace Particular.LicensingComponent.Contracts; + +/// +/// Provides environment data that is included in usage reports +/// +public interface IEnvironmentDataProvider +{ + IEnumerable<(string key, string value)> GetData(); +} diff --git a/src/Particular.LicensingComponent.UnitTests/ThroughputCollector/ThroughputCollector_AdditionalEnvironmentDataProvider_Tests.cs b/src/Particular.LicensingComponent.UnitTests/ThroughputCollector/ThroughputCollector_AdditionalEnvironmentDataProvider_Tests.cs new file mode 100644 index 0000000000..8d42beea1d --- /dev/null +++ b/src/Particular.LicensingComponent.UnitTests/ThroughputCollector/ThroughputCollector_AdditionalEnvironmentDataProvider_Tests.cs @@ -0,0 +1,42 @@ +namespace Particular.LicensingComponent.UnitTests; + +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using NUnit.Framework; +using Particular.LicensingComponent.Contracts; +using Particular.LicensingComponent.UnitTests.Infrastructure; + +[TestFixture] +class ThroughputCollector_AdditionalEnvironmentDataProvider_Tests : ThroughputCollectorTestFixture +{ + public override Task Setup() + { + SetExtraDependencies = services => services.AddSingleton(); + + return base.Setup(); + } + + [Test] + public async Task Should_include_additional_environment_data_in_throughput_report() + { + // Arrange + // Act + var report = await ThroughputCollector.GenerateThroughputReport(null, null, default); + // Assert + Assert.That(report, Is.Not.Null); + Assert.That(report.ReportData, Is.Not.Null); + Assert.That(report.ReportData.EnvironmentInformation, Is.Not.Null); + Assert.That(report.ReportData.EnvironmentInformation.EnvironmentData, Is.Not.Null); + Assert.That(report.ReportData.EnvironmentInformation.EnvironmentData.ContainsKey("TestKey")); + Assert.That(report.ReportData.EnvironmentInformation.EnvironmentData["TestKey"], Is.EqualTo("TestValue")); + } + + class TestAdditionalEnvironmentDataProvider : IEnvironmentDataProvider + { + public IEnumerable<(string key, string value)> GetData() + { + yield return ("TestKey", "TestValue"); + } + } +} diff --git a/src/Particular.LicensingComponent.UnitTests/ThroughputCollector/ThroughputCollector_SanitizedNameGrouping_Tests.cs b/src/Particular.LicensingComponent.UnitTests/ThroughputCollector/ThroughputCollector_SanitizedNameGrouping_Tests.cs index 1b1d258b57..703e842cea 100644 --- a/src/Particular.LicensingComponent.UnitTests/ThroughputCollector/ThroughputCollector_SanitizedNameGrouping_Tests.cs +++ b/src/Particular.LicensingComponent.UnitTests/ThroughputCollector/ThroughputCollector_SanitizedNameGrouping_Tests.cs @@ -35,7 +35,7 @@ await DataStore.CreateBuilder() .WithThroughput(data: [60]) .Build(); - var throughputCollector = new ThroughputCollector(DataStore, configuration.ThroughputSettings, configuration.AuditQuery, configuration.MonitoringService, new BrokerThroughputQuery_WithLowerCaseSanitizedNameCleanse()); + var throughputCollector = new ThroughputCollector(DataStore, configuration.ThroughputSettings, configuration.AuditQuery, configuration.MonitoringService, [], new BrokerThroughputQuery_WithLowerCaseSanitizedNameCleanse()); // Act var summary = await throughputCollector.GetThroughputSummary(default); @@ -61,7 +61,7 @@ await DataStore.CreateBuilder() .WithThroughput(data: [60]) .Build(); - var throughputCollector = new ThroughputCollector(DataStore, configuration.ThroughputSettings, configuration.AuditQuery, configuration.MonitoringService, new BrokerThroughputQuery_WithLowerCaseSanitizedNameCleanse()); + var throughputCollector = new ThroughputCollector(DataStore, configuration.ThroughputSettings, configuration.AuditQuery, configuration.MonitoringService, [], new BrokerThroughputQuery_WithLowerCaseSanitizedNameCleanse()); // Act var report = await throughputCollector.GenerateThroughputReport(null, null, default); @@ -88,7 +88,7 @@ await DataStore.CreateBuilder() .WithThroughput(data: [60]) .Build(); - var throughputCollector = new ThroughputCollector(DataStore, configuration.ThroughputSettings, configuration.AuditQuery, configuration.MonitoringService, new BrokerThroughputQuery_WithNoSanitizedNameCleanse()); + var throughputCollector = new ThroughputCollector(DataStore, configuration.ThroughputSettings, configuration.AuditQuery, configuration.MonitoringService, [], new BrokerThroughputQuery_WithNoSanitizedNameCleanse()); // Act var summary = await throughputCollector.GetThroughputSummary(default); @@ -114,7 +114,7 @@ await DataStore.CreateBuilder() .WithThroughput(data: [60]) .Build(); - var throughputCollector = new ThroughputCollector(DataStore, configuration.ThroughputSettings, configuration.AuditQuery, configuration.MonitoringService, new BrokerThroughputQuery_WithNoSanitizedNameCleanse()); + var throughputCollector = new ThroughputCollector(DataStore, configuration.ThroughputSettings, configuration.AuditQuery, configuration.MonitoringService, [], new BrokerThroughputQuery_WithNoSanitizedNameCleanse()); // Act var report = await throughputCollector.GenerateThroughputReport(null, null, default); diff --git a/src/Particular.LicensingComponent/ThroughputCollector.cs b/src/Particular.LicensingComponent/ThroughputCollector.cs index f7cc882b28..958de9a6c1 100644 --- a/src/Particular.LicensingComponent/ThroughputCollector.cs +++ b/src/Particular.LicensingComponent/ThroughputCollector.cs @@ -12,7 +12,7 @@ using Shared; using QueueThroughput = Report.QueueThroughput; -public class ThroughputCollector(ILicensingDataStore dataStore, ThroughputSettings throughputSettings, IAuditQuery auditQuery, MonitoringService monitoringService, IBrokerThroughputQuery? throughputQuery = null) +public class ThroughputCollector(ILicensingDataStore dataStore, ThroughputSettings throughputSettings, IAuditQuery auditQuery, MonitoringService monitoringService, IEnumerable environmentDataProviders, IBrokerThroughputQuery? throughputQuery = null) : IThroughputCollector { public async Task GetThroughputConnectionSettingsInformation(CancellationToken cancellationToken) @@ -179,6 +179,14 @@ public async Task GenerateThroughputReport(string spVersion, DateT report.EnvironmentInformation.EnvironmentData[EnvironmentDataType.AuditEnabled.ToString()] = systemHasAuditEnabled.ToString(); report.EnvironmentInformation.EnvironmentData[EnvironmentDataType.MonitoringEnabled.ToString()] = systemHasMonitoringEnabled.ToString(); + foreach (var environmentDataProvider in environmentDataProviders) + { + foreach (var (key, value) in environmentDataProvider.GetData()) + { + report.EnvironmentInformation.EnvironmentData[key] = value; + } + } + var throughputReport = new SignedReport { ReportData = report, Signature = Signature.SignReport(report) }; return throughputReport; From c2cf175a19bf1a98e98a32d1b1c6fc89cc63ff6e Mon Sep 17 00:00:00 2001 From: Mike Minutillo Date: Fri, 6 Feb 2026 15:48:04 +0800 Subject: [PATCH 17/20] Include Integrated ServicePulse status in usage report --- ...LicensingComponentServiceCollectionExtensions.cs | 11 +++++++++++ .../HostApplicationBuilderExtensions.cs | 2 ++ ...ceControlErrorInstanceEnvironmentDataProvider.cs | 13 +++++++++++++ 3 files changed, 26 insertions(+) create mode 100644 src/Particular.LicensingComponent/LicensingComponentServiceCollectionExtensions.cs create mode 100644 src/ServiceControl/ServiceControlErrorInstanceEnvironmentDataProvider.cs diff --git a/src/Particular.LicensingComponent/LicensingComponentServiceCollectionExtensions.cs b/src/Particular.LicensingComponent/LicensingComponentServiceCollectionExtensions.cs new file mode 100644 index 0000000000..34d26ad4eb --- /dev/null +++ b/src/Particular.LicensingComponent/LicensingComponentServiceCollectionExtensions.cs @@ -0,0 +1,11 @@ +namespace Particular.LicensingComponent; + +using Contracts; +using Microsoft.Extensions.DependencyInjection; + +public static class LicensingComponentServiceCollectionExtensions +{ + public static IServiceCollection AddEnvironmentDataProvider(this IServiceCollection services) + where T : class, IEnvironmentDataProvider + => services.AddSingleton(); +} diff --git a/src/ServiceControl/HostApplicationBuilderExtensions.cs b/src/ServiceControl/HostApplicationBuilderExtensions.cs index fbe99dada5..23987c6543 100644 --- a/src/ServiceControl/HostApplicationBuilderExtensions.cs +++ b/src/ServiceControl/HostApplicationBuilderExtensions.cs @@ -24,6 +24,7 @@ namespace Particular.ServiceControl using NServiceBus; using NServiceBus.Configuration.AdvancedExtensibility; using NServiceBus.Transport; + using Particular.LicensingComponent; using ServiceBus.Management.Infrastructure; using ServiceBus.Management.Infrastructure.Installers; using ServiceBus.Management.Infrastructure.Settings; @@ -54,6 +55,7 @@ public static void AddServiceControl(this IHostApplicationBuilder hostBuilder, S services.AddSingleton(); services.AddSingleton(settings); + services.AddEnvironmentDataProvider(); services.AddHttpLogging(options => { diff --git a/src/ServiceControl/ServiceControlErrorInstanceEnvironmentDataProvider.cs b/src/ServiceControl/ServiceControlErrorInstanceEnvironmentDataProvider.cs new file mode 100644 index 0000000000..b3828e8b43 --- /dev/null +++ b/src/ServiceControl/ServiceControlErrorInstanceEnvironmentDataProvider.cs @@ -0,0 +1,13 @@ +namespace Particular.ServiceControl; + +using System.Collections.Generic; +using Particular.LicensingComponent.Contracts; +using ServiceBus.Management.Infrastructure.Settings; + +class ServiceControlErrorInstanceEnvironmentDataProvider(Settings settings) : IEnvironmentDataProvider +{ + public IEnumerable<(string key, string value)> GetData() + { + yield return ("Features.IntegratedServicePulse", settings.EnableIntegratedServicePulse ? "Enabled" : "Disabled"); + } +} \ No newline at end of file From 1d497a2f29e4477abbae307e0e7efdd11f49c9cc Mon Sep 17 00:00:00 2001 From: Mike Minutillo Date: Fri, 6 Feb 2026 16:24:22 +0800 Subject: [PATCH 18/20] Fix version number for introduction of setting --- .../Configuration/Monitoring/SettingsList.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ServiceControlInstaller.Engine/Configuration/Monitoring/SettingsList.cs b/src/ServiceControlInstaller.Engine/Configuration/Monitoring/SettingsList.cs index 42f0cb980c..d651da5e90 100644 --- a/src/ServiceControlInstaller.Engine/Configuration/Monitoring/SettingsList.cs +++ b/src/ServiceControlInstaller.Engine/Configuration/Monitoring/SettingsList.cs @@ -30,7 +30,7 @@ public static class SettingsList public static readonly SettingInfo HttpsEnabled = new() { Name = "Monitoring/Https.Enabled", - SupportedFrom = new SemanticVersion(6, 9, 0) + SupportedFrom = new SemanticVersion(6, 11, 0) }; } From 1a339d044066c9a2e1ecf49e7f56dc3c45177e1d Mon Sep 17 00:00:00 2001 From: Mike Minutillo Date: Fri, 6 Feb 2026 16:29:52 +0800 Subject: [PATCH 19/20] Clean up project file --- src/ServiceControl/ServiceControl.csproj | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/ServiceControl/ServiceControl.csproj b/src/ServiceControl/ServiceControl.csproj index dd8ef9aa2e..495d60cec1 100644 --- a/src/ServiceControl/ServiceControl.csproj +++ b/src/ServiceControl/ServiceControl.csproj @@ -16,16 +16,12 @@ - + - - - + + + From 53e0d437c8bb235136d32f4a9452a47951d6c685 Mon Sep 17 00:00:00 2001 From: Mike Minutillo Date: Fri, 6 Feb 2026 16:30:23 +0800 Subject: [PATCH 20/20] Clean up project file 2 --- src/ServiceControl/ServiceControl.csproj | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ServiceControl/ServiceControl.csproj b/src/ServiceControl/ServiceControl.csproj index 495d60cec1..d931751d34 100644 --- a/src/ServiceControl/ServiceControl.csproj +++ b/src/ServiceControl/ServiceControl.csproj @@ -52,8 +52,7 @@ - +