mirror of
https://github.com/actions/runner.git
synced 2025-12-11 21:06:55 +00:00
Compare commits
5 Commits
users/pje/
...
v2.283.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b03ca604ff | ||
|
|
ecfc2cc9e9 | ||
|
|
740fb43731 | ||
|
|
f259e5706f | ||
|
|
5d84918ed5 |
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
@@ -54,4 +54,4 @@
|
|||||||
"requireExactSource": false,
|
"requireExactSource": false,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,13 @@
|
|||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Support the `--ephemeral` flag (#660)
|
- Collect more telemetry
|
||||||
- This optional flag will configure the runner to only take one job, and let the service un-configure the runner after that job finishes.
|
- Make `runner.name` available as a runner context variable
|
||||||
- Expect to see more info in the Github API documentation soon. We'll link to those docs directly as they become generally available!
|
- Add attempt number (`run_attempt`) to GitHub context
|
||||||
|
- When using the `--ephemeral` flag, ensure that the runner cleans up local `.runner` and `.credentials` files after completion (#1337)
|
||||||
## Bugs
|
|
||||||
|
|
||||||
- Fix a bug in `script/delete` wherein a repo with multiple runners would be unable to find the correct runner (#1268) (#1269)
|
|
||||||
- Mitigate a race condition when requesting an OIDC `Id_token` (#1320)
|
|
||||||
- Make client retries more resilient in JobServer (#1316)
|
|
||||||
|
|
||||||
## Misc
|
## Misc
|
||||||
|
|
||||||
- Increase readability of colored console output (#1295) (#1319)
|
- Improved network troubleshooting docs
|
||||||
- Add more network troubleshooting to the docs (#1325)
|
|
||||||
- Bump [path-parse](https://github.com/jbgutierrez/path-parse) from 1.0.6 to 1.0.7 (#1256)
|
|
||||||
|
|
||||||
## Windows x64
|
## Windows x64
|
||||||
We recommend configuring the runner in a root folder of the Windows drive (e.g. "C:\actions-runner"). This will help avoid issues related to service identity folder permissions and long file path restrictions on Windows.
|
We recommend configuring the runner in a root folder of the Windows drive (e.g. "C:\actions-runner"). This will help avoid issues related to service identity folder permissions and long file path restrictions on Windows.
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
<Update to ./src/runnerversion when creating release>
|
2.283.0
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ namespace GitHub.Runner.Listener.Configuration
|
|||||||
bool IsConfigured();
|
bool IsConfigured();
|
||||||
Task ConfigureAsync(CommandSettings command);
|
Task ConfigureAsync(CommandSettings command);
|
||||||
Task UnconfigureAsync(CommandSettings command);
|
Task UnconfigureAsync(CommandSettings command);
|
||||||
|
void DeleteLocalRunnerConfig();
|
||||||
RunnerSettings LoadSettings();
|
RunnerSettings LoadSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -329,6 +330,38 @@ namespace GitHub.Runner.Listener.Configuration
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delete .runner and .credentials files
|
||||||
|
public void DeleteLocalRunnerConfig()
|
||||||
|
{
|
||||||
|
bool isConfigured = _store.IsConfigured();
|
||||||
|
bool hasCredentials = _store.HasCredentials();
|
||||||
|
//delete credential config files
|
||||||
|
var currentAction = "Removing .credentials";
|
||||||
|
if (hasCredentials)
|
||||||
|
{
|
||||||
|
_store.DeleteCredential();
|
||||||
|
var keyManager = HostContext.GetService<IRSAKeyManager>();
|
||||||
|
keyManager.DeleteKey();
|
||||||
|
_term.WriteSuccessMessage("Removed .credentials");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_term.WriteLine("Does not exist. Skipping " + currentAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
//delete settings config file
|
||||||
|
currentAction = "Removing .runner";
|
||||||
|
if (isConfigured)
|
||||||
|
{
|
||||||
|
_store.DeleteSettings();
|
||||||
|
_term.WriteSuccessMessage("Removed .runner");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_term.WriteLine("Does not exist. Skipping " + currentAction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async Task UnconfigureAsync(CommandSettings command)
|
public async Task UnconfigureAsync(CommandSettings command)
|
||||||
{
|
{
|
||||||
string currentAction = string.Empty;
|
string currentAction = string.Empty;
|
||||||
@@ -402,31 +435,7 @@ namespace GitHub.Runner.Listener.Configuration
|
|||||||
_term.WriteLine("Cannot connect to server, because config files are missing. Skipping removing runner from the server.");
|
_term.WriteLine("Cannot connect to server, because config files are missing. Skipping removing runner from the server.");
|
||||||
}
|
}
|
||||||
|
|
||||||
//delete credential config files
|
DeleteLocalRunnerConfig();
|
||||||
currentAction = "Removing .credentials";
|
|
||||||
if (hasCredentials)
|
|
||||||
{
|
|
||||||
_store.DeleteCredential();
|
|
||||||
var keyManager = HostContext.GetService<IRSAKeyManager>();
|
|
||||||
keyManager.DeleteKey();
|
|
||||||
_term.WriteSuccessMessage("Removed .credentials");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_term.WriteLine("Does not exist. Skipping " + currentAction);
|
|
||||||
}
|
|
||||||
|
|
||||||
//delete settings config file
|
|
||||||
currentAction = "Removing .runner";
|
|
||||||
if (isConfigured)
|
|
||||||
{
|
|
||||||
_store.DeleteSettings();
|
|
||||||
_term.WriteSuccessMessage("Removed .runner");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_term.WriteLine("Does not exist. Skipping " + currentAction);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -36,7 +36,10 @@ namespace GitHub.Runner.Listener
|
|||||||
{
|
{
|
||||||
private readonly Lazy<Dictionary<long, TaskResult>> _localRunJobResult = new Lazy<Dictionary<long, TaskResult>>();
|
private readonly Lazy<Dictionary<long, TaskResult>> _localRunJobResult = new Lazy<Dictionary<long, TaskResult>>();
|
||||||
private int _poolId;
|
private int _poolId;
|
||||||
RunnerSettings _runnerSetting;
|
|
||||||
|
IConfigurationStore _configurationStore;
|
||||||
|
|
||||||
|
RunnerSettings _runnerSettings;
|
||||||
private static readonly string _workerProcessName = $"Runner.Worker{IOUtil.ExeExtension}";
|
private static readonly string _workerProcessName = $"Runner.Worker{IOUtil.ExeExtension}";
|
||||||
|
|
||||||
// this is not thread-safe
|
// this is not thread-safe
|
||||||
@@ -54,9 +57,9 @@ namespace GitHub.Runner.Listener
|
|||||||
base.Initialize(hostContext);
|
base.Initialize(hostContext);
|
||||||
|
|
||||||
// get pool id from config
|
// get pool id from config
|
||||||
var configurationStore = hostContext.GetService<IConfigurationStore>();
|
_configurationStore = hostContext.GetService<IConfigurationStore>();
|
||||||
_runnerSetting = configurationStore.GetSettings();
|
_runnerSettings = _configurationStore.GetSettings();
|
||||||
_poolId = _runnerSetting.PoolId;
|
_poolId = _runnerSettings.PoolId;
|
||||||
|
|
||||||
int channelTimeoutSeconds;
|
int channelTimeoutSeconds;
|
||||||
if (!int.TryParse(Environment.GetEnvironmentVariable("GITHUB_ACTIONS_RUNNER_CHANNEL_TIMEOUT") ?? string.Empty, out channelTimeoutSeconds))
|
if (!int.TryParse(Environment.GetEnvironmentVariable("GITHUB_ACTIONS_RUNNER_CHANNEL_TIMEOUT") ?? string.Empty, out channelTimeoutSeconds))
|
||||||
@@ -661,13 +664,15 @@ namespace GitHub.Runner.Listener
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
request = await runnerServer.RenewAgentRequestAsync(poolId, requestId, lockToken, orchestrationId, token);
|
request = await runnerServer.RenewAgentRequestAsync(poolId, requestId, lockToken, orchestrationId, token);
|
||||||
|
|
||||||
Trace.Info($"Successfully renew job request {requestId}, job is valid till {request.LockedUntil.Value}");
|
Trace.Info($"Successfully renew job request {requestId}, job is valid till {request.LockedUntil.Value}");
|
||||||
|
|
||||||
if (!firstJobRequestRenewed.Task.IsCompleted)
|
if (!firstJobRequestRenewed.Task.IsCompleted)
|
||||||
{
|
{
|
||||||
// fire first renew succeed event.
|
// fire first renew succeed event.
|
||||||
firstJobRequestRenewed.TrySetResult(0);
|
firstJobRequestRenewed.TrySetResult(0);
|
||||||
|
|
||||||
|
// Update settings if the runner name has been changed server-side
|
||||||
|
UpdateAgentNameIfNeeded(request.ReservedAgent?.Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (encounteringError > 0)
|
if (encounteringError > 0)
|
||||||
@@ -767,6 +772,27 @@ namespace GitHub.Runner.Listener
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void UpdateAgentNameIfNeeded(string agentName)
|
||||||
|
{
|
||||||
|
var isNewAgentName = !string.Equals(_runnerSettings.AgentName, agentName, StringComparison.Ordinal);
|
||||||
|
if (!isNewAgentName || string.IsNullOrEmpty(agentName))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_runnerSettings.AgentName = agentName;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_configurationStore.SaveSettings(_runnerSettings);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Trace.Error("Cannot update the settings file:");
|
||||||
|
Trace.Error(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// Best effort upload any logs for this job.
|
// Best effort upload any logs for this job.
|
||||||
private async Task TryUploadUnfinishedLogs(Pipelines.AgentJobRequestMessage message)
|
private async Task TryUploadUnfinishedLogs(Pipelines.AgentJobRequestMessage message)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -214,7 +214,7 @@ namespace GitHub.Runner.Listener
|
|||||||
var startupTypeAsString = command.GetStartupType();
|
var startupTypeAsString = command.GetStartupType();
|
||||||
if (string.IsNullOrEmpty(startupTypeAsString) && configuredAsService)
|
if (string.IsNullOrEmpty(startupTypeAsString) && configuredAsService)
|
||||||
{
|
{
|
||||||
// We need try our best to make the startup type accurate
|
// We need try our best to make the startup type accurate
|
||||||
// The problem is coming from runner autoupgrade, which result an old version service host binary but a newer version runner binary
|
// The problem is coming from runner autoupgrade, which result an old version service host binary but a newer version runner binary
|
||||||
// At that time the servicehost won't pass --startuptype to Runner.Listener while the runner is actually running as service.
|
// At that time the servicehost won't pass --startuptype to Runner.Listener while the runner is actually running as service.
|
||||||
// We will guess the startup type only when the runner is configured as service and the guess will based on whether STDOUT/STDERR/STDIN been redirect or not
|
// We will guess the startup type only when the runner is configured as service and the guess will based on whether STDOUT/STDERR/STDIN been redirect or not
|
||||||
@@ -478,6 +478,12 @@ namespace GitHub.Runner.Listener
|
|||||||
}
|
}
|
||||||
|
|
||||||
messageQueueLoopTokenSource.Dispose();
|
messageQueueLoopTokenSource.Dispose();
|
||||||
|
|
||||||
|
if (settings.Ephemeral)
|
||||||
|
{
|
||||||
|
var configManager = HostContext.GetService<IConfigurationManager>();
|
||||||
|
configManager.DeleteLocalRunnerConfig();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (TaskAgentAccessTokenExpiredException)
|
catch (TaskAgentAccessTokenExpiredException)
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
using GitHub.DistributedTask.Pipelines;
|
using GitHub.DistributedTask.Pipelines.ContextData;
|
||||||
using GitHub.DistributedTask.Pipelines.ContextData;
|
|
||||||
using GitHub.DistributedTask.WebApi;
|
using GitHub.DistributedTask.WebApi;
|
||||||
using GitHub.Runner.Common.Util;
|
|
||||||
using GitHub.Runner.Worker.Container;
|
using GitHub.Runner.Worker.Container;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -113,6 +111,15 @@ namespace GitHub.Runner.Worker
|
|||||||
context.Output(input);
|
context.Output(input);
|
||||||
context.Debug("Paused processing commands until '##[{actionCommand.Data}]' is received");
|
context.Debug("Paused processing commands until '##[{actionCommand.Data}]' is received");
|
||||||
_stopToken = actionCommand.Data;
|
_stopToken = actionCommand.Data;
|
||||||
|
if (_registeredCommands.Contains(actionCommand.Data) || string.IsNullOrEmpty(actionCommand.Data))
|
||||||
|
{
|
||||||
|
var telemetry = new JobTelemetry
|
||||||
|
{
|
||||||
|
Message = $"Invoked ::stopCommand:: with token: [{actionCommand.Data}]",
|
||||||
|
Type = JobTelemetryType.ActionCommand
|
||||||
|
};
|
||||||
|
context.JobTelemetry.Add(telemetry);
|
||||||
|
}
|
||||||
_stopProcessCommand = true;
|
_stopProcessCommand = true;
|
||||||
_registeredCommands.Add(_stopToken);
|
_registeredCommands.Add(_stopToken);
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ namespace GitHub.Runner.Worker
|
|||||||
Dictionary<string, VariableValue> JobOutputs { get; }
|
Dictionary<string, VariableValue> JobOutputs { get; }
|
||||||
ActionsEnvironmentReference ActionsEnvironment { get; }
|
ActionsEnvironmentReference ActionsEnvironment { get; }
|
||||||
List<ActionsStepTelemetry> ActionsStepsTelemetry { get; }
|
List<ActionsStepTelemetry> ActionsStepsTelemetry { get; }
|
||||||
|
List<JobTelemetry> JobTelemetry { get; }
|
||||||
DictionaryContextData ExpressionValues { get; }
|
DictionaryContextData ExpressionValues { get; }
|
||||||
IList<IFunctionInfo> ExpressionFunctions { get; }
|
IList<IFunctionInfo> ExpressionFunctions { get; }
|
||||||
JobContext JobContext { get; }
|
JobContext JobContext { get; }
|
||||||
@@ -150,6 +151,7 @@ namespace GitHub.Runner.Worker
|
|||||||
|
|
||||||
public ActionsEnvironmentReference ActionsEnvironment { get; private set; }
|
public ActionsEnvironmentReference ActionsEnvironment { get; private set; }
|
||||||
public List<ActionsStepTelemetry> ActionsStepsTelemetry { get; private set; }
|
public List<ActionsStepTelemetry> ActionsStepsTelemetry { get; private set; }
|
||||||
|
public List<JobTelemetry> JobTelemetry { get; private set; }
|
||||||
public DictionaryContextData ExpressionValues { get; } = new DictionaryContextData();
|
public DictionaryContextData ExpressionValues { get; } = new DictionaryContextData();
|
||||||
public IList<IFunctionInfo> ExpressionFunctions { get; } = new List<IFunctionInfo>();
|
public IList<IFunctionInfo> ExpressionFunctions { get; } = new List<IFunctionInfo>();
|
||||||
|
|
||||||
@@ -294,6 +296,7 @@ namespace GitHub.Runner.Worker
|
|||||||
child.ContextName = contextName;
|
child.ContextName = contextName;
|
||||||
child.EmbeddedId = embeddedId;
|
child.EmbeddedId = embeddedId;
|
||||||
child.SiblingScopeName = siblingScopeName;
|
child.SiblingScopeName = siblingScopeName;
|
||||||
|
child.JobTelemetry = JobTelemetry;
|
||||||
if (intraActionState == null)
|
if (intraActionState == null)
|
||||||
{
|
{
|
||||||
child.IntraActionState = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
child.IntraActionState = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||||
@@ -650,6 +653,8 @@ namespace GitHub.Runner.Worker
|
|||||||
// ActionsStepTelemetry
|
// ActionsStepTelemetry
|
||||||
ActionsStepsTelemetry = new List<ActionsStepTelemetry>();
|
ActionsStepsTelemetry = new List<ActionsStepTelemetry>();
|
||||||
|
|
||||||
|
JobTelemetry = new List<JobTelemetry>();
|
||||||
|
|
||||||
// Service container info
|
// Service container info
|
||||||
Global.ServiceContainers = new List<ContainerInfo>();
|
Global.ServiceContainers = new List<ContainerInfo>();
|
||||||
|
|
||||||
|
|||||||
@@ -106,6 +106,9 @@ namespace GitHub.Runner.Worker
|
|||||||
|
|
||||||
jobContext.SetRunnerContext("os", VarUtil.OS);
|
jobContext.SetRunnerContext("os", VarUtil.OS);
|
||||||
|
|
||||||
|
var runnerSettings = HostContext.GetService<IConfigurationStore>().GetSettings();
|
||||||
|
jobContext.SetRunnerContext("name", runnerSettings.AgentName);
|
||||||
|
|
||||||
string toolsDirectory = HostContext.GetDirectory(WellKnownDirectory.Tools);
|
string toolsDirectory = HostContext.GetDirectory(WellKnownDirectory.Tools);
|
||||||
Directory.CreateDirectory(toolsDirectory);
|
Directory.CreateDirectory(toolsDirectory);
|
||||||
jobContext.SetRunnerContext("tool_cache", toolsDirectory);
|
jobContext.SetRunnerContext("tool_cache", toolsDirectory);
|
||||||
@@ -225,8 +228,12 @@ namespace GitHub.Runner.Worker
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make sure we don't submit secrets as telemetry
|
||||||
|
MaskTelemetrySecrets(jobContext.JobTelemetry);
|
||||||
|
|
||||||
Trace.Info("Raising job completed event.");
|
Trace.Info("Raising job completed event.");
|
||||||
var jobCompletedEvent = new JobCompletedEvent(message.RequestId, message.JobId, result, jobContext.JobOutputs, jobContext.ActionsEnvironment, jobContext.ActionsStepsTelemetry);
|
var jobCompletedEvent = new JobCompletedEvent(message.RequestId, message.JobId, result, jobContext.JobOutputs, jobContext.ActionsEnvironment, jobContext.ActionsStepsTelemetry, jobContext.JobTelemetry);
|
||||||
|
|
||||||
|
|
||||||
var completeJobRetryLimit = 5;
|
var completeJobRetryLimit = 5;
|
||||||
var exceptions = new List<Exception>();
|
var exceptions = new List<Exception>();
|
||||||
@@ -270,6 +277,14 @@ namespace GitHub.Runner.Worker
|
|||||||
throw new AggregateException(exceptions);
|
throw new AggregateException(exceptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void MaskTelemetrySecrets(List<JobTelemetry> jobTelemetry)
|
||||||
|
{
|
||||||
|
foreach (var telemetryItem in jobTelemetry)
|
||||||
|
{
|
||||||
|
telemetryItem.Message = HostContext.SecretMasker.MaskSecrets(telemetryItem.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task ShutdownQueue(bool throwOnFailure)
|
private async Task ShutdownQueue(bool throwOnFailure)
|
||||||
{
|
{
|
||||||
if (_jobServerQueue != null)
|
if (_jobServerQueue != null)
|
||||||
|
|||||||
@@ -153,6 +153,19 @@ namespace GitHub.DistributedTask.WebApi
|
|||||||
{
|
{
|
||||||
this.ActionsEnvironment = actionsEnvironment;
|
this.ActionsEnvironment = actionsEnvironment;
|
||||||
this.ActionsStepsTelemetry = actionsStepsTelemetry;
|
this.ActionsStepsTelemetry = actionsStepsTelemetry;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JobCompletedEvent(
|
||||||
|
Int64 requestId,
|
||||||
|
Guid jobId,
|
||||||
|
TaskResult result,
|
||||||
|
Dictionary<String, VariableValue> outputs,
|
||||||
|
ActionsEnvironmentReference actionsEnvironment,
|
||||||
|
List<ActionsStepTelemetry> actionsStepsTelemetry,
|
||||||
|
List<JobTelemetry> jobTelemetry)
|
||||||
|
: this(requestId, jobId, result, outputs, actionsEnvironment, actionsStepsTelemetry)
|
||||||
|
{
|
||||||
|
this.JobTelemetry = jobTelemetry;
|
||||||
}
|
}
|
||||||
|
|
||||||
[DataMember(EmitDefaultValue = false)]
|
[DataMember(EmitDefaultValue = false)]
|
||||||
@@ -189,6 +202,13 @@ namespace GitHub.DistributedTask.WebApi
|
|||||||
get;
|
get;
|
||||||
set;
|
set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[DataMember(EmitDefaultValue = false)]
|
||||||
|
public List<JobTelemetry> JobTelemetry
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[DataContract]
|
[DataContract]
|
||||||
|
|||||||
17
src/Sdk/DTWebApi/WebApi/JobTelemetry.cs
Normal file
17
src/Sdk/DTWebApi/WebApi/JobTelemetry.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
using System.Runtime.Serialization;
|
||||||
|
|
||||||
|
namespace GitHub.DistributedTask.WebApi
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Information about a job run on the runner
|
||||||
|
/// </summary>
|
||||||
|
[DataContract]
|
||||||
|
public class JobTelemetry
|
||||||
|
{
|
||||||
|
[DataMember(EmitDefaultValue = false)]
|
||||||
|
public string Message { get; set; }
|
||||||
|
|
||||||
|
[DataMember(EmitDefaultValue = false)]
|
||||||
|
public JobTelemetryType Type { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
13
src/Sdk/DTWebApi/WebApi/JobTelemetryType.cs
Normal file
13
src/Sdk/DTWebApi/WebApi/JobTelemetryType.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
using System.Runtime.Serialization;
|
||||||
|
|
||||||
|
namespace GitHub.DistributedTask.WebApi
|
||||||
|
{
|
||||||
|
public enum JobTelemetryType
|
||||||
|
{
|
||||||
|
[EnumMember]
|
||||||
|
General = 0,
|
||||||
|
|
||||||
|
[EnumMember]
|
||||||
|
ActionCommand = 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -161,7 +161,8 @@ namespace GitHub.Runner.Common.Tests.Listener.Configuration
|
|||||||
"--work", _expectedWorkFolder,
|
"--work", _expectedWorkFolder,
|
||||||
"--auth", _expectedAuthType,
|
"--auth", _expectedAuthType,
|
||||||
"--token", _expectedToken,
|
"--token", _expectedToken,
|
||||||
"--labels", userLabels
|
"--labels", userLabels,
|
||||||
|
"--ephemeral",
|
||||||
});
|
});
|
||||||
trace.Info("Constructed.");
|
trace.Info("Constructed.");
|
||||||
_store.Setup(x => x.IsConfigured()).Returns(false);
|
_store.Setup(x => x.IsConfigured()).Returns(false);
|
||||||
@@ -179,6 +180,7 @@ namespace GitHub.Runner.Common.Tests.Listener.Configuration
|
|||||||
Assert.True(s.AgentName.Equals(_expectedAgentName));
|
Assert.True(s.AgentName.Equals(_expectedAgentName));
|
||||||
Assert.True(s.PoolId.Equals(_secondRunnerGroupId));
|
Assert.True(s.PoolId.Equals(_secondRunnerGroupId));
|
||||||
Assert.True(s.WorkFolder.Equals(_expectedWorkFolder));
|
Assert.True(s.WorkFolder.Equals(_expectedWorkFolder));
|
||||||
|
Assert.True(s.Ephemeral.Equals(true));
|
||||||
|
|
||||||
// validate GetAgentPoolsAsync gets called twice with automation pool type
|
// validate GetAgentPoolsAsync gets called twice with automation pool type
|
||||||
_runnerServer.Verify(x => x.GetAgentPoolsAsync(It.IsAny<string>(), It.Is<TaskAgentPoolType>(p => p == TaskAgentPoolType.Automation)), Times.Exactly(2));
|
_runnerServer.Verify(x => x.GetAgentPoolsAsync(It.IsAny<string>(), It.Is<TaskAgentPoolType>(p => p == TaskAgentPoolType.Automation)), Times.Exactly(2));
|
||||||
|
|||||||
@@ -264,6 +264,170 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[Trait("Level", "L0")]
|
||||||
|
[Trait("Category", "Runner")]
|
||||||
|
public async void RenewJobRequestNewAgentNameUpdatesSettings()
|
||||||
|
{
|
||||||
|
//Arrange
|
||||||
|
using (var hc = new TestHostContext(this))
|
||||||
|
{
|
||||||
|
var count = 0;
|
||||||
|
var oldName = "OldName";
|
||||||
|
var newName = "NewName";
|
||||||
|
var oldSettings = new RunnerSettings { AgentName = oldName };
|
||||||
|
var reservedAgent = new TaskAgentReference { Name = newName };
|
||||||
|
|
||||||
|
var trace = hc.GetTrace(nameof(DispatcherRenewJobRequestStopOnJobTokenExpiredExceptions));
|
||||||
|
TaskCompletionSource<int> firstJobRequestRenewed = new TaskCompletionSource<int>();
|
||||||
|
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
|
||||||
|
|
||||||
|
var request = new Mock<TaskAgentJobRequest>();
|
||||||
|
request.Object.ReservedAgent = reservedAgent;
|
||||||
|
PropertyInfo lockUntilProperty = request.Object.GetType().GetProperty("LockedUntil", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
|
||||||
|
Assert.NotNull(lockUntilProperty);
|
||||||
|
lockUntilProperty.SetValue(request.Object, DateTime.UtcNow.AddMinutes(5));
|
||||||
|
hc.SetSingleton<IRunnerServer>(_runnerServer.Object);
|
||||||
|
hc.SetSingleton<IConfigurationStore>(_configurationStore.Object);
|
||||||
|
_configurationStore.Setup(x => x.GetSettings()).Returns(oldSettings);
|
||||||
|
_runnerServer.Setup(x => x.RenewAgentRequestAsync(It.IsAny<int>(), It.IsAny<long>(), It.IsAny<Guid>(), It.IsAny<string>(), It.IsAny<CancellationToken>()))
|
||||||
|
.Returns(() =>
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
if (count < 5)
|
||||||
|
{
|
||||||
|
return Task.FromResult<TaskAgentJobRequest>(request.Object);
|
||||||
|
}
|
||||||
|
else if (count == 5 || count == 6 || count == 7)
|
||||||
|
{
|
||||||
|
throw new TimeoutException("");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cancellationTokenSource.Cancel();
|
||||||
|
return Task.FromResult<TaskAgentJobRequest>(request.Object);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var jobDispatcher = new JobDispatcher();
|
||||||
|
jobDispatcher.Initialize(hc);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await jobDispatcher.RenewJobRequestAsync(0, 0, Guid.Empty, Guid.NewGuid().ToString(), firstJobRequestRenewed, cancellationTokenSource.Token);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
_configurationStore.Verify(x => x.SaveSettings(It.Is<RunnerSettings>(settings => settings.AgentName == newName)), Times.Once);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[Trait("Level", "L0")]
|
||||||
|
[Trait("Category", "Runner")]
|
||||||
|
public async void RenewJobRequestSameAgentNameIgnored()
|
||||||
|
{
|
||||||
|
//Arrange
|
||||||
|
using (var hc = new TestHostContext(this))
|
||||||
|
{
|
||||||
|
var count = 0;
|
||||||
|
var oldName = "OldName";
|
||||||
|
var newName = "OldName";
|
||||||
|
var oldSettings = new RunnerSettings { AgentName = oldName };
|
||||||
|
var reservedAgent = new TaskAgentReference { Name = newName };
|
||||||
|
|
||||||
|
var trace = hc.GetTrace(nameof(DispatcherRenewJobRequestStopOnJobTokenExpiredExceptions));
|
||||||
|
TaskCompletionSource<int> firstJobRequestRenewed = new TaskCompletionSource<int>();
|
||||||
|
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
|
||||||
|
|
||||||
|
var request = new Mock<TaskAgentJobRequest>();
|
||||||
|
request.Object.ReservedAgent = reservedAgent;
|
||||||
|
PropertyInfo lockUntilProperty = request.Object.GetType().GetProperty("LockedUntil", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
|
||||||
|
Assert.NotNull(lockUntilProperty);
|
||||||
|
lockUntilProperty.SetValue(request.Object, DateTime.UtcNow.AddMinutes(5));
|
||||||
|
hc.SetSingleton<IRunnerServer>(_runnerServer.Object);
|
||||||
|
hc.SetSingleton<IConfigurationStore>(_configurationStore.Object);
|
||||||
|
_configurationStore.Setup(x => x.GetSettings()).Returns(oldSettings);
|
||||||
|
_runnerServer.Setup(x => x.RenewAgentRequestAsync(It.IsAny<int>(), It.IsAny<long>(), It.IsAny<Guid>(), It.IsAny<string>(), It.IsAny<CancellationToken>()))
|
||||||
|
.Returns(() =>
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
if (count < 5)
|
||||||
|
{
|
||||||
|
return Task.FromResult<TaskAgentJobRequest>(request.Object);
|
||||||
|
}
|
||||||
|
else if (count == 5 || count == 6 || count == 7)
|
||||||
|
{
|
||||||
|
throw new TimeoutException("");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cancellationTokenSource.Cancel();
|
||||||
|
return Task.FromResult<TaskAgentJobRequest>(request.Object);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var jobDispatcher = new JobDispatcher();
|
||||||
|
jobDispatcher.Initialize(hc);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await jobDispatcher.RenewJobRequestAsync(0, 0, Guid.Empty, Guid.NewGuid().ToString(), firstJobRequestRenewed, cancellationTokenSource.Token);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
_configurationStore.Verify(x => x.SaveSettings(It.IsAny<RunnerSettings>()), Times.Never);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[Trait("Level", "L0")]
|
||||||
|
[Trait("Category", "Runner")]
|
||||||
|
public async void RenewJobRequestNullAgentNameIgnored()
|
||||||
|
{
|
||||||
|
//Arrange
|
||||||
|
using (var hc = new TestHostContext(this))
|
||||||
|
{
|
||||||
|
var count = 0;
|
||||||
|
var oldName = "OldName";
|
||||||
|
var oldSettings = new RunnerSettings { AgentName = oldName };
|
||||||
|
|
||||||
|
var trace = hc.GetTrace(nameof(DispatcherRenewJobRequestStopOnJobTokenExpiredExceptions));
|
||||||
|
TaskCompletionSource<int> firstJobRequestRenewed = new TaskCompletionSource<int>();
|
||||||
|
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
|
||||||
|
|
||||||
|
var request = new Mock<TaskAgentJobRequest>();
|
||||||
|
PropertyInfo lockUntilProperty = request.Object.GetType().GetProperty("LockedUntil", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
|
||||||
|
Assert.NotNull(lockUntilProperty);
|
||||||
|
lockUntilProperty.SetValue(request.Object, DateTime.UtcNow.AddMinutes(5));
|
||||||
|
hc.SetSingleton<IRunnerServer>(_runnerServer.Object);
|
||||||
|
hc.SetSingleton<IConfigurationStore>(_configurationStore.Object);
|
||||||
|
_configurationStore.Setup(x => x.GetSettings()).Returns(oldSettings);
|
||||||
|
_runnerServer.Setup(x => x.RenewAgentRequestAsync(It.IsAny<int>(), It.IsAny<long>(), It.IsAny<Guid>(), It.IsAny<string>(), It.IsAny<CancellationToken>()))
|
||||||
|
.Returns(() =>
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
if (count < 5)
|
||||||
|
{
|
||||||
|
return Task.FromResult<TaskAgentJobRequest>(request.Object);
|
||||||
|
}
|
||||||
|
else if (count == 5 || count == 6 || count == 7)
|
||||||
|
{
|
||||||
|
throw new TimeoutException("");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cancellationTokenSource.Cancel();
|
||||||
|
return Task.FromResult<TaskAgentJobRequest>(request.Object);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var jobDispatcher = new JobDispatcher();
|
||||||
|
jobDispatcher.Initialize(hc);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await jobDispatcher.RenewJobRequestAsync(0, 0, Guid.Empty, Guid.NewGuid().ToString(), firstJobRequestRenewed, cancellationTokenSource.Token);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
_configurationStore.Verify(x => x.SaveSettings(It.IsAny<RunnerSettings>()), Times.Never);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
[Trait("Level", "L0")]
|
[Trait("Level", "L0")]
|
||||||
[Trait("Category", "Runner")]
|
[Trait("Category", "Runner")]
|
||||||
|
|||||||
@@ -149,6 +149,9 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
_messageListener.Verify(x => x.CreateSessionAsync(It.IsAny<CancellationToken>()), Times.Once());
|
_messageListener.Verify(x => x.CreateSessionAsync(It.IsAny<CancellationToken>()), Times.Once());
|
||||||
_messageListener.Verify(x => x.DeleteSessionAsync(), Times.Once());
|
_messageListener.Verify(x => x.DeleteSessionAsync(), Times.Once());
|
||||||
_messageListener.Verify(x => x.DeleteMessageAsync(It.IsAny<TaskAgentMessage>()), Times.AtLeastOnce());
|
_messageListener.Verify(x => x.DeleteMessageAsync(It.IsAny<TaskAgentMessage>()), Times.AtLeastOnce());
|
||||||
|
|
||||||
|
// verify that we didn't try to delete local settings file (since we're not ephemeral)
|
||||||
|
_configurationManager.Verify(x => x.DeleteLocalRunnerConfig(), Times.Never());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -312,6 +315,9 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
_messageListener.Verify(x => x.CreateSessionAsync(It.IsAny<CancellationToken>()), Times.Once());
|
_messageListener.Verify(x => x.CreateSessionAsync(It.IsAny<CancellationToken>()), Times.Once());
|
||||||
_messageListener.Verify(x => x.DeleteSessionAsync(), Times.Once());
|
_messageListener.Verify(x => x.DeleteSessionAsync(), Times.Once());
|
||||||
_messageListener.Verify(x => x.DeleteMessageAsync(It.IsAny<TaskAgentMessage>()), Times.AtLeastOnce());
|
_messageListener.Verify(x => x.DeleteMessageAsync(It.IsAny<TaskAgentMessage>()), Times.AtLeastOnce());
|
||||||
|
|
||||||
|
// verify that we did try to delete local settings file (since we're ephemeral)
|
||||||
|
_configurationManager.Verify(x => x.DeleteLocalRunnerConfig(), Times.Once());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
2.282.0
|
2.283.0
|
||||||
|
|||||||
Reference in New Issue
Block a user