diff --git a/src/VirtualClient/VirtualClient.Actions.UnitTests/MLPerf/MLPerfExecutorTests.cs b/src/VirtualClient/VirtualClient.Actions.UnitTests/MLPerf/MLPerfExecutorTests.cs index e5a5362c97..cb94dcfa9b 100644 --- a/src/VirtualClient/VirtualClient.Actions.UnitTests/MLPerf/MLPerfExecutorTests.cs +++ b/src/VirtualClient/VirtualClient.Actions.UnitTests/MLPerf/MLPerfExecutorTests.cs @@ -345,9 +345,9 @@ public TestMLPerfExecutor(IServiceCollection dependencies, IDictionary(process.StandardError.ToString(), ErrorReason.WorkloadUnexpectedAnomaly); + } + } + + using (IProcessProxy process = await this.ExecuteCommandAsync( + "unzip", + "silesia.zip -d silesia", + this.Compressor7zipDirectory, + telemetryContext, + cancellationToken, + runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfErrored(process.StandardError.ToString(), ErrorReason.WorkloadUnexpectedAnomaly); + } + } } state.Compressor7zipStateInitialized = true; @@ -155,37 +178,6 @@ private void CaptureMetrics(IProcessProxy process, EventContext telemetryContext telemetryContext); } - private async Task ExecuteCommandAsync(string pathToExe, string commandLineArguments, string workingDirectory, CancellationToken cancellationToken) - { - if (!cancellationToken.IsCancellationRequested) - { - this.Logger.LogTraceMessage($"Executing process '{pathToExe}' '{commandLineArguments}' at directory '{workingDirectory}'."); - - EventContext telemetryContext = EventContext.Persisted() - .AddContext("command", pathToExe) - .AddContext("commandArguments", commandLineArguments); - - await this.Logger.LogMessageAsync($"{nameof(Compression7zipExecutor)}.ExecuteProcess", telemetryContext, async () => - { - DateTime start = DateTime.Now; - using (IProcessProxy process = this.systemManager.ProcessManager.CreateElevatedProcess(this.Platform, pathToExe, commandLineArguments, workingDirectory)) - { - this.CleanupTasks.Add(() => process.SafeKill()); - await process.StartAndWaitAsync(cancellationToken) - .ConfigureAwait(false); - - if (!cancellationToken.IsCancellationRequested) - { - await this.LogProcessDetailsAsync(process, telemetryContext) - .ConfigureAwait(false); - - process.ThrowIfErrored(errorReason: ErrorReason.WorkloadFailed); - } - } - }).ConfigureAwait(false); - } - } - private string GetCommandLineArguments() { string inputFilesOrDirs = string.IsNullOrWhiteSpace(this.InputFilesOrDirs) diff --git a/src/VirtualClient/VirtualClient.Actions/ASPNET/AspNetBenchExecutor.cs b/src/VirtualClient/VirtualClient.Actions/ASPNET/AspNetBenchExecutor.cs index 4243bf0f86..150f3a8df6 100644 --- a/src/VirtualClient/VirtualClient.Actions/ASPNET/AspNetBenchExecutor.cs +++ b/src/VirtualClient/VirtualClient.Actions/ASPNET/AspNetBenchExecutor.cs @@ -5,6 +5,7 @@ namespace VirtualClient.Actions { using System; using System.Collections.Generic; + using System.Diagnostics; using System.IO.Abstractions; using System.Runtime.InteropServices; using System.Threading; @@ -101,7 +102,7 @@ public string DotNetSdkPackageName /// protected override async Task ExecuteAsync(EventContext telemetryContext, CancellationToken cancellationToken) { - Task serverTask = this.StartAspNetServerAsync(cancellationToken); + Task serverTask = this.StartAspNetServerAsync(telemetryContext, cancellationToken); await this.RunBombardierAsync(telemetryContext, cancellationToken) .ConfigureAwait(false); @@ -150,8 +151,15 @@ await this.systemManagement.MakeFileExecutableAsync(this.bombardierFilePath, thi // ~/vc/packages/dotnet/dotnet build -c Release -p:BenchmarksTargetFramework=net8.0 // Build the aspnetbenchmark project string buildArgument = $"build -c Release -p:BenchmarksTargetFramework={this.TargetFramework}"; - await this.ExecuteCommandAsync(this.dotnetExePath, buildArgument, this.aspnetBenchDirectory, cancellationToken) - .ConfigureAwait(false); + + using (IProcessProxy process = await this.ExecuteCommandAsync(this.dotnetExePath, buildArgument, this.aspnetBenchDirectory, telemetryContext, cancellationToken, runElevated: true) + .ConfigureAwait(false)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfErrored(process.StandardError.ToString(), ErrorReason.WorkloadUnexpectedAnomaly); + } + } // "C:\Users\vcvmadmin\Benchmarks\src\Benchmarks\bin\Release\net8.0\Benchmarks.dll" this.aspnetBenchDllPath = this.Combine( @@ -192,7 +200,7 @@ private void CaptureMetrics(IProcessProxy process, EventContext telemetryContext } } - private Task StartAspNetServerAsync(CancellationToken cancellationToken) + private async Task StartAspNetServerAsync(EventContext telemetryContext, CancellationToken cancellationToken) { // Example: // dotnet \Benchmarks.dll --nonInteractive true --scenarios json --urls http://localhost:5000 --server Kestrel --kestrelTransport Sockets --protocol http @@ -202,7 +210,13 @@ private Task StartAspNetServerAsync(CancellationToken cancellationToken) string headers = @"--header ""Accept: application/json,text/html;q=0.9,application/xhtml+xml;q=0.9,application/xml;q=0.8,*/*;q=0.7"" --header ""Connection: keep-alive"""; this.serverArgument = $"{this.aspnetBenchDllPath} {options} {headers}"; - return this.ExecuteCommandAsync(this.dotnetExePath, this.serverArgument, this.aspnetBenchDirectory, cancellationToken, isServer: true); + using (IProcessProxy process = await this.ExecuteCommandAsync(this.dotnetExePath, this.serverArgument, this.aspnetBenchDirectory, telemetryContext, cancellationToken, runElevated: true) + .ConfigureAwait(false)) + { + this.killServer = () => process.SafeKill(); + } + + return; } private async Task RunBombardierAsync(EventContext telemetryContext, CancellationToken cancellationToken) @@ -218,47 +232,11 @@ private async Task RunBombardierAsync(EventContext telemetryContext, Cancellatio { if (!cancellationToken.IsCancellationRequested) { - await this.LogProcessDetailsAsync(process, telemetryContext, "AspNetBench", logToFile: true); - - process.ThrowIfWorkloadFailed(); + process.ThrowIfWorkloadFailed(process.StandardError.ToString()); this.CaptureMetrics(process, telemetryContext); } } } } - - private async Task ExecuteCommandAsync(string pathToExe, string commandLineArguments, string workingDirectory, CancellationToken cancellationToken, bool isServer = false) - { - if (!cancellationToken.IsCancellationRequested) - { - this.Logger.LogTraceMessage($"Executing process '{pathToExe}' '{commandLineArguments}' at directory '{workingDirectory}'."); - - EventContext telemetryContext = EventContext.Persisted() - .AddContext("command", pathToExe) - .AddContext("commandArguments", commandLineArguments); - - using (IProcessProxy process = this.systemManagement.ProcessManager.CreateElevatedProcess(this.Platform, pathToExe, commandLineArguments, workingDirectory)) - { - if (isServer) - { - this.killServer = () => process.SafeKill(); - } - - this.CleanupTasks.Add(() => process.SafeKill()); - await process.StartAndWaitAsync(cancellationToken).ConfigureAwait(false); - - if (!cancellationToken.IsCancellationRequested) - { - await this.LogProcessDetailsAsync(process, telemetryContext); - - if (!isServer) - { - // We will kill the server at the end, exit code is -1, and we don't want it to log as failure. - process.ThrowIfWorkloadFailed(); - } - } - } - } - } } } \ No newline at end of file diff --git a/src/VirtualClient/VirtualClient.Actions/DCGMI/DCGMIExecutor.cs b/src/VirtualClient/VirtualClient.Actions/DCGMI/DCGMIExecutor.cs index 8d249b4e34..8e95ee9f16 100644 --- a/src/VirtualClient/VirtualClient.Actions/DCGMI/DCGMIExecutor.cs +++ b/src/VirtualClient/VirtualClient.Actions/DCGMI/DCGMIExecutor.cs @@ -165,19 +165,39 @@ protected override async Task InitializeAsync(EventContext telemetryContext, Can if (this.Subsystem == DCGMIExecutor.Diagnostics) { - await this.ExecuteCommandAsync(@"nvidia-smi -pm 1", Environment.CurrentDirectory, cancellationToken) - .ConfigureAwait(false); + using (IProcessProxy process = await this.ExecuteCommandAsync( + @"nvidia-smi -pm 1", + Environment.CurrentDirectory, + telemetryContext, + cancellationToken, + runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfErrored(process.StandardError.ToString(), ErrorReason.WorkloadUnexpectedAnomaly); + } + } State installationState = await this.stateManager.GetStateAsync(nameof(DCGMIExecutor), cancellationToken) .ConfigureAwait(false); if (installationState == null) { - await this.ExecuteCommandAsync(@"nvidia-smi -e 1", Environment.CurrentDirectory, cancellationToken) - .ConfigureAwait(false); + using (IProcessProxy process = await this.ExecuteCommandAsync( + @"nvidia-smi -e 1", + Environment.CurrentDirectory, + telemetryContext, + cancellationToken, + runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfErrored(process.StandardError.ToString(), ErrorReason.WorkloadUnexpectedAnomaly); + } + } await this.stateManager.SaveStateAsync(nameof(DCGMIExecutor), new State(), cancellationToken) - .ConfigureAwait(false); + .ConfigureAwait(false); this.RequestReboot(); } @@ -185,8 +205,18 @@ await this.ExecuteCommandAsync(@"nvidia-smi -e 1", Environment.Cu if (this.Subsystem == DCGMIExecutor.Health) { - await this.ExecuteCommandAsync(@"dcgmi health -s mpi", Environment.CurrentDirectory, cancellationToken) - .ConfigureAwait(false); + using (IProcessProxy process = await this.ExecuteCommandAsync( + @"dcgmi health -s mpi", + Environment.CurrentDirectory, + telemetryContext, + cancellationToken, + runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfErrored(process.StandardError.ToString(), ErrorReason.WorkloadUnexpectedAnomaly); + } + } } } @@ -245,57 +275,27 @@ await this.ExecuteDCGMIProfTesterSubsystemAsync(telemetryContext, cancellationTo } } - /// - /// Executes the commands. - /// - /// Command that needs to be executed - /// The directory where we want to execute the command - /// A token that can be used to cancel the operation. - /// Output of the workload command. - protected async Task ExecuteCommandAsync(string command, string workingDirectory, CancellationToken cancellationToken) - where TExecutor : VirtualClientComponent - { - string output = string.Empty; - if (!cancellationToken.IsCancellationRequested) - { - this.Logger.LogTraceMessage($"Executing process '{command}' at directory '{workingDirectory}'."); - - EventContext telemetryContext = EventContext.Persisted() - .AddContext("command", command); - - await this.Logger.LogMessageAsync($"{typeof(TExecutor).Name}.ExecuteProcess", telemetryContext, async () => - { - using (IProcessProxy process = this.systemManagement.ProcessManager.CreateElevatedProcess(this.Platform, command, null, workingDirectory)) - { - this.CleanupTasks.Add(() => process.SafeKill()); - process.RedirectStandardOutput = true; - await process.StartAndWaitAsync(cancellationToken).ConfigureAwait(false); - - if (!cancellationToken.IsCancellationRequested) - { - await this.LogProcessDetailsAsync(process, telemetryContext, "DCGMI", logToFile: true); - process.ThrowIfWorkloadFailed(); - } - - output = process.StandardOutput.ToString(); - } - - return output; - }).ConfigureAwait(false); - } - - return output; - } - private async Task ExecuteDCGMIDiagnosticsSubsystemAsync(EventContext telemetryContext, CancellationToken cancellationToken) { using (BackgroundOperations profiling = BackgroundOperations.BeginProfiling(this, cancellationToken)) { string command = $"dcgmi diag -r {this.Level} -j"; DateTime startTime = DateTime.UtcNow; - - string results = await this.ExecuteCommandAsync(command, Environment.CurrentDirectory, cancellationToken) - .ConfigureAwait(false); + string results = string.Empty; + + using (IProcessProxy process = await this.ExecuteCommandAsync( + command, + Environment.CurrentDirectory, + telemetryContext, + cancellationToken, + runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfErrored(process.StandardError.ToString(), ErrorReason.WorkloadUnexpectedAnomaly); + results = process.StandardOutput.ToString(); + } + } this.CaptureWorkloadResultsAsync(results, command, startTime, DateTime.UtcNow, telemetryContext, cancellationToken); } @@ -307,9 +307,21 @@ private async Task ExecuteDCGMIDiscoverySubsystemAsync(EventContext telemetryCon { string command = "dcgmi discovery -l"; DateTime startTime = DateTime.UtcNow; - - string results = await this.ExecuteCommandAsync(command, Environment.CurrentDirectory, cancellationToken) - .ConfigureAwait(false); + string results = string.Empty; + + using (IProcessProxy process = await this.ExecuteCommandAsync( + command, + Environment.CurrentDirectory, + telemetryContext, + cancellationToken, + runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfErrored(process.StandardError.ToString(), ErrorReason.WorkloadUnexpectedAnomaly); + results = process.StandardOutput.ToString(); + } + } this.CaptureWorkloadResultsAsync(results, command, startTime, DateTime.UtcNow, telemetryContext, cancellationToken); } @@ -321,8 +333,21 @@ private async Task ExecuteDCGMIFieldGroupSubsystemAsync(EventContext telemetryCo { string command = "dcgmi fieldgroup -l"; DateTime startTime = DateTime.UtcNow; - string results = await this.ExecuteCommandAsync(command, Environment.CurrentDirectory, cancellationToken) - .ConfigureAwait(false); + string results = string.Empty; + + using (IProcessProxy process = await this.ExecuteCommandAsync( + command, + Environment.CurrentDirectory, + telemetryContext, + cancellationToken, + runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfErrored(process.StandardError.ToString(), ErrorReason.WorkloadUnexpectedAnomaly); + results = process.StandardOutput.ToString(); + } + } this.CaptureWorkloadResultsAsync(results, command, startTime, DateTime.UtcNow, telemetryContext, cancellationToken); } @@ -334,8 +359,21 @@ private async Task ExecuteDCGMIGroupSubsystemAsync(EventContext telemetryContext { string command = "dcgmi group -l"; DateTime startTime = DateTime.UtcNow; - string results = await this.ExecuteCommandAsync(command, Environment.CurrentDirectory, cancellationToken) - .ConfigureAwait(false); + string results = string.Empty; + + using (IProcessProxy process = await this.ExecuteCommandAsync( + command, + Environment.CurrentDirectory, + telemetryContext, + cancellationToken, + runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfErrored(process.StandardError.ToString(), ErrorReason.WorkloadUnexpectedAnomaly); + results = process.StandardOutput.ToString(); + } + } this.CaptureWorkloadResultsAsync(results, command, startTime, DateTime.UtcNow, telemetryContext, cancellationToken); } @@ -347,8 +385,21 @@ private async Task ExecuteDCGMIHealthSubsystemAsync(EventContext telemetryContex { string command = "dcgmi health -c -j"; DateTime startTime = DateTime.UtcNow; - string results = await this.ExecuteCommandAsync(command, Environment.CurrentDirectory, cancellationToken) - .ConfigureAwait(false); + string results = string.Empty; + + using (IProcessProxy process = await this.ExecuteCommandAsync( + command, + Environment.CurrentDirectory, + telemetryContext, + cancellationToken, + runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfErrored(process.StandardError.ToString(), ErrorReason.WorkloadUnexpectedAnomaly); + results = process.StandardOutput.ToString(); + } + } this.CaptureWorkloadResultsAsync(results, command, startTime, DateTime.UtcNow, telemetryContext, cancellationToken); } @@ -360,8 +411,21 @@ private async Task ExecuteDCGMIModulesSubsystemAsync(EventContext telemetryConte { string command = "dcgmi modules -l"; DateTime startTime = DateTime.UtcNow; - string results = await this.ExecuteCommandAsync(command, Environment.CurrentDirectory, cancellationToken) - .ConfigureAwait(false); + string results = string.Empty; + + using (IProcessProxy process = await this.ExecuteCommandAsync( + command, + Environment.CurrentDirectory, + telemetryContext, + cancellationToken, + runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfErrored(process.StandardError.ToString(), ErrorReason.WorkloadUnexpectedAnomaly); + results = process.StandardOutput.ToString(); + } + } this.CaptureWorkloadResultsAsync(results, command, startTime, DateTime.UtcNow, telemetryContext, cancellationToken); } @@ -371,21 +435,40 @@ private async Task ExecuteDCGMIProfTesterSubsystemAsync(EventContext telemetryCo { using (BackgroundOperations profiling = BackgroundOperations.BeginProfiling(this, cancellationToken)) { - List> tasksList = new List>(); string dcgmproftestercommand = $"/usr/bin/dcgmproftester{(int)Convert.ToDouble(this.LinuxCudaVersion)} --no-dcgm-validation -t {this.FieldIDProftester} -d 10"; string dmoncommand = $"dcgmi dmon -e {this.ListOfFieldIDsDmon} -c 15"; DateTime startTime = DateTime.UtcNow; - tasksList.Add(Task.Run(async () => await this.ExecuteCommandAsync(dcgmproftestercommand, Environment.CurrentDirectory, cancellationToken) - .ConfigureAwait(false))); + string dcgmiproftesterresults = string.Empty; + string dcgmidmonresults = string.Empty; - tasksList.Add(Task.Run(async () => await this.ExecuteCommandAsync(dmoncommand, Environment.CurrentDirectory, cancellationToken) - .ConfigureAwait(false))); - - string[] outputresults = await Task.WhenAll(tasksList); + using (IProcessProxy process = await this.ExecuteCommandAsync( + dcgmproftestercommand, + Environment.CurrentDirectory, + telemetryContext, + cancellationToken, + runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfErrored(process.StandardError.ToString(), ErrorReason.WorkloadUnexpectedAnomaly); + dcgmiproftesterresults = process.StandardOutput.ToString(); + } + } - string dcgmiproftesterresults = outputresults[0]; - string dcgmidmonresults = outputresults[1]; + using (IProcessProxy process = await this.ExecuteCommandAsync( + dmoncommand, + Environment.CurrentDirectory, + telemetryContext, + cancellationToken, + runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfErrored(process.StandardError.ToString(), ErrorReason.WorkloadUnexpectedAnomaly); + dcgmidmonresults = process.StandardOutput.ToString(); + } + } this.CaptureDmonResultsAsync(dcgmidmonresults, dmoncommand, this.StartTime, DateTime.Now, telemetryContext, cancellationToken); this.CaptureWorkloadResultsAsync(dcgmiproftesterresults, dcgmproftestercommand, startTime, DateTime.UtcNow, telemetryContext, cancellationToken); diff --git a/src/VirtualClient/VirtualClient.Actions/DeathStarBench/DeathStarBenchClientExecutor.cs b/src/VirtualClient/VirtualClient.Actions/DeathStarBench/DeathStarBenchClientExecutor.cs index 781e755ca0..ebb3675e7b 100644 --- a/src/VirtualClient/VirtualClient.Actions/DeathStarBench/DeathStarBenchClientExecutor.cs +++ b/src/VirtualClient/VirtualClient.Actions/DeathStarBench/DeathStarBenchClientExecutor.cs @@ -144,7 +144,7 @@ await this.ServerApiClient.PollForExpectedStateAsync( finally { this.Logger.LogTraceMessage("Client Stopping docker services..."); - await this.StopDockerAsync(CancellationToken.None); + await this.StopDockerAsync(telemetryContext, CancellationToken.None); this.Logger.LogTraceMessage("Deleting states..."); await this.DeleteWorkloadStateAsync(telemetryContext, cancellationToken); @@ -206,7 +206,7 @@ private Task ExecuteClientWorkloadAsync(EventContext telemetryContext, Cancellat await this.ResetServerAsync(telemetryContext, cancellationToken); // stop docker services at client side. - await this.StopDockerAsync(cancellationToken); + await this.StopDockerAsync(telemetryContext, cancellationToken); // 4) Request the server to start the next workload. // =========================================================================== @@ -244,7 +244,7 @@ await this.ServerApiClient.PollForExpectedStateAsync( finally { // Stop docker services at client side(If multiVM get out of swarm network and single VM stop the services). - await this.StopDockerAsync(CancellationToken.None); + await this.StopDockerAsync(telemetryContext, CancellationToken.None); this.Logger.LogTraceMessage("Synchronization: Wait for server to stop workload..."); @@ -286,12 +286,30 @@ private async Task ExecuteClientAsync(EventContext telemetryContext, Cancellatio { string joinSwarmCommand = await this.GetJoinSwarmCommand(cancellationToken); - await this.ExecuteCommandAsync(joinSwarmCommand, this.ServiceDirectory, cancellationToken); - await this.WaitAsync(DeathStarBenchExecutor.ServerWarmUpTime, cancellationToken); + using (IProcessProxy process = await this.ExecuteCommandAsync(joinSwarmCommand, this.ServiceDirectory, telemetryContext, cancellationToken, runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfErrored(process.StandardError.ToString(), ErrorReason.WorkloadUnexpectedAnomaly); + } + } } - await this.ExecuteCommandAsync("make clean", this.MakefileDirectory, cancellationToken); - await this.ExecuteCommandAsync("make", this.MakefileDirectory, cancellationToken); + using (IProcessProxy process = await this.ExecuteCommandAsync("make clean", this.MakefileDirectory, telemetryContext, cancellationToken, runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfErrored(process.StandardError.ToString(), ErrorReason.WorkloadUnexpectedAnomaly); + } + } + + using (IProcessProxy process = await this.ExecuteCommandAsync("make", this.MakefileDirectory, telemetryContext, cancellationToken, runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfErrored(process.StandardError.ToString(), ErrorReason.WorkloadUnexpectedAnomaly); + } + } foreach (var action in this.actionScript[this.ServiceName].Keys) { diff --git a/src/VirtualClient/VirtualClient.Actions/DeathStarBench/DeathStarBenchExecutor.cs b/src/VirtualClient/VirtualClient.Actions/DeathStarBench/DeathStarBenchExecutor.cs index af7276a7d3..56c10e7b2f 100644 --- a/src/VirtualClient/VirtualClient.Actions/DeathStarBench/DeathStarBenchExecutor.cs +++ b/src/VirtualClient/VirtualClient.Actions/DeathStarBench/DeathStarBenchExecutor.cs @@ -171,7 +171,7 @@ await this.SystemManager.MakeFileExecutableAsync( } - await this.InstallDependenciesAsync(cancellationToken) + await this.InstallDependenciesAsync(telemetryContext, cancellationToken) .ConfigureAwait(); } @@ -270,12 +270,12 @@ protected void OnInstructionsReceived(object sender, JObject instructions) { this.Logger.LogTraceMessage($"Synchronization: Stopping all workloads..."); - await this.StopDockerAsync(CancellationToken.None); + await this.StopDockerAsync(relatedContext, CancellationToken.None); await this.DeleteWorkloadStateAsync(relatedContext, cancellationToken); } else if (workloadInstructions.Type == InstructionsType.ClientServerStartExecution) { - await this.StopDockerAsync(CancellationToken.None); + await this.StopDockerAsync(relatedContext, CancellationToken.None); await this.DeleteWorkloadStateAsync(relatedContext, cancellationToken); this.Parameters[nameof(this.ServiceName)] = workloadInstructions.Properties[nameof(this.ServiceName)]; @@ -398,49 +398,6 @@ protected virtual VirtualClientComponent CreateWorkloadServer() return new DeathStarBenchServerExecutor(this.Dependencies, this.Parameters); } - /// - /// Executes the commands. - /// - /// Command that needs to be executed - /// The directory where we want to execute the command - /// A token that can be used to cancel the operation. - /// Output of the workload command. - protected async Task ExecuteCommandAsync(string command, string workingDirectory, CancellationToken cancellationToken) - { - string output = string.Empty; - if (!cancellationToken.IsCancellationRequested) - { - this.Logger.LogTraceMessage($"Executing process '{command}' at directory '{workingDirectory}'."); - - EventContext telemetryContext = EventContext.Persisted() - .AddContext("command", command); - - await this.Logger.LogMessageAsync($"{this.TypeName}.ExecuteProcess", telemetryContext, async () => - { - using (IProcessProxy process = this.SystemManager.ProcessManager.CreateElevatedProcess(this.Platform, command, null, workingDirectory)) - { - this.CleanupTasks.Add(() => process.SafeKill()); - await process.StartAndWaitAsync(cancellationToken) - .ConfigureAwait(); - - if (!cancellationToken.IsCancellationRequested) - { - await this.LogProcessDetailsAsync(process, telemetryContext) - .ConfigureAwait(); - - process.ThrowIfErrored(errorReason: ErrorReason.WorkloadFailed); - } - - output = process.StandardOutput.ToString(); - } - - return output; - }).ConfigureAwait(); - } - - return output; - } - /// /// Resets the file to empty file. /// @@ -461,30 +418,47 @@ protected void ResetFile(string filePath, EventContext telemetryContext) /// /// Stopping docker services after a service is executed for freeing up the ports for next service to be executed. /// + /// Provides context information that will be captured with telemetry events. /// A token that can be used to cancel the operation. /// - protected async Task StopDockerAsync(CancellationToken cancellationToken) + protected async Task StopDockerAsync(EventContext telemetryContext, CancellationToken cancellationToken) { if (this.IsMultiRoleLayout()) { string isSwarmNodeScriptPath = this.PlatformSpecifics.Combine(this.ScriptsDirectory, "isSwarmNode.sh"); - string isSwarmNodeCommand = "bash " + isSwarmNodeScriptPath; - string isSwarmNode = await this.ExecuteCommandAsync(isSwarmNodeCommand, this.PackageDirectory, cancellationToken) - .ConfigureAwait(); - - if (isSwarmNode.Trim('\n') == "true") + string isSwarmNode = string.Empty; + + using (IProcessProxy process = await this.ExecuteCommandAsync( + "bash", + isSwarmNodeScriptPath, + this.PackageDirectory, + telemetryContext, + cancellationToken, + runElevated: true)) { - this.Logger.LogTraceMessage($"{isSwarmNode.Trim('\n')} is equal to true"); - if (this.IsInRole(ClientRole.Client)) + if (!cancellationToken.IsCancellationRequested) { - await this.ExecuteCommandAsync("docker swarm leave", this.ServiceDirectory, cancellationToken) - .ConfigureAwait(); + process.ThrowIfErrored(process.StandardError.ToString(), ErrorReason.WorkloadUnexpectedAnomaly); + isSwarmNode = process.StandardOutput.ToString(); } + } - if (this.IsInRole(ClientRole.Server)) + if (isSwarmNode.Trim('\n') == "true") + { + this.Logger.LogTraceMessage($"{isSwarmNode.Trim('\n')} is equal to true"); + string swarmLeaveCommand = this.IsInRole(ClientRole.Server) ? "docker swarm leave --force" : "docker swarm leave"; + + using (IProcessProxy process = await this.ExecuteCommandAsync( + swarmLeaveCommand, + this.ServiceDirectory, + telemetryContext, + cancellationToken, + runElevated: true)) { - await this.ExecuteCommandAsync("docker swarm leave --force", this.ServiceDirectory, cancellationToken) - .ConfigureAwait(); + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfErrored(process.StandardError.ToString(), ErrorReason.WorkloadUnexpectedAnomaly); + } } } @@ -492,24 +466,60 @@ await this.ExecuteCommandAsync("docker swarm leave --force", this.ServiceDirecto do { await this.WaitAsync(TimeSpan.FromSeconds(30), cancellationToken); - isSwarmNode = await this.ExecuteCommandAsync(isSwarmNodeCommand, this.PackageDirectory, cancellationToken) - .ConfigureAwait(); + + using (IProcessProxy process = await this.ExecuteCommandAsync( + "bash", + isSwarmNodeScriptPath, + this.PackageDirectory, + telemetryContext, + cancellationToken, + runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfErrored(process.StandardError.ToString(), ErrorReason.WorkloadUnexpectedAnomaly); + isSwarmNode = process.StandardOutput.ToString(); + } + } } while (isSwarmNode.Trim('\n') == "true"); } else { string dockerProcessCountCommand = @"bash -c ""docker ps | wc -l"""; - string numberOfDockerProcess = await this.ExecuteCommandAsync(dockerProcessCountCommand, this.PackageDirectory, cancellationToken) - .ConfigureAwait(); + string numberOfDockerProcess = string.Empty; + + using (IProcessProxy process = await this.ExecuteCommandAsync( + dockerProcessCountCommand, + this.PackageDirectory, + telemetryContext, + cancellationToken, + runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfErrored(process.StandardError.ToString(), ErrorReason.WorkloadUnexpectedAnomaly); + numberOfDockerProcess = process.StandardOutput.ToString(); + } + } if (int.Parse(numberOfDockerProcess) > 1) { string stopContainersScriptPath = this.PlatformSpecifics.Combine(this.ScriptsDirectory, "stopContainers.sh"); - string stopContainersCommand = "bash " + stopContainersScriptPath; - await this.ExecuteCommandAsync(stopContainersCommand, this.ServiceDirectory, cancellationToken) - .ConfigureAwait(); + using (IProcessProxy process = await this.ExecuteCommandAsync( + "bash", + stopContainersScriptPath, + this.PackageDirectory, + telemetryContext, + cancellationToken, + runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfErrored(process.StandardError.ToString(), ErrorReason.WorkloadUnexpectedAnomaly); + } + } } } } @@ -537,35 +547,87 @@ private void InitializeApiClients() /// /// Install required dependencies like dockerCompose, pip installations. /// + /// Provides context information that will be captured with telemetry events. /// A token that can be used to cancel the operation. /// - private async Task InstallDependenciesAsync(CancellationToken cancellationToken) + private async Task InstallDependenciesAsync(EventContext telemetryContext, CancellationToken cancellationToken) { string dockerComposeScriptPath = this.PlatformSpecifics.Combine(this.ScriptsDirectory, "dockerComposeScript.sh"); - string dockerComposeCommand = "bash " + dockerComposeScriptPath; - await this.ExecuteCommandAsync(dockerComposeCommand, this.PackageDirectory, cancellationToken) - .ConfigureAwait(); + + using (IProcessProxy process = await this.ExecuteCommandAsync( + "bash", + dockerComposeScriptPath, + this.PackageDirectory, + telemetryContext, + cancellationToken, + runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfErrored(process.StandardError.ToString(), ErrorReason.WorkloadUnexpectedAnomaly); + } + } string dockerComposeFilePath = "/usr/local/bin/docker-compose"; await this.SystemManager.MakeFileExecutableAsync(dockerComposeFilePath, this.Platform, cancellationToken) .ConfigureAwait(); string pipUpgradeCommand = "python3 -m pip install -U pip"; - await this.ExecuteCommandAsync(pipUpgradeCommand, this.PackageDirectory, cancellationToken) - .ConfigureAwait(); + using (IProcessProxy process = await this.ExecuteCommandAsync( + pipUpgradeCommand, + this.PackageDirectory, + telemetryContext, + cancellationToken, + runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfErrored(process.StandardError.ToString(), ErrorReason.WorkloadUnexpectedAnomaly); + } + } string setupToolsUpgradeCommand = "python3 -m pip install -U setuptools"; - await this.ExecuteCommandAsync(setupToolsUpgradeCommand, this.PackageDirectory, cancellationToken) - .ConfigureAwait(); + using (IProcessProxy process = await this.ExecuteCommandAsync( + setupToolsUpgradeCommand, + this.PackageDirectory, + telemetryContext, + cancellationToken, + runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfErrored(process.StandardError.ToString(), ErrorReason.WorkloadUnexpectedAnomaly); + } + } // python3-pip installs pip3 and not pip, better to leave it on python which version of pip to use string pipInstallPackagesCommand = "-H python3 -m pip install aiohttp asyncio"; - await this.ExecuteCommandAsync(pipInstallPackagesCommand, this.PackageDirectory, cancellationToken) - .ConfigureAwait(); + using (IProcessProxy process = await this.ExecuteCommandAsync( + pipInstallPackagesCommand, + this.PackageDirectory, + telemetryContext, + cancellationToken, + runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfErrored(process.StandardError.ToString(), ErrorReason.WorkloadUnexpectedAnomaly); + } + } string luarocksCommand = "luarocks install luasocket"; - await this.ExecuteCommandAsync(luarocksCommand, this.PackageDirectory, cancellationToken) - .ConfigureAwait(); + using (IProcessProxy process = await this.ExecuteCommandAsync( + luarocksCommand, + this.PackageDirectory, + telemetryContext, + cancellationToken, + runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfErrored(process.StandardError.ToString(), ErrorReason.WorkloadUnexpectedAnomaly); + } + } } diff --git a/src/VirtualClient/VirtualClient.Actions/DeathStarBench/DeathStarBenchServerExecutor.cs b/src/VirtualClient/VirtualClient.Actions/DeathStarBench/DeathStarBenchServerExecutor.cs index c4017f4ddd..85b7e22f81 100644 --- a/src/VirtualClient/VirtualClient.Actions/DeathStarBench/DeathStarBenchServerExecutor.cs +++ b/src/VirtualClient/VirtualClient.Actions/DeathStarBench/DeathStarBenchServerExecutor.cs @@ -10,6 +10,7 @@ namespace VirtualClient.Actions using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json.Linq; + using VirtualClient.Common; using VirtualClient.Common.Contracts; using VirtualClient.Common.Extensions; using VirtualClient.Common.Telemetry; @@ -63,7 +64,7 @@ protected override Task ExecuteAsync(EventContext telemetryContext, Cancellation using (this.ServerCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken)) { await this.DeleteStateAsync(telemetryContext, cancellationToken); - await this.StopDockerAsync(this.ServerCancellationSource.Token); + await this.StopDockerAsync(telemetryContext, this.ServerCancellationSource.Token); await this.ExecuteServerAsync(telemetryContext, cancellationToken); await this.SaveStateAsync(this.ServiceName, telemetryContext, cancellationToken); @@ -94,27 +95,48 @@ protected async Task ExecuteServerAsync(EventContext telemetryContext, Cancellat this.ServerIpAddress = clientInstance.IPAddress; string swarmHostCommand = $@"bash -c ""docker swarm init --advertise-addr {this.ServerIpAddress} | grep ' docker swarm join' >> token.txt"""; - await this.ExecuteCommandAsync(swarmHostCommand, this.ServiceDirectory, cancellationToken) - .ConfigureAwait(); + + using (IProcessProxy process = await this.ExecuteCommandAsync(swarmHostCommand, this.ServiceDirectory, telemetryContext, cancellationToken, runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfErrored(process.StandardError.ToString(), ErrorReason.WorkloadUnexpectedAnomaly); + } + } await this.SetOrUpdateClientCommandLine(cancellationToken) .ConfigureAwait(); if (!string.Equals(this.ServiceName, DeathStarBenchExecutor.MediaMicroservices, StringComparison.OrdinalIgnoreCase)) { - await this.ExecuteCommandAsync(@$"bash -c ""docker stack deploy --compose-file=docker-compose-swarm.yml {this.ServiceName}""", this.ServiceDirectory, cancellationToken) - .ConfigureAwait(); + using (IProcessProxy process = await this.ExecuteCommandAsync(@$"bash -c ""docker stack deploy --compose-file=docker-compose-swarm.yml {this.ServiceName}""", this.ServiceDirectory, telemetryContext, cancellationToken, runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfErrored(process.StandardError.ToString(), ErrorReason.WorkloadUnexpectedAnomaly); + } + } } else { - await this.ExecuteCommandAsync(@$"bash -c ""docker stack deploy --compose-file=docker-compose.yml {this.ServiceName}""", this.ServiceDirectory, cancellationToken) - .ConfigureAwait(); + using (IProcessProxy process = await this.ExecuteCommandAsync(@$"bash -c ""docker stack deploy --compose-file=docker-compose.yml {this.ServiceName}""", this.ServiceDirectory, telemetryContext, cancellationToken, runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfErrored(process.StandardError.ToString(), ErrorReason.WorkloadUnexpectedAnomaly); + } + } } } else { - await this.ExecuteCommandAsync($"docker-compose -f docker-compose.yml up -d", this.ServiceDirectory, cancellationToken) - .ConfigureAwait(); + using (IProcessProxy process = await this.ExecuteCommandAsync($"docker-compose -f docker-compose.yml up -d", this.ServiceDirectory, telemetryContext, cancellationToken, runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfErrored(process.StandardError.ToString(), ErrorReason.WorkloadUnexpectedAnomaly); + } + } } // wait for docker services to be up and running. @@ -123,13 +145,23 @@ await this.WaitAsync(DeathStarBenchExecutor.ServerWarmUpTime, cancellationToken) if (string.Equals(this.ServiceName, DeathStarBenchExecutor.MediaMicroservices, StringComparison.OrdinalIgnoreCase)) { - await this.ExecuteCommandAsync("python3 scripts/write_movie_info.py -c datasets/tmdb/casts.json -m datasets/tmdb/movies.json", this.ServiceDirectory, cancellationToken) - .ConfigureAwait(); + using (IProcessProxy process = await this.ExecuteCommandAsync("python3 scripts/write_movie_info.py -c datasets/tmdb/casts.json -m datasets/tmdb/movies.json", this.ServiceDirectory, telemetryContext, cancellationToken, runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfErrored(process.StandardError.ToString(), ErrorReason.WorkloadUnexpectedAnomaly); + } + } } else if (string.Equals(this.ServiceName, DeathStarBenchExecutor.SocialNetwork, StringComparison.OrdinalIgnoreCase)) { - await this.ExecuteCommandAsync(@$"bash -c ""python3 scripts/init_social_graph.py --graph={this.GraphType} --limit=1000""", this.ServiceDirectory, cancellationToken) - .ConfigureAwait(); + using (IProcessProxy process = await this.ExecuteCommandAsync(@$"bash -c ""python3 scripts/init_social_graph.py --graph={this.GraphType} --limit=1000""", this.ServiceDirectory, telemetryContext, cancellationToken, runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfErrored(process.StandardError.ToString(), ErrorReason.WorkloadUnexpectedAnomaly); + } + } } } catch diff --git a/src/VirtualClient/VirtualClient.Actions/Graph500/Graph500Executor.cs b/src/VirtualClient/VirtualClient.Actions/Graph500/Graph500Executor.cs index fbe3b10ab1..1af77a2b65 100644 --- a/src/VirtualClient/VirtualClient.Actions/Graph500/Graph500Executor.cs +++ b/src/VirtualClient/VirtualClient.Actions/Graph500/Graph500Executor.cs @@ -108,45 +108,22 @@ protected override async Task ExecuteAsync(EventContext telemetryContext, Cancel { using (BackgroundOperations profiling = BackgroundOperations.BeginProfiling(this, cancellationToken)) { - await this.ExecuteCommandAsync("make", this.CompilerFlags, this.PackageDirectory, cancellationToken); - - using (IProcessProxy process = await this.ExecuteCommandAsync(this.ExecutableFilePath, this.Scale + " " + this.EdgeFactor, this.PackageDirectory, telemetryContext, cancellationToken)) + using (IProcessProxy process = await this.ExecuteCommandAsync("make", this.CompilerFlags, this.PackageDirectory, telemetryContext, cancellationToken)) { if (!cancellationToken.IsCancellationRequested) { - await this.LogProcessDetailsAsync(process, telemetryContext, "Graph500", logToFile: true); - - process.ThrowIfWorkloadFailed(); - this.CaptureMetrics(process, telemetryContext, cancellationToken); + process.ThrowIfErrored(process.StandardError.ToString(), ErrorReason.WorkloadUnexpectedAnomaly); } } - } - } - private async Task ExecuteCommandAsync(string pathToExe, string commandLineArguments, string workingDirectory, CancellationToken cancellationToken) - { - if (!cancellationToken.IsCancellationRequested) - { - this.Logger.LogTraceMessage($"Executing process '{pathToExe}' '{commandLineArguments}' at directory '{workingDirectory}'."); - - EventContext telemetryContext = EventContext.Persisted() - .AddContext("command", pathToExe) - .AddContext("commandArguments", commandLineArguments); - - await this.Logger.LogMessageAsync($"{nameof(Graph500Executor)}.ExecuteProcess", telemetryContext, async () => + using (IProcessProxy process = await this.ExecuteCommandAsync(this.ExecutableFilePath, this.Scale + " " + this.EdgeFactor, this.PackageDirectory, telemetryContext, cancellationToken)) { - using (IProcessProxy process = this.systemManagement.ProcessManager.CreateProcess(pathToExe, commandLineArguments, workingDirectory)) + if (!cancellationToken.IsCancellationRequested) { - this.CleanupTasks.Add(() => process.SafeKill()); - await process.StartAndWaitAsync(cancellationToken).ConfigureAwait(); - - if (!cancellationToken.IsCancellationRequested) - { - await this.LogProcessDetailsAsync(process, telemetryContext).ConfigureAwait(); - process.ThrowIfWorkloadFailed(); - } + process.ThrowIfWorkloadFailed(); + this.CaptureMetrics(process, telemetryContext, cancellationToken); } - }).ConfigureAwait(); + } } } diff --git a/src/VirtualClient/VirtualClient.Actions/Gzip/GzipExecutor.cs b/src/VirtualClient/VirtualClient.Actions/Gzip/GzipExecutor.cs index 67a349299e..24a346d823 100644 --- a/src/VirtualClient/VirtualClient.Actions/Gzip/GzipExecutor.cs +++ b/src/VirtualClient/VirtualClient.Actions/Gzip/GzipExecutor.cs @@ -118,48 +118,39 @@ await this.CheckDistroSupportAsync(telemetryContext, cancellationToken) // Choose default file for compression and decompression if files/dirs are not provided. if (string.IsNullOrWhiteSpace(this.InputFilesOrDirs)) { - await this.ExecuteCommandAsync("wget", $"https://sun.aei.polsl.pl//~sdeor/corpus/silesia.zip", this.GzipDirectory, cancellationToken); - await this.ExecuteCommandAsync("unzip", "silesia.zip -d silesia", this.GzipDirectory, cancellationToken); - } - - state.GzipStateInitialized = true; - } - - await this.stateManager.SaveStateAsync($"{nameof(GzipState)}", state, cancellationToken); - } - - private async Task ExecuteCommandAsync(string pathToExe, string commandLineArguments, string workingDirectory, CancellationToken cancellationToken) - { - string output = string.Empty; - - if (!cancellationToken.IsCancellationRequested) - { - this.Logger.LogTraceMessage($"Executing process '{pathToExe}' '{commandLineArguments}' at directory '{workingDirectory}'."); - - EventContext telemetryContext = EventContext.Persisted() - .AddContext("command", pathToExe) - .AddContext("commandArguments", commandLineArguments); - - await this.Logger.LogMessageAsync($"{nameof(GzipExecutor)}.ExecuteProcess", telemetryContext, async () => - { - DateTime start = DateTime.Now; - using (IProcessProxy process = this.systemManager.ProcessManager.CreateElevatedProcess(this.Platform, pathToExe, commandLineArguments, workingDirectory)) + using (IProcessProxy process = await this.ExecuteCommandAsync( + "wget", + $"https://sun.aei.polsl.pl//~sdeor/corpus/silesia.zip", + this.GzipDirectory, + telemetryContext, + cancellationToken, + runElevated: true)) { - this.CleanupTasks.Add(() => process.SafeKill()); - await process.StartAndWaitAsync(cancellationToken).ConfigureAwait(); - if (!cancellationToken.IsCancellationRequested) { - await this.LogProcessDetailsAsync(process, telemetryContext); - process.ThrowIfWorkloadFailed(); + process.ThrowIfErrored(process.StandardError.ToString(), ErrorReason.WorkloadUnexpectedAnomaly); } + } - output = process.StandardOutput.ToString(); + using (IProcessProxy process = await this.ExecuteCommandAsync( + "unzip", + "silesia.zip -d silesia", + this.GzipDirectory, + telemetryContext, + cancellationToken, + runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfErrored(process.StandardError.ToString(), ErrorReason.WorkloadUnexpectedAnomaly); + } } - }).ConfigureAwait(); + } + + state.GzipStateInitialized = true; } - return output; + await this.stateManager.SaveStateAsync($"{nameof(GzipState)}", state, cancellationToken); } private void CaptureMetrics(IProcessProxy process, string commandArguments, EventContext telemetryContext) diff --git a/src/VirtualClient/VirtualClient.Actions/LAPACK/LAPACKExecutor.cs b/src/VirtualClient/VirtualClient.Actions/LAPACK/LAPACKExecutor.cs index 0cd24aeaf4..3bb08be550 100644 --- a/src/VirtualClient/VirtualClient.Actions/LAPACK/LAPACKExecutor.cs +++ b/src/VirtualClient/VirtualClient.Actions/LAPACK/LAPACKExecutor.cs @@ -90,9 +90,13 @@ protected override async Task ExecuteAsync(EventContext telemetryContext, Cancel { if (this.Platform == PlatformID.Unix) { - // Run make to generate all object files for fortran subroutines. - await this.ExecuteCommandAsync("make", null, this.packageDirectory, cancellationToken) - .ConfigureAwait(false); + using (IProcessProxy process = await this.ExecuteCommandAsync("make", this.packageDirectory, telemetryContext, cancellationToken, runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfWorkloadFailed(); + } + } // Delete results file that gets generated. if (this.fileSystem.File.Exists(this.ResultsFilePath)) @@ -104,20 +108,20 @@ await this.ExecuteCommandAsync("make", null, this.packageDirectory, cancellation { if (!cancellationToken.IsCancellationRequested) { - if (process.IsErrored()) - { - await this.LogProcessDetailsAsync(process, telemetryContext, "LAPACK", logToFile: true); - process.ThrowIfWorkloadFailed(); - } - + process.ThrowIfWorkloadFailed(); await this.CaptureMetricsAsync(process, this.ResultsFilePath, telemetryContext, cancellationToken); } } } else if (this.Platform == PlatformID.Win32NT) { - await this.ExecuteCygwinBashAsync("./cmakescript.sh", this.packageDirectory, this.cygwinPackageDirectory, telemetryContext, cancellationToken) - .ConfigureAwait(false); + using (IProcessProxy process = await this.ExecuteCygwinBashAsync("./cmakescript.sh", this.packageDirectory, this.cygwinPackageDirectory, telemetryContext, cancellationToken)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfWorkloadFailed(); + } + } // Delete results file that gets generated. if (this.fileSystem.File.Exists(this.ResultsFilePath)) @@ -129,12 +133,7 @@ await this.ExecuteCygwinBashAsync("./cmakescript.sh", this.packageDirectory, thi { if (!cancellationToken.IsCancellationRequested) { - if (process.IsErrored()) - { - await this.LogProcessDetailsAsync(process, telemetryContext, "LAPACK", logToFile: true); - process.ThrowIfWorkloadFailed(); - } - + process.ThrowIfWorkloadFailed(); await this.CaptureMetricsAsync(process, this.ResultsFilePath, telemetryContext, cancellationToken); } } @@ -142,35 +141,6 @@ await this.ExecuteCygwinBashAsync("./cmakescript.sh", this.packageDirectory, thi } } - private async Task ExecuteCommandAsync(string pathToExe, string commandLineArguments, string workingDirectory, CancellationToken cancellationToken) - { - if (!cancellationToken.IsCancellationRequested) - { - this.Logger.LogTraceMessage($"Executing process '{pathToExe}' '{commandLineArguments}' at directory '{workingDirectory}'."); - - EventContext telemetryContext = EventContext.Persisted() - .AddContext("command", pathToExe) - .AddContext("commandArguments", commandLineArguments); - - await this.Logger.LogMessageAsync($"{nameof(LAPACKExecutor)}.ExecuteProcess", telemetryContext, async () => - { - using (IProcessProxy process = this.systemManagement.ProcessManager.CreateElevatedProcess(this.Platform, pathToExe, commandLineArguments, workingDirectory)) - { - this.CleanupTasks.Add(() => process.SafeKill()); - await process.StartAndWaitAsync(cancellationToken).ConfigureAwait(); - - if (!cancellationToken.IsCancellationRequested) - { - this.LogProcessDetailsAsync(process, telemetryContext) - .ConfigureAwait(); - - process.ThrowIfErrored(errorReason: ErrorReason.WorkloadFailed); - } - } - }).ConfigureAwait(); - } - } - private async Task CaptureMetricsAsync(IProcessProxy process, string resultsFilePath, EventContext telemetryContext, CancellationToken cancellationToken) { if (!cancellationToken.IsCancellationRequested) diff --git a/src/VirtualClient/VirtualClient.Actions/Lzbench/LzbenchExecutor.cs b/src/VirtualClient/VirtualClient.Actions/Lzbench/LzbenchExecutor.cs index e7461c8573..5ffb852e23 100644 --- a/src/VirtualClient/VirtualClient.Actions/Lzbench/LzbenchExecutor.cs +++ b/src/VirtualClient/VirtualClient.Actions/Lzbench/LzbenchExecutor.cs @@ -132,17 +132,41 @@ protected override async Task InitializeAsync(EventContext telemetryContext, Can if (!state.LzbenchInitialized) { - // Clone Lzbench code from git. - await this.ExecuteCommandAsync("git", $"clone -b v{this.Version} https://github.com/inikep/lzbench.git", this.PlatformSpecifics.PackagesDirectory, cancellationToken); - + using (IProcessProxy process = await this.ExecuteCommandAsync("git", $"clone -b v{this.Version} https://github.com/inikep/lzbench.git", this.PlatformSpecifics.PackagesDirectory, telemetryContext, cancellationToken, runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfErrored(process.StandardError.ToString(), ErrorReason.WorkloadUnexpectedAnomaly); + } + } + // Build Lzbench. - await this.ExecuteCommandAsync("make", string.Empty, this.LzbenchDirectory, cancellationToken); + using (IProcessProxy process = await this.ExecuteCommandAsync("make", this.LzbenchDirectory, telemetryContext, cancellationToken, runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfErrored(process.StandardError.ToString(), ErrorReason.WorkloadUnexpectedAnomaly); + } + } // Choose default file for compression and decompression if files/dirs are not provided. if (string.IsNullOrWhiteSpace(this.InputFilesOrDirs)) { - await this.ExecuteCommandAsync("wget", $"https://sun.aei.polsl.pl//~sdeor/corpus/silesia.zip", this.LzbenchDirectory, cancellationToken); - await this.ExecuteCommandAsync("unzip", "silesia.zip -d silesia", this.LzbenchDirectory, cancellationToken); + using (IProcessProxy process = await this.ExecuteCommandAsync("wget", $"https://sun.aei.polsl.pl//~sdeor/corpus/silesia.zip", this.LzbenchDirectory, telemetryContext, cancellationToken, runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfErrored(process.StandardError.ToString(), ErrorReason.WorkloadUnexpectedAnomaly); + } + } + + using (IProcessProxy process = await this.ExecuteCommandAsync("unzip", "silesia.zip -d silesia", this.LzbenchDirectory, telemetryContext, cancellationToken, runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfErrored(process.StandardError.ToString(), ErrorReason.WorkloadUnexpectedAnomaly); + } + } } foreach (string file in this.fileSystem.Directory.GetFiles(this.PlatformSpecifics.GetScriptPath("lzbench"))) @@ -196,37 +220,6 @@ private async Task CaptureMetricsAsync(IProcessProxy process, string commandArgu } } - private async Task ExecuteCommandAsync(string pathToExe, string commandLineArguments, string workingDirectory, CancellationToken cancellationToken) - { - if (!cancellationToken.IsCancellationRequested) - { - this.Logger.LogTraceMessage($"Executing process '{pathToExe}' '{commandLineArguments}' at directory '{workingDirectory}'."); - - EventContext telemetryContext = EventContext.Persisted() - .AddContext("command", pathToExe) - .AddContext("commandArguments", commandLineArguments); - - await this.Logger.LogMessageAsync($"{nameof(LzbenchExecutor)}.ExecuteProcess", telemetryContext, async () => - { - using (IProcessProxy process = this.systemManager.ProcessManager.CreateElevatedProcess(this.Platform, pathToExe, commandLineArguments, workingDirectory)) - { - this.CleanupTasks.Add(() => process.SafeKill()); - - await process.StartAndWaitAsync(cancellationToken) - .ConfigureAwait(false); - - if (!cancellationToken.IsCancellationRequested) - { - await this.LogProcessDetailsAsync(process, telemetryContext) - .ConfigureAwait(false); - - process.ThrowIfErrored(errorReason: ErrorReason.WorkloadFailed); - } - } - }).ConfigureAwait(false); - } - } - private string GetCommandLineArguments() { string inputFilesOrDirs = string.IsNullOrWhiteSpace(this.InputFilesOrDirs) diff --git a/src/VirtualClient/VirtualClient.Actions/MLPerf/MLPerfExecutor.cs b/src/VirtualClient/VirtualClient.Actions/MLPerf/MLPerfExecutor.cs index 3fae5983e3..a3c941919f 100644 --- a/src/VirtualClient/VirtualClient.Actions/MLPerf/MLPerfExecutor.cs +++ b/src/VirtualClient/VirtualClient.Actions/MLPerf/MLPerfExecutor.cs @@ -172,31 +172,22 @@ protected override async Task ExecuteAsync(EventContext telemetryContext, Cancel $"make run RUN_ARGS=\'--benchmarks={this.Model} --scenarios={this.scenarios[this.Model]} " + $"--config_ver={config} --test_mode=AccuracyOnly --fast\'\""; - using (IProcessProxy process = await this.ExecuteCommandAsync("sudo", perfModeExecCommand, this.NvidiaDirectory, telemetryContext, cancellationToken) + using (IProcessProxy process = await this.ExecuteCommandAsync(perfModeExecCommand, this.NvidiaDirectory, telemetryContext, cancellationToken, runElevated: true) .ConfigureAwait()) { if (!cancellationToken.IsCancellationRequested) { - if (process.IsErrored()) - { - await this.LogProcessDetailsAsync(process, telemetryContext, "MLPerf", logToFile: true); - process.ThrowIfWorkloadFailed(); - } - + process.ThrowIfWorkloadFailed(); await this.CaptureMetricsAsync(process, telemetryContext, cancellationToken, MLPerfExecutor.PerformanceSummary); } } - using (IProcessProxy process = await this.ExecuteCommandAsync("sudo", accuracyModeExecCommand, this.NvidiaDirectory, telemetryContext, cancellationToken) - .ConfigureAwait()) + using (IProcessProxy process = await this.ExecuteCommandAsync(accuracyModeExecCommand, this.NvidiaDirectory, telemetryContext, cancellationToken, runElevated: true) + .ConfigureAwait()) { if (!cancellationToken.IsCancellationRequested) { - await this.LogProcessDetailsAsync(process, telemetryContext) - .ConfigureAwait(); - - process.ThrowIfErrored(errorReason: ErrorReason.WorkloadFailed); - + process.ThrowIfWorkloadFailed(); await this.CaptureMetricsAsync(process, telemetryContext, cancellationToken, MLPerfExecutor.AccuracySummary) .ConfigureAwait(); } @@ -224,7 +215,13 @@ await this.ThrowIfUnixDistroNotSupportedAsync(cancellationToken) if (!state.Initialized) { // add user in docker group and create scratch space - await this.ExecuteCommandAsync("usermod", $"-aG docker {this.Username}", this.NvidiaDirectory, cancellationToken); + using (IProcessProxy process = await this.ExecuteCommandAsync($"usermod -aG docker {this.Username}", this.NvidiaDirectory, telemetryContext, cancellationToken, runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfWorkloadFailed(); + } + } if (this.RequireCustomSystemSupport) { @@ -234,7 +231,7 @@ await this.ThrowIfUnixDistroNotSupportedAsync(cancellationToken) this.ReplaceMakefile(); - await this.SetupEnvironmentAsync(cancellationToken); + await this.SetupEnvironmentAsync(telemetryContext, cancellationToken); state.Initialized = true; await this.stateManager.SaveStateAsync($"{nameof(MLPerfState)}", state, cancellationToken); } @@ -318,9 +315,10 @@ protected string GetContainerName() /// /// Creates setup for MLPerf workload. /// + /// Event context persisted. /// A token that can be used to cancel the operations. /// - protected async Task SetupEnvironmentAsync(CancellationToken cancellationToken) + protected async Task SetupEnvironmentAsync(EventContext telemetryContext, CancellationToken cancellationToken) { string dockerExecCommand = $"docker exec -u {this.Username} {this.GetContainerName()}"; @@ -328,68 +326,86 @@ protected async Task SetupEnvironmentAsync(CancellationToken cancellationToken) this.fileSystem.Directory.CreateDirectory(this.PlatformSpecifics.Combine(this.mlperfScratchSpace, "models")); this.fileSystem.Directory.CreateDirectory(this.PlatformSpecifics.Combine(this.mlperfScratchSpace, "preprocessed_data")); - await this.ExecuteCommandAsync( - "sudo", - $"systemctl restart docker", - this.NvidiaDirectory, - cancellationToken); - - await this.ExecuteCommandAsync( - "sudo", - $"systemctl start nvidia-fabricmanager", - this.NvidiaDirectory, - cancellationToken); - - await this.ExecuteCommandAsync( - "sudo", - $" -u {this.Username} bash -c \"make prebuild MLPERF_SCRATCH_PATH={this.mlperfScratchSpace}\"", - this.NvidiaDirectory, - cancellationToken); - - await this.ExecuteCommandAsync( - "sudo", - $"docker ps", - this.NvidiaDirectory, - cancellationToken); - - await this.ExecuteCommandAsync( - "sudo", + IEnumerable dockerSetupCommands = new List() + { + "systemctl restart docker", + "systemctl start nvidia-fabricmanager", + $"-u {this.Username} bash -c \"make prebuild MLPERF_SCRATCH_PATH={this.mlperfScratchSpace}\"", + "docker ps", $"{dockerExecCommand} sudo bash -c \"{this.ExportScratchSpace} && make clean\"", - this.NvidiaDirectory, - cancellationToken); + $"{dockerExecCommand} sudo bash -c \"{this.ExportScratchSpace} && make link_dirs\"", + }; - await this.ExecuteCommandAsync( - "sudo", - $"{dockerExecCommand} sudo bash -c \"{this.ExportScratchSpace} && make link_dirs\"", - this.NvidiaDirectory, - cancellationToken); + foreach (string dockerCommand in dockerSetupCommands) + { + using (IProcessProxy process = await this.ExecuteCommandAsync( + dockerCommand, + this.NvidiaDirectory, + telemetryContext, + cancellationToken, + runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfWorkloadFailed(); + } + } + } foreach (string benchmark in this.benchmarks) { - await this.ExecuteCommandAsync( - "sudo", - $"{dockerExecCommand} sudo bash -c \"{this.ExportScratchSpace} && make download_data BENCHMARKS={benchmark}\"", - this.NvidiaDirectory, - cancellationToken); + using (IProcessProxy process = await this.ExecuteCommandAsync( + $"{dockerExecCommand} sudo bash -c \"{this.ExportScratchSpace} && make download_data BENCHMARKS={benchmark}\"", + this.NvidiaDirectory, + telemetryContext, + cancellationToken, + runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfWorkloadFailed(); + } + } - await this.ExecuteCommandAsync( - "sudo", - $"{dockerExecCommand} sudo bash -c \"{this.ExportScratchSpace} && make download_model BENCHMARKS={benchmark}\"", - this.NvidiaDirectory, - cancellationToken); + using (IProcessProxy process = await this.ExecuteCommandAsync( + $"{dockerExecCommand} sudo bash -c \"{this.ExportScratchSpace} && make download_model BENCHMARKS={benchmark}\"", + this.NvidiaDirectory, + telemetryContext, + cancellationToken, + runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfWorkloadFailed(); + } + } - await this.ExecuteCommandAsync( - "sudo", - $"{dockerExecCommand} sudo bash -c \"{this.ExportScratchSpace} && make preprocess_data BENCHMARKS={benchmark}\"", - this.NvidiaDirectory, - cancellationToken); + using (IProcessProxy process = await this.ExecuteCommandAsync( + $"{dockerExecCommand} sudo bash -c \"{this.ExportScratchSpace} && make preprocess_data BENCHMARKS={benchmark}\"", + this.NvidiaDirectory, + telemetryContext, + cancellationToken, + runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfWorkloadFailed(); + } + } } - await this.ExecuteCommandAsync( - "sudo", + using (IProcessProxy process = await this.ExecuteCommandAsync( $"{dockerExecCommand} sudo bash -c \"{this.ExportScratchSpace} && make build\"", this.NvidiaDirectory, - cancellationToken); + telemetryContext, + cancellationToken, + runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfWorkloadFailed(); + } + } } private async Task CaptureMetricsAsync(IProcessProxy process, EventContext telemetryContext, CancellationToken cancellationToken, string context = null) @@ -508,33 +524,6 @@ private async Task ThrowIfUnixDistroNotSupportedAsync(CancellationToken cancella } } - private async Task ExecuteCommandAsync(string pathToExe, string commandLineArguments, string workingDirectory, CancellationToken cancellationToken) - { - if (!cancellationToken.IsCancellationRequested) - { - this.Logger.LogTraceMessage($"Executing process '{pathToExe}' '{commandLineArguments}' at directory '{workingDirectory}'."); - - EventContext telemetryContext = EventContext.Persisted() - .AddContext("command", pathToExe) - .AddContext("commandArguments", commandLineArguments); - - await this.Logger.LogMessageAsync($"{nameof(MLPerfExecutor)}.ExecuteProcess", telemetryContext, async () => - { - using (IProcessProxy process = this.systemManager.ProcessManager.CreateElevatedProcess(this.Platform, pathToExe, commandLineArguments, workingDirectory)) - { - this.CleanupTasks.Add(() => process.SafeKill()); - await process.StartAndWaitAsync(cancellationToken).ConfigureAwait(false); - - if (!cancellationToken.IsCancellationRequested) - { - await this.LogProcessDetailsAsync(process, telemetryContext).ConfigureAwait(); - process.ThrowIfErrored(errorReason: ErrorReason.WorkloadFailed); - } - } - }).ConfigureAwait(false); - } - } - private void PrepareBenchmarkConfigsAndScenarios() { this.scenarios = new Dictionary(); diff --git a/src/VirtualClient/VirtualClient.Actions/Memcached/MemcachedExecutor.cs b/src/VirtualClient/VirtualClient.Actions/Memcached/MemcachedExecutor.cs index aac52f09ea..9b98075cd7 100644 --- a/src/VirtualClient/VirtualClient.Actions/Memcached/MemcachedExecutor.cs +++ b/src/VirtualClient/VirtualClient.Actions/Memcached/MemcachedExecutor.cs @@ -123,45 +123,6 @@ protected override Task ExecuteAsync(EventContext telemetryContext, Cancellation throw new NotImplementedException(); } - /// - /// Executes the commands. - /// - /// The command to run. - /// The command line arguments to supply to the command. - /// The working directory for the command. - /// A token that can be used to cancel the operation. - /// Alternative exit codes to use to represent successful process exit. - protected async Task ExecuteCommandAsync(string command, string arguments, string workingDir, CancellationToken cancellationToken, IEnumerable successCodes = null) - { - if (!cancellationToken.IsCancellationRequested) - { - this.Logger.LogTraceMessage($"Executing process '{command}' '{arguments}' at directory '{workingDir}'."); - - EventContext telemetryContext = EventContext.Persisted() - .AddContext("packagePath", workingDir) - .AddContext("command", command) - .AddContext("commandArguments", arguments); - - await this.Logger.LogMessageAsync($"{this.TypeName}.ExecuteProcess", telemetryContext, async () => - { - using (IProcessProxy process = this.ProcessManager.CreateElevatedProcess(this.Platform, command, arguments, workingDir)) - { - this.CleanupTasks.Add(() => process.SafeKill()); - await process.StartAndWaitAsync(cancellationToken).ConfigureAwait(); - - if (!cancellationToken.IsCancellationRequested) - { - await this.LogProcessDetailsAsync(process, telemetryContext); - - process.ThrowIfErrored( - successCodes ?? ProcessProxy.DefaultSuccessCodes, - errorReason: ErrorReason.WorkloadFailed); - } - } - }).ConfigureAwait(false); - } - } - /// /// Initializes the environment and dependencies for running the Memcached Memtier workload. /// diff --git a/src/VirtualClient/VirtualClient.Actions/Pbzip2/Pbzip2Executor.cs b/src/VirtualClient/VirtualClient.Actions/Pbzip2/Pbzip2Executor.cs index 8b9d43ba0f..5108b3af89 100644 --- a/src/VirtualClient/VirtualClient.Actions/Pbzip2/Pbzip2Executor.cs +++ b/src/VirtualClient/VirtualClient.Actions/Pbzip2/Pbzip2Executor.cs @@ -122,8 +122,33 @@ await this.CheckDistroSupportAsync(telemetryContext, cancellationToken) // Choose default file for compression and decompression if files/dirs are not provided. if (string.IsNullOrWhiteSpace(this.InputFiles)) { - await this.ExecuteCommandAsync("wget", $"https://sun.aei.polsl.pl//~sdeor/corpus/silesia.zip", this.Pbzip2Directory, cancellationToken); - await this.ExecuteCommandAsync("unzip", "silesia.zip -d silesia", this.Pbzip2Directory, cancellationToken); + using (IProcessProxy process = await this.ExecuteCommandAsync( + "wget", + $"https://sun.aei.polsl.pl//~sdeor/corpus/silesia.zip", + this.Pbzip2Directory, + telemetryContext, + cancellationToken, + runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfErrored(process.StandardError.ToString(), ErrorReason.WorkloadUnexpectedAnomaly); + } + } + + using (IProcessProxy process = await this.ExecuteCommandAsync( + "unzip", + "silesia.zip -d silesia", + this.Pbzip2Directory, + telemetryContext, + cancellationToken, + runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfErrored(process.StandardError.ToString(), ErrorReason.WorkloadUnexpectedAnomaly); + } + } } state.Pbzip2StateInitialized = true; @@ -162,35 +187,6 @@ private void CaptureMetrics(IProcessProxy process, string commandArguments, Even } } - private async Task ExecuteCommandAsync(string pathToExe, string commandLineArguments, string workingDirectory, CancellationToken cancellationToken) - { - if (!cancellationToken.IsCancellationRequested) - { - this.Logger.LogTraceMessage($"Executing process '{pathToExe}' '{commandLineArguments}' at directory '{workingDirectory}'."); - - EventContext telemetryContext = EventContext.Persisted() - .AddContext("command", pathToExe) - .AddContext("commandArguments", commandLineArguments); - - await this.Logger.LogMessageAsync($"{nameof(Pbzip2Executor)}.ExecuteProcess", telemetryContext, async () => - { - using (IProcessProxy process = this.systemManager.ProcessManager.CreateElevatedProcess(this.Platform, pathToExe, commandLineArguments, workingDirectory)) - { - this.CleanupTasks.Add(() => process.SafeKill()); - await process.StartAndWaitAsync(cancellationToken).ConfigureAwait(false); - - if (!cancellationToken.IsCancellationRequested) - { - this.LogProcessDetailsAsync(process, telemetryContext) - .ConfigureAwait(); - - process.ThrowIfErrored(errorReason: ErrorReason.WorkloadFailed); - } - } - }).ConfigureAwait(false); - } - } - private string GetCommandLineArguments() { string inputFiles = string.IsNullOrEmpty(this.InputFiles) diff --git a/src/VirtualClient/VirtualClient.Actions/Redis/RedisExecutor.cs b/src/VirtualClient/VirtualClient.Actions/Redis/RedisExecutor.cs index 52ded57e5d..b0563e8ec2 100644 --- a/src/VirtualClient/VirtualClient.Actions/Redis/RedisExecutor.cs +++ b/src/VirtualClient/VirtualClient.Actions/Redis/RedisExecutor.cs @@ -81,46 +81,6 @@ protected override Task ExecuteAsync(EventContext telemetryContext, Cancellation throw new NotImplementedException(); } - /// - /// Executes the commands. - /// - /// Command that needs to be executed - /// The directory where we want to execute the command - /// A token that can be used to cancel the operation. - /// Alternative exit codes to use to represent successful process exit. - /// String output of the command. - protected async Task ExecuteCommandAsync(string command, string workingDir, CancellationToken cancellationToken, IEnumerable successCodes = null) - { - if (!cancellationToken.IsCancellationRequested) - { - this.Logger.LogTraceMessage($"Executing process '{command}' at directory '{workingDir}'."); - - EventContext telemetryContext = EventContext.Persisted() - .AddContext("packageName", this.PackageName) - .AddContext("packagePath", workingDir) - .AddContext("command", "sudo") - .AddContext("commandArguments", command); - - await this.Logger.LogMessageAsync($"{this.TypeName}.ExecuteProcess", telemetryContext, async () => - { - using (IProcessProxy process = this.SystemManagement.ProcessManager.CreateElevatedProcess(this.Platform, command, null, workingDir)) - { - this.CleanupTasks.Add(() => process.SafeKill()); - await process.StartAndWaitAsync(cancellationToken); - - if (!cancellationToken.IsCancellationRequested) - { - this.LogProcessDetailsAsync(process, telemetryContext); - - process.ThrowIfErrored( - successCodes ?? ProcessProxy.DefaultSuccessCodes, - errorReason: ErrorReason.WorkloadFailed); - } - } - }).ConfigureAwait(false); - } - } - /// /// Initializes the environment and dependencies for running the Redis Memtier workload. /// diff --git a/src/VirtualClient/VirtualClient.Actions/SPECcpu/SpecCpuExecutor.cs b/src/VirtualClient/VirtualClient.Actions/SPECcpu/SpecCpuExecutor.cs index 9836463c9e..e6fb4bf72d 100644 --- a/src/VirtualClient/VirtualClient.Actions/SPECcpu/SpecCpuExecutor.cs +++ b/src/VirtualClient/VirtualClient.Actions/SPECcpu/SpecCpuExecutor.cs @@ -165,9 +165,7 @@ protected override async Task ExecuteAsync(EventContext telemetryContext, Cancel { if (!cancellationToken.IsCancellationRequested) { - await this.LogProcessDetailsAsync(process, telemetryContext, "SPECcpu", logToFile: true); process.ThrowIfWorkloadFailed(); - await this.CaptureMetricsAsync(process, commandLineArguments, telemetryContext, cancellationToken); } } @@ -255,32 +253,83 @@ private async Task SetupSpecCpuAsync(string isoFilePath, EventContext telemetryC if (this.Platform == PlatformID.Unix) { - await this.ExecuteCommandAsync("mount", $"-t iso9660 -o ro,exec,loop {isoFilePath} {mountPath}", this.PackageDirectory, telemetryContext, cancellationToken); - await this.ExecuteCommandAsync("./install.sh", $"-f -d {this.PackageDirectory}", mountPath, telemetryContext, cancellationToken); - await this.WriteSpecCpuConfigAsync(cancellationToken); - await this.ExecuteCommandAsync("chmod", $"-R ugo=rwx {this.PackageDirectory}", this.PackageDirectory, telemetryContext, cancellationToken); - await this.ExecuteCommandAsync("umount", mountPath, this.PackageDirectory, telemetryContext, cancellationToken); + using (IProcessProxy process = await this.ExecuteCommandAsync("mount", $"-t iso9660 -o ro,exec,loop {isoFilePath} {mountPath}", this.PackageDirectory, telemetryContext, cancellationToken, runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfWorkloadFailed(); + } + } + + using (IProcessProxy process = await this.ExecuteCommandAsync("./install.sh", $"-f -d {this.PackageDirectory}", mountPath, telemetryContext, cancellationToken, runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfWorkloadFailed(); + } + } + + await this.WriteSpecCpuConfigAsync(telemetryContext, cancellationToken); + + using (IProcessProxy process = await this.ExecuteCommandAsync("chmod", $"-R ugo=rwx {this.PackageDirectory}", this.PackageDirectory, telemetryContext, cancellationToken, runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfWorkloadFailed(); + } + } + + using (IProcessProxy process = await this.ExecuteCommandAsync("umount", mountPath, this.PackageDirectory, telemetryContext, cancellationToken, runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfWorkloadFailed(); + } + } } else { // powershell -Command "Mount-DiskImage -ImagePath "C:\Users\azureuser\Desktop\cpu2017-1.1.8.iso"" - string mountIsoCmd = $"-Command \"Mount-DiskImage -ImagePath {isoFilePath}\""; - await this.ExecuteCommandAsync("powershell", mountIsoCmd, this.PackageDirectory, telemetryContext, cancellationToken); + using (IProcessProxy process = await this.ExecuteCommandAsync("powershell", $"-Command \"Mount-DiskImage -ImagePath {isoFilePath}\"", this.PackageDirectory, telemetryContext, cancellationToken, runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfWorkloadFailed(); + } + } + string driveLetter = string.Empty; // powershell -Command "(Get-DiskImage -ImagePath "C:\Users\azureuser\Desktop\cpu2017-1.1.8.iso" | Get-Volume).DriveLetter " - string getDriveLetterCmd = $"-Command \"(Get-DiskImage -ImagePath {isoFilePath}| Get-Volume).DriveLetter\""; - string driveLetter = await this.ExecuteCommandAsync("powershell", getDriveLetterCmd, this.PackageDirectory, telemetryContext, cancellationToken); + using (IProcessProxy process = await this.ExecuteCommandAsync("powershell", $"-Command \"(Get-DiskImage -ImagePath {isoFilePath}| Get-Volume).DriveLetter\"", this.PackageDirectory, telemetryContext, cancellationToken, runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfWorkloadFailed(); + } + + driveLetter = process.StandardOutput.ToString(); + } // The reason for the echo is that there is a "pause" in the install.bat. The echo skips it. // echo 1 | install.bat C:\cpu2017 - string installCmd = $"/c echo 1 | {this.PlatformSpecifics.Combine($"{driveLetter.Trim()}:", "install.bat")} {this.PackageDirectory}"; - await this.ExecuteCommandAsync("cmd", installCmd, this.PackageDirectory, telemetryContext, cancellationToken); + using (IProcessProxy process = await this.ExecuteCommandAsync("cmd", $"/c echo 1 | {this.PlatformSpecifics.Combine($"{driveLetter.Trim()}:", "install.bat")} {this.PackageDirectory}", this.PackageDirectory, telemetryContext, cancellationToken, runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfWorkloadFailed(); + } + } - await this.WriteSpecCpuConfigAsync(cancellationToken); + await this.WriteSpecCpuConfigAsync(telemetryContext, cancellationToken); // powershell -Command "Dismount-DiskImage -ImagePath "C:\Users\azureuser\Desktop\cpu2017-1.1.8.iso"" - string dismountCmd = $"-Command \"Dismount-DiskImage -ImagePath {isoFilePath}\""; - await this.ExecuteCommandAsync("powershell", dismountCmd, this.PackageDirectory, telemetryContext, cancellationToken); + using (IProcessProxy process = await this.ExecuteCommandAsync("powershell", $"-Command \"Dismount-DiskImage -ImagePath {isoFilePath}\"", this.PackageDirectory, telemetryContext, cancellationToken, runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfWorkloadFailed(); + } + } } state.SpecCpuInitialized = true; @@ -289,32 +338,6 @@ private async Task SetupSpecCpuAsync(string isoFilePath, EventContext telemetryC await this.stateManager.SaveStateAsync($"{nameof(SpecCpuState)}", state, cancellationToken); } - private async Task ExecuteCommandAsync(string command, string commandArguments, string workingDirectory, EventContext telemetryContext, CancellationToken cancellationToken) - { - EventContext relatedContext = EventContext.Persisted() - .AddContext(nameof(command), command) - .AddContext(nameof(commandArguments), commandArguments); - - using (IProcessProxy process = this.systemManager.ProcessManager.CreateElevatedProcess(this.Platform, command, commandArguments, workingDirectory)) - { - this.CleanupTasks.Add(() => process.SafeKill()); - this.LogProcessTrace(process); - - await process.StartAndWaitAsync(cancellationToken); - - if (!cancellationToken.IsCancellationRequested) - { - if (process.IsErrored()) - { - await this.LogProcessDetailsAsync(process, relatedContext, logToFile: true); - process.ThrowIfWorkloadFailed(); - } - } - - return process.StandardOutput.ToString(); - } - } - private async Task CaptureMetricsAsync(IProcessProxy process, string commandArguments, EventContext telemetryContext, CancellationToken cancellationToken) { if (!cancellationToken.IsCancellationRequested) @@ -400,7 +423,7 @@ private string GetCommandLineArguments() return cmd; } - private async Task WriteSpecCpuConfigAsync(CancellationToken cancellationToken) + private async Task WriteSpecCpuConfigAsync(EventContext telemetryContext, CancellationToken cancellationToken) { // Copy SPECcpu configuration file to the config folder. string configurationFile = this.GetConfigurationFileName(); @@ -422,7 +445,7 @@ private async Task WriteSpecCpuConfigAsync(CancellationToken cancellationToken) true); } - string compilerVersion = await this.GetInstalledCompilerDumpVersionAsync("gcc", cancellationToken); + string compilerVersion = await this.GetInstalledCompilerDumpVersionAsync("gcc", telemetryContext, cancellationToken); if (string.IsNullOrEmpty(compilerVersion)) { @@ -439,19 +462,17 @@ private async Task WriteSpecCpuConfigAsync(CancellationToken cancellationToken) await this.fileSystem.File.WriteAllTextAsync(this.Combine(this.PackageDirectory, "config", configurationFile), templateText, cancellationToken); } - private async Task GetInstalledCompilerDumpVersionAsync(string compilerName, CancellationToken cancellationToken) + private async Task GetInstalledCompilerDumpVersionAsync(string compilerName, EventContext telemetryContext, CancellationToken cancellationToken) { string command = compilerName; string commandArguments = "-dumpversion"; string version = string.Empty; - using (IProcessProxy process = this.systemManager.ProcessManager.CreateElevatedProcess(this.Platform, command, commandArguments)) + using (IProcessProxy process = await this.ExecuteCommandAsync(command, commandArguments, this.PackageDirectory, telemetryContext, cancellationToken, runElevated: true)) { try { - await process.StartAndWaitAsync(cancellationToken); - if (!cancellationToken.IsCancellationRequested) { version = process.StandardOutput.ToString().Trim().Split(".")[0]; diff --git a/src/VirtualClient/VirtualClient.Actions/SPECjbb/SpecJbbExecutor.cs b/src/VirtualClient/VirtualClient.Actions/SPECjbb/SpecJbbExecutor.cs index efd54ff4cd..999334e615 100644 --- a/src/VirtualClient/VirtualClient.Actions/SPECjbb/SpecJbbExecutor.cs +++ b/src/VirtualClient/VirtualClient.Actions/SPECjbb/SpecJbbExecutor.cs @@ -99,12 +99,7 @@ protected override async Task ExecuteAsync(EventContext telemetryContext, Cancel { if (!cancellationToken.IsCancellationRequested) { - if (process.IsErrored()) - { - await this.LogProcessDetailsAsync(process, telemetryContext, "SPECjbb", logToFile: true); - process.ThrowIfWorkloadFailed(); - } - + process.ThrowIfWorkloadFailed(); await this.CaptureMetricsAsync(process, commandLineArguments, telemetryContext, cancellationToken); } } @@ -192,34 +187,6 @@ private async Task CaptureMetricsAsync(IProcessProxy process, string commandArgu } } - private async Task ExecuteCommandAsync(string pathToExe, string commandLineArguments, string workingDirectory, CancellationToken cancellationToken) - { - if (!cancellationToken.IsCancellationRequested) - { - EventContext telemetryContext = EventContext.Persisted() - .AddContext("command", pathToExe) - .AddContext("commandArguments", commandLineArguments); - - await this.Logger.LogMessageAsync($"{nameof(SpecJbbExecutor)}.ExecuteProcess", telemetryContext, async () => - { - using (IProcessProxy process = this.systemManagement.ProcessManager.CreateElevatedProcess(this.Platform, pathToExe, commandLineArguments, workingDirectory)) - { - this.CleanupTasks.Add(() => process.SafeKill()); - this.LogProcessTrace(process); - await process.StartAndWaitAsync(cancellationToken); - - await this.ValidateProcessExitedAsync(process.Id, TimeSpan.FromMinutes(10), cancellationToken); - - if (!cancellationToken.IsCancellationRequested) - { - await this.LogProcessDetailsAsync(process, telemetryContext, "SPECjbb"); - process.ThrowIfErrored(errorReason: ErrorReason.WorkloadFailed); - } - } - }); - } - } - private string GetCommandLineArguments() { string options = this.CalculateJavaOptions(); diff --git a/src/VirtualClient/VirtualClient.Core/VirtualClientComponentExtensions.cs b/src/VirtualClient/VirtualClient.Core/VirtualClientComponentExtensions.cs index b1232bfdc2..0a29fe63a2 100644 --- a/src/VirtualClient/VirtualClient.Core/VirtualClientComponentExtensions.cs +++ b/src/VirtualClient/VirtualClient.Core/VirtualClientComponentExtensions.cs @@ -153,6 +153,8 @@ public static async Task ExecuteCommandAsync( beforeExecution?.Invoke(process); await process.StartAndWaitAsync(cancellationToken) .ConfigureAwait(false); + + await component.LogProcessDetailsAsync(process, telemetryContext, logToFile: component.LogToFile); } return process; diff --git a/src/VirtualClient/VirtualClient.Dependencies.UnitTests/CUDAAndNvidiaGPUDriverInstallationTests.cs b/src/VirtualClient/VirtualClient.Dependencies.UnitTests/CUDAAndNvidiaGPUDriverInstallationTests.cs index b4cd8c8359..2f94d9eb78 100644 --- a/src/VirtualClient/VirtualClient.Dependencies.UnitTests/CUDAAndNvidiaGPUDriverInstallationTests.cs +++ b/src/VirtualClient/VirtualClient.Dependencies.UnitTests/CUDAAndNvidiaGPUDriverInstallationTests.cs @@ -79,19 +79,47 @@ public async Task CUDAAndNvidiaGPUDriverInstallationDependencyStartsCorrectProce { this.SetupDefaultMockBehavior(PlatformID.Unix); - this.SetupProcessManager("sudo", UpdateCommand, Environment.CurrentDirectory); - this.SetupProcessManager("sudo", BuildEssentialInstallationCommand, Environment.CurrentDirectory); - this.SetupProcessManager("sudo", GetRunFileCommand, Environment.CurrentDirectory); - this.SetupProcessManager("sudo", RunRunFileCommand, Environment.CurrentDirectory); - this.SetupProcessManager("sudo", ExportPathCommand, Environment.CurrentDirectory); - this.SetupProcessManager("sudo", ExportLibraryPathCommand, Environment.CurrentDirectory); - this.SetupProcessManager("sudo", UpgradeCommand, Environment.CurrentDirectory); - this.SetupProcessManager("sudo", InstallDriverCommand, Environment.CurrentDirectory); - this.SetupProcessManager("sudo", InstallFabricManagerCommand, Environment.CurrentDirectory); - - await this.component.ExecuteAsync(CancellationToken.None); - - this.mockProcessManager.Verify(); + List expectedCommands = new List() + { + $"sudo {UpdateCommand}", + $"sudo {BuildEssentialInstallationCommand}", + $"sudo {GetRunFileCommand}", + $"sudo {RunRunFileCommand}", + $"sudo {UpgradeCommand}", + $"sudo {InstallDriverCommand}", + $"sudo {InstallFabricManagerCommand}", + $"sudo {ExportPathCommand}", + $"sudo {ExportLibraryPathCommand}", + }; + int commandExecuted = 0; + this.fixture.ProcessManager.OnCreateProcess = (exe, arguments, workingDir) => + { + if (expectedCommands[commandExecuted] == $"{exe} {arguments}") + { + commandExecuted++; + } + + IProcessProxy process = new InMemoryProcess + { + StartInfo = new ProcessStartInfo + { + FileName = exe, + Arguments = arguments + }, + ExitCode = 0, + OnStart = () => true, + OnHasExited = () => true + }; + + return process; + }; + using (TestComponent driverInstallation = new TestComponent(this.fixture.Dependencies, this.fixture.Parameters)) + { + await driverInstallation.ExecuteAsync(CancellationToken.None) + .ConfigureAwait(false); + } + + Assert.AreEqual(expectedCommands.Count, commandExecuted); } [Test] @@ -101,10 +129,32 @@ public async Task CUDAAndNvidiaGPUDriverInstallationDependencyDoesNotInstallCUDA this.fixture.StateManager.OnGetState(nameof(CudaAndNvidiaGPUDriverInstallation)).ReturnsAsync(JObject.FromObject(this.mockState)); - this.SetupProcessManager("sudo", UpdateCommand); + int commandExecuted = 0; + this.fixture.ProcessManager.OnCreateProcess = (exe, arguments, workingDir) => + { + commandExecuted++; + + IProcessProxy process = new InMemoryProcess + { + StartInfo = new ProcessStartInfo + { + FileName = exe, + Arguments = arguments + }, + ExitCode = 0, + OnStart = () => true, + OnHasExited = () => true + }; + + return process; + }; + using (TestComponent driverInstallation = new TestComponent(this.fixture.Dependencies, this.fixture.Parameters)) + { + await driverInstallation.ExecuteAsync(CancellationToken.None) + .ConfigureAwait(false); + } - await this.component.ExecuteAsync(CancellationToken.None); - Assert.Throws(() => this.mockProcessManager.Verify()); + Assert.AreEqual(0, commandExecuted); } [Test] @@ -112,26 +162,23 @@ public void CUDAAndNvidiaGPUDriverInstallationDependencySurfacesExceptionWhenPro { this.SetupDefaultMockBehavior(PlatformID.Unix); - this.SetupProcessManager("sudo", UpdateCommand, Environment.CurrentDirectory); - this.SetupProcessManager("sudo", BuildEssentialInstallationCommand, Environment.CurrentDirectory); - this.SetupProcessManager("sudo", GetRunFileCommand, Environment.CurrentDirectory); - this.SetupProcessManager("sudo", RunRunFileCommand, Environment.CurrentDirectory); - this.SetupProcessManager("sudo", ExportPathCommand, Environment.CurrentDirectory); - this.SetupProcessManager("sudo", UpgradeCommand, Environment.CurrentDirectory); - this.SetupProcessManager("sudo", InstallDriverCommand, Environment.CurrentDirectory); - var setup = this.SetupProcessManager("sudo", InstallFabricManagerCommand, Environment.CurrentDirectory); - - setup.Returns(CUDAAndNvidiaGPUDriverInstallationTests.GetProcessProxy(1)); + this.fixture.ProcessManager.OnCreateProcess = (command, arguments, workingDirectory) => + { + this.fixture.Process.ExitCode = 1; + this.fixture.Process.OnHasExited = () => true; + return this.fixture.Process; + }; - this.component.RetryPolicy = Policy.NoOpAsync(); - DependencyException exc = Assert.ThrowsAsync(() => this.component.ExecuteAsync(CancellationToken.None)); - Assert.AreEqual(ErrorReason.DependencyInstallationFailed, exc.Reason); + using TestComponent component = new TestComponent(this.fixture.Dependencies, this.fixture.Parameters); + DependencyException exception = Assert.ThrowsAsync(() => component.ExecuteAsync(CancellationToken.None)); + Assert.AreEqual(ErrorReason.DependencyInstallationFailed, exception.Reason); } [Test] public async Task CUDAAndNvidiaGPUDriverInstallationDependencyExecutesCorrectInsatllerCommandOnWindows() { this.SetupDefaultMockBehavior(PlatformID.Win32NT); + this.fixture.Parameters["packageName"] = "NvidiaDrivers"; this.fixture.Directory.Setup(di => di.Exists(It.IsAny())) .Returns(true); @@ -145,12 +192,37 @@ public async Task CUDAAndNvidiaGPUDriverInstallationDependencyExecutesCorrectIns this.fixture.FileSystem.Setup(fe => fe.Directory.GetCurrentDirectory()) .Returns(this.mockPackage.Path); - this.SetupProcessManager(this.fixture.Combine(this.mockPackage.Path, "nvidiaDriversInstaller.exe"), "-y -s", Environment.CurrentDirectory); + string expectedCommand = $"{this.fixture.Combine(this.mockPackage.Path, "nvidiaDriversInstaller.exe")} -y -s"; + bool commandExecuted = false; - this.component = new TestComponent(this.fixture.Dependencies, this.fixture.Parameters); + this.fixture.ProcessManager.OnCreateProcess = (exe, arguments, workingDir) => + { + if (expectedCommand == $"{exe} {arguments}") + { + commandExecuted = true; + } + + IProcessProxy process = new InMemoryProcess + { + StartInfo = new ProcessStartInfo + { + FileName = exe, + Arguments = arguments + }, + ExitCode = 0, + OnStart = () => true, + OnHasExited = () => true + }; + + return process; + }; + using (TestComponent driverInstallation = new TestComponent(this.fixture.Dependencies, this.fixture.Parameters)) + { + await driverInstallation.ExecuteAsync(CancellationToken.None) + .ConfigureAwait(false); + } - await this.component.ExecuteAsync(CancellationToken.None); - this.mockProcessManager.Verify(); + Assert.IsTrue(commandExecuted); } private void SetupDefaultMockBehavior(PlatformID platformID) diff --git a/src/VirtualClient/VirtualClient.Dependencies.UnitTests/CompilerInstallationTests.cs b/src/VirtualClient/VirtualClient.Dependencies.UnitTests/CompilerInstallationTests.cs index 2db72d9d2b..5e203d821e 100644 --- a/src/VirtualClient/VirtualClient.Dependencies.UnitTests/CompilerInstallationTests.cs +++ b/src/VirtualClient/VirtualClient.Dependencies.UnitTests/CompilerInstallationTests.cs @@ -121,6 +121,7 @@ public async Task CompilerInstallationRunsTheExpectedWorkloadCommandInLinuxForGc ProcessStartInfo expectedInfo = new ProcessStartInfo(); List expectedCommands = new List() { + "sudo gcc -dumpversion", "sudo update-alternatives --remove-all gcc", "sudo update-alternatives --remove-all gfortran", "sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y", @@ -134,6 +135,14 @@ public async Task CompilerInstallationRunsTheExpectedWorkloadCommandInLinuxForGc $"--slave /usr/bin/gfortran gfortran /usr/bin/gfortran-123", "sudo update-alternatives --remove-all cpp", "sudo update-alternatives --install /usr/bin/cpp cpp /usr/bin/cpp-123 1230", + "gcc --version", + "cc --version", + "sudo gcc -dumpversion", + "sudo cc -dumpversion", + "sudo gfortran -dumpversion", + "cc --version", + "gcc --version", + "gfortran --version" }; int commandExecuted = 0; @@ -389,7 +398,7 @@ public void CompilerInstallationConfirmsTheInstalledVersionOfGCCAsExpected(strin process.StandardOutput.AppendLine($"{process.StartInfo.FileName} {versionOutput}"); }; - Assert.IsTrue(compilerInstallation.ConfirmGccVersionInstalledAsync(CancellationToken.None) + Assert.IsTrue(compilerInstallation.ConfirmGccVersionInstalledAsync(EventContext.Persisted(), CancellationToken.None) .GetAwaiter().GetResult()); } } @@ -429,7 +438,7 @@ public void CompilerInstallationConfirmsExpectedCompilersForGcc() compilers[process.StartInfo.FileName] = true; }; - Assert.IsTrue(compilerInstallation.ConfirmGccVersionInstalledAsync(CancellationToken.None) + Assert.IsTrue(compilerInstallation.ConfirmGccVersionInstalledAsync(EventContext.Persisted(), CancellationToken.None) .GetAwaiter().GetResult()); Assert.IsTrue(compilers.Values.All(installed => installed)); } @@ -447,9 +456,9 @@ public TestCompilerInstallation(IServiceCollection dependencies, IDictionary ConfirmGccVersionInstalledAsync(CancellationToken cancellationToken) + public new Task ConfirmGccVersionInstalledAsync(EventContext context, CancellationToken cancellationToken) { - return base.ConfirmGccVersionInstalledAsync(cancellationToken); + return base.ConfirmGccVersionInstalledAsync(context, cancellationToken); } } } diff --git a/src/VirtualClient/VirtualClient.Dependencies.UnitTests/NvidiaContainerToolkitInstallationTests.cs b/src/VirtualClient/VirtualClient.Dependencies.UnitTests/NvidiaContainerToolkitInstallationTests.cs index 9f068c193d..2c849fd203 100644 --- a/src/VirtualClient/VirtualClient.Dependencies.UnitTests/NvidiaContainerToolkitInstallationTests.cs +++ b/src/VirtualClient/VirtualClient.Dependencies.UnitTests/NvidiaContainerToolkitInstallationTests.cs @@ -82,15 +82,43 @@ public async Task NvidiaContainerToolkitInstallationDependencyStartsCorrectProce { this.SetupDefaultMockBehavior(PlatformID.Unix); - this.SetupProcessManager("sudo", $"bash -c \"{SetupCommand}\"", Environment.CurrentDirectory); - this.SetupProcessManager("sudo", UpdateCommand, Environment.CurrentDirectory); - this.SetupProcessManager("sudo", NvidiaDockerInstallationCommand, Environment.CurrentDirectory); - this.SetupProcessManager("sudo", ConfigureDockerRuntimeCommand, Environment.CurrentDirectory); - this.SetupProcessManager("sudo", RestartDockerCommand, Environment.CurrentDirectory); - - await this.component.ExecuteAsync(CancellationToken.None); + List expectedCommands = new List() + { + $"sudo bash -c \"{SetupCommand}\"", + $"sudo {UpdateCommand}", + $"sudo {NvidiaDockerInstallationCommand}", + $"sudo {ConfigureDockerRuntimeCommand}", + $"sudo {RestartDockerCommand}" + }; + int commandExecuted = 0; + this.fixture.ProcessManager.OnCreateProcess = (exe, arguments, workingDir) => + { + if (expectedCommands[commandExecuted] == $"{exe} {arguments}") + { + commandExecuted++; + } + + IProcessProxy process = new InMemoryProcess + { + StartInfo = new ProcessStartInfo + { + FileName = exe, + Arguments = arguments + }, + ExitCode = 0, + OnStart = () => true, + OnHasExited = () => true + }; + + return process; + }; + using (TestComponent toolkitInstallation = new TestComponent(this.fixture.Dependencies, this.fixture.Parameters)) + { + await toolkitInstallation.ExecuteAsync(CancellationToken.None) + .ConfigureAwait(false); + } - this.mockProcessManager.Verify(); + Assert.AreEqual(expectedCommands.Count, commandExecuted); } [Test] @@ -100,10 +128,32 @@ public async Task NvidiaContainerToolkitInstallationDependencyDoesNotInstallNvid this.fixture.StateManager.OnGetState(nameof(NvidiaContainerToolkitInstallation)).ReturnsAsync(JObject.FromObject(this.mockState)); - this.SetupProcessManager("sudo", SetupCommand); + int commandExecuted = 0; + this.fixture.ProcessManager.OnCreateProcess = (exe, arguments, workingDir) => + { + commandExecuted++; + + IProcessProxy process = new InMemoryProcess + { + StartInfo = new ProcessStartInfo + { + FileName = exe, + Arguments = arguments + }, + ExitCode = 0, + OnStart = () => true, + OnHasExited = () => true + }; + + return process; + }; + using (TestComponent toolkitInstallation = new TestComponent(this.fixture.Dependencies, this.fixture.Parameters)) + { + await toolkitInstallation.ExecuteAsync(CancellationToken.None) + .ConfigureAwait(false); + } - await this.component.ExecuteAsync(CancellationToken.None); - Assert.Throws(() => this.mockProcessManager.Verify()); + Assert.AreEqual(0, commandExecuted); } [Test] @@ -111,17 +161,16 @@ public void NvidiaContainerToolkitInstallationDependencySurfacesExceptionWhenPro { this.SetupDefaultMockBehavior(PlatformID.Unix); - this.SetupProcessManager("sudo", $"bash -c \"{SetupCommand}\"", Environment.CurrentDirectory); - this.SetupProcessManager("sudo", UpdateCommand, Environment.CurrentDirectory); - this.SetupProcessManager("sudo", NvidiaDockerInstallationCommand, Environment.CurrentDirectory); - this.SetupProcessManager("sudo", ConfigureDockerRuntimeCommand, Environment.CurrentDirectory); - - var setup = this.SetupProcessManager("sudo", RestartDockerCommand, Environment.CurrentDirectory); - setup.Returns(NvidiaContainerToolkitInstallationTests.GetProcessProxy(1)); + this.fixture.ProcessManager.OnCreateProcess = (command, arguments, workingDirectory) => + { + this.fixture.Process.ExitCode = 1; + this.fixture.Process.OnHasExited = () => true; + return this.fixture.Process; + }; - this.component.RetryPolicy = Policy.NoOpAsync(); - DependencyException exc = Assert.ThrowsAsync(() => this.component.ExecuteAsync(CancellationToken.None)); - Assert.AreEqual(ErrorReason.DependencyInstallationFailed, exc.Reason); + using TestComponent component = new TestComponent(this.fixture.Dependencies, this.fixture.Parameters); + DependencyException exception = Assert.ThrowsAsync(() => component.ExecuteAsync(CancellationToken.None)); + Assert.AreEqual(ErrorReason.DependencyInstallationFailed, exception.Reason); } private void SetupDefaultMockBehavior(PlatformID platformID) diff --git a/src/VirtualClient/VirtualClient.Dependencies.UnitTests/OpenFOAMInstallationTests.cs b/src/VirtualClient/VirtualClient.Dependencies.UnitTests/OpenFOAMInstallationTests.cs index e138dd42af..3069a35800 100644 --- a/src/VirtualClient/VirtualClient.Dependencies.UnitTests/OpenFOAMInstallationTests.cs +++ b/src/VirtualClient/VirtualClient.Dependencies.UnitTests/OpenFOAMInstallationTests.cs @@ -52,22 +52,56 @@ public void TearDown() public async Task OpenFOAMInstallationDependencyStartsCorrectProcessesOnExecute(PlatformID platform, Architecture architecture) { this.SetupDefaultMockBehavior(platform, architecture); + List expectedCommands = new List(); + if (architecture == Architecture.X64) { - this.SetupProcessManager("sudo", AddPublicKeyCommand); - this.SetupProcessManager("sudo", UpdateSoftwareRepositoriesCommand); - this.SetupProcessManager("sudo", UpdateAptPackageCommand); - this.SetupProcessManager("sudo", InstallOpenFOAMx64Command); + expectedCommands = new List + { + $"sudo {AddPublicKeyCommand}", + $"sudo {UpdateSoftwareRepositoriesCommand}", + $"sudo {UpdateAptPackageCommand}", + $"sudo {InstallOpenFOAMx64Command}" + }; } else { - this.SetupProcessManager("sudo", UpdateAptPackageCommand); - this.SetupProcessManager("sudo", InstallOpenFOAMarm64Command); + expectedCommands = new List + { + $"sudo {UpdateAptPackageCommand}", + $"sudo {InstallOpenFOAMarm64Command}" + }; + } + + int commandExecuted = 0; + this.fixture.ProcessManager.OnCreateProcess = (exe, arguments, workingDir) => + { + if (expectedCommands[commandExecuted] == $"{exe} {arguments}") + { + commandExecuted++; + } + + IProcessProxy process = new InMemoryProcess + { + StartInfo = new ProcessStartInfo + { + FileName = exe, + Arguments = arguments + }, + ExitCode = 0, + OnStart = () => true, + OnHasExited = () => true + }; + + return process; + }; + using (TestComponent toolkitInstallation = new TestComponent(this.fixture.Dependencies, this.fixture.Parameters)) + { + await toolkitInstallation.ExecuteAsync(CancellationToken.None) + .ConfigureAwait(false); } - - await this.component.ExecuteAsync(CancellationToken.None); - this.mockProcessManager.Verify(); + Assert.AreEqual(expectedCommands.Count, commandExecuted); } [Test] @@ -77,25 +111,16 @@ public void OpenFOAMInstallationDependencySurfacesExceptionWhenProcessDoesNotExi { this.SetupDefaultMockBehavior(platform, architecture); - if (architecture == Architecture.X64) + this.fixture.ProcessManager.OnCreateProcess = (command, arguments, workingDirectory) => { - this.SetupProcessManager("sudo", AddPublicKeyCommand); - this.SetupProcessManager("sudo", UpdateSoftwareRepositoriesCommand); - this.SetupProcessManager("sudo", UpdateAptPackageCommand); - var setup = this.SetupProcessManager("sudo", InstallOpenFOAMx64Command); - - setup.Returns(OpenFOAMInstallationTests.GetProcessProxy(1)); - } - else - { - this.SetupProcessManager("sudo", UpdateAptPackageCommand); - var setup = this.SetupProcessManager("sudo", InstallOpenFOAMarm64Command); - - setup.Returns(OpenFOAMInstallationTests.GetProcessProxy(1)); - } - - DependencyException exc = Assert.ThrowsAsync(() => this.component.ExecuteAsync(CancellationToken.None)); - Assert.AreEqual(ErrorReason.DependencyInstallationFailed, exc.Reason); + this.fixture.Process.ExitCode = 1; + this.fixture.Process.OnHasExited = () => true; + return this.fixture.Process; + }; + + using TestComponent component = new TestComponent(this.fixture.Dependencies, this.fixture.Parameters); + DependencyException exception = Assert.ThrowsAsync(() => component.ExecuteAsync(CancellationToken.None)); + Assert.AreEqual(ErrorReason.DependencyInstallationFailed, exception.Reason); } private void SetupDefaultMockBehavior(PlatformID platform = PlatformID.Unix, Architecture architecture = Architecture.X64) @@ -123,28 +148,6 @@ private void SetupDefaultMockBehavior(PlatformID platform = PlatformID.Unix, Arc .Returns(false); } - private ISetup SetupProcessManager(string expectedCmd, string expectedArgs = null, string expectedWorkingDirectory = null) - { - ISetup setup = this.mockProcessManager.Setup(mgr => mgr.CreateProcess( - It.Is(cmd => cmd.Equals(expectedCmd)), - It.Is(args => args == null || args.Equals(expectedArgs)), - It.Is(wd => wd == null || wd.Equals(expectedWorkingDirectory)))); - - setup.Verifiable(); - setup.Returns(OpenFOAMInstallationTests.GetProcessProxy()); - return setup; - } - - private static IProcessProxy GetProcessProxy(int exitCode = 0) - { - Mock process = new Mock(); - process.SetupGet(p => p.ExitCode).Returns(exitCode); - process.SetupGet(p => p.HasExited).Returns(true); - process.SetupGet(p => p.StartInfo).Returns(new ProcessStartInfo()); - process.SetupGet(p => p.ProcessDetails).Returns(new ProcessDetails()); - return process.Object; - } - private class TestComponent : OpenFOAMInstallation { public TestComponent(IServiceCollection services, IDictionary parameters = null) diff --git a/src/VirtualClient/VirtualClient.Dependencies/CUDAAndNvidiaGPUDriverInstallation.cs b/src/VirtualClient/VirtualClient.Dependencies/CUDAAndNvidiaGPUDriverInstallation.cs index 65f741ea17..218342961a 100644 --- a/src/VirtualClient/VirtualClient.Dependencies/CUDAAndNvidiaGPUDriverInstallation.cs +++ b/src/VirtualClient/VirtualClient.Dependencies/CUDAAndNvidiaGPUDriverInstallation.cs @@ -243,8 +243,13 @@ await this.fileSystem.File.WriteAllLinesAsync( { foreach (string command in commandsList) { - await this.ExecuteCommandAsync(command, null, Environment.CurrentDirectory, telemetryContext, cancellationToken) - .ConfigureAwait(false); + using (IProcessProxy process = await this.ExecuteCommandAsync(command, Environment.CurrentDirectory, telemetryContext, cancellationToken, runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfDependencyInstallationFailed(); + } + } } } } @@ -394,32 +399,13 @@ private async Task CudaAndNvidiaGPUDriverInstallationOnWindowsAsync(EventContext throw new DependencyException($"The installer file was not found in the directory {nvidiaDriverInstallerPackage.Path}", ErrorReason.DependencyNotFound); } - await this.ExecuteCommandAsync(installerPath, "-y -s", Environment.CurrentDirectory, telemetryContext, cancellationToken) - .ConfigureAwait(false); - } - - private Task ExecuteCommandAsync(string commandLine, string commandLineArgs, string workingDirectory, EventContext telemetryContext, CancellationToken cancellationToken) - { - return this.RetryPolicy.ExecuteAsync(async () => + using (IProcessProxy process = await this.ExecuteCommandAsync(installerPath, "-y -s", Environment.CurrentDirectory, telemetryContext, cancellationToken, runElevated: true)) { - string output = string.Empty; - using (IProcessProxy process = this.systemManager.ProcessManager.CreateElevatedProcess(this.Platform, commandLine, commandLineArgs, workingDirectory)) + if (!cancellationToken.IsCancellationRequested) { - this.CleanupTasks.Add(() => process.SafeKill()); - this.LogProcessTrace(process); - - await process.StartAndWaitAsync(cancellationToken) - .ConfigureAwait(false); - - if (!cancellationToken.IsCancellationRequested) - { - await this.LogProcessDetailsAsync(process, telemetryContext, "GpuDriverInstallation") - .ConfigureAwait(false); - - process.ThrowIfErrored(errorReason: ErrorReason.DependencyInstallationFailed); - } + process.ThrowIfDependencyInstallationFailed(); } - }); + } } } } diff --git a/src/VirtualClient/VirtualClient.Dependencies/CompilerInstallation.cs b/src/VirtualClient/VirtualClient.Dependencies/CompilerInstallation.cs index c3c0228639..e4e49bc0d0 100644 --- a/src/VirtualClient/VirtualClient.Dependencies/CompilerInstallation.cs +++ b/src/VirtualClient/VirtualClient.Dependencies/CompilerInstallation.cs @@ -103,7 +103,7 @@ protected override async Task ExecuteAsync(EventContext telemetryContext, Cancel { await this.InstallGccAsync(this.CompilerVersion, telemetryContext, cancellationToken); - if (!string.IsNullOrEmpty(this.CompilerVersion) && !await this.ConfirmGccVersionInstalledAsync(cancellationToken)) + if (!string.IsNullOrEmpty(this.CompilerVersion) && !await this.ConfirmGccVersionInstalledAsync(telemetryContext, cancellationToken)) { throw new DependencyException($"gcc compiler version '{this.CompilerVersion}' not confirmed.", ErrorReason.DependencyInstallationFailed); } @@ -149,30 +149,28 @@ protected override async Task ExecuteAsync(EventContext telemetryContext, Cancel /// /// Waits for the expected version of the compiler to be confirmed installed on the system. /// + /// The telemetry context for the operation. /// A token that can be used to cancel the operation. - protected async Task ConfirmGccVersionInstalledAsync(CancellationToken cancellationToken) + protected async Task ConfirmGccVersionInstalledAsync(EventContext telemetryContext, CancellationToken cancellationToken) { List compilersToCheck = new List() { Compilers.Gcc, Compilers.Cc }; int confirmedCompilers = 0; foreach (string compiler in compilersToCheck) { Regex versionConfirmationExpression = new Regex($@"{compiler}\s*\([\x20-\x7F]+\)\s*{this.CompilerVersion}", RegexOptions.Multiline); - using (IProcessProxy process = this.systemManager.ProcessManager.CreateProcess(compiler, "--version")) - { - this.Logger.LogTraceMessage($"Confirming expected compiler version installed..."); - await process.StartAndWaitAsync(cancellationToken); + using (IProcessProxy process = await this.ExecuteCommandAsync(compiler, "--version", Environment.CurrentDirectory, telemetryContext, cancellationToken)) + { + this.Logger.LogTraceMessage($"Confirming expected compiler version installed...", telemetryContext); if (!cancellationToken.IsCancellationRequested) { - await this.LogProcessDetailsAsync(process, EventContext.Persisted(), "CompilerVersion"); - - ConsoleLogger.Default.LogTraceMessage(compiler + "2" + process.StandardOutput.ToString()); + this.Logger.LogTraceMessage(compiler + "2" + process.StandardOutput.ToString(), telemetryContext); if (versionConfirmationExpression.IsMatch(process.StandardOutput.ToString())) { confirmedCompilers++; - this.Logger.LogTraceMessage($"Compiler {compiler} confirmed for version {this.CompilerVersion}."); + this.Logger.LogTraceMessage($"Compiler {compiler} confirmed for version {this.CompilerVersion}.", telemetryContext); } } } @@ -190,7 +188,7 @@ private async Task ConfirmCompilerVerionsMatchAsync(EventContext telemetry return gccVersion == ccVersion && ccVersion == gfortranVersion; } - private Task InstallCygwinAsync(DependencyPath cygwinInstallationPath, EventContext telemetryContext, CancellationToken cancellationToken) + private async Task InstallCygwinAsync(DependencyPath cygwinInstallationPath, EventContext telemetryContext, CancellationToken cancellationToken) { string cygwinCommandArguments; string cygwinInstallerPath = this.Combine(cygwinInstallationPath.Path, "cygwinsetup.exe"); @@ -204,7 +202,13 @@ private Task InstallCygwinAsync(DependencyPath cygwinInstallationPath, EventCont cygwinCommandArguments = @$"--quiet-mode --root {cygwinInstallationPath.Path} --site http://cygwin.mirror.constant.com --packages make,cmake"; } - return this.ExecuteCommandAsync(cygwinInstallerPath, cygwinCommandArguments, Environment.CurrentDirectory, telemetryContext, cancellationToken); + using (IProcessProxy process = await this.ExecuteCommandAsync(cygwinInstallerPath, cygwinCommandArguments, Environment.CurrentDirectory, telemetryContext, cancellationToken, runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfDependencyInstallationFailed(); + } + } } private async Task InstallGccAsync(string gccVersion, EventContext telemetryContext, CancellationToken cancellationToken) @@ -213,23 +217,27 @@ private async Task InstallGccAsync(string gccVersion, EventContext telemetryCont gccVersion = (string.IsNullOrEmpty(gccVersion)) ? string.Empty : gccVersion; string installedVersion = await this.GetInstalledCompilerDumpVersionAsync("gcc", telemetryContext, cancellationToken); + List commands = new List(); + switch (distro.LinuxDistribution) { case LinuxDistribution.Ubuntu: case LinuxDistribution.Debian: - await this.ExecuteCommandAsync("add-apt-repository", $"ppa:ubuntu-toolchain-r/test -y", Environment.CurrentDirectory, telemetryContext, cancellationToken); - await this.ExecuteCommandAsync("apt", $"update", Environment.CurrentDirectory, telemetryContext, cancellationToken); + commands.Add("add-apt-repository ppa:ubuntu-toolchain-r/test -y"); + commands.Add("apt update"); if (string.IsNullOrEmpty(gccVersion) && string.IsNullOrEmpty(installedVersion)) { - await this.ExecuteCommandAsync("apt", "purge gcc -y", Environment.CurrentDirectory, telemetryContext, cancellationToken); - await this.ExecuteCommandAsync("apt", "install build-essential gcc g++ gfortran make -y --quiet", Environment.CurrentDirectory, telemetryContext, cancellationToken); + commands.Add("apt purge gcc -y"); + commands.Add("apt install build-essential gcc g++ gfortran make -y --quiet"); } else if (!string.IsNullOrEmpty(gccVersion)) { await this.RemoveAlternativesAsync(telemetryContext, cancellationToken); - await this.ExecuteCommandAsync("apt", @$"install build-essential gcc-{gccVersion} g++-{gccVersion} gfortran-{gccVersion} -y --quiet", Environment.CurrentDirectory, telemetryContext, cancellationToken); - await this.SetGccPriorityAsync(gccVersion, telemetryContext, cancellationToken); + commands.Add($"apt install build-essential gcc-{gccVersion} g++-{gccVersion} gfortran-{gccVersion} -y --quiet"); + + List setPriorityCommands = this.SetGccPriorityAsync(gccVersion); + commands = commands.Concat(setPriorityCommands).ToList(); } break; @@ -238,18 +246,21 @@ private async Task InstallGccAsync(string gccVersion, EventContext telemetryCont case LinuxDistribution.RHEL8: if (string.IsNullOrEmpty(gccVersion) && string.IsNullOrEmpty(installedVersion)) { - await this.ExecuteCommandAsync("dnf", "install kernel-headers kernel-devel -y", Environment.CurrentDirectory, telemetryContext, cancellationToken); - await this.ExecuteCommandAsync("dnf", "install binutils -y", Environment.CurrentDirectory, telemetryContext, cancellationToken); - await this.ExecuteCommandAsync("dnf", "install glibc-headers glibc-devel -y", Environment.CurrentDirectory, telemetryContext, cancellationToken); - await this.ExecuteCommandAsync("dnf", "install git -y", Environment.CurrentDirectory, telemetryContext, cancellationToken); - await this.ExecuteCommandAsync("dnf", "install libnsl -y", Environment.CurrentDirectory, telemetryContext, cancellationToken); - await this.ExecuteCommandAsync("dnf", "install make gcc -y", Environment.CurrentDirectory, telemetryContext, cancellationToken); + commands.Add("dnf install kernel-headers kernel-devel -y"); + commands.Add("dnf install binutils -y"); + commands.Add("dnf install glibc-headers glibc-devel -y"); + commands.Add("dnf install git -y"); + commands.Add("dnf install libnsl -y"); + commands.Add("dnf install make gcc -y"); } else if (!string.IsNullOrEmpty(gccVersion) && string.IsNullOrEmpty(installedVersion)) { await this.RemoveAlternativesAsync(telemetryContext, cancellationToken); - await this.ExecuteCommandAsync("dnf", @$"install make gcc-toolset-{gccVersion} gcc-toolset-{gccVersion}-gcc-gfortran -y --quiet", Environment.CurrentDirectory, telemetryContext, cancellationToken); - await this.SetGccPriorityAsync(gccVersion, telemetryContext, cancellationToken); + + commands.Add($"dnf install make gcc-toolset-{gccVersion} gcc-toolset-{gccVersion}-gcc-gfortran -y --quiet"); + + IEnumerable setPriorityCommands = this.SetGccPriorityAsync(gccVersion); + commands.Concat(setPriorityCommands); } break; @@ -260,17 +271,28 @@ private async Task InstallGccAsync(string gccVersion, EventContext telemetryCont throw new Exception($"gcc version must not be supplied for {distro.LinuxDistribution}"); } - await this.ExecuteCommandAsync("dnf", "install kernel-headers kernel-devel -y", Environment.CurrentDirectory, telemetryContext, cancellationToken); - await this.ExecuteCommandAsync("dnf", "install binutils -y", Environment.CurrentDirectory, telemetryContext, cancellationToken); - await this.ExecuteCommandAsync("dnf", "install glibc-headers glibc-devel -y", Environment.CurrentDirectory, telemetryContext, cancellationToken); - await this.ExecuteCommandAsync("dnf", "install git -y", Environment.CurrentDirectory, telemetryContext, cancellationToken); - await this.ExecuteCommandAsync("dnf", "install gcc gfortran -y", Environment.CurrentDirectory, telemetryContext, cancellationToken); + commands.Add("dnf install kernel-headers kernel-devel -y"); + commands.Add("dnf install binutils -y"); + commands.Add("dnf install glibc-headers glibc-devel -y"); + commands.Add("dnf install git -y"); + commands.Add("dnf install gcc gfortran -y"); break; default: throw new PlatformNotSupportedException($"This Linux distribution '{distro}' is not supported for this profile."); } + + foreach (string command in commands) + { + using (IProcessProxy process = await this.ExecuteCommandAsync(command, Environment.CurrentDirectory, telemetryContext, cancellationToken, runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfDependencyInstallationFailed(); + } + } + } } private async Task RemoveAlternativesAsync(EventContext telemetryContext, CancellationToken cancellationToken) @@ -289,7 +311,13 @@ private async Task RemoveAlternativesAsync(EventContext telemetryContext, Cancel { try { - await this.ExecuteCommandAsync("update-alternatives", $"--remove-all {package}", Environment.CurrentDirectory, telemetryContext, cancellationToken, this.successCodes); + using (IProcessProxy process = await this.ExecuteCommandAsync("update-alternatives", $"--remove-all {package}", Environment.CurrentDirectory, telemetryContext, cancellationToken, runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfErrored(successCodes: this.successCodes, errorReason: ErrorReason.DependencyInstallationFailed); + } + } } catch { @@ -300,7 +328,7 @@ private async Task RemoveAlternativesAsync(EventContext telemetryContext, Cancel } } - private async Task SetGccPriorityAsync(string gccVersion, EventContext telemetryContext, CancellationToken cancellationToken) + private List SetGccPriorityAsync(string gccVersion) { string updateAlternativeArgument = $"--install /usr/bin/gcc gcc /usr/bin/gcc-{gccVersion} {gccVersion}0 " + $"--slave /usr/bin/g++ g++ /usr/bin/g++-{gccVersion} " + @@ -309,57 +337,59 @@ private async Task SetGccPriorityAsync(string gccVersion, EventContext telemetry $"--slave /usr/bin/gcc-ranlib gcc-ranlib /usr/bin/gcc-ranlib-{gccVersion} " + $"--slave /usr/bin/gfortran gfortran /usr/bin/gfortran-{gccVersion}"; - await this.ExecuteCommandAsync("update-alternatives", updateAlternativeArgument, Environment.CurrentDirectory, telemetryContext, cancellationToken); - - // Remove all existing alternatives for cpp before the subsequent "update-alternatives" of cpp - await this.ExecuteCommandAsync("update-alternatives", "--remove-all cpp", Environment.CurrentDirectory, telemetryContext, cancellationToken); - - // For some update path, the cpp can't be update-alternative by a gcc, so needs a separate call. - string updateAlternativeArgumentCpp = $"--install /usr/bin/cpp cpp /usr/bin/cpp-{gccVersion} {gccVersion}0"; - - await this.ExecuteCommandAsync("update-alternatives", updateAlternativeArgumentCpp, Environment.CurrentDirectory, telemetryContext, cancellationToken); + return new List() + { + $"update-alternatives {updateAlternativeArgument}", + $"update-alternatives --remove-all cpp", + $"update-alternatives --install /usr/bin/cpp cpp /usr/bin/cpp-{gccVersion} {gccVersion}0", + }; } private async Task InstallCharmplusplusAsync(string charmplusplusVersion, EventContext telemetryContext, CancellationToken cancellationToken) { // default latest charmplusplusVersion = (string.IsNullOrEmpty(charmplusplusVersion)) ? "latest" : charmplusplusVersion; - await this.ExecuteCommandAsync("wget", $"https://charm.cs.illinois.edu/distrib/charm-{charmplusplusVersion}.tar.gz -O charm.tar.gz", Environment.CurrentDirectory, telemetryContext, cancellationToken); - await this.ExecuteCommandAsync("tar", $"-xzf charm.tar.gz", Environment.CurrentDirectory, telemetryContext, cancellationToken); + + using (IProcessProxy process = await this.ExecuteCommandAsync("wget", $"https://charm.cs.illinois.edu/distrib/charm-{charmplusplusVersion}.tar.gz -O charm.tar.gz", Environment.CurrentDirectory, telemetryContext, cancellationToken, runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfDependencyInstallationFailed(); + } + } + + using (IProcessProxy process = await this.ExecuteCommandAsync("tar", $"-xzf charm.tar.gz", Environment.CurrentDirectory, telemetryContext, cancellationToken, runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfDependencyInstallationFailed(); + } + } + string charmPath = Directory.GetDirectories(Environment.CurrentDirectory, "charm-v*").FirstOrDefault(); if (this.CpuArchitecture == System.Runtime.InteropServices.Architecture.X64) { - await this.ExecuteCommandAsync("./build", "charm++ netlrts-linux-x86_64 --with-production -j4", workingDirectory: charmPath, telemetryContext, cancellationToken); + using (IProcessProxy process = await this.ExecuteCommandAsync("./build", "charm++ netlrts-linux-x86_64 --with-production -j4", workingDirectory: charmPath, telemetryContext, cancellationToken, runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfDependencyInstallationFailed(); + } + } } if (this.CpuArchitecture == System.Runtime.InteropServices.Architecture.Arm64) { - await this.ExecuteCommandAsync("./build", "charm++ netlrts-linux-arm8 --with-production -j4", workingDirectory: charmPath, telemetryContext, cancellationToken); + using (IProcessProxy process = await this.ExecuteCommandAsync("./build", "charm++ netlrts-linux-arm8 --with-production -j4", workingDirectory: charmPath, telemetryContext, cancellationToken, runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfDependencyInstallationFailed(); + } + } } } - private Task ExecuteCommandAsync(string pathToExe, string commandLineArguments, string workingDirectory, EventContext telemetryContext, CancellationToken cancellationToken, int[] successCodes = null) - { - return this.RetryPolicy.ExecuteAsync(async () => - { - string output = string.Empty; - using (IProcessProxy process = this.systemManager.ProcessManager.CreateElevatedProcess(this.Platform, pathToExe, commandLineArguments, workingDirectory)) - { - this.CleanupTasks.Add(() => process.SafeKill()); - this.LogProcessTrace(process); - - await process.StartAndWaitAsync(cancellationToken); - - if (!cancellationToken.IsCancellationRequested) - { - await this.LogProcessDetailsAsync(process, telemetryContext); - - process.ThrowIfErrored(successCodes: this.successCodes, errorReason: ErrorReason.DependencyInstallationFailed); - } - } - }); - } - private async Task GetInstalledCompilerDumpVersionAsync(string compilerName, EventContext telemetryContext, CancellationToken cancellationToken) { string command = compilerName; @@ -367,18 +397,11 @@ private async Task GetInstalledCompilerDumpVersionAsync(string compilerN string version = string.Empty; - using (IProcessProxy process = this.systemManager.ProcessManager.CreateElevatedProcess(this.Platform, command, commandArguments)) + using (IProcessProxy process = await this.ExecuteCommandAsync(command, commandArguments, Environment.CurrentDirectory, telemetryContext, cancellationToken, runElevated: true)) { try { - await process.StartAndWaitAsync(cancellationToken); - - if (!cancellationToken.IsCancellationRequested) - { - await this.LogProcessDetailsAsync(process, telemetryContext); - - version = process.StandardOutput.ToString().Trim().Split(".")[0]; - } + version = process.StandardOutput.ToString().Trim().Split(".")[0]; } catch { diff --git a/src/VirtualClient/VirtualClient.Dependencies/DCGMIInstallation.cs b/src/VirtualClient/VirtualClient.Dependencies/DCGMIInstallation.cs index f5b537da1e..0565391989 100644 --- a/src/VirtualClient/VirtualClient.Dependencies/DCGMIInstallation.cs +++ b/src/VirtualClient/VirtualClient.Dependencies/DCGMIInstallation.cs @@ -133,8 +133,13 @@ private async Task InstallDCGMIUbuntuOrDebianAsync(EventContext telemetryContext commands.Add(enableDCGMCommand); foreach (string command in commands) { - await this.ExecuteCommandAsync(command, Environment.CurrentDirectory, telemetryContext, cancellationToken) - .ConfigureAwait(false); + using (IProcessProxy process = await this.ExecuteCommandAsync(command, Environment.CurrentDirectory, telemetryContext, cancellationToken, runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfWorkloadFailed(); + } + } } } @@ -164,8 +169,13 @@ private async Task InstallDCGMIRHELOrCentOSAsync(EventContext telemetryContext, commands.Add(enableDCGMCommand); foreach (string command in commands) { - await this.ExecuteCommandAsync(command, Environment.CurrentDirectory, telemetryContext, cancellationToken) - .ConfigureAwait(false); + using (IProcessProxy process = await this.ExecuteCommandAsync(command, Environment.CurrentDirectory, telemetryContext, cancellationToken, runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfWorkloadFailed(); + } + } } } @@ -197,32 +207,14 @@ private async Task InstallDCGMISUSEAsync(EventContext telemetryContext, Cancella commands.Add(enableDCGMCommand); foreach (string command in commands) { - await this.ExecuteCommandAsync(command, Environment.CurrentDirectory, telemetryContext, cancellationToken) - .ConfigureAwait(false); - } - } - - private Task ExecuteCommandAsync(string commandLine, string workingDirectory, EventContext telemetryContext, CancellationToken cancellationToken) - { - EventContext relatedContext = telemetryContext.Clone(); - - return this.RetryPolicy.ExecuteAsync(async () => - { - string output = string.Empty; - using (IProcessProxy process = this.systemManager.ProcessManager.CreateElevatedProcess(this.Platform, commandLine, null, workingDirectory)) + using (IProcessProxy process = await this.ExecuteCommandAsync(command, Environment.CurrentDirectory, telemetryContext, cancellationToken, runElevated: true)) { - this.CleanupTasks.Add(() => process.SafeKill()); - this.Logger.LogTraceMessage($"Executing process '{commandLine}' at directory '{workingDirectory}'.", EventContext.Persisted()); - - await process.StartAndWaitAsync(cancellationToken).ConfigureAwait(false); - if (!cancellationToken.IsCancellationRequested) { - await this.LogProcessDetailsAsync(process, relatedContext, logToFile: true); - process.ThrowIfDependencyInstallationFailed(); + process.ThrowIfWorkloadFailed(); } } - }); + } } } } diff --git a/src/VirtualClient/VirtualClient.Dependencies/DockerInstallation.cs b/src/VirtualClient/VirtualClient.Dependencies/DockerInstallation.cs index 6bb194671a..fb58360249 100644 --- a/src/VirtualClient/VirtualClient.Dependencies/DockerInstallation.cs +++ b/src/VirtualClient/VirtualClient.Dependencies/DockerInstallation.cs @@ -5,6 +5,7 @@ namespace VirtualClient.Dependencies { using System; using System.Collections.Generic; + using System.CommandLine; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; @@ -131,57 +132,26 @@ await this.DockerInstallInUbuntuAsync(telemetryContext, cancellationToken) private async Task DockerInstallInUbuntuAsync(EventContext telemetryContext, CancellationToken cancellationToken) { - string updateAptPackageCommand = "apt update"; - string requiredPackagesCommand = "apt-get install ca-certificates curl gnupg lsb-release --yes --quiet"; - string addOfficialGPGKeyCommand = @"bash -c ""curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg --batch --yes"""; - string setUpRepositoryCommand = + IEnumerable commands = new List() + { + "apt update", + "apt-get install ca-certificates curl gnupg lsb-release --yes --quiet", + @"bash -c ""curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg --batch --yes""", @"bash -c ""echo """"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"""" " + - @"| sudo tee /etc/apt/sources.list.d/docker.list > /dev/null"""; - - await this.ExecuteCommandAsync(updateAptPackageCommand, Environment.CurrentDirectory, telemetryContext, cancellationToken) - .ConfigureAwait(false); - - await this.ExecuteCommandAsync(requiredPackagesCommand, Environment.CurrentDirectory, telemetryContext, cancellationToken) - .ConfigureAwait(false); - - await this.ExecuteCommandAsync("mkdir -p /etc/apt/keyrings", Environment.CurrentDirectory, telemetryContext, cancellationToken) - .ConfigureAwait(false); - - await this.ExecuteCommandAsync(addOfficialGPGKeyCommand, Environment.CurrentDirectory, telemetryContext, cancellationToken) - .ConfigureAwait(false); - - await this.ExecuteCommandAsync(setUpRepositoryCommand, Environment.CurrentDirectory, telemetryContext, cancellationToken) - .ConfigureAwait(false); + @"| sudo tee /etc/apt/sources.list.d/docker.list > /dev/null""", + this.installDockerCommand + }; - await this.ExecuteCommandAsync(updateAptPackageCommand, Environment.CurrentDirectory, telemetryContext, cancellationToken) - .ConfigureAwait(false); - - await this.ExecuteCommandAsync(this.installDockerCommand, Environment.CurrentDirectory, telemetryContext, cancellationToken) - .ConfigureAwait(false); - } - - private Task ExecuteCommandAsync(string commandLine, string workingDirectory, EventContext telemetryContext, CancellationToken cancellationToken) - { - return this.RetryPolicy.ExecuteAsync(async () => + foreach (string command in commands) { - string output = string.Empty; - using (IProcessProxy process = this.systemManager.ProcessManager.CreateElevatedProcess(this.Platform, commandLine, null, workingDirectory)) + using (IProcessProxy process = await this.ExecuteCommandAsync(command, Environment.CurrentDirectory, telemetryContext, cancellationToken, runElevated: true)) { - this.CleanupTasks.Add(() => process.SafeKill()); - this.LogProcessTrace(process); - - await process.StartAndWaitAsync(cancellationToken) - .ConfigureAwait(false); - if (!cancellationToken.IsCancellationRequested) { - await this.LogProcessDetailsAsync(process, telemetryContext) - .ConfigureAwait(false); - - process.ThrowIfErrored(errorReason: ErrorReason.DependencyInstallationFailed); + process.ThrowIfDependencyInstallationFailed(); } } - }); + } } } } diff --git a/src/VirtualClient/VirtualClient.Dependencies/DotNet/DotNetInstallation.cs b/src/VirtualClient/VirtualClient.Dependencies/DotNet/DotNetInstallation.cs index 6f0afbc649..109c4bccbd 100644 --- a/src/VirtualClient/VirtualClient.Dependencies/DotNet/DotNetInstallation.cs +++ b/src/VirtualClient/VirtualClient.Dependencies/DotNet/DotNetInstallation.cs @@ -77,11 +77,26 @@ protected override async Task ExecuteAsync(EventContext telemetryContext, Cancel if (this.Platform == PlatformID.Unix) { await this.systemManagement.MakeFileExecutableAsync(destinyFile, this.Platform, cancellationToken).ConfigureAwait(false); - await this.ExecuteCommandAsync(destinyFile, this.GetInstallArgument(), this.installDirectory, telemetryContext, cancellationToken).ConfigureAwait(false); + + using (IProcessProxy process = await this.ExecuteCommandAsync(destinyFile, this.GetInstallArgument(), this.installDirectory, telemetryContext, cancellationToken, runElevated: true) + .ConfigureAwait(false)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfWorkloadFailed(errorMessage: process.StandardError.ToString()); + } + } } else { - await this.ExecuteCommandAsync("powershell", $"{destinyFile} {this.GetInstallArgument()}", this.installDirectory, telemetryContext, cancellationToken).ConfigureAwait(false); + using (IProcessProxy process = await this.ExecuteCommandAsync("powershell", $"{destinyFile} {this.GetInstallArgument()}", this.installDirectory, telemetryContext, cancellationToken, runElevated: true) + .ConfigureAwait(false)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfWorkloadFailed(errorMessage: process.StandardError.ToString()); + } + } } DependencyPath dotnetPackage = new DependencyPath(this.PackageName, this.installDirectory, "DotNet SDK", this.DotNetVersion); @@ -105,25 +120,5 @@ private string GetInstallArgument() return argument; } - - private async Task ExecuteCommandAsync(string pathToExe, string commandLineArguments, string workingDirectory, EventContext telemetryContext, CancellationToken cancellationToken) - { - EventContext relatedContext = telemetryContext.Clone(); - using (IProcessProxy process = this.systemManagement.ProcessManager.CreateElevatedProcess(this.Platform, pathToExe, commandLineArguments, workingDirectory)) - { - this.CleanupTasks.Add(() => process.SafeKill()); - this.Logger.LogTraceMessage($"Executing process '{pathToExe}' '{commandLineArguments}' at directory '{workingDirectory}'.", EventContext.Persisted()); - - await process.StartAndWaitAsync(cancellationToken).ConfigureAwait(false); - - if (!cancellationToken.IsCancellationRequested) - { - await this.LogProcessDetailsAsync(process, relatedContext) - .ConfigureAwait(false); - - process.ThrowIfErrored(ProcessProxy.DefaultSuccessCodes, errorReason: ErrorReason.DependencyInstallationFailed); - } - } - } } } \ No newline at end of file diff --git a/src/VirtualClient/VirtualClient.Dependencies/NvidiaContainerToolkitInstallation.cs b/src/VirtualClient/VirtualClient.Dependencies/NvidiaContainerToolkitInstallation.cs index 68e1735b8e..ee11390053 100644 --- a/src/VirtualClient/VirtualClient.Dependencies/NvidiaContainerToolkitInstallation.cs +++ b/src/VirtualClient/VirtualClient.Dependencies/NvidiaContainerToolkitInstallation.cs @@ -100,8 +100,13 @@ private async Task NvidiaContainerToolkitInstallationAsync(LinuxDistribution lin foreach (string command in installationCommands) { - await this.ExecuteCommandAsync(command, Environment.CurrentDirectory, telemetryContext, cancellationToken) - .ConfigureAwait(false); + using (IProcessProxy process = await this.ExecuteCommandAsync(command, Environment.CurrentDirectory, telemetryContext, cancellationToken, runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfDependencyInstallationFailed(); + } + } } } @@ -178,30 +183,5 @@ private List NvidiaContainerToolkitInstallationCommands(LinuxDistributio return commands; } - - private Task ExecuteCommandAsync(string commandLine, string workingDirectory, EventContext telemetryContext, CancellationToken cancellationToken) - { - EventContext relatedContext = telemetryContext.Clone(); - - return this.RetryPolicy.ExecuteAsync(async () => - { - using (IProcessProxy process = this.systemManager.ProcessManager.CreateElevatedProcess(this.Platform, commandLine, null, workingDirectory)) - { - this.CleanupTasks.Add(() => process.SafeKill()); - this.LogProcessTrace(process); - - await process.StartAndWaitAsync(cancellationToken) - .ConfigureAwait(false); - - if (!cancellationToken.IsCancellationRequested) - { - await this.LogProcessDetailsAsync(process, relatedContext, "NvidiaToolkitInstallation") - .ConfigureAwait(false); - - process.ThrowIfErrored(errorReason: ErrorReason.DependencyInstallationFailed); - } - } - }); - } } } diff --git a/src/VirtualClient/VirtualClient.Dependencies/OpenFOAMInstallation.cs b/src/VirtualClient/VirtualClient.Dependencies/OpenFOAMInstallation.cs index 7d8802e2a5..d4535eb037 100644 --- a/src/VirtualClient/VirtualClient.Dependencies/OpenFOAMInstallation.cs +++ b/src/VirtualClient/VirtualClient.Dependencies/OpenFOAMInstallation.cs @@ -76,8 +76,13 @@ private async Task InstallOpenFOAM(CancellationToken cancellationToken, EventCon foreach (var command in installationCommands) { - await this.ExecuteCommandAsync(command, null, telemetryContext, cancellationToken) - .ConfigureAwait(false); + using (IProcessProxy process = await this.ExecuteCommandAsync(command, null, telemetryContext, cancellationToken, runElevated: true)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfDependencyInstallationFailed(); + } + } } } else @@ -87,23 +92,5 @@ await this.ExecuteCommandAsync(command, null, telemetryContext, cancellationToke ErrorReason.LinuxDistributionNotSupported); } } - - private async Task ExecuteCommandAsync(string command, string arguments, EventContext telemetryContext, CancellationToken cancellationToken) - { - using (IProcessProxy process = this.SystemManager.ProcessManager.CreateElevatedProcess(this.Platform, command, arguments)) - { - this.CleanupTasks.Add(() => process.SafeKill()); - await process.StartAndWaitAsync(cancellationToken, null) - .ConfigureAwait(false); - - if (!cancellationToken.IsCancellationRequested) - { - await this.LogProcessDetailsAsync(process, telemetryContext) - .ConfigureAwait(false); - - process.ThrowIfErrored(errorReason: ErrorReason.DependencyInstallationFailed); - } - } - } } } diff --git a/src/VirtualClient/VirtualClient.Dependencies/Packaging/AptPackageInstallation.cs b/src/VirtualClient/VirtualClient.Dependencies/Packaging/AptPackageInstallation.cs index c829d06d27..9a6de6029b 100644 --- a/src/VirtualClient/VirtualClient.Dependencies/Packaging/AptPackageInstallation.cs +++ b/src/VirtualClient/VirtualClient.Dependencies/Packaging/AptPackageInstallation.cs @@ -119,8 +119,14 @@ protected override async Task ExecuteAsync(EventContext telemetryContext, Cancel // Repo could only be add one by one foreach (string repo in repos) { - await this.ExecuteCommandAsync("add-apt-repository", $"\"{repo}\" -y", Environment.CurrentDirectory, telemetryContext, cancellationToken) - .ConfigureAwait(false); + using (IProcessProxy process = await this.ExecuteCommandAsync("add-apt-repository", $"\"{repo}\" -y", Environment.CurrentDirectory, telemetryContext, cancellationToken, runElevated: true) + .ConfigureAwait(false)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfDependencyInstallationFailed(); + } + } } } @@ -128,7 +134,7 @@ await this.ExecuteCommandAsync("add-apt-repository", $"\"{repo}\" -y", Environme List toInstall = new List(); foreach (string package in packages) { - if (!this.AllowUpgrades && await this.IsPackageInstalledAsync(package, cancellationToken)) + if (!this.AllowUpgrades && await this.IsPackageInstalledAsync(package, telemetryContext, cancellationToken)) { this.Logger.LogTraceMessage($"Package '{package}' is already installed, skipping.", EventContext.Persisted()); } @@ -149,23 +155,35 @@ await this.ExecuteCommandAsync("add-apt-repository", $"\"{repo}\" -y", Environme await this.InstallRetryPolicy.ExecuteAsync(async () => { // Runs apt update first. - await this.ExecuteCommandAsync(AptPackageInstallation.AptCommand, $"update", Environment.CurrentDirectory, telemetryContext, cancellationToken) - .ConfigureAwait(false); + using (IProcessProxy process = await this.ExecuteCommandAsync(AptPackageInstallation.AptCommand, $"update", Environment.CurrentDirectory, telemetryContext, cancellationToken, runElevated: true) + .ConfigureAwait(false)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfDependencyInstallationFailed(); + } + } // append DEBIAN_FRONTEND=noninteractive if installation is required to be non-interactive. string command = this.Interactive ? AptPackageInstallation.AptCommand : $"DEBIAN_FRONTEND=noninteractive {AptPackageInstallation.AptCommand}"; // Runs the installation command with retries and throws if the command fails after all // retries are expended. - await this.ExecuteCommandAsync(command, formattedArguments, Environment.CurrentDirectory, telemetryContext, cancellationToken) - .ConfigureAwait(false); + using (IProcessProxy process = await this.ExecuteCommandAsync(command, formattedArguments, Environment.CurrentDirectory, telemetryContext, cancellationToken, runElevated: true) + .ConfigureAwait(false)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfDependencyInstallationFailed(); + } + } }).ConfigureAwait(false); this.Logger.LogTraceMessage($"VirtualClient installed apt package(s): '[{string.Join(' ', toInstall)}]'.", EventContext.Persisted()); // Then, confirms that the packages were installed. - List failedPackages = toInstall.Where(package => !(this.IsPackageInstalledAsync(package, cancellationToken).GetAwaiter().GetResult())).ToList(); + List failedPackages = toInstall.Where(package => !(this.IsPackageInstalledAsync(package, telemetryContext, cancellationToken).GetAwaiter().GetResult())).ToList(); if (failedPackages?.Count > 0) { throw new ProcessException( @@ -174,49 +192,17 @@ await this.ExecuteCommandAsync(command, formattedArguments, Environment.CurrentD } } - private async Task IsPackageInstalledAsync(string packageName, CancellationToken cancellationToken) + private async Task IsPackageInstalledAsync(string packageName, EventContext telemetryContext, CancellationToken cancellationToken) { - using (IProcessProxy process = this.systemManagement.ProcessManager.CreateElevatedProcess(this.Platform, AptPackageInstallation.AptCommand, $"list {packageName}")) + using (IProcessProxy process = await this.ExecuteCommandAsync(AptPackageInstallation.AptCommand, $"list {packageName}", Environment.CurrentDirectory, telemetryContext, cancellationToken, runElevated: true)) { - this.CleanupTasks.Add(() => process.SafeKill()); - - await process.StartAndWaitAsync(cancellationToken) - .ConfigureAwait(false); - if (!cancellationToken.IsCancellationRequested) { - await this.LogProcessDetailsAsync(process, EventContext.Persisted(), "Apt") - .ConfigureAwait(false); - process.ThrowIfErrored(errorReason: ErrorReason.DependencyInstallationFailed); } return process.ExitCode == 0; } } - - private Task ExecuteCommandAsync(string pathToExe, string commandLineArguments, string workingDirectory, EventContext telemetryContext, CancellationToken cancellationToken) - { - EventContext relatedContext = telemetryContext.Clone(); - return this.InstallRetryPolicy.ExecuteAsync(async () => - { - string output = string.Empty; - using (IProcessProxy process = this.systemManagement.ProcessManager.CreateElevatedProcess(this.Platform, pathToExe, commandLineArguments, workingDirectory)) - { - this.CleanupTasks.Add(() => process.SafeKill()); - this.LogProcessTrace(process); - - await process.StartAndWaitAsync(cancellationToken).ConfigureAwait(false); - - if (!cancellationToken.IsCancellationRequested) - { - await this.LogProcessDetailsAsync(process, telemetryContext, "Apt") - .ConfigureAwait(false); - - process.ThrowIfErrored(errorReason: ErrorReason.DependencyInstallationFailed); - } - } - }); - } } } diff --git a/src/VirtualClient/VirtualClient.Dependencies/Packaging/DnfPackageInstallation.cs b/src/VirtualClient/VirtualClient.Dependencies/Packaging/DnfPackageInstallation.cs index 47678e2e8a..7cb98dbd2c 100644 --- a/src/VirtualClient/VirtualClient.Dependencies/Packaging/DnfPackageInstallation.cs +++ b/src/VirtualClient/VirtualClient.Dependencies/Packaging/DnfPackageInstallation.cs @@ -114,8 +114,11 @@ protected override async Task ExecuteAsync(EventContext telemetryContext, Cancel foreach (string repo in repos) { // https://dnf-plugins-core.readthedocs.io/en/latest/config_manager.html - await this.ExecuteCommandAsync(DnfPackageInstallation.DnfCommand, $"config-manager --add-repo {repo} -y", Environment.CurrentDirectory, telemetryContext, cancellationToken) - .ConfigureAwait(false); + using (IProcessProxy process = await this.ExecuteCommandAsync(DnfPackageInstallation.DnfCommand, $"config-manager --add-repo {repo} -y", Environment.CurrentDirectory, telemetryContext, cancellationToken) + .ConfigureAwait(false)) + { + process.ThrowIfErrored(DnfPackageInstallation.DnfSuccessfulCodes, process.StandardError.ToString(), ErrorReason.DependencyInstallationFailed); + } } } @@ -123,7 +126,7 @@ await this.ExecuteCommandAsync(DnfPackageInstallation.DnfCommand, $"config-manag List toInstall = new List(); foreach (string package in packages) { - if (!this.AllowUpgrades && await this.IsPackageInstalledAsync(package, cancellationToken)) + if (!this.AllowUpgrades && await this.IsPackageInstalledAsync(package, telemetryContext, cancellationToken)) { this.Logger.LogTraceMessage($"Package '{package}' is already installed, skipping.", EventContext.Persisted()); } @@ -144,20 +147,32 @@ await this.ExecuteCommandAsync(DnfPackageInstallation.DnfCommand, $"config-manag await this.InstallRetryPolicy.ExecuteAsync(async () => { // Runs Dnf update first. - await this.ExecuteCommandAsync(DnfPackageInstallation.DnfCommand, $"check-update -y", Environment.CurrentDirectory, telemetryContext, cancellationToken) - .ConfigureAwait(false); + using (IProcessProxy process = await this.ExecuteCommandAsync(DnfPackageInstallation.DnfCommand, $"check-update -y", Environment.CurrentDirectory, telemetryContext, cancellationToken, runElevated: true) + .ConfigureAwait(false)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfErrored(DnfPackageInstallation.DnfSuccessfulCodes, process.StandardError.ToString(), ErrorReason.DependencyInstallationFailed); + } + } // Runs the installation command with retries and throws if the command fails after all // retries are expended. - await this.ExecuteCommandAsync(DnfPackageInstallation.DnfCommand, formattedArguments, Environment.CurrentDirectory, telemetryContext, cancellationToken) - .ConfigureAwait(false); + using (IProcessProxy process = await this.ExecuteCommandAsync(DnfPackageInstallation.DnfCommand, formattedArguments, Environment.CurrentDirectory, telemetryContext, cancellationToken, runElevated: true) + .ConfigureAwait(false)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfErrored(DnfPackageInstallation.DnfSuccessfulCodes, process.StandardError.ToString(), ErrorReason.DependencyInstallationFailed); + } + } }).ConfigureAwait(false); this.Logger.LogTraceMessage($"VirtualClient installed Dnf package(s): '[{string.Join(' ', toInstall)}]'.", EventContext.Persisted()); // Then, confirms that the packages were installed. - List failedPackages = toInstall.Where(package => !(this.IsPackageInstalledAsync(package, cancellationToken).GetAwaiter().GetResult())).ToList(); + List failedPackages = toInstall.Where(package => !(this.IsPackageInstalledAsync(package, telemetryContext, cancellationToken).GetAwaiter().GetResult())).ToList(); if (failedPackages?.Count > 0) { @@ -167,51 +182,17 @@ await this.ExecuteCommandAsync(DnfPackageInstallation.DnfCommand, formattedArgum } } - private async Task IsPackageInstalledAsync(string packageName, CancellationToken cancellationToken) + private async Task IsPackageInstalledAsync(string packageName, EventContext telemetryContext, CancellationToken cancellationToken) { - ISystemManagement systemManagement = this.Dependencies.GetService(); - - using (IProcessProxy process = systemManagement.ProcessManager.CreateElevatedProcess(this.Platform, DnfPackageInstallation.DnfCommand, $"list {packageName}")) + using (IProcessProxy process = await this.ExecuteCommandAsync(DnfPackageInstallation.DnfCommand, $"list {packageName}", Environment.CurrentDirectory, telemetryContext, cancellationToken, runElevated: true)) { - this.CleanupTasks.Add(() => process.SafeKill()); - - await process.StartAndWaitAsync(cancellationToken) - .ConfigureAwait(false); - if (!cancellationToken.IsCancellationRequested) { - await this.LogProcessDetailsAsync(process, EventContext.Persisted(), "Dnf") - .ConfigureAwait(false); - process.ThrowIfErrored(errorReason: ErrorReason.DependencyInstallationFailed); } return process.ExitCode == 0; } } - - private Task ExecuteCommandAsync(string pathToExe, string commandLineArguments, string workingDirectory, EventContext telemetryContext, CancellationToken cancellationToken) - { - EventContext relatedContext = telemetryContext.Clone(); - return this.InstallRetryPolicy.ExecuteAsync(async () => - { - string output = string.Empty; - using (IProcessProxy process = this.systemManagement.ProcessManager.CreateElevatedProcess(this.Platform, pathToExe, commandLineArguments, workingDirectory)) - { - this.CleanupTasks.Add(() => process.SafeKill()); - this.Logger.LogTraceMessage($"Executing process '{pathToExe}' '{commandLineArguments}' at directory '{workingDirectory}'.", EventContext.Persisted()); - - await process.StartAndWaitAsync(cancellationToken).ConfigureAwait(false); - - if (!cancellationToken.IsCancellationRequested) - { - await this.LogProcessDetailsAsync(process, telemetryContext, "Dnf") - .ConfigureAwait(false); - - process.ThrowIfErrored(DnfPackageInstallation.DnfSuccessfulCodes, errorReason: ErrorReason.DependencyInstallationFailed); - } - } - }); - } } } diff --git a/src/VirtualClient/VirtualClient.Dependencies/Packaging/SnapPackageInstallation.cs b/src/VirtualClient/VirtualClient.Dependencies/Packaging/SnapPackageInstallation.cs index 2f8fd957e1..a041388ba2 100644 --- a/src/VirtualClient/VirtualClient.Dependencies/Packaging/SnapPackageInstallation.cs +++ b/src/VirtualClient/VirtualClient.Dependencies/Packaging/SnapPackageInstallation.cs @@ -97,7 +97,7 @@ await this.SetupSnapdSockets(telemetryContext, cancellationToken) List toInstall = new List(); foreach (string package in packages) { - if (!this.AllowUpgrades && await this.IsPackageInstalledAsync(package, cancellationToken)) + if (!this.AllowUpgrades && await this.IsPackageInstalledAsync(package, telemetryContext, cancellationToken)) { this.Logger.LogTraceMessage($"Package '{package}' is already installed, skipping.", EventContext.Persisted()); } @@ -118,17 +118,24 @@ await this.SetupSnapdSockets(telemetryContext, cancellationToken) await this.InstallRetryPolicy.ExecuteAsync(async () => { // Runs Snap update first. - await this.ExecuteCommandAsync(SnapPackageInstallation.SnapCommand, $"refresh", Environment.CurrentDirectory, telemetryContext, cancellationToken).ConfigureAwait(false); + using (IProcessProxy process = await this.ExecuteCommandAsync(SnapPackageInstallation.SnapCommand, $"refresh", Environment.CurrentDirectory, telemetryContext, cancellationToken, runElevated: true) + .ConfigureAwait(false)) + { + process.ThrowIfDependencyInstallationFailed(); + } // Runs the installation command with retries and throws if the command fails after all // retries are expended. - await this.ExecuteCommandAsync(SnapPackageInstallation.SnapCommand, formattedArguments, Environment.CurrentDirectory, telemetryContext, cancellationToken).ConfigureAwait(false); + using (IProcessProxy process = await this.ExecuteCommandAsync(SnapPackageInstallation.SnapCommand, formattedArguments, Environment.CurrentDirectory, telemetryContext, cancellationToken, runElevated: true)) + { + process.ThrowIfDependencyInstallationFailed(process.StandardError.ToString()); + } }).ConfigureAwait(false); this.Logger.LogTraceMessage($"VirtualClient installed Snap package(s): '[{string.Join(' ', toInstall)}]'.", EventContext.Persisted()); // Then, confirms that the packages were installed. - List failedPackages = toInstall.Where(package => !(this.IsPackageInstalledAsync(package, cancellationToken).GetAwaiter().GetResult())).ToList(); + List failedPackages = toInstall.Where(package => !(this.IsPackageInstalledAsync(package, telemetryContext, cancellationToken).GetAwaiter().GetResult())).ToList(); if (failedPackages?.Count > 0) { throw new ProcessException( @@ -142,64 +149,46 @@ private async Task SetupSnapdSockets(EventContext telemetryContext, Cancellation var linuxDistributionInfo = await this.systemManagement.GetLinuxDistributionAsync(cancellationToken) .ConfigureAwait(false); + List snapdSocketCommands = new List(); + // for ubuntu, debian, centos8, rhel8, etc. no socket enabling needed switch (linuxDistributionInfo.LinuxDistribution) { case LinuxDistribution.CentOS7: case LinuxDistribution.RHEL7: - await this.ExecuteCommandAsync(SnapPackageInstallation.SystemCtlCommand, $"enable --now snapd.socket", Environment.CurrentDirectory, telemetryContext, cancellationToken).ConfigureAwait(false); + snapdSocketCommands.Add($"enable --now snapd.socket"); break; case LinuxDistribution.SUSE: - await this.ExecuteCommandAsync(SnapPackageInstallation.SystemCtlCommand, $"enable --now snapd", Environment.CurrentDirectory, telemetryContext, cancellationToken).ConfigureAwait(false); - await this.ExecuteCommandAsync(SnapPackageInstallation.SystemCtlCommand, $"enable --now snapd.apparmor", Environment.CurrentDirectory, telemetryContext, cancellationToken).ConfigureAwait(false); + snapdSocketCommands.Add($"enable --now snapd"); + snapdSocketCommands.Add($"enable --now snapd.apparmor"); break; default: break; } - } - private async Task IsPackageInstalledAsync(string packageName, CancellationToken cancellationToken) - { - ISystemManagement systemManagement = this.Dependencies.GetService(); - - using (IProcessProxy process = systemManagement.ProcessManager.CreateElevatedProcess(this.Platform, SnapPackageInstallation.SnapCommand, $"list {packageName}")) + foreach (string command in snapdSocketCommands) { - this.CleanupTasks.Add(() => process.SafeKill()); - - await process.StartAndWaitAsync(cancellationToken) - .ConfigureAwait(false); - - if (!cancellationToken.IsCancellationRequested) + using (IProcessProxy process = await this.ExecuteCommandAsync(SnapPackageInstallation.SystemCtlCommand, command, Environment.CurrentDirectory, telemetryContext, cancellationToken, runElevated: true) + .ConfigureAwait(false)) { - await this.LogProcessDetailsAsync(process, EventContext.Persisted(), logToFile: true); process.ThrowIfDependencyInstallationFailed(); } - - return process.ExitCode == 0; } } - private Task ExecuteCommandAsync(string pathToExe, string commandLineArguments, string workingDirectory, EventContext telemetryContext, CancellationToken cancellationToken) + private async Task IsPackageInstalledAsync(string packageName, EventContext telemetryContext, CancellationToken cancellationToken) { - EventContext relatedContext = telemetryContext.Clone(); - return this.InstallRetryPolicy.ExecuteAsync(async () => + using (IProcessProxy process = await this.ExecuteCommandAsync(SnapPackageInstallation.SnapCommand, $"list {packageName}", Environment.CurrentDirectory, telemetryContext, cancellationToken, runElevated: true) + .ConfigureAwait(false)) { - string output = string.Empty; - using (IProcessProxy process = this.systemManagement.ProcessManager.CreateElevatedProcess(this.Platform, pathToExe, commandLineArguments, workingDirectory)) + if (!cancellationToken.IsCancellationRequested) { - this.CleanupTasks.Add(() => process.SafeKill()); - this.Logger.LogTraceMessage($"Executing process '{pathToExe}' '{commandLineArguments}' at directory '{workingDirectory}'.", EventContext.Persisted()); - - await process.StartAndWaitAsync(cancellationToken).ConfigureAwait(false); - - if (!cancellationToken.IsCancellationRequested) - { - await this.LogProcessDetailsAsync(process, telemetryContext, logToFile: true); - process.ThrowIfDependencyInstallationFailed(); - } + process.ThrowIfDependencyInstallationFailed(); } - }); + + return process.ExitCode == 0; + } } } } diff --git a/src/VirtualClient/VirtualClient.Dependencies/Packaging/YumPackageInstallation.cs b/src/VirtualClient/VirtualClient.Dependencies/Packaging/YumPackageInstallation.cs index db1334d3c7..24ad3d3b8a 100644 --- a/src/VirtualClient/VirtualClient.Dependencies/Packaging/YumPackageInstallation.cs +++ b/src/VirtualClient/VirtualClient.Dependencies/Packaging/YumPackageInstallation.cs @@ -117,7 +117,7 @@ await this.ExecuteCommandAsync("yum-config-manager", $"--enable {repo} -y", Envi List toInstall = new List(); foreach (string package in packages) { - if (!this.AllowUpgrades && await this.IsPackageInstalledAsync(package, cancellationToken)) + if (!this.AllowUpgrades && await this.IsPackageInstalledAsync(package, telemetryContext, cancellationToken)) { this.Logger.LogTraceMessage($"Package '{package}' is already installed, skipping."); } @@ -138,20 +138,32 @@ await this.ExecuteCommandAsync("yum-config-manager", $"--enable {repo} -y", Envi await this.InstallRetryPolicy.ExecuteAsync(async () => { // Runs Yum update first. - await this.ExecuteCommandAsync(YumPackageInstallation.YumCommand, $"update -y", Environment.CurrentDirectory, telemetryContext, cancellationToken) - .ConfigureAwait(false); + using (IProcessProxy process = await this.ExecuteCommandAsync(YumPackageInstallation.YumCommand, $"update -y", Environment.CurrentDirectory, telemetryContext, cancellationToken, runElevated: true) + .ConfigureAwait(false)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfErrored(ProcessProxy.DefaultSuccessCodes, errorReason: ErrorReason.DependencyInstallationFailed); + } + } // Runs the installation command with retries and throws if the command fails after all // retries are expended. - await this.ExecuteCommandAsync(YumPackageInstallation.YumCommand, formattedArguments, Environment.CurrentDirectory, telemetryContext, cancellationToken) - .ConfigureAwait(false); + using (IProcessProxy process = await this.ExecuteCommandAsync(YumPackageInstallation.YumCommand, formattedArguments, Environment.CurrentDirectory, telemetryContext, cancellationToken, runElevated: true) + .ConfigureAwait(false)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfErrored(ProcessProxy.DefaultSuccessCodes, errorReason: ErrorReason.DependencyInstallationFailed); + } + } }).ConfigureAwait(false); this.Logger.LogTraceMessage($"VirtualClient installed Yum package(s): '[{string.Join(' ', toInstall)}]'."); // Then, confirms that the packages were installed. - List failedPackages = toInstall.Where(package => !(this.IsPackageInstalledAsync(package, cancellationToken).GetAwaiter().GetResult())).ToList(); + List failedPackages = toInstall.Where(package => !(this.IsPackageInstalledAsync(package, telemetryContext, cancellationToken).GetAwaiter().GetResult())).ToList(); if (failedPackages?.Count > 0) { @@ -161,51 +173,18 @@ await this.ExecuteCommandAsync(YumPackageInstallation.YumCommand, formattedArgum } } - private async Task IsPackageInstalledAsync(string packageName, CancellationToken cancellationToken) + private async Task IsPackageInstalledAsync(string packageName, EventContext telemetryContext, CancellationToken cancellationToken) { - ISystemManagement systemManagement = this.Dependencies.GetService(); - - using (IProcessProxy process = systemManagement.ProcessManager.CreateElevatedProcess(this.Platform, YumPackageInstallation.YumCommand, $"list {packageName}")) + using (IProcessProxy process = await this.ExecuteCommandAsync(YumPackageInstallation.YumCommand, $"list {packageName}", Environment.CurrentDirectory, telemetryContext, cancellationToken, runElevated: true) + .ConfigureAwait(false)) { - this.CleanupTasks.Add(() => process.SafeKill()); - - await process.StartAndWaitAsync(cancellationToken) - .ConfigureAwait(false); - if (!cancellationToken.IsCancellationRequested) { - await this.LogProcessDetailsAsync(process, EventContext.Persisted(), "Yum") - .ConfigureAwait(false); - process.ThrowIfErrored(ProcessProxy.DefaultSuccessCodes, errorReason: ErrorReason.DependencyInstallationFailed); } return process.ExitCode == 0; } } - - private Task ExecuteCommandAsync(string pathToExe, string commandLineArguments, string workingDirectory, EventContext telemetryContext, CancellationToken cancellationToken) - { - EventContext relatedContext = telemetryContext.Clone(); - return this.InstallRetryPolicy.ExecuteAsync(async () => - { - string output = string.Empty; - using (IProcessProxy process = this.systemManagement.ProcessManager.CreateElevatedProcess(this.Platform, pathToExe, commandLineArguments, workingDirectory)) - { - this.CleanupTasks.Add(() => process.SafeKill()); - this.LogProcessTrace(process); - - await process.StartAndWaitAsync(cancellationToken).ConfigureAwait(false); - - if (!cancellationToken.IsCancellationRequested) - { - await this.LogProcessDetailsAsync(process, telemetryContext, "Yum") - .ConfigureAwait(false); - - process.ThrowIfErrored(ProcessProxy.DefaultSuccessCodes, errorReason: ErrorReason.DependencyInstallationFailed); - } - } - }); - } } } diff --git a/src/VirtualClient/VirtualClient.Dependencies/Packaging/ZypperPackageInstallation.cs b/src/VirtualClient/VirtualClient.Dependencies/Packaging/ZypperPackageInstallation.cs index b91ab1a50a..51fcf70657 100644 --- a/src/VirtualClient/VirtualClient.Dependencies/Packaging/ZypperPackageInstallation.cs +++ b/src/VirtualClient/VirtualClient.Dependencies/Packaging/ZypperPackageInstallation.cs @@ -120,7 +120,7 @@ await this.ExecuteCommandAsync( List toInstall = new List(); foreach (string package in packages) { - if (!this.AllowUpgrades && await this.IsPackageInstalledAsync(package, cancellationToken)) + if (!this.AllowUpgrades && await this.IsPackageInstalledAsync(package, telemetryContext, cancellationToken)) { this.Logger.LogTraceMessage($"Package '{package}' is already installed, skipping."); } @@ -141,20 +141,32 @@ await this.ExecuteCommandAsync( await this.InstallRetryPolicy.ExecuteAsync(async () => { // Runs Zypper update first. - await this.ExecuteCommandAsync(ZypperPackageInstallation.ZypperCommand, $"update", Environment.CurrentDirectory, telemetryContext, cancellationToken) - .ConfigureAwait(false); + using (IProcessProxy process = await this.ExecuteCommandAsync(ZypperPackageInstallation.ZypperCommand, $"update", Environment.CurrentDirectory, telemetryContext, cancellationToken, runElevated: true) + .ConfigureAwait(false)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfDependencyInstallationFailed(); + } + } // Runs the installation command with retries and throws if the command fails after all // retries are expended. - await this.ExecuteCommandAsync(ZypperPackageInstallation.ZypperCommand, formattedArguments, Environment.CurrentDirectory, telemetryContext, cancellationToken) - .ConfigureAwait(false); + using (IProcessProxy process = await this.ExecuteCommandAsync(ZypperPackageInstallation.ZypperCommand, formattedArguments, Environment.CurrentDirectory, telemetryContext, cancellationToken, runElevated: true) + .ConfigureAwait(false)) + { + if (!cancellationToken.IsCancellationRequested) + { + process.ThrowIfDependencyInstallationFailed(); + } + } }).ConfigureAwait(false); this.Logger.LogTraceMessage($"Installed Zypper package(s): '[{string.Join(' ', toInstall)}]'."); // Then, confirms that the packages were installed. - List failedPackages = toInstall.Where(package => !(this.IsPackageInstalledAsync(package, cancellationToken).GetAwaiter().GetResult())).ToList(); + List failedPackages = toInstall.Where(package => !(this.IsPackageInstalledAsync(package, telemetryContext, cancellationToken).GetAwaiter().GetResult())).ToList(); if (failedPackages?.Count > 0) { @@ -164,51 +176,18 @@ await this.ExecuteCommandAsync(ZypperPackageInstallation.ZypperCommand, formatte } } - private async Task IsPackageInstalledAsync(string packageName, CancellationToken cancellationToken) + private async Task IsPackageInstalledAsync(string packageName, EventContext telemetryContext, CancellationToken cancellationToken) { - ISystemManagement systemManagement = this.Dependencies.GetService(); - - using (IProcessProxy process = systemManagement.ProcessManager.CreateElevatedProcess(this.Platform, ZypperPackageInstallation.ZypperCommand, $"list {packageName}")) + using (IProcessProxy process = await this.ExecuteCommandAsync(ZypperPackageInstallation.ZypperCommand, $"list {packageName}", Environment.CurrentDirectory, telemetryContext, cancellationToken, runElevated: true) + .ConfigureAwait(false)) { - this.CleanupTasks.Add(() => process.SafeKill()); - - await process.StartAndWaitAsync(cancellationToken) - .ConfigureAwait(false); - if (!cancellationToken.IsCancellationRequested) { - await this.LogProcessDetailsAsync(process, EventContext.Persisted(), "Zypper") - .ConfigureAwait(false); - - process.ThrowIfErrored(errorReason: ErrorReason.DependencyInstallationFailed); + process.ThrowIfDependencyInstallationFailed(); } return process.ExitCode == 0; } } - - private Task ExecuteCommandAsync(string pathToExe, string commandLineArguments, string workingDirectory, EventContext telemetryContext, CancellationToken cancellationToken) - { - return this.InstallRetryPolicy.ExecuteAsync(async () => - { - string output = string.Empty; - using (IProcessProxy process = this.systemManagement.ProcessManager.CreateElevatedProcess(this.Platform, pathToExe, commandLineArguments, workingDirectory)) - { - this.CleanupTasks.Add(() => process.SafeKill()); - this.LogProcessTrace(process); - - await process.StartAndWaitAsync(cancellationToken) - .ConfigureAwait(false); - - if (!cancellationToken.IsCancellationRequested) - { - await this.LogProcessDetailsAsync(process, telemetryContext, "Zypper") - .ConfigureAwait(false); - - process.ThrowIfErrored(errorReason: ErrorReason.DependencyInstallationFailed); - } - } - }); - } } }