public ContainerInfo(IHostContext hostContext, Pipelines.ContainerResource container, Boolean isJobContainer = true) { this.ContainerName = container.Alias; string containerImage = container.Properties.Get <string>("image"); ArgUtil.NotNullOrEmpty(containerImage, nameof(containerImage)); this.ContainerImage = containerImage; this.ContainerDisplayName = $"{container.Alias}_{Pipelines.Validation.NameValidation.Sanitize(containerImage)}_{Guid.NewGuid().ToString("N").Substring(0, 6)}"; this.ContainerRegistryEndpoint = container.Endpoint?.Id ?? Guid.Empty; this.ContainerCreateOptions = container.Properties.Get <string>("options"); this.SkipContainerImagePull = container.Properties.Get <bool>("localimage"); _environmentVariables = container.Environment; this.ContainerCommand = container.Properties.Get <string>("command", defaultValue: ""); this.IsJobContainer = isJobContainer; #if OS_WINDOWS _pathMappings[hostContext.GetDirectory(WellKnownDirectory.Tools)] = "C:\\__t"; // Tool cache folder may come from ENV, so we need a unique folder to avoid collision _pathMappings[hostContext.GetDirectory(WellKnownDirectory.Work)] = "C:\\__w"; _pathMappings[hostContext.GetDirectory(WellKnownDirectory.Root)] = "C:\\__a"; // add -v '\\.\pipe\docker_engine:\\.\pipe\docker_engine' when they are available (17.09) #else _pathMappings[hostContext.GetDirectory(WellKnownDirectory.Tools)] = "/__t"; // Tool cache folder may come from ENV, so we need a unique folder to avoid collision _pathMappings[hostContext.GetDirectory(WellKnownDirectory.Work)] = "/__w"; _pathMappings[hostContext.GetDirectory(WellKnownDirectory.Root)] = "/__a"; if (this.IsJobContainer) { this.MountVolumes.Add(new MountVolume("/var/run/docker.sock", "/var/run/docker.sock")); } #endif if (container.Ports?.Count > 0) { foreach (var port in container.Ports) { UserPortMappings[port] = port; } } if (container.Volumes?.Count > 0) { foreach (var volume in container.Volumes) { UserMountVolumes[volume] = volume; } } }
public ContainerInfo(IHostContext hostContext, Pipelines.JobContainer container, bool isJobContainer = true, string networkAlias = null) { this.ContainerName = container.Alias; string containerImage = container.Image; ArgUtil.NotNullOrEmpty(containerImage, nameof(containerImage)); this.ContainerImage = containerImage; this.ContainerDisplayName = $"{container.Alias}_{Pipelines.Validation.NameValidation.Sanitize(containerImage)}_{Guid.NewGuid().ToString("N").Substring(0, 6)}"; this.ContainerCreateOptions = container.Options; _environmentVariables = container.Environment; this.IsJobContainer = isJobContainer; this.ContainerNetworkAlias = networkAlias; #if OS_WINDOWS _pathMappings.Add(new PathMapping(hostContext.GetDirectory(WellKnownDirectory.Work), "C:\\__w")); _pathMappings.Add(new PathMapping(hostContext.GetDirectory(WellKnownDirectory.Tools), "C:\\__t")); // Tool cache folder may come from ENV, so we need a unique folder to avoid collision _pathMappings.Add(new PathMapping(hostContext.GetDirectory(WellKnownDirectory.Externals), "C:\\__e")); // add -v '\\.\pipe\docker_engine:\\.\pipe\docker_engine' when they are available (17.09) #else _pathMappings.Add(new PathMapping(hostContext.GetDirectory(WellKnownDirectory.Work), "/__w")); _pathMappings.Add(new PathMapping(hostContext.GetDirectory(WellKnownDirectory.Tools), "/__t")); // Tool cache folder may come from ENV, so we need a unique folder to avoid collision _pathMappings.Add(new PathMapping(hostContext.GetDirectory(WellKnownDirectory.Externals), "/__e")); if (this.IsJobContainer) { this.MountVolumes.Add(new MountVolume("/var/run/docker.sock", "/var/run/docker.sock")); } #endif if (container.Ports?.Count > 0) { foreach (var port in container.Ports) { UserPortMappings[port] = port; } } if (container.Volumes?.Count > 0) { foreach (var volume in container.Volumes) { UserMountVolumes[volume] = volume; } } }
// Return code definition: (this will be used by service host to determine whether it will re-launch agent.listener) // 0: Agent exit // 1: Terminate failure // 2: Retriable failure // 3: Exit for self update public async static Task <int> MainAsync(IHostContext context, string[] args) { Tracing trace = context.GetTrace("AgentProcess"); trace.Info($"Agent package {BuildConstants.AgentPackage.PackageName}."); trace.Info($"Running on {PlatformUtil.HostOS} ({PlatformUtil.HostArchitecture})."); trace.Info($"RuntimeInformation: {RuntimeInformation.OSDescription}."); context.WritePerfCounter("AgentProcessStarted"); var terminal = context.GetService <ITerminal>(); // TODO: check that the right supporting tools are available for this platform // (replaces the check for build platform vs runtime platform) try { trace.Info($"Version: {BuildConstants.AgentPackage.Version}"); trace.Info($"Commit: {BuildConstants.Source.CommitHash}"); trace.Info($"Culture: {CultureInfo.CurrentCulture.Name}"); trace.Info($"UI Culture: {CultureInfo.CurrentUICulture.Name}"); // Validate directory permissions. string agentDirectory = context.GetDirectory(WellKnownDirectory.Root); trace.Info($"Validating directory permissions for: '{agentDirectory}'"); try { IOUtil.ValidateExecutePermission(agentDirectory); } catch (Exception e) { terminal.WriteError(StringUtil.Loc("ErrorOccurred", e.Message)); trace.Error(e); return(Constants.Agent.ReturnCode.TerminatedError); } if (PlatformUtil.RunningOnWindows) { // Validate PowerShell 3.0 or higher is installed. var powerShellExeUtil = context.GetService <IPowerShellExeUtil>(); try { powerShellExeUtil.GetPath(); } catch (Exception e) { terminal.WriteError(StringUtil.Loc("ErrorOccurred", e.Message)); trace.Error(e); return(Constants.Agent.ReturnCode.TerminatedError); } // Validate .NET Framework 4.5 or higher is installed. if (!NetFrameworkUtil.Test(new Version(4, 5), trace)) { terminal.WriteError(StringUtil.Loc("MinimumNetFramework")); return(Constants.Agent.ReturnCode.TerminatedError); } } // Add environment variables from .env file string envFile = Path.Combine(context.GetDirectory(WellKnownDirectory.Root), ".env"); if (File.Exists(envFile)) { var envContents = File.ReadAllLines(envFile); foreach (var env in envContents) { if (!string.IsNullOrEmpty(env) && env.IndexOf('=') > 0) { string envKey = env.Substring(0, env.IndexOf('=')); string envValue = env.Substring(env.IndexOf('=') + 1); Environment.SetEnvironmentVariable(envKey, envValue); } } } // Parse the command line args. var command = new CommandSettings(context, args); trace.Info("Arguments parsed"); // Up front validation, warn for unrecognized commandline args. var unknownCommandlines = command.Validate(); if (unknownCommandlines.Count > 0) { terminal.WriteError(StringUtil.Loc("UnrecognizedCmdArgs", string.Join(", ", unknownCommandlines))); } // Defer to the Agent class to execute the command. IAgent agent = context.GetService <IAgent>(); try { return(await agent.ExecuteCommand(command)); } catch (OperationCanceledException) when(context.AgentShutdownToken.IsCancellationRequested) { trace.Info("Agent execution been cancelled."); return(Constants.Agent.ReturnCode.Success); } catch (NonRetryableException e) { terminal.WriteError(StringUtil.Loc("ErrorOccurred", e.Message)); trace.Error(e); return(Constants.Agent.ReturnCode.TerminatedError); } } catch (Exception e) { terminal.WriteError(StringUtil.Loc("ErrorOccurred", e.Message)); trace.Error(e); return(Constants.Agent.ReturnCode.RetryableError); } }
public override void Initialize(IHostContext hostContext) { base.Initialize(hostContext); DockerPath = WhichUtil.Which("docker", true, Trace); DockerInstanceLabel = IOUtil.GetPathHash(hostContext.GetDirectory(WellKnownDirectory.Root)).Substring(0, 6); }
// Return code definition: (this will be used by service host to determine whether it will re-launch Runner.Listener) // 0: Runner exit // 1: Terminate failure // 2: Retriable failure // 3: Exit for self update private async static Task <int> MainAsync(IHostContext context, string[] args) { Tracing trace = context.GetTrace(nameof(GitHub.Runner.Listener)); trace.Info($"Runner is built for {Constants.Runner.Platform} ({Constants.Runner.PlatformArchitecture}) - {BuildConstants.RunnerPackage.PackageName}."); trace.Info($"RuntimeInformation: {RuntimeInformation.OSDescription}."); context.WritePerfCounter("RunnerProcessStarted"); var terminal = context.GetService <ITerminal>(); // Validate the binaries intended for one OS are not running on a different OS. switch (Constants.Runner.Platform) { case Constants.OSPlatform.Linux: if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { terminal.WriteLine("This runner version is built for Linux. Please install a correct build for your OS."); return(Constants.Runner.ReturnCode.TerminatedError); } break; case Constants.OSPlatform.OSX: if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { terminal.WriteLine("This runner version is built for OSX. Please install a correct build for your OS."); return(Constants.Runner.ReturnCode.TerminatedError); } break; case Constants.OSPlatform.Windows: if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { terminal.WriteLine("This runner version is built for Windows. Please install a correct build for your OS."); return(Constants.Runner.ReturnCode.TerminatedError); } break; default: terminal.WriteLine($"Running the runner on this platform is not supported. The current platform is {RuntimeInformation.OSDescription} and it was built for {Constants.Runner.Platform.ToString()}."); return(Constants.Runner.ReturnCode.TerminatedError); } try { trace.Info($"Version: {BuildConstants.RunnerPackage.Version}"); trace.Info($"Commit: {BuildConstants.Source.CommitHash}"); trace.Info($"Culture: {CultureInfo.CurrentCulture.Name}"); trace.Info($"UI Culture: {CultureInfo.CurrentUICulture.Name}"); // Validate directory permissions. string runnerDirectory = context.GetDirectory(WellKnownDirectory.Root); trace.Info($"Validating directory permissions for: '{runnerDirectory}'"); try { IOUtil.ValidateExecutePermission(runnerDirectory); } catch (Exception e) { terminal.WriteError($"An error occurred: {e.Message}"); trace.Error(e); return(Constants.Runner.ReturnCode.TerminatedError); } // Parse the command line args. var command = new CommandSettings(context, args); trace.Info("Arguments parsed"); // Up front validation, warn for unrecognized commandline args. var unknownCommandlines = command.Validate(); if (unknownCommandlines.Count > 0) { terminal.WriteError($"Unrecognized command-line input arguments: '{string.Join(", ", unknownCommandlines)}'. For usage refer to: .\\config.cmd --help or ./config.sh --help"); } // Defer to the Runner class to execute the command. IRunner runner = context.GetService <IRunner>(); try { return(await runner.ExecuteCommand(command)); } catch (OperationCanceledException) when(context.RunnerShutdownToken.IsCancellationRequested) { trace.Info("Runner execution been cancelled."); return(Constants.Runner.ReturnCode.Success); } catch (NonRetryableException e) { terminal.WriteError($"An error occurred: {e.Message}"); trace.Error(e); return(Constants.Runner.ReturnCode.TerminatedError); } } catch (Exception e) { terminal.WriteError($"An error occurred: {e.Message}"); trace.Error(e); return(Constants.Runner.ReturnCode.RetryableError); } }
// Return code definition: (this will be used by service host to determine whether it will re-launch agent.listener) // 0: Agent exit // 1: Terminate failure // 2: Retriable failure // 3: Exit for self update public async static Task <int> MainAsync(IHostContext context, string[] args) { Tracing trace = context.GetTrace("AgentProcess"); trace.Info($"Agent is built for {Constants.Agent.Platform} - {BuildConstants.AgentPackage.PackageName}."); trace.Info($"RuntimeInformation: {RuntimeInformation.OSDescription}."); var terminal = context.GetService <ITerminal>(); // Validate the binaries intended for one OS are not running on a different OS. switch (Constants.Agent.Platform) { case Constants.OSPlatform.Linux: if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { terminal.WriteLine(StringUtil.Loc("NotLinux")); return(Constants.Agent.ReturnCode.TerminatedError); } break; case Constants.OSPlatform.OSX: if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { terminal.WriteLine(StringUtil.Loc("NotOSX")); return(Constants.Agent.ReturnCode.TerminatedError); } break; case Constants.OSPlatform.Windows: if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { terminal.WriteLine(StringUtil.Loc("NotWindows")); return(Constants.Agent.ReturnCode.TerminatedError); } break; default: terminal.WriteLine(StringUtil.Loc("PlatformNotSupport", RuntimeInformation.OSDescription, Constants.Agent.Platform.ToString())); return(Constants.Agent.ReturnCode.TerminatedError); } try { trace.Info($"Version: {Constants.Agent.Version}"); trace.Info($"Commit: {BuildConstants.Source.CommitHash}"); trace.Info($"Culture: {CultureInfo.CurrentCulture.Name}"); trace.Info($"UI Culture: {CultureInfo.CurrentUICulture.Name}"); // Validate directory permissions. string agentDirectory = context.GetDirectory(WellKnownDirectory.Root); trace.Info($"Validating directory permissions for: '{agentDirectory}'"); try { IOUtil.ValidateExecutePermission(agentDirectory); } catch (Exception e) { terminal.WriteError(StringUtil.Loc("ErrorOccurred", e.Message)); trace.Error(e); return(Constants.Agent.ReturnCode.TerminatedError); } #if OS_WINDOWS // Validate PowerShell 3.0 or higher is installed. var powerShellExeUtil = context.GetService <IPowerShellExeUtil>(); try { powerShellExeUtil.GetPath(); } catch (Exception e) { terminal.WriteError(StringUtil.Loc("ErrorOccurred", e.Message)); trace.Error(e); return(Constants.Agent.ReturnCode.TerminatedError); } // Validate .NET Framework 4.5 or higher is installed. var netFrameworkUtil = context.GetService <INetFrameworkUtil>(); if (!netFrameworkUtil.Test(new Version(4, 5))) { terminal.WriteError(StringUtil.Loc("MinimumNetFramework")); return(Constants.Agent.ReturnCode.TerminatedError); } #endif // Add environment variables from .env file string envFile = Path.Combine(context.GetDirectory(WellKnownDirectory.Root), ".env"); if (File.Exists(envFile)) { var envContents = File.ReadAllLines(envFile); foreach (var env in envContents) { if (!string.IsNullOrEmpty(env) && env.IndexOf('=') > 0) { string envKey = env.Substring(0, env.IndexOf('=')); string envValue = env.Substring(env.IndexOf('=') + 1); Environment.SetEnvironmentVariable(envKey, envValue); } } } // Parse the command line args. var command = new CommandSettings(context, args); trace.Info("Arguments parsed"); // Up front validation, warn for unrecognized commandline args. var unknownCommandlines = command.Validate(); if (unknownCommandlines.Count > 0) { terminal.WriteError(StringUtil.Loc("UnrecognizedCmdArgs", string.Join(", ", unknownCommandlines))); } // Defer to the Agent class to execute the command. IAgent agent = context.GetService <IAgent>(); try { return(await agent.ExecuteCommand(command)); } catch (OperationCanceledException) when(context.AgentShutdownToken.IsCancellationRequested) { trace.Info("Agent execution been cancelled."); return(Constants.Agent.ReturnCode.Success); } catch (NonRetryableException e) { terminal.WriteError(StringUtil.Loc("ErrorOccurred", e.Message)); trace.Error(e); return(Constants.Agent.ReturnCode.TerminatedError); } } catch (Exception e) { terminal.WriteError(StringUtil.Loc("ErrorOccurred", e.Message)); trace.Error(e); return(Constants.Agent.ReturnCode.RetryableError); } }
private static string GetEnvironmentVariableUsingPs(Process process, IHostContext hostContext, string variable) { // On OSX, there is no /proc folder for us to read environment for given process, // So we have call `ps e -p <pid> -o command` to print out env to STDOUT, // However, the output env are not format in a parseable way, it's just a string that concatenate all envs with space, // It doesn't escape '=' or ' ', so we can't parse the output into a dictionary of all envs. // So we only look for the env you request, in the format of variable=value. (it won't work if you variable contains = or space) var trace = hostContext.GetTrace(nameof(ProcessExtensions)); trace.Info($"Read env from output of `ps e -p {process.Id} -o command`"); Dictionary <string, string> env = new Dictionary <string, string>(); List <string> psOut = new List <string>(); object outputLock = new object(); using (var p = hostContext.CreateService <IProcessInvoker>()) { p.OutputDataReceived += delegate(object sender, ProcessDataReceivedEventArgs stdout) { if (!string.IsNullOrEmpty(stdout.Data)) { lock (outputLock) { psOut.Add(stdout.Data); } } }; p.ErrorDataReceived += delegate(object sender, ProcessDataReceivedEventArgs stderr) { if (!string.IsNullOrEmpty(stderr.Data)) { lock (outputLock) { trace.Error(stderr.Data); } } }; int exitCode = p.ExecuteAsync(workingDirectory: hostContext.GetDirectory(WellKnownDirectory.Root), fileName: "ps", arguments: $"e -p {process.Id} -o command", environment: null, cancellationToken: CancellationToken.None).GetAwaiter().GetResult(); if (exitCode == 0) { trace.Info($"Successfully dump environment variables for {process.Id}"); if (psOut.Count > 0) { string psOutputString = string.Join(" ", psOut); trace.Verbose($"ps output: '{psOutputString}'"); int varStartIndex = psOutputString.IndexOf(variable, StringComparison.Ordinal); if (varStartIndex >= 0) { string rightPart = psOutputString.Substring(varStartIndex + variable.Length + 1); if (rightPart.IndexOf(' ') > 0) { string value = rightPart.Substring(0, rightPart.IndexOf(' ')); env[variable] = value; } else { env[variable] = rightPart; } trace.Verbose($"PID:{process.Id} ({variable}={env[variable]})"); } } } } if (env.TryGetValue(variable, out string envVariable)) { return(envVariable); } else { return(null); } }
public static async Task <CheckResult> DownloadExtraCA(this IHostContext hostContext, string url, string pat) { var result = new CheckResult(); try { result.Logs.Add($"{DateTime.UtcNow.ToString("O")} ***************************************************************************************************************"); result.Logs.Add($"{DateTime.UtcNow.ToString("O")} **** ****"); result.Logs.Add($"{DateTime.UtcNow.ToString("O")} **** Download SSL Certificate from {url} "); result.Logs.Add($"{DateTime.UtcNow.ToString("O")} **** ****"); result.Logs.Add($"{DateTime.UtcNow.ToString("O")} ***************************************************************************************************************"); var uri = new Uri(url); var env = new Dictionary <string, string>() { { "HOSTNAME", uri.Host }, { "PORT", uri.IsDefaultPort ? (uri.Scheme.ToLowerInvariant() == "https" ? "443" : "80") : uri.Port.ToString() }, { "PATH", uri.AbsolutePath }, { "PAT", pat } }; var proxy = hostContext.WebProxy.GetProxy(uri); if (proxy != null) { env["PROXYHOST"] = proxy.Host; env["PROXYPORT"] = proxy.IsDefaultPort ? (proxy.Scheme.ToLowerInvariant() == "https" ? "443" : "80") : proxy.Port.ToString(); if (hostContext.WebProxy.HttpProxyUsername != null || hostContext.WebProxy.HttpsProxyUsername != null) { env["PROXYUSERNAME"] = hostContext.WebProxy.HttpProxyUsername ?? hostContext.WebProxy.HttpsProxyUsername; env["PROXYPASSWORD"] = hostContext.WebProxy.HttpProxyPassword ?? hostContext.WebProxy.HttpsProxyPassword; } else { env["PROXYUSERNAME"] = ""; env["PROXYPASSWORD"] = ""; } } else { env["PROXYHOST"] = ""; env["PROXYPORT"] = ""; env["PROXYUSERNAME"] = ""; env["PROXYPASSWORD"] = ""; } using (var processInvoker = hostContext.CreateService <IProcessInvoker>()) { processInvoker.OutputDataReceived += new EventHandler <ProcessDataReceivedEventArgs>((sender, args) => { if (!string.IsNullOrEmpty(args.Data)) { result.Logs.Add($"{DateTime.UtcNow.ToString("O")} [STDOUT] {args.Data}"); } }); processInvoker.ErrorDataReceived += new EventHandler <ProcessDataReceivedEventArgs>((sender, args) => { if (!string.IsNullOrEmpty(args.Data)) { result.Logs.Add($"{DateTime.UtcNow.ToString("O")} [STDERR] {args.Data}"); } }); var downloadCertScript = Path.Combine(hostContext.GetDirectory(WellKnownDirectory.Bin), "checkScripts", "downloadCert"); var node12 = Path.Combine(hostContext.GetDirectory(WellKnownDirectory.Externals), "node12", "bin", $"node{IOUtil.ExeExtension}"); result.Logs.Add($"{DateTime.UtcNow.ToString("O")} Run '{node12} \"{downloadCertScript}\"' "); result.Logs.Add($"{DateTime.UtcNow.ToString("O")} {StringUtil.ConvertToJson(env)}"); await processInvoker.ExecuteAsync( hostContext.GetDirectory(WellKnownDirectory.Root), node12, $"\"{downloadCertScript}\"", env, true, CancellationToken.None); } result.Pass = true; } catch (Exception ex) { result.Pass = false; result.Logs.Add($"{DateTime.UtcNow.ToString("O")} ***************************************************************************************************************"); result.Logs.Add($"{DateTime.UtcNow.ToString("O")} **** ****"); result.Logs.Add($"{DateTime.UtcNow.ToString("O")} **** Download SSL Certificate from '{url}' failed with error: {ex}"); result.Logs.Add($"{DateTime.UtcNow.ToString("O")} **** ****"); result.Logs.Add($"{DateTime.UtcNow.ToString("O")} ***************************************************************************************************************"); } return(result); }
// Return code definition: (this will be used by service host to determine whether it will re-launch agent.listener) // 0: Agent exit // 1: Terminate failure // 2: Retriable failure // 3: Exit for self update private async static Task <int> MainAsync(IHostContext context, string[] args) { Tracing trace = context.GetTrace("AgentProcess"); trace.Info($"Agent package {BuildConstants.AgentPackage.PackageName}."); trace.Info($"Running on {PlatformUtil.HostOS} ({PlatformUtil.HostArchitecture})."); trace.Info($"RuntimeInformation: {RuntimeInformation.OSDescription}."); context.WritePerfCounter("AgentProcessStarted"); var terminal = context.GetService <ITerminal>(); // TODO: check that the right supporting tools are available for this platform // (replaces the check for build platform vs runtime platform) try { trace.Info($"Version: {BuildConstants.AgentPackage.Version}"); trace.Info($"Commit: {BuildConstants.Source.CommitHash}"); trace.Info($"Culture: {CultureInfo.CurrentCulture.Name}"); trace.Info($"UI Culture: {CultureInfo.CurrentUICulture.Name}"); // Validate directory permissions. string agentDirectory = context.GetDirectory(WellKnownDirectory.Root); trace.Info($"Validating directory permissions for: '{agentDirectory}'"); try { IOUtil.ValidateExecutePermission(agentDirectory); } catch (Exception e) { terminal.WriteError(StringUtil.Loc("ErrorOccurred", e.Message)); trace.Error(e); return(Constants.Agent.ReturnCode.TerminatedError); } if (PlatformUtil.UseLegacyHttpHandler) { trace.Warning($"You are using the legacy HTTP handler because you set ${AgentKnobs.LegacyHttpVariableName}."); trace.Warning($"This feature will go away with .NET 5.0, and we recommend you don't use it."); trace.Warning($"If you continue using it, you must ensure libcurl is installed on your system."); } if (PlatformUtil.RunningOnWindows) { // Validate PowerShell 3.0 or higher is installed. var powerShellExeUtil = context.GetService <IPowerShellExeUtil>(); try { powerShellExeUtil.GetPath(); } catch (Exception e) { terminal.WriteError(StringUtil.Loc("ErrorOccurred", e.Message)); trace.Error(e); return(Constants.Agent.ReturnCode.TerminatedError); } // Validate .NET Framework 4.5 or higher is installed. if (!NetFrameworkUtil.Test(new Version(4, 5), trace)) { terminal.WriteError(StringUtil.Loc("MinimumNetFramework")); // warn only, like configurationmanager.cs does. this enables windows edition with just .netcore to work } // Upgrade process priority to avoid Listener starvation using (Process p = Process.GetCurrentProcess()) { try { p.PriorityClass = ProcessPriorityClass.AboveNormal; } catch (Exception e) { trace.Warning("Unable to change Windows process priority"); trace.Warning(e.Message); } } } // Add environment variables from .env file string envFile = Path.Combine(context.GetDirectory(WellKnownDirectory.Root), ".env"); if (File.Exists(envFile)) { var envContents = File.ReadAllLines(envFile); foreach (var env in envContents) { if (!string.IsNullOrEmpty(env) && env.IndexOf('=') > 0) { string envKey = env.Substring(0, env.IndexOf('=')); string envValue = env.Substring(env.IndexOf('=') + 1); Environment.SetEnvironmentVariable(envKey, envValue); } } } // Parse the command line args. var command = new CommandSettings(context, args, new SystemEnvironment()); trace.Info("Arguments parsed"); // Print any Parse Errros if (command.ParseErrors?.Any() == true) { List <string> errorStr = new List <string>(); foreach (var error in command.ParseErrors) { if (error is TokenError tokenError) { errorStr.Add(tokenError.Token); } else { // Unknown type of error dump to log terminal.WriteError(StringUtil.Loc("ErrorOccurred", error.Tag)); } } terminal.WriteError( StringUtil.Loc("UnrecognizedCmdArgs", string.Join(", ", errorStr))); } // Defer to the Agent class to execute the command. IAgent agent = context.GetService <IAgent>(); try { return(await agent.ExecuteCommand(command)); } catch (OperationCanceledException) when(context.AgentShutdownToken.IsCancellationRequested) { trace.Info("Agent execution been cancelled."); return(Constants.Agent.ReturnCode.Success); } catch (NonRetryableException e) { terminal.WriteError(StringUtil.Loc("ErrorOccurred", e.Message)); trace.Error(e); return(Constants.Agent.ReturnCode.TerminatedError); } } catch (Exception e) { terminal.WriteError(StringUtil.Loc("ErrorOccurred", e.Message)); trace.Error(e); return(Constants.Agent.ReturnCode.RetryableError); } }
// Return code definition: (this will be used by service host to determine whether it will re-launch agent.listener) // 0: Agent exit // 1: Terminate failure // 2: Retriable failure // 3: Exit for self update public async static Task <int> MainAsync(IHostContext context, string[] args) { Tracing trace = context.GetTrace("AgentProcess"); trace.Info($"Agent is built for {Constants.Agent.Platform} - {BuildConstants.AgentPackage.PackageName}."); trace.Info($"RuntimeInformation: {RuntimeInformation.OSDescription}."); var terminal = context.GetService <ITerminal>(); // Validate the binaries intended for one OS are not running on a different OS. switch (Constants.Agent.Platform) { case Constants.OSPlatform.Linux: if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { terminal.WriteLine(StringUtil.Loc("NotLinux")); return(Constants.Agent.ReturnCode.TerminatedError); } break; case Constants.OSPlatform.OSX: if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { terminal.WriteLine(StringUtil.Loc("NotOSX")); return(Constants.Agent.ReturnCode.TerminatedError); } break; case Constants.OSPlatform.Windows: if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { terminal.WriteLine(StringUtil.Loc("NotWindows")); return(Constants.Agent.ReturnCode.TerminatedError); } break; default: terminal.WriteLine(StringUtil.Loc("PlatformNotSupport", RuntimeInformation.OSDescription, Constants.Agent.Platform.ToString())); return(Constants.Agent.ReturnCode.TerminatedError); } try { trace.Info($"Version: {Constants.Agent.Version}"); trace.Info($"Commit: {BuildConstants.Source.CommitHash}"); trace.Info($"Culture: {CultureInfo.CurrentCulture.Name}"); trace.Info($"UI Culture: {CultureInfo.CurrentUICulture.Name}"); // Validate directory permissions. string agentDirectory = context.GetDirectory(WellKnownDirectory.Root); trace.Info($"Validating directory permissions for: '{agentDirectory}'"); try { IOUtil.ValidateExecutePermission(agentDirectory); } catch (Exception e) { terminal.WriteError(StringUtil.Loc("ErrorOccurred", e.Message)); trace.Error(e); return(Constants.Agent.ReturnCode.TerminatedError); } // Parse the command line args. var command = new CommandSettings(context, args); trace.Info("Arguments parsed"); // Defer to the Agent class to execute the command. IAgent agent = context.GetService <IAgent>(); using (agent.TokenSource = new CancellationTokenSource()) { try { return(await agent.ExecuteCommand(command)); } catch (OperationCanceledException) when(agent.TokenSource.IsCancellationRequested) { trace.Info("Agent execution been cancelled."); return(Constants.Agent.ReturnCode.Success); } catch (NonRetryableException e) { terminal.WriteError(StringUtil.Loc("ErrorOccurred", e.Message)); trace.Error(e); return(Constants.Agent.ReturnCode.TerminatedError); } } } catch (Exception e) { terminal.WriteError(StringUtil.Loc("ErrorOccurred", e.Message)); trace.Error(e); return(Constants.Agent.ReturnCode.RetryableError); } }