/// <summary>Spawn avatar if spawn point is registered</summary> /// <param name="playerNo">Number of the avatar to spawn</param> /// <param name="active">If true, the avatar is spawned active</param> /// <param name="allowControl">If true, the avatar is immediately controllable</param> public GameObject SpawnCharacter(int playerNo, bool allowControl = true) { Transform spawnTr = spawnPoints[playerNo - 1]; if (spawnTr == null) { throw ExceptionsUtil.CreateExceptionFormat("No spawn point registered for player {0}.", playerNo); } // activate stored avatar instance and move to spawn position CharacterMaster avatar = m_Characters[playerNo - 1]; avatar.gameObject.SetActive(true); avatar.transform.position = spawnTr.position; // stop control if needed (prefab should have CharacterControl active by default) if (!allowControl) { avatar.GetComponentOrFail <CharacterControl>().StopControl(); } // update derivated counter of avatars registered and active in the scene remainingCharacterNb++; // return the spawned avatar as a game object return(avatar.gameObject); }
public async Task ConnectAsync(VssConnection jobConnection) { ArgUtil.NotNull(jobConnection, nameof(jobConnection)); _connection = jobConnection; int attemptCount = 5; while (!_connection.HasAuthenticated && attemptCount-- > 0) { try { await _connection.ConnectAsync(); break; } catch (SocketException ex) { ExceptionsUtil.HandleSocketException(ex, _connection.Uri.ToString(), Trace.Error); } catch (Exception ex) when(attemptCount > 0) { Trace.Info($"Catch exception during connect. {attemptCount} attemp left."); Trace.Error(ex); } await Task.Delay(100); } _releaseHttpClient = _connection.GetClient <ReleaseHttpClient>(); }
public void PublishEvent(IExecutionContext context, CustomerIntelligenceEvent ciEvent) { ICustomerIntelligenceServer ciService; VssConnection vssConnection; try { ciService = context.GetHostContext().GetService <ICustomerIntelligenceServer>(); vssConnection = WorkerUtilities.GetVssConnection(context); ciService.Initialize(vssConnection); } catch (SocketException ex) { ExceptionsUtil.HandleSocketException(ex, WorkerUtilities.GetVssConnection(context).Uri.ToString(), context.Warning); return; } catch (Exception ex) { context.Warning(StringUtil.Loc("TelemetryCommandFailed", ex.Message)); return; } var commandContext = context.GetHostContext().CreateService <IAsyncCommandContext>(); commandContext.InitializeCommandContext(context, StringUtil.Loc("Telemetry")); commandContext.Task = PublishEventsAsync(context, ciService, ciEvent); }
public async Task <Stream> GetFileTask(ContainerItem ticketedItem, CancellationToken cancellationToken) { ArgUtil.NotNull(ticketedItem, nameof(ticketedItem)); this._executionContext.Debug(StringUtil.Format("Get file container client for file {0}", ticketedItem.Path)); VssConnection vssConnection = await GetVssConnection(); FileContainerHttpClient fileContainer = null; try { fileContainer = vssConnection.GetClient <FileContainerHttpClient>(); } catch (SocketException e) { ExceptionsUtil.HandleSocketException(e, vssConnection.Uri.ToString(), this._executionContext.Error); throw; } this._executionContext.Debug(StringUtil.Format("Start fetch file stream from filecontainer service for file {0}", ticketedItem.Path)); Stream stream = await fileContainer.DownloadFileAsync( ticketedItem.ContainerId, ticketedItem.Path, cancellationToken, scopeIdentifier : ticketedItem.ScopeIdentifier); this._executionContext.Debug(StringUtil.Format("Finished fetch file stream from filecontainer service for file {0}", ticketedItem.Path)); return(stream); }
public void Spawn(Vector2 position) { if (IsInUse()) { throw ExceptionsUtil.CreateExceptionFormat("Cannot spawn {0}, already in use.", this); } gameObject.SetActive(true); transform.position = position; }
private static async Task <int> MainAsync(IHostContext context, string[] args) { //ITerminal registers a CTRL-C handler, which keeps the Agent.Worker process running //and lets the Agent.Listener handle gracefully the exit. var term = context.GetService <ITerminal>(); Tracing trace = context.GetTrace(nameof(Program)); 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}"); context.WritePerfCounter("WorkerProcessStarted"); // Validate args. ArgUtil.NotNull(args, nameof(args)); ArgUtil.Equal(3, args.Length, nameof(args.Length)); ArgUtil.NotNullOrEmpty(args[0], $"{nameof(args)}[0]"); ArgUtil.Equal("spawnclient", args[0].ToLowerInvariant(), $"{nameof(args)}[0]"); ArgUtil.NotNullOrEmpty(args[1], $"{nameof(args)}[1]"); ArgUtil.NotNullOrEmpty(args[2], $"{nameof(args)}[2]"); var worker = context.GetService <IWorker>(); // Run the worker. return(await worker.RunAsync( pipeIn : args[1], pipeOut : args[2])); } catch (AggregateException ex) { ExceptionsUtil.HandleAggregateException((AggregateException)ex, trace.Error); } catch (Exception ex) { // Populate any exception that cause worker failure back to agent. Console.WriteLine(ex.ToString()); try { trace.Error(ex); } catch (Exception e) { // make sure we don't crash the app on trace error. // since IOException will throw when we run out of disk space. Console.WriteLine(e.ToString()); } } return(1); }
/// <inheritdoc /> public async Task <bool> InitializeAsync(IAgentLogPluginContext context) { try { _logger = _logger ?? new TraceLogger(context); _telemetry = _telemetry ?? new TelemetryDataCollector(new ClientFactory(context.VssConnection), _logger); await PopulatePipelineConfig(context); if (DisablePlugin(context)) { _telemetry.AddOrUpdate(TelemetryConstants.PluginDisabled, true); await _telemetry.PublishCumulativeTelemetryAsync(); return(false); // disable the plugin } _testFilePublisher = _testFilePublisher ?? new TestFilePublisher(context.VssConnection, PipelineConfig, new TestFileTraceListener(context), _logger, _telemetry); await _testFilePublisher.InitializeAsync(); _telemetry.AddOrUpdate(TelemetryConstants.PluginInitialized, true); } catch (SocketException ex) { ExceptionsUtil.HandleSocketException(ex, context.VssConnection.Uri.ToString(), _logger.Warning); if (_telemetry != null) { _telemetry.AddOrUpdate(TelemetryConstants.PluginDisabled, true); _telemetry.AddOrUpdate(TelemetryConstants.InitializeFailed, ex); await _telemetry.PublishCumulativeTelemetryAsync(); } return(false); } catch (Exception ex) { context.Trace(ex.ToString()); _logger?.Warning($"Unable to initialize {FriendlyName}."); if (_telemetry != null) { _telemetry.AddOrUpdate(TelemetryConstants.PluginDisabled, true); _telemetry.AddOrUpdate(TelemetryConstants.InitializeFailed, ex); await _telemetry.PublishCumulativeTelemetryAsync(); } return(false); } return(true); }
public LoginForm() { try { frmLoadingApplication.Show(); this.srvLoginAttempt = SamsaraAppContext.Resolve <ILoginAttemptService>(); frmLoadingApplication.Close(); InitializeComponent(); this.StartPosition = FormStartPosition.CenterScreen; } catch (Exception ex) { MessageBox.Show(ExceptionsUtil.InnerExceptionsMessages(ex)); Environment.Exit(1); } }
private Uri GetTenantAuthorityUrl(IHostContext context, string serverUrl) { Tracing trace = context.GetTrace(nameof(AadDeviceCodeAccessToken)); using (var handler = context.CreateHttpClientHandler()) using (var client = new HttpClient(handler)) { client.DefaultRequestHeaders.Accept.Clear(); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); client.DefaultRequestHeaders.Add("X-TFS-FedAuthRedirect", "Suppress"); client.DefaultRequestHeaders.UserAgent.Clear(); client.DefaultRequestHeaders.UserAgent.AddRange(VssClientHttpRequestSettings.Default.UserAgent); using (var requestMessage = new HttpRequestMessage(HttpMethod.Head, $"{serverUrl.Trim('/')}/_apis/connectiondata")) { HttpResponseMessage response; try { response = client.SendAsync(requestMessage).GetAwaiter().GetResult(); } catch (SocketException e) { ExceptionsUtil.HandleSocketException(e, serverUrl, trace.Error); throw; } // Get the tenant from the Login URL, MSA backed accounts will not return `Bearer` www-authenticate header. var bearerResult = response.Headers.WwwAuthenticate.Where(p => p.Scheme.Equals("Bearer", StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); if (bearerResult != null && bearerResult.Parameter.StartsWith("authorization_uri=", StringComparison.OrdinalIgnoreCase)) { var authorizationUri = bearerResult.Parameter.Substring("authorization_uri=".Length); if (Uri.TryCreate(authorizationUri, UriKind.Absolute, out Uri aadTenantUrl)) { return(aadTenantUrl); } } return(null); } } }
private async Task DownloadArtifactsAndCommitsAsync(IExecutionContext executionContext, object data) { Trace.Entering(); try { await DownloadArtifacts(executionContext, ReleaseArtifacts, ArtifactsWorkingFolder); await DownloadCommits(executionContext, TeamProjectId, ReleaseArtifacts); } catch (SocketException ex) { LogDownloadFailureTelemetry(executionContext, ex); ExceptionsUtil.HandleSocketException(ex, WorkerUtilities.GetVssConnection(executionContext).Uri.ToString(), Trace.Error); throw; } catch (Exception ex) { LogDownloadFailureTelemetry(executionContext, ex); throw; } }
private async Task ShutdownQueue(bool throwOnFailure) { if (_jobServerQueue != null) { try { Trace.Info("Shutting down the job server queue."); await _jobServerQueue.ShutdownAsync(); } catch (AggregateException ex) { ExceptionsUtil.HandleAggregateException((AggregateException)ex, Trace.Error); } catch (Exception ex) when(!throwOnFailure) { Trace.Error($"Caught exception from {nameof(JobServerQueue)}.{nameof(_jobServerQueue.ShutdownAsync)}"); Trace.Error(ex); } finally { _jobServerQueue = null; // Prevent multiple attempts. } } }
private async Task <string> GetAzureSubscriptionIdAsync() { // We will use the Azure Instance Metadata Service in order to fetch metadata ( in this case Subscription Id used to provision the VM) if the VM is an Azure VM // More on Instance Metadata Service can be found here: https://docs.microsoft.com/en-us/azure/virtual-machines/windows/instance-metadata-service string azureSubscriptionId = string.Empty; const string imdsUri = "http://169.254.169.254/metadata/instance/compute/subscriptionId?api-version=2017-08-01&format=text"; using (var handler = HostContext.CreateHttpClientHandler()) using (var httpClient = new HttpClient(handler)) { httpClient.DefaultRequestHeaders.Add("Metadata", "True"); httpClient.Timeout = TimeSpan.FromSeconds(5); try { azureSubscriptionId = await httpClient.GetStringAsync(imdsUri); if (!Guid.TryParse(azureSubscriptionId, out Guid result)) { azureSubscriptionId = string.Empty; } } catch (SocketException ex) { azureSubscriptionId = string.Empty; ExceptionsUtil.HandleSocketException(ex, imdsUri, Trace.Info); } catch (Exception ex) { // An exception will be thrown if the Agent Machine is a non-Azure VM. azureSubscriptionId = string.Empty; Trace.Info($"GetAzureSubscriptionId ex: {ex.Message}"); } } return(azureSubscriptionId); }
public async Task ConnectAsync(VssConnection jobConnection) { ArgUtil.NotNull(jobConnection, nameof(jobConnection)); _connection = jobConnection; try { await _connection.ConnectAsync(); } catch (SocketException ex) { ExceptionsUtil.HandleSocketException(ex, _connection.Uri.ToString(), Trace.Error); throw; } catch (Exception ex) { Trace.Info($"Unable to connect to {_connection.Uri}."); Trace.Error(ex); throw; } _locationClient = _connection.GetClient <LocationHttpClient>(); _hasConnection = true; }
public async Task ConfigureAsync(CommandSettings command) { ArgUtil.NotNull(command, nameof(command)); if (PlatformUtil.RunningOnWindows) { CheckAgentRootDirectorySecure(); } Trace.Info(nameof(ConfigureAsync)); if (IsConfigured()) { throw new InvalidOperationException(StringUtil.Loc("AlreadyConfiguredError")); } // Populate proxy setting from commandline args var vstsProxy = HostContext.GetService <IVstsAgentWebProxy>(); bool saveProxySetting = false; string proxyUrl = command.GetProxyUrl(); if (!string.IsNullOrEmpty(proxyUrl)) { if (!Uri.IsWellFormedUriString(proxyUrl, UriKind.Absolute)) { throw new ArgumentOutOfRangeException(nameof(proxyUrl)); } Trace.Info("Reset proxy base on commandline args."); string proxyUserName = command.GetProxyUserName(); string proxyPassword = command.GetProxyPassword(); (vstsProxy as VstsAgentWebProxy).SetupProxy(proxyUrl, proxyUserName, proxyPassword); saveProxySetting = true; } // Populate cert setting from commandline args var agentCertManager = HostContext.GetService <IAgentCertificateManager>(); bool saveCertSetting = false; bool skipCertValidation = command.GetSkipCertificateValidation(); string caCert = command.GetCACertificate(); string clientCert = command.GetClientCertificate(); string clientCertKey = command.GetClientCertificatePrivateKey(); string clientCertArchive = command.GetClientCertificateArchrive(); string clientCertPassword = command.GetClientCertificatePassword(); // We require all Certificate files are under agent root. // So we can set ACL correctly when configure as service if (!string.IsNullOrEmpty(caCert)) { caCert = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), caCert); ArgUtil.File(caCert, nameof(caCert)); } if (!string.IsNullOrEmpty(clientCert) && !string.IsNullOrEmpty(clientCertKey) && !string.IsNullOrEmpty(clientCertArchive)) { // Ensure all client cert pieces are there. clientCert = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), clientCert); clientCertKey = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), clientCertKey); clientCertArchive = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), clientCertArchive); ArgUtil.File(clientCert, nameof(clientCert)); ArgUtil.File(clientCertKey, nameof(clientCertKey)); ArgUtil.File(clientCertArchive, nameof(clientCertArchive)); } else if (!string.IsNullOrEmpty(clientCert) || !string.IsNullOrEmpty(clientCertKey) || !string.IsNullOrEmpty(clientCertArchive)) { // Print out which args are missing. ArgUtil.NotNullOrEmpty(Constants.Agent.CommandLine.Args.SslClientCert, Constants.Agent.CommandLine.Args.SslClientCert); ArgUtil.NotNullOrEmpty(Constants.Agent.CommandLine.Args.SslClientCertKey, Constants.Agent.CommandLine.Args.SslClientCertKey); ArgUtil.NotNullOrEmpty(Constants.Agent.CommandLine.Args.SslClientCertArchive, Constants.Agent.CommandLine.Args.SslClientCertArchive); } if (skipCertValidation || !string.IsNullOrEmpty(caCert) || !string.IsNullOrEmpty(clientCert)) { Trace.Info("Reset agent cert setting base on commandline args."); (agentCertManager as AgentCertificateManager).SetupCertificate(skipCertValidation, caCert, clientCert, clientCertKey, clientCertArchive, clientCertPassword); saveCertSetting = true; } AgentSettings agentSettings = new AgentSettings(); // TEE EULA agentSettings.AcceptTeeEula = false; switch (PlatformUtil.HostOS) { case PlatformUtil.OS.OSX: case PlatformUtil.OS.Linux: // Write the section header. WriteSection(StringUtil.Loc("EulasSectionHeader")); // Verify the EULA exists on disk in the expected location. string eulaFile = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), "license.html"); ArgUtil.File(eulaFile, nameof(eulaFile)); // Write elaborate verbiage about the TEE EULA. _term.WriteLine(StringUtil.Loc("TeeEula", eulaFile)); _term.WriteLine(); // Prompt to acccept the TEE EULA. agentSettings.AcceptTeeEula = command.GetAcceptTeeEula(); break; case PlatformUtil.OS.Windows: // Warn and continue if .NET 4.6 is not installed. if (!NetFrameworkUtil.Test(new Version(4, 6), Trace)) { WriteSection(StringUtil.Loc("PrerequisitesSectionHeader")); // Section header. _term.WriteLine(StringUtil.Loc("MinimumNetFrameworkTfvc")); // Warning. } break; default: throw new NotSupportedException(); } // Create the configuration provider as per agent type. string agentType; if (command.GetDeploymentOrMachineGroup()) { agentType = Constants.Agent.AgentConfigurationProvider.DeploymentAgentConfiguration; } else if (command.GetDeploymentPool()) { agentType = Constants.Agent.AgentConfigurationProvider.SharedDeploymentAgentConfiguration; } else if (command.GetEnvironmentVMResource()) { agentType = Constants.Agent.AgentConfigurationProvider.EnvironmentVMResourceConfiguration; } else { agentType = Constants.Agent.AgentConfigurationProvider.BuildReleasesAgentConfiguration; } var extensionManager = HostContext.GetService <IExtensionManager>(); IConfigurationProvider agentProvider = (extensionManager.GetExtensions <IConfigurationProvider>()) .FirstOrDefault(x => x.ConfigurationProviderType == agentType); ArgUtil.NotNull(agentProvider, agentType); bool isHostedServer = false; // Loop getting url and creds until you can connect ICredentialProvider credProvider = null; VssCredentials creds = null; WriteSection(StringUtil.Loc("ConnectSectionHeader")); while (true) { // Get the URL agentProvider.GetServerUrl(agentSettings, command); // Get the credentials credProvider = GetCredentialProvider(command, agentSettings.ServerUrl); creds = credProvider.GetVssCredentials(HostContext); Trace.Info("cred retrieved"); try { // Determine the service deployment type based on connection data. (Hosted/OnPremises) await _serverUtil.DetermineDeploymentType(agentSettings.ServerUrl, creds, _locationServer); if (!_serverUtil.TryGetDeploymentType(out isHostedServer)) { Trace.Warning(@"Deployment type determination has been failed; assume it is OnPremises and the deployment type determination was not implemented for this server version."); } // Get the collection name for deployment group agentProvider.GetCollectionName(agentSettings, command, isHostedServer); // Validate can connect. await agentProvider.TestConnectionAsync(agentSettings, creds, isHostedServer); Trace.Info("Test Connection complete."); break; } catch (SocketException e) { ExceptionsUtil.HandleSocketException(e, agentSettings.ServerUrl, _term.WriteError); } catch (Exception e) when(!command.Unattended()) { _term.WriteError(e); _term.WriteError(StringUtil.Loc("FailedToConnect")); } } // 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); } // Loop getting agent name and pool name WriteSection(StringUtil.Loc("RegisterAgentSectionHeader")); while (true) { try { await agentProvider.GetPoolIdAndName(agentSettings, command); break; } catch (Exception e) when(!command.Unattended()) { _term.WriteError(e); _term.WriteError(agentProvider.GetFailedToFindPoolErrorString()); } } TaskAgent agent; while (true) { agentSettings.AgentName = command.GetAgentName(); // Get the system capabilities. // TODO: Hook up to ctrl+c cancellation token. _term.WriteLine(StringUtil.Loc("ScanToolCapabilities")); Dictionary <string, string> systemCapabilities = await HostContext.GetService <ICapabilitiesManager>().GetCapabilitiesAsync(agentSettings, CancellationToken.None); _term.WriteLine(StringUtil.Loc("ConnectToServer")); agent = await agentProvider.GetAgentAsync(agentSettings); if (agent != null) { if (command.GetReplace()) { // Update existing agent with new PublicKey, agent version and SystemCapabilities. agent = UpdateExistingAgent(agent, publicKey, systemCapabilities); try { agent = await agentProvider.UpdateAgentAsync(agentSettings, agent, command); _term.WriteLine(StringUtil.Loc("AgentReplaced")); break; } catch (Exception e) when(!command.Unattended()) { _term.WriteError(e); _term.WriteError(StringUtil.Loc("FailedToReplaceAgent")); } } else if (command.Unattended()) { // if not replace and it is unattended config. agentProvider.ThrowTaskAgentExistException(agentSettings); } } else { // Create a new agent. agent = CreateNewAgent(agentSettings.AgentName, publicKey, systemCapabilities); try { agent = await agentProvider.AddAgentAsync(agentSettings, agent, command); _term.WriteLine(StringUtil.Loc("AgentAddedSuccessfully")); break; } catch (Exception e) when(!command.Unattended()) { _term.WriteError(e); _term.WriteError(StringUtil.Loc("AddAgentFailed")); } } } // Add Agent Id to settings agentSettings.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(agentSettings.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}'."); agentSettings.ServerUrl = inputServerUrl.Uri.AbsoluteUri; } else { agentSettings.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) { // We use authorizationUrl as the oauth endpoint url by default. // For TFS, we need make sure the Schema/Host/Port component of the oauth endpoint url also match configuration url. (Incase of customer's agent configure URL and TFS server public URL are different) // Which means, we will keep use the original authorizationUrl in the VssOAuthJwtBearerClientCredential (authorizationUrl is the audience), // But might have different Url in VssOAuthCredential (connection url) // We can't do this for VSTS, since its SPS/TFS urls are different. UriBuilder configServerUrl = new UriBuilder(agentSettings.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 agent 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 { switch (PlatformUtil.HostOS) { case PlatformUtil.OS.OSX: case PlatformUtil.OS.Linux: // Save the provided admin cred for compat with previous agent. _store.SaveCredential(credProvider.CredentialData); break; case PlatformUtil.OS.Windows: // Not supported against TFS 2015. _term.WriteError(StringUtil.Loc("Tfs2015NotSupported")); return; default: throw new NotSupportedException(); } } // Testing agent connection, detect any protential connection issue, like local clock skew that cause OAuth token expired. _term.WriteLine(StringUtil.Loc("TestAgentConnection")); var credMgr = HostContext.GetService <ICredentialManager>(); VssCredentials credential = credMgr.LoadCredentials(); var agentSvr = HostContext.GetService <IAgentServer>(); try { await agentSvr.ConnectAsync(new Uri(agentSettings.ServerUrl), credential); } 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 InvalidOperationException(StringUtil.Loc("LocalClockSkewed")); } catch (SocketException ex) { ExceptionsUtil.HandleSocketException(ex, agentSettings.ServerUrl, Trace.Error); throw; } // We will Combine() what's stored with root. Defaults to string a relative path agentSettings.WorkFolder = command.GetWork(); // notificationPipeName for Hosted agent provisioner. agentSettings.NotificationPipeName = command.GetNotificationPipeName(); agentSettings.MonitorSocketAddress = command.GetMonitorSocketAddress(); agentSettings.NotificationSocketAddress = command.GetNotificationSocketAddress(); agentSettings.DisableLogUploads = command.GetDisableLogUploads(); agentSettings.AlwaysExtractTask = command.GetAlwaysExtractTask(); _store.SaveSettings(agentSettings); if (saveProxySetting) { Trace.Info("Save proxy setting to disk."); (vstsProxy as VstsAgentWebProxy).SaveProxySetting(); } if (saveCertSetting) { Trace.Info("Save agent cert setting to disk."); (agentCertManager as AgentCertificateManager).SaveCertificateSetting(); } _term.WriteLine(StringUtil.Loc("SavedSettings", DateTime.UtcNow)); bool saveRuntimeOptions = false; var runtimeOptions = new AgentRuntimeOptions(); if (PlatformUtil.RunningOnWindows && command.GetGitUseSChannel()) { saveRuntimeOptions = true; runtimeOptions.GitUseSecureChannel = true; } if (saveRuntimeOptions) { Trace.Info("Save agent runtime options to disk."); _store.SaveAgentRuntimeOptions(runtimeOptions); } if (PlatformUtil.RunningOnWindows) { // config windows service bool runAsService = command.GetRunAsService(); if (runAsService) { Trace.Info("Configuring to run the agent as service"); var serviceControlManager = HostContext.GetService <IWindowsServiceControlManager>(); agentSettings.EnableServiceSidTypeUnrestricted = command.GetEnableServiceSidTypeUnrestricted(); serviceControlManager.ConfigureService(agentSettings, command); } // config auto logon else if (command.GetRunAsAutoLogon()) { Trace.Info("Agent is going to run as process setting up the 'AutoLogon' capability for the agent."); var autoLogonConfigManager = HostContext.GetService <IAutoLogonManager>(); await autoLogonConfigManager.ConfigureAsync(command); //Important: The machine may restart if the autologon user is not same as the current user //if you are adding code after this, keep that in mind } } else if (PlatformUtil.RunningOnLinux) { // generate service config script for Linux var serviceControlManager = HostContext.GetService <ILinuxServiceControlManager>(); serviceControlManager.GenerateScripts(agentSettings); } else if (PlatformUtil.RunningOnMacOS) { // generate service config script for macOS var serviceControlManager = HostContext.GetService <IMacOSServiceControlManager>(); serviceControlManager.GenerateScripts(agentSettings); } }
public async Task UnconfigureAsync(CommandSettings command) { ArgUtil.NotNull(command, nameof(command)); string currentAction = string.Empty; try { //stop, uninstall service and remove service config file if (_store.IsServiceConfigured()) { currentAction = StringUtil.Loc("UninstallingService"); _term.WriteLine(currentAction); if (PlatformUtil.RunningOnWindows) { var serviceControlManager = HostContext.GetService <IWindowsServiceControlManager>(); serviceControlManager.UnconfigureService(); _term.WriteLine(StringUtil.Loc("Success") + currentAction); } else if (PlatformUtil.RunningOnLinux) { // unconfig systemd service first throw new InvalidOperationException(StringUtil.Loc("UnconfigureServiceDService")); } else if (PlatformUtil.RunningOnMacOS) { // unconfig macOS service first throw new InvalidOperationException(StringUtil.Loc("UnconfigureOSXService")); } } else { if (PlatformUtil.RunningOnWindows) { //running as process, unconfigure autologon if it was configured if (_store.IsAutoLogonConfigured()) { currentAction = StringUtil.Loc("UnconfigAutologon"); _term.WriteLine(currentAction); var autoLogonConfigManager = HostContext.GetService <IAutoLogonManager>(); autoLogonConfigManager.Unconfigure(); _term.WriteLine(StringUtil.Loc("Success") + currentAction); } else { Trace.Info("AutoLogon was not configured on the agent."); } } } //delete agent from the server currentAction = StringUtil.Loc("UnregisteringAgent"); _term.WriteLine(currentAction); bool isConfigured = _store.IsConfigured(); bool hasCredentials = _store.HasCredentials(); if (isConfigured && hasCredentials) { AgentSettings settings = _store.GetSettings(); var credentialManager = HostContext.GetService <ICredentialManager>(); // Get the credentials var credProvider = GetCredentialProvider(command, settings.ServerUrl); VssCredentials creds = credProvider.GetVssCredentials(HostContext); Trace.Info("cred retrieved"); bool isEnvironmentVMResource = false; bool isDeploymentGroup = (settings.MachineGroupId > 0) || (settings.DeploymentGroupId > 0); if (!isDeploymentGroup) { isEnvironmentVMResource = settings.EnvironmentId > 0; } Trace.Info("Agent configured for deploymentGroup : {0}", isDeploymentGroup.ToString()); string agentType = isDeploymentGroup ? Constants.Agent.AgentConfigurationProvider.DeploymentAgentConfiguration : isEnvironmentVMResource ? Constants.Agent.AgentConfigurationProvider.EnvironmentVMResourceConfiguration : Constants.Agent.AgentConfigurationProvider.BuildReleasesAgentConfiguration; var extensionManager = HostContext.GetService <IExtensionManager>(); IConfigurationProvider agentProvider = (extensionManager.GetExtensions <IConfigurationProvider>()).FirstOrDefault(x => x.ConfigurationProviderType == agentType); ArgUtil.NotNull(agentProvider, agentType); // Determine the service deployment type based on connection data. (Hosted/OnPremises) bool isHostedServer; await _serverUtil.DetermineDeploymentType(settings.ServerUrl, creds, _locationServer); if (!_serverUtil.TryGetDeploymentType(out isHostedServer)) { Trace.Warning(@"Deployment type determination has been failed; assume it is OnPremises and the deployment type determination was not implemented for this server version."); } await agentProvider.TestConnectionAsync(settings, creds, isHostedServer); TaskAgent agent = await agentProvider.GetAgentAsync(settings); if (agent == null) { _term.WriteLine(StringUtil.Loc("Skipping") + currentAction); } else { await agentProvider.DeleteAgentAsync(settings); _term.WriteLine(StringUtil.Loc("Success") + currentAction); } } else { _term.WriteLine(StringUtil.Loc("MissingConfig")); } //delete credential config files currentAction = StringUtil.Loc("DeletingCredentials"); _term.WriteLine(currentAction); if (hasCredentials) { _store.DeleteCredential(); var keyManager = HostContext.GetService <IRSAKeyManager>(); keyManager.DeleteKey(); _term.WriteLine(StringUtil.Loc("Success") + currentAction); } else { _term.WriteLine(StringUtil.Loc("Skipping") + currentAction); } //delete settings config file currentAction = StringUtil.Loc("DeletingSettings"); _term.WriteLine(currentAction); if (isConfigured) { // delete proxy setting (HostContext.GetService <IVstsAgentWebProxy>() as VstsAgentWebProxy).DeleteProxySetting(); // delete agent cert setting (HostContext.GetService <IAgentCertificateManager>() as AgentCertificateManager).DeleteCertificateSetting(); // delete agent runtime option _store.DeleteAgentRuntimeOptions(); _store.DeleteSettings(); _term.WriteLine(StringUtil.Loc("Success") + currentAction); } else { _term.WriteLine(StringUtil.Loc("Skipping") + currentAction); } } catch (SocketException ex) { ExceptionsUtil.HandleSocketException(ex, _store.GetSettings().ServerUrl, _term.WriteLine); throw; } catch (Exception) { _term.WriteLine(StringUtil.Loc("Failed") + currentAction); throw; } }
/// <summary> /// _work /// \_update /// \bin /// \externals /// \run.sh /// \run.cmd /// \package.zip //temp download .zip/.tar.gz /// </summary> /// <param name="token"></param> /// <returns></returns> private async Task DownloadLatestAgent(CancellationToken token) { string latestAgentDirectory = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Work), Constants.Path.UpdateDirectory); IOUtil.DeleteDirectory(latestAgentDirectory, token); Directory.CreateDirectory(latestAgentDirectory); int agentSuffix = 1; string archiveFile = null; bool downloadSucceeded = false; bool validationSucceeded = false; try { // Download the agent, using multiple attempts in order to be resilient against any networking/CDN issues for (int attempt = 1; attempt <= Constants.AgentDownloadRetryMaxAttempts && !validationSucceeded; attempt++) { // Generate an available package name, and do our best effort to clean up stale local zip files while (true) { if (_targetPackage.Platform.StartsWith("win")) { archiveFile = Path.Combine(latestAgentDirectory, $"agent{agentSuffix}.zip"); } else { archiveFile = Path.Combine(latestAgentDirectory, $"agent{agentSuffix}.tar.gz"); } // The package name is generated, check if there is already a file with the same name and path if (!string.IsNullOrEmpty(archiveFile) && File.Exists(archiveFile)) { Trace.Verbose("Deleting latest agent package zip '{0}'", archiveFile); try { // Such a file already exists, so try deleting it IOUtil.DeleteFile(archiveFile); // The file was successfully deleted, so we can use the generated package name break; } catch (Exception ex) { // Couldn't delete the file for whatever reason, so generate another package name Trace.Warning("Failed to delete agent package zip '{0}'. Exception: {1}", archiveFile, ex); agentSuffix++; } } else { // There is no a file with the same name and path, so we can use the generated package name break; } } // Allow a 15-minute package download timeout, which is good enough to update the agent from a 1 Mbit/s ADSL connection. var timeoutSeconds = AgentKnobs.AgentDownloadTimeout.GetValue(_knobContext).AsInt(); Trace.Info($"Attempt {attempt}: save latest agent into {archiveFile}."); using (var downloadTimeout = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutSeconds))) using (var downloadCts = CancellationTokenSource.CreateLinkedTokenSource(downloadTimeout.Token, token)) { try { Trace.Info($"Download agent: begin download"); //open zip stream in async mode using (var handler = HostContext.CreateHttpClientHandler()) using (var httpClient = new HttpClient(handler)) using (var fs = new FileStream(archiveFile, FileMode.Create, FileAccess.Write, FileShare.None, bufferSize: 4096, useAsync: true)) using (var result = await httpClient.GetStreamAsync(_targetPackage.DownloadUrl)) { //81920 is the default used by System.IO.Stream.CopyTo and is under the large object heap threshold (85k). await result.CopyToAsync(fs, 81920, downloadCts.Token); await fs.FlushAsync(downloadCts.Token); } Trace.Info($"Download agent: finished download"); downloadSucceeded = true; validationSucceeded = HashValidation(archiveFile); } catch (OperationCanceledException) when(token.IsCancellationRequested) { Trace.Info($"Agent download has been canceled."); throw; } catch (SocketException ex) { ExceptionsUtil.HandleSocketException(ex, _targetPackage.DownloadUrl, Trace.Warning); } catch (Exception ex) { if (downloadCts.Token.IsCancellationRequested) { Trace.Warning($"Agent download has timed out after {timeoutSeconds} seconds"); } Trace.Warning($"Failed to get package '{archiveFile}' from '{_targetPackage.DownloadUrl}'. Exception {ex}"); } } } if (!downloadSucceeded) { throw new TaskCanceledException($"Agent package '{archiveFile}' failed after {Constants.AgentDownloadRetryMaxAttempts} download attempts."); } if (!validationSucceeded) { throw new TaskCanceledException(@"Agent package checksum validation failed. There are possible reasons why this happened: 1) The agent package was compromised. 2) The agent package was not fully downloaded or was corrupted during the download process. You can skip checksum validation for the agent package by setting the environment variable DISABLE_HASH_VALIDATION=true"); } // If we got this far, we know that we've successfully downloadeded the agent package if (archiveFile.EndsWith(".zip", StringComparison.OrdinalIgnoreCase)) { ZipFile.ExtractToDirectory(archiveFile, latestAgentDirectory); } else if (archiveFile.EndsWith(".tar.gz", StringComparison.OrdinalIgnoreCase)) { string tar = WhichUtil.Which("tar", trace: Trace); if (string.IsNullOrEmpty(tar)) { throw new NotSupportedException($"tar -xzf"); } // tar -xzf using (var processInvoker = HostContext.CreateService <IProcessInvoker>()) { processInvoker.OutputDataReceived += new EventHandler <ProcessDataReceivedEventArgs>((sender, args) => { if (!string.IsNullOrEmpty(args.Data)) { Trace.Info(args.Data); } }); processInvoker.ErrorDataReceived += new EventHandler <ProcessDataReceivedEventArgs>((sender, args) => { if (!string.IsNullOrEmpty(args.Data)) { Trace.Error(args.Data); } }); int exitCode = await processInvoker.ExecuteAsync(latestAgentDirectory, tar, $"-xzf \"{archiveFile}\"", null, token); if (exitCode != 0) { throw new NotSupportedException($"Can't use 'tar -xzf' extract archive file: {archiveFile}. return code: {exitCode}."); } } } else { throw new NotSupportedException($"{archiveFile}"); } Trace.Info($"Finished getting latest agent package at: {latestAgentDirectory}."); } finally { try { // delete .zip file if (!string.IsNullOrEmpty(archiveFile) && File.Exists(archiveFile)) { Trace.Verbose("Deleting latest agent package zip: {0}", archiveFile); IOUtil.DeleteFile(archiveFile); } } catch (Exception ex) { //it is not critical if we fail to delete the .zip file Trace.Warning("Failed to delete agent package zip '{0}'. Exception: {1}", archiveFile, ex); } } if (!String.IsNullOrEmpty(AgentKnobs.DisableAuthenticodeValidation.GetValue(HostContext).AsString())) { Trace.Warning("Authenticode validation skipped for downloaded agent package since it is disabled currently by agent settings."); } else { if (PlatformUtil.RunningOnWindows) { var isValid = this.VerifyAgentAuthenticode(latestAgentDirectory); if (!isValid) { throw new Exception("Authenticode validation of agent assemblies failed."); } else { Trace.Info("Authenticode validation of agent assemblies passed successfully."); } } else { Trace.Info("Authenticode validation skipped since it's not supported on non-Windows platforms at the moment."); } } // copy latest agent into agent root folder // copy bin from _work/_update -> bin.version under root string binVersionDir = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), $"{Constants.Path.BinDirectory}.{_targetPackage.Version}"); Directory.CreateDirectory(binVersionDir); Trace.Info($"Copy {Path.Combine(latestAgentDirectory, Constants.Path.BinDirectory)} to {binVersionDir}."); IOUtil.CopyDirectory(Path.Combine(latestAgentDirectory, Constants.Path.BinDirectory), binVersionDir, token); // copy externals from _work/_update -> externals.version under root string externalsVersionDir = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), $"{Constants.Path.ExternalsDirectory}.{_targetPackage.Version}"); Directory.CreateDirectory(externalsVersionDir); Trace.Info($"Copy {Path.Combine(latestAgentDirectory, Constants.Path.ExternalsDirectory)} to {externalsVersionDir}."); IOUtil.CopyDirectory(Path.Combine(latestAgentDirectory, Constants.Path.ExternalsDirectory), externalsVersionDir, token); // copy and replace all .sh/.cmd files Trace.Info($"Copy any remaining .sh/.cmd files into agent root."); foreach (FileInfo file in new DirectoryInfo(latestAgentDirectory).GetFiles() ?? new FileInfo[0]) { // Copy and replace the file. file.CopyTo(Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), file.Name), true); } // for windows service back compat with old windows agent, we need make sure the servicehost.exe is still the old name // if the current bin folder has VsoAgentService.exe, then the new agent bin folder needs VsoAgentService.exe as well if (PlatformUtil.RunningOnWindows) { if (File.Exists(Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Bin), "VsoAgentService.exe"))) { Trace.Info($"Make a copy of AgentService.exe, name it VsoAgentService.exe"); File.Copy(Path.Combine(binVersionDir, "AgentService.exe"), Path.Combine(binVersionDir, "VsoAgentService.exe"), true); File.Copy(Path.Combine(binVersionDir, "AgentService.exe.config"), Path.Combine(binVersionDir, "VsoAgentService.exe.config"), true); Trace.Info($"Make a copy of Agent.Listener.exe, name it VsoAgent.exe"); File.Copy(Path.Combine(binVersionDir, "Agent.Listener.exe"), Path.Combine(binVersionDir, "VsoAgent.exe"), true); File.Copy(Path.Combine(binVersionDir, "Agent.Listener.dll"), Path.Combine(binVersionDir, "VsoAgent.dll"), true); // in case of we remove all pdb file from agent package. if (File.Exists(Path.Combine(binVersionDir, "AgentService.pdb"))) { File.Copy(Path.Combine(binVersionDir, "AgentService.pdb"), Path.Combine(binVersionDir, "VsoAgentService.pdb"), true); } if (File.Exists(Path.Combine(binVersionDir, "Agent.Listener.pdb"))) { File.Copy(Path.Combine(binVersionDir, "Agent.Listener.pdb"), Path.Combine(binVersionDir, "VsoAgent.pdb"), true); } } } }
private async Task <UploadResult> BlobUploadAsync(IAsyncCommandContext context, IReadOnlyList <string> files, int concurrentUploads, CancellationToken token) { // return files that fail to upload and total artifact size var uploadResult = new UploadResult(); // nothing needs to upload if (files.Count == 0) { return(uploadResult); } DedupStoreClient dedupClient = null; BlobStoreClientTelemetryTfs clientTelemetry = null; try { var verbose = String.Equals(context.GetVariableValueOrDefault("system.debug"), "true", StringComparison.InvariantCultureIgnoreCase); int maxParallelism = context.GetHostContext().GetService <IConfigurationStore>().GetSettings().MaxDedupParallelism; (dedupClient, clientTelemetry) = await DedupManifestArtifactClientFactory.Instance .CreateDedupClientAsync(verbose, (str) => context.Output(str), this._connection, maxParallelism, token); // Upload to blobstore var results = await BlobStoreUtils.UploadBatchToBlobstore(verbose, files, (level, uri, type) => new BuildArtifactActionRecord(level, uri, type, nameof(BlobUploadAsync), context), (str) => context.Output(str), dedupClient, clientTelemetry, token, enableReporting : true); // Associate with TFS context.Output(StringUtil.Loc("AssociateFiles")); var queue = new ConcurrentQueue <BlobFileInfo>(); foreach (var file in results.fileDedupIds) { queue.Enqueue(file); } // Start associate monitor var uploadFinished = new TaskCompletionSource <int>(); var associateMonitor = AssociateReportingAsync(context, files.Count(), uploadFinished, token); // Start parallel associate tasks. var parallelAssociateTasks = new List <Task <UploadResult> >(); for (int uploader = 0; uploader < concurrentUploads; uploader++) { parallelAssociateTasks.Add(AssociateAsync(context, queue, token)); } // Wait for parallel associate tasks to finish. await Task.WhenAll(parallelAssociateTasks); foreach (var associateTask in parallelAssociateTasks) { // record all failed files. uploadResult.AddUploadResult(await associateTask); } // Stop monitor task uploadFinished.SetResult(0); await associateMonitor; // report telemetry if (!Guid.TryParse(context.GetVariableValueOrDefault(WellKnownDistributedTaskVariables.PlanId), out var planId)) { planId = Guid.Empty; } if (!Guid.TryParse(context.GetVariableValueOrDefault(WellKnownDistributedTaskVariables.JobId), out var jobId)) { jobId = Guid.Empty; } await clientTelemetry.CommitTelemetryUpload(planId, jobId); } catch (SocketException e) { ExceptionsUtil.HandleSocketException(e, this._connection.Uri.ToString(), context.Warn); throw; } catch { var blobStoreHost = dedupClient.Client.BaseAddress.Host; var allowListLink = BlobStoreWarningInfoProvider.GetAllowListLinkForCurrentPlatform(); var warningMessage = StringUtil.Loc("BlobStoreUploadWarning", blobStoreHost, allowListLink); context.Warn(warningMessage); throw; } return(uploadResult); }
// Refresh connection is best effort. it should never throw exception public async Task RefreshConnectionAsync(AgentConnectionType connectionType, TimeSpan timeout) { Trace.Info($"Refresh {connectionType} VssConnection to get on a different AFD node."); VssConnection newConnection = null; switch (connectionType) { case AgentConnectionType.MessageQueue: try { _hasMessageConnection = false; newConnection = await EstablishVssConnection(_messageConnection.Uri, _messageConnection.Credentials, timeout); var client = newConnection.GetClient <TaskAgentHttpClient>(); _messageConnection = newConnection; _messageTaskAgentClient = client; } catch (SocketException ex) { ExceptionsUtil.HandleSocketException(ex, _requestConnection.Uri.ToString(), Trace.Error); newConnection?.Dispose(); } catch (Exception ex) { Trace.Error($"Catch exception during reset {connectionType} connection."); Trace.Error(ex); newConnection?.Dispose(); } finally { _hasMessageConnection = true; } break; case AgentConnectionType.JobRequest: try { _hasRequestConnection = false; newConnection = await EstablishVssConnection(_requestConnection.Uri, _requestConnection.Credentials, timeout); var client = newConnection.GetClient <TaskAgentHttpClient>(); _requestConnection = newConnection; _requestTaskAgentClient = client; } catch (SocketException ex) { ExceptionsUtil.HandleSocketException(ex, _requestConnection.Uri.ToString(), Trace.Error); newConnection?.Dispose(); } catch (Exception ex) { Trace.Error($"Catch exception during reset {connectionType} connection."); Trace.Error(ex); newConnection?.Dispose(); } finally { _hasRequestConnection = true; } break; case AgentConnectionType.Generic: try { _hasGenericConnection = false; newConnection = await EstablishVssConnection(_genericConnection.Uri, _genericConnection.Credentials, timeout); var client = newConnection.GetClient <TaskAgentHttpClient>(); _genericConnection = newConnection; _genericTaskAgentClient = client; } catch (SocketException ex) { ExceptionsUtil.HandleSocketException(ex, _requestConnection.Uri.ToString(), Trace.Error); newConnection?.Dispose(); } catch (Exception ex) { Trace.Error($"Catch exception during reset {connectionType} connection."); Trace.Error(ex); newConnection?.Dispose(); } finally { _hasGenericConnection = true; } break; default: Trace.Error($"Unexpected connection type: {connectionType}."); break; } }
public static int Main(string[] args) { if (PlatformUtil.UseLegacyHttpHandler) { AppContext.SetSwitch("System.Net.Http.UseSocketsHttpHandler", false); } Console.CancelKeyPress += Console_CancelKeyPress; // Set encoding to UTF8, process invoker will use UTF8 write to STDIN Console.InputEncoding = Encoding.UTF8; Console.OutputEncoding = Encoding.UTF8; try { ArgUtil.NotNull(args, nameof(args)); ArgUtil.Equal(2, args.Length, nameof(args.Length)); string pluginType = args[0]; if (string.Equals("task", pluginType, StringComparison.OrdinalIgnoreCase)) { string assemblyQualifiedName = args[1]; ArgUtil.NotNullOrEmpty(assemblyQualifiedName, nameof(assemblyQualifiedName)); string serializedContext = Console.ReadLine(); ArgUtil.NotNullOrEmpty(serializedContext, nameof(serializedContext)); AgentTaskPluginExecutionContext executionContext = StringUtil.ConvertFromJson <AgentTaskPluginExecutionContext>(serializedContext); ArgUtil.NotNull(executionContext, nameof(executionContext)); VariableValue culture; ArgUtil.NotNull(executionContext.Variables, nameof(executionContext.Variables)); if (executionContext.Variables.TryGetValue("system.culture", out culture) && !string.IsNullOrEmpty(culture?.Value)) { CultureInfo.DefaultThreadCurrentCulture = new CultureInfo(culture.Value); CultureInfo.DefaultThreadCurrentUICulture = new CultureInfo(culture.Value); } AssemblyLoadContext.Default.Resolving += ResolveAssembly; try { Type type = Type.GetType(assemblyQualifiedName, throwOnError: true); var taskPlugin = Activator.CreateInstance(type) as IAgentTaskPlugin; ArgUtil.NotNull(taskPlugin, nameof(taskPlugin)); taskPlugin.RunAsync(executionContext, tokenSource.Token).GetAwaiter().GetResult(); } catch (SocketException ex) { ExceptionsUtil.HandleSocketException(ex, executionContext.VssConnection.Uri.ToString(), executionContext.Error); } catch (AggregateException ex) { ExceptionsUtil.HandleAggregateException((AggregateException)ex, executionContext.Error); } catch (Exception ex) { executionContext.Error(ex.Message); executionContext.Debug(ex.StackTrace); } finally { AssemblyLoadContext.Default.Resolving -= ResolveAssembly; } return(0); } else if (string.Equals("command", pluginType, StringComparison.OrdinalIgnoreCase)) { string assemblyQualifiedName = args[1]; ArgUtil.NotNullOrEmpty(assemblyQualifiedName, nameof(assemblyQualifiedName)); string serializedContext = Console.ReadLine(); ArgUtil.NotNullOrEmpty(serializedContext, nameof(serializedContext)); AgentCommandPluginExecutionContext executionContext = StringUtil.ConvertFromJson <AgentCommandPluginExecutionContext>(serializedContext); ArgUtil.NotNull(executionContext, nameof(executionContext)); AssemblyLoadContext.Default.Resolving += ResolveAssembly; try { Type type = Type.GetType(assemblyQualifiedName, throwOnError: true); var commandPlugin = Activator.CreateInstance(type) as IAgentCommandPlugin; ArgUtil.NotNull(commandPlugin, nameof(commandPlugin)); commandPlugin.ProcessCommandAsync(executionContext, tokenSource.Token).GetAwaiter().GetResult(); } catch (Exception ex) { // any exception throw from plugin will fail the command. executionContext.Error(ex.ToString()); } finally { AssemblyLoadContext.Default.Resolving -= ResolveAssembly; } return(0); } else if (string.Equals("log", pluginType, StringComparison.OrdinalIgnoreCase)) { // read commandline arg to get the instance id var instanceId = args[1]; ArgUtil.NotNullOrEmpty(instanceId, nameof(instanceId)); // read STDIN, the first line will be the HostContext for the log plugin host string serializedContext = Console.ReadLine(); ArgUtil.NotNullOrEmpty(serializedContext, nameof(serializedContext)); AgentLogPluginHostContext hostContext = StringUtil.ConvertFromJson <AgentLogPluginHostContext>(serializedContext); ArgUtil.NotNull(hostContext, nameof(hostContext)); // create plugin object base on plugin assembly names from the HostContext List <IAgentLogPlugin> logPlugins = new List <IAgentLogPlugin>(); AssemblyLoadContext.Default.Resolving += ResolveAssembly; try { foreach (var pluginAssembly in hostContext.PluginAssemblies) { try { Type type = Type.GetType(pluginAssembly, throwOnError: true); var logPlugin = Activator.CreateInstance(type) as IAgentLogPlugin; ArgUtil.NotNull(logPlugin, nameof(logPlugin)); logPlugins.Add(logPlugin); } catch (Exception ex) { // any exception throw from plugin will get trace and ignore, error from plugins will not fail the job. Console.WriteLine($"Unable to load plugin '{pluginAssembly}': {ex}"); } } } finally { AssemblyLoadContext.Default.Resolving -= ResolveAssembly; } // start the log plugin host var logPluginHost = new AgentLogPluginHost(hostContext, logPlugins); Task hostTask = logPluginHost.Run(); while (true) { var consoleInput = Console.ReadLine(); if (string.Equals(consoleInput, $"##vso[logplugin.finish]{instanceId}", StringComparison.OrdinalIgnoreCase)) { // singal all plugins, the job has finished. // plugin need to start their finalize process. logPluginHost.Finish(); break; } else { // the format is TimelineRecordId(GUID):Output(String) logPluginHost.EnqueueOutput(consoleInput); } } // wait for the host to finish. hostTask.GetAwaiter().GetResult(); return(0); } else { throw new ArgumentOutOfRangeException(pluginType); } } catch (Exception ex) { // infrastructure failure. Console.Error.WriteLine(ex.ToString()); return(1); } finally { Console.CancelKeyPress -= Console_CancelKeyPress; } }
private async Task PublishCodeCoverageAsync( IExecutionContext executionContext, IAsyncCommandContext commandContext, ICodeCoveragePublisher codeCoveragePublisher, IEnumerable <CodeCoverageStatistics> coverageData, string project, Guid projectId, long containerId, CancellationToken cancellationToken) { //step 2: publish code coverage summary to TFS if (coverageData != null && coverageData.Count() > 0) { commandContext.Output(StringUtil.Loc("PublishingCodeCoverage")); foreach (var coverage in coverageData) { commandContext.Output(StringUtil.Format(" {0}- {1} of {2} covered.", coverage.Label, coverage.Covered, coverage.Total)); } await codeCoveragePublisher.PublishCodeCoverageSummaryAsync(commandContext, coverageData, project, cancellationToken); } // step 3: publish code coverage files as build artifacts string additionalCodeCoverageFilePath = null; string destinationSummaryFile = null; var newReportDirectory = _reportDirectory; try { var filesToPublish = new List <Tuple <string, string> >(); if (!Directory.Exists(newReportDirectory)) { if (!string.IsNullOrWhiteSpace(newReportDirectory)) { // user gave a invalid report directory. Write warning and continue. executionContext.Warning(StringUtil.Loc("DirectoryNotFound", newReportDirectory)); } newReportDirectory = GetCoverageDirectory(_buildId.ToString(), CodeCoverageConstants.ReportDirectory); Directory.CreateDirectory(newReportDirectory); } var summaryFileName = Path.GetFileName(_summaryFileLocation); destinationSummaryFile = Path.Combine(newReportDirectory, CodeCoverageConstants.SummaryFileDirectory + _buildId, summaryFileName); Directory.CreateDirectory(Path.GetDirectoryName(destinationSummaryFile)); File.Copy(_summaryFileLocation, destinationSummaryFile, true); commandContext.Output(StringUtil.Loc("ModifyingCoberturaIndexFile")); ModifyCoberturaIndexDotHTML(newReportDirectory, executionContext); filesToPublish.Add(new Tuple <string, string>(newReportDirectory, GetCoverageDirectoryName(_buildId.ToString(), CodeCoverageConstants.ReportDirectory))); if (_additionalCodeCoverageFiles != null && _additionalCodeCoverageFiles.Count != 0) { additionalCodeCoverageFilePath = GetCoverageDirectory(_buildId.ToString(), CodeCoverageConstants.RawFilesDirectory); CodeCoverageUtilities.CopyFilesFromFileListWithDirStructure(_additionalCodeCoverageFiles, ref additionalCodeCoverageFilePath); filesToPublish.Add(new Tuple <string, string>(additionalCodeCoverageFilePath, GetCoverageDirectoryName(_buildId.ToString(), CodeCoverageConstants.RawFilesDirectory))); } commandContext.Output(StringUtil.Loc("PublishingCodeCoverageFiles")); ChangeHtmExtensionToHtmlIfRequired(newReportDirectory, executionContext); await codeCoveragePublisher.PublishCodeCoverageFilesAsync(commandContext, projectId, executionContext.Variables.System_JobId, containerId, filesToPublish, File.Exists(Path.Combine(newReportDirectory, CodeCoverageConstants.DefaultIndexFile)), cancellationToken); } catch (SocketException ex) { #pragma warning disable CA2000 // Dispose objects before losing scope ExceptionsUtil.HandleSocketException(ex, WorkerUtilities.GetVssConnection(executionContext).Uri.ToString(), executionContext.Warning); #pragma warning restore CA2000 // Dispose objects before losing scope } catch (Exception ex) { executionContext.Warning(StringUtil.Loc("ErrorOccurredWhilePublishingCCFiles", ex.Message)); } finally { // clean temporary files. if (!string.IsNullOrEmpty(additionalCodeCoverageFilePath)) { if (Directory.Exists(additionalCodeCoverageFilePath)) { Directory.Delete(path: additionalCodeCoverageFilePath, recursive: true); } } if (!string.IsNullOrEmpty(destinationSummaryFile)) { var summaryFileDirectory = Path.GetDirectoryName(destinationSummaryFile); if (Directory.Exists(summaryFileDirectory)) { Directory.Delete(path: summaryFileDirectory, recursive: true); } } if (!Directory.Exists(_reportDirectory)) { if (Directory.Exists(newReportDirectory)) { //delete the generated report directory Directory.Delete(path: newReportDirectory, recursive: true); } } } }
public IArtifactDetails GetArtifactDetails( IExecutionContext context, AgentArtifactDefinition agentArtifactDefinition) { ArgUtil.NotNull(context, nameof(context)); ArgUtil.NotNull(agentArtifactDefinition, nameof(agentArtifactDefinition)); var artifactDetails = JsonConvert.DeserializeObject <Dictionary <string, string> >(agentArtifactDefinition.Details); string connectionName; string repositoryName = string.Empty; string branch = string.Empty; if (artifactDetails.TryGetValue(ArtifactDefinitionConstants.ConnectionName, out connectionName) && artifactDetails.TryGetValue(ArtifactDefinitionConstants.RepositoryId, out repositoryName) && artifactDetails.TryGetValue(ArtifactDefinitionConstants.BranchId, out branch)) { string checkoutNestedSubmodules; string checkoutSubmodules; string gitLfsSupport; string fetchDepth; artifactDetails.TryGetValue("checkoutNestedSubmodules", out checkoutNestedSubmodules); artifactDetails.TryGetValue("checkoutSubmodules", out checkoutSubmodules); artifactDetails.TryGetValue("gitLfsSupport", out gitLfsSupport); artifactDetails.TryGetValue("fetchDepth", out fetchDepth); ServiceEndpoint gitHubEndpoint = context.Endpoints.FirstOrDefault((e => string.Equals(e.Name, connectionName, StringComparison.OrdinalIgnoreCase))); if (gitHubEndpoint == null) { throw new InvalidOperationException(StringUtil.Loc("RMGitHubEndpointNotFound", agentArtifactDefinition.Name)); } string accessToken = gitHubEndpoint.Authorization.Parameters[EndpointAuthorizationParameters.AccessToken]; GitHubRepository repository = null; try { repository = HostContext.GetService <IGitHubHttpClient>().GetUserRepo(accessToken, repositoryName); } catch (SocketException e) { ExceptionsUtil.HandleSocketException(e, "https://api.github.com", Trace.Info); throw; } Trace.Info($"Found github repository url {repository.Clone_url}"); return(new GitHubArtifactDetails { RelativePath = Path.DirectorySeparatorChar.ToString(), ConnectionName = connectionName, CloneUrl = new Uri(repository.Clone_url), Branch = branch, CheckoutSubmodules = checkoutSubmodules, CheckoutNestedSubmodules = checkoutNestedSubmodules, GitLfsSupport = gitLfsSupport, FetchDepth = fetchDepth }); } else { throw new InvalidOperationException(StringUtil.Loc("RMArtifactDetailsIncomplete")); } }
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); // Capabilities. _term.WriteLine(StringUtil.Loc("ScanToolCapabilities")); Dictionary <string, string> systemCapabilities = await HostContext.GetService <ICapabilitiesManager>().GetCapabilitiesAsync(_settings, token); // 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.AgentPackage.Version, OSDescription = RuntimeInformation.OSDescription, }; string sessionName = $"{Environment.MachineName ?? "AGENT"}"; var taskAgentSession = new TaskAgentSession(sessionName, agent, systemCapabilities); string errorMessage = string.Empty; bool encounteringError = false; _term.WriteLine(StringUtil.Loc("ConnectToServer")); while (true) { token.ThrowIfCancellationRequested(); Trace.Info($"Attempt to create session."); try { Trace.Info("Connecting to the Agent Server..."); await _agentServer.ConnectAsync(new Uri(serverUrl), creds); Trace.Info("VssConnection created"); _session = await _agentServer.CreateAgentSessionAsync( _settings.PoolId, taskAgentSession, token); Trace.Info($"Session created."); if (encounteringError) { _term.WriteLine(StringUtil.Loc("QueueConnected", DateTime.UtcNow)); _sessionCreationExceptionTracker.Clear(); encounteringError = false; } return(true); } catch (OperationCanceledException) when(token.IsCancellationRequested) { Trace.Info("Session creation has been cancelled."); throw; } catch (TaskAgentAccessTokenExpiredException) { Trace.Info("Agent OAuth token has been revoked. Session creation failed."); throw; } catch (SocketException ex) { ExceptionsUtil.HandleSocketException(ex, serverUrl, Trace.Error); throw; } catch (Exception ex) { Trace.Error("Catch exception during create session."); Trace.Error(ex); if (!IsSessionCreationExceptionRetriable(ex)) { _term.WriteError(StringUtil.Loc("SessionCreateFailed", ex.Message)); return(false); } if (!encounteringError) //print the message only on the first error { _term.WriteError(StringUtil.Loc("QueueConError", DateTime.UtcNow, ex.Message, _sessionCreationRetryInterval.TotalSeconds)); encounteringError = true; } Trace.Info("Sleeping for {0} seconds before retrying.", _sessionCreationRetryInterval.TotalSeconds); await HostContext.Delay(_sessionCreationRetryInterval, token); } } }
//create worker manager, create message listener and start listening to the queue private async Task <int> RunAsync(AgentSettings settings, bool runOnce = false) { try { Trace.Info(nameof(RunAsync)); _listener = HostContext.GetService <IMessageListener>(); if (!await _listener.CreateSessionAsync(HostContext.AgentShutdownToken)) { return(Constants.Agent.ReturnCode.TerminatedError); } HostContext.WritePerfCounter("SessionCreated"); _term.WriteLine(StringUtil.Loc("ListenForJobs", DateTime.UtcNow)); IJobDispatcher jobDispatcher = null; CancellationTokenSource messageQueueLoopTokenSource = CancellationTokenSource.CreateLinkedTokenSource(HostContext.AgentShutdownToken); try { var notification = HostContext.GetService <IJobNotification>(); if (!String.IsNullOrEmpty(settings.NotificationSocketAddress)) { notification.StartClient(settings.NotificationSocketAddress, settings.MonitorSocketAddress); } else { notification.StartClient(settings.NotificationPipeName, settings.MonitorSocketAddress, HostContext.AgentShutdownToken); } // this is not a reliable way to disable auto update. // we need server side work to really enable the feature // https://github.com/Microsoft/vsts-agent/issues/446 (Feature: Allow agent / pool to opt out of automatic updates) bool disableAutoUpdate = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("agent.disableupdate")); bool autoUpdateInProgress = false; Task <bool> selfUpdateTask = null; bool runOnceJobReceived = false; jobDispatcher = HostContext.CreateService <IJobDispatcher>(); while (!HostContext.AgentShutdownToken.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 agent update is ready to apply exit the current agent 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.Agent.ReturnCode.RunOnceAgentUpdating); } else { return(Constants.Agent.ReturnCode.AgentUpdating); } } else { Trace.Info("Auto update task finished at backend, there is no available agent update needs to apply, continue message queue looping."); } } } if (runOnceJobReceived) { Trace.Verbose("One time used agent 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 agent 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.Agent.ReturnCode.Success); } } message = await getNextMessage; //get next message HostContext.WritePerfCounter($"MessageReceived_{message.MessageType}"); if (string.Equals(message.MessageType, AgentRefreshMessage.MessageType, StringComparison.OrdinalIgnoreCase)) { if (disableAutoUpdate) { Trace.Info("Refresh message received, skip autoupdate since environment variable agent.disableupdate is set."); } else { if (autoUpdateInProgress == false) { autoUpdateInProgress = true; var agentUpdateMessage = JsonUtility.FromString <AgentRefreshMessage>(message.Body); var selfUpdater = HostContext.GetService <ISelfUpdater>(); selfUpdateTask = selfUpdater.SelfUpdate(agentUpdateMessage, jobDispatcher, !runOnce && HostContext.StartupType != StartupType.Service, HostContext.AgentShutdownToken); 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.AgentJobRequest, StringComparison.OrdinalIgnoreCase) || 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 { Pipelines.AgentJobRequestMessage pipelineJobMessage = null; switch (message.MessageType) { case JobRequestMessageTypes.AgentJobRequest: var legacyJobMessage = JsonUtility.FromString <AgentJobRequestMessage>(message.Body); pipelineJobMessage = Pipelines.AgentJobRequestMessageUtil.Convert(legacyJobMessage); break; case JobRequestMessageTypes.PipelineAgentJobRequest: pipelineJobMessage = JsonUtility.FromString <Pipelines.AgentJobRequestMessage>(message.Body); break; } jobDispatcher.Run(pipelineJobMessage, runOnce); if (runOnce) { Trace.Info("One time used agent 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 if (string.Equals(message.MessageType, JobMetadataMessage.MessageType, StringComparison.OrdinalIgnoreCase)) { var metadataMessage = JsonUtility.FromString <JobMetadataMessage>(message.Body); jobDispatcher.MetadataUpdate(metadataMessage); } else { Trace.Error($"Received message {message.MessageId} with unsupported message type {message.MessageType}."); } } catch (AggregateException e) { ExceptionsUtil.HandleAggregateException((AggregateException)e, Trace.Error); } 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("Agent OAuth token has been revoked. Shutting down."); } return(Constants.Agent.ReturnCode.Success); }
private async Task DownloadFileContainerAsync(IEnumerable <FileContainerItem> items, ArtifactDownloadParameters downloadParameters, BuildArtifact artifact, string rootPath, AgentTaskPluginExecutionContext context, CancellationToken cancellationToken, bool isSingleArtifactDownload = true) { var containerIdAndRoot = ParseContainerId(artifact.Resource.Data); var projectId = downloadParameters.ProjectId; tracer.Info($"Start downloading FCS artifact- {artifact.Name}"); if (!isSingleArtifactDownload && items.Any()) { Directory.CreateDirectory(rootPath); } var folderItems = items.Where(i => i.ItemType == ContainerItemType.Folder); Parallel.ForEach(folderItems, (folder) => { var targetPath = ResolveTargetPath(rootPath, folder, containerIdAndRoot.Item2, downloadParameters.IncludeArtifactNameInPath); Directory.CreateDirectory(targetPath); }); var fileItems = items.Where(i => i.ItemType == ContainerItemType.File); // Only initialize these clients if we know we need to download from Blobstore // If a client cannot connect to Blobstore, we shouldn't stop them from downloading from FCS var downloadFromBlob = !AgentKnobs.DisableBuildArtifactsToBlob.GetValue(context).AsBoolean(); DedupStoreClient dedupClient = null; BlobStoreClientTelemetryTfs clientTelemetry = null; if (downloadFromBlob && fileItems.Any(x => x.BlobMetadata != null)) { try { (dedupClient, clientTelemetry) = await DedupManifestArtifactClientFactory.Instance.CreateDedupClientAsync( false, (str) => this.tracer.Info(str), this.connection, DedupManifestArtifactClientFactory.Instance.GetDedupStoreClientMaxParallelism(context), cancellationToken); } catch (SocketException e) { ExceptionsUtil.HandleSocketException(e, connection.Uri.ToString(), context.Warning); } catch { var blobStoreHost = dedupClient.Client.BaseAddress.Host; var allowListLink = BlobStoreWarningInfoProvider.GetAllowListLinkForCurrentPlatform(); var warningMessage = StringUtil.Loc("BlobStoreDownloadWarning", blobStoreHost, allowListLink); // Fall back to streaming through TFS if we cannot reach blobstore downloadFromBlob = false; tracer.Warn(warningMessage); } } var downloadBlock = NonSwallowingActionBlock.Create <FileContainerItem>( async item => { var targetPath = ResolveTargetPath(rootPath, item, containerIdAndRoot.Item2, downloadParameters.IncludeArtifactNameInPath); var directory = Path.GetDirectoryName(targetPath); Directory.CreateDirectory(directory); await AsyncHttpRetryHelper.InvokeVoidAsync( async() => { tracer.Info($"Downloading: {targetPath}"); if (item.BlobMetadata != null && downloadFromBlob) { await this.DownloadFileFromBlobAsync(context, containerIdAndRoot, targetPath, projectId, item, dedupClient, clientTelemetry, cancellationToken); } else { using (var sourceStream = await this.DownloadFileAsync(containerIdAndRoot, projectId, containerClient, item, cancellationToken)) using (var targetStream = new FileStream(targetPath, FileMode.Create)) { await sourceStream.CopyToAsync(targetStream); } } }, maxRetries: downloadParameters.RetryDownloadCount, cancellationToken: cancellationToken, tracer: tracer, continueOnCapturedContext: false, canRetryDelegate: exception => exception is IOException, context: null ); }, new ExecutionDataflowBlockOptions() { BoundedCapacity = 5000, MaxDegreeOfParallelism = downloadParameters.ParallelizationLimit, CancellationToken = cancellationToken, }); await downloadBlock.SendAllAndCompleteSingleBlockNetworkAsync(fileItems, cancellationToken); // Send results to CustomerIntelligence if (clientTelemetry != null) { var planId = new Guid(context.Variables.GetValueOrDefault(WellKnownDistributedTaskVariables.PlanId)?.Value ?? Guid.Empty.ToString()); var jobId = new Guid(context.Variables.GetValueOrDefault(WellKnownDistributedTaskVariables.JobId)?.Value ?? Guid.Empty.ToString()); context.PublishTelemetry(area: PipelineArtifactConstants.AzurePipelinesAgent, feature: PipelineArtifactConstants.BuildArtifactDownload, properties: clientTelemetry.GetArtifactDownloadTelemetry(planId, jobId)); } // check files (will throw an exception if a file is corrupt) if (downloadParameters.CheckDownloadedFiles) { CheckDownloads(items, rootPath, containerIdAndRoot.Item2, downloadParameters.IncludeArtifactNameInPath); } }
public async Task <TaskResult> RunAsync(Pipelines.AgentJobRequestMessage message, CancellationToken jobRequestCancellationToken) { // Validate parameters. Trace.Entering(); ArgUtil.NotNull(message, nameof(message)); ArgUtil.NotNull(message.Resources, nameof(message.Resources)); ArgUtil.NotNull(message.Variables, nameof(message.Variables)); ArgUtil.NotNull(message.Steps, nameof(message.Steps)); Trace.Info("Job ID {0}", message.JobId); DateTime jobStartTimeUtc = DateTime.UtcNow; ServiceEndpoint systemConnection = message.Resources.Endpoints.Single(x => string.Equals(x.Name, WellKnownServiceEndpointNames.SystemVssConnection, StringComparison.OrdinalIgnoreCase)); // System.AccessToken if (message.Variables.ContainsKey(Constants.Variables.System.EnableAccessToken) && StringUtil.ConvertToBoolean(message.Variables[Constants.Variables.System.EnableAccessToken].Value)) { message.Variables[Constants.Variables.System.AccessToken] = new VariableValue(systemConnection.Authorization.Parameters["AccessToken"], false); } // back compat TfsServerUrl message.Variables[Constants.Variables.System.TFServerUrl] = systemConnection.Url.AbsoluteUri; // Make sure SystemConnection Url and Endpoint Url match Config Url base for OnPremises server // System.ServerType will always be there after M133 if (!message.Variables.ContainsKey(Constants.Variables.System.ServerType) || string.Equals(message.Variables[Constants.Variables.System.ServerType]?.Value, "OnPremises", StringComparison.OrdinalIgnoreCase)) { ReplaceConfigUriBaseInJobRequestMessage(message); } // Setup the job server and job server queue. var jobServer = HostContext.GetService <IJobServer>(); VssCredentials jobServerCredential = VssUtil.GetVssCredential(systemConnection); Uri jobServerUrl = systemConnection.Url; Trace.Info($"Creating job server with URL: {jobServerUrl}"); // jobServerQueue is the throttling reporter. _jobServerQueue = HostContext.GetService <IJobServerQueue>(); VssConnection jobConnection = VssUtil.CreateConnection( jobServerUrl, jobServerCredential, trace: Trace, new DelegatingHandler[] { new ThrottlingReportHandler(_jobServerQueue) } ); await jobServer.ConnectAsync(jobConnection); _jobServerQueue.Start(message); HostContext.WritePerfCounter($"WorkerJobServerQueueStarted_{message.RequestId.ToString()}"); IExecutionContext jobContext = null; CancellationTokenRegistration?agentShutdownRegistration = null; try { // Create the job execution context. jobContext = HostContext.CreateService <IExecutionContext>(); jobContext.InitializeJob(message, jobRequestCancellationToken); Trace.Info("Starting the job execution context."); jobContext.Start(); jobContext.Section(StringUtil.Loc("StepStarting", message.JobDisplayName)); agentShutdownRegistration = HostContext.AgentShutdownToken.Register(() => { // log an issue, then agent get shutdown by Ctrl-C or Ctrl-Break. // the server will use Ctrl-Break to tells the agent that operating system is shutting down. string errorMessage; switch (HostContext.AgentShutdownReason) { case ShutdownReason.UserCancelled: errorMessage = StringUtil.Loc("UserShutdownAgent"); break; case ShutdownReason.OperatingSystemShutdown: errorMessage = StringUtil.Loc("OperatingSystemShutdown", Environment.MachineName); break; default: throw new ArgumentException(HostContext.AgentShutdownReason.ToString(), nameof(HostContext.AgentShutdownReason)); } jobContext.AddIssue(new Issue() { Type = IssueType.Error, Message = errorMessage }); }); // Validate directory permissions. string workDirectory = HostContext.GetDirectory(WellKnownDirectory.Work); Trace.Info($"Validating directory permissions for: '{workDirectory}'"); try { Directory.CreateDirectory(workDirectory); IOUtil.ValidateExecutePermission(workDirectory); } catch (Exception ex) { Trace.Error(ex); jobContext.Error(ex); return(await CompleteJobAsync(jobServer, jobContext, message, TaskResult.Failed)); } // Set agent variables. AgentSettings settings = HostContext.GetService <IConfigurationStore>().GetSettings(); jobContext.SetVariable(Constants.Variables.Agent.Id, settings.AgentId.ToString(CultureInfo.InvariantCulture)); jobContext.SetVariable(Constants.Variables.Agent.HomeDirectory, HostContext.GetDirectory(WellKnownDirectory.Root), isFilePath: true); jobContext.SetVariable(Constants.Variables.Agent.JobName, message.JobDisplayName); jobContext.SetVariable(Constants.Variables.Agent.MachineName, Environment.MachineName); jobContext.SetVariable(Constants.Variables.Agent.Name, settings.AgentName); jobContext.SetVariable(Constants.Variables.Agent.OS, VarUtil.OS); jobContext.SetVariable(Constants.Variables.Agent.OSArchitecture, VarUtil.OSArchitecture); jobContext.SetVariable(Constants.Variables.Agent.RootDirectory, HostContext.GetDirectory(WellKnownDirectory.Work), isFilePath: true); if (PlatformUtil.RunningOnWindows) { jobContext.SetVariable(Constants.Variables.Agent.ServerOMDirectory, HostContext.GetDirectory(WellKnownDirectory.ServerOM), isFilePath: true); } if (!PlatformUtil.RunningOnWindows) { jobContext.SetVariable(Constants.Variables.Agent.AcceptTeeEula, settings.AcceptTeeEula.ToString()); } jobContext.SetVariable(Constants.Variables.Agent.WorkFolder, HostContext.GetDirectory(WellKnownDirectory.Work), isFilePath: true); jobContext.SetVariable(Constants.Variables.System.WorkFolder, HostContext.GetDirectory(WellKnownDirectory.Work), isFilePath: true); string toolsDirectory = HostContext.GetDirectory(WellKnownDirectory.Tools); Directory.CreateDirectory(toolsDirectory); jobContext.SetVariable(Constants.Variables.Agent.ToolsDirectory, toolsDirectory, isFilePath: true); if (AgentKnobs.DisableGitPrompt.GetValue(jobContext).AsBoolean()) { jobContext.SetVariable("GIT_TERMINAL_PROMPT", "0"); } // Setup TEMP directories _tempDirectoryManager = HostContext.GetService <ITempDirectoryManager>(); _tempDirectoryManager.InitializeTempDirectory(jobContext); // todo: task server can throw. try/catch and fail job gracefully. // prefer task definitions url, then TFS collection url, then TFS account url var taskServer = HostContext.GetService <ITaskServer>(); Uri taskServerUri = null; if (!string.IsNullOrEmpty(jobContext.Variables.System_TaskDefinitionsUri)) { taskServerUri = new Uri(jobContext.Variables.System_TaskDefinitionsUri); } else if (!string.IsNullOrEmpty(jobContext.Variables.System_TFCollectionUrl)) { taskServerUri = new Uri(jobContext.Variables.System_TFCollectionUrl); } var taskServerCredential = VssUtil.GetVssCredential(systemConnection); if (taskServerUri != null) { Trace.Info($"Creating task server with {taskServerUri}"); await taskServer.ConnectAsync(VssUtil.CreateConnection(taskServerUri, taskServerCredential, trace: Trace)); } // for back compat TFS 2015 RTM/QU1, we may need to switch the task server url to agent config url if (!string.Equals(message?.Variables.GetValueOrDefault(Constants.Variables.System.ServerType)?.Value, "Hosted", StringComparison.OrdinalIgnoreCase)) { if (taskServerUri == null || !await taskServer.TaskDefinitionEndpointExist()) { Trace.Info($"Can't determine task download url from JobMessage or the endpoint doesn't exist."); var configStore = HostContext.GetService <IConfigurationStore>(); taskServerUri = new Uri(configStore.GetSettings().ServerUrl); Trace.Info($"Recreate task server with configuration server url: {taskServerUri}"); await taskServer.ConnectAsync(VssUtil.CreateConnection(taskServerUri, taskServerCredential, trace: Trace)); } } // Expand the endpoint data values. foreach (ServiceEndpoint endpoint in jobContext.Endpoints) { jobContext.Variables.ExpandValues(target: endpoint.Data); VarUtil.ExpandEnvironmentVariables(HostContext, target: endpoint.Data); } // Expand the repository property values. foreach (var repository in jobContext.Repositories) { // expand checkout option var checkoutOptions = repository.Properties.Get <JToken>(Pipelines.RepositoryPropertyNames.CheckoutOptions); if (checkoutOptions != null) { checkoutOptions = jobContext.Variables.ExpandValues(target: checkoutOptions); checkoutOptions = VarUtil.ExpandEnvironmentVariables(HostContext, target: checkoutOptions); repository.Properties.Set <JToken>(Pipelines.RepositoryPropertyNames.CheckoutOptions, checkoutOptions);; } // expand workspace mapping var mappings = repository.Properties.Get <JToken>(Pipelines.RepositoryPropertyNames.Mappings); if (mappings != null) { mappings = jobContext.Variables.ExpandValues(target: mappings); mappings = VarUtil.ExpandEnvironmentVariables(HostContext, target: mappings); repository.Properties.Set <JToken>(Pipelines.RepositoryPropertyNames.Mappings, mappings); } } // Expand container properties foreach (var container in jobContext.Containers) { this.ExpandProperties(container, jobContext.Variables); } foreach (var sidecar in jobContext.SidecarContainers) { this.ExpandProperties(sidecar, jobContext.Variables); } // Get the job extension. Trace.Info("Getting job extension."); var hostType = jobContext.Variables.System_HostType; var extensionManager = HostContext.GetService <IExtensionManager>(); // We should always have one job extension IJobExtension jobExtension = (extensionManager.GetExtensions <IJobExtension>() ?? new List <IJobExtension>()) .Where(x => x.HostType.HasFlag(hostType)) .FirstOrDefault(); ArgUtil.NotNull(jobExtension, nameof(jobExtension)); List <IStep> jobSteps = null; try { Trace.Info("Initialize job. Getting all job steps."); jobSteps = await jobExtension.InitializeJob(jobContext, message); } catch (OperationCanceledException ex) when(jobContext.CancellationToken.IsCancellationRequested) { // set the job to canceled // don't log error issue to job ExecutionContext, since server owns the job level issue Trace.Error($"Job is canceled during initialize."); Trace.Error($"Caught exception: {ex}"); return(await CompleteJobAsync(jobServer, jobContext, message, TaskResult.Canceled)); } catch (Exception ex) { // set the job to failed. // don't log error issue to job ExecutionContext, since server owns the job level issue Trace.Error($"Job initialize failed."); Trace.Error($"Caught exception from {nameof(jobExtension.InitializeJob)}: {ex}"); return(await CompleteJobAsync(jobServer, jobContext, message, TaskResult.Failed)); } // trace out all steps Trace.Info($"Total job steps: {jobSteps.Count}."); Trace.Verbose($"Job steps: '{string.Join(", ", jobSteps.Select(x => x.DisplayName))}'"); HostContext.WritePerfCounter($"WorkerJobInitialized_{message?.RequestId.ToString()}"); // Run all job steps Trace.Info("Run all job steps."); var stepsRunner = HostContext.GetService <IStepsRunner>(); try { await stepsRunner.RunAsync(jobContext, jobSteps); } catch (Exception ex) { // StepRunner should never throw exception out. // End up here mean there is a bug in StepRunner // Log the error and fail the job. Trace.Error($"Caught exception from job steps {nameof(StepsRunner)}: {ex}"); jobContext.Error(ex); return(await CompleteJobAsync(jobServer, jobContext, message, TaskResult.Failed)); } finally { Trace.Info("Finalize job."); await jobExtension.FinalizeJob(jobContext); } Trace.Info($"Job result after all job steps finish: {jobContext.Result ?? TaskResult.Succeeded}"); if (jobContext.Variables.GetBoolean(Constants.Variables.Agent.Diagnostic) ?? false) { Trace.Info("Support log upload starting."); IDiagnosticLogManager diagnosticLogManager = HostContext.GetService <IDiagnosticLogManager>(); try { await diagnosticLogManager.UploadDiagnosticLogsAsync(executionContext : jobContext, message : message, jobStartTimeUtc : jobStartTimeUtc); Trace.Info("Support log upload complete."); } catch (Exception ex) { // Log the error but make sure we continue gracefully. Trace.Info("Error uploading support logs."); Trace.Error(ex); } } Trace.Info("Completing the job execution context."); return(await CompleteJobAsync(jobServer, jobContext, message)); } catch (AggregateException e) { ExceptionsUtil.HandleAggregateException((AggregateException)e, Trace.Error); return(TaskResult.Failed); } finally { if (agentShutdownRegistration != null) { agentShutdownRegistration.Value.Dispose(); agentShutdownRegistration = null; } await ShutdownQueue(throwOnFailure : false); } }
public bool TryProcessCommand(IExecutionContext context, string input) { ArgUtil.NotNull(context, nameof(context)); if (string.IsNullOrEmpty(input)) { return(false); } // TryParse input to Command Command command; var unescapePercents = AgentKnobs.DecodePercents.GetValue(context).AsBoolean(); if (!Command.TryParse(input, unescapePercents, out command)) { // if parse fail but input contains ##vso, print warning with DOC link if (input.IndexOf("##vso") >= 0) { context.Warning(StringUtil.Loc("CommandKeywordDetected", input)); } return(false); } IWorkerCommandExtension extension = null; if (_invokePluginInternalCommand && string.Equals(command.Area, _pluginInternalCommandExtensions.CommandArea, StringComparison.OrdinalIgnoreCase)) { extension = _pluginInternalCommandExtensions; } if (extension != null || _commandExtensions.TryGetValue(command.Area, out extension)) { if (!extension.SupportedHostTypes.HasFlag(context.Variables.System_HostType)) { context.Error(StringUtil.Loc("CommandNotSupported", command.Area, context.Variables.System_HostType)); context.CommandResult = TaskResult.Failed; return(false); } // process logging command in serialize oreder. lock (_commandSerializeLock) { try { extension.ProcessCommand(context, command); } catch (SocketException ex) { #pragma warning disable CA2000 // Dispose objects before losing scope ExceptionsUtil.HandleSocketException(ex, WorkerUtilities.GetVssConnection(context).Uri.ToString(), context.Error); #pragma warning restore CA2000 // Dispose objects before losing scope context.CommandResult = TaskResult.Failed; } catch (Exception ex) { context.Error(StringUtil.Loc("CommandProcessFailed", input)); context.Error(ex); context.CommandResult = TaskResult.Failed; } finally { // trace the ##vso command as long as the command is not a ##vso[task.debug] command. if (!(string.Equals(command.Area, "task", StringComparison.OrdinalIgnoreCase) && string.Equals(command.Event, "debug", StringComparison.OrdinalIgnoreCase))) { context.Debug($"Processed: {input}"); } } } } else { context.Warning(StringUtil.Loc("CommandNotFound", command.Area)); } // Only if we've successfully parsed do we show this warning if (AgentKnobs.DecodePercents.GetValue(context).AsString() == "" && input.Contains("%AZP25")) { context.Warning("%AZP25 detected in ##vso command. In March 2021, the agent command parser will be updated to unescape this to %. To opt out of this behavior, set a job level variable DECODE_PERCENTS to false. Setting to true will force this behavior immediately. More information can be found at https://github.com/microsoft/azure-pipelines-agent/blob/master/docs/design/percentEncoding.md"); } return(true); }
private async Task <TaskResult> CompleteJobAsync(IJobServer jobServer, IExecutionContext jobContext, Pipelines.AgentJobRequestMessage message, TaskResult?taskResult = null) { ArgUtil.NotNull(message, nameof(message)); jobContext.Section(StringUtil.Loc("StepFinishing", message.JobDisplayName)); TaskResult result = jobContext.Complete(taskResult); try { await ShutdownQueue(throwOnFailure : true); } catch (AggregateException ex) { ExceptionsUtil.HandleAggregateException((AggregateException)ex, Trace.Error); result = TaskResultUtil.MergeTaskResults(result, TaskResult.Failed); } catch (Exception ex) { Trace.Error($"Caught exception from {nameof(JobServerQueue)}.{nameof(_jobServerQueue.ShutdownAsync)}"); Trace.Error("This indicate a failure during publish output variables. Fail the job to prevent unexpected job outputs."); Trace.Error(ex); result = TaskResultUtil.MergeTaskResults(result, TaskResult.Failed); } // Clean TEMP after finish process jobserverqueue, since there might be a pending fileupload still use the TEMP dir. _tempDirectoryManager?.CleanupTempDirectory(); if (!jobContext.Features.HasFlag(PlanFeatures.JobCompletedPlanEvent)) { Trace.Info($"Skip raise job completed event call from worker because Plan version is {message.Plan.Version}"); return(result); } Trace.Info("Raising job completed event."); var jobCompletedEvent = new JobCompletedEvent(message.RequestId, message.JobId, result, jobContext.Variables.Get(Constants.Variables.Agent.RunMode) == Constants.Agent.CommandLine.Flags.Once); var completeJobRetryLimit = 5; var exceptions = new List <Exception>(); while (completeJobRetryLimit-- > 0) { try { await jobServer.RaisePlanEventAsync(message.Plan.ScopeIdentifier, message.Plan.PlanType, message.Plan.PlanId, jobCompletedEvent, default(CancellationToken)); return(result); } catch (TaskOrchestrationPlanNotFoundException ex) { Trace.Error($"TaskOrchestrationPlanNotFoundException received, while attempting to raise JobCompletedEvent for job {message.JobId}."); Trace.Error(ex); return(TaskResult.Failed); } catch (TaskOrchestrationPlanSecurityException ex) { Trace.Error($"TaskOrchestrationPlanSecurityException received, while attempting to raise JobCompletedEvent for job {message.JobId}."); Trace.Error(ex); return(TaskResult.Failed); } catch (Exception ex) { Trace.Error($"Catch exception while attempting to raise JobCompletedEvent for job {message.JobId}, job request {message.RequestId}."); Trace.Error(ex); exceptions.Add(ex); } // delay 5 seconds before next retry. await Task.Delay(TimeSpan.FromSeconds(5)); } // rethrow exceptions from all attempts. throw new AggregateException(exceptions); }