Ejemplo n.º 1
0
        private IJobDefinition AddBuildVariableProperty(IJobDefinition def, string key, string azdoVariableName)
        {
            string envName = FromAzdoVariableNameToEnvironmentVariableName(azdoVariableName);

            var value = Environment.GetEnvironmentVariable(envName);

            if (string.IsNullOrEmpty(value))
            {
                return(def);
            }

            def.WithProperty(key, value);
            Log.LogMessage($"Added build variable property '{key}' (value: '{value}') to job definition.");
            return(def);
        }
Ejemplo n.º 2
0
        private IJobDefinition AddProperty(IJobDefinition def, ITaskItem property)
        {
            if (!property.GetRequiredMetadata(Log, "Identity", out string key))
            {
                return(def);
            }

            if (!property.GetRequiredMetadata(Log, "Value", out string value))
            {
                return(def);
            }

            def.WithProperty(key, value);
            Log.LogMessage($"Added property '{key}' (value: '{value}') to job definition.");
            return(def);
        }
        public async Task <AgentInfoItem> CreateJob(
            CancellationToken cancellationToken,
            Dictionary <string, string> properties)
        {
            string   credentialsPath   = null;
            string   agentSettingsPath = null;
            ISentJob sentJob           = null;

            try
            {
                _logger.LogInformation($"Creating payloads for agent id {_agentRequestItem.agentId}");

                credentialsPath   = CreateAgentCredentialsPayload();
                agentSettingsPath = CreateAgentSettingsPayload();

                // Now that we have a valid queue, construct the Helix job on that queue

                // Notes: if we timeout, it's going to be in the subsequent call.
                // SendAsync() causes both the storage account container creation / uploads and sends to Helix API, which both can stall.
                // We have to do this this way (and non-ideal workarounds like trying to destroy the object won't likely solve this) because today
                // the job, if started, will still contain valid Azure DevOps tokens to be a build agent and we can't easily guarantee it doesn't get sent.
                // ********************************
                // The right long-term fix is for Azure DevOps to not have the tokens passed be usable until they've received an "accepted = true" response from our provider.
                // ********************************
                cancellationToken.ThrowIfCancellationRequested();
                IJobDefinition preparedJob = _api.Job.Define()
                                             .WithType($"byoc/{_configuration.HelixCreator}/")
                                             .WithTargetQueue(_queueInfo.QueueId);

                if (properties != null)
                {
                    foreach ((string key, string value) in properties)
                    {
                        preparedJob.WithProperty(key, value);
                    }
                }

                preparedJob = preparedJob.WithContainerName(_configuration.ContainerName)
                              .WithCorrelationPayloadUris(AgentPayloadUri)
                              .WithSource($"agent/{_agentRequestItem.accountId}/{_orchestrationId}/{_jobName}/");

                IWorkItemDefinition workitem = preparedJob.DefineWorkItem(_agentRequestItem.agentId)
                                               .WithCommand(ConstructCommand())
                                               .WithFiles(credentialsPath, agentSettingsPath, StartupScriptPath)
                                               .WithTimeout(TimeSpan.FromMinutes(_configuration.TimeoutInMinutes));

                preparedJob = workitem.AttachToJob();

                sentJob = await preparedJob.SendAsync(l => _logger.LogInformation(l), cancellationToken);

                _logger.LogInformation($"Successfully submitted new Helix job {sentJob.CorrelationId} (Agent id {_agentRequestItem.agentId}) to queue { _queueInfo.QueueId}");

                // In case the cancellation token got signalled between above and here, let's try to cancel the Helix Job.
                cancellationToken.ThrowIfCancellationRequested();

                return(new AgentInfoItem
                {
                    accepted = true,
                    agentData = new AgentDataItem
                    {
                        correlationId = sentJob.CorrelationId,
                        queueId = _queueInfo.QueueId,
                        workItemId = _agentRequestItem.agentId,
                        isPublicQueue = !_queueInfo.IsInternalOnly.GetValueOrDefault(true)
                    }
                });
            }
            catch (HttpOperationException e)
            {
                _logger.LogError(e, $"Failed to submit new Helix job to queue {_queueInfo.QueueId} for agent id {_agentRequestItem.agentId}: {e.Response.Content}");

                return(new AgentInfoItem()
                {
                    accepted = false
                });
            }
            catch (OperationCanceledException ranOutOfTime) when(ranOutOfTime.CancellationToken == cancellationToken)
            {
                _logger.LogError($"Unable to complete request to create Helix job in specified timeout, attempting to cancel it.");
                if (sentJob != null && !string.IsNullOrEmpty(sentJob.CorrelationId))
                {
                    await _api.Job.CancelAsync(sentJob.CorrelationId);

                    _logger.LogError($"Possible race condition: cancelled Helix Job '{sentJob.CorrelationId}' may still run.");
                }
                return(new AgentInfoItem()
                {
                    accepted = false
                });
            }
            catch (Exception e)
            {
                _logger.LogError(e, $"Failed to submit new Helix job to queue {_queueInfo.QueueId} for agent id {_agentRequestItem.agentId}");
                return(new AgentInfoItem()
                {
                    accepted = false
                });
            }
            finally
            {
                if (credentialsPath != null)
                {
                    // Delete the temporary files containing the credentials and agent config
                    System.IO.File.Delete(credentialsPath);
                }
                if (agentSettingsPath != null)
                {
                    System.IO.File.Delete(agentSettingsPath);
                }
            }
        }