mirror of
https://github.com/actions/runner.git
synced 2025-12-10 20:36:49 +00:00
Compare commits
1 Commits
v2.298.0
...
users/eric
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7731bf9ad5 |
@@ -47,6 +47,11 @@
|
|||||||
<DefineConstants>$(DefineConstants);DEBUG</DefineConstants>
|
<DefineConstants>$(DefineConstants);DEBUG</DefineConstants>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<!-- Set USE_BROKER vars -->
|
||||||
|
<PropertyGroup Condition="'$(USE_BROKER)' == 'true'">
|
||||||
|
<DefineConstants>$(DefineConstants);USE_BROKER</DefineConstants>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
<!-- Set Treat tarnings as errors -->
|
<!-- Set Treat tarnings as errors -->
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
|
|||||||
38
src/Runner.Common/BrokerServer.cs
Normal file
38
src/Runner.Common/BrokerServer.cs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using GitHub.Runner.Common.Util;
|
||||||
|
using GitHub.Services.WebApi;
|
||||||
|
using GitHub.Services.Common;
|
||||||
|
using GitHub.Runner.Sdk;
|
||||||
|
using System.Net.Http;
|
||||||
|
|
||||||
|
namespace GitHub.Runner.Common
|
||||||
|
{
|
||||||
|
[ServiceLocator(Default = typeof(BrokerServer))]
|
||||||
|
public interface IBrokerServer : IRunnerService
|
||||||
|
{
|
||||||
|
Task ConnectAsync(Uri serverUrl);
|
||||||
|
Task<GitHub.DistributedTask.WebApi.TaskAgentMessage> GetMessageAsync(Int32 poolId, Guid sessionId, Int64? lastMessageId, CancellationToken cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class BrokerServer : RunnerService, IBrokerServer
|
||||||
|
{
|
||||||
|
private HttpClient _httpClient;
|
||||||
|
|
||||||
|
public async Task ConnectAsync(Uri serverUrl)
|
||||||
|
{
|
||||||
|
_httpClient = new HttpClient();
|
||||||
|
_httpClient.BaseAddress = serverUrl;
|
||||||
|
_httpClient.Timeout = TimeSpan.FromSeconds(50);
|
||||||
|
await _httpClient.GetAsync("health");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<GitHub.DistributedTask.WebApi.TaskAgentMessage> GetMessageAsync(Int32 poolId, Guid sessionId, Int64? lastMessageId, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
await _httpClient.GetAsync("message");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -40,6 +40,12 @@ namespace GitHub.Runner.Listener.Configuration
|
|||||||
return creds;
|
return creds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if USE_BROKER
|
||||||
|
public VssCredentials LoadCredentials()
|
||||||
|
{
|
||||||
|
return new VssCredentials();
|
||||||
|
}
|
||||||
|
#else
|
||||||
public VssCredentials LoadCredentials()
|
public VssCredentials LoadCredentials()
|
||||||
{
|
{
|
||||||
IConfigurationStore store = HostContext.GetService<IConfigurationStore>();
|
IConfigurationStore store = HostContext.GetService<IConfigurationStore>();
|
||||||
@@ -69,6 +75,7 @@ namespace GitHub.Runner.Listener.Configuration
|
|||||||
|
|
||||||
return creds;
|
return creds;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
[DataContract]
|
[DataContract]
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ namespace GitHub.Runner.Listener
|
|||||||
private RunnerSettings _settings;
|
private RunnerSettings _settings;
|
||||||
private ITerminal _term;
|
private ITerminal _term;
|
||||||
private IRunnerServer _runnerServer;
|
private IRunnerServer _runnerServer;
|
||||||
|
private IBrokerServer _brokerServer;
|
||||||
private TaskAgentSession _session;
|
private TaskAgentSession _session;
|
||||||
private TimeSpan _getNextMessageRetryInterval;
|
private TimeSpan _getNextMessageRetryInterval;
|
||||||
private bool _accessTokenRevoked = false;
|
private bool _accessTokenRevoked = false;
|
||||||
@@ -45,8 +46,44 @@ namespace GitHub.Runner.Listener
|
|||||||
|
|
||||||
_term = HostContext.GetService<ITerminal>();
|
_term = HostContext.GetService<ITerminal>();
|
||||||
_runnerServer = HostContext.GetService<IRunnerServer>();
|
_runnerServer = HostContext.GetService<IRunnerServer>();
|
||||||
|
_brokerServer = HostContext.GetService<IBrokerServer>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if USE_BROKER
|
||||||
|
public async Task<Boolean> CreateSessionAsync(CancellationToken token)
|
||||||
|
{
|
||||||
|
Trace.Entering();
|
||||||
|
|
||||||
|
// Settings
|
||||||
|
var configManager = HostContext.GetService<IConfigurationManager>();
|
||||||
|
_settings = configManager.LoadSettings();
|
||||||
|
var serverUrl = _settings.ServerUrl;
|
||||||
|
Trace.Info(_settings);
|
||||||
|
|
||||||
|
// Connect
|
||||||
|
token.ThrowIfCancellationRequested();
|
||||||
|
Trace.Info($"Attempt to create session.");
|
||||||
|
Trace.Info("Connecting to the Runner Server...");
|
||||||
|
_term.WriteLine($"Connecting to {new Uri(serverUrl)}");
|
||||||
|
await _brokerServer.ConnectAsync(new Uri(serverUrl));
|
||||||
|
_term.WriteLine();
|
||||||
|
_term.WriteSuccessMessage("Connected to GitHub");
|
||||||
|
_term.WriteLine();
|
||||||
|
|
||||||
|
// Session info
|
||||||
|
var agent = new TaskAgentReference
|
||||||
|
{
|
||||||
|
Id = _settings.AgentId,
|
||||||
|
Name = _settings.AgentName,
|
||||||
|
Version = BuildConstants.RunnerPackage.Version,
|
||||||
|
OSDescription = RuntimeInformation.OSDescription,
|
||||||
|
};
|
||||||
|
string sessionName = $"{Environment.MachineName ?? "RUNNER"}";
|
||||||
|
_session = new TaskAgentSession(sessionName, agent);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#else
|
||||||
public async Task<Boolean> CreateSessionAsync(CancellationToken token)
|
public async Task<Boolean> CreateSessionAsync(CancellationToken token)
|
||||||
{
|
{
|
||||||
Trace.Entering();
|
Trace.Entering();
|
||||||
@@ -151,6 +188,7 @@ namespace GitHub.Runner.Listener
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
public async Task DeleteSessionAsync()
|
public async Task DeleteSessionAsync()
|
||||||
{
|
{
|
||||||
@@ -170,6 +208,116 @@ namespace GitHub.Runner.Listener
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if USE_BROKER
|
||||||
|
public async Task<TaskAgentMessage> GetNextMessageAsync(CancellationToken token)
|
||||||
|
{
|
||||||
|
Trace.Entering();
|
||||||
|
ArgUtil.NotNull(_session, nameof(_session));
|
||||||
|
ArgUtil.NotNull(_settings, nameof(_settings));
|
||||||
|
bool encounteringError = false;
|
||||||
|
int continuousError = 0;
|
||||||
|
string errorMessage = string.Empty;
|
||||||
|
Stopwatch heartbeat = new Stopwatch();
|
||||||
|
heartbeat.Restart();
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
token.ThrowIfCancellationRequested();
|
||||||
|
TaskAgentMessage message = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
message = await _brokerServer.GetMessageAsync(_settings.PoolId, _session.SessionId, _lastMessageId, token);
|
||||||
|
|
||||||
|
// Decrypt the message body if the session is using encryption
|
||||||
|
message = DecryptMessage(message);
|
||||||
|
|
||||||
|
if (message != null)
|
||||||
|
{
|
||||||
|
_lastMessageId = message.MessageId;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (encounteringError) //print the message once only if there was an error
|
||||||
|
{
|
||||||
|
_term.WriteLine($"{DateTime.UtcNow:u}: Runner reconnected.");
|
||||||
|
encounteringError = false;
|
||||||
|
continuousError = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException) when (token.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
Trace.Info("Get next message has been cancelled.");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
catch (TaskAgentAccessTokenExpiredException)
|
||||||
|
{
|
||||||
|
Trace.Info("Runner OAuth token has been revoked. Unable to pull message.");
|
||||||
|
_accessTokenRevoked = true;
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Trace.Error("Catch exception during get next message.");
|
||||||
|
Trace.Error(ex);
|
||||||
|
|
||||||
|
// don't retry if SkipSessionRecover = true, DT service will delete agent session to stop agent from taking more jobs.
|
||||||
|
if (ex is TaskAgentSessionExpiredException && !_settings.SkipSessionRecover && await CreateSessionAsync(token))
|
||||||
|
{
|
||||||
|
Trace.Info($"{nameof(TaskAgentSessionExpiredException)} received, recovered by recreate session.");
|
||||||
|
}
|
||||||
|
else if (!IsGetNextMessageExceptionRetriable(ex))
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
continuousError++;
|
||||||
|
//retry after a random backoff to avoid service throttling
|
||||||
|
//in case of there is a service error happened and all agents get kicked off of the long poll and all agent try to reconnect back at the same time.
|
||||||
|
if (continuousError <= 5)
|
||||||
|
{
|
||||||
|
// random backoff [15, 30]
|
||||||
|
_getNextMessageRetryInterval = BackoffTimerHelper.GetRandomBackoff(TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(30), _getNextMessageRetryInterval);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// more aggressive backoff [30, 60]
|
||||||
|
_getNextMessageRetryInterval = BackoffTimerHelper.GetRandomBackoff(TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(60), _getNextMessageRetryInterval);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!encounteringError)
|
||||||
|
{
|
||||||
|
//print error only on the first consecutive error
|
||||||
|
_term.WriteError($"{DateTime.UtcNow:u}: Runner connect error: {ex.Message}. Retrying until reconnected.");
|
||||||
|
encounteringError = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// re-create VssConnection before next retry
|
||||||
|
await _runnerServer.RefreshConnectionAsync(RunnerConnectionType.MessageQueue, TimeSpan.FromSeconds(60));
|
||||||
|
|
||||||
|
Trace.Info("Sleeping for {0} seconds before retrying.", _getNextMessageRetryInterval.TotalSeconds);
|
||||||
|
await HostContext.Delay(_getNextMessageRetryInterval, token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message == null)
|
||||||
|
{
|
||||||
|
if (heartbeat.Elapsed > TimeSpan.FromMinutes(30))
|
||||||
|
{
|
||||||
|
Trace.Info($"No message retrieved from session '{_session.SessionId}' within last 30 minutes.");
|
||||||
|
heartbeat.Restart();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Trace.Verbose($"No message retrieved from session '{_session.SessionId}'.");
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Trace.Info($"Message '{message.MessageId}' received from session '{_session.SessionId}'.");
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
public async Task<TaskAgentMessage> GetNextMessageAsync(CancellationToken token)
|
public async Task<TaskAgentMessage> GetNextMessageAsync(CancellationToken token)
|
||||||
{
|
{
|
||||||
Trace.Entering();
|
Trace.Entering();
|
||||||
@@ -281,6 +429,7 @@ namespace GitHub.Runner.Listener
|
|||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
public async Task DeleteMessageAsync(TaskAgentMessage message)
|
public async Task DeleteMessageAsync(TaskAgentMessage message)
|
||||||
{
|
{
|
||||||
|
|||||||
14
src/dev.sh
14
src/dev.sh
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
#
|
#
|
||||||
# ./dev.sh build/layout/test/package [Debug/Release]
|
# ./dev.sh build/layout/test/package [Debug/Release] [linux-x64|linux-x86|linux-arm64|linux-arm|osx-x64|win-x64|win-x86] [use-broker]
|
||||||
#
|
#
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
@@ -11,6 +11,7 @@ set -e
|
|||||||
DEV_CMD=$1
|
DEV_CMD=$1
|
||||||
DEV_CONFIG=$2
|
DEV_CONFIG=$2
|
||||||
DEV_TARGET_RUNTIME=$3
|
DEV_TARGET_RUNTIME=$3
|
||||||
|
DEV_USE_BROKER=$4
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
LAYOUT_DIR="$SCRIPT_DIR/../_layout"
|
LAYOUT_DIR="$SCRIPT_DIR/../_layout"
|
||||||
@@ -81,6 +82,13 @@ elif [[ "$CURRENT_PLATFORM" == 'darwin' ]]; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ -n "$DEV_USE_BROKER" ]; then
|
||||||
|
USE_BROKER='-p:USE_BROKER="true"'
|
||||||
|
else
|
||||||
|
USE_BROKER=''
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
function failed()
|
function failed()
|
||||||
{
|
{
|
||||||
local error=${1:-Undefined error}
|
local error=${1:-Undefined error}
|
||||||
@@ -114,13 +122,13 @@ function heading()
|
|||||||
function build ()
|
function build ()
|
||||||
{
|
{
|
||||||
heading "Building ..."
|
heading "Building ..."
|
||||||
dotnet msbuild -t:Build -p:PackageRuntime="${RUNTIME_ID}" -p:BUILDCONFIG="${BUILD_CONFIG}" -p:RunnerVersion="${RUNNER_VERSION}" ./dir.proj || failed build
|
dotnet msbuild -t:Build -p:PackageRuntime="${RUNTIME_ID}" -p:BUILDCONFIG="${BUILD_CONFIG}" -p:RunnerVersion="${RUNNER_VERSION}" $USE_BROKER ./dir.proj || failed build
|
||||||
}
|
}
|
||||||
|
|
||||||
function layout ()
|
function layout ()
|
||||||
{
|
{
|
||||||
heading "Create layout ..."
|
heading "Create layout ..."
|
||||||
dotnet msbuild -t:layout -p:PackageRuntime="${RUNTIME_ID}" -p:BUILDCONFIG="${BUILD_CONFIG}" -p:RunnerVersion="${RUNNER_VERSION}" ./dir.proj || failed build
|
dotnet msbuild -t:layout -p:PackageRuntime="${RUNTIME_ID}" -p:BUILDCONFIG="${BUILD_CONFIG}" -p:RunnerVersion="${RUNNER_VERSION}" $USE_BROKER ./dir.proj || failed build
|
||||||
|
|
||||||
#change execution flag to allow running with sudo
|
#change execution flag to allow running with sudo
|
||||||
if [[ ("$CURRENT_PLATFORM" == "linux") || ("$CURRENT_PLATFORM" == "darwin") ]]; then
|
if [[ ("$CURRENT_PLATFORM" == "linux") || ("$CURRENT_PLATFORM" == "darwin") ]]; then
|
||||||
|
|||||||
Reference in New Issue
Block a user