public override RunningProcess ExecuteCommand(NodeCommand cmd) { var args = new List <string>(); args.Add("exec"); args.Add("-i"); foreach (var env in cmd.Env) { args.Add("-e"); args.Add(env.Key + "=" + env.Value); } args.Add(ContainerName); args.Add("/bin/sh"); args.Add("-c"); args.Add($"{cmd.FileName} {string.Join(" ", cmd.Arguments)}"); return(ExecuteLocalCommand(new NodeCommand { FileName = "docker", Arguments = args, Output = cmd.Output ?? InternalOutput, })); }
protected RunningProcess ExecuteLocalCommand(NodeCommand cmd) { var result = new RunningProcess(); TaskCompletionSource <bool> startedTask = new TaskCompletionSource <bool>(); TaskCompletionSource startedOutput = new TaskCompletionSource(); result.CancellationTokenSource = new CancellationTokenSource(); result.CompletedTask = ExecuteLocalCommandInternal(cmd, startedTask, startedOutput, result.CancellationTokenSource.Token); result.StartedTask = startedTask.Task; result.StartedOutput = startedOutput.Task; return(result); }
public override RunningProcess ExecuteCommand(NodeCommand cmd) { var cmdLine = $"{cmd.FileName} {string.Join(" ", cmd.Arguments)}"; cmd.Output.LogDebug("SSH: " + cmdLine); using var command = SshClient.CreateCommand(cmdLine); // hacky: https://stackoverflow.com/questions/37059305/c-sharp-streamreader-readline-returning-null-before-end-of-stream var result = command.BeginExecute(); var channelField = command.GetType().GetField("_channel", BindingFlags.Instance | BindingFlags.NonPublic); var channel = channelField.GetValue(command); var receivedEvent = channel.GetType().GetEvent("DataReceived", BindingFlags.Instance | BindingFlags.Public); var extendedDataEvent = channel.GetType().GetEvent("ExtendedDataReceived", BindingFlags.Instance | BindingFlags.Public); using var handler = new ReceivedHandler(cmd.Output); using var handler2 = new ExtendedReceivedHandler(cmd.Output); // add event handler here receivedEvent.AddEventHandler(channel, Delegate.CreateDelegate(receivedEvent.EventHandlerType, handler, handler.GetType().GetMethod("OnReceive"))); extendedDataEvent.AddEventHandler(channel, Delegate.CreateDelegate(extendedDataEvent.EventHandlerType, handler2, handler2.GetType().GetMethod("OnReceive"))); result.AsyncWaitHandle.WaitOne(); var procResult = new ProcessResult { ExitCode = command.ExitStatus, Completed = true, }; return(new RunningProcess { StartedTask = Task.FromResult(true), CompletedTask = Task.FromResult(procResult), StartedOutput = Task.CompletedTask, }); }
public override RunningProcess ExecuteCommand(NodeCommand cmd) { return(ExecuteLocalCommand(cmd)); }
public abstract RunningProcess ExecuteCommand(NodeCommand cmd);
private async Task <ProcessResult> ExecuteLocalCommandInternal(NodeCommand cmd, TaskCompletionSource <bool> startedTask, TaskCompletionSource startedOutput, CancellationToken cancellationToken) { var num = Interlocked.Increment(ref ExecCount); Context.InternalOutput.LogInformation("LocalExec #" + num.ToString() + ":" + cmd.FileName + " " + string.Join(" ", cmd.Arguments)); var result = new ProcessResult(); var startInfo = new ProcessStartInfo { FileName = cmd.FileName, UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardInput = true, RedirectStandardError = true, CreateNoWindow = true, }; foreach (var arg in cmd.Arguments) { startInfo.ArgumentList.Add(arg); } var process = new Process() { StartInfo = startInfo, }; var outputCloseEvent = new TaskCompletionSource <bool>(); process.OutputDataReceived += (s, e) => { lock (startedOutput) if (!startedOutput.Task.IsCompleted) { startedOutput.SetResult(); } // The output stream has been closed i.e. the process has terminated if (e.Data == null) { outputCloseEvent.SetResult(true); } else { cmd.Output.LogProcessStandartOutput(e.Data); } }; var errorCloseEvent = new TaskCompletionSource <bool>(); process.ErrorDataReceived += (s, e) => { lock (startedOutput) if (!startedOutput.Task.IsCompleted) { startedOutput.SetResult(); } // The error stream has been closed i.e. the process has terminated if (e.Data == null) { errorCloseEvent.SetResult(true); } else { cmd.Output.LogProcessErrorOutput(e.Data); } }; bool isStarted; try { isStarted = process.Start(); } catch { // Usually it occurs when an executable file is not found or is not executable result.Completed = true; result.ExitCode = -1; isStarted = false; } if (isStarted) { // Reads the output stream first and then waits because deadlocks are possible process.BeginOutputReadLine(); process.BeginErrorReadLine(); startedTask.SetResult(true); // Creates task to wait for process exit using timeout var waitForExit = WaitForExitAsync(process, cancellationToken); // Create task to wait for process exit and closing all output streams var processTask = Task.WhenAll(waitForExit, outputCloseEvent.Task, errorCloseEvent.Task); // Waits process completion and then checks it was not completed by timeout if (await Task.WhenAny(Task.Delay(CapTimeout(cmd.Timeout)), processTask) == processTask && !waitForExit.IsCanceled) { result.Completed = true; result.ExitCode = process.ExitCode; Context.InternalOutput.LogInformation("LocalExec #" + num.ToString() + ": ExitCode " + result.ExitCode); } else { try { // Kill hung process process.Kill(); Context.InternalOutput.LogError("LocalExec #" + num.ToString() + ": Killed"); } catch { } } } else { startedTask.SetResult(false); Context.InternalOutput.LogError("LocalExec #" + num.ToString() + ": Failed"); } return(result); }