Skip to content

Commit dce5d2a

Browse files
fix: support internal runner env overrides and keep runner folder (#605)
* these flags has been ignored in the experiment before
1 parent d8516b5 commit dce5d2a

4 files changed

Lines changed: 121 additions & 72 deletions

File tree

src/Runner.Client/ExternalQueueService.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ partial class Program
1414
{
1515
private class ExternalQueueService : Runner.Server.IQueueService, IRunnerServer
1616
{
17-
private string customConfigDir;
1817
private Parameters parameters;
1918
private SemaphoreSlim semaphore;
2019

@@ -118,18 +117,19 @@ public async void PickJob(AgentJobRequestMessage message, CancellationToken toke
118117
File.WriteAllText(Path.Join(tmpdir, ".runner"), "{\"isHostedServer\": false, \"agentName\": \"my-runner\", \"workFolder\": \"_work\"}");
119118
var ctx = new HostContext("EXTERNALRUNNERCLIENT", customConfigDir: tmpdir);
120119
ctx.PutService<IRunnerServer>(this);
121-
ctx.PutService<ExternalQueueService>(this);
122120
ctx.PutService<ITerminal>(new Terminal());
123121
ctx.PutServiceFactory<IProcessInvoker, WrapProcService>();
124122
var dispatcher = new GitHub.Runner.Listener.JobDispatcher();
125123
dispatcher.Initialize(ctx);
126124
dispatcher.Run(message, true);
127125
await dispatcher.WaitAsync(token);
128126
await dispatcher.ShutdownAsync();
129-
try {
130-
Directory.Delete(tmpdir, true);
131-
} catch {
132-
127+
if(parameters.KeepRunnerDirectory == false) {
128+
try {
129+
Directory.Delete(tmpdir, true);
130+
} catch {
131+
132+
}
133133
}
134134
} finally {
135135
semaphore.Release();

src/Runner.Client/Program.cs

Lines changed: 81 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,8 @@ private static async Task<int> CreateRunner(string binpath, Parameters parameter
284284
try {
285285
int attempt = 1;
286286
while(!source.IsCancellationRequested) {
287-
try {
287+
try
288+
{
288289
var inv = new GitHub.Runner.Sdk.ProcessInvoker(new TraceWriter(parameters));
289290
EventHandler<ProcessDataReceivedEventArgs> _out = (s, e) =>
290291
{
@@ -298,84 +299,90 @@ private static async Task<int> CreateRunner(string binpath, Parameters parameter
298299

299300
var systemEnv = System.Environment.GetEnvironmentVariables();
300301
var runnerEnv = new Dictionary<string, string>();
301-
foreach(var e in systemEnv.Keys) {
302+
foreach (var e in systemEnv.Keys)
303+
{
302304
runnerEnv[e as string] = systemEnv[e] as string;
303305
}
304306
runnerEnv["RUNNER_SERVER_CONFIG_ROOT"] = tmpdir;
305307
runnerEnv["GHARUN_CHANGE_PROCESS_GROUP"] = "1";
306-
if(!parameters.NoSharedToolcache && Environment.GetEnvironmentVariable("RUNNER_TOOL_CACHE") == null) {
307-
runnerEnv["RUNNER_TOOL_CACHE"] = Path.Combine(GitHub.Runner.Sdk.GharunUtil.GetLocalStorage(), "tool_cache");
308-
}
309-
if(parameters.ContainerDaemonSocket != null) {
310-
runnerEnv["RUNNER_CONTAINER_DAEMON_SOCKET"] = parameters.ContainerDaemonSocket;
311-
}
312-
if(parameters.ContainerPlatform != null) {
313-
runnerEnv["RUNNER_CONTAINER_ARCH"] = parameters.ContainerPlatform;
314-
}
315-
if(parameters.Privileged) {
316-
runnerEnv["RUNNER_CONTAINER_PRIVILEGED"] = "1";
317-
}
318-
if(parameters.Userns != null) {
319-
runnerEnv["RUNNER_CONTAINER_USERNS"] = parameters.Userns;
320-
}
321-
if(parameters.KeepContainer) {
322-
runnerEnv["RUNNER_CONTAINER_KEEP"] = "1";
323-
}
308+
ParameterToRunnerEnv(parameters, runnerEnv);
324309

325310
var arguments = $"configure --name {agentname} --unattended --url {parameters.Server}/runner/server --token {parameters.Token ?? "empty"} --labels container-host --work w";
326-
if(!string.IsNullOrWhiteSpace(binpath)) {
311+
if (!string.IsNullOrWhiteSpace(binpath))
312+
{
327313
#if !OS_LINUX && !OS_WINDOWS && !OS_OSX && !X64 && !X86 && !ARM && !ARM64
328314
arguments = $"\"{runner}\" {arguments}";
329315
#endif
330316
}
331-
317+
332318
var code = await inv.ExecuteAsync(binpath, file, arguments, runnerEnv, true, null, true, CancellationTokenSource.CreateLinkedTokenSource(source.Token, new CancellationTokenSource(60 * 1000).Token).Token);
333-
int execAttempt = 1;
334-
while(true) {
335-
try {
319+
int execAttempt = 1;
320+
while (true)
321+
{
322+
try
323+
{
336324
var runnerlistener = new GitHub.Runner.Sdk.ProcessInvoker(new TraceWriter(parameters));
337-
if(parameters.Verbose) {
325+
if (parameters.Verbose)
326+
{
338327
runnerlistener.OutputDataReceived += _out;
339328
runnerlistener.ErrorDataReceived += _out;
340329
}
341-
330+
342331
var runToken = CancellationTokenSource.CreateLinkedTokenSource(source.Token);
343-
using(var timer = new Timer(obj => {
332+
using (var timer = new Timer(obj =>
333+
{
344334
runToken.Cancel();
345-
}, null, 60 * 1000, -1)) {
346-
runnerlistener.OutputDataReceived += (s, e) => {
347-
if(e.Data.Contains("Listening for Jobs")) {
335+
}, null, 60 * 1000, -1))
336+
{
337+
runnerlistener.OutputDataReceived += (s, e) =>
338+
{
339+
if (e.Data.Contains("Listening for Jobs"))
340+
{
348341
timer.Change(-1, -1);
349342
workerchannel.Writer.WriteAsync(true);
350343
}
351344
};
352-
if(source.IsCancellationRequested) {
345+
if (source.IsCancellationRequested)
346+
{
353347
return 1;
354348
}
355349
arguments = $"run{(parameters.KeepContainer || parameters.NoReuse ? " --once" : "")}";
356-
if(!string.IsNullOrWhiteSpace(binpath)) {
350+
if (!string.IsNullOrWhiteSpace(binpath))
351+
{
357352
#if !OS_LINUX && !OS_WINDOWS && !OS_OSX && !X64 && !X86 && !ARM && !ARM64
358353
arguments = $"\"{runner}\" {arguments}";
359354
#endif
360355
}
361356
await runnerlistener.ExecuteAsync(binpath, file, arguments, runnerEnv, true, null, true, runToken.Token);
362357
break;
363358
}
364-
} catch {
365-
if(execAttempt++ <= 3) {
359+
}
360+
catch
361+
{
362+
if (execAttempt++ <= 3)
363+
{
366364
await Task.Delay(500);
367-
} else {
365+
}
366+
else
367+
{
368368
WriteLogMessage(parameters, "error", "Failed to start actions runner after 3 attempts");
369369
int delattempt = 1;
370-
while(true) {
371-
try {
370+
while (true)
371+
{
372+
try
373+
{
372374
Directory.Delete(tmpdir, true);
373375
break;
374-
} catch {
375-
if(delattempt++ >= 3) {
376+
}
377+
catch
378+
{
379+
if (delattempt++ >= 3)
380+
{
376381
WriteLogMessage(parameters, "error", $"Failed to cleanup {tmpdir} after 3 attempts");
377382
break;
378-
} else {
383+
}
384+
else
385+
{
379386
await Task.Delay(500);
380387
}
381388
}
@@ -385,7 +392,8 @@ private static async Task<int> CreateRunner(string binpath, Parameters parameter
385392
}
386393
}
387394
break;
388-
} catch {
395+
}
396+
catch {
389397
if(attempt++ <= 3) {
390398
await Task.Delay(500);
391399
} else {
@@ -442,6 +450,34 @@ private static async Task<int> CreateRunner(string binpath, Parameters parameter
442450
return 0;
443451
}
444452

453+
private static void ParameterToRunnerEnv(Parameters parameters, Dictionary<string, string> runnerEnv)
454+
{
455+
if (!parameters.NoSharedToolcache && Environment.GetEnvironmentVariable("RUNNER_TOOL_CACHE") == null)
456+
{
457+
runnerEnv["RUNNER_TOOL_CACHE"] = Path.Combine(GitHub.Runner.Sdk.GharunUtil.GetLocalStorage(), "tool_cache");
458+
}
459+
if (parameters.ContainerDaemonSocket != null)
460+
{
461+
runnerEnv["RUNNER_CONTAINER_DAEMON_SOCKET"] = parameters.ContainerDaemonSocket;
462+
}
463+
if (parameters.ContainerPlatform != null)
464+
{
465+
runnerEnv["RUNNER_CONTAINER_ARCH"] = parameters.ContainerPlatform;
466+
}
467+
if (parameters.Privileged)
468+
{
469+
runnerEnv["RUNNER_CONTAINER_PRIVILEGED"] = "1";
470+
}
471+
if (parameters.Userns != null)
472+
{
473+
runnerEnv["RUNNER_CONTAINER_USERNS"] = parameters.Userns;
474+
}
475+
if (parameters.KeepContainer)
476+
{
477+
runnerEnv["RUNNER_CONTAINER_KEEP"] = "1";
478+
}
479+
}
480+
445481
private static void WriteLogMessage(Parameters parameters, string level, string msg)
446482
{
447483
if (parameters.Json)
@@ -1549,7 +1585,9 @@ static int Main(string[] args)
15491585
.ConfigureServices(services => {
15501586
if(parameters.Parallel > 0 && !parameters.LegacyRunner) {
15511587
if(string.IsNullOrEmpty(parameters.RunnerPath)) {
1552-
services.Add(new ServiceDescriptor(typeof(IQueueService), p => new QueueService(parameters.RunnerDirectory, parameters.Parallel.Value), ServiceLifetime.Singleton));
1588+
Dictionary<string, string> env = new Dictionary<string, string>();
1589+
ParameterToRunnerEnv(parameters, env);
1590+
services.Add(new ServiceDescriptor(typeof(IQueueService), p => new QueueService(parameters.RunnerDirectory, parameters.Parallel.Value, parameters.KeepRunnerDirectory, env), ServiceLifetime.Singleton));
15531591
} else {
15541592
services.Add(new ServiceDescriptor(typeof(IQueueService), p => new ExternalQueueService(parameters), ServiceLifetime.Singleton));
15551593
}

src/Runner.Client/QueueService.cs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,18 @@ partial class Program
1414
{
1515
private class QueueService : Runner.Server.IQueueService, IRunnerServer
1616
{
17+
private readonly bool keepRunnerDirectory;
18+
public readonly Dictionary<string, string> env;
1719
private string customConfigDir;
1820

1921
private SemaphoreSlim semaphore;
2022

21-
public QueueService(string customConfigDir, int parallel)
23+
public QueueService(string customConfigDir, int parallel, bool keepRunnerDirectory, Dictionary<string, string> env)
2224
{
25+
this.keepRunnerDirectory = keepRunnerDirectory;
2326
this.customConfigDir = customConfigDir;
2427
semaphore = new SemaphoreSlim(parallel, parallel);
28+
this.env = env;
2529
}
2630

2731
public Task<TaskAgent> AddAgentAsync(int agentPoolId, TaskAgent agent)
@@ -121,10 +125,12 @@ public async void PickJob(AgentJobRequestMessage message, CancellationToken toke
121125
dispatcher.Run(message, true);
122126
await dispatcher.WaitAsync(token);
123127
await dispatcher.ShutdownAsync();
124-
try {
125-
Directory.Delete(tmpdir, true);
126-
} catch {
128+
if(keepRunnerDirectory == false) {
129+
try {
130+
Directory.Delete(tmpdir, true);
131+
} catch {
127132

133+
}
128134
}
129135
} finally {
130136
semaphore.Release();

src/Runner.Client/WrapProcService.cs

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,8 @@ public Task<int> ExecuteAsync(string workingDirectory, string fileName, string a
6969

7070
public Task<int> ExecuteAsync(string workingDirectory, string fileName, string arguments, IDictionary<string, string> environment, bool requireExitCodeZero, Encoding outputEncoding, bool killProcessOnCancel, Channel<string> redirectStandardIn, bool inheritConsoleHandler, bool keepStandardInOpen, bool highPriorityProcess, CancellationToken cancellationToken)
7171
{
72-
try {
73-
var queue = _context.GetService<ExternalQueueService>();
72+
var server = _context.GetService<IRunnerServer>();
73+
if(server is ExternalQueueService queue) {
7474
int i = arguments.IndexOf("spawnclient");
7575
if (!System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows))
7676
{
@@ -93,25 +93,30 @@ public Task<int> ExecuteAsync(string workingDirectory, string fileName, string a
9393
} else {
9494
return org.ExecuteAsync(workingDirectory, Path.Join(_context.GetDirectory(WellKnownDirectory.ConfigRoot), "bin", $"{queue.Prefix}.Worker{queue.Suffix}"), i == -1 ? arguments : arguments.Substring(i), environment, requireExitCodeZero, outputEncoding, killProcessOnCancel, redirectStandardIn, inheritConsoleHandler, keepStandardInOpen, highPriorityProcess, cancellationToken);
9595
}
96-
} catch {}
97-
if (!System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows))
98-
{
99-
var binpath = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
100-
if(string.IsNullOrWhiteSpace(binpath)) {
101-
arguments = $"spawn \"{fileName}\" {arguments}";
102-
fileName = Environment.ProcessPath;
103-
} else {
104-
#if !OS_LINUX && !OS_WINDOWS && !OS_OSX && !X64 && !X86 && !ARM && !ARM64
105-
arguments = $"\"{Path.Join(binpath, "Runner.Client.dll")}\" spawn \"{fileName}\" {arguments}";
106-
fileName = Sdk.Utils.DotNetMuxer.MuxerPath ?? WhichUtil.Which("dotnet", true);
107-
#else
108-
arguments = $"spawn \"{fileName}\" {arguments}";
109-
fileName = Path.Join(binpath, $"Runner.Client{IOUtil.ExeExtension}");
110-
#endif
96+
}
97+
if(server is QueueService queueService) {
98+
var env = new Dictionary<string, string>(queueService.env ?? new Dictionary<string, string>());
99+
env["RUNNER_SERVER_CONFIG_ROOT"] = _context.GetDirectory(WellKnownDirectory.ConfigRoot);
100+
if (!System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows))
101+
{
102+
var binpath = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
103+
if(string.IsNullOrWhiteSpace(binpath)) {
104+
arguments = $"spawn \"{fileName}\" {arguments}";
105+
fileName = Environment.ProcessPath;
106+
} else {
107+
#if !OS_LINUX && !OS_WINDOWS && !OS_OSX && !X64 && !X86 && !ARM && !ARM64
108+
arguments = $"\"{Path.Join(binpath, "Runner.Client.dll")}\" spawn \"{fileName}\" {arguments}";
109+
fileName = Sdk.Utils.DotNetMuxer.MuxerPath ?? WhichUtil.Which("dotnet", true);
110+
#else
111+
arguments = $"spawn \"{fileName}\" {arguments}";
112+
fileName = Path.Join(binpath, $"Runner.Client{IOUtil.ExeExtension}");
113+
#endif
114+
}
115+
return org.ExecuteAsync(workingDirectory, fileName, arguments, env, requireExitCodeZero, outputEncoding, killProcessOnCancel, redirectStandardIn, inheritConsoleHandler, keepStandardInOpen, highPriorityProcess, cancellationToken);
111116
}
112-
return org.ExecuteAsync(workingDirectory, fileName, arguments, new Dictionary<string, string>() { {"RUNNER_SERVER_CONFIG_ROOT", _context.GetDirectory(WellKnownDirectory.ConfigRoot)} }, requireExitCodeZero, outputEncoding, killProcessOnCancel, redirectStandardIn, inheritConsoleHandler, keepStandardInOpen, highPriorityProcess, cancellationToken);
117+
return org.ExecuteAsync(workingDirectory, fileName, arguments, env, requireExitCodeZero, outputEncoding, killProcessOnCancel, redirectStandardIn, inheritConsoleHandler, keepStandardInOpen, highPriorityProcess, cancellationToken);
113118
}
114-
return org.ExecuteAsync(workingDirectory, fileName, arguments, new Dictionary<string, string>() { {"RUNNER_SERVER_CONFIG_ROOT", _context.GetDirectory(WellKnownDirectory.ConfigRoot)} }, requireExitCodeZero, outputEncoding, killProcessOnCancel, redirectStandardIn, inheritConsoleHandler, keepStandardInOpen, highPriorityProcess, cancellationToken);
119+
return Task.FromException<int>(new NotImplementedException($"WrapProcService.ExecuteAsync not implemented for {server.GetType()}"));
115120
}
116121

117122
public void Initialize(IHostContext context)

0 commit comments

Comments
 (0)