1
- using Microsoft . VisualBasic ;
2
1
2
+ using NuGet . Packaging ;
3
+ using NuGet . RuntimeModel ;
3
4
using System . Diagnostics ;
4
5
using System . IO . Compression ;
5
6
using System . Net ;
@@ -23,7 +24,7 @@ public record struct ManifestV2(int schemaVersion, string tag, string mediaType,
23
24
// public enum GoOS { linux, windows };
24
25
// not a complete list, only the subset that we support
25
26
// public enum GoArch { amd64, arm , arm64, [JsonStringEnumMember("386")] x386 };
26
- public record struct PlatformInformation ( string architecture , string os , string ? variant , string [ ] features ) ;
27
+ public record struct PlatformInformation ( string architecture , string os , string ? variant , string [ ] features , [ property : JsonPropertyName ( "os.version" ) ] [ field : JsonPropertyName ( "os.version" ) ] string ? version ) ;
27
28
public record struct PlatformSpecificManifest ( string mediaType , long size , string digest , PlatformInformation platform ) ;
28
29
public record struct ManifestListV2 ( int schemaVersion , string mediaType , PlatformSpecificManifest [ ] manifests ) ;
29
30
@@ -35,14 +36,6 @@ public record Registry(Uri BaseUri)
35
36
private const string DockerContainerV1 = "application/vnd.docker.container.image.v1+json" ;
36
37
private const int MaxChunkSizeBytes = 1024 * 64 ;
37
38
38
- public static string [ ] SupportedRuntimeIdentifiers = new [ ] {
39
- "linux-x86" ,
40
- "linux-x64" ,
41
- "linux-arm" ,
42
- "linux-arm64" ,
43
- "win-x64"
44
- } ;
45
-
46
39
private string RegistryName { get ; } = BaseUri . Host ;
47
40
48
41
public async Task < Image ? > GetImageManifest ( string name , string reference , string runtimeIdentifier )
@@ -83,29 +76,83 @@ async Task<HttpResponseMessage> GetBlob(string digest) {
83
76
}
84
77
85
78
async Task < Image ? > TryPickBestImageFromManifestList ( ManifestListV2 manifestList , string runtimeIdentifier ) {
86
- // TODO: we probably need to pull in actual RID parsing code and look for 'platform' here.
87
- // 'win' can take a version number and we'd break.
88
- // Also, there are more specific linux RIDs (like rhel) that we should instead be looking for the correct 'family' for?
89
- // we probably also need to look at the 'variant' field if the RID contains a version.
90
- ( string os , string arch , string ? variant ) = runtimeIdentifier . Split ( '-' ) switch {
91
- [ "linux" , "x64" ] => ( "linux" , "amd64" , null ) ,
92
- [ "linux" , "x86" ] => ( "linux" , "386" , null ) ,
93
- [ "linux" , "arm" ] => ( "linux" , "arm" , "v7" ) ,
94
- [ "linux" , "arm64" ] => ( "linux" , "arm64" , "v8" ) ,
95
- [ "win" , "x64" ] => ( "windows" , "amd64" , null ) ,
96
- var parts => throw new ArgumentException ( $ "The runtimeIdentifier '{ runtimeIdentifier } ' is not supported. The supported RuntimeIdentifiers are { Registry . SupportedRuntimeIdentifiers } .")
97
- } ;
98
-
99
- var potentialManifest = manifestList . manifests . SingleOrDefault ( manifest => manifest . platform . os == os && manifest . platform . architecture == arch && manifest . platform . variant == variant ) ;
100
- if ( potentialManifest != default ) {
101
- var manifestResponse = await GetManifest ( potentialManifest . digest ) ;
102
- return await TryReadSingleImage ( await manifestResponse . Content . ReadFromJsonAsync < ManifestV2 > ( ) ) ;
103
- } else {
104
- return null ;
79
+ var runtimeGraph = GetRuntimeGraphForDotNet ( ) ;
80
+ var compatibleRuntimesForUserRid = runtimeGraph . ExpandRuntime ( runtimeIdentifier ) ;
81
+ var ( ridDict , graphForManifestList ) = ConstructRuntimeGraphForManifestList ( manifestList , runtimeGraph ) ;
82
+ var bestManifestRid = PickBestRidFromCompatibleUserRids ( graphForManifestList , compatibleRuntimesForUserRid ) ;
83
+ if ( bestManifestRid is null ) {
84
+ throw new ArgumentException ( $ "The runtimeIdentifier '{ runtimeIdentifier } ' is not supported. The supported RuntimeIdentifiers for the base image { name } :{ reference } are { String . Join ( "," , compatibleRuntimesForUserRid ) } ") ;
105
85
}
86
+ var matchingManifest = ridDict [ bestManifestRid ] ;
87
+ var manifestResponse = await GetManifest ( matchingManifest . digest ) ;
88
+ return await TryReadSingleImage ( await manifestResponse . Content . ReadFromJsonAsync < ManifestV2 > ( ) ) ;
106
89
}
107
90
}
108
91
92
+ private string ? PickBestRidFromCompatibleUserRids ( RuntimeGraph graphForManifestList , IEnumerable < string > compatibleRuntimesForUserRid )
93
+ {
94
+ return compatibleRuntimesForUserRid . First ( rid => graphForManifestList . Runtimes . ContainsKey ( rid ) ) ;
95
+ }
96
+
97
+ private ( IReadOnlyDictionary < string , PlatformSpecificManifest > , RuntimeGraph ) ConstructRuntimeGraphForManifestList ( ManifestListV2 manifestList , RuntimeGraph dotnetRuntimeGraph )
98
+ {
99
+ var ridDict = new Dictionary < string , PlatformSpecificManifest > ( ) ;
100
+ var runtimeDescriptionSet = new HashSet < RuntimeDescription > ( ) ;
101
+ foreach ( var manifest in manifestList . manifests ) {
102
+ if ( CreateRidForPlatform ( manifest . platform ) is { } rid )
103
+ {
104
+ if ( ridDict . TryAdd ( rid , manifest ) )
105
+ {
106
+ AddRidAndDescendentsToSet ( runtimeDescriptionSet , rid , dotnetRuntimeGraph ) ;
107
+ }
108
+ }
109
+ }
110
+
111
+ // todo: inheritance relationships for these RIDs?
112
+ var graph = new RuntimeGraph ( runtimeDescriptionSet ) ;
113
+ return ( ridDict , graph ) ;
114
+ }
115
+
116
+ private void AddRidAndDescendentsToSet ( HashSet < RuntimeDescription > runtimeDescriptionSet , string rid , RuntimeGraph dotnetRuntimeGraph )
117
+ {
118
+ var R = dotnetRuntimeGraph . Runtimes [ rid ] ;
119
+ runtimeDescriptionSet . Add ( R ) ;
120
+ foreach ( var r in R . InheritedRuntimes ) AddRidAndDescendentsToSet ( runtimeDescriptionSet , r , dotnetRuntimeGraph ) ;
121
+ }
122
+
123
+ private string ? CreateRidForPlatform ( PlatformInformation platform )
124
+ {
125
+ var osPart = platform . os switch
126
+ {
127
+ "linux" => "linux" ,
128
+ "windows" => "win" ,
129
+ _ => null
130
+ } ;
131
+ // TODO: this part needs a lot of work, the RID graph isn't super precise here and version numbers (especially on windows) are _whack_
132
+ var versionPart = platform . version ? . Split ( '.' ) switch
133
+ {
134
+ [ var major , .. ] => major ,
135
+ _ => null
136
+ } ;
137
+ var platformPart = platform . architecture switch
138
+ {
139
+ "amd64" => "x64" ,
140
+ "x386" => "x86" ,
141
+ "arm" => $ "arm{ ( platform . variant != "v7" ? platform . variant : "" ) } ",
142
+ "arm64" => "arm64"
143
+ } ;
144
+
145
+
146
+ if ( osPart is null || platformPart is null ) return null ;
147
+ return $ "{ osPart } { versionPart ?? "" } -{ platformPart } ";
148
+ }
149
+
150
+ private RuntimeGraph GetRuntimeGraphForDotNet ( )
151
+ {
152
+ var runtimePath = Path . Combine ( "C:" , "Program Files" , "dotnet" , "sdk" , "7.0.100" , "RuntimeIdentifierGraph.json" ) ; // how to get this at runtime?
153
+ return JsonRuntimeFormat . ReadRuntimeGraph ( runtimePath ) ;
154
+ }
155
+
109
156
/// <summary>
110
157
/// Ensure a blob associated with <paramref name="name"/> from the registry is available locally.
111
158
/// </summary>
0 commit comments