diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 151f0a9e0..c8616ede7 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -21,4 +21,4 @@ }, "postCreateCommand": "dotnet restore src/Test && dotnet restore src/Runner.PluginHost", "remoteUser": "vscode" -} +} \ No newline at end of file diff --git a/src/Runner.Common/BrokerServer.cs b/src/Runner.Common/BrokerServer.cs index f3a3a5715..d6fefdc17 100644 --- a/src/Runner.Common/BrokerServer.cs +++ b/src/Runner.Common/BrokerServer.cs @@ -15,7 +15,7 @@ namespace GitHub.Runner.Common [ServiceLocator(Default = typeof(BrokerServer))] public interface IBrokerServer : IRunnerService { - Task ConnectAsync(Uri serverUrl, VssCredentials credentials); + Task ConnectAsync(Uri serverUrl, VssCredentials credentials); Task GetRunnerMessageAsync(CancellationToken token, TaskAgentStatus status, string version); } @@ -26,19 +26,25 @@ namespace GitHub.Runner.Common private Uri _brokerUri; private RawConnection _connection; private BrokerHttpClient _brokerHttpClient; + private bool _hasSession; + private BrokerSession _session; - public async Task ConnectAsync(Uri serverUri, VssCredentials credentials) + public async Task ConnectAsync(Uri serverUri, VssCredentials credentials) { _brokerUri = serverUri; _connection = VssUtil.CreateRawConnection(serverUri, credentials); _brokerHttpClient = await _connection.GetClientAsync(); + _session = await _brokerHttpClient.CreateSessionAsync(); _hasConnection = true; + _hasSession = true; + + return _session; } private void CheckConnection() { - if (!_hasConnection) + if (!_hasConnection || !_hasSession) { throw new InvalidOperationException($"SetConnection"); } @@ -48,7 +54,7 @@ namespace GitHub.Runner.Common { CheckConnection(); var jobMessage = RetryRequest( - async () => await _brokerHttpClient.GetRunnerMessageAsync(version, status, cancellationToken), cancellationToken); + async () => await _brokerHttpClient.GetRunnerMessageAsync(_session.id, version, status, cancellationToken), cancellationToken); return jobMessage; } diff --git a/src/Runner.Listener/BrokerMessageListener.cs b/src/Runner.Listener/BrokerMessageListener.cs index f770fd8ac..d31f1ea10 100644 --- a/src/Runner.Listener/BrokerMessageListener.cs +++ b/src/Runner.Listener/BrokerMessageListener.cs @@ -26,6 +26,8 @@ namespace GitHub.Runner.Listener private CancellationTokenSource _getMessagesTokenSource; private IBrokerServer _brokerServer; + public string _sessionId; + public override void Initialize(IHostContext hostContext) { base.Initialize(hostContext); @@ -203,7 +205,8 @@ namespace GitHub.Runner.Listener var credMgr = HostContext.GetService(); VssCredentials creds = credMgr.LoadCredentials(); - await _brokerServer.ConnectAsync(new Uri(_settings.ServerUrlV2), creds); + var sessionResponse = await _brokerServer.ConnectAsync(new Uri(_settings.ServerUrlV2), creds); + _sessionId = sessionResponse.id; } } } diff --git a/src/Sdk/WebApi/WebApi/BrokerHttpClient.cs b/src/Sdk/WebApi/WebApi/BrokerHttpClient.cs index 42d69ee46..8d079dd95 100644 --- a/src/Sdk/WebApi/WebApi/BrokerHttpClient.cs +++ b/src/Sdk/WebApi/WebApi/BrokerHttpClient.cs @@ -56,7 +56,32 @@ namespace GitHub.Actions.RunService.WebApi { } + public async Task CreateSessionAsync( + CancellationToken cancellationToken = default + ) + { + var requestUri = new Uri(Client.BaseAddress, "session"); + + var result = await SendAsync( + new HttpMethod("POST"), + requestUri: requestUri, + cancellationToken: cancellationToken + ); + + if (result.IsSuccess) + { + return result.Value; + } + + if (result.StatusCode == HttpStatusCode.Forbidden) + { + throw new AccessDeniedException(result.Error); + } + + throw new Exception($"Failed to get job message: {result.Error}"); + } public async Task GetRunnerMessageAsync( + string sessionID, string runnerVersion, TaskAgentStatus? status, CancellationToken cancellationToken = default @@ -66,6 +91,10 @@ namespace GitHub.Actions.RunService.WebApi List> queryParams = new List>(); + if (sessionID != null) + { + queryParams.Add("sessionID", runnerVersion); + } if (status != null) { queryParams.Add("status", status.Value.ToString()); diff --git a/src/Sdk/WebApi/WebApi/BrokerSession.cs b/src/Sdk/WebApi/WebApi/BrokerSession.cs new file mode 100644 index 000000000..df955a5e3 --- /dev/null +++ b/src/Sdk/WebApi/WebApi/BrokerSession.cs @@ -0,0 +1,9 @@ +using System; + +namespace GitHub.Actions.RunService.WebApi +{ + public sealed class BrokerSession + { + public string id; + } +} diff --git a/src/Test/L0/Listener/BrokerMessageListenerL0.cs b/src/Test/L0/Listener/BrokerMessageListenerL0.cs new file mode 100644 index 000000000..12e92d2c4 --- /dev/null +++ b/src/Test/L0/Listener/BrokerMessageListenerL0.cs @@ -0,0 +1,61 @@ +using System; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; +using GitHub.Actions.RunService.WebApi; +using GitHub.Runner.Listener; +using GitHub.Runner.Listener.Configuration; +using GitHub.Services.Common; +using Moq; +using Xunit; + +namespace GitHub.Runner.Common.Tests.Listener +{ + public sealed class BrokerMessageListenerL0 + { + private readonly RunnerSettings _settings; + private readonly Mock _config; + private readonly Mock _brokerServer; + private readonly Mock _credMgr; + + public BrokerMessageListenerL0() + { + _settings = new RunnerSettings { AgentId = 1, AgentName = "myagent", PoolId = 123, PoolName = "default", ServerUrlV2 = "http://myserver", WorkFolder = "_work" }; + _config = new Mock(); + _config.Setup(x => x.LoadSettings()).Returns(_settings); + _brokerServer = new Mock(); + _credMgr = new Mock(); + _credMgr.Setup(x => x.LoadCredentials()).Returns(new VssCredentials()); + } + + [Fact] + [Trait("Level", "L0")] + [Trait("Category", "Runner")] + public async void CreatesSessionAsync() + { + using TestHostContext tc = CreateTestContext(); + using var tokenSource = new CancellationTokenSource(); + + // Arrange. + _brokerServer.Setup(x => x.ConnectAsync(new Uri(_settings.ServerUrlV2), It.Is(y => y != null))).Returns(Task.FromResult(new BrokerSession { id = "my-phony-session-id" })); + BrokerMessageListener listener = new(); + listener.Initialize(tc); + + // Act. + bool result = await listener.CreateSessionAsync(tokenSource.Token); + + // Assert. + Assert.True(result); + Assert.Equal("my-phony-session-id", listener._sessionId); + } + + private TestHostContext CreateTestContext([CallerMemberName] String testName = "") + { + TestHostContext tc = new(this, testName); + tc.SetSingleton(_config.Object); + tc.SetSingleton(_brokerServer.Object); + tc.SetSingleton(_credMgr.Object); + return tc; + } + } +}