diff --git a/src/ServiceControl.Config.Tests/.editorconfig b/src/ServiceControl.Config.Tests/.editorconfig index 235eef7320..c5410d8c02 100644 --- a/src/ServiceControl.Config.Tests/.editorconfig +++ b/src/ServiceControl.Config.Tests/.editorconfig @@ -6,9 +6,3 @@ dotnet_diagnostic.CA2007.severity = none # Justification: Executable specifications intentionally assign properties after the # object initializer to mirror user interaction order (e.g. typing a name after load) dotnet_diagnostic.IDE0017.severity = none - -# Cancellation analyzer debt. These fire because Particular.Analyzers is no longer pinned to 0.9.0. -# They are scheduled work, not accepted exceptions: remove a line once this project has no -# violations of that rule left, and never add a rule back to this list. -dotnet_diagnostic.PS0003.severity = none -dotnet_diagnostic.PS0013.severity = none diff --git a/src/ServiceControl.Config.Tests/EventAggregationAutoSubscriptionModuleTests.cs b/src/ServiceControl.Config.Tests/EventAggregationAutoSubscriptionModuleTests.cs index 7db70027fe..b378faae8c 100644 --- a/src/ServiceControl.Config.Tests/EventAggregationAutoSubscriptionModuleTests.cs +++ b/src/ServiceControl.Config.Tests/EventAggregationAutoSubscriptionModuleTests.cs @@ -40,7 +40,7 @@ class FakeEvent; class FakeEventHandler : IHandle { - public Task HandleAsync(FakeEvent message, CancellationToken cancellationToken) => throw new NotImplementedException(); + public Task HandleAsync(FakeEvent message, CancellationToken cancellationToken = default) => throw new NotImplementedException(); } class FakeNonEventHandler; @@ -51,11 +51,13 @@ class FakeEventAggregator : IEventAggregator public bool HandlerExistsFor(Type messageType) => throw new NotImplementedException(); +#pragma warning disable PS0013 // Caliburn.Micro's IEventAggregator declares the marshal delegate as Func, Task> public void Subscribe(object subscriber, Func, Task> marshal) => Subscribers.Add(subscriber); public void Unsubscribe(object subscriber) => throw new NotImplementedException(); public Task PublishAsync(object message, Func, Task> marshal, CancellationToken cancellationToken = new CancellationToken()) => throw new NotImplementedException(); +#pragma warning restore PS0013 } } } \ No newline at end of file diff --git a/src/ServiceControl.Config/.editorconfig b/src/ServiceControl.Config/.editorconfig index d2502b8251..6181205b06 100644 --- a/src/ServiceControl.Config/.editorconfig +++ b/src/ServiceControl.Config/.editorconfig @@ -3,10 +3,14 @@ # Justification: Application synchronization contexts don't require ConfigureAwait(false) dotnet_diagnostic.CA2007.severity = none -# may be enabled in future -dotnet_diagnostic.PS0003.severity = none # A parameter of type CancellationToken on a non-private delegate or method should be optional -dotnet_diagnostic.PS0013.severity = none # A Func used as a method parameter with a Task, ValueTask, or ValueTask return type argument should have at least one CancellationToken parameter type argument unless it has a parameter type argument implementing ICancellableContext -dotnet_diagnostic.PS0018.severity = none # A task-returning method should have a CancellationToken parameter unless it has a parameter implementing ICancellableContext - # Use auto property - should be fixed in an independent PR -dotnet_diagnostic.IDE0032.severity = suggestion \ No newline at end of file +dotnet_diagnostic.IDE0032.severity = suggestion + +# System.Windows.Input.ICommand.Execute(object) returns void and supplies no CancellationToken, +# so the async command chain built on it has no token to accept or forward. A token added here +# could only ever be CancellationToken.None. Command bodies themselves do take tokens: they are +# wired through ReactiveCommand.CreateFromTask(Func), which supplies a +# real one. +[{Framework/Commands/*.cs,Framework/Command.cs,Commands/SelectPathCommand.cs}] +dotnet_diagnostic.PS0013.severity = none +dotnet_diagnostic.PS0018.severity = none diff --git a/src/ServiceControl.Config/Commands/ForceUpgradeAuditInstanceCommand.cs b/src/ServiceControl.Config/Commands/ForceUpgradeAuditInstanceCommand.cs index c500588817..341c3a4815 100644 --- a/src/ServiceControl.Config/Commands/ForceUpgradeAuditInstanceCommand.cs +++ b/src/ServiceControl.Config/Commands/ForceUpgradeAuditInstanceCommand.cs @@ -1,6 +1,7 @@ namespace ServiceControl.Config.Commands; using System.IO; +using System.Threading; using System.Threading.Tasks; using Caliburn.Micro; using Events; @@ -36,34 +37,34 @@ public override async Task ExecuteAsync(ServiceControlAdvancedViewModel model) return; } - await UpgradeServiceControlInstance(model, instance, new ServiceControlUpgradeOptions()); + await UpgradeServiceControlInstance(model, instance, new ServiceControlUpgradeOptions(), CancellationToken.None); await eventAggregator.PublishOnUIThreadAsync(new ResetInstances()); } - async Task UpgradeServiceControlInstance(ServiceControlAdvancedViewModel model, ServiceControlAuditInstance instance, ServiceControlUpgradeOptions upgradeOptions) + async Task UpgradeServiceControlInstance(ServiceControlAdvancedViewModel model, ServiceControlAuditInstance instance, ServiceControlUpgradeOptions upgradeOptions, CancellationToken cancellationToken) { using (var progress = model.GetProgressObject($"UPGRADING {model.Name}")) { var reportCard = new ReportCard(); var restartAgain = model.IsRunning; - var stopped = await model.StopService(progress); + var stopped = await model.StopService(progress, cancellationToken); if (!stopped) { - await eventAggregator.PublishOnUIThreadAsync(new RefreshInstances()); + await eventAggregator.PublishOnUIThreadAsync(new RefreshInstances(), cancellationToken); reportCard.Errors.Add("Failed to stop the service"); reportCard.SetStatus(); - await windowManager.ShowActionReport(reportCard, "ISSUES UPGRADING INSTANCE", "Could not upgrade instance because of the following errors:"); + await windowManager.ShowActionReport(reportCard, "ISSUES UPGRADING INSTANCE", "Could not upgrade instance because of the following errors:", cancellationToken: cancellationToken); return; } if (Directory.Exists(model.ForcedUpgradeBackupLocation)) { - await windowManager.ShowMessage("Cannot make database backup.", $"The target database backup location: {model.ForcedUpgradeBackupLocation} already exists.", hideCancel: true); + await windowManager.ShowMessage("Cannot make database backup.", $"The target database backup location: {model.ForcedUpgradeBackupLocation} already exists.", hideCancel: true, cancellationToken: cancellationToken); return; } @@ -78,18 +79,18 @@ async Task UpgradeServiceControlInstance(ServiceControlAdvancedViewModel model, if (reportCard.HasErrors || reportCard.HasWarnings) { - await windowManager.ShowActionReport(reportCard, "ISSUES UPGRADING INSTANCE", "Could not upgrade instance because of the following errors:", "There were some warnings while upgrading the instance:"); + await windowManager.ShowActionReport(reportCard, "ISSUES UPGRADING INSTANCE", "Could not upgrade instance because of the following errors:", "There were some warnings while upgrading the instance:", cancellationToken: cancellationToken); return; } if (restartAgain) { - var serviceStarted = await model.StartService(progress, maintenanceMode: false); + var serviceStarted = await model.StartService(progress, maintenanceMode: false, cancellationToken); if (!serviceStarted) { reportCard.Errors.Add("The Service failed to start. Please consult the ServiceControl logs for this instance"); - await windowManager.ShowActionReport(reportCard, "UPGRADE FAILURE", "Instance reported this error after upgrade:"); + await windowManager.ShowActionReport(reportCard, "UPGRADE FAILURE", "Instance reported this error after upgrade:", cancellationToken: cancellationToken); return; } @@ -97,7 +98,7 @@ async Task UpgradeServiceControlInstance(ServiceControlAdvancedViewModel model, } await model.TryCloseAsync(true); - await eventAggregator.PublishOnUIThreadAsync(new ResetInstances()); + await eventAggregator.PublishOnUIThreadAsync(new ResetInstances(), cancellationToken); } readonly IEventAggregator eventAggregator; diff --git a/src/ServiceControl.Config/Commands/ForceUpgradeServiceControlInstanceCommand.cs b/src/ServiceControl.Config/Commands/ForceUpgradeServiceControlInstanceCommand.cs index 40992c6083..b3d8cb3549 100644 --- a/src/ServiceControl.Config/Commands/ForceUpgradeServiceControlInstanceCommand.cs +++ b/src/ServiceControl.Config/Commands/ForceUpgradeServiceControlInstanceCommand.cs @@ -1,6 +1,7 @@ namespace ServiceControl.Config.Commands; using System.IO; +using System.Threading; using System.Threading.Tasks; using Caliburn.Micro; using Events; @@ -36,34 +37,34 @@ public override async Task ExecuteAsync(ServiceControlAdvancedViewModel model) return; } - await UpgradeServiceControlInstance(model, instance); + await UpgradeServiceControlInstance(model, instance, CancellationToken.None); await eventAggregator.PublishOnUIThreadAsync(new ResetInstances()); } - async Task UpgradeServiceControlInstance(ServiceControlAdvancedViewModel model, ServiceControlInstance instance) + async Task UpgradeServiceControlInstance(ServiceControlAdvancedViewModel model, ServiceControlInstance instance, CancellationToken cancellationToken) { using (var progress = model.GetProgressObject($"UPGRADING {model.Name}")) { var reportCard = new ReportCard(); var restartAgain = model.IsRunning; - var stopped = await model.StopService(progress); + var stopped = await model.StopService(progress, cancellationToken); if (!stopped) { - await eventAggregator.PublishOnUIThreadAsync(new RefreshInstances()); + await eventAggregator.PublishOnUIThreadAsync(new RefreshInstances(), cancellationToken); reportCard.Errors.Add("Failed to stop the service"); reportCard.SetStatus(); - await windowManager.ShowActionReport(reportCard, "ISSUES UPGRADING INSTANCE", "Could not upgrade instance because of the following errors:"); + await windowManager.ShowActionReport(reportCard, "ISSUES UPGRADING INSTANCE", "Could not upgrade instance because of the following errors:", cancellationToken: cancellationToken); return; } if (Directory.Exists(model.ForcedUpgradeBackupLocation)) { - await windowManager.ShowMessage("Cannot make database backup.", $"The target database backup location: {model.ForcedUpgradeBackupLocation} already exists.", hideCancel: true); + await windowManager.ShowMessage("Cannot make database backup.", $"The target database backup location: {model.ForcedUpgradeBackupLocation} already exists.", hideCancel: true, cancellationToken: cancellationToken); return; } @@ -78,20 +79,20 @@ async Task UpgradeServiceControlInstance(ServiceControlAdvancedViewModel model, if (reportCard.HasErrors || reportCard.HasWarnings) { - await windowManager.ShowActionReport(reportCard, "ISSUES UPGRADING INSTANCE", "Could not upgrade instance because of the following errors:", "There were some warnings while upgrading the instance:"); + await windowManager.ShowActionReport(reportCard, "ISSUES UPGRADING INSTANCE", "Could not upgrade instance because of the following errors:", "There were some warnings while upgrading the instance:", cancellationToken: cancellationToken); return; } if (restartAgain) { - var serviceStarted = await model.StartService(progress, maintenanceMode: false); + var serviceStarted = await model.StartService(progress, maintenanceMode: false, cancellationToken); if (!serviceStarted) { reportCard.Errors.Add( "The Service failed to start. Please consult the ServiceControl logs for this instance"); await windowManager.ShowActionReport(reportCard, "UPGRADE FAILURE", - "Instance reported this error after upgrade:"); + "Instance reported this error after upgrade:", cancellationToken: cancellationToken); return; } @@ -99,7 +100,7 @@ await windowManager.ShowActionReport(reportCard, "UPGRADE FAILURE", } await model.TryCloseAsync(true); - await eventAggregator.PublishOnUIThreadAsync(new ResetInstances()); + await eventAggregator.PublishOnUIThreadAsync(new ResetInstances(), cancellationToken); } readonly IEventAggregator eventAggregator; diff --git a/src/ServiceControl.Config/Commands/ScmuCommandChecks.cs b/src/ServiceControl.Config/Commands/ScmuCommandChecks.cs index b21e0b37fd..5b47955af4 100644 --- a/src/ServiceControl.Config/Commands/ScmuCommandChecks.cs +++ b/src/ServiceControl.Config/Commands/ScmuCommandChecks.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics; using System.Text; + using System.Threading; using System.Threading.Tasks; using ServiceControl.Config.Framework; using ServiceControlInstaller.Engine; @@ -18,7 +19,7 @@ public ScmuCommandChecks(IServiceControlWindowManager windowManager) this.windowManager = windowManager; } - protected override async Task PromptForRabbitMqCheck(bool isUpgrade) + protected override async Task PromptForRabbitMqCheck(bool isUpgrade, CancellationToken cancellationToken = default) { var title = isUpgrade ? "UPGRADE WARNING" : "INSTALL WARNING"; var beforeWhat = isUpgrade ? "upgrading" : "installing"; @@ -34,27 +35,28 @@ protected override async Task PromptForRabbitMqCheck(bool isUpgrade) message.AppendLine(); message.AppendLine($"Please confirm your broker meets the minimum requirements before {beforeWhat}."); - var continueInstall = await windowManager.ShowYesNoDialog(title, message.ToString(), question, yes, no); + var continueInstall = await windowManager.ShowYesNoDialog(title, message.ToString(), question, yes, no, cancellationToken); return continueInstall; } - protected override Task NotifyForDeprecatedMessageTransport(TransportInfo transport) + protected override Task NotifyForDeprecatedMessageTransport(TransportInfo transport, CancellationToken cancellationToken = default) { - return windowManager.ShowMessage("DEPRECATED MESSAGE TRANSPORT", $"The message transport '{transport.DisplayName}' is not available in this version of ServiceControl, and this instance cannot be upgraded.", acceptText: "Cancel Upgrade", hideCancel: true); + return windowManager.ShowMessage("DEPRECATED MESSAGE TRANSPORT", $"The message transport '{transport.DisplayName}' is not available in this version of ServiceControl, and this instance cannot be upgraded.", acceptText: "Cancel Upgrade", hideCancel: true, cancellationToken: cancellationToken); } - protected override Task NotifyForMissingSystemPrerequisites(string missingPrereqsMessage) + protected override Task NotifyForMissingSystemPrerequisites(string missingPrereqsMessage, CancellationToken cancellationToken = default) { - return windowManager.ShowMessage("Missing prerequisites", missingPrereqsMessage, acceptText: "Cancel", hideCancel: true); + return windowManager.ShowMessage("Missing prerequisites", missingPrereqsMessage, acceptText: "Cancel", hideCancel: true, cancellationToken: cancellationToken); } - protected override async Task NotifyForIncompatibleStorageEngine(IServiceControlBaseInstance baseInstance) + protected override async Task NotifyForIncompatibleStorageEngine(IServiceControlBaseInstance baseInstance, CancellationToken cancellationToken = default) { var openUpgradeGuide = await windowManager.ShowYesNoDialog("STORAGE ENGINE INCOMPATIBLE", $"The storage format has changed and the {baseInstance.PersistenceManifest.DisplayName} storage engine is no longer available. Upgrading requires a side-by-side deployment of both versions. Migration guidance is available in the version 4 to 5 upgrade guidance at {UpgradeGuide4to5Url}", "Open online ServiceControl 4 to 5 upgrade guide in system default browser?", "Yes", - "No" + "No", + cancellationToken ); if (openUpgradeGuide) @@ -63,7 +65,7 @@ protected override async Task NotifyForIncompatibleStorageEngine(IServiceControl } } - protected override async Task NotifyForIncompatibleUpgradeVersion(UpgradeInfo upgradeInfo) + protected override async Task NotifyForIncompatibleUpgradeVersion(UpgradeInfo upgradeInfo, CancellationToken cancellationToken = default) { var nextVersion = upgradeInfo.UpgradePath[0]; await windowManager.ShowMessage("VERSION UPGRADE INCOMPATIBLE", @@ -76,26 +78,26 @@ await windowManager.ShowMessage("VERSION UPGRADE INCOMPATIBLE", "Upgrade this instance to the latest version of ServiceControl.\r\n" + "\r\n" + "", - hideCancel: true); + hideCancel: true, cancellationToken: cancellationToken); } - protected override Task NotifyError(string title, string message) + protected override Task NotifyError(string title, string message, CancellationToken cancellationToken = default) { - return windowManager.ShowMessage(title.ToUpperInvariant(), message, hideCancel: true); + return windowManager.ShowMessage(title.ToUpperInvariant(), message, hideCancel: true, cancellationToken: cancellationToken); } - protected override Task PromptToStopRunningInstance(BaseService instance) + protected override Task PromptToStopRunningInstance(BaseService instance, CancellationToken cancellationToken = default) { return windowManager.ShowYesNoDialog($"STOP INSTANCE AND UPGRADE TO {Constants.CurrentVersion}", $"{instance.Name} needs to be stopped in order to upgrade to version {Constants.CurrentVersion}.", "Do you want to proceed?", - "Yes, I want to proceed", "No"); + "Yes, I want to proceed", "No", cancellationToken); } - protected override Task PromptToContinueWithForcedUpgrade() + protected override Task PromptToContinueWithForcedUpgrade(CancellationToken cancellationToken = default) { return windowManager.ShowMessage("Forced migration", - "Do you want to proceed with forced migration to ServiceControl 5? The current RavenDB 3.5 database will be moved aside and a new database will be created.", "Yes"); + "Do you want to proceed with forced migration to ServiceControl 5? The current RavenDB 3.5 database will be moved aside and a new database will be created.", "Yes", cancellationToken: cancellationToken); } } } diff --git a/src/ServiceControl.Config/Commands/UpgradeAuditInstanceCommand.cs b/src/ServiceControl.Config/Commands/UpgradeAuditInstanceCommand.cs index 1c0d966a94..6ddcb8295c 100644 --- a/src/ServiceControl.Config/Commands/UpgradeAuditInstanceCommand.cs +++ b/src/ServiceControl.Config/Commands/UpgradeAuditInstanceCommand.cs @@ -1,6 +1,7 @@ namespace ServiceControl.Config.Commands { using System; + using System.Threading; using System.Threading.Tasks; using Caliburn.Micro; using Events; @@ -46,7 +47,7 @@ public override async Task ExecuteAsync(InstanceDetailsViewModel model) return; } - await UpgradeAuditInstance(model, instance, upgradeOptions); + await UpgradeAuditInstance(model, instance, upgradeOptions, CancellationToken.None); await eventAggregator.PublishOnUIThreadAsync(new RefreshInstances()); } @@ -54,7 +55,8 @@ public override async Task ExecuteAsync(InstanceDetailsViewModel model) async Task UpgradeAuditInstance( InstanceDetailsViewModel model, ServiceControlAuditInstance instance, - ServiceControlUpgradeOptions upgradeOptions + ServiceControlUpgradeOptions upgradeOptions, + CancellationToken cancellationToken ) { using (var progress = model.GetProgressObject($"UPGRADING {model.Name}")) @@ -62,15 +64,15 @@ ServiceControlUpgradeOptions upgradeOptions var reportCard = new ReportCard(); var restartAgain = model.IsRunning; - var stopped = await model.StopService(progress); + var stopped = await model.StopService(progress, cancellationToken); if (!stopped) { - await eventAggregator.PublishOnUIThreadAsync(new RefreshInstances()); + await eventAggregator.PublishOnUIThreadAsync(new RefreshInstances(), cancellationToken); reportCard.Errors.Add("Failed to stop the service"); reportCard.SetStatus(); - await windowManager.ShowActionReport(reportCard, "ISSUES UPGRADING INSTANCE", "Could not upgrade instance because of the following errors:"); + await windowManager.ShowActionReport(reportCard, "ISSUES UPGRADING INSTANCE", "Could not upgrade instance because of the following errors:", cancellationToken: cancellationToken); return; } @@ -79,17 +81,17 @@ ServiceControlUpgradeOptions upgradeOptions if (reportCard.HasErrors || reportCard.HasWarnings) { - await windowManager.ShowActionReport(reportCard, "ISSUES UPGRADING INSTANCE", "Could not upgrade instance because of the following errors:", "There were some warnings while upgrading the instance:"); + await windowManager.ShowActionReport(reportCard, "ISSUES UPGRADING INSTANCE", "Could not upgrade instance because of the following errors:", "There were some warnings while upgrading the instance:", cancellationToken: cancellationToken); return; } if (restartAgain) { - var serviceStarted = await model.StartService(progress); + var serviceStarted = await model.StartService(progress, cancellationToken); if (!serviceStarted) { reportCard.Errors.Add("The Service failed to start. Please consult the ServiceControl logs for this instance"); - await windowManager.ShowActionReport(reportCard, "UPGRADE FAILURE", "Instance reported this error after upgrade:"); + await windowManager.ShowActionReport(reportCard, "UPGRADE FAILURE", "Instance reported this error after upgrade:", cancellationToken: cancellationToken); } } } diff --git a/src/ServiceControl.Config/Commands/UpgradeServiceControlInstanceCommand.cs b/src/ServiceControl.Config/Commands/UpgradeServiceControlInstanceCommand.cs index 26f2671b7e..fc7bdc26f0 100644 --- a/src/ServiceControl.Config/Commands/UpgradeServiceControlInstanceCommand.cs +++ b/src/ServiceControl.Config/Commands/UpgradeServiceControlInstanceCommand.cs @@ -1,6 +1,7 @@ namespace ServiceControl.Config.Commands { using System; + using System.Threading; using System.Threading.Tasks; using Caliburn.Micro; using Events; @@ -146,27 +147,27 @@ public override async Task ExecuteAsync(InstanceDetailsViewModel model) return; } - await UpgradeServiceControlInstance(model, instance, upgradeOptions); + await UpgradeServiceControlInstance(model, instance, upgradeOptions, CancellationToken.None); await eventAggregator.PublishOnUIThreadAsync(new RefreshInstances()); } - async Task UpgradeServiceControlInstance(InstanceDetailsViewModel model, ServiceControlInstance instance, ServiceControlUpgradeOptions upgradeOptions) + async Task UpgradeServiceControlInstance(InstanceDetailsViewModel model, ServiceControlInstance instance, ServiceControlUpgradeOptions upgradeOptions, CancellationToken cancellationToken) { using (var progress = model.GetProgressObject($"UPGRADING {model.Name}")) { var reportCard = new ReportCard(); var restartAgain = model.IsRunning; - var stopped = await model.StopService(progress); + var stopped = await model.StopService(progress, cancellationToken); if (!stopped) { - await eventAggregator.PublishOnUIThreadAsync(new RefreshInstances()); + await eventAggregator.PublishOnUIThreadAsync(new RefreshInstances(), cancellationToken); reportCard.Errors.Add("Failed to stop the service"); reportCard.SetStatus(); - await windowManager.ShowActionReport(reportCard, "ISSUES UPGRADING INSTANCE", "Could not upgrade instance because of the following errors:"); + await windowManager.ShowActionReport(reportCard, "ISSUES UPGRADING INSTANCE", "Could not upgrade instance because of the following errors:", cancellationToken: cancellationToken); return; } @@ -175,17 +176,17 @@ async Task UpgradeServiceControlInstance(InstanceDetailsViewModel model, Service if (reportCard.HasErrors || reportCard.HasWarnings) { - await windowManager.ShowActionReport(reportCard, "ISSUES UPGRADING INSTANCE", "Could not upgrade instance because of the following errors:", "There were some warnings while upgrading the instance:"); + await windowManager.ShowActionReport(reportCard, "ISSUES UPGRADING INSTANCE", "Could not upgrade instance because of the following errors:", "There were some warnings while upgrading the instance:", cancellationToken: cancellationToken); } else { if (restartAgain) { - var serviceStarted = await model.StartService(progress); + var serviceStarted = await model.StartService(progress, cancellationToken); if (!serviceStarted) { reportCard.Errors.Add("The Service failed to start. Please consult the ServiceControl logs for this instance"); - await windowManager.ShowActionReport(reportCard, "UPGRADE FAILURE", "Instance reported this error after upgrade:"); + await windowManager.ShowActionReport(reportCard, "UPGRADE FAILURE", "Instance reported this error after upgrade:", cancellationToken: cancellationToken); } } } diff --git a/src/ServiceControl.Config/Framework/Modules/InstallerModule.cs b/src/ServiceControl.Config/Framework/Modules/InstallerModule.cs index 531997dba8..8bbe5c60cb 100644 --- a/src/ServiceControl.Config/Framework/Modules/InstallerModule.cs +++ b/src/ServiceControl.Config/Framework/Modules/InstallerModule.cs @@ -1,6 +1,7 @@ namespace ServiceControl.Config.Framework.Modules { using System; + using System.Threading; using System.Threading.Tasks; using Autofac; using ServiceControlInstaller.Engine.FileSystem; @@ -43,7 +44,7 @@ public abstract class InstallerBase public class ServiceControlInstallerBase : InstallerBase { - internal async Task Add(ServiceControlInstallableBase details, IProgress progress, Func> promptToProceed) + internal async Task Add(ServiceControlInstallableBase details, IProgress progress, Func> promptToProceed, CancellationToken cancellationToken = default) { ZipInfo.ValidateZip(); @@ -51,7 +52,7 @@ internal async Task Add(ServiceControlInstallableBase details, IProg instanceInstaller.ReportCard = new ReportCard(); //Validation - await instanceInstaller.Validate(promptToProceed); + await instanceInstaller.Validate(promptToProceed, cancellationToken); if (instanceInstaller.ReportCard.HasErrors || instanceInstaller.ReportCard.CancelRequested) { instanceInstaller.ReportCard.Status = Status.FailedValidation; @@ -148,12 +149,12 @@ protected virtual void UpgradeOptions(ServiceControlUpgradeOptions upgradeOption upgradeOptions.ApplyChangesToInstance(instance); } - internal async Task Update(ServiceControlBaseService instance, bool startService) + internal async Task Update(ServiceControlBaseService instance, bool startService, CancellationToken cancellationToken = default) { try { instance.ReportCard = new ReportCard(); - await instance.ValidateChanges(); + await instance.ValidateChanges(cancellationToken); if (instance.ReportCard.HasErrors) { instance.ReportCard.Status = Status.FailedValidation; @@ -238,7 +239,7 @@ public MonitoringInstanceInstaller() ZipInfo = new PlatformZipInfo(Constants.MonitoringExe, "ServiceControl Monitoring", "Particular.ServiceControl.Monitoring.zip"); } - internal async Task Add(MonitoringNewInstance details, IProgress progress, Func> promptToProceed) + internal async Task Add(MonitoringNewInstance details, IProgress progress, Func> promptToProceed, CancellationToken cancellationToken = default) { ZipInfo.ValidateZip(); @@ -246,7 +247,7 @@ internal async Task Add(MonitoringNewInstance details, IProgress prog return instance.ReportCard; } - internal async Task Update(MonitoringInstance instance, bool startService) + internal async Task Update(MonitoringInstance instance, bool startService, CancellationToken cancellationToken = default) { try { instance.ReportCard = new ReportCard(); - await instance.ValidateChanges(); + await instance.ValidateChanges(cancellationToken); if (instance.ReportCard.HasErrors) { instance.ReportCard.Status = Status.FailedValidation; diff --git a/src/ServiceControl.Config/Framework/RaygunFeedBack.cs b/src/ServiceControl.Config/Framework/RaygunFeedBack.cs index 6128a68511..b4e62316c9 100644 --- a/src/ServiceControl.Config/Framework/RaygunFeedBack.cs +++ b/src/ServiceControl.Config/Framework/RaygunFeedBack.cs @@ -7,6 +7,7 @@ namespace ServiceControl.Config.Framework using System.Net; using System.Net.Http; using System.Reflection; + using System.Threading; using System.Threading.Tasks; using Extensions; using Mindscape.Raygun4Net; @@ -21,9 +22,9 @@ public RaygunFeedback() init = Task.Run(async () => { - enabled = await TryInitializeRaygunClientWithCredentials() || - await TryInitializeRaygunClientWithCredentials(CredentialCache.DefaultCredentials) || - await TryInitializeRaygunClientWithCredentials(CredentialCache.DefaultNetworkCredentials); + enabled = await TryInitializeRaygunClientWithCredentials(null, CancellationToken.None) || + await TryInitializeRaygunClientWithCredentials(CredentialCache.DefaultCredentials, CancellationToken.None) || + await TryInitializeRaygunClientWithCredentials(CredentialCache.DefaultNetworkCredentials, CancellationToken.None); version = GetVersion(); }); } @@ -45,7 +46,7 @@ static Guid GetOrSetTrackingId() return trackingId; } - public Task SendFeedBack(string emailAddress, string message, bool includeSystemInfo) + public Task SendFeedBack(string emailAddress, string message, bool includeSystemInfo, CancellationToken cancellationToken = default) { var userInfo = ((IRaygunUserProvider)this).GetUser()!; userInfo.Email = emailAddress; @@ -62,10 +63,10 @@ public Task SendFeedBack(string emailAddress, string message, bool includeSystem } var m = raygunMessage.Build(); - return raygunClient.Send(m); + return raygunClient.Send(m, cancellationToken); } - public Task SendException(Exception ex, bool includeSystemInfo) + public Task SendException(Exception ex, bool includeSystemInfo, CancellationToken cancellationToken = default) { var userInfo = ((IRaygunUserProvider)this).GetUser()!; @@ -81,7 +82,7 @@ public Task SendException(Exception ex, bool includeSystemInfo) } var m = raygunMessage.Build(); - return raygunClient.Send(m); + return raygunClient.Send(m, cancellationToken); } RaygunIdentifierMessage IRaygunUserProvider.GetUser() => new(trackingId.BareString()) @@ -102,7 +103,7 @@ public bool Enabled } } - async Task TryInitializeRaygunClientWithCredentials(ICredentials? credentials = default) + async Task TryInitializeRaygunClientWithCredentials(ICredentials? credentials, CancellationToken cancellationToken) { HttpClient? http = null; try @@ -111,12 +112,17 @@ async Task TryInitializeRaygunClientWithCredentials(ICredentials? credenti { Timeout = TimeSpan.FromSeconds(5) }; - using var response = await http.GetAsync(RaygunUrl); + using var response = await http.GetAsync(RaygunUrl, cancellationToken); response.EnsureSuccessStatusCode(); raygunClient = new RaygunClient(raygunSettings, http, this); return true; } + catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested) + { + http?.Dispose(); + throw; + } catch { http?.Dispose(); diff --git a/src/ServiceControl.Config/Framework/Rx/RxConductor.cs b/src/ServiceControl.Config/Framework/Rx/RxConductor.cs index db071efcde..fb912b2ac4 100644 --- a/src/ServiceControl.Config/Framework/Rx/RxConductor.cs +++ b/src/ServiceControl.Config/Framework/Rx/RxConductor.cs @@ -7,23 +7,23 @@ public partial class RxConductor : RxConductorBaseWithActiveItem where T : class { - public override async Task ActivateItem(T item) + public override async Task ActivateItem(T item, CancellationToken cancellationToken = default) { if (item != null && item.Equals(ActiveItem)) { if (IsActive) { - await ScreenExtensions.TryActivateAsync(item); + await ScreenExtensions.TryActivateAsync(item, cancellationToken); OnActivationProcessed(item, true); } return; } - var result = await CloseStrategy.ExecuteAsync(new[] { ActiveItem }); + var result = await CloseStrategy.ExecuteAsync(new[] { ActiveItem }, cancellationToken); if (result.CloseCanOccur) { - await ChangeActiveItem(item, true); + await ChangeActiveItem(item, true, cancellationToken); } else { @@ -31,29 +31,29 @@ public override async Task ActivateItem(T item) } } - public override async Task DeactivateItem(T item, bool close) + public override async Task DeactivateItem(T item, bool close, CancellationToken cancellationToken = default) { if (item == null || !item.Equals(ActiveItem)) { return; } - var result = await CloseStrategy.ExecuteAsync(new[] { ActiveItem }); + var result = await CloseStrategy.ExecuteAsync(new[] { ActiveItem }, cancellationToken); if (result.CloseCanOccur) { - await ChangeActiveItem(default, close); + await ChangeActiveItem(default, close, cancellationToken); } } - public override async Task CanCloseAsync(CancellationToken cancellationToken) + public override async Task CanCloseAsync(CancellationToken cancellationToken = default) { var result = await CloseStrategy.ExecuteAsync(new[] { ActiveItem }, cancellationToken); return result.CloseCanOccur; } - protected override Task OnActivate() => ScreenExtensions.TryActivateAsync(ActiveItem); + protected override Task OnActivate(CancellationToken cancellationToken = default) => ScreenExtensions.TryActivateAsync(ActiveItem, cancellationToken); - protected override Task OnDeactivate(bool close) => ScreenExtensions.TryDeactivateAsync(ActiveItem, close); + protected override Task OnDeactivate(bool close, CancellationToken cancellationToken = default) => ScreenExtensions.TryDeactivateAsync(ActiveItem, close, cancellationToken); public override IEnumerable GetChildren() { diff --git a/src/ServiceControl.Config/Framework/Rx/RxConductorBase.cs b/src/ServiceControl.Config/Framework/Rx/RxConductorBase.cs index 7dc66ca48d..7d38d94cc4 100644 --- a/src/ServiceControl.Config/Framework/Rx/RxConductorBase.cs +++ b/src/ServiceControl.Config/Framework/Rx/RxConductorBase.cs @@ -17,12 +17,12 @@ public ICloseStrategy CloseStrategy Task IConductor.ActivateItemAsync(object item, CancellationToken cancellationToken) { - return ActivateItem((T)item); + return ActivateItem((T)item, cancellationToken); } Task IConductor.DeactivateItemAsync(object item, bool close, CancellationToken cancellationToken) { - return DeactivateItem((T)item, close); + return DeactivateItem((T)item, close, cancellationToken); } IEnumerable IParent.GetChildren() @@ -34,9 +34,9 @@ IEnumerable IParent.GetChildren() public abstract IEnumerable GetChildren(); - public abstract Task ActivateItem(T item); + public abstract Task ActivateItem(T item, CancellationToken cancellationToken = default); - public abstract Task DeactivateItem(T item, bool close); + public abstract Task DeactivateItem(T item, bool close, CancellationToken cancellationToken = default); protected virtual void OnActivationProcessed(T item, bool success) { diff --git a/src/ServiceControl.Config/Framework/Rx/RxConductorBaseWithActiveItem.cs b/src/ServiceControl.Config/Framework/Rx/RxConductorBaseWithActiveItem.cs index 6e3669f930..5b6107fe2b 100644 --- a/src/ServiceControl.Config/Framework/Rx/RxConductorBaseWithActiveItem.cs +++ b/src/ServiceControl.Config/Framework/Rx/RxConductorBaseWithActiveItem.cs @@ -1,5 +1,6 @@ namespace ServiceControl.Config.Framework.Rx { + using System.Threading; using System.Threading.Tasks; using Caliburn.Micro; @@ -17,15 +18,15 @@ object IHaveActiveItem.ActiveItem set { ActiveItem = (T)value; } } - protected virtual async Task ChangeActiveItem(T newItem, bool closePrevious) + protected virtual async Task ChangeActiveItem(T newItem, bool closePrevious, CancellationToken cancellationToken = default) { - await ScreenExtensions.TryDeactivateAsync(activeItem, closePrevious); + await ScreenExtensions.TryDeactivateAsync(activeItem, closePrevious, cancellationToken); newItem = EnsureItem(newItem); if (IsActive) { - await ScreenExtensions.TryActivateAsync(newItem); + await ScreenExtensions.TryActivateAsync(newItem, cancellationToken); } activeItem = newItem; diff --git a/src/ServiceControl.Config/Framework/Rx/RxConductorWithCollectionOneActive.cs b/src/ServiceControl.Config/Framework/Rx/RxConductorWithCollectionOneActive.cs index 95cb7a932a..375cd87aaa 100644 --- a/src/ServiceControl.Config/Framework/Rx/RxConductorWithCollectionOneActive.cs +++ b/src/ServiceControl.Config/Framework/Rx/RxConductorWithCollectionOneActive.cs @@ -48,23 +48,23 @@ public override IEnumerable GetChildren() return items; } - public override async Task ActivateItem(T item) + public override async Task ActivateItem(T item, CancellationToken cancellationToken = default) { if (item != null && item.Equals(ActiveItem)) { if (IsActive) { - await ScreenExtensions.TryActivateAsync(item); + await ScreenExtensions.TryActivateAsync(item, cancellationToken); OnActivationProcessed(item, true); } return; } - await ChangeActiveItem(item, false); + await ChangeActiveItem(item, false, cancellationToken); } - public override async Task DeactivateItem(T item, bool close) + public override async Task DeactivateItem(T item, bool close, CancellationToken cancellationToken = default) { if (item == null) { @@ -73,30 +73,30 @@ public override async Task DeactivateItem(T item, bool close) if (!close) { - await ScreenExtensions.TryDeactivateAsync(item, false); + await ScreenExtensions.TryDeactivateAsync(item, false, cancellationToken); } else { - var result = await CloseStrategy.ExecuteAsync(new[] { item }); + var result = await CloseStrategy.ExecuteAsync(new[] { item }, cancellationToken); if (result.CloseCanOccur) { - await CloseItemCore(item); + await CloseItemCore(item, cancellationToken); } } } - async Task CloseItemCore(T item) + async Task CloseItemCore(T item, CancellationToken cancellationToken) { if (item.Equals(ActiveItem)) { var index = items.IndexOf(item); var next = DetermineNextItemToActivate(items, index); - await ChangeActiveItem(next, true); + await ChangeActiveItem(next, true, cancellationToken); } else { - await ScreenExtensions.TryDeactivateAsync(item, true); + await ScreenExtensions.TryDeactivateAsync(item, true, cancellationToken); } items.Remove(item); @@ -119,7 +119,7 @@ protected virtual T DetermineNextItemToActivate(IList list, int lastIndex) return default; } - public override async Task CanCloseAsync(CancellationToken cancellationToken) + public override async Task CanCloseAsync(CancellationToken cancellationToken = default) { var result = await CloseStrategy.ExecuteAsync(items.ToList(), cancellationToken); var canClose = result.CloseCanOccur; @@ -140,7 +140,7 @@ public override async Task CanCloseAsync(CancellationToken cancellationTok while (closable.Contains(next)); var previousActive = ActiveItem; - await ChangeActiveItem(next, true); + await ChangeActiveItem(next, true, cancellationToken); items.Remove(previousActive); var stillToClose = closable.ToList(); @@ -150,7 +150,7 @@ public override async Task CanCloseAsync(CancellationToken cancellationTok await Task.WhenAll( from deactivatable in closable.OfType() - select deactivatable.DeactivateAsync(true) + select deactivatable.DeactivateAsync(true, cancellationToken) ); items.RemoveRange(closable); @@ -159,9 +159,9 @@ select deactivatable.DeactivateAsync(true) return canClose; } - protected override Task OnActivate() => ScreenExtensions.TryActivateAsync(ActiveItem); + protected override Task OnActivate(CancellationToken cancellationToken = default) => ScreenExtensions.TryActivateAsync(ActiveItem, cancellationToken); - protected override async Task OnDeactivate(bool close) + protected override async Task OnDeactivate(bool close, CancellationToken cancellationToken = default) { if (close) { @@ -169,14 +169,14 @@ protected override async Task OnDeactivate(bool close) { if (item is IDeactivate deactivatable) { - await deactivatable.DeactivateAsync(true); + await deactivatable.DeactivateAsync(true, cancellationToken); } } items.Clear(); } else { - await ScreenExtensions.TryDeactivateAsync(ActiveItem, false); + await ScreenExtensions.TryDeactivateAsync(ActiveItem, false, cancellationToken); } } diff --git a/src/ServiceControl.Config/Framework/Rx/RxProgressScreen.cs b/src/ServiceControl.Config/Framework/Rx/RxProgressScreen.cs index 5453f17cd7..736fb942f6 100644 --- a/src/ServiceControl.Config/Framework/Rx/RxProgressScreen.cs +++ b/src/ServiceControl.Config/Framework/Rx/RxProgressScreen.cs @@ -47,7 +47,7 @@ public int ProgressPercent } } - public override Task CanCloseAsync(CancellationToken cancellationToken) => Task.FromResult(!InProgress); + public override Task CanCloseAsync(CancellationToken cancellationToken = default) => Task.FromResult(!InProgress); void NotifyUpdates() { diff --git a/src/ServiceControl.Config/Framework/Rx/RxScreen.cs b/src/ServiceControl.Config/Framework/Rx/RxScreen.cs index 44b7a33f5f..7d5a91b6d9 100644 --- a/src/ServiceControl.Config/Framework/Rx/RxScreen.cs +++ b/src/ServiceControl.Config/Framework/Rx/RxScreen.cs @@ -63,12 +63,12 @@ async Task IActivate.ActivateAsync(CancellationToken cancellationToken) if (!IsInitialized) { IsInitialized = initialized = true; - await OnInitialize(); + await OnInitialize(cancellationToken); } IsActive = true; Log.Info("Activating {0}.", this); - await OnActivate(); + await OnActivate(cancellationToken); await Activated(this, new ActivationEventArgs { @@ -87,7 +87,7 @@ async Task IDeactivate.DeactivateAsync(bool close, CancellationToken cancellatio IsActive = false; Log.Info("Deactivating {0}.", this); - await OnDeactivate(close); + await OnDeactivate(close, cancellationToken); await Deactivated(this, new DeactivationEventArgs { @@ -106,14 +106,16 @@ async Task IDeactivate.DeactivateAsync(bool close, CancellationToken cancellatio /// Called to check whether or not this instance can close. /// /// The implementor calls this action with the result of the close check. - public virtual Task CanCloseAsync(CancellationToken cancellationToken) => Task.FromResult(true); + public virtual Task CanCloseAsync(CancellationToken cancellationToken = default) => Task.FromResult(true); /// /// Tries to close this instance by asking its Parent to initiate shutdown or by asking its corresponding view to close. /// Also provides an opportunity to pass a dialog result to it's corresponding view. /// /// The dialog result. +#pragma warning disable PS0018 // Caliburn.Micro's IClose.TryCloseAsync declares no CancellationToken, so one cannot be added here public virtual Task TryCloseAsync(bool? dialogResult = null) +#pragma warning restore PS0018 { Result = dialogResult; @@ -141,18 +143,18 @@ static bool IsHandler(object obj) /// /// Called when initializing. /// - protected virtual Task OnInitialize() => Task.CompletedTask; + protected virtual Task OnInitialize(CancellationToken cancellationToken = default) => Task.CompletedTask; /// /// Called when activating. /// - protected virtual Task OnActivate() => Task.CompletedTask; + protected virtual Task OnActivate(CancellationToken cancellationToken = default) => Task.CompletedTask; /// /// Called when deactivating. /// /// Inidicates whether this instance will be closed. - protected virtual Task OnDeactivate(bool close) => Task.CompletedTask; + protected virtual Task OnDeactivate(bool close, CancellationToken cancellationToken = default) => Task.CompletedTask; static readonly ILog Log = LogManager.GetLog(typeof(Screen)); } diff --git a/src/ServiceControl.Config/Framework/ServiceControlWindowManager.cs b/src/ServiceControl.Config/Framework/ServiceControlWindowManager.cs index 5de1ec8bdc..cff3833016 100644 --- a/src/ServiceControl.Config/Framework/ServiceControlWindowManager.cs +++ b/src/ServiceControl.Config/Framework/ServiceControlWindowManager.cs @@ -2,6 +2,7 @@ { using System; using System.Collections.Generic; + using System.Threading; using System.Threading.Tasks; using System.Windows; using Caliburn.Micro; @@ -13,23 +14,23 @@ public interface IServiceControlWindowManager : IWindowManager { - Task NavigateTo(RxScreen screen, object context = null, IDictionary settings = null); + Task NavigateTo(RxScreen screen, object context = null, IDictionary settings = null, CancellationToken cancellationToken = default); - Task ShowInnerDialog(RxScreen screen, object context = null, IDictionary settings = null); + Task ShowInnerDialog(RxScreen screen, object context = null, IDictionary settings = null, CancellationToken cancellationToken = default); - Task ShowOverlayDialog(RxScreen screen, object context = null, IDictionary settings = null); + Task ShowOverlayDialog(RxScreen screen, object context = null, IDictionary settings = null, CancellationToken cancellationToken = default); - Task ShowMessage(string title, string message, string acceptText = "Ok", bool hideCancel = false); + Task ShowMessage(string title, string message, string acceptText = "Ok", bool hideCancel = false, CancellationToken cancellationToken = default); - Task ShowYesNoCancelDialog(string title, string message, string question, string yesText, string noText); + Task ShowYesNoCancelDialog(string title, string message, string question, string yesText, string noText, CancellationToken cancellationToken = default); - Task ShowYesNoDialog(string title, string message, string question, string yesText, string noText); + Task ShowYesNoDialog(string title, string message, string question, string yesText, string noText, CancellationToken cancellationToken = default); - Task ShowSliderDialog(SliderDialogViewModel viewModel); + Task ShowSliderDialog(SliderDialogViewModel viewModel, CancellationToken cancellationToken = default); - Task ShowTextBoxDialog(TextBoxDialogViewModel viewModel); + Task ShowTextBoxDialog(TextBoxDialogViewModel viewModel, CancellationToken cancellationToken = default); - Task ShowActionReport(ReportCard reportcard, string title, string errorsMessage = "", string warningsMessage = ""); + Task ShowActionReport(ReportCard reportcard, string title, string errorsMessage = "", string warningsMessage = "", CancellationToken cancellationToken = default); void ScrollFirstErrorIntoView(object viewModel, object context = null); } @@ -41,22 +42,22 @@ public ServiceControlWindowManager(Func reportC this.reportCardViewModelFactory = reportCardViewModelFactory; } - public Task NavigateTo(RxScreen screen, object context = null, IDictionary settings = null) + public Task NavigateTo(RxScreen screen, object context = null, IDictionary settings = null, CancellationToken cancellationToken = default) { var shell = GetShell(); shell.ActiveContext = context; - return shell.ActivateItem(screen); + return shell.ActivateItem(screen, cancellationToken); } - public async Task ShowInnerDialog(RxScreen screen, object context = null, IDictionary settings = null) + public async Task ShowInnerDialog(RxScreen screen, object context = null, IDictionary settings = null, CancellationToken cancellationToken = default) { var shell = GetShell(); var previousContext = shell.ActiveContext; shell.IsModal = true; shell.ActiveContext = context; - await shell.ActivateItem(screen); + await shell.ActivateItem(screen, cancellationToken); screen.RunModal(); shell.IsModal = false; shell.ActiveContext = previousContext; @@ -69,62 +70,62 @@ public Task NavigateTo(RxScreen screen, object context = null, IDictionary ShowOverlayDialog(RxScreen screen, object context = null, IDictionary settings = null) + public async Task ShowOverlayDialog(RxScreen screen, object context = null, IDictionary settings = null, CancellationToken cancellationToken = default) { var shell = GetShell(); var previousContext = shell.ActiveContext; shell.Overlay = screen; shell.ActiveContext = context; - await screen.ActivateAsync(); + await ((IActivate)screen).ActivateAsync(cancellationToken); screen.RunModal(); shell.Overlay = null; shell.ActiveContext = previousContext; return screen.Result; } - public async Task ShowMessage(string title, string message, string acceptText = "Ok", bool hideCancel = false) + public async Task ShowMessage(string title, string message, string acceptText = "Ok", bool hideCancel = false, CancellationToken cancellationToken = default) { var messageBox = new MessageBoxViewModel(title, message, acceptText, hideCancel); - var result = await ShowOverlayDialog(messageBox); + var result = await ShowOverlayDialog(messageBox, cancellationToken: cancellationToken); return result ?? false; } - public Task ShowYesNoCancelDialog(string title, string message, string question, string yesText, string noText) + public Task ShowYesNoCancelDialog(string title, string message, string question, string yesText, string noText, CancellationToken cancellationToken = default) { var messageBox = new YesNoCancelViewModel(title, message, question, yesText, noText); - return ShowOverlayDialog(messageBox); + return ShowOverlayDialog(messageBox, cancellationToken: cancellationToken); } - public async Task ShowYesNoDialog(string title, string message, string question, string yesText, string noText) + public async Task ShowYesNoDialog(string title, string message, string question, string yesText, string noText, CancellationToken cancellationToken = default) { var messageBox = new YesNoCancelViewModel(title, message, question, yesText, noText) { ShowCancelButton = false }; - var result = await ShowOverlayDialog(messageBox); + var result = await ShowOverlayDialog(messageBox, cancellationToken: cancellationToken); return result.Value; } - public async Task ShowSliderDialog(SliderDialogViewModel viewModel) + public async Task ShowSliderDialog(SliderDialogViewModel viewModel, CancellationToken cancellationToken = default) { - var result = await ShowOverlayDialog(viewModel); + var result = await ShowOverlayDialog(viewModel, cancellationToken: cancellationToken); return result ?? false; } - public async Task ShowTextBoxDialog(TextBoxDialogViewModel viewModel) + public async Task ShowTextBoxDialog(TextBoxDialogViewModel viewModel, CancellationToken cancellationToken = default) { - var result = await ShowOverlayDialog(viewModel); + var result = await ShowOverlayDialog(viewModel, cancellationToken: cancellationToken); return result ?? false; } - public async Task ShowActionReport(ReportCard reportcard, string title, string errorsMessage = "", string warningsMessage = "") + public async Task ShowActionReport(ReportCard reportcard, string title, string errorsMessage = "", string warningsMessage = "", CancellationToken cancellationToken = default) { var messageBox = reportCardViewModelFactory(reportcard); messageBox.Title = title; messageBox.ErrorsMessage = errorsMessage; messageBox.WarningsMessage = warningsMessage; - var result = await ShowOverlayDialog(messageBox); + var result = await ShowOverlayDialog(messageBox, cancellationToken: cancellationToken); return result ?? false; } diff --git a/src/ServiceControl.Config/UI/AdvancedOptions/ServiceControlAdvancedViewModel.cs b/src/ServiceControl.Config/UI/AdvancedOptions/ServiceControlAdvancedViewModel.cs index d83e9edb0a..638fad9e38 100644 --- a/src/ServiceControl.Config/UI/AdvancedOptions/ServiceControlAdvancedViewModel.cs +++ b/src/ServiceControl.Config/UI/AdvancedOptions/ServiceControlAdvancedViewModel.cs @@ -140,7 +140,7 @@ public bool AllowStop public string ForcedUpgradeBackupLocation => ServiceControlInstance.DatabaseBackupPath; - public Task HandleAsync(PostRefreshInstances message, CancellationToken cancellationToken) + public Task HandleAsync(PostRefreshInstances message, CancellationToken cancellationToken = default) { NotifyOfPropertyChange("AllowStop"); NotifyOfPropertyChange("IsRunning"); @@ -149,7 +149,7 @@ public Task HandleAsync(PostRefreshInstances message, CancellationToken cancella return Task.CompletedTask; } - public async Task StartService(IProgressObject progress, bool maintenanceMode) + public async Task StartService(IProgressObject progress, bool maintenanceMode, CancellationToken cancellationToken = default) { var disposeProgress = progress == null; var result = false; @@ -170,7 +170,7 @@ await Task.Run(() => } result = ServiceControlInstance.TryStartService(); - }); + }, cancellationToken); return result; } @@ -183,7 +183,7 @@ await Task.Run(() => } } - public async Task StopService(IProgressObject progress = null) + public async Task StopService(IProgressObject progress = null, CancellationToken cancellationToken = default) { var disposeProgress = progress == null; var result = false; @@ -200,7 +200,7 @@ await Task.Run(() => { ServiceControlInstance.DisableMaintenanceMode(); } - }); + }, cancellationToken); return result; } diff --git a/src/ServiceControl.Config/UI/FeedBack/FeedBackViewModel.cs b/src/ServiceControl.Config/UI/FeedBack/FeedBackViewModel.cs index ed627be581..04dee4fccd 100644 --- a/src/ServiceControl.Config/UI/FeedBack/FeedBackViewModel.cs +++ b/src/ServiceControl.Config/UI/FeedBack/FeedBackViewModel.cs @@ -1,5 +1,7 @@ namespace ServiceControl.Config.UI.FeedBack { + using System; + using System.Threading; using System.Threading.Tasks; using System.Windows.Input; using Framework; @@ -15,7 +17,7 @@ public FeedBackViewModel(RaygunFeedback raygunFeedBack) feedBack = raygunFeedBack; validationTemplate = new ValidationTemplate(this); Cancel = Command.Create(async () => await TryCloseAsync(false)); - SendFeedBack = Command.Create(async () => await Send()); + SendFeedBack = Command.Create(async () => await Send(CancellationToken.None)); } public string EmailAddress { get; set; } @@ -30,7 +32,7 @@ public FeedBackViewModel(RaygunFeedback raygunFeedBack) public bool SubmitAttempted { get; set; } - async Task Send() + async Task Send(CancellationToken cancellationToken) { SubmitAttempted = true; if (!validationTemplate.Validate()) @@ -42,9 +44,13 @@ async Task Send() try { - await feedBack.SendFeedBack(EmailAddress, Message, IncludeSystemInfo); + await feedBack.SendFeedBack(EmailAddress, Message, IncludeSystemInfo, cancellationToken); Success = true; } + catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested) + { + throw; + } catch { Success = false; diff --git a/src/ServiceControl.Config/UI/InstanceAdd/MonitoringAddAttachment.cs b/src/ServiceControl.Config/UI/InstanceAdd/MonitoringAddAttachment.cs index 384118f339..d6d3ada53b 100644 --- a/src/ServiceControl.Config/UI/InstanceAdd/MonitoringAddAttachment.cs +++ b/src/ServiceControl.Config/UI/InstanceAdd/MonitoringAddAttachment.cs @@ -1,6 +1,7 @@ namespace ServiceControl.Config.UI.InstanceAdd { using System; + using System.Threading; using System.Threading.Tasks; using Caliburn.Micro; using Events; @@ -40,7 +41,7 @@ bool IsInProgress() return viewModel != null && !viewModel.InProgress; } - async Task Add() + async Task Add(CancellationToken cancellationToken) { viewModel.SubmitAttempted = true; if (!viewModel.ValidationTemplate.Validate()) @@ -71,7 +72,7 @@ async Task Add() ServiceAccountPwd = viewModel.Password }; - if (!await commandChecks.ValidateNewInstance(instanceMetadata)) + if (!await commandChecks.ValidateNewInstance([instanceMetadata], cancellationToken)) { viewModel.InProgress = false; return; @@ -79,11 +80,11 @@ async Task Add() using (var progress = viewModel.GetProgressObject("ADDING INSTANCE")) { - var reportCard = await Task.Run(() => installer.Add(instanceMetadata, progress, PromptToProceed)); + var reportCard = await Task.Run(() => installer.Add(instanceMetadata, progress, PromptToProceed, cancellationToken), cancellationToken); if (reportCard.HasErrors || reportCard.HasWarnings) { - await windowManager.ShowActionReport(reportCard, "ISSUES ADDING INSTANCE", "Could not add new instance because of the following errors:", "There were some warnings while adding the instance:"); + await windowManager.ShowActionReport(reportCard, "ISSUES ADDING INSTANCE", "Could not add new instance because of the following errors:", "There were some warnings while adding the instance:", cancellationToken); return; } @@ -95,14 +96,14 @@ async Task Add() await viewModel.TryCloseAsync(true); - await eventAggregator.PublishOnUIThreadAsync(new RefreshInstances()); + await eventAggregator.PublishOnUIThreadAsync(new RefreshInstances(), cancellationToken); } - async Task PromptToProceed(PathInfo pathInfo) + async Task PromptToProceed(PathInfo pathInfo, CancellationToken cancellationToken) { var result = false; - await Execute.OnUIThreadAsync(async () => { result = await windowManager.ShowYesNoDialog("ADDING INSTANCE QUESTION - DIRECTORY NOT EMPTY", $"The directory specified as the {pathInfo.Name} is not empty.", $"Are you sure you want to use '{pathInfo.Path}' ?", "Yes use it", "No I want to change it"); }); + await Execute.OnUIThreadAsync(async () => { result = await windowManager.ShowYesNoDialog("ADDING INSTANCE QUESTION - DIRECTORY NOT EMPTY", $"The directory specified as the {pathInfo.Name} is not empty.", $"Are you sure you want to use '{pathInfo.Path}' ?", "Yes use it", "No I want to change it", cancellationToken); }); return result; } diff --git a/src/ServiceControl.Config/UI/InstanceAdd/ServiceControlAddAttachment.cs b/src/ServiceControl.Config/UI/InstanceAdd/ServiceControlAddAttachment.cs index 5c9863dd03..8cf5c88352 100644 --- a/src/ServiceControl.Config/UI/InstanceAdd/ServiceControlAddAttachment.cs +++ b/src/ServiceControl.Config/UI/InstanceAdd/ServiceControlAddAttachment.cs @@ -1,6 +1,7 @@ namespace ServiceControl.Config.UI.InstanceAdd { using System; + using System.Threading; using System.Threading.Tasks; using Caliburn.Micro; using Events; @@ -38,7 +39,7 @@ protected override void OnAttach() bool IsInProgress() => viewModel != null && !viewModel.InProgress; - async Task Add() + async Task Add(CancellationToken cancellationToken) { viewModel.SubmitAttempted = true; if (!viewModel.ValidationTemplate.Validate()) @@ -104,7 +105,7 @@ async Task Add() auditNewInstance.EnableFullTextSearchOnBodies = viewModel.ServiceControlAudit.EnableFullTextSearchOnBodies.Value; } - if (!await commandChecks.ValidateNewInstance(serviceControlNewInstance, auditNewInstance)) + if (!await commandChecks.ValidateNewInstance([serviceControlNewInstance, auditNewInstance], cancellationToken)) { viewModel.InProgress = false; return; @@ -119,7 +120,7 @@ async Task Add() { using (var progress = viewModel.GetProgressObject("ADDING INSTANCE")) { - var installationCancelled = await InstallInstance(serviceControlNewInstance, progress); + var installationCancelled = await InstallInstance(serviceControlNewInstance, progress, cancellationToken); if (installationCancelled) { return; @@ -131,7 +132,7 @@ async Task Add() { using (var progress = viewModel.GetProgressObject("ADDING AUDIT INSTANCE")) { - var installationCancelled = await InstallInstance(auditNewInstance, progress); + var installationCancelled = await InstallInstance(auditNewInstance, progress, cancellationToken); if (installationCancelled) { return; @@ -141,16 +142,16 @@ async Task Add() await viewModel.TryCloseAsync(true); - await eventAggregator.PublishOnUIThreadAsync(new RefreshInstances()); + await eventAggregator.PublishOnUIThreadAsync(new RefreshInstances(), cancellationToken); } - async Task InstallInstance(ServiceControlNewInstance instanceData, IProgressObject progress) + async Task InstallInstance(ServiceControlNewInstance instanceData, IProgressObject progress, CancellationToken cancellationToken) { - var reportCard = await Task.Run(() => serviceControlInstaller.Add(instanceData, progress, PromptToProceed)); + var reportCard = await Task.Run(() => serviceControlInstaller.Add(instanceData, progress, PromptToProceed, cancellationToken), cancellationToken); if (reportCard.HasErrors || reportCard.HasWarnings) { - await windowManager.ShowActionReport(reportCard, "ISSUES ADDING INSTANCE", "Could not add new instance because of the following errors:", "There were some warnings while adding the instance:"); + await windowManager.ShowActionReport(reportCard, "ISSUES ADDING INSTANCE", "Could not add new instance because of the following errors:", "There were some warnings while adding the instance:", cancellationToken); return true; } @@ -162,13 +163,13 @@ async Task InstallInstance(ServiceControlNewInstance instanceData, IProgre return false; } - async Task InstallInstance(ServiceControlAuditNewInstance instanceData, IProgressObject progress) + async Task InstallInstance(ServiceControlAuditNewInstance instanceData, IProgressObject progress, CancellationToken cancellationToken) { - var reportCard = await Task.Run(() => serviceControlAuditInstaller.Add(instanceData, progress, PromptToProceed)); + var reportCard = await Task.Run(() => serviceControlAuditInstaller.Add(instanceData, progress, PromptToProceed, cancellationToken), cancellationToken); if (reportCard.HasErrors || reportCard.HasWarnings) { - await windowManager.ShowActionReport(reportCard, "ISSUES ADDING INSTANCE", "Could not add new instance because of the following errors:", "There were some warnings while adding the instance:"); + await windowManager.ShowActionReport(reportCard, "ISSUES ADDING INSTANCE", "Could not add new instance because of the following errors:", "There were some warnings while adding the instance:", cancellationToken); return true; } @@ -180,11 +181,11 @@ async Task InstallInstance(ServiceControlAuditNewInstance instanceData, IP return false; } - async Task PromptToProceed(PathInfo pathInfo) + async Task PromptToProceed(PathInfo pathInfo, CancellationToken cancellationToken) { var result = false; - await Execute.OnUIThreadAsync(async () => { result = await windowManager.ShowYesNoDialog("ADDING INSTANCE QUESTION - DIRECTORY NOT EMPTY", $"The directory specified as the {pathInfo.Name} is not empty.", $"Are you sure you want to use '{pathInfo.Path}' ?", "Yes use it", "No I want to change it"); }); + await Execute.OnUIThreadAsync(async () => { result = await windowManager.ShowYesNoDialog("ADDING INSTANCE QUESTION - DIRECTORY NOT EMPTY", $"The directory specified as the {pathInfo.Name} is not empty.", $"Are you sure you want to use '{pathInfo.Path}' ?", "Yes use it", "No I want to change it", cancellationToken); }); return result; } diff --git a/src/ServiceControl.Config/UI/InstanceDetails/InstanceDetailsViewModel.cs b/src/ServiceControl.Config/UI/InstanceDetails/InstanceDetailsViewModel.cs index 4a1c9dd14d..8264f7b54d 100644 --- a/src/ServiceControl.Config/UI/InstanceDetails/InstanceDetailsViewModel.cs +++ b/src/ServiceControl.Config/UI/InstanceDetails/InstanceDetailsViewModel.cs @@ -362,7 +362,7 @@ public bool AllowStop public bool Exists() => ServiceInstance.Service.Exists(); - public Task HandleAsync(PostRefreshInstances message, CancellationToken cancellationToken) + public Task HandleAsync(PostRefreshInstances message, CancellationToken cancellationToken = default) { UpdateServiceProperties(); @@ -383,7 +383,7 @@ public Task HandleAsync(PostRefreshInstances message, CancellationToken cancella return Task.CompletedTask; } - public async Task StartService(IProgressObject progress = null) + public async Task StartService(IProgressObject progress = null, CancellationToken cancellationToken = default) { var disposeProgress = progress == null; var result = false; @@ -414,7 +414,7 @@ public async Task StartService(IProgressObject progress = null) } } - public async Task StopService(IProgressObject progress = null) + public async Task StopService(IProgressObject progress = null, CancellationToken cancellationToken = default) { var disposeProgress = progress == null; var result = false; @@ -431,7 +431,7 @@ await Task.Run(() => { ServiceControlInstance.DisableMaintenanceMode(); } - }); + }, cancellationToken); UpdateServiceProperties(); diff --git a/src/ServiceControl.Config/UI/InstanceEdit/MonitoringEditAttachment.cs b/src/ServiceControl.Config/UI/InstanceEdit/MonitoringEditAttachment.cs index 86d38420de..4c0d22e74d 100644 --- a/src/ServiceControl.Config/UI/InstanceEdit/MonitoringEditAttachment.cs +++ b/src/ServiceControl.Config/UI/InstanceEdit/MonitoringEditAttachment.cs @@ -2,6 +2,7 @@ namespace ServiceControl.Config.UI.InstanceEdit { using System; using System.ServiceProcess; + using System.Threading; using System.Threading.Tasks; using Caliburn.Micro; using Events; @@ -28,7 +29,7 @@ protected override void OnAttach() viewModel.Cancel = Command.Create(async () => { await viewModel.TryCloseAsync(false); - await eventAggregator.PublishOnUIThreadAsync(new RefreshInstances()); + await eventAggregator.PublishOnUIThreadAsync(new RefreshInstances(), CancellationToken.None); }, IsInProgress); } @@ -37,7 +38,7 @@ bool IsInProgress() return viewModel != null && !viewModel.InProgress; } - async Task Save() + async Task Save(CancellationToken cancellationToken) { viewModel.SubmitAttempted = true; if (!viewModel.ValidationTemplate.Validate()) @@ -53,7 +54,7 @@ async Task Save() if (instance.Service.Status == ServiceControllerStatus.Running) { var shouldProceed = await windowManager.ShowMessage("STOP INSTANCE AND MODIFY", - $"{instance.Name} needs to be stopped in order to modify the settings. Do you want to proceed."); + $"{instance.Name} needs to be stopped in order to modify the settings. Do you want to proceed.", cancellationToken: cancellationToken); if (!shouldProceed) { return; @@ -81,7 +82,7 @@ async Task Save() if (reportCard.HasErrors || reportCard.HasWarnings) { - await windowManager.ShowActionReport(reportCard, "ISSUES MODIFYING INSTANCE", "Could not modify instance because of the following errors:", "There were some warnings while modifying the instance:"); + await windowManager.ShowActionReport(reportCard, "ISSUES MODIFYING INSTANCE", "Could not modify instance because of the following errors:", "There were some warnings while modifying the instance:", cancellationToken: cancellationToken); return; } @@ -90,7 +91,7 @@ async Task Save() await viewModel.TryCloseAsync(true); - await eventAggregator.PublishOnUIThreadAsync(new RefreshInstances()); + await eventAggregator.PublishOnUIThreadAsync(new RefreshInstances(), cancellationToken); } readonly IServiceControlWindowManager windowManager; diff --git a/src/ServiceControl.Config/UI/InstanceEdit/ServiceControlAuditEditAttachment.cs b/src/ServiceControl.Config/UI/InstanceEdit/ServiceControlAuditEditAttachment.cs index b7b876dbff..3a3c4f8e97 100644 --- a/src/ServiceControl.Config/UI/InstanceEdit/ServiceControlAuditEditAttachment.cs +++ b/src/ServiceControl.Config/UI/InstanceEdit/ServiceControlAuditEditAttachment.cs @@ -2,6 +2,7 @@ namespace ServiceControl.Config.UI.InstanceEdit { using System; using System.ServiceProcess; + using System.Threading; using System.Threading.Tasks; using Caliburn.Micro; using Events; @@ -28,7 +29,7 @@ protected override void OnAttach() viewModel.Cancel = Command.Create(async () => { await viewModel.TryCloseAsync(false); - await eventAggregator.PublishOnUIThreadAsync(new RefreshInstances()); + await eventAggregator.PublishOnUIThreadAsync(new RefreshInstances(), CancellationToken.None); }, IsInProgress); } @@ -37,7 +38,7 @@ bool IsInProgress() return viewModel != null && !viewModel.InProgress; } - async Task Save() + async Task Save(CancellationToken cancellationToken) { viewModel.SubmitAttempted = true; if (!viewModel.ValidationTemplate.Validate()) @@ -52,7 +53,7 @@ async Task Save() if (instance.Service.Status == ServiceControllerStatus.Running) { var shouldProceed = await windowManager.ShowMessage("STOP INSTANCE AND MODIFY", - $"{instance.Name} needs to be stopped in order to modify the settings. Do you want to proceed."); + $"{instance.Name} needs to be stopped in order to modify the settings. Do you want to proceed.", cancellationToken: cancellationToken); if (!shouldProceed) { return; @@ -86,7 +87,7 @@ async Task Save() if (reportCard.HasErrors || reportCard.HasWarnings) { - await windowManager.ShowActionReport(reportCard, "ISSUES MODIFYING INSTANCE", "Could not modify instance because of the following errors:", "There were some warnings while modifying the instance:"); + await windowManager.ShowActionReport(reportCard, "ISSUES MODIFYING INSTANCE", "Could not modify instance because of the following errors:", "There were some warnings while modifying the instance:", cancellationToken: cancellationToken); return; } @@ -95,7 +96,7 @@ async Task Save() await viewModel.TryCloseAsync(true); - await eventAggregator.PublishOnUIThreadAsync(new RefreshInstances()); + await eventAggregator.PublishOnUIThreadAsync(new RefreshInstances(), cancellationToken); } readonly IServiceControlWindowManager windowManager; diff --git a/src/ServiceControl.Config/UI/InstanceEdit/ServiceControlEditAttachment.cs b/src/ServiceControl.Config/UI/InstanceEdit/ServiceControlEditAttachment.cs index f978f2f82c..3ae699c8fd 100644 --- a/src/ServiceControl.Config/UI/InstanceEdit/ServiceControlEditAttachment.cs +++ b/src/ServiceControl.Config/UI/InstanceEdit/ServiceControlEditAttachment.cs @@ -2,6 +2,7 @@ namespace ServiceControl.Config.UI.InstanceEdit { using System; using System.ServiceProcess; + using System.Threading; using System.Threading.Tasks; using Caliburn.Micro; using Events; @@ -28,7 +29,7 @@ protected override void OnAttach() viewModel.Cancel = Command.Create(async () => { await viewModel.TryCloseAsync(false); - await eventAggregator.PublishOnUIThreadAsync(new RefreshInstances()); + await eventAggregator.PublishOnUIThreadAsync(new RefreshInstances(), CancellationToken.None); }, IsInProgress); } @@ -37,7 +38,7 @@ bool IsInProgress() return viewModel != null && !viewModel.InProgress; } - async Task Save() + async Task Save(CancellationToken cancellationToken) { viewModel.SubmitAttempted = true; if (!viewModel.ValidationTemplate.Validate()) @@ -52,7 +53,7 @@ async Task Save() if (instance.Service.Status == ServiceControllerStatus.Running) { var shouldProceed = await windowManager.ShowMessage("STOP INSTANCE AND MODIFY", - $"{instance.Name} needs to be stopped in order to modify the settings. Do you want to proceed."); + $"{instance.Name} needs to be stopped in order to modify the settings. Do you want to proceed.", cancellationToken: cancellationToken); if (!shouldProceed) { return; @@ -88,7 +89,7 @@ async Task Save() if (reportCard.HasErrors || reportCard.HasWarnings) { - await windowManager.ShowActionReport(reportCard, "ISSUES MODIFYING INSTANCE", "Could not modify instance because of the following errors:", "There were some warnings while modifying the instance:"); + await windowManager.ShowActionReport(reportCard, "ISSUES MODIFYING INSTANCE", "Could not modify instance because of the following errors:", "There were some warnings while modifying the instance:", cancellationToken: cancellationToken); return; } @@ -97,7 +98,7 @@ async Task Save() await viewModel.TryCloseAsync(true); - await eventAggregator.PublishOnUIThreadAsync(new RefreshInstances()); + await eventAggregator.PublishOnUIThreadAsync(new RefreshInstances(), cancellationToken); } readonly IServiceControlWindowManager windowManager; diff --git a/src/ServiceControl.Config/UI/License/LicenseViewModel.cs b/src/ServiceControl.Config/UI/License/LicenseViewModel.cs index efcfd2d27d..8eecdde9d1 100644 --- a/src/ServiceControl.Config/UI/License/LicenseViewModel.cs +++ b/src/ServiceControl.Config/UI/License/LicenseViewModel.cs @@ -2,6 +2,7 @@ { using System.Collections.Generic; using System.Linq; + using System.Threading; using System.Threading.Tasks; using System.Windows.Input; using Caliburn.Micro; @@ -29,7 +30,7 @@ class LicenseViewModel : RxScreen public string ExtendLicenseUrl { get; set; } - protected override Task OnActivate() + protected override Task OnActivate(CancellationToken cancellationToken = default) { RefreshLicenseInfo(); return Task.CompletedTask; @@ -46,7 +47,9 @@ void RefreshLicenseInfo() ExtendLicenseUrl = $"https://particular.net/license/nservicebus?t={(license.IsEvaluationLicense ? 0 : 1)}&p=servicecontrol"; } +#pragma warning disable PS0018 // Bound to AwaitableSelectPathCommand's Func, which the tokenless ICommand.Execute drives async Task OpenLicenseFile(string path) +#pragma warning restore PS0018 { if (LicenseManager.TryImportLicense(path, out var importError)) { diff --git a/src/ServiceControl.Config/UI/ListInstances/ListInstancesViewModel.cs b/src/ServiceControl.Config/UI/ListInstances/ListInstancesViewModel.cs index 0313b9de8e..bb89720cd6 100644 --- a/src/ServiceControl.Config/UI/ListInstances/ListInstancesViewModel.cs +++ b/src/ServiceControl.Config/UI/ListInstances/ListInstancesViewModel.cs @@ -67,7 +67,7 @@ public string ConfigurationErrorMessage [AlsoNotifyFor(nameof(OrderedInstances), nameof(HasConfigurationErrors), nameof(ConfigurationErrorMessage))] IList Instances { get; } - public Task HandleAsync(LicenseUpdated licenseUpdatedEvent, CancellationToken cancellationToken) + public Task HandleAsync(LicenseUpdated licenseUpdatedEvent, CancellationToken cancellationToken = default) { // on license change inform each instance to refresh the license (1.23.0 and below don't support this) foreach (var instance in Instances) @@ -105,13 +105,13 @@ public Task HandleAsync(LicenseUpdated licenseUpdatedEvent, CancellationToken ca /// before the PostRefreshInstances handlers do all their rebinding. That way, deleting an instance /// in PowerShell won't cause an error from a deleted instance viewmodel trying to refresh itself. /// - public async Task HandleAsync(RefreshInstances message, CancellationToken cancellationToken) + public async Task HandleAsync(RefreshInstances message, CancellationToken cancellationToken = default) { AddAndRemoveInstances(); await EventAggregator.PublishOnUIThreadAsync(new PostRefreshInstances(), cancellationToken); } - public async Task HandleAsync(ResetInstances message, CancellationToken cancellationToken) + public async Task HandleAsync(ResetInstances message, CancellationToken cancellationToken = default) { foreach (var instance in Instances) { diff --git a/src/ServiceControl.Config/UI/MessageBox/ExceptionMessageBox.xaml.cs b/src/ServiceControl.Config/UI/MessageBox/ExceptionMessageBox.xaml.cs index bfb921fa8e..40f7cb82f6 100644 --- a/src/ServiceControl.Config/UI/MessageBox/ExceptionMessageBox.xaml.cs +++ b/src/ServiceControl.Config/UI/MessageBox/ExceptionMessageBox.xaml.cs @@ -1,6 +1,7 @@ namespace ServiceControl.Config.UI.MessageBox { using System; + using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Input; @@ -95,7 +96,9 @@ void CopyClick(object sender, RoutedEventArgs e) Clipboard.SetText(ErrorDetails.Text); } +#pragma warning disable PS0018 // Bound to AwaitableDelegateCommand's Func, which the tokenless ICommand.Execute drives async Task CallReportClick(object sender) +#pragma warning restore PS0018 { ProgressTitle = "Processing"; ProgressMessage = "Sending Exception Details..."; diff --git a/src/ServiceControl.Config/UI/Shell/FeedBackAttachment.cs b/src/ServiceControl.Config/UI/Shell/FeedBackAttachment.cs index 128ba1d451..a0616a2f56 100644 --- a/src/ServiceControl.Config/UI/Shell/FeedBackAttachment.cs +++ b/src/ServiceControl.Config/UI/Shell/FeedBackAttachment.cs @@ -1,6 +1,7 @@ namespace ServiceControl.Config.UI.Shell { using System; + using System.Threading; using System.Threading.Tasks; using FeedBack; using Framework; @@ -23,10 +24,10 @@ public FeedBackAttachment( protected override void OnAttach() { - viewModel.OpenFeedBack = Command.Create(async () => await FeedBack()); + viewModel.OpenFeedBack = Command.Create(async () => await FeedBack(CancellationToken.None)); } - async Task FeedBack() + async Task FeedBack(CancellationToken cancellationToken) { if (raygunFeedBack.Enabled) { diff --git a/src/ServiceControl.Config/UI/Shell/LicenseStatusManager.cs b/src/ServiceControl.Config/UI/Shell/LicenseStatusManager.cs index 241163baac..f73dc3e7de 100644 --- a/src/ServiceControl.Config/UI/Shell/LicenseStatusManager.cs +++ b/src/ServiceControl.Config/UI/Shell/LicenseStatusManager.cs @@ -37,13 +37,13 @@ public bool ShowPopup public bool IsSerious { get; private set; } public bool HasFocus { get; private set; } - public Task HandleAsync(FocusChanged message, CancellationToken cancellationToken) + public Task HandleAsync(FocusChanged message, CancellationToken cancellationToken = default) { HasFocus = message.HasFocus; return Task.CompletedTask; } - public Task HandleAsync(LicenseUpdated message, CancellationToken cancellationToken) + public Task HandleAsync(LicenseUpdated message, CancellationToken cancellationToken = default) { RefreshStatus(false); return Task.CompletedTask; diff --git a/src/ServiceControl.Config/UI/Shell/ShellViewModel.cs b/src/ServiceControl.Config/UI/Shell/ShellViewModel.cs index ea9328d98f..fb50e3a731 100644 --- a/src/ServiceControl.Config/UI/Shell/ShellViewModel.cs +++ b/src/ServiceControl.Config/UI/Shell/ShellViewModel.cs @@ -86,20 +86,20 @@ IEventAggregator eventAggregator public string AvailableUpgradeReleaseLink { get; set; } - public Task HandleAsync(PostRefreshInstances message, CancellationToken cancellationToken) => RefreshInstances(); + public Task HandleAsync(PostRefreshInstances message, CancellationToken cancellationToken = default) => RefreshInstances(cancellationToken); - public Task HandleAsync(ResetInstances message, CancellationToken cancellationToken) => RefreshInstances(); + public Task HandleAsync(ResetInstances message, CancellationToken cancellationToken = default) => RefreshInstances(cancellationToken); - protected override Task OnInitialize() => RefreshInstances(); + protected override Task OnInitialize(CancellationToken cancellationToken = default) => RefreshInstances(cancellationToken); - protected override async Task OnActivate() + protected override async Task OnActivate(CancellationToken cancellationToken = default) { - await base.OnActivate(); + await base.OnActivate(cancellationToken); BeginCheckForUpdates(); } - public async Task RefreshInstances() + public async Task RefreshInstances(CancellationToken cancellationToken = default) { HasInstances = InstanceFinder.AllInstances().Any(); @@ -107,11 +107,11 @@ public async Task RefreshInstances() { if (HasInstances) { - await ActivateItem(listInstances); + await ActivateItem(listInstances, cancellationToken); } else { - await ActivateItem(noInstances); + await ActivateItem(noInstances, cancellationToken); } } } @@ -125,16 +125,16 @@ void BeginCheckForUpdates() return; } - updateCheckTask = CheckForUpdates(); + updateCheckTask = CheckForUpdates(CancellationToken.None); NotifyOfPropertyChange(nameof(IsCheckingForUpdate)); } - async Task CheckForUpdates() + async Task CheckForUpdates(CancellationToken cancellationToken) { try { - var availableUpgradeRelease = await VersionCheckerHelper.GetLatestRelease(AppVersion); + var availableUpgradeRelease = await VersionCheckerHelper.GetLatestRelease(AppVersion, cancellationToken); if (availableUpgradeRelease.Version == AppVersion) { @@ -147,6 +147,10 @@ async Task CheckForUpdates() UpdateAvailable = true; } } + catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested) + { + throw; + } catch { UpdateAvailable = false; diff --git a/src/ServiceControl.Config/UI/Shell/VersionCheckerHelper.cs b/src/ServiceControl.Config/UI/Shell/VersionCheckerHelper.cs index e91190b5e3..d1bf7786d3 100644 --- a/src/ServiceControl.Config/UI/Shell/VersionCheckerHelper.cs +++ b/src/ServiceControl.Config/UI/Shell/VersionCheckerHelper.cs @@ -8,14 +8,15 @@ using System.Net.Http.Headers; using System.Net.Http.Json; using System.Text.Json.Serialization; + using System.Threading; using System.Threading.Tasks; using NuGet.Versioning; public static class VersionCheckerHelper { - public static async Task GetLatestRelease(SemanticVersion currentVersion) + public static async Task GetLatestRelease(SemanticVersion currentVersion, CancellationToken cancellationToken = default) { - List releases = await GetVersionInformation(); + List releases = await GetVersionInformation(cancellationToken); if (releases != null) { @@ -31,11 +32,15 @@ public static async Task GetLatestRelease(SemanticVersion currentVersio return new Release(currentVersion); } - static async Task> GetVersionInformation() + static async Task> GetVersionInformation(CancellationToken cancellationToken) { try { - return await httpClient.GetFromJsonAsync>("https://s3.us-east-1.amazonaws.com/platformupdate.particular.net/servicecontrol.txt"); + return await httpClient.GetFromJsonAsync>("https://s3.us-east-1.amazonaws.com/platformupdate.particular.net/servicecontrol.txt", cancellationToken); + } + catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested) + { + throw; } catch { diff --git a/src/ServiceControl.Config/UI/Upgrades/AddNewAuditInstanceAttachment.cs b/src/ServiceControl.Config/UI/Upgrades/AddNewAuditInstanceAttachment.cs index 49cd2b357d..80e0b464de 100644 --- a/src/ServiceControl.Config/UI/Upgrades/AddNewAuditInstanceAttachment.cs +++ b/src/ServiceControl.Config/UI/Upgrades/AddNewAuditInstanceAttachment.cs @@ -1,5 +1,6 @@ namespace ServiceControl.Config.UI.Upgrades { + using System.Threading; using System.Threading.Tasks; using Framework; using ReactiveUI; @@ -28,7 +29,7 @@ protected override void OnAttach() viewModel.Continue = ReactiveCommand.CreateFromTask(Continue); } - async Task Continue() + async Task Continue(CancellationToken cancellationToken) { viewModel.SubmitAttempted = true; diff --git a/src/ServiceControl.Management.PowerShell/.editorconfig b/src/ServiceControl.Management.PowerShell/.editorconfig index 2815635efb..c0448b0dda 100644 --- a/src/ServiceControl.Management.PowerShell/.editorconfig +++ b/src/ServiceControl.Management.PowerShell/.editorconfig @@ -1,6 +1 @@ [*.cs] - -# Cancellation analyzer debt. These fire because Particular.Analyzers is no longer pinned to 0.9.0. -# They are scheduled work, not accepted exceptions: remove a line once this project has no -# violations of that rule left, and never add a rule back to this list. -dotnet_diagnostic.PS0018.severity = none diff --git a/src/ServiceControl.Management.PowerShell/Cmdlets/AuditInstances/NewServiceControlAuditInstance.cs b/src/ServiceControl.Management.PowerShell/Cmdlets/AuditInstances/NewServiceControlAuditInstance.cs index c531667c82..768041419f 100644 --- a/src/ServiceControl.Management.PowerShell/Cmdlets/AuditInstances/NewServiceControlAuditInstance.cs +++ b/src/ServiceControl.Management.PowerShell/Cmdlets/AuditInstances/NewServiceControlAuditInstance.cs @@ -2,6 +2,7 @@ { using System; using System.Management.Automation; + using System.Threading; using System.Threading.Tasks; using ServiceControlInstaller.Engine.Instances; using ServiceControlInstaller.Engine.Unattended; @@ -172,7 +173,7 @@ protected override void ProcessRecord() { return; } - if (!checks.ValidateNewInstance(newAuditInstance).GetAwaiter().GetResult()) + if (!checks.ValidateNewInstance([newAuditInstance]).GetAwaiter().GetResult()) { return; } @@ -201,7 +202,7 @@ protected override void ProcessRecord() } } - Task PromptToProceed(PathInfo pathInfo) + Task PromptToProceed(PathInfo pathInfo, CancellationToken cancellationToken) { if (!pathInfo.CheckIfEmpty) { diff --git a/src/ServiceControl.Management.PowerShell/Cmdlets/MonitoringInstances/NewMonitoringInstance.cs b/src/ServiceControl.Management.PowerShell/Cmdlets/MonitoringInstances/NewMonitoringInstance.cs index 8f90c7e732..315a8bb828 100644 --- a/src/ServiceControl.Management.PowerShell/Cmdlets/MonitoringInstances/NewMonitoringInstance.cs +++ b/src/ServiceControl.Management.PowerShell/Cmdlets/MonitoringInstances/NewMonitoringInstance.cs @@ -2,6 +2,7 @@ namespace ServiceControl.Management.PowerShell { using System; using System.Management.Automation; + using System.Threading; using System.Threading.Tasks; using Cmdlets.Instances; using ServiceControlInstaller.Engine.Instances; @@ -142,7 +143,7 @@ protected override void ProcessRecord() { return; } - if (!checks.ValidateNewInstance(monitoringNewInstance).GetAwaiter().GetResult()) + if (!checks.ValidateNewInstance([monitoringNewInstance]).GetAwaiter().GetResult()) { return; } @@ -171,7 +172,7 @@ protected override void ProcessRecord() } } - Task PromptToProceed(PathInfo pathInfo) + Task PromptToProceed(PathInfo pathInfo, CancellationToken cancellationToken) { if (!pathInfo.CheckIfEmpty) { diff --git a/src/ServiceControl.Management.PowerShell/Cmdlets/ServiceControlInstances/NewServiceControlInstance.cs b/src/ServiceControl.Management.PowerShell/Cmdlets/ServiceControlInstances/NewServiceControlInstance.cs index 96e905e064..eb2e792fcf 100644 --- a/src/ServiceControl.Management.PowerShell/Cmdlets/ServiceControlInstances/NewServiceControlInstance.cs +++ b/src/ServiceControl.Management.PowerShell/Cmdlets/ServiceControlInstances/NewServiceControlInstance.cs @@ -4,6 +4,7 @@ namespace ServiceControl.Management.PowerShell using System.IO; using System.Linq; using System.Management.Automation; + using System.Threading; using System.Threading.Tasks; using ServiceControlInstaller.Engine.Instances; using ServiceControlInstaller.Engine.Unattended; @@ -188,7 +189,7 @@ protected override void ProcessRecord() { return; } - if (!checks.ValidateNewInstance(details).GetAwaiter().GetResult()) + if (!checks.ValidateNewInstance([details]).GetAwaiter().GetResult()) { return; } @@ -228,7 +229,7 @@ protected override void ProcessRecord() } } - Task PromptToProceed(PathInfo pathInfo) + Task PromptToProceed(PathInfo pathInfo, CancellationToken cancellationToken) { if (!pathInfo.CheckIfEmpty) { diff --git a/src/ServiceControl.Management.PowerShell/Validation/PowerShellCommandChecks.cs b/src/ServiceControl.Management.PowerShell/Validation/PowerShellCommandChecks.cs index 9d9ffd27bc..ca81261a14 100644 --- a/src/ServiceControl.Management.PowerShell/Validation/PowerShellCommandChecks.cs +++ b/src/ServiceControl.Management.PowerShell/Validation/PowerShellCommandChecks.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Management.Automation; using System.Text; + using System.Threading; using System.Threading.Tasks; using ServiceControlInstaller.Engine; using ServiceControlInstaller.Engine.Configuration.ServiceControl; @@ -28,21 +29,21 @@ void Terminate(string message, string errorId, ErrorCategory category) cmdlet.ThrowTerminatingError(errorRecord); } - protected override Task NotifyForDeprecatedMessageTransport(TransportInfo transport) + protected override Task NotifyForDeprecatedMessageTransport(TransportInfo transport, CancellationToken cancellationToken = default) { var terminateMsg = $"The message transport '{transport.DisplayName}' is not available in this version of ServiceControl, and this instance cannot be upgraded."; Terminate(terminateMsg, "Install Error", ErrorCategory.InvalidOperation); return Task.CompletedTask; } - protected override Task NotifyForIncompatibleStorageEngine(IServiceControlBaseInstance baseInstance) + protected override Task NotifyForIncompatibleStorageEngine(IServiceControlBaseInstance baseInstance, CancellationToken cancellationToken = default) { var msg = $"The storage format has changed and the {baseInstance.PersistenceManifest.DisplayName} storage engine is no longer available. Upgrading requires a side-by-side deployment of both versions. Migration guidance is available in the version 4 to 5 upgrade guidance at {UpgradeGuide4to5Url}"; Terminate(msg, "Install Error", ErrorCategory.InvalidOperation); return Task.CompletedTask; } - protected override Task NotifyForIncompatibleUpgradeVersion(UpgradeInfo upgradeInfo) + protected override Task NotifyForIncompatibleUpgradeVersion(UpgradeInfo upgradeInfo, CancellationToken cancellationToken = default) { var nextVersion = upgradeInfo.UpgradePath[0]; var b = new StringBuilder(); @@ -57,19 +58,19 @@ protected override Task NotifyForIncompatibleUpgradeVersion(UpgradeInfo upgradeI return Task.CompletedTask; } - protected override Task NotifyError(string title, string message) + protected override Task NotifyError(string title, string message, CancellationToken cancellationToken = default) { Terminate(message, title, ErrorCategory.InvalidOperation); return Task.CompletedTask; } - protected override Task NotifyForMissingSystemPrerequisites(string missingPrereqsMessage) + protected override Task NotifyForMissingSystemPrerequisites(string missingPrereqsMessage, CancellationToken cancellationToken = default) { Terminate(missingPrereqsMessage, "Missing Prerequisites", ErrorCategory.NotInstalled); return Task.CompletedTask; } - protected override Task PromptForRabbitMqCheck(bool isUpgrade) + protected override Task PromptForRabbitMqCheck(bool isUpgrade, CancellationToken cancellationToken = default) { if (!acknowledgements.Any(ack => ack.Equals(AcknowledgementValues.RabbitMQBrokerVersion310, StringComparison.OrdinalIgnoreCase))) { @@ -90,13 +91,13 @@ protected override Task PromptForRabbitMqCheck(bool isUpgrade) return Task.FromResult(true); } - protected override Task PromptToStopRunningInstance(BaseService instance) + protected override Task PromptToStopRunningInstance(BaseService instance, CancellationToken cancellationToken = default) { // PowerShell assumes you always want to stop the service if it's running return Task.FromResult(true); } - protected override Task PromptToContinueWithForcedUpgrade() + protected override Task PromptToContinueWithForcedUpgrade(CancellationToken cancellationToken = default) { // In PowerShell, you passed the -Force parameter to get here in the first place return Task.FromResult(true); diff --git a/src/ServiceControlInstaller.Engine.UnitTests/RunEngineTasksExplicitly.cs b/src/ServiceControlInstaller.Engine.UnitTests/RunEngineTasksExplicitly.cs index 97927c0bcf..d45f8f1866 100644 --- a/src/ServiceControlInstaller.Engine.UnitTests/RunEngineTasksExplicitly.cs +++ b/src/ServiceControlInstaller.Engine.UnitTests/RunEngineTasksExplicitly.cs @@ -72,13 +72,13 @@ public async Task CreateInstanceMSMQ() // constructer of ServiceControlInstanceMetadata extracts version from zip details.Version = Constants.CurrentVersion; - await details.Validate(s => Task.FromResult(false)).ConfigureAwait(false); + await details.Validate((s, token) => Task.FromResult(false)).ConfigureAwait(false); if (details.ReportCard.HasErrors) { throw new Exception($"Validation errors: {string.Join("\r\n", details.ReportCard.Errors)}"); } - Assert.DoesNotThrowAsync(() => installer.Add(details, s => Task.FromResult(false))); + Assert.DoesNotThrowAsync(() => installer.Add(details, (s, token) => Task.FromResult(false))); } [Test] diff --git a/src/ServiceControlInstaller.Engine/.editorconfig b/src/ServiceControlInstaller.Engine/.editorconfig index e3bf403607..c0448b0dda 100644 --- a/src/ServiceControlInstaller.Engine/.editorconfig +++ b/src/ServiceControlInstaller.Engine/.editorconfig @@ -1,5 +1 @@ -[*.cs] - -# may be enabled in future -dotnet_diagnostic.PS0013.severity = none # A Func used as a method parameter with a Task, ValueTask, or ValueTask return type argument should have at least one CancellationToken parameter type argument unless it has a parameter type argument implementing ICancellableContext -dotnet_diagnostic.PS0018.severity = none # A task-returning method should have a CancellationToken parameter unless it has a parameter implementing ICancellableContext +[*.cs] diff --git a/src/ServiceControlInstaller.Engine/Instances/MonitoringInstance.cs b/src/ServiceControlInstaller.Engine/Instances/MonitoringInstance.cs index 09a91d848c..acc834a920 100644 --- a/src/ServiceControlInstaller.Engine/Instances/MonitoringInstance.cs +++ b/src/ServiceControlInstaller.Engine/Instances/MonitoringInstance.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Security.AccessControl; using System.Security.Principal; + using System.Threading; using System.Threading.Tasks; using Accounts; using Configuration; @@ -136,11 +137,11 @@ TransportInfo DetermineTransportPackage() return transport ?? throw new Exception($"{SettingsList.TransportType.Name} value of '{transportAppSetting}' in app.config is invalid."); } - public async Task ValidateChanges() + public async Task ValidateChanges(CancellationToken cancellationToken = default) { try { - await new PathsValidator(this).RunValidation(false).ConfigureAwait(false); + await new PathsValidator(this).RunValidation(false, cancellationToken).ConfigureAwait(false); } catch (EngineValidationException ex) { diff --git a/src/ServiceControlInstaller.Engine/Instances/MonitoringNewInstance.cs b/src/ServiceControlInstaller.Engine/Instances/MonitoringNewInstance.cs index 3339de8823..6b629e8e9e 100644 --- a/src/ServiceControlInstaller.Engine/Instances/MonitoringNewInstance.cs +++ b/src/ServiceControlInstaller.Engine/Instances/MonitoringNewInstance.cs @@ -5,6 +5,7 @@ using System.IO; using System.Security.AccessControl; using System.Security.Principal; + using System.Threading; using System.Threading.Tasks; using Accounts; using Configuration.Monitoring; @@ -142,7 +143,7 @@ public void SetupInstance() } } - public async Task Validate(Func> promptToProceed) + public async Task Validate(Func> promptToProceed, CancellationToken cancellationToken = default) { if (TransportPackage.ZipName.Equals("MSMQ", StringComparison.OrdinalIgnoreCase)) { @@ -167,7 +168,7 @@ public async Task Validate(Func> promptToProceed) try { - ReportCard.CancelRequested = await new PathsValidator(this).RunValidation(true, promptToProceed).ConfigureAwait(false); + ReportCard.CancelRequested = await new PathsValidator(this).RunValidation(true, promptToProceed, cancellationToken).ConfigureAwait(false); } catch (EngineValidationException ex) { diff --git a/src/ServiceControlInstaller.Engine/Instances/ServiceControlBaseService.cs b/src/ServiceControlInstaller.Engine/Instances/ServiceControlBaseService.cs index b0c5916ff1..e2983cb02e 100644 --- a/src/ServiceControlInstaller.Engine/Instances/ServiceControlBaseService.cs +++ b/src/ServiceControlInstaller.Engine/Instances/ServiceControlBaseService.cs @@ -7,6 +7,7 @@ namespace ServiceControlInstaller.Engine.Instances using System.Linq; using System.Security.AccessControl; using System.Security.Principal; + using System.Threading; using System.Threading.Tasks; using Accounts; using Configuration; @@ -300,7 +301,7 @@ protected virtual void ValidateConnectionString() { } - protected virtual Task ValidatePaths() => Task.CompletedTask; + protected virtual Task ValidatePaths(CancellationToken cancellationToken = default) => Task.CompletedTask; protected virtual void ValidateQueueNames() { @@ -360,9 +361,9 @@ public void SetupInstance() } } - public async Task ValidateChanges() + public async Task ValidateChanges(CancellationToken cancellationToken = default) { - await ValidatePaths().ConfigureAwait(false); + await ValidatePaths(cancellationToken).ConfigureAwait(false); ValidateQueueNames(); diff --git a/src/ServiceControlInstaller.Engine/Instances/ServiceControlInstallableBase.cs b/src/ServiceControlInstaller.Engine/Instances/ServiceControlInstallableBase.cs index 49d7b73ca9..d66df319ee 100644 --- a/src/ServiceControlInstaller.Engine/Instances/ServiceControlInstallableBase.cs +++ b/src/ServiceControlInstaller.Engine/Instances/ServiceControlInstallableBase.cs @@ -5,6 +5,7 @@ using System.IO; using System.Security.AccessControl; using System.Security.Principal; + using System.Threading; using System.Threading.Tasks; using System.Xml.Serialization; using Accounts; @@ -219,7 +220,7 @@ public void Save(string path) } } - public async Task Validate(Func> promptToProceed) + public async Task Validate(Func> promptToProceed, CancellationToken cancellationToken = default) { RunValidation(ValidateTransport); RunValidation(ValidatePort); @@ -227,7 +228,7 @@ public async Task Validate(Func> promptToProceed) try { - ReportCard.CancelRequested = await ValidatePaths(promptToProceed).ConfigureAwait(false); + ReportCard.CancelRequested = await ValidatePaths(promptToProceed, cancellationToken).ConfigureAwait(false); } catch (EngineValidationException ex) { @@ -267,9 +268,9 @@ protected virtual void ValidateQueueNames() { } - protected virtual Task ValidatePaths(Func> promptToProceed) + protected virtual Task ValidatePaths(Func> promptToProceed, CancellationToken cancellationToken = default) { - return new PathsValidator(this).RunValidation(true, promptToProceed); + return new PathsValidator(this).RunValidation(true, promptToProceed, cancellationToken); } protected virtual void ValidateMaintenancePort() diff --git a/src/ServiceControlInstaller.Engine/Instances/ServiceControlInstance.cs b/src/ServiceControlInstaller.Engine/Instances/ServiceControlInstance.cs index 9817315782..34328ebe5d 100644 --- a/src/ServiceControlInstaller.Engine/Instances/ServiceControlInstance.cs +++ b/src/ServiceControlInstaller.Engine/Instances/ServiceControlInstance.cs @@ -5,6 +5,7 @@ namespace ServiceControlInstaller.Engine.Instances using System.Configuration; using System.IO; using System.Linq; + using System.Threading; using System.Threading.Tasks; using Configuration; using Configuration.ServiceControl; @@ -96,11 +97,11 @@ protected override void ValidateQueueNames() } } - protected override async Task ValidatePaths() + protected override async Task ValidatePaths(CancellationToken cancellationToken = default) { try { - await new PathsValidator(this).RunValidation(false).ConfigureAwait(false); + await new PathsValidator(this).RunValidation(false, cancellationToken).ConfigureAwait(false); } catch (EngineValidationException ex) { diff --git a/src/ServiceControlInstaller.Engine/Unattended/UnattendAuditInstaller.cs b/src/ServiceControlInstaller.Engine/Unattended/UnattendAuditInstaller.cs index eb80879cdb..8280fb29a8 100644 --- a/src/ServiceControlInstaller.Engine/Unattended/UnattendAuditInstaller.cs +++ b/src/ServiceControlInstaller.Engine/Unattended/UnattendAuditInstaller.cs @@ -2,6 +2,7 @@ { using System; using System.ServiceProcess; + using System.Threading; using System.Threading.Tasks; using FileSystem; using Instances; @@ -18,7 +19,7 @@ public UnattendAuditInstaller(ILogging loggingInstance) public PlatformZipInfo ZipInfo { get; } - public async Task Add(ServiceControlAuditNewInstance details, Func> promptToProceed) + public async Task Add(ServiceControlAuditNewInstance details, Func> promptToProceed, CancellationToken cancellationToken = default) { ZipInfo.ValidateZip(); @@ -27,7 +28,7 @@ public async Task Add(ServiceControlAuditNewInstance details, Func Add(MonitoringNewInstance details, Func> promptToProceed) + public async Task Add(MonitoringNewInstance details, Func> promptToProceed, CancellationToken cancellationToken = default) { ZipInfo.ValidateZip(); @@ -26,7 +27,7 @@ public async Task Add(MonitoringNewInstance details, Func Update(MonitoringInstance instance, bool startService) + internal async Task Update(MonitoringInstance instance, bool startService, CancellationToken cancellationToken = default) { instance.ReportCard = new ReportCard(); - await instance.ValidateChanges().ConfigureAwait(false); + await instance.ValidateChanges(cancellationToken).ConfigureAwait(false); if (instance.ReportCard.HasErrors) { foreach (var error in instance.ReportCard.Errors) diff --git a/src/ServiceControlInstaller.Engine/Unattended/UnattendServiceControlInstaller.cs b/src/ServiceControlInstaller.Engine/Unattended/UnattendServiceControlInstaller.cs index ff2fc16964..565e9cf783 100644 --- a/src/ServiceControlInstaller.Engine/Unattended/UnattendServiceControlInstaller.cs +++ b/src/ServiceControlInstaller.Engine/Unattended/UnattendServiceControlInstaller.cs @@ -3,6 +3,7 @@ using System; using System.Linq; using System.ServiceProcess; + using System.Threading; using System.Threading.Tasks; using Configuration.ServiceControl; using FileSystem; @@ -20,7 +21,7 @@ public UnattendServiceControlInstaller(ILogging loggingInstance) public PlatformZipInfo ZipInfo { get; } - public async Task Add(ServiceControlNewInstance details, Func> promptToProceed) + public async Task Add(ServiceControlNewInstance details, Func> promptToProceed, CancellationToken cancellationToken = default) { ZipInfo.ValidateZip(); @@ -32,7 +33,7 @@ public async Task Add(ServiceControlNewInstance details, Func Update(ServiceControlInstance instance, bool startService) + internal async Task Update(ServiceControlInstance instance, bool startService, CancellationToken cancellationToken = default) { instance.ReportCard = new ReportCard(); - await instance.ValidateChanges().ConfigureAwait(false); + await instance.ValidateChanges(cancellationToken).ConfigureAwait(false); if (instance.ReportCard.HasErrors) { return false; diff --git a/src/ServiceControlInstaller.Engine/Validation/AbstractCommandChecks.cs b/src/ServiceControlInstaller.Engine/Validation/AbstractCommandChecks.cs index 9f4e2771b4..4e9363e14a 100644 --- a/src/ServiceControlInstaller.Engine/Validation/AbstractCommandChecks.cs +++ b/src/ServiceControlInstaller.Engine/Validation/AbstractCommandChecks.cs @@ -3,6 +3,7 @@ using System; using System.Linq; using System.ServiceProcess; + using System.Threading; using System.Threading.Tasks; using NuGet.Versioning; using ServiceControl.LicenseManagement; @@ -14,24 +15,24 @@ public abstract class AbstractCommandChecks { protected const string UpgradeGuide4to5Url = "https://docs.particular.net/servicecontrol/upgrades/4to5/"; - protected abstract Task PromptForRabbitMqCheck(bool isUpgrade); - protected abstract Task PromptToStopRunningInstance(BaseService instance); - protected abstract Task PromptToContinueWithForcedUpgrade(); - protected abstract Task NotifyForDeprecatedMessageTransport(TransportInfo transport); - protected abstract Task NotifyForMissingSystemPrerequisites(string missingPrereqsMessage); - protected abstract Task NotifyForIncompatibleStorageEngine(IServiceControlBaseInstance baseInstance); - protected abstract Task NotifyForIncompatibleUpgradeVersion(UpgradeInfo upgradeInfo); - protected abstract Task NotifyError(string title, string message); + protected abstract Task PromptForRabbitMqCheck(bool isUpgrade, CancellationToken cancellationToken = default); + protected abstract Task PromptToStopRunningInstance(BaseService instance, CancellationToken cancellationToken = default); + protected abstract Task PromptToContinueWithForcedUpgrade(CancellationToken cancellationToken = default); + protected abstract Task NotifyForDeprecatedMessageTransport(TransportInfo transport, CancellationToken cancellationToken = default); + protected abstract Task NotifyForMissingSystemPrerequisites(string missingPrereqsMessage, CancellationToken cancellationToken = default); + protected abstract Task NotifyForIncompatibleStorageEngine(IServiceControlBaseInstance baseInstance, CancellationToken cancellationToken = default); + protected abstract Task NotifyForIncompatibleUpgradeVersion(UpgradeInfo upgradeInfo, CancellationToken cancellationToken = default); + protected abstract Task NotifyError(string title, string message, CancellationToken cancellationToken = default); - public async Task CanAddInstance(bool allowExpiredLicense = false) + public async Task CanAddInstance(bool allowExpiredLicense = false, CancellationToken cancellationToken = default) { // Check for license - if (!allowExpiredLicense && !await IsLicenseOk().ConfigureAwait(false)) + if (!allowExpiredLicense && !await IsLicenseOk(cancellationToken).ConfigureAwait(false)) { return false; } - if (await OldVersionOfServiceControlInstalled().ConfigureAwait(false)) + if (await OldVersionOfServiceControlInstalled(cancellationToken).ConfigureAwait(false)) { return false; } @@ -39,7 +40,7 @@ public async Task CanAddInstance(bool allowExpiredLicense = false) return true; } - public async Task ValidateNewInstance(params IServiceInstance[] instances) + public async Task ValidateNewInstance(IServiceInstance[] instances, CancellationToken cancellationToken = default) { var transport = instances .OfType() @@ -47,16 +48,16 @@ public async Task ValidateNewInstance(params IServiceInstance[] instances) .Select(i => i.TransportPackage) .First(t => t is not null); - var continueInstall = await RabbitMqCheckIsOK(transport, Constants.CurrentVersion, false).ConfigureAwait(false); + var continueInstall = await RabbitMqCheckIsOK(transport, Constants.CurrentVersion, false, cancellationToken).ConfigureAwait(false); return continueInstall; } - public Task CanEditInstance(BaseService instance) => CanEditOrDelete(instance, isDelete: false); + public Task CanEditInstance(BaseService instance, CancellationToken cancellationToken = default) => CanEditOrDelete(instance, isDelete: false, cancellationToken); - public Task CanDeleteInstance(BaseService instance) => CanEditOrDelete(instance, isDelete: true); + public Task CanDeleteInstance(BaseService instance, CancellationToken cancellationToken = default) => CanEditOrDelete(instance, isDelete: true, cancellationToken); - async Task CanEditOrDelete(BaseService instance, bool isDelete) + async Task CanEditOrDelete(BaseService instance, bool isDelete, CancellationToken cancellationToken) { var instanceVersion = instance.Version; var instanceIsNewer = instanceVersion > Constants.CurrentVersion; @@ -68,21 +69,21 @@ async Task CanEditOrDelete(BaseService instance, bool isDelete) { var verb = isDelete ? "remove" : "edit"; var message = $"This instance version {instanceVersion} is newer than the installer version {Constants.CurrentVersion}. This installer can only {verb} instances with versions between {Constants.CurrentVersion.Major}.0.0 and {Constants.CurrentVersion}."; - await NotifyError(title, message).ConfigureAwait(false); + await NotifyError(title, message, cancellationToken).ConfigureAwait(false); return false; } if (installerOfDifferentMajor && !isDelete) { var message = $"This installer cannot edit instances created by a different major version. A {instanceVersion.Major}.* installer version greater or equal to {instanceVersion.Major}.{instanceVersion.Minor}.{instanceVersion.Patch} must be used instead."; - await NotifyError(title, message).ConfigureAwait(false); + await NotifyError(title, message, cancellationToken).ConfigureAwait(false); return false; } return true; } - async Task RabbitMqCheckIsOK(TransportInfo transport, SemanticVersion instanceVersion, bool isUpgrade) + async Task RabbitMqCheckIsOK(TransportInfo transport, SemanticVersion instanceVersion, bool isUpgrade, CancellationToken cancellationToken) { ArgumentNullException.ThrowIfNull(transport); @@ -99,18 +100,18 @@ async Task RabbitMqCheckIsOK(TransportInfo transport, SemanticVersion inst return true; } - return await PromptForRabbitMqCheck(isUpgrade).ConfigureAwait(false); + return await PromptForRabbitMqCheck(isUpgrade, cancellationToken).ConfigureAwait(false); } - public async Task CanUpgradeInstance(BaseService instance, bool forceUpgradeDb = false) + public async Task CanUpgradeInstance(BaseService instance, bool forceUpgradeDb = false, CancellationToken cancellationToken = default) { // Check for license - if (!await IsLicenseOk().ConfigureAwait(false)) + if (!await IsLicenseOk(cancellationToken).ConfigureAwait(false)) { return false; } - if (await OldVersionOfServiceControlInstalled().ConfigureAwait(false)) + if (await OldVersionOfServiceControlInstalled(cancellationToken).ConfigureAwait(false)) { return false; } @@ -119,7 +120,7 @@ public async Task CanUpgradeInstance(BaseService instance, bool forceUpgra var cantUpdateTransport = instance.TransportPackage.Removed && instance.TransportPackage.AutoMigrateTo is null; if (cantUpdateTransport) { - await NotifyForDeprecatedMessageTransport(instance.TransportPackage).ConfigureAwait(false); + await NotifyForDeprecatedMessageTransport(instance.TransportPackage, cancellationToken).ConfigureAwait(false); return false; } @@ -134,11 +135,11 @@ public async Task CanUpgradeInstance(BaseService instance, bool forceUpgra if (!forceUpgradeAllowed) { - await NotifyError("Cannot run the command", "Only ServiceControl 4.x primary instances that use RavenDB 3.5 persistence can be force-upgraded.").ConfigureAwait(false); + await NotifyError("Cannot run the command", "Only ServiceControl 4.x primary instances that use RavenDB 3.5 persistence can be force-upgraded.", cancellationToken).ConfigureAwait(false); return false; } - if (!await PromptToContinueWithForcedUpgrade().ConfigureAwait(false)) + if (!await PromptToContinueWithForcedUpgrade(cancellationToken).ConfigureAwait(false)) { return false; } @@ -149,7 +150,7 @@ public async Task CanUpgradeInstance(BaseService instance, bool forceUpgra if (!compatibleStorageEngine) { - await NotifyForIncompatibleStorageEngine(baseInstance).ConfigureAwait(false); + await NotifyForIncompatibleStorageEngine(baseInstance, cancellationToken).ConfigureAwait(false); return false; } } @@ -159,12 +160,12 @@ public async Task CanUpgradeInstance(BaseService instance, bool forceUpgra var upgradeInfo = UpgradeInfo.GetUpgradePathFor(instance.Version); if (upgradeInfo.HasIncompatibleVersion) { - await NotifyForIncompatibleUpgradeVersion(upgradeInfo).ConfigureAwait(false); + await NotifyForIncompatibleUpgradeVersion(upgradeInfo, cancellationToken).ConfigureAwait(false); return false; } } - if (!await RabbitMqCheckIsOK(instance.TransportPackage, instance.Version, isUpgrade: true).ConfigureAwait(false)) + if (!await RabbitMqCheckIsOK(instance.TransportPackage, instance.Version, isUpgrade: true, cancellationToken).ConfigureAwait(false)) { return false; } @@ -172,38 +173,38 @@ public async Task CanUpgradeInstance(BaseService instance, bool forceUpgra return true; } - async Task OldVersionOfServiceControlInstalled() + async Task OldVersionOfServiceControlInstalled(CancellationToken cancellationToken) { if (OldScmuCheck.OldVersionOfServiceControlInstalled(out var installedVersion)) { var message = $"An old version {installedVersion} of ServiceControl Management is installed, which will not work after installing new instances. Before installing ServiceControl 5 instances, you must either uninstall the {installedVersion} instance or update it to a 4.x version at least {OldScmuCheck.MinimumScmuVersion}."; - await NotifyError("Outdated Version Installed", message).ConfigureAwait(false); + await NotifyError("Outdated Version Installed", message, cancellationToken).ConfigureAwait(false); return true; } return false; } - async Task IsLicenseOk() + async Task IsLicenseOk(CancellationToken cancellationToken) { var licenseCheckResult = CheckLicenseIsValid(); if (!licenseCheckResult.Valid) { - await NotifyError("License Error", $"Upgrade could not continue due to an issue with the current license. {licenseCheckResult.Message}. Contact contact@particular.net").ConfigureAwait(false); + await NotifyError("License Error", $"Upgrade could not continue due to an issue with the current license. {licenseCheckResult.Message}. Contact contact@particular.net", cancellationToken).ConfigureAwait(false); return false; } return true; } - public async Task StopBecauseInstanceIsRunning(BaseService instance) + public async Task StopBecauseInstanceIsRunning(BaseService instance, CancellationToken cancellationToken = default) { if (instance.Service.Status == ServiceControllerStatus.Stopped) { return false; } - var proceed = await PromptToStopRunningInstance(instance).ConfigureAwait(false); + var proceed = await PromptToStopRunningInstance(instance, cancellationToken).ConfigureAwait(false); return !proceed; } diff --git a/src/ServiceControlInstaller.Engine/Validation/PathsValidator.cs b/src/ServiceControlInstaller.Engine/Validation/PathsValidator.cs index b24eec3886..edfa7a5c5f 100644 --- a/src/ServiceControlInstaller.Engine/Validation/PathsValidator.cs +++ b/src/ServiceControlInstaller.Engine/Validation/PathsValidator.cs @@ -4,6 +4,7 @@ namespace ServiceControlInstaller.Engine.Validation using System.Collections.Generic; using System.IO; using System.Linq; + using System.Threading; using System.Threading.Tasks; class PathsValidator @@ -52,12 +53,12 @@ public PathsValidator(IServiceControlPaths instance) paths = pathList.Where(p => !string.IsNullOrWhiteSpace(p.Path)).ToList(); } - public Task RunValidation(bool includeNewInstanceChecks) + public Task RunValidation(bool includeNewInstanceChecks, CancellationToken cancellationToken = default) { - return RunValidation(includeNewInstanceChecks, info => Task.FromResult(false)); + return RunValidation(includeNewInstanceChecks, (info, token) => Task.FromResult(false), cancellationToken); } - public async Task RunValidation(bool includeNewInstanceChecks, Func> promptToProceed) + public async Task RunValidation(bool includeNewInstanceChecks, Func> promptToProceed, CancellationToken cancellationToken = default) { try { @@ -69,7 +70,7 @@ public async Task RunValidation(bool includeNewInstanceChecks, Func RunValidation(bool includeNewInstanceChecks, Func CheckPathsAreEmpty(Func> promptToProceed) + async Task CheckPathsAreEmpty(Func> promptToProceed, CancellationToken cancellationToken) { foreach (var pathInfo in paths) { @@ -104,7 +109,7 @@ async Task CheckPathsAreEmpty(Func> promptToProceed) if (directory.EnumerateFileSystemInfos().Any()) { - var shouldProceed = await promptToProceed(pathInfo).ConfigureAwait(false); + var shouldProceed = await promptToProceed(pathInfo, cancellationToken).ConfigureAwait(false); if (!shouldProceed) { return true;