public void DecryptedContextShouldMatchEnvironments() { var encrypted = EncryptedHostAssignmentContext.Create(_context, _encryptionKey); var decrypted = encrypted.Decrypt(_encryptionKey); Assert.Equal(_context.Environment, decrypted.Environment); }
public void SetContext_AppliesHostAssignmentContext() { var context = new HostAssignmentContext { Environment = new Dictionary <string, string>(), SiteName = "TestSite", Secrets = _secrets }; string json = JsonConvert.SerializeObject(context); string encrypted = SimpleWebTokenHelper.Encrypt(json, environment: _environment); var encryptedContext = new EncryptedHostAssignmentContext { EncryptedContext = encrypted }; var result = _startupContextProvider.SetContext(encryptedContext); Assert.Equal(context.SiteName, result.SiteName); Assert.Equal(_secrets.Host.Master, result.Secrets.Host.Master); var secrets = _startupContextProvider.GetHostSecretsOrNull(); Assert.Equal(_secrets.Host.Master, secrets.MasterKey); Assert.Equal(_secrets.Host.Function, secrets.FunctionKeys); Assert.Equal(_secrets.Host.System, secrets.SystemKeys); }
public async Task <IActionResult> Assign([FromBody] EncryptedHostAssignmentContext encryptedAssignmentContext) { _logger.LogDebug($"Starting container assignment for host : {Request?.Host}. ContextLength is: {encryptedAssignmentContext.EncryptedContext?.Length}"); var containerKey = _environment.GetEnvironmentVariable(EnvironmentSettingNames.ContainerEncryptionKey); var assignmentContext = encryptedAssignmentContext.IsWarmup ? null : encryptedAssignmentContext.Decrypt(containerKey); // before starting the assignment we want to perform as much // up front validation on the context as possible string error = await _instanceManager.ValidateContext(assignmentContext, encryptedAssignmentContext.IsWarmup); if (error != null) { return(StatusCode(StatusCodes.Status400BadRequest, error)); } // Wait for Sidecar specialization to complete before returning ok. // This shouldn't take too long so ok to do this sequentially. error = await _instanceManager.SpecializeMSISidecar(assignmentContext, encryptedAssignmentContext.IsWarmup); if (error != null) { return(StatusCode(StatusCodes.Status500InternalServerError, error)); } var result = _instanceManager.StartAssignment(assignmentContext, encryptedAssignmentContext.IsWarmup); return(result || encryptedAssignmentContext.IsWarmup ? Accepted() : StatusCode(StatusCodes.Status409Conflict, "Instance already assigned")); }
public void DecryptedContextShouldMatchByEqual() { var encrypted = EncryptedHostAssignmentContext.Create(_context, _encryptionKey); var decrypted = encrypted.Decrypt(_encryptionKey); Assert.True(_context.Equals(decrypted)); }
public async Task Assignment_Succeeds_With_Encryption_Key() { var environment = new TestEnvironment(); environment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebsitePlaceholderMode, "1"); var scriptWebEnvironment = new ScriptWebHostEnvironment(environment); var loggerFactory = new LoggerFactory(); var loggerProvider = new TestLoggerProvider(); loggerFactory.AddProvider(loggerProvider); var handlerMock = new Mock <HttpMessageHandler>(MockBehavior.Strict); handlerMock.Protected().Setup <Task <HttpResponseMessage> >("SendAsync", ItExpr.IsAny <HttpRequestMessage>(), ItExpr.IsAny <CancellationToken>()).ReturnsAsync(new HttpResponseMessage { StatusCode = HttpStatusCode.OK }); var instanceManager = new InstanceManager(_optionsFactory, TestHelpers.CreateHttpClientFactory(handlerMock.Object), scriptWebEnvironment, environment, loggerFactory.CreateLogger <InstanceManager>(), new TestMetricsLogger(), null, new Mock <IRunFromPackageHandler>().Object, new Mock <IPackageDownloadHandler>(MockBehavior.Strict).Object); var startupContextProvider = new StartupContextProvider(environment, loggerFactory.CreateLogger <StartupContextProvider>()); InstanceManager.Reset(); var podController = new KubernetesPodController(environment, instanceManager, loggerFactory, startupContextProvider); const string podEncryptionKey = "/a/vXvWJ3Hzgx4PFxlDUJJhQm5QVyGiu0NNLFm/ZMMg="; var hostAssignmentContext = new HostAssignmentContext { Environment = new Dictionary <string, string>() { [EnvironmentSettingNames.AzureWebsiteRunFromPackage] = "http://localhost:1234" } }; hostAssignmentContext.Secrets = new FunctionAppSecrets(); hostAssignmentContext.IsWarmupRequest = false; var encryptedHostAssignmentValue = SimpleWebTokenHelper.Encrypt(JsonConvert.SerializeObject(hostAssignmentContext), podEncryptionKey.ToKeyBytes()); var encryptedHostAssignmentContext = new EncryptedHostAssignmentContext() { EncryptedContext = encryptedHostAssignmentValue }; environment.SetEnvironmentVariable(EnvironmentSettingNames.PodEncryptionKey, podEncryptionKey); environment.SetEnvironmentVariable(EnvironmentSettingNames.KubernetesServiceHost, "http://localhost:80"); environment.SetEnvironmentVariable(EnvironmentSettingNames.PodNamespace, "k8se-apps"); var result = await podController.Assign(encryptedHostAssignmentContext); Assert.NotNull(startupContextProvider.Context); Assert.IsType <AcceptedResult>(result); }
public async Task Assign_MSISpecializationFailure_ReturnsError() { var environment = new TestEnvironment(); environment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebsitePlaceholderMode, "1"); var scriptWebEnvironment = new ScriptWebHostEnvironment(environment); var loggerFactory = new LoggerFactory(); var loggerProvider = new TestLoggerProvider(); loggerFactory.AddProvider(loggerProvider); var handlerMock = new Mock <HttpMessageHandler>(MockBehavior.Strict); handlerMock.Protected().Setup <Task <HttpResponseMessage> >("SendAsync", ItExpr.IsAny <HttpRequestMessage>(), ItExpr.IsAny <CancellationToken>()).ReturnsAsync(new HttpResponseMessage { StatusCode = HttpStatusCode.BadRequest }); var instanceManager = new InstanceManager(_optionsFactory, new HttpClient(handlerMock.Object), scriptWebEnvironment, environment, loggerFactory.CreateLogger <InstanceManager>(), new TestMetricsLogger(), null, _runFromPackageHandler.Object); var startupContextProvider = new StartupContextProvider(environment, loggerFactory.CreateLogger <StartupContextProvider>()); InstanceManager.Reset(); var instanceController = new InstanceController(environment, instanceManager, loggerFactory, startupContextProvider); const string containerEncryptionKey = "/a/vXvWJ3Hzgx4PFxlDUJJhQm5QVyGiu0NNLFm/ZMMg="; var hostAssignmentContext = new HostAssignmentContext { Environment = new Dictionary <string, string>(), MSIContext = new MSIContext() }; hostAssignmentContext.Environment[EnvironmentSettingNames.MsiEndpoint] = "http://localhost:8081"; hostAssignmentContext.Environment[EnvironmentSettingNames.MsiSecret] = "secret"; var encryptedHostAssignmentValue = SimpleWebTokenHelper.Encrypt(JsonConvert.SerializeObject(hostAssignmentContext), containerEncryptionKey.ToKeyBytes()); var encryptedHostAssignmentContext = new EncryptedHostAssignmentContext() { EncryptedContext = encryptedHostAssignmentValue }; environment.SetEnvironmentVariable(EnvironmentSettingNames.ContainerEncryptionKey, containerEncryptionKey); IActionResult result = await instanceController.Assign(encryptedHostAssignmentContext); var objectResult = result as ObjectResult; Assert.Equal(objectResult.StatusCode, 500); Assert.Equal(objectResult.Value, "Specialize MSI sidecar call failed. StatusCode=BadRequest"); }
public IActionResult Assign([FromBody] EncryptedHostAssignmentContext encryptedAssignmentContext) { var containerKey = _settingsManager.GetSetting(EnvironmentSettingNames.ContainerEncryptionKey); var assignmentContext = encryptedAssignmentContext.Decrypt(containerKey); return(_instanceManager.StartAssignment(assignmentContext) ? Accepted() : StatusCode(StatusCodes.Status409Conflict, "Instance already assigned")); }
public async Task Assignment_Does_Not_Set_Secrets_Context_For_Warmup_Request() { var environment = new TestEnvironment(); environment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebsitePlaceholderMode, "1"); var scriptWebEnvironment = new ScriptWebHostEnvironment(environment); var loggerFactory = new LoggerFactory(); var loggerProvider = new TestLoggerProvider(); loggerFactory.AddProvider(loggerProvider); var handlerMock = new Mock <HttpMessageHandler>(MockBehavior.Strict); handlerMock.Protected().Setup <Task <HttpResponseMessage> >("SendAsync", ItExpr.IsAny <HttpRequestMessage>(), ItExpr.IsAny <CancellationToken>()).ReturnsAsync(new HttpResponseMessage { StatusCode = HttpStatusCode.OK }); var instanceManager = new InstanceManager(_optionsFactory, new HttpClient(handlerMock.Object), scriptWebEnvironment, environment, loggerFactory.CreateLogger <InstanceManager>(), new TestMetricsLogger(), null, _runFromPackageHandler.Object); var startupContextProvider = new StartupContextProvider(environment, loggerFactory.CreateLogger <StartupContextProvider>()); InstanceManager.Reset(); var instanceController = new InstanceController(environment, instanceManager, loggerFactory, startupContextProvider); const string containerEncryptionKey = "/a/vXvWJ3Hzgx4PFxlDUJJhQm5QVyGiu0NNLFm/ZMMg="; var hostAssignmentContext = new HostAssignmentContext { Environment = new Dictionary <string, string>() { [EnvironmentSettingNames.AzureWebsiteRunFromPackage] = "http://localhost:1234" } }; hostAssignmentContext.Secrets = new FunctionAppSecrets(); hostAssignmentContext.IsWarmupRequest = true; // Warmup Request var encryptedHostAssignmentValue = SimpleWebTokenHelper.Encrypt(JsonConvert.SerializeObject(hostAssignmentContext), containerEncryptionKey.ToKeyBytes()); var encryptedHostAssignmentContext = new EncryptedHostAssignmentContext() { EncryptedContext = encryptedHostAssignmentValue }; environment.SetEnvironmentVariable(EnvironmentSettingNames.ContainerEncryptionKey, containerEncryptionKey); await instanceController.Assign(encryptedHostAssignmentContext); Assert.Null(startupContextProvider.Context); }
public async Task <IActionResult> AssignAsync([FromBody] EncryptedHostAssignmentContext encryptedAssignmentContext) { var containerKey = System.Environment.GetEnvironmentVariable(SettingsKeys.ContainerEncryptionKey); var assignmentContext = encryptedAssignmentContext.Decrypt(containerKey); // before starting the assignment we want to perform as much // up front validation on the context as possible string error = await _instanceManager.ValidateContext(assignmentContext); if (error != null) { return(StatusCode(StatusCodes.Status400BadRequest, error)); } var assignmentResult = _instanceManager.StartAssignment(assignmentContext); // modify app settings from environment variables (for all start with "APPSETTING_") // setting APPSETTING_FUNCTIONS_EXTENSION_VERSION will both set the appsetting and environment variable if (assignmentResult) { foreach (KeyValuePair <string, string> env in assignmentContext.Environment) { string key = env.Key; string value = env.Value; if (key.StartsWith(_appsettingPrefix)) { key = key.Substring(_appsettingPrefix.Length); _settingsManager.SetValue(key, value); } // configure function app specialization properly for Linux Consumption plan Dictionary <string, string> newSettings = FunctionAppSpecializationHelper.HandleLinuxConsumption(key, value); foreach (KeyValuePair <string, string> newSetting in newSettings) { if (System.Environment.GetEnvironmentVariable(newSetting.Key) == null) { System.Environment.SetEnvironmentVariable(newSetting.Key, newSetting.Value); } if (_settingsManager.GetValue(newSetting.Key) == null) { _settingsManager.SetValue(newSetting.Key, newSetting.Value); } } } } return(assignmentResult ? Accepted() : StatusCode(StatusCodes.Status409Conflict, "Instance already assigned")); }
/// <summary> /// Decrypt and deserialize the specified context, and apply values from it to the /// startup cache context. /// </summary> /// <param name="encryptedContext">The encrypted assignment context.</param> /// <returns>The decrypted assignment context</returns> public virtual HostAssignmentContext SetContext(EncryptedHostAssignmentContext encryptedContext) { string decryptedContext = SimpleWebTokenHelper.Decrypt(encryptedContext.EncryptedContext, environment: _environment); var hostAssignmentContext = JsonConvert.DeserializeObject <HostAssignmentContext>(decryptedContext); // apply values from the context to our cached context Context = new StartupContext { Secrets = hostAssignmentContext.Secrets }; return(hostAssignmentContext); }
public async Task Assignment_Invokes_InstanceManager_Methods_For_Warmup_Requests_Also(bool isWarmupRequest, bool shouldInvokeMethod) { var environment = new TestEnvironment(); environment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebsitePlaceholderMode, "1"); var loggerFactory = new LoggerFactory(); var loggerProvider = new TestLoggerProvider(); loggerFactory.AddProvider(loggerProvider); var instanceManager = new Mock <IInstanceManager>(); var startupContextProvider = new StartupContextProvider(environment, loggerFactory.CreateLogger <StartupContextProvider>()); InstanceManager.Reset(); var instanceController = new InstanceController(environment, instanceManager.Object, loggerFactory, startupContextProvider); const string containerEncryptionKey = "/a/vXvWJ3Hzgx4PFxlDUJJhQm5QVyGiu0NNLFm/ZMMg="; var hostAssignmentContext = new HostAssignmentContext { Environment = new Dictionary <string, string>() }; hostAssignmentContext.IsWarmupRequest = isWarmupRequest; var encryptedHostAssignmentValue = SimpleWebTokenHelper.Encrypt(JsonConvert.SerializeObject(hostAssignmentContext), containerEncryptionKey.ToKeyBytes()); var encryptedHostAssignmentContext = new EncryptedHostAssignmentContext() { EncryptedContext = encryptedHostAssignmentValue }; environment.SetEnvironmentVariable(EnvironmentSettingNames.ContainerEncryptionKey, containerEncryptionKey); await instanceController.Assign(encryptedHostAssignmentContext); instanceManager.Verify(i => i.ValidateContext(It.IsAny <HostAssignmentContext>()), shouldInvokeMethod ? Times.Once() : Times.Never()); instanceManager.Verify(i => i.SpecializeMSISidecar(It.IsAny <HostAssignmentContext>()), shouldInvokeMethod ? Times.Once() : Times.Never()); instanceManager.Verify(i => i.StartAssignment(It.IsAny <HostAssignmentContext>()), shouldInvokeMethod ? Times.Once() : Times.Never()); }
public async Task <IActionResult> Assign([FromBody] EncryptedHostAssignmentContext encryptedAssignmentContext) { _logger.LogDebug($"Starting container assignment for host : {Request?.Host}"); var assignmentContext = _startupContextProvider.SetContext(encryptedAssignmentContext); string error = await _instanceManager.ValidateContext(assignmentContext); if (error != null) { return(StatusCode(StatusCodes.Status400BadRequest, error)); } var succeeded = _instanceManager.StartAssignment(assignmentContext); return(succeeded ? Accepted() : StatusCode(StatusCodes.Status409Conflict, "Instance already assigned")); }
public async Task <IActionResult> Assign([FromBody] EncryptedHostAssignmentContext encryptedAssignmentContext) { var containerKey = _environment.GetEnvironmentVariable(EnvironmentSettingNames.ContainerEncryptionKey); var assignmentContext = encryptedAssignmentContext.Decrypt(containerKey); // before starting the assignment we want to perform as much // up front validation on the context as possible string error = await _instanceManager.ValidateContext(assignmentContext); if (error != null) { return(StatusCode(StatusCodes.Status400BadRequest, error)); } var result = _instanceManager.StartAssignment(assignmentContext); return(result ? Accepted() : StatusCode(StatusCodes.Status409Conflict, "Instance already assigned")); }
private async Task Assign(string encryptionKey) { // create a zip package var contentRoot = Path.Combine(Path.GetTempPath(), @"FunctionsTest"); var sourcePath = Path.Combine(Directory.GetCurrentDirectory(), @"TestScripts\Node\HttpTrigger"); var zipFilePath = Path.Combine(contentRoot, "content.zip"); await TestHelpers.CreateContentZip(contentRoot, zipFilePath, @"TestScripts\Node\HttpTrigger"); // upload the blob and get a SAS uri var configuration = _httpServer.Host.Services.GetService <IConfiguration>(); string connectionString = configuration.GetWebJobsConnectionString(ConnectionStringNames.Storage); var sasUri = await TestHelpers.CreateBlobSas(connectionString, zipFilePath, "azure-functions-test", "appcontents.zip"); // Now specialize the host by invoking assign var secretManager = _httpServer.Host.Services.GetService <ISecretManagerProvider>().Current; var masterKey = (await secretManager.GetHostSecretsAsync()).MasterKey; string uri = "admin/instance/assign"; var request = new HttpRequestMessage(HttpMethod.Post, uri); var environment = new Dictionary <string, string>() { { EnvironmentSettingNames.AzureWebsiteZipDeployment, sasUri.ToString() }, { RpcWorkerConstants.FunctionWorkerRuntimeVersionSettingName, "~2" }, { RpcWorkerConstants.FunctionWorkerRuntimeSettingName, "node" } }; var assignmentContext = new HostAssignmentContext { SiteId = 1234, SiteName = "TestApp", Environment = environment }; var encryptedAssignmentContext = EncryptedHostAssignmentContext.Create(assignmentContext, encryptionKey); string json = JsonConvert.SerializeObject(encryptedAssignmentContext); request.Content = new StringContent(json, Encoding.UTF8, "application/json"); request.Headers.Add(AuthenticationLevelHandler.FunctionsKeyHeaderName, masterKey); var response = await _httpClient.SendAsync(request); Assert.Equal(HttpStatusCode.Accepted, response.StatusCode); }
public async Task <IActionResult> Assign([FromBody] EncryptedHostAssignmentContext encryptedAssignmentContext) { _logger.LogDebug($"Starting container assignment for host : {Request?.Host}. ContextLength is: {encryptedAssignmentContext.EncryptedContext?.Length}"); bool succeeded = false; if (!encryptedAssignmentContext.IsWarmup) { var assignmentContext = _startupContextProvider.SetContext(encryptedAssignmentContext); // before starting the assignment we want to perform as much // up front validation on the context as possible string error = await _instanceManager.ValidateContext(assignmentContext, encryptedAssignmentContext.IsWarmup); if (error != null) { return(StatusCode(StatusCodes.Status400BadRequest, error)); } // Wait for Sidecar specialization to complete before returning ok. // This shouldn't take too long so ok to do this sequentially. error = await _instanceManager.SpecializeMSISidecar(assignmentContext, encryptedAssignmentContext.IsWarmup); if (error != null) { return(StatusCode(StatusCodes.Status500InternalServerError, error)); } succeeded = _instanceManager.StartAssignment(assignmentContext, encryptedAssignmentContext.IsWarmup); } else { succeeded = true; } return(succeeded ? Accepted() : StatusCode(StatusCodes.Status409Conflict, "Instance already assigned")); }
public void Does_Not_SetContext_AppliesHostAssignmentContext_For_Warmup_Request() { var context = new HostAssignmentContext { Environment = new Dictionary <string, string>(), SiteName = "TestSite", Secrets = _secrets, IsWarmupRequest = true }; string json = JsonConvert.SerializeObject(context); string encrypted = SimpleWebTokenHelper.Encrypt(json, environment: _environment); var encryptedContext = new EncryptedHostAssignmentContext { EncryptedContext = encrypted }; var result = _startupContextProvider.SetContext(encryptedContext); Assert.Equal(context.SiteName, result.SiteName); Assert.Equal(_secrets.Host.Master, result.Secrets.Host.Master); var secrets = _startupContextProvider.GetHostSecretsOrNull(); Assert.Null(secrets); }
public void EncryptedContextShouldExistAfterCreate() { var result = EncryptedHostAssignmentContext.Create(_context, _encryptionKey); Assert.NotEmpty(result.EncryptedContext); }
public async Task StandbyMode_EndToEnd_LinuxContainer() { byte[] bytes = TestHelpers.GenerateKeyBytes(); var encryptionKey = Convert.ToBase64String(bytes); var vars = new Dictionary <string, string> { { EnvironmentSettingNames.ContainerName, "TestContainer" }, { EnvironmentSettingNames.ContainerEncryptionKey, encryptionKey }, { EnvironmentSettingNames.AzureWebsiteContainerReady, null }, { "AzureWebEncryptionKey", "0F75CA46E7EBDD39E4CA6B074D1F9A5972B849A55F91A248" } }; using (var env = new TestScopedEnvironmentVariable(vars)) { var httpConfig = new HttpConfiguration(); var testRootPath = Path.Combine(Path.GetTempPath(), "StandbyModeTest_Linux"); await FileUtility.DeleteDirectoryAsync(testRootPath, true); var loggerProvider = new TestLoggerProvider(); var loggerProviderFactory = new TestLoggerProviderFactory(loggerProvider); var webHostSettings = new WebHostSettings { IsSelfHost = true, LogPath = Path.Combine(testRootPath, "Logs"), SecretsPath = Path.Combine(testRootPath, "Secrets"), ScriptPath = Path.Combine(testRootPath, "WWWRoot") }; var loggerFactory = new LoggerFactory(); loggerFactory.AddProvider(loggerProvider); var webHostBuilder = Program.CreateWebHostBuilder() .ConfigureServices(c => { c.AddSingleton(webHostSettings) .AddSingleton <ILoggerProviderFactory>(loggerProviderFactory) .AddSingleton <ILoggerFactory>(loggerFactory); }); var httpServer = new TestServer(webHostBuilder); var httpClient = httpServer.CreateClient(); httpClient.BaseAddress = new Uri("https://localhost/"); TestHelpers.WaitForWebHost(httpClient); var traces = loggerProvider.GetAllLogMessages().ToArray(); Assert.NotNull(traces.Single(p => p.FormattedMessage.StartsWith("Starting Host (HostId=placeholder-host"))); Assert.NotNull(traces.Single(p => p.FormattedMessage.StartsWith("Host is in standby mode"))); // issue warmup request and verify var request = new HttpRequestMessage(HttpMethod.Get, "api/warmup"); var response = await httpClient.SendAsync(request); Assert.Equal(HttpStatusCode.OK, response.StatusCode); string responseBody = await response.Content.ReadAsStringAsync(); Assert.Equal("WarmUp complete.", responseBody); // issue warmup request with restart and verify request = new HttpRequestMessage(HttpMethod.Get, "api/warmup?restart=1"); response = await httpClient.SendAsync(request); Assert.Equal(HttpStatusCode.OK, response.StatusCode); responseBody = await response.Content.ReadAsStringAsync(); Assert.Equal("WarmUp complete.", responseBody); // Now specialize the host by invoking assign var secretManager = httpServer.Host.Services.GetService <ISecretManager>(); var masterKey = (await secretManager.GetHostSecretsAsync()).MasterKey; string uri = "admin/instance/assign"; request = new HttpRequestMessage(HttpMethod.Post, uri); var environment = new Dictionary <string, string>(); var assignmentContext = new HostAssignmentContext { SiteId = 1234, SiteName = "TestSite", Environment = environment }; var encryptedAssignmentContext = EncryptedHostAssignmentContext.Create(assignmentContext, encryptionKey); string json = JsonConvert.SerializeObject(encryptedAssignmentContext); request.Content = new StringContent(json, Encoding.UTF8, "application/json"); request.Headers.Add(AuthenticationLevelHandler.FunctionsKeyHeaderName, masterKey); response = await httpClient.SendAsync(request); Assert.Equal(HttpStatusCode.Accepted, response.StatusCode); // give time for the specialization to happen string[] logLines = null; await TestHelpers.Await(() => { // wait for the trace indicating that the host has been specialized logLines = loggerProvider.GetAllLogMessages().Where(p => p.FormattedMessage != null).Select(p => p.FormattedMessage).ToArray(); return(logLines.Contains("Generating 0 job function(s)")); }, userMessageCallback : () => string.Join(Environment.NewLine, loggerProvider.GetAllLogMessages().Select(p => $"[{p.Timestamp.ToString("HH:mm:ss.fff")}] {p.FormattedMessage}"))); httpServer.Dispose(); httpClient.Dispose(); await Task.Delay(2000); var hostConfig = WebHostResolver.CreateScriptHostConfiguration(webHostSettings, true); var expectedHostId = hostConfig.HostConfig.HostId; // verify the rest of the expected logs string text = string.Join(Environment.NewLine, logLines); Assert.True(logLines.Count(p => p.Contains("Stopping Host")) >= 1); Assert.Equal(1, logLines.Count(p => p.Contains("Creating StandbyMode placeholder function directory"))); Assert.Equal(1, logLines.Count(p => p.Contains("StandbyMode placeholder function directory created"))); Assert.Equal(2, logLines.Count(p => p.Contains("Starting Host (HostId=placeholder-host"))); Assert.Equal(2, logLines.Count(p => p.Contains("Host is in standby mode"))); Assert.Equal(2, logLines.Count(p => p.Contains("Executed 'Functions.WarmUp' (Succeeded"))); Assert.Equal(1, logLines.Count(p => p.Contains("Starting host specialization"))); Assert.Equal(1, logLines.Count(p => p.Contains($"Starting Host (HostId={expectedHostId}"))); Assert.Contains("Generating 0 job function(s)", logLines); WebScriptHostManager.ResetStandbyMode(); } }