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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ public TimeSpan? MonitorFrequency
{
get
{
return this.Parameters.GetTimeSpanValue(nameof(this.MonitorFrequency));
return this.Parameters.ContainsKey(nameof(this.MonitorFrequency))
? this.Parameters.GetTimeSpanValue(nameof(this.MonitorFrequency))
: null;
}

protected set
Expand All @@ -71,7 +73,9 @@ public long? MonitorIterations
{
get
{
return this.Parameters.GetValue<long>(nameof(this.MonitorIterations));
return this.Parameters.ContainsKey(nameof(this.MonitorIterations))
? this.Parameters.GetValue<long>(nameof(this.MonitorIterations))
: null;
}

protected set
Expand Down Expand Up @@ -104,7 +108,9 @@ public TimeSpan? MonitorWarmupPeriod
{
get
{
return this.Parameters.GetTimeSpanValue(nameof(this.MonitorWarmupPeriod));
return this.Parameters.ContainsKey(nameof(this.MonitorWarmupPeriod))
? this.Parameters.GetTimeSpanValue(nameof(this.MonitorWarmupPeriod))
: null;
}

protected set
Expand Down Expand Up @@ -163,29 +169,73 @@ protected override Task ExecuteAsync(EventContext telemetryContext, Cancellation
protected abstract Task ExecuteMonitorAsync(EventContext telemetryContext, CancellationToken cancellationToken);

/// <summary>
/// Executes the monitor on an interval as defined by the 'MonitorFrequency' parameter.
/// Executes the monitor on an interval as defined by the 'MonitorFrequency', 'MonitorIterations',
/// or 'MonitorStrategy' parameters.
/// </summary>
/// <param name="telemetryContext">Provides information to include with telemetry.</param>
/// <param name="cancellationToken">A token that can be used to cancel the operations.</param>
protected Task ExecuteMonitorOnIntervalAsync(EventContext telemetryContext, CancellationToken cancellationToken)
protected async Task ExecuteMonitorOnIntervalAsync(EventContext telemetryContext, CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
try
{
try
long iterations = 0;
while (!cancellationToken.IsCancellationRequested)
{
try
{
iterations++;
if (this.MonitorStrategy != null)
{
if (iterations <= 1)
{
switch (this.MonitorStrategy)
{
case VirtualClient.MonitorStrategy.Once:
case VirtualClient.MonitorStrategy.OnceAtBeginAndEnd:
await this.ExecuteMonitorAsync(telemetryContext, cancellationToken);
break;
}
}
}
else
{
if (this.IsIterationComplete(iterations))
{
break;
}

await this.ExecuteMonitorAsync(telemetryContext, cancellationToken);
}
}
catch (OperationCanceledException)
{
// Expected on ctrl-c or a cancellation is requested.
}
catch (Exception exc)
{
// Do not let the monitor operations crash the application.
this.Logger.LogErrorMessage(exc, telemetryContext, LogLevel.Warning);
}
finally
{
await this.WaitAsync(this.MonitorFrequency ?? TimeSpan.Zero, cancellationToken);
}
}
catch (OperationCanceledException)
{
// Expected on ctrl-c or a cancellation is requested.
}
catch (Exception exc)

if (this.MonitorStrategy == VirtualClient.MonitorStrategy.OnceAtBeginAndEnd)
{
// Do not let the monitor operations crash the application.
this.Logger.LogErrorMessage(exc, telemetryContext, LogLevel.Warning);
await this.ExecuteMonitorAsync(telemetryContext, CancellationToken.None);
}
}

return Task.CompletedTask;
catch (OperationCanceledException)
{
// Expected on ctrl-c or a cancellation is requested.
}
catch (Exception exc)
{
// Do not let the monitor operations crash the application.
this.Logger.LogErrorMessage(exc, telemetryContext, LogLevel.Warning);
}
}

/// <summary>
Expand Down
77 changes: 7 additions & 70 deletions src/VirtualClient/VirtualClient.Monitors/ExecuteCommandMonitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ namespace VirtualClient.Dependencies
/// package installed.
/// </summary>
[SupportedPlatforms("linux-arm64,linux-x64,win-arm64,win-x64")]
public class ExecuteCommandMonitor : VirtualClientIntervalBasedMonitor
public class ExecuteCommandMonitor : VirtualClientMonitorComponent
{
private const int MaxOutputLength = 125000;
private ProcessManager processManager;
Expand Down Expand Up @@ -138,78 +138,15 @@ public string WorkingDirectory
}

/// <summary>
/// Execute the monitor to run the command on intervals.
/// Executes the command(s) as the monitor operation.
/// </summary>
protected override Task ExecuteAsync(EventContext telemetryContext, CancellationToken cancellationToken)
protected override Task ExecuteMonitorAsync(EventContext telemetryContext, CancellationToken cancellationToken)
{
// All background monitor ExecuteAsync methods should be either 'async' or should use a Task.Run() if running a 'while' loop or the
// logic will block without returning. Monitors are typically expected to be fire-and-forget.
telemetryContext.AddContext("command", SensitiveData.ObscureSecrets(this.Command));
telemetryContext.AddContext("workingDirectory", SensitiveData.ObscureSecrets(this.WorkingDirectory));
telemetryContext.AddContext("platforms", string.Join(VirtualClientComponent.CommonDelimiters.First(), this.SupportedPlatforms));

return Task.Run(async () =>
{
try
{
telemetryContext.AddContext("command", SensitiveData.ObscureSecrets(this.Command));
telemetryContext.AddContext("workingDirectory", SensitiveData.ObscureSecrets(this.WorkingDirectory));
telemetryContext.AddContext("platforms", string.Join(VirtualClientComponent.CommonDelimiters.First(), this.SupportedPlatforms));

await this.WaitAsync(this.MonitorWarmupPeriod, cancellationToken);

int iterations = 0;
while (!cancellationToken.IsCancellationRequested)
{
try
{
iterations++;
if (this.MonitorStrategy != null)
{
if (iterations <= 1)
{
switch (this.MonitorStrategy)
{
case VirtualClient.MonitorStrategy.Once:
case VirtualClient.MonitorStrategy.OnceAtBeginAndEnd:
await this.ExecuteCommandAsync(telemetryContext, cancellationToken);
break;
}
}
}
else
{
if (this.IsIterationComplete(iterations))
{
break;
}

await this.ExecuteCommandAsync(telemetryContext, cancellationToken);
}
}
catch (Exception exc)
{
// Do not let the monitor operations crash the application.
this.Logger.LogErrorMessage(exc, telemetryContext, LogLevel.Warning);
}
finally
{
await this.WaitAsync(this.MonitorFrequency, cancellationToken);
}
}

if (this.MonitorStrategy == VirtualClient.MonitorStrategy.OnceAtBeginAndEnd)
{
await this.ExecuteCommandAsync(telemetryContext, CancellationToken.None);
}
}
catch (OperationCanceledException)
{
// Expected on ctrl-c or a cancellation is requested.
}
catch (Exception exc)
{
// Do not let the monitor operations crash the application.
this.Logger.LogErrorMessage(exc, telemetryContext, LogLevel.Warning);
}
});
return this.ExecuteCommandAsync(telemetryContext, cancellationToken);
}

/// <summary>
Expand Down
Loading