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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 15 additions & 12 deletions src/Runner.Common/Util/UnixUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,21 +42,24 @@ public async Task ExecAsync(string workingDirectory, string toolName, string arg
string toolPath = WhichUtil.Which(toolName, trace: Trace);
Trace.Info($"Running {toolPath} {argLine}");

var processInvoker = HostContext.CreateService<IProcessInvoker>();
processInvoker.OutputDataReceived += OnOutputDataReceived;
processInvoker.ErrorDataReceived += OnErrorDataReceived;

try
// Dispose invoker per call to release process/CTS resources promptly.
using (var processInvoker = HostContext.CreateService<IProcessInvoker>())
{
using (var cs = new CancellationTokenSource(TimeSpan.FromSeconds(45)))
processInvoker.OutputDataReceived += OnOutputDataReceived;
processInvoker.ErrorDataReceived += OnErrorDataReceived;

try
{
await processInvoker.ExecuteAsync(workingDirectory, toolPath, argLine, null, true, cs.Token);
using (var cs = new CancellationTokenSource(TimeSpan.FromSeconds(45)))
{
await processInvoker.ExecuteAsync(workingDirectory, toolPath, argLine, null, true, cs.Token);
}
}
finally
{
processInvoker.OutputDataReceived -= OnOutputDataReceived;
processInvoker.ErrorDataReceived -= OnErrorDataReceived;
}
}
finally
{
processInvoker.OutputDataReceived -= OnOutputDataReceived;
processInvoker.ErrorDataReceived -= OnErrorDataReceived;
}
}

Expand Down
209 changes: 110 additions & 99 deletions src/Runner.Worker/Container/DockerCommandManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -309,45 +309,48 @@ public async Task<int> DockerExec(IExecutionContext context, string containerId,
{
ArgUtil.NotNull(output, nameof(output));

if (!Constants.Runner.Platform.Equals(Constants.OSPlatform.Linux))
{
throw new NotSupportedException("Container operations are only supported on Linux runners");
}

string arg = $"exec {options} {containerId} {command}".Trim();
context.Command($"{DockerPath} {arg}");

object outputLock = new();
var processInvoker = HostContext.CreateService<IProcessInvoker>();
processInvoker.OutputDataReceived += delegate (object sender, ProcessDataReceivedEventArgs message)
using (var processInvoker = HostContext.CreateService<IProcessInvoker>())
{
if (!string.IsNullOrEmpty(message.Data))
processInvoker.OutputDataReceived += delegate (object sender, ProcessDataReceivedEventArgs message)
{
lock (outputLock)
if (!string.IsNullOrEmpty(message.Data))
{
output.Add(message.Data);
lock (outputLock)
{
output.Add(message.Data);
}
}
}
};
};

processInvoker.ErrorDataReceived += delegate (object sender, ProcessDataReceivedEventArgs message)
{
if (!string.IsNullOrEmpty(message.Data))
processInvoker.ErrorDataReceived += delegate (object sender, ProcessDataReceivedEventArgs message)
{
lock (outputLock)
if (!string.IsNullOrEmpty(message.Data))
{
output.Add(message.Data);
lock (outputLock)
{
output.Add(message.Data);
}
}
}
};

if (!Constants.Runner.Platform.Equals(Constants.OSPlatform.Linux))
{
throw new NotSupportedException("Container operations are only supported on Linux runners");
};

return await processInvoker.ExecuteAsync(
workingDirectory: HostContext.GetDirectory(WellKnownDirectory.Work),
fileName: DockerPath,
arguments: arg,
environment: null,
requireExitCodeZero: false,
outputEncoding: null,
cancellationToken: CancellationToken.None);
}
return await processInvoker.ExecuteAsync(
workingDirectory: HostContext.GetDirectory(WellKnownDirectory.Work),
fileName: DockerPath,
arguments: arg,
environment: null,
requireExitCodeZero: false,
outputEncoding: null,
cancellationToken: CancellationToken.None);
}

public async Task<List<string>> DockerInspect(IExecutionContext context, string dockerObject, string options)
Expand All @@ -361,26 +364,27 @@ public async Task<List<PortMapping>> DockerPort(IExecutionContext context, strin
return DockerUtil.ParseDockerPort(portMappingLines);
}

public Task<int> DockerLogin(IExecutionContext context, string configFileDirectory, string registry, string username, string password)
public async Task<int> DockerLogin(IExecutionContext context, string configFileDirectory, string registry, string username, string password)
{
string args = $"--config {configFileDirectory} login {registry} -u {username} --password-stdin";
context.Command($"{DockerPath} {args}");

var input = Channel.CreateBounded<string>(new BoundedChannelOptions(1) { SingleReader = true, SingleWriter = true });
input.Writer.TryWrite(password);

var processInvoker = HostContext.CreateService<IProcessInvoker>();

return processInvoker.ExecuteAsync(
workingDirectory: context.GetGitHubContext("workspace"),
fileName: DockerPath,
arguments: args,
environment: null,
requireExitCodeZero: false,
outputEncoding: null,
killProcessOnCancel: false,
redirectStandardIn: input,
cancellationToken: context.CancellationToken);
using (var processInvoker = HostContext.CreateService<IProcessInvoker>())
{
return await processInvoker.ExecuteAsync(
workingDirectory: context.GetGitHubContext("workspace"),
fileName: DockerPath,
arguments: args,
environment: null,
requireExitCodeZero: false,
outputEncoding: null,
killProcessOnCancel: false,
redirectStandardIn: input,
cancellationToken: context.CancellationToken);
}
}

private Task<int> ExecuteDockerCommandAsync(IExecutionContext context, string command, string options, CancellationToken cancellationToken = default(CancellationToken))
Expand All @@ -390,59 +394,64 @@ public Task<int> DockerLogin(IExecutionContext context, string configFileDirecto

private async Task<int> ExecuteDockerCommandAsync(IExecutionContext context, string command, string options, IDictionary<string, string> environment, EventHandler<ProcessDataReceivedEventArgs> stdoutDataReceived, EventHandler<ProcessDataReceivedEventArgs> stderrDataReceived, CancellationToken cancellationToken = default(CancellationToken))
{
if (!Constants.Runner.Platform.Equals(Constants.OSPlatform.Linux))
{
throw new NotSupportedException("Container operations are only supported on Linux runners");
}

string arg = $"{command} {options}".Trim();
context.Command($"{DockerPath} {arg}");

var processInvoker = HostContext.CreateService<IProcessInvoker>();
processInvoker.OutputDataReceived += stdoutDataReceived;
processInvoker.ErrorDataReceived += stderrDataReceived;


if (!Constants.Runner.Platform.Equals(Constants.OSPlatform.Linux))
using (var processInvoker = HostContext.CreateService<IProcessInvoker>())
{
throw new NotSupportedException("Container operations are only supported on Linux runners");
processInvoker.OutputDataReceived += stdoutDataReceived;
processInvoker.ErrorDataReceived += stderrDataReceived;

return await processInvoker.ExecuteAsync(
workingDirectory: context.GetGitHubContext("workspace"),
fileName: DockerPath,
arguments: arg,
environment: environment,
requireExitCodeZero: false,
outputEncoding: null,
killProcessOnCancel: false,
cancellationToken: cancellationToken);
}
return await processInvoker.ExecuteAsync(
workingDirectory: context.GetGitHubContext("workspace"),
fileName: DockerPath,
arguments: arg,
environment: environment,
requireExitCodeZero: false,
outputEncoding: null,
killProcessOnCancel: false,
cancellationToken: cancellationToken);
}

private async Task<int> ExecuteDockerCommandAsync(IExecutionContext context, string command, string options, string workingDirectory, CancellationToken cancellationToken = default(CancellationToken))
{
if (!Constants.Runner.Platform.Equals(Constants.OSPlatform.Linux))
{
throw new NotSupportedException("Container operations are only supported on Linux runners");
}

string arg = $"{command} {options}".Trim();
context.Command($"{DockerPath} {arg}");

var processInvoker = HostContext.CreateService<IProcessInvoker>();
processInvoker.OutputDataReceived += delegate (object sender, ProcessDataReceivedEventArgs message)
{
context.Output(message.Data);
};

processInvoker.ErrorDataReceived += delegate (object sender, ProcessDataReceivedEventArgs message)
using (var processInvoker = HostContext.CreateService<IProcessInvoker>())
{
context.Output(message.Data);
};
processInvoker.OutputDataReceived += delegate (object sender, ProcessDataReceivedEventArgs message)
{
context.Output(message.Data);
};

if (!Constants.Runner.Platform.Equals(Constants.OSPlatform.Linux))
{
throw new NotSupportedException("Container operations are only supported on Linux runners");
processInvoker.ErrorDataReceived += delegate (object sender, ProcessDataReceivedEventArgs message)
{
context.Output(message.Data);
};

return await processInvoker.ExecuteAsync(
workingDirectory: workingDirectory ?? context.GetGitHubContext("workspace"),
fileName: DockerPath,
arguments: arg,
environment: null,
requireExitCodeZero: false,
outputEncoding: null,
killProcessOnCancel: false,
redirectStandardIn: null,
cancellationToken: cancellationToken);
}
return await processInvoker.ExecuteAsync(
workingDirectory: workingDirectory ?? context.GetGitHubContext("workspace"),
fileName: DockerPath,
arguments: arg,
environment: null,
requireExitCodeZero: false,
outputEncoding: null,
killProcessOnCancel: false,
redirectStandardIn: null,
cancellationToken: cancellationToken);
}

private async Task<List<string>> ExecuteDockerCommandAsync(IExecutionContext context, string command, string options)
Expand All @@ -451,32 +460,34 @@ private async Task<List<string>> ExecuteDockerCommandAsync(IExecutionContext con
context.Command($"{DockerPath} {arg}");

List<string> output = new();
var processInvoker = HostContext.CreateService<IProcessInvoker>();
processInvoker.OutputDataReceived += delegate (object sender, ProcessDataReceivedEventArgs message)
using (var processInvoker = HostContext.CreateService<IProcessInvoker>())
{
if (!string.IsNullOrEmpty(message.Data))
processInvoker.OutputDataReceived += delegate (object sender, ProcessDataReceivedEventArgs message)
{
output.Add(message.Data);
context.Output(message.Data);
}
};
if (!string.IsNullOrEmpty(message.Data))
{
output.Add(message.Data);
context.Output(message.Data);
}
};

processInvoker.ErrorDataReceived += delegate (object sender, ProcessDataReceivedEventArgs message)
{
if (!string.IsNullOrEmpty(message.Data))
processInvoker.ErrorDataReceived += delegate (object sender, ProcessDataReceivedEventArgs message)
{
context.Output(message.Data);
}
};

await processInvoker.ExecuteAsync(
workingDirectory: context.GetGitHubContext("workspace"),
fileName: DockerPath,
arguments: arg,
environment: null,
requireExitCodeZero: true,
outputEncoding: null,
cancellationToken: CancellationToken.None);
if (!string.IsNullOrEmpty(message.Data))
{
context.Output(message.Data);
}
};

await processInvoker.ExecuteAsync(
workingDirectory: context.GetGitHubContext("workspace"),
fileName: DockerPath,
arguments: arg,
environment: null,
requireExitCodeZero: true,
outputEncoding: null,
cancellationToken: CancellationToken.None);
}

return output;
}
Expand Down
48 changes: 25 additions & 23 deletions src/Runner.Worker/ContainerOperationProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -362,37 +362,39 @@ private async Task<List<string>> ExecuteCommandAsync(IExecutionContext context,

List<string> outputs = new();
object outputLock = new();
var processInvoker = HostContext.CreateService<IProcessInvoker>();
processInvoker.OutputDataReceived += delegate (object sender, ProcessDataReceivedEventArgs message)
using (var processInvoker = HostContext.CreateService<IProcessInvoker>())
{
if (!string.IsNullOrEmpty(message.Data))
processInvoker.OutputDataReceived += delegate (object sender, ProcessDataReceivedEventArgs message)
{
lock (outputLock)
if (!string.IsNullOrEmpty(message.Data))
{
outputs.Add(message.Data);
lock (outputLock)
{
outputs.Add(message.Data);
}
}
}
};
};

processInvoker.ErrorDataReceived += delegate (object sender, ProcessDataReceivedEventArgs message)
{
if (!string.IsNullOrEmpty(message.Data))
processInvoker.ErrorDataReceived += delegate (object sender, ProcessDataReceivedEventArgs message)
{
lock (outputLock)
if (!string.IsNullOrEmpty(message.Data))
{
outputs.Add(message.Data);
lock (outputLock)
{
outputs.Add(message.Data);
}
}
}
};

await processInvoker.ExecuteAsync(
workingDirectory: HostContext.GetDirectory(WellKnownDirectory.Work),
fileName: command,
arguments: arg,
environment: null,
requireExitCodeZero: true,
outputEncoding: null,
cancellationToken: CancellationToken.None);
};

await processInvoker.ExecuteAsync(
workingDirectory: HostContext.GetDirectory(WellKnownDirectory.Work),
fileName: command,
arguments: arg,
environment: null,
requireExitCodeZero: true,
outputEncoding: null,
cancellationToken: CancellationToken.None);
}

foreach (var outputLine in outputs)
{
Expand Down
Loading