Beispiel #1
0
        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,
            }));
        }
Beispiel #2
0
        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);
        }
Beispiel #3
0
        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,
            });
        }
Beispiel #4
0
 public override RunningProcess ExecuteCommand(NodeCommand cmd)
 {
     return(ExecuteLocalCommand(cmd));
 }
Beispiel #5
0
 public abstract RunningProcess ExecuteCommand(NodeCommand cmd);
Beispiel #6
0
        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);
        }