mirror of
https://github.com/actions/runner.git
synced 2025-12-10 12:36:23 +00:00
Enable hostcontext to track auth migration. (#3776)
This commit is contained in:
13
src/Runner.Common/AuthMigration.cs
Normal file
13
src/Runner.Common/AuthMigration.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace GitHub.Runner.Common
|
||||||
|
{
|
||||||
|
public class AuthMigrationEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
public AuthMigrationEventArgs(string trace)
|
||||||
|
{
|
||||||
|
Trace = trace;
|
||||||
|
}
|
||||||
|
public string Trace { get; private set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -37,6 +37,11 @@ namespace GitHub.Runner.Common
|
|||||||
void ShutdownRunner(ShutdownReason reason);
|
void ShutdownRunner(ShutdownReason reason);
|
||||||
void WritePerfCounter(string counter);
|
void WritePerfCounter(string counter);
|
||||||
void LoadDefaultUserAgents();
|
void LoadDefaultUserAgents();
|
||||||
|
|
||||||
|
bool AllowAuthMigration { get; }
|
||||||
|
void EnableAuthMigration(string trace);
|
||||||
|
void DeferAuthMigration(TimeSpan deferred, string trace);
|
||||||
|
event EventHandler<AuthMigrationEventArgs> AuthMigrationChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum StartupType
|
public enum StartupType
|
||||||
@@ -70,12 +75,21 @@ namespace GitHub.Runner.Common
|
|||||||
private RunnerWebProxy _webProxy = new();
|
private RunnerWebProxy _webProxy = new();
|
||||||
private string _hostType = string.Empty;
|
private string _hostType = string.Empty;
|
||||||
|
|
||||||
|
// disable auth migration by default
|
||||||
|
private readonly ManualResetEventSlim _allowAuthMigration = new ManualResetEventSlim(false);
|
||||||
|
private DateTime _deferredAuthMigrationTime = DateTime.MaxValue;
|
||||||
|
private readonly object _authMigrationLock = new object();
|
||||||
|
private CancellationTokenSource _authMigrationAutoReenableTaskCancellationTokenSource = new();
|
||||||
|
private Task _authMigrationAutoReenableTask;
|
||||||
|
|
||||||
public event EventHandler Unloading;
|
public event EventHandler Unloading;
|
||||||
|
public event EventHandler<AuthMigrationEventArgs> AuthMigrationChanged;
|
||||||
public CancellationToken RunnerShutdownToken => _runnerShutdownTokenSource.Token;
|
public CancellationToken RunnerShutdownToken => _runnerShutdownTokenSource.Token;
|
||||||
public ShutdownReason RunnerShutdownReason { get; private set; }
|
public ShutdownReason RunnerShutdownReason { get; private set; }
|
||||||
public ISecretMasker SecretMasker => _secretMasker;
|
public ISecretMasker SecretMasker => _secretMasker;
|
||||||
public List<ProductInfoHeaderValue> UserAgents => _userAgents;
|
public List<ProductInfoHeaderValue> UserAgents => _userAgents;
|
||||||
public RunnerWebProxy WebProxy => _webProxy;
|
public RunnerWebProxy WebProxy => _webProxy;
|
||||||
|
public bool AllowAuthMigration => _allowAuthMigration.IsSet;
|
||||||
public HostContext(string hostType, string logFile = null)
|
public HostContext(string hostType, string logFile = null)
|
||||||
{
|
{
|
||||||
// Validate args.
|
// Validate args.
|
||||||
@@ -207,6 +221,71 @@ namespace GitHub.Runner.Common
|
|||||||
LoadDefaultUserAgents();
|
LoadDefaultUserAgents();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// marked as internal for testing
|
||||||
|
internal async Task AuthMigrationAuthReenableAsync(TimeSpan refreshInterval, CancellationToken token)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
while (!token.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
_trace.Verbose($"Auth migration defer timer is set to expire at {_deferredAuthMigrationTime.ToString("O")}. AllowAuthMigration: {_allowAuthMigration.IsSet}.");
|
||||||
|
await Task.Delay(refreshInterval, token);
|
||||||
|
if (!_allowAuthMigration.IsSet && DateTime.UtcNow > _deferredAuthMigrationTime)
|
||||||
|
{
|
||||||
|
_trace.Info($"Auth migration defer timer expired. Allowing auth migration.");
|
||||||
|
EnableAuthMigration("Auth migration defer timer expired.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (TaskCanceledException)
|
||||||
|
{
|
||||||
|
// Task was cancelled, exit the loop.
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_trace.Info("Error in auth migration reenable task.");
|
||||||
|
_trace.Error(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EnableAuthMigration(string trace)
|
||||||
|
{
|
||||||
|
_allowAuthMigration.Set();
|
||||||
|
|
||||||
|
lock (_authMigrationLock)
|
||||||
|
{
|
||||||
|
if (_authMigrationAutoReenableTask == null)
|
||||||
|
{
|
||||||
|
var refreshIntervalInMS = 60 * 1000;
|
||||||
|
#if DEBUG
|
||||||
|
// For L0, we will refresh faster
|
||||||
|
if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("_GITHUB_ACTION_AUTH_MIGRATION_REFRESH_INTERVAL")))
|
||||||
|
{
|
||||||
|
refreshIntervalInMS = int.Parse(Environment.GetEnvironmentVariable("_GITHUB_ACTION_AUTH_MIGRATION_REFRESH_INTERVAL"));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
_authMigrationAutoReenableTask = AuthMigrationAuthReenableAsync(TimeSpan.FromMilliseconds(refreshIntervalInMS), _authMigrationAutoReenableTaskCancellationTokenSource.Token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_trace.Info($"Enable auth migration at {_deferredAuthMigrationTime.ToString("O")}.");
|
||||||
|
AuthMigrationChanged?.Invoke(this, new AuthMigrationEventArgs(trace));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DeferAuthMigration(TimeSpan deferred, string trace)
|
||||||
|
{
|
||||||
|
_allowAuthMigration.Reset();
|
||||||
|
|
||||||
|
// defer migration for a while
|
||||||
|
lock (_authMigrationLock)
|
||||||
|
{
|
||||||
|
_deferredAuthMigrationTime = DateTime.UtcNow.Add(deferred);
|
||||||
|
}
|
||||||
|
|
||||||
|
_trace.Info($"Disabled auth migration until {_deferredAuthMigrationTime.ToString("O")}.");
|
||||||
|
AuthMigrationChanged?.Invoke(this, new AuthMigrationEventArgs(trace));
|
||||||
|
}
|
||||||
|
|
||||||
public void LoadDefaultUserAgents()
|
public void LoadDefaultUserAgents()
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(WebProxy.HttpProxyAddress) && string.IsNullOrEmpty(WebProxy.HttpsProxyAddress))
|
if (string.IsNullOrEmpty(WebProxy.HttpProxyAddress) && string.IsNullOrEmpty(WebProxy.HttpsProxyAddress))
|
||||||
@@ -549,6 +628,18 @@ namespace GitHub.Runner.Common
|
|||||||
_loadContext.Unloading -= LoadContext_Unloading;
|
_loadContext.Unloading -= LoadContext_Unloading;
|
||||||
_loadContext = null;
|
_loadContext = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_authMigrationAutoReenableTask != null)
|
||||||
|
{
|
||||||
|
_authMigrationAutoReenableTaskCancellationTokenSource?.Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_authMigrationAutoReenableTaskCancellationTokenSource != null)
|
||||||
|
{
|
||||||
|
_authMigrationAutoReenableTaskCancellationTokenSource?.Dispose();
|
||||||
|
_authMigrationAutoReenableTaskCancellationTokenSource = null;
|
||||||
|
}
|
||||||
|
|
||||||
_httpTraceSubscription?.Dispose();
|
_httpTraceSubscription?.Dispose();
|
||||||
_diagListenerSubscription?.Dispose();
|
_diagListenerSubscription?.Dispose();
|
||||||
_traceManager?.Dispose();
|
_traceManager?.Dispose();
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
using GitHub.Runner.Common.Util;
|
using System;
|
||||||
using System;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace GitHub.Runner.Common.Tests
|
namespace GitHub.Runner.Common.Tests
|
||||||
@@ -172,6 +172,133 @@ namespace GitHub.Runner.Common.Tests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[Trait("Level", "L0")]
|
||||||
|
[Trait("Category", "Common")]
|
||||||
|
public void AuthMigrationDisabledByDefault()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Environment.SetEnvironmentVariable("_GITHUB_ACTION_AUTH_MIGRATION_REFRESH_INTERVAL", "100");
|
||||||
|
|
||||||
|
// Arrange.
|
||||||
|
Setup();
|
||||||
|
|
||||||
|
// Assert.
|
||||||
|
Assert.False(_hc.AllowAuthMigration);
|
||||||
|
|
||||||
|
// Change migration state is error free.
|
||||||
|
_hc.EnableAuthMigration("L0Test");
|
||||||
|
_hc.DeferAuthMigration(TimeSpan.FromHours(1), "L0Test");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Environment.SetEnvironmentVariable("_GITHUB_ACTION_AUTH_MIGRATION_REFRESH_INTERVAL", null);
|
||||||
|
// Cleanup.
|
||||||
|
Teardown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[Trait("Level", "L0")]
|
||||||
|
[Trait("Category", "Common")]
|
||||||
|
public async Task AuthMigrationReenableTaskNotRunningByDefault()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Environment.SetEnvironmentVariable("_GITHUB_ACTION_AUTH_MIGRATION_REFRESH_INTERVAL", "50");
|
||||||
|
|
||||||
|
// Arrange.
|
||||||
|
Setup();
|
||||||
|
|
||||||
|
// Assert.
|
||||||
|
Assert.False(_hc.AllowAuthMigration);
|
||||||
|
await Task.Delay(TimeSpan.FromMilliseconds(200));
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Environment.SetEnvironmentVariable("_GITHUB_ACTION_AUTH_MIGRATION_REFRESH_INTERVAL", null);
|
||||||
|
// Cleanup.
|
||||||
|
Teardown();
|
||||||
|
}
|
||||||
|
|
||||||
|
var logFile = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), $"trace_{nameof(HostContextL0)}_{nameof(AuthMigrationReenableTaskNotRunningByDefault)}.log");
|
||||||
|
var logContent = await File.ReadAllTextAsync(logFile);
|
||||||
|
Assert.Contains("HostContext", logContent);
|
||||||
|
Assert.DoesNotContain("Auth migration defer timer", logContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[Trait("Level", "L0")]
|
||||||
|
[Trait("Category", "Common")]
|
||||||
|
public void AuthMigrationEnableDisable()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Arrange.
|
||||||
|
Setup();
|
||||||
|
|
||||||
|
var eventFiredCount = 0;
|
||||||
|
_hc.AuthMigrationChanged += (sender, e) =>
|
||||||
|
{
|
||||||
|
eventFiredCount++;
|
||||||
|
Assert.Equal("L0Test", e.Trace);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Assert.
|
||||||
|
_hc.EnableAuthMigration("L0Test");
|
||||||
|
Assert.True(_hc.AllowAuthMigration);
|
||||||
|
|
||||||
|
_hc.DeferAuthMigration(TimeSpan.FromHours(1), "L0Test");
|
||||||
|
Assert.False(_hc.AllowAuthMigration);
|
||||||
|
Assert.Equal(2, eventFiredCount);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
// Cleanup.
|
||||||
|
Teardown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[Trait("Level", "L0")]
|
||||||
|
[Trait("Category", "Common")]
|
||||||
|
public async Task AuthMigrationAutoReset()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Environment.SetEnvironmentVariable("_GITHUB_ACTION_AUTH_MIGRATION_REFRESH_INTERVAL", "100");
|
||||||
|
|
||||||
|
// Arrange.
|
||||||
|
Setup();
|
||||||
|
|
||||||
|
var eventFiredCount = 0;
|
||||||
|
_hc.AuthMigrationChanged += (sender, e) =>
|
||||||
|
{
|
||||||
|
eventFiredCount++;
|
||||||
|
Assert.NotEmpty(e.Trace);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Assert.
|
||||||
|
_hc.EnableAuthMigration("L0Test");
|
||||||
|
Assert.True(_hc.AllowAuthMigration);
|
||||||
|
|
||||||
|
_hc.DeferAuthMigration(TimeSpan.FromMilliseconds(500), "L0Test");
|
||||||
|
Assert.False(_hc.AllowAuthMigration);
|
||||||
|
|
||||||
|
await Task.Delay(TimeSpan.FromSeconds(1));
|
||||||
|
Assert.True(_hc.AllowAuthMigration);
|
||||||
|
Assert.Equal(3, eventFiredCount);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Environment.SetEnvironmentVariable("_GITHUB_ACTION_AUTH_MIGRATION_REFRESH_INTERVAL", null);
|
||||||
|
|
||||||
|
// Cleanup.
|
||||||
|
Teardown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void Setup([CallerMemberName] string testName = "")
|
private void Setup([CallerMemberName] string testName = "")
|
||||||
{
|
{
|
||||||
_tokenSource = new CancellationTokenSource();
|
_tokenSource = new CancellationTokenSource();
|
||||||
|
|||||||
@@ -1,16 +1,15 @@
|
|||||||
using GitHub.Runner.Common.Util;
|
using System;
|
||||||
using System;
|
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Net.Http.Headers;
|
||||||
|
using System.Reflection;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.Loader;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Runtime.Loader;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using GitHub.DistributedTask.Logging;
|
using GitHub.DistributedTask.Logging;
|
||||||
using System.Net.Http.Headers;
|
|
||||||
using GitHub.Runner.Sdk;
|
using GitHub.Runner.Sdk;
|
||||||
|
|
||||||
namespace GitHub.Runner.Common.Tests
|
namespace GitHub.Runner.Common.Tests
|
||||||
@@ -31,6 +30,7 @@ namespace GitHub.Runner.Common.Tests
|
|||||||
private StartupType _startupType;
|
private StartupType _startupType;
|
||||||
public event EventHandler Unloading;
|
public event EventHandler Unloading;
|
||||||
public event EventHandler<DelayEventArgs> Delaying;
|
public event EventHandler<DelayEventArgs> Delaying;
|
||||||
|
public event EventHandler<AuthMigrationEventArgs> AuthMigrationChanged;
|
||||||
public CancellationToken RunnerShutdownToken => _runnerShutdownTokenSource.Token;
|
public CancellationToken RunnerShutdownToken => _runnerShutdownTokenSource.Token;
|
||||||
public ShutdownReason RunnerShutdownReason { get; private set; }
|
public ShutdownReason RunnerShutdownReason { get; private set; }
|
||||||
public ISecretMasker SecretMasker => _secretMasker;
|
public ISecretMasker SecretMasker => _secretMasker;
|
||||||
@@ -92,6 +92,8 @@ namespace GitHub.Runner.Common.Tests
|
|||||||
|
|
||||||
public RunnerWebProxy WebProxy => new();
|
public RunnerWebProxy WebProxy => new();
|
||||||
|
|
||||||
|
public bool AllowAuthMigration { get; set; }
|
||||||
|
|
||||||
public async Task Delay(TimeSpan delay, CancellationToken token)
|
public async Task Delay(TimeSpan delay, CancellationToken token)
|
||||||
{
|
{
|
||||||
// Event callback
|
// Event callback
|
||||||
@@ -387,6 +389,18 @@ namespace GitHub.Runner.Common.Tests
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void EnableAuthMigration(string trace)
|
||||||
|
{
|
||||||
|
AllowAuthMigration = true;
|
||||||
|
AuthMigrationChanged?.Invoke(this, new AuthMigrationEventArgs(trace));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DeferAuthMigration(TimeSpan deferred, string trace)
|
||||||
|
{
|
||||||
|
AllowAuthMigration = false;
|
||||||
|
AuthMigrationChanged?.Invoke(this, new AuthMigrationEventArgs(trace));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class DelayEventArgs : EventArgs
|
public class DelayEventArgs : EventArgs
|
||||||
|
|||||||
Reference in New Issue
Block a user