From 5914c1f7aaac0eb5dce408624df6a1361f84a054 Mon Sep 17 00:00:00 2001 From: Luke Tomlinson Date: Thu, 12 Oct 2023 08:30:27 -0700 Subject: [PATCH 01/33] wip --- src/Sdk/DTWebApi/WebApi/AgentRefreshMessage.cs | 8 ++++++++ src/runnerversion | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Sdk/DTWebApi/WebApi/AgentRefreshMessage.cs b/src/Sdk/DTWebApi/WebApi/AgentRefreshMessage.cs index 70a825ec7f4..fdaa5d15870 100644 --- a/src/Sdk/DTWebApi/WebApi/AgentRefreshMessage.cs +++ b/src/Sdk/DTWebApi/WebApi/AgentRefreshMessage.cs @@ -1,5 +1,6 @@ using Newtonsoft.Json; using System; +using System.Collections.Generic; using System.Runtime.Serialization; @@ -45,5 +46,12 @@ public String TargetVersion get; private set; } + + [DataMember] + public Dictionary Package + { + get; + set; + } } } diff --git a/src/runnerversion b/src/runnerversion index cb7d746ef3d..20ab9090bd6 100644 --- a/src/runnerversion +++ b/src/runnerversion @@ -1 +1 @@ -2.310.0 +2.308.0 From 50037b79d2a8add084f736421bc9cfe2aafd7876 Mon Sep 17 00:00:00 2001 From: Luke Tomlinson Date: Thu, 12 Oct 2023 14:46:05 -0700 Subject: [PATCH 02/33] wip fork selfupdater --- src/Runner.Listener/SelfUpdaterV2.cs | 847 ++++++++++++++++++ .../DTWebApi/WebApi/RunnerRefreshMessage.cs | 2 + src/runnerversion | 2 +- 3 files changed, 850 insertions(+), 1 deletion(-) create mode 100644 src/Runner.Listener/SelfUpdaterV2.cs diff --git a/src/Runner.Listener/SelfUpdaterV2.cs b/src/Runner.Listener/SelfUpdaterV2.cs new file mode 100644 index 00000000000..69c36e6a89f --- /dev/null +++ b/src/Runner.Listener/SelfUpdaterV2.cs @@ -0,0 +1,847 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Net.Http; +using System.Reflection; +using System.Security.Cryptography; +using System.Threading; +using System.Threading.Tasks; +using GitHub.DistributedTask.WebApi; +using GitHub.Runner.Common; +using GitHub.Runner.Common.Util; +using GitHub.Runner.Sdk; +using GitHub.Services.Common; +using GitHub.Services.WebApi; + +namespace GitHub.Runner.Listener +{ + public class SelfUpdaterV2 : RunnerService, ISelfUpdater + { + private static string _packageType = "agent"; + private static string _platform = BuildConstants.RunnerPackage.PackageName; + private static string _dotnetRuntime = "dotnetRuntime"; + private static string _externals = "externals"; + private readonly Dictionary _contentHashes = new(); + + private PackageMetadata _targetPackage; + private ITerminal _terminal; + private IRunnerServer _runnerServer; + private int _poolId; + private ulong _agentId; + private readonly ConcurrentQueue _updateTrace = new(); + private Task _cloneAndCalculateContentHashTask; + private string _dotnetRuntimeCloneDirectory; + private string _externalsCloneDirectory; + + public bool Busy { get; private set; } + + public override void Initialize(IHostContext hostContext) + { + base.Initialize(hostContext); + + _terminal = hostContext.GetService(); + _runnerServer = HostContext.GetService(); + var configStore = HostContext.GetService(); + var settings = configStore.GetSettings(); + _poolId = settings.PoolId; + _agentId = settings.AgentId; + _dotnetRuntimeCloneDirectory = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Work), "__dotnet_runtime__"); + _externalsCloneDirectory = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Work), "__externals__"); + } + + public async Task SelfUpdate(AgentRefreshMessage updateMessage, IJobDispatcher jobDispatcher, bool restartInteractiveRunner, CancellationToken token) + { + Busy = true; + try + { + var totalUpdateTime = Stopwatch.StartNew(); + + // Copy dotnet runtime and externals of current runner to a temp folder + // So we can re-use them with trimmed runner package, if possible. + // This process is best effort, if we can't use trimmed runner package, + // we will just go with the full package. + var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token); + _cloneAndCalculateContentHashTask = CloneAndCalculateAssetsHash(_dotnetRuntimeCloneDirectory, _externalsCloneDirectory, linkedTokenSource.Token); + + Trace.Info($"An update is available."); + _updateTrace.Enqueue($"RunnerPlatform: {_targetPackage.Platform}"); + + // Print console line that warn user not shutdown runner. + await UpdateRunnerUpdateStateAsync("Runner update in progress, do not shutdown runner."); + await UpdateRunnerUpdateStateAsync($"Downloading {_targetPackage.Version} runner"); + + linkedTokenSource.Cancel(); + try + { + await _cloneAndCalculateContentHashTask; + } + catch (Exception ex) + { + Trace.Info($"Ingore errors after cancelling cloning assets task: {ex}"); + } + + await DownloadLatestRunner(token, updateMessage.TargetVersion); + Trace.Info($"Download latest runner and unzip into runner root."); + + // wait till all running job finish + await UpdateRunnerUpdateStateAsync("Waiting for current job finish running."); + + await jobDispatcher.WaitAsync(token); + Trace.Info($"All running job has exited."); + + // We need to keep runner backup around for macOS until we fixed https://github.com/actions/runner/issues/743 + // delete runner backup + var stopWatch = Stopwatch.StartNew(); + DeletePreviousVersionRunnerBackup(token); + Trace.Info($"Delete old version runner backup."); + stopWatch.Stop(); + // generate update script from template + _updateTrace.Enqueue($"DeleteRunnerBackupTime: {stopWatch.ElapsedMilliseconds}ms"); + await UpdateRunnerUpdateStateAsync("Generate and execute update script."); + + string updateScript = GenerateUpdateScript(restartInteractiveRunner); + Trace.Info($"Generate update script into: {updateScript}"); + + + // For L0, we will skip execute update script. + if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("_GITHUB_ACTION_EXECUTE_UPDATE_SCRIPT"))) + { + string flagFile = "update.finished"; + IOUtil.DeleteFile(flagFile); + // kick off update script + Process invokeScript = new(); +#if OS_WINDOWS + invokeScript.StartInfo.FileName = WhichUtil.Which("cmd.exe", trace: Trace); + invokeScript.StartInfo.Arguments = $"/c \"{updateScript}\""; +#elif (OS_OSX || OS_LINUX) + invokeScript.StartInfo.FileName = WhichUtil.Which("bash", trace: Trace); + invokeScript.StartInfo.Arguments = $"\"{updateScript}\""; +#endif + invokeScript.Start(); + Trace.Info($"Update script start running"); + } + + totalUpdateTime.Stop(); + + _updateTrace.Enqueue($"TotalUpdateTime: {totalUpdateTime.ElapsedMilliseconds}ms"); + await UpdateRunnerUpdateStateAsync("Runner will exit shortly for update, should be back online within 10 seconds."); + + return true; + } + catch (Exception ex) + { + _updateTrace.Enqueue(ex.ToString()); + throw; + } + finally + { + await UpdateRunnerUpdateStateAsync("Runner update process finished."); + Busy = false; + } + } + + private async Task UpdateNeeded(string targetVersion, CancellationToken token) + { + // when talk to old version server, always prefer latest package. + // old server won't send target version as part of update message. + if (string.IsNullOrEmpty(targetVersion)) + { + var packages = await _runnerServer.GetPackagesAsync(_packageType, _platform, 1, true, token); + if (packages == null || packages.Count == 0) + { + Trace.Info($"There is no package for {_packageType} and {_platform}."); + return false; + } + + _targetPackage = packages.FirstOrDefault(); + } + else + { + _targetPackage = await _runnerServer.GetPackageAsync(_packageType, _platform, targetVersion, true, token); + if (_targetPackage == null) + { + Trace.Info($"There is no package for {_packageType} and {_platform} with version {targetVersion}."); + return false; + } + } + + Trace.Info($"Version '{_targetPackage.Version}' of '{_targetPackage.Type}' package available in server."); + PackageVersion serverVersion = new(_targetPackage.Version); + Trace.Info($"Current running runner version is {BuildConstants.RunnerPackage.Version}"); + PackageVersion runnerVersion = new(BuildConstants.RunnerPackage.Version); + + return serverVersion.CompareTo(runnerVersion) > 0; + } + + /// + /// _work + /// \_update + /// \bin + /// \externals + /// \run.sh + /// \run.cmd + /// \package.zip //temp download .zip/.tar.gz + /// + /// + /// + private async Task DownloadLatestRunner(CancellationToken token, string targetVersion) + { + string latestRunnerDirectory = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Work), Constants.Path.UpdateDirectory); + IOUtil.DeleteDirectory(latestRunnerDirectory, token); + Directory.CreateDirectory(latestRunnerDirectory); + + string archiveFile = null; + var packageDownloadUrl = _targetPackage.DownloadUrl; + var packageHashValue = _targetPackage.HashValue; + + // Only try trimmed package if sever sends them and we have calculated hash value of the current runtime/externals. + _updateTrace.Enqueue($"DownloadUrl: {packageDownloadUrl}"); + + try + { +#if DEBUG + // Much of the update process (targetVersion, archive) is server-side, this is a way to control it from here for testing specific update scenarios + // Add files like 'runner2.281.2.tar.gz' or 'runner2.283.0.zip' (depending on your platform) to your runner root folder + // Note that runners still need to be older than the server's runner version in order to receive an 'AgentRefreshMessage' and trigger this update + // Wrapped in #if DEBUG as this should not be in the RELEASE build + if (StringUtil.ConvertToBoolean(Environment.GetEnvironmentVariable("GITHUB_ACTIONS_RUNNER_IS_MOCK_UPDATE"))) + { + var waitForDebugger = StringUtil.ConvertToBoolean(Environment.GetEnvironmentVariable("GITHUB_ACTIONS_RUNNER_IS_MOCK_UPDATE_WAIT_FOR_DEBUGGER")); + if (waitForDebugger) + { + int waitInSeconds = 20; + while (!Debugger.IsAttached && waitInSeconds-- > 0) + { + await Task.Delay(1000); + } + Debugger.Break(); + } + + if (_targetPackage.Platform.StartsWith("win")) + { + archiveFile = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), $"runner{targetVersion}.zip"); + } + else + { + archiveFile = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), $"runner{targetVersion}.tar.gz"); + } + + if (File.Exists(archiveFile)) + { + _updateTrace.Enqueue($"Mocking update with file: '{archiveFile}' and targetVersion: '{targetVersion}', nothing is downloaded"); + _terminal.WriteLine($"Mocking update with file: '{archiveFile}' and targetVersion: '{targetVersion}', nothing is downloaded"); + } + else + { + archiveFile = null; + _terminal.WriteLine($"Mock runner archive not found at {archiveFile} for target version {targetVersion}, proceeding with download instead"); + _updateTrace.Enqueue($"Mock runner archive not found at {archiveFile} for target version {targetVersion}, proceeding with download instead"); + } + } +#endif + // archiveFile is not null only if we mocked it above + if (string.IsNullOrEmpty(archiveFile)) + { + archiveFile = await DownLoadRunner(latestRunnerDirectory, packageDownloadUrl, packageHashValue, token); + + if (string.IsNullOrEmpty(archiveFile)) + { + throw new TaskCanceledException($"Runner package '{packageDownloadUrl}' failed after {Constants.RunnerDownloadRetryMaxAttempts} download attempts"); + } + await ValidateRunnerHash(archiveFile, packageHashValue); + } + + await ExtractRunnerPackage(archiveFile, latestRunnerDirectory, token); + } + finally + { + try + { + // delete .zip file + if (!string.IsNullOrEmpty(archiveFile) && File.Exists(archiveFile)) + { + Trace.Verbose("Deleting latest runner package zip: {0}", archiveFile); + IOUtil.DeleteFile(archiveFile); + } + } + catch (Exception ex) + { + //it is not critical if we fail to delete the .zip file + Trace.Warning("Failed to delete runner package zip '{0}'. Exception: {1}", archiveFile, ex); + } + } + + var trimmedPackageRestoreTasks = new List>(); + if (!fallbackToFullPackage) + { + // Skip restoring externals and runtime if we are going to fullback to the full package. + if (externalsTrimmed) + { + trimmedPackageRestoreTasks.Add(RestoreTrimmedExternals(latestRunnerDirectory, token)); + } + if (runtimeTrimmed) + { + trimmedPackageRestoreTasks.Add(RestoreTrimmedDotnetRuntime(latestRunnerDirectory, token)); + } + } + + if (trimmedPackageRestoreTasks.Count > 0) + { + var restoreResults = await Task.WhenAll(trimmedPackageRestoreTasks); + if (restoreResults.Any(x => x == false)) + { + // if any of the restore failed, fallback to full package. + fallbackToFullPackage = true; + } + } + + if (fallbackToFullPackage) + { + Trace.Error("Something wrong with the trimmed runner package, failback to use the full package for runner updates."); + _updateTrace.Enqueue($"FallbackToFullPackage: {fallbackToFullPackage}"); + + IOUtil.DeleteDirectory(latestRunnerDirectory, token); + Directory.CreateDirectory(latestRunnerDirectory); + + packageDownloadUrl = _targetPackage.DownloadUrl; + packageHashValue = _targetPackage.HashValue; + _updateTrace.Enqueue($"DownloadUrl: {packageDownloadUrl}"); + + try + { + archiveFile = await DownLoadRunner(latestRunnerDirectory, packageDownloadUrl, packageHashValue, token); + + if (string.IsNullOrEmpty(archiveFile)) + { + throw new TaskCanceledException($"Runner package '{packageDownloadUrl}' failed after {Constants.RunnerDownloadRetryMaxAttempts} download attempts"); + } + + await ValidateRunnerHash(archiveFile, packageHashValue); + + await ExtractRunnerPackage(archiveFile, latestRunnerDirectory, token); + } + finally + { + try + { + // delete .zip file + if (!string.IsNullOrEmpty(archiveFile) && File.Exists(archiveFile)) + { + Trace.Verbose("Deleting latest runner package zip: {0}", archiveFile); + IOUtil.DeleteFile(archiveFile); + } + } + catch (Exception ex) + { + //it is not critical if we fail to delete the .zip file + Trace.Warning("Failed to delete runner package zip '{0}'. Exception: {1}", archiveFile, ex); + } + } + } + + await CopyLatestRunnerToRoot(latestRunnerDirectory, token); + } + + private async Task DownLoadRunner(string downloadDirectory, string packageDownloadUrl, string packageHashValue, CancellationToken token) + { + var stopWatch = Stopwatch.StartNew(); + int runnerSuffix = 1; + string archiveFile = null; + bool downloadSucceeded = false; + + // Download the runner, using multiple attempts in order to be resilient against any networking/CDN issues + for (int attempt = 1; attempt <= Constants.RunnerDownloadRetryMaxAttempts; attempt++) + { + // Generate an available package name, and do our best effort to clean up stale local zip files + while (true) + { + if (_targetPackage.Platform.StartsWith("win")) + { + archiveFile = Path.Combine(downloadDirectory, $"runner{runnerSuffix}.zip"); + } + else + { + archiveFile = Path.Combine(downloadDirectory, $"runner{runnerSuffix}.tar.gz"); + } + + try + { + // delete .zip file + if (!string.IsNullOrEmpty(archiveFile) && File.Exists(archiveFile)) + { + Trace.Verbose("Deleting latest runner package zip '{0}'", archiveFile); + IOUtil.DeleteFile(archiveFile); + } + + break; + } + catch (Exception ex) + { + // couldn't delete the file for whatever reason, so generate another name + Trace.Warning("Failed to delete runner package zip '{0}'. Exception: {1}", archiveFile, ex); + runnerSuffix++; + } + } + + // Allow a 15-minute package download timeout, which is good enough to update the runner from a 1 Mbit/s ADSL connection. + if (!int.TryParse(Environment.GetEnvironmentVariable("GITHUB_ACTIONS_RUNNER_DOWNLOAD_TIMEOUT") ?? string.Empty, out int timeoutSeconds)) + { + timeoutSeconds = 15 * 60; + } + + Trace.Info($"Attempt {attempt}: save latest runner into {archiveFile}."); + + using (var downloadTimeout = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutSeconds))) + using (var downloadCts = CancellationTokenSource.CreateLinkedTokenSource(downloadTimeout.Token, token)) + { + try + { + Trace.Info($"Download runner: begin download"); + long downloadSize = 0; + + //open zip stream in async mode + using (HttpClient httpClient = new(HostContext.CreateHttpClientHandler())) + { + if (!string.IsNullOrEmpty(_targetPackage.Token)) + { + Trace.Info($"Adding authorization token ({_targetPackage.Token.Length} chars)"); + httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _targetPackage.Token); + } + + Trace.Info($"Downloading {packageDownloadUrl}"); + + using (FileStream fs = new(archiveFile, FileMode.Create, FileAccess.Write, FileShare.None, bufferSize: 4096, useAsync: true)) + using (Stream result = await httpClient.GetStreamAsync(packageDownloadUrl)) + { + //81920 is the default used by System.IO.Stream.CopyTo and is under the large object heap threshold (85k). + await result.CopyToAsync(fs, 81920, downloadCts.Token); + await fs.FlushAsync(downloadCts.Token); + downloadSize = fs.Length; + } + } + + Trace.Info($"Download runner: finished download"); + downloadSucceeded = true; + stopWatch.Stop(); + _updateTrace.Enqueue($"PackageDownloadTime: {stopWatch.ElapsedMilliseconds}ms"); + _updateTrace.Enqueue($"Attempts: {attempt}"); + _updateTrace.Enqueue($"PackageSize: {downloadSize / 1024 / 1024}MB"); + break; + } + catch (OperationCanceledException) when (token.IsCancellationRequested) + { + Trace.Info($"Runner download has been cancelled."); + throw; + } + catch (Exception ex) + { + if (downloadCts.Token.IsCancellationRequested) + { + Trace.Warning($"Runner download has timed out after {timeoutSeconds} seconds"); + } + + Trace.Warning($"Failed to get package '{archiveFile}' from '{packageDownloadUrl}'. Exception {ex}"); + } + } + } + + if (downloadSucceeded) + { + return archiveFile; + } + else + { + return null; + } + } + + private async Task ValidateRunnerHash(string archiveFile, string packageHashValue) + { + var stopWatch = Stopwatch.StartNew(); + // Validate Hash Matches if it is provided + using (FileStream stream = File.OpenRead(archiveFile)) + { + if (!string.IsNullOrEmpty(packageHashValue)) + { + using (SHA256 sha256 = SHA256.Create()) + { + byte[] srcHashBytes = await sha256.ComputeHashAsync(stream); + var hash = PrimitiveExtensions.ConvertToHexString(srcHashBytes); + if (hash != packageHashValue) + { + // Hash did not match, we can't recover from this, just throw + throw new Exception($"Computed runner hash {hash} did not match expected Runner Hash {packageHashValue} for {archiveFile}"); + } + + stopWatch.Stop(); + Trace.Info($"Validated Runner Hash matches {archiveFile} : {packageHashValue}"); + _updateTrace.Enqueue($"ValidateHashTime: {stopWatch.ElapsedMilliseconds}ms"); + } + } + } + } + + private async Task ExtractRunnerPackage(string archiveFile, string extractDirectory, CancellationToken token) + { + var stopWatch = Stopwatch.StartNew(); + + if (archiveFile.EndsWith(".zip", StringComparison.OrdinalIgnoreCase)) + { + ZipFile.ExtractToDirectory(archiveFile, extractDirectory); + } + else if (archiveFile.EndsWith(".tar.gz", StringComparison.OrdinalIgnoreCase)) + { + string tar = WhichUtil.Which("tar", trace: Trace); + + if (string.IsNullOrEmpty(tar)) + { + throw new NotSupportedException($"tar -xzf"); + } + + // tar -xzf + using (var processInvoker = HostContext.CreateService()) + { + processInvoker.OutputDataReceived += new EventHandler((sender, args) => + { + if (!string.IsNullOrEmpty(args.Data)) + { + Trace.Info(args.Data); + } + }); + + processInvoker.ErrorDataReceived += new EventHandler((sender, args) => + { + if (!string.IsNullOrEmpty(args.Data)) + { + Trace.Error(args.Data); + } + }); + + int exitCode = await processInvoker.ExecuteAsync(extractDirectory, tar, $"-xzf \"{archiveFile}\"", null, token); + if (exitCode != 0) + { + throw new NotSupportedException($"Can't use 'tar -xzf' to extract archive file: {archiveFile}. return code: {exitCode}."); + } + } + } + else + { + throw new NotSupportedException($"{archiveFile}"); + } + + stopWatch.Stop(); + Trace.Info($"Finished getting latest runner package at: {extractDirectory}."); + _updateTrace.Enqueue($"PackageExtractTime: {stopWatch.ElapsedMilliseconds}ms"); + } + + private Task CopyLatestRunnerToRoot(string latestRunnerDirectory, CancellationToken token) + { + var stopWatch = Stopwatch.StartNew(); + // copy latest runner into runner root folder + // copy bin from _work/_update -> bin.version under root + string binVersionDir = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), $"{Constants.Path.BinDirectory}.{_targetPackage.Version}"); + Directory.CreateDirectory(binVersionDir); + Trace.Info($"Copy {Path.Combine(latestRunnerDirectory, Constants.Path.BinDirectory)} to {binVersionDir}."); + IOUtil.CopyDirectory(Path.Combine(latestRunnerDirectory, Constants.Path.BinDirectory), binVersionDir, token); + + // copy externals from _work/_update -> externals.version under root + string externalsVersionDir = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), $"{Constants.Path.ExternalsDirectory}.{_targetPackage.Version}"); + Directory.CreateDirectory(externalsVersionDir); + Trace.Info($"Copy {Path.Combine(latestRunnerDirectory, Constants.Path.ExternalsDirectory)} to {externalsVersionDir}."); + IOUtil.CopyDirectory(Path.Combine(latestRunnerDirectory, Constants.Path.ExternalsDirectory), externalsVersionDir, token); + + // copy and replace all .sh/.cmd files + Trace.Info($"Copy any remaining .sh/.cmd files into runner root."); + foreach (FileInfo file in new DirectoryInfo(latestRunnerDirectory).GetFiles() ?? new FileInfo[0]) + { + string destination = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), file.Name); + + // Removing the file instead of just trying to overwrite it works around permissions issues on linux. + // https://github.com/actions/runner/issues/981 + Trace.Info($"Copy {file.FullName} to {destination}"); + IOUtil.DeleteFile(destination); + file.CopyTo(destination, true); + } + + stopWatch.Stop(); + _updateTrace.Enqueue($"CopyRunnerToRootTime: {stopWatch.ElapsedMilliseconds}ms"); + return Task.CompletedTask; + } + + private void DeletePreviousVersionRunnerBackup(CancellationToken token) + { + // delete previous backup runner (back compat, can be remove after serval sprints) + // bin.bak.2.99.0 + // externals.bak.2.99.0 + foreach (string existBackUp in Directory.GetDirectories(HostContext.GetDirectory(WellKnownDirectory.Root), "*.bak.*")) + { + Trace.Info($"Delete existing runner backup at {existBackUp}."); + try + { + IOUtil.DeleteDirectory(existBackUp, token); + } + catch (Exception ex) when (!(ex is OperationCanceledException)) + { + Trace.Error(ex); + Trace.Info($"Catch exception during delete backup folder {existBackUp}, ignore this error try delete the backup folder on next auto-update."); + } + } + + // delete old bin.2.99.0 folder, only leave the current version and the latest download version + var allBinDirs = Directory.GetDirectories(HostContext.GetDirectory(WellKnownDirectory.Root), "bin.*"); + if (allBinDirs.Length > 2) + { + // there are more than 2 bin.version folder. + // delete older bin.version folders. + foreach (var oldBinDir in allBinDirs) + { + if (string.Equals(oldBinDir, Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), $"bin"), StringComparison.OrdinalIgnoreCase) || + string.Equals(oldBinDir, Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), $"bin.{BuildConstants.RunnerPackage.Version}"), StringComparison.OrdinalIgnoreCase) || + string.Equals(oldBinDir, Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), $"bin.{_targetPackage.Version}"), StringComparison.OrdinalIgnoreCase)) + { + // skip for current runner version + continue; + } + + Trace.Info($"Delete runner bin folder's backup at {oldBinDir}."); + try + { + IOUtil.DeleteDirectory(oldBinDir, token); + } + catch (Exception ex) when (!(ex is OperationCanceledException)) + { + Trace.Error(ex); + Trace.Info($"Catch exception during delete backup folder {oldBinDir}, ignore this error try delete the backup folder on next auto-update."); + } + } + } + + // delete old externals.2.99.0 folder, only leave the current version and the latest download version + var allExternalsDirs = Directory.GetDirectories(HostContext.GetDirectory(WellKnownDirectory.Root), "externals.*"); + if (allExternalsDirs.Length > 2) + { + // there are more than 2 externals.version folder. + // delete older externals.version folders. + foreach (var oldExternalDir in allExternalsDirs) + { + if (string.Equals(oldExternalDir, Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), $"externals"), StringComparison.OrdinalIgnoreCase) || + string.Equals(oldExternalDir, Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), $"externals.{BuildConstants.RunnerPackage.Version}"), StringComparison.OrdinalIgnoreCase) || + string.Equals(oldExternalDir, Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), $"externals.{_targetPackage.Version}"), StringComparison.OrdinalIgnoreCase)) + { + // skip for current runner version + continue; + } + + Trace.Info($"Delete runner externals folder's backup at {oldExternalDir}."); + try + { + IOUtil.DeleteDirectory(oldExternalDir, token); + } + catch (Exception ex) when (!(ex is OperationCanceledException)) + { + Trace.Error(ex); + Trace.Info($"Catch exception during delete backup folder {oldExternalDir}, ignore this error try delete the backup folder on next auto-update."); + } + } + } + } + + private string GenerateUpdateScript(bool restartInteractiveRunner) + { + int processId = Process.GetCurrentProcess().Id; + string updateLog = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Diag), $"SelfUpdate-{DateTime.UtcNow.ToString("yyyyMMdd-HHmmss")}.log"); + string runnerRoot = HostContext.GetDirectory(WellKnownDirectory.Root); + +#if OS_WINDOWS + string templateName = "update.cmd.template"; +#else + string templateName = "update.sh.template"; +#endif + + string templatePath = Path.Combine(runnerRoot, $"bin.{_targetPackage.Version}", templateName); + string template = File.ReadAllText(templatePath); + + template = template.Replace("_PROCESS_ID_", processId.ToString()); + template = template.Replace("_RUNNER_PROCESS_NAME_", $"Runner.Listener{IOUtil.ExeExtension}"); + template = template.Replace("_ROOT_FOLDER_", runnerRoot); + template = template.Replace("_EXIST_RUNNER_VERSION_", BuildConstants.RunnerPackage.Version); + template = template.Replace("_DOWNLOAD_RUNNER_VERSION_", _targetPackage.Version); + template = template.Replace("_UPDATE_LOG_", updateLog); + template = template.Replace("_RESTART_INTERACTIVE_RUNNER_", restartInteractiveRunner ? "1" : "0"); + +#if OS_WINDOWS + string scriptName = "_update.cmd"; +#else + string scriptName = "_update.sh"; +#endif + + string updateScript = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Work), scriptName); + if (File.Exists(updateScript)) + { + IOUtil.DeleteFile(updateScript); + } + + File.WriteAllText(updateScript, template); + return updateScript; + } + + private async Task UpdateRunnerUpdateStateAsync(string currentState) + { + _terminal.WriteLine(currentState); + + var traces = new List(); + while (_updateTrace.TryDequeue(out var trace)) + { + traces.Add(trace); + } + + if (traces.Count > 0) + { + foreach (var trace in traces) + { + Trace.Info(trace); + } + } + + try + { + await _runnerServer.UpdateAgentUpdateStateAsync(_poolId, _agentId, currentState, string.Join(Environment.NewLine, traces)); + _updateTrace.Clear(); + } + catch (VssResourceNotFoundException) + { + // ignore VssResourceNotFoundException, this exception means the runner is configured against a old server that doesn't support report runner update detail. + Trace.Info($"Catch VssResourceNotFoundException during report update state, ignore this error for backcompat."); + } + catch (Exception ex) + { + Trace.Error(ex); + Trace.Info($"Catch exception during report update state, ignore this error and continue auto-update."); + } + } + + private async Task RestoreTrimmedExternals(string downloadDirectory, CancellationToken token) + { + // Copy the current runner's externals if we are using a externals trimmed package + // Execute the node.js to make sure the copied externals is working. + var stopWatch = Stopwatch.StartNew(); + try + { + Trace.Info($"Copy {_externalsCloneDirectory} to {Path.Combine(downloadDirectory, Constants.Path.ExternalsDirectory)}."); + IOUtil.CopyDirectory(_externalsCloneDirectory, Path.Combine(downloadDirectory, Constants.Path.ExternalsDirectory), token); + + // try run node.js to see if current node.js works fine after copy over to new location. + var nodeVersions = NodeUtil.BuiltInNodeVersions; + foreach (var nodeVersion in nodeVersions) + { + var newNodeBinary = Path.Combine(downloadDirectory, Constants.Path.ExternalsDirectory, nodeVersion, "bin", $"node{IOUtil.ExeExtension}"); + if (File.Exists(newNodeBinary)) + { + using (var p = HostContext.CreateService()) + { + var outputs = ""; + p.ErrorDataReceived += (_, data) => + { + if (!string.IsNullOrEmpty(data.Data)) + { + Trace.Error(data.Data); + } + }; + p.OutputDataReceived += (_, data) => + { + if (!string.IsNullOrEmpty(data.Data)) + { + Trace.Info(data.Data); + outputs = data.Data; + } + }; + var exitCode = await p.ExecuteAsync(HostContext.GetDirectory(WellKnownDirectory.Root), newNodeBinary, $"-e \"console.log('{nameof(RestoreTrimmedExternals)}')\"", null, token); + if (exitCode != 0) + { + Trace.Error($"{newNodeBinary} -e \"console.log()\" failed with exit code {exitCode}"); + return false; + } + + if (!string.Equals(outputs, nameof(RestoreTrimmedExternals), StringComparison.OrdinalIgnoreCase)) + { + Trace.Error($"{newNodeBinary} -e \"console.log()\" did not output expected content."); + return false; + } + } + } + } + + return true; + } + catch (Exception ex) + { + Trace.Error($"Fail to restore externals for trimmed package: {ex}"); + return false; + } + finally + { + stopWatch.Stop(); + _updateTrace.Enqueue($"{nameof(RestoreTrimmedExternals)}Time: {stopWatch.ElapsedMilliseconds}ms"); + } + } + + private async Task HashFiles(string fileFolder, CancellationToken token) + { + Trace.Info($"Calculating hash for {fileFolder}"); + + var stopWatch = Stopwatch.StartNew(); + string binDir = HostContext.GetDirectory(WellKnownDirectory.Bin); + string node = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Externals), NodeUtil.GetInternalNodeVersion(), "bin", $"node{IOUtil.ExeExtension}"); + string hashFilesScript = Path.Combine(binDir, "hashFiles"); + var hashResult = string.Empty; + + using (var processInvoker = HostContext.CreateService()) + { + processInvoker.ErrorDataReceived += (_, data) => + { + if (!string.IsNullOrEmpty(data.Data) && data.Data.StartsWith("__OUTPUT__") && data.Data.EndsWith("__OUTPUT__")) + { + hashResult = data.Data.Substring(10, data.Data.Length - 20); + Trace.Info($"Hash result: '{hashResult}'"); + } + else + { + Trace.Info(data.Data); + } + }; + + processInvoker.OutputDataReceived += (_, data) => + { + Trace.Verbose(data.Data); + }; + + var env = new Dictionary + { + ["patterns"] = "**" + }; + + int exitCode = await processInvoker.ExecuteAsync(workingDirectory: fileFolder, + fileName: node, + arguments: $"\"{hashFilesScript.Replace("\"", "\\\"")}\"", + environment: env, + requireExitCodeZero: false, + outputEncoding: null, + killProcessOnCancel: true, + cancellationToken: token); + + if (exitCode != 0) + { + Trace.Error($"hashFiles returns '{exitCode}' failed. Fail to hash files under directory '{fileFolder}'"); + } + + stopWatch.Stop(); + _updateTrace.Enqueue($"{nameof(HashFiles)}{Path.GetFileName(fileFolder)}Time: {stopWatch.ElapsedMilliseconds}ms"); + return hashResult; + } + } + } +} diff --git a/src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs b/src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs index 2e1e92836ac..18ff14e3ad8 100644 --- a/src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs +++ b/src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs @@ -45,5 +45,7 @@ public String TargetVersion get; private set; } + + [DataMember] PackageMetadata Package { get; set; } } } diff --git a/src/runnerversion b/src/runnerversion index 20ab9090bd6..847c4973f62 100644 --- a/src/runnerversion +++ b/src/runnerversion @@ -1 +1 @@ -2.308.0 +2.307.0 From 10a27687d86bf0b2a5df05e32a248a8be020bfe3 Mon Sep 17 00:00:00 2001 From: Luke Tomlinson Date: Thu, 12 Oct 2023 15:16:24 -0700 Subject: [PATCH 03/33] wip --- src/Runner.Listener/Runner.cs | 18 ++- src/Runner.Listener/SelfUpdaterV2.cs | 190 --------------------------- 2 files changed, 14 insertions(+), 194 deletions(-) diff --git a/src/Runner.Listener/Runner.cs b/src/Runner.Listener/Runner.cs index efa5cf70700..ac23b873b42 100644 --- a/src/Runner.Listener/Runner.cs +++ b/src/Runner.Listener/Runner.cs @@ -464,14 +464,14 @@ private async Task RunAsync(RunnerSettings settings, bool runOnce = false) { autoUpdateInProgress = true; AgentRefreshMessage runnerUpdateMessage = null; + RunnerRefreshMessage brokerRunnerUpdateMessage = null; if (string.Equals(message.MessageType, AgentRefreshMessage.MessageType, StringComparison.OrdinalIgnoreCase)) { runnerUpdateMessage = JsonUtility.FromString(message.Body); } else { - var brokerRunnerUpdateMessage = JsonUtility.FromString(message.Body); - runnerUpdateMessage = new AgentRefreshMessage(brokerRunnerUpdateMessage.RunnerId, brokerRunnerUpdateMessage.TargetVersion, TimeSpan.FromSeconds(brokerRunnerUpdateMessage.TimeoutInSeconds)); + brokerRunnerUpdateMessage = JsonUtility.FromString(message.Body); } #if DEBUG // Can mock the update for testing @@ -494,8 +494,18 @@ private async Task RunAsync(RunnerSettings settings, bool runOnce = false) } } #endif - var selfUpdater = HostContext.GetService(); - selfUpdateTask = selfUpdater.SelfUpdate(runnerUpdateMessage, jobDispatcher, false, HostContext.RunnerShutdownToken); + if (brokerRunnerUpdateMessage != null) + { + var selfUpdater = new SelfUpdaterV2(); + selfUpdater.Initialize(HostContext); + selfUpdater = selfUpdater.SelfUpdate(brokerRunnerUpdateMessage, jobDispatcher, false, HostContext.RunnerShutdownToken); + } + else + { + var selfUpdater = HostContext.GetService(); + selfUpdateTask = selfUpdater.SelfUpdate(runnerUpdateMessage, jobDispatcher, false, HostContext.RunnerShutdownToken); + } + Trace.Info("Refresh message received, kick-off selfupdate background process."); } else diff --git a/src/Runner.Listener/SelfUpdaterV2.cs b/src/Runner.Listener/SelfUpdaterV2.cs index 69c36e6a89f..174783e8b03 100644 --- a/src/Runner.Listener/SelfUpdaterV2.cs +++ b/src/Runner.Listener/SelfUpdaterV2.cs @@ -21,22 +21,13 @@ namespace GitHub.Runner.Listener { public class SelfUpdaterV2 : RunnerService, ISelfUpdater { - private static string _packageType = "agent"; private static string _platform = BuildConstants.RunnerPackage.PackageName; - private static string _dotnetRuntime = "dotnetRuntime"; - private static string _externals = "externals"; - private readonly Dictionary _contentHashes = new(); - private PackageMetadata _targetPackage; private ITerminal _terminal; private IRunnerServer _runnerServer; private int _poolId; private ulong _agentId; private readonly ConcurrentQueue _updateTrace = new(); - private Task _cloneAndCalculateContentHashTask; - private string _dotnetRuntimeCloneDirectory; - private string _externalsCloneDirectory; - public bool Busy { get; private set; } public override void Initialize(IHostContext hostContext) @@ -49,8 +40,6 @@ public override void Initialize(IHostContext hostContext) var settings = configStore.GetSettings(); _poolId = settings.PoolId; _agentId = settings.AgentId; - _dotnetRuntimeCloneDirectory = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Work), "__dotnet_runtime__"); - _externalsCloneDirectory = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Work), "__externals__"); } public async Task SelfUpdate(AgentRefreshMessage updateMessage, IJobDispatcher jobDispatcher, bool restartInteractiveRunner, CancellationToken token) @@ -64,9 +53,6 @@ public async Task SelfUpdate(AgentRefreshMessage updateMessage, IJobDispat // So we can re-use them with trimmed runner package, if possible. // This process is best effort, if we can't use trimmed runner package, // we will just go with the full package. - var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token); - _cloneAndCalculateContentHashTask = CloneAndCalculateAssetsHash(_dotnetRuntimeCloneDirectory, _externalsCloneDirectory, linkedTokenSource.Token); - Trace.Info($"An update is available."); _updateTrace.Enqueue($"RunnerPlatform: {_targetPackage.Platform}"); @@ -74,16 +60,6 @@ public async Task SelfUpdate(AgentRefreshMessage updateMessage, IJobDispat await UpdateRunnerUpdateStateAsync("Runner update in progress, do not shutdown runner."); await UpdateRunnerUpdateStateAsync($"Downloading {_targetPackage.Version} runner"); - linkedTokenSource.Cancel(); - try - { - await _cloneAndCalculateContentHashTask; - } - catch (Exception ex) - { - Trace.Info($"Ingore errors after cancelling cloning assets task: {ex}"); - } - await DownloadLatestRunner(token, updateMessage.TargetVersion); Trace.Info($"Download latest runner and unzip into runner root."); @@ -144,39 +120,6 @@ public async Task SelfUpdate(AgentRefreshMessage updateMessage, IJobDispat } } - private async Task UpdateNeeded(string targetVersion, CancellationToken token) - { - // when talk to old version server, always prefer latest package. - // old server won't send target version as part of update message. - if (string.IsNullOrEmpty(targetVersion)) - { - var packages = await _runnerServer.GetPackagesAsync(_packageType, _platform, 1, true, token); - if (packages == null || packages.Count == 0) - { - Trace.Info($"There is no package for {_packageType} and {_platform}."); - return false; - } - - _targetPackage = packages.FirstOrDefault(); - } - else - { - _targetPackage = await _runnerServer.GetPackageAsync(_packageType, _platform, targetVersion, true, token); - if (_targetPackage == null) - { - Trace.Info($"There is no package for {_packageType} and {_platform} with version {targetVersion}."); - return false; - } - } - - Trace.Info($"Version '{_targetPackage.Version}' of '{_targetPackage.Type}' package available in server."); - PackageVersion serverVersion = new(_targetPackage.Version); - Trace.Info($"Current running runner version is {BuildConstants.RunnerPackage.Version}"); - PackageVersion runnerVersion = new(BuildConstants.RunnerPackage.Version); - - return serverVersion.CompareTo(runnerVersion) > 0; - } - /// /// _work /// \_update @@ -275,74 +218,6 @@ private async Task DownloadLatestRunner(CancellationToken token, string targetVe } } - var trimmedPackageRestoreTasks = new List>(); - if (!fallbackToFullPackage) - { - // Skip restoring externals and runtime if we are going to fullback to the full package. - if (externalsTrimmed) - { - trimmedPackageRestoreTasks.Add(RestoreTrimmedExternals(latestRunnerDirectory, token)); - } - if (runtimeTrimmed) - { - trimmedPackageRestoreTasks.Add(RestoreTrimmedDotnetRuntime(latestRunnerDirectory, token)); - } - } - - if (trimmedPackageRestoreTasks.Count > 0) - { - var restoreResults = await Task.WhenAll(trimmedPackageRestoreTasks); - if (restoreResults.Any(x => x == false)) - { - // if any of the restore failed, fallback to full package. - fallbackToFullPackage = true; - } - } - - if (fallbackToFullPackage) - { - Trace.Error("Something wrong with the trimmed runner package, failback to use the full package for runner updates."); - _updateTrace.Enqueue($"FallbackToFullPackage: {fallbackToFullPackage}"); - - IOUtil.DeleteDirectory(latestRunnerDirectory, token); - Directory.CreateDirectory(latestRunnerDirectory); - - packageDownloadUrl = _targetPackage.DownloadUrl; - packageHashValue = _targetPackage.HashValue; - _updateTrace.Enqueue($"DownloadUrl: {packageDownloadUrl}"); - - try - { - archiveFile = await DownLoadRunner(latestRunnerDirectory, packageDownloadUrl, packageHashValue, token); - - if (string.IsNullOrEmpty(archiveFile)) - { - throw new TaskCanceledException($"Runner package '{packageDownloadUrl}' failed after {Constants.RunnerDownloadRetryMaxAttempts} download attempts"); - } - - await ValidateRunnerHash(archiveFile, packageHashValue); - - await ExtractRunnerPackage(archiveFile, latestRunnerDirectory, token); - } - finally - { - try - { - // delete .zip file - if (!string.IsNullOrEmpty(archiveFile) && File.Exists(archiveFile)) - { - Trace.Verbose("Deleting latest runner package zip: {0}", archiveFile); - IOUtil.DeleteFile(archiveFile); - } - } - catch (Exception ex) - { - //it is not critical if we fail to delete the .zip file - Trace.Warning("Failed to delete runner package zip '{0}'. Exception: {1}", archiveFile, ex); - } - } - } - await CopyLatestRunnerToRoot(latestRunnerDirectory, token); } @@ -724,71 +599,6 @@ private async Task UpdateRunnerUpdateStateAsync(string currentState) } } - private async Task RestoreTrimmedExternals(string downloadDirectory, CancellationToken token) - { - // Copy the current runner's externals if we are using a externals trimmed package - // Execute the node.js to make sure the copied externals is working. - var stopWatch = Stopwatch.StartNew(); - try - { - Trace.Info($"Copy {_externalsCloneDirectory} to {Path.Combine(downloadDirectory, Constants.Path.ExternalsDirectory)}."); - IOUtil.CopyDirectory(_externalsCloneDirectory, Path.Combine(downloadDirectory, Constants.Path.ExternalsDirectory), token); - - // try run node.js to see if current node.js works fine after copy over to new location. - var nodeVersions = NodeUtil.BuiltInNodeVersions; - foreach (var nodeVersion in nodeVersions) - { - var newNodeBinary = Path.Combine(downloadDirectory, Constants.Path.ExternalsDirectory, nodeVersion, "bin", $"node{IOUtil.ExeExtension}"); - if (File.Exists(newNodeBinary)) - { - using (var p = HostContext.CreateService()) - { - var outputs = ""; - p.ErrorDataReceived += (_, data) => - { - if (!string.IsNullOrEmpty(data.Data)) - { - Trace.Error(data.Data); - } - }; - p.OutputDataReceived += (_, data) => - { - if (!string.IsNullOrEmpty(data.Data)) - { - Trace.Info(data.Data); - outputs = data.Data; - } - }; - var exitCode = await p.ExecuteAsync(HostContext.GetDirectory(WellKnownDirectory.Root), newNodeBinary, $"-e \"console.log('{nameof(RestoreTrimmedExternals)}')\"", null, token); - if (exitCode != 0) - { - Trace.Error($"{newNodeBinary} -e \"console.log()\" failed with exit code {exitCode}"); - return false; - } - - if (!string.Equals(outputs, nameof(RestoreTrimmedExternals), StringComparison.OrdinalIgnoreCase)) - { - Trace.Error($"{newNodeBinary} -e \"console.log()\" did not output expected content."); - return false; - } - } - } - } - - return true; - } - catch (Exception ex) - { - Trace.Error($"Fail to restore externals for trimmed package: {ex}"); - return false; - } - finally - { - stopWatch.Stop(); - _updateTrace.Enqueue($"{nameof(RestoreTrimmedExternals)}Time: {stopWatch.ElapsedMilliseconds}ms"); - } - } - private async Task HashFiles(string fileFolder, CancellationToken token) { Trace.Info($"Calculating hash for {fileFolder}"); From 69e070701fd8a8a054a10f70acfeb1aa898039b7 Mon Sep 17 00:00:00 2001 From: Luke Tomlinson Date: Fri, 13 Oct 2023 08:36:35 -0700 Subject: [PATCH 04/33] working --- src/Runner.Listener/Runner.cs | 12 ++++- src/Runner.Listener/SelfUpdaterV2.cs | 48 +++---------------- .../DTWebApi/WebApi/AgentRefreshMessage.cs | 2 +- .../DTWebApi/WebApi/RunnerRefreshMessage.cs | 40 +++++++++++++++- 4 files changed, 58 insertions(+), 44 deletions(-) diff --git a/src/Runner.Listener/Runner.cs b/src/Runner.Listener/Runner.cs index ac23b873b42..811db0b6d8d 100644 --- a/src/Runner.Listener/Runner.cs +++ b/src/Runner.Listener/Runner.cs @@ -498,7 +498,17 @@ private async Task RunAsync(RunnerSettings settings, bool runOnce = false) { var selfUpdater = new SelfUpdaterV2(); selfUpdater.Initialize(HostContext); - selfUpdater = selfUpdater.SelfUpdate(brokerRunnerUpdateMessage, jobDispatcher, false, HostContext.RunnerShutdownToken); + var refreshMessage = new AgentRefreshMessage(brokerRunnerUpdateMessage.RunnerId, brokerRunnerUpdateMessage.TargetVersion, TimeSpan.FromSeconds(brokerRunnerUpdateMessage.TimeoutInSeconds)) + { + PackageMetadata = new PackageMetadata() + { + DownloadUrl = brokerRunnerUpdateMessage.Package?.DownloadUrl, + HashValue = brokerRunnerUpdateMessage.Package?.HashValue, + Platform = brokerRunnerUpdateMessage.Package?.Platform, + Version = new PackageVersion(brokerRunnerUpdateMessage.TargetVersion) + } + }; + selfUpdateTask = selfUpdater.SelfUpdate(refreshMessage, jobDispatcher, false, HostContext.RunnerShutdownToken); } else { diff --git a/src/Runner.Listener/SelfUpdaterV2.cs b/src/Runner.Listener/SelfUpdaterV2.cs index 174783e8b03..b7a420146cd 100644 --- a/src/Runner.Listener/SelfUpdaterV2.cs +++ b/src/Runner.Listener/SelfUpdaterV2.cs @@ -44,6 +44,7 @@ public override void Initialize(IHostContext hostContext) public async Task SelfUpdate(AgentRefreshMessage updateMessage, IJobDispatcher jobDispatcher, bool restartInteractiveRunner, CancellationToken token) { + _targetPackage = updateMessage.PackageMetadata; Busy = true; try { @@ -57,14 +58,14 @@ public async Task SelfUpdate(AgentRefreshMessage updateMessage, IJobDispat _updateTrace.Enqueue($"RunnerPlatform: {_targetPackage.Platform}"); // Print console line that warn user not shutdown runner. - await UpdateRunnerUpdateStateAsync("Runner update in progress, do not shutdown runner."); - await UpdateRunnerUpdateStateAsync($"Downloading {_targetPackage.Version} runner"); + _terminal.WriteLine("Runner update in progress, do not shutdown runner."); + _terminal.WriteLine($"Downloading {_targetPackage.Version} runner"); await DownloadLatestRunner(token, updateMessage.TargetVersion); Trace.Info($"Download latest runner and unzip into runner root."); // wait till all running job finish - await UpdateRunnerUpdateStateAsync("Waiting for current job finish running."); + _terminal.WriteLine("Waiting for current job finish running."); await jobDispatcher.WaitAsync(token); Trace.Info($"All running job has exited."); @@ -77,7 +78,7 @@ public async Task SelfUpdate(AgentRefreshMessage updateMessage, IJobDispat stopWatch.Stop(); // generate update script from template _updateTrace.Enqueue($"DeleteRunnerBackupTime: {stopWatch.ElapsedMilliseconds}ms"); - await UpdateRunnerUpdateStateAsync("Generate and execute update script."); + _terminal.WriteLine("Generate and execute update script."); string updateScript = GenerateUpdateScript(restartInteractiveRunner); Trace.Info($"Generate update script into: {updateScript}"); @@ -104,7 +105,7 @@ public async Task SelfUpdate(AgentRefreshMessage updateMessage, IJobDispat totalUpdateTime.Stop(); _updateTrace.Enqueue($"TotalUpdateTime: {totalUpdateTime.ElapsedMilliseconds}ms"); - await UpdateRunnerUpdateStateAsync("Runner will exit shortly for update, should be back online within 10 seconds."); + _terminal.WriteLine("Runner will exit shortly for update, should be back online within 10 seconds."); return true; } @@ -115,7 +116,7 @@ public async Task SelfUpdate(AgentRefreshMessage updateMessage, IJobDispat } finally { - await UpdateRunnerUpdateStateAsync("Runner update process finished."); + _terminal.WriteLine("Runner update process finished."); Busy = false; } } @@ -564,41 +565,6 @@ private string GenerateUpdateScript(bool restartInteractiveRunner) return updateScript; } - private async Task UpdateRunnerUpdateStateAsync(string currentState) - { - _terminal.WriteLine(currentState); - - var traces = new List(); - while (_updateTrace.TryDequeue(out var trace)) - { - traces.Add(trace); - } - - if (traces.Count > 0) - { - foreach (var trace in traces) - { - Trace.Info(trace); - } - } - - try - { - await _runnerServer.UpdateAgentUpdateStateAsync(_poolId, _agentId, currentState, string.Join(Environment.NewLine, traces)); - _updateTrace.Clear(); - } - catch (VssResourceNotFoundException) - { - // ignore VssResourceNotFoundException, this exception means the runner is configured against a old server that doesn't support report runner update detail. - Trace.Info($"Catch VssResourceNotFoundException during report update state, ignore this error for backcompat."); - } - catch (Exception ex) - { - Trace.Error(ex); - Trace.Info($"Catch exception during report update state, ignore this error and continue auto-update."); - } - } - private async Task HashFiles(string fileFolder, CancellationToken token) { Trace.Info($"Calculating hash for {fileFolder}"); diff --git a/src/Sdk/DTWebApi/WebApi/AgentRefreshMessage.cs b/src/Sdk/DTWebApi/WebApi/AgentRefreshMessage.cs index fdaa5d15870..04e385aebe3 100644 --- a/src/Sdk/DTWebApi/WebApi/AgentRefreshMessage.cs +++ b/src/Sdk/DTWebApi/WebApi/AgentRefreshMessage.cs @@ -48,7 +48,7 @@ public String TargetVersion } [DataMember] - public Dictionary Package + public PackageMetadata PackageMetadata { get; set; diff --git a/src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs b/src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs index 18ff14e3ad8..c875cd29fb5 100644 --- a/src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs +++ b/src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs @@ -1,5 +1,6 @@ using Newtonsoft.Json; using System; +using System.Collections.Generic; using System.Runtime.Serialization; @@ -10,6 +11,37 @@ public sealed class RunnerRefreshMessage { public static readonly String MessageType = "RunnerRefresh"; + [DataContract] + public sealed class BrokerPackageMetadata + { + [JsonConstructor] + internal BrokerPackageMetadata() + { + } + + + [DataMember(Name = "download_url")] + public string DownloadUrl + { + get; + set; + } + + [DataMember(Name = "sha256_checksum")] + public string HashValue + { + get; + set; + } + + [DataMember(Name = "os")] + public string Platform + { + get; + set; + } + } + [JsonConstructor] internal RunnerRefreshMessage() { @@ -46,6 +78,12 @@ public String TargetVersion private set; } - [DataMember] PackageMetadata Package { get; set; } + + [DataMember] + public BrokerPackageMetadata Package + { + get; + set; + } } } From 5b58e67f7b9b420701848167bfdf3ea82cd96226 Mon Sep 17 00:00:00 2001 From: Luke Tomlinson Date: Fri, 13 Oct 2023 11:34:41 -0700 Subject: [PATCH 05/33] wip l0 --- src/Test/L0/Listener/SelfUpdaterV2L0.cs | 292 ++++++++++++++++++++++++ 1 file changed, 292 insertions(+) create mode 100644 src/Test/L0/Listener/SelfUpdaterV2L0.cs diff --git a/src/Test/L0/Listener/SelfUpdaterV2L0.cs b/src/Test/L0/Listener/SelfUpdaterV2L0.cs new file mode 100644 index 00000000000..6f2b4305531 --- /dev/null +++ b/src/Test/L0/Listener/SelfUpdaterV2L0.cs @@ -0,0 +1,292 @@ +#if !(OS_WINDOWS && ARM64) +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Reflection; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; +using GitHub.DistributedTask.WebApi; +using GitHub.Runner.Listener; +using GitHub.Runner.Sdk; +using Moq; +using Xunit; + +namespace GitHub.Runner.Common.Tests.Listener +{ + public sealed class SelfUpdaterV2L0 + { + private Mock _runnerServer; + private Mock _term; + private Mock _configStore; + private Mock _jobDispatcher; + private AgentRefreshMessage _refreshMessage = new(1, "2.999.0"); + private List _trimmedPackages = new(); + +#if !OS_WINDOWS + private string _packageUrl = null; +#else + private string _packageUrl = null; +#endif + public SelfUpdaterV2L0() + { + _runnerServer = new Mock(); + _term = new Mock(); + _configStore = new Mock(); + _jobDispatcher = new Mock(); + _configStore.Setup(x => x.GetSettings()).Returns(new RunnerSettings() { PoolId = 1, AgentId = 1 }); + + Environment.SetEnvironmentVariable("_GITHUB_ACTION_EXECUTE_UPDATE_SCRIPT", "1"); + } + + private async Task FetchLatestRunner() + { + var latestVersion = ""; + var httpClientHandler = new HttpClientHandler(); + httpClientHandler.AllowAutoRedirect = false; + using (var client = new HttpClient(httpClientHandler)) + { + var response = await client.SendAsync(new HttpRequestMessage(HttpMethod.Get, "https://github.com/actions/runner/releases/latest")); + if (response.StatusCode == System.Net.HttpStatusCode.Redirect) + { + var redirectUrl = response.Headers.Location.ToString(); + Regex regex = new(@"/runner/releases/tag/v(?\d+\.\d+\.\d+)"); + var match = regex.Match(redirectUrl); + if (match.Success) + { + latestVersion = match.Groups["version"].Value; + +#if !OS_WINDOWS + _packageUrl = $"https://github.com/actions/runner/releases/download/v{latestVersion}/actions-runner-{BuildConstants.RunnerPackage.PackageName}-{latestVersion}.tar.gz"; +#else + _packageUrl = $"https://github.com/actions/runner/releases/download/v{latestVersion}/actions-runner-{BuildConstants.RunnerPackage.PackageName}-{latestVersion}.zip"; +#endif + } + else + { + throw new Exception("The latest runner version could not be determined so a download URL could not be generated for it. Please check the location header of the redirect response of 'https://github.com/actions/runner/releases/latest'"); + } + } + } + } + + [Fact] + [Trait("Level", "L0")] + [Trait("Category", "Runner")] + public async void TestSelfUpdateAsync() + { + try + { + await FetchLatestRunner(); + Assert.NotNull(_packageUrl); + Assert.NotNull(_trimmedPackages); + Environment.SetEnvironmentVariable("RUNNER_L0_OVERRIDEBINDIR", Path.GetFullPath(Path.Combine(TestUtil.GetSrcPath(), "..", "_layout", "bin"))); + using (var hc = new TestHostContext(this)) + { + hc.GetTrace().Info(_packageUrl); + hc.GetTrace().Info(StringUtil.ConvertToJson(_trimmedPackages)); + + //Arrange + var updater = new Runner.Listener.SelfUpdaterV2(); + hc.SetSingleton(_term.Object); + hc.SetSingleton(_runnerServer.Object); + hc.SetSingleton(_configStore.Object); + hc.SetSingleton(new HttpClientHandlerFactory()); + + var p1 = new ProcessInvokerWrapper(); + p1.Initialize(hc); + var p2 = new ProcessInvokerWrapper(); + p2.Initialize(hc); + var p3 = new ProcessInvokerWrapper(); + p3.Initialize(hc); + hc.EnqueueInstance(p1); + hc.EnqueueInstance(p2); + hc.EnqueueInstance(p3); + updater.Initialize(hc); + + try + { + var packageMetadata = new PackageMetadata() { Platform = BuildConstants.RunnerPackage.PackageName, Version = new PackageVersion("2.999.0"), DownloadUrl = _packageUrl }; + var message = new AgentRefreshMessage(1, "2.999.0") + { + PackageMetadata = packageMetadata + }; + + var result = await updater.SelfUpdate(message, _jobDispatcher.Object, true, hc.RunnerShutdownToken); + Assert.True(result); + Assert.True(Directory.Exists(Path.Combine(hc.GetDirectory(WellKnownDirectory.Root), "bin.2.999.0"))); + Assert.True(Directory.Exists(Path.Combine(hc.GetDirectory(WellKnownDirectory.Root), "externals.2.999.0"))); + } + finally + { + IOUtil.DeleteDirectory(Path.Combine(hc.GetDirectory(WellKnownDirectory.Root), "bin.2.999.0"), CancellationToken.None); + IOUtil.DeleteDirectory(Path.Combine(hc.GetDirectory(WellKnownDirectory.Root), "externals.2.999.0"), CancellationToken.None); + } + } + } + finally + { + Environment.SetEnvironmentVariable("RUNNER_L0_OVERRIDEBINDIR", null); + } + } + + // [Fact] + // [Trait("Level", "L0")] + // [Trait("Category", "Runner")] + // public async void TestSelfUpdateAsync_NoUpdateOnOldVersion() + // { + // try + // { + // await FetchLatestRunner(); + // Assert.NotNull(_packageUrl); + // Assert.NotNull(_trimmedPackages); + // Environment.SetEnvironmentVariable("RUNNER_L0_OVERRIDEBINDIR", Path.GetFullPath(Path.Combine(TestUtil.GetSrcPath(), "..", "_layout", "bin"))); + // using (var hc = new TestHostContext(this)) + // { + // hc.GetTrace().Info(_packageUrl); + // hc.GetTrace().Info(StringUtil.ConvertToJson(_trimmedPackages)); + + // //Arrange + // var updater = new Runner.Listener.SelfUpdaterV2(); + // hc.SetSingleton(_term.Object); + // hc.SetSingleton(_runnerServer.Object); + // hc.SetSingleton(_configStore.Object); + + // var p1 = new ProcessInvokerWrapper(); + // p1.Initialize(hc); + // var p2 = new ProcessInvokerWrapper(); + // p2.Initialize(hc); + // var p3 = new ProcessInvokerWrapper(); + // p3.Initialize(hc); + // hc.EnqueueInstance(p1); + // hc.EnqueueInstance(p2); + // hc.EnqueueInstance(p3); + // updater.Initialize(hc); + + // var packageMetadata = new PackageMetadata() { Platform = BuildConstants.RunnerPackage.PackageName, Version = new PackageVersion("2.200.0"), DownloadUrl = _packageUrl }; + + // var message = new AgentRefreshMessage(1, "2.200.0") + // { + // PackageMetadata = packageMetadata + // }; + // var result = await updater.SelfUpdate(message, _jobDispatcher.Object, true, hc.RunnerShutdownToken); + // Assert.False(result); + // } + // } + // finally + // { + // Environment.SetEnvironmentVariable("RUNNER_L0_OVERRIDEBINDIR", null); + // } + // } + + // [Fact] + // [Trait("Level", "L0")] + // [Trait("Category", "Runner")] + // public async void TestSelfUpdateAsync_DownloadRetry() + // { + // try + // { + // await FetchLatestRunner(); + // Assert.NotNull(_packageUrl); + // Assert.NotNull(_trimmedPackages); + // Environment.SetEnvironmentVariable("RUNNER_L0_OVERRIDEBINDIR", Path.GetFullPath(Path.Combine(TestUtil.GetSrcPath(), "..", "_layout", "bin"))); + // using (var hc = new TestHostContext(this)) + // { + // hc.GetTrace().Info(_packageUrl); + // hc.GetTrace().Info(StringUtil.ConvertToJson(_trimmedPackages)); + + // //Arrange + // var updater = new Runner.Listener.SelfUpdaterV2(); + // hc.SetSingleton(_term.Object); + // hc.SetSingleton(_runnerServer.Object); + // hc.SetSingleton(_configStore.Object); + // hc.SetSingleton(new HttpClientHandlerFactory()); + + // _runnerServer.Setup(x => x.GetPackageAsync("agent", BuildConstants.RunnerPackage.PackageName, "2.999.0", true, It.IsAny())) + // .Returns(Task.FromResult(new PackageMetadata() { Platform = BuildConstants.RunnerPackage.PackageName, Version = new PackageVersion("2.999.0"), DownloadUrl = $"https://github.com/actions/runner/notexists" })); + + // var p1 = new ProcessInvokerWrapper(); + // p1.Initialize(hc); + // var p2 = new ProcessInvokerWrapper(); + // p2.Initialize(hc); + // var p3 = new ProcessInvokerWrapper(); + // p3.Initialize(hc); + // hc.EnqueueInstance(p1); + // hc.EnqueueInstance(p2); + // hc.EnqueueInstance(p3); + // updater.Initialize(hc); + + // var packageMetadata = new PackageMetadata() { Platform = BuildConstants.RunnerPackage.PackageName, Version = new PackageVersion("2.999.0"), DownloadUrl = _packageUrl }; + // var message = new AgentRefreshMessage(1, "2.999.0") + // { + // PackageMetadata = packageMetadata + // }; + + // var ex = await Assert.ThrowsAsync(() => updater.SelfUpdate(message, _jobDispatcher.Object, true, hc.RunnerShutdownToken)); + // Assert.Contains($"failed after {Constants.RunnerDownloadRetryMaxAttempts} download attempts", ex.Message); + // } + // } + // finally + // { + // Environment.SetEnvironmentVariable("RUNNER_L0_OVERRIDEBINDIR", null); + // } + // } + + // [Fact] + // [Trait("Level", "L0")] + // [Trait("Category", "Runner")] + // public async void TestSelfUpdateAsync_ValidateHash() + // { + // try + // { + // await FetchLatestRunner(); + // Assert.NotNull(_packageUrl); + // Assert.NotNull(_trimmedPackages); + // Environment.SetEnvironmentVariable("RUNNER_L0_OVERRIDEBINDIR", Path.GetFullPath(Path.Combine(TestUtil.GetSrcPath(), "..", "_layout", "bin"))); + // using (var hc = new TestHostContext(this)) + // { + // hc.GetTrace().Info(_packageUrl); + // hc.GetTrace().Info(StringUtil.ConvertToJson(_trimmedPackages)); + + // //Arrange + // var updater = new Runner.Listener.SelfUpdaterV2(); + // hc.SetSingleton(_term.Object); + // hc.SetSingleton(_runnerServer.Object); + // hc.SetSingleton(_configStore.Object); + // hc.SetSingleton(new HttpClientHandlerFactory()); + + // _runnerServer.Setup(x => x.GetPackageAsync("agent", BuildConstants.RunnerPackage.PackageName, "2.999.0", true, It.IsAny())) + // .Returns(Task.FromResult(new PackageMetadata() { Platform = BuildConstants.RunnerPackage.PackageName, Version = new PackageVersion("2.999.0"), DownloadUrl = _packageUrl, HashValue = "bad_hash" })); + + // var p1 = new ProcessInvokerWrapper(); + // p1.Initialize(hc); + // var p2 = new ProcessInvokerWrapper(); + // p2.Initialize(hc); + // var p3 = new ProcessInvokerWrapper(); + // p3.Initialize(hc); + // hc.EnqueueInstance(p1); + // hc.EnqueueInstance(p2); + // hc.EnqueueInstance(p3); + // updater.Initialize(hc); + + // var packageMetadata = new PackageMetadata() { Platform = BuildConstants.RunnerPackage.PackageName, Version = new PackageVersion("2.999.0"), DownloadUrl = _packageUrl }; + // var message = new AgentRefreshMessage(1, "2.999.0") + // { + // PackageMetadata = packageMetadata + // }; + + + // var ex = await Assert.ThrowsAsync(() => updater.SelfUpdate(_refreshMessage, _jobDispatcher.Object, true, hc.RunnerShutdownToken)); + // Assert.Contains("did not match expected Runner Hash", ex.Message); + // } + // } + // finally + // { + // Environment.SetEnvironmentVariable("RUNNER_L0_OVERRIDEBINDIR", null); + // } + // } + } +} +#endif From bca63dcc18490e7d5eba72f17af30b281cdebc6a Mon Sep 17 00:00:00 2001 From: Luke Tomlinson Date: Fri, 13 Oct 2023 11:54:10 -0700 Subject: [PATCH 06/33] Add back another test --- src/Test/L0/Listener/SelfUpdaterV2L0.cs | 142 ++++++++---------------- 1 file changed, 45 insertions(+), 97 deletions(-) diff --git a/src/Test/L0/Listener/SelfUpdaterV2L0.cs b/src/Test/L0/Listener/SelfUpdaterV2L0.cs index 6f2b4305531..6477d35a98e 100644 --- a/src/Test/L0/Listener/SelfUpdaterV2L0.cs +++ b/src/Test/L0/Listener/SelfUpdaterV2L0.cs @@ -132,107 +132,55 @@ public async void TestSelfUpdateAsync() } } - // [Fact] - // [Trait("Level", "L0")] - // [Trait("Category", "Runner")] - // public async void TestSelfUpdateAsync_NoUpdateOnOldVersion() - // { - // try - // { - // await FetchLatestRunner(); - // Assert.NotNull(_packageUrl); - // Assert.NotNull(_trimmedPackages); - // Environment.SetEnvironmentVariable("RUNNER_L0_OVERRIDEBINDIR", Path.GetFullPath(Path.Combine(TestUtil.GetSrcPath(), "..", "_layout", "bin"))); - // using (var hc = new TestHostContext(this)) - // { - // hc.GetTrace().Info(_packageUrl); - // hc.GetTrace().Info(StringUtil.ConvertToJson(_trimmedPackages)); - - // //Arrange - // var updater = new Runner.Listener.SelfUpdaterV2(); - // hc.SetSingleton(_term.Object); - // hc.SetSingleton(_runnerServer.Object); - // hc.SetSingleton(_configStore.Object); - - // var p1 = new ProcessInvokerWrapper(); - // p1.Initialize(hc); - // var p2 = new ProcessInvokerWrapper(); - // p2.Initialize(hc); - // var p3 = new ProcessInvokerWrapper(); - // p3.Initialize(hc); - // hc.EnqueueInstance(p1); - // hc.EnqueueInstance(p2); - // hc.EnqueueInstance(p3); - // updater.Initialize(hc); - - // var packageMetadata = new PackageMetadata() { Platform = BuildConstants.RunnerPackage.PackageName, Version = new PackageVersion("2.200.0"), DownloadUrl = _packageUrl }; - - // var message = new AgentRefreshMessage(1, "2.200.0") - // { - // PackageMetadata = packageMetadata - // }; - // var result = await updater.SelfUpdate(message, _jobDispatcher.Object, true, hc.RunnerShutdownToken); - // Assert.False(result); - // } - // } - // finally - // { - // Environment.SetEnvironmentVariable("RUNNER_L0_OVERRIDEBINDIR", null); - // } - // } - - // [Fact] - // [Trait("Level", "L0")] - // [Trait("Category", "Runner")] - // public async void TestSelfUpdateAsync_DownloadRetry() - // { - // try - // { - // await FetchLatestRunner(); - // Assert.NotNull(_packageUrl); - // Assert.NotNull(_trimmedPackages); - // Environment.SetEnvironmentVariable("RUNNER_L0_OVERRIDEBINDIR", Path.GetFullPath(Path.Combine(TestUtil.GetSrcPath(), "..", "_layout", "bin"))); - // using (var hc = new TestHostContext(this)) - // { - // hc.GetTrace().Info(_packageUrl); - // hc.GetTrace().Info(StringUtil.ConvertToJson(_trimmedPackages)); - - // //Arrange - // var updater = new Runner.Listener.SelfUpdaterV2(); - // hc.SetSingleton(_term.Object); - // hc.SetSingleton(_runnerServer.Object); - // hc.SetSingleton(_configStore.Object); - // hc.SetSingleton(new HttpClientHandlerFactory()); + [Fact] + [Trait("Level", "L0")] + [Trait("Category", "Runner")] + public async void TestSelfUpdateAsync_DownloadRetry() + { + try + { + await FetchLatestRunner(); + Assert.NotNull(_packageUrl); + Assert.NotNull(_trimmedPackages); + Environment.SetEnvironmentVariable("RUNNER_L0_OVERRIDEBINDIR", Path.GetFullPath(Path.Combine(TestUtil.GetSrcPath(), "..", "_layout", "bin"))); + using (var hc = new TestHostContext(this)) + { + hc.GetTrace().Info(_packageUrl); + hc.GetTrace().Info(StringUtil.ConvertToJson(_trimmedPackages)); - // _runnerServer.Setup(x => x.GetPackageAsync("agent", BuildConstants.RunnerPackage.PackageName, "2.999.0", true, It.IsAny())) - // .Returns(Task.FromResult(new PackageMetadata() { Platform = BuildConstants.RunnerPackage.PackageName, Version = new PackageVersion("2.999.0"), DownloadUrl = $"https://github.com/actions/runner/notexists" })); + //Arrange + var updater = new Runner.Listener.SelfUpdaterV2(); + hc.SetSingleton(_term.Object); + hc.SetSingleton(_runnerServer.Object); + hc.SetSingleton(_configStore.Object); + hc.SetSingleton(new HttpClientHandlerFactory()); - // var p1 = new ProcessInvokerWrapper(); - // p1.Initialize(hc); - // var p2 = new ProcessInvokerWrapper(); - // p2.Initialize(hc); - // var p3 = new ProcessInvokerWrapper(); - // p3.Initialize(hc); - // hc.EnqueueInstance(p1); - // hc.EnqueueInstance(p2); - // hc.EnqueueInstance(p3); - // updater.Initialize(hc); + var p1 = new ProcessInvokerWrapper(); + p1.Initialize(hc); + var p2 = new ProcessInvokerWrapper(); + p2.Initialize(hc); + var p3 = new ProcessInvokerWrapper(); + p3.Initialize(hc); + hc.EnqueueInstance(p1); + hc.EnqueueInstance(p2); + hc.EnqueueInstance(p3); + updater.Initialize(hc); - // var packageMetadata = new PackageMetadata() { Platform = BuildConstants.RunnerPackage.PackageName, Version = new PackageVersion("2.999.0"), DownloadUrl = _packageUrl }; - // var message = new AgentRefreshMessage(1, "2.999.0") - // { - // PackageMetadata = packageMetadata - // }; + var packageMetadata = new PackageMetadata() { Platform = BuildConstants.RunnerPackage.PackageName, Version = new PackageVersion("2.999.0"), DownloadUrl = "https://github.com/actions/runner/notthere" }; + var message = new AgentRefreshMessage(1, "2.999.0") + { + PackageMetadata = packageMetadata + }; - // var ex = await Assert.ThrowsAsync(() => updater.SelfUpdate(message, _jobDispatcher.Object, true, hc.RunnerShutdownToken)); - // Assert.Contains($"failed after {Constants.RunnerDownloadRetryMaxAttempts} download attempts", ex.Message); - // } - // } - // finally - // { - // Environment.SetEnvironmentVariable("RUNNER_L0_OVERRIDEBINDIR", null); - // } - // } + var ex = await Assert.ThrowsAsync(() => updater.SelfUpdate(message, _jobDispatcher.Object, true, hc.RunnerShutdownToken)); + Assert.Contains($"failed after {Constants.RunnerDownloadRetryMaxAttempts} download attempts", ex.Message); + } + } + finally + { + Environment.SetEnvironmentVariable("RUNNER_L0_OVERRIDEBINDIR", null); + } + } // [Fact] // [Trait("Level", "L0")] From 3fa3934296584b5a7c66fa2a787e6491e24b8b42 Mon Sep 17 00:00:00 2001 From: Luke Tomlinson Date: Fri, 13 Oct 2023 12:00:23 -0700 Subject: [PATCH 07/33] Add back another test --- src/Test/L0/Listener/SelfUpdaterV2L0.cs | 103 ++++++++++++------------ 1 file changed, 50 insertions(+), 53 deletions(-) diff --git a/src/Test/L0/Listener/SelfUpdaterV2L0.cs b/src/Test/L0/Listener/SelfUpdaterV2L0.cs index 6477d35a98e..582d3f767e4 100644 --- a/src/Test/L0/Listener/SelfUpdaterV2L0.cs +++ b/src/Test/L0/Listener/SelfUpdaterV2L0.cs @@ -182,59 +182,56 @@ public async void TestSelfUpdateAsync_DownloadRetry() } } - // [Fact] - // [Trait("Level", "L0")] - // [Trait("Category", "Runner")] - // public async void TestSelfUpdateAsync_ValidateHash() - // { - // try - // { - // await FetchLatestRunner(); - // Assert.NotNull(_packageUrl); - // Assert.NotNull(_trimmedPackages); - // Environment.SetEnvironmentVariable("RUNNER_L0_OVERRIDEBINDIR", Path.GetFullPath(Path.Combine(TestUtil.GetSrcPath(), "..", "_layout", "bin"))); - // using (var hc = new TestHostContext(this)) - // { - // hc.GetTrace().Info(_packageUrl); - // hc.GetTrace().Info(StringUtil.ConvertToJson(_trimmedPackages)); - - // //Arrange - // var updater = new Runner.Listener.SelfUpdaterV2(); - // hc.SetSingleton(_term.Object); - // hc.SetSingleton(_runnerServer.Object); - // hc.SetSingleton(_configStore.Object); - // hc.SetSingleton(new HttpClientHandlerFactory()); - - // _runnerServer.Setup(x => x.GetPackageAsync("agent", BuildConstants.RunnerPackage.PackageName, "2.999.0", true, It.IsAny())) - // .Returns(Task.FromResult(new PackageMetadata() { Platform = BuildConstants.RunnerPackage.PackageName, Version = new PackageVersion("2.999.0"), DownloadUrl = _packageUrl, HashValue = "bad_hash" })); - - // var p1 = new ProcessInvokerWrapper(); - // p1.Initialize(hc); - // var p2 = new ProcessInvokerWrapper(); - // p2.Initialize(hc); - // var p3 = new ProcessInvokerWrapper(); - // p3.Initialize(hc); - // hc.EnqueueInstance(p1); - // hc.EnqueueInstance(p2); - // hc.EnqueueInstance(p3); - // updater.Initialize(hc); - - // var packageMetadata = new PackageMetadata() { Platform = BuildConstants.RunnerPackage.PackageName, Version = new PackageVersion("2.999.0"), DownloadUrl = _packageUrl }; - // var message = new AgentRefreshMessage(1, "2.999.0") - // { - // PackageMetadata = packageMetadata - // }; - - - // var ex = await Assert.ThrowsAsync(() => updater.SelfUpdate(_refreshMessage, _jobDispatcher.Object, true, hc.RunnerShutdownToken)); - // Assert.Contains("did not match expected Runner Hash", ex.Message); - // } - // } - // finally - // { - // Environment.SetEnvironmentVariable("RUNNER_L0_OVERRIDEBINDIR", null); - // } - // } + [Fact] + [Trait("Level", "L0")] + [Trait("Category", "Runner")] + public async void TestSelfUpdateAsync_ValidateHash() + { + try + { + await FetchLatestRunner(); + Assert.NotNull(_packageUrl); + Assert.NotNull(_trimmedPackages); + Environment.SetEnvironmentVariable("RUNNER_L0_OVERRIDEBINDIR", Path.GetFullPath(Path.Combine(TestUtil.GetSrcPath(), "..", "_layout", "bin"))); + using (var hc = new TestHostContext(this)) + { + hc.GetTrace().Info(_packageUrl); + hc.GetTrace().Info(StringUtil.ConvertToJson(_trimmedPackages)); + + //Arrange + var updater = new Runner.Listener.SelfUpdaterV2(); + hc.SetSingleton(_term.Object); + hc.SetSingleton(_runnerServer.Object); + hc.SetSingleton(_configStore.Object); + hc.SetSingleton(new HttpClientHandlerFactory()); + + var p1 = new ProcessInvokerWrapper(); + p1.Initialize(hc); + var p2 = new ProcessInvokerWrapper(); + p2.Initialize(hc); + var p3 = new ProcessInvokerWrapper(); + p3.Initialize(hc); + hc.EnqueueInstance(p1); + hc.EnqueueInstance(p2); + hc.EnqueueInstance(p3); + updater.Initialize(hc); + + var packageMetadata = new PackageMetadata() { Platform = BuildConstants.RunnerPackage.PackageName, Version = new PackageVersion("2.999.0"), DownloadUrl = _packageUrl, HashValue = "bad_hash" }; + var message = new AgentRefreshMessage(1, "2.999.0") + { + PackageMetadata = packageMetadata + }; + + + var ex = await Assert.ThrowsAsync(() => updater.SelfUpdate(message, _jobDispatcher.Object, true, hc.RunnerShutdownToken)); + Assert.Contains("did not match expected Runner Hash", ex.Message); + } + } + finally + { + Environment.SetEnvironmentVariable("RUNNER_L0_OVERRIDEBINDIR", null); + } + } } } #endif From 0d8ee424dcf97ea736bf06db81bda5792e3351bd Mon Sep 17 00:00:00 2001 From: Luke Tomlinson Date: Fri, 13 Oct 2023 12:03:11 -0700 Subject: [PATCH 08/33] revert runnerversion changes --- src/runnerversion | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/runnerversion b/src/runnerversion index 847c4973f62..cb7d746ef3d 100644 --- a/src/runnerversion +++ b/src/runnerversion @@ -1 +1 @@ -2.307.0 +2.310.0 From 5114f751689e1991d7490d921eac15960c88c1e2 Mon Sep 17 00:00:00 2001 From: Luke Tomlinson Date: Mon, 16 Oct 2023 07:00:33 -0700 Subject: [PATCH 09/33] format --- src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs b/src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs index c875cd29fb5..530843a564e 100644 --- a/src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs +++ b/src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs @@ -78,12 +78,12 @@ public String TargetVersion private set; } - - [DataMember] - public BrokerPackageMetadata Package - { - get; - set; + + [DataMember] + public BrokerPackageMetadata Package + { + get; + set; } } } From 97748195b7c12bbf2dd52c0139277c392b53fc6c Mon Sep 17 00:00:00 2001 From: Luke Tomlinson Date: Mon, 16 Oct 2023 07:05:22 -0700 Subject: [PATCH 10/33] more formatting --- src/Runner.Listener/Runner.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Runner.Listener/Runner.cs b/src/Runner.Listener/Runner.cs index 811db0b6d8d..96e00fa0685 100644 --- a/src/Runner.Listener/Runner.cs +++ b/src/Runner.Listener/Runner.cs @@ -500,9 +500,9 @@ private async Task RunAsync(RunnerSettings settings, bool runOnce = false) selfUpdater.Initialize(HostContext); var refreshMessage = new AgentRefreshMessage(brokerRunnerUpdateMessage.RunnerId, brokerRunnerUpdateMessage.TargetVersion, TimeSpan.FromSeconds(brokerRunnerUpdateMessage.TimeoutInSeconds)) { - PackageMetadata = new PackageMetadata() - { - DownloadUrl = brokerRunnerUpdateMessage.Package?.DownloadUrl, + PackageMetadata = new PackageMetadata() + { + DownloadUrl = brokerRunnerUpdateMessage.Package?.DownloadUrl, HashValue = brokerRunnerUpdateMessage.Package?.HashValue, Platform = brokerRunnerUpdateMessage.Package?.Platform, Version = new PackageVersion(brokerRunnerUpdateMessage.TargetVersion) @@ -515,7 +515,7 @@ private async Task RunAsync(RunnerSettings settings, bool runOnce = false) var selfUpdater = HostContext.GetService(); selfUpdateTask = selfUpdater.SelfUpdate(runnerUpdateMessage, jobDispatcher, false, HostContext.RunnerShutdownToken); } - + Trace.Info("Refresh message received, kick-off selfupdate background process."); } else From 19dae984cbaf00c067679a234db34c4a01bfa60e Mon Sep 17 00:00:00 2001 From: Luke Tomlinson Date: Mon, 16 Oct 2023 10:58:06 -0700 Subject: [PATCH 11/33] whitespace --- src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs b/src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs index 530843a564e..541199ac4ca 100644 --- a/src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs +++ b/src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs @@ -19,7 +19,6 @@ internal BrokerPackageMetadata() { } - [DataMember(Name = "download_url")] public string DownloadUrl { @@ -46,7 +45,6 @@ public string Platform internal RunnerRefreshMessage() { } - public RunnerRefreshMessage( ulong runnerId, String targetVersion, @@ -78,7 +76,6 @@ public String TargetVersion private set; } - [DataMember] public BrokerPackageMetadata Package { From 1836e5c4b8c37814bd578cc9e9a5c338dfcd7148 Mon Sep 17 00:00:00 2001 From: Luke Tomlinson Date: Tue, 17 Oct 2023 11:54:20 -0700 Subject: [PATCH 12/33] pr feedback --- src/Runner.Listener/Runner.cs | 14 ++++++-------- src/Runner.Listener/SelfUpdaterV2.cs | 3 +++ src/Sdk/DTWebApi/WebApi/AgentRefreshMessage.cs | 6 ++++-- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/Runner.Listener/Runner.cs b/src/Runner.Listener/Runner.cs index 96e00fa0685..b2caef86162 100644 --- a/src/Runner.Listener/Runner.cs +++ b/src/Runner.Listener/Runner.cs @@ -498,16 +498,14 @@ private async Task RunAsync(RunnerSettings settings, bool runOnce = false) { var selfUpdater = new SelfUpdaterV2(); selfUpdater.Initialize(HostContext); - var refreshMessage = new AgentRefreshMessage(brokerRunnerUpdateMessage.RunnerId, brokerRunnerUpdateMessage.TargetVersion, TimeSpan.FromSeconds(brokerRunnerUpdateMessage.TimeoutInSeconds)) + var packageMetadata = new PackageMetadata() { - PackageMetadata = new PackageMetadata() - { - DownloadUrl = brokerRunnerUpdateMessage.Package?.DownloadUrl, - HashValue = brokerRunnerUpdateMessage.Package?.HashValue, - Platform = brokerRunnerUpdateMessage.Package?.Platform, - Version = new PackageVersion(brokerRunnerUpdateMessage.TargetVersion) - } + DownloadUrl = brokerRunnerUpdateMessage.Package?.DownloadUrl, + HashValue = brokerRunnerUpdateMessage.Package?.HashValue, + Platform = brokerRunnerUpdateMessage.Package?.Platform, + Version = new PackageVersion(brokerRunnerUpdateMessage.TargetVersion) }; + var refreshMessage = new AgentRefreshMessage(brokerRunnerUpdateMessage.RunnerId, brokerRunnerUpdateMessage.TargetVersion, TimeSpan.FromSeconds(brokerRunnerUpdateMessage.TimeoutInSeconds), packageMetadata); selfUpdateTask = selfUpdater.SelfUpdate(refreshMessage, jobDispatcher, false, HostContext.RunnerShutdownToken); } else diff --git a/src/Runner.Listener/SelfUpdaterV2.cs b/src/Runner.Listener/SelfUpdaterV2.cs index b7a420146cd..5b824205acb 100644 --- a/src/Runner.Listener/SelfUpdaterV2.cs +++ b/src/Runner.Listener/SelfUpdaterV2.cs @@ -19,6 +19,9 @@ namespace GitHub.Runner.Listener { + // This class is a fork of SelfUpdater.cs and is intended to only be used for the + // new self-update flow where the PackageMetadata is sent in the message directly. + // Forking the class prevents us from accidentally breaking the old flow while it's still in production public class SelfUpdaterV2 : RunnerService, ISelfUpdater { private static string _platform = BuildConstants.RunnerPackage.PackageName; diff --git a/src/Sdk/DTWebApi/WebApi/AgentRefreshMessage.cs b/src/Sdk/DTWebApi/WebApi/AgentRefreshMessage.cs index 04e385aebe3..396a0b8c5d6 100644 --- a/src/Sdk/DTWebApi/WebApi/AgentRefreshMessage.cs +++ b/src/Sdk/DTWebApi/WebApi/AgentRefreshMessage.cs @@ -19,11 +19,13 @@ internal AgentRefreshMessage() public AgentRefreshMessage( ulong agentId, String targetVersion, - TimeSpan? timeout = null) + TimeSpan? timeout = null, + PackageMetadata packageMetadata = null) { this.AgentId = agentId; this.Timeout = timeout ?? TimeSpan.FromMinutes(60); this.TargetVersion = targetVersion; + this.PackageMetadata = packageMetadata; } [DataMember] @@ -51,7 +53,7 @@ public String TargetVersion public PackageMetadata PackageMetadata { get; - set; + private set; } } } From 1ab062136e3fe326d97cab5512ac77937c92dc9d Mon Sep 17 00:00:00 2001 From: Luke Tomlinson Date: Tue, 17 Oct 2023 13:30:47 -0700 Subject: [PATCH 13/33] fix test --- src/Test/L0/Listener/SelfUpdaterV2L0.cs | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/src/Test/L0/Listener/SelfUpdaterV2L0.cs b/src/Test/L0/Listener/SelfUpdaterV2L0.cs index 582d3f767e4..cc7df14286f 100644 --- a/src/Test/L0/Listener/SelfUpdaterV2L0.cs +++ b/src/Test/L0/Listener/SelfUpdaterV2L0.cs @@ -109,10 +109,7 @@ public async void TestSelfUpdateAsync() try { var packageMetadata = new PackageMetadata() { Platform = BuildConstants.RunnerPackage.PackageName, Version = new PackageVersion("2.999.0"), DownloadUrl = _packageUrl }; - var message = new AgentRefreshMessage(1, "2.999.0") - { - PackageMetadata = packageMetadata - }; + var message = new AgentRefreshMessage(1, "2.999.0", packageMetadata: packageMetadata); var result = await updater.SelfUpdate(message, _jobDispatcher.Object, true, hc.RunnerShutdownToken); Assert.True(result); @@ -167,10 +164,7 @@ public async void TestSelfUpdateAsync_DownloadRetry() updater.Initialize(hc); var packageMetadata = new PackageMetadata() { Platform = BuildConstants.RunnerPackage.PackageName, Version = new PackageVersion("2.999.0"), DownloadUrl = "https://github.com/actions/runner/notthere" }; - var message = new AgentRefreshMessage(1, "2.999.0") - { - PackageMetadata = packageMetadata - }; + var message = new AgentRefreshMessage(1, "2.999.0", packageMetadata: packageMetadata); var ex = await Assert.ThrowsAsync(() => updater.SelfUpdate(message, _jobDispatcher.Object, true, hc.RunnerShutdownToken)); Assert.Contains($"failed after {Constants.RunnerDownloadRetryMaxAttempts} download attempts", ex.Message); @@ -217,11 +211,7 @@ public async void TestSelfUpdateAsync_ValidateHash() updater.Initialize(hc); var packageMetadata = new PackageMetadata() { Platform = BuildConstants.RunnerPackage.PackageName, Version = new PackageVersion("2.999.0"), DownloadUrl = _packageUrl, HashValue = "bad_hash" }; - var message = new AgentRefreshMessage(1, "2.999.0") - { - PackageMetadata = packageMetadata - }; - + var message = new AgentRefreshMessage(1, "2.999.0", packageMetadata: packageMetadata); var ex = await Assert.ThrowsAsync(() => updater.SelfUpdate(message, _jobDispatcher.Object, true, hc.RunnerShutdownToken)); Assert.Contains("did not match expected Runner Hash", ex.Message); From 54e80cc154ece0dd914513279b672c30b85f9781 Mon Sep 17 00:00:00 2001 From: Luke Tomlinson Date: Wed, 1 Nov 2023 14:12:49 -0700 Subject: [PATCH 14/33] Simplify and use RunnerRefreshMessage --- src/Runner.Listener/Runner.cs | 10 +------ src/Runner.Listener/SelfUpdaterV2.cs | 17 ++++++----- .../DTWebApi/WebApi/RunnerRefreshMessage.cs | 13 +++++++- src/Test/L0/Listener/SelfUpdaterV2L0.cs | 30 +++++++++++++++---- 4 files changed, 47 insertions(+), 23 deletions(-) diff --git a/src/Runner.Listener/Runner.cs b/src/Runner.Listener/Runner.cs index b2caef86162..563becd9c6d 100644 --- a/src/Runner.Listener/Runner.cs +++ b/src/Runner.Listener/Runner.cs @@ -498,15 +498,7 @@ private async Task RunAsync(RunnerSettings settings, bool runOnce = false) { var selfUpdater = new SelfUpdaterV2(); selfUpdater.Initialize(HostContext); - var packageMetadata = new PackageMetadata() - { - DownloadUrl = brokerRunnerUpdateMessage.Package?.DownloadUrl, - HashValue = brokerRunnerUpdateMessage.Package?.HashValue, - Platform = brokerRunnerUpdateMessage.Package?.Platform, - Version = new PackageVersion(brokerRunnerUpdateMessage.TargetVersion) - }; - var refreshMessage = new AgentRefreshMessage(brokerRunnerUpdateMessage.RunnerId, brokerRunnerUpdateMessage.TargetVersion, TimeSpan.FromSeconds(brokerRunnerUpdateMessage.TimeoutInSeconds), packageMetadata); - selfUpdateTask = selfUpdater.SelfUpdate(refreshMessage, jobDispatcher, false, HostContext.RunnerShutdownToken); + selfUpdateTask = selfUpdater.SelfUpdate(brokerRunnerUpdateMessage, jobDispatcher, false, HostContext.RunnerShutdownToken); } else { diff --git a/src/Runner.Listener/SelfUpdaterV2.cs b/src/Runner.Listener/SelfUpdaterV2.cs index 5b824205acb..c5a96c3e57d 100644 --- a/src/Runner.Listener/SelfUpdaterV2.cs +++ b/src/Runner.Listener/SelfUpdaterV2.cs @@ -22,7 +22,14 @@ namespace GitHub.Runner.Listener // This class is a fork of SelfUpdater.cs and is intended to only be used for the // new self-update flow where the PackageMetadata is sent in the message directly. // Forking the class prevents us from accidentally breaking the old flow while it's still in production - public class SelfUpdaterV2 : RunnerService, ISelfUpdater + + [ServiceLocator(Default = typeof(SelfUpdaterV2))] + public interface ISelfUpdaterV2 : IRunnerService + { + bool Busy { get; } + Task SelfUpdate(RunnerRefreshMessage updateMessage, IJobDispatcher jobDispatcher, bool restartInteractiveRunner, CancellationToken token); + } + public class SelfUpdaterV2 : RunnerService, ISelfUpdaterV2 { private static string _platform = BuildConstants.RunnerPackage.PackageName; private PackageMetadata _targetPackage; @@ -45,18 +52,14 @@ public override void Initialize(IHostContext hostContext) _agentId = settings.AgentId; } - public async Task SelfUpdate(AgentRefreshMessage updateMessage, IJobDispatcher jobDispatcher, bool restartInteractiveRunner, CancellationToken token) + public async Task SelfUpdate(RunnerRefreshMessage updateMessage, IJobDispatcher jobDispatcher, bool restartInteractiveRunner, CancellationToken token) { - _targetPackage = updateMessage.PackageMetadata; + _targetPackage = updateMessage.GetPackageMetadata(); Busy = true; try { var totalUpdateTime = Stopwatch.StartNew(); - // Copy dotnet runtime and externals of current runner to a temp folder - // So we can re-use them with trimmed runner package, if possible. - // This process is best effort, if we can't use trimmed runner package, - // we will just go with the full package. Trace.Info($"An update is available."); _updateTrace.Enqueue($"RunnerPlatform: {_targetPackage.Platform}"); diff --git a/src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs b/src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs index 541199ac4ca..99a423461ed 100644 --- a/src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs +++ b/src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs @@ -77,10 +77,21 @@ public String TargetVersion } [DataMember] - public BrokerPackageMetadata Package + public BrokerPackageMetadata PackageMetadata { get; set; } + + public PackageMetadata GetPackageMetadata() + { + return new PackageMetadata() + { + DownloadUrl = this.PackageMetadata?.DownloadUrl, + HashValue = this.PackageMetadata?.HashValue, + Platform = this.PackageMetadata?.Platform, + Version = new PackageVersion(this.TargetVersion) + }; + } } } diff --git a/src/Test/L0/Listener/SelfUpdaterV2L0.cs b/src/Test/L0/Listener/SelfUpdaterV2L0.cs index cc7df14286f..1d5ba05c4f5 100644 --- a/src/Test/L0/Listener/SelfUpdaterV2L0.cs +++ b/src/Test/L0/Listener/SelfUpdaterV2L0.cs @@ -108,8 +108,14 @@ public async void TestSelfUpdateAsync() try { - var packageMetadata = new PackageMetadata() { Platform = BuildConstants.RunnerPackage.PackageName, Version = new PackageVersion("2.999.0"), DownloadUrl = _packageUrl }; - var message = new AgentRefreshMessage(1, "2.999.0", packageMetadata: packageMetadata); + var message = new RunnerRefreshMessage(1, "2.999.0") + { + PackageMetadata = new RunnerRefreshMessage.BrokerPackageMetadata() + { + Platform = BuildConstants.RunnerPackage.PackageName, + DownloadUrl = _packageUrl + } + }; var result = await updater.SelfUpdate(message, _jobDispatcher.Object, true, hc.RunnerShutdownToken); Assert.True(result); @@ -163,8 +169,14 @@ public async void TestSelfUpdateAsync_DownloadRetry() hc.EnqueueInstance(p3); updater.Initialize(hc); - var packageMetadata = new PackageMetadata() { Platform = BuildConstants.RunnerPackage.PackageName, Version = new PackageVersion("2.999.0"), DownloadUrl = "https://github.com/actions/runner/notthere" }; - var message = new AgentRefreshMessage(1, "2.999.0", packageMetadata: packageMetadata); + var message = new RunnerRefreshMessage(1, "2.999.0") + { + PackageMetadata = new RunnerRefreshMessage.BrokerPackageMetadata() + { + Platform = BuildConstants.RunnerPackage.PackageName, + DownloadUrl = _packageUrl + } + }; var ex = await Assert.ThrowsAsync(() => updater.SelfUpdate(message, _jobDispatcher.Object, true, hc.RunnerShutdownToken)); Assert.Contains($"failed after {Constants.RunnerDownloadRetryMaxAttempts} download attempts", ex.Message); @@ -210,8 +222,14 @@ public async void TestSelfUpdateAsync_ValidateHash() hc.EnqueueInstance(p3); updater.Initialize(hc); - var packageMetadata = new PackageMetadata() { Platform = BuildConstants.RunnerPackage.PackageName, Version = new PackageVersion("2.999.0"), DownloadUrl = _packageUrl, HashValue = "bad_hash" }; - var message = new AgentRefreshMessage(1, "2.999.0", packageMetadata: packageMetadata); + var message = new RunnerRefreshMessage(1, "2.999.0") + { + PackageMetadata = new RunnerRefreshMessage.BrokerPackageMetadata() + { + Platform = BuildConstants.RunnerPackage.PackageName, + DownloadUrl = _packageUrl + } + }; var ex = await Assert.ThrowsAsync(() => updater.SelfUpdate(message, _jobDispatcher.Object, true, hc.RunnerShutdownToken)); Assert.Contains("did not match expected Runner Hash", ex.Message); From 95875b122d9ffa2ab6a86cde7169db8623dfc47b Mon Sep 17 00:00:00 2001 From: Luke Tomlinson Date: Wed, 1 Nov 2023 14:14:59 -0700 Subject: [PATCH 15/33] . --- src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs b/src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs index 99a423461ed..91986a23f41 100644 --- a/src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs +++ b/src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs @@ -77,7 +77,7 @@ public String TargetVersion } [DataMember] - public BrokerPackageMetadata PackageMetadata + public BrokerPackageMetadata Package { get; set; @@ -87,9 +87,9 @@ public PackageMetadata GetPackageMetadata() { return new PackageMetadata() { - DownloadUrl = this.PackageMetadata?.DownloadUrl, - HashValue = this.PackageMetadata?.HashValue, - Platform = this.PackageMetadata?.Platform, + DownloadUrl = this.Package?.DownloadUrl, + HashValue = this.Package?.HashValue, + Platform = this.Package?.Platform, Version = new PackageVersion(this.TargetVersion) }; } From 76c5c6eb522bb574b3af90a645afd158037b33c4 Mon Sep 17 00:00:00 2001 From: Luke Tomlinson Date: Wed, 1 Nov 2023 14:25:04 -0700 Subject: [PATCH 16/33] format --- .../DTWebApi/WebApi/RunnerRefreshMessage.cs | 18 +++++++++--------- src/Test/L0/Listener/SelfUpdaterV2L0.cs | 6 +++--- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs b/src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs index 91986a23f41..8ad46e13a6e 100644 --- a/src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs +++ b/src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs @@ -83,15 +83,15 @@ public BrokerPackageMetadata Package set; } - public PackageMetadata GetPackageMetadata() + public PackageMetadata GetPackageMetadata() + { + return new PackageMetadata() { - return new PackageMetadata() - { - DownloadUrl = this.Package?.DownloadUrl, - HashValue = this.Package?.HashValue, - Platform = this.Package?.Platform, - Version = new PackageVersion(this.TargetVersion) - }; - } + DownloadUrl = this.Package?.DownloadUrl, + HashValue = this.Package?.HashValue, + Platform = this.Package?.Platform, + Version = new PackageVersion(this.TargetVersion) + }; + } } } diff --git a/src/Test/L0/Listener/SelfUpdaterV2L0.cs b/src/Test/L0/Listener/SelfUpdaterV2L0.cs index 1d5ba05c4f5..53e4aa21c8c 100644 --- a/src/Test/L0/Listener/SelfUpdaterV2L0.cs +++ b/src/Test/L0/Listener/SelfUpdaterV2L0.cs @@ -110,7 +110,7 @@ public async void TestSelfUpdateAsync() { var message = new RunnerRefreshMessage(1, "2.999.0") { - PackageMetadata = new RunnerRefreshMessage.BrokerPackageMetadata() + Package = new RunnerRefreshMessage.BrokerPackageMetadata() { Platform = BuildConstants.RunnerPackage.PackageName, DownloadUrl = _packageUrl @@ -171,7 +171,7 @@ public async void TestSelfUpdateAsync_DownloadRetry() var message = new RunnerRefreshMessage(1, "2.999.0") { - PackageMetadata = new RunnerRefreshMessage.BrokerPackageMetadata() + Package = new RunnerRefreshMessage.BrokerPackageMetadata() { Platform = BuildConstants.RunnerPackage.PackageName, DownloadUrl = _packageUrl @@ -224,7 +224,7 @@ public async void TestSelfUpdateAsync_ValidateHash() var message = new RunnerRefreshMessage(1, "2.999.0") { - PackageMetadata = new RunnerRefreshMessage.BrokerPackageMetadata() + Package = new RunnerRefreshMessage.BrokerPackageMetadata() { Platform = BuildConstants.RunnerPackage.PackageName, DownloadUrl = _packageUrl From 73a92749e7b5b73100f6a4bea5b35aeb26de250b Mon Sep 17 00:00:00 2001 From: Luke Tomlinson Date: Wed, 1 Nov 2023 14:39:06 -0700 Subject: [PATCH 17/33] fix tests --- src/Test/L0/Listener/SelfUpdaterV2L0.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Test/L0/Listener/SelfUpdaterV2L0.cs b/src/Test/L0/Listener/SelfUpdaterV2L0.cs index 53e4aa21c8c..6839e22e112 100644 --- a/src/Test/L0/Listener/SelfUpdaterV2L0.cs +++ b/src/Test/L0/Listener/SelfUpdaterV2L0.cs @@ -174,7 +174,7 @@ public async void TestSelfUpdateAsync_DownloadRetry() Package = new RunnerRefreshMessage.BrokerPackageMetadata() { Platform = BuildConstants.RunnerPackage.PackageName, - DownloadUrl = _packageUrl + DownloadUrl = "https://github.com/actions/runner/notexists" } }; @@ -227,7 +227,8 @@ public async void TestSelfUpdateAsync_ValidateHash() Package = new RunnerRefreshMessage.BrokerPackageMetadata() { Platform = BuildConstants.RunnerPackage.PackageName, - DownloadUrl = _packageUrl + DownloadUrl = _packageUrl, + HashValue = "badhash" } }; From 9e7d45b995b5acc30d9b7c095ad450692c107083 Mon Sep 17 00:00:00 2001 From: Luke Tomlinson Date: Thu, 2 Nov 2023 17:51:59 -0700 Subject: [PATCH 18/33] fix --- src/Runner.Listener/Runner.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Runner.Listener/Runner.cs b/src/Runner.Listener/Runner.cs index 563becd9c6d..b096ee33220 100644 --- a/src/Runner.Listener/Runner.cs +++ b/src/Runner.Listener/Runner.cs @@ -496,8 +496,7 @@ private async Task RunAsync(RunnerSettings settings, bool runOnce = false) #endif if (brokerRunnerUpdateMessage != null) { - var selfUpdater = new SelfUpdaterV2(); - selfUpdater.Initialize(HostContext); + var selfUpdater = HostContext.GetService(); selfUpdateTask = selfUpdater.SelfUpdate(brokerRunnerUpdateMessage, jobDispatcher, false, HostContext.RunnerShutdownToken); } else From 0e5052efbae4459abcc75d472233d53c744bda42 Mon Sep 17 00:00:00 2001 From: Luke Tomlinson Date: Thu, 2 Nov 2023 17:57:25 -0700 Subject: [PATCH 19/33] feedback --- src/Runner.Listener/Runner.cs | 44 +++++++++---------- .../DTWebApi/WebApi/AgentRefreshMessage.cs | 4 +- 2 files changed, 23 insertions(+), 25 deletions(-) diff --git a/src/Runner.Listener/Runner.cs b/src/Runner.Listener/Runner.cs index b096ee33220..2e363a3d153 100644 --- a/src/Runner.Listener/Runner.cs +++ b/src/Runner.Listener/Runner.cs @@ -457,22 +457,13 @@ private async Task RunAsync(RunnerSettings settings, bool runOnce = false) message = await getNextMessage; //get next message HostContext.WritePerfCounter($"MessageReceived_{message.MessageType}"); - if (string.Equals(message.MessageType, AgentRefreshMessage.MessageType, StringComparison.OrdinalIgnoreCase) || - string.Equals(message.MessageType, RunnerRefreshMessage.MessageType, StringComparison.OrdinalIgnoreCase)) + if (string.Equals(message.MessageType, AgentRefreshMessage.MessageType, StringComparison.OrdinalIgnoreCase)) { if (autoUpdateInProgress == false) { autoUpdateInProgress = true; - AgentRefreshMessage runnerUpdateMessage = null; - RunnerRefreshMessage brokerRunnerUpdateMessage = null; - if (string.Equals(message.MessageType, AgentRefreshMessage.MessageType, StringComparison.OrdinalIgnoreCase)) - { - runnerUpdateMessage = JsonUtility.FromString(message.Body); - } - else - { - brokerRunnerUpdateMessage = JsonUtility.FromString(message.Body); - } + AgentRefreshMessage runnerUpdateMessage = JsonUtility.FromString(message.Body); + #if DEBUG // Can mock the update for testing if (StringUtil.ConvertToBoolean(Environment.GetEnvironmentVariable("GITHUB_ACTIONS_RUNNER_IS_MOCK_UPDATE"))) @@ -494,16 +485,24 @@ private async Task RunAsync(RunnerSettings settings, bool runOnce = false) } } #endif - if (brokerRunnerUpdateMessage != null) - { - var selfUpdater = HostContext.GetService(); - selfUpdateTask = selfUpdater.SelfUpdate(brokerRunnerUpdateMessage, jobDispatcher, false, HostContext.RunnerShutdownToken); - } - else - { - var selfUpdater = HostContext.GetService(); - selfUpdateTask = selfUpdater.SelfUpdate(runnerUpdateMessage, jobDispatcher, false, HostContext.RunnerShutdownToken); - } + var selfUpdater = HostContext.GetService(); + selfUpdateTask = selfUpdater.SelfUpdate(runnerUpdateMessage, jobDispatcher, false, HostContext.RunnerShutdownToken); + Trace.Info("Refresh message received, kick-off selfupdate background process."); + } + else + { + Trace.Info("Refresh message received, skip autoupdate since a previous autoupdate is already running."); + } + } + else if (string.Equals(message.MessageType, RunnerRefreshMessage.MessageType, StringComparison.OrdinalIgnoreCase)) + { + if (autoUpdateInProgress == false) + { + autoUpdateInProgress = true; + RunnerRefreshMessage brokerRunnerUpdateMessage = JsonUtility.FromString(message.Body); + + var selfUpdater = HostContext.GetService(); + selfUpdateTask = selfUpdater.SelfUpdate(brokerRunnerUpdateMessage, jobDispatcher, false, HostContext.RunnerShutdownToken); Trace.Info("Refresh message received, kick-off selfupdate background process."); } @@ -511,6 +510,7 @@ private async Task RunAsync(RunnerSettings settings, bool runOnce = false) { Trace.Info("Refresh message received, skip autoupdate since a previous autoupdate is already running."); } + } else if (string.Equals(message.MessageType, JobRequestMessageTypes.PipelineAgentJobRequest, StringComparison.OrdinalIgnoreCase)) { diff --git a/src/Sdk/DTWebApi/WebApi/AgentRefreshMessage.cs b/src/Sdk/DTWebApi/WebApi/AgentRefreshMessage.cs index 396a0b8c5d6..0a20999caa2 100644 --- a/src/Sdk/DTWebApi/WebApi/AgentRefreshMessage.cs +++ b/src/Sdk/DTWebApi/WebApi/AgentRefreshMessage.cs @@ -19,13 +19,11 @@ internal AgentRefreshMessage() public AgentRefreshMessage( ulong agentId, String targetVersion, - TimeSpan? timeout = null, - PackageMetadata packageMetadata = null) + TimeSpan? timeout = null) { this.AgentId = agentId; this.Timeout = timeout ?? TimeSpan.FromMinutes(60); this.TargetVersion = targetVersion; - this.PackageMetadata = packageMetadata; } [DataMember] From 607a26a71a066c1bbfb22a5685b3a6b1dca1f7a8 Mon Sep 17 00:00:00 2001 From: Luke Tomlinson Date: Thu, 2 Nov 2023 17:58:44 -0700 Subject: [PATCH 20/33] format --- src/Runner.Listener/Runner.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Runner.Listener/Runner.cs b/src/Runner.Listener/Runner.cs index 2e363a3d153..f6e3e23df60 100644 --- a/src/Runner.Listener/Runner.cs +++ b/src/Runner.Listener/Runner.cs @@ -510,7 +510,6 @@ private async Task RunAsync(RunnerSettings settings, bool runOnce = false) { Trace.Info("Refresh message received, skip autoupdate since a previous autoupdate is already running."); } - } else if (string.Equals(message.MessageType, JobRequestMessageTypes.PipelineAgentJobRequest, StringComparison.OrdinalIgnoreCase)) { From b61c5ea34441393dac3ef3abb7a777af3d944d7b Mon Sep 17 00:00:00 2001 From: Luke Tomlinson Date: Thu, 2 Nov 2023 17:59:18 -0700 Subject: [PATCH 21/33] . --- src/Sdk/DTWebApi/WebApi/AgentRefreshMessage.cs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/Sdk/DTWebApi/WebApi/AgentRefreshMessage.cs b/src/Sdk/DTWebApi/WebApi/AgentRefreshMessage.cs index 0a20999caa2..9811622cc09 100644 --- a/src/Sdk/DTWebApi/WebApi/AgentRefreshMessage.cs +++ b/src/Sdk/DTWebApi/WebApi/AgentRefreshMessage.cs @@ -46,12 +46,5 @@ public String TargetVersion get; private set; } - - [DataMember] - public PackageMetadata PackageMetadata - { - get; - private set; - } } } From d5c084c3818a9ce83d5f9c3f9cfbc0ed0d0220b7 Mon Sep 17 00:00:00 2001 From: Luke Tomlinson Date: Thu, 2 Nov 2023 17:59:32 -0700 Subject: [PATCH 22/33] . --- src/Sdk/DTWebApi/WebApi/AgentRefreshMessage.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Sdk/DTWebApi/WebApi/AgentRefreshMessage.cs b/src/Sdk/DTWebApi/WebApi/AgentRefreshMessage.cs index 9811622cc09..70a825ec7f4 100644 --- a/src/Sdk/DTWebApi/WebApi/AgentRefreshMessage.cs +++ b/src/Sdk/DTWebApi/WebApi/AgentRefreshMessage.cs @@ -1,6 +1,5 @@ using Newtonsoft.Json; using System; -using System.Collections.Generic; using System.Runtime.Serialization; From 87f8cb37a60192b822af079583cf10678aeebf95 Mon Sep 17 00:00:00 2001 From: Luke Tomlinson Date: Thu, 2 Nov 2023 18:05:23 -0700 Subject: [PATCH 23/33] feedback --- src/Runner.Listener/Runner.cs | 1 - .../DTWebApi/WebApi/BrokerPackageMetadata.cs | 38 +++++++++++++++++++ .../DTWebApi/WebApi/RunnerRefreshMessage.cs | 30 --------------- src/Test/L0/Listener/SelfUpdaterV2L0.cs | 6 +-- 4 files changed, 41 insertions(+), 34 deletions(-) create mode 100644 src/Sdk/DTWebApi/WebApi/BrokerPackageMetadata.cs diff --git a/src/Runner.Listener/Runner.cs b/src/Runner.Listener/Runner.cs index f6e3e23df60..0e05e8a1518 100644 --- a/src/Runner.Listener/Runner.cs +++ b/src/Runner.Listener/Runner.cs @@ -503,7 +503,6 @@ private async Task RunAsync(RunnerSettings settings, bool runOnce = false) var selfUpdater = HostContext.GetService(); selfUpdateTask = selfUpdater.SelfUpdate(brokerRunnerUpdateMessage, jobDispatcher, false, HostContext.RunnerShutdownToken); - Trace.Info("Refresh message received, kick-off selfupdate background process."); } else diff --git a/src/Sdk/DTWebApi/WebApi/BrokerPackageMetadata.cs b/src/Sdk/DTWebApi/WebApi/BrokerPackageMetadata.cs new file mode 100644 index 00000000000..2a1f3bea87f --- /dev/null +++ b/src/Sdk/DTWebApi/WebApi/BrokerPackageMetadata.cs @@ -0,0 +1,38 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; + + +namespace GitHub.DistributedTask.WebApi +{ + [DataContract] + public sealed class BrokerPackageMetadata + { + [JsonConstructor] + internal BrokerPackageMetadata() + { + } + + [DataMember(Name = "download_url")] + public string DownloadUrl + { + get; + set; + } + + [DataMember(Name = "sha256_checksum")] + public string HashValue + { + get; + set; + } + + [DataMember(Name = "os")] + public string Platform + { + get; + set; + } + } +} diff --git a/src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs b/src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs index 8ad46e13a6e..7ded3a20ed2 100644 --- a/src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs +++ b/src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs @@ -11,36 +11,6 @@ public sealed class RunnerRefreshMessage { public static readonly String MessageType = "RunnerRefresh"; - [DataContract] - public sealed class BrokerPackageMetadata - { - [JsonConstructor] - internal BrokerPackageMetadata() - { - } - - [DataMember(Name = "download_url")] - public string DownloadUrl - { - get; - set; - } - - [DataMember(Name = "sha256_checksum")] - public string HashValue - { - get; - set; - } - - [DataMember(Name = "os")] - public string Platform - { - get; - set; - } - } - [JsonConstructor] internal RunnerRefreshMessage() { diff --git a/src/Test/L0/Listener/SelfUpdaterV2L0.cs b/src/Test/L0/Listener/SelfUpdaterV2L0.cs index 6839e22e112..074fd55c764 100644 --- a/src/Test/L0/Listener/SelfUpdaterV2L0.cs +++ b/src/Test/L0/Listener/SelfUpdaterV2L0.cs @@ -110,7 +110,7 @@ public async void TestSelfUpdateAsync() { var message = new RunnerRefreshMessage(1, "2.999.0") { - Package = new RunnerRefreshMessage.BrokerPackageMetadata() + Package = new BrokerPackageMetadata() { Platform = BuildConstants.RunnerPackage.PackageName, DownloadUrl = _packageUrl @@ -171,7 +171,7 @@ public async void TestSelfUpdateAsync_DownloadRetry() var message = new RunnerRefreshMessage(1, "2.999.0") { - Package = new RunnerRefreshMessage.BrokerPackageMetadata() + Package = new BrokerPackageMetadata() { Platform = BuildConstants.RunnerPackage.PackageName, DownloadUrl = "https://github.com/actions/runner/notexists" @@ -224,7 +224,7 @@ public async void TestSelfUpdateAsync_ValidateHash() var message = new RunnerRefreshMessage(1, "2.999.0") { - Package = new RunnerRefreshMessage.BrokerPackageMetadata() + Package = new BrokerPackageMetadata() { Platform = BuildConstants.RunnerPackage.PackageName, DownloadUrl = _packageUrl, From 09a228acf926106edab4aeb500728c67f1ca875e Mon Sep 17 00:00:00 2001 From: Luke Tomlinson Date: Thu, 2 Nov 2023 18:07:03 -0700 Subject: [PATCH 24/33] format --- .../DTWebApi/WebApi/RunnerRefreshMessage.cs | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs b/src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs index 7ded3a20ed2..77b1a4b3ad9 100644 --- a/src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs +++ b/src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs @@ -17,26 +17,10 @@ internal RunnerRefreshMessage() } public RunnerRefreshMessage( ulong runnerId, - String targetVersion, - int? timeoutInSeconds = null) + String targetVersion) { this.RunnerId = runnerId; this.TimeoutInSeconds = timeoutInSeconds ?? TimeSpan.FromMinutes(60).Seconds; - this.TargetVersion = targetVersion; - } - - [DataMember] - public ulong RunnerId - { - get; - private set; - } - - [DataMember] - public int TimeoutInSeconds - { - get; - private set; } [DataMember] From 7249c05f1b7cabc00d658ac6ac24c0dc081537aa Mon Sep 17 00:00:00 2001 From: Luke Tomlinson Date: Thu, 2 Nov 2023 18:09:33 -0700 Subject: [PATCH 25/33] . --- src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs b/src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs index 77b1a4b3ad9..53cc5e9143b 100644 --- a/src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs +++ b/src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs @@ -15,12 +15,9 @@ public sealed class RunnerRefreshMessage internal RunnerRefreshMessage() { } - public RunnerRefreshMessage( - ulong runnerId, - String targetVersion) + public RunnerRefreshMessage(String targetVersion) { - this.RunnerId = runnerId; - this.TimeoutInSeconds = timeoutInSeconds ?? TimeSpan.FromMinutes(60).Seconds; + this.TargetVersion = targetVersion; } [DataMember] From 73d9aa734ccf57a9b33f958f8c581d47a9ac62f2 Mon Sep 17 00:00:00 2001 From: Luke Tomlinson Date: Thu, 2 Nov 2023 18:19:17 -0700 Subject: [PATCH 26/33] cleanup --- src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs | 2 +- src/Test/L0/Listener/SelfUpdaterV2L0.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs b/src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs index 53cc5e9143b..99c99e1ad13 100644 --- a/src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs +++ b/src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs @@ -17,7 +17,7 @@ internal RunnerRefreshMessage() } public RunnerRefreshMessage(String targetVersion) { - this.TargetVersion = targetVersion; + this.TargetVersion = targetVersion; } [DataMember] diff --git a/src/Test/L0/Listener/SelfUpdaterV2L0.cs b/src/Test/L0/Listener/SelfUpdaterV2L0.cs index 074fd55c764..b62fd08eba0 100644 --- a/src/Test/L0/Listener/SelfUpdaterV2L0.cs +++ b/src/Test/L0/Listener/SelfUpdaterV2L0.cs @@ -108,7 +108,7 @@ public async void TestSelfUpdateAsync() try { - var message = new RunnerRefreshMessage(1, "2.999.0") + var message = new RunnerRefreshMessage("2.999.0") { Package = new BrokerPackageMetadata() { @@ -169,7 +169,7 @@ public async void TestSelfUpdateAsync_DownloadRetry() hc.EnqueueInstance(p3); updater.Initialize(hc); - var message = new RunnerRefreshMessage(1, "2.999.0") + var message = new RunnerRefreshMessage("2.999.0") { Package = new BrokerPackageMetadata() { @@ -222,7 +222,7 @@ public async void TestSelfUpdateAsync_ValidateHash() hc.EnqueueInstance(p3); updater.Initialize(hc); - var message = new RunnerRefreshMessage(1, "2.999.0") + var message = new RunnerRefreshMessage("2.999.0") { Package = new BrokerPackageMetadata() { From 2dd14e92033c2bb93db39ad42ae175acee89de0a Mon Sep 17 00:00:00 2001 From: Luke Tomlinson Date: Tue, 7 Nov 2023 14:53:55 -0800 Subject: [PATCH 27/33] cleanup --- src/Runner.Listener/SelfUpdaterV2.cs | 50 ++++++++----------- .../DTWebApi/WebApi/BrokerPackageMetadata.cs | 38 -------------- .../DTWebApi/WebApi/RunnerRefreshMessage.cs | 29 ++++++----- src/Test/L0/Listener/SelfUpdaterV2L0.cs | 33 ++++++------ 4 files changed, 48 insertions(+), 102 deletions(-) delete mode 100644 src/Sdk/DTWebApi/WebApi/BrokerPackageMetadata.cs diff --git a/src/Runner.Listener/SelfUpdaterV2.cs b/src/Runner.Listener/SelfUpdaterV2.cs index c5a96c3e57d..e821cf567b0 100644 --- a/src/Runner.Listener/SelfUpdaterV2.cs +++ b/src/Runner.Listener/SelfUpdaterV2.cs @@ -32,7 +32,6 @@ public interface ISelfUpdaterV2 : IRunnerService public class SelfUpdaterV2 : RunnerService, ISelfUpdaterV2 { private static string _platform = BuildConstants.RunnerPackage.PackageName; - private PackageMetadata _targetPackage; private ITerminal _terminal; private IRunnerServer _runnerServer; private int _poolId; @@ -54,20 +53,19 @@ public override void Initialize(IHostContext hostContext) public async Task SelfUpdate(RunnerRefreshMessage updateMessage, IJobDispatcher jobDispatcher, bool restartInteractiveRunner, CancellationToken token) { - _targetPackage = updateMessage.GetPackageMetadata(); Busy = true; try { var totalUpdateTime = Stopwatch.StartNew(); Trace.Info($"An update is available."); - _updateTrace.Enqueue($"RunnerPlatform: {_targetPackage.Platform}"); + _updateTrace.Enqueue($"RunnerPlatform: {updateMessage.Platform}"); // Print console line that warn user not shutdown runner. _terminal.WriteLine("Runner update in progress, do not shutdown runner."); - _terminal.WriteLine($"Downloading {_targetPackage.Version} runner"); + _terminal.WriteLine($"Downloading {updateMessage.TargetVersion} runner"); - await DownloadLatestRunner(token, updateMessage.TargetVersion); + await DownloadLatestRunner(token, updateMessage.TargetVersion, updateMessage.DownloadUrl, updateMessage.HashValue, updateMessage.Platform); Trace.Info($"Download latest runner and unzip into runner root."); // wait till all running job finish @@ -79,14 +77,14 @@ public async Task SelfUpdate(RunnerRefreshMessage updateMessage, IJobDispa // We need to keep runner backup around for macOS until we fixed https://github.com/actions/runner/issues/743 // delete runner backup var stopWatch = Stopwatch.StartNew(); - DeletePreviousVersionRunnerBackup(token); + DeletePreviousVersionRunnerBackup(token, updateMessage.TargetVersion); Trace.Info($"Delete old version runner backup."); stopWatch.Stop(); // generate update script from template _updateTrace.Enqueue($"DeleteRunnerBackupTime: {stopWatch.ElapsedMilliseconds}ms"); _terminal.WriteLine("Generate and execute update script."); - string updateScript = GenerateUpdateScript(restartInteractiveRunner); + string updateScript = GenerateUpdateScript(restartInteractiveRunner, updateMessage.TargetVersion); Trace.Info($"Generate update script into: {updateScript}"); @@ -138,15 +136,13 @@ public async Task SelfUpdate(RunnerRefreshMessage updateMessage, IJobDispa /// /// /// - private async Task DownloadLatestRunner(CancellationToken token, string targetVersion) + private async Task DownloadLatestRunner(CancellationToken token, string targetVersion, string packageDownloadUrl, string packageHashValue, string targetPlatform) { string latestRunnerDirectory = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Work), Constants.Path.UpdateDirectory); IOUtil.DeleteDirectory(latestRunnerDirectory, token); Directory.CreateDirectory(latestRunnerDirectory); string archiveFile = null; - var packageDownloadUrl = _targetPackage.DownloadUrl; - var packageHashValue = _targetPackage.HashValue; // Only try trimmed package if sever sends them and we have calculated hash value of the current runtime/externals. _updateTrace.Enqueue($"DownloadUrl: {packageDownloadUrl}"); @@ -171,7 +167,7 @@ private async Task DownloadLatestRunner(CancellationToken token, string targetVe Debugger.Break(); } - if (_targetPackage.Platform.StartsWith("win")) + if (targetPlatform.StartsWith("win")) { archiveFile = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), $"runner{targetVersion}.zip"); } @@ -196,7 +192,7 @@ private async Task DownloadLatestRunner(CancellationToken token, string targetVe // archiveFile is not null only if we mocked it above if (string.IsNullOrEmpty(archiveFile)) { - archiveFile = await DownLoadRunner(latestRunnerDirectory, packageDownloadUrl, packageHashValue, token); + archiveFile = await DownLoadRunner(latestRunnerDirectory, packageDownloadUrl, packageHashValue, targetPlatform, token); if (string.IsNullOrEmpty(archiveFile)) { @@ -225,10 +221,10 @@ private async Task DownloadLatestRunner(CancellationToken token, string targetVe } } - await CopyLatestRunnerToRoot(latestRunnerDirectory, token); + await CopyLatestRunnerToRoot(latestRunnerDirectory, targetVersion, token); } - private async Task DownLoadRunner(string downloadDirectory, string packageDownloadUrl, string packageHashValue, CancellationToken token) + private async Task DownLoadRunner(string downloadDirectory, string packageDownloadUrl, string packageHashValue, string packagePlatform, CancellationToken token) { var stopWatch = Stopwatch.StartNew(); int runnerSuffix = 1; @@ -241,7 +237,7 @@ private async Task DownLoadRunner(string downloadDirectory, string packa // Generate an available package name, and do our best effort to clean up stale local zip files while (true) { - if (_targetPackage.Platform.StartsWith("win")) + if (packagePlatform.StartsWith("win")) { archiveFile = Path.Combine(downloadDirectory, $"runner{runnerSuffix}.zip"); } @@ -288,12 +284,6 @@ private async Task DownLoadRunner(string downloadDirectory, string packa //open zip stream in async mode using (HttpClient httpClient = new(HostContext.CreateHttpClientHandler())) { - if (!string.IsNullOrEmpty(_targetPackage.Token)) - { - Trace.Info($"Adding authorization token ({_targetPackage.Token.Length} chars)"); - httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _targetPackage.Token); - } - Trace.Info($"Downloading {packageDownloadUrl}"); using (FileStream fs = new(archiveFile, FileMode.Create, FileAccess.Write, FileShare.None, bufferSize: 4096, useAsync: true)) @@ -420,18 +410,18 @@ private async Task ExtractRunnerPackage(string archiveFile, string extractDirect _updateTrace.Enqueue($"PackageExtractTime: {stopWatch.ElapsedMilliseconds}ms"); } - private Task CopyLatestRunnerToRoot(string latestRunnerDirectory, CancellationToken token) + private Task CopyLatestRunnerToRoot(string latestRunnerDirectory, string targetVersion, CancellationToken token) { var stopWatch = Stopwatch.StartNew(); // copy latest runner into runner root folder // copy bin from _work/_update -> bin.version under root - string binVersionDir = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), $"{Constants.Path.BinDirectory}.{_targetPackage.Version}"); + string binVersionDir = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), $"{Constants.Path.BinDirectory}.{targetVersion}"); Directory.CreateDirectory(binVersionDir); Trace.Info($"Copy {Path.Combine(latestRunnerDirectory, Constants.Path.BinDirectory)} to {binVersionDir}."); IOUtil.CopyDirectory(Path.Combine(latestRunnerDirectory, Constants.Path.BinDirectory), binVersionDir, token); // copy externals from _work/_update -> externals.version under root - string externalsVersionDir = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), $"{Constants.Path.ExternalsDirectory}.{_targetPackage.Version}"); + string externalsVersionDir = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), $"{Constants.Path.ExternalsDirectory}.{targetVersion}"); Directory.CreateDirectory(externalsVersionDir); Trace.Info($"Copy {Path.Combine(latestRunnerDirectory, Constants.Path.ExternalsDirectory)} to {externalsVersionDir}."); IOUtil.CopyDirectory(Path.Combine(latestRunnerDirectory, Constants.Path.ExternalsDirectory), externalsVersionDir, token); @@ -454,7 +444,7 @@ private Task CopyLatestRunnerToRoot(string latestRunnerDirectory, CancellationTo return Task.CompletedTask; } - private void DeletePreviousVersionRunnerBackup(CancellationToken token) + private void DeletePreviousVersionRunnerBackup(CancellationToken token, string targetVersion) { // delete previous backup runner (back compat, can be remove after serval sprints) // bin.bak.2.99.0 @@ -483,7 +473,7 @@ private void DeletePreviousVersionRunnerBackup(CancellationToken token) { if (string.Equals(oldBinDir, Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), $"bin"), StringComparison.OrdinalIgnoreCase) || string.Equals(oldBinDir, Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), $"bin.{BuildConstants.RunnerPackage.Version}"), StringComparison.OrdinalIgnoreCase) || - string.Equals(oldBinDir, Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), $"bin.{_targetPackage.Version}"), StringComparison.OrdinalIgnoreCase)) + string.Equals(oldBinDir, Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), $"bin.{targetVersion}"), StringComparison.OrdinalIgnoreCase)) { // skip for current runner version continue; @@ -512,7 +502,7 @@ private void DeletePreviousVersionRunnerBackup(CancellationToken token) { if (string.Equals(oldExternalDir, Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), $"externals"), StringComparison.OrdinalIgnoreCase) || string.Equals(oldExternalDir, Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), $"externals.{BuildConstants.RunnerPackage.Version}"), StringComparison.OrdinalIgnoreCase) || - string.Equals(oldExternalDir, Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), $"externals.{_targetPackage.Version}"), StringComparison.OrdinalIgnoreCase)) + string.Equals(oldExternalDir, Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), $"externals.{targetVersion}"), StringComparison.OrdinalIgnoreCase)) { // skip for current runner version continue; @@ -532,7 +522,7 @@ private void DeletePreviousVersionRunnerBackup(CancellationToken token) } } - private string GenerateUpdateScript(bool restartInteractiveRunner) + private string GenerateUpdateScript(bool restartInteractiveRunner, string targetVersion) { int processId = Process.GetCurrentProcess().Id; string updateLog = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Diag), $"SelfUpdate-{DateTime.UtcNow.ToString("yyyyMMdd-HHmmss")}.log"); @@ -544,14 +534,14 @@ private string GenerateUpdateScript(bool restartInteractiveRunner) string templateName = "update.sh.template"; #endif - string templatePath = Path.Combine(runnerRoot, $"bin.{_targetPackage.Version}", templateName); + string templatePath = Path.Combine(runnerRoot, $"bin.{targetVersion}", templateName); string template = File.ReadAllText(templatePath); template = template.Replace("_PROCESS_ID_", processId.ToString()); template = template.Replace("_RUNNER_PROCESS_NAME_", $"Runner.Listener{IOUtil.ExeExtension}"); template = template.Replace("_ROOT_FOLDER_", runnerRoot); template = template.Replace("_EXIST_RUNNER_VERSION_", BuildConstants.RunnerPackage.Version); - template = template.Replace("_DOWNLOAD_RUNNER_VERSION_", _targetPackage.Version); + template = template.Replace("_DOWNLOAD_RUNNER_VERSION_", targetVersion); template = template.Replace("_UPDATE_LOG_", updateLog); template = template.Replace("_RESTART_INTERACTIVE_RUNNER_", restartInteractiveRunner ? "1" : "0"); diff --git a/src/Sdk/DTWebApi/WebApi/BrokerPackageMetadata.cs b/src/Sdk/DTWebApi/WebApi/BrokerPackageMetadata.cs deleted file mode 100644 index 2a1f3bea87f..00000000000 --- a/src/Sdk/DTWebApi/WebApi/BrokerPackageMetadata.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.Runtime.Serialization; - - -namespace GitHub.DistributedTask.WebApi -{ - [DataContract] - public sealed class BrokerPackageMetadata - { - [JsonConstructor] - internal BrokerPackageMetadata() - { - } - - [DataMember(Name = "download_url")] - public string DownloadUrl - { - get; - set; - } - - [DataMember(Name = "sha256_checksum")] - public string HashValue - { - get; - set; - } - - [DataMember(Name = "os")] - public string Platform - { - get; - set; - } - } -} diff --git a/src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs b/src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs index 99c99e1ad13..9dc08d6d46d 100644 --- a/src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs +++ b/src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs @@ -15,34 +15,33 @@ public sealed class RunnerRefreshMessage internal RunnerRefreshMessage() { } - public RunnerRefreshMessage(String targetVersion) + + [DataMember(Name = "target_version")] + public String TargetVersion { - this.TargetVersion = targetVersion; + get; + set; } - [DataMember] - public String TargetVersion + [DataMember(Name = "download_url")] + public string DownloadUrl { get; - private set; + set; } - [DataMember] - public BrokerPackageMetadata Package + [DataMember(Name = "sha256_checksum")] + public string HashValue { get; set; } - public PackageMetadata GetPackageMetadata() + [DataMember(Name = "os")] + public string Platform { - return new PackageMetadata() - { - DownloadUrl = this.Package?.DownloadUrl, - HashValue = this.Package?.HashValue, - Platform = this.Package?.Platform, - Version = new PackageVersion(this.TargetVersion) - }; + get; + set; } } } diff --git a/src/Test/L0/Listener/SelfUpdaterV2L0.cs b/src/Test/L0/Listener/SelfUpdaterV2L0.cs index b62fd08eba0..fca39ee771c 100644 --- a/src/Test/L0/Listener/SelfUpdaterV2L0.cs +++ b/src/Test/L0/Listener/SelfUpdaterV2L0.cs @@ -108,13 +108,12 @@ public async void TestSelfUpdateAsync() try { - var message = new RunnerRefreshMessage("2.999.0") + var message = new RunnerRefreshMessage() { - Package = new BrokerPackageMetadata() - { - Platform = BuildConstants.RunnerPackage.PackageName, - DownloadUrl = _packageUrl - } + TargetVersion = "2.999.0", + Platform = BuildConstants.RunnerPackage.PackageName, + DownloadUrl = _packageUrl + }; var result = await updater.SelfUpdate(message, _jobDispatcher.Object, true, hc.RunnerShutdownToken); @@ -169,13 +168,11 @@ public async void TestSelfUpdateAsync_DownloadRetry() hc.EnqueueInstance(p3); updater.Initialize(hc); - var message = new RunnerRefreshMessage("2.999.0") + var message = new RunnerRefreshMessage() { - Package = new BrokerPackageMetadata() - { - Platform = BuildConstants.RunnerPackage.PackageName, - DownloadUrl = "https://github.com/actions/runner/notexists" - } + TargetVersion = "2.999.0", + Platform = BuildConstants.RunnerPackage.PackageName, + DownloadUrl = "https://github.com/actions/runner/notexists" }; var ex = await Assert.ThrowsAsync(() => updater.SelfUpdate(message, _jobDispatcher.Object, true, hc.RunnerShutdownToken)); @@ -222,14 +219,12 @@ public async void TestSelfUpdateAsync_ValidateHash() hc.EnqueueInstance(p3); updater.Initialize(hc); - var message = new RunnerRefreshMessage("2.999.0") + var message = new RunnerRefreshMessage() { - Package = new BrokerPackageMetadata() - { - Platform = BuildConstants.RunnerPackage.PackageName, - DownloadUrl = _packageUrl, - HashValue = "badhash" - } + TargetVersion = "2.999.0", + Platform = BuildConstants.RunnerPackage.PackageName, + DownloadUrl = _packageUrl, + HashValue = "badhash" }; var ex = await Assert.ThrowsAsync(() => updater.SelfUpdate(message, _jobDispatcher.Object, true, hc.RunnerShutdownToken)); From efbbfda27a41496d2b4bb835736e3e80dbc3e554 Mon Sep 17 00:00:00 2001 From: Luke Tomlinson Date: Mon, 13 Nov 2023 07:30:27 -0800 Subject: [PATCH 28/33] fix field names --- src/Runner.Listener/SelfUpdaterV2.cs | 4 ++-- src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs | 4 ++-- src/Test/L0/Listener/SelfUpdaterV2L0.cs | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Runner.Listener/SelfUpdaterV2.cs b/src/Runner.Listener/SelfUpdaterV2.cs index e821cf567b0..ec218971f8b 100644 --- a/src/Runner.Listener/SelfUpdaterV2.cs +++ b/src/Runner.Listener/SelfUpdaterV2.cs @@ -59,13 +59,13 @@ public async Task SelfUpdate(RunnerRefreshMessage updateMessage, IJobDispa var totalUpdateTime = Stopwatch.StartNew(); Trace.Info($"An update is available."); - _updateTrace.Enqueue($"RunnerPlatform: {updateMessage.Platform}"); + _updateTrace.Enqueue($"RunnerPlatform: {updateMessage.OS}"); // Print console line that warn user not shutdown runner. _terminal.WriteLine("Runner update in progress, do not shutdown runner."); _terminal.WriteLine($"Downloading {updateMessage.TargetVersion} runner"); - await DownloadLatestRunner(token, updateMessage.TargetVersion, updateMessage.DownloadUrl, updateMessage.HashValue, updateMessage.Platform); + await DownloadLatestRunner(token, updateMessage.TargetVersion, updateMessage.DownloadUrl, updateMessage.SHA256Checksum, updateMessage.OS); Trace.Info($"Download latest runner and unzip into runner root."); // wait till all running job finish diff --git a/src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs b/src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs index 9dc08d6d46d..43fddb56c36 100644 --- a/src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs +++ b/src/Sdk/DTWebApi/WebApi/RunnerRefreshMessage.cs @@ -31,14 +31,14 @@ public string DownloadUrl } [DataMember(Name = "sha256_checksum")] - public string HashValue + public string SHA256Checksum { get; set; } [DataMember(Name = "os")] - public string Platform + public string OS { get; set; diff --git a/src/Test/L0/Listener/SelfUpdaterV2L0.cs b/src/Test/L0/Listener/SelfUpdaterV2L0.cs index fca39ee771c..51004e1fb0c 100644 --- a/src/Test/L0/Listener/SelfUpdaterV2L0.cs +++ b/src/Test/L0/Listener/SelfUpdaterV2L0.cs @@ -111,7 +111,7 @@ public async void TestSelfUpdateAsync() var message = new RunnerRefreshMessage() { TargetVersion = "2.999.0", - Platform = BuildConstants.RunnerPackage.PackageName, + OS = BuildConstants.RunnerPackage.PackageName, DownloadUrl = _packageUrl }; @@ -171,7 +171,7 @@ public async void TestSelfUpdateAsync_DownloadRetry() var message = new RunnerRefreshMessage() { TargetVersion = "2.999.0", - Platform = BuildConstants.RunnerPackage.PackageName, + OS = BuildConstants.RunnerPackage.PackageName, DownloadUrl = "https://github.com/actions/runner/notexists" }; @@ -222,9 +222,9 @@ public async void TestSelfUpdateAsync_ValidateHash() var message = new RunnerRefreshMessage() { TargetVersion = "2.999.0", - Platform = BuildConstants.RunnerPackage.PackageName, + OS = BuildConstants.RunnerPackage.PackageName, DownloadUrl = _packageUrl, - HashValue = "badhash" + SHA256Checksum = "badhash" }; var ex = await Assert.ThrowsAsync(() => updater.SelfUpdate(message, _jobDispatcher.Object, true, hc.RunnerShutdownToken)); From 0cfe10926c201cae81a141a91a533186c119e81b Mon Sep 17 00:00:00 2001 From: Luke Tomlinson Date: Mon, 13 Nov 2023 08:13:17 -0800 Subject: [PATCH 29/33] pr feedback --- src/Runner.Listener/SelfUpdaterV2.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Runner.Listener/SelfUpdaterV2.cs b/src/Runner.Listener/SelfUpdaterV2.cs index ec218971f8b..4dd2a47cbb9 100644 --- a/src/Runner.Listener/SelfUpdaterV2.cs +++ b/src/Runner.Listener/SelfUpdaterV2.cs @@ -88,6 +88,7 @@ public async Task SelfUpdate(RunnerRefreshMessage updateMessage, IJobDispa Trace.Info($"Generate update script into: {updateScript}"); +#if DEBUG // For L0, we will skip execute update script. if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("_GITHUB_ACTION_EXECUTE_UPDATE_SCRIPT"))) { @@ -105,6 +106,7 @@ public async Task SelfUpdate(RunnerRefreshMessage updateMessage, IJobDispa invokeScript.Start(); Trace.Info($"Update script start running"); } +#endif totalUpdateTime.Stop(); From 457cc2251ea3c5e1a4a3c5cc45b72cbede3cccf5 Mon Sep 17 00:00:00 2001 From: Luke Tomlinson Date: Mon, 13 Nov 2023 08:32:11 -0800 Subject: [PATCH 30/33] feedback --- src/Runner.Listener/SelfUpdaterV2.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Runner.Listener/SelfUpdaterV2.cs b/src/Runner.Listener/SelfUpdaterV2.cs index 4dd2a47cbb9..a4a55681346 100644 --- a/src/Runner.Listener/SelfUpdaterV2.cs +++ b/src/Runner.Listener/SelfUpdaterV2.cs @@ -465,11 +465,12 @@ private void DeletePreviousVersionRunnerBackup(CancellationToken token, string t } } + var numberOfOldVersionsToKeep = 1; // delete old bin.2.99.0 folder, only leave the current version and the latest download version var allBinDirs = Directory.GetDirectories(HostContext.GetDirectory(WellKnownDirectory.Root), "bin.*"); - if (allBinDirs.Length > 2) + if (allBinDirs.Length > numberOfOldVersionsToKeep) { - // there are more than 2 bin.version folder. + // there are more than {numberOfOldVersionsToKeep} bin.version folder. // delete older bin.version folders. foreach (var oldBinDir in allBinDirs) { @@ -496,9 +497,9 @@ private void DeletePreviousVersionRunnerBackup(CancellationToken token, string t // delete old externals.2.99.0 folder, only leave the current version and the latest download version var allExternalsDirs = Directory.GetDirectories(HostContext.GetDirectory(WellKnownDirectory.Root), "externals.*"); - if (allExternalsDirs.Length > 2) + if (allExternalsDirs.Length > numberOfOldVersionsToKeep) { - // there are more than 2 externals.version folder. + // there are more than {numberOfOldVersionsToKeep} externals.version folder. // delete older externals.version folders. foreach (var oldExternalDir in allExternalsDirs) { From 673f9d9f904de26c3d4db120e71bafff2085cd43 Mon Sep 17 00:00:00 2001 From: Luke Tomlinson Date: Mon, 13 Nov 2023 08:39:22 -0800 Subject: [PATCH 31/33] delete dead code --- src/Runner.Listener/SelfUpdaterV2.cs | 55 ---------------------------- 1 file changed, 55 deletions(-) diff --git a/src/Runner.Listener/SelfUpdaterV2.cs b/src/Runner.Listener/SelfUpdaterV2.cs index a4a55681346..f3c61606614 100644 --- a/src/Runner.Listener/SelfUpdaterV2.cs +++ b/src/Runner.Listener/SelfUpdaterV2.cs @@ -563,60 +563,5 @@ private string GenerateUpdateScript(bool restartInteractiveRunner, string target File.WriteAllText(updateScript, template); return updateScript; } - - private async Task HashFiles(string fileFolder, CancellationToken token) - { - Trace.Info($"Calculating hash for {fileFolder}"); - - var stopWatch = Stopwatch.StartNew(); - string binDir = HostContext.GetDirectory(WellKnownDirectory.Bin); - string node = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Externals), NodeUtil.GetInternalNodeVersion(), "bin", $"node{IOUtil.ExeExtension}"); - string hashFilesScript = Path.Combine(binDir, "hashFiles"); - var hashResult = string.Empty; - - using (var processInvoker = HostContext.CreateService()) - { - processInvoker.ErrorDataReceived += (_, data) => - { - if (!string.IsNullOrEmpty(data.Data) && data.Data.StartsWith("__OUTPUT__") && data.Data.EndsWith("__OUTPUT__")) - { - hashResult = data.Data.Substring(10, data.Data.Length - 20); - Trace.Info($"Hash result: '{hashResult}'"); - } - else - { - Trace.Info(data.Data); - } - }; - - processInvoker.OutputDataReceived += (_, data) => - { - Trace.Verbose(data.Data); - }; - - var env = new Dictionary - { - ["patterns"] = "**" - }; - - int exitCode = await processInvoker.ExecuteAsync(workingDirectory: fileFolder, - fileName: node, - arguments: $"\"{hashFilesScript.Replace("\"", "\\\"")}\"", - environment: env, - requireExitCodeZero: false, - outputEncoding: null, - killProcessOnCancel: true, - cancellationToken: token); - - if (exitCode != 0) - { - Trace.Error($"hashFiles returns '{exitCode}' failed. Fail to hash files under directory '{fileFolder}'"); - } - - stopWatch.Stop(); - _updateTrace.Enqueue($"{nameof(HashFiles)}{Path.GetFileName(fileFolder)}Time: {stopWatch.ElapsedMilliseconds}ms"); - return hashResult; - } - } } } From ff87f7398bd667b5394fe6153aaac8510bd01130 Mon Sep 17 00:00:00 2001 From: Luke Tomlinson Date: Mon, 13 Nov 2023 08:46:53 -0800 Subject: [PATCH 32/33] . --- src/Runner.Listener/SelfUpdaterV2.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Runner.Listener/SelfUpdaterV2.cs b/src/Runner.Listener/SelfUpdaterV2.cs index f3c61606614..ea561f5039c 100644 --- a/src/Runner.Listener/SelfUpdaterV2.cs +++ b/src/Runner.Listener/SelfUpdaterV2.cs @@ -36,6 +36,9 @@ public class SelfUpdaterV2 : RunnerService, ISelfUpdaterV2 private IRunnerServer _runnerServer; private int _poolId; private ulong _agentId; + + private const int _numberOfOldVersionsToKeep = 1; + private readonly ConcurrentQueue _updateTrace = new(); public bool Busy { get; private set; } @@ -465,12 +468,11 @@ private void DeletePreviousVersionRunnerBackup(CancellationToken token, string t } } - var numberOfOldVersionsToKeep = 1; // delete old bin.2.99.0 folder, only leave the current version and the latest download version var allBinDirs = Directory.GetDirectories(HostContext.GetDirectory(WellKnownDirectory.Root), "bin.*"); - if (allBinDirs.Length > numberOfOldVersionsToKeep) + if (allBinDirs.Length > _numberOfOldVersionsToKeep) { - // there are more than {numberOfOldVersionsToKeep} bin.version folder. + // there are more than {_numberOfOldVersionsToKeep} bin.version folder. // delete older bin.version folders. foreach (var oldBinDir in allBinDirs) { @@ -497,9 +499,9 @@ private void DeletePreviousVersionRunnerBackup(CancellationToken token, string t // delete old externals.2.99.0 folder, only leave the current version and the latest download version var allExternalsDirs = Directory.GetDirectories(HostContext.GetDirectory(WellKnownDirectory.Root), "externals.*"); - if (allExternalsDirs.Length > numberOfOldVersionsToKeep) + if (allExternalsDirs.Length > _numberOfOldVersionsToKeep) { - // there are more than {numberOfOldVersionsToKeep} externals.version folder. + // there are more than {_numberOfOldVersionsToKeep} externals.version folder. // delete older externals.version folders. foreach (var oldExternalDir in allExternalsDirs) { From 8b0d538028e0d67a60bfd440e5352e5c7e4239b5 Mon Sep 17 00:00:00 2001 From: Luke Tomlinson Date: Mon, 13 Nov 2023 11:35:41 -0800 Subject: [PATCH 33/33] fix debug condition --- src/Runner.Listener/SelfUpdaterV2.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Runner.Listener/SelfUpdaterV2.cs b/src/Runner.Listener/SelfUpdaterV2.cs index ea561f5039c..46bced18c08 100644 --- a/src/Runner.Listener/SelfUpdaterV2.cs +++ b/src/Runner.Listener/SelfUpdaterV2.cs @@ -94,6 +94,7 @@ public async Task SelfUpdate(RunnerRefreshMessage updateMessage, IJobDispa #if DEBUG // For L0, we will skip execute update script. if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("_GITHUB_ACTION_EXECUTE_UPDATE_SCRIPT"))) +#endif { string flagFile = "update.finished"; IOUtil.DeleteFile(flagFile); @@ -109,7 +110,6 @@ public async Task SelfUpdate(RunnerRefreshMessage updateMessage, IJobDispa invokeScript.Start(); Trace.Info($"Update script start running"); } -#endif totalUpdateTime.Stop();