1
1
namespace Microsoft . NET . Build . Containers ;
2
2
3
+ using System ;
3
4
using System . Diagnostics . CodeAnalysis ;
4
- using System . Text . Json ;
5
5
using System . Text . RegularExpressions ;
6
6
7
+ record Label ( string name , string value ) ;
8
+
9
+ // Explicitly lowercase to ease parsing - the incoming values are
10
+ // lowercased by spec
11
+ public enum PortType
12
+ {
13
+ tcp ,
14
+ udp
15
+ }
16
+
17
+ public record Port ( int number , PortType type ) ;
18
+
7
19
public static class ContainerHelpers
8
20
{
9
21
private static Regex imageTagRegex = new Regex ( @"^[a-zA-Z0-9_][a-zA-Z0-9._-]{0,127}$" ) ;
@@ -13,7 +25,8 @@ public static class ContainerHelpers
13
25
/// <summary>
14
26
/// Matches if the string is not lowercase or numeric, or ., _, or -.
15
27
/// </summary>
16
- private static Regex imageNameCharacters = new Regex ( @"[^a-z0-9._\-/]" ) ;
28
+ /// <remarks>Technically the period should be allowed as well, but due to inconsistent support between cloud providers we're removing it.</remarks>
29
+ private static Regex imageNameCharacters = new Regex ( @"[^a-z0-9_\-/]" ) ;
17
30
18
31
/// <summary>
19
32
/// Given some "fully qualified" image name (e.g. mcr.microsoft.com/dotnet/runtime), return
@@ -125,7 +138,8 @@ public static bool TryParseFullyQualifiedContainerName(string fullyQualifiedCont
125
138
/// <summary>
126
139
/// Checks if a given container image name adheres to the image name spec. If not, and recoverable, then normalizes invalid characters.
127
140
/// </summary>
128
- public static bool NormalizeImageName ( string containerImageName , [ NotNullWhen ( false ) ] out string ? normalizedImageName )
141
+ public static bool NormalizeImageName ( string containerImageName ,
142
+ [ NotNullWhen ( false ) ] out string ? normalizedImageName )
129
143
{
130
144
if ( IsValidImageName ( containerImageName ) )
131
145
{
@@ -194,13 +208,16 @@ public static bool TryParsePort(string? portNumber, string? portType, [NotNullWh
194
208
public static bool TryParsePort ( string input , [ NotNullWhen ( true ) ] out Port ? port , [ NotNullWhen ( false ) ] out ParsePortError ? error )
195
209
{
196
210
var parts = input . Split ( '/' ) ;
197
- if ( parts is [ var portNumber , var type ] )
211
+ if ( parts . Length == 2 )
198
212
{
213
+ string portNumber = parts [ 0 ] ;
214
+ string type = parts [ 1 ] ;
199
215
return TryParsePort ( portNumber , type , out port , out error ) ;
200
216
}
201
- else if ( parts is [ var portNo ] )
217
+ else if ( parts . Length == 1 )
202
218
{
203
- return TryParsePort ( portNo , null , out port , out error ) ;
219
+ string portNum = parts [ 0 ] ;
220
+ return TryParsePort ( portNum , null , out port , out error ) ;
204
221
}
205
222
else
206
223
{
@@ -209,74 +226,4 @@ public static bool TryParsePort(string input, [NotNullWhen(true)] out Port? port
209
226
return false ;
210
227
}
211
228
}
212
-
213
- public static async Task Containerize ( DirectoryInfo folder , string workingDir , string registryName , string baseName , string baseTag , string [ ] entrypoint , string [ ] entrypointArgs , string imageName , string [ ] imageTags , string outputRegistry , string [ ] labels , Port [ ] exposedPorts )
214
- {
215
- Registry baseRegistry = new Registry ( new Uri ( registryName ) ) ;
216
-
217
- Console . WriteLine ( $ "Reading from { baseRegistry . BaseUri } ") ;
218
-
219
- Image img = await baseRegistry . GetImageManifest ( baseName , baseTag ) ;
220
- img . WorkingDirectory = workingDir ;
221
-
222
- JsonSerializerOptions options = new ( )
223
- {
224
- WriteIndented = true ,
225
- } ;
226
-
227
- Console . WriteLine ( $ "Copying from { folder . FullName } to { workingDir } ") ;
228
- Layer l = Layer . FromDirectory ( folder . FullName , workingDir ) ;
229
-
230
- img . AddLayer ( l ) ;
231
-
232
- img . SetEntrypoint ( entrypoint , entrypointArgs ) ;
233
-
234
- var isDockerPush = outputRegistry . StartsWith ( "docker://" ) ;
235
- Registry ? outputReg = isDockerPush ? null : new Registry ( new Uri ( outputRegistry ) ) ;
236
-
237
- foreach ( var label in labels )
238
- {
239
- string [ ] labelPieces = label . Split ( '=' ) ;
240
-
241
- // labels are validated by System.CommandLine API
242
- img . Label ( labelPieces [ 0 ] , labelPieces [ 1 ] ) ;
243
- }
244
-
245
- foreach ( var ( number , type ) in exposedPorts )
246
- {
247
- // ports are validated by System.CommandLine API
248
- img . ExposePort ( number , type ) ;
249
- }
250
-
251
- foreach ( var tag in imageTags )
252
- {
253
- if ( isDockerPush )
254
- {
255
- try
256
- {
257
- LocalDocker . Load ( img , imageName , tag , baseName ) . Wait ( ) ;
258
- Console . WriteLine ( "Pushed container '{0}:{1}' to Docker daemon" , imageName , tag ) ;
259
- }
260
- catch ( AggregateException ex ) when ( ex . InnerException is DockerLoadException dle )
261
- {
262
- Console . WriteLine ( dle ) ;
263
- Environment . ExitCode = - 1 ;
264
- }
265
- }
266
- else
267
- {
268
- try
269
- {
270
- Console . WriteLine ( $ "Trying to push container '{ imageName } :{ tag } ' to registry '{ outputRegistry } '") ;
271
- outputReg ? . Push ( img , imageName , tag , imageName ) . Wait ( ) ;
272
- Console . WriteLine ( $ "Pushed container '{ imageName } :{ tag } ' to registry '{ outputRegistry } '") ;
273
- }
274
- catch ( Exception e )
275
- {
276
- Console . WriteLine ( "Failed to push to output registry: {0}" , e ) ;
277
- Environment . ExitCode = - 1 ;
278
- }
279
- }
280
- }
281
- }
282
229
}
0 commit comments