public void UniqueSlotName_ReturnsExpectedValue(string siteName, string slotName, string expectedValue) { var settingsManager = ScriptSettingsManager.Instance; var variables = new Dictionary <string, string> { { EnvironmentSettingNames.AzureWebsiteName, siteName }, { EnvironmentSettingNames.AzureWebsiteSlotName, slotName }, }; using (var tempVariables = new TestScopedEnvironmentVariable(variables)) { Assert.Equal(expectedValue, ScriptSettingsManager.Instance.AzureWebsiteUniqueSlotName); } }
public void GetUrl_ReturnsExpectedResult() { _hostSecrets.SystemKeys = new Dictionary <string, string> { { "testextension_extension", "abc123" } }; var vars = new Dictionary <string, string> { { EnvironmentSettingNames.AzureWebsiteHostName, "test.azurewebsites.net" } }; using (var env = new TestScopedEnvironmentVariable(vars)) { var configProvider = new TestExtensionConfigProvider(); var url = _webHookProvider.GetUrl(configProvider); Assert.Equal("https://test.azurewebsites.net/runtime/webhooks/testextension?code=abc123", url.ToString()); } }
public void GetAttributes_DynamicSku_ValidatesScheduleExpression() { var vars = new Dictionary <string, string> { { "TEST_SCHEDULE_CRON", "0 * * * * *" }, { "TEST_SCHEDULE_TIMESPAN", "00:00:15" }, { "WEBSITE_SKU", "Dynamic" }, }; using (var env = new TestScopedEnvironmentVariable(vars)) { var triggerMetadata = new JObject { { "direction", "in" }, { "name", "timer" }, { "type", "timerTrigger" } }; var bindingContext = new ScriptBindingContext(triggerMetadata); var binding = new CoreExtensionsScriptBindingProvider.TimerTriggerScriptBinding(bindingContext); // TimeSpan expression is invalid triggerMetadata["schedule"] = "00:00:15"; var ex = Assert.Throws <ArgumentException>(() => binding.GetAttributes()); Assert.Equal("'00:00:15' is not a valid CRON expression.", ex.Message); // TimeSpan specified via app setting is invalid triggerMetadata["schedule"] = "%TEST_SCHEDULE_TIMESPAN%"; ex = Assert.Throws <ArgumentException>(() => binding.GetAttributes()); Assert.Equal("'00:00:15' is not a valid CRON expression.", ex.Message); // Cron expression is valid triggerMetadata["schedule"] = "0 * * * * *"; var timerAttribute = (TimerTriggerAttribute)binding.GetAttributes().Single(); Assert.Equal("0 * * * * *", timerAttribute.ScheduleExpression); // Cron expression specified via app setting is valid triggerMetadata["schedule"] = "%TEST_SCHEDULE_CRON%"; timerAttribute = (TimerTriggerAttribute)binding.GetAttributes().Single(); Assert.Equal("0 * * * * *", timerAttribute.ScheduleExpression); } }
public void IsAntaresInternalRequest_ReturnsExpectedResult() { // not running under Azure var request = new HttpRequestMessage(HttpMethod.Get, "http://foobar"); Assert.False(request.IsAntaresInternalRequest()); // running under Azure var vars = new Dictionary <string, string> { { EnvironmentSettingNames.AzureWebsiteInstanceId, "123" } }; using (var env = new TestScopedEnvironmentVariable(vars)) { // with header request = new HttpRequestMessage(HttpMethod.Get, "http://foobar"); request.Headers.Add(ScriptConstants.AntaresLogIdHeaderName, "123"); Assert.False(request.IsAntaresInternalRequest()); request = new HttpRequestMessage(HttpMethod.Get, "http://foobar"); Assert.True(request.IsAntaresInternalRequest()); } }
public async Task Compilation_WithMissingBindingArguments_LogsAF004Warning(IDictionary <string, string> environment) { using (var testEnvironment = new TestScopedEnvironmentVariable(environment)) { // Create the compilation exception we expect to throw during the reload string rootFunctionsFolder = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); Directory.CreateDirectory(rootFunctionsFolder); // Create the invoker dependencies and setup the appropriate method to throw the exception RunDependencies dependencies = CreateDependencies(); // Create a dummy file to represent our function string filePath = Path.Combine(rootFunctionsFolder, Guid.NewGuid().ToString() + ".csx"); File.WriteAllText(filePath, Resources.TestFunctionWithMissingBindingArgumentsCode); var metadata = new FunctionMetadata { ScriptFile = filePath, FunctionDirectory = Path.GetDirectoryName(filePath), Name = Guid.NewGuid().ToString(), Language = DotNetScriptTypes.CSharp }; metadata.Bindings.Add(new BindingMetadata() { Name = "myQueueItem", Type = "ManualTrigger" }); var testBinding = new Mock <FunctionBinding>(null, new BindingMetadata() { Name = "TestBinding", Type = "blob" }, FileAccess.Write); var invoker = new DotNetFunctionInvoker(dependencies.Host, metadata, new Collection <FunctionBinding>(), new Collection <FunctionBinding> { testBinding.Object }, new FunctionEntryPointResolver(), new DotNetCompilationServiceFactory(null), dependencies.LoggerFactory, dependencies.MetricsLogger, new Collection <IScriptBindingProvider>()); try { await invoker.GetFunctionTargetAsync(); } catch (CompilationErrorException exc) { var builder = new StringBuilder(); builder.AppendLine(Resources.TestFunctionWithMissingBindingArgumentsCode); builder.AppendLine(); string compilationDetails = exc.Diagnostics.Aggregate( builder, (a, d) => a.AppendLine(d.ToString()), a => a.ToString()); throw new Exception(compilationDetails, exc); } Assert.Contains(dependencies.LoggerProvider.GetAllLogMessages(), t => t.FormattedMessage.Contains($"warning {DotNetConstants.MissingBindingArgumentCompilationCode}") && t.FormattedMessage.Contains("'TestBinding'")); } }
public async Task StandbyMode_EndToEnd() { var vars = new Dictionary <string, string> { { EnvironmentSettingNames.AzureWebsitePlaceholderMode, "1" }, { EnvironmentSettingNames.AzureWebsiteContainerReady, null }, { EnvironmentSettingNames.AzureWebsiteInstanceId, "87654639876900123453445678890144" }, { "AzureWebEncryptionKey", "0F75CA46E7EBDD39E4CA6B074D1F9A5972B849A55F91A248" } }; using (var env = new TestScopedEnvironmentVariable(vars)) { var httpConfig = new HttpConfiguration(); var testRootPath = Path.Combine(Path.GetTempPath(), "StandbyModeTest"); 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 ScriptSettingsManager.Instance.SetSetting(EnvironmentSettingNames.AzureWebsitePlaceholderMode, "0"); ScriptSettingsManager.Instance.SetSetting(EnvironmentSettingNames.AzureWebsiteContainerReady, "1"); Assert.False(WebScriptHostManager.InStandbyMode); Assert.True(ScriptSettingsManager.Instance.ContainerReady); // 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(); } }
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(); } }
public async Task StandbyMode_EndToEnd_LinuxContainer() { byte[] bytes = TestHelpers.GenerateKeyBytes(); var encryptionKey = Convert.ToBase64String(bytes); var vars = new Dictionary <string, string> { { EnvironmentSettingNames.ContainerName, "TestApp" }, { EnvironmentSettingNames.AzureWebsiteName, "TestApp" }, { EnvironmentSettingNames.ContainerEncryptionKey, encryptionKey }, { EnvironmentSettingNames.AzureWebsiteContainerReady, null }, { EnvironmentSettingNames.AzureWebsiteConfigurationReady, null }, { EnvironmentSettingNames.AzureWebsiteSku, "Dynamic" }, { EnvironmentSettingNames.AzureWebsiteZipDeployment, null }, { "AzureWebEncryptionKey", "0F75CA46E7EBDD39E4CA6B074D1F9A5972B849A55F91A248" } }; using (var env = new TestScopedEnvironmentVariable(vars)) { InitializeTestHost("Linux"); await VerifyWarmupSucceeds(); await VerifyWarmupSucceeds(restart : true); // now specialize the site await Assign(encryptionKey); // immediately call a function - expect the call to block until // the host is fully specialized // the Unauthorized is expected since we havne't specified the key // it's enough here to ensure we don't get a 404 var request = new HttpRequestMessage(HttpMethod.Get, $"api/httptrigger"); request.Headers.Add(ScriptConstants.AntaresColdStartHeaderName, "1"); var response = await _httpClient.SendAsync(request); Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); // now that the host is initialized, send a valid key // and expect success var secretManager = _httpServer.Host.Services.GetService <ISecretManager>(); var keys = await secretManager.GetFunctionSecretsAsync("HttpTrigger"); string key = keys.First().Value; request = new HttpRequestMessage(HttpMethod.Get, $"api/httptrigger?code={key}"); request.Headers.Add(ScriptConstants.AntaresColdStartHeaderName, "1"); response = await _httpClient.SendAsync(request); Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.False(WebScriptHostManager.InStandbyMode); Assert.True(ScriptSettingsManager.Instance.ContainerReady); // verify warmup function no longer there request = new HttpRequestMessage(HttpMethod.Get, "api/warmup"); response = await _httpClient.SendAsync(request); Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); _httpServer.Dispose(); _httpClient.Dispose(); // verify the expected logs var logLines = _loggerProvider.GetAllLogMessages().Where(p => p.FormattedMessage != null).Select(p => p.FormattedMessage).ToArray(); 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("Validating host assignment context"))); Assert.Equal(1, logLines.Count(p => p.Contains("Starting Assignment"))); Assert.Equal(1, logLines.Count(p => p.Contains("Applying 1 app setting(s)"))); Assert.Equal(1, logLines.Count(p => p.Contains($"Extracting files to '{_webHostSettings.ScriptPath}'"))); Assert.Equal(1, logLines.Count(p => p.Contains("Zip extraction complete"))); Assert.Equal(1, logLines.Count(p => p.Contains("Triggering specialization"))); 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("Node.js HttpTrigger function invoked.", logLines); // verify cold start log entry var coldStartLog = _loggerProvider.GetAllLogMessages().FirstOrDefault(p => p.Category == ScriptConstants.LogCategoryHostMetrics); JObject coldStartData = JObject.Parse(coldStartLog.FormattedMessage); Assert.Equal("Dynamic", coldStartData["sku"]); Assert.True((int)coldStartData["dispatchDuration"] > 0); Assert.True((int)coldStartData["functionDuration"] > 0); WebScriptHostManager.ResetStandbyMode(); } }
public async Task StandbyMode_EndToEnd() { var vars = new Dictionary <string, string> { { EnvironmentSettingNames.AzureWebsitePlaceholderMode, "1" }, { EnvironmentSettingNames.AzureWebsiteInstanceId, "87654639876900123453445678890144" } }; using (var env = new TestScopedEnvironmentVariable(vars)) { var httpConfig = new HttpConfiguration(); httpConfig.Formatters.Add(new PlaintextMediaTypeFormatter()); var settingsManager = ScriptSettingsManager.Instance; var testRootPath = Path.Combine(Path.GetTempPath(), "StandbyModeTest"); if (Directory.Exists(testRootPath)) { Directory.Delete(testRootPath, true); } var traceWriter = new TestTraceWriter(TraceLevel.Info); var webHostSettings = new WebHostSettings { IsSelfHost = true, LogPath = Path.Combine(testRootPath, "Logs"), SecretsPath = Path.Combine(testRootPath, "Secrets"), TraceWriter = traceWriter }; WebApiConfig.Register(httpConfig, _settingsManager, webHostSettings); var httpServer = new HttpServer(httpConfig); var httpClient = new HttpClient(httpServer); httpClient.BaseAddress = new Uri("https://localhost/"); TestHelpers.WaitForWebHost(httpClient); var traces = traceWriter.Traces.ToArray(); Assert.Equal($"Creating StandbyMode placeholder function directory ({Path.GetTempPath()}Functions\\Standby)", traces[0].Message); Assert.Equal("StandbyMode placeholder function directory created", traces[1].Message); // 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); httpServer.Dispose(); httpClient.Dispose(); await Task.Delay(2000); // verify logs string[] logLines = traceWriter.Traces.Select(p => p.Message).ToArray(); Assert.Equal(2, logLines.Count(p => p.Contains("Host is in standby mode"))); Assert.Equal(1, logLines.Count(p => p.Contains("Stopping Host"))); Assert.Equal(2, logLines.Count(p => p.Contains("Executed 'Functions.WarmUp' (Succeeded"))); } }