Skip to content

Commit 09d768f

Browse files
Integrated ServicePulse (#5260)
* Embed ServicePulse * Add setting to enable/disable embedded ServicePulse * Move settings * Tell ServicePulse it is running in embedded mode * Show SP url in SCMU and default to enabled * Allow disabling embedded ServicePulse at creation * Enable updating a running instance * Show ServicePulse heading if using embedded * Fix ApiUrl * Ask about embedded ServicePulse on upgrade * Enable configuration of embedded SP in PWSH * Update SCMU to use https if enabled * Change URL for monitoring instance when https is enabled * Use new package * Improvements to upgrade questions based on review * Add abstraction for environment data providers * Include Integrated ServicePulse status in usage report * Fix version number for introduction of setting * Clean up project file * Clean up project file 2 * Test with real ServicePulse package * Fix setting version number * Approve settings These settings can be ignored by platform sample as it does not use integrated ServicePulse * Update docker documentation To reference integrated ServicePulse * Switch to using release package * Log integrated ServicePulse status at startup * Do not use host name This does not work for containers where the hostname is * * Include link to documentation in upgrade question
1 parent 35d500f commit 09d768f

37 files changed

Lines changed: 290 additions & 15 deletions

File tree

nuget.config

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@
1515
<package pattern="*" />
1616
</packageSource>
1717
</packageSourceMapping>
18-
</configuration>
18+
</configuration>

src/Directory.Packages.props

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
<PackageVersion Include="Particular.LicensingComponent.Report" Version="1.0.0" />
6464
<PackageVersion Include="Particular.Licensing.Sources" Version="6.1.0" />
6565
<PackageVersion Include="Particular.Obsoletes" Version="1.0.0" />
66+
<PackageVersion Include="Particular.ServicePulse.Core" Version="2.6.0" />
6667
<PackageVersion Include="Polly.Core" Version="8.6.5" />
6768
<PackageVersion Include="PropertyChanged.Fody" Version="4.1.0" />
6869
<PackageVersion Include="PropertyChanging.Fody" Version="1.31.0" />
@@ -80,6 +81,7 @@
8081
<PackageVersion Include="System.Reactive" Version="6.1.0" />
8182
<PackageVersion Include="System.Reflection.MetadataLoadContext" Version="10.0.3" />
8283
<PackageVersion Include="System.ServiceProcess.ServiceController" Version="10.0.3" />
84+
<PackageVersion Include="ServicePulse.Core" Version="2.5.0-alpha.0.58" />
8385
<PackageVersion Include="Validar.Fody" Version="1.9.0" />
8486
<PackageVersion Include="Yarp.ReverseProxy" Version="2.3.0" />
8587
</ItemGroup>
@@ -92,4 +94,4 @@
9294
<GlobalPackageReference Include="Microsoft.Build.CopyOnWrite" Version="1.0.334" />
9395
<GlobalPackageReference Include="Particular.Packaging" Version="4.5.0" />
9496
</ItemGroup>
95-
</Project>
97+
</Project>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
namespace Particular.LicensingComponent.Contracts;
2+
3+
/// <summary>
4+
/// Provides environment data that is included in usage reports
5+
/// </summary>
6+
public interface IEnvironmentDataProvider
7+
{
8+
IEnumerable<(string key, string value)> GetData();
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
namespace Particular.LicensingComponent.UnitTests;
2+
3+
using System.Collections.Generic;
4+
using System.Threading.Tasks;
5+
using Microsoft.Extensions.DependencyInjection;
6+
using NUnit.Framework;
7+
using Particular.LicensingComponent.Contracts;
8+
using Particular.LicensingComponent.UnitTests.Infrastructure;
9+
10+
[TestFixture]
11+
class ThroughputCollector_AdditionalEnvironmentDataProvider_Tests : ThroughputCollectorTestFixture
12+
{
13+
public override Task Setup()
14+
{
15+
SetExtraDependencies = services => services.AddSingleton<IEnvironmentDataProvider, TestAdditionalEnvironmentDataProvider>();
16+
17+
return base.Setup();
18+
}
19+
20+
[Test]
21+
public async Task Should_include_additional_environment_data_in_throughput_report()
22+
{
23+
// Arrange
24+
// Act
25+
var report = await ThroughputCollector.GenerateThroughputReport(null, null, default);
26+
// Assert
27+
Assert.That(report, Is.Not.Null);
28+
Assert.That(report.ReportData, Is.Not.Null);
29+
Assert.That(report.ReportData.EnvironmentInformation, Is.Not.Null);
30+
Assert.That(report.ReportData.EnvironmentInformation.EnvironmentData, Is.Not.Null);
31+
Assert.That(report.ReportData.EnvironmentInformation.EnvironmentData.ContainsKey("TestKey"));
32+
Assert.That(report.ReportData.EnvironmentInformation.EnvironmentData["TestKey"], Is.EqualTo("TestValue"));
33+
}
34+
35+
class TestAdditionalEnvironmentDataProvider : IEnvironmentDataProvider
36+
{
37+
public IEnumerable<(string key, string value)> GetData()
38+
{
39+
yield return ("TestKey", "TestValue");
40+
}
41+
}
42+
}

src/Particular.LicensingComponent.UnitTests/ThroughputCollector/ThroughputCollector_SanitizedNameGrouping_Tests.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ await DataStore.CreateBuilder()
3535
.WithThroughput(data: [60])
3636
.Build();
3737

38-
var throughputCollector = new ThroughputCollector(DataStore, configuration.ThroughputSettings, configuration.AuditQuery, configuration.MonitoringService, new BrokerThroughputQuery_WithLowerCaseSanitizedNameCleanse());
38+
var throughputCollector = new ThroughputCollector(DataStore, configuration.ThroughputSettings, configuration.AuditQuery, configuration.MonitoringService, [], new BrokerThroughputQuery_WithLowerCaseSanitizedNameCleanse());
3939

4040
// Act
4141
var summary = await throughputCollector.GetThroughputSummary(default);
@@ -61,7 +61,7 @@ await DataStore.CreateBuilder()
6161
.WithThroughput(data: [60])
6262
.Build();
6363

64-
var throughputCollector = new ThroughputCollector(DataStore, configuration.ThroughputSettings, configuration.AuditQuery, configuration.MonitoringService, new BrokerThroughputQuery_WithLowerCaseSanitizedNameCleanse());
64+
var throughputCollector = new ThroughputCollector(DataStore, configuration.ThroughputSettings, configuration.AuditQuery, configuration.MonitoringService, [], new BrokerThroughputQuery_WithLowerCaseSanitizedNameCleanse());
6565

6666
// Act
6767
var report = await throughputCollector.GenerateThroughputReport(null, null, default);
@@ -88,7 +88,7 @@ await DataStore.CreateBuilder()
8888
.WithThroughput(data: [60])
8989
.Build();
9090

91-
var throughputCollector = new ThroughputCollector(DataStore, configuration.ThroughputSettings, configuration.AuditQuery, configuration.MonitoringService, new BrokerThroughputQuery_WithNoSanitizedNameCleanse());
91+
var throughputCollector = new ThroughputCollector(DataStore, configuration.ThroughputSettings, configuration.AuditQuery, configuration.MonitoringService, [], new BrokerThroughputQuery_WithNoSanitizedNameCleanse());
9292

9393
// Act
9494
var summary = await throughputCollector.GetThroughputSummary(default);
@@ -114,7 +114,7 @@ await DataStore.CreateBuilder()
114114
.WithThroughput(data: [60])
115115
.Build();
116116

117-
var throughputCollector = new ThroughputCollector(DataStore, configuration.ThroughputSettings, configuration.AuditQuery, configuration.MonitoringService, new BrokerThroughputQuery_WithNoSanitizedNameCleanse());
117+
var throughputCollector = new ThroughputCollector(DataStore, configuration.ThroughputSettings, configuration.AuditQuery, configuration.MonitoringService, [], new BrokerThroughputQuery_WithNoSanitizedNameCleanse());
118118

119119
// Act
120120
var report = await throughputCollector.GenerateThroughputReport(null, null, default);
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
namespace Particular.LicensingComponent;
2+
3+
using Contracts;
4+
using Microsoft.Extensions.DependencyInjection;
5+
6+
public static class LicensingComponentServiceCollectionExtensions
7+
{
8+
public static IServiceCollection AddEnvironmentDataProvider<T>(this IServiceCollection services)
9+
where T : class, IEnvironmentDataProvider
10+
=> services.AddSingleton<IEnvironmentDataProvider, T>();
11+
}

src/Particular.LicensingComponent/ThroughputCollector.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
using Shared;
1313
using QueueThroughput = Report.QueueThroughput;
1414

15-
public class ThroughputCollector(ILicensingDataStore dataStore, ThroughputSettings throughputSettings, IAuditQuery auditQuery, MonitoringService monitoringService, IBrokerThroughputQuery? throughputQuery = null)
15+
public class ThroughputCollector(ILicensingDataStore dataStore, ThroughputSettings throughputSettings, IAuditQuery auditQuery, MonitoringService monitoringService, IEnumerable<IEnvironmentDataProvider> environmentDataProviders, IBrokerThroughputQuery? throughputQuery = null)
1616
: IThroughputCollector
1717
{
1818
public async Task<ThroughputConnectionSettings> GetThroughputConnectionSettingsInformation(CancellationToken cancellationToken)
@@ -179,6 +179,14 @@ public async Task<SignedReport> GenerateThroughputReport(string spVersion, DateT
179179
report.EnvironmentInformation.EnvironmentData[EnvironmentDataType.AuditEnabled.ToString()] = systemHasAuditEnabled.ToString();
180180
report.EnvironmentInformation.EnvironmentData[EnvironmentDataType.MonitoringEnabled.ToString()] = systemHasMonitoringEnabled.ToString();
181181

182+
foreach (var environmentDataProvider in environmentDataProviders)
183+
{
184+
foreach (var (key, value) in environmentDataProvider.GetData())
185+
{
186+
report.EnvironmentInformation.EnvironmentData[key] = value;
187+
}
188+
}
189+
182190
var throughputReport = new SignedReport { ReportData = report, Signature = Signature.SignReport(report) };
183191
return throughputReport;
184192

src/ServiceControl.Config/Commands/UpgradeServiceControlInstanceCommand.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,28 @@ public override async Task ExecuteAsync(InstanceDetailsViewModel model)
119119
}
120120
}
121121

122+
if (!instance.AppConfig.AppSettingExists(ServiceControlSettings.EnableIntegratedServicePulse.Name))
123+
{
124+
var result = await windowManager.ShowYesNoCancelDialog("INPUT REQUIRED - INTEGRATED SERVICEPULSE",
125+
"""
126+
ServiceControl can host an integrated version of ServicePulse, which allows you to monitor your ServiceControl instance without needing to install ServicePulse separately.
127+
128+
For more information, see https://docs.particular.net/servicecontrol/servicecontrol-instances/integrated-servicepulse
129+
""",
130+
"Should an integrated ServicePulse be enabled for this ServiceControl instance?",
131+
"Enable integrated ServicePulse",
132+
"Do NOT enable integrated ServicePulse");
133+
134+
if (!result.HasValue)
135+
{
136+
//Dialog was cancelled
137+
await eventAggregator.PublishOnUIThreadAsync(new RefreshInstances());
138+
return;
139+
}
140+
141+
upgradeOptions.EnableIntegratedServicePulse = result.Value;
142+
}
143+
122144
if (await commandChecks.StopBecauseInstanceIsRunning(instance))
123145
{
124146
return;
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace ServiceControl.Config.UI.InstanceAdd
2+
{
3+
public class EnableIntegratedServicePulseOption
4+
{
5+
public string Name { get; set; }
6+
public bool Value { get; set; }
7+
}
8+
}

src/ServiceControl.Config/UI/InstanceAdd/ServiceControlAddAttachment.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ async Task Add()
7676
serviceControlNewInstance.ServiceAccount = viewModel.ServiceControl.ServiceAccount;
7777
serviceControlNewInstance.ServiceAccountPwd = viewModel.ServiceControl.Password;
7878
serviceControlNewInstance.EnableFullTextSearchOnBodies = viewModel.ServiceControl.EnableFullTextSearchOnBodies.Value;
79+
serviceControlNewInstance.EnableIntegratedServicePulse = viewModel.ServiceControl.EnableIntegratedServicePulse.Value;
7980
}
8081

8182
var auditNewInstance = viewModel.InstallAuditInstance ? ServiceControlAuditNewInstance.CreateWithDefaultPersistence() : null;

0 commit comments

Comments
 (0)