Skip to content

Commit b2b0984

Browse files
committed
Add ExecuteCommandMonitor to source so that we can run any command and capture the output in a log file.
1 parent 8701195 commit b2b0984

5 files changed

Lines changed: 416 additions & 2 deletions

File tree

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
2.0.24
1+
2.0.25

src/VirtualClient/VirtualClient.Dependencies/ExecuteCommand.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ namespace VirtualClient.Dependencies
2121
/// Executes a command on the system with the working directory set to a
2222
/// package installed.
2323
/// </summary>
24+
[SupportedPlatforms("linux-arm64,linux-x64,win-arm64,win-x64")]
2425
public class ExecuteCommand : VirtualClientComponent
2526
{
2627
/// <summary>
@@ -137,7 +138,7 @@ protected static bool TryGetCommandParts(string fullCommand, out string command,
137138
/// </summary>
138139
protected override async Task ExecuteAsync(EventContext telemetryContext, CancellationToken cancellationToken)
139140
{
140-
telemetryContext.AddContext("command", this.Command);
141+
telemetryContext.AddContext("command", SensitiveData.ObscureSecrets(this.Command));
141142
telemetryContext.AddContext("workingDirectory", this.WorkingDirectory);
142143
telemetryContext.AddContext("platforms", string.Join(VirtualClientComponent.CommonDelimiters.First(), this.SupportedPlatforms));
143144

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
namespace VirtualClient.Monitors
5+
{
6+
using System;
7+
using System.Collections;
8+
using System.Collections.Generic;
9+
using System.Runtime.InteropServices;
10+
using System.Threading;
11+
using System.Threading.Tasks;
12+
using NUnit.Framework;
13+
using VirtualClient.Common.Telemetry;
14+
using VirtualClient.Dependencies;
15+
16+
[TestFixture]
17+
[Category("Unit")]
18+
internal class ExecuteCommandMonitorTests : MockFixture
19+
{
20+
public void SetupDefaults(PlatformID platform, Architecture architecture = Architecture.X64)
21+
{
22+
this.Setup(platform, architecture);
23+
this.Parameters[nameof(ExecuteCommandMonitor.Command)] = "anycommand";
24+
this.Parameters[nameof(ExecuteCommandMonitor.MonitorEventType)] = "any_event_type";
25+
this.Parameters[nameof(ExecuteCommandMonitor.MonitorEventSource)] = "any_event_source";
26+
}
27+
28+
[Test]
29+
[TestCase("anycommand", "anycommand", null)]
30+
[TestCase("anycommand ", "anycommand", null)]
31+
[TestCase("anycommand --argument=value", "anycommand", "--argument=value")]
32+
[TestCase("/home/user/anycommand", "/home/user/anycommand", null)]
33+
[TestCase("/home/user/anycommand --argument=value --argument2 value2", "/home/user/anycommand", "--argument=value --argument2 value2")]
34+
[TestCase("\"/home/user/dir with space/anycommand\" --argument=value --argument2 value2", "\"/home/user/dir with space/anycommand\"", "--argument=value --argument2 value2")]
35+
[TestCase("sudo anycommand", "sudo", "anycommand")]
36+
[TestCase("sudo /home/user/anycommand", "sudo", "/home/user/anycommand")]
37+
[TestCase("sudo /home/user/anycommand --argument=value --argument2 value2", "sudo", "/home/user/anycommand --argument=value --argument2 value2")]
38+
[TestCase("sudo \"/home/user/dir with space/anycommand\" --argument=value --argument2 value2", "sudo", "\"/home/user/dir with space/anycommand\" --argument=value --argument2 value2")]
39+
public async Task ExecuteCommandMonitorExecutesTheExpectedCommandOnUnixSystems(string fullCommand, string expectedCommand, string expectedCommandArguments)
40+
{
41+
this.SetupDefaults(PlatformID.Unix);
42+
43+
using (TestExecuteCommandMonitor command = new TestExecuteCommandMonitor(this))
44+
{
45+
command.Parameters[nameof(ExecuteCommandMonitor.Command)] = fullCommand;
46+
47+
this.ProcessManager.OnProcessCreated = (process) =>
48+
{
49+
Assert.AreEqual($"{expectedCommand} {expectedCommandArguments}".Trim(), process.FullCommand());
50+
};
51+
52+
await command.ExecuteCommandAsync(EventContext.None, CancellationToken.None);
53+
}
54+
}
55+
56+
[Test]
57+
[TestCase("anycommand.exe", "anycommand.exe", null)]
58+
[TestCase("anycommand.exe ", "anycommand.exe", null)]
59+
[TestCase("anycommand.exe --argument=value --argument2 value2", "anycommand.exe", "--argument=value --argument2 value2")]
60+
[TestCase("C:\\Users\\User\\anycommand.exe", "C:\\Users\\User\\anycommand.exe", null)]
61+
[TestCase("C:\\Users\\User\\anycommand.exe --argument=value --argument2 value2", "C:\\Users\\User\\anycommand.exe", "--argument=value --argument2 value2")]
62+
[TestCase("\"C:\\Users\\User\\Dir With Space\\anycommand.exe\" --argument=value --argument2 value2", "\"C:\\Users\\User\\Dir With Space\\anycommand.exe\"", "--argument=value --argument2 value2")]
63+
public async Task ExecuteCommandMonitorExecutesTheExpectedCommandOnWindowsSystems(string fullCommand, string expectedCommand, string expectedCommandArguments)
64+
{
65+
this.SetupDefaults(PlatformID.Win32NT);
66+
67+
using (TestExecuteCommandMonitor command = new TestExecuteCommandMonitor(this))
68+
{
69+
command.Parameters[nameof(ExecuteCommandMonitor.Command)] = fullCommand;
70+
71+
this.ProcessManager.OnProcessCreated = (process) =>
72+
{
73+
Assert.AreEqual($"{expectedCommand} {expectedCommandArguments}".Trim(), process.FullCommand());
74+
};
75+
76+
await command.ExecuteCommandAsync(EventContext.None, CancellationToken.None);
77+
}
78+
}
79+
80+
private class TestExecuteCommandMonitor : ExecuteCommandMonitor
81+
{
82+
public TestExecuteCommandMonitor(MockFixture mockFixture)
83+
: base(mockFixture?.Dependencies, mockFixture?.Parameters)
84+
{
85+
}
86+
87+
public new Task InitializeAsync(EventContext telemetryContext, CancellationToken cancellationToken)
88+
{
89+
return base.InitializeAsync(telemetryContext, cancellationToken);
90+
}
91+
92+
public new Task ExecuteCommandAsync(EventContext telemetryContext, CancellationToken cancellationToken)
93+
{
94+
return base.ExecuteCommandAsync(telemetryContext, cancellationToken);
95+
}
96+
}
97+
}
98+
}

0 commit comments

Comments
 (0)