mirror of
https://github.com/actions/runner.git
synced 2025-12-11 12:57:05 +00:00
Compare commits
1 Commits
fhammerl/e
...
fhammerl/p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
71ed68384e |
38
docs/adrs/0000-proxy-update.md
Normal file
38
docs/adrs/0000-proxy-update.md
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
# ADR 000: Update Proxy Behavior of Self-Hosted Runners
|
||||||
|
|
||||||
|
**Date**: 2023-02-21
|
||||||
|
|
||||||
|
**Status**: Pending
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
Today, the different user-accessible building blocks of GitHub Actions implement proxy behaviour with significant functional differences.
|
||||||
|
Users could realistically run all of the below examples and they would reasonably expect that their proxy settings will have the same networking effects across all of them.
|
||||||
|
|
||||||
|
A user running `actions/actions-runner-controller`, which starts instances of `actions/runner`, that the runs node.js actions made with `actions/toolkit`
|
||||||
|
- ARC and its controller and listener pods in k8s will follow `golang` defaults for proxy behaviour
|
||||||
|
- The `runner` overrides the default proxy behaviour of C# and implements it [explicitly](https://github.com/actions/runner/blob/main/src/Runner.Sdk/RunnerWebProxy.cs), however currently differently from `toolkit`
|
||||||
|
- `toolkit` overrides the default proxy behaviour of node.js and implements it [explicitly](https://github.com/actions/toolkit/blob/main/packages/http-client/src/proxy.ts), however currently differently from `runner`
|
||||||
|
|
||||||
|
|
||||||
|
## Example 1 - ARC
|
||||||
|
|
||||||
|
A user wants to create a scaleset in ARC. They give following settings when creating an ARC Scale Set:
|
||||||
|
- `https_proxy=https://someproxy.company.com`
|
||||||
|
- `no_proxy=8.8.8.8,192.168.1.1/32`
|
||||||
|
The ENV variables are propagated through all actors, but:
|
||||||
|
|
||||||
|
- *ARC operators and listener pods* will: follow the proxy but bypass it for `8.8.8.8` and the CIDR block `192.168.1.1/32`
|
||||||
|
- *The runner* will: use a proxy and ignore these `no_proxy` settings (no IP support in `runner` for `no_proxy`)
|
||||||
|
- *A node.js GitHub Action in a job executed by the runner* will: bypass `8.8.8.8` but use proxy for the CIDR block `192.168.1.1/32`
|
||||||
|
|
||||||
|
## Example 2 - Self-Hosted runner
|
||||||
|
|
||||||
|
Given the following settings when creating an ARC Scale Set:
|
||||||
|
- `https_proxy=someproxy.company.com`
|
||||||
|
|
||||||
|
- *The runner* will: silently ignore `https_proxy` value because it doesn't have a protocol (missing `https://`)
|
||||||
|
- *A node.js GitHub Action in a job executed by the runner* will: throw an exception because proxy could not be parsed (missing `https://`)
|
||||||
|
|
||||||
|
## Decisions
|
||||||
|
## Consequences
|
||||||
@@ -24,26 +24,11 @@ RUN export DOCKER_ARCH=x86_64 \
|
|||||||
|
|
||||||
FROM mcr.microsoft.com/dotnet/runtime-deps:6.0
|
FROM mcr.microsoft.com/dotnet/runtime-deps:6.0
|
||||||
|
|
||||||
ENV DEBIAN_FRONTEND=noninteractive
|
ENV RUNNER_ALLOW_RUNASROOT=1
|
||||||
ENV RUNNER_MANUALLY_TRAP_SIG=1
|
ENV RUNNER_MANUALLY_TRAP_SIG=1
|
||||||
ENV ACTIONS_RUNNER_PRINT_LOG_TO_STDOUT=1
|
ENV ACTIONS_RUNNER_PRINT_LOG_TO_STDOUT=1
|
||||||
|
|
||||||
RUN apt-get update -y \
|
WORKDIR /actions-runner
|
||||||
&& apt-get install -y --no-install-recommends \
|
COPY --from=build /actions-runner .
|
||||||
sudo \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
RUN adduser --disabled-password --gecos "" --uid 1001 runner \
|
|
||||||
&& groupadd docker --gid 123 \
|
|
||||||
&& usermod -aG sudo runner \
|
|
||||||
&& usermod -aG docker runner \
|
|
||||||
&& echo "%sudo ALL=(ALL:ALL) NOPASSWD:ALL" > /etc/sudoers \
|
|
||||||
&& echo "Defaults env_keep += \"DEBIAN_FRONTEND\"" >> /etc/sudoers
|
|
||||||
|
|
||||||
WORKDIR /home/runner
|
|
||||||
|
|
||||||
COPY --chown=runner:docker --from=build /actions-runner .
|
|
||||||
|
|
||||||
RUN install -o root -g root -m 755 docker/* /usr/bin/ && rm -rf docker
|
RUN install -o root -g root -m 755 docker/* /usr/bin/ && rm -rf docker
|
||||||
|
|
||||||
USER runner
|
|
||||||
@@ -24,8 +24,7 @@ if (exitServiceAfterNFailures <= 0) {
|
|||||||
exitServiceAfterNFailures = NaN;
|
exitServiceAfterNFailures = NaN;
|
||||||
}
|
}
|
||||||
|
|
||||||
var unknownFailureRetryCount = 0;
|
var consecutiveFailureCount = 0;
|
||||||
var retriableFailureRetryCount = 0;
|
|
||||||
|
|
||||||
var gracefulShutdown = function () {
|
var gracefulShutdown = function () {
|
||||||
console.log("Shutting down runner listener");
|
console.log("Shutting down runner listener");
|
||||||
@@ -63,8 +62,7 @@ var runService = function () {
|
|||||||
|
|
||||||
listener.stdout.on("data", (data) => {
|
listener.stdout.on("data", (data) => {
|
||||||
if (data.toString("utf8").includes("Listening for Jobs")) {
|
if (data.toString("utf8").includes("Listening for Jobs")) {
|
||||||
unknownFailureRetryCount = 0;
|
consecutiveFailureCount = 0;
|
||||||
retriableFailureRetryCount = 0;
|
|
||||||
}
|
}
|
||||||
process.stdout.write(data.toString("utf8"));
|
process.stdout.write(data.toString("utf8"));
|
||||||
});
|
});
|
||||||
@@ -94,38 +92,24 @@ var runService = function () {
|
|||||||
console.log(
|
console.log(
|
||||||
"Runner listener exit with retryable error, re-launch runner in 5 seconds."
|
"Runner listener exit with retryable error, re-launch runner in 5 seconds."
|
||||||
);
|
);
|
||||||
unknownFailureRetryCount = 0;
|
consecutiveFailureCount = 0;
|
||||||
retriableFailureRetryCount++;
|
|
||||||
if (retriableFailureRetryCount >= 10) {
|
|
||||||
console.error(
|
|
||||||
"Stopping the runner after 10 consecutive re-tryable failures"
|
|
||||||
);
|
|
||||||
stopping = true;
|
|
||||||
}
|
|
||||||
} else if (code === 3 || code === 4) {
|
} else if (code === 3 || code === 4) {
|
||||||
console.log(
|
console.log(
|
||||||
"Runner listener exit because of updating, re-launch runner in 5 seconds."
|
"Runner listener exit because of updating, re-launch runner in 5 seconds."
|
||||||
);
|
);
|
||||||
unknownFailureRetryCount = 0;
|
consecutiveFailureCount = 0;
|
||||||
retriableFailureRetryCount++;
|
|
||||||
if (retriableFailureRetryCount >= 10) {
|
|
||||||
console.error(
|
|
||||||
"Stopping the runner after 10 consecutive re-tryable failures"
|
|
||||||
);
|
|
||||||
stopping = true;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
var messagePrefix = "Runner listener exit with undefined return code";
|
var messagePrefix = "Runner listener exit with undefined return code";
|
||||||
unknownFailureRetryCount++;
|
consecutiveFailureCount++;
|
||||||
retriableFailureRetryCount = 0;
|
|
||||||
if (
|
if (
|
||||||
!isNaN(exitServiceAfterNFailures) &&
|
!isNaN(exitServiceAfterNFailures) &&
|
||||||
unknownFailureRetryCount >= exitServiceAfterNFailures
|
consecutiveFailureCount >= exitServiceAfterNFailures
|
||||||
) {
|
) {
|
||||||
console.error(
|
console.error(
|
||||||
`${messagePrefix}, exiting service after ${unknownFailureRetryCount} consecutive failures`
|
`${messagePrefix}, exiting service after ${consecutiveFailureCount} consecutive failures`
|
||||||
);
|
);
|
||||||
stopping = true
|
gracefulShutdown();
|
||||||
|
return;
|
||||||
} else {
|
} else {
|
||||||
console.log(`${messagePrefix}, re-launch runner in 5 seconds.`);
|
console.log(`${messagePrefix}, re-launch runner in 5 seconds.`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@@ -7,7 +7,6 @@ using GitHub.DistributedTask.Pipelines;
|
|||||||
using GitHub.DistributedTask.WebApi;
|
using GitHub.DistributedTask.WebApi;
|
||||||
using GitHub.Runner.Sdk;
|
using GitHub.Runner.Sdk;
|
||||||
using GitHub.Services.Common;
|
using GitHub.Services.Common;
|
||||||
using Sdk.RSWebApi.Contracts;
|
|
||||||
using Sdk.WebApi.WebApi.RawClient;
|
using Sdk.WebApi.WebApi.RawClient;
|
||||||
|
|
||||||
namespace GitHub.Runner.Common
|
namespace GitHub.Runner.Common
|
||||||
@@ -20,8 +19,6 @@ namespace GitHub.Runner.Common
|
|||||||
Task<AgentJobRequestMessage> GetJobMessageAsync(string id, CancellationToken token);
|
Task<AgentJobRequestMessage> GetJobMessageAsync(string id, CancellationToken token);
|
||||||
|
|
||||||
Task CompleteJobAsync(Guid planId, Guid jobId, TaskResult result, Dictionary<String, VariableValue> outputs, IList<StepResult> stepResults, CancellationToken token);
|
Task CompleteJobAsync(Guid planId, Guid jobId, TaskResult result, Dictionary<String, VariableValue> outputs, IList<StepResult> stepResults, CancellationToken token);
|
||||||
|
|
||||||
Task<RenewJobResponse> RenewJobAsync(Guid planId, Guid jobId, CancellationToken token);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class RunServer : RunnerService, IRunServer
|
public sealed class RunServer : RunnerService, IRunServer
|
||||||
@@ -67,18 +64,5 @@ namespace GitHub.Runner.Common
|
|||||||
return RetryRequest(
|
return RetryRequest(
|
||||||
async () => await _runServiceHttpClient.CompleteJobAsync(requestUri, planId, jobId, result, outputs, stepResults, cancellationToken), cancellationToken);
|
async () => await _runServiceHttpClient.CompleteJobAsync(requestUri, planId, jobId, result, outputs, stepResults, cancellationToken), cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<RenewJobResponse> RenewJobAsync(Guid planId, Guid jobId, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
CheckConnection();
|
|
||||||
var renewJobResponse = RetryRequest<RenewJobResponse>(
|
|
||||||
async () => await _runServiceHttpClient.RenewJobAsync(requestUri, planId, jobId, cancellationToken), cancellationToken);
|
|
||||||
if (renewJobResponse == null)
|
|
||||||
{
|
|
||||||
throw new TaskOrchestrationJobNotFoundException(jobId.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
return renewJobResponse;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
namespace GitHub.Runner.Common.Util
|
|
||||||
{
|
|
||||||
using System;
|
|
||||||
using GitHub.DistributedTask.WebApi;
|
|
||||||
|
|
||||||
public static class MessageUtil
|
|
||||||
{
|
|
||||||
public static bool IsRunServiceJob(string messageType)
|
|
||||||
{
|
|
||||||
return string.Equals(messageType, JobRequestMessageTypes.RunnerJobRequest, StringComparison.OrdinalIgnoreCase);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
using System.Runtime.CompilerServices;
|
|
||||||
|
|
||||||
[assembly: InternalsVisibleTo("Test")]
|
|
||||||
@@ -7,7 +7,6 @@ using System.Text;
|
|||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using GitHub.DistributedTask.Pipelines;
|
|
||||||
using GitHub.DistributedTask.WebApi;
|
using GitHub.DistributedTask.WebApi;
|
||||||
using GitHub.Runner.Common;
|
using GitHub.Runner.Common;
|
||||||
using GitHub.Runner.Common.Util;
|
using GitHub.Runner.Common.Util;
|
||||||
@@ -59,8 +58,6 @@ namespace GitHub.Runner.Listener
|
|||||||
|
|
||||||
public event EventHandler<JobStatusEventArgs> JobStatus;
|
public event EventHandler<JobStatusEventArgs> JobStatus;
|
||||||
|
|
||||||
private bool _isRunServiceJob;
|
|
||||||
|
|
||||||
public override void Initialize(IHostContext hostContext)
|
public override void Initialize(IHostContext hostContext)
|
||||||
{
|
{
|
||||||
base.Initialize(hostContext);
|
base.Initialize(hostContext);
|
||||||
@@ -89,8 +86,6 @@ namespace GitHub.Runner.Listener
|
|||||||
{
|
{
|
||||||
Trace.Info($"Job request {jobRequestMessage.RequestId} for plan {jobRequestMessage.Plan.PlanId} job {jobRequestMessage.JobId} received.");
|
Trace.Info($"Job request {jobRequestMessage.RequestId} for plan {jobRequestMessage.Plan.PlanId} job {jobRequestMessage.JobId} received.");
|
||||||
|
|
||||||
_isRunServiceJob = MessageUtil.IsRunServiceJob(jobRequestMessage.MessageType);
|
|
||||||
|
|
||||||
WorkerDispatcher currentDispatch = null;
|
WorkerDispatcher currentDispatch = null;
|
||||||
if (_jobDispatchedQueue.Count > 0)
|
if (_jobDispatchedQueue.Count > 0)
|
||||||
{
|
{
|
||||||
@@ -244,13 +239,6 @@ namespace GitHub.Runner.Listener
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._isRunServiceJob)
|
|
||||||
{
|
|
||||||
Trace.Error($"We are not yet checking the state of jobrequest {jobDispatch.JobId} status. Cancel running worker right away.");
|
|
||||||
jobDispatch.WorkerCancellationTokenSource.Cancel();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// based on the current design, server will only send one job for a given runner at a time.
|
// based on the current design, server will only send one job for a given runner at a time.
|
||||||
// if the runner received a new job request while a previous job request is still running, this typically indicates two situations
|
// if the runner received a new job request while a previous job request is still running, this typically indicates two situations
|
||||||
// 1. a runner bug caused a server and runner mismatch on the state of the job request, e.g. the runner didn't renew the jobrequest
|
// 1. a runner bug caused a server and runner mismatch on the state of the job request, e.g. the runner didn't renew the jobrequest
|
||||||
@@ -379,11 +367,9 @@ namespace GitHub.Runner.Listener
|
|||||||
long requestId = message.RequestId;
|
long requestId = message.RequestId;
|
||||||
Guid lockToken = Guid.Empty; // lockToken has never been used, keep this here of compat
|
Guid lockToken = Guid.Empty; // lockToken has never been used, keep this here of compat
|
||||||
|
|
||||||
var systemConnection = message.Resources.Endpoints.SingleOrDefault(x => string.Equals(x.Name, WellKnownServiceEndpointNames.SystemVssConnection, StringComparison.OrdinalIgnoreCase));
|
|
||||||
|
|
||||||
// start renew job request
|
// start renew job request
|
||||||
Trace.Info($"Start renew job request {requestId} for job {message.JobId}.");
|
Trace.Info($"Start renew job request {requestId} for job {message.JobId}.");
|
||||||
Task renewJobRequest = RenewJobRequestAsync(message, systemConnection, _poolId, requestId, lockToken, orchestrationId, firstJobRequestRenewed, lockRenewalTokenSource.Token);
|
Task renewJobRequest = RenewJobRequestAsync(_poolId, requestId, lockToken, orchestrationId, firstJobRequestRenewed, lockRenewalTokenSource.Token);
|
||||||
|
|
||||||
// wait till first renew succeed or job request is cancelled
|
// wait till first renew succeed or job request is cancelled
|
||||||
// not even start worker if the first renew fail
|
// not even start worker if the first renew fail
|
||||||
@@ -522,6 +508,7 @@ namespace GitHub.Runner.Listener
|
|||||||
|
|
||||||
// we get first jobrequest renew succeed and start the worker process with the job message.
|
// we get first jobrequest renew succeed and start the worker process with the job message.
|
||||||
// send notification to machine provisioner.
|
// send notification to machine provisioner.
|
||||||
|
var systemConnection = message.Resources.Endpoints.SingleOrDefault(x => string.Equals(x.Name, WellKnownServiceEndpointNames.SystemVssConnection, StringComparison.OrdinalIgnoreCase));
|
||||||
var accessToken = systemConnection?.Authorization?.Parameters["AccessToken"];
|
var accessToken = systemConnection?.Authorization?.Parameters["AccessToken"];
|
||||||
notification.JobStarted(message.JobId, accessToken, systemConnection.Url);
|
notification.JobStarted(message.JobId, accessToken, systemConnection.Url);
|
||||||
|
|
||||||
@@ -544,8 +531,11 @@ namespace GitHub.Runner.Listener
|
|||||||
detailInfo = string.Join(Environment.NewLine, workerOutput);
|
detailInfo = string.Join(Environment.NewLine, workerOutput);
|
||||||
Trace.Info($"Return code {returnCode} indicate worker encounter an unhandled exception or app crash, attach worker stdout/stderr to JobRequest result.");
|
Trace.Info($"Return code {returnCode} indicate worker encounter an unhandled exception or app crash, attach worker stdout/stderr to JobRequest result.");
|
||||||
|
|
||||||
|
var jobServer = HostContext.GetService<IJobServer>();
|
||||||
|
VssCredentials jobServerCredential = VssUtil.GetVssCredential(systemConnection);
|
||||||
|
VssConnection jobConnection = VssUtil.CreateConnection(systemConnection.Url, jobServerCredential);
|
||||||
|
await jobServer.ConnectAsync(jobConnection);
|
||||||
|
|
||||||
var jobServer = await InitializeJobServerAsync(systemConnection);
|
|
||||||
await LogWorkerProcessUnhandledException(jobServer, message, detailInfo);
|
await LogWorkerProcessUnhandledException(jobServer, message, detailInfo);
|
||||||
|
|
||||||
// Go ahead to finish the job with result 'Failed' if the STDERR from worker is System.IO.IOException, since it typically means we are running out of disk space.
|
// Go ahead to finish the job with result 'Failed' if the STDERR from worker is System.IO.IOException, since it typically means we are running out of disk space.
|
||||||
@@ -685,128 +675,9 @@ namespace GitHub.Runner.Listener
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task RenewJobRequestAsync(Pipelines.AgentJobRequestMessage message, ServiceEndpoint systemConnection, int poolId, long requestId, Guid lockToken, string orchestrationId, TaskCompletionSource<int> firstJobRequestRenewed, CancellationToken token)
|
public async Task RenewJobRequestAsync(int poolId, long requestId, Guid lockToken, string orchestrationId, TaskCompletionSource<int> firstJobRequestRenewed, CancellationToken token)
|
||||||
{
|
|
||||||
if (this._isRunServiceJob)
|
|
||||||
{
|
|
||||||
var runServer = await GetRunServerAsync(systemConnection);
|
|
||||||
await RenewJobRequestAsync(runServer, message.Plan.PlanId, message.JobId, firstJobRequestRenewed, token);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
var runnerServer = HostContext.GetService<IRunnerServer>();
|
var runnerServer = HostContext.GetService<IRunnerServer>();
|
||||||
await RenewJobRequestAsync(runnerServer, poolId, requestId, lockToken, orchestrationId, firstJobRequestRenewed, token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task RenewJobRequestAsync(IRunServer runServer, Guid planId, Guid jobId, TaskCompletionSource<int> firstJobRequestRenewed, CancellationToken token)
|
|
||||||
{
|
|
||||||
TaskAgentJobRequest request = null;
|
|
||||||
int firstRenewRetryLimit = 5;
|
|
||||||
int encounteringError = 0;
|
|
||||||
|
|
||||||
// renew lock during job running.
|
|
||||||
// stop renew only if cancellation token for lock renew task been signal or exception still happen after retry.
|
|
||||||
while (!token.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var renewResponse = await runServer.RenewJobAsync(planId, jobId, token);
|
|
||||||
Trace.Info($"Successfully renew job {jobId}, job is valid till {renewResponse.LockedUntil}");
|
|
||||||
|
|
||||||
if (!firstJobRequestRenewed.Task.IsCompleted)
|
|
||||||
{
|
|
||||||
// fire first renew succeed event.
|
|
||||||
firstJobRequestRenewed.TrySetResult(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (encounteringError > 0)
|
|
||||||
{
|
|
||||||
encounteringError = 0;
|
|
||||||
HostContext.WritePerfCounter("JobRenewRecovered");
|
|
||||||
}
|
|
||||||
|
|
||||||
// renew again after 60 sec delay
|
|
||||||
await HostContext.Delay(TimeSpan.FromSeconds(60), token);
|
|
||||||
}
|
|
||||||
catch (TaskOrchestrationJobNotFoundException)
|
|
||||||
{
|
|
||||||
// no need for retry. the job is not valid anymore.
|
|
||||||
Trace.Info($"TaskAgentJobNotFoundException received when renew job {jobId}, job is no longer valid, stop renew job request.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
catch (OperationCanceledException) when (token.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
// OperationCanceledException may caused by http timeout or _lockRenewalTokenSource.Cance();
|
|
||||||
// Stop renew only on cancellation token fired.
|
|
||||||
Trace.Info($"job renew has been cancelled, stop renew job {jobId}.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Trace.Error($"Catch exception during renew runner job {jobId}.");
|
|
||||||
Trace.Error(ex);
|
|
||||||
encounteringError++;
|
|
||||||
|
|
||||||
// retry
|
|
||||||
TimeSpan remainingTime = TimeSpan.Zero;
|
|
||||||
if (!firstJobRequestRenewed.Task.IsCompleted)
|
|
||||||
{
|
|
||||||
// retry 5 times every 10 sec for the first renew
|
|
||||||
if (firstRenewRetryLimit-- > 0)
|
|
||||||
{
|
|
||||||
remainingTime = TimeSpan.FromSeconds(10);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// retry till reach lockeduntil + 5 mins extra buffer.
|
|
||||||
remainingTime = request.LockedUntil.Value + TimeSpan.FromMinutes(5) - DateTime.UtcNow;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (remainingTime > TimeSpan.Zero)
|
|
||||||
{
|
|
||||||
TimeSpan delayTime;
|
|
||||||
if (!firstJobRequestRenewed.Task.IsCompleted)
|
|
||||||
{
|
|
||||||
Trace.Info($"Retrying lock renewal for job {jobId}. The first job renew request has failed.");
|
|
||||||
delayTime = BackoffTimerHelper.GetRandomBackoff(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(10));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Trace.Info($"Retrying lock renewal for job {jobId}. Job is valid until {request.LockedUntil.Value}.");
|
|
||||||
if (encounteringError > 5)
|
|
||||||
{
|
|
||||||
delayTime = BackoffTimerHelper.GetRandomBackoff(TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(30));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
delayTime = BackoffTimerHelper.GetRandomBackoff(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(15));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// back-off before next retry.
|
|
||||||
await HostContext.Delay(delayTime, token);
|
|
||||||
}
|
|
||||||
catch (OperationCanceledException) when (token.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
Trace.Info($"job renew has been cancelled, stop renew job {jobId}.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Trace.Info($"Lock renewal has run out of retry, stop renew lock for job {jobId}.");
|
|
||||||
HostContext.WritePerfCounter("JobRenewReachLimit");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task RenewJobRequestAsync(IRunnerServer runnerServer, int poolId, long requestId, Guid lockToken, string orchestrationId, TaskCompletionSource<int> firstJobRequestRenewed, CancellationToken token)
|
|
||||||
{
|
|
||||||
TaskAgentJobRequest request = null;
|
TaskAgentJobRequest request = null;
|
||||||
int firstRenewRetryLimit = 5;
|
int firstRenewRetryLimit = 5;
|
||||||
int encounteringError = 0;
|
int encounteringError = 0;
|
||||||
@@ -969,10 +840,12 @@ namespace GitHub.Runner.Listener
|
|||||||
var systemConnection = message.Resources.Endpoints.SingleOrDefault(x => string.Equals(x.Name, WellKnownServiceEndpointNames.SystemVssConnection));
|
var systemConnection = message.Resources.Endpoints.SingleOrDefault(x => string.Equals(x.Name, WellKnownServiceEndpointNames.SystemVssConnection));
|
||||||
ArgUtil.NotNull(systemConnection, nameof(systemConnection));
|
ArgUtil.NotNull(systemConnection, nameof(systemConnection));
|
||||||
|
|
||||||
var server = await InitializeJobServerAsync(systemConnection);
|
var jobServer = HostContext.GetService<IJobServer>();
|
||||||
|
VssCredentials jobServerCredential = VssUtil.GetVssCredential(systemConnection);
|
||||||
|
VssConnection jobConnection = VssUtil.CreateConnection(systemConnection.Url, jobServerCredential);
|
||||||
|
|
||||||
|
await jobServer.ConnectAsync(jobConnection);
|
||||||
|
|
||||||
if (server is IJobServer jobServer)
|
|
||||||
{
|
|
||||||
var timeline = await jobServer.GetTimelineAsync(message.Plan.ScopeIdentifier, message.Plan.PlanType, message.Plan.PlanId, message.Timeline.Id, CancellationToken.None);
|
var timeline = await jobServer.GetTimelineAsync(message.Plan.ScopeIdentifier, message.Plan.PlanType, message.Plan.PlanId, message.Timeline.Id, CancellationToken.None);
|
||||||
|
|
||||||
var updatedRecords = new List<TimelineRecord>();
|
var updatedRecords = new List<TimelineRecord>();
|
||||||
@@ -1053,11 +926,6 @@ namespace GitHub.Runner.Listener
|
|||||||
await jobServer.UpdateTimelineRecordsAsync(message.Plan.ScopeIdentifier, message.Plan.PlanType, message.Plan.PlanId, message.Timeline.Id, updatedRecords, CancellationToken.None);
|
await jobServer.UpdateTimelineRecordsAsync(message.Plan.ScopeIdentifier, message.Plan.PlanType, message.Plan.PlanId, message.Timeline.Id, updatedRecords, CancellationToken.None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
Trace.Info("Job server does not support log upload yet.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
// Ignore any error during log upload since it's best effort
|
// Ignore any error during log upload since it's best effort
|
||||||
@@ -1075,12 +943,6 @@ namespace GitHub.Runner.Listener
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._isRunServiceJob)
|
|
||||||
{
|
|
||||||
Trace.Verbose($"Skip FinishAgentRequest call from Listener because MessageType is {message.MessageType}");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var runnerServer = HostContext.GetService<IRunnerServer>();
|
var runnerServer = HostContext.GetService<IRunnerServer>();
|
||||||
int completeJobRequestRetryLimit = 5;
|
int completeJobRequestRetryLimit = 5;
|
||||||
List<Exception> exceptions = new();
|
List<Exception> exceptions = new();
|
||||||
@@ -1117,9 +979,7 @@ namespace GitHub.Runner.Listener
|
|||||||
}
|
}
|
||||||
|
|
||||||
// log an error issue to job level timeline record
|
// log an error issue to job level timeline record
|
||||||
private async Task LogWorkerProcessUnhandledException(IRunnerService server, Pipelines.AgentJobRequestMessage message, string errorMessage)
|
private async Task LogWorkerProcessUnhandledException(IJobServer jobServer, Pipelines.AgentJobRequestMessage message, string errorMessage)
|
||||||
{
|
|
||||||
if (server is IJobServer jobServer)
|
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -1165,17 +1025,9 @@ namespace GitHub.Runner.Listener
|
|||||||
Trace.Error(ex);
|
Trace.Error(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
Trace.Info("Job server does not support handling unhandled exception yet, error message: {0}", errorMessage);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// raise job completed event to fail the job.
|
// raise job completed event to fail the job.
|
||||||
private async Task ForceFailJob(IRunnerService server, Pipelines.AgentJobRequestMessage message)
|
private async Task ForceFailJob(IJobServer jobServer, Pipelines.AgentJobRequestMessage message)
|
||||||
{
|
|
||||||
if (server is IJobServer jobServer)
|
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -1188,47 +1040,6 @@ namespace GitHub.Runner.Listener
|
|||||||
Trace.Error(ex);
|
Trace.Error(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (server is IRunServer runServer)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await runServer.CompleteJobAsync(message.Plan.PlanId, message.JobId, TaskResult.Failed, outputs: null, stepResults: null, CancellationToken.None);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Trace.Error("Fail to raise job completion back to service.");
|
|
||||||
Trace.Error(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new NotSupportedException($"Server type {server.GetType().FullName} is not supported.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<IRunnerService> InitializeJobServerAsync(ServiceEndpoint systemConnection)
|
|
||||||
{
|
|
||||||
if (this._isRunServiceJob)
|
|
||||||
{
|
|
||||||
return await GetRunServerAsync(systemConnection);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var jobServer = HostContext.GetService<IJobServer>();
|
|
||||||
VssCredentials jobServerCredential = VssUtil.GetVssCredential(systemConnection);
|
|
||||||
VssConnection jobConnection = VssUtil.CreateConnection(systemConnection.Url, jobServerCredential);
|
|
||||||
await jobServer.ConnectAsync(jobConnection);
|
|
||||||
return jobServer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<IRunServer> GetRunServerAsync(ServiceEndpoint systemConnection)
|
|
||||||
{
|
|
||||||
var runServer = HostContext.GetService<IRunServer>();
|
|
||||||
VssCredentials jobServerCredential = VssUtil.GetVssCredential(systemConnection);
|
|
||||||
await runServer.ConnectAsync(systemConnection.Url, jobServerCredential);
|
|
||||||
return runServer;
|
|
||||||
}
|
|
||||||
|
|
||||||
private class WorkerDispatcher : IDisposable
|
private class WorkerDispatcher : IDisposable
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ using System.Threading;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using GitHub.DistributedTask.WebApi;
|
using GitHub.DistributedTask.WebApi;
|
||||||
using GitHub.Runner.Common;
|
using GitHub.Runner.Common;
|
||||||
using GitHub.Runner.Common.Util;
|
|
||||||
using GitHub.Runner.Listener.Check;
|
using GitHub.Runner.Listener.Check;
|
||||||
using GitHub.Runner.Listener.Configuration;
|
using GitHub.Runner.Listener.Configuration;
|
||||||
using GitHub.Runner.Sdk;
|
using GitHub.Runner.Sdk;
|
||||||
@@ -503,7 +502,7 @@ namespace GitHub.Runner.Listener
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Broker flow
|
// Broker flow
|
||||||
else if (MessageUtil.IsRunServiceJob(message.MessageType))
|
else if (string.Equals(message.MessageType, JobRequestMessageTypes.RunnerJobRequest, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
if (autoUpdateInProgress || runOnceJobReceived)
|
if (autoUpdateInProgress || runOnceJobReceived)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -164,6 +164,7 @@ namespace GitHub.Runner.Sdk
|
|||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
_noProxyList.Add(noProxyInfo);
|
_noProxyList.Add(noProxyInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -206,11 +207,6 @@ namespace GitHub.Runner.Sdk
|
|||||||
{
|
{
|
||||||
foreach (var noProxy in _noProxyList)
|
foreach (var noProxy in _noProxyList)
|
||||||
{
|
{
|
||||||
// bypass on wildcard no_proxy
|
|
||||||
if (string.Equals(noProxy.Host, "*", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
var matchHost = false;
|
var matchHost = false;
|
||||||
var matchPort = false;
|
var matchPort = false;
|
||||||
|
|
||||||
|
|||||||
@@ -181,7 +181,7 @@ namespace GitHub.Runner.Worker
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
var templateEvaluator = ExecutionContext.ToPipelineTemplateEvaluator();
|
var templateEvaluator = ExecutionContext.ToPipelineTemplateEvaluator();
|
||||||
inputs = templateEvaluator.EvaluateStepInputs(Action.Inputs, ExecutionContext.ExpressionValues, ExecutionContext.ExpressionFunctions, ExecutionContext.ToExpressionState());
|
inputs = templateEvaluator.EvaluateStepInputs(Action.Inputs, ExecutionContext.ExpressionValues, ExecutionContext.ExpressionFunctions);
|
||||||
}
|
}
|
||||||
|
|
||||||
var userInputs = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
var userInputs = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||||
@@ -355,7 +355,7 @@ namespace GitHub.Runner.Worker
|
|||||||
{
|
{
|
||||||
DictionaryContextData expressionValues = ExecutionContext.GetExpressionValues(stepHost);
|
DictionaryContextData expressionValues = ExecutionContext.GetExpressionValues(stepHost);
|
||||||
var templateEvaluator = ExecutionContext.ToPipelineTemplateEvaluator();
|
var templateEvaluator = ExecutionContext.ToPipelineTemplateEvaluator();
|
||||||
var inputs = templateEvaluator.EvaluateStepInputs(Action.Inputs, expressionValues, ExecutionContext.ExpressionFunctions, ExecutionContext.ToExpressionState());
|
var inputs = templateEvaluator.EvaluateStepInputs(Action.Inputs, expressionValues, ExecutionContext.ExpressionFunctions);
|
||||||
|
|
||||||
return inputs;
|
return inputs;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,9 +29,6 @@ namespace GitHub.Runner.Worker.Expressions
|
|||||||
githubContext.TryGetValue(PipelineTemplateConstants.Workspace, out var workspace);
|
githubContext.TryGetValue(PipelineTemplateConstants.Workspace, out var workspace);
|
||||||
var workspaceData = workspace as StringContextData;
|
var workspaceData = workspace as StringContextData;
|
||||||
ArgUtil.NotNull(workspaceData, nameof(workspaceData));
|
ArgUtil.NotNull(workspaceData, nameof(workspaceData));
|
||||||
var executionContext = templateContext.State[nameof(IExecutionContext)] as IExecutionContext;
|
|
||||||
ArgUtil.NotNull(executionContext, nameof(executionContext));
|
|
||||||
|
|
||||||
|
|
||||||
string githubWorkspace = workspaceData.Value;
|
string githubWorkspace = workspaceData.Value;
|
||||||
bool followSymlink = false;
|
bool followSymlink = false;
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ using System.Net.Http;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using GitHub.DistributedTask.Pipelines;
|
|
||||||
using GitHub.DistributedTask.WebApi;
|
using GitHub.DistributedTask.WebApi;
|
||||||
using GitHub.Runner.Common;
|
using GitHub.Runner.Common;
|
||||||
using GitHub.Runner.Common.Util;
|
using GitHub.Runner.Common.Util;
|
||||||
@@ -20,7 +19,7 @@ namespace GitHub.Runner.Worker
|
|||||||
[ServiceLocator(Default = typeof(JobRunner))]
|
[ServiceLocator(Default = typeof(JobRunner))]
|
||||||
public interface IJobRunner : IRunnerService
|
public interface IJobRunner : IRunnerService
|
||||||
{
|
{
|
||||||
Task<TaskResult> RunAsync(AgentJobRequestMessage message, CancellationToken jobRequestCancellationToken);
|
Task<TaskResult> RunAsync(Pipelines.AgentJobRequestMessage message, CancellationToken jobRequestCancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class JobRunner : RunnerService, IJobRunner
|
public sealed class JobRunner : RunnerService, IJobRunner
|
||||||
@@ -29,7 +28,7 @@ namespace GitHub.Runner.Worker
|
|||||||
private RunnerSettings _runnerSettings;
|
private RunnerSettings _runnerSettings;
|
||||||
private ITempDirectoryManager _tempDirectoryManager;
|
private ITempDirectoryManager _tempDirectoryManager;
|
||||||
|
|
||||||
public async Task<TaskResult> RunAsync(AgentJobRequestMessage message, CancellationToken jobRequestCancellationToken)
|
public async Task<TaskResult> RunAsync(Pipelines.AgentJobRequestMessage message, CancellationToken jobRequestCancellationToken)
|
||||||
{
|
{
|
||||||
// Validate parameters.
|
// Validate parameters.
|
||||||
Trace.Entering();
|
Trace.Entering();
|
||||||
@@ -43,7 +42,7 @@ namespace GitHub.Runner.Worker
|
|||||||
IRunnerService server = null;
|
IRunnerService server = null;
|
||||||
|
|
||||||
ServiceEndpoint systemConnection = message.Resources.Endpoints.Single(x => string.Equals(x.Name, WellKnownServiceEndpointNames.SystemVssConnection, StringComparison.OrdinalIgnoreCase));
|
ServiceEndpoint systemConnection = message.Resources.Endpoints.Single(x => string.Equals(x.Name, WellKnownServiceEndpointNames.SystemVssConnection, StringComparison.OrdinalIgnoreCase));
|
||||||
if (MessageUtil.IsRunServiceJob(message.MessageType))
|
if (string.Equals(message.MessageType, JobRequestMessageTypes.RunnerJobRequest, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
var runServer = HostContext.GetService<IRunServer>();
|
var runServer = HostContext.GetService<IRunServer>();
|
||||||
VssCredentials jobServerCredential = VssUtil.GetVssCredential(systemConnection);
|
VssCredentials jobServerCredential = VssUtil.GetVssCredential(systemConnection);
|
||||||
|
|||||||
@@ -164,14 +164,13 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
|||||||
public Dictionary<String, String> EvaluateStepInputs(
|
public Dictionary<String, String> EvaluateStepInputs(
|
||||||
TemplateToken token,
|
TemplateToken token,
|
||||||
DictionaryContextData contextData,
|
DictionaryContextData contextData,
|
||||||
IList<IFunctionInfo> expressionFunctions,
|
IList<IFunctionInfo> expressionFunctions)
|
||||||
IEnumerable<KeyValuePair<String, Object>> expressionState = null)
|
|
||||||
{
|
{
|
||||||
var result = default(Dictionary<String, String>);
|
var result = default(Dictionary<String, String>);
|
||||||
|
|
||||||
if (token != null && token.Type != TokenType.Null)
|
if (token != null && token.Type != TokenType.Null)
|
||||||
{
|
{
|
||||||
var context = CreateContext(contextData, expressionFunctions, expressionState);
|
var context = CreateContext(contextData, expressionFunctions);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
token = TemplateEvaluator.Evaluate(context, PipelineTemplateConstants.StepWith, token, 0, null, omitHeader: true);
|
token = TemplateEvaluator.Evaluate(context, PipelineTemplateConstants.StepWith, token, 0, null, omitHeader: true);
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.Serialization;
|
|
||||||
|
|
||||||
namespace GitHub.Actions.RunService.WebApi
|
|
||||||
{
|
|
||||||
[DataContract]
|
|
||||||
public class RenewJobRequest
|
|
||||||
{
|
|
||||||
[DataMember(Name = "planId", EmitDefaultValue = false)]
|
|
||||||
public Guid PlanID { get; set; }
|
|
||||||
|
|
||||||
[DataMember(Name = "jobId", EmitDefaultValue = false)]
|
|
||||||
public Guid JobID { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.Serialization;
|
|
||||||
|
|
||||||
namespace Sdk.RSWebApi.Contracts
|
|
||||||
{
|
|
||||||
[DataContract]
|
|
||||||
public class RenewJobResponse
|
|
||||||
{
|
|
||||||
[DataMember]
|
|
||||||
public DateTime LockedUntil
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
internal set;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -8,7 +8,6 @@ using GitHub.DistributedTask.WebApi;
|
|||||||
using GitHub.Services.Common;
|
using GitHub.Services.Common;
|
||||||
using GitHub.Services.OAuth;
|
using GitHub.Services.OAuth;
|
||||||
using GitHub.Services.WebApi;
|
using GitHub.Services.WebApi;
|
||||||
using Sdk.RSWebApi.Contracts;
|
|
||||||
using Sdk.WebApi.WebApi;
|
using Sdk.WebApi.WebApi;
|
||||||
|
|
||||||
namespace GitHub.Actions.RunService.WebApi
|
namespace GitHub.Actions.RunService.WebApi
|
||||||
@@ -104,28 +103,5 @@ namespace GitHub.Actions.RunService.WebApi
|
|||||||
content: requestContent,
|
content: requestContent,
|
||||||
cancellationToken: cancellationToken);
|
cancellationToken: cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<RenewJobResponse> RenewJobAsync(
|
|
||||||
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));
|
|
||||||
return SendAsync<RenewJobResponse>(
|
|
||||||
httpMethod,
|
|
||||||
requestUri,
|
|
||||||
content: requestContent,
|
|
||||||
cancellationToken: cancellationToken);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,12 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using GitHub.DistributedTask.ObjectTemplating.Tokens;
|
|
||||||
using GitHub.DistributedTask.Pipelines;
|
|
||||||
using GitHub.DistributedTask.Pipelines.ContextData;
|
|
||||||
using GitHub.DistributedTask.WebApi;
|
using GitHub.DistributedTask.WebApi;
|
||||||
using GitHub.Runner.Listener;
|
using GitHub.Runner.Listener;
|
||||||
using GitHub.Runner.Listener.Configuration;
|
|
||||||
using GitHub.Services.WebApi;
|
using GitHub.Services.WebApi;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Sdk.RSWebApi.Contracts;
|
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
using Pipelines = GitHub.DistributedTask.Pipelines;
|
using Pipelines = GitHub.DistributedTask.Pipelines;
|
||||||
@@ -23,8 +18,6 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
private Mock<IProcessChannel> _processChannel;
|
private Mock<IProcessChannel> _processChannel;
|
||||||
private Mock<IProcessInvoker> _processInvoker;
|
private Mock<IProcessInvoker> _processInvoker;
|
||||||
private Mock<IRunnerServer> _runnerServer;
|
private Mock<IRunnerServer> _runnerServer;
|
||||||
|
|
||||||
private Mock<IRunServer> _runServer;
|
|
||||||
private Mock<IConfigurationStore> _configurationStore;
|
private Mock<IConfigurationStore> _configurationStore;
|
||||||
|
|
||||||
public JobDispatcherL0()
|
public JobDispatcherL0()
|
||||||
@@ -32,7 +25,6 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
_processChannel = new Mock<IProcessChannel>();
|
_processChannel = new Mock<IProcessChannel>();
|
||||||
_processInvoker = new Mock<IProcessInvoker>();
|
_processInvoker = new Mock<IProcessInvoker>();
|
||||||
_runnerServer = new Mock<IRunnerServer>();
|
_runnerServer = new Mock<IRunnerServer>();
|
||||||
_runServer = new Mock<IRunServer>();
|
|
||||||
_configurationStore = new Mock<IConfigurationStore>();
|
_configurationStore = new Mock<IConfigurationStore>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,7 +139,7 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
var jobDispatcher = new JobDispatcher();
|
var jobDispatcher = new JobDispatcher();
|
||||||
jobDispatcher.Initialize(hc);
|
jobDispatcher.Initialize(hc);
|
||||||
|
|
||||||
await jobDispatcher.RenewJobRequestAsync(It.IsAny<AgentJobRequestMessage>(), It.IsAny<ServiceEndpoint>(), poolId, requestId, Guid.Empty, Guid.NewGuid().ToString(), firstJobRequestRenewed, cancellationTokenSource.Token);
|
await jobDispatcher.RenewJobRequestAsync(poolId, requestId, Guid.Empty, Guid.NewGuid().ToString(), firstJobRequestRenewed, cancellationTokenSource.Token);
|
||||||
|
|
||||||
Assert.True(firstJobRequestRenewed.Task.IsCompletedSuccessfully);
|
Assert.True(firstJobRequestRenewed.Task.IsCompletedSuccessfully);
|
||||||
_runnerServer.Verify(x => x.RenewAgentRequestAsync(It.IsAny<int>(), It.IsAny<long>(), It.IsAny<Guid>(), It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Exactly(5));
|
_runnerServer.Verify(x => x.RenewAgentRequestAsync(It.IsAny<int>(), It.IsAny<long>(), It.IsAny<Guid>(), It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Exactly(5));
|
||||||
@@ -205,7 +197,7 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
var jobDispatcher = new JobDispatcher();
|
var jobDispatcher = new JobDispatcher();
|
||||||
jobDispatcher.Initialize(hc);
|
jobDispatcher.Initialize(hc);
|
||||||
|
|
||||||
await jobDispatcher.RenewJobRequestAsync(It.IsAny<AgentJobRequestMessage>(), It.IsAny<ServiceEndpoint>(), poolId, requestId, Guid.Empty, Guid.NewGuid().ToString(), firstJobRequestRenewed, cancellationTokenSource.Token);
|
await jobDispatcher.RenewJobRequestAsync(poolId, requestId, Guid.Empty, Guid.NewGuid().ToString(), firstJobRequestRenewed, cancellationTokenSource.Token);
|
||||||
|
|
||||||
Assert.True(firstJobRequestRenewed.Task.IsCompletedSuccessfully, "First renew should succeed.");
|
Assert.True(firstJobRequestRenewed.Task.IsCompletedSuccessfully, "First renew should succeed.");
|
||||||
Assert.False(cancellationTokenSource.IsCancellationRequested);
|
Assert.False(cancellationTokenSource.IsCancellationRequested);
|
||||||
@@ -213,75 +205,6 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
[Trait("Level", "L0")]
|
|
||||||
[Trait("Category", "Runner")]
|
|
||||||
public async void DispatcherRenewJobOnRunServiceStopOnJobNotFoundExceptions()
|
|
||||||
{
|
|
||||||
//Arrange
|
|
||||||
using (var hc = new TestHostContext(this))
|
|
||||||
{
|
|
||||||
int poolId = 1;
|
|
||||||
Int64 requestId = 1000;
|
|
||||||
int count = 0;
|
|
||||||
|
|
||||||
var trace = hc.GetTrace(nameof(DispatcherRenewJobOnRunServiceStopOnJobNotFoundExceptions));
|
|
||||||
TaskCompletionSource<int> firstJobRequestRenewed = new();
|
|
||||||
CancellationTokenSource cancellationTokenSource = new();
|
|
||||||
|
|
||||||
TaskAgentJobRequest request = new();
|
|
||||||
PropertyInfo lockUntilProperty = request.GetType().GetProperty("LockedUntil", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
|
|
||||||
Assert.NotNull(lockUntilProperty);
|
|
||||||
lockUntilProperty.SetValue(request, DateTime.UtcNow.AddMinutes(5));
|
|
||||||
|
|
||||||
hc.SetSingleton<IRunServer>(_runServer.Object);
|
|
||||||
hc.SetSingleton<IConfigurationStore>(_configurationStore.Object);
|
|
||||||
_configurationStore.Setup(x => x.GetSettings()).Returns(new RunnerSettings() { PoolId = 1 });
|
|
||||||
_ = _runServer.Setup(x => x.RenewJobAsync(It.IsAny<Guid>(), It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
|
|
||||||
.Returns(() =>
|
|
||||||
{
|
|
||||||
count++;
|
|
||||||
if (!firstJobRequestRenewed.Task.IsCompletedSuccessfully)
|
|
||||||
{
|
|
||||||
trace.Info("First renew happens.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count < 5)
|
|
||||||
{
|
|
||||||
var response = new RenewJobResponse()
|
|
||||||
{
|
|
||||||
LockedUntil = request.LockedUntil.Value
|
|
||||||
};
|
|
||||||
return Task.FromResult<RenewJobResponse>(response);
|
|
||||||
}
|
|
||||||
else if (count == 5)
|
|
||||||
{
|
|
||||||
cancellationTokenSource.CancelAfter(10000);
|
|
||||||
throw new TaskOrchestrationJobNotFoundException("");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("Should not reach here.");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
var jobDispatcher = new JobDispatcher();
|
|
||||||
jobDispatcher.Initialize(hc);
|
|
||||||
EnableRunServiceJobForJobDispatcher(jobDispatcher);
|
|
||||||
|
|
||||||
// Set the value of the _isRunServiceJob field to true
|
|
||||||
var isRunServiceJobField = typeof(JobDispatcher).GetField("_isRunServiceJob", BindingFlags.NonPublic | BindingFlags.Instance);
|
|
||||||
isRunServiceJobField.SetValue(jobDispatcher, true);
|
|
||||||
|
|
||||||
await jobDispatcher.RenewJobRequestAsync(GetAgentJobRequestMessage(), GetServiceEndpoint(), poolId, requestId, Guid.Empty, Guid.NewGuid().ToString(), firstJobRequestRenewed, cancellationTokenSource.Token);
|
|
||||||
|
|
||||||
Assert.True(firstJobRequestRenewed.Task.IsCompletedSuccessfully, "First renew should succeed.");
|
|
||||||
Assert.False(cancellationTokenSource.IsCancellationRequested);
|
|
||||||
_runServer.Verify(x => x.RenewJobAsync(It.IsAny<Guid>(), It.IsAny<Guid>(), It.IsAny<CancellationToken>()), Times.Exactly(5));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
[Trait("Level", "L0")]
|
[Trait("Level", "L0")]
|
||||||
[Trait("Category", "Runner")]
|
[Trait("Category", "Runner")]
|
||||||
@@ -333,7 +256,7 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
var jobDispatcher = new JobDispatcher();
|
var jobDispatcher = new JobDispatcher();
|
||||||
jobDispatcher.Initialize(hc);
|
jobDispatcher.Initialize(hc);
|
||||||
|
|
||||||
await jobDispatcher.RenewJobRequestAsync(It.IsAny<AgentJobRequestMessage>(), It.IsAny<ServiceEndpoint>(), poolId, requestId, Guid.Empty, Guid.NewGuid().ToString(), firstJobRequestRenewed, cancellationTokenSource.Token);
|
await jobDispatcher.RenewJobRequestAsync(poolId, requestId, Guid.Empty, Guid.NewGuid().ToString(), firstJobRequestRenewed, cancellationTokenSource.Token);
|
||||||
|
|
||||||
Assert.True(firstJobRequestRenewed.Task.IsCompletedSuccessfully, "First renew should succeed.");
|
Assert.True(firstJobRequestRenewed.Task.IsCompletedSuccessfully, "First renew should succeed.");
|
||||||
Assert.False(cancellationTokenSource.IsCancellationRequested);
|
Assert.False(cancellationTokenSource.IsCancellationRequested);
|
||||||
@@ -389,9 +312,8 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
var jobDispatcher = new JobDispatcher();
|
var jobDispatcher = new JobDispatcher();
|
||||||
jobDispatcher.Initialize(hc);
|
jobDispatcher.Initialize(hc);
|
||||||
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
await jobDispatcher.RenewJobRequestAsync(It.IsAny<AgentJobRequestMessage>(), It.IsAny<ServiceEndpoint>(), 0, 0, Guid.Empty, Guid.NewGuid().ToString(), firstJobRequestRenewed, cancellationTokenSource.Token);
|
await jobDispatcher.RenewJobRequestAsync(0, 0, Guid.Empty, Guid.NewGuid().ToString(), firstJobRequestRenewed, cancellationTokenSource.Token);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
_configurationStore.Verify(x => x.SaveSettings(It.Is<RunnerSettings>(settings => settings.AgentName == newName)), Times.Once);
|
_configurationStore.Verify(x => x.SaveSettings(It.Is<RunnerSettings>(settings => settings.AgentName == newName)), Times.Once);
|
||||||
@@ -446,7 +368,7 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
jobDispatcher.Initialize(hc);
|
jobDispatcher.Initialize(hc);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
await jobDispatcher.RenewJobRequestAsync(It.IsAny<AgentJobRequestMessage>(), It.IsAny<ServiceEndpoint>(), 0, 0, Guid.Empty, Guid.NewGuid().ToString(), firstJobRequestRenewed, cancellationTokenSource.Token);
|
await jobDispatcher.RenewJobRequestAsync(0, 0, Guid.Empty, Guid.NewGuid().ToString(), firstJobRequestRenewed, cancellationTokenSource.Token);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
_configurationStore.Verify(x => x.SaveSettings(It.IsAny<RunnerSettings>()), Times.Never);
|
_configurationStore.Verify(x => x.SaveSettings(It.IsAny<RunnerSettings>()), Times.Never);
|
||||||
@@ -499,7 +421,7 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
jobDispatcher.Initialize(hc);
|
jobDispatcher.Initialize(hc);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
await jobDispatcher.RenewJobRequestAsync(It.IsAny<AgentJobRequestMessage>(), It.IsAny<ServiceEndpoint>(), 0, 0, Guid.Empty, Guid.NewGuid().ToString(), firstJobRequestRenewed, cancellationTokenSource.Token);
|
await jobDispatcher.RenewJobRequestAsync(0, 0, Guid.Empty, Guid.NewGuid().ToString(), firstJobRequestRenewed, cancellationTokenSource.Token);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
_configurationStore.Verify(x => x.SaveSettings(It.IsAny<RunnerSettings>()), Times.Never);
|
_configurationStore.Verify(x => x.SaveSettings(It.IsAny<RunnerSettings>()), Times.Never);
|
||||||
@@ -557,7 +479,7 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
var jobDispatcher = new JobDispatcher();
|
var jobDispatcher = new JobDispatcher();
|
||||||
jobDispatcher.Initialize(hc);
|
jobDispatcher.Initialize(hc);
|
||||||
|
|
||||||
await jobDispatcher.RenewJobRequestAsync(It.IsAny<AgentJobRequestMessage>(), It.IsAny<ServiceEndpoint>(), poolId, requestId, Guid.Empty, Guid.NewGuid().ToString(), firstJobRequestRenewed, cancellationTokenSource.Token);
|
await jobDispatcher.RenewJobRequestAsync(poolId, requestId, Guid.Empty, Guid.NewGuid().ToString(), firstJobRequestRenewed, cancellationTokenSource.Token);
|
||||||
|
|
||||||
Assert.True(firstJobRequestRenewed.Task.IsCompletedSuccessfully, "First renew should succeed.");
|
Assert.True(firstJobRequestRenewed.Task.IsCompletedSuccessfully, "First renew should succeed.");
|
||||||
Assert.True(cancellationTokenSource.IsCancellationRequested);
|
Assert.True(cancellationTokenSource.IsCancellationRequested);
|
||||||
@@ -614,7 +536,7 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
var jobDispatcher = new JobDispatcher();
|
var jobDispatcher = new JobDispatcher();
|
||||||
jobDispatcher.Initialize(hc);
|
jobDispatcher.Initialize(hc);
|
||||||
|
|
||||||
await jobDispatcher.RenewJobRequestAsync(It.IsAny<AgentJobRequestMessage>(), It.IsAny<ServiceEndpoint>(), poolId, requestId, Guid.Empty, Guid.NewGuid().ToString(), firstJobRequestRenewed, cancellationTokenSource.Token);
|
await jobDispatcher.RenewJobRequestAsync(poolId, requestId, Guid.Empty, Guid.NewGuid().ToString(), firstJobRequestRenewed, cancellationTokenSource.Token);
|
||||||
|
|
||||||
Assert.False(firstJobRequestRenewed.Task.IsCompletedSuccessfully, "First renew should failed.");
|
Assert.False(firstJobRequestRenewed.Task.IsCompletedSuccessfully, "First renew should failed.");
|
||||||
Assert.False(cancellationTokenSource.IsCancellationRequested);
|
Assert.False(cancellationTokenSource.IsCancellationRequested);
|
||||||
@@ -678,7 +600,7 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
var jobDispatcher = new JobDispatcher();
|
var jobDispatcher = new JobDispatcher();
|
||||||
jobDispatcher.Initialize(hc);
|
jobDispatcher.Initialize(hc);
|
||||||
|
|
||||||
await jobDispatcher.RenewJobRequestAsync(It.IsAny<AgentJobRequestMessage>(), It.IsAny<ServiceEndpoint>(), poolId, requestId, Guid.Empty, Guid.NewGuid().ToString(), firstJobRequestRenewed, cancellationTokenSource.Token);
|
await jobDispatcher.RenewJobRequestAsync(poolId, requestId, Guid.Empty, Guid.NewGuid().ToString(), firstJobRequestRenewed, cancellationTokenSource.Token);
|
||||||
|
|
||||||
Assert.True(firstJobRequestRenewed.Task.IsCompletedSuccessfully, "First renew should succeed.");
|
Assert.True(firstJobRequestRenewed.Task.IsCompletedSuccessfully, "First renew should succeed.");
|
||||||
Assert.False(cancellationTokenSource.IsCancellationRequested);
|
Assert.False(cancellationTokenSource.IsCancellationRequested);
|
||||||
@@ -737,78 +659,5 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
Assert.True(jobDispatcher.RunOnceJobCompleted.Task.Result, "JobDispatcher should set task complete token to 'TRUE' for one time agent.");
|
Assert.True(jobDispatcher.RunOnceJobCompleted.Task.Result, "JobDispatcher should set task complete token to 'TRUE' for one time agent.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void EnableRunServiceJobForJobDispatcher(JobDispatcher jobDispatcher)
|
|
||||||
{
|
|
||||||
// Set the value of the _isRunServiceJob field to true
|
|
||||||
var isRunServiceJobField = typeof(JobDispatcher).GetField("_isRunServiceJob", BindingFlags.NonPublic | BindingFlags.Instance);
|
|
||||||
isRunServiceJobField.SetValue(jobDispatcher, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ServiceEndpoint GetServiceEndpoint()
|
|
||||||
{
|
|
||||||
var serviceEndpoint = new ServiceEndpoint
|
|
||||||
{
|
|
||||||
Authorization = new EndpointAuthorization
|
|
||||||
{
|
|
||||||
Scheme = EndpointAuthorizationSchemes.OAuth
|
|
||||||
}
|
|
||||||
};
|
|
||||||
serviceEndpoint.Authorization.Parameters.Add("AccessToken", "token");
|
|
||||||
return serviceEndpoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static AgentJobRequestMessage GetAgentJobRequestMessage()
|
|
||||||
{
|
|
||||||
var message = new AgentJobRequestMessage(
|
|
||||||
new TaskOrchestrationPlanReference()
|
|
||||||
{
|
|
||||||
PlanType = "Build",
|
|
||||||
PlanId = Guid.NewGuid(),
|
|
||||||
Version = 1
|
|
||||||
},
|
|
||||||
new TimelineReference()
|
|
||||||
{
|
|
||||||
Id = Guid.NewGuid()
|
|
||||||
},
|
|
||||||
Guid.NewGuid(),
|
|
||||||
"jobDisplayName",
|
|
||||||
"jobName",
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
new List<TemplateToken>(),
|
|
||||||
new Dictionary<string, VariableValue>()
|
|
||||||
{
|
|
||||||
{
|
|
||||||
"variables",
|
|
||||||
new VariableValue()
|
|
||||||
{
|
|
||||||
IsSecret = false,
|
|
||||||
Value = "variables"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new List<MaskHint>()
|
|
||||||
{
|
|
||||||
new MaskHint()
|
|
||||||
{
|
|
||||||
Type = MaskType.Variable,
|
|
||||||
Value = "maskHints"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new JobResources(),
|
|
||||||
new DictionaryContextData(),
|
|
||||||
new WorkspaceOptions(),
|
|
||||||
new List<JobStep>(),
|
|
||||||
new List<string>()
|
|
||||||
{
|
|
||||||
"fileTable"
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
new List<TemplateToken>(),
|
|
||||||
new ActionsEnvironmentReference("env")
|
|
||||||
);
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -351,7 +351,7 @@ namespace GitHub.Runner.Common.Tests
|
|||||||
Assert.False(proxy.IsBypassed(new Uri("https://actions.com")));
|
Assert.False(proxy.IsBypassed(new Uri("https://actions.com")));
|
||||||
Assert.False(proxy.IsBypassed(new Uri("https://ggithub.com")));
|
Assert.False(proxy.IsBypassed(new Uri("https://ggithub.com")));
|
||||||
Assert.False(proxy.IsBypassed(new Uri("https://github.comm")));
|
Assert.False(proxy.IsBypassed(new Uri("https://github.comm")));
|
||||||
Assert.False(proxy.IsBypassed(new Uri("https://google.com"))); // no_proxy has '.google.com', specifying only subdomains bypass
|
Assert.False(proxy.IsBypassed(new Uri("https://google.com")));
|
||||||
Assert.False(proxy.IsBypassed(new Uri("https://example.com")));
|
Assert.False(proxy.IsBypassed(new Uri("https://example.com")));
|
||||||
Assert.False(proxy.IsBypassed(new Uri("http://example.com:333")));
|
Assert.False(proxy.IsBypassed(new Uri("http://example.com:333")));
|
||||||
Assert.False(proxy.IsBypassed(new Uri("http://192.168.0.123:123")));
|
Assert.False(proxy.IsBypassed(new Uri("http://192.168.0.123:123")));
|
||||||
@@ -374,76 +374,6 @@ namespace GitHub.Runner.Common.Tests
|
|||||||
CleanProxyEnv();
|
CleanProxyEnv();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
[Fact]
|
|
||||||
[Trait("Level", "L0")]
|
|
||||||
[Trait("Category", "Common")]
|
|
||||||
public void BypassAllOnWildcardNoProxy()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Environment.SetEnvironmentVariable("http_proxy", "http://user1:pass1%40@127.0.0.1:8888");
|
|
||||||
Environment.SetEnvironmentVariable("https_proxy", "http://user2:pass2%40@127.0.0.1:9999");
|
|
||||||
Environment.SetEnvironmentVariable("no_proxy", "example.com, * , example2.com");
|
|
||||||
var proxy = new RunnerWebProxy();
|
|
||||||
|
|
||||||
Assert.True(proxy.IsBypassed(new Uri("http://actions.com")));
|
|
||||||
Assert.True(proxy.IsBypassed(new Uri("http://localhost")));
|
|
||||||
Assert.True(proxy.IsBypassed(new Uri("http://127.0.0.1:8080")));
|
|
||||||
Assert.True(proxy.IsBypassed(new Uri("https://actions.com")));
|
|
||||||
Assert.True(proxy.IsBypassed(new Uri("https://localhost")));
|
|
||||||
Assert.True(proxy.IsBypassed(new Uri("https://127.0.0.1:8080")));
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
CleanProxyEnv();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
[Trait("Level", "L0")]
|
|
||||||
[Trait("Category", "Common")]
|
|
||||||
public void IgnoreWildcardInNoProxySubdomain()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Environment.SetEnvironmentVariable("http_proxy", "http://user1:pass1%40@127.0.0.1:8888");
|
|
||||||
Environment.SetEnvironmentVariable("https_proxy", "http://user2:pass2%40@127.0.0.1:9999");
|
|
||||||
Environment.SetEnvironmentVariable("no_proxy", "*.example.com");
|
|
||||||
var proxy = new RunnerWebProxy();
|
|
||||||
|
|
||||||
Assert.False(proxy.IsBypassed(new Uri("http://sub.example.com")));
|
|
||||||
Assert.False(proxy.IsBypassed(new Uri("http://example.com")));
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
CleanProxyEnv();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
[Trait("Level", "L0")]
|
|
||||||
[Trait("Category", "Common")]
|
|
||||||
public void WildcardNoProxyWorksWhenOtherNoProxyAreAround()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Environment.SetEnvironmentVariable("http_proxy", "http://user1:pass1%40@127.0.0.1:8888");
|
|
||||||
Environment.SetEnvironmentVariable("https_proxy", "http://user2:pass2%40@127.0.0.1:9999");
|
|
||||||
Environment.SetEnvironmentVariable("no_proxy", "example.com,*,example2.com");
|
|
||||||
var proxy = new RunnerWebProxy();
|
|
||||||
|
|
||||||
Assert.True(proxy.IsBypassed(new Uri("http://actions.com")));
|
|
||||||
Assert.True(proxy.IsBypassed(new Uri("http://localhost")));
|
|
||||||
Assert.True(proxy.IsBypassed(new Uri("http://127.0.0.1:8080")));
|
|
||||||
Assert.True(proxy.IsBypassed(new Uri("https://actions.com")));
|
|
||||||
Assert.True(proxy.IsBypassed(new Uri("https://localhost")));
|
|
||||||
Assert.True(proxy.IsBypassed(new Uri("https://127.0.0.1:8080")));
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
CleanProxyEnv();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
[Trait("Level", "L0")]
|
[Trait("Level", "L0")]
|
||||||
|
|||||||
Reference in New Issue
Block a user