public async Task <int> RunAsync(string pipeIn, string pipeOut) { // Validate args. ArgUtil.NotNullOrEmpty(pipeIn, nameof(pipeIn)); ArgUtil.NotNullOrEmpty(pipeOut, nameof(pipeOut)); WebProxy.ApplyProxySettings(); var jobRunner = HostContext.GetService <IJobRunner>(); using (var channel = HostContext.CreateService <IProcessChannel>()) using (var jobRequestCancellationToken = new CancellationTokenSource()) using (var channelTokenSource = new CancellationTokenSource()) { // Start the channel. channel.StartClient(pipeIn, pipeOut); // Wait for up to 30 seconds for a message from the channel. Trace.Info("Waiting to receive the job message from the channel."); WorkerMessage channelMessage; using (var csChannelMessage = new CancellationTokenSource(_workerStartTimeout)) { channelMessage = await channel.ReceiveAsync(csChannelMessage.Token); } // Deserialize the job message. Trace.Info("Message received."); ArgUtil.Equal(MessageType.NewJobRequest, channelMessage.MessageType, nameof(channelMessage.MessageType)); ArgUtil.NotNullOrEmpty(channelMessage.Body, nameof(channelMessage.Body)); var jobMessage = JsonUtility.FromString <JobRequestMessage>(channelMessage.Body); ArgUtil.NotNull(jobMessage, nameof(jobMessage)); // Initialize the secret masker and set the thread culture. InitializeSecretMasker(jobMessage); SetCulture(jobMessage); // Start the job. Trace.Info($"Job message: {channelMessage.Body}"); Task <TaskResult> jobRunnerTask = jobRunner.RunAsync(jobMessage, jobRequestCancellationToken.Token); // Start listening for a cancel message from the channel. Trace.Info("Listening for cancel message from the channel."); Task <WorkerMessage> channelTask = channel.ReceiveAsync(channelTokenSource.Token); // Wait for one of the tasks to complete. Trace.Info("Waiting for the job to complete or for a cancel message from the channel."); Task.WaitAny(jobRunnerTask, channelTask); // Handle if the job completed. if (jobRunnerTask.IsCompleted) { Trace.Info("Job completed."); channelTokenSource.Cancel(); // Cancel waiting for a message from the channel. return(TaskResultUtil.TranslateToReturnCode(await jobRunnerTask)); } // Otherwise a cancel message was received from the channel. Trace.Info("Cancellation message received."); channelMessage = await channelTask; ArgUtil.Equal(MessageType.CancelRequest, channelMessage.MessageType, nameof(channelMessage.MessageType)); jobRequestCancellationToken.Cancel(); // Expire the host cancellation token. // Await the job. return(TaskResultUtil.TranslateToReturnCode(await jobRunnerTask)); } }
public static int Main(string[] args) { // We can't use the new SocketsHttpHandler for now for both Windows and Linux // On linux, Negotiate auth is not working if the TFS url is behind Https // On windows, Proxy is not working AppContext.SetSwitch("System.Net.Http.UseSocketsHttpHandler", false); Console.CancelKeyPress += Console_CancelKeyPress; 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)); // Set encoding to UTF8, process invoker will use UTF8 write to STDIN Console.InputEncoding = Encoding.UTF8; Console.OutputEncoding = Encoding.UTF8; 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 (Exception ex) { // any exception throw from plugin will fail the task. 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 { throw new ArgumentOutOfRangeException(pluginType); } } catch (Exception ex) { // infrastructure failure. Console.Error.WriteLine(ex.ToString()); return(1); } finally { Console.CancelKeyPress -= Console_CancelKeyPress; } }
public Task <TaskAgentJobRequest> GetAgentRequestAsync(int poolId, long requestId, CancellationToken cancellationToken = default(CancellationToken)) { ArgUtil.Equal(RunMode.Normal, HostContext.RunMode, nameof(HostContext.RunMode)); CheckConnection(AgentConnectionType.JobRequest); return(_requestTaskAgentClient.GetAgentRequestAsync(poolId, requestId, cancellationToken: cancellationToken)); }
//----------------------------------------------------------------- // Agent Package //----------------------------------------------------------------- public Task <List <PackageMetadata> > GetPackagesAsync(string packageType, string platform, int top, CancellationToken cancellationToken) { ArgUtil.Equal(RunMode.Normal, HostContext.RunMode, nameof(HostContext.RunMode)); CheckConnection(AgentConnectionType.Generic); return(_genericTaskAgentClient.GetPackagesAsync(packageType, platform, top, cancellationToken: cancellationToken)); }
public void DeleteSettings() { ArgUtil.Equal(RunMode.Normal, HostContext.RunMode, nameof(HostContext.RunMode)); IOUtil.Delete(_configFilePath, default(CancellationToken)); }
public async Task RunAsync(IExecutionContext jobContext, IList <IStep> steps) { ArgUtil.NotNull(jobContext, nameof(jobContext)); ArgUtil.NotNull(steps, nameof(steps)); // TaskResult: // Abandoned // Canceled // Failed // Skipped // Succeeded // SucceededWithIssues bool stepFailed = false; bool criticalStepFailed = false; int stepCount = 0; jobContext.Variables.Agent_JobStatus = TaskResult.Succeeded; foreach (IStep step in steps) { Trace.Info($"Processing step: DisplayName='{step.DisplayName}', AlwaysRun={step.AlwaysRun}, ContinueOnError={step.ContinueOnError}, Critical={step.Critical}, Enabled={step.Enabled}, Finally={step.Finally}"); ArgUtil.Equal(true, step.Enabled, nameof(step.Enabled)); ArgUtil.NotNull(step.ExecutionContext, nameof(step.ExecutionContext)); ArgUtil.NotNull(step.ExecutionContext.Variables, nameof(step.ExecutionContext.Variables)); jobContext.Progress(stepCount++ *100 / steps.Count); // TODO: Run finally even if canceled? // Skip if a previous step failed and the current step is not AlwaysRun. if ((stepFailed && !step.AlwaysRun && !step.Finally) // Or if a previous Critical step failed and the current step is not Finally. || (criticalStepFailed && !step.Finally)) { Trace.Info("Skipping step."); step.ExecutionContext.Result = TaskResult.Skipped; continue; } // Run the step. Trace.Info("Starting the step."); TimeSpan?stepTimeout = null; if (step is ITaskRunner && (step as ITaskRunner).TaskInstance.TimeoutInMinutes > 0) { stepTimeout = TimeSpan.FromMinutes((step as ITaskRunner).TaskInstance.TimeoutInMinutes); } step.ExecutionContext.Start(timeout: stepTimeout); List <string> expansionWarnings; step.ExecutionContext.Variables.RecalculateExpanded(out expansionWarnings); expansionWarnings?.ForEach(x => step.ExecutionContext.Warning(x)); List <OperationCanceledException> allCancelExceptions = new List <OperationCanceledException>(); try { await step.RunAsync(); } catch (OperationCanceledException ex) { if (step.ExecutionContext.CancellationToken.IsCancellationRequested && !jobContext.CancellationToken.IsCancellationRequested) { Trace.Error($"Caught timeout exception from step: {ex.Message}"); step.ExecutionContext.Error(StringUtil.Loc("StepTimedOut")); step.ExecutionContext.Result = TaskResult.Failed; } else { // Log the exception and cancel the step. Trace.Error($"Caught cancellation exception from step: {ex}"); step.ExecutionContext.Error(ex); step.ExecutionContext.Result = TaskResult.Canceled; } //save the OperationCanceledException, merge with OperationCanceledException throw from Async Commands. allCancelExceptions.Add(ex); } catch (Exception ex) { // Log the error and fail the step. Trace.Error($"Caught exception from step: {ex}"); step.ExecutionContext.Error(ex); step.ExecutionContext.Result = TaskResult.Failed; } // Wait till all async commands finish. foreach (var command in step.ExecutionContext.AsyncCommands ?? new List <IAsyncCommandContext>()) { try { // wait async command to finish. await command.WaitAsync(); } catch (OperationCanceledException ex) { if (step.ExecutionContext.CancellationToken.IsCancellationRequested && !jobContext.CancellationToken.IsCancellationRequested) { // Log the timeout error, set step result to falied if the current result is not canceled. Trace.Error($"Caught timeout exception from async command {command.Name}: {ex}"); step.ExecutionContext.Error(StringUtil.Loc("StepTimedOut")); // if the step already canceled, don't set it to failed. step.ExecutionContext.CommandResult = TaskResultUtil.MergeTaskResults(step.ExecutionContext.CommandResult, TaskResult.Failed); } else { // log and save the OperationCanceledException, set step result to canceled if the current result is not failed. Trace.Error($"Caught cancellation exception from async command {command.Name}: {ex}"); step.ExecutionContext.Error(ex); // if the step already failed, don't set it to canceled. step.ExecutionContext.CommandResult = TaskResultUtil.MergeTaskResults(step.ExecutionContext.CommandResult, TaskResult.Canceled); } allCancelExceptions.Add(ex); } catch (Exception ex) { // Log the error, set step result to falied if the current result is not canceled. Trace.Error($"Caught exception from async command {command.Name}: {ex}"); step.ExecutionContext.Error(ex); // if the step already canceled, don't set it to failed. step.ExecutionContext.CommandResult = TaskResultUtil.MergeTaskResults(step.ExecutionContext.CommandResult, TaskResult.Failed); } } // Merge executioncontext result with command result if (step.ExecutionContext.CommandResult != null) { step.ExecutionContext.Result = TaskResultUtil.MergeTaskResults(step.ExecutionContext.Result, step.ExecutionContext.CommandResult.Value); } // TODO: consider use token.IsCancellationRequest determine step cancelled instead of catch OperationCancelException all over the place if (step.ExecutionContext.Result == TaskResult.Canceled && allCancelExceptions.Count > 0) { step.ExecutionContext.Complete(); throw allCancelExceptions.First(); } // Fixup the step result if ContinueOnError. if (step.ExecutionContext.Result == TaskResult.Failed && step.ContinueOnError) { step.ExecutionContext.Result = TaskResult.SucceededWithIssues; Trace.Info($"Updated step result: {step.ExecutionContext.Result}"); } else { Trace.Info($"Step result: {step.ExecutionContext.Result}"); } // Complete the step context. step.ExecutionContext.Complete(); // Update the step failed flags. stepFailed = stepFailed || step.ExecutionContext.Result == TaskResult.Failed; criticalStepFailed = criticalStepFailed || (step.Critical && step.ExecutionContext.Result == TaskResult.Failed); // Update the job result. if (step.ExecutionContext.Result == TaskResult.Failed) { jobContext.Result = TaskResult.Failed; jobContext.Variables.Agent_JobStatus = TaskResult.Failed; } else if ((jobContext.Result ?? TaskResult.Succeeded) == TaskResult.Succeeded && step.ExecutionContext.Result == TaskResult.SucceededWithIssues) { jobContext.Result = TaskResult.SucceededWithIssues; jobContext.Variables.Agent_JobStatus = TaskResult.SucceededWithIssues; } Trace.Info($"Current state: job state = '{jobContext.Result}', step failed = {stepFailed}, critical step failed = {criticalStepFailed}"); } }
public async Task RunAsync(IExecutionContext jobContext, IList <IStep> steps) { ArgUtil.NotNull(jobContext, nameof(jobContext)); ArgUtil.NotNull(steps, nameof(steps)); // TaskResult: // Abandoned // Canceled // Failed // Skipped // Succeeded // SucceededWithIssues bool stepFailed = false; bool criticalFailure = false; int stepCount = 0; jobContext.Variables.Agent_JobStatus = TaskResult.Succeeded; foreach (IStep step in steps) { Trace.Info($"Processing step: DisplayName='{step.DisplayName}', ContinueOnError={step.ContinueOnError}, Critical={step.Critical}, Enabled={step.Enabled}, Finally={step.Finally}"); ArgUtil.Equal(true, step.Enabled, nameof(step.Enabled)); ArgUtil.NotNull(step.ExecutionContext, nameof(step.ExecutionContext)); ArgUtil.NotNull(step.ExecutionContext.Variables, nameof(step.ExecutionContext.Variables)); jobContext.Progress(stepCount++ *100 / steps.Count); // Start. step.ExecutionContext.Start(); // Test critical failure. if (criticalFailure && !step.Finally) { Trace.Info("Skipping step due to previous critical failure."); step.ExecutionContext.Complete(TaskResult.Skipped); continue; } // Variable expansion. List <string> expansionWarnings; step.ExecutionContext.Variables.RecalculateExpanded(out expansionWarnings); expansionWarnings?.ForEach(x => step.ExecutionContext.Warning(x)); // Evaluate condition. step.ExecutionContext.Debug($"Evaluating condition for step: '{step.DisplayName}'"); var expressionManager = HostContext.GetService <IExpressionManager>(); bool?conditionResult = null; try { conditionResult = expressionManager.Evaluate(step.ExecutionContext, step.Condition); } catch (Exception ex) { Trace.Info("Caught exception from expression evaluation."); Trace.Error(ex); step.ExecutionContext.Error(ex); } if (conditionResult == null) { // Error evaluating condition. step.ExecutionContext.Complete(TaskResult.Failed); } else if (!conditionResult.Value) { // Condition == false Trace.Info("Skipping step due to condition evaluation."); step.ExecutionContext.Complete(TaskResult.Skipped); } else { // Run the step. await RunStepAsync(jobContext, step); } // Update the step failed flags. stepFailed = stepFailed || step.ExecutionContext.Result == TaskResult.Failed; criticalFailure = criticalFailure || conditionResult == null || (step.Critical && step.ExecutionContext.Result == TaskResult.Failed); // Update the job result. if (step.ExecutionContext.Result == TaskResult.Failed) { jobContext.Result = TaskResult.Failed; jobContext.Variables.Agent_JobStatus = TaskResult.Failed; } else if ((jobContext.Result ?? TaskResult.Succeeded) == TaskResult.Succeeded && step.ExecutionContext.Result == TaskResult.SucceededWithIssues) { jobContext.Result = TaskResult.SucceededWithIssues; jobContext.Variables.Agent_JobStatus = TaskResult.SucceededWithIssues; } Trace.Info($"Current state: job state = '{jobContext.Result}', step failed = {stepFailed}, critical failure = {criticalFailure}"); } }
public async Task ConfigureAsync(CommandSettings command) { ArgUtil.Equal(RunMode.Normal, HostContext.RunMode, nameof(HostContext.RunMode)); 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; } AgentSettings agentSettings = new AgentSettings(); // TEE EULA agentSettings.AcceptTeeEula = false; switch (Constants.Agent.Platform) { case Constants.OSPlatform.OSX: case Constants.OSPlatform.Linux: // Write the section header. WriteSection(StringUtil.Loc("EulasSectionHeader")); // Verify the EULA exists on disk in the expected location. string eulaFile = Path.Combine(IOUtil.GetExternalsPath(), Constants.Path.TeeDirectory, "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 Constants.OSPlatform.Windows: // Warn and continue if .NET 4.6 is not installed. var netFrameworkUtil = HostContext.GetService <INetFrameworkUtil>(); if (!netFrameworkUtil.Test(new Version(4, 6))) { 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 = command.DeploymentGroup ? Constants.Agent.AgentConfigurationProvider.DeploymentAgentConfiguration : Constants.Agent.AgentConfigurationProvider.BuildReleasesAgentConfiguration; var extensionManager = HostContext.GetService <IExtensionManager>(); IConfigurationProvider agentProvider = (extensionManager.GetExtensions <IConfigurationProvider>()) .FirstOrDefault(x => x.ConfigurationProviderType == agentType); ArgUtil.NotNull(agentProvider, agentType); // 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 { // Validate can connect. await agentProvider.TestConnectionAsync(agentSettings, creds); Trace.Info("Test Connection complete."); break; } catch (Exception e) when(!command.Unattended) { _term.WriteError(e); _term.WriteError(StringUtil.Loc("FailedToConnect")); } } _agentServer = HostContext.GetService <IAgentServer>(); // 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.GetPoolId(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) { var credentialData = new CredentialData { Scheme = Constants.Configuration.OAuth, Data = { { "clientId", agent.Authorization.ClientId.ToString("D") }, { "authorizationUrl", agent.Authorization.AuthorizationUrl.AbsoluteUri }, }, }; // Save the negotiated OAuth credential data _store.SaveCredential(credentialData); } else { switch (Constants.Agent.Platform) { case Constants.OSPlatform.OSX: case Constants.OSPlatform.Linux: // Save the provided admin cred for compat with previous agent. _store.SaveCredential(credProvider.CredentialData); break; case Constants.OSPlatform.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(); VssConnection conn = ApiUtil.CreateConnection(new Uri(agentSettings.ServerUrl), credential); var agentSvr = HostContext.GetService <IAgentServer>(); try { await agentSvr.ConnectAsync(conn); } catch (VssOAuthTokenRequestException ex) when(ex.Message.Contains("Current server time is")) { // there are two exception messages server send that indicate clock skew. // 1. The bearer token expired on {jwt.ValidTo}. Current server time is {DateTime.UtcNow}. // 2. The bearer token is not valid until {jwt.ValidFrom}. Current server time is {DateTime.UtcNow}. Trace.Error("Catch exception during test agent connection."); Trace.Error(ex); throw new Exception(StringUtil.Loc("LocalClockSkewed")); } // 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.NotificationSocketAddress = command.GetNotificationSocketAddress(); _store.SaveSettings(agentSettings); if (saveProxySetting) { Trace.Info("Save proxy setting to disk."); (vstsProxy as VstsAgentWebProxy).SaveProxySetting(); } _term.WriteLine(StringUtil.Loc("SavedSettings", DateTime.UtcNow)); #if OS_WINDOWS // config windows service bool runAsService = command.GetRunAsService(); if (runAsService) { Trace.Info("Configuring to run the agent as service"); var serviceControlManager = HostContext.GetService <IWindowsServiceControlManager>(); serviceControlManager.ConfigureService(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 } #elif OS_LINUX || OS_OSX // generate service config script for OSX and Linux, GenerateScripts() will no-opt on windows. var serviceControlManager = HostContext.GetService <ILinuxServiceControlManager>(); serviceControlManager.GenerateScripts(agentSettings); #endif }
public async Task UnconfigureAsync(CommandSettings command) { ArgUtil.Equal(RunMode.Normal, HostContext.RunMode, nameof(HostContext.RunMode)); 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 Exception(StringUtil.Loc("UnconfigureServiceDService")); } else if (PlatformUtil.RunningOnMacOS) { // unconfig macOS service first throw new Exception(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 IsHostedServer(settings.ServerUrl, creds); 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 (Exception) { _term.WriteLine(StringUtil.Loc("Failed") + currentAction); throw; } }
public async Task ConfigureAsync(CommandSettings command) { ArgUtil.Equal(RunMode.Normal, HostContext.RunMode, nameof(HostContext.RunMode)); 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.Externals), Constants.Path.TeeDirectory, "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.DeploymentGroup) { agentType = Constants.Agent.AgentConfigurationProvider.DeploymentAgentConfiguration; } else if (command.DeploymentPool) { agentType = Constants.Agent.AgentConfigurationProvider.SharedDeploymentAgentConfiguration; } else if (command.EnvironmentVMResource) { 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) isHostedServer = await IsHostedServer(agentSettings.ServerUrl, creds); // 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 (Exception e) when(!command.Unattended) { _term.WriteError(e); _term.WriteError(StringUtil.Loc("FailedToConnect")); } } _agentServer = HostContext.GetService <IAgentServer>(); // 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 Exception(StringUtil.Loc("LocalClockSkewed")); } // 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(); _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.GitUseSChannel) { 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>(); 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); } }
// StepsRunner should never throw exception to caller public async Task RunAsync(IExecutionContext jobContext, IList <IStep> steps) { ArgUtil.NotNull(jobContext, nameof(jobContext)); ArgUtil.NotNull(steps, nameof(steps)); // TaskResult: // Abandoned (Server set this.) // Canceled // Failed // Skipped // Succeeded // SucceededWithIssues CancellationTokenRegistration?jobCancelRegister = null; int stepIndex = 0; jobContext.Variables.Agent_JobStatus = jobContext.Result ?? TaskResult.Succeeded; foreach (IStep step in steps) { Trace.Info($"Processing step: DisplayName='{step.DisplayName}', ContinueOnError={step.ContinueOnError}, Enabled={step.Enabled}"); ArgUtil.Equal(true, step.Enabled, nameof(step.Enabled)); ArgUtil.NotNull(step.ExecutionContext, nameof(step.ExecutionContext)); ArgUtil.NotNull(step.ExecutionContext.Variables, nameof(step.ExecutionContext.Variables)); stepIndex++; // Start. step.ExecutionContext.Start(); var taskStep = step as ITaskRunner; if (taskStep != null) { HostContext.WritePerfCounter($"TaskStart_{taskStep.Task.Reference.Name}_{stepIndex}"); } // Variable expansion. List <string> expansionWarnings; step.ExecutionContext.Variables.RecalculateExpanded(out expansionWarnings); expansionWarnings?.ForEach(x => step.ExecutionContext.Warning(x)); var expressionManager = HostContext.GetService <IExpressionManager>(); try { // Register job cancellation call back only if job cancellation token not been fire before each step run if (!jobContext.CancellationToken.IsCancellationRequested) { // Test the condition again. The job was canceled after the condition was originally evaluated. jobCancelRegister = jobContext.CancellationToken.Register(() => { // mark job as cancelled jobContext.Result = TaskResult.Canceled; jobContext.Variables.Agent_JobStatus = jobContext.Result; step.ExecutionContext.Debug($"Re-evaluate condition on job cancellation for step: '{step.DisplayName}'."); ConditionResult conditionReTestResult; if (HostContext.AgentShutdownToken.IsCancellationRequested) { step.ExecutionContext.Debug($"Skip Re-evaluate condition on agent shutdown."); conditionReTestResult = false; } else { try { conditionReTestResult = expressionManager.Evaluate(step.ExecutionContext, step.Condition, hostTracingOnly: true); } catch (Exception ex) { // Cancel the step since we get exception while re-evaluate step condition. Trace.Info("Caught exception from expression when re-test condition on job cancellation."); step.ExecutionContext.Error(ex); conditionReTestResult = false; } } if (!conditionReTestResult.Value) { // Cancel the step. Trace.Info("Cancel current running step."); step.ExecutionContext.CancelToken(); } }); } else { if (jobContext.Result != TaskResult.Canceled) { // mark job as cancelled jobContext.Result = TaskResult.Canceled; jobContext.Variables.Agent_JobStatus = jobContext.Result; } } // Evaluate condition. step.ExecutionContext.Debug($"Evaluating condition for step: '{step.DisplayName}'"); Exception conditionEvaluateError = null; ConditionResult conditionResult; if (HostContext.AgentShutdownToken.IsCancellationRequested) { step.ExecutionContext.Debug($"Skip evaluate condition on agent shutdown."); conditionResult = false; } else { try { conditionResult = expressionManager.Evaluate(step.ExecutionContext, step.Condition); } catch (Exception ex) { Trace.Info("Caught exception from expression."); Trace.Error(ex); conditionResult = false; conditionEvaluateError = ex; } } // no evaluate error but condition is false if (!conditionResult.Value && conditionEvaluateError == null) { // Condition == false Trace.Info("Skipping step due to condition evaluation."); step.ExecutionContext.Complete(TaskResult.Skipped, resultCode: conditionResult.Trace); continue; } if (conditionEvaluateError != null) { // fail the step since there is an evaluate error. step.ExecutionContext.Error(conditionEvaluateError); step.ExecutionContext.Complete(TaskResult.Failed); } else { // Run the step. await RunStepAsync(step, jobContext.CancellationToken); } } finally { if (jobCancelRegister != null) { jobCancelRegister?.Dispose(); jobCancelRegister = null; } } // Update the job result. if (step.ExecutionContext.Result == TaskResult.SucceededWithIssues || step.ExecutionContext.Result == TaskResult.Failed) { Trace.Info($"Update job result with current step result '{step.ExecutionContext.Result}'."); jobContext.Result = TaskResultUtil.MergeTaskResults(jobContext.Result, step.ExecutionContext.Result.Value); jobContext.Variables.Agent_JobStatus = jobContext.Result; } else { Trace.Info($"No need for updating job result with current step result '{step.ExecutionContext.Result}'."); } if (taskStep != null) { HostContext.WritePerfCounter($"TaskCompleted_{taskStep.Task.Reference.Name}_{stepIndex}"); } Trace.Info($"Current state: job state = '{jobContext.Result}'"); } }
public TrackingConfig Create( IExecutionContext executionContext, RepositoryResource repository, string hashKey, string file, bool overrideBuildDirectory) { Trace.Entering(); // Get or create the top-level tracking config. TopLevelTrackingConfig topLevelConfig; string topLevelFile = Path.Combine( HostContext.GetDirectory(WellKnownDirectory.Work), Constants.Build.Path.SourceRootMappingDirectory, Constants.Build.Path.TopLevelTrackingConfigFile); Trace.Verbose($"Loading top-level tracking config if exists: {topLevelFile}"); if (!File.Exists(topLevelFile)) { topLevelConfig = new TopLevelTrackingConfig(); } else { topLevelConfig = JsonConvert.DeserializeObject <TopLevelTrackingConfig>(File.ReadAllText(topLevelFile)); if (topLevelConfig == null) { executionContext.Warning($"Rebuild corruptted top-level tracking configure file {topLevelFile}."); // save the corruptted file in case we need to investigate more. File.Copy(topLevelFile, $"{topLevelFile}.corruptted", true); topLevelConfig = new TopLevelTrackingConfig(); DirectoryInfo workDir = new DirectoryInfo(HostContext.GetDirectory(WellKnownDirectory.Work)); foreach (var dir in workDir.EnumerateDirectories()) { // we scan the entire _work directory and find the directory with the highest integer number. if (int.TryParse(dir.Name, NumberStyles.Integer, CultureInfo.InvariantCulture, out int lastBuildNumber) && lastBuildNumber > topLevelConfig.LastBuildDirectoryNumber) { topLevelConfig.LastBuildDirectoryNumber = lastBuildNumber; } } } } // Determine the build directory. if (overrideBuildDirectory) { // This should only occur during hosted builds. This was added due to TFVC. // TFVC does not allow a local path for a single machine to be mapped in multiple // workspaces. The machine name for a hosted images is not unique. // // So if a customer is running two hosted builds at the same time, they could run // into the local mapping conflict. // // The workaround is to force the build directory to be different across all concurrent // hosted builds (for TFVC). The agent ID will be unique across all concurrent hosted // builds so that can safely be used as the build directory. ArgUtil.Equal(default(int), topLevelConfig.LastBuildDirectoryNumber, nameof(topLevelConfig.LastBuildDirectoryNumber)); var configurationStore = HostContext.GetService <IConfigurationStore>(); AgentSettings settings = configurationStore.GetSettings(); topLevelConfig.LastBuildDirectoryNumber = settings.AgentId; } else { topLevelConfig.LastBuildDirectoryNumber++; } // Update the top-level tracking config. topLevelConfig.LastBuildDirectoryCreatedOn = DateTimeOffset.Now; WriteToFile(topLevelFile, topLevelConfig); // Create the new tracking config. TrackingConfig config = new TrackingConfig( executionContext, repository, topLevelConfig.LastBuildDirectoryNumber, hashKey); WriteToFile(file, config); return(config); }
//----------------------------------------------------------------- // Task Manager: Query and Download Task //----------------------------------------------------------------- public Task <Stream> GetTaskContentZipAsync(Guid taskId, TaskVersion taskVersion, CancellationToken token) { ArgUtil.Equal(RunMode.Normal, HostContext.RunMode, nameof(HostContext.RunMode)); CheckConnection(); return(_taskAgentClient.GetTaskContentZipAsync(taskId, taskVersion, cancellationToken: token)); }
public async Task <int> RunAsync(string pipeIn, string pipeOut) { // Validate args. ArgUtil.NotNullOrEmpty(pipeIn, nameof(pipeIn)); ArgUtil.NotNullOrEmpty(pipeOut, nameof(pipeOut)); var agentWebProxy = HostContext.GetService <IVstsAgentWebProxy>(); var agentCertManager = HostContext.GetService <IAgentCertificateManager>(); VssUtil.InitializeVssClientSettings(HostContext.UserAgent, agentWebProxy.WebProxy, agentCertManager.VssClientCertificateManager); var jobRunner = HostContext.CreateService <IJobRunner>(); using (var channel = HostContext.CreateService <IProcessChannel>()) using (var jobRequestCancellationToken = CancellationTokenSource.CreateLinkedTokenSource(HostContext.AgentShutdownToken)) using (var channelTokenSource = new CancellationTokenSource()) { // Start the channel. channel.StartClient(pipeIn, pipeOut); // Wait for up to 30 seconds for a message from the channel. Trace.Info("Waiting to receive the job message from the channel."); WorkerMessage channelMessage; using (var csChannelMessage = new CancellationTokenSource(_workerStartTimeout)) { channelMessage = await channel.ReceiveAsync(csChannelMessage.Token); } // Deserialize the job message. Trace.Info("Message received."); ArgUtil.Equal(MessageType.NewJobRequest, channelMessage.MessageType, nameof(channelMessage.MessageType)); ArgUtil.NotNullOrEmpty(channelMessage.Body, nameof(channelMessage.Body)); var jobMessage = JsonUtility.FromString <Pipelines.AgentJobRequestMessage>(channelMessage.Body); ArgUtil.NotNull(jobMessage, nameof(jobMessage)); HostContext.WritePerfCounter($"WorkerJobMessageReceived_{jobMessage.RequestId.ToString()}"); // Initialize the secret masker and set the thread culture. InitializeSecretMasker(jobMessage); SetCulture(jobMessage); // Start the job. Trace.Info($"Job message:{Environment.NewLine} {StringUtil.ConvertToJson(jobMessage)}"); Task <TaskResult> jobRunnerTask = jobRunner.RunAsync(jobMessage, jobRequestCancellationToken.Token); // Start listening for a cancel message from the channel. Trace.Info("Listening for cancel message from the channel."); Task <WorkerMessage> channelTask = channel.ReceiveAsync(channelTokenSource.Token); // Wait for one of the tasks to complete. Trace.Info("Waiting for the job to complete or for a cancel message from the channel."); Task.WaitAny(jobRunnerTask, channelTask); // Handle if the job completed. if (jobRunnerTask.IsCompleted) { Trace.Info("Job completed."); channelTokenSource.Cancel(); // Cancel waiting for a message from the channel. return(TaskResultUtil.TranslateToReturnCode(await jobRunnerTask)); } // Otherwise a cancel message was received from the channel. Trace.Info("Cancellation/Shutdown message received."); channelMessage = await channelTask; switch (channelMessage.MessageType) { case MessageType.CancelRequest: jobRequestCancellationToken.Cancel(); // Expire the host cancellation token. break; case MessageType.AgentShutdown: HostContext.ShutdownAgent(ShutdownReason.UserCancelled); break; case MessageType.OperatingSystemShutdown: HostContext.ShutdownAgent(ShutdownReason.OperatingSystemShutdown); break; default: throw new ArgumentOutOfRangeException(nameof(channelMessage.MessageType), channelMessage.MessageType, nameof(channelMessage.MessageType)); } // Await the job. return(TaskResultUtil.TranslateToReturnCode(await jobRunnerTask)); } }
public void DeleteCredential() { ArgUtil.Equal(RunMode.Normal, HostContext.RunMode, nameof(HostContext.RunMode)); IOUtil.Delete(_credFilePath, default(CancellationToken)); }
private string FormatArguments(FormatFlags formatFlags, params string[] args) { // Validation. ArgUtil.NotNull(args, nameof(args)); ArgUtil.NotNull(Endpoint, nameof(Endpoint)); ArgUtil.NotNull(Endpoint.Authorization, nameof(Endpoint.Authorization)); ArgUtil.NotNull(Endpoint.Authorization.Parameters, nameof(Endpoint.Authorization.Parameters)); ArgUtil.Equal(EndpointAuthorizationSchemes.OAuth, Endpoint.Authorization.Scheme, nameof(Endpoint.Authorization.Scheme)); string accessToken = Endpoint.Authorization.Parameters.TryGetValue(EndpointAuthorizationParameters.AccessToken, out accessToken) ? accessToken : null; ArgUtil.NotNullOrEmpty(accessToken, EndpointAuthorizationParameters.AccessToken); ArgUtil.NotNull(Repository.Url, nameof(Repository.Url)); // Format each arg. var formattedArgs = new List <string>(); foreach (string arg in args ?? new string[0]) { // Validate the arg. if (!string.IsNullOrEmpty(arg) && arg.IndexOfAny(new char[] { '"', '\r', '\n' }) >= 0) { throw new Exception(StringUtil.Loc("InvalidCommandArg", arg)); } // Add the arg. formattedArgs.Add(arg != null && arg.Contains(" ") ? $@"""{arg}""" : $"{arg}"); } // Add the common parameters. if (!formatFlags.HasFlag(FormatFlags.OmitCollectionUrl)) { if (Features.HasFlag(TfsVCFeatures.EscapedUrl)) { formattedArgs.Add($"{Switch}collection:{Repository.Url.AbsoluteUri}"); } else { // TEE CLC expects the URL in unescaped form. string url; try { url = Uri.UnescapeDataString(Repository.Url.AbsoluteUri); } catch (Exception ex) { // Unlikely (impossible?), but don't fail if encountered. If we don't hear complaints // about this warning then it is likely OK to remove the try/catch altogether and have // faith that UnescapeDataString won't throw for this scenario. url = Repository.Url.AbsoluteUri; ExecutionContext.Warning($"{ex.Message} ({url})"); } formattedArgs.Add($"\"{Switch}collection:{url}\""); } } if (!formatFlags.HasFlag(FormatFlags.OmitLogin)) { if (Features.HasFlag(TfsVCFeatures.LoginType)) { formattedArgs.Add($"{Switch}loginType:OAuth"); formattedArgs.Add($"{Switch}login:.,{accessToken}"); } else { formattedArgs.Add($"{Switch}jwt:{accessToken}"); } } if (!formatFlags.HasFlag(FormatFlags.OmitNoPrompt)) { formattedArgs.Add($"{Switch}noprompt"); } return(string.Join(" ", formattedArgs)); }
public TrackingConfig Create( IExecutionContext executionContext, ServiceEndpoint endpoint, string hashKey, string file, bool overrideBuildDirectory) { Trace.Entering(); // Get or create the top-level tracking config. TopLevelTrackingConfig topLevelConfig; string topLevelFile = Path.Combine( IOUtil.GetWorkPath(HostContext), Constants.Build.Path.SourceRootMappingDirectory, Constants.Build.Path.TopLevelTrackingConfigFile); Trace.Verbose($"Loading top-level tracking config if exists: {topLevelFile}"); if (!File.Exists(topLevelFile)) { topLevelConfig = new TopLevelTrackingConfig(); } else { topLevelConfig = JsonConvert.DeserializeObject <TopLevelTrackingConfig>( value: File.ReadAllText(topLevelFile)); } // Determine the build directory. if (overrideBuildDirectory) { // This should only occur during hosted builds. This was added due to TFVC. // TFVC does not allow a local path for a single machine to be mapped in multiple // workspaces. The machine name for a hosted images is not unique. // // So if a customer is running two hosted builds at the same time, they could run // into the local mapping conflict. // // The workaround is to force the build directory to be different across all concurrent // hosted builds (for TFVC). The agent ID will be unique across all concurrent hosted // builds so that can safely be used as the build directory. ArgUtil.Equal(default(int), topLevelConfig.LastBuildDirectoryNumber, nameof(topLevelConfig.LastBuildDirectoryNumber)); var configurationStore = HostContext.GetService <IConfigurationStore>(); AgentSettings settings = configurationStore.GetSettings(); topLevelConfig.LastBuildDirectoryNumber = settings.AgentId; } else { topLevelConfig.LastBuildDirectoryNumber++; } // Update the top-level tracking config. topLevelConfig.LastBuildDirectoryCreatedOn = DateTimeOffset.Now; WriteToFile(topLevelFile, topLevelConfig); // Create the new tracking config. TrackingConfig config = new TrackingConfig( executionContext, endpoint, topLevelConfig.LastBuildDirectoryNumber, hashKey); WriteToFile(file, config); return(config); }