mirror of
https://github.com/actions/runner.git
synced 2026-02-09 14:52:06 +08:00
Compare commits
1 Commits
copilot/su
...
users/tihu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
336ab6d5ca |
@@ -24,7 +24,7 @@ namespace GitHub.Runner.Listener
|
||||
public interface IJobDispatcher : IRunnerService
|
||||
{
|
||||
bool Busy { get; }
|
||||
TaskCompletionSource<bool> RunOnceJobCompleted { get; }
|
||||
TaskCompletionSource<int> RunOnceJobCompleted { get; }
|
||||
void Run(Pipelines.AgentJobRequestMessage message, bool runOnce = false);
|
||||
bool Cancel(JobCancelMessage message);
|
||||
Task WaitAsync(CancellationToken token);
|
||||
@@ -56,7 +56,7 @@ namespace GitHub.Runner.Listener
|
||||
// timeout limit can be overwritten by environment GITHUB_ACTIONS_RUNNER_CHANNEL_TIMEOUT
|
||||
private TimeSpan _channelTimeout;
|
||||
|
||||
private TaskCompletionSource<bool> _runOnceJobCompleted = new();
|
||||
private TaskCompletionSource<int> _runOnceJobCompleted = new();
|
||||
|
||||
public event EventHandler<JobStatusEventArgs> JobStatus;
|
||||
|
||||
@@ -82,7 +82,7 @@ namespace GitHub.Runner.Listener
|
||||
Trace.Info($"Set runner/worker IPC timeout to {_channelTimeout.TotalSeconds} seconds.");
|
||||
}
|
||||
|
||||
public TaskCompletionSource<bool> RunOnceJobCompleted => _runOnceJobCompleted;
|
||||
public TaskCompletionSource<int> RunOnceJobCompleted => _runOnceJobCompleted;
|
||||
|
||||
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)
|
||||
{
|
||||
int returnCode = 0;
|
||||
try
|
||||
{
|
||||
await RunAsync(message, orchestrationId, previousJobDispatch, jobRequestCancellationToken, workerCancelTimeoutKillToken);
|
||||
returnCode = await RunAsync(message, orchestrationId, previousJobDispatch, jobRequestCancellationToken, workerCancelTimeoutKillToken);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Trace.Info("Fire signal for one time used runner.");
|
||||
_runOnceJobCompleted.TrySetResult(true);
|
||||
_runOnceJobCompleted.TrySetResult(returnCode);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RunAsync(Pipelines.AgentJobRequestMessage message, string orchestrationId, WorkerDispatcher previousJobDispatch, CancellationToken jobRequestCancellationToken, CancellationToken workerCancelTimeoutKillToken)
|
||||
private async Task<int> RunAsync(Pipelines.AgentJobRequestMessage message, string orchestrationId, WorkerDispatcher previousJobDispatch, CancellationToken jobRequestCancellationToken, CancellationToken workerCancelTimeoutKillToken)
|
||||
{
|
||||
Busy = true;
|
||||
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.
|
||||
Trace.Info($"Unable to renew job request for job {message.JobId} for the first time, stop dispatching job to worker.");
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (jobRequestCancellationToken.IsCancellationRequested)
|
||||
@@ -412,7 +413,7 @@ namespace GitHub.Runner.Listener
|
||||
|
||||
// complete job request with result Cancelled
|
||||
await CompleteJobRequestAsync(_poolId, message, systemConnection, lockToken, TaskResult.Canceled);
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
HostContext.WritePerfCounter($"JobRequestRenewed_{requestId.ToString()}");
|
||||
@@ -523,7 +524,7 @@ namespace GitHub.Runner.Listener
|
||||
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.
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
return;
|
||||
return returnCode;
|
||||
}
|
||||
else if (completedTask == renewJobRequest)
|
||||
{
|
||||
@@ -706,6 +707,8 @@ namespace GitHub.Runner.Listener
|
||||
|
||||
// complete job request
|
||||
await CompleteJobRequestAsync(_poolId, message, systemConnection, lockToken, resultOnAbandonOrCancel);
|
||||
|
||||
return TaskResultUtil.TranslateToReturnCode(resultOnAbandonOrCancel);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
@@ -580,6 +580,20 @@ namespace GitHub.Runner.Listener
|
||||
Trace.Info($"Ignore any exception after cancel message loop. {ex}");
|
||||
}
|
||||
|
||||
if (StringUtil.ConvertToBoolean(Environment.GetEnvironmentVariable("ACTIONS_RUNNER_RETURN_RUN_ONCE_JOB_RESULT")))
|
||||
{
|
||||
try
|
||||
{
|
||||
var jobResult = await jobDispatcher.RunOnceJobCompleted.Task;
|
||||
return jobResult;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Trace.Error("run once job finished with error.");
|
||||
Trace.Error(ex);
|
||||
}
|
||||
}
|
||||
|
||||
return Constants.Runner.ReturnCode.Success;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.");
|
||||
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(0, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,13 +295,13 @@ namespace GitHub.Runner.Common.Tests.Listener
|
||||
_messageListener.Setup(x => x.DeleteMessageAsync(It.IsAny<TaskAgentMessage>()))
|
||||
.Returns(Task.CompletedTask);
|
||||
|
||||
var runOnceJobCompleted = new TaskCompletionSource<bool>();
|
||||
var runOnceJobCompleted = new TaskCompletionSource<int>();
|
||||
_jobDispatcher.Setup(x => x.RunOnceJobCompleted)
|
||||
.Returns(runOnceJobCompleted);
|
||||
_jobDispatcher.Setup(x => x.Run(It.IsAny<Pipelines.AgentJobRequestMessage>(), It.IsAny<bool>()))
|
||||
.Callback(() =>
|
||||
{
|
||||
runOnceJobCompleted.TrySetResult(true);
|
||||
runOnceJobCompleted.TrySetResult(0);
|
||||
});
|
||||
_jobNotification.Setup(x => x.StartClient(It.IsAny<String>()))
|
||||
.Callback(() =>
|
||||
@@ -399,13 +399,13 @@ namespace GitHub.Runner.Common.Tests.Listener
|
||||
_messageListener.Setup(x => x.DeleteMessageAsync(It.IsAny<TaskAgentMessage>()))
|
||||
.Returns(Task.CompletedTask);
|
||||
|
||||
var runOnceJobCompleted = new TaskCompletionSource<bool>();
|
||||
var runOnceJobCompleted = new TaskCompletionSource<int>();
|
||||
_jobDispatcher.Setup(x => x.RunOnceJobCompleted)
|
||||
.Returns(runOnceJobCompleted);
|
||||
_jobDispatcher.Setup(x => x.Run(It.IsAny<Pipelines.AgentJobRequestMessage>(), It.IsAny<bool>()))
|
||||
.Callback(() =>
|
||||
{
|
||||
runOnceJobCompleted.TrySetResult(true);
|
||||
runOnceJobCompleted.TrySetResult(0);
|
||||
});
|
||||
_jobNotification.Setup(x => x.StartClient(It.IsAny<String>()))
|
||||
.Callback(() =>
|
||||
@@ -733,8 +733,8 @@ namespace GitHub.Runner.Common.Tests.Listener
|
||||
|
||||
_configStore.Setup(x => x.IsServiceConfigured()).Returns(false);
|
||||
|
||||
var completedTask = new TaskCompletionSource<bool>();
|
||||
completedTask.SetResult(true);
|
||||
var completedTask = new TaskCompletionSource<int>();
|
||||
completedTask.SetResult(0);
|
||||
_jobDispatcher.Setup(x => x.RunOnceJobCompleted).Returns(completedTask);
|
||||
|
||||
//Act
|
||||
@@ -834,8 +834,8 @@ namespace GitHub.Runner.Common.Tests.Listener
|
||||
|
||||
_configStore.Setup(x => x.IsServiceConfigured()).Returns(false);
|
||||
|
||||
var completedTask = new TaskCompletionSource<bool>();
|
||||
completedTask.SetResult(true);
|
||||
var completedTask = new TaskCompletionSource<int>();
|
||||
completedTask.SetResult(0);
|
||||
_jobDispatcher.Setup(x => x.RunOnceJobCompleted).Returns(completedTask);
|
||||
|
||||
//Act
|
||||
@@ -954,8 +954,8 @@ namespace GitHub.Runner.Common.Tests.Listener
|
||||
|
||||
_configStore.Setup(x => x.IsServiceConfigured()).Returns(false);
|
||||
|
||||
var completedTask = new TaskCompletionSource<bool>();
|
||||
completedTask.SetResult(true);
|
||||
var completedTask = new TaskCompletionSource<int>();
|
||||
completedTask.SetResult(0);
|
||||
_jobDispatcher.Setup(x => x.RunOnceJobCompleted).Returns(completedTask);
|
||||
|
||||
//Act
|
||||
@@ -1076,75 +1076,5 @@ namespace GitHub.Runner.Common.Tests.Listener
|
||||
Assert.True(hc.AllowAuthMigration);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Runner")]
|
||||
public async Task RunCommand_ShouldReturnTerminatedError_WhenWorkDirCreationFails()
|
||||
{
|
||||
using (var hostCtx = new TestHostContext(this))
|
||||
{
|
||||
// Setup: arrange mocks and runner instance
|
||||
var runnerInstance = new Runner.Listener.Runner();
|
||||
hostCtx.SetSingleton<IConfigurationManager>(_configurationManager.Object);
|
||||
hostCtx.SetSingleton<IJobNotification>(_jobNotification.Object);
|
||||
hostCtx.SetSingleton<IMessageListener>(_messageListener.Object);
|
||||
hostCtx.SetSingleton<IPromptManager>(_promptManager.Object);
|
||||
hostCtx.SetSingleton<IConfigurationStore>(_configStore.Object);
|
||||
hostCtx.SetSingleton<IRunnerServer>(_runnerServer.Object);
|
||||
hostCtx.EnqueueInstance<IErrorThrottler>(_acquireJobThrottler.Object);
|
||||
|
||||
runnerInstance.Initialize(hostCtx);
|
||||
|
||||
// Create a file at the work directory path to block directory creation
|
||||
string workPath = hostCtx.GetDirectory(WellKnownDirectory.Work);
|
||||
|
||||
// Clean up any existing directory first
|
||||
if (Directory.Exists(workPath))
|
||||
{
|
||||
Directory.Delete(workPath, recursive: true);
|
||||
}
|
||||
|
||||
// Place a file where the work directory should be - this blocks Directory.CreateDirectory
|
||||
string parentPath = Path.GetDirectoryName(workPath);
|
||||
Assert.NotNull(parentPath);
|
||||
Assert.NotEmpty(parentPath);
|
||||
Directory.CreateDirectory(parentPath);
|
||||
|
||||
const string blockingFileContent = "test file blocking directory creation";
|
||||
File.WriteAllText(workPath, blockingFileContent);
|
||||
|
||||
const int testPoolId = 12345;
|
||||
const int testAgentId = 67890;
|
||||
|
||||
var runnerConfig = new RunnerSettings
|
||||
{
|
||||
PoolId = testPoolId,
|
||||
AgentId = testAgentId
|
||||
};
|
||||
|
||||
_configurationManager.Setup(m => m.LoadSettings()).Returns(runnerConfig);
|
||||
_configurationManager.Setup(m => m.IsConfigured()).Returns(true);
|
||||
_configStore.Setup(m => m.IsServiceConfigured()).Returns(false);
|
||||
|
||||
try
|
||||
{
|
||||
// Execute: run the command which should fail during work dir validation
|
||||
var cmd = new CommandSettings(hostCtx, new string[] { "run" });
|
||||
int exitCode = await runnerInstance.ExecuteCommand(cmd);
|
||||
|
||||
// Verify: should return TerminatedError code when dir creation fails
|
||||
Assert.Equal(Constants.Runner.ReturnCode.TerminatedError, exitCode);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Cleanup: remove the blocking file
|
||||
if (File.Exists(workPath))
|
||||
{
|
||||
File.Delete(workPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user