Support return job result as exitcode in hosted runner. (#4233)
This commit is contained in:
@@ -24,7 +24,7 @@ namespace GitHub.Runner.Listener
|
|||||||
public interface IJobDispatcher : IRunnerService
|
public interface IJobDispatcher : IRunnerService
|
||||||
{
|
{
|
||||||
bool Busy { get; }
|
bool Busy { get; }
|
||||||
TaskCompletionSource<bool> RunOnceJobCompleted { get; }
|
TaskCompletionSource<TaskResult> RunOnceJobCompleted { get; }
|
||||||
void Run(Pipelines.AgentJobRequestMessage message, bool runOnce = false);
|
void Run(Pipelines.AgentJobRequestMessage message, bool runOnce = false);
|
||||||
bool Cancel(JobCancelMessage message);
|
bool Cancel(JobCancelMessage message);
|
||||||
Task WaitAsync(CancellationToken token);
|
Task WaitAsync(CancellationToken token);
|
||||||
@@ -56,7 +56,7 @@ namespace GitHub.Runner.Listener
|
|||||||
// timeout limit can be overwritten by environment GITHUB_ACTIONS_RUNNER_CHANNEL_TIMEOUT
|
// timeout limit can be overwritten by environment GITHUB_ACTIONS_RUNNER_CHANNEL_TIMEOUT
|
||||||
private TimeSpan _channelTimeout;
|
private TimeSpan _channelTimeout;
|
||||||
|
|
||||||
private TaskCompletionSource<bool> _runOnceJobCompleted = new();
|
private TaskCompletionSource<TaskResult> _runOnceJobCompleted = new();
|
||||||
|
|
||||||
public event EventHandler<JobStatusEventArgs> JobStatus;
|
public event EventHandler<JobStatusEventArgs> JobStatus;
|
||||||
|
|
||||||
@@ -82,7 +82,7 @@ namespace GitHub.Runner.Listener
|
|||||||
Trace.Info($"Set runner/worker IPC timeout to {_channelTimeout.TotalSeconds} seconds.");
|
Trace.Info($"Set runner/worker IPC timeout to {_channelTimeout.TotalSeconds} seconds.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public TaskCompletionSource<bool> RunOnceJobCompleted => _runOnceJobCompleted;
|
public TaskCompletionSource<TaskResult> RunOnceJobCompleted => _runOnceJobCompleted;
|
||||||
|
|
||||||
public bool Busy { get; private set; }
|
public bool Busy { get; private set; }
|
||||||
|
|
||||||
@@ -340,18 +340,19 @@ namespace GitHub.Runner.Listener
|
|||||||
|
|
||||||
private async Task RunOnceAsync(Pipelines.AgentJobRequestMessage message, string orchestrationId, WorkerDispatcher previousJobDispatch, CancellationToken jobRequestCancellationToken, CancellationToken workerCancelTimeoutKillToken)
|
private async Task RunOnceAsync(Pipelines.AgentJobRequestMessage message, string orchestrationId, WorkerDispatcher previousJobDispatch, CancellationToken jobRequestCancellationToken, CancellationToken workerCancelTimeoutKillToken)
|
||||||
{
|
{
|
||||||
|
var jobResult = TaskResult.Succeeded;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await RunAsync(message, orchestrationId, previousJobDispatch, jobRequestCancellationToken, workerCancelTimeoutKillToken);
|
jobResult = await RunAsync(message, orchestrationId, previousJobDispatch, jobRequestCancellationToken, workerCancelTimeoutKillToken);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
Trace.Info("Fire signal for one time used runner.");
|
Trace.Info("Fire signal for one time used runner.");
|
||||||
_runOnceJobCompleted.TrySetResult(true);
|
_runOnceJobCompleted.TrySetResult(jobResult);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task RunAsync(Pipelines.AgentJobRequestMessage message, string orchestrationId, WorkerDispatcher previousJobDispatch, CancellationToken jobRequestCancellationToken, CancellationToken workerCancelTimeoutKillToken)
|
private async Task<TaskResult> RunAsync(Pipelines.AgentJobRequestMessage message, string orchestrationId, WorkerDispatcher previousJobDispatch, CancellationToken jobRequestCancellationToken, CancellationToken workerCancelTimeoutKillToken)
|
||||||
{
|
{
|
||||||
Busy = true;
|
Busy = true;
|
||||||
try
|
try
|
||||||
@@ -399,7 +400,7 @@ namespace GitHub.Runner.Listener
|
|||||||
{
|
{
|
||||||
// renew job request task complete means we run out of retry for the first job request renew.
|
// renew job request task complete means we run out of retry for the first job request renew.
|
||||||
Trace.Info($"Unable to renew job request for job {message.JobId} for the first time, stop dispatching job to worker.");
|
Trace.Info($"Unable to renew job request for job {message.JobId} for the first time, stop dispatching job to worker.");
|
||||||
return;
|
return TaskResult.Abandoned;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (jobRequestCancellationToken.IsCancellationRequested)
|
if (jobRequestCancellationToken.IsCancellationRequested)
|
||||||
@@ -412,7 +413,7 @@ namespace GitHub.Runner.Listener
|
|||||||
|
|
||||||
// complete job request with result Cancelled
|
// complete job request with result Cancelled
|
||||||
await CompleteJobRequestAsync(_poolId, message, systemConnection, lockToken, TaskResult.Canceled);
|
await CompleteJobRequestAsync(_poolId, message, systemConnection, lockToken, TaskResult.Canceled);
|
||||||
return;
|
return TaskResult.Canceled;
|
||||||
}
|
}
|
||||||
|
|
||||||
HostContext.WritePerfCounter($"JobRequestRenewed_{requestId.ToString()}");
|
HostContext.WritePerfCounter($"JobRequestRenewed_{requestId.ToString()}");
|
||||||
@@ -523,7 +524,7 @@ namespace GitHub.Runner.Listener
|
|||||||
await renewJobRequest;
|
await renewJobRequest;
|
||||||
|
|
||||||
// not finish the job request since the job haven't run on worker at all, we will not going to set a result to server.
|
// not finish the job request since the job haven't run on worker at all, we will not going to set a result to server.
|
||||||
return;
|
return TaskResult.Failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
// we get first jobrequest renew succeed and start the worker process with the job message.
|
// we get first jobrequest renew succeed and start the worker process with the job message.
|
||||||
@@ -604,7 +605,7 @@ namespace GitHub.Runner.Listener
|
|||||||
Trace.Error(detailInfo);
|
Trace.Error(detailInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return TaskResultUtil.TranslateFromReturnCode(returnCode);
|
||||||
}
|
}
|
||||||
else if (completedTask == renewJobRequest)
|
else if (completedTask == renewJobRequest)
|
||||||
{
|
{
|
||||||
@@ -706,6 +707,8 @@ namespace GitHub.Runner.Listener
|
|||||||
|
|
||||||
// complete job request
|
// complete job request
|
||||||
await CompleteJobRequestAsync(_poolId, message, systemConnection, lockToken, resultOnAbandonOrCancel);
|
await CompleteJobRequestAsync(_poolId, message, systemConnection, lockToken, resultOnAbandonOrCancel);
|
||||||
|
|
||||||
|
return resultOnAbandonOrCancel;
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -324,8 +324,11 @@ namespace GitHub.Runner.Listener
|
|||||||
HostContext.EnableAuthMigration("EnableAuthMigrationByDefault");
|
HostContext.EnableAuthMigration("EnableAuthMigrationByDefault");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// hosted runner only run one job and would like to know the result of the job for telemetry and alerting on failure spike.
|
||||||
|
var returnJobResultForHosted = StringUtil.ConvertToBoolean(Environment.GetEnvironmentVariable("ACTIONS_RUNNER_RETURN_JOB_RESULT_FOR_HOSTED"));
|
||||||
|
|
||||||
// Run the runner interactively or as service
|
// Run the runner interactively or as service
|
||||||
return await ExecuteRunnerAsync(settings, command.RunOnce || settings.Ephemeral);
|
return await ExecuteRunnerAsync(settings, command.RunOnce || settings.Ephemeral || returnJobResultForHosted, returnJobResultForHosted);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -401,7 +404,7 @@ namespace GitHub.Runner.Listener
|
|||||||
}
|
}
|
||||||
|
|
||||||
//create worker manager, create message listener and start listening to the queue
|
//create worker manager, create message listener and start listening to the queue
|
||||||
private async Task<int> RunAsync(RunnerSettings settings, bool runOnce = false)
|
private async Task<int> RunAsync(RunnerSettings settings, bool runOnce = false, bool returnRunOnceJobResult = false)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -580,6 +583,21 @@ namespace GitHub.Runner.Listener
|
|||||||
Trace.Info($"Ignore any exception after cancel message loop. {ex}");
|
Trace.Info($"Ignore any exception after cancel message loop. {ex}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (returnRunOnceJobResult)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var jobResult = await jobDispatcher.RunOnceJobCompleted.Task;
|
||||||
|
return TaskResultUtil.TranslateToReturnCode(jobResult);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Trace.Error("run once job finished with error.");
|
||||||
|
Trace.Error(ex);
|
||||||
|
return Constants.Runner.ReturnCode.TerminatedError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return Constants.Runner.ReturnCode.Success;
|
return Constants.Runner.ReturnCode.Success;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -866,14 +884,14 @@ namespace GitHub.Runner.Listener
|
|||||||
return Constants.Runner.ReturnCode.Success;
|
return Constants.Runner.ReturnCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<int> ExecuteRunnerAsync(RunnerSettings settings, bool runOnce)
|
private async Task<int> ExecuteRunnerAsync(RunnerSettings settings, bool runOnce, bool returnRunOnceJobResult)
|
||||||
{
|
{
|
||||||
int returnCode = Constants.Runner.ReturnCode.Success;
|
int returnCode = Constants.Runner.ReturnCode.Success;
|
||||||
bool restart = false;
|
bool restart = false;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
restart = false;
|
restart = false;
|
||||||
returnCode = await RunAsync(settings, runOnce);
|
returnCode = await RunAsync(settings, runOnce, returnRunOnceJobResult);
|
||||||
|
|
||||||
if (returnCode == Constants.Runner.ReturnCode.RunnerConfigurationRefreshed)
|
if (returnCode == Constants.Runner.ReturnCode.RunnerConfigurationRefreshed)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -739,7 +739,8 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
Assert.True(jobDispatcher.RunOnceJobCompleted.Task.IsCompleted, "JobDispatcher should set task complete token for one time agent.");
|
Assert.True(jobDispatcher.RunOnceJobCompleted.Task.IsCompleted, "JobDispatcher should set task complete token for one time agent.");
|
||||||
if (jobDispatcher.RunOnceJobCompleted.Task.IsCompleted)
|
if (jobDispatcher.RunOnceJobCompleted.Task.IsCompleted)
|
||||||
{
|
{
|
||||||
Assert.True(await jobDispatcher.RunOnceJobCompleted.Task, "JobDispatcher should set task complete token to 'TRUE' for one time agent.");
|
var result = await jobDispatcher.RunOnceJobCompleted.Task;
|
||||||
|
Assert.Equal(TaskResult.Succeeded, result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -295,13 +295,13 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
_messageListener.Setup(x => x.DeleteMessageAsync(It.IsAny<TaskAgentMessage>()))
|
_messageListener.Setup(x => x.DeleteMessageAsync(It.IsAny<TaskAgentMessage>()))
|
||||||
.Returns(Task.CompletedTask);
|
.Returns(Task.CompletedTask);
|
||||||
|
|
||||||
var runOnceJobCompleted = new TaskCompletionSource<bool>();
|
var runOnceJobCompleted = new TaskCompletionSource<TaskResult>();
|
||||||
_jobDispatcher.Setup(x => x.RunOnceJobCompleted)
|
_jobDispatcher.Setup(x => x.RunOnceJobCompleted)
|
||||||
.Returns(runOnceJobCompleted);
|
.Returns(runOnceJobCompleted);
|
||||||
_jobDispatcher.Setup(x => x.Run(It.IsAny<Pipelines.AgentJobRequestMessage>(), It.IsAny<bool>()))
|
_jobDispatcher.Setup(x => x.Run(It.IsAny<Pipelines.AgentJobRequestMessage>(), It.IsAny<bool>()))
|
||||||
.Callback(() =>
|
.Callback(() =>
|
||||||
{
|
{
|
||||||
runOnceJobCompleted.TrySetResult(true);
|
runOnceJobCompleted.TrySetResult(TaskResult.Succeeded);
|
||||||
});
|
});
|
||||||
_jobNotification.Setup(x => x.StartClient(It.IsAny<String>()))
|
_jobNotification.Setup(x => x.StartClient(It.IsAny<String>()))
|
||||||
.Callback(() =>
|
.Callback(() =>
|
||||||
@@ -399,13 +399,13 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
_messageListener.Setup(x => x.DeleteMessageAsync(It.IsAny<TaskAgentMessage>()))
|
_messageListener.Setup(x => x.DeleteMessageAsync(It.IsAny<TaskAgentMessage>()))
|
||||||
.Returns(Task.CompletedTask);
|
.Returns(Task.CompletedTask);
|
||||||
|
|
||||||
var runOnceJobCompleted = new TaskCompletionSource<bool>();
|
var runOnceJobCompleted = new TaskCompletionSource<TaskResult>();
|
||||||
_jobDispatcher.Setup(x => x.RunOnceJobCompleted)
|
_jobDispatcher.Setup(x => x.RunOnceJobCompleted)
|
||||||
.Returns(runOnceJobCompleted);
|
.Returns(runOnceJobCompleted);
|
||||||
_jobDispatcher.Setup(x => x.Run(It.IsAny<Pipelines.AgentJobRequestMessage>(), It.IsAny<bool>()))
|
_jobDispatcher.Setup(x => x.Run(It.IsAny<Pipelines.AgentJobRequestMessage>(), It.IsAny<bool>()))
|
||||||
.Callback(() =>
|
.Callback(() =>
|
||||||
{
|
{
|
||||||
runOnceJobCompleted.TrySetResult(true);
|
runOnceJobCompleted.TrySetResult(TaskResult.Succeeded);
|
||||||
});
|
});
|
||||||
_jobNotification.Setup(x => x.StartClient(It.IsAny<String>()))
|
_jobNotification.Setup(x => x.StartClient(It.IsAny<String>()))
|
||||||
.Callback(() =>
|
.Callback(() =>
|
||||||
@@ -733,8 +733,8 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
|
|
||||||
_configStore.Setup(x => x.IsServiceConfigured()).Returns(false);
|
_configStore.Setup(x => x.IsServiceConfigured()).Returns(false);
|
||||||
|
|
||||||
var completedTask = new TaskCompletionSource<bool>();
|
var completedTask = new TaskCompletionSource<TaskResult>();
|
||||||
completedTask.SetResult(true);
|
completedTask.SetResult(TaskResult.Succeeded);
|
||||||
_jobDispatcher.Setup(x => x.RunOnceJobCompleted).Returns(completedTask);
|
_jobDispatcher.Setup(x => x.RunOnceJobCompleted).Returns(completedTask);
|
||||||
|
|
||||||
//Act
|
//Act
|
||||||
@@ -834,8 +834,8 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
|
|
||||||
_configStore.Setup(x => x.IsServiceConfigured()).Returns(false);
|
_configStore.Setup(x => x.IsServiceConfigured()).Returns(false);
|
||||||
|
|
||||||
var completedTask = new TaskCompletionSource<bool>();
|
var completedTask = new TaskCompletionSource<TaskResult>();
|
||||||
completedTask.SetResult(true);
|
completedTask.SetResult(TaskResult.Succeeded);
|
||||||
_jobDispatcher.Setup(x => x.RunOnceJobCompleted).Returns(completedTask);
|
_jobDispatcher.Setup(x => x.RunOnceJobCompleted).Returns(completedTask);
|
||||||
|
|
||||||
//Act
|
//Act
|
||||||
@@ -954,8 +954,8 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
|
|
||||||
_configStore.Setup(x => x.IsServiceConfigured()).Returns(false);
|
_configStore.Setup(x => x.IsServiceConfigured()).Returns(false);
|
||||||
|
|
||||||
var completedTask = new TaskCompletionSource<bool>();
|
var completedTask = new TaskCompletionSource<TaskResult>();
|
||||||
completedTask.SetResult(true);
|
completedTask.SetResult(TaskResult.Succeeded);
|
||||||
_jobDispatcher.Setup(x => x.RunOnceJobCompleted).Returns(completedTask);
|
_jobDispatcher.Setup(x => x.RunOnceJobCompleted).Returns(completedTask);
|
||||||
|
|
||||||
//Act
|
//Act
|
||||||
|
|||||||
Reference in New Issue
Block a user