7
7
using System . Linq ;
8
8
using System . Net ;
9
9
using System . Net . Http ;
10
+ using System . Text ;
10
11
using System . Threading . Tasks ;
11
12
using System . Web . Http ;
12
13
using Microsoft . AspNetCore . TestHost ;
13
14
using Microsoft . Azure . WebJobs . Script . Config ;
14
15
using Microsoft . Azure . WebJobs . Script . WebHost ;
16
+ using Microsoft . Azure . WebJobs . Script . WebHost . Authentication ;
17
+ using Microsoft . Azure . WebJobs . Script . WebHost . Models ;
15
18
using Microsoft . Extensions . DependencyInjection ;
16
19
using Microsoft . Extensions . Logging ;
17
20
using Microsoft . WebJobs . Script . Tests ;
21
+ using Newtonsoft . Json ;
18
22
using Xunit ;
19
23
20
24
namespace Microsoft . Azure . WebJobs . Script . Tests
@@ -42,6 +46,10 @@ public void IsWarmUpRequest_ReturnsExpectedValue()
42
46
} ;
43
47
using ( var env = new TestScopedEnvironmentVariable ( vars ) )
44
48
{
49
+ // in this test we're forcing a transition from non-placeholder mode to placeholder mode
50
+ // which can't happen in the wild, so we force a reset here
51
+ WebScriptHostManager . ResetStandbyMode ( ) ;
52
+
45
53
_settingsManager . SetSetting ( EnvironmentSettingNames . AzureWebsitePlaceholderMode , "1" ) ;
46
54
Assert . False ( StandbyManager . IsWarmUpRequest ( request ) ) ;
47
55
@@ -58,6 +66,24 @@ public void IsWarmUpRequest_ReturnsExpectedValue()
58
66
request = HttpTestHelpers . CreateHttpRequest ( "POST" , "http://azure.com/api/foo" ) ;
59
67
Assert . False ( StandbyManager . IsWarmUpRequest ( request ) ) ;
60
68
}
69
+
70
+ vars = new Dictionary < string , string >
71
+ {
72
+ { EnvironmentSettingNames . AzureWebsitePlaceholderMode , "0" } ,
73
+ { EnvironmentSettingNames . AzureWebsiteInstanceId , null }
74
+ } ;
75
+ using ( var env = new TestScopedEnvironmentVariable ( vars ) )
76
+ {
77
+ WebScriptHostManager . ResetStandbyMode ( ) ;
78
+
79
+ _settingsManager . SetSetting ( EnvironmentSettingNames . AzureWebsitePlaceholderMode , "1" ) ;
80
+ Assert . False ( StandbyManager . IsWarmUpRequest ( request ) ) ;
81
+
82
+ request = HttpTestHelpers . CreateHttpRequest ( "POST" , "http://azure.com/api/warmup" ) ;
83
+ _settingsManager . SetSetting ( EnvironmentSettingNames . ContainerName , "TestContainer" ) ;
84
+ Assert . True ( _settingsManager . IsLinuxContainerEnvironment ) ;
85
+ Assert . True ( StandbyManager . IsWarmUpRequest ( request ) ) ;
86
+ }
61
87
}
62
88
63
89
[ Fact ]
@@ -161,6 +187,123 @@ await TestHelpers.Await(() =>
161
187
}
162
188
}
163
189
190
+ [ Fact ]
191
+ public async Task StandbyMode_EndToEnd_LinuxContainer ( )
192
+ {
193
+ byte [ ] bytes = TestHelpers . GenerateKeyBytes ( ) ;
194
+ var encryptionKey = Convert . ToBase64String ( bytes ) ;
195
+
196
+ var vars = new Dictionary < string , string >
197
+ {
198
+ { EnvironmentSettingNames . ContainerName , "TestContainer" } ,
199
+ { EnvironmentSettingNames . ContainerEncryptionKey , encryptionKey } ,
200
+ { EnvironmentSettingNames . AzureWebsiteContainerReady , null } ,
201
+ { "AzureWebEncryptionKey" , "0F75CA46E7EBDD39E4CA6B074D1F9A5972B849A55F91A248" }
202
+ } ;
203
+ using ( var env = new TestScopedEnvironmentVariable ( vars ) )
204
+ {
205
+ var httpConfig = new HttpConfiguration ( ) ;
206
+
207
+ var testRootPath = Path . Combine ( Path . GetTempPath ( ) , "StandbyModeTest_Linux" ) ;
208
+ await FileUtility . DeleteDirectoryAsync ( testRootPath , true ) ;
209
+
210
+ var loggerProvider = new TestLoggerProvider ( ) ;
211
+ var loggerProviderFactory = new TestLoggerProviderFactory ( loggerProvider ) ;
212
+ var webHostSettings = new WebHostSettings
213
+ {
214
+ IsSelfHost = true ,
215
+ LogPath = Path . Combine ( testRootPath , "Logs" ) ,
216
+ SecretsPath = Path . Combine ( testRootPath , "Secrets" ) ,
217
+ ScriptPath = Path . Combine ( testRootPath , "WWWRoot" )
218
+ } ;
219
+
220
+ var loggerFactory = new LoggerFactory ( ) ;
221
+ loggerFactory . AddProvider ( loggerProvider ) ;
222
+
223
+ var webHostBuilder = Program . CreateWebHostBuilder ( )
224
+ . ConfigureServices ( c =>
225
+ {
226
+ c . AddSingleton ( webHostSettings )
227
+ . AddSingleton < ILoggerProviderFactory > ( loggerProviderFactory )
228
+ . AddSingleton < ILoggerFactory > ( loggerFactory ) ;
229
+ } ) ;
230
+
231
+ var httpServer = new TestServer ( webHostBuilder ) ;
232
+ var httpClient = httpServer . CreateClient ( ) ;
233
+ httpClient . BaseAddress = new Uri ( "https://localhost/" ) ;
234
+
235
+ TestHelpers . WaitForWebHost ( httpClient ) ;
236
+
237
+ var traces = loggerProvider . GetAllLogMessages ( ) . ToArray ( ) ;
238
+ Assert . NotNull ( traces . Single ( p => p . FormattedMessage . StartsWith ( "Starting Host (HostId=placeholder-host" ) ) ) ;
239
+ Assert . NotNull ( traces . Single ( p => p . FormattedMessage . StartsWith ( "Host is in standby mode" ) ) ) ;
240
+
241
+ // issue warmup request and verify
242
+ var request = new HttpRequestMessage ( HttpMethod . Get , "api/warmup" ) ;
243
+ var response = await httpClient . SendAsync ( request ) ;
244
+ Assert . Equal ( HttpStatusCode . OK , response . StatusCode ) ;
245
+ string responseBody = await response . Content . ReadAsStringAsync ( ) ;
246
+ Assert . Equal ( "WarmUp complete." , responseBody ) ;
247
+
248
+ // issue warmup request with restart and verify
249
+ request = new HttpRequestMessage ( HttpMethod . Get , "api/warmup?restart=1" ) ;
250
+ response = await httpClient . SendAsync ( request ) ;
251
+ Assert . Equal ( HttpStatusCode . OK , response . StatusCode ) ;
252
+ responseBody = await response . Content . ReadAsStringAsync ( ) ;
253
+ Assert . Equal ( "WarmUp complete." , responseBody ) ;
254
+
255
+ // Now specialize the host by invoking assign
256
+ var secretManager = httpServer . Host . Services . GetService < ISecretManager > ( ) ;
257
+ var masterKey = ( await secretManager . GetHostSecretsAsync ( ) ) . MasterKey ;
258
+ string uri = "admin/instance/assign" ;
259
+ request = new HttpRequestMessage ( HttpMethod . Post , uri ) ;
260
+ var environment = new Dictionary < string , string > ( ) ;
261
+ var assignmentContext = new HostAssignmentContext
262
+ {
263
+ SiteId = 1234 ,
264
+ SiteName = "TestSite" ,
265
+ Environment = environment
266
+ } ;
267
+ var encryptedAssignmentContext = EncryptedHostAssignmentContext . Create ( assignmentContext , encryptionKey ) ;
268
+ string json = JsonConvert . SerializeObject ( encryptedAssignmentContext ) ;
269
+ request . Content = new StringContent ( json , Encoding . UTF8 , "application/json" ) ;
270
+ request . Headers . Add ( AuthenticationLevelHandler . FunctionsKeyHeaderName , masterKey ) ;
271
+ response = await httpClient . SendAsync ( request ) ;
272
+ Assert . Equal ( HttpStatusCode . Accepted , response . StatusCode ) ;
273
+
274
+ // give time for the specialization to happen
275
+ string [ ] logLines = null ;
276
+ await TestHelpers . Await ( ( ) =>
277
+ {
278
+ // wait for the trace indicating that the host has been specialized
279
+ logLines = loggerProvider . GetAllLogMessages ( ) . Where ( p => p . FormattedMessage != null ) . Select ( p => p . FormattedMessage ) . ToArray ( ) ;
280
+ return logLines . Contains ( "Generating 0 job function(s)" ) ;
281
+ } , userMessageCallback : ( ) => string . Join ( Environment . NewLine , loggerProvider . GetAllLogMessages ( ) . Select ( p => $ "[{ p . Timestamp . ToString ( "HH:mm:ss.fff" ) } ] { p . FormattedMessage } ") ) ) ;
282
+
283
+ httpServer . Dispose ( ) ;
284
+ httpClient . Dispose ( ) ;
285
+
286
+ await Task . Delay ( 2000 ) ;
287
+
288
+ var hostConfig = WebHostResolver . CreateScriptHostConfiguration ( webHostSettings , true ) ;
289
+ var expectedHostId = hostConfig . HostConfig . HostId ;
290
+
291
+ // verify the rest of the expected logs
292
+ string text = string . Join ( Environment . NewLine , logLines ) ;
293
+ Assert . True ( logLines . Count ( p => p . Contains ( "Stopping Host" ) ) >= 1 ) ;
294
+ Assert . Equal ( 1 , logLines . Count ( p => p . Contains ( "Creating StandbyMode placeholder function directory" ) ) ) ;
295
+ Assert . Equal ( 1 , logLines . Count ( p => p . Contains ( "StandbyMode placeholder function directory created" ) ) ) ;
296
+ Assert . Equal ( 2 , logLines . Count ( p => p . Contains ( "Starting Host (HostId=placeholder-host" ) ) ) ;
297
+ Assert . Equal ( 2 , logLines . Count ( p => p . Contains ( "Host is in standby mode" ) ) ) ;
298
+ Assert . Equal ( 2 , logLines . Count ( p => p . Contains ( "Executed 'Functions.WarmUp' (Succeeded" ) ) ) ;
299
+ Assert . Equal ( 1 , logLines . Count ( p => p . Contains ( "Starting host specialization" ) ) ) ;
300
+ Assert . Equal ( 1 , logLines . Count ( p => p . Contains ( $ "Starting Host (HostId={ expectedHostId } ") ) ) ;
301
+ Assert . Contains ( "Generating 0 job function(s)" , logLines ) ;
302
+
303
+ WebScriptHostManager . ResetStandbyMode ( ) ;
304
+ }
305
+ }
306
+
164
307
public void Dispose ( )
165
308
{
166
309
}
0 commit comments