public void CalculateServiceNameSanitizeOutOfRangeChars() { RunnerSettings settings = new RunnerSettings(); settings.AgentName = "name"; settings.ServerUrl = "https://example.githubusercontent.com/12345678901234567890123456789012345678901234567890"; settings.GitHubUrl = "https://github.com/org!@$*+[]()/repo!@$*+[]()"; string serviceNamePattern = "actions.runner.{0}.{1}"; string serviceDisplayNamePattern = "GitHub Actions Runner ({0}.{1})"; using (TestHostContext hc = CreateTestContext()) { ServiceControlManager scm = new ServiceControlManager(); scm.Initialize(hc); scm.CalculateServiceName( settings, serviceNamePattern, serviceDisplayNamePattern, out string serviceName, out string serviceDisplayName); var serviceNameParts = serviceName.Split('.'); // Verify service name parts are sanitized correctly Assert.Equal("actions", serviceNameParts[0]); Assert.Equal("runner", serviceNameParts[1]); Assert.Equal("org----------repo---------", serviceNameParts[2]); // Chars replaced with '-' Assert.Equal("name", serviceNameParts[3]); } }
public void GenerateScripts(RunnerSettings settings) { try { string serviceName; string serviceDisplayName; CalculateServiceName(settings, _svcNamePattern, _svcDisplayPattern, out serviceName, out serviceDisplayName); string svcShPath = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), _shName); string svcShContent = File.ReadAllText(Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Bin), _shTemplate)); var tokensToReplace = new Dictionary <string, string> { { "{{SvcDescription}}", serviceDisplayName }, { "{{SvcNameVar}}", serviceName } }; svcShContent = tokensToReplace.Aggregate( svcShContent, (current, item) => current.Replace(item.Key, item.Value)); File.WriteAllText(svcShPath, svcShContent, new UTF8Encoding(false)); var unixUtil = HostContext.CreateService <IUnixUtil>(); unixUtil.ChmodAsync("755", svcShPath).GetAwaiter().GetResult(); } catch (Exception ex) { Trace.Error(ex); throw; } }
public void CalculateServiceNameLimitsServiceNameTo80Chars() { RunnerSettings settings = new RunnerSettings(); settings.AgentName = "thisisareallyreallylongbutstillvalidagentname"; settings.ServerUrl = "https://example.githubusercontent.com/12345678901234567890123456789012345678901234567890"; settings.GitHubUrl = "https://github.com/myreallylongorganizationexample/myreallylongrepoexample"; string serviceNamePattern = "actions.runner.{0}.{1}"; string serviceDisplayNamePattern = "GitHub Actions Runner ({0}.{1})"; using (TestHostContext hc = CreateTestContext()) { ServiceControlManager scm = new ServiceControlManager(); scm.Initialize(hc); scm.CalculateServiceName( settings, serviceNamePattern, serviceDisplayNamePattern, out string serviceName, out string serviceDisplayName); // Verify name has been shortened to 80 characters Assert.Equal(80, serviceName.Length); var serviceNameParts = serviceName.Split('.'); // Verify that each component has been shortened to a sensible length Assert.Equal("actions", serviceNameParts[0]); // Never shortened Assert.Equal("runner", serviceNameParts[1]); // Never shortened Assert.Equal("myreallylongorganizationexample-myreallylongr", serviceNameParts[2]); // First 45 chars, '/' has been replaced with '-' Assert.Equal("thisisareallyreally", serviceNameParts[3]); // Remainder of unused chars } }
public void CalculateServiceName80Chars() { RunnerSettings settings = new RunnerSettings(); settings.AgentName = "thisiskindofalongrunnername12"; settings.ServerUrl = "https://example.githubusercontent.com/12345678901234567890123456789012345678901234567890"; settings.GitHubUrl = "https://github.com/myorganizationexample/myrepoexample"; string serviceNamePattern = "actions.runner.{0}.{1}"; string serviceDisplayNamePattern = "GitHub Actions Runner ({0}.{1})"; using (TestHostContext hc = CreateTestContext()) { ServiceControlManager scm = new ServiceControlManager(); scm.Initialize(hc); scm.CalculateServiceName( settings, serviceNamePattern, serviceDisplayNamePattern, out string serviceName, out string serviceDisplayName); // Verify name is still equal to 80 characters Assert.Equal(80, serviceName.Length); var serviceNameParts = serviceName.Split('.'); // Verify nothing has been shortened out Assert.Equal("actions", serviceNameParts[0]); Assert.Equal("runner", serviceNameParts[1]); Assert.Equal("myorganizationexample-myrepoexample", serviceNameParts[2]); // '/' has been replaced with '-' Assert.Equal("thisiskindofalongrunnername12", serviceNameParts[3]); } }
public async Task CanEnsureConfigure() { using (TestHostContext tc = CreateTestContext()) { Tracing trace = tc.GetTrace(); trace.Info("Creating config manager"); IConfigurationManager configManager = new ConfigurationManager(); configManager.Initialize(tc); var userLabels = "userlabel1,userlabel2"; trace.Info("Preparing command line arguments"); var command = new CommandSettings( tc, new[] { "configure", "--url", _expectedServerUrl, "--name", _expectedAgentName, "--runnergroup", _secondRunnerGroupName, "--work", _expectedWorkFolder, "--auth", _expectedAuthType, "--token", _expectedToken, "--labels", userLabels, "--ephemeral", }); trace.Info("Constructed."); _store.Setup(x => x.IsConfigured()).Returns(false); _configMgrAgentSettings = null; trace.Info("Ensuring all the required parameters are available in the command line parameter"); await configManager.ConfigureAsync(command); _store.Setup(x => x.IsConfigured()).Returns(true); trace.Info("Configured, verifying all the parameter value"); var s = configManager.LoadSettings(); Assert.NotNull(s); Assert.True(s.ServerUrl.Equals(_expectedServerUrl)); Assert.True(s.AgentName.Equals(_expectedAgentName)); Assert.True(s.PoolId.Equals(_secondRunnerGroupId)); Assert.True(s.WorkFolder.Equals(_expectedWorkFolder)); Assert.True(s.Ephemeral.Equals(true)); // validate GetAgentPoolsAsync gets called twice with automation pool type _runnerServer.Verify(x => x.GetAgentPoolsAsync(It.IsAny <string>(), It.Is <TaskAgentPoolType>(p => p == TaskAgentPoolType.Automation)), Times.Exactly(2)); var expectedLabels = new List <string>() { "self-hosted", VarUtil.OS, VarUtil.OSArchitecture }; expectedLabels.AddRange(userLabels.Split(",").ToList()); _runnerServer.Verify(x => x.AddAgentAsync(It.IsAny <int>(), It.Is <TaskAgent>(a => a.Labels.Select(x => x.Name).ToHashSet().SetEquals(expectedLabels))), Times.Once); } }
public async void RenewJobRequestNewAgentNameUpdatesSettings() { //Arrange using (var hc = new TestHostContext(this)) { var count = 0; var oldName = "OldName"; var newName = "NewName"; var oldSettings = new RunnerSettings { AgentName = oldName }; var reservedAgent = new TaskAgentReference { Name = newName }; var trace = hc.GetTrace(nameof(DispatcherRenewJobRequestStopOnJobTokenExpiredExceptions)); TaskCompletionSource <int> firstJobRequestRenewed = new TaskCompletionSource <int>(); CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); var request = new Mock <TaskAgentJobRequest>(); request.Object.ReservedAgent = reservedAgent; PropertyInfo lockUntilProperty = request.Object.GetType().GetProperty("LockedUntil", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); Assert.NotNull(lockUntilProperty); lockUntilProperty.SetValue(request.Object, DateTime.UtcNow.AddMinutes(5)); hc.SetSingleton <IRunnerServer>(_runnerServer.Object); hc.SetSingleton <IConfigurationStore>(_configurationStore.Object); _configurationStore.Setup(x => x.GetSettings()).Returns(oldSettings); _runnerServer.Setup(x => x.RenewAgentRequestAsync(It.IsAny <int>(), It.IsAny <long>(), It.IsAny <Guid>(), It.IsAny <string>(), It.IsAny <CancellationToken>())) .Returns(() => { count++; if (count < 5) { return(Task.FromResult <TaskAgentJobRequest>(request.Object)); } else if (count == 5 || count == 6 || count == 7) { throw new TimeoutException(""); } else { cancellationTokenSource.Cancel(); return(Task.FromResult <TaskAgentJobRequest>(request.Object)); } }); var jobDispatcher = new JobDispatcher(); jobDispatcher.Initialize(hc); // Act await jobDispatcher.RenewJobRequestAsync(0, 0, Guid.Empty, Guid.NewGuid().ToString(), firstJobRequestRenewed, cancellationTokenSource.Token); // Assert _configurationStore.Verify(x => x.SaveSettings(It.Is <RunnerSettings>(settings => settings.AgentName == newName)), Times.Once); } }
public MessageListenerL0() { _settings = new RunnerSettings { AgentId = 1, AgentName = "myagent", PoolId = 123, PoolName = "default", ServerUrl = "http://myserver", WorkFolder = "_work" }; _config = new Mock <IConfigurationManager>(); _config.Setup(x => x.LoadSettings()).Returns(_settings); _runnerServer = new Mock <IRunnerServer>(); _credMgr = new Mock <ICredentialManager>(); }
public override void Help() { CpuSettings cpuSettings = SettingsManager.GetSettings <CpuSettings>(); RunnerSettings settings = new RunnerSettings(); HlCompilerSettings hls = SettingsManager.GetSettings <HlCompilerSettings>(); MemorySettings ms = SettingsManager.GetSettings <MemorySettings>(); MemoryBusSettings mbs = SettingsManager.GetSettings <MemoryBusSettings>(); HelpSubSystem.WriteSubsystem("vis run", settings, hls, ms, mbs, cpuSettings); }
public void ConfigureRun( string assemblyLocation, IEnumerable <string> categories, IEnumerable <string> otherTestFixtures, IEnumerable <string> categoriesToRun, RunnerSettings runnerSettings) { _assemblyLocation = assemblyLocation; _runnerSettings = runnerSettings; //TODO does this design make any sense... not all runners need the same thing throw new NotImplementedException("This runner needs to be completely re-worked..."); }
public RunnerSettings LoadSettings() { Trace.Info(nameof(LoadSettings)); if (!IsConfigured()) { throw new InvalidOperationException("Not configured"); } RunnerSettings settings = _store.GetSettings(); Trace.Info("Settings Loaded"); return(settings); }
private static string RunPreRunSteps(RunnerSettings settings, string file) { string ret = file; foreach (KeyValuePair <string, Func <string, string> > keyValuePair in settings.PreRunMap) { if (Path.GetExtension(ret) == keyValuePair.Key) { ret = RunPreRunSteps(settings, keyValuePair.Value(ret)); } } return(ret); }
public ReflectionDiscovery(IBenchmarkOutput output, IBenchmarkAssertionRunner benchmarkAssertions, RunnerSettings settings) { Output = output; BenchmarkAssertions = benchmarkAssertions; RunnerSettings = settings; if (RunnerSettings.TracingEnabled) { Trace = new BenchmarkOutputTrace(Output); } else { Trace = NoOpBenchmarkTrace.Instance; } }
public async Task CanEnsureConfigure() { using (TestHostContext tc = CreateTestContext()) { Tracing trace = tc.GetTrace(); trace.Info("Creating config manager"); IConfigurationManager configManager = new ConfigurationManager(); configManager.Initialize(tc); trace.Info("Preparing command line arguments"); var command = new CommandSettings( tc, new[] { "configure", "--url", _expectedServerUrl, "--name", _expectedAgentName, "--pool", _expectedPoolName, "--work", _expectedWorkFolder, "--auth", _expectedAuthType, "--token", _expectedToken }); trace.Info("Constructed."); _store.Setup(x => x.IsConfigured()).Returns(false); _configMgrAgentSettings = null; trace.Info("Ensuring all the required parameters are available in the command line parameter"); await configManager.ConfigureAsync(command); _store.Setup(x => x.IsConfigured()).Returns(true); trace.Info("Configured, verifying all the parameter value"); var s = configManager.LoadSettings(); Assert.NotNull(s); Assert.True(s.ServerUrl.Equals(_expectedServerUrl)); Assert.True(s.AgentName.Equals(_expectedAgentName)); Assert.True(s.PoolId.Equals(_expectedPoolId)); Assert.True(s.WorkFolder.Equals(_expectedWorkFolder)); // validate GetAgentPoolsAsync gets called once with automation pool type _runnerServer.Verify(x => x.GetAgentPoolsAsync(It.IsAny <string>(), It.Is <TaskAgentPoolType>(p => p == TaskAgentPoolType.Automation)), Times.Once); _runnerServer.Verify(x => x.AddAgentAsync(It.IsAny <int>(), It.Is <TaskAgent>(a => a.Labels.Contains("self-hosted") && a.Labels.Contains(VarUtil.OS) && a.Labels.Contains(VarUtil.OSArchitecture))), Times.Once); } }
public async Task ConfigureErrorOnMissingRunnerGroup() { using (TestHostContext tc = CreateTestContext()) { var expectedPools = new List <TaskAgentPool>() { new TaskAgentPool(_defaultRunnerGroupName) { Id = _defaultRunnerGroupId, IsInternal = true } }; _runnerServer.Setup(x => x.GetAgentPoolsAsync(It.IsAny <string>(), It.IsAny <TaskAgentPoolType>())).Returns(Task.FromResult(expectedPools)); Tracing trace = tc.GetTrace(); trace.Info("Creating config manager"); IConfigurationManager configManager = new ConfigurationManager(); configManager.Initialize(tc); trace.Info("Preparing command line arguments"); var command = new CommandSettings( tc, new[] { "configure", "--url", _expectedServerUrl, "--name", _expectedAgentName, "--runnergroup", "notexists", "--work", _expectedWorkFolder, "--auth", _expectedAuthType, "--token", _expectedToken, }); trace.Info("Constructed."); _store.Setup(x => x.IsConfigured()).Returns(false); _configMgrAgentSettings = null; trace.Info("Ensuring all the required parameters are available in the command line parameter"); var ex = await Assert.ThrowsAsync <TaskAgentPoolNotFoundException>(() => configManager.ConfigureAsync(command)); Assert.Contains("notexists", ex.Message); _runnerServer.Verify(x => x.GetAgentPoolsAsync(It.IsAny <string>(), It.Is <TaskAgentPoolType>(p => p == TaskAgentPoolType.Automation)), Times.Exactly(1)); } }
static int Main(string[] args) { var runnerSettings = new RunnerSettings(); var showHelp = false; var displayDetails = false; var options = new OptionSet { { "r|recursive", "Recursive", v => runnerSettings.Recursive = v != null }, { "h|help", "Show help", v => showHelp = true }, { "d|details", "Display Details", v => displayDetails = true } }; try { runnerSettings.StartPaths = options.Parse(args).ToArray(); } catch (OptionException e) { Console.Write("invalid options: {0}", e.Message); Console.WriteLine("Try `DotNetAssemblyInformer --help' for more information."); return(-1); } if (showHelp) { ShowHelp(options); return(0); } var runner = new Runner(runnerSettings); var runnerResult = runner.Run(); var reporter = !displayDetails ? new BasicConsoleReporter() : new JsonConsoleReporter(); reporter.Generate(runnerResult); if (Debugger.IsAttached) { Console.ReadKey(); } return(runnerResult.OverralSuccess ? 0 : 1); }
public static Cpu Create( IEnumerable <string> args, CpuSettings cpuSettings = null, RunnerSettings settings = null, HlCompilerSettings hls = null, MemorySettings ms = null, MemoryBusSettings mbs = null) { Logger.LogMessage(LoggerSystems.Console, "Creating CPU..."); cpuSettings = cpuSettings ?? SettingsManager.GetSettings <CpuSettings>(); settings = settings ?? new RunnerSettings(); hls = hls ?? SettingsManager.GetSettings <HlCompilerSettings>(); ms = ms ?? SettingsManager.GetSettings <MemorySettings>(); mbs = mbs ?? SettingsManager.GetSettings <MemoryBusSettings>(); ArgumentSyntaxParser.Parse( args.ToArray(), settings, hls, ms, mbs, cpuSettings ); SettingsManager.SaveSettings(hls); SettingsManager.SaveSettings(ms); SettingsManager.SaveSettings(mbs); SettingsManager.SaveSettings(cpuSettings); if (settings.InputFiles == null) { Logger.LogMessage(LoggerSystems.Console, "No Files Specified"); return(null); } MemoryBus bus = CreateBus(mbs); Cpu cpu = new Cpu(bus, cpuSettings.CpuResetAddr, cpuSettings.CpuIntAddr); return(cpu); }
public void ConfigureRun( string assemblyLocation, IEnumerable <string> categories, IEnumerable <string> otherTestFixtures, IEnumerable <string> categoriesToRun, RunnerSettings runnerSettings) { if (_configured) { throw new InvalidOperationException("Need to at least wait until this run is complete..." + "\n" + "Then we can talk, but for now, just create a new one"); } _assemblyLocation = assemblyLocation; _otherTestFixtures = otherTestFixtures; _categories = categories.ToList(); _categoriesToRun = categoriesToRun.ToList(); _runnerSettings = runnerSettings; _configured = true; }
public void CalculateServiceName(RunnerSettings settings, string serviceNamePattern, string serviceDisplayNamePattern, out string serviceName, out string serviceDisplayName) { Trace.Entering(); serviceName = string.Empty; serviceDisplayName = string.Empty; if (string.IsNullOrEmpty(settings.RepoOrOrgName)) { throw new InvalidOperationException($"Cannot find GitHub repository/organization name from server url: '{settings.ServerUrl}'"); } // For the service name, replace any characters outside of the alpha-numeric set and ".", "_", "-" with "-" Regex regex = new Regex(@"[^0-9a-zA-Z._\-]"); string repoOrOrgName = regex.Replace(settings.RepoOrOrgName, "-"); serviceName = StringUtil.Format(serviceNamePattern, repoOrOrgName, settings.AgentName); if (serviceName.Length > 80) { Trace.Verbose($"Calculated service name is too long (> 80 chars). Trying again by calculating a shorter name."); int exceededCharLength = serviceName.Length - 80; string repoOrOrgNameSubstring = StringUtil.SubstringPrefix(repoOrOrgName, 45); exceededCharLength -= repoOrOrgName.Length - repoOrOrgNameSubstring.Length; string runnerNameSubstring = settings.AgentName; // Only trim runner name if it's really necessary if (exceededCharLength > 0) { runnerNameSubstring = StringUtil.SubstringPrefix(settings.AgentName, settings.AgentName.Length - exceededCharLength); } serviceName = StringUtil.Format(serviceNamePattern, repoOrOrgNameSubstring, runnerNameSubstring); } serviceDisplayName = StringUtil.Format(serviceDisplayNamePattern, repoOrOrgName, settings.AgentName); Trace.Info($"Service name '{serviceName}' display name '{serviceDisplayName}' will be used for service configuration."); }
public override void Initialize(IHostContext hostContext) { base.Initialize(hostContext); // get pool id from config var configurationStore = hostContext.GetService <IConfigurationStore>(); _runnerSetting = configurationStore.GetSettings(); _poolId = _runnerSetting.PoolId; int channelTimeoutSeconds; if (!int.TryParse(Environment.GetEnvironmentVariable("GITHUB_ACTIONS_RUNNER_CHANNEL_TIMEOUT") ?? string.Empty, out channelTimeoutSeconds)) { channelTimeoutSeconds = 30; } // _channelTimeout should in range [30, 300] seconds _channelTimeout = TimeSpan.FromSeconds(Math.Min(Math.Max(channelTimeoutSeconds, 30), 300)); Trace.Info($"Set runner/worker IPC timeout to {_channelTimeout.TotalSeconds} seconds."); }
public IRunner Create(RunnerSettings runnerSettings, Assembly assembly, string categoriesList = null) { Func <Type, bool> filterTestFixtures = null; if (runnerSettings.Namespace != null) { filterTestFixtures = type => type.Namespace .IfNotNull(x => x.StartsWith(runnerSettings.Namespace)); } var categories = _categoryFinderService.FindCategories(assembly, filterTestFixtures); var other = categories.Concat(new[] { "Long" }); var otherFixtures = _categoryFinderService.FindTestFixturesExcludingCategories(assembly, other, filterTestFixtures).ToList(); if (!categories.Any() && !otherFixtures.Any()) { throw new InvalidOperationException("Cannot run if there are no tests."); } var categoriesToRun = categoriesList != null ? categoriesList.Split(',', ';', '\n', '\r') .Select(x => x.Trim()) .Where(x => x.Length > 0) : new string[0]; if (runnerSettings.RerunFailedCategories) { var erroredCategories = _resultsParser.GetErrorsCategories(runnerSettings.ResultsStatsFilepath); categoriesToRun = categoriesToRun.Concat(erroredCategories); } //return new ThreadRunner(_assembly.Location, _featureTypes, ObjectFactory.GetInstance<ILogger>(), _outputPath); //return new ProcessRunner(assembly.Location, categories, otherFixtures, categoriesToRun, ObjectFactory.GetInstance<ILogger>(), runnerSettings); //TODO: With this, this is no longer a factory... _runner.ConfigureRun(assembly.Location, categories, otherFixtures, categoriesToRun, runnerSettings); return(_runner); }
public async Task <int> ExecuteCommand(CommandSettings command) { try { VssUtil.InitializeVssClientSettings(HostContext.UserAgent, HostContext.WebProxy); _inConfigStage = true; _completedCommand.Reset(); _term.CancelKeyPress += CtrlCHandler; //register a SIGTERM handler HostContext.Unloading += Runner_Unloading; // TODO Unit test to cover this logic Trace.Info(nameof(ExecuteCommand)); var configManager = HostContext.GetService <IConfigurationManager>(); // command is not required, if no command it just starts if configured // TODO: Invalid config prints usage if (command.Help) { PrintUsage(command); return(Constants.Runner.ReturnCode.Success); } if (command.Version) { _term.WriteLine(BuildConstants.RunnerPackage.Version); return(Constants.Runner.ReturnCode.Success); } if (command.Commit) { _term.WriteLine(BuildConstants.Source.CommitHash); return(Constants.Runner.ReturnCode.Success); } // Configure runner prompt for args if not supplied // Unattended configure mode will not prompt for args if not supplied and error on any missing or invalid value. if (command.Configure) { try { await configManager.ConfigureAsync(command); return(Constants.Runner.ReturnCode.Success); } catch (Exception ex) { Trace.Error(ex); _term.WriteError(ex.Message); return(Constants.Runner.ReturnCode.TerminatedError); } } // remove config files, remove service, and exit if (command.Remove) { try { await configManager.UnconfigureAsync(command); return(Constants.Runner.ReturnCode.Success); } catch (Exception ex) { Trace.Error(ex); _term.WriteError(ex.Message); return(Constants.Runner.ReturnCode.TerminatedError); } } _inConfigStage = false; // warmup runner process (JIT/CLR) // In scenarios where the runner is single use (used and then thrown away), the system provisioning the runner can call `Runner.Listener --warmup` before the machine is made available to the pool for use. // this will optimizes the runner process startup time. if (command.Warmup) { var binDir = HostContext.GetDirectory(WellKnownDirectory.Bin); foreach (var assemblyFile in Directory.EnumerateFiles(binDir, "*.dll")) { try { Trace.Info($"Load assembly: {assemblyFile}."); var assembly = Assembly.LoadFrom(assemblyFile); var types = assembly.GetTypes(); foreach (Type loadedType in types) { try { Trace.Info($"Load methods: {loadedType.FullName}."); var methods = loadedType.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static); foreach (var method in methods) { if (!method.IsAbstract && !method.ContainsGenericParameters) { Trace.Verbose($"Prepare method: {method.Name}."); RuntimeHelpers.PrepareMethod(method.MethodHandle); } } } catch (Exception ex) { Trace.Error(ex); } } } catch (Exception ex) { Trace.Error(ex); } } return(Constants.Runner.ReturnCode.Success); } RunnerSettings settings = configManager.LoadSettings(); var store = HostContext.GetService <IConfigurationStore>(); bool configuredAsService = store.IsServiceConfigured(); // Run runner if (command.Run) // this line is current break machine provisioner. { // Error if runner not configured. if (!configManager.IsConfigured()) { _term.WriteError("Runner is not configured."); PrintUsage(command); return(Constants.Runner.ReturnCode.TerminatedError); } Trace.Verbose($"Configured as service: '{configuredAsService}'"); //Get the startup type of the runner i.e., autostartup, service, manual StartupType startType; var startupTypeAsString = command.GetStartupType(); if (string.IsNullOrEmpty(startupTypeAsString) && configuredAsService) { // We need try our best to make the startup type accurate // The problem is coming from runner autoupgrade, which result an old version service host binary but a newer version runner binary // At that time the servicehost won't pass --startuptype to Runner.Listener while the runner is actually running as service. // We will guess the startup type only when the runner is configured as service and the guess will based on whether STDOUT/STDERR/STDIN been redirect or not Trace.Info($"Try determine runner startup type base on console redirects."); startType = (Console.IsErrorRedirected && Console.IsInputRedirected && Console.IsOutputRedirected) ? StartupType.Service : StartupType.Manual; } else { if (!Enum.TryParse(startupTypeAsString, true, out startType)) { Trace.Info($"Could not parse the argument value '{startupTypeAsString}' for StartupType. Defaulting to {StartupType.Manual}"); startType = StartupType.Manual; } } Trace.Info($"Set runner startup type - {startType}"); HostContext.StartupType = startType; // Run the runner interactively or as service return(await RunAsync(settings, command.RunOnce)); } else { PrintUsage(command); return(Constants.Runner.ReturnCode.Success); } } finally { _term.CancelKeyPress -= CtrlCHandler; HostContext.Unloading -= Runner_Unloading; _completedCommand.Set(); } }
public async Task ConfigureAsync(CommandSettings command) { _term.WriteLine(); _term.WriteLine("--------------------------------------------------------------------------------", ConsoleColor.White); _term.WriteLine("| ____ _ _ _ _ _ _ _ _ |", ConsoleColor.White); _term.WriteLine("| / ___(_) |_| | | |_ _| |__ / \\ ___| |_(_) ___ _ __ ___ |", ConsoleColor.White); _term.WriteLine("| | | _| | __| |_| | | | | '_ \\ / _ \\ / __| __| |/ _ \\| '_ \\/ __| |", ConsoleColor.White); _term.WriteLine("| | |_| | | |_| _ | |_| | |_) | / ___ \\ (__| |_| | (_) | | | \\__ \\ |", ConsoleColor.White); _term.WriteLine("| \\____|_|\\__|_| |_|\\__,_|_.__/ /_/ \\_\\___|\\__|_|\\___/|_| |_|___/ |", ConsoleColor.White); _term.WriteLine("| |", ConsoleColor.White); _term.Write("| ", ConsoleColor.White); _term.Write("Self-hosted runner registration", ConsoleColor.Cyan); _term.WriteLine(" |", ConsoleColor.White); _term.WriteLine("| |", ConsoleColor.White); _term.WriteLine("--------------------------------------------------------------------------------", ConsoleColor.White); Trace.Info(nameof(ConfigureAsync)); if (IsConfigured()) { throw new InvalidOperationException("Cannot configure the runner because it is already configured. To reconfigure the runner, run 'config.cmd remove' or './config.sh remove' first."); } RunnerSettings runnerSettings = new RunnerSettings(); bool isHostedServer = false; // Loop getting url and creds until you can connect ICredentialProvider credProvider = null; VssCredentials creds = null; _term.WriteSection("Authentication"); while (true) { // Get the URL var inputUrl = command.GetUrl(); if (!inputUrl.Contains("github.com", StringComparison.OrdinalIgnoreCase) && !inputUrl.Contains("github.localhost", StringComparison.OrdinalIgnoreCase)) { runnerSettings.ServerUrl = inputUrl; // Get the credentials credProvider = GetCredentialProvider(command, runnerSettings.ServerUrl); creds = credProvider.GetVssCredentials(HostContext); Trace.Info("legacy vss cred retrieved"); } else { runnerSettings.GitHubUrl = inputUrl; var githubToken = command.GetRunnerRegisterToken(); GitHubAuthResult authResult = await GetTenantCredential(inputUrl, githubToken, Constants.RunnerEvent.Register); runnerSettings.ServerUrl = authResult.TenantUrl; creds = authResult.ToVssCredentials(); Trace.Info("cred retrieved via GitHub auth"); } try { // Determine the service deployment type based on connection data. (Hosted/OnPremises) isHostedServer = await IsHostedServer(runnerSettings.ServerUrl, creds); // Validate can connect. await _runnerServer.ConnectAsync(new Uri(runnerSettings.ServerUrl), creds); _term.WriteLine(); _term.WriteSuccessMessage("Connected to GitHub"); Trace.Info("Test Connection complete."); break; } catch (Exception e) when(!command.Unattended) { _term.WriteError(e); _term.WriteError("Failed to connect. Try again or ctrl-c to quit"); _term.WriteLine(); } } // We want to use the native CSP of the platform for storage, so we use the RSACSP directly RSAParameters publicKey; var keyManager = HostContext.GetService <IRSAKeyManager>(); using (var rsa = keyManager.CreateKey()) { publicKey = rsa.ExportParameters(false); } _term.WriteSection("Runner Registration"); //Get all the agent pools, and select the first private pool List <TaskAgentPool> agentPools = await _runnerServer.GetAgentPoolsAsync(); TaskAgentPool agentPool = agentPools?.Where(x => x.IsHosted == false).FirstOrDefault(); if (agentPool == null) { throw new TaskAgentPoolNotFoundException($"Could not find any private pool. Contact support."); } else { Trace.Info("Found a private pool with id {1} and name {2}", agentPool.Id, agentPool.Name); runnerSettings.PoolId = agentPool.Id; runnerSettings.PoolName = agentPool.Name; } TaskAgent agent; while (true) { runnerSettings.AgentName = command.GetRunnerName(); _term.WriteLine(); var agents = await _runnerServer.GetAgentsAsync(runnerSettings.PoolId, runnerSettings.AgentName); Trace.Verbose("Returns {0} agents", agents.Count); agent = agents.FirstOrDefault(); if (agent != null) { _term.WriteLine("A runner exists with the same name", ConsoleColor.Yellow); if (command.GetReplace()) { // Update existing agent with new PublicKey, agent version. agent = UpdateExistingAgent(agent, publicKey); try { agent = await _runnerServer.ReplaceAgentAsync(runnerSettings.PoolId, agent); _term.WriteSuccessMessage("Successfully replaced the runner"); break; } catch (Exception e) when(!command.Unattended) { _term.WriteError(e); _term.WriteError("Failed to replace the runner. Try again or ctrl-c to quit"); } } else if (command.Unattended) { // if not replace and it is unattended config. throw new TaskAgentExistsException($"Pool {runnerSettings.PoolId} already contains a runner with name {runnerSettings.AgentName}."); } } else { // Create a new agent. agent = CreateNewAgent(runnerSettings.AgentName, publicKey); try { agent = await _runnerServer.AddAgentAsync(runnerSettings.PoolId, agent); _term.WriteSuccessMessage("Runner successfully added"); break; } catch (Exception e) when(!command.Unattended) { _term.WriteError(e); _term.WriteError("Failed to add the runner. Try again or ctrl-c to quit"); } } } // Add Agent Id to settings runnerSettings.AgentId = agent.Id; // respect the serverUrl resolve by server. // in case of agent configured using collection url instead of account url. string agentServerUrl; if (agent.Properties.TryGetValidatedValue <string>("ServerUrl", out agentServerUrl) && !string.IsNullOrEmpty(agentServerUrl)) { Trace.Info($"Agent server url resolve by server: '{agentServerUrl}'."); // we need make sure the Schema/Host/Port component of the url remain the same. UriBuilder inputServerUrl = new UriBuilder(runnerSettings.ServerUrl); UriBuilder serverReturnedServerUrl = new UriBuilder(agentServerUrl); if (Uri.Compare(inputServerUrl.Uri, serverReturnedServerUrl.Uri, UriComponents.SchemeAndServer, UriFormat.Unescaped, StringComparison.OrdinalIgnoreCase) != 0) { inputServerUrl.Path = serverReturnedServerUrl.Path; Trace.Info($"Replace server returned url's scheme://host:port component with user input server url's scheme://host:port: '{inputServerUrl.Uri.AbsoluteUri}'."); runnerSettings.ServerUrl = inputServerUrl.Uri.AbsoluteUri; } else { runnerSettings.ServerUrl = agentServerUrl; } } // See if the server supports our OAuth key exchange for credentials if (agent.Authorization != null && agent.Authorization.ClientId != Guid.Empty && agent.Authorization.AuthorizationUrl != null) { UriBuilder configServerUrl = new UriBuilder(runnerSettings.ServerUrl); UriBuilder oauthEndpointUrlBuilder = new UriBuilder(agent.Authorization.AuthorizationUrl); if (!isHostedServer && Uri.Compare(configServerUrl.Uri, oauthEndpointUrlBuilder.Uri, UriComponents.SchemeAndServer, UriFormat.Unescaped, StringComparison.OrdinalIgnoreCase) != 0) { oauthEndpointUrlBuilder.Scheme = configServerUrl.Scheme; oauthEndpointUrlBuilder.Host = configServerUrl.Host; oauthEndpointUrlBuilder.Port = configServerUrl.Port; Trace.Info($"Set oauth endpoint url's scheme://host:port component to match runner configure url's scheme://host:port: '{oauthEndpointUrlBuilder.Uri.AbsoluteUri}'."); } var credentialData = new CredentialData { Scheme = Constants.Configuration.OAuth, Data = { { "clientId", agent.Authorization.ClientId.ToString("D") }, { "authorizationUrl", agent.Authorization.AuthorizationUrl.AbsoluteUri }, { "oauthEndpointUrl", oauthEndpointUrlBuilder.Uri.AbsoluteUri }, }, }; // Save the negotiated OAuth credential data _store.SaveCredential(credentialData); } else { throw new NotSupportedException("Message queue listen OAuth token."); } // Testing agent connection, detect any potential connection issue, like local clock skew that cause OAuth token expired. var credMgr = HostContext.GetService <ICredentialManager>(); VssCredentials credential = credMgr.LoadCredentials(); try { await _runnerServer.ConnectAsync(new Uri(runnerSettings.ServerUrl), credential); // ConnectAsync() hits _apis/connectionData which is an anonymous endpoint // Need to hit an authenticate endpoint to trigger OAuth token exchange. await _runnerServer.GetAgentPoolsAsync(); _term.WriteSuccessMessage("Runner connection is good"); } catch (VssOAuthTokenRequestException ex) when(ex.Message.Contains("Current server time is")) { // there are two exception messages server send that indicate clock skew. // 1. The bearer token expired on {jwt.ValidTo}. Current server time is {DateTime.UtcNow}. // 2. The bearer token is not valid until {jwt.ValidFrom}. Current server time is {DateTime.UtcNow}. Trace.Error("Catch exception during test agent connection."); Trace.Error(ex); throw new Exception("The local machine's clock may be out of sync with the server time by more than five minutes. Please sync your clock with your domain or internet time and try again."); } _term.WriteSection("Runner settings"); // We will Combine() what's stored with root. Defaults to string a relative path runnerSettings.WorkFolder = command.GetWork(); runnerSettings.MonitorSocketAddress = command.GetMonitorSocketAddress(); _store.SaveSettings(runnerSettings); _term.WriteLine(); _term.WriteSuccessMessage("Settings Saved."); _term.WriteLine(); #if OS_WINDOWS // config windows service bool runAsService = command.GetRunAsService(); if (runAsService) { Trace.Info("Configuring to run the agent as service"); var serviceControlManager = HostContext.GetService <IWindowsServiceControlManager>(); serviceControlManager.ConfigureService(runnerSettings, command); } #elif OS_LINUX || OS_OSX // generate service config script for OSX and Linux, GenerateScripts() will no-opt on windows. var serviceControlManager = HostContext.GetService <ILinuxServiceControlManager>(); serviceControlManager.GenerateScripts(runnerSettings); #endif }
public async Task UnconfigureAsync(CommandSettings command) { string currentAction = string.Empty; _term.WriteSection("Runner removal"); try { //stop, uninstall service and remove service config file if (_store.IsServiceConfigured()) { currentAction = "Removing service"; _term.WriteLine(currentAction); #if OS_WINDOWS var serviceControlManager = HostContext.GetService <IWindowsServiceControlManager>(); serviceControlManager.UnconfigureService(); _term.WriteLine(); _term.WriteSuccessMessage("Runner service removed"); #elif OS_LINUX // unconfig system D service first throw new Exception("Unconfigure service first"); #elif OS_OSX // unconfig osx service first throw new Exception("Unconfigure service first"); #endif } //delete agent from the server currentAction = "Removing runner from the server"; bool isConfigured = _store.IsConfigured(); bool hasCredentials = _store.HasCredentials(); if (isConfigured && hasCredentials) { RunnerSettings settings = _store.GetSettings(); var credentialManager = HostContext.GetService <ICredentialManager>(); // Get the credentials VssCredentials creds = null; if (string.IsNullOrEmpty(settings.GitHubUrl)) { var credProvider = GetCredentialProvider(command, settings.ServerUrl); creds = credProvider.GetVssCredentials(HostContext); Trace.Info("legacy vss cred retrieved"); } else { var githubToken = command.GetRunnerDeletionToken(); GitHubAuthResult authResult = await GetTenantCredential(settings.GitHubUrl, githubToken, Constants.RunnerEvent.Remove); creds = authResult.ToVssCredentials(); Trace.Info("cred retrieved via GitHub auth"); } // Determine the service deployment type based on connection data. (Hosted/OnPremises) bool isHostedServer = await IsHostedServer(settings.ServerUrl, creds); await _runnerServer.ConnectAsync(new Uri(settings.ServerUrl), creds); var agents = await _runnerServer.GetAgentsAsync(settings.PoolId, settings.AgentName); Trace.Verbose("Returns {0} agents", agents.Count); TaskAgent agent = agents.FirstOrDefault(); if (agent == null) { _term.WriteLine("Does not exist. Skipping " + currentAction); } else { await _runnerServer.DeleteAgentAsync(settings.PoolId, settings.AgentId); _term.WriteLine(); _term.WriteSuccessMessage("Runner removed successfully"); } } else { _term.WriteLine("Cannot connect to server, because config files are missing. Skipping removing runner from the server."); } //delete credential config files currentAction = "Removing .credentials"; if (hasCredentials) { _store.DeleteCredential(); var keyManager = HostContext.GetService <IRSAKeyManager>(); keyManager.DeleteKey(); _term.WriteSuccessMessage("Removed .credentials"); } else { _term.WriteLine("Does not exist. Skipping " + currentAction); } //delete settings config file currentAction = "Removing .runner"; if (isConfigured) { _store.DeleteSettings(); _term.WriteSuccessMessage("Removed .runner"); } else { _term.WriteLine("Does not exist. Skipping " + currentAction); } } catch (Exception) { _term.WriteError("Failed: " + currentAction); throw; } _term.WriteLine(); }
public async Task <Boolean> CreateSessionAsync(CancellationToken token) { Trace.Entering(); // Settings var configManager = HostContext.GetService <IConfigurationManager>(); _settings = configManager.LoadSettings(); var serverUrl = _settings.ServerUrl; Trace.Info(_settings); // Create connection. Trace.Info("Loading Credentials"); var credMgr = HostContext.GetService <ICredentialManager>(); VssCredentials creds = credMgr.LoadCredentials(); var agent = new TaskAgentReference { Id = _settings.AgentId, Name = _settings.AgentName, Version = BuildConstants.RunnerPackage.Version, OSDescription = RuntimeInformation.OSDescription, }; string sessionName = $"{Environment.MachineName ?? "RUNNER"}"; var taskAgentSession = new TaskAgentSession(sessionName, agent); string errorMessage = string.Empty; bool encounteringError = false; while (true) { token.ThrowIfCancellationRequested(); Trace.Info($"Attempt to create session."); try { Trace.Info("Connecting to the Runner Server..."); await _runnerServer.ConnectAsync(new Uri(serverUrl), creds); Trace.Info("VssConnection created"); _term.WriteLine(); _term.WriteSuccessMessage("Connected to GitHub"); _term.WriteLine(); _session = await _runnerServer.CreateAgentSessionAsync( _settings.PoolId, taskAgentSession, token); Trace.Info($"Session created."); if (encounteringError) { _term.WriteLine($"{DateTime.UtcNow:u}: Runner reconnected."); _sessionCreationExceptionTracker.Clear(); encounteringError = false; } return(true); } catch (OperationCanceledException) when(token.IsCancellationRequested) { Trace.Info("Session creation has been cancelled."); throw; } catch (TaskAgentAccessTokenExpiredException) { Trace.Info("Runner OAuth token has been revoked. Session creation failed."); throw; } catch (Exception ex) { Trace.Error("Catch exception during create session."); Trace.Error(ex); if (ex is VssOAuthTokenRequestException && creds.Federated is VssOAuthCredential vssOAuthCred) { // Check whether we get 401 because the runner registration already removed by the service. // If the runner registration get deleted, we can't exchange oauth token. Trace.Error("Test oauth app registration."); var oauthTokenProvider = new VssOAuthTokenProvider(vssOAuthCred, new Uri(serverUrl)); var authError = await oauthTokenProvider.ValidateCredentialAsync(token); if (string.Equals(authError, "invalid_client", StringComparison.OrdinalIgnoreCase)) { _term.WriteError("Failed to create a session. The runner registration has been deleted from the server, please re-configure."); return(false); } } if (!IsSessionCreationExceptionRetriable(ex)) { _term.WriteError($"Failed to create session. {ex.Message}"); return(false); } if (!encounteringError) //print the message only on the first error { _term.WriteError($"{DateTime.UtcNow:u}: Runner connect error: {ex.Message}. Retrying until reconnected."); encounteringError = true; } Trace.Info("Sleeping for {0} seconds before retrying.", _sessionCreationRetryInterval.TotalSeconds); await HostContext.Delay(_sessionCreationRetryInterval, token); } } }
private TestHostContext CreateTestContext([CallerMemberName] String testName = "") { var hc = new TestHostContext(this, testName); _jobEc = new Runner.Worker.ExecutionContext(); _actionManager = new Mock <IActionManager>(); _jobServerQueue = new Mock <IJobServerQueue>(); _config = new Mock <IConfigurationStore>(); _logger = new Mock <IPagingLogger>(); _containerProvider = new Mock <IContainerOperationProvider>(); _diagnosticLogManager = new Mock <IDiagnosticLogManager>(); _directoryManager = new Mock <IPipelineDirectoryManager>(); _directoryManager.Setup(x => x.PrepareDirectory(It.IsAny <IExecutionContext>(), It.IsAny <Pipelines.WorkspaceOptions>())) .Returns(new TrackingConfig() { PipelineDirectory = "runner", WorkspaceDirectory = "runner/runner" }); IActionRunner step1 = new ActionRunner(); IActionRunner step2 = new ActionRunner(); IActionRunner step3 = new ActionRunner(); IActionRunner step4 = new ActionRunner(); IActionRunner step5 = new ActionRunner(); _logger.Setup(x => x.Setup(It.IsAny <Guid>(), It.IsAny <Guid>())); var settings = new RunnerSettings { AgentId = 1, AgentName = "runner", ServerUrl = "https://pipelines.actions.githubusercontent.com/abcd", WorkFolder = "_work", }; _config.Setup(x => x.GetSettings()) .Returns(settings); if (_tokenSource != null) { _tokenSource.Dispose(); _tokenSource = null; } _tokenSource = new CancellationTokenSource(); TaskOrchestrationPlanReference plan = new TaskOrchestrationPlanReference(); TimelineReference timeline = new Timeline(Guid.NewGuid()); List <Pipelines.ActionStep> steps = new List <Pipelines.ActionStep>() { new Pipelines.ActionStep() { Id = Guid.NewGuid(), DisplayName = "action1", }, new Pipelines.ActionStep() { Id = Guid.NewGuid(), DisplayName = "action2", }, new Pipelines.ActionStep() { Id = Guid.NewGuid(), DisplayName = "action3", }, new Pipelines.ActionStep() { Id = Guid.NewGuid(), DisplayName = "action4", }, new Pipelines.ActionStep() { Id = Guid.NewGuid(), DisplayName = "action5", } }; Guid jobId = Guid.NewGuid(); _message = new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, "test", "test", null, null, null, new Dictionary <string, VariableValue>(), new List <MaskHint>(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), steps, null, null, null, null); GitHubContext github = new GitHubContext(); github["repository"] = new Pipelines.ContextData.StringContextData("actions/runner"); github["secret_source"] = new Pipelines.ContextData.StringContextData("Actions"); _message.ContextData.Add("github", github); hc.SetSingleton(_actionManager.Object); hc.SetSingleton(_config.Object); hc.SetSingleton(_jobServerQueue.Object); hc.SetSingleton(_containerProvider.Object); hc.SetSingleton(_directoryManager.Object); hc.SetSingleton(_diagnosticLogManager.Object); hc.EnqueueInstance <IPagingLogger>(_logger.Object); // JobExecutionContext hc.EnqueueInstance <IPagingLogger>(_logger.Object); // Initial Job hc.EnqueueInstance <IPagingLogger>(_logger.Object); // step1 hc.EnqueueInstance <IPagingLogger>(_logger.Object); // step2 hc.EnqueueInstance <IPagingLogger>(_logger.Object); // step3 hc.EnqueueInstance <IPagingLogger>(_logger.Object); // step4 hc.EnqueueInstance <IPagingLogger>(_logger.Object); // step5 hc.EnqueueInstance <IPagingLogger>(_logger.Object); // prepare1 hc.EnqueueInstance <IPagingLogger>(_logger.Object); // prepare2 hc.EnqueueInstance <IActionRunner>(step1); hc.EnqueueInstance <IActionRunner>(step2); hc.EnqueueInstance <IActionRunner>(step3); hc.EnqueueInstance <IActionRunner>(step4); hc.EnqueueInstance <IActionRunner>(step5); _jobEc.Initialize(hc); _jobEc.InitializeJob(_message, _tokenSource.Token); return(hc); }
public ReflectionDiscovery(IBenchmarkOutput output, IBenchmarkAssertionRunner benchmarkAssertions, RunnerSettings settings) { Output = _reflectionOutput = output; BenchmarkAssertions = benchmarkAssertions; RunnerSettings = settings; if(RunnerSettings.TracingEnabled) Trace = new BenchmarkOutputTrace(Output); else Trace = NoOpBenchmarkTrace.Instance; }
private TestHostContext CreateTestContext([CallerMemberName] String testName = "") { var hc = new TestHostContext(this, testName); _jobEc = new Runner.Worker.ExecutionContext(); _config = new Mock <IConfigurationStore>(); _extensions = new Mock <IExtensionManager>(); _jobExtension = new Mock <IJobExtension>(); _jobServer = new Mock <IJobServer>(); _jobServerQueue = new Mock <IJobServerQueue>(); _stepRunner = new Mock <IStepsRunner>(); _logger = new Mock <IPagingLogger>(); _temp = new Mock <ITempDirectoryManager>(); _diagnosticLogManager = new Mock <IDiagnosticLogManager>(); if (_tokenSource != null) { _tokenSource.Dispose(); _tokenSource = null; } _tokenSource = new CancellationTokenSource(); var expressionManager = new ExpressionManager(); expressionManager.Initialize(hc); hc.SetSingleton <IExpressionManager>(expressionManager); _jobRunner = new JobRunner(); _jobRunner.Initialize(hc); TaskOrchestrationPlanReference plan = new TaskOrchestrationPlanReference(); TimelineReference timeline = new Timeline(Guid.NewGuid()); Guid jobId = Guid.NewGuid(); _message = new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, testName, testName, null, null, null, new Dictionary <string, VariableValue>(), new List <MaskHint>(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), new List <Pipelines.ActionStep>(), null, null); _message.Variables[Constants.Variables.System.Culture] = "en-US"; _message.Resources.Endpoints.Add(new ServiceEndpoint() { Name = WellKnownServiceEndpointNames.SystemVssConnection, Url = new Uri("https://pipelines.actions.githubusercontent.com"), Authorization = new EndpointAuthorization() { Scheme = "Test", Parameters = { { "AccessToken", "token" } } }, }); _message.Resources.Repositories.Add(new Pipelines.RepositoryResource() { Alias = Pipelines.PipelineConstants.SelfAlias, Id = "github", Version = "sha1" }); _message.ContextData.Add("github", new Pipelines.ContextData.DictionaryContextData()); _initResult.Clear(); _jobExtension.Setup(x => x.InitializeJob(It.IsAny <IExecutionContext>(), It.IsAny <Pipelines.AgentJobRequestMessage>())). Returns(Task.FromResult(_initResult)); var settings = new RunnerSettings { AgentId = 1, AgentName = "agent1", ServerUrl = "https://pipelines.actions.githubusercontent.com", WorkFolder = "_work", }; _config.Setup(x => x.GetSettings()) .Returns(settings); _logger.Setup(x => x.Setup(It.IsAny <Guid>(), It.IsAny <Guid>())); hc.SetSingleton(_config.Object); hc.SetSingleton(_jobServer.Object); hc.SetSingleton(_jobServerQueue.Object); hc.SetSingleton(_stepRunner.Object); hc.SetSingleton(_extensions.Object); hc.SetSingleton(_temp.Object); hc.SetSingleton(_diagnosticLogManager.Object); hc.EnqueueInstance <IExecutionContext>(_jobEc); hc.EnqueueInstance <IPagingLogger>(_logger.Object); hc.EnqueueInstance <IJobExtension>(_jobExtension.Object); return(hc); }
//create worker manager, create message listener and start listening to the queue private async Task <int> RunAsync(RunnerSettings settings, bool runOnce = false) { try { Trace.Info(nameof(RunAsync)); _listener = HostContext.GetService <IMessageListener>(); if (!await _listener.CreateSessionAsync(HostContext.RunnerShutdownToken)) { return(Constants.Runner.ReturnCode.TerminatedError); } HostContext.WritePerfCounter("SessionCreated"); _term.WriteLine($"{DateTime.UtcNow:u}: Listening for Jobs"); IJobDispatcher jobDispatcher = null; CancellationTokenSource messageQueueLoopTokenSource = CancellationTokenSource.CreateLinkedTokenSource(HostContext.RunnerShutdownToken); try { var notification = HostContext.GetService <IJobNotification>(); notification.StartClient(settings.MonitorSocketAddress); bool autoUpdateInProgress = false; Task <bool> selfUpdateTask = null; bool runOnceJobReceived = false; jobDispatcher = HostContext.CreateService <IJobDispatcher>(); while (!HostContext.RunnerShutdownToken.IsCancellationRequested) { TaskAgentMessage message = null; bool skipMessageDeletion = false; try { Task <TaskAgentMessage> getNextMessage = _listener.GetNextMessageAsync(messageQueueLoopTokenSource.Token); if (autoUpdateInProgress) { Trace.Verbose("Auto update task running at backend, waiting for getNextMessage or selfUpdateTask to finish."); Task completeTask = await Task.WhenAny(getNextMessage, selfUpdateTask); if (completeTask == selfUpdateTask) { autoUpdateInProgress = false; if (await selfUpdateTask) { Trace.Info("Auto update task finished at backend, an runner update is ready to apply exit the current runner instance."); Trace.Info("Stop message queue looping."); messageQueueLoopTokenSource.Cancel(); try { await getNextMessage; } catch (Exception ex) { Trace.Info($"Ignore any exception after cancel message loop. {ex}"); } if (runOnce) { return(Constants.Runner.ReturnCode.RunOnceRunnerUpdating); } else { return(Constants.Runner.ReturnCode.RunnerUpdating); } } else { Trace.Info("Auto update task finished at backend, there is no available runner update needs to apply, continue message queue looping."); } } } if (runOnceJobReceived) { Trace.Verbose("One time used runner has start running its job, waiting for getNextMessage or the job to finish."); Task completeTask = await Task.WhenAny(getNextMessage, jobDispatcher.RunOnceJobCompleted.Task); if (completeTask == jobDispatcher.RunOnceJobCompleted.Task) { Trace.Info("Job has finished at backend, the runner will exit since it is running under onetime use mode."); Trace.Info("Stop message queue looping."); messageQueueLoopTokenSource.Cancel(); try { await getNextMessage; } catch (Exception ex) { Trace.Info($"Ignore any exception after cancel message loop. {ex}"); } return(Constants.Runner.ReturnCode.Success); } } message = await getNextMessage; //get next message HostContext.WritePerfCounter($"MessageReceived_{message.MessageType}"); if (string.Equals(message.MessageType, AgentRefreshMessage.MessageType, StringComparison.OrdinalIgnoreCase)) { if (autoUpdateInProgress == false) { autoUpdateInProgress = true; var runnerUpdateMessage = JsonUtility.FromString <AgentRefreshMessage>(message.Body); var selfUpdater = HostContext.GetService <ISelfUpdater>(); selfUpdateTask = selfUpdater.SelfUpdate(runnerUpdateMessage, jobDispatcher, !runOnce && HostContext.StartupType != StartupType.Service, 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, JobRequestMessageTypes.PipelineAgentJobRequest, StringComparison.OrdinalIgnoreCase)) { if (autoUpdateInProgress || runOnceJobReceived) { skipMessageDeletion = true; Trace.Info($"Skip message deletion for job request message '{message.MessageId}'."); } else { var jobMessage = StringUtil.ConvertFromJson <Pipelines.AgentJobRequestMessage>(message.Body); jobDispatcher.Run(jobMessage, runOnce); if (runOnce) { Trace.Info("One time used runner received job message."); runOnceJobReceived = true; } } } else if (string.Equals(message.MessageType, JobCancelMessage.MessageType, StringComparison.OrdinalIgnoreCase)) { var cancelJobMessage = JsonUtility.FromString <JobCancelMessage>(message.Body); bool jobCancelled = jobDispatcher.Cancel(cancelJobMessage); skipMessageDeletion = (autoUpdateInProgress || runOnceJobReceived) && !jobCancelled; if (skipMessageDeletion) { Trace.Info($"Skip message deletion for cancellation message '{message.MessageId}'."); } } else { Trace.Error($"Received message {message.MessageId} with unsupported message type {message.MessageType}."); } } finally { if (!skipMessageDeletion && message != null) { try { await _listener.DeleteMessageAsync(message); } catch (Exception ex) { Trace.Error($"Catch exception during delete message from message queue. message id: {message.MessageId}"); Trace.Error(ex); } finally { message = null; } } } } } finally { if (jobDispatcher != null) { await jobDispatcher.ShutdownAsync(); } //TODO: make sure we don't mask more important exception await _listener.DeleteSessionAsync(); messageQueueLoopTokenSource.Dispose(); } } catch (TaskAgentAccessTokenExpiredException) { Trace.Info("Runner OAuth token has been revoked. Shutting down."); } return(Constants.Runner.ReturnCode.Success); }
public async Task <Boolean> CreateSessionAsync(CancellationToken token) { Trace.Entering(); // Settings var configManager = HostContext.GetService <IConfigurationManager>(); _settings = configManager.LoadSettings(); var serverUrl = _settings.ServerUrl; Trace.Info(_settings); // Create connection. Trace.Info("Loading Credentials"); var credMgr = HostContext.GetService <ICredentialManager>(); VssCredentials creds = credMgr.LoadCredentials(); var agent = new TaskAgentReference { Id = _settings.AgentId, Name = _settings.AgentName, Version = BuildConstants.RunnerPackage.Version, OSDescription = RuntimeInformation.OSDescription, }; string sessionName = $"{Environment.MachineName ?? "RUNNER"}"; var taskAgentSession = new TaskAgentSession(sessionName, agent); string errorMessage = string.Empty; bool encounteringError = false; while (true) { token.ThrowIfCancellationRequested(); Trace.Info($"Attempt to create session."); try { Trace.Info("Connecting to the Runner Server..."); await _runnerServer.ConnectAsync(new Uri(serverUrl), creds); Trace.Info("VssConnection created"); _term.WriteLine(); _term.WriteSuccessMessage("Connected to GitHub"); _term.WriteLine(); _session = await _runnerServer.CreateAgentSessionAsync( _settings.PoolId, taskAgentSession, token); Trace.Info($"Session created."); if (encounteringError) { _term.WriteLine($"{DateTime.UtcNow:u}: Runner reconnected."); _sessionCreationExceptionTracker.Clear(); encounteringError = false; } return(true); } catch (OperationCanceledException) when(token.IsCancellationRequested) { Trace.Info("Session creation has been cancelled."); throw; } catch (TaskAgentAccessTokenExpiredException) { Trace.Info("Runner OAuth token has been revoked. Session creation failed."); throw; } catch (Exception ex) { Trace.Error("Catch exception during create session."); Trace.Error(ex); if (!IsSessionCreationExceptionRetriable(ex)) { _term.WriteError($"Failed to create session. {ex.Message}"); return(false); } if (!encounteringError) //print the message only on the first error { _term.WriteError($"{DateTime.UtcNow:u}: Runner connect error: {ex.Message}. Retrying until reconnected."); encounteringError = true; } Trace.Info("Sleeping for {0} seconds before retrying.", _sessionCreationRetryInterval.TotalSeconds); await HostContext.Delay(_sessionCreationRetryInterval, token); } } }
public ConfigurationManagerL0() { _runnerServer = new Mock <IRunnerServer>(); _locationServer = new Mock <ILocationServer>(); _credMgr = new Mock <ICredentialManager>(); _promptManager = new Mock <IPromptManager>(); _store = new Mock <IConfigurationStore>(); _extnMgr = new Mock <IExtensionManager>(); _rsaKeyManager = new Mock <IRSAKeyManager>(); #if OS_WINDOWS _serviceControlManager = new Mock <IWindowsServiceControlManager>(); #endif #if !OS_WINDOWS _serviceControlManager = new Mock <ILinuxServiceControlManager>(); #endif var expectedAgent = new TaskAgent(_expectedAgentName) { Id = 1 }; expectedAgent.Authorization = new TaskAgentAuthorization { ClientId = Guid.NewGuid(), AuthorizationUrl = new Uri("http://localhost:8080/pipelines"), }; var connectionData = new ConnectionData() { InstanceId = Guid.NewGuid(), DeploymentType = DeploymentFlags.Hosted, DeploymentId = Guid.NewGuid() }; _runnerServer.Setup(x => x.ConnectAsync(It.IsAny <Uri>(), It.IsAny <VssCredentials>())).Returns(Task.FromResult <object>(null)); _locationServer.Setup(x => x.ConnectAsync(It.IsAny <VssConnection>())).Returns(Task.FromResult <object>(null)); _locationServer.Setup(x => x.GetConnectionDataAsync()).Returns(Task.FromResult <ConnectionData>(connectionData)); _store.Setup(x => x.IsConfigured()).Returns(false); _store.Setup(x => x.HasCredentials()).Returns(false); _store.Setup(x => x.GetSettings()).Returns(() => _configMgrAgentSettings); _store.Setup(x => x.SaveSettings(It.IsAny <RunnerSettings>())) .Callback((RunnerSettings settings) => { _configMgrAgentSettings = settings; }); _credMgr.Setup(x => x.GetCredentialProvider(It.IsAny <string>())).Returns(new TestRunnerCredential()); #if !OS_WINDOWS _serviceControlManager.Setup(x => x.GenerateScripts(It.IsAny <RunnerSettings>())); #endif var expectedPools = new List <TaskAgentPool>() { new TaskAgentPool(_expectedPoolName) { Id = _expectedPoolId } }; _runnerServer.Setup(x => x.GetAgentPoolsAsync(It.IsAny <string>(), It.IsAny <TaskAgentPoolType>())).Returns(Task.FromResult(expectedPools)); var expectedAgents = new List <TaskAgent>(); _runnerServer.Setup(x => x.GetAgentsAsync(It.IsAny <int>(), It.IsAny <string>())).Returns(Task.FromResult(expectedAgents)); _runnerServer.Setup(x => x.AddAgentAsync(It.IsAny <int>(), It.IsAny <TaskAgent>())).Returns(Task.FromResult(expectedAgent)); _runnerServer.Setup(x => x.ReplaceAgentAsync(It.IsAny <int>(), It.IsAny <TaskAgent>())).Returns(Task.FromResult(expectedAgent)); rsa = new RSACryptoServiceProvider(2048); _rsaKeyManager.Setup(x => x.CreateKey()).Returns(rsa); }
public async Task <Boolean> CreateSessionAsync(CancellationToken token) { Trace.Entering(); // Settings var configManager = HostContext.GetService <IConfigurationManager>(); _settings = configManager.LoadSettings(); var serverUrl = _settings.ServerUrl; Trace.Info(_settings); // Create connection. Trace.Info("Loading Credentials"); _useMigratedCredentials = !StringUtil.ConvertToBoolean(Environment.GetEnvironmentVariable("GITHUB_ACTIONS_RUNNER_SPSAUTHURL")); VssCredentials creds = _credMgr.LoadCredentials(_useMigratedCredentials); var agent = new TaskAgentReference { Id = _settings.AgentId, Name = _settings.AgentName, Version = BuildConstants.RunnerPackage.Version, OSDescription = RuntimeInformation.OSDescription, }; string sessionName = $"{Environment.MachineName ?? "RUNNER"}"; var taskAgentSession = new TaskAgentSession(sessionName, agent); string errorMessage = string.Empty; bool encounteringError = false; var originalCreds = _configStore.GetCredentials(); var migratedCreds = _configStore.GetMigratedCredentials(); if (migratedCreds == null) { _useMigratedCredentials = false; if (originalCreds.Scheme == Constants.Configuration.OAuth) { _needToCheckAuthorizationUrlUpdate = true; } } while (true) { token.ThrowIfCancellationRequested(); Trace.Info($"Attempt to create session."); try { Trace.Info("Connecting to the Runner Server..."); await _runnerServer.ConnectAsync(new Uri(serverUrl), creds); Trace.Info("VssConnection created"); _term.WriteLine(); _term.WriteSuccessMessage("Connected to GitHub"); _term.WriteLine(); _session = await _runnerServer.CreateAgentSessionAsync( _settings.PoolId, taskAgentSession, token); Trace.Info($"Session created."); if (encounteringError) { _term.WriteLine($"{DateTime.UtcNow:u}: Runner reconnected."); _sessionCreationExceptionTracker.Clear(); encounteringError = false; } if (_needToCheckAuthorizationUrlUpdate) { // start background task try to get new authorization url _authorizationUrlMigrationBackgroundTask = GetNewOAuthAuthorizationSetting(token); } return(true); } catch (OperationCanceledException) when(token.IsCancellationRequested) { Trace.Info("Session creation has been cancelled."); throw; } catch (TaskAgentAccessTokenExpiredException) { Trace.Info("Runner OAuth token has been revoked. Session creation failed."); throw; } catch (Exception ex) { Trace.Error("Catch exception during create session."); Trace.Error(ex); if (!IsSessionCreationExceptionRetriable(ex)) { if (_useMigratedCredentials) { // migrated credentials might cause lose permission during permission check, // we will force to use original credential and try again _useMigratedCredentials = false; var reattemptBackoff = BackoffTimerHelper.GetRandomBackoff(TimeSpan.FromHours(24), TimeSpan.FromHours(36)); _authorizationUrlRollbackReattemptDelayBackgroundTask = HostContext.Delay(reattemptBackoff, token); // retry migrated creds in 24-36 hours. creds = _credMgr.LoadCredentials(false); Trace.Error("Fallback to original credentials and try again."); } else { _term.WriteError($"Failed to create session. {ex.Message}"); return(false); } } if (!encounteringError) //print the message only on the first error { _term.WriteError($"{DateTime.UtcNow:u}: Runner connect error: {ex.Message}. Retrying until reconnected."); encounteringError = true; } Trace.Info("Sleeping for {0} seconds before retrying.", _sessionCreationRetryInterval.TotalSeconds); await HostContext.Delay(_sessionCreationRetryInterval, token); } } }