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 void RemoveOutOfDateContent(ImageArtifactDetails imageArtifactDetails) { for (int repoIndex = imageArtifactDetails.Repos.Count - 1; repoIndex >= 0; repoIndex--) { RepoData repoData = imageArtifactDetails.Repos[repoIndex]; // Since the registry name is not represented in the image info, make sure to compare the repo name with the // manifest's repo model name which isn't registry-qualified. RepoInfo?manifestRepo = Manifest.AllRepos.FirstOrDefault(manifestRepo => manifestRepo.Name == repoData.Repo); // If there doesn't exist a matching repo in the manifest, remove it from the image info if (manifestRepo is null) { imageArtifactDetails.Repos.Remove(repoData); continue; } for (int imageIndex = repoData.Images.Count - 1; imageIndex >= 0; imageIndex--) { ImageData imageData = repoData.Images[imageIndex]; ImageInfo manifestImage = imageData.ManifestImage; // If there doesn't exist a matching image in the manifest, remove it from the image info if (manifestImage is null) { repoData.Images.Remove(imageData); continue; } for (int platformIndex = imageData.Platforms.Count - 1; platformIndex >= 0; platformIndex--) { PlatformData platformData = imageData.Platforms[platformIndex]; PlatformInfo?manifestPlatform = manifestImage.AllPlatforms .FirstOrDefault(manifestPlatform => platformData.PlatformInfo == manifestPlatform); // If there doesn't exist a matching platform in the manifest, remove it from the image info if (manifestPlatform is null) { imageData.Platforms.Remove(platformData); } } } } if (imageArtifactDetails.Repos.Count == 0) { // Failsafe to prevent wiping out the image info due to a bug in the logic throw new InvalidOperationException( "Removal of out-of-date content resulted in there being no content remaining in the target image info file. Something is probably wrong with the logic."); } }
/// <summary> /// Loads image info string content as a parsed model. /// </summary> /// <param name="imageInfoContent">The image info content to load.</param> /// <param name="manifest">Representation of the manifest model.</param> /// <param name="skipManifestValidation"> /// Whether to skip validation if no associated manifest model item was found for a given image info model item. /// </param> /// <param name="useFilteredManifest">Whether to use the filtered content of the manifest for lookups.</param> public static ImageArtifactDetails LoadFromContent(string imageInfoContent, ManifestInfo manifest, bool skipManifestValidation = false, bool useFilteredManifest = false) { ImageArtifactDetails imageArtifactDetails = JsonConvert.DeserializeObject <ImageArtifactDetails>(imageInfoContent); foreach (RepoData repoData in imageArtifactDetails.Repos) { RepoInfo manifestRepo = (useFilteredManifest ? manifest.FilteredRepos : manifest.AllRepos) .FirstOrDefault(repo => repo.Name == repoData.Repo); if (manifestRepo == null) { Console.WriteLine($"Image info repo not loaded: {repoData.Repo}"); continue; } foreach (ImageData imageData in repoData.Images) { imageData.ManifestRepo = manifestRepo; foreach (PlatformData platformData in imageData.Platforms) { foreach (ImageInfo manifestImage in (useFilteredManifest ? manifestRepo.FilteredImages : manifestRepo.AllImages)) { PlatformInfo matchingManifestPlatform = (useFilteredManifest ? manifestImage.FilteredPlatforms : manifestImage.AllPlatforms) .FirstOrDefault(platform => ArePlatformsEqual(platformData, imageData, platform, manifestImage)); if (matchingManifestPlatform != null) { if (imageData.ManifestImage is null) { imageData.ManifestImage = manifestImage; } platformData.PlatformInfo = matchingManifestPlatform; platformData.ImageInfo = manifestImage; break; } } } PlatformData representativePlatform = imageData.Platforms.FirstOrDefault(); if (!skipManifestValidation && imageData.ManifestImage == null && representativePlatform != null) { throw new InvalidOperationException( $"Unable to find matching platform in manifest for platform '{representativePlatform.GetIdentifier()}'."); } } } return(imageArtifactDetails); }
public async Task BuildCommand_ImageInfoOutput_CustomDockerfile() { using (TempFolderContext tempFolderContext = TestHelper.UseTempFolder()) { Mock <IDockerService> dockerServiceMock = new Mock <IDockerService>(); dockerServiceMock .SetupGet(o => o.Architecture) .Returns(Architecture.AMD64); const string digest = "runtime@sha256:c74364a9f125ca612f9a67e4a0551937b7a37c82fabb46172c4867b73edd638c"; dockerServiceMock .Setup(o => o.GetImageDigest("runtime:runtime", false)) .Returns(digest); const string runtimeRelativeDir = "1.0/runtime/os"; Directory.CreateDirectory(Path.Combine(tempFolderContext.Path, runtimeRelativeDir)); string dockerfileRelativePath = Path.Combine(runtimeRelativeDir, "Dockerfile.custom"); File.WriteAllText(Path.Combine(tempFolderContext.Path, dockerfileRelativePath), "FROM repo:tag"); const string dockerfileCommitSha = "mycommit"; Mock <IGitService> gitServiceMock = new Mock <IGitService>(); gitServiceMock .Setup(o => o.GetCommitSha(dockerfileRelativePath, It.IsAny <bool>())) .Returns(dockerfileCommitSha); BuildCommand command = new BuildCommand(dockerServiceMock.Object, Mock.Of <ILoggerService>(), Mock.Of <IEnvironmentService>(), gitServiceMock.Object); command.Options.Manifest = Path.Combine(tempFolderContext.Path, "manifest.json"); command.Options.ImageInfoOutputPath = Path.Combine(tempFolderContext.Path, "image-info.json"); command.Options.SourceRepoUrl = "https://source"; Manifest manifest = CreateManifest( CreateRepo("runtime", CreateImage( CreatePlatform(dockerfileRelativePath, new string[] { "runtime" }))) ); File.WriteAllText(Path.Combine(tempFolderContext.Path, command.Options.Manifest), JsonConvert.SerializeObject(manifest)); command.LoadManifest(); await command.ExecuteAsync(); ImageArtifactDetails imageArtifactDetails = JsonConvert.DeserializeObject <ImageArtifactDetails>( File.ReadAllText(command.Options.ImageInfoOutputPath)); Assert.Equal( PathHelper.NormalizePath(dockerfileRelativePath), imageArtifactDetails.Repos[0].Images.First().Platforms.First().Dockerfile); } }
public override async Task ExecuteAsync() { _loggerService.WriteHeading("TRIMMING UNCHANGED PLATFORMS"); string imageInfoContents = await File.ReadAllTextAsync(Options.ImageInfoPath); ImageArtifactDetails imageArtifactDetails = JsonConvert.DeserializeObject <ImageArtifactDetails>(imageInfoContents); RemoveUnchangedPlatforms(imageArtifactDetails); imageInfoContents = JsonHelper.SerializeObject(imageArtifactDetails); if (!Options.IsDryRun) { await File.WriteAllTextAsync(Options.ImageInfoPath, imageInfoContents); } }
public override Task ExecuteAsync() { IEnumerable <(string Tag, string Platform)> platformTags; if (string.IsNullOrEmpty(Options.ImageInfoPath)) { platformTags = Manifest.GetFilteredPlatforms() .Where(platform => platform.Tags.Any()) .Select(platform => (platform.Tags.First().FullyQualifiedName, platform.PlatformLabel)); } else { // We want to apply manifest filtering to the loading of the image info file. This allows, for example, // only images of a specific architecture to be pulled. ImageArtifactDetails imageArtifactDetails = ImageInfoHelper.LoadFromFile( Options.ImageInfoPath, Manifest, skipManifestValidation: true, useFilteredManifest: true); platformTags = imageArtifactDetails.Repos .SelectMany(repo => repo.Images) .SelectMany(image => image.Platforms) // If the platform doesn't have an associated manifest instance, it means the manifest filter // options had filtered out the platform. In that case, it doesn't apply and shouldn't be pulled. .Where(platform => platform.PlatformInfo is not null && platform.SimpleTags.Any()) .Select(platform => ( TagInfo.GetFullyQualifiedName(platform.PlatformInfo !.FullRepoModelName, platform.SimpleTags.First()), platform.PlatformInfo !.PlatformLabel)); } platformTags = platformTags .Distinct() .ToList(); _loggerService.WriteHeading("PULLING IMAGES"); foreach ((string tag, string platform) in platformTags) { _dockerService.PullImage(tag, platform, Options.IsDryRun); } if (Options.OutputVariableName is not null) { _loggerService.WriteMessage( PipelineHelper.FormatOutputVariable( Options.OutputVariableName, string.Join(',', platformTags.Select(platformTag => platformTag.Tag)))); } return(Task.CompletedTask); }
private async Task <IEnumerable <string> > GetPathsToRebuildAsync(Subscription subscription) { // If the command is filtered with an OS type that does not match the OsType filter of the subscription, // then there are no images that need to be inspected. string osTypeRegexPattern = ManifestFilter.GetFilterRegexPattern(Options.FilterOptions.OsType); if (!string.IsNullOrEmpty(subscription.OsType) && !Regex.IsMatch(subscription.OsType, osTypeRegexPattern, RegexOptions.IgnoreCase)) { return(Enumerable.Empty <string>()); } _loggerService.WriteMessage($"Processing subscription: {subscription.Id}"); string repoPath = await GetGitRepoPath(subscription); TempManifestOptions manifestOptions = new TempManifestOptions(Options.FilterOptions) { Manifest = Path.Combine(repoPath, subscription.Manifest.Path) }; ManifestInfo manifest = ManifestInfo.Load(manifestOptions); ImageArtifactDetails imageArtifactDetails = await GetImageInfoForSubscriptionAsync(subscription, manifest); List <string> pathsToRebuild = new List <string>(); IEnumerable <PlatformInfo> allPlatforms = manifest.GetAllPlatforms().ToList(); foreach (RepoInfo repo in manifest.FilteredRepos) { IEnumerable <PlatformInfo> platforms = repo.FilteredImages .SelectMany(image => image.FilteredPlatforms) .Where(platform => !platform.IsInternalFromImage(platform.FinalStageFromImage)); RepoData repoData = imageArtifactDetails.Repos .FirstOrDefault(s => s.Repo == repo.Name); foreach (PlatformInfo platform in platforms) { pathsToRebuild.AddRange(GetPathsToRebuild(allPlatforms, platform, repoData)); } } return(pathsToRebuild.Distinct().ToList()); }
private async Task RunTestAsync(ImageArtifactDetails input, ImageArtifactDetails expectedOutput) { using TempFolderContext tempFolderContext = new TempFolderContext(); TrimUnchangedPlatformsCommand command = new TrimUnchangedPlatformsCommand(Mock.Of <ILoggerService>()); command.Options.ImageInfoPath = Path.Combine(tempFolderContext.Path, "imageinfo.json"); File.WriteAllText(command.Options.ImageInfoPath, JsonHelper.SerializeObject(input)); await command.ExecuteAsync(); string expectedContents = JsonHelper.SerializeObject(expectedOutput); string actualContents = File.ReadAllText(command.Options.ImageInfoPath); Assert.Equal(expectedContents, actualContents); }
private async Task ValidateExecuteAsync( TempFolderContext tempFolderContext, string manifestPath, ImageArtifactDetails srcImageArtifactDetails, string expectedImageData, string expectedLayerData) { string imageInfoPath = Path.Combine(tempFolderContext.Path, "image-info.json"); File.WriteAllText(imageInfoPath, JsonHelper.SerializeObject(srcImageArtifactDetails)); Dictionary <string, string> ingestedData = new(); Mock <IKustoClient> kustoClientMock = new(); kustoClientMock .Setup(o => o.IngestFromCsvAsync( It.IsAny <string>(), It.IsAny <ServicePrincipalOptions>(), It.IsAny <string>(), It.IsAny <string>(), It.IsAny <string>(), It.IsAny <bool>())) .Callback <string, ServicePrincipalOptions, string, string, string, bool>( (csv, servicePrincipal, cluster, database, table, isDryRun) => { ingestedData.Add(table, csv); }); IngestKustoImageInfoCommand command = new(Mock.Of <ILoggerService>(), kustoClientMock.Object); command.Options.ImageInfoPath = imageInfoPath; command.Options.Manifest = manifestPath; command.Options.ImageTable = "ImageInfo"; command.Options.LayerTable = "LayerInfo"; command.LoadManifest(); await command.ExecuteAsync(); _outputHelper.WriteLine($"Expected Image Data: {Environment.NewLine}{expectedImageData}"); _outputHelper.WriteLine($"Actual Image Data: {Environment.NewLine}{ingestedData[command.Options.ImageTable]}"); _outputHelper.WriteLine($"Expected Layer Data: {Environment.NewLine}{expectedLayerData}"); _outputHelper.WriteLine($"Actual Layer Data: {Environment.NewLine}{ingestedData[command.Options.LayerTable]}"); kustoClientMock.Verify(o => o.IngestFromCsvAsync( It.IsAny <string>(), It.IsAny <ServicePrincipalOptions>(), It.IsAny <string>(), It.IsAny <string>(), It.IsAny <string>(), It.IsAny <bool>())); Assert.Equal(expectedImageData, ingestedData[command.Options.ImageTable]); Assert.Equal(expectedLayerData, ingestedData[command.Options.LayerTable]); }
private string?GetUpdatedImageInfo(string repoPath) { ImageArtifactDetails srcImageArtifactDetails = ImageInfoHelper.LoadFromFile(Options.ImageInfoPath, Manifest); string repoImageInfoPath = Path.Combine(repoPath, Options.GitOptions.Path); string originalTargetImageInfoContents = File.ReadAllText(repoImageInfoPath); ImageArtifactDetails newImageArtifactDetails; if (originalTargetImageInfoContents != null) { ImageArtifactDetails targetImageArtifactDetails = ImageInfoHelper.LoadFromContent( originalTargetImageInfoContents, Manifest, skipManifestValidation: true); RemoveOutOfDateContent(targetImageArtifactDetails); ImageInfoMergeOptions options = new ImageInfoMergeOptions { IsPublish = true }; ImageInfoHelper.MergeImageArtifactDetails(srcImageArtifactDetails, targetImageArtifactDetails, options); newImageArtifactDetails = targetImageArtifactDetails; } else { // If there is no existing file to update, there's nothing to merge with so the source data // becomes the target data. newImageArtifactDetails = srcImageArtifactDetails; } string newTargetImageInfoContents = JsonHelper.SerializeObject(newImageArtifactDetails) + Environment.NewLine; if (originalTargetImageInfoContents != newTargetImageInfoContents) { return(newTargetImageInfoContents); } else { return(null); } }
private async Task SaveTagInfoToImageInfoFileAsync(DateTime createdDate, ImageArtifactDetails imageArtifactDetails) { _loggerService.WriteSubheading("SETTING TAG INFO"); IEnumerable <ImageData> images = imageArtifactDetails.Repos .SelectMany(repo => repo.Images) .Where(image => image.Manifest != null); await Parallel.ForEachAsync(images, async (image, cancellationToken) => { image.Manifest.Created = createdDate; TagInfo sharedTag = image.ManifestImage.SharedTags.First(); image.Manifest.Digest = DockerHelper.GetDigestString( image.ManifestRepo.FullModelName, await _manifestToolService.GetManifestDigestShaAsync( sharedTag.FullyQualifiedName, Options.CredentialsOptions, Options.IsDryRun)); IEnumerable <(string Repo, string Tag)> syndicatedRepresentativeSharedTags = image.ManifestImage.SharedTags .Where(tag => tag.SyndicatedRepo is not null) .GroupBy(tag => tag.SyndicatedRepo) .Select(group => (group.Key, group.First().SyndicatedDestinationTags.First())) .Cast <(string Repo, string Tag)>() .OrderBy(obj => obj.Repo) .ThenBy(obj => obj.Tag); foreach ((string Repo, string Tag)syndicatedSharedTag in syndicatedRepresentativeSharedTags) { string digest = DockerHelper.GetDigestString( DockerHelper.GetImageName(Manifest.Model.Registry, syndicatedSharedTag.Repo), await _manifestToolService.GetManifestDigestShaAsync( DockerHelper.GetImageName(Manifest.Registry, Options.RepoPrefix + syndicatedSharedTag.Repo, syndicatedSharedTag.Tag), Options.CredentialsOptions, Options.IsDryRun)); image.Manifest.SyndicatedDigests.Add(digest); } }); string imageInfoString = JsonHelper.SerializeObject(imageArtifactDetails); File.WriteAllText(Options.ImageInfoPath, imageInfoString); }
private void WriteImagesMarkdown(StringBuilder notificationMarkdown) { if (!File.Exists(Options.ImageInfoPath)) { return; } ImageArtifactDetails imageArtifactDetails = ImageInfoHelper.LoadFromFile(Options.ImageInfoPath, Manifest); IEnumerable <ImageData> images = imageArtifactDetails.Repos.SelectMany(repo => repo.Images); List <(string digestSha, IEnumerable <string> tags)> publishedImages = new(); foreach (ImageData image in images) { if (image.Manifest is not null) { string digestSha = DockerHelper.GetDigestSha(image.Manifest.Digest); IEnumerable <string> tags = GetTags(image.ManifestImage.SharedTags); publishedImages.Add((digestSha, tags)); } publishedImages.AddRange( image.Platforms .Where(platform => platform.PlatformInfo.Tags.Any()) .Select(platform => { string digestSha = DockerHelper.GetDigestSha(platform.Digest); IEnumerable <string> tags = GetTags(platform.PlatformInfo.Tags); return(digestSha, tags); })); } notificationMarkdown.AppendLine("## Images"); notificationMarkdown.AppendLine(); foreach ((string digestSha, IEnumerable <string> tags) in publishedImages.OrderBy(digestTags => digestTags.digestSha)) { notificationMarkdown.AppendLine($"* {digestSha}"); foreach (string tag in tags.OrderBy(tag => tag)) { notificationMarkdown.AppendLine($" * {tag}"); } } }
public override Task ExecuteAsync() { _loggerService.WriteHeading("GENERATING MANIFESTS"); ImageArtifactDetails imageArtifactDetails = ImageInfoHelper.LoadFromFile(Options.ImageInfoPath, Manifest); ExecuteWithUser(() => { IEnumerable <string> manifests = Manifest.FilteredRepos .SelectMany(repo => repo.FilteredImages .Where(image => image.SharedTags.Any()) .Select(image => (repo, image))) .SelectMany(repoImage => GenerateManifests(repoImage.repo, repoImage.image)) .ToList(); DateTime createdDate = DateTime.Now.ToUniversalTime(); Parallel.ForEach(manifests, manifest => { string manifestFilename = $"manifest.{Guid.NewGuid()}.yml"; _loggerService.WriteSubheading($"PUBLISHING MANIFEST: '{manifestFilename}'{Environment.NewLine}{manifest}"); File.WriteAllText(manifestFilename, manifest); try { _manifestToolService.PushFromSpec(manifestFilename, Options.IsDryRun); } finally { File.Delete(manifestFilename); } }); WriteManifestSummary(); SaveTagInfoToImageInfoFile(createdDate, imageArtifactDetails); }); return(Task.CompletedTask); }
public static ImageArtifactDetails LoadFromContent(string imageInfoContent, ManifestInfo manifest, bool skipManifestValidation = false) { ImageArtifactDetails imageArtifactDetails = JsonConvert.DeserializeObject <ImageArtifactDetails>(imageInfoContent); foreach (RepoData repoData in imageArtifactDetails.Repos) { RepoInfo manifestRepo = manifest.AllRepos.FirstOrDefault(repo => repo.Name == repoData.Repo); if (manifestRepo == null) { Console.WriteLine($"Image info repo not loaded: {repoData.Repo}"); continue; } foreach (ImageData imageData in repoData.Images) { PlatformData platformData = imageData.Platforms.FirstOrDefault(); if (platformData != null) { foreach (ImageInfo manifestImage in manifestRepo.AllImages) { PlatformInfo matchingManifestPlatform = manifestImage.AllPlatforms .FirstOrDefault(platform => platformData.Equals(platform)); if (matchingManifestPlatform != null) { imageData.ManifestImage = manifestImage; break; } } if (!skipManifestValidation && imageData.ManifestImage == null) { throw new InvalidOperationException( $"Unable to find matching platform in manifest for platform '{platformData.GetIdentifier()}'."); } } } } return(imageArtifactDetails); }
private void SaveTagInfoToImageInfoFile(DateTime createdDate, ImageArtifactDetails imageArtifactDetails) { _loggerService.WriteSubheading("SETTING TAG INFO"); IEnumerable <ImageData> images = imageArtifactDetails.Repos .SelectMany(repo => repo.Images) .Where(image => image.Manifest != null); Parallel.ForEach(images, image => { image.Manifest.Created = createdDate; TagInfo sharedTag = image.ManifestImage.SharedTags.First(); image.Manifest.Digest = DockerHelper.GetDigestString( image.ManifestRepo.FullModelName, _manifestToolService.GetManifestDigestSha( ManifestMediaType.ManifestList, sharedTag.FullyQualifiedName, Options.IsDryRun)); }); string imageInfoString = JsonHelper.SerializeObject(imageArtifactDetails); File.WriteAllText(Options.ImageInfoPath, imageInfoString); }
public void ImageInfoHelper_MergeRepos_EmptyTarget() { ImageArtifactDetails imageArtifactDetails = new ImageArtifactDetails { Repos = { new RepoData { Repo = "repo1", }, new RepoData { Repo = "repo2", Images = { new ImageData { Platforms = { new PlatformData { Dockerfile = "image1" } } } } } } }; ImageArtifactDetails targetImageArtifactDetails = new ImageArtifactDetails(); ImageInfoHelper.MergeImageArtifactDetails(imageArtifactDetails, targetImageArtifactDetails); CompareImageArtifactDetails(imageArtifactDetails, targetImageArtifactDetails); }
public async Task MergeImageInfoFilesCommand_SourceFolderEmpty() { using (TempFolderContext context = TestHelper.UseTempFolder()) { ImageArtifactDetails imageArtifactDetails = new ImageArtifactDetails { Repos = { new RepoData { Repo = "repo" } } }; // Store the content in a .txt file which the command should NOT be looking for. File.WriteAllText("image-info.txt", JsonHelper.SerializeObject(imageArtifactDetails)); MergeImageInfoCommand command = new MergeImageInfoCommand(); command.Options.SourceImageInfoFolderPath = context.Path; command.Options.DestinationImageInfoPath = "output.json"; await Assert.ThrowsAsync <InvalidOperationException>(() => command.ExecuteAsync()); } }
public async Task PublishImageInfoCommand_ReplaceTags() { using (TempFolderContext tempFolderContext = TestHelper.UseTempFolder()) { string repo1Image1DockerfilePath = DockerfileHelper.CreateDockerfile("1.0/runtime/os", tempFolderContext); string repo2Image2DockerfilePath = DockerfileHelper.CreateDockerfile("2.0/runtime/os", tempFolderContext); Manifest manifest = CreateManifest( CreateRepo("repo1", CreateImage( CreatePlatform(repo1Image1DockerfilePath, new string[0]))), CreateRepo("repo2", CreateImage( CreatePlatform(repo2Image2DockerfilePath, new string[0]))) ); RepoData repo2; ImageArtifactDetails srcImageArtifactDetails = new ImageArtifactDetails { Repos = { new RepoData { Repo = "repo1", Images = { new ImageData { Platforms = { Helpers.ImageInfoHelper.CreatePlatform(repo1Image1DockerfilePath, simpleTags: new List <string> { "newtag" }) } } } }, { repo2 = new RepoData { Repo = "repo2", Images = { new ImageData { Platforms = { Helpers.ImageInfoHelper.CreatePlatform(repo2Image2DockerfilePath, simpleTags: new List <string> { "tag1" }) } } } } } } }; string file = Path.Combine(tempFolderContext.Path, "image-info.json"); File.WriteAllText(file, JsonHelper.SerializeObject(srcImageArtifactDetails)); ImageArtifactDetails targetImageArtifactDetails = new ImageArtifactDetails { Repos = { new RepoData { Repo = "repo1", Images = { new ImageData { Platforms = { Helpers.ImageInfoHelper.CreatePlatform(repo1Image1DockerfilePath, simpleTags: new List <string> { "oldtag" }) } } } } } }; Mock <IGitHubClient> gitHubClientMock = GetGitHubClientMock(); Mock <IGitHubClientFactory> gitHubClientFactoryMock = new Mock <IGitHubClientFactory>(); gitHubClientFactoryMock .Setup(o => o.GetClient(It.IsAny <GitHubAuth>(), false)) .Returns(gitHubClientMock.Object); GitOptions gitOptions = new GitOptions { AuthToken = "token", Repo = "testRepo", Branch = "testBranch", Path = "imageinfo.json" }; PublishImageInfoCommand command = new PublishImageInfoCommand( gitHubClientFactoryMock.Object, Mock.Of <ILoggerService>(), CreateHttpClientFactory(gitOptions, targetImageArtifactDetails)); command.Options.ImageInfoPath = file; command.Options.GitOptions = gitOptions; command.Options.Manifest = Path.Combine(tempFolderContext.Path, "manifest.json"); File.WriteAllText(Path.Combine(tempFolderContext.Path, command.Options.Manifest), JsonConvert.SerializeObject(manifest)); command.LoadManifest(); await command.ExecuteAsync(); ImageArtifactDetails expectedImageArtifactDetails = new ImageArtifactDetails { Repos = { new RepoData { Repo = "repo1", Images = { new ImageData { Platforms = { Helpers.ImageInfoHelper.CreatePlatform(repo1Image1DockerfilePath, simpleTags: new List <string> { "newtag" }) } } } }, repo2 } }; Func <GitObject[], bool> verifyGitObjects = (gitObjects) => { if (gitObjects.Length != 1) { return(false); } return(gitObjects[0].Content.Trim() == JsonHelper.SerializeObject(expectedImageArtifactDetails).Trim()); }; gitHubClientMock.Verify( o => o.PostTreeAsync(It.IsAny <GitHubProject>(), It.IsAny <string>(), It.Is <GitObject[]>(gitObjects => verifyGitObjects(gitObjects)))); } }
public void ImageInfoHelper_MergeRepos_ExistingTarget() { PlatformData repo2Image1; PlatformData repo2Image2; PlatformData repo2Image3; PlatformData repo3Image1; DateTime oldCreatedDate = DateTime.Now.Subtract(TimeSpan.FromDays(1)); DateTime newCreatedDate = DateTime.Now; ImageInfo imageInfo1 = CreateImageInfo(); ImageInfo imageInfo2 = CreateImageInfo(); ImageArtifactDetails imageArtifactDetails = new ImageArtifactDetails { Repos = { new RepoData { Repo = "repo2", Images = { new ImageData { ManifestImage = imageInfo1, Platforms = { { repo2Image1 = new PlatformData { Dockerfile = "image1", BaseImageDigest = "base1digest-NEW", Created = newCreatedDate } }, { repo2Image3 = new PlatformData { Dockerfile = "image3" } } } } } }, new RepoData { Repo = "repo3", Images = { new ImageData { ManifestImage = imageInfo2, Platforms = { { repo3Image1 = new PlatformData { Dockerfile = "image1" } } } } } }, new RepoData { Repo = "repo4", } } }; ImageArtifactDetails targetImageArtifactDetails = new ImageArtifactDetails { Repos = { new RepoData { Repo = "repo1" }, new RepoData { Repo = "repo2", Images = { new ImageData { ManifestImage = imageInfo1, Platforms = { new PlatformData { Dockerfile = "image1", BaseImageDigest = "base1digest", Created = oldCreatedDate }, { repo2Image2 = new PlatformData { Dockerfile = "image2", BaseImageDigest = "base2digest" } } } } } }, new RepoData { Repo = "repo3" } } }; ImageInfoHelper.MergeImageArtifactDetails(imageArtifactDetails, targetImageArtifactDetails); ImageArtifactDetails expected = new ImageArtifactDetails { Repos = { new RepoData { Repo = "repo1" }, new RepoData { Repo = "repo2", Images = { new ImageData { Platforms = { repo2Image1, repo2Image2, repo2Image3 } } } }, new RepoData { Repo = "repo3", Images = { new ImageData { Platforms = { repo3Image1 } } } }, new RepoData { Repo = "repo4", } } }; CompareImageArtifactDetails(expected, targetImageArtifactDetails); }
public async Task MergeImageInfoFilesCommand_DuplicateDockerfilePaths() { const string OsType = "Linux"; const string Os1 = "bionic"; const string Os2 = "focal"; using (TempFolderContext context = TestHelper.UseTempFolder()) { string dockerfile1 = CreateDockerfile("1.0/repo1/os1", context); string dockerfile2 = CreateDockerfile("1.0/repo1/os2", context); List <ImageArtifactDetails> imageArtifactDetailsList = new List <ImageArtifactDetails> { new ImageArtifactDetails { Repos = { new RepoData { Repo = "repo1", Images = { new ImageData { Platforms = { new PlatformData { Architecture = "arm32", OsVersion = Os1, OsType = OsType, Digest = "digest1", Dockerfile = dockerfile1, SimpleTags = { "tag1", "tag2" } }, new PlatformData { Architecture = "arm64", OsVersion = Os1, OsType = OsType, Digest = "digest2", Dockerfile = dockerfile1, SimpleTags = { "tag2" } } }, ProductVersion = "1.0" }, new ImageData { Platforms = { new PlatformData { Architecture = "arm32", OsVersion = Os2, OsType = OsType, Digest = "digest4", Dockerfile = dockerfile1, SimpleTags = { "tagA" } } }, ProductVersion = "1.0" } } } } }, new ImageArtifactDetails { Repos = { new RepoData { Repo = "repo1", Images = { new ImageData { Platforms = { new PlatformData { Architecture = "arm64", OsVersion = Os1, OsType = OsType, Digest = "digest2-new", Dockerfile = dockerfile1, SimpleTags = { "tag3", "tag2" } } }, ProductVersion = "1.0" } } } } }, new ImageArtifactDetails { Repos = { new RepoData { Repo = "repo1", Images = { new ImageData { Platforms = { new PlatformData { Architecture = "amd64", Digest = "digest3", OsVersion = Os1, OsType = OsType, Dockerfile = dockerfile2, SimpleTags = { "tag1" } } }, ProductVersion = "1.0" }, new ImageData { Platforms = { new PlatformData { Architecture = "arm32", OsVersion = Os2, OsType = OsType, Digest = "digest4-new", Dockerfile = dockerfile1, SimpleTags = { "tagB" } } }, ProductVersion = "1.0" } } } } } }; MergeImageInfoCommand command = new MergeImageInfoCommand(); command.Options.SourceImageInfoFolderPath = Path.Combine(context.Path, "image-infos"); command.Options.DestinationImageInfoPath = Path.Combine(context.Path, "output.json"); command.Options.Manifest = Path.Combine(context.Path, "manifest.json"); Directory.CreateDirectory(command.Options.SourceImageInfoFolderPath); for (int i = 0; i < imageArtifactDetailsList.Count; i++) { string file = Path.Combine(command.Options.SourceImageInfoFolderPath, $"{i}.json"); File.WriteAllText(file, JsonHelper.SerializeObject(imageArtifactDetailsList[i])); } Manifest manifest = CreateManifest( CreateRepo("repo1", CreateImage( new Platform[] { CreatePlatform(dockerfile1, new string[] { "tag1" }, osVersion: Os1, architecture: Architecture.ARM), CreatePlatform(dockerfile1, new string[] { "tag2" }, osVersion: Os1, architecture: Architecture.ARM64) }, productVersion: "1.0"), CreateImage( new Platform[] { CreatePlatform(dockerfile1, new string[] { "tag3" }, osVersion: Os2, architecture: Architecture.ARM) }, productVersion: "1.0"), CreateImage( new Platform[] { CreatePlatform(dockerfile2, new string[] { "tag4" }, osVersion: Os1) }, productVersion: "1.0")) ); File.WriteAllText(Path.Combine(context.Path, command.Options.Manifest), JsonConvert.SerializeObject(manifest)); command.LoadManifest(); await command.ExecuteAsync(); string resultsContent = File.ReadAllText(command.Options.DestinationImageInfoPath); ImageArtifactDetails actual = JsonConvert.DeserializeObject <ImageArtifactDetails>(resultsContent); ImageArtifactDetails expected = new ImageArtifactDetails { Repos = { new RepoData { Repo = "repo1", Images = { new ImageData { Platforms = { new PlatformData { Architecture = "arm32", OsVersion = Os1, OsType = OsType, Digest = "digest1", Dockerfile = dockerfile1, SimpleTags = { "tag1", "tag2" } }, new PlatformData { Architecture = "arm64", OsVersion = Os1, OsType = OsType, Digest = "digest2-new", Dockerfile = dockerfile1, SimpleTags = { "tag2", "tag3" } } }, ProductVersion = "1.0" }, new ImageData { Platforms = { new PlatformData { Architecture = "arm32", OsVersion = Os2, OsType = OsType, Digest = "digest4-new", Dockerfile = dockerfile1, SimpleTags = { "tagA", "tagB" } } }, ProductVersion = "1.0" }, new ImageData { Platforms = { new PlatformData { Architecture = "amd64", Digest = "digest3", OsVersion = Os1, OsType = OsType, Dockerfile = dockerfile2, SimpleTags = { "tag1" } } }, ProductVersion = "1.0" } } } } }; ImageInfoHelperTests.CompareImageArtifactDetails(expected, actual); } }
public async Task MergeImageInfoFilesCommand_HappyPath() { using (TempFolderContext context = TestHelper.UseTempFolder()) { string repo2Image1Dockerfile = CreateDockerfile("1.0/repo2/os1", context); string repo2Image2Dockerfile = CreateDockerfile("1.0/repo2/os2", context); string repo4Image2Dockerfile = CreateDockerfile("1.0/repo4/os2", context); string repo4Image3Dockerfile = CreateDockerfile("1.0/repo4/os3", context); List <ImageArtifactDetails> imageArtifactDetailsList = new List <ImageArtifactDetails> { new ImageArtifactDetails { Repos = { new RepoData { Repo = "repo1" }, new RepoData { Repo = "repo2", Images = { new ImageData { Platforms = { CreatePlatform( repo2Image1Dockerfile, simpleTags: new List <string> { "tag3" }) }, ProductVersion = "1.0" }, new ImageData { Platforms = { CreatePlatform(repo2Image2Dockerfile) }, ProductVersion = "1.0" } } }, new RepoData { Repo = "repo4", Images = { new ImageData { Platforms = { CreatePlatform( repo4Image2Dockerfile, simpleTags: new List <string> { "tag1" }) }, ProductVersion = "1.0" } } } } }, new ImageArtifactDetails { Repos = { new RepoData { Repo = "repo2", Images = { new ImageData { Platforms = { CreatePlatform( repo2Image1Dockerfile, simpleTags: new List <string> { "tag1" }, baseImageDigest: "base1hash") }, ProductVersion = "1.0" }, new ImageData { Platforms = { CreatePlatform( repo2Image2Dockerfile, simpleTags: new List <string> { "tag2" }) }, ProductVersion = "1.0" } } }, new RepoData { Repo = "repo3", }, new RepoData { Repo = "repo4", Images = { new ImageData { Platforms = { CreatePlatform( repo4Image2Dockerfile, simpleTags: new List <string> { "tag2" }) }, ProductVersion = "1.0" }, new ImageData { Platforms = { CreatePlatform( repo4Image3Dockerfile, simpleTags: new List <string> { "tag1" }) }, ProductVersion = "1.0" } } } } } }; MergeImageInfoCommand command = new MergeImageInfoCommand(); command.Options.SourceImageInfoFolderPath = Path.Combine(context.Path, "image-infos"); command.Options.DestinationImageInfoPath = Path.Combine(context.Path, "output.json"); command.Options.Manifest = Path.Combine(context.Path, "manifest.json"); Directory.CreateDirectory(command.Options.SourceImageInfoFolderPath); for (int i = 0; i < imageArtifactDetailsList.Count; i++) { string file = Path.Combine(command.Options.SourceImageInfoFolderPath, $"{i}.json"); File.WriteAllText(file, JsonHelper.SerializeObject(imageArtifactDetailsList[i])); } Manifest manifest = CreateManifest( CreateRepo("repo1"), CreateRepo("repo2", CreateImage( new Platform[] { CreatePlatform(repo2Image1Dockerfile, new string[] { "tag1" }) }, productVersion: "1.0"), CreateImage( new Platform[] { CreatePlatform(repo2Image2Dockerfile, new string[] { "tag2" }) }, productVersion: "1.0")), CreateRepo("repo3"), CreateRepo("repo4", CreateImage( new Platform[] { CreatePlatform(repo4Image2Dockerfile, new string[] { "tag1" }) }, productVersion: "1.0"), CreateImage( new Platform[] { CreatePlatform(repo4Image3Dockerfile, new string[] { "tag2" }) }, productVersion: "1.0")) ); File.WriteAllText(Path.Combine(context.Path, command.Options.Manifest), JsonConvert.SerializeObject(manifest)); command.LoadManifest(); await command.ExecuteAsync(); string resultsContent = File.ReadAllText(command.Options.DestinationImageInfoPath); ImageArtifactDetails actual = JsonConvert.DeserializeObject <ImageArtifactDetails>(resultsContent); ImageArtifactDetails expected = new ImageArtifactDetails { Repos = { new RepoData { Repo = "repo1" }, new RepoData { Repo = "repo2", Images = { new ImageData { Platforms = { CreatePlatform( repo2Image1Dockerfile, simpleTags: new List <string> { "tag1", "tag3" }, baseImageDigest: "base1hash") }, ProductVersion = "1.0" }, new ImageData { Platforms = { CreatePlatform( repo2Image2Dockerfile, simpleTags: new List <string> { "tag2" }) }, ProductVersion = "1.0" } } }, new RepoData { Repo = "repo3", }, new RepoData { Repo = "repo4", Images = { new ImageData { Platforms = { CreatePlatform( repo4Image2Dockerfile, simpleTags: new List <string> { "tag1", "tag2" }) }, ProductVersion = "1.0" }, new ImageData { Platforms = { CreatePlatform( repo4Image3Dockerfile, simpleTags: new List <string> { "tag1" }) }, ProductVersion = "1.0" } } } } }; ImageInfoHelperTests.CompareImageArtifactDetails(expected, actual); } }
public async Task BuildCommand_ImageInfoOutput_Basic() { const string repoName = "runtime"; const string sha = "sha256:c74364a9f125ca612f9a67e4a0551937b7a37c82fabb46172c4867b73edd638c"; string digest = $"{repoName}@{sha}"; const string tag = "tag"; const string baseImageRepo = "baserepo"; string baseImageTag = $"{baseImageRepo}:basetag"; string baseImageDigest = $"{baseImageRepo}@sha256:d21234a9f125ca612f9a67e4a0551937b7a37c82fabb46172c4867b73edd1349"; using (TempFolderContext tempFolderContext = TestHelper.UseTempFolder()) { Mock <IDockerService> dockerServiceMock = new Mock <IDockerService>(); dockerServiceMock .SetupGet(o => o.Architecture) .Returns(Architecture.AMD64); dockerServiceMock .Setup(o => o.GetImageDigest($"{repoName}:{tag}", false)) .Returns(digest); dockerServiceMock .Setup(o => o.GetImageDigest(baseImageTag, false)) .Returns(baseImageDigest); DateTime createdDate = DateTime.Now; dockerServiceMock .Setup(o => o.GetCreatedDate($"{repoName}:{tag}", false)) .Returns(createdDate); const string runtimeRelativeDir = "1.0/runtime/os"; Directory.CreateDirectory(Path.Combine(tempFolderContext.Path, runtimeRelativeDir)); string dockerfileRelativePath = PathHelper.NormalizePath(Path.Combine(runtimeRelativeDir, "Dockerfile")); string fullDockerfilePath = PathHelper.NormalizePath(Path.Combine(tempFolderContext.Path, dockerfileRelativePath)); File.WriteAllText(fullDockerfilePath, $"FROM {baseImageTag}"); const string dockerfileCommitSha = "mycommit"; Mock <IGitService> gitServiceMock = new Mock <IGitService>(); gitServiceMock .Setup(o => o.GetCommitSha(fullDockerfilePath, It.IsAny <bool>())) .Returns(dockerfileCommitSha); BuildCommand command = new BuildCommand(dockerServiceMock.Object, Mock.Of <ILoggerService>(), Mock.Of <IEnvironmentService>(), gitServiceMock.Object); command.Options.Manifest = Path.Combine(tempFolderContext.Path, "manifest.json"); command.Options.ImageInfoOutputPath = Path.Combine(tempFolderContext.Path, "image-info.json"); command.Options.IsPushEnabled = true; command.Options.SourceRepoUrl = "https://github.com/dotnet/test"; const string ProductVersion = "1.0.1"; Manifest manifest = CreateManifest( CreateRepo(repoName, CreateImage( new Platform[] { CreatePlatform(dockerfileRelativePath, new string[] { tag }) }, new Dictionary <string, Tag> { { "shared", new Tag() } }, ProductVersion)) ); File.WriteAllText(Path.Combine(tempFolderContext.Path, command.Options.Manifest), JsonConvert.SerializeObject(manifest)); command.LoadManifest(); await command.ExecuteAsync(); ImageArtifactDetails imageArtifactDetails = new ImageArtifactDetails { Repos = { new RepoData { Repo = repoName, Images = { new ImageData { ProductVersion = ProductVersion, Platforms = { new PlatformData { Dockerfile = $"{runtimeRelativeDir}/Dockerfile", Architecture = "amd64", OsType = "Linux", OsVersion = "Ubuntu 19.04", Digest = sha, BaseImageDigest = baseImageDigest, Created = createdDate.ToUniversalTime(), SimpleTags = { tag }, CommitUrl = $"{command.Options.SourceRepoUrl}/blob/{dockerfileCommitSha}/{dockerfileRelativePath}" } }, Manifest = new ManifestData { SharedTags = { "shared" } } } } } } }; string expectedOutput = JsonHelper.SerializeObject(imageArtifactDetails); string actualOutput = File.ReadAllText(command.Options.ImageInfoOutputPath); Assert.Equal(expectedOutput, actualOutput); } }
public void ImageInfoHelper_MergeRepos_MergeTags() { PlatformData srcImage1; PlatformData targetImage2; ImageInfo imageInfo1 = CreateImageInfo(); ImageArtifactDetails imageArtifactDetails = new ImageArtifactDetails { Repos = { new RepoData { Repo = "repo1", Images = { new ImageData { ManifestImage = imageInfo1, Platforms = { { srcImage1 = new PlatformData { Dockerfile = "image1", SimpleTags = { "tag1", "tag3" } } } }, Manifest = new ManifestData { SharedTags = { "shared1", "shared2" } } } } } } }; ImageArtifactDetails targetImageArtifactDetails = new ImageArtifactDetails { Repos = { new RepoData { Repo = "repo1", Images = { new ImageData { ManifestImage = imageInfo1, Platforms = { new PlatformData { Dockerfile = "image1", SimpleTags = { "tag1", "tag2", "tag4" } }, { targetImage2 = new PlatformData { Dockerfile = "image2", SimpleTags = { "a" } } } }, Manifest = new ManifestData { SharedTags = { "shared2", "shared3" } } } } } } }; ImageInfoHelper.MergeImageArtifactDetails(imageArtifactDetails, targetImageArtifactDetails); ImageArtifactDetails expected = new ImageArtifactDetails { Repos = { new RepoData { Repo = "repo1", Images = { new ImageData { Platforms = { new PlatformData { Dockerfile = "image1", SimpleTags = { "tag1", "tag2", "tag3", "tag4" } }, targetImage2 }, Manifest = new ManifestData { SharedTags = { "shared1", "shared2", "shared3" } } } } } } }; CompareImageArtifactDetails(expected, targetImageArtifactDetails); }
private IEnumerable <DigestInfo> GetImageDigestInfos(ImageArtifactDetails imageArtifactDetails) => imageArtifactDetails.Repos .SelectMany(repo => repo.Images.SelectMany(image => GetImageDigestInfos(image, repo)));
public void ImageInfoHelper_MergeRepos_RemoveTag() { PlatformData srcPlatform1; PlatformData targetPlatform2; ImageInfo imageInfo1 = CreateImageInfo(); ImageArtifactDetails imageArtifactDetails = new ImageArtifactDetails { Repos = { new RepoData { Repo = "repo1", Images = { new ImageData { ManifestImage = imageInfo1, Platforms = { { srcPlatform1 = new PlatformData { Dockerfile = "image1", SimpleTags = { "tag1", "tag3" } } } }, Manifest = new ManifestData { SharedTags = { "sharedtag1", } } } } } } }; ImageArtifactDetails targetImageArtifactDetails = new ImageArtifactDetails { Repos = { new RepoData { Repo = "repo1", Images = { new ImageData { ManifestImage = imageInfo1, Platforms = { { new PlatformData { Dockerfile = "image1", SimpleTags = { "tag1", "tag2", "tag4" } } }, { targetPlatform2 = new PlatformData { Dockerfile = "image2", SimpleTags = { "a" } } } }, Manifest = new ManifestData { SharedTags = { "sharedtag2", } } } } } } }; ImageInfoMergeOptions options = new ImageInfoMergeOptions { ReplaceTags = true }; ImageInfoHelper.MergeImageArtifactDetails(imageArtifactDetails, targetImageArtifactDetails, options); ImageArtifactDetails expected = new ImageArtifactDetails { Repos = { new RepoData { Repo = "repo1", Images = { new ImageData { Platforms = { srcPlatform1, targetPlatform2 }, Manifest = new ManifestData { SharedTags = { "sharedtag1", } } } } } } }; CompareImageArtifactDetails(expected, targetImageArtifactDetails); }
public async Task IngestKustoImageInfoCommand_MultipleRepos() { using TempFolderContext tempFolderContext = TestHelper.UseTempFolder(); string repo1Image1DockerfilePath = DockerfileHelper.CreateDockerfile("1.0/sdk/os", tempFolderContext); string repo1Image2DockerfilePath = DockerfileHelper.CreateDockerfile("1.0/sdk/os2", tempFolderContext); string repo2Image2DockerfilePath = DockerfileHelper.CreateDockerfile("2.0/sdk/os", tempFolderContext); Manifest manifest = CreateManifest( CreateRepo("r1", CreateImage( new Platform[] { CreatePlatform(repo1Image1DockerfilePath, new string[] { "t1" }), CreatePlatform(repo1Image2DockerfilePath, new string[] { "t2" }) }, productVersion: "1.0.2", sharedTags: new Dictionary <string, Tag> { { "st1", new Tag() } })), CreateRepo("r2", CreateImage( new Platform[] { CreatePlatform(repo2Image2DockerfilePath, new string[] { "t3" }) }, productVersion: "2.0.5")) ); string manifestPath = Path.Combine(tempFolderContext.Path, "manifest.json"); File.WriteAllText(manifestPath, JsonConvert.SerializeObject(manifest)); ImageArtifactDetails srcImageArtifactDetails = new ImageArtifactDetails { Repos = { new RepoData { Repo = "r1", Images = { new ImageData { Manifest = new ManifestData { Created = new DateTime(2020, 4, 20, 21, 57, 00, DateTimeKind.Utc), Digest = "abc", SharedTags = new List <string> { "st1" } }, Platforms = { { Helpers.ImageInfoHelper.CreatePlatform( repo1Image1DockerfilePath, created: new DateTime(2020, 4, 20, 21, 56, 50, DateTimeKind.Utc), digest: "def", simpleTags: new List <string> { "t1" }) }, { Helpers.ImageInfoHelper.CreatePlatform( repo1Image2DockerfilePath, created: new DateTime(2020, 4, 20, 21, 56, 56, DateTimeKind.Utc), digest: "ghi", simpleTags: new List <string> { "t2" }) } }, ProductVersion = "1.0.2", } } }, new RepoData { Repo = "r2", Images = { new ImageData { Platforms = { Helpers.ImageInfoHelper.CreatePlatform( repo2Image2DockerfilePath, created: new DateTime(2020, 4, 20, 21, 56, 58, DateTimeKind.Utc), digest: "jkl", simpleTags: new List <string> { "t3" }) }, ProductVersion = "2.0.5" } } } } }; string expectedData = @"""def"",""amd64"",""Linux"",""Ubuntu 20.04"",""1.0.2"",""1.0/sdk/os/Dockerfile"",""r1"",""2020-04-20 21:56:50"" ""t1"",""amd64"",""Linux"",""Ubuntu 20.04"",""1.0.2"",""1.0/sdk/os/Dockerfile"",""r1"",""2020-04-20 21:56:50"" ""ghi"",""amd64"",""Linux"",""Ubuntu 20.04"",""1.0.2"",""1.0/sdk/os2/Dockerfile"",""r1"",""2020-04-20 21:56:56"" ""t2"",""amd64"",""Linux"",""Ubuntu 20.04"",""1.0.2"",""1.0/sdk/os2/Dockerfile"",""r1"",""2020-04-20 21:56:56"" ""jkl"",""amd64"",""Linux"",""Ubuntu 20.04"",""2.0.5"",""2.0/sdk/os/Dockerfile"",""r2"",""2020-04-20 21:56:58"" ""t3"",""amd64"",""Linux"",""Ubuntu 20.04"",""2.0.5"",""2.0/sdk/os/Dockerfile"",""r2"",""2020-04-20 21:56:58"""; expectedData = expectedData.NormalizeLineEndings(Environment.NewLine).Trim(); string imageInfoPath = Path.Combine(tempFolderContext.Path, "image-info.json"); File.WriteAllText(imageInfoPath, JsonHelper.SerializeObject(srcImageArtifactDetails)); string ingestedData = null; Mock <IKustoClient> kustoClientMock = new Mock <IKustoClient>(); kustoClientMock .Setup(o => o.IngestFromCsvStreamAsync(It.IsAny <Stream>(), It.IsAny <IngestKustoImageInfoOptions>())) .Callback <Stream, IngestKustoImageInfoOptions>((s, o) => { StreamReader reader = new StreamReader(s); ingestedData = reader.ReadToEnd(); }); IngestKustoImageInfoCommand command = new IngestKustoImageInfoCommand( Mock.Of <ILoggerService>(), kustoClientMock.Object); command.Options.ImageInfoPath = imageInfoPath; command.Options.Manifest = manifestPath; command.LoadManifest(); await command.ExecuteAsync(); _outputHelper.WriteLine($"Expected Data: {Environment.NewLine}{expectedData}"); _outputHelper.WriteLine($"Actual Data: {Environment.NewLine}{ingestedData}"); kustoClientMock.Verify(o => o.IngestFromCsvStreamAsync(It.IsAny <Stream>(), It.IsAny <IngestKustoImageInfoOptions>())); Assert.Equal(expectedData, ingestedData); }
public static void CompareImageArtifactDetails(ImageArtifactDetails expected, ImageArtifactDetails actual) { Assert.Equal(JsonHelper.SerializeObject(expected), JsonHelper.SerializeObject(actual)); }
public void PlatformVersionedOs_Cached(bool isRuntimeCached, bool isAspnetCached, bool isSdkCached, string expectedPaths) { using TempFolderContext tempFolderContext = TestHelper.UseTempFolder(); GenerateBuildMatrixCommand command = new GenerateBuildMatrixCommand(); command.Options.Manifest = Path.Combine(tempFolderContext.Path, "manifest.json"); command.Options.MatrixType = MatrixType.PlatformVersionedOs; command.Options.ImageInfoPath = Path.Combine(tempFolderContext.Path, "imageinfo.json"); command.Options.ProductVersionComponents = 2; string runtimeDockerfilePath; string aspnetDockerfilePath; string sdkDockerfilePath; Manifest manifest = CreateManifest( CreateRepo("runtime", CreateImage( new Platform[] { CreatePlatform( runtimeDockerfilePath = DockerfileHelper.CreateDockerfile("1.0/runtime/os", tempFolderContext), new string[] { "tag" }) }, productVersion: "1.0")), CreateRepo("aspnet", CreateImage( new Platform[] { CreatePlatform( aspnetDockerfilePath = DockerfileHelper.CreateDockerfile("1.0/aspnet/os", tempFolderContext, "runtime:tag"), new string[] { "tag" }) }, productVersion: "1.0")), CreateRepo("sdk", CreateImage( new Platform[] { CreatePlatform( sdkDockerfilePath = DockerfileHelper.CreateDockerfile("1.0/sdk/os", tempFolderContext, "aspnet:tag"), new string[] { "tag" }) }, productVersion: "1.0")) ); File.WriteAllText(Path.Combine(tempFolderContext.Path, command.Options.Manifest), JsonConvert.SerializeObject(manifest)); ImageArtifactDetails imageArtifactDetails = new ImageArtifactDetails { Repos = { new RepoData { Repo = "runtime", Images = { new ImageData { ProductVersion = "1.0", Platforms = { CreateSimplePlatformData(runtimeDockerfilePath, isCached: isRuntimeCached) } } } }, new RepoData { Repo = "aspnet", Images = { new ImageData { ProductVersion = "1.0", Platforms = { CreateSimplePlatformData(aspnetDockerfilePath, isCached: isAspnetCached) } } } }, new RepoData { Repo = "sdk", Images = { new ImageData { ProductVersion = "1.0", Platforms = { CreateSimplePlatformData(sdkDockerfilePath, isCached: isSdkCached) } } } } } }; File.WriteAllText(command.Options.ImageInfoPath, JsonHelper.SerializeObject(imageArtifactDetails)); command.LoadManifest(); IEnumerable <BuildMatrixInfo> matrixInfos = command.GenerateMatrixInfo(); if (isRuntimeCached && isAspnetCached && isSdkCached) { Assert.Empty(matrixInfos); } else { Assert.Single(matrixInfos); Assert.Single(matrixInfos.First().Legs); BuildLegInfo buildLeg = matrixInfos.First().Legs.First(); string imageBuilderPaths = buildLeg.Variables.First(variable => variable.Name == "imageBuilderPaths").Value; Assert.Equal(expectedPaths, imageBuilderPaths); } }
public async Task MixOfCachedPlatforms() { ImageArtifactDetails imageArtifactDetails = new ImageArtifactDetails { Repos = new List <RepoData> { new RepoData { Repo = "repo1", Images = new List <ImageData> { new ImageData { Platforms = new List <PlatformData> { new PlatformData(), new PlatformData { IsUnchanged = true }, } }, new ImageData { Platforms = new List <PlatformData> { new PlatformData { IsUnchanged = true }, } }, new ImageData { Platforms = new List <PlatformData> { new PlatformData(), new PlatformData(), } } } }, new RepoData { Repo = "repo2", Images = new List <ImageData> { new ImageData { Platforms = new List <PlatformData> { new PlatformData { IsUnchanged = true }, } }, new ImageData { Platforms = new List <PlatformData> { new PlatformData { IsUnchanged = true }, } } } }, new RepoData { Repo = "repo3", Images = new List <ImageData> { new ImageData { Platforms = new List <PlatformData> { new PlatformData() } } } } } }; ImageArtifactDetails expectedImageArtifactDetails = new ImageArtifactDetails { Repos = new List <RepoData> { new RepoData { Repo = "repo1", Images = new List <ImageData> { new ImageData { Platforms = new List <PlatformData> { new PlatformData(), } }, new ImageData { Platforms = new List <PlatformData> { new PlatformData(), new PlatformData(), } } } }, new RepoData { Repo = "repo3", Images = new List <ImageData> { new ImageData { Platforms = new List <PlatformData> { new PlatformData() } } } } } }; await RunTestAsync(imageArtifactDetails, expectedImageArtifactDetails); }
private IHttpClientFactory CreateHttpClientFactory(IGitFileRef imageOptionsFileRef, ImageArtifactDetails imageArtifactDetails) { string tempDir = Directory.CreateDirectory( Path.Combine(Path.GetTempPath(), Path.GetRandomFileName())).FullName; this.foldersToDelete.Add(tempDir); string repoPath = Directory.CreateDirectory( Path.Combine(tempDir, $"{imageOptionsFileRef.Repo}-{imageOptionsFileRef.Branch}")).FullName; string imageInfoPath = Path.Combine(repoPath, imageOptionsFileRef.Path); File.WriteAllText(imageInfoPath, JsonConvert.SerializeObject(imageArtifactDetails)); string repoZipPath = Path.Combine(tempDir, "repo.zip"); ZipFile.CreateFromDirectory(repoPath, repoZipPath, CompressionLevel.Fastest, true); Dictionary <string, HttpResponseMessage> responses = new Dictionary <string, HttpResponseMessage> { { GitHelper.GetArchiveUrl(imageOptionsFileRef).ToString(), new HttpResponseMessage { StatusCode = HttpStatusCode.OK, Content = new ByteArrayContent(File.ReadAllBytes(repoZipPath)) } } }; HttpClient client = new HttpClient(new TestHttpMessageHandler(responses)); Mock <IHttpClientFactory> httpClientFactoryMock = new Mock <IHttpClientFactory>(); httpClientFactoryMock .Setup(o => o.GetClient()) .Returns(client); return(httpClientFactoryMock.Object); }