This commit is contained in:
TingluoHuang
2020-03-24 16:14:02 -04:00
parent 738602ee55
commit c44b34e436
7 changed files with 149 additions and 0 deletions

View File

@@ -46,6 +46,7 @@ namespace GitHub.Runner.Common
Add<T>(extensions, "GitHub.Runner.Worker.SetOutputCommandExtension, Runner.Worker");
Add<T>(extensions, "GitHub.Runner.Worker.SaveStateCommandExtension, Runner.Worker");
Add<T>(extensions, "GitHub.Runner.Worker.AddPathCommandExtension, Runner.Worker");
Add<T>(extensions, "GitHub.Runner.Worker.RefreshTokenCommandExtension, Runner.Worker");
Add<T>(extensions, "GitHub.Runner.Worker.AddMaskCommandExtension, Runner.Worker");
Add<T>(extensions, "GitHub.Runner.Worker.AddMatcherCommandExtension, Runner.Worker");
Add<T>(extensions, "GitHub.Runner.Worker.RemoveMatcherCommandExtension, Runner.Worker");

View File

@@ -22,6 +22,7 @@ namespace GitHub.Runner.Common
Task<List<TimelineRecord>> UpdateTimelineRecordsAsync(Guid scopeIdentifier, string hubName, Guid planId, Guid timelineId, IEnumerable<TimelineRecord> records, CancellationToken cancellationToken);
Task RaisePlanEventAsync<T>(Guid scopeIdentifier, string hubName, Guid planId, T eventData, CancellationToken cancellationToken) where T : JobEvent;
Task<Timeline> GetTimelineAsync(Guid scopeIdentifier, string hubName, Guid planId, Guid timelineId, CancellationToken cancellationToken);
Task<GitHubToken> RefreshGitHubTokenAsync(Guid scopeIdentifier, string hubName, Guid planId, Guid jobId, CancellationToken cancellationToken);
}
public sealed class JobServer : RunnerService, IJobServer
@@ -113,5 +114,11 @@ namespace GitHub.Runner.Common
CheckConnection();
return _taskClient.GetTimelineAsync(scopeIdentifier, hubName, planId, timelineId, includeRecords: true, cancellationToken: cancellationToken);
}
public Task<GitHubToken> RefreshGitHubTokenAsync(Guid scopeIdentifier, string hubName, Guid planId, Guid jobId, CancellationToken cancellationToken)
{
CheckConnection();
return _taskClient.RefreshTokenAsync(scopeIdentifier, hubName, planId, jobId, cancellationToken: cancellationToken);
}
}
}

View File

@@ -288,6 +288,30 @@ namespace GitHub.Runner.Worker
}
}
public sealed class RefreshTokenCommandExtension : RunnerService, IActionCommandExtension
{
public string Command => "refresh-token";
public bool OmitEcho => false;
public Type ExtensionType => typeof(IActionCommandExtension);
public void ProcessCommand(IExecutionContext context, string line, ActionCommand command, ContainerInfo container)
{
ArgUtil.NotNullOrEmpty(command.Data, "token file");
var runnerTemp = HostContext.GetDirectory(WellKnownDirectory.Temp);
var tempFile = Path.Combine(runnerTemp, command.Data);
if (!tempFile.StartsWith(runnerTemp + Path.DirectorySeparatorChar))
{
throw new Exception($"'{command.Data}' has to be under runner temp directory '{runnerTemp}'");
}
Trace.Info("Here");
var githubToken = context.GetGitHubToken().GetAwaiter().GetResult();
File.WriteAllText(tempFile, githubToken);
Trace.Info("HereAgain");
}
}
public sealed class AddMatcherCommandExtension : RunnerService, IActionCommandExtension
{
public string Command => "add-matcher";

View File

@@ -21,6 +21,7 @@ using System.Collections;
using ObjectTemplating = GitHub.DistributedTask.ObjectTemplating;
using Pipelines = GitHub.DistributedTask.Pipelines;
using GitHub.DistributedTask.Expressions2;
using System.Diagnostics;
namespace GitHub.Runner.Worker
{
@@ -99,6 +100,8 @@ namespace GitHub.Runner.Worker
// others
void ForceTaskComplete();
void RegisterPostJobStep(IStep step);
Task<string> GetGitHubToken();
Task UpdateGitHubTokenInContext();
}
public sealed class ExecutionContext : RunnerService, IExecutionContext
@@ -110,6 +113,7 @@ namespace GitHub.Runner.Worker
private readonly object _loggerLock = new object();
private readonly HashSet<string> _outputvariables = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
private readonly object _matchersLock = new object();
public readonly List<Task> _getGitHubTokenTasks = new List<Task>();
private event OnMatcherChanged _onMatcherChanged;
@@ -130,6 +134,13 @@ namespace GitHub.Runner.Worker
// only job level ExecutionContext will track throttling delay.
private long _totalThrottlingDelayInMilliseconds = 0;
private Guid _jobId;
private Guid _scopeIdentifier;
private string _hubName;
private Guid _planId;
private Stopwatch _githubTokenExpireTimer = new Stopwatch();
public Guid Id => _record.Id;
public string ScopeName { get; private set; }
public string ContextName { get; private set; }
@@ -242,6 +253,59 @@ namespace GitHub.Runner.Worker
});
}
public Task<string> GetGitHubToken()
{
Trace.Info($"Try get new GITHUB_TOKEN");
return Root.RefreshGitHubToken();
}
public async Task UpdateGitHubTokenInContext()
{
try
{
await Root.EnsureGitHubToken();
}
catch (Exception ex)
{
this.Warning($"Fail to get a new GITHUB_TOKEN, error: {ex.Message}");
this.Debug(ex.ToString());
}
}
private async Task<string> RefreshGitHubToken()
{
Trace.Info("Request new GITHUB_TOKEN");
var jobServer = HostContext.GetService<IJobServer>();
var githubToken = await jobServer.RefreshGitHubTokenAsync(_scopeIdentifier, _hubName, _planId, _jobId, CancellationToken.None);
if (!string.IsNullOrEmpty(githubToken?.Token))
{
// register secret masker
Trace.Info("Register secret masker for new GITHUB_TOKEN");
HostContext.SecretMasker.AddValue(githubToken.Token);
return githubToken.Token;
}
else
{
throw new InvalidOperationException("Get empty GTIHUB_TOKEN.");
}
}
private async Task EnsureGitHubToken()
{
// needs to refresh GITHUB_TOKEN every 50 mins since the token is good for 60 min by default
if (_githubTokenExpireTimer.Elapsed.TotalMilliseconds > 10)
{
var githubToken = await this.RefreshGitHubToken();
var secretsContext = ExpressionValues["secrets"] as DictionaryContextData;
secretsContext["GITHUB_TOKEN"] = new StringContextData(githubToken);
var githubContext = ExpressionValues["github"] as GitHubContext;
githubContext["token"] = new StringContextData(githubToken);
_githubTokenExpireTimer.Restart();
}
}
public void RegisterPostJobStep(IStep step)
{
if (step is IActionRunner actionRunner && !Root.StepsWithPostRegisted.Add(actionRunner.Action.Id))
@@ -617,6 +681,7 @@ namespace GitHub.Runner.Worker
ExpressionValues["env"] = new CaseSensitiveDictionaryContextData();
#endif
_githubTokenExpireTimer.Start();
// Prepend Path
PrependPath = new List<string>();
@@ -630,6 +695,11 @@ namespace GitHub.Runner.Worker
// StepsWithPostRegisted for job ExecutionContext
StepsWithPostRegisted = new HashSet<Guid>();
_scopeIdentifier = message.Plan.ScopeIdentifier;
_hubName = message.Plan.PlanType;
_jobId = message.JobId;
_planId = message.Plan.PlanId;
// Job timeline record.
InitializeTimelineRecord(
timelineId: message.Timeline.Id,

View File

@@ -75,6 +75,7 @@ namespace GitHub.Runner.Worker
// Start
step.ExecutionContext.Start();
await step.ExecutionContext.UpdateGitHubTokenInContext();
// Initialize scope
if (InitializeScope(step, scopeInputs))

View File

@@ -317,5 +317,40 @@ namespace GitHub.DistributedTask.WebApi
userState: userState,
cancellationToken: cancellationToken);
}
/// <summary>
/// [Preview API]
/// </summary>
/// <param name="scopeIdentifier">The project GUID to scope the request</param>
/// <param name="hubName">The name of the server hub: "build" for the Build server or "rm" for the Release Management server</param>
/// <param name="planId"></param>
/// <param name="jobId"></param>
/// <param name="userState"></param>
/// <param name="cancellationToken">The cancellation token to cancel operation.</param>
public virtual Task<GitHubToken> RefreshTokenAsync(
Guid scopeIdentifier,
string hubName,
Guid planId,
Guid jobId,
object userState = null,
CancellationToken cancellationToken = default)
{
HttpMethod httpMethod = new HttpMethod("GET");
Guid locationId = new Guid("8aa8aff7-751b-496e-be8d-b7818770efb3");
object routeValues = new { scopeIdentifier = scopeIdentifier, hubName = hubName, planId = planId };
List<KeyValuePair<string, string>> queryParams = new List<KeyValuePair<string, string>>();
queryParams.Add("jobId", jobId.ToString());
return SendAsync<GitHubToken>(
httpMethod,
locationId,
routeValues: routeValues,
version: new ApiResourceVersion(6.0, 1),
queryParameters: queryParams,
userState: userState,
cancellationToken: cancellationToken);
}
}
}

View File

@@ -4,6 +4,17 @@ using System.Runtime.Serialization;
namespace GitHub.DistributedTask.WebApi
{
[DataContract]
public class GitHubToken
{
[DataMember(EmitDefaultValue = false)]
public string Token { get; set; }
[DataMember(EmitDefaultValue = false)]
public DateTime Expires_at { get; set; }
}
[DataContract]
public class Issue
{