Esempio n. 1
0
        // TODO: We need send detailInfo back to DT in order to add an issue for the job
        private async Task CompleteJobRequestAsync(int poolId, Pipelines.AgentJobRequestMessage message, Guid lockToken, TaskResult result, string detailInfo = null)
        {
            Trace.Entering();
            if (HostContext.RunMode == RunMode.Local)
            {
                _localRunJobResult.Value[message.RequestId] = result;
                return;
            }

            if (ApiUtil.GetFeatures(message.Plan).HasFlag(PlanFeatures.JobCompletedPlanEvent))
            {
                Trace.Verbose($"Skip FinishAgentRequest call from Listener because Plan version is {message.Plan.Version}");
                return;
            }

            var agentServer = HostContext.GetService <IAgentServer>();
            int completeJobRequestRetryLimit = 5;
            List <Exception> exceptions      = new List <Exception>();

            while (completeJobRequestRetryLimit-- > 0)
            {
                try
                {
                    await agentServer.FinishAgentRequestAsync(poolId, message.RequestId, lockToken, DateTime.UtcNow, result, CancellationToken.None);

                    return;
                }
                catch (TaskAgentJobNotFoundException)
                {
                    Trace.Info($"TaskAgentJobNotFoundException received, job {message.JobId} is no longer valid.");
                    return;
                }
                catch (TaskAgentJobTokenExpiredException)
                {
                    Trace.Info($"TaskAgentJobTokenExpiredException received, job {message.JobId} is no longer valid.");
                    return;
                }
                catch (Exception ex)
                {
                    Trace.Error($"Catch exception during complete agent jobrequest {message.RequestId}.");
                    Trace.Error(ex);
                    exceptions.Add(ex);
                }

                // delay 5 seconds before next retry.
                await Task.Delay(TimeSpan.FromSeconds(5));
            }

            // rethrow all catched exceptions during retry.
            throw new AggregateException(exceptions);
        }
Esempio n. 2
0
        private async Task <TestResults> RunWorker(HostContext HostContext, Pipelines.AgentJobRequestMessage message, CancellationToken jobRequestCancellationToken)
        {
            var worker = HostContext.GetService <IWorker>();

            Task <int> workerTask = null;

            // Setup the anonymous pipes to use for communication with the worker.
            using (var processChannel = HostContext.CreateService <IProcessChannel>())
            {
                processChannel.StartServer(startProcess: (string pipeHandleOut, string pipeHandleIn) =>
                {
                    // Run the worker
                    // Note: this happens on the same process as the test
                    workerTask = worker.RunAsync(
                        pipeIn: pipeHandleOut,
                        pipeOut: pipeHandleIn);
                }, disposeClient: false);

                // Send the job request message to the worker
                var body = JsonUtility.ToString(message);
                using (var csSendJobRequest = new CancellationTokenSource(ChannelTimeout))
                {
                    await processChannel.SendAsync(
                        messageType : MessageType.NewJobRequest,
                        body : body,
                        cancellationToken : csSendJobRequest.Token);
                }

                // wait for worker process or cancellation token been fired.
                var completedTask = await Task.WhenAny(workerTask, Task.Delay(-1, jobRequestCancellationToken));

                if (completedTask == workerTask)
                {
                    int returnCode = await workerTask;

                    TaskResult result = TaskResultUtil.TranslateFromReturnCode(returnCode);
                    return(new TestResults
                    {
                        ReturnCode = returnCode,
                        Result = result
                    });
                }
                else
                {
                    return(new TestResults
                    {
                        TimedOut = true
                    });
                }
            }
        }
Esempio n. 3
0
        public async void DispatchesOneTimeJobRequest()
        {
            //Arrange
            using (var hc = new TestHostContext(this))
            {
                var jobDispatcher = new JobDispatcher();
                hc.SetSingleton <IConfigurationStore>(_configurationStore.Object);
                hc.SetSingleton <IAgentServer>(_agentServer.Object);

                hc.EnqueueInstance <IProcessChannel>(_processChannel.Object);
                hc.EnqueueInstance <IProcessInvoker>(_processInvoker.Object);

                _configurationStore.Setup(x => x.GetSettings()).Returns(new AgentSettings()
                {
                    PoolId = 1
                });
                jobDispatcher.Initialize(hc);

                var ts = new CancellationTokenSource();
                Pipelines.AgentJobRequestMessage message = CreateJobRequestMessage();
                string strMessage = JsonUtility.ToString(message);

                _processInvoker.Setup(x => x.ExecuteAsync(It.IsAny <String>(), It.IsAny <String>(), "spawnclient 1 2", null, It.IsAny <CancellationToken>()))
                .Returns(Task.FromResult <int>(56));

                _processChannel.Setup(x => x.StartServer(It.IsAny <StartProcessDelegate>(), It.IsAny <bool>()))
                .Callback((StartProcessDelegate startDel, bool disposeClient) => { startDel("1", "2"); });
                _processChannel.Setup(x => x.SendAsync(MessageType.NewJobRequest, It.Is <string>(s => s.Equals(strMessage)), It.IsAny <CancellationToken>()))
                .Returns(Task.CompletedTask);

                var          request           = new TaskAgentJobRequest();
                PropertyInfo sessionIdProperty = request.GetType().GetProperty("LockedUntil", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
                Assert.NotNull(sessionIdProperty);
                sessionIdProperty.SetValue(request, DateTime.UtcNow.AddMinutes(5));

                _agentServer.Setup(x => x.RenewAgentRequestAsync(It.IsAny <int>(), It.IsAny <long>(), It.IsAny <Guid>(), It.IsAny <CancellationToken>())).Returns(Task.FromResult <TaskAgentJobRequest>(request));

                _agentServer.Setup(x => x.FinishAgentRequestAsync(It.IsAny <int>(), It.IsAny <long>(), It.IsAny <Guid>(), It.IsAny <DateTime>(), It.IsAny <TaskResult>(), It.IsAny <CancellationToken>())).Returns(Task.FromResult <TaskAgentJobRequest>(new TaskAgentJobRequest()));

                //Act
                jobDispatcher.Run(message, true);

                //Assert
                await jobDispatcher.WaitAsync(CancellationToken.None);

                Assert.True(jobDispatcher.RunOnceJobCompleted.Task.IsCompleted, "JobDispatcher should set task complete token for one time agent.");
                Assert.True(jobDispatcher.RunOnceJobCompleted.Task.Result, "JobDispatcher should set task complete token to 'TRUE' for one time agent.");
            }
        }
Esempio n. 4
0
        private void SetCulture(Pipelines.AgentJobRequestMessage message)
        {
            // Extract the culture name from the job's variable dictionary.
            // The variable does not exist for TFS 2015 RTM and Update 1.
            // It was introduced in Update 2.
            VariableValue culture;

            ArgUtil.NotNull(message, nameof(message));
            ArgUtil.NotNull(message.Variables, nameof(message.Variables));
            if (message.Variables.TryGetValue(Constants.Variables.System.Culture, out culture))
            {
                // Set the default thread culture.
                HostContext.SetDefaultCulture(culture.Value);
            }
        }
        protected async Task <TestResults> RunWorker(Pipelines.AgentJobRequestMessage message)
        {
            if (_l1HostContext == null)
            {
                throw new InvalidOperationException("Must call SetupL1() to initialize L1HostContext before calling RunWorker()");
            }

            await SetupMessage(_l1HostContext, message);

            using (var cts = new CancellationTokenSource())
            {
                cts.CancelAfter((int)JobTimeout.TotalMilliseconds);
                return(await RunWorker(_l1HostContext, message, cts.Token));
            }
        }
Esempio n. 6
0
        public void StepTarget_RestrictedCommands_Host()
        {
            using (TestHostContext hc = CreateTestContext())
                using (var ec = new Agent.Worker.ExecutionContext())
                {
                    ec.Initialize(hc);

                    var pipeContainer = new Pipelines.ContainerResource {
                        Alias = "container"
                    };
                    pipeContainer.Properties.Set <string>("image", "someimage");
                    // Arrange: Create a job request message.
                    TaskOrchestrationPlanReference plan = new TaskOrchestrationPlanReference();
                    TimelineReference timeline          = new TimelineReference();
                    JobEnvironment    environment       = new JobEnvironment();
                    environment.SystemConnection = new ServiceEndpoint();
                    List <Pipelines.JobStep> steps = new List <Pipelines.JobStep>();
                    steps.Add(new Pipelines.TaskStep
                    {
                        Target = new Pipelines.StepTarget
                        {
                            Target   = "host",
                            Commands = "restricted"
                        },
                        Reference = new Pipelines.TaskStepDefinitionReference()
                    });
                    var resources = new Pipelines.JobResources();
                    resources.Containers.Add(pipeContainer);
                    Guid   JobId      = Guid.NewGuid();
                    string jobName    = "some job name";
                    var    jobRequest = new Pipelines.AgentJobRequestMessage(plan, timeline, JobId, jobName, jobName, null, new Dictionary <string, string>(),
                                                                             new Dictionary <string, VariableValue>(), new List <MaskHint>(), resources, new Pipelines.WorkspaceOptions(), steps);

                    // Arrange: Setup command manager
                    var commandMock = new Mock <IWorkerCommandManager>();
                    hc.SetSingleton(commandMock.Object);
                    var pagingLogger = new Mock <IPagingLogger>();
                    hc.EnqueueInstance(pagingLogger.Object);

                    // Act.
                    ec.InitializeJob(jobRequest, CancellationToken.None);
                    ec.SetStepTarget(steps[0].Target);

                    // Assert.
                    Assert.IsType <HostInfo>(ec.StepTarget());
                    commandMock.Verify(x => x.SetCommandRestrictionPolicy(It.IsAny <AttributeBasedWorkerCommandRestrictionPolicy>()));
                }
        }
Esempio n. 7
0
        private void ReplaceConfigUriBaseInJobRequestMessage(Pipelines.AgentJobRequestMessage message)
        {
            ServiceEndpoint systemConnection    = message.Resources.Endpoints.Single(x => string.Equals(x.Name, WellKnownServiceEndpointNames.SystemVssConnection, StringComparison.OrdinalIgnoreCase));
            Uri             systemConnectionUrl = systemConnection.Url;

            // fixup any endpoint Url that match SystemConnection Url.
            foreach (var endpoint in message.Resources.Endpoints)
            {
                if (Uri.Compare(endpoint.Url, systemConnectionUrl, UriComponents.SchemeAndServer, UriFormat.Unescaped, StringComparison.OrdinalIgnoreCase) == 0)
                {
                    endpoint.Url = ReplaceWithConfigUriBase(endpoint.Url);
                    Trace.Info($"Ensure endpoint url match config url base. {endpoint.Url}");
                }
            }

            // fixup any repository Url that match SystemConnection Url.
            foreach (var repo in message.Resources.Repositories)
            {
                if (Uri.Compare(repo.Url, systemConnectionUrl, UriComponents.SchemeAndServer, UriFormat.Unescaped, StringComparison.OrdinalIgnoreCase) == 0)
                {
                    repo.Url = ReplaceWithConfigUriBase(repo.Url);
                    Trace.Info($"Ensure repository url match config url base. {repo.Url}");
                }
            }

            // fixup well known variables. (taskDefinitionsUrl, tfsServerUrl, tfsCollectionUrl)
            if (message.Variables.ContainsKey(WellKnownDistributedTaskVariables.TaskDefinitionsUrl))
            {
                string taskDefinitionsUrl = message.Variables[WellKnownDistributedTaskVariables.TaskDefinitionsUrl].Value;
                message.Variables[WellKnownDistributedTaskVariables.TaskDefinitionsUrl] = ReplaceWithConfigUriBase(new Uri(taskDefinitionsUrl)).AbsoluteUri;
                Trace.Info($"Ensure System.TaskDefinitionsUrl match config url base. {message.Variables[WellKnownDistributedTaskVariables.TaskDefinitionsUrl].Value}");
            }

            if (message.Variables.ContainsKey(WellKnownDistributedTaskVariables.TFCollectionUrl))
            {
                string tfsCollectionUrl = message.Variables[WellKnownDistributedTaskVariables.TFCollectionUrl].Value;
                message.Variables[WellKnownDistributedTaskVariables.TFCollectionUrl] = ReplaceWithConfigUriBase(new Uri(tfsCollectionUrl)).AbsoluteUri;
                Trace.Info($"Ensure System.TFCollectionUrl match config url base. {message.Variables[WellKnownDistributedTaskVariables.TFCollectionUrl].Value}");
            }

            if (message.Variables.ContainsKey(Constants.Variables.System.TFServerUrl))
            {
                string tfsServerUrl = message.Variables[Constants.Variables.System.TFServerUrl].Value;
                message.Variables[Constants.Variables.System.TFServerUrl] = ReplaceWithConfigUriBase(new Uri(tfsServerUrl)).AbsoluteUri;
                Trace.Info($"Ensure System.TFServerUrl match config url base. {message.Variables[Constants.Variables.System.TFServerUrl].Value}");
            }
        }
Esempio n. 8
0
        public void StepTarget_LoadStepContainersWithoutJobContainer()
        {
            using (TestHostContext hc = CreateTestContext())
                using (var ec = new Agent.Worker.ExecutionContext())
                {
                    ec.Initialize(hc);

                    var pipeContainer = new Pipelines.ContainerResource {
                        Alias = "container"
                    };
                    pipeContainer.Properties.Set <string>("image", "someimage");
                    // Arrange: Create a job request message.
                    TaskOrchestrationPlanReference plan = new TaskOrchestrationPlanReference();
                    TimelineReference timeline          = new TimelineReference();
                    JobEnvironment    environment       = new JobEnvironment();
                    environment.SystemConnection = new ServiceEndpoint();
                    List <Pipelines.JobStep> steps = new List <Pipelines.JobStep>();
                    steps.Add(new Pipelines.TaskStep
                    {
                        Target = new Pipelines.StepTarget
                        {
                            Target = "container"
                        },
                        Reference = new Pipelines.TaskStepDefinitionReference()
                    });
                    var resources = new Pipelines.JobResources();
                    resources.Containers.Add(pipeContainer);
                    Guid   JobId      = Guid.NewGuid();
                    string jobName    = "some job name";
                    var    jobRequest = new Pipelines.AgentJobRequestMessage(plan, timeline, JobId, jobName, jobName, null, new Dictionary <string, string>(),
                                                                             new Dictionary <string, VariableValue>(), new List <MaskHint>(), resources, new Pipelines.WorkspaceOptions(), steps);

                    // Arrange: Setup command manager
                    var pagingLogger = new Mock <IPagingLogger>();
                    hc.EnqueueInstance(pagingLogger.Object);

                    // Act.
                    ec.InitializeJob(jobRequest, CancellationToken.None);

                    // Assert.
                    Assert.Equal(1, ec.Containers.Count());
                }
        }
Esempio n. 9
0
        public void Start(Pipelines.AgentJobRequestMessage jobRequest)
        {
            Trace.Entering();

            if (_queueInProcess)
            {
                Trace.Info("No-opt, all queue process tasks are running.");
                return;
            }

            ArgUtil.NotNull(jobRequest, nameof(jobRequest));
            ArgUtil.NotNull(jobRequest.Plan, nameof(jobRequest.Plan));
            ArgUtil.NotNull(jobRequest.Timeline, nameof(jobRequest.Timeline));

            _scopeIdentifier     = jobRequest.Plan.ScopeIdentifier;
            _hubName             = jobRequest.Plan.PlanType;
            _planId              = jobRequest.Plan.PlanId;
            _jobTimelineId       = jobRequest.Timeline.Id;
            _jobTimelineRecordId = jobRequest.JobId;

            if (jobRequest.Variables.TryGetValue("agent.LogToBlobstorageService", out var val))
            {
                Boolean.TryParse(val.Value, out _writeToBlobstorageService);
            }

            // Server already create the job timeline
            _timelineUpdateQueue[_jobTimelineId] = new ConcurrentQueue <TimelineRecord>();
            _allTimelines.Add(_jobTimelineId);

            // Start three dequeue task
            Trace.Info("Start process web console line queue.");
            _webConsoleLineDequeueTask = ProcessWebConsoleLinesQueueAsync();

            Trace.Info("Start process file upload queue.");
            _fileUploadDequeueTask = ProcessFilesUploadQueueAsync();

            Trace.Info("Start process timeline update queue.");
            _timelineUpdateDequeueTask = ProcessTimelinesUpdateQueueAsync();

            _allDequeueTasks = new Task[] { _webConsoleLineDequeueTask, _fileUploadDequeueTask, _timelineUpdateDequeueTask };
            _queueInProcess  = true;
        }
Esempio n. 10
0
        public void Start(Pipelines.AgentJobRequestMessage jobRequest)
        {
            Trace.Entering();
            if (HostContext.RunMode == RunMode.Local)
            {
                _term = HostContext.GetService <ITerminal>();
                return;
            }

            if (_queueInProcess)
            {
                Trace.Info("No-opt, all queue process tasks are running.");
                return;
            }

            ArgUtil.NotNull(jobRequest, nameof(jobRequest));
            ArgUtil.NotNull(jobRequest.Plan, nameof(jobRequest.Plan));
            ArgUtil.NotNull(jobRequest.Timeline, nameof(jobRequest.Timeline));

            _scopeIdentifier     = jobRequest.Plan.ScopeIdentifier;
            _hubName             = jobRequest.Plan.PlanType;
            _planId              = jobRequest.Plan.PlanId;
            _jobTimelineId       = jobRequest.Timeline.Id;
            _jobTimelineRecordId = jobRequest.JobId;

            // Server already create the job timeline
            _timelineUpdateQueue[_jobTimelineId] = new ConcurrentQueue <TimelineRecord>();
            _allTimelines.Add(_jobTimelineId);

            // Start three dequeue task
            Trace.Info("Start process web console line queue.");
            _webConsoleLineDequeueTask = ProcessWebConsoleLinesQueueAsync();

            Trace.Info("Start process file upload queue.");
            _fileUploadDequeueTask = ProcessFilesUploadQueueAsync();

            Trace.Info("Start process timeline update queue.");
            _timelineUpdateDequeueTask = ProcessTimelinesUpdateQueueAsync();

            _allDequeueTasks = new Task[] { _webConsoleLineDequeueTask, _fileUploadDequeueTask, _timelineUpdateDequeueTask };
            _queueInProcess  = true;
        }
Esempio n. 11
0
        public void Run(Pipelines.AgentJobRequestMessage jobRequestMessage)
        {
            Trace.Info($"Job request {jobRequestMessage.RequestId} for plan {jobRequestMessage.Plan.PlanId} job {jobRequestMessage.JobId} received.");

            WorkerDispatcher currentDispatch = null;

            if (_jobDispatchedQueue.Count > 0)
            {
                Guid dispatchedJobId = _jobDispatchedQueue.Dequeue();
                if (_jobInfos.TryGetValue(dispatchedJobId, out currentDispatch))
                {
                    Trace.Verbose($"Retrive previous WorkerDispather for job {currentDispatch.JobId}.");
                }
            }

            WorkerDispatcher newDispatch = new WorkerDispatcher(jobRequestMessage.JobId, jobRequestMessage.RequestId);

            newDispatch.WorkerDispatch = RunAsync(jobRequestMessage, currentDispatch, newDispatch.WorkerCancellationTokenSource.Token, newDispatch.WorkerCancelTimeoutKillTokenSource.Token);

            _jobInfos.TryAdd(newDispatch.JobId, newDispatch);
            _jobDispatchedQueue.Enqueue(newDispatch.JobId);
        }
Esempio n. 12
0
        private Pipelines.AgentJobRequestMessage CreateJobRequestMessage(string jobName)
        {
            TaskOrchestrationPlanReference plan = new TaskOrchestrationPlanReference()
            {
                PlanId = Guid.NewGuid()
            };
            TimelineReference timeline = null;
            Dictionary <string, VariableValue> variables = new Dictionary <string, VariableValue>(StringComparer.OrdinalIgnoreCase);

            variables[Constants.Variables.System.Culture] = "en-US";
            Pipelines.JobResources resources = new Pipelines.JobResources();
            var serviceEndpoint = new ServiceEndpoint();

            serviceEndpoint.Authorization = new EndpointAuthorization();
            serviceEndpoint.Authorization.Parameters.Add("nullValue", null);
            resources.Endpoints.Add(serviceEndpoint);

            List <Pipelines.JobStep> tasks = new List <Pipelines.JobStep>();

            tasks.Add(new Pipelines.TaskStep()
            {
                Id        = Guid.NewGuid(),
                Reference = new Pipelines.TaskStepDefinitionReference()
                {
                    Id      = Guid.NewGuid(),
                    Name    = "TestTask",
                    Version = "1.0.0"
                }
            });
            Guid JobId             = Guid.NewGuid();
            var  sidecarContainers = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase)
            {
                ["nginx"] = "nginx"
            };
            var jobRequest = new Pipelines.AgentJobRequestMessage(plan, timeline, JobId, jobName, jobName, "ubuntu", sidecarContainers, variables, new List <MaskHint>(), resources, null, tasks);

            return(jobRequest);
        }
Esempio n. 13
0
        private async Task RunAsync(Pipelines.AgentJobRequestMessage message, WorkerDispatcher previousJobDispatch, CancellationToken jobRequestCancellationToken, CancellationToken workerCancelTimeoutKillToken)
        {
            if (previousJobDispatch != null)
            {
                Trace.Verbose($"Make sure the previous job request {previousJobDispatch.JobId} has successfully finished on worker.");
                await EnsureDispatchFinished(previousJobDispatch);
            }
            else
            {
                Trace.Verbose($"This is the first job request.");
            }

            var term = HostContext.GetService <ITerminal>();

            term.WriteLine(StringUtil.Loc("RunningJob", DateTime.UtcNow, message.JobDisplayName));

            // first job request renew succeed.
            TaskCompletionSource <int> firstJobRequestRenewed = new TaskCompletionSource <int>();
            var notification = HostContext.GetService <IJobNotification>();

            // lock renew cancellation token.
            using (var lockRenewalTokenSource = new CancellationTokenSource())
                using (var workerProcessCancelTokenSource = new CancellationTokenSource())
                {
                    long requestId = message.RequestId;
                    Guid lockToken = Guid.Empty; // lockToken has never been used, keep this here of compat

                    // start renew job request
                    Trace.Info($"Start renew job request {requestId} for job {message.JobId}.");
                    Task renewJobRequest = RenewJobRequestAsync(_poolId, requestId, lockToken, firstJobRequestRenewed, lockRenewalTokenSource.Token);

                    // wait till first renew succeed or job request is canceled
                    // not even start worker if the first renew fail
                    await Task.WhenAny(firstJobRequestRenewed.Task, renewJobRequest, Task.Delay(-1, jobRequestCancellationToken));

                    if (renewJobRequest.IsCompleted)
                    {
                        // renew job request task complete means we run out of retry for the first job request renew.
                        Trace.Info($"Unable to renew job request for job {message.JobId} for the first time, stop dispatching job to worker.");
                        return;
                    }

                    if (jobRequestCancellationToken.IsCancellationRequested)
                    {
                        Trace.Info($"Stop renew job request for job {message.JobId}.");
                        // stop renew lock
                        lockRenewalTokenSource.Cancel();
                        // renew job request should never blows up.
                        await renewJobRequest;

                        // complete job request with result Cancelled
                        await CompleteJobRequestAsync(_poolId, message, lockToken, TaskResult.Canceled);

                        return;
                    }

                    HostContext.WritePerfCounter($"JobRequestRenewed_{requestId.ToString()}");

                    Task <int>    workerProcessTask = null;
                    object        _outputLock       = new object();
                    List <string> workerOutput      = new List <string>();
                    using (var processChannel = HostContext.CreateService <IProcessChannel>())
                        using (var processInvoker = HostContext.CreateService <IProcessInvoker>())
                        {
                            // Start the process channel.
                            // It's OK if StartServer bubbles an execption after the worker process has already started.
                            // The worker will shutdown after 30 seconds if it hasn't received the job message.
                            processChannel.StartServer(
                                // Delegate to start the child process.
                                startProcess: (string pipeHandleOut, string pipeHandleIn) =>
                            {
                                // Validate args.
                                ArgUtil.NotNullOrEmpty(pipeHandleOut, nameof(pipeHandleOut));
                                ArgUtil.NotNullOrEmpty(pipeHandleIn, nameof(pipeHandleIn));

                                if (HostContext.RunMode == RunMode.Normal)
                                {
                                    // Save STDOUT from worker, worker will use STDOUT report unhandle exception.
                                    processInvoker.OutputDataReceived += delegate(object sender, ProcessDataReceivedEventArgs stdout)
                                    {
                                        if (!string.IsNullOrEmpty(stdout.Data))
                                        {
                                            lock (_outputLock)
                                            {
                                                workerOutput.Add(stdout.Data);
                                            }
                                        }
                                    };

                                    // Save STDERR from worker, worker will use STDERR on crash.
                                    processInvoker.ErrorDataReceived += delegate(object sender, ProcessDataReceivedEventArgs stderr)
                                    {
                                        if (!string.IsNullOrEmpty(stderr.Data))
                                        {
                                            lock (_outputLock)
                                            {
                                                workerOutput.Add(stderr.Data);
                                            }
                                        }
                                    };
                                }
                                else if (HostContext.RunMode == RunMode.Local)
                                {
                                    processInvoker.OutputDataReceived += (object sender, ProcessDataReceivedEventArgs e) => Console.WriteLine(e.Data);
                                    processInvoker.ErrorDataReceived  += (object sender, ProcessDataReceivedEventArgs e) => Console.WriteLine(e.Data);
                                }

                                // Start the child process.
                                HostContext.WritePerfCounter("StartingWorkerProcess");
                                var assemblyDirectory = HostContext.GetDirectory(WellKnownDirectory.Bin);
                                string workerFileName = Path.Combine(assemblyDirectory, _workerProcessName);
                                workerProcessTask     = processInvoker.ExecuteAsync(
                                    workingDirectory: assemblyDirectory,
                                    fileName: workerFileName,
                                    arguments: "spawnclient " + pipeHandleOut + " " + pipeHandleIn,
                                    environment: null,
                                    requireExitCodeZero: false,
                                    outputEncoding: null,
                                    killProcessOnCancel: true,
                                    cancellationToken: workerProcessCancelTokenSource.Token);
                            });

                            // Send the job request message.
                            // Kill the worker process if sending the job message times out. The worker
                            // process may have successfully received the job message.
                            try
                            {
                                Trace.Info($"Send job request message to worker for job {message.JobId}.");
                                HostContext.WritePerfCounter($"AgentSendingJobToWorker_{message.JobId}");
                                using (var csSendJobRequest = new CancellationTokenSource(_channelTimeout))
                                {
                                    await processChannel.SendAsync(
                                        messageType : MessageType.NewJobRequest,
                                        body : JsonUtility.ToString(message),
                                        cancellationToken : csSendJobRequest.Token);
                                }
                            }
                            catch (OperationCanceledException)
                            {
                                // message send been cancelled.
                                // timeout 30 sec. kill worker.
                                Trace.Info($"Job request message sending for job {message.JobId} been cancelled, kill running worker.");
                                workerProcessCancelTokenSource.Cancel();
                                try
                                {
                                    await workerProcessTask;
                                }
                                catch (OperationCanceledException)
                                {
                                    Trace.Info("worker process has been killed.");
                                }

                                Trace.Info($"Stop renew job request for job {message.JobId}.");
                                // stop renew lock
                                lockRenewalTokenSource.Cancel();
                                // renew job request should never blows up.
                                await renewJobRequest;

                                // not finish the job request since the job haven't run on worker at all, we will not going to set a result to server.
                                return;
                            }

                            // we get first jobrequest renew succeed and start the worker process with the job message.
                            // send notification to machine provisioner.
                            await notification.JobStarted(message.JobId);

                            HostContext.WritePerfCounter($"SentJobToWorker_{requestId.ToString()}");

                            try
                            {
                                TaskResult resultOnAbandonOrCancel = TaskResult.Succeeded;
                                // wait for renewlock, worker process or cancellation token been fired.
                                var completedTask = await Task.WhenAny(renewJobRequest, workerProcessTask, Task.Delay(-1, jobRequestCancellationToken));

                                if (completedTask == workerProcessTask)
                                {
                                    // worker finished successfully, complete job request with result, attach unhandled exception reported by worker, stop renew lock, job has finished.
                                    int returnCode = await workerProcessTask;
                                    Trace.Info($"Worker finished for job {message.JobId}. Code: " + returnCode);

                                    string detailInfo = null;
                                    if (!TaskResultUtil.IsValidReturnCode(returnCode))
                                    {
                                        detailInfo = string.Join(Environment.NewLine, workerOutput);
                                        Trace.Info($"Return code {returnCode} indicate worker encounter an unhandled exception or app crash, attach worker stdout/stderr to JobRequest result.");
                                        await LogWorkerProcessUnhandledException(message, detailInfo);
                                    }

                                    TaskResult result = TaskResultUtil.TranslateFromReturnCode(returnCode);
                                    Trace.Info($"finish job request for job {message.JobId} with result: {result}");
                                    term.WriteLine(StringUtil.Loc("JobCompleted", DateTime.UtcNow, message.JobDisplayName, result));

                                    Trace.Info($"Stop renew job request for job {message.JobId}.");
                                    // stop renew lock
                                    lockRenewalTokenSource.Cancel();
                                    // renew job request should never blows up.
                                    await renewJobRequest;

                                    // complete job request
                                    await CompleteJobRequestAsync(_poolId, message, lockToken, result, detailInfo);

                                    // print out unhandled exception happened in worker after we complete job request.
                                    // when we run out of disk space, report back to server has higher priority.
                                    if (!string.IsNullOrEmpty(detailInfo))
                                    {
                                        Trace.Error("Unhandled exception happened in worker:");
                                        Trace.Error(detailInfo);
                                    }

                                    return;
                                }
                                else if (completedTask == renewJobRequest)
                                {
                                    resultOnAbandonOrCancel = TaskResult.Abandoned;
                                }
                                else
                                {
                                    resultOnAbandonOrCancel = TaskResult.Canceled;
                                }

                                // renew job request completed or job request cancellation token been fired for RunAsync(jobrequestmessage)
                                // cancel worker gracefully first, then kill it after worker cancel timeout
                                try
                                {
                                    Trace.Info($"Send job cancellation message to worker for job {message.JobId}.");
                                    using (var csSendCancel = new CancellationTokenSource(_channelTimeout))
                                    {
                                        var messageType = MessageType.CancelRequest;
                                        if (HostContext.AgentShutdownToken.IsCancellationRequested)
                                        {
                                            switch (HostContext.AgentShutdownReason)
                                            {
                                            case ShutdownReason.UserCancelled:
                                                messageType = MessageType.AgentShutdown;
                                                break;

                                            case ShutdownReason.OperatingSystemShutdown:
                                                messageType = MessageType.OperatingSystemShutdown;
                                                break;
                                            }
                                        }

                                        await processChannel.SendAsync(
                                            messageType : messageType,
                                            body : string.Empty,
                                            cancellationToken : csSendCancel.Token);
                                    }
                                }
                                catch (OperationCanceledException)
                                {
                                    // message send been cancelled.
                                    Trace.Info($"Job cancel message sending for job {message.JobId} been cancelled, kill running worker.");
                                    workerProcessCancelTokenSource.Cancel();
                                    try
                                    {
                                        await workerProcessTask;
                                    }
                                    catch (OperationCanceledException)
                                    {
                                        Trace.Info("worker process has been killed.");
                                    }
                                }

                                // wait worker to exit
                                // if worker doesn't exit within timeout, then kill worker.
                                completedTask = await Task.WhenAny(workerProcessTask, Task.Delay(-1, workerCancelTimeoutKillToken));

                                // worker haven't exit within cancellation timeout.
                                if (completedTask != workerProcessTask)
                                {
                                    Trace.Info($"worker process for job {message.JobId} haven't exit within cancellation timout, kill running worker.");
                                    workerProcessCancelTokenSource.Cancel();
                                    try
                                    {
                                        await workerProcessTask;
                                    }
                                    catch (OperationCanceledException)
                                    {
                                        Trace.Info("worker process has been killed.");
                                    }
                                }

                                Trace.Info($"finish job request for job {message.JobId} with result: {resultOnAbandonOrCancel}");
                                term.WriteLine(StringUtil.Loc("JobCompleted", DateTime.UtcNow, message.JobDisplayName, resultOnAbandonOrCancel));
                                // complete job request with cancel result, stop renew lock, job has finished.

                                Trace.Info($"Stop renew job request for job {message.JobId}.");
                                // stop renew lock
                                lockRenewalTokenSource.Cancel();
                                // renew job request should never blows up.
                                await renewJobRequest;

                                // complete job request
                                await CompleteJobRequestAsync(_poolId, message, lockToken, resultOnAbandonOrCancel);
                            }
                            finally
                            {
                                // This should be the last thing to run so we don't notify external parties until actually finished
                                await notification.JobCompleted(message.JobId);
                            }
                        }
                }
        }
Esempio n. 14
0
        public async Task UploadDiagnosticLogsAsync(IExecutionContext executionContext,
                                                    Pipelines.AgentJobRequestMessage message,
                                                    DateTime jobStartTimeUtc)
        {
            executionContext.Debug("Starting diagnostic file upload.");

            // Setup folders
            // \_layout\_work\_temp\[jobname-support]
            executionContext.Debug("Setting up diagnostic log folders.");
            string tempDirectory = HostContext.GetDirectory(WellKnownDirectory.Temp);

            ArgUtil.Directory(tempDirectory, nameof(tempDirectory));

            string supportRootFolder = Path.Combine(tempDirectory, message.JobName + "-support");

            Directory.CreateDirectory(supportRootFolder);

            // \_layout\_work\_temp\[jobname-support]\files
            executionContext.Debug("Creating diagnostic log files folder.");
            string supportFilesFolder = Path.Combine(supportRootFolder, "files");

            Directory.CreateDirectory(supportFilesFolder);

            // Create the environment file
            // \_layout\_work\_temp\[jobname-support]\files\environment.txt
            var           configurationStore = HostContext.GetService <IConfigurationStore>();
            AgentSettings settings           = configurationStore.GetSettings();
            int           agentId            = settings.AgentId;
            string        agentName          = settings.AgentName;
            int           poolId             = settings.PoolId;

            executionContext.Debug("Creating diagnostic log environment file.");
            string environmentFile = Path.Combine(supportFilesFolder, "environment.txt");
            string content         = await GetEnvironmentContent(agentId, agentName, message.Steps);

            File.WriteAllText(environmentFile, content);

            // Create the capabilities file
            var capabilitiesManager = HostContext.GetService <ICapabilitiesManager>();
            Dictionary <string, string> capabilities = await capabilitiesManager.GetCapabilitiesAsync(configurationStore.GetSettings(), default(CancellationToken));

            executionContext.Debug("Creating capabilities file.");
            string capabilitiesFile    = Path.Combine(supportFilesFolder, "capabilities.txt");
            string capabilitiesContent = GetCapabilitiesContent(capabilities);

            File.WriteAllText(capabilitiesFile, capabilitiesContent);

            // Copy worker diag log files
            List <string> workerDiagLogFiles = GetWorkerDiagLogFiles(HostContext.GetDirectory(WellKnownDirectory.Diag), jobStartTimeUtc);

            executionContext.Debug($"Copying {workerDiagLogFiles.Count()} worker diag logs.");

            foreach (string workerLogFile in workerDiagLogFiles)
            {
                ArgUtil.File(workerLogFile, nameof(workerLogFile));

                string destination = Path.Combine(supportFilesFolder, Path.GetFileName(workerLogFile));
                File.Copy(workerLogFile, destination);
            }

            // Copy agent diag log files
            List <string> agentDiagLogFiles = GetAgentDiagLogFiles(HostContext.GetDirectory(WellKnownDirectory.Diag), jobStartTimeUtc);

            executionContext.Debug($"Copying {agentDiagLogFiles.Count()} agent diag logs.");

            foreach (string agentLogFile in agentDiagLogFiles)
            {
                ArgUtil.File(agentLogFile, nameof(agentLogFile));

                string destination = Path.Combine(supportFilesFolder, Path.GetFileName(agentLogFile));
                File.Copy(agentLogFile, destination);
            }

            executionContext.Debug("Zipping diagnostic files.");

            string buildNumber = executionContext.Variables.Build_Number ?? "UnknownBuildNumber";
            string buildName   = $"Build {buildNumber}";
            string phaseName   = executionContext.Variables.System_PhaseDisplayName ?? "UnknownPhaseName";

            // zip the files
            string diagnosticsZipFileName = $"{buildName}-{phaseName}.zip";
            string diagnosticsZipFilePath = Path.Combine(supportRootFolder, diagnosticsZipFileName);

            ZipFile.CreateFromDirectory(supportFilesFolder, diagnosticsZipFilePath);

            // upload the json metadata file
            executionContext.Debug("Uploading diagnostic metadata file.");
            string metadataFileName = $"diagnostics-{buildName}-{phaseName}.json";
            string metadataFilePath = Path.Combine(supportFilesFolder, metadataFileName);
            string phaseResult      = GetTaskResultAsString(executionContext.Result);

            IOUtil.SaveObject(new DiagnosticLogMetadata(agentName, agentId, poolId, phaseName, diagnosticsZipFileName, phaseResult), metadataFilePath);

            executionContext.QueueAttachFile(type: CoreAttachmentType.DiagnosticLog, name: metadataFileName, filePath: metadataFilePath);

            executionContext.QueueAttachFile(type: CoreAttachmentType.DiagnosticLog, name: diagnosticsZipFileName, filePath: diagnosticsZipFilePath);

            executionContext.Debug("Diagnostic file upload complete.");
        }
Esempio n. 15
0
        private TestHostContext CreateTestContext(CancellationTokenSource _tokenSource, [CallerMemberName] String testName = "")
        {
            var hc = new TestHostContext(this, testName);

            _jobEc             = new Agent.Worker.ExecutionContext();
            _taskManager       = new Mock <ITaskManager>();
            _jobServerQueue    = new Mock <IJobServerQueue>();
            _config            = new Mock <IConfigurationStore>();
            _logger            = new Mock <IPagingLogger>();
            _proxy             = new Mock <IVstsAgentWebProxy>();
            _cert              = new Mock <IAgentCertificateManager>();
            _express           = new Mock <IExpressionManager>();
            _containerProvider = new Mock <IContainerOperationProvider>();
            _logPlugin         = new Mock <IAgentLogPlugin>();

            TaskRunner step1  = new TaskRunner();
            TaskRunner step2  = new TaskRunner();
            TaskRunner step3  = new TaskRunner();
            TaskRunner step4  = new TaskRunner();
            TaskRunner step5  = new TaskRunner();
            TaskRunner step6  = new TaskRunner();
            TaskRunner step7  = new TaskRunner();
            TaskRunner step8  = new TaskRunner();
            TaskRunner step9  = new TaskRunner();
            TaskRunner step10 = new TaskRunner();
            TaskRunner step11 = new TaskRunner();
            TaskRunner step12 = new TaskRunner();

            _logger.Setup(x => x.Setup(It.IsAny <Guid>(), It.IsAny <Guid>()));
            var settings = new AgentSettings
            {
                AgentId    = 1,
                AgentName  = "agent1",
                ServerUrl  = "https://test.visualstudio.com",
                WorkFolder = "_work",
            };

            _config.Setup(x => x.GetSettings())
            .Returns(settings);

            _proxy.Setup(x => x.ProxyAddress)
            .Returns(string.Empty);

            TaskOrchestrationPlanReference plan = new TaskOrchestrationPlanReference();
            TimelineReference timeline          = new Timeline(Guid.NewGuid());
            JobEnvironment    environment       = new JobEnvironment();

            environment.Variables[Constants.Variables.System.Culture] = "en-US";
            environment.SystemConnection = new ServiceEndpoint()
            {
                Name          = WellKnownServiceEndpointNames.SystemVssConnection,
                Url           = new Uri("https://test.visualstudio.com"),
                Authorization = new EndpointAuthorization()
                {
                    Scheme = "Test",
                }
            };
            environment.SystemConnection.Authorization.Parameters["AccessToken"] = "token";

            List <TaskInstance> tasks = new List <TaskInstance>()
            {
                new TaskInstance()
                {
                    InstanceId  = Guid.NewGuid(),
                    DisplayName = "task1",
                },
                new TaskInstance()
                {
                    InstanceId  = Guid.NewGuid(),
                    DisplayName = "task2",
                },
                new TaskInstance()
                {
                    InstanceId  = Guid.NewGuid(),
                    DisplayName = "task3",
                },
                new TaskInstance()
                {
                    InstanceId  = Guid.NewGuid(),
                    DisplayName = "task4",
                },
                new TaskInstance()
                {
                    InstanceId  = Guid.NewGuid(),
                    DisplayName = "task5",
                },
                new TaskInstance()
                {
                    InstanceId  = Guid.NewGuid(),
                    DisplayName = "task6",
                },
                new TaskInstance()
                {
                    InstanceId  = Guid.NewGuid(),
                    DisplayName = "task7",
                },
            };

            Guid JobId = Guid.NewGuid();

            _message = Pipelines.AgentJobRequestMessageUtil.Convert(new AgentJobRequestMessage(plan, timeline, JobId, testName, testName, environment, tasks));

            _taskManager.Setup(x => x.DownloadAsync(It.IsAny <IExecutionContext>(), It.IsAny <IEnumerable <Pipelines.TaskStep> >()))
            .Returns(Task.CompletedTask);

            _taskManager.Setup(x => x.Load(It.Is <Pipelines.TaskStep>(t => t.DisplayName == "task1")))
            .Returns(new Definition()
            {
                Data = new DefinitionData()
                {
                    PreJobExecution  = null,
                    Execution        = new ExecutionData(),
                    PostJobExecution = null,
                },
            });
            _taskManager.Setup(x => x.Load(It.Is <Pipelines.TaskStep>(t => t.DisplayName == "task2")))
            .Returns(new Definition()
            {
                Data = new DefinitionData()
                {
                    PreJobExecution  = new ExecutionData(),
                    Execution        = new ExecutionData(),
                    PostJobExecution = new ExecutionData(),
                },
            });
            _taskManager.Setup(x => x.Load(It.Is <Pipelines.TaskStep>(t => t.DisplayName == "task3")))
            .Returns(new Definition()
            {
                Data = new DefinitionData()
                {
                    PreJobExecution  = new ExecutionData(),
                    Execution        = null,
                    PostJobExecution = new ExecutionData(),
                },
            });
            _taskManager.Setup(x => x.Load(It.Is <Pipelines.TaskStep>(t => t.DisplayName == "task4")))
            .Returns(new Definition()
            {
                Data = new DefinitionData()
                {
                    PreJobExecution  = new ExecutionData(),
                    Execution        = null,
                    PostJobExecution = null,
                },
            });
            _taskManager.Setup(x => x.Load(It.Is <Pipelines.TaskStep>(t => t.DisplayName == "task5")))
            .Returns(new Definition()
            {
                Data = new DefinitionData()
                {
                    PreJobExecution  = null,
                    Execution        = null,
                    PostJobExecution = new ExecutionData(),
                },
            });
            _taskManager.Setup(x => x.Load(It.Is <Pipelines.TaskStep>(t => t.DisplayName == "task6")))
            .Returns(new Definition()
            {
                Data = new DefinitionData()
                {
                    PreJobExecution  = new ExecutionData(),
                    Execution        = new ExecutionData(),
                    PostJobExecution = null,
                },
            });
            _taskManager.Setup(x => x.Load(It.Is <Pipelines.TaskStep>(t => t.DisplayName == "task7")))
            .Returns(new Definition()
            {
                Data = new DefinitionData()
                {
                    PreJobExecution  = null,
                    Execution        = new ExecutionData(),
                    PostJobExecution = new ExecutionData(),
                },
            });

            hc.SetSingleton(_taskManager.Object);
            hc.SetSingleton(_config.Object);
            hc.SetSingleton(_jobServerQueue.Object);
            hc.SetSingleton(_proxy.Object);
            hc.SetSingleton(_cert.Object);
            hc.SetSingleton(_express.Object);
            hc.SetSingleton(_containerProvider.Object);
            hc.SetSingleton(_logPlugin.Object);
            hc.EnqueueInstance <IPagingLogger>(_logger.Object); // jobcontext logger
            hc.EnqueueInstance <IPagingLogger>(_logger.Object); // init step logger
            hc.EnqueueInstance <IPagingLogger>(_logger.Object); // step 1
            hc.EnqueueInstance <IPagingLogger>(_logger.Object);
            hc.EnqueueInstance <IPagingLogger>(_logger.Object);
            hc.EnqueueInstance <IPagingLogger>(_logger.Object);
            hc.EnqueueInstance <IPagingLogger>(_logger.Object);
            hc.EnqueueInstance <IPagingLogger>(_logger.Object);
            hc.EnqueueInstance <IPagingLogger>(_logger.Object);
            hc.EnqueueInstance <IPagingLogger>(_logger.Object);
            hc.EnqueueInstance <IPagingLogger>(_logger.Object);
            hc.EnqueueInstance <IPagingLogger>(_logger.Object);
            hc.EnqueueInstance <IPagingLogger>(_logger.Object);
            hc.EnqueueInstance <IPagingLogger>(_logger.Object); // step 12

            hc.EnqueueInstance <ITaskRunner>(step1);
            hc.EnqueueInstance <ITaskRunner>(step2);
            hc.EnqueueInstance <ITaskRunner>(step3);
            hc.EnqueueInstance <ITaskRunner>(step4);
            hc.EnqueueInstance <ITaskRunner>(step5);
            hc.EnqueueInstance <ITaskRunner>(step6);
            hc.EnqueueInstance <ITaskRunner>(step7);
            hc.EnqueueInstance <ITaskRunner>(step8);
            hc.EnqueueInstance <ITaskRunner>(step9);
            hc.EnqueueInstance <ITaskRunner>(step10);
            hc.EnqueueInstance <ITaskRunner>(step11);
            hc.EnqueueInstance <ITaskRunner>(step12);

            _jobEc.Initialize(hc);
            _jobEc.InitializeJob(_message, _tokenSource.Token);
            return(hc);
        }
Esempio n. 16
0
        private async Task <TaskResult> CompleteJobAsync(IJobServer jobServer, IExecutionContext jobContext, Pipelines.AgentJobRequestMessage message, TaskResult?taskResult = null)
        {
            jobContext.Section(StringUtil.Loc("StepFinishing", message.JobDisplayName));
            TaskResult result = jobContext.Complete(taskResult);

            try
            {
                await ShutdownQueue(throwOnFailure : true);
            }
            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);

            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);
        }
Esempio n. 17
0
        // download all required tasks.
        // make sure all task's condition inputs are valid.
        // build up three list of steps for jobrunner. (pre-job, job, post-job)
        public async Task <JobInitializeResult> InitializeJob(IExecutionContext jobContext, Pipelines.AgentJobRequestMessage message)
        {
            Trace.Entering();
            ArgUtil.NotNull(jobContext, nameof(jobContext));
            ArgUtil.NotNull(message, nameof(message));

            // create a new timeline record node for 'Initialize job'
            IExecutionContext context = jobContext.CreateChild(Guid.NewGuid(), StringUtil.Loc("InitializeJob"), nameof(JobExtension));

            JobInitializeResult initResult = new JobInitializeResult();

            using (var register = jobContext.CancellationToken.Register(() => { context.CancelToken(); }))
            {
                try
                {
                    context.Start();
                    context.Section(StringUtil.Loc("StepStarting", StringUtil.Loc("InitializeJob")));

                    // Set agent version variable.
                    context.Variables.Set(Constants.Variables.Agent.Version, Constants.Agent.Version);
                    context.Output(StringUtil.Loc("AgentVersion", Constants.Agent.Version));

                    // Print proxy setting information for better diagnostic experience
                    var agentWebProxy = HostContext.GetService <IVstsAgentWebProxy>();
                    if (!string.IsNullOrEmpty(agentWebProxy.ProxyAddress))
                    {
                        context.Output(StringUtil.Loc("AgentRunningBehindProxy", agentWebProxy.ProxyAddress));
                    }

                    // Give job extension a chance to initialize
                    Trace.Info($"Run initial step from extension {this.GetType().Name}.");
                    InitializeJobExtension(context, message.Steps, message.Workspace);

                    // Download tasks if not already in the cache
                    Trace.Info("Downloading task definitions.");
                    var taskManager = HostContext.GetService <ITaskManager>();
                    await taskManager.DownloadAsync(context, message.Steps);

                    // Parse all Task conditions.
                    Trace.Info("Parsing all task's condition inputs.");
                    var expression = HostContext.GetService <IExpressionManager>();
                    Dictionary <Guid, IExpressionNode> taskConditionMap = new Dictionary <Guid, IExpressionNode>();
                    foreach (var task in message.Steps.OfType <Pipelines.TaskStep>())
                    {
                        IExpressionNode condition;
                        if (!string.IsNullOrEmpty(task.Condition))
                        {
                            context.Debug($"Task '{task.DisplayName}' has following condition: '{task.Condition}'.");
                            condition = expression.Parse(context, task.Condition);
                        }
                        else
                        {
                            condition = ExpressionManager.Succeeded;
                        }

                        taskConditionMap[task.Id] = condition;
                    }

#if OS_WINDOWS
                    // This is for internal testing and is not publicly supported. This will be removed from the agent at a later time.
                    var             prepareScript    = Environment.GetEnvironmentVariable("VSTS_AGENT_INIT_INTERNAL_TEMP_HACK");
                    ServiceEndpoint systemConnection = context.Endpoints.Single(x => string.Equals(x.Name, WellKnownServiceEndpointNames.SystemVssConnection, StringComparison.OrdinalIgnoreCase));
                    if (!string.IsNullOrEmpty(prepareScript) && context.Container == null)
                    {
                        var prepareStep = new ManagementScriptStep(
                            scriptPath: prepareScript,
                            condition: ExpressionManager.Succeeded,
                            displayName: "Agent Initialization");

                        Trace.Verbose($"Adding agent init script step.");
                        prepareStep.Initialize(HostContext);
                        prepareStep.ExecutionContext = jobContext.CreateChild(Guid.NewGuid(), prepareStep.DisplayName, nameof(ManagementScriptStep));
                        prepareStep.AccessToken      = systemConnection.Authorization.Parameters["AccessToken"];
                        prepareStep.Condition        = ExpressionManager.Succeeded;
                        initResult.PreJobSteps.Add(prepareStep);
                    }
#endif

                    // build up 3 lists of steps, pre-job, job, post-job
                    Stack <IStep> postJobStepsBuilder = new Stack <IStep>();
                    Dictionary <Guid, Variables> taskVariablesMapping = new Dictionary <Guid, Variables>();

                    if (context.Container != null || context.SidecarContainers.Count > 0)
                    {
                        var containerProvider = HostContext.GetService <IContainerOperationProvider>();
                        var containers        = new List <Container.ContainerInfo>();
                        if (context.Container != null)
                        {
                            containers.Add(context.Container);
                        }
                        containers.AddRange(context.SidecarContainers);

                        initResult.PreJobSteps.Add(new JobExtensionRunner(runAsync: containerProvider.StartContainersAsync,
                                                                          condition: ExpressionManager.Succeeded,
                                                                          displayName: StringUtil.Loc("InitializeContainer"),
                                                                          data: (object)containers));
                        postJobStepsBuilder.Push(new JobExtensionRunner(runAsync: containerProvider.StopContainersAsync,
                                                                        condition: ExpressionManager.Always,
                                                                        displayName: StringUtil.Loc("StopContainer"),
                                                                        data: (object)containers));
                    }

                    foreach (var task in message.Steps.OfType <Pipelines.TaskStep>())
                    {
                        var taskDefinition = taskManager.Load(task);

                        List <string> warnings;
                        taskVariablesMapping[task.Id] = new Variables(HostContext, new Dictionary <string, VariableValue>(), out warnings);

                        // Add pre-job steps from Tasks
                        if (taskDefinition.Data?.PreJobExecution != null)
                        {
                            Trace.Info($"Adding Pre-Job {task.DisplayName}.");
                            var taskRunner = HostContext.CreateService <ITaskRunner>();
                            taskRunner.Task      = task;
                            taskRunner.Stage     = JobRunStage.PreJob;
                            taskRunner.Condition = taskConditionMap[task.Id];
                            initResult.PreJobSteps.Add(taskRunner);
                        }

                        // Add execution steps from Tasks
                        if (taskDefinition.Data?.Execution != null)
                        {
                            Trace.Verbose($"Adding {task.DisplayName}.");
                            var taskRunner = HostContext.CreateService <ITaskRunner>();
                            taskRunner.Task      = task;
                            taskRunner.Stage     = JobRunStage.Main;
                            taskRunner.Condition = taskConditionMap[task.Id];
                            initResult.JobSteps.Add(taskRunner);
                        }

                        // Add post-job steps from Tasks
                        if (taskDefinition.Data?.PostJobExecution != null)
                        {
                            Trace.Verbose($"Adding Post-Job {task.DisplayName}.");
                            var taskRunner = HostContext.CreateService <ITaskRunner>();
                            taskRunner.Task      = task;
                            taskRunner.Stage     = JobRunStage.PostJob;
                            taskRunner.Condition = ExpressionManager.Always;
                            postJobStepsBuilder.Push(taskRunner);
                        }
                    }

                    // Add pre-job step from Extension
                    Trace.Info("Adding pre-job step from extension.");
                    var extensionPreJobStep = GetExtensionPreJobStep(jobContext);
                    if (extensionPreJobStep != null)
                    {
                        initResult.PreJobSteps.Add(extensionPreJobStep);
                    }

                    // Add post-job step from Extension
                    Trace.Info("Adding post-job step from extension.");
                    var extensionPostJobStep = GetExtensionPostJobStep(jobContext);
                    if (extensionPostJobStep != null)
                    {
                        postJobStepsBuilder.Push(extensionPostJobStep);
                    }

                    // create execution context for all pre-job steps
                    foreach (var step in initResult.PreJobSteps)
                    {
#if OS_WINDOWS
                        if (step is ManagementScriptStep)
                        {
                            continue;
                        }
#endif
                        if (step is JobExtensionRunner)
                        {
                            JobExtensionRunner extensionStep = step as JobExtensionRunner;
                            ArgUtil.NotNull(extensionStep, extensionStep.DisplayName);
                            Guid stepId = Guid.NewGuid();
                            extensionStep.ExecutionContext = jobContext.CreateChild(stepId, extensionStep.DisplayName, stepId.ToString("N"));
                        }
                        else if (step is ITaskRunner)
                        {
                            ITaskRunner taskStep = step as ITaskRunner;
                            ArgUtil.NotNull(taskStep, step.DisplayName);
                            taskStep.ExecutionContext = jobContext.CreateChild(Guid.NewGuid(), StringUtil.Loc("PreJob", taskStep.DisplayName), taskStep.Task.Name, taskVariablesMapping[taskStep.Task.Id]);
                        }
                    }

                    // create task execution context for all job steps from task
                    foreach (var step in initResult.JobSteps)
                    {
                        ITaskRunner taskStep = step as ITaskRunner;
                        ArgUtil.NotNull(taskStep, step.DisplayName);
                        taskStep.ExecutionContext = jobContext.CreateChild(taskStep.Task.Id, taskStep.DisplayName, taskStep.Task.Name, taskVariablesMapping[taskStep.Task.Id]);
                    }

                    // Add post-job steps from Tasks
                    Trace.Info("Adding post-job steps from tasks.");
                    while (postJobStepsBuilder.Count > 0)
                    {
                        initResult.PostJobStep.Add(postJobStepsBuilder.Pop());
                    }

                    // create task execution context for all post-job steps from task
                    foreach (var step in initResult.PostJobStep)
                    {
                        if (step is JobExtensionRunner)
                        {
                            JobExtensionRunner extensionStep = step as JobExtensionRunner;
                            ArgUtil.NotNull(extensionStep, extensionStep.DisplayName);
                            Guid stepId = Guid.NewGuid();
                            extensionStep.ExecutionContext = jobContext.CreateChild(stepId, extensionStep.DisplayName, stepId.ToString("N"));
                        }
                        else if (step is ITaskRunner)
                        {
                            ITaskRunner taskStep = step as ITaskRunner;
                            ArgUtil.NotNull(taskStep, step.DisplayName);
                            taskStep.ExecutionContext = jobContext.CreateChild(Guid.NewGuid(), StringUtil.Loc("PostJob", taskStep.DisplayName), taskStep.Task.Name, taskVariablesMapping[taskStep.Task.Id]);
                        }
                    }

#if OS_WINDOWS
                    // Add script post steps.
                    // This is for internal testing and is not publicly supported. This will be removed from the agent at a later time.
                    var finallyScript = Environment.GetEnvironmentVariable("VSTS_AGENT_CLEANUP_INTERNAL_TEMP_HACK");
                    if (!string.IsNullOrEmpty(finallyScript) && context.Container == null)
                    {
                        var finallyStep = new ManagementScriptStep(
                            scriptPath: finallyScript,
                            condition: ExpressionManager.Always,
                            displayName: "Agent Cleanup");

                        Trace.Verbose($"Adding agent cleanup script step.");
                        finallyStep.Initialize(HostContext);
                        finallyStep.ExecutionContext = jobContext.CreateChild(Guid.NewGuid(), finallyStep.DisplayName, nameof(ManagementScriptStep));
                        finallyStep.Condition        = ExpressionManager.Always;
                        finallyStep.AccessToken      = systemConnection.Authorization.Parameters["AccessToken"];
                        initResult.PostJobStep.Add(finallyStep);
                    }
#endif
                    return(initResult);
                }
                catch (OperationCanceledException ex) when(jobContext.CancellationToken.IsCancellationRequested)
                {
                    // Log the exception and cancel the JobExtension Initialization.
                    Trace.Error($"Caught cancellation exception from JobExtension Initialization: {ex}");
                    context.Error(ex);
                    context.Result = TaskResult.Canceled;
                    throw;
                }
                catch (Exception ex)
                {
                    // Log the error and fail the JobExtension Initialization.
                    Trace.Error($"Caught exception from JobExtension Initialization: {ex}");
                    context.Error(ex);
                    context.Result = TaskResult.Failed;
                    throw;
                }
                finally
                {
                    context.Section(StringUtil.Loc("StepFinishing", StringUtil.Loc("InitializeJob")));
                    context.Complete();
                }
            }
        }
Esempio n. 18
0
        //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}.");
                            }
                        }
                        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);
        }
Esempio n. 19
0
        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;

            // Agent.RunMode
            RunMode runMode;

            if (message.Variables.ContainsKey(Constants.Variables.Agent.RunMode) &&
                Enum.TryParse(message.Variables[Constants.Variables.Agent.RunMode].Value, ignoreCase: true, result: out runMode) &&
                runMode == RunMode.Local)
            {
                HostContext.RunMode = runMode;
            }

            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
            if (!message.Variables.ContainsKey(Constants.Variables.System.ServerType))
            {
                if (!UrlUtil.IsHosted(systemConnection.Url.AbsoluteUri)) // TODO: remove this after TFS/RM move to M133
                {
                    ReplaceConfigUriBaseInJobRequestMessage(message);
                }
            }
            else if (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 = ApiUtil.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 = ApiUtil.CreateConnection(jobServerUrl, jobServerCredential, new DelegatingHandler[] { new ThrottlingReportHandler(_jobServerQueue) });
            await jobServer.ConnectAsync(jobConnection);

            _jobServerQueue.Start(message);

            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
                    });
                });

                // Set agent version variable.
                jobContext.Variables.Set(Constants.Variables.Agent.Version, Constants.Agent.Version);
                jobContext.Output(StringUtil.Loc("AgentVersion", Constants.Agent.Version));

                // Print proxy setting information for better diagnostic experience
                var agentWebProxy = HostContext.GetService <IVstsAgentWebProxy>();
                if (!string.IsNullOrEmpty(agentWebProxy.ProxyAddress))
                {
                    jobContext.Output(StringUtil.Loc("AgentRunningBehindProxy", agentWebProxy.ProxyAddress));
                }

                // 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.Variables.Set(Constants.Variables.Agent.Id, settings.AgentId.ToString(CultureInfo.InvariantCulture));
                jobContext.Variables.Set(Constants.Variables.Agent.HomeDirectory, HostContext.GetDirectory(WellKnownDirectory.Root));
                jobContext.Variables.Set(Constants.Variables.Agent.JobName, message.JobDisplayName);
                jobContext.Variables.Set(Constants.Variables.Agent.MachineName, Environment.MachineName);
                jobContext.Variables.Set(Constants.Variables.Agent.Name, settings.AgentName);
                jobContext.Variables.Set(Constants.Variables.Agent.OS, VarUtil.OS);
                jobContext.Variables.Set(Constants.Variables.Agent.RootDirectory, IOUtil.GetWorkPath(HostContext));
#if OS_WINDOWS
                jobContext.Variables.Set(Constants.Variables.Agent.ServerOMDirectory, Path.Combine(IOUtil.GetExternalsPath(), Constants.Path.ServerOMDirectory));
#endif
                jobContext.Variables.Set(Constants.Variables.Agent.WorkFolder, IOUtil.GetWorkPath(HostContext));
                jobContext.Variables.Set(Constants.Variables.System.WorkFolder, IOUtil.GetWorkPath(HostContext));

                string toolsDirectory = Environment.GetEnvironmentVariable("AGENT_TOOLSDIRECTORY") ?? Environment.GetEnvironmentVariable(Constants.Variables.Agent.ToolsDirectory);
                if (string.IsNullOrEmpty(toolsDirectory))
                {
                    toolsDirectory = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Work), Constants.Path.ToolDirectory);
                    Directory.CreateDirectory(toolsDirectory);
                }
                else
                {
                    Trace.Info($"Set tool cache directory base on environment: '{toolsDirectory}'");
                    Directory.CreateDirectory(toolsDirectory);
                }
                jobContext.Variables.Set(Constants.Variables.Agent.ToolsDirectory, toolsDirectory);

                // 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 = ApiUtil.GetVssCredential(systemConnection);
                if (taskServerUri != null)
                {
                    Trace.Info($"Creating task server with {taskServerUri}");
                    await taskServer.ConnectAsync(ApiUtil.CreateConnection(taskServerUri, taskServerCredential));
                }

                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(ApiUtil.CreateConnection(taskServerUri, taskServerCredential));
                }

                // Expand the endpoint data values.
                foreach (ServiceEndpoint endpoint in jobContext.Endpoints)
                {
                    jobContext.Variables.ExpandValues(target: endpoint.Data);
                    VarUtil.ExpandEnvironmentVariables(HostContext, target: endpoint.Data);
                }

                // 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 = new List <IStep>();
                try
                {
                    Trace.Info("Initialize job. Getting all job steps.");
                    var initializeResult = await jobExtension.InitializeJob(jobContext, message);

                    jobSteps.AddRange(initializeResult.PreJobSteps);
                    jobSteps.AddRange(initializeResult.JobSteps);
                    jobSteps.AddRange(initializeResult.PostJobStep);
                }
                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))}'");

                bool             processCleanup    = jobContext.Variables.GetBoolean("process.clean") ?? true;
                HashSet <string> existingProcesses = new HashSet <string>(StringComparer.OrdinalIgnoreCase);
                string           processLookupId   = null;
                if (processCleanup)
                {
                    processLookupId = $"vsts_{Guid.NewGuid()}";

                    // Set the VSTS_PROCESS_LOOKUP_ID env variable.
                    jobContext.SetVariable(Constants.ProcessLookupId, processLookupId, false, false);

                    // Take a snapshot of current running processes
                    Dictionary <int, Process> processes = SnapshotProcesses();
                    foreach (var proc in processes)
                    {
                        // Pid_ProcessName
                        existingProcesses.Add($"{proc.Key}_{proc.Value.ProcessName}");
                    }
                }

                // 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
                {
                    if (processCleanup)
                    {
                        // Only check environment variable for any process that doesn't run before we invoke our process.
                        Dictionary <int, Process> currentProcesses = SnapshotProcesses();
                        foreach (var proc in currentProcesses)
                        {
                            if (existingProcesses.Contains($"{proc.Key}_{proc.Value.ProcessName}"))
                            {
                                Trace.Verbose($"Skip existing process. PID: {proc.Key} ({proc.Value.ProcessName})");
                            }
                            else
                            {
                                Trace.Info($"Inspecting process environment variables. PID: {proc.Key} ({proc.Value.ProcessName})");

                                Dictionary <string, string> env = new Dictionary <string, string>();
                                try
                                {
                                    env = proc.Value.GetEnvironmentVariables();
                                    foreach (var e in env)
                                    {
                                        Trace.Verbose($"PID:{proc.Key} ({e.Key}={e.Value})");
                                    }
                                }
                                catch (Exception ex)
                                {
                                    Trace.Verbose("Ignore any exception during read process environment variables.");
                                    Trace.Verbose(ex.ToString());
                                }

                                if (env.TryGetValue(Constants.ProcessLookupId, out string lookupId) &&
                                    lookupId.Equals(processLookupId, StringComparison.OrdinalIgnoreCase))
                                {
                                    Trace.Info($"Terminate orphan process: pid ({proc.Key}) ({proc.Value.ProcessName})");
                                    try
                                    {
                                        proc.Value.Kill();
                                    }
                                    catch (Exception ex)
                                    {
                                        Trace.Error("Catch exception during orphan process cleanup.");
                                        Trace.Error(ex);
                                    }
                                }
                            }
                        }
                    }
                }

                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));
            }
            finally
            {
                if (agentShutdownRegistration != null)
                {
                    agentShutdownRegistration.Value.Dispose();
                    agentShutdownRegistration = null;
                }

                await ShutdownQueue(throwOnFailure : false);
            }
        }
Esempio n. 20
0
        private void InitializeSecretMasker(Pipelines.AgentJobRequestMessage message)
        {
            Trace.Entering();
            ArgUtil.NotNull(message, nameof(message));
            ArgUtil.NotNull(message.Resources, nameof(message.Resources));
            // Add mask hints for secret variables
            foreach (var variable in (message.Variables ?? new Dictionary <string, VariableValue>()))
            {
                // Skip secrets which are just white spaces.
                if (variable.Value.IsSecret && !string.IsNullOrWhiteSpace(variable.Value.Value))
                {
                    AddUserSuppliedSecret(variable.Value.Value);
                    // also, we escape some characters for variables when we print them out in debug mode. We need to
                    // add the escaped version of these secrets as well
                    var escapedSecret = variable.Value.Value.Replace("%", "%AZP25")
                                        .Replace("\r", "%0D")
                                        .Replace("\n", "%0A");
                    AddUserSuppliedSecret(escapedSecret);

                    // Since % escaping may be turned off, also mask a version escaped with just newlines
                    var escapedSecret2 = variable.Value.Value.Replace("\r", "%0D")
                                         .Replace("\n", "%0A");
                    AddUserSuppliedSecret(escapedSecret2);
                }
            }

            // Add mask hints
            foreach (MaskHint maskHint in (message.MaskHints ?? new List <MaskHint>()))
            {
                if (maskHint.Type == MaskType.Regex)
                {
                    HostContext.SecretMasker.AddRegex(maskHint.Value, $"Worker_{WellKnownSecretAliases.AddingMaskHint}");

                    // We need this because the worker will print out the job message JSON to diag log
                    // and SecretMasker has JsonEscapeEncoder hook up
                    HostContext.SecretMasker.AddValue(maskHint.Value, WellKnownSecretAliases.AddingMaskHint);
                }
                else
                {
                    // TODO: Should we fail instead? Do any additional pains need to be taken here? Should the job message not be traced?
                    Trace.Warning($"Unsupported mask type '{maskHint.Type}'.");
                }
            }

            // TODO: Avoid adding redundant secrets. If the endpoint auth matches the system connection, then it's added as a value secret and as a regex secret. Once as a value secret b/c of the following code that iterates over each endpoint. Once as a regex secret due to the hint sent down in the job message.

            // Add masks for service endpoints
            foreach (ServiceEndpoint endpoint in message.Resources.Endpoints ?? new List <ServiceEndpoint>())
            {
                foreach (var keyValuePair in endpoint.Authorization?.Parameters ?? new Dictionary <string, string>())
                {
                    if (!string.IsNullOrEmpty(keyValuePair.Value) && MaskingUtil.IsEndpointAuthorizationParametersSecret(keyValuePair.Key))
                    {
                        HostContext.SecretMasker.AddValue(keyValuePair.Value, $"Worker_EndpointAuthorizationParameters_{keyValuePair.Key}");
                    }
                }
            }

            // Add masks for secure file download tickets
            foreach (SecureFile file in message.Resources.SecureFiles ?? new List <SecureFile>())
            {
                if (!string.IsNullOrEmpty(file.Ticket))
                {
                    HostContext.SecretMasker.AddValue(file.Ticket, WellKnownSecretAliases.SecureFileTicket);
                }
            }
        }
Esempio n. 21
0
        public void InitializeJob(Pipelines.AgentJobRequestMessage message, CancellationToken token)
        {
            // Validation
            Trace.Entering();
            ArgUtil.NotNull(message, nameof(message));
            ArgUtil.NotNull(message.Resources, nameof(message.Resources));
            ArgUtil.NotNull(message.Variables, nameof(message.Variables));
            ArgUtil.NotNull(message.Plan, nameof(message.Plan));

            _cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token);

            // Features
            Features = PlanUtil.GetFeatures(message.Plan);

            // Endpoints
            Endpoints = message.Resources.Endpoints;

            // SecureFiles
            SecureFiles = message.Resources.SecureFiles;

            // Repositories
            Repositories = message.Resources.Repositories;

            // JobSettings
            var checkouts = message.Steps?.Where(x => Pipelines.PipelineConstants.IsCheckoutTask(x)).ToList();

            JobSettings = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase);
            JobSettings[WellKnownJobSettings.HasMultipleCheckouts] = Boolean.FalseString;
            if (checkouts != null && checkouts.Count > 0)
            {
                JobSettings[WellKnownJobSettings.HasMultipleCheckouts] = checkouts.Count > 1 ? Boolean.TrueString : Boolean.FalseString;
                var firstCheckout = checkouts.First() as Pipelines.TaskStep;
                if (firstCheckout != null && Repositories != null && firstCheckout.Inputs.TryGetValue(Pipelines.PipelineConstants.CheckoutTaskInputs.Repository, out string repoAlias))
                {
                    JobSettings[WellKnownJobSettings.FirstRepositoryCheckedOut] = repoAlias;
                    var repo = Repositories.Find(r => String.Equals(r.Alias, repoAlias, StringComparison.OrdinalIgnoreCase));
                    if (repo != null)
                    {
                        repo.Properties.Set <bool>(RepositoryUtil.IsPrimaryRepository, true);
                    }
                }
            }

            // Variables (constructor performs initial recursive expansion)
            List <string> warnings;

            Variables = new Variables(HostContext, message.Variables, out warnings);
            Variables.StringTranslator = TranslatePathForStepTarget;

            if (Variables.GetBoolean("agent.useWorkspaceId") == true)
            {
                try
                {
                    // We need an identifier that represents which repos make up the workspace.
                    // This allows similar jobs in the same definition to reuse that workspace and other jobs to have their own.
                    JobSettings[WellKnownJobSettings.WorkspaceIdentifier] = GetWorkspaceIdentifier(message);
                }
                catch (Exception ex)
                {
                    Trace.Warning($"Unable to generate workspace ID: {ex.Message}");
                }
            }

            // Prepend Path
            PrependPath = new List <string>();

            // Docker (JobContainer)
            string imageName = Variables.Get("_PREVIEW_VSTS_DOCKER_IMAGE");

            if (string.IsNullOrEmpty(imageName))
            {
                imageName = Environment.GetEnvironmentVariable("_PREVIEW_VSTS_DOCKER_IMAGE");
            }

            Containers         = new List <ContainerInfo>();
            _defaultStepTarget = null;
            _currentStepTarget = null;
            if (!string.IsNullOrEmpty(imageName) &&
                string.IsNullOrEmpty(message.JobContainer))
            {
                var dockerContainer = new Pipelines.ContainerResource()
                {
                    Alias = "vsts_container_preview"
                };
                dockerContainer.Properties.Set("image", imageName);
                var defaultJobContainer = HostContext.CreateContainerInfo(dockerContainer);
                _defaultStepTarget = defaultJobContainer;
                Containers.Add(defaultJobContainer);
            }
            else if (!string.IsNullOrEmpty(message.JobContainer))
            {
                var defaultJobContainer = HostContext.CreateContainerInfo(message.Resources.Containers.Single(x => string.Equals(x.Alias, message.JobContainer, StringComparison.OrdinalIgnoreCase)));
                _defaultStepTarget = defaultJobContainer;
                Containers.Add(defaultJobContainer);
            }
            else
            {
                _defaultStepTarget = new HostInfo();
            }
            // Include other step containers
            var sidecarContainers = new HashSet <string>(message.JobSidecarContainers.Values, StringComparer.OrdinalIgnoreCase);

            foreach (var container in message.Resources.Containers.Where(x =>
                                                                         !string.Equals(x.Alias, message.JobContainer, StringComparison.OrdinalIgnoreCase) && !sidecarContainers.Contains(x.Alias)))
            {
                Containers.Add(HostContext.CreateContainerInfo(container));
            }

            // Docker (Sidecar Containers)
            SidecarContainers = new List <ContainerInfo>();
            foreach (var sidecar in message.JobSidecarContainers)
            {
                var           networkAlias           = sidecar.Key;
                var           containerResourceAlias = sidecar.Value;
                var           containerResource      = message.Resources.Containers.Single(c => string.Equals(c.Alias, containerResourceAlias, StringComparison.OrdinalIgnoreCase));
                ContainerInfo containerInfo          = HostContext.CreateContainerInfo(containerResource, isJobContainer: false);
                containerInfo.ContainerNetworkAlias = networkAlias;
                SidecarContainers.Add(containerInfo);
            }

            // Proxy variables
            var agentWebProxy = HostContext.GetService <IVstsAgentWebProxy>();

            if (!string.IsNullOrEmpty(agentWebProxy.ProxyAddress))
            {
                Variables.Set(Constants.Variables.Agent.ProxyUrl, agentWebProxy.ProxyAddress);
                Environment.SetEnvironmentVariable("VSTS_HTTP_PROXY", string.Empty);

                if (!string.IsNullOrEmpty(agentWebProxy.ProxyUsername))
                {
                    Variables.Set(Constants.Variables.Agent.ProxyUsername, agentWebProxy.ProxyUsername);
                    Environment.SetEnvironmentVariable("VSTS_HTTP_PROXY_USERNAME", string.Empty);
                }

                if (!string.IsNullOrEmpty(agentWebProxy.ProxyPassword))
                {
                    Variables.Set(Constants.Variables.Agent.ProxyPassword, agentWebProxy.ProxyPassword, true);
                    Environment.SetEnvironmentVariable("VSTS_HTTP_PROXY_PASSWORD", string.Empty);
                }

                if (agentWebProxy.ProxyBypassList.Count > 0)
                {
                    Variables.Set(Constants.Variables.Agent.ProxyBypassList, JsonUtility.ToString(agentWebProxy.ProxyBypassList));
                }
            }

            // Certificate variables
            var agentCert = HostContext.GetService <IAgentCertificateManager>();

            if (agentCert.SkipServerCertificateValidation)
            {
                Variables.Set(Constants.Variables.Agent.SslSkipCertValidation, bool.TrueString);
            }

            if (!string.IsNullOrEmpty(agentCert.CACertificateFile))
            {
                Variables.Set(Constants.Variables.Agent.SslCAInfo, agentCert.CACertificateFile);
            }

            if (!string.IsNullOrEmpty(agentCert.ClientCertificateFile) &&
                !string.IsNullOrEmpty(agentCert.ClientCertificatePrivateKeyFile) &&
                !string.IsNullOrEmpty(agentCert.ClientCertificateArchiveFile))
            {
                Variables.Set(Constants.Variables.Agent.SslClientCert, agentCert.ClientCertificateFile);
                Variables.Set(Constants.Variables.Agent.SslClientCertKey, agentCert.ClientCertificatePrivateKeyFile);
                Variables.Set(Constants.Variables.Agent.SslClientCertArchive, agentCert.ClientCertificateArchiveFile);

                if (!string.IsNullOrEmpty(agentCert.ClientCertificatePassword))
                {
                    Variables.Set(Constants.Variables.Agent.SslClientCertPassword, agentCert.ClientCertificatePassword, true);
                }
            }

            // Runtime option variables
            var runtimeOptions = HostContext.GetService <IConfigurationStore>().GetAgentRuntimeOptions();

            if (runtimeOptions != null)
            {
                if (PlatformUtil.RunningOnWindows && runtimeOptions.GitUseSecureChannel)
                {
                    Variables.Set(Constants.Variables.Agent.GitUseSChannel, runtimeOptions.GitUseSecureChannel.ToString());
                }
            }

            // Job timeline record.
            InitializeTimelineRecord(
                timelineId: message.Timeline.Id,
                timelineRecordId: message.JobId,
                parentTimelineRecordId: null,
                recordType: ExecutionContextType.Job,
                displayName: message.JobDisplayName,
                refName: message.JobName,
                order: null); // The job timeline record's order is set by server.

            // Logger (must be initialized before writing warnings).
            _logger = HostContext.CreateService <IPagingLogger>();
            _logger.Setup(_mainTimelineId, _record.Id);

            // Log warnings from recursive variable expansion.
            warnings?.ForEach(x => this.Warning(x));

            // Verbosity (from system.debug).
            WriteDebug = Variables.System_Debug ?? false;

            // Hook up JobServerQueueThrottling event, we will log warning on server tarpit.
            _jobServerQueue.JobServerQueueThrottling += JobServerQueueThrottling_EventReceived;
        }
Esempio n. 22
0
        // download all required tasks.
        // make sure all task's condition inputs are valid.
        // build up three list of steps for jobrunner. (pre-job, job, post-job)
        public async Task <List <IStep> > InitializeJob(IExecutionContext jobContext, Pipelines.AgentJobRequestMessage message)
        {
            Trace.Entering();
            ArgUtil.NotNull(jobContext, nameof(jobContext));
            ArgUtil.NotNull(message, nameof(message));

            // create a new timeline record node for 'Initialize job'
            IExecutionContext context = jobContext.CreateChild(Guid.NewGuid(), StringUtil.Loc("InitializeJob"), $"{nameof(JobExtension)}_Init");

            List <IStep> preJobSteps  = new List <IStep>();
            List <IStep> jobSteps     = new List <IStep>();
            List <IStep> postJobSteps = new List <IStep>();

            using (var register = jobContext.CancellationToken.Register(() => { context.CancelToken(); }))
            {
                try
                {
                    context.Start();
                    context.Section(StringUtil.Loc("StepStarting", StringUtil.Loc("InitializeJob")));

                    // Set agent version variable.
                    context.SetVariable(Constants.Variables.Agent.Version, BuildConstants.AgentPackage.Version);
                    context.Output(StringUtil.Loc("AgentNameLog", context.Variables.Get(Constants.Variables.Agent.Name)));
                    context.Output(StringUtil.Loc("AgentMachineNameLog", context.Variables.Get(Constants.Variables.Agent.MachineName)));
                    context.Output(StringUtil.Loc("AgentVersion", BuildConstants.AgentPackage.Version));

                    // Machine specific setup info
                    OutputSetupInfo(context);

                    string imageVersion = System.Environment.GetEnvironmentVariable(Constants.ImageVersionVariable);
                    if (imageVersion != null)
                    {
                        context.Output(StringUtil.Loc("ImageVersionLog", imageVersion));
                    }

                    context.Output(StringUtil.Loc("UserNameLog", System.Environment.UserName));

                    // Print proxy setting information for better diagnostic experience
                    var agentWebProxy = HostContext.GetService <IVstsAgentWebProxy>();
                    if (!string.IsNullOrEmpty(agentWebProxy.ProxyAddress))
                    {
                        context.Output(StringUtil.Loc("AgentRunningBehindProxy", agentWebProxy.ProxyAddress));
                    }

                    // Give job extension a chance to initialize
                    Trace.Info($"Run initial step from extension {this.GetType().Name}.");
                    InitializeJobExtension(context, message?.Steps, message?.Workspace);

                    // Download tasks if not already in the cache
                    Trace.Info("Downloading task definitions.");
                    var taskManager = HostContext.GetService <ITaskManager>();
                    await taskManager.DownloadAsync(context, message.Steps);

                    // Parse all Task conditions.
                    Trace.Info("Parsing all task's condition inputs.");
                    var expression = HostContext.GetService <IExpressionManager>();
                    Dictionary <Guid, IExpressionNode> taskConditionMap = new Dictionary <Guid, IExpressionNode>();
                    foreach (var task in message.Steps.OfType <Pipelines.TaskStep>())
                    {
                        IExpressionNode condition;
                        if (!string.IsNullOrEmpty(task.Condition))
                        {
                            context.Debug($"Task '{task.DisplayName}' has following condition: '{task.Condition}'.");
                            condition = expression.Parse(context, task.Condition);
                        }
                        else
                        {
                            condition = ExpressionManager.Succeeded;
                        }

                        task.DisplayName = context.Variables.ExpandValue(nameof(task.DisplayName), task.DisplayName);

                        taskConditionMap[task.Id] = condition;
                    }
                    context.Output("Checking job knob settings.");
                    foreach (var knob in Knob.GetAllKnobsFor <AgentKnobs>())
                    {
                        var value = knob.GetValue(jobContext);
                        if (value.Source.GetType() != typeof(BuiltInDefaultKnobSource))
                        {
                            var tag = "";
                            if (knob.IsDeprecated)
                            {
                                tag = "(DEPRECATED)";
                            }
                            else if (knob.IsExperimental)
                            {
                                tag = "(EXPERIMENTAL)";
                            }
                            var outputLine = $"   Knob: {knob.Name} = {value.AsString()} Source: {value.Source.GetDisplayString()} {tag}";
                            if (knob.IsDeprecated)
                            {
                                context.Warning(outputLine);
                            }
                            else
                            {
                                context.Output(outputLine);
                            }
                        }
                    }
                    context.Output("Finished checking job knob settings.");

                    if (PlatformUtil.RunningOnWindows)
                    {
                        // This is for internal testing and is not publicly supported. This will be removed from the agent at a later time.
                        var             prepareScript    = Environment.GetEnvironmentVariable("VSTS_AGENT_INIT_INTERNAL_TEMP_HACK");
                        ServiceEndpoint systemConnection = context.Endpoints.Single(x => string.Equals(x.Name, WellKnownServiceEndpointNames.SystemVssConnection, StringComparison.OrdinalIgnoreCase));
                        if (!string.IsNullOrEmpty(prepareScript) && context.StepTarget() is HostInfo)
                        {
                            var prepareStep = new ManagementScriptStep(
                                scriptPath: prepareScript,
                                condition: ExpressionManager.Succeeded,
                                displayName: "Agent Initialization");

                            Trace.Verbose($"Adding agent init script step.");
                            prepareStep.Initialize(HostContext);
                            prepareStep.ExecutionContext = jobContext?.CreateChild(Guid.NewGuid(), prepareStep.DisplayName, nameof(ManagementScriptStep));
                            prepareStep.AccessToken      = systemConnection.Authorization.Parameters["AccessToken"];
                            prepareStep.Condition        = ExpressionManager.Succeeded;
                            preJobSteps.Add(prepareStep);
                        }
                    }

                    // build up 3 lists of steps, pre-job, job, post-job
                    Stack <IStep> postJobStepsBuilder = new Stack <IStep>();
                    Dictionary <Guid, Variables> taskVariablesMapping = new Dictionary <Guid, Variables>();

                    if (context.Containers.Count > 0 || context.SidecarContainers.Count > 0)
                    {
                        var containerProvider = HostContext.GetService <IContainerOperationProvider>();
                        var containers        = new List <ContainerInfo>();
                        containers.AddRange(context.Containers);
                        containers.AddRange(context.SidecarContainers);

                        preJobSteps.Add(new JobExtensionRunner(runAsync: containerProvider.StartContainersAsync,
                                                               condition: ExpressionManager.Succeeded,
                                                               displayName: StringUtil.Loc("InitializeContainer"),
                                                               data: (object)containers));
                        postJobStepsBuilder.Push(new JobExtensionRunner(runAsync: containerProvider.StopContainersAsync,
                                                                        condition: ExpressionManager.Always,
                                                                        displayName: StringUtil.Loc("StopContainer"),
                                                                        data: (object)containers));
                    }

                    Dictionary <Guid, List <TaskRestrictions> > taskRestrictionsMap = new Dictionary <Guid, List <TaskRestrictions> >();
                    foreach (var task in message?.Steps.OfType <Pipelines.TaskStep>())
                    {
                        var taskDefinition = taskManager.Load(task);

                        // Collect any task restrictions from the definition or step
                        var restrictions = new List <TaskRestrictions>();
                        if (taskDefinition.Data.Restrictions != null)
                        {
                            restrictions.Add(taskDefinition.Data.Restrictions);
                        }
                        if (string.Equals(WellKnownStepTargetStrings.Restricted, task.Target?.Commands, StringComparison.OrdinalIgnoreCase))
                        {
                            restrictions.Add(new TaskRestrictions()
                            {
                                Commands = new TaskCommandRestrictions()
                                {
                                    Mode = TaskCommandMode.Restricted
                                }
                            });
                        }
                        if (task.Target?.SettableVariables != null)
                        {
                            restrictions.Add(new TaskRestrictions()
                            {
                                SettableVariables = task.Target.SettableVariables
                            });
                        }
                        taskRestrictionsMap[task.Id] = restrictions;

                        List <string> warnings;
                        taskVariablesMapping[task.Id] = new Variables(HostContext, new Dictionary <string, VariableValue>(), out warnings);

                        // Add pre-job steps from Tasks
                        if (taskDefinition.Data?.PreJobExecution != null)
                        {
                            Trace.Info($"Adding Pre-Job {task.DisplayName}.");
                            var taskRunner = HostContext.CreateService <ITaskRunner>();
                            taskRunner.Task      = task;
                            taskRunner.Stage     = JobRunStage.PreJob;
                            taskRunner.Condition = taskConditionMap[task.Id];
                            preJobSteps.Add(taskRunner);
                        }

                        // Add execution steps from Tasks
                        if (taskDefinition.Data?.Execution != null)
                        {
                            Trace.Verbose($"Adding {task.DisplayName}.");
                            var taskRunner = HostContext.CreateService <ITaskRunner>();
                            taskRunner.Task      = task;
                            taskRunner.Stage     = JobRunStage.Main;
                            taskRunner.Condition = taskConditionMap[task.Id];
                            jobSteps.Add(taskRunner);
                        }

                        // Add post-job steps from Tasks
                        if (taskDefinition.Data?.PostJobExecution != null)
                        {
                            Trace.Verbose($"Adding Post-Job {task.DisplayName}.");
                            var taskRunner = HostContext.CreateService <ITaskRunner>();
                            taskRunner.Task      = task;
                            taskRunner.Stage     = JobRunStage.PostJob;
                            taskRunner.Condition = ExpressionManager.Always;
                            postJobStepsBuilder.Push(taskRunner);
                        }
                    }

                    // Add pre-job step from Extension
                    Trace.Info("Adding pre-job step from extension.");
                    var extensionPreJobStep = GetExtensionPreJobStep(jobContext);
                    if (extensionPreJobStep != null)
                    {
                        preJobSteps.Add(extensionPreJobStep);
                    }

                    // Add post-job step from Extension
                    Trace.Info("Adding post-job step from extension.");
                    var extensionPostJobStep = GetExtensionPostJobStep(jobContext);
                    if (extensionPostJobStep != null)
                    {
                        postJobStepsBuilder.Push(extensionPostJobStep);
                    }

                    ArgUtil.NotNull(jobContext, nameof(jobContext)); // I am not sure why this is needed, but static analysis flagged all uses of jobContext below this point
                    // create execution context for all pre-job steps
                    foreach (var step in preJobSteps)
                    {
                        if (PlatformUtil.RunningOnWindows && step is ManagementScriptStep)
                        {
                            continue;
                        }

                        if (step is JobExtensionRunner)
                        {
                            JobExtensionRunner extensionStep = step as JobExtensionRunner;
                            ArgUtil.NotNull(extensionStep, extensionStep.DisplayName);
                            Guid stepId = Guid.NewGuid();
                            extensionStep.ExecutionContext = jobContext.CreateChild(stepId, extensionStep.DisplayName, stepId.ToString("N"));
                        }
                        else if (step is ITaskRunner)
                        {
                            ITaskRunner taskStep = step as ITaskRunner;
                            ArgUtil.NotNull(taskStep, step.DisplayName);
                            taskStep.ExecutionContext = jobContext.CreateChild(
                                Guid.NewGuid(),
                                StringUtil.Loc("PreJob", taskStep.DisplayName),
                                taskStep.Task.Name,
                                taskVariablesMapping[taskStep.Task.Id],
                                outputForward: true,
                                taskRestrictions: taskRestrictionsMap[taskStep.Task.Id]);
                        }
                    }

                    // create task execution context for all job steps from task
                    foreach (var step in jobSteps)
                    {
                        ITaskRunner taskStep = step as ITaskRunner;
                        ArgUtil.NotNull(taskStep, step.DisplayName);
                        taskStep.ExecutionContext = jobContext.CreateChild(
                            taskStep.Task.Id,
                            taskStep.DisplayName,
                            taskStep.Task.Name,
                            taskVariablesMapping[taskStep.Task.Id],
                            outputForward: true,
                            taskRestrictions: taskRestrictionsMap[taskStep.Task.Id]);
                    }

                    // Add post-job steps from Tasks
                    Trace.Info("Adding post-job steps from tasks.");
                    while (postJobStepsBuilder.Count > 0)
                    {
                        postJobSteps.Add(postJobStepsBuilder.Pop());
                    }

                    // create task execution context for all post-job steps from task
                    foreach (var step in postJobSteps)
                    {
                        if (step is JobExtensionRunner)
                        {
                            JobExtensionRunner extensionStep = step as JobExtensionRunner;
                            ArgUtil.NotNull(extensionStep, extensionStep.DisplayName);
                            Guid stepId = Guid.NewGuid();
                            extensionStep.ExecutionContext = jobContext.CreateChild(stepId, extensionStep.DisplayName, stepId.ToString("N"));
                        }
                        else if (step is ITaskRunner)
                        {
                            ITaskRunner taskStep = step as ITaskRunner;
                            ArgUtil.NotNull(taskStep, step.DisplayName);
                            taskStep.ExecutionContext = jobContext.CreateChild(
                                Guid.NewGuid(),
                                StringUtil.Loc("PostJob", taskStep.DisplayName),
                                taskStep.Task.Name,
                                taskVariablesMapping[taskStep.Task.Id],
                                outputForward: true,
                                taskRestrictions: taskRestrictionsMap[taskStep.Task.Id]);
                        }
                    }

                    if (PlatformUtil.RunningOnWindows)
                    {
                        // Add script post steps.
                        // This is for internal testing and is not publicly supported. This will be removed from the agent at a later time.
                        var finallyScript = Environment.GetEnvironmentVariable("VSTS_AGENT_CLEANUP_INTERNAL_TEMP_HACK");
                        if (!string.IsNullOrEmpty(finallyScript) && context.StepTarget() is HostInfo)
                        {
                            var finallyStep = new ManagementScriptStep(
                                scriptPath: finallyScript,
                                condition: ExpressionManager.Always,
                                displayName: "Agent Cleanup");

                            Trace.Verbose($"Adding agent cleanup script step.");
                            finallyStep.Initialize(HostContext);
                            finallyStep.ExecutionContext = jobContext.CreateChild(Guid.NewGuid(), finallyStep.DisplayName, nameof(ManagementScriptStep));
                            finallyStep.Condition        = ExpressionManager.Always;
                            ServiceEndpoint systemConnection = context.Endpoints.Single(x => string.Equals(x.Name, WellKnownServiceEndpointNames.SystemVssConnection, StringComparison.OrdinalIgnoreCase));
                            finallyStep.AccessToken = systemConnection.Authorization.Parameters["AccessToken"];
                            postJobSteps.Add(finallyStep);
                        }
                    }

                    List <IStep> steps = new List <IStep>();
                    steps.AddRange(preJobSteps);
                    steps.AddRange(jobSteps);
                    steps.AddRange(postJobSteps);

                    // Start agent log plugin host process
                    var logPlugin = HostContext.GetService <IAgentLogPlugin>();
                    await logPlugin.StartAsync(context, steps, jobContext.CancellationToken);

                    // Prepare for orphan process cleanup
                    _processCleanup = jobContext.Variables.GetBoolean("process.clean") ?? true;
                    if (_processCleanup)
                    {
                        // Set the VSTS_PROCESS_LOOKUP_ID env variable.
                        context.SetVariable(Constants.ProcessLookupId, _processLookupId, false, false);
                        context.Output("Start tracking orphan processes.");

                        // Take a snapshot of current running processes
                        Dictionary <int, Process> processes = SnapshotProcesses();
                        foreach (var proc in processes)
                        {
                            // Pid_ProcessName
                            _existingProcesses.Add($"{proc.Key}_{proc.Value.ProcessName}");
                        }
                    }
                    _taskKeyCleanup = jobContext.Variables.GetBoolean("process.cleanTaskKey") ?? true;

                    return(steps);
                }
                catch (OperationCanceledException ex) when(jobContext.CancellationToken.IsCancellationRequested)
                {
                    // Log the exception and cancel the JobExtension Initialization.
                    Trace.Error($"Caught cancellation exception from JobExtension Initialization: {ex}");
                    context.Error(ex);
                    context.Result = TaskResult.Canceled;
                    throw;
                }
                catch (Exception ex)
                {
                    // Log the error and fail the JobExtension Initialization.
                    Trace.Error($"Caught exception from JobExtension Initialization: {ex}");
                    context.Error(ex);
                    context.Result = TaskResult.Failed;
                    throw;
                }
                finally
                {
                    context.Section(StringUtil.Loc("StepFinishing", StringUtil.Loc("InitializeJob")));
                    context.Complete();
                }
            }
        }
        public async Task UploadDiagnosticLogsAsync(IExecutionContext executionContext,
                                                    Pipelines.AgentJobRequestMessage message,
                                                    DateTime jobStartTimeUtc)
        {
            ArgUtil.NotNull(executionContext, nameof(executionContext));
            ArgUtil.NotNull(message, nameof(message));

            executionContext.Debug("Starting diagnostic file upload.");

            // Setup folders
            // \_layout\_work\_temp\[jobname-support]
            executionContext.Debug("Setting up diagnostic log folders.");
            string tempDirectory = HostContext.GetDirectory(WellKnownDirectory.Temp);

            ArgUtil.Directory(tempDirectory, nameof(tempDirectory));

            string supportRootFolder = Path.Combine(tempDirectory, message.JobName + "-support");

            Directory.CreateDirectory(supportRootFolder);

            // \_layout\_work\_temp\[jobname-support]\files
            executionContext.Debug("Creating diagnostic log files folder.");
            string supportFilesFolder = Path.Combine(supportRootFolder, "files");

            Directory.CreateDirectory(supportFilesFolder);

            // Create the environment file
            // \_layout\_work\_temp\[jobname-support]\files\environment.txt
            var           configurationStore = HostContext.GetService <IConfigurationStore>();
            AgentSettings settings           = configurationStore.GetSettings();
            int           agentId            = settings.AgentId;
            string        agentName          = settings.AgentName;
            int           poolId             = settings.PoolId;

            executionContext.Debug("Creating diagnostic log environment file.");
            string environmentFile = Path.Combine(supportFilesFolder, "environment.txt");
            string content         = await GetEnvironmentContent(agentId, agentName, message.Steps);

            File.WriteAllText(environmentFile, content);

            // Create the capabilities file
            var capabilitiesManager = HostContext.GetService <ICapabilitiesManager>();
            Dictionary <string, string> capabilities = await capabilitiesManager.GetCapabilitiesAsync(configurationStore.GetSettings(), default(CancellationToken));

            executionContext.Debug("Creating capabilities file.");
            string capabilitiesFile    = Path.Combine(supportFilesFolder, "capabilities.txt");
            string capabilitiesContent = GetCapabilitiesContent(capabilities);

            File.WriteAllText(capabilitiesFile, capabilitiesContent);

            // Copy worker diag log files
            List <string> workerDiagLogFiles = GetWorkerDiagLogFiles(HostContext.GetDirectory(WellKnownDirectory.Diag), jobStartTimeUtc);

            executionContext.Debug($"Copying {workerDiagLogFiles.Count()} worker diag logs.");

            foreach (string workerLogFile in workerDiagLogFiles)
            {
                ArgUtil.File(workerLogFile, nameof(workerLogFile));

                string destination = Path.Combine(supportFilesFolder, Path.GetFileName(workerLogFile));
                File.Copy(workerLogFile, destination);
            }

            // Copy agent diag log files
            List <string> agentDiagLogFiles = GetAgentDiagLogFiles(HostContext.GetDirectory(WellKnownDirectory.Diag), jobStartTimeUtc);

            executionContext.Debug($"Copying {agentDiagLogFiles.Count()} agent diag logs.");

            foreach (string agentLogFile in agentDiagLogFiles)
            {
                ArgUtil.File(agentLogFile, nameof(agentLogFile));

                string destination = Path.Combine(supportFilesFolder, Path.GetFileName(agentLogFile));
                File.Copy(agentLogFile, destination);
            }

            // Read and add to logs waagent.conf settings on Linux
            if (PlatformUtil.RunningOnLinux)
            {
                executionContext.Debug("Dumping of waagent.conf file");
                string waagentDumpFile = Path.Combine(supportFilesFolder, "waagentConf.txt");

                string configFileName = "waagent.conf";
                try
                {
                    string filePath = Directory.GetFiles("/etc", configFileName).FirstOrDefault();
                    if (!string.IsNullOrWhiteSpace(filePath))
                    {
                        string waagentContent = File.ReadAllText(filePath);

                        File.AppendAllText(waagentDumpFile, "waagent.conf settings");
                        File.AppendAllText(waagentDumpFile, Environment.NewLine);
                        File.AppendAllText(waagentDumpFile, waagentContent);

                        executionContext.Debug("Dumping waagent.conf file is completed.");
                    }
                    else
                    {
                        executionContext.Debug("waagent.conf file wasn't found. Dumping was not done.");
                    }
                }
                catch (Exception ex)
                {
                    string warningMessage = $"Dumping of waagent.conf was not completed successfully. Error message: {ex.Message}";
                    executionContext.Warning(warningMessage);
                }
            }

            // Copy cloud-init log files from linux machines
            if (PlatformUtil.RunningOnLinux)
            {
                executionContext.Debug("Dumping cloud-init logs.");

                string logsFilePath = $"{HostContext.GetDirectory(WellKnownDirectory.Diag)}/cloudinit-{jobStartTimeUtc.ToString("yyyyMMdd-HHmmss")}-logs.tar.gz";
                string resultLogs   = await DumpCloudInitLogs(logsFilePath);

                executionContext.Debug(resultLogs);

                if (File.Exists(logsFilePath))
                {
                    string destination = Path.Combine(supportFilesFolder, Path.GetFileName(logsFilePath));
                    File.Copy(logsFilePath, destination);
                    executionContext.Debug("Cloud-init logs added to the diagnostics archive.");
                }
                else
                {
                    executionContext.Debug("Cloud-init logs were not found.");
                }

                executionContext.Debug("Dumping cloud-init logs is ended.");
            }

            // Copy event logs for windows machines
            if (PlatformUtil.RunningOnWindows)
            {
                executionContext.Debug("Dumping event viewer logs for current job.");

                try
                {
                    string eventLogsFile = $"{HostContext.GetDirectory(WellKnownDirectory.Diag)}/EventViewer-{ jobStartTimeUtc.ToString("yyyyMMdd-HHmmss") }.log";
                    await DumpCurrentJobEventLogs(executionContext, eventLogsFile, jobStartTimeUtc);

                    string destination = Path.Combine(supportFilesFolder, Path.GetFileName(eventLogsFile));
                    File.Copy(eventLogsFile, destination);
                }
                catch (Exception ex)
                {
                    executionContext.Debug("Failed to dump event viewer logs. Skipping.");
                    executionContext.Debug($"Error message: {ex}");
                }
            }

            if (PlatformUtil.RunningOnLinux && !PlatformUtil.RunningOnRHEL6)
            {
                executionContext.Debug("Dumping info about invalid MD5 sums of installed packages.");

                try
                {
                    string packageVerificationResults = await GetPackageVerificationResult();

                    IEnumerable <string> brokenPackagesInfo = packageVerificationResults
                                                              .Split("\n")
                                                              .Where((line) => !String.IsNullOrEmpty(line) && !line.EndsWith("OK"));

                    string brokenPackagesLogsPath = $"{HostContext.GetDirectory(WellKnownDirectory.Diag)}/BrokenPackages-{ jobStartTimeUtc.ToString("yyyyMMdd-HHmmss") }.log";
                    File.AppendAllLines(brokenPackagesLogsPath, brokenPackagesInfo);

                    string destination = Path.Combine(supportFilesFolder, Path.GetFileName(brokenPackagesLogsPath));
                    File.Copy(brokenPackagesLogsPath, destination);
                }
                catch (Exception ex)
                {
                    executionContext.Debug("Failed to dump broken packages logs. Skipping.");
                    executionContext.Debug($"Error message: {ex}");
                }
            }
            else
            {
                executionContext.Debug("The platform is not based on Debian - skipping debsums check.");
            }

            try
            {
                executionContext.Debug("Starting dumping Agent Azure VM extension logs.");
                bool logsSuccessfullyDumped = DumpAgentExtensionLogs(executionContext, supportFilesFolder, jobStartTimeUtc);
                if (logsSuccessfullyDumped)
                {
                    executionContext.Debug("Agent Azure VM extension logs successfully dumped.");
                }
                else
                {
                    executionContext.Debug("Agent Azure VM extension logs not found. Skipping.");
                }
            }
            catch (Exception ex)
            {
                executionContext.Debug("Failed to dump Agent Azure VM extension logs. Skipping.");
                executionContext.Debug($"Error message: {ex}");
            }

            executionContext.Debug("Zipping diagnostic files.");

            string buildNumber = executionContext.Variables.Build_Number ?? "UnknownBuildNumber";
            string buildName   = $"Build {buildNumber}";
            string phaseName   = executionContext.Variables.System_PhaseDisplayName ?? "UnknownPhaseName";

            // zip the files
            string diagnosticsZipFileName = $"{buildName}-{phaseName}.zip";
            string diagnosticsZipFilePath = Path.Combine(supportRootFolder, diagnosticsZipFileName);

            ZipFile.CreateFromDirectory(supportFilesFolder, diagnosticsZipFilePath);

            // upload the json metadata file
            executionContext.Debug("Uploading diagnostic metadata file.");
            string metadataFileName = $"diagnostics-{buildName}-{phaseName}.json";
            string metadataFilePath = Path.Combine(supportFilesFolder, metadataFileName);
            string phaseResult      = GetTaskResultAsString(executionContext.Result);

            IOUtil.SaveObject(new DiagnosticLogMetadata(agentName, agentId, poolId, phaseName, diagnosticsZipFileName, phaseResult), metadataFilePath);

            executionContext.QueueAttachFile(type: CoreAttachmentType.DiagnosticLog, name: metadataFileName, filePath: metadataFilePath);

            executionContext.QueueAttachFile(type: CoreAttachmentType.DiagnosticLog, name: diagnosticsZipFileName, filePath: diagnosticsZipFilePath);

            executionContext.Debug("Diagnostic file upload complete.");
        }
Esempio n. 24
0
        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;

            // Agent.RunMode
            RunMode runMode;

            if (message.Variables.ContainsKey(Constants.Variables.Agent.RunMode) &&
                Enum.TryParse(message.Variables[Constants.Variables.Agent.RunMode].Value, ignoreCase: true, result: out runMode) &&
                runMode == RunMode.Local)
            {
                HostContext.RunMode = runMode;
            }

            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, 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.Variables.Set(Constants.Variables.Agent.Id, settings.AgentId.ToString(CultureInfo.InvariantCulture));
                jobContext.SetVariable(Constants.Variables.Agent.HomeDirectory, HostContext.GetDirectory(WellKnownDirectory.Root), isFilePath: true);
                jobContext.Variables.Set(Constants.Variables.Agent.JobName, message.JobDisplayName);
                jobContext.Variables.Set(Constants.Variables.Agent.MachineName, Environment.MachineName);
                jobContext.Variables.Set(Constants.Variables.Agent.Name, settings.AgentName);
                jobContext.Variables.Set(Constants.Variables.Agent.OS, VarUtil.OS);
                jobContext.Variables.Set(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.Variables.Set(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);

                bool disableGitPrompt = jobContext.Variables.GetBoolean("VSTS_DISABLE_GIT_PROMPT") ?? StringUtil.ConvertToBoolean(Environment.GetEnvironmentVariable("VSTS_DISABLE_GIT_PROMPT"), true);
                if (disableGitPrompt &&
                    string.IsNullOrEmpty(jobContext.Variables.Get("GIT_TERMINAL_PROMPT")))
                {
                    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));
                }

                // 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));
                    }
                }

                // 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
                if (jobContext.Container != null)
                {
                    this.ExpandProperties(jobContext.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));
            }
            finally
            {
                if (agentShutdownRegistration != null)
                {
                    agentShutdownRegistration.Value.Dispose();
                    agentShutdownRegistration = null;
                }

                await ShutdownQueue(throwOnFailure : false);
            }
        }
        public void InitializeJob(Pipelines.AgentJobRequestMessage message, CancellationToken token)
        {
            // Validation
            Trace.Entering();
            ArgUtil.NotNull(message, nameof(message));
            ArgUtil.NotNull(message.Resources, nameof(message.Resources));
            ArgUtil.NotNull(message.Variables, nameof(message.Variables));
            ArgUtil.NotNull(message.Plan, nameof(message.Plan));

            _cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token);

            // Features
            Features = PlanUtil.GetFeatures(message.Plan);

            // Endpoints
            Endpoints = message.Resources.Endpoints;

            // SecureFiles
            SecureFiles = message.Resources.SecureFiles;

            // Repositories
            Repositories = message.Resources.Repositories;

            // JobSettings
            JobSettings = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase);
            JobSettings[WellKnownJobSettings.HasMultipleCheckouts] = message.Steps?.Where(x => Pipelines.PipelineConstants.IsCheckoutTask(x)).Count() > 1 ? Boolean.TrueString : Boolean.FalseString;

            // Variables (constructor performs initial recursive expansion)
            List <string> warnings;

            Variables = new Variables(HostContext, message.Variables, out warnings);

            // Prepend Path
            PrependPath = new List <string>();

            // Docker (JobContainer)
            string imageName = Variables.Get("_PREVIEW_VSTS_DOCKER_IMAGE");

            if (string.IsNullOrEmpty(imageName))
            {
                imageName = Environment.GetEnvironmentVariable("_PREVIEW_VSTS_DOCKER_IMAGE");
            }

            if (!string.IsNullOrEmpty(imageName) &&
                string.IsNullOrEmpty(message.JobContainer))
            {
                var dockerContainer = new Pipelines.ContainerResource()
                {
                    Alias = "vsts_container_preview"
                };
                dockerContainer.Properties.Set("image", imageName);
                Container = HostContext.CreateContainerInfo(dockerContainer);
            }
            else if (!string.IsNullOrEmpty(message.JobContainer))
            {
                Container = HostContext.CreateContainerInfo(message.Resources.Containers.Single(x => string.Equals(x.Alias, message.JobContainer, StringComparison.OrdinalIgnoreCase)));
            }
            else
            {
                Container = null;
            }

            if (Container != null)
            {
                Container.ImageOSChanged += HandleContainerImageOSChange;
            }

            // Docker (Sidecar Containers)
            SidecarContainers = new List <ContainerInfo>();
            foreach (var sidecar in message.JobSidecarContainers)
            {
                var           networkAlias           = sidecar.Key;
                var           containerResourceAlias = sidecar.Value;
                var           containerResource      = message.Resources.Containers.Single(c => string.Equals(c.Alias, containerResourceAlias, StringComparison.OrdinalIgnoreCase));
                ContainerInfo containerInfo          = HostContext.CreateContainerInfo(containerResource, isJobContainer: false);
                containerInfo.ContainerNetworkAlias = networkAlias;
                SidecarContainers.Add(containerInfo);
            }

            // Proxy variables
            var agentWebProxy = HostContext.GetService <IVstsAgentWebProxy>();

            if (!string.IsNullOrEmpty(agentWebProxy.ProxyAddress))
            {
                Variables.Set(Constants.Variables.Agent.ProxyUrl, agentWebProxy.ProxyAddress);
                Environment.SetEnvironmentVariable("VSTS_HTTP_PROXY", string.Empty);

                if (!string.IsNullOrEmpty(agentWebProxy.ProxyUsername))
                {
                    Variables.Set(Constants.Variables.Agent.ProxyUsername, agentWebProxy.ProxyUsername);
                    Environment.SetEnvironmentVariable("VSTS_HTTP_PROXY_USERNAME", string.Empty);
                }

                if (!string.IsNullOrEmpty(agentWebProxy.ProxyPassword))
                {
                    Variables.Set(Constants.Variables.Agent.ProxyPassword, agentWebProxy.ProxyPassword, true);
                    Environment.SetEnvironmentVariable("VSTS_HTTP_PROXY_PASSWORD", string.Empty);
                }

                if (agentWebProxy.ProxyBypassList.Count > 0)
                {
                    Variables.Set(Constants.Variables.Agent.ProxyBypassList, JsonUtility.ToString(agentWebProxy.ProxyBypassList));
                }
            }

            // Certificate variables
            var agentCert = HostContext.GetService <IAgentCertificateManager>();

            if (agentCert.SkipServerCertificateValidation)
            {
                Variables.Set(Constants.Variables.Agent.SslSkipCertValidation, bool.TrueString);
            }

            if (!string.IsNullOrEmpty(agentCert.CACertificateFile))
            {
                Variables.Set(Constants.Variables.Agent.SslCAInfo, agentCert.CACertificateFile);
            }

            if (!string.IsNullOrEmpty(agentCert.ClientCertificateFile) &&
                !string.IsNullOrEmpty(agentCert.ClientCertificatePrivateKeyFile) &&
                !string.IsNullOrEmpty(agentCert.ClientCertificateArchiveFile))
            {
                Variables.Set(Constants.Variables.Agent.SslClientCert, agentCert.ClientCertificateFile);
                Variables.Set(Constants.Variables.Agent.SslClientCertKey, agentCert.ClientCertificatePrivateKeyFile);
                Variables.Set(Constants.Variables.Agent.SslClientCertArchive, agentCert.ClientCertificateArchiveFile);

                if (!string.IsNullOrEmpty(agentCert.ClientCertificatePassword))
                {
                    Variables.Set(Constants.Variables.Agent.SslClientCertPassword, agentCert.ClientCertificatePassword, true);
                }
            }

            // Runtime option variables
            var runtimeOptions = HostContext.GetService <IConfigurationStore>().GetAgentRuntimeOptions();

            if (runtimeOptions != null)
            {
                if (PlatformUtil.RunningOnWindows && runtimeOptions.GitUseSecureChannel)
                {
                    Variables.Set(Constants.Variables.Agent.GitUseSChannel, runtimeOptions.GitUseSecureChannel.ToString());
                }
            }

            // Job timeline record.
            InitializeTimelineRecord(
                timelineId: message.Timeline.Id,
                timelineRecordId: message.JobId,
                parentTimelineRecordId: null,
                recordType: ExecutionContextType.Job,
                displayName: message.JobDisplayName,
                refName: message.JobName,
                order: null); // The job timeline record's order is set by server.

            // Logger (must be initialized before writing warnings).
            _logger = HostContext.CreateService <IPagingLogger>();
            _logger.Setup(_mainTimelineId, _record.Id);

            // Log warnings from recursive variable expansion.
            warnings?.ForEach(x => this.Warning(x));

            // Verbosity (from system.debug).
            WriteDebug = Variables.System_Debug ?? false;

            // Hook up JobServerQueueThrottling event, we will log warning on server tarpit.
            _jobServerQueue.JobServerQueueThrottling += JobServerQueueThrottling_EventReceived;
        }
 private async Task SetupMessage(HostContext context, Pipelines.AgentJobRequestMessage message)
 {
     // The agent assumes the server creates this
     var jobServer = context.GetService <IJobServer>();
     await jobServer.CreateTimelineAsync(message.Plan.ScopeIdentifier, message.Plan.PlanType, message.Plan.PlanId, message.Timeline.Id, default(CancellationToken));
 }
Esempio n. 27
0
        // log an error issue to job level timeline record
        private async Task LogWorkerProcessUnhandledException(Pipelines.AgentJobRequestMessage message, string errorMessage)
        {
            try
            {
                var systemConnection = message.Resources.Endpoints.SingleOrDefault(x => string.Equals(x.Name, WellKnownServiceEndpointNames.SystemVssConnection));
                ArgUtil.NotNull(systemConnection, nameof(systemConnection));

                var            jobServer           = HostContext.GetService <IJobServer>();
                VssCredentials jobServerCredential = VssUtil.GetVssCredential(systemConnection);
                Uri            jobServerUrl        = systemConnection.Url;

                // Make sure SystemConnection Url match Config Url base for OnPremises server
                if (!message.Variables.ContainsKey(Constants.Variables.System.ServerType) ||
                    string.Equals(message.Variables[Constants.Variables.System.ServerType]?.Value, "OnPremises", StringComparison.OrdinalIgnoreCase))
                {
                    try
                    {
                        Uri result    = null;
                        Uri configUri = new Uri(_agentSetting.ServerUrl);
                        if (Uri.TryCreate(new Uri(configUri.GetComponents(UriComponents.SchemeAndServer, UriFormat.Unescaped)), jobServerUrl.PathAndQuery, out result))
                        {
                            //replace the schema and host portion of messageUri with the host from the
                            //server URI (which was set at config time)
                            jobServerUrl = result;
                        }
                    }
                    catch (InvalidOperationException ex)
                    {
                        //cannot parse the Uri - not a fatal error
                        Trace.Error(ex);
                    }
                    catch (UriFormatException ex)
                    {
                        //cannot parse the Uri - not a fatal error
                        Trace.Error(ex);
                    }
                }

                using (var jobConnection = VssUtil.CreateConnection(jobServerUrl, jobServerCredential))
                {
                    await jobServer.ConnectAsync(jobConnection);

                    var timeline = await jobServer.GetTimelineAsync(message.Plan.ScopeIdentifier, message.Plan.PlanType, message.Plan.PlanId, message.Timeline.Id, CancellationToken.None);

                    ArgUtil.NotNull(timeline, nameof(timeline));
                    TimelineRecord jobRecord = timeline.Records.FirstOrDefault(x => x.Id == message.JobId && x.RecordType == "Job");
                    ArgUtil.NotNull(jobRecord, nameof(jobRecord));
                    jobRecord.ErrorCount++;
                    jobRecord.Issues.Add(new Issue()
                    {
                        Type = IssueType.Error, Message = errorMessage
                    });
                    await jobServer.UpdateTimelineRecordsAsync(message.Plan.ScopeIdentifier, message.Plan.PlanType, message.Plan.PlanId, message.Timeline.Id, new TimelineRecord[] { jobRecord }, CancellationToken.None);
                }
            }
            catch (Exception ex)
            {
                Trace.Error("Fail to report unhandled exception from Agent.Worker process");
                Trace.Error(ex);
            }
        }
Esempio n. 28
0
        public void InitializeJob(Pipelines.AgentJobRequestMessage message, CancellationToken token)
        {
            // Validation
            Trace.Entering();
            ArgUtil.NotNull(message, nameof(message));
            ArgUtil.NotNull(message.Resources, nameof(message.Resources));
            ArgUtil.NotNull(message.Variables, nameof(message.Variables));
            ArgUtil.NotNull(message.Plan, nameof(message.Plan));

            _cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token);

            // Features
            Features = ApiUtil.GetFeatures(message.Plan);

            // Endpoints
            Endpoints = message.Resources.Endpoints;

            // SecureFiles
            SecureFiles = message.Resources.SecureFiles;

            // Variables (constructor performs initial recursive expansion)
            List <string> warnings;

            Variables = new Variables(HostContext, message.Variables, out warnings);

            // Prepend Path
            PrependPath = new List <string>();

            // Docker
            string imageName = Variables.Get("_PREVIEW_VSTS_DOCKER_IMAGE");

            if (string.IsNullOrEmpty(imageName))
            {
                imageName = Environment.GetEnvironmentVariable("_PREVIEW_VSTS_DOCKER_IMAGE");
            }

            if (!string.IsNullOrEmpty(imageName) &&
                string.IsNullOrEmpty(message.JobContainer))
            {
                var dockerContainer = new Pipelines.ContainerResource()
                {
                    Name = "vsts_container_preview"
                };
                dockerContainer.Properties.Set("image", imageName);
                Container = new ContainerInfo(dockerContainer);
            }
            else if (!string.IsNullOrEmpty(message.JobContainer))
            {
                Container = new ContainerInfo(message.Resources.Containers.Single(x => string.Equals(x.Name, message.JobContainer, StringComparison.OrdinalIgnoreCase)));
            }
            else
            {
                Container = null;
            }

            // Proxy variables
            var agentWebProxy = HostContext.GetService <IVstsAgentWebProxy>();

            if (!string.IsNullOrEmpty(agentWebProxy.ProxyAddress))
            {
                Variables.Set(Constants.Variables.Agent.ProxyUrl, agentWebProxy.ProxyAddress);
                Environment.SetEnvironmentVariable("VSTS_HTTP_PROXY", string.Empty);

                if (!string.IsNullOrEmpty(agentWebProxy.ProxyUsername))
                {
                    Variables.Set(Constants.Variables.Agent.ProxyUsername, agentWebProxy.ProxyUsername);
                    Environment.SetEnvironmentVariable("VSTS_HTTP_PROXY_USERNAME", string.Empty);
                }

                if (!string.IsNullOrEmpty(agentWebProxy.ProxyPassword))
                {
                    Variables.Set(Constants.Variables.Agent.ProxyPassword, agentWebProxy.ProxyPassword, true);
                    Environment.SetEnvironmentVariable("VSTS_HTTP_PROXY_PASSWORD", string.Empty);
                }

                if (agentWebProxy.ProxyBypassList.Count > 0)
                {
                    Variables.Set(Constants.Variables.Agent.ProxyBypassList, JsonUtility.ToString(agentWebProxy.ProxyBypassList));
                }
            }

            // Certificate variables
            var agentCert = HostContext.GetService <IAgentCertificateManager>();

            if (agentCert.SkipServerCertificateValidation)
            {
                Variables.Set(Constants.Variables.Agent.SslSkipCertValidation, bool.TrueString);
            }

            if (!string.IsNullOrEmpty(agentCert.CACertificateFile))
            {
                Variables.Set(Constants.Variables.Agent.SslCAInfo, agentCert.CACertificateFile);
            }

            if (!string.IsNullOrEmpty(agentCert.ClientCertificateFile) &&
                !string.IsNullOrEmpty(agentCert.ClientCertificatePrivateKeyFile) &&
                !string.IsNullOrEmpty(agentCert.ClientCertificateArchiveFile))
            {
                Variables.Set(Constants.Variables.Agent.SslClientCert, agentCert.ClientCertificateFile);
                Variables.Set(Constants.Variables.Agent.SslClientCertKey, agentCert.ClientCertificatePrivateKeyFile);
                Variables.Set(Constants.Variables.Agent.SslClientCertArchive, agentCert.ClientCertificateArchiveFile);

                if (!string.IsNullOrEmpty(agentCert.ClientCertificatePassword))
                {
                    Variables.Set(Constants.Variables.Agent.SslClientCertPassword, agentCert.ClientCertificatePassword, true);
                }
            }

            // Job timeline record.
            InitializeTimelineRecord(
                timelineId: message.Timeline.Id,
                timelineRecordId: message.JobId,
                parentTimelineRecordId: null,
                recordType: ExecutionContextType.Job,
                displayName: message.JobDisplayName,
                refName: message.JobName,
                order: null); // The job timeline record's order is set by server.

            // Logger (must be initialized before writing warnings).
            _logger = HostContext.CreateService <IPagingLogger>();
            _logger.Setup(_mainTimelineId, _record.Id);

            // Log warnings from recursive variable expansion.
            warnings?.ForEach(x => this.Warning(x));

            // Verbosity (from system.debug).
            WriteDebug = Variables.System_Debug ?? false;

            // Hook up JobServerQueueThrottling event, we will log warning on server tarpit.
            _jobServerQueue.JobServerQueueThrottling += JobServerQueueThrottling_EventReceived;
        }
        public static Pipelines.AgentJobRequestMessage ScrubPiiData(Pipelines.AgentJobRequestMessage message)
        {
            ArgUtil.NotNull(message, nameof(message));

            var scrubbedVariables = new Dictionary <string, VariableValue>();

            // Scrub the known PII variables
            foreach (var variable in message.Variables)
            {
                if (Variables.PiiVariables.Contains(variable.Key) ||
                    (variable.Key.StartsWith(Variables.PiiArtifactVariablePrefix, StringComparison.OrdinalIgnoreCase) &&
                     Variables.PiiArtifactVariableSuffixes.Any(varSuffix => variable.Key.EndsWith(varSuffix, StringComparison.OrdinalIgnoreCase))))
                {
                    scrubbedVariables[variable.Key] = "[PII]";
                }
                else
                {
                    scrubbedVariables[variable.Key] = variable.Value;
                }
            }

            var scrubbedRepositories = new List <Pipelines.RepositoryResource>();

            // Scrub the repository resources
            foreach (var repository in message.Resources.Repositories)
            {
                Pipelines.RepositoryResource scrubbedRepository = repository.Clone();

                var versionInfo = repository.Properties.Get <Pipelines.VersionInfo>(Pipelines.RepositoryPropertyNames.VersionInfo);

                if (versionInfo != null)
                {
                    scrubbedRepository.Properties.Set(
                        Pipelines.RepositoryPropertyNames.VersionInfo,
                        new Pipelines.VersionInfo()
                    {
                        Author = "[PII]"
                    });
                }

                scrubbedRepositories.Add(scrubbedRepository);
            }

            var scrubbedJobResources = new Pipelines.JobResources();

            scrubbedJobResources.Containers.AddRange(message.Resources.Containers);
            scrubbedJobResources.Endpoints.AddRange(message.Resources.Endpoints);
            scrubbedJobResources.Repositories.AddRange(scrubbedRepositories);
            scrubbedJobResources.SecureFiles.AddRange(message.Resources.SecureFiles);

            // Reconstitute a new agent job request message from the scrubbed parts
            return(new Pipelines.AgentJobRequestMessage(
                       plan: message.Plan,
                       timeline: message.Timeline,
                       jobId: message.JobId,
                       jobDisplayName: message.JobDisplayName,
                       jobName: message.JobName,
                       jobContainer: message.JobContainer,
                       jobSidecarContainers: message.JobSidecarContainers,
                       variables: scrubbedVariables,
                       maskHints: message.MaskHints,
                       jobResources: scrubbedJobResources,
                       workspaceOptions: message.Workspace,
                       steps: message.Steps));
        }
        private TestHostContext CreateTestContext([CallerMemberName] String testName = "")
        {
            var hc = new TestHostContext(this, testName);

            _jobEc                = new Agent.Worker.ExecutionContext();
            _config               = new Mock <IConfigurationStore>();
            _extensions           = new Mock <IExtensionManager>();
            _jobExtension         = new Mock <IJobExtension>();
            _jobServer            = new Mock <IJobServer>();
            _jobServerQueue       = new Mock <IJobServerQueue>();
            _proxyConfig          = new Mock <IVstsAgentWebProxy>();
            _cert                 = new Mock <IAgentCertificateManager>();
            _taskServer           = new Mock <ITaskServer>();
            _stepRunner           = new Mock <IStepsRunner>();
            _logger               = new Mock <IPagingLogger>();
            _temp                 = new Mock <ITempDirectoryManager>();
            _diagnosticLogManager = new Mock <IDiagnosticLogManager>();

            if (_tokenSource != null)
            {
                _tokenSource.Dispose();
                _tokenSource = null;
            }

            _tokenSource = new CancellationTokenSource();
            var expressionManager = new ExpressionManager();

            expressionManager.Initialize(hc);
            hc.SetSingleton <IExpressionManager>(expressionManager);

            _jobRunner = new JobRunner();
            _jobRunner.Initialize(hc);

            TaskOrchestrationPlanReference plan = new TaskOrchestrationPlanReference();
            TimelineReference timeline          = new Timeline(Guid.NewGuid());
            JobEnvironment    environment       = new JobEnvironment();

            environment.Variables[Constants.Variables.System.Culture] = "en-US";
            environment.SystemConnection = new ServiceEndpoint()
            {
                Name          = WellKnownServiceEndpointNames.SystemVssConnection,
                Url           = new Uri("https://test.visualstudio.com"),
                Authorization = new EndpointAuthorization()
                {
                    Scheme = "Test",
                }
            };
            environment.SystemConnection.Authorization.Parameters["AccessToken"] = "token";

            List <TaskInstance> tasks = new List <TaskInstance>();
            Guid JobId = Guid.NewGuid();

            _message = Pipelines.AgentJobRequestMessageUtil.Convert(new AgentJobRequestMessage(plan, timeline, JobId, testName, testName, environment, tasks));

            _extensions.Setup(x => x.GetExtensions <IJobExtension>()).
            Returns(new[] { _jobExtension.Object }.ToList());

            _initResult.Clear();

            _jobExtension.Setup(x => x.InitializeJob(It.IsAny <IExecutionContext>(), It.IsAny <Pipelines.AgentJobRequestMessage>())).
            Returns(Task.FromResult(_initResult));
            _jobExtension.Setup(x => x.HostType)
            .Returns <string>(null);

            _proxyConfig.Setup(x => x.ProxyAddress)
            .Returns(string.Empty);

            var settings = new AgentSettings
            {
                AgentId    = 1,
                AgentName  = "agent1",
                ServerUrl  = "https://test.visualstudio.com",
                WorkFolder = "_work",
            };

            _config.Setup(x => x.GetSettings())
            .Returns(settings);

            _logger.Setup(x => x.Setup(It.IsAny <Guid>(), It.IsAny <Guid>()));

            hc.SetSingleton(_config.Object);
            hc.SetSingleton(_jobServer.Object);
            hc.SetSingleton(_jobServerQueue.Object);
            hc.SetSingleton(_proxyConfig.Object);
            hc.SetSingleton(_cert.Object);
            hc.SetSingleton(_taskServer.Object);
            hc.SetSingleton(_stepRunner.Object);
            hc.SetSingleton(_extensions.Object);
            hc.SetSingleton(_temp.Object);
            hc.SetSingleton(_diagnosticLogManager.Object);
            hc.EnqueueInstance <IExecutionContext>(_jobEc);
            hc.EnqueueInstance <IPagingLogger>(_logger.Object);
            return(hc);
        }