public static async Task BuildEvent( [QueueTrigger(AzureConstants.QueueNames.BuildEvent)] string message, [Queue(AzureConstants.QueueNames.ProcessBuild)] CloudQueue processBuildQueue, [Table(AzureConstants.TableNames.UnprocessedBuild)] CloudTable unprocessedBuildTable, TextWriter logger, CancellationToken cancellationToken) { var messageJson = (BuildEventMessageJson)JsonConvert.DeserializeObject(message, typeof(BuildEventMessageJson)); // First make sure that we note this value in the unprocessed table as it has not yet // been processed. var entity = new UnprocessedBuildEntity(messageJson.BoundBuildId); var operation = TableOperation.InsertOrReplace(entity); await unprocessedBuildTable.ExecuteAsync(TableOperation.InsertOrReplace(entity)); // If this is a finalized event then the build is ready. Go ahead and process it now. if (messageJson.Phase == "FINALIZED") { logger.WriteLine($"Queue event to process build {messageJson.BuildId}"); await StateUtil.EnqueueProcessBuild(processBuildQueue, messageJson.JenkinsHostName, messageJson.BuildId); } }
private async Task UpdateEntity(UnprocessedBuildEntity entity, CloudQueue processBuildQueue, CancellationToken cancellationToken) { var jenkinsUri = entity.BoundBuildId.HostUri; var buildId = entity.BuildId; var client = CreateJenkinsClient(jenkinsUri, entity.JobId); try { var buildInfo = await client.GetBuildInfoAsync(buildId); if (buildInfo.State == BuildState.Running) { _logger.WriteLine($"Build {buildId}: stil running"); if (string.IsNullOrEmpty(entity.StatusText)) { entity.StatusText = "Still running"; try { await _unprocessedBuildTable.ExecuteAsync(TableOperation.Replace(entity)); } catch { // Can be deleted in parallel. That's okay and should be ignored. } } } else { _logger.WriteLine($"Build {buildId}: sending for processing as it's completed"); await EnqueueProcessBuild(processBuildQueue, jenkinsUri.Host, buildId); } } catch (Exception e) { _logger.WriteLine($"Build {buildId}: error querying Jenkins: {e}"); } }
/// <summary> /// Populate the given build and update the unprocessed table accordingly. If there is no /// existing entity in the unprocessed table, this won't add one. It will only update existing /// ones. /// </summary> internal async Task Populate(BuildId buildId, BuildTablePopulator populator, bool force, CancellationToken cancellationToken) { var key = UnprocessedBuildEntity.GetEntityKey(buildId); try { // If we are not forcing the update then check for the existence of a completed run before // requerying Jenkins. if (force || !(await populator.IsPopulated(buildId))) { await populator.PopulateBuild(buildId); } await AzureUtil.MaybeDeleteAsync(_unprocessedBuildTable, key, cancellationToken); } catch (Exception e) { // Update the error state for the row. var entity = await AzureUtil.QueryAsync <UnprocessedBuildEntity>(_unprocessedBuildTable, key, cancellationToken); if (entity != null) { entity.StatusText = $"{e.Message} - {e.StackTrace.Take(1000)}"; var operation = TableOperation.Replace(entity); try { await _unprocessedBuildTable.ExecuteAsync(operation); } catch { // It's possible the enity was deleted / updated in parallel. That's okay. This table // is meant as an approximation of the build state and always moving towards complete. } } } }