private async Task <ImageResultInfo> ReportImageStatusWithContinuationAsync(IMcrStatusClient statusClient, DigestInfo digestInfo) { return(await(await ReportImageStatusAsync(statusClient, digestInfo) .ContinueWith(async task => { if (task.IsCompletedSuccessfully) { if (task.Result.DigestInfo.RemainingTags.Any()) { await Task.Delay(Options.RequeryDelay); return await ReportImageStatusWithContinuationAsync(statusClient, digestInfo); } else { return task.Result; } } else if (task.Exception is not null) { throw task.Exception; } throw new NotSupportedException(); }))); }
private static IMcrStatusClientFactory CreateMcrStatusClientFactory( string tenant, string clientId, string clientSecret, IMcrStatusClient statusClient) { Mock <IMcrStatusClientFactory> mock = new Mock <IMcrStatusClientFactory>(); mock .Setup(o => o.Create(tenant, clientId, clientSecret)) .Returns(statusClient); return(mock.Object); }
private async Task <CommitResult> WaitForIngestionAsync(IMcrStatusClient statusClient) { CommitResult commitResult = null; DateTime startTime = DateTime.Now; bool isComplete = false; while (!isComplete) { commitResult = await statusClient.GetCommitResultAsync(Options.CommitDigest); foreach (CommitStatus commitStatus in commitResult.Value) { _loggerService.WriteMessage( $"Readme status results for commit digest '{Options.CommitDigest}' with request ID '{commitStatus.OnboardingRequestId}': {commitStatus.OverallStatus}"); switch (commitStatus.OverallStatus) { case StageStatus.Processing: case StageStatus.NotStarted: await Task.Delay(Options.RequeryDelay); break; case StageStatus.Failed: _loggerService.WriteError(await GetFailureResultsAsync(statusClient, commitStatus)); break; case StageStatus.Succeeded: isComplete = true; break; case StageStatus.NotApplicable: default: throw new NotSupportedException( $"Unexpected status for commit digest '{Options.CommitDigest}' with request ID '{commitStatus.OnboardingRequestId}: {commitStatus.OverallStatus}"); } } if (commitResult.Value.All(status => status.OverallStatus == StageStatus.Failed)) { _loggerService.WriteError("Doc ingestion failed."); _environmentService.Exit(1); } if (DateTime.Now - startTime >= Options.WaitTimeout) { throw new TimeoutException($"Timed out after '{Options.WaitTimeout}' waiting for the docs to be ingested."); } } return(commitResult); }
private async Task <string> GetFailedStatusAsync(IMcrStatusClient statusClient, string digest, ImageStatus imageStatus) { ImageResultDetailed result = await statusClient.GetImageResultDetailedAsync(digest, imageStatus.OnboardingRequestId); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine($"Failure for '{GetQualifiedDigest(imageStatus.TargetRepository, digest)}':"); stringBuilder.AppendLine($"\tID: {imageStatus.OnboardingRequestId}"); stringBuilder.AppendLine($"\tTag: {imageStatus.Tag}"); stringBuilder.AppendLine($"\tCommit digest: {result.CommitDigest}"); stringBuilder.Append(result.Substatus.ToString("\t")); return(stringBuilder.ToString()); }
private async Task <string> GetFailureResultsAsync(IMcrStatusClient statusClient, CommitStatus commitStatus) { CommitResultDetailed result = await statusClient.GetCommitResultDetailedAsync(Options.CommitDigest, commitStatus.OnboardingRequestId); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine($"Failure for commit digest '{Options.CommitDigest}':"); stringBuilder.AppendLine($"\tID: {result.OnboardingRequestId}"); stringBuilder.AppendLine($"\tBranch: {result.Branch}"); result.ContentFiles.ForEach(file => stringBuilder.AppendLine($"\t\t{file}")); stringBuilder.Append(result.Substatus.ToString("\t")); return(stringBuilder.ToString()); }
public override async Task ExecuteAsync() { _loggerService.WriteHeading("QUERYING COMMIT RESULT"); if (!Options.IsDryRun) { IMcrStatusClient statusClient = _mcrStatusClientFactory.Create( Options.ServicePrincipal.Tenant, Options.ServicePrincipal.ClientId, Options.ServicePrincipal.Secret); CommitResult result = await WaitForIngestionAsync(statusClient); LogSuccessfulResults(result); } _loggerService.WriteMessage(); _loggerService.WriteMessage("Doc ingestion successfully completed!"); }
public override async Task ExecuteAsync() { _loggerService.WriteHeading("WAITING FOR IMAGE INGESTION"); if (!Options.IsDryRun) { IMcrStatusClient statusClient = _mcrStatusClientFactory.Create( Options.ServicePrincipal.Tenant, Options.ServicePrincipal.ClientId, Options.ServicePrincipal.Secret); IEnumerable <ImageResultInfo> imageResultInfos = await WaitForImageIngestionAsync(statusClient); _loggerService.WriteMessage(); await LogResults(statusClient, imageResultInfos); } _loggerService.WriteMessage("Image ingestion complete!"); }
private async Task <ImageResultInfo> ReportImageStatusAsync(IMcrStatusClient statusClient, DigestInfo digestInfo) { string qualifiedDigest = GetQualifiedDigest(digestInfo.Repo, digestInfo.Digest); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine($"Querying image status for '{qualifiedDigest}'"); stringBuilder.AppendLine("Remaining tags:"); digestInfo.RemainingTags.ForEach(tag => stringBuilder.AppendLine(tag)); _loggerService.WriteMessage(stringBuilder.ToString()); ImageResult imageResult = await statusClient.GetImageResultAsync(digestInfo.Digest); IEnumerable <ImageStatus> imageStatuses = imageResult.Value .Where(status => ShouldProcessImageStatus(status, digestInfo)); if (imageStatuses.Any()) { stringBuilder = new StringBuilder(); stringBuilder.AppendLine(); stringBuilder.AppendLine($"Image status results for '{qualifiedDigest}':"); IEnumerable <IGrouping <string, ImageStatus> > statusesByTag = imageStatuses.GroupBy(status => status.Tag); foreach (IGrouping <string, ImageStatus> tagImageStatuses in statusesByTag) { foreach (ImageStatus imageStatus in tagImageStatuses) { stringBuilder.AppendLine( $"Status for tag '{imageStatus.Tag}' with request ID '{imageStatus.OnboardingRequestId}': {imageStatus.OverallStatus}"); switch (imageStatus.OverallStatus) { case StageStatus.Processing: case StageStatus.NotStarted: case StageStatus.Failed: break; case StageStatus.Succeeded: // If we've found at least one successful overall status for the tag, we're done with that tag. digestInfo.RemainingTags.Remove(imageStatus.Tag); break; case StageStatus.NotApplicable: default: throw new NotSupportedException( $"Unexpected image status for digest '{qualifiedDigest}' with tag '{imageStatus.Tag}' and request ID '{imageStatus.OnboardingRequestId}': {imageStatus.OverallStatus}"); } } // If all found statuses for a given tag have failed, we're done with that tag. if (tagImageStatuses.All(status => status.OverallStatus == StageStatus.Failed)) { digestInfo.RemainingTags.Remove(tagImageStatuses.Key); } } _loggerService.WriteMessage(stringBuilder.ToString()); } return(new ImageResultInfo(imageResult, digestInfo)); }
private async Task <IEnumerable <ImageResultInfo> > WaitForImageIngestionAsync(IMcrStatusClient statusClient) { ImageArtifactDetails imageArtifactDetails = ImageInfoHelper.LoadFromFile(Options.ImageInfoPath, Manifest); List <Task <ImageResultInfo> > tasks = GetImageDigestInfos(imageArtifactDetails) .Select(digestInfo => ReportImageStatusWithContinuationAsync(statusClient, digestInfo)) .ToList(); return(await TaskHelper.WhenAll(tasks, Options.WaitTimeout)); }
private async Task LogResults(IMcrStatusClient statusClient, IEnumerable <ImageResultInfo> imageResultInfos) { _loggerService.WriteHeading("IMAGE RESULTS"); List <Task <string> > failedStatusTasks = new List <Task <string> >(); IEnumerable <ImageResultInfo> failedResults = imageResultInfos // Find any result where all of the statuses of a given tag have failed .Where(result => result.ImageResult.Value .Where(status => ShouldProcessImageStatus(status, result.DigestInfo)) .GroupBy(status => status.Tag) .Any(statusGroup => statusGroup.All(status => status.OverallStatus == StageStatus.Failed))) .OrderBy(result => result.DigestInfo.Repo) .ThenBy(result => result.DigestInfo.Digest); IEnumerable <ImageResultInfo> successfulResults = imageResultInfos.Except(failedResults); foreach (ImageResultInfo result in failedResults) { IEnumerable <ImageStatus> failedStatuses = result.ImageResult.Value.Where(status => status.OverallStatus == StageStatus.Failed); List <Task <string> > resultFailedStatusTasks = failedStatuses .Select(status => ( result.DigestInfo.Digest, Status: status )) .OrderBy(statusInfo => statusInfo.Status.Tag) .Select(statusInfo => GetFailedStatusAsync(statusClient, result.DigestInfo.Digest, statusInfo.Status)) .ToList(); failedStatusTasks.AddRange(resultFailedStatusTasks); } if (failedStatusTasks.Any()) { _loggerService.WriteMessage(); _loggerService.WriteMessage("Querying details of failed results..."); _loggerService.WriteMessage(); await Task.WhenAll(failedStatusTasks); } if (successfulResults.Any()) { _loggerService.WriteSubheading("Successful results"); foreach (ImageResultInfo imageResult in successfulResults) { _loggerService.WriteMessage(GetQualifiedDigest(imageResult.DigestInfo.Repo, imageResult.DigestInfo.Digest)); string tags = string.Join(", ", imageResult.ImageResult.Value .Where(imageStatus => imageStatus.OverallStatus == StageStatus.Succeeded) .Select(imageStatus => imageStatus.Tag)); _loggerService.WriteMessage($"\tTags: {tags}"); _loggerService.WriteMessage(); } } if (failedStatusTasks.Any()) { _loggerService.WriteSubheading("Failed results"); foreach (Task <string> failedStatusTask in failedStatusTasks) { _loggerService.WriteError(failedStatusTask.Result); _loggerService.WriteMessage(); } _environmentService.Exit(1); } }
private async Task <ImageResultInfo> ReportImageStatusAsync(IMcrStatusClient statusClient, DigestInfo digestInfo) { string qualifiedDigest = GetQualifiedDigest(digestInfo.Repo.Repo, digestInfo.Digest); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine($"Querying image status for '{qualifiedDigest}'"); stringBuilder.AppendLine("Remaining tags:"); digestInfo.RemainingTags.ForEach(tag => stringBuilder.AppendLine(tag)); this.loggerService.WriteMessage(stringBuilder.ToString()); ImageResult imageResult = await statusClient.GetImageResultAsync(digestInfo.Digest); // Find the image statuses that are associated with the repo indicated in the image info. This filter is needed // because MCR's webhook responds to all image pushes in the ACR, even those to staging locations. A queue time filter // is needed in order to filter out onboarding requests from a previous ingestion of the same digests. IEnumerable <ImageStatus> imageStatuses = imageResult.Value .Where(status => status.TargetRepository == digestInfo.Repo.Repo && status.QueueTime >= Options.MinimumQueueTime); if (imageStatuses.Any()) { stringBuilder = new StringBuilder(); stringBuilder.AppendLine(); stringBuilder.AppendLine($"Image status results for '{qualifiedDigest}':"); var statusesByTag = imageStatuses.GroupBy(status => status.Tag); foreach (IGrouping <string, ImageStatus> tagImageStatuses in statusesByTag) { foreach (ImageStatus imageStatus in tagImageStatuses) { stringBuilder.AppendLine( $"Status for tag '{imageStatus.Tag}' with request ID '{imageStatus.OnboardingRequestId}': {imageStatus.OverallStatus}"); switch (imageStatus.OverallStatus) { case StageStatus.Processing: case StageStatus.NotStarted: case StageStatus.Failed: break; case StageStatus.Succeeded: // If we've found at least one successful overall status for the tag, we're done with that tag. digestInfo.RemainingTags.Remove(imageStatus.Tag); break; case StageStatus.NotApplicable: default: throw new NotSupportedException( $"Unexpected image status for digest '{qualifiedDigest}' with tag '{imageStatus.Tag}' and request ID '{imageStatus.OnboardingRequestId}': {imageStatus.OverallStatus}"); } } // If all found statuses for a given tag have failed, we're done with that tag. if (tagImageStatuses.All(status => status.OverallStatus == StageStatus.Failed)) { digestInfo.RemainingTags.Remove(tagImageStatuses.Key); } } this.loggerService.WriteMessage(stringBuilder.ToString()); } return(new ImageResultInfo { ImageResult = imageResult, DigestInfo = digestInfo }); }