Files
runner/src/Sdk/RSWebApi/RunServiceHttpClient.cs
T

260 lines
8.7 KiB
C#
Raw Normal View History

2023-06-02 21:47:59 +02:00
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using GitHub.DistributedTask.Pipelines;
using GitHub.DistributedTask.WebApi;
using GitHub.Services.Common;
using GitHub.Services.OAuth;
using GitHub.Services.WebApi;
using Newtonsoft.Json;
2023-02-27 11:50:28 -05:00
using Sdk.RSWebApi.Contracts;
using Sdk.WebApi.WebApi;
namespace GitHub.Actions.RunService.WebApi
{
public class RunServiceHttpClient : RawHttpClientBase
{
private static readonly JsonSerializerSettings s_serializerSettings;
static RunServiceHttpClient()
{
s_serializerSettings = new VssJsonMediaTypeFormatter().SerializerSettings;
s_serializerSettings.DateParseHandling = DateParseHandling.None;
s_serializerSettings.FloatParseHandling = FloatParseHandling.Double;
}
public RunServiceHttpClient(
Uri baseUrl,
VssOAuthCredential credentials)
: base(baseUrl, credentials)
{
}
public RunServiceHttpClient(
Uri baseUrl,
VssOAuthCredential credentials,
RawClientHttpRequestSettings settings)
: base(baseUrl, credentials, settings)
{
}
public RunServiceHttpClient(
Uri baseUrl,
VssOAuthCredential credentials,
params DelegatingHandler[] handlers)
: base(baseUrl, credentials, handlers)
{
}
public RunServiceHttpClient(
Uri baseUrl,
VssOAuthCredential credentials,
RawClientHttpRequestSettings settings,
params DelegatingHandler[] handlers)
: base(baseUrl, credentials, settings, handlers)
{
}
public RunServiceHttpClient(
Uri baseUrl,
HttpMessageHandler pipeline,
Boolean disposeHandler)
: base(baseUrl, pipeline, disposeHandler)
{
}
public async Task<AgentJobRequestMessage> GetJobMessageAsync(
Uri requestUri,
string messageId,
2024-02-09 15:19:52 -05:00
string runnerOS,
CancellationToken cancellationToken = default)
{
HttpMethod httpMethod = new HttpMethod("POST");
var payload = new AcquireJobRequest
{
JobMessageId = messageId,
2024-02-09 15:19:52 -05:00
RunnerOS = runnerOS
};
requestUri = new Uri(requestUri, "acquirejob");
var requestContent = new ObjectContent<AcquireJobRequest>(payload, new VssJsonMediaTypeFormatter(true));
var result = await SendAsync<AgentJobRequestMessage>(
httpMethod,
requestUri: requestUri,
content: requestContent,
2024-06-19 11:38:32 -05:00
readErrorBody: true,
cancellationToken: cancellationToken);
if (result.IsSuccess)
{
return result.Value;
}
2024-06-19 11:38:32 -05:00
if (TryParseErrorBody(result.ErrorBody, out RunServiceError error))
{
switch ((HttpStatusCode)error.Code)
{
case HttpStatusCode.NotFound:
throw new TaskOrchestrationJobNotFoundException($"Job message not found '{messageId}'. {error.Message}");
case HttpStatusCode.Conflict:
throw new TaskOrchestrationJobAlreadyAcquiredException($"Job message already acquired '{messageId}'. {error.Message}");
case HttpStatusCode.UnprocessableEntity:
throw new TaskOrchestrationJobUnprocessableException($"Unprocessable job '{messageId}'. {error.Message}");
}
}
if (!string.IsNullOrEmpty(result.ErrorBody))
{
throw new Exception($"Failed to get job message: {result.Error}. {Truncate(result.ErrorBody)}");
}
else
{
throw new Exception($"Failed to get job message: {result.Error}");
}
}
public async Task CompleteJobAsync(
Uri requestUri,
Guid planId,
Guid jobId,
2024-06-19 11:38:32 -05:00
TaskResult conclusion,
Dictionary<String, VariableValue> outputs,
IList<StepResult> stepResults,
2023-05-01 08:33:03 -04:00
IList<Annotation> jobAnnotations,
2023-06-16 00:13:08 -04:00
string environmentUrl,
CancellationToken cancellationToken = default)
{
HttpMethod httpMethod = new HttpMethod("POST");
var payload = new CompleteJobRequest()
{
PlanID = planId,
JobID = jobId,
2024-06-19 11:38:32 -05:00
Conclusion = conclusion,
Outputs = outputs,
2023-05-01 08:33:03 -04:00
StepResults = stepResults,
2023-06-16 00:13:08 -04:00
Annotations = jobAnnotations,
EnvironmentUrl = environmentUrl,
};
requestUri = new Uri(requestUri, "completejob");
var requestContent = new ObjectContent<CompleteJobRequest>(payload, new VssJsonMediaTypeFormatter(true));
2024-06-19 11:38:32 -05:00
var result = await Send2Async(
2023-02-27 11:50:28 -05:00
httpMethod,
requestUri,
content: requestContent,
cancellationToken: cancellationToken);
2024-06-19 11:38:32 -05:00
if (result.IsSuccess)
{
return;
}
2024-06-19 11:38:32 -05:00
if (TryParseErrorBody(result.ErrorBody, out RunServiceError error))
{
switch ((HttpStatusCode)error.Code)
{
case HttpStatusCode.NotFound:
throw new TaskOrchestrationJobNotFoundException($"Job not found: {jobId}. {error.Message}");
}
}
if (!string.IsNullOrEmpty(result.ErrorBody))
{
throw new Exception($"Failed to complete job: {result.Error}. {Truncate(result.ErrorBody)}");
}
else
{
throw new Exception($"Failed to complete job: {result.Error}");
}
2023-02-27 11:50:28 -05:00
}
public async Task<RenewJobResponse> RenewJobAsync(
2023-02-27 11:50:28 -05:00
Uri requestUri,
Guid planId,
Guid jobId,
CancellationToken cancellationToken = default)
{
HttpMethod httpMethod = new HttpMethod("POST");
var payload = new RenewJobRequest()
{
PlanID = planId,
JobID = jobId
};
requestUri = new Uri(requestUri, "renewjob");
var requestContent = new ObjectContent<RenewJobRequest>(payload, new VssJsonMediaTypeFormatter(true));
var result = await SendAsync<RenewJobResponse>(
httpMethod,
requestUri,
content: requestContent,
2024-06-19 11:38:32 -05:00
readErrorBody: true,
cancellationToken: cancellationToken);
if (result.IsSuccess)
{
return result.Value;
}
2024-06-19 11:38:32 -05:00
if (TryParseErrorBody(result.ErrorBody, out RunServiceError error))
{
switch ((HttpStatusCode)error.Code)
{
case HttpStatusCode.NotFound:
throw new TaskOrchestrationJobNotFoundException($"Job not found: {jobId}. {error.Message}");
}
}
if (!string.IsNullOrEmpty(result.ErrorBody))
{
throw new Exception($"Failed to renew job: {result.Error}. {Truncate(result.ErrorBody)}");
}
else
{
throw new Exception($"Failed to renew job: {result.Error}");
}
}
protected override async Task<T> ReadJsonContentAsync<T>(HttpResponseMessage response, CancellationToken cancellationToken = default(CancellationToken))
{
var json = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
return JsonConvert.DeserializeObject<T>(json, s_serializerSettings);
}
2024-06-19 11:38:32 -05:00
private static bool TryParseErrorBody(string errorBody, out RunServiceError error)
{
if (!string.IsNullOrEmpty(errorBody))
{
try
{
error = JsonUtility.FromString<RunServiceError>(errorBody);
if (error?.Source == "actions-run-service")
{
return true;
}
}
catch (Exception)
{
}
}
error = null;
return false;
}
private static string Truncate(string errorBody)
{
if (errorBody.Length > 100)
{
return errorBody.Substring(0, 100) + "[truncated]";
}
return errorBody;
}
}
}