1
1
// Licensed to the .NET Foundation under one or more agreements.
2
2
// The .NET Foundation licenses this file to you under the MIT license.
3
3
4
+ using System . Security . Cryptography . Xml ;
5
+ using System . Threading ;
4
6
using Microsoft . DotNet . Cli . Utils ;
5
7
using Microsoft . Extensions . Logging ;
6
- using Microsoft . Extensions . Logging . Abstractions ;
7
8
using Microsoft . NET . TestFramework ;
8
9
using Microsoft . NET . TestFramework . Assertions ;
9
- using Microsoft . NET . TestFramework . Commands ;
10
10
using Xunit . Abstractions ;
11
11
12
12
namespace Microsoft . NET . Build . Containers . IntegrationTests ;
@@ -29,29 +29,69 @@ public static void StartAndPopulateDockerRegistry(ITestOutputHelper testOutput)
29
29
{
30
30
using TestLoggerFactory loggerFactory = new ( testOutput ) ;
31
31
32
- testOutput . WriteLine ( "Spawning local registry" ) ;
33
32
if ( ! new DockerCli ( loggerFactory ) . IsAvailable ( ) ) {
34
33
throw new InvalidOperationException ( "Docker is not available, tests cannot run" ) ;
35
34
}
36
- CommandResult processResult = ContainerCli . RunCommand ( testOutput , "--rm" , "--publish" , "5010:5000" , "--detach" , "docker.io/library/registry:2" ) . Execute ( ) ;
37
- processResult . Should ( ) . Pass ( ) . And . HaveStdOut ( ) ;
38
- using var reader = new StringReader ( processResult . StdOut ! ) ;
39
- s_registryContainerId = reader . ReadLine ( ) ;
40
35
41
- foreach ( var tag in new [ ] { Net6ImageTag , Net7ImageTag , Net8PreviewImageTag } )
36
+ ILogger logger = loggerFactory . CreateLogger ( "Docker Registry Init" ) ;
37
+
38
+ const int spawnRegistryMaxRetry = 5 ;
39
+ int spawnRegistryDelay = 1000 ;
40
+
41
+ for ( int spawnRegistryAttempt = 1 ; spawnRegistryAttempt <= spawnRegistryMaxRetry ; spawnRegistryAttempt ++ )
42
42
{
43
- ContainerCli . PullCommand ( testOutput , $ " { BaseImageSource } { RuntimeBaseImage } : { tag } " )
44
- . Execute ( )
45
- . Should ( ) . Pass ( ) ;
43
+ try
44
+ {
45
+ logger . LogInformation ( $ "Spawning local registry at ' { LocalRegistry } , attempt # { spawnRegistryAttempt } ." ) ;
46
46
47
- ContainerCli . TagCommand ( testOutput , $ "{ BaseImageSource } { RuntimeBaseImage } :{ tag } ", $ "{ LocalRegistry } /{ RuntimeBaseImage } :{ tag } ")
48
- . Execute ( )
49
- . Should ( ) . Pass ( ) ;
47
+ CommandResult processResult = ContainerCli . RunCommand ( testOutput , "--rm" , "--publish" , "5010:5000" , "--detach" , "docker.io/library/registry:2" ) . Execute ( ) ;
50
48
51
- ContainerCli . PushCommand ( testOutput , $ "{ LocalRegistry } /{ RuntimeBaseImage } :{ tag } ")
52
- . Execute ( )
53
- . Should ( ) . Pass ( ) ;
49
+ processResult . Should ( ) . Pass ( ) . And . HaveStdOut ( ) ;
50
+
51
+ logger . LogInformation ( $ "StdOut: { processResult . StdOut } ") ;
52
+ logger . LogInformation ( $ "StdErr: { processResult . StdErr } ") ;
53
+
54
+ using var reader = new StringReader ( processResult . StdOut ! ) ;
55
+ s_registryContainerId = reader . ReadLine ( ) ;
56
+
57
+ EnsureRegistryLoaded ( LocalRegistry , logger ) ;
58
+
59
+ foreach ( string ? tag in new [ ] { Net6ImageTag , Net7ImageTag , Net8PreviewImageTag } )
60
+ {
61
+ logger . LogInformation ( $ "Pulling image '{ BaseImageSource } { RuntimeBaseImage } :{ tag } '.") ;
62
+ ContainerCli . PullCommand ( testOutput , $ "{ BaseImageSource } { RuntimeBaseImage } :{ tag } ")
63
+ . Execute ( )
64
+ . Should ( ) . Pass ( ) ;
65
+
66
+ logger . LogInformation ( $ "Tagging image '{ BaseImageSource } { RuntimeBaseImage } :{ tag } ' as '{ LocalRegistry } /{ RuntimeBaseImage } :{ tag } '.") ;
67
+ ContainerCli . TagCommand ( testOutput , $ "{ BaseImageSource } { RuntimeBaseImage } :{ tag } ", $ "{ LocalRegistry } /{ RuntimeBaseImage } :{ tag } ")
68
+ . Execute ( )
69
+ . Should ( ) . Pass ( ) ;
70
+
71
+ logger . LogInformation ( $ "Pushing image '{ LocalRegistry } /{ RuntimeBaseImage } :{ tag } '.") ;
72
+ ContainerCli . PushCommand ( testOutput , $ "{ LocalRegistry } /{ RuntimeBaseImage } :{ tag } ")
73
+ . Execute ( )
74
+ . Should ( ) . Pass ( ) ;
75
+ }
76
+ return ;
77
+ }
78
+ catch ( Exception ex )
79
+ {
80
+ logger . LogError ( $ "Spawn registry attempt #{ spawnRegistryAttempt } failed, { ex . Message } ") ;
81
+ if ( ! string . IsNullOrWhiteSpace ( s_registryContainerId ) )
82
+ {
83
+ try
84
+ {
85
+ ContainerCli . StopCommand ( testOutput , s_registryContainerId ) . Execute ( ) ;
86
+ }
87
+ catch { }
88
+ }
89
+ logger . LogInformation ( $ "Retrying after { spawnRegistryDelay } ms.") ;
90
+ Thread . Sleep ( spawnRegistryDelay ) ;
91
+ spawnRegistryDelay *= 2 ;
92
+ }
54
93
}
94
+ throw new InvalidOperationException ( $ "The registry was not loaded after { spawnRegistryMaxRetry } retries.") ;
55
95
}
56
96
57
97
public static void ShutdownDockerRegistry ( ITestOutputHelper testOutput )
@@ -63,4 +103,35 @@ public static void ShutdownDockerRegistry(ITestOutputHelper testOutput)
63
103
. Should ( ) . Pass ( ) ;
64
104
}
65
105
}
106
+
107
+ private static void EnsureRegistryLoaded ( string registryBaseUri , ILogger logger )
108
+ {
109
+ const int registryLoadMaxRetry = 10 ;
110
+ const int registryLoadTimeout = 1000 ; //ms
111
+
112
+ using HttpClient client = new ( ) ;
113
+ using HttpRequestMessage request = new ( HttpMethod . Get , new Uri ( ContainerHelpers . TryExpandRegistryToUri ( registryBaseUri ) , "/v2/" ) ) ;
114
+
115
+ logger . LogInformation ( $ "Checking if the registry '{ registryBaseUri } ' is available.") ;
116
+
117
+ int attempt = 1 ;
118
+ while ( attempt <= registryLoadMaxRetry )
119
+ {
120
+ //added an additional delay to allow registry to load
121
+ Thread . Sleep ( registryLoadTimeout ) ;
122
+ HttpResponseMessage response = client . Send ( request ) ;
123
+ if ( response . IsSuccessStatusCode )
124
+ {
125
+ break ;
126
+ }
127
+ logger . LogWarning ( $ "The registry '{ registryBaseUri } is not loaded after { attempt * registryLoadTimeout } ms. Returned status code: { response . StatusCode } .") ;
128
+ attempt ++ ;
129
+ }
130
+ if ( attempt > registryLoadMaxRetry )
131
+ {
132
+ throw new Exception ( $ "The registry was not loaded after { registryLoadMaxRetry * registryLoadTimeout } ms.") ;
133
+ }
134
+ logger . LogInformation ( $ "The registry '{ registryBaseUri } ' is available after { attempt * registryLoadTimeout } ms.") ;
135
+ }
136
+
66
137
}
0 commit comments