in src/Agent.Listener/Agent.cs [40:239]
public async Task<int> ExecuteCommand(CommandSettings command)
{
ArgUtil.NotNull(command, nameof(command));
try
{
var agentWebProxy = HostContext.GetService<IVstsAgentWebProxy>();
var agentCertManager = HostContext.GetService<IAgentCertificateManager>();
VssUtil.InitializeVssClientSettings(HostContext.UserAgent, agentWebProxy.WebProxy, agentCertManager.VssClientCertificateManager);
_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 if configured
// TODO: Invalid config prints usage
if (command.IsHelp())
{
PrintUsage(command);
return Constants.Agent.ReturnCode.Success;
}
if (command.IsVersion())
{
_term.WriteLine(BuildConstants.AgentPackage.Version);
return Constants.Agent.ReturnCode.Success;
}
if (command.IsCommit())
{
_term.WriteLine(BuildConstants.Source.CommitHash);
return Constants.Agent.ReturnCode.Success;
}
if (command.IsDiagnostics())
{
PrintBanner();
_term.WriteLine("Running Diagnostics Only...");
_term.WriteLine(string.Empty);
DiagnosticTests diagnostics = new DiagnosticTests(_term);
diagnostics.Execute();
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.IsConfigureCommand())
{
PrintBanner();
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;
}
}
// remove config files, remove service, and exit
if (command.IsRemoveCommand())
{
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;
// warmup agent process (JIT/CLR)
// In scenarios where the agent is single use (used and then thrown away), the system provisioning the agent can call `agent.listener --warmup` before the machine is made available to the pool for use.
// this will optimizes the agent process startup time.
if (command.IsWarmupCommand())
{
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.Agent.ReturnCode.Success;
}
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(command);
return Constants.Agent.ReturnCode.TerminatedError;
}
Trace.Verbose($"Configured as service: '{configuredAsService}'");
//Get the startup type of the agent 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 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.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 agent startup type - {startType}");
HostContext.StartupType = startType;
if (PlatformUtil.RunningOnWindows)
{
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.Listener.exe is launched from the windows service");
}
}
}
// Run the agent interactively or as service
return await RunAsync(settings, command.GetRunOnce());
}
finally
{
_term.CancelKeyPress -= CtrlCHandler;
HostContext.Unloading -= Agent_Unloading;
_completedCommand.Set();
}
}