From 7ffd9af644647114f924a8b6cede3aaf5d79b71f Mon Sep 17 00:00:00 2001 From: Tingluo Huang Date: Mon, 13 Sep 2021 11:28:09 -0400 Subject: [PATCH] Support `--ephemeral` flag (#660) This optional flag will configure the runner to only take one job, and let the service un-configure the runner after that job finishes. --- src/Runner.Common/ConfigurationStore.cs | 3 +++ src/Runner.Common/Constants.cs | 3 ++- src/Runner.Listener/CommandSettings.cs | 4 +++- .../Configuration/ConfigurationManager.cs | 12 ++++++++---- src/Runner.Listener/Runner.cs | 18 ++++++++++++++---- src/Sdk/DTWebApi/WebApi/TaskAgentReference.cs | 11 +++++++++++ src/Test/L0/Listener/RunnerL0.cs | 15 +++++++++------ 7 files changed, 50 insertions(+), 16 deletions(-) diff --git a/src/Runner.Common/ConfigurationStore.cs b/src/Runner.Common/ConfigurationStore.cs index 0ae270420..942307314 100644 --- a/src/Runner.Common/ConfigurationStore.cs +++ b/src/Runner.Common/ConfigurationStore.cs @@ -33,6 +33,9 @@ namespace GitHub.Runner.Common [DataMember(EmitDefaultValue = false)] public string PoolName { get; set; } + [DataMember(EmitDefaultValue = false)] + public bool Ephemeral { get; set; } + [DataMember(EmitDefaultValue = false)] public string ServerUrl { get; set; } diff --git a/src/Runner.Common/Constants.cs b/src/Runner.Common/Constants.cs index 93cd7ffe7..05b939270 100644 --- a/src/Runner.Common/Constants.cs +++ b/src/Runner.Common/Constants.cs @@ -125,9 +125,10 @@ namespace GitHub.Runner.Common { public static readonly string Check = "check"; public static readonly string Commit = "commit"; + public static readonly string Ephemeral = "ephemeral"; public static readonly string Help = "help"; public static readonly string Replace = "replace"; - public static readonly string Once = "once"; + public static readonly string Once = "once"; // TODO: Remove in 10/2021 public static readonly string RunAsService = "runasservice"; public static readonly string Unattended = "unattended"; public static readonly string Version = "version"; diff --git a/src/Runner.Listener/CommandSettings.cs b/src/Runner.Listener/CommandSettings.cs index 9b3aecd61..b5e1c1a69 100644 --- a/src/Runner.Listener/CommandSettings.cs +++ b/src/Runner.Listener/CommandSettings.cs @@ -29,10 +29,10 @@ namespace GitHub.Runner.Listener { Constants.Runner.CommandLine.Flags.Check, Constants.Runner.CommandLine.Flags.Commit, + Constants.Runner.CommandLine.Flags.Ephemeral, Constants.Runner.CommandLine.Flags.Help, Constants.Runner.CommandLine.Flags.Replace, Constants.Runner.CommandLine.Flags.RunAsService, - Constants.Runner.CommandLine.Flags.Once, Constants.Runner.CommandLine.Flags.Unattended, Constants.Runner.CommandLine.Flags.Version }; @@ -66,7 +66,9 @@ namespace GitHub.Runner.Listener public bool Help => TestFlag(Constants.Runner.CommandLine.Flags.Help); public bool Unattended => TestFlag(Constants.Runner.CommandLine.Flags.Unattended); public bool Version => TestFlag(Constants.Runner.CommandLine.Flags.Version); + public bool Ephemeral => TestFlag(Constants.Runner.CommandLine.Flags.Ephemeral); + // TODO: Remove in 10/2021 public bool RunOnce => TestFlag(Constants.Runner.CommandLine.Flags.Once); // Constructor. diff --git a/src/Runner.Listener/Configuration/ConfigurationManager.cs b/src/Runner.Listener/Configuration/ConfigurationManager.cs index 4ee20d300..578739246 100644 --- a/src/Runner.Listener/Configuration/ConfigurationManager.cs +++ b/src/Runner.Listener/Configuration/ConfigurationManager.cs @@ -195,6 +195,7 @@ namespace GitHub.Runner.Listener.Configuration TaskAgent agent; while (true) { + runnerSettings.Ephemeral = command.Ephemeral; runnerSettings.AgentName = command.GetRunnerName(); _term.WriteLine(); @@ -211,7 +212,7 @@ namespace GitHub.Runner.Listener.Configuration if (command.GetReplace()) { // Update existing agent with new PublicKey, agent version. - agent = UpdateExistingAgent(agent, publicKey, userLabels); + agent = UpdateExistingAgent(agent, publicKey, userLabels, runnerSettings.Ephemeral); try { @@ -234,7 +235,7 @@ namespace GitHub.Runner.Listener.Configuration else { // Create a new agent. - agent = CreateNewAgent(runnerSettings.AgentName, publicKey, userLabels); + agent = CreateNewAgent(runnerSettings.AgentName, publicKey, userLabels, runnerSettings.Ephemeral); try { @@ -456,7 +457,7 @@ namespace GitHub.Runner.Listener.Configuration } - private TaskAgent UpdateExistingAgent(TaskAgent agent, RSAParameters publicKey, ISet userLabels) + private TaskAgent UpdateExistingAgent(TaskAgent agent, RSAParameters publicKey, ISet userLabels, bool ephemeral) { ArgUtil.NotNull(agent, nameof(agent)); agent.Authorization = new TaskAgentAuthorization @@ -467,6 +468,8 @@ namespace GitHub.Runner.Listener.Configuration // update should replace the existing labels agent.Version = BuildConstants.RunnerPackage.Version; agent.OSDescription = RuntimeInformation.OSDescription; + agent.Ephemeral = ephemeral; + agent.MaxParallelism = 1; agent.Labels.Clear(); @@ -482,7 +485,7 @@ namespace GitHub.Runner.Listener.Configuration return agent; } - private TaskAgent CreateNewAgent(string agentName, RSAParameters publicKey, ISet userLabels) + private TaskAgent CreateNewAgent(string agentName, RSAParameters publicKey, ISet userLabels, bool ephemeral) { TaskAgent agent = new TaskAgent(agentName) { @@ -493,6 +496,7 @@ namespace GitHub.Runner.Listener.Configuration MaxParallelism = 1, Version = BuildConstants.RunnerPackage.Version, OSDescription = RuntimeInformation.OSDescription, + Ephemeral = ephemeral, }; agent.Labels.Add(new AgentLabel("self-hosted", LabelType.System)); diff --git a/src/Runner.Listener/Runner.cs b/src/Runner.Listener/Runner.cs index 68b0b4ecd..f320f70b2 100644 --- a/src/Runner.Listener/Runner.cs +++ b/src/Runner.Listener/Runner.cs @@ -234,7 +234,7 @@ namespace GitHub.Runner.Listener HostContext.StartupType = startType; // Run the runner interactively or as service - return await RunAsync(settings, command.RunOnce); + return await RunAsync(settings, command.RunOnce || settings.Ephemeral); // TODO: Remove RunOnce later. } else { @@ -466,8 +466,16 @@ namespace GitHub.Runner.Listener await jobDispatcher.ShutdownAsync(); } - //TODO: make sure we don't mask more important exception - await _listener.DeleteSessionAsync(); + try + { + await _listener.DeleteSessionAsync(); + } + catch (Exception ex) when (runOnce) + { + // ignore exception during delete session for ephemeral runner since the runner might already be deleted from the server side + // and the delete session call will ends up with 401. + Trace.Info($"Ignore any exception during DeleteSession for an ephemeral runner. {ex}"); + } messageQueueLoopTokenSource.Dispose(); } @@ -512,7 +520,9 @@ Config Options: --labels string Extra labels in addition to the default: 'self-hosted,{Constants.Runner.Platform},{Constants.Runner.PlatformArchitecture}' --work string Relative runner work directory (default {Constants.Path.WorkDirectory}) --replace Replace any existing runner with the same name (default false) - --pat GitHub personal access token used for checking network connectivity when executing `.{separator}run.{ext} --check`"); + --pat GitHub personal access token used for checking network connectivity when executing `.{separator}run.{ext} --check` + --ephemeral Configure the runner to only take one job and then let the service un-configure the runner after the job finishes (default false)"); + #if OS_WINDOWS _term.WriteLine($@" --runasservice Run the runner as a service"); _term.WriteLine($@" --windowslogonaccount string Account to run the service as. Requires runasservice"); diff --git a/src/Sdk/DTWebApi/WebApi/TaskAgentReference.cs b/src/Sdk/DTWebApi/WebApi/TaskAgentReference.cs index 124274426..3031241a8 100644 --- a/src/Sdk/DTWebApi/WebApi/TaskAgentReference.cs +++ b/src/Sdk/DTWebApi/WebApi/TaskAgentReference.cs @@ -24,6 +24,7 @@ namespace GitHub.DistributedTask.WebApi this.OSDescription = referenceToBeCloned.OSDescription; this.ProvisioningState = referenceToBeCloned.ProvisioningState; this.AccessPoint = referenceToBeCloned.AccessPoint; + this.Ephemeral = referenceToBeCloned.Ephemeral; if (referenceToBeCloned.m_links != null) { @@ -81,6 +82,16 @@ namespace GitHub.DistributedTask.WebApi set; } + /// + /// Signifies that this Agent can only run one job and will be removed by the server after that one job finish. + /// + [DataMember] + public bool? Ephemeral + { + get; + set; + } + /// /// Whether or not the agent is online. /// diff --git a/src/Test/L0/Listener/RunnerL0.cs b/src/Test/L0/Listener/RunnerL0.cs index 32a21521b..bc9fe562b 100644 --- a/src/Test/L0/Listener/RunnerL0.cs +++ b/src/Test/L0/Listener/RunnerL0.cs @@ -243,7 +243,8 @@ namespace GitHub.Runner.Common.Tests.Listener runner.Initialize(hc); var settings = new RunnerSettings { - PoolId = 43242 + PoolId = 43242, + Ephemeral = true }; var message = new TaskAgentMessage() @@ -294,7 +295,7 @@ namespace GitHub.Runner.Common.Tests.Listener _configStore.Setup(x => x.IsServiceConfigured()).Returns(false); //Act - var command = new CommandSettings(hc, new string[] { "run", "--once" }); + var command = new CommandSettings(hc, new string[] { "run" }); Task runnerTask = runner.ExecuteCommand(command); //Assert @@ -332,7 +333,8 @@ namespace GitHub.Runner.Common.Tests.Listener runner.Initialize(hc); var settings = new RunnerSettings { - PoolId = 43242 + PoolId = 43242, + Ephemeral = true }; var message1 = new TaskAgentMessage() @@ -390,7 +392,7 @@ namespace GitHub.Runner.Common.Tests.Listener _configStore.Setup(x => x.IsServiceConfigured()).Returns(false); //Act - var command = new CommandSettings(hc, new string[] { "run", "--once" }); + var command = new CommandSettings(hc, new string[] { "run" }); Task runnerTask = runner.ExecuteCommand(command); //Assert @@ -431,7 +433,8 @@ namespace GitHub.Runner.Common.Tests.Listener var settings = new RunnerSettings { PoolId = 43242, - AgentId = 5678 + AgentId = 5678, + Ephemeral = true }; var message1 = new TaskAgentMessage() @@ -475,7 +478,7 @@ namespace GitHub.Runner.Common.Tests.Listener _configStore.Setup(x => x.IsServiceConfigured()).Returns(false); //Act - var command = new CommandSettings(hc, new string[] { "run", "--once" }); + var command = new CommandSettings(hc, new string[] { "run" }); Task runnerTask = runner.ExecuteCommand(command); //Assert