From 01d87dd1eddde4678326390b236b4706f8186e87 Mon Sep 17 00:00:00 2001 From: QuanWanxx <68055742+QuanWanxx@users.noreply.github.com> Date: Thu, 18 Aug 2022 14:48:42 +0800 Subject: [PATCH 1/6] Update version to 5.0.5 (#424) --- release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release.yml b/release.yml index 22e032fe5..d30eaff2f 100644 --- a/release.yml +++ b/release.yml @@ -18,7 +18,7 @@ variables: buildConfiguration: 'Release' major: 5 minor: 0 - patch: 4 + patch: 5 buildnum: $[counter(format('{0}.{1}.{2}',variables['major'],variables['minor'], variables['patch']), 1)] version: $(major).$(minor).$(patch).$(buildnum) From 8660f994fa28d604c564815d9002505b5ff5faff Mon Sep 17 00:00:00 2001 From: QuanWanxx <68055742+QuanWanxx@users.noreply.github.com> Date: Thu, 18 Aug 2022 15:55:45 +0800 Subject: [PATCH 2/6] Interface to convert JObject (#416) --- .../Processors/ProcessorTests.cs | 13 ++++++ .../TestData/Expected/ExamplePatient.json | 44 +++++++++++++++++++ .../Parsers/JsonDataParser.cs | 1 - .../Processors/JsonProcessor.cs | 8 ++++ 4 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 src/Microsoft.Health.Fhir.Liquid.Converter.UnitTests/TestData/Expected/ExamplePatient.json diff --git a/src/Microsoft.Health.Fhir.Liquid.Converter.UnitTests/Processors/ProcessorTests.cs b/src/Microsoft.Health.Fhir.Liquid.Converter.UnitTests/Processors/ProcessorTests.cs index 68a8054eb..e871ae6c5 100644 --- a/src/Microsoft.Health.Fhir.Liquid.Converter.UnitTests/Processors/ProcessorTests.cs +++ b/src/Microsoft.Health.Fhir.Liquid.Converter.UnitTests/Processors/ProcessorTests.cs @@ -11,6 +11,7 @@ using Microsoft.Health.Fhir.Liquid.Converter.Exceptions; using Microsoft.Health.Fhir.Liquid.Converter.Models; using Microsoft.Health.Fhir.Liquid.Converter.Processors; +using Newtonsoft.Json.Linq; using Xunit; namespace Microsoft.Health.Fhir.Liquid.Converter.UnitTests.Processors @@ -20,6 +21,7 @@ public class ProcessorTests private static readonly string _hl7v2TestData; private static readonly string _ccdaTestData; private static readonly string _jsonTestData; + private static readonly string _jsonExpectData; private static readonly string _fhirStu3TestData; private static readonly ProcessorSettings _processorSettings; @@ -28,6 +30,7 @@ static ProcessorTests() _hl7v2TestData = File.ReadAllText(Path.Join(TestConstants.SampleDataDirectory, "Hl7v2", "LRI_2.0-NG_CBC_Typ_Message.hl7")); _ccdaTestData = File.ReadAllText(Path.Join(TestConstants.SampleDataDirectory, "Ccda", "CCD.ccda")); _jsonTestData = File.ReadAllText(Path.Join(TestConstants.SampleDataDirectory, "Json", "ExamplePatient.json")); + _jsonExpectData = File.ReadAllText(Path.Join(TestConstants.ExpectedDirectory, "ExamplePatient.json")); _fhirStu3TestData = File.ReadAllText(Path.Join(TestConstants.SampleDataDirectory, "Stu3", "Patient.json")); _processorSettings = new ProcessorSettings(); } @@ -242,5 +245,15 @@ public void GivenTemplateWithNestingTooDeep_WhenConvert_ExceptionShouldBeThrown( exception = Assert.Throws(() => processor.Convert(data, "NestingTooDeepDiffTemplate", templateProvider)); Assert.Contains("Nesting too deep", exception.Message); } + + [Fact] + public void GivenJObjectInput_WhenConvertWithJsonProcessor_CorrectResultShouldBeReturned() + { + var processor = new JsonProcessor(_processorSettings); + var templateProvider = new TemplateProvider(TestConstants.JsonTemplateDirectory, DataType.Json); + var testData = JObject.Parse(_jsonTestData); + var result = processor.Convert(testData, "ExamplePatient", templateProvider); + Assert.True(JToken.DeepEquals(JObject.Parse(_jsonExpectData), JToken.Parse(result))); + } } } diff --git a/src/Microsoft.Health.Fhir.Liquid.Converter.UnitTests/TestData/Expected/ExamplePatient.json b/src/Microsoft.Health.Fhir.Liquid.Converter.UnitTests/TestData/Expected/ExamplePatient.json new file mode 100644 index 000000000..1e65aca0c --- /dev/null +++ b/src/Microsoft.Health.Fhir.Liquid.Converter.UnitTests/TestData/Expected/ExamplePatient.json @@ -0,0 +1,44 @@ +{ + "resourceType": "Patient", + "id": "e8b835f7-0c46-4166-22ad-23e6783aaf54", + "identifier": [ + { + "use": "usual", + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0203", + "code": "MR" + } + ] + }, + "system": "urn:oid:2.16.840.1.113883.19.5", + "value": "M0R1N2" + } + ], + "active": true, + "name": [ + { + "family": "Smith", + "given": [ + "Jerry" + ] + } + ], + "telecom": [ + { + "system": "phone", + "value": "1234-5678" + }, + { + "system": "phone", + "value": "1234-5679" + } + ], + "gender": "male", + "birthDate": "2001-01-10", + "managingOrganization": { + "reference": "Organization/2.16.840.1.113883.19.5", + "display": "Good Health Clinic" + } +} \ No newline at end of file diff --git a/src/Microsoft.Health.Fhir.Liquid.Converter/Parsers/JsonDataParser.cs b/src/Microsoft.Health.Fhir.Liquid.Converter/Parsers/JsonDataParser.cs index e86839480..c1be2cf78 100644 --- a/src/Microsoft.Health.Fhir.Liquid.Converter/Parsers/JsonDataParser.cs +++ b/src/Microsoft.Health.Fhir.Liquid.Converter/Parsers/JsonDataParser.cs @@ -4,7 +4,6 @@ // ------------------------------------------------------------------------------------------------- using System; -using System.Collections.Generic; using Microsoft.Health.Fhir.Liquid.Converter.Exceptions; using Microsoft.Health.Fhir.Liquid.Converter.Extensions; using Microsoft.Health.Fhir.Liquid.Converter.Models; diff --git a/src/Microsoft.Health.Fhir.Liquid.Converter/Processors/JsonProcessor.cs b/src/Microsoft.Health.Fhir.Liquid.Converter/Processors/JsonProcessor.cs index ac3e45401..3b8a9ec35 100644 --- a/src/Microsoft.Health.Fhir.Liquid.Converter/Processors/JsonProcessor.cs +++ b/src/Microsoft.Health.Fhir.Liquid.Converter/Processors/JsonProcessor.cs @@ -6,9 +6,11 @@ using System.Collections.Generic; using System.Globalization; using DotLiquid; +using Microsoft.Health.Fhir.Liquid.Converter.Extensions; using Microsoft.Health.Fhir.Liquid.Converter.Models; using Microsoft.Health.Fhir.Liquid.Converter.Models.Json; using Microsoft.Health.Fhir.Liquid.Converter.Parsers; +using Newtonsoft.Json.Linq; using NJsonSchema; namespace Microsoft.Health.Fhir.Liquid.Converter.Processors @@ -28,6 +30,12 @@ public override string Convert(string data, string rootTemplate, ITemplateProvid return Convert(jsonData, rootTemplate, templateProvider, traceInfo); } + public string Convert(JObject data, string rootTemplate, ITemplateProvider templateProvider, TraceInfo traceInfo = null) + { + var jsonData = data.ToObject(); + return Convert(jsonData, rootTemplate, templateProvider, traceInfo); + } + protected override Context CreateContext(ITemplateProvider templateProvider, IDictionary data) { // Load data and templates From cfe5b0299fe919eb6db652a8490a380864db7df6 Mon Sep 17 00:00:00 2001 From: healukas <86768844+healukas@users.noreply.github.com> Date: Thu, 27 Oct 2022 08:38:44 +0200 Subject: [PATCH 3/6] build: Insert missing ampersand issue #432 (#433) * Update README.md * Create HL7v2-templates.md * Create HL7v2-FHIRValidator.md * Create HL7v2-ImportantPoints.md * Update README.md * Update README.md Updated STU3-R4 information for OSS FHIR Server * Fix StyleCop Dependency (#397) - Fix build-time dependency on StyleCop.Analyzers to no longer force consumers to use the same analyzers * Bump Newtonsoft.Json in /src/Microsoft.Health.Fhir.TemplateManagement Bumps [Newtonsoft.Json](https://github.com/JamesNK/Newtonsoft.Json) from 12.0.3 to 13.0.1. - [Release notes](https://github.com/JamesNK/Newtonsoft.Json/releases) - [Commits](https://github.com/JamesNK/Newtonsoft.Json/compare/12.0.3...13.0.1) --- updated-dependencies: - dependency-name: Newtonsoft.Json dependency-type: direct:production ... Signed-off-by: dependabot[bot] * Bump Newtonsoft.Json from 12.0.3 to 13.0.1 in src/Microsoft.Health.Fhir.TemplateManagement/Microsoft.Health.Fhir.Liquid.Converter.nuspec * Exclude data folder * Update the GeoPol * Update version * Remove StyleCop from Nuspec (#410) - Remove StyleCop from the nuspec to prevent forcing consumers to import StyleCop * Prepare new release for 5.0.3 (#413) * Fix more than 3 digits for milliseconds in datetime (#409) * fix * fix * Update version to 5.0.3 (#412) Co-authored-by: sowu880 <57981365+sowu880@users.noreply.github.com> * Add NJsonSchema dependency to nuspec file (#423) * Prepare new release for 5.0.5 (#425) * Update version to 5.0.5 (#424) * Interface to convert JObject (#416) * build: Insert missing ampersand * build: Fix bin-folder creation * build: fix mkdir command for windows * build: unfix mkdir command for windows * build: remove double binfolder * build: Ignore error in folder creation Signed-off-by: dependabot[bot] Co-authored-by: yankunhuang-pku <66581117+yankunhuang-pku@users.noreply.github.com> Co-authored-by: ginalee-dotcom <68250213+ginalee-dotcom@users.noreply.github.com> Co-authored-by: sowu880 <57981365+sowu880@users.noreply.github.com> Co-authored-by: Irene Joseph <90474712+irenepjoseph@users.noreply.github.com> Co-authored-by: Qiwei Jin Co-authored-by: Will Sugarman Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Yankun Huang Co-authored-by: Yue Fei Co-authored-by: Will Sugarman Co-authored-by: QuanWanxx <68055742+QuanWanxx@users.noreply.github.com> --- .../Microsoft.Health.Fhir.Liquid.Converter.nuspec | 4 ++-- .../Microsoft.Health.Fhir.TemplateManagement.csproj | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Microsoft.Health.Fhir.TemplateManagement/Microsoft.Health.Fhir.Liquid.Converter.nuspec b/src/Microsoft.Health.Fhir.TemplateManagement/Microsoft.Health.Fhir.Liquid.Converter.nuspec index 9164a898b..9a4ee8179 100644 --- a/src/Microsoft.Health.Fhir.TemplateManagement/Microsoft.Health.Fhir.Liquid.Converter.nuspec +++ b/src/Microsoft.Health.Fhir.TemplateManagement/Microsoft.Health.Fhir.Liquid.Converter.nuspec @@ -16,13 +16,13 @@ - + + - diff --git a/src/Microsoft.Health.Fhir.TemplateManagement/Microsoft.Health.Fhir.TemplateManagement.csproj b/src/Microsoft.Health.Fhir.TemplateManagement/Microsoft.Health.Fhir.TemplateManagement.csproj index 63925939b..3c191d994 100644 --- a/src/Microsoft.Health.Fhir.TemplateManagement/Microsoft.Health.Fhir.TemplateManagement.csproj +++ b/src/Microsoft.Health.Fhir.TemplateManagement/Microsoft.Health.Fhir.TemplateManagement.csproj @@ -17,6 +17,7 @@ $(SolutionFolder)data\ $(SolutionFolder)bin\ copy + mkdir @@ -24,6 +25,7 @@ $(SolutionFolder)data/ $(SolutionFolder)bin/ cp + mkdir -p @@ -76,7 +78,7 @@ - + @@ -86,7 +88,7 @@ - + From 78ce2d96e294637b1a0c690d6592b1acbb1cbe97 Mon Sep 17 00:00:00 2001 From: sowu880 <57981365+sowu880@users.noreply.github.com> Date: Fri, 25 Nov 2022 15:06:49 +0800 Subject: [PATCH 4/6] Refine Media Types (#441) * Update README.md * Create HL7v2-templates.md * Create HL7v2-FHIRValidator.md * Create HL7v2-ImportantPoints.md * Update README.md * Update README.md Updated STU3-R4 information for OSS FHIR Server * Fix StyleCop Dependency (#397) - Fix build-time dependency on StyleCop.Analyzers to no longer force consumers to use the same analyzers * Bump Newtonsoft.Json in /src/Microsoft.Health.Fhir.TemplateManagement Bumps [Newtonsoft.Json](https://github.com/JamesNK/Newtonsoft.Json) from 12.0.3 to 13.0.1. - [Release notes](https://github.com/JamesNK/Newtonsoft.Json/releases) - [Commits](https://github.com/JamesNK/Newtonsoft.Json/compare/12.0.3...13.0.1) --- updated-dependencies: - dependency-name: Newtonsoft.Json dependency-type: direct:production ... Signed-off-by: dependabot[bot] * Bump Newtonsoft.Json from 12.0.3 to 13.0.1 in src/Microsoft.Health.Fhir.TemplateManagement/Microsoft.Health.Fhir.Liquid.Converter.nuspec * Exclude data folder * Update the GeoPol * Update version * Remove StyleCop from Nuspec (#410) - Remove StyleCop from the nuspec to prevent forcing consumers to import StyleCop * Prepare new release for 5.0.3 (#413) * Fix more than 3 digits for milliseconds in datetime (#409) * fix * fix * Update version to 5.0.3 (#412) Co-authored-by: sowu880 <57981365+sowu880@users.noreply.github.com> * Add NJsonSchema dependency to nuspec file (#423) * Prepare new release for 5.0.5 (#425) * Update version to 5.0.5 (#424) * Interface to convert JObject (#416) * update * update * update * update * update * update * update * update * update * update Signed-off-by: dependabot[bot] Co-authored-by: yankunhuang-pku <66581117+yankunhuang-pku@users.noreply.github.com> Co-authored-by: ginalee-dotcom <68250213+ginalee-dotcom@users.noreply.github.com> Co-authored-by: Irene Joseph <90474712+irenepjoseph@users.noreply.github.com> Co-authored-by: Qiwei Jin Co-authored-by: Will Sugarman Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Yankun Huang Co-authored-by: Yue Fei Co-authored-by: Will Sugarman Co-authored-by: QuanWanxx <68055742+QuanWanxx@users.noreply.github.com> --- .../OCIArtifactFunctionalTests.cs | 60 ++++++++- .../TemplateCollectionFunctionalTests.cs | 126 ++++++++++++++++++ .../Client/OrasClientTests.cs | 71 +++++----- .../Models/DigestTest.cs | 29 ++++ .../OCIFileManagerTests.cs | 48 +++---- .../OrasUtility.cs | 71 ++++++++++ .../Client/ACRClient.cs | 11 +- .../Client/OrasClient.cs | 11 +- .../Constants.cs | 7 +- .../Models/Digest.cs | 5 + 10 files changed, 367 insertions(+), 72 deletions(-) create mode 100644 src/Microsoft.Health.Fhir.TemplateManagement.UnitTests/OrasUtility.cs diff --git a/src/Microsoft.Health.Fhir.TemplateManagement.FunctionalTests/OCIArtifactFunctionalTests.cs b/src/Microsoft.Health.Fhir.TemplateManagement.FunctionalTests/OCIArtifactFunctionalTests.cs index 3abd824ab..6d9e378d0 100644 --- a/src/Microsoft.Health.Fhir.TemplateManagement.FunctionalTests/OCIArtifactFunctionalTests.cs +++ b/src/Microsoft.Health.Fhir.TemplateManagement.FunctionalTests/OCIArtifactFunctionalTests.cs @@ -7,7 +7,6 @@ using System.IO; using System.Linq; using System.Threading.Tasks; -using EnsureThat; using Microsoft.Health.Fhir.TemplateManagement.Client; using Microsoft.Health.Fhir.TemplateManagement.Exceptions; using Microsoft.Health.Fhir.TemplateManagement.Models; @@ -18,7 +17,6 @@ namespace Microsoft.Health.Fhir.TemplateManagement.FunctionalTests { public class OciArtifactFunctionalTests : IAsyncLifetime { - private const string _orasCacheEnvironmentVariableName = "ORAS_CACHE"; private static readonly string _testTarGzPath = Path.Join("TestData", "TarGzFiles"); private readonly string _containerRegistryServer; private readonly string _baseLayerTemplatePath = Path.Join(_testTarGzPath, "layerbase.tar.gz"); @@ -31,8 +29,12 @@ public class OciArtifactFunctionalTests : IAsyncLifetime private readonly string _testMultiLayersWithValidSequenceNumberImageReference; private readonly string _testMultiLayersWithInValidSequenceNumberImageReference; private readonly string _testInvalidCompressedImageReference; + private string _testOneLayerImageDigest; + private string _testMultiLayerImageDigest; private bool _isOrasValid = true; private readonly string _orasErrorMessage = "Oras tool invalid."; + private const string _orasCacheEnvironmentVariableName = "ORAS_CACHE"; + private const string _defaultOrasCacheEnvironmentVariable = ".oras/cache"; public OciArtifactFunctionalTests() { @@ -43,6 +45,11 @@ public OciArtifactFunctionalTests() _testMultiLayersWithValidSequenceNumberImageReference = _containerRegistryServer + "/templatetest:multilayers_valid_sequence"; _testMultiLayersWithInValidSequenceNumberImageReference = _containerRegistryServer + "/templatetest:multilayers_invalid_sequence"; _testInvalidCompressedImageReference = _containerRegistryServer + "/templatetest:invalid_image"; + + if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable(_orasCacheEnvironmentVariableName))) + { + Environment.SetEnvironmentVariable(_orasCacheEnvironmentVariableName, _defaultOrasCacheEnvironmentVariable); + } } public async Task InitializeAsync() @@ -64,7 +71,7 @@ public Task DisposeAsync() private async Task PushOneLayerWithValidSequenceNumberAsync() { string command = $"push {_testOneLayerWithValidSequenceNumberImageReference} {_baseLayerTemplatePath}"; - await ExecuteOrasCommandAsync(command); + _testOneLayerImageDigest = await ExecuteOrasCommandAsync(command); } private async Task PushOneLayerWithoutSequenceNumberAsync() @@ -83,6 +90,7 @@ private async Task PushMultiLayersWithValidSequenceNumberAsync() { string command = $"push {_testMultiLayersWithValidSequenceNumberImageReference} {_baseLayerTemplatePath} {_userLayerTemplatePath}"; await ExecuteOrasCommandAsync(command); + _testMultiLayerImageDigest = await ExecuteOrasCommandAsync(command); } private async Task PushMultiLayersWithInValidSequenceNumberAsync() @@ -97,18 +105,32 @@ private async Task PushInvalidCompressedImageAsync() await ExecuteOrasCommandAsync(command); } - private async Task ExecuteOrasCommandAsync(string command) + private async Task ExecuteOrasCommandAsync(string command) { try { - await OrasClient.OrasExecutionAsync(command, Directory.GetCurrentDirectory()); + var output = await OrasClient.OrasExecutionAsync(command, Directory.GetCurrentDirectory()); + var digest = GetImageDigest(output); + return digest.Value; } catch { _isOrasValid = false; + return null; } } + private Digest GetImageDigest(string input) + { + var digests = Digest.GetDigest(input); + if (digests.Count == 0) + { + throw new OciClientException(TemplateManagementErrorCode.OrasProcessFailed, "Failed to parse image digest."); + } + + return digests[0]; + } + // Pull one layer image with valid sequence number, successfully pulled with base layer copied. [Fact] public async Task GivenOneLayerImage_WhenPulled_ArtifactsWillBePulledWithBaseLayerCopiedAsync() @@ -143,6 +165,20 @@ public async Task GivenOneLayerImageWithoutSequenceNumber_WhenPulled_ArtifactsWi DirectoryHelper.ClearFolder(outputFolder); } + [Fact] + public async Task GivenOneLayerImage_WhenPulledUsingDigest_ArtifactsWillBePulledWithBaseLayerCopiedAsync() + { + Assert.True(_isOrasValid, _orasErrorMessage); + string outputFolder = "TestData/testOneLayerWithDigest"; + DirectoryHelper.ClearFolder(outputFolder); + + var testManager = new OciFileManager(_containerRegistryServer, outputFolder); + await testManager.PullOciImageAsync("templatetest", _testOneLayerImageDigest, true); + Assert.Equal(843, Directory.EnumerateFiles(outputFolder, "*.*", SearchOption.AllDirectories).Count()); + Assert.Single(Directory.EnumerateFiles(Path.Combine(outputFolder, ".image", "base"), "*.tar.gz", SearchOption.AllDirectories)); + DirectoryHelper.ClearFolder(outputFolder); + } + // Pull one layer image with invalid sequence number, successfully pulled with base layer copied. [Fact] public async Task GivenOneLayerImageWithInvalidSequenceNumber_WhenPulled_ArtifactsWillBePulledWithBaseLayerCopiedAsync() @@ -194,6 +230,20 @@ public async Task GivenMultiLayersImageWithInvalidSequenceNumber_WhenPulled_Arti DirectoryHelper.ClearFolder(outputFolder); } + [Fact] + public async Task GivenMultiLayerImage_WhenPulledUsingDigest_ArtifactsWillBePulledWithBaseLayerCopiedAsync() + { + Assert.True(_isOrasValid, _orasErrorMessage); + string outputFolder = "TestData/testMultiLayerWithDigest"; + DirectoryHelper.ClearFolder(outputFolder); + + var testManager = new OciFileManager(_containerRegistryServer, outputFolder); + await testManager.PullOciImageAsync("templatetest", _testMultiLayerImageDigest, true); + Assert.Equal(10, Directory.EnumerateFiles(outputFolder, "*.*", SearchOption.AllDirectories).Count()); + Assert.Single(Directory.EnumerateFiles(Path.Combine(outputFolder, ".image", "base"), "*.tar.gz", SearchOption.AllDirectories)); + DirectoryHelper.ClearFolder(outputFolder); + } + // Pull invalid image, exception will be thrown. [Fact] public async Task GivenInvalidCompressedImage_WhenPulled_ExceptionWillBeThrownAsync() diff --git a/src/Microsoft.Health.Fhir.TemplateManagement.FunctionalTests/TemplateCollectionFunctionalTests.cs b/src/Microsoft.Health.Fhir.TemplateManagement.FunctionalTests/TemplateCollectionFunctionalTests.cs index c910a0156..f72b77cb6 100644 --- a/src/Microsoft.Health.Fhir.TemplateManagement.FunctionalTests/TemplateCollectionFunctionalTests.cs +++ b/src/Microsoft.Health.Fhir.TemplateManagement.FunctionalTests/TemplateCollectionFunctionalTests.cs @@ -18,8 +18,10 @@ using Microsoft.Health.Fhir.Liquid.Converter.Exceptions; using Microsoft.Health.Fhir.Liquid.Converter.Models; using Microsoft.Health.Fhir.Liquid.Converter.Processors; +using Microsoft.Health.Fhir.TemplateManagement.Client; using Microsoft.Health.Fhir.TemplateManagement.Exceptions; using Microsoft.Health.Fhir.TemplateManagement.Models; +using Microsoft.Health.Fhir.TemplateManagement.Utilities; using Newtonsoft.Json.Linq; using Xunit; @@ -40,13 +42,24 @@ public class TemplateCollectionFunctionalTests : IAsyncLifetime private readonly string _defaultStu3ToR4TemplateImageReference = "microsofthealth/stu3tor4templates:default"; private readonly string testOneLayerImageReference; private readonly string testMultiLayerImageReference; + private readonly string testOneLayerOCIImageReference; + private readonly string testMultiLayerOCIImageReference; private readonly string testInvalidImageReference; private readonly string testInvalidTemplateImageReference; + private string testOneLayerImageDigest; + private string testMultiLayerImageDigest; private readonly ContainerRegistry _containerRegistry = new ContainerRegistry(); private readonly ContainerRegistryInfo _containerRegistryInfo; private static readonly string _templateDirectory = Path.Join("..", "..", "data", "Templates"); private static readonly string _sampleDataDirectory = Path.Join("..", "..", "data", "SampleData"); + private static readonly string _testTarGzPath = Path.Join("TestData", "TarGzFiles"); + private readonly string _baseLayerTemplatePath = Path.Join(_testTarGzPath, "layerbase.tar.gz"); + private readonly string _userLayerTemplatePath = Path.Join(_testTarGzPath, "layer2.tar.gz"); private static readonly ProcessorSettings _processorSettings = new ProcessorSettings(); + private bool _isOrasValid = true; + private readonly string _orasErrorMessage = "Oras tool invalid."; + private const string _orasCacheEnvironmentVariableName = "ORAS_CACHE"; + private const string _defaultOrasCacheEnvironmentVariable = ".oras/cache"; public TemplateCollectionFunctionalTests() { @@ -60,7 +73,14 @@ public TemplateCollectionFunctionalTests() testMultiLayerImageReference = _containerRegistryInfo.ContainerRegistryServer + "/templatetest:multilayers"; testInvalidImageReference = _containerRegistryInfo.ContainerRegistryServer + "/templatetest:invalidlayers"; testInvalidTemplateImageReference = _containerRegistryInfo.ContainerRegistryServer + "/templatetest:invalidtemplateslayers"; + testOneLayerOCIImageReference = _containerRegistryInfo.ContainerRegistryServer + "/templatetest:ocionelayer"; + testMultiLayerOCIImageReference = _containerRegistryInfo.ContainerRegistryServer + "/templatetest:ocimultilayer"; token = "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes($"{_containerRegistryInfo.ContainerRegistryUsername}:{_containerRegistryInfo.ContainerRegistryPassword}")); + + if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable(_orasCacheEnvironmentVariableName))) + { + Environment.SetEnvironmentVariable(_orasCacheEnvironmentVariableName, _defaultOrasCacheEnvironmentVariable); + } } public async Task InitializeAsync() @@ -74,10 +94,15 @@ public async Task InitializeAsync() await InitMultiLayerImageAsync(); await InitInvalidTarGzImageAsync(); await InitInvalidTemplateImageAsync(); + + await OrasLogin(); + await PushOneLayerOCIImageAsync(); + await PushMultiLayersOCIImageAsync(); } public Task DisposeAsync() { + DirectoryHelper.ClearFolder(Environment.GetEnvironmentVariable(_orasCacheEnvironmentVariableName)); return Task.CompletedTask; } @@ -87,6 +112,12 @@ public static IEnumerable GetValidImageInfoWithTag() yield return new object[] { new List { 767, 838 }, "templatetest", "multilayers" }; } + public static IEnumerable GetValidOCIImageInfoWithTag() + { + yield return new object[] { new List { 838 }, "templatetest", "ocionelayer" }; + yield return new object[] { new List { 834, 838 }, "templatetest", "ocimultilayer" }; + } + public static IEnumerable GetHl7v2DataAndEntryTemplate() { var data = new List @@ -310,6 +341,50 @@ public async Task GiveImageReference_WhenGetTemplateCollection_ACorrectTemplateC } } + [Theory] + [MemberData(nameof(GetValidOCIImageInfoWithTag))] + public async Task GiveOCIImageReference_WhenGetTemplateCollection_ACorrectTemplateCollectionWillBeReturnedAsync(List expectedTemplatesCounts, string imageName, string tag) + { + if (_containerRegistryInfo == null) + { + return; + } + + Assert.True(_isOrasValid, _orasErrorMessage); + + string imageReference = string.Format("{0}/{1}:{2}", _containerRegistryInfo.ContainerRegistryServer, imageName, tag); + TemplateCollectionProviderFactory factory = new TemplateCollectionProviderFactory(cache, Options.Create(_config)); + var templateCollectionProvider = factory.CreateTemplateCollectionProvider(imageReference, token); + var templateCollection = await templateCollectionProvider.GetTemplateCollectionAsync(); + Assert.Equal(expectedTemplatesCounts.Count(), templateCollection.Count()); + for (var i = 0; i < expectedTemplatesCounts.Count(); i++) + { + Assert.Equal(expectedTemplatesCounts[i], templateCollection[i].Count()); + } + } + + [Fact] + public async Task GiveOCIImageReferenceWithDigest_WhenGetTemplateCollection_ACorrectTemplateCollectionWillBeReturnedAsync() + { + if (_containerRegistryInfo == null) + { + return; + } + + Assert.True(_isOrasValid, _orasErrorMessage); + + string imageReference = string.Format("{0}/{1}@{2}", _containerRegistryInfo.ContainerRegistryServer, "templatetest", testOneLayerImageDigest); + TemplateCollectionProviderFactory factory = new TemplateCollectionProviderFactory(cache, Options.Create(_config)); + var templateCollectionProvider = factory.CreateTemplateCollectionProvider(imageReference, token); + var templateCollection = await templateCollectionProvider.GetTemplateCollectionAsync(); + Assert.Single(templateCollection); + + imageReference = string.Format("{0}/{1}@{2}", _containerRegistryInfo.ContainerRegistryServer, "templatetest", testMultiLayerImageDigest); + templateCollectionProvider = factory.CreateTemplateCollectionProvider(imageReference, token); + templateCollection = await templateCollectionProvider.GetTemplateCollectionAsync(); + Assert.Equal(2, templateCollection.Count()); + } + [Theory] [MemberData(nameof(GetHl7v2DataAndEntryTemplate))] public async Task GetTemplateCollectionFromAcr_WhenGivenHl7v2DataForConverting__ExpectedFhirResourceShouldBeReturnedAsync(string hl7v2Data, string entryTemplate) @@ -486,5 +561,56 @@ private async Task InitInvalidTemplateImageAsync() List templateFiles = new List { invalidTemplatePath }; await _containerRegistry.GenerateTemplateImageAsync(_containerRegistryInfo, testInvalidTemplateImageReference, templateFiles); } + + private async Task PushOneLayerOCIImageAsync() + { + string command = $"push {testOneLayerOCIImageReference} {_baseLayerTemplatePath}"; + testOneLayerImageDigest = await ExecuteOrasCommandAsync(command); + } + + private async Task PushMultiLayersOCIImageAsync() + { + string command = $"push {testMultiLayerOCIImageReference} {_baseLayerTemplatePath} {_userLayerTemplatePath}"; + testMultiLayerImageDigest = await ExecuteOrasCommandAsync(command); + } + + private async Task OrasLogin() + { + try + { + var command = $"login {_containerRegistryInfo.ContainerRegistryServer} -u {_containerRegistryInfo.ContainerRegistryUsername} -p {_containerRegistryInfo.ContainerRegistryPassword}"; + await OrasClient.OrasExecutionAsync(command); + } + catch + { + _isOrasValid = false; + } + } + + private async Task ExecuteOrasCommandAsync(string command) + { + try + { + var output = await OrasClient.OrasExecutionAsync(command, Directory.GetCurrentDirectory()); + var digest = GetImageDigest(output); + return digest.Value; + } + catch + { + _isOrasValid = false; + return null; + } + } + + private Digest GetImageDigest(string input) + { + var digests = Digest.GetDigest(input); + if (digests.Count == 0) + { + throw new OciClientException(TemplateManagementErrorCode.OrasProcessFailed, "Failed to parse image digest."); + } + + return digests[0]; + } } } \ No newline at end of file diff --git a/src/Microsoft.Health.Fhir.TemplateManagement.UnitTests/Client/OrasClientTests.cs b/src/Microsoft.Health.Fhir.TemplateManagement.UnitTests/Client/OrasClientTests.cs index 6e89397ef..0a0f7d366 100644 --- a/src/Microsoft.Health.Fhir.TemplateManagement.UnitTests/Client/OrasClientTests.cs +++ b/src/Microsoft.Health.Fhir.TemplateManagement.UnitTests/Client/OrasClientTests.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Threading.Tasks; using Microsoft.Health.Fhir.TemplateManagement.Client; using Microsoft.Health.Fhir.TemplateManagement.Exceptions; @@ -19,26 +20,32 @@ public class OrasClientTests : IAsyncLifetime { private readonly string _containerRegistryServer; private readonly string _baseLayerTemplatePath = "TestData/TarGzFiles/baseLayer.tar.gz"; - private readonly string _userLayerTemplatePath = "TestData/TarGzFiles/userV1.tar.gz"; private readonly string _testOneLayerImageReference; - private readonly string _testMultiLayersImageReference; + private readonly string _testMultiLayerImageReference; + private string _testOneLayerImageDigest; + private string _testMultiLayerImageDigest; private bool _isOrasValid = true; + private const string _orasCacheEnvironmentVariableName = "ORAS_CACHE"; public OrasClientTests() { _containerRegistryServer = Environment.GetEnvironmentVariable("TestContainerRegistryServer"); _testOneLayerImageReference = _containerRegistryServer + "/templatetest:v1"; - _testMultiLayersImageReference = _containerRegistryServer + "/templatetest:v2"; + _testMultiLayerImageReference = _containerRegistryServer + "/templatetest:v2"; + + OrasUtility.InitOrasCache(); } public async Task InitializeAsync() { - await PushOneLayerImageAsync(); - await PushMultiLayersImageAsync(); + _testOneLayerImageDigest = await OrasUtility.PushOneLayerImageAsync(_testOneLayerImageReference); + _testMultiLayerImageDigest = await OrasUtility.PushMultiLayersImageAsync(_testMultiLayerImageReference); + _isOrasValid = !(_testOneLayerImageDigest == null || _testMultiLayerImageDigest == null); } public Task DisposeAsync() { + DirectoryHelper.ClearFolder(Environment.GetEnvironmentVariable(_orasCacheEnvironmentVariableName)); return Task.CompletedTask; } @@ -200,41 +207,41 @@ public async Task GivenAValidImageReference_WhenPullImageUseOras_ImageWillBePull return; } - DirectoryHelper.ClearFolder("TestData/PullTest"); + var folder = "TestData/PullTest"; + DirectoryHelper.ClearFolder(folder); + // Pull one layer image by tag string imageReference = _testOneLayerImageReference; - OrasClient orasClient = new OrasClient(_containerRegistryServer, "TestData/PullTest"); + OrasClient orasClient = new OrasClient(_containerRegistryServer, folder); var imageInfo = ImageInfo.CreateFromImageReference(imageReference); var ex = await Record.ExceptionAsync(async () => await orasClient.PullImageAsync(imageInfo.ImageName, imageInfo.Tag)); Assert.Null(ex); + Assert.Single(Directory.EnumerateFiles(folder, "*.*", SearchOption.AllDirectories)); - DirectoryHelper.ClearFolder("TestData/PullTest"); - } + DirectoryHelper.ClearFolder(folder); - private async Task PushOneLayerImageAsync() - { - string command = $"push {_testOneLayerImageReference} {_baseLayerTemplatePath}"; - try - { - await OrasClient.OrasExecutionAsync(command, Directory.GetCurrentDirectory()); - } - catch - { - _isOrasValid = false; - } - } + // Pull one layer image by digest + ex = await Record.ExceptionAsync(async () => await orasClient.PullImageAsync(imageInfo.ImageName, _testOneLayerImageDigest)); + Assert.Null(ex); + Assert.Single(Directory.EnumerateFiles(folder, "*.*", SearchOption.AllDirectories)); - private async Task PushMultiLayersImageAsync() - { - string command = $"push {_testMultiLayersImageReference} {_baseLayerTemplatePath} {_userLayerTemplatePath}"; - try - { - await OrasClient.OrasExecutionAsync(command, Directory.GetCurrentDirectory()); - } - catch - { - _isOrasValid = false; - } + DirectoryHelper.ClearFolder(folder); + + // Pull multi layer image by tag + imageReference = _testMultiLayerImageReference; + imageInfo = ImageInfo.CreateFromImageReference(imageReference); + ex = await Record.ExceptionAsync(async () => await orasClient.PullImageAsync(imageInfo.ImageName, imageInfo.Tag)); + Assert.Null(ex); + Assert.Equal(2, Directory.EnumerateFiles(folder, "*.*", SearchOption.AllDirectories).Count()); + + DirectoryHelper.ClearFolder(folder); + + // Pull multi layer image by digest + ex = await Record.ExceptionAsync(async () => await orasClient.PullImageAsync(imageInfo.ImageName, _testMultiLayerImageDigest)); + Assert.Null(ex); + Assert.Equal(2, Directory.EnumerateFiles(folder, "*.*", SearchOption.AllDirectories).Count()); + + DirectoryHelper.ClearFolder(folder); } } } \ No newline at end of file diff --git a/src/Microsoft.Health.Fhir.TemplateManagement.UnitTests/Models/DigestTest.cs b/src/Microsoft.Health.Fhir.TemplateManagement.UnitTests/Models/DigestTest.cs index 01d003ccb..37a67c952 100644 --- a/src/Microsoft.Health.Fhir.TemplateManagement.UnitTests/Models/DigestTest.cs +++ b/src/Microsoft.Health.Fhir.TemplateManagement.UnitTests/Models/DigestTest.cs @@ -48,6 +48,21 @@ public static IEnumerable GetInputStringContainsNoDigests() yield return new object[] { "sha256:d377125165eb6d770f" }; } + public static IEnumerable GetValidDigest() + { + yield return new object[] { "sha256:d377125165eb6d770f344429a7a55379d4028774aebe267fe620cd1fcd2daab7" }; + yield return new object[] { "sha256:123425165eb6d770f344429a7a55379d4028774aebe267fe620cd1fcd2daab7" }; + yield return new object[] { "sha128:123425165eb6d770f344429a7a55379d4028774aebe267fe620cd1fcd2daab7" }; + } + + public static IEnumerable GetInvalidDigest() + { + yield return new object[] { "sha256d377125165eb6d770f344429a7a55379d4028774aebe267fe620cd1fcd2daab7" }; + yield return new object[] { "256:123425165eb6d770f344429a7a55379d4028774aebe267fe620cd1fcd2daab7" }; + yield return new object[] { "sha128:55379d4028774ae:be267fe620cd1fcd2daab7" }; + yield return new object[] { "test" }; + } + [Theory] [MemberData(nameof(GetInputStringContainsDigests))] public void GivenAnInputStringContainsDigests_WhenGetDigest_CorrectDigestsWillBeReturned(string input, List expectedDigests) @@ -70,5 +85,19 @@ public void GivenAnInputStringWithoutDigests_WhenGetDigest_EmptyResultWillBeRetu var results = Digest.GetDigest(input); Assert.Empty(results); } + + [Theory] + [MemberData(nameof(GetValidDigest))] + public void GivenValidDigest_WhenCheckIsDigest_TrueWillBeReturned(string input) + { + Assert.True(Digest.IsDigest(input)); + } + + [Theory] + [MemberData(nameof(GetInvalidDigest))] + public void GivenInvalidDigest_WhenCheckIsDigest_FalseWillBeReturned(string input) + { + Assert.False(Digest.IsDigest(input)); + } } } diff --git a/src/Microsoft.Health.Fhir.TemplateManagement.UnitTests/OCIFileManagerTests.cs b/src/Microsoft.Health.Fhir.TemplateManagement.UnitTests/OCIFileManagerTests.cs index d1cddf990..1d55fcd1f 100644 --- a/src/Microsoft.Health.Fhir.TemplateManagement.UnitTests/OCIFileManagerTests.cs +++ b/src/Microsoft.Health.Fhir.TemplateManagement.UnitTests/OCIFileManagerTests.cs @@ -8,7 +8,6 @@ using System.IO; using System.Linq; using System.Threading.Tasks; -using Microsoft.Health.Fhir.TemplateManagement.Client; using Microsoft.Health.Fhir.TemplateManagement.Exceptions; using Microsoft.Health.Fhir.TemplateManagement.Models; using Microsoft.Health.Fhir.TemplateManagement.Utilities; @@ -19,27 +18,32 @@ namespace Microsoft.Health.Fhir.TemplateManagement.UnitTests public class OciFileManagerTests : IAsyncLifetime { private readonly string _containerRegistryServer; - private readonly string _baseLayerTemplatePath = "TestData/TarGzFiles/layer1.tar.gz"; - private readonly string _userLayerTemplatePath = "TestData/TarGzFiles/layer2.tar.gz"; private readonly string _testOneLayerImageReference; private readonly string _testMultiLayersImageReference; + private string _testOneLayerImageDigest; + private string _testMultiLayerImageDigest; private bool _isOrasValid = true; + private const string _orasCacheEnvironmentVariableName = "ORAS_CACHE"; public OciFileManagerTests() { _containerRegistryServer = Environment.GetEnvironmentVariable("TestContainerRegistryServer"); _testOneLayerImageReference = _containerRegistryServer + "/templatetest:user1"; _testMultiLayersImageReference = _containerRegistryServer + "/templatetest:user2"; + + OrasUtility.InitOrasCache(); } public async Task InitializeAsync() { - await PushOneLayerImageAsync(); - await PushMultiLayersImageAsync(); + _testOneLayerImageDigest = await OrasUtility.PushOneLayerImageAsync(_testOneLayerImageReference); + _testMultiLayerImageDigest = await OrasUtility.PushMultiLayersImageAsync(_testMultiLayersImageReference); + _isOrasValid = !(_testOneLayerImageDigest == null || _testMultiLayerImageDigest == null); } public Task DisposeAsync() { + DirectoryHelper.ClearFolder(Environment.GetEnvironmentVariable(_orasCacheEnvironmentVariableName)); return Task.CompletedTask; } @@ -109,6 +113,10 @@ public async Task GivenValidOutputFolder_WhenPullOciFiles_CorrectFilesWillBePull await testManager.PullOciImageAsync(imageInfo.ImageName, imageInfo.Tag, true); Assert.Equal(843, Directory.EnumerateFiles(outputFolder, "*.*", SearchOption.AllDirectories).Count()); DirectoryHelper.ClearFolder(outputFolder); + + await testManager.PullOciImageAsync(imageInfo.ImageName, _testOneLayerImageDigest, true); + Assert.Equal(843, Directory.EnumerateFiles(outputFolder, "*.*", SearchOption.AllDirectories).Count()); + DirectoryHelper.ClearFolder(outputFolder); } [Fact] @@ -126,6 +134,10 @@ public async Task GivenAnImageReferenceAndOutputFolder_WhenPullOciFiles_CorrectF await testManager.PullOciImageAsync(imageInfo.ImageName, imageInfo.Tag, true); Assert.Equal(10, Directory.EnumerateFiles(outputFolder, "*.*", SearchOption.AllDirectories).Count()); DirectoryHelper.ClearFolder(outputFolder); + + await testManager.PullOciImageAsync(imageInfo.ImageName, _testMultiLayerImageDigest, true); + Assert.Equal(10, Directory.EnumerateFiles(outputFolder, "*.*", SearchOption.AllDirectories).Count()); + DirectoryHelper.ClearFolder(outputFolder); } [Fact] @@ -143,31 +155,5 @@ public async Task GivenAnImageReferenceAndInputFolder_WhenPushOciFiles_CorrectIm var ex = await Record.ExceptionAsync(async () => await testManager.PushOciImageAsync(imageInfo.ImageName, imageInfo.Tag, true)); Assert.Null(ex); } - - private async Task PushOneLayerImageAsync() - { - string command = $"push {_testOneLayerImageReference} {_baseLayerTemplatePath}"; - try - { - await OrasClient.OrasExecutionAsync(command, Directory.GetCurrentDirectory()); - } - catch - { - _isOrasValid = false; - } - } - - private async Task PushMultiLayersImageAsync() - { - string command = $"push {_testMultiLayersImageReference} {_baseLayerTemplatePath} {_userLayerTemplatePath}"; - try - { - await OrasClient.OrasExecutionAsync(command, Directory.GetCurrentDirectory()); - } - catch - { - _isOrasValid = false; - } - } } } diff --git a/src/Microsoft.Health.Fhir.TemplateManagement.UnitTests/OrasUtility.cs b/src/Microsoft.Health.Fhir.TemplateManagement.UnitTests/OrasUtility.cs new file mode 100644 index 000000000..ee7d59eae --- /dev/null +++ b/src/Microsoft.Health.Fhir.TemplateManagement.UnitTests/OrasUtility.cs @@ -0,0 +1,71 @@ +// ------------------------------------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------------------------------------------- + +using System; +using System.IO; +using System.Threading.Tasks; +using Microsoft.Health.Fhir.TemplateManagement.Client; +using Microsoft.Health.Fhir.TemplateManagement.Exceptions; +using Microsoft.Health.Fhir.TemplateManagement.Models; + +namespace Microsoft.Health.Fhir.TemplateManagement.UnitTests +{ + public static class OrasUtility + { + private static readonly string _baseLayerTemplatePath = "TestData/TarGzFiles/layer1.tar.gz"; + private static readonly string _userLayerTemplatePath = "TestData/TarGzFiles/layer2.tar.gz"; + private const string _orasCacheEnvironmentVariableName = "ORAS_CACHE"; + private const string _defaultOrasCacheEnvironmentVariable = ".oras/cache"; + + public static void InitOrasCache() + { + if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable(_orasCacheEnvironmentVariableName))) + { + Environment.SetEnvironmentVariable(_orasCacheEnvironmentVariableName, _defaultOrasCacheEnvironmentVariable); + } + } + + public static async Task PushOneLayerImageAsync(string testOneLayerImageReference) + { + string command = $"push {testOneLayerImageReference} {_baseLayerTemplatePath}"; + try + { + var output = await OrasClient.OrasExecutionAsync(command, Directory.GetCurrentDirectory()); + var digest = GetImageDigest(output); + return digest.Value; + } + catch + { + return null; + } + } + + public static async Task PushMultiLayersImageAsync(string testMultiLayersImageReference) + { + string command = $"push {testMultiLayersImageReference} {_baseLayerTemplatePath} {_userLayerTemplatePath}"; + try + { + var output = await OrasClient.OrasExecutionAsync(command, Directory.GetCurrentDirectory()); + var digest = GetImageDigest(output); + return digest.Value; + } + catch + { + return null; + } + } + + private static Digest GetImageDigest(string input) + { + var digests = Digest.GetDigest(input); + if (digests.Count == 0) + { + throw new OciClientException(TemplateManagementErrorCode.OrasProcessFailed, "Failed to parse image digest."); + } + + return digests[0]; + } + } +} diff --git a/src/Microsoft.Health.Fhir.TemplateManagement/Client/ACRClient.cs b/src/Microsoft.Health.Fhir.TemplateManagement/Client/ACRClient.cs index 70e93b9f9..c4811e118 100644 --- a/src/Microsoft.Health.Fhir.TemplateManagement/Client/ACRClient.cs +++ b/src/Microsoft.Health.Fhir.TemplateManagement/Client/ACRClient.cs @@ -4,6 +4,7 @@ // ------------------------------------------------------------------------------------------------- using System; +using System.Collections.Generic; using System.IO; using System.Net; using System.Threading; @@ -23,6 +24,14 @@ public class AcrClient : IOciClient { private readonly IAzureContainerRegistryClient _client; + // Accept media type for manifest. + private readonly List _acceptedManifestMediatype = + new List() + { + Constants.V2MediaTypeManifest, + Constants.OCIMediaTypeImageManifest, + }; + public AcrClient(string registry, string token) { EnsureArg.IsNotNull(registry, nameof(registry)); @@ -76,7 +85,7 @@ public async Task GetManifestAsync(string imageName, string ref try { cancellationToken.ThrowIfCancellationRequested(); - var manifestInfo = await _client.Manifests.GetAsync(imageName, reference, Constants.MediatypeV2Manifest, cancellationToken); + var manifestInfo = await _client.Manifests.GetAsync(imageName, reference, string.Join(",", _acceptedManifestMediatype), cancellationToken); return manifestInfo; } catch (TemplateManagementException) diff --git a/src/Microsoft.Health.Fhir.TemplateManagement/Client/OrasClient.cs b/src/Microsoft.Health.Fhir.TemplateManagement/Client/OrasClient.cs index 91eb06fe1..d9c1a73c4 100644 --- a/src/Microsoft.Health.Fhir.TemplateManagement/Client/OrasClient.cs +++ b/src/Microsoft.Health.Fhir.TemplateManagement/Client/OrasClient.cs @@ -42,7 +42,16 @@ public async Task PullImageAsync(string name, string reference, C DirectoryHelper.ClearFolder(_imageFolder); - string imageReference = string.Format("{0}/{1}:{2}", _registry, name, reference); + string imageReference; + if (Digest.IsDigest(reference)) + { + imageReference = string.Format("{0}/{1}@{2}", _registry, name, reference); + } + else + { + imageReference = string.Format("{0}/{1}:{2}", _registry, name, reference); + } + string command = $"pull \"{imageReference}\" -o \"{_imageFolder}\""; string output = await OrasExecutionAsync(command, null); diff --git a/src/Microsoft.Health.Fhir.TemplateManagement/Constants.cs b/src/Microsoft.Health.Fhir.TemplateManagement/Constants.cs index 5f5cb73c1..c70e77e0a 100644 --- a/src/Microsoft.Health.Fhir.TemplateManagement/Constants.cs +++ b/src/Microsoft.Health.Fhir.TemplateManagement/Constants.cs @@ -7,8 +7,11 @@ namespace Microsoft.Health.Fhir.TemplateManagement { internal static class Constants { - // Accept media type for manifest. - internal const string MediatypeV2Manifest = "application/vnd.docker.distribution.manifest.v2+json"; + // Accepted media type for manifest. https://github.com/distribution/distribution/blob/main/docs/spec/manifest-v2-2.md + internal const string V2MediaTypeManifest = "application/vnd.docker.distribution.manifest.v2+json"; + + // Accepted media type for OCI manifest https://github.com/opencontainers/image-spec/blob/main/manifest.md#image-manifest + internal const string OCIMediaTypeImageManifest = "application/vnd.oci.image.manifest.v1+json"; internal const string ImageReferenceFormat = "{0}/{1}:{2}"; diff --git a/src/Microsoft.Health.Fhir.TemplateManagement/Models/Digest.cs b/src/Microsoft.Health.Fhir.TemplateManagement/Models/Digest.cs index e86e426a0..696505ed0 100644 --- a/src/Microsoft.Health.Fhir.TemplateManagement/Models/Digest.cs +++ b/src/Microsoft.Health.Fhir.TemplateManagement/Models/Digest.cs @@ -33,5 +33,10 @@ public static List GetDigest(string input) return digests; } + + public static bool IsDigest(string input) + { + return _digestRegex.IsMatch(input); + } } } From 927fff1a44f41faa6699ba47a6e43611e7586a33 Mon Sep 17 00:00:00 2001 From: sowu880 <57981365+sowu880@users.noreply.github.com> Date: Fri, 25 Nov 2022 15:59:16 +0800 Subject: [PATCH 5/6] update (#442) --- release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/release.yml b/release.yml index d30eaff2f..35c9f32ed 100644 --- a/release.yml +++ b/release.yml @@ -17,8 +17,8 @@ variables: functionalTests: "**/*FunctionalTests/*.csproj" buildConfiguration: 'Release' major: 5 - minor: 0 - patch: 5 + minor: 1 + patch: 0 buildnum: $[counter(format('{0}.{1}.{2}',variables['major'],variables['minor'], variables['patch']), 1)] version: $(major).$(minor).$(patch).$(buildnum) From 0f6cbc7bae43909239598062670a85ffb9bd7ed7 Mon Sep 17 00:00:00 2001 From: Songrui Wu Date: Fri, 25 Nov 2022 17:52:56 +0800 Subject: [PATCH 6/6] update --- .../OCIArtifactFunctionalTests.cs | 1 - .../TemplateCollectionFunctionalTests.cs | 1 - .../Client/OrasClientTests.cs | 1 - .../OCIFileManagerTests.cs | 1 - 4 files changed, 4 deletions(-) diff --git a/src/Microsoft.Health.Fhir.TemplateManagement.FunctionalTests/OCIArtifactFunctionalTests.cs b/src/Microsoft.Health.Fhir.TemplateManagement.FunctionalTests/OCIArtifactFunctionalTests.cs index 6d9e378d0..a75c3ec22 100644 --- a/src/Microsoft.Health.Fhir.TemplateManagement.FunctionalTests/OCIArtifactFunctionalTests.cs +++ b/src/Microsoft.Health.Fhir.TemplateManagement.FunctionalTests/OCIArtifactFunctionalTests.cs @@ -64,7 +64,6 @@ public async Task InitializeAsync() public Task DisposeAsync() { - DirectoryHelper.ClearFolder(Environment.GetEnvironmentVariable(_orasCacheEnvironmentVariableName)); return Task.CompletedTask; } diff --git a/src/Microsoft.Health.Fhir.TemplateManagement.FunctionalTests/TemplateCollectionFunctionalTests.cs b/src/Microsoft.Health.Fhir.TemplateManagement.FunctionalTests/TemplateCollectionFunctionalTests.cs index f72b77cb6..6e67778b3 100644 --- a/src/Microsoft.Health.Fhir.TemplateManagement.FunctionalTests/TemplateCollectionFunctionalTests.cs +++ b/src/Microsoft.Health.Fhir.TemplateManagement.FunctionalTests/TemplateCollectionFunctionalTests.cs @@ -102,7 +102,6 @@ public async Task InitializeAsync() public Task DisposeAsync() { - DirectoryHelper.ClearFolder(Environment.GetEnvironmentVariable(_orasCacheEnvironmentVariableName)); return Task.CompletedTask; } diff --git a/src/Microsoft.Health.Fhir.TemplateManagement.UnitTests/Client/OrasClientTests.cs b/src/Microsoft.Health.Fhir.TemplateManagement.UnitTests/Client/OrasClientTests.cs index 0a0f7d366..fabecf567 100644 --- a/src/Microsoft.Health.Fhir.TemplateManagement.UnitTests/Client/OrasClientTests.cs +++ b/src/Microsoft.Health.Fhir.TemplateManagement.UnitTests/Client/OrasClientTests.cs @@ -45,7 +45,6 @@ public async Task InitializeAsync() public Task DisposeAsync() { - DirectoryHelper.ClearFolder(Environment.GetEnvironmentVariable(_orasCacheEnvironmentVariableName)); return Task.CompletedTask; } diff --git a/src/Microsoft.Health.Fhir.TemplateManagement.UnitTests/OCIFileManagerTests.cs b/src/Microsoft.Health.Fhir.TemplateManagement.UnitTests/OCIFileManagerTests.cs index 1d55fcd1f..a610b5c6e 100644 --- a/src/Microsoft.Health.Fhir.TemplateManagement.UnitTests/OCIFileManagerTests.cs +++ b/src/Microsoft.Health.Fhir.TemplateManagement.UnitTests/OCIFileManagerTests.cs @@ -43,7 +43,6 @@ public async Task InitializeAsync() public Task DisposeAsync() { - DirectoryHelper.ClearFolder(Environment.GetEnvironmentVariable(_orasCacheEnvironmentVariableName)); return Task.CompletedTask; }