Fixed additional threading issues in PowerShell cmdlets to ensure WriteObject and WriteError are only called from the main thread

This commit is contained in:
GraceSolutions
2025-04-15 12:45:29 -04:00
parent 2b31bf23a9
commit 04b0fd5550
6 changed files with 323 additions and 257 deletions
@@ -34,30 +34,35 @@ namespace PSOPNSenseAPI.Cmdlets
/// <summary> /// <summary>
/// Processes the cmdlet /// Processes the cmdlet
/// </summary> /// </summary>
protected override void ProcessRecord() protected override void ProcessRecordInternal()
{
try
{ {
var cronService = new CronService(ApiClient, Logger); var cronService = new CronService(ApiClient, Logger);
var task = Task.Run(async () => await cronService.ToggleJobAsync(Uuid, false)); // Use our safe execution method
var result = task.GetAwaiter().GetResult(); var result = ExecuteAsyncTask(() => cronService.ToggleJobAsync(Uuid, false));
// Only continue if no exception occurred
if (ProcessingException != null || result == null)
{
return;
}
WriteVerbose($"Cron job {Uuid} disabled: {result.Result}"); WriteVerbose($"Cron job {Uuid} disabled: {result.Result}");
// Apply the changes if requested // Apply the changes if requested
if (Apply.IsPresent) if (Apply.IsPresent)
{ {
var applyTask = Task.Run(async () => await cronService.ApplyChangesAsync()); // Use our safe execution method
var applyResult = applyTask.GetAwaiter().GetResult(); var applyResult = ExecuteAsyncTask(() => cronService.ApplyChangesAsync());
// Only continue if no exception occurred
if (ProcessingException != null || applyResult == null)
{
return;
}
WriteVerbose($"Cron changes applied: {applyResult.Status}"); WriteVerbose($"Cron changes applied: {applyResult.Status}");
} }
} }
catch (Exception ex)
{
HandleException(ex);
}
}
} }
} }
@@ -105,15 +105,18 @@ namespace PSOPNSenseAPI.Cmdlets
/// <summary> /// <summary>
/// Processes the cmdlet /// Processes the cmdlet
/// </summary> /// </summary>
protected override void ProcessRecord() protected override void ProcessRecordInternal()
{
try
{ {
var tailscaleService = new TailscaleService(ApiClient, Logger); var tailscaleService = new TailscaleService(ApiClient, Logger);
// Check if the plugin is installed // Check if the plugin is installed
var isInstalledTask = Task.Run(async () => await tailscaleService.IsPluginInstalledAsync()); var isInstalled = ExecuteAsyncTask(() => tailscaleService.IsPluginInstalledAsync());
var isInstalled = isInstalledTask.GetAwaiter().GetResult();
// Only continue if no exception occurred
if (ProcessingException != null)
{
return;
}
if (!isInstalled) if (!isInstalled)
{ {
@@ -124,16 +127,17 @@ namespace PSOPNSenseAPI.Cmdlets
} }
WriteVerbose("Tailscale plugin is not installed. Installing..."); WriteVerbose("Tailscale plugin is not installed. Installing...");
var installTask = Task.Run(async () => await tailscaleService.InstallPluginAsync()); var installResult = ExecuteAsyncTask(() => tailscaleService.InstallPluginAsync());
var installResult = installTask.GetAwaiter().GetResult();
// Only continue if no exception occurred
if (ProcessingException != null)
{
return;
}
if (!installResult) if (!installResult)
{ {
WriteError(new ErrorRecord( ProcessingException = new Exception("Failed to install Tailscale plugin.");
new Exception("Failed to install Tailscale plugin."),
"TailscalePluginInstallFailed",
ErrorCategory.InvalidOperation,
null));
return; return;
} }
@@ -141,8 +145,15 @@ namespace PSOPNSenseAPI.Cmdlets
} }
// Get current settings // Get current settings
var settingsTask = Task.Run(async () => await tailscaleService.GetSettingsAsync()); var settingsResult = ExecuteAsyncTask(() => tailscaleService.GetSettingsAsync());
var currentSettings = settingsTask.GetAwaiter().GetResult().General;
// Only continue if no exception occurred
if (ProcessingException != null || settingsResult == null)
{
return;
}
var currentSettings = settingsResult.General;
// Process subnet routes if provided // Process subnet routes if provided
string routesToAdvertise = currentSettings.RoutesToAdvertise; string routesToAdvertise = currentSettings.RoutesToAdvertise;
@@ -181,14 +192,24 @@ namespace PSOPNSenseAPI.Cmdlets
} }
// Update settings // Update settings
var updateTask = Task.Run(async () => await tailscaleService.UpdateSettingsAsync(settings)); var updateResult = ExecuteAsyncTask(() => tailscaleService.UpdateSettingsAsync(settings));
var updateResult = updateTask.GetAwaiter().GetResult();
// Only continue if no exception occurred
if (ProcessingException != null || updateResult == null)
{
return;
}
WriteVerbose($"Tailscale settings updated: {updateResult.Result}"); WriteVerbose($"Tailscale settings updated: {updateResult.Result}");
// Get current status // Get current status
var statusTask = Task.Run(async () => await tailscaleService.GetStatusAsync()); var status = ExecuteAsyncTask(() => tailscaleService.GetStatusAsync());
var status = statusTask.GetAwaiter().GetResult();
// Only continue if no exception occurred
if (ProcessingException != null || status == null)
{
return;
}
// Start or restart the service if requested // Start or restart the service if requested
if (Restart.IsPresent || (Start.IsPresent && !status.Running)) if (Restart.IsPresent || (Start.IsPresent && !status.Running))
@@ -198,23 +219,40 @@ namespace PSOPNSenseAPI.Cmdlets
if (Restart.IsPresent) if (Restart.IsPresent)
{ {
WriteVerbose("Restarting Tailscale service..."); WriteVerbose("Restarting Tailscale service...");
var restartTask = Task.Run(async () => await tailscaleService.RestartServiceAsync()); var restartResult = ExecuteAsyncTask(() => tailscaleService.RestartServiceAsync());
var restartResult = restartTask.GetAwaiter().GetResult();
// Only continue if no exception occurred
if (ProcessingException != null || restartResult == null)
{
return;
}
WriteVerbose($"Tailscale service restarted: {restartResult.Status}"); WriteVerbose($"Tailscale service restarted: {restartResult.Status}");
} }
} }
else if (Start.IsPresent) else if (Start.IsPresent)
{ {
WriteVerbose("Starting Tailscale service..."); WriteVerbose("Starting Tailscale service...");
var startTask = Task.Run(async () => await tailscaleService.StartServiceAsync()); var startResult = ExecuteAsyncTask(() => tailscaleService.StartServiceAsync());
var startResult = startTask.GetAwaiter().GetResult();
// Only continue if no exception occurred
if (ProcessingException != null || startResult == null)
{
return;
}
WriteVerbose($"Tailscale service started: {startResult.Status}"); WriteVerbose($"Tailscale service started: {startResult.Status}");
} }
} }
// Get updated status // Get updated status
statusTask = Task.Run(async () => await tailscaleService.GetStatusAsync()); status = ExecuteAsyncTask(() => tailscaleService.GetStatusAsync());
status = statusTask.GetAwaiter().GetResult();
// Only continue if no exception occurred
if (ProcessingException != null || status == null)
{
return;
}
// Create result object // Create result object
var result = new PSObject(); var result = new PSObject();
@@ -226,10 +264,5 @@ namespace PSOPNSenseAPI.Cmdlets
WriteObject(result); WriteObject(result);
} }
catch (Exception ex)
{
HandleException(ex);
}
}
} }
} }
@@ -33,15 +33,18 @@ namespace PSOPNSenseAPI.Cmdlets
/// <summary> /// <summary>
/// Processes the cmdlet /// Processes the cmdlet
/// </summary> /// </summary>
protected override void ProcessRecord() protected override void ProcessRecordInternal()
{
try
{ {
var tailscaleService = new TailscaleService(ApiClient, Logger); var tailscaleService = new TailscaleService(ApiClient, Logger);
// Check if the plugin is installed // Check if the plugin is installed
var isInstalledTask = Task.Run(async () => await tailscaleService.IsPluginInstalledAsync()); var isInstalled = ExecuteAsyncTask(() => tailscaleService.IsPluginInstalledAsync());
var isInstalled = isInstalledTask.GetAwaiter().GetResult();
// Only continue if no exception occurred
if (ProcessingException != null)
{
return;
}
if (!isInstalled) if (!isInstalled)
{ {
@@ -58,8 +61,13 @@ namespace PSOPNSenseAPI.Cmdlets
} }
// Get the status // Get the status
var statusTask = Task.Run(async () => await tailscaleService.GetStatusAsync()); var status = ExecuteAsyncTask(() => tailscaleService.GetStatusAsync());
var status = statusTask.GetAwaiter().GetResult();
// Only continue if no exception occurred
if (ProcessingException != null || status == null)
{
return;
}
var result = new PSObject(); var result = new PSObject();
result.Properties.Add(new PSNoteProperty("PluginInstalled", true)); result.Properties.Add(new PSNoteProperty("PluginInstalled", true));
@@ -70,8 +78,13 @@ namespace PSOPNSenseAPI.Cmdlets
// Get interfaces if requested // Get interfaces if requested
if (IncludeInterfaces.IsPresent) if (IncludeInterfaces.IsPresent)
{ {
var interfacesTask = Task.Run(async () => await tailscaleService.GetInterfacesAsync()); var interfaces = ExecuteAsyncTask(() => tailscaleService.GetInterfacesAsync());
var interfaces = interfacesTask.GetAwaiter().GetResult();
// Only continue if no exception occurred
if (ProcessingException != null || interfaces == null)
{
return;
}
result.Properties.Add(new PSNoteProperty("Interfaces", interfaces.Interfaces)); result.Properties.Add(new PSNoteProperty("Interfaces", interfaces.Interfaces));
} }
@@ -79,18 +92,18 @@ namespace PSOPNSenseAPI.Cmdlets
// Get settings if requested // Get settings if requested
if (IncludeSettings.IsPresent) if (IncludeSettings.IsPresent)
{ {
var settingsTask = Task.Run(async () => await tailscaleService.GetSettingsAsync()); var settings = ExecuteAsyncTask(() => tailscaleService.GetSettingsAsync());
var settings = settingsTask.GetAwaiter().GetResult();
// Only continue if no exception occurred
if (ProcessingException != null || settings == null)
{
return;
}
result.Properties.Add(new PSNoteProperty("Settings", settings.General)); result.Properties.Add(new PSNoteProperty("Settings", settings.General));
} }
WriteObject(result); WriteObject(result);
} }
catch (Exception ex)
{
HandleException(ex);
}
}
} }
} }
@@ -53,9 +53,7 @@ namespace PSOPNSenseAPI.Cmdlets
/// <summary> /// <summary>
/// Processes the cmdlet /// Processes the cmdlet
/// </summary> /// </summary>
protected override void ProcessRecord() protected override void ProcessRecordInternal()
{
try
{ {
var dnsService = new DNSService(ApiClient, Logger); var dnsService = new DNSService(ApiClient, Logger);
@@ -67,26 +65,33 @@ namespace PSOPNSenseAPI.Cmdlets
Enabled = Enabled.IsPresent ? "1" : "0" Enabled = Enabled.IsPresent ? "1" : "0"
}; };
var createTask = Task.Run(async () => await dnsService.CreateDNSForwardingHostAsync(host)); // Use our safe execution method
var createResult = createTask.GetAwaiter().GetResult(); var createResult = ExecuteAsyncTask(() => dnsService.CreateDNSForwardingHostAsync(host));
// Only continue if no exception occurred
if (ProcessingException != null || createResult == null)
{
return;
}
WriteVerbose($"Created DNS forwarding host with UUID {createResult.Uuid}"); WriteVerbose($"Created DNS forwarding host with UUID {createResult.Uuid}");
// Apply changes if requested // Apply changes if requested
if (Apply.IsPresent) if (Apply.IsPresent)
{ {
var applyTask = Task.Run(async () => await dnsService.ApplyDNSChangesAsync()); // Use our safe execution method
var applyResult = applyTask.GetAwaiter().GetResult(); var applyResult = ExecuteAsyncTask(() => dnsService.ApplyDNSChangesAsync());
// Only continue if no exception occurred
if (ProcessingException != null || applyResult == null)
{
return;
}
WriteVerbose($"DNS changes applied: {applyResult.Status}"); WriteVerbose($"DNS changes applied: {applyResult.Status}");
} }
WriteObject(createResult.Uuid); WriteObject(createResult.Uuid);
} }
catch (Exception ex)
{
HandleException(ex);
}
}
} }
} }
@@ -93,15 +93,20 @@ namespace PSOPNSenseAPI.Cmdlets
/// <summary> /// <summary>
/// Processes the cmdlet /// Processes the cmdlet
/// </summary> /// </summary>
protected override void ProcessRecord() protected override void ProcessRecordInternal()
{
try
{ {
var cronService = new CronService(ApiClient, Logger); var cronService = new CronService(ApiClient, Logger);
// First, get the current job // First, get the current job
var getTask = Task.Run(async () => await cronService.GetJobAsync(Uuid)); var getResult = ExecuteAsyncTask(() => cronService.GetJobAsync(Uuid));
var currentJob = getTask.GetAwaiter().GetResult().Job;
// Only continue if no exception occurred
if (ProcessingException != null || getResult == null)
{
return;
}
var currentJob = getResult.Job;
// Create the updated job // Create the updated job
var job = new CronJobConfig var job = new CronJobConfig
@@ -135,24 +140,29 @@ namespace PSOPNSenseAPI.Cmdlets
} }
// Update the job // Update the job
var updateTask = Task.Run(async () => await cronService.UpdateJobAsync(Uuid, job)); var updateResult = ExecuteAsyncTask(() => cronService.UpdateJobAsync(Uuid, job));
var updateResult = updateTask.GetAwaiter().GetResult();
// Only continue if no exception occurred
if (ProcessingException != null || updateResult == null)
{
return;
}
WriteVerbose($"Cron job {Uuid} updated: {updateResult.Result}"); WriteVerbose($"Cron job {Uuid} updated: {updateResult.Result}");
// Apply the changes if requested // Apply the changes if requested
if (Apply.IsPresent) if (Apply.IsPresent)
{ {
var applyTask = Task.Run(async () => await cronService.ApplyChangesAsync()); var applyResult = ExecuteAsyncTask(() => cronService.ApplyChangesAsync());
var applyResult = applyTask.GetAwaiter().GetResult();
// Only continue if no exception occurred
if (ProcessingException != null || applyResult == null)
{
return;
}
WriteVerbose($"Cron changes applied: {applyResult.Status}"); WriteVerbose($"Cron changes applied: {applyResult.Status}");
} }
} }
catch (Exception ex)
{
HandleException(ex);
}
}
} }
} }
+1 -1
View File
@@ -3,7 +3,7 @@
RootModule = 'lib\PSOPNSenseAPI.dll' RootModule = 'lib\PSOPNSenseAPI.dll'
# Version number of this module. # Version number of this module.
ModuleVersion = '2025.04.15.1234' ModuleVersion = '2025.04.15.1245'
# Supported PSEditions # Supported PSEditions
CompatiblePSEditions = @('Desktop', 'Core') CompatiblePSEditions = @('Desktop', 'Core')