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();
            })));
        }
Example #2
0
        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);
            }
        }
Example #11
0
        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
            });
        }