public async Task <int> ExecuteCommand(CommandSettings command) { try { var agentWebProxy = HostContext.GetService <IVstsAgentWebProxy>(); VssHttpMessageHandler.DefaultWebProxy = agentWebProxy; _inConfigStage = true; _completedCommand.Reset(); _term.CancelKeyPress += CtrlCHandler; //register a SIGTERM handler HostContext.Unloading += Agent_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 and/or configures if not configured // TODO: Invalid config prints usage if (command.Help) { PrintUsage(); return(Constants.Agent.ReturnCode.Success); } if (command.Version) { _term.WriteLine(Constants.Agent.Version); return(Constants.Agent.ReturnCode.Success); } if (command.Commit) { _term.WriteLine(BuildConstants.Source.CommitHash); return(Constants.Agent.ReturnCode.Success); } // Configure agent prompt for args if not supplied // Unattend 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.Agent.ReturnCode.Success); } catch (Exception ex) { Trace.Error(ex); _term.WriteError(ex.Message); return(Constants.Agent.ReturnCode.TerminatedError); } } // Unconfigure, remove config files, service and exit if (command.Unconfigure) { try { await configManager.UnconfigureAsync(command); return(Constants.Agent.ReturnCode.Success); } catch (Exception ex) { Trace.Error(ex); _term.WriteError(ex.Message); return(Constants.Agent.ReturnCode.TerminatedError); } } _inConfigStage = false; // YAML string yamlFile = command.GetYaml(); if (!string.IsNullOrEmpty(yamlFile)) { _term.WriteLine("Local run mode is currently experimental. The interface and behavior will change in a future version."); if (!command.Unattended) { _term.WriteLine("Press Enter to continue."); _term.ReadLine(); } HostContext.RunMode = RunMode.Local; command.SetUnattended(); var localRunner = HostContext.GetService <ILocalRunner>(); return(await localRunner.RunAsync(command, HostContext.AgentShutdownToken)); } AgentSettings settings = configManager.LoadSettings(); var store = HostContext.GetService <IConfigurationStore>(); bool configuredAsService = store.IsServiceConfigured(); // Run agent //if (command.Run) // this line is current break machine provisioner. //{ // Error if agent not configured. if (!configManager.IsConfigured()) { _term.WriteError(StringUtil.Loc("AgentIsNotConfigured")); PrintUsage(); return(Constants.Agent.ReturnCode.TerminatedError); } Trace.Verbose($"Configured as service: '{configuredAsService}'"); //Get the startup type of the agent i.e., autostartup, service, manualinteractive 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 agent autoupgrade, which result an old version service host binary but a newer version agent binary // At that time the servicehost won't pass --startuptype to agent.listener while the agent is actually running as service. // We will guess the startup type only when the agent is configured as service and the guess will based on whether STDOUT/STDERR/STDIN been redirect or not Trace.Info($"Try determine agent startup type base on console redirects."); startType = (Console.IsErrorRedirected && Console.IsInputRedirected && Console.IsOutputRedirected) ? StartupType.Service : StartupType.ManualInteractive; } else { if (!Enum.TryParse(startupTypeAsString, true, out startType)) { Trace.Info($"Could not parse the argument value '{startupTypeAsString}' for StartupType. Defaulting to {StartupType.ManualInteractive}"); startType = StartupType.ManualInteractive; } } Trace.Info($"Set agent startup type - {startType}"); HostContext.StartupType = startType; #if OS_WINDOWS if (store.IsAutoLogonConfigured()) { if (HostContext.StartupType != StartupType.Service) { Trace.Info($"Autologon is configured on the machine, dumping all the autologon related registry settings"); var autoLogonRegManager = HostContext.GetService <IAutoLogonRegistryManager>(); autoLogonRegManager.DumpAutoLogonRegistrySettings(); } else { Trace.Info($"Autologon is configured on the machine but current Agent.Listner.exe is launched from the windows service"); } } #endif // Run the agent interactively or as service return(await RunAsync(settings)); } finally { _term.CancelKeyPress -= CtrlCHandler; HostContext.Unloading -= Agent_Unloading; _completedCommand.Set(); } }
public async Task <int> RunAsync(CommandSettings command, CancellationToken token) { Trace.Info(nameof(RunAsync)); var configStore = HostContext.GetService <IConfigurationStore>(); AgentSettings settings = configStore.GetSettings(); // Store the HTTP client. // todo: fix in master to allow URL to be empty and then rebase on master. const string DefaultUrl = "http://127.0.0.1/local-runner-default-url"; string url = command.GetUrl(DefaultUrl); if (!string.Equals(url, DefaultUrl, StringComparison.Ordinal)) { var credentialManager = HostContext.GetService <ICredentialManager>(); string authType = command.GetAuth(defaultValue: Constants.Configuration.Integrated); ICredentialProvider provider = credentialManager.GetCredentialProvider(authType); provider.EnsureCredential(HostContext, command, url); _httpClient = new TaskAgentHttpClient(new Uri(url), provider.GetVssCredentials(HostContext)); } // Load the YAML file. string yamlFile = command.GetYaml(); ArgUtil.File(yamlFile, nameof(yamlFile)); var parseOptions = new ParseOptions { MaxFiles = 10, MustacheEvaluationMaxResultLength = 512 * 1024, // 512k string length MustacheEvaluationTimeout = TimeSpan.FromSeconds(10), MustacheMaxDepth = 5, }; var pipelineParser = new PipelineParser(new PipelineTraceWriter(), new PipelineFileProvider(), parseOptions); Pipelines.Process process = pipelineParser.Load( defaultRoot: Directory.GetCurrentDirectory(), path: yamlFile, mustacheContext: null, cancellationToken: HostContext.AgentShutdownToken); ArgUtil.NotNull(process, nameof(process)); if (command.WhatIf) { return(Constants.Agent.ReturnCode.Success); } // Create job message. IJobDispatcher jobDispatcher = null; try { jobDispatcher = HostContext.CreateService <IJobDispatcher>(); foreach (JobInfo job in await ConvertToJobMessagesAsync(process, token)) { job.RequestMessage.Environment.Variables[Constants.Variables.Agent.RunMode] = RunMode.Local.ToString(); jobDispatcher.Run(job.RequestMessage); Task jobDispatch = jobDispatcher.WaitAsync(token); if (!Task.WaitAll(new[] { jobDispatch }, job.Timeout)) { jobDispatcher.Cancel(job.CancelMessage); // Finish waiting on the same job dispatch task. The first call to WaitAsync dequeues // the dispatch task and then proceeds to wait on it. So we need to continue awaiting // the task instance (queue is now empty). await jobDispatch; } } } finally { if (jobDispatcher != null) { await jobDispatcher.ShutdownAsync(); } } return(Constants.Agent.ReturnCode.Success); }