Beispiel #1
0
        public void PublishMemoryActivity_SendsRequestHeaders()
        {
            _metricsPublisher.Initialize();
            DateTime currentTime = DateTime.UtcNow;

            _metricsPublisher.AddMemoryActivity(_testMemoryActivity.EventTimeStamp, _testMemoryActivity.CommitSizeInBytes);
            Assert.Empty(_testLoggerProvider.GetLog());

            _metricsPublisher.OnFunctionMetricsPublishTimer(null);
            _metricsPublisher.OnFunctionMetricsPublishTimer(null);
            Assert.Empty(_testLoggerProvider.GetLog());
        }
Beispiel #2
0
        private async Task <LogMessage> RunTest(Action <IServiceCollection> configureWebHost = null, Action <IServiceCollection> configureJobHost = null, bool expectSuccess = true)
        {
            LogMessage         invalidServicesMessage = null;
            TestLoggerProvider loggerProvider         = new TestLoggerProvider();

            var builder = Program.CreateWebHostBuilder(null)
                          .ConfigureLogging(b =>
            {
                b.AddProvider(loggerProvider);
            })
                          .ConfigureServices(s =>
            {
                string uniqueTestRootPath = Path.Combine(Path.GetTempPath(), "FunctionsTest", "DependencyValidatorTests");
                s.PostConfigureAll <ScriptApplicationHostOptions>(o =>
                {
                    o.IsSelfHost  = true;
                    o.LogPath     = Path.Combine(uniqueTestRootPath, "logs");
                    o.SecretsPath = Path.Combine(uniqueTestRootPath, "secrets");
                    o.ScriptPath  = uniqueTestRootPath;
                });

                configureWebHost?.Invoke(s);
            })
                          .ConfigureScriptHostLogging(b =>
            {
                b.AddProvider(loggerProvider);
            })
                          .ConfigureScriptHostServices(b =>
            {
                configureJobHost?.Invoke(b);
            });

            using (var host = builder.Build())
            {
                await host.StartAsync();

                await TestHelpers.Await(() =>
                {
                    string expectedMessage = "Host initialization";

                    if (!expectSuccess)
                    {
                        expectedMessage = "A host error has occurred during startup operation";
                    }

                    return(loggerProvider.GetAllLogMessages()
                           .FirstOrDefault(p => p.FormattedMessage.StartsWith(expectedMessage)) != null);
                }, userMessageCallback : () => loggerProvider.GetLog());

                invalidServicesMessage = loggerProvider.GetAllLogMessages()
                                         .FirstOrDefault(m => m.Category.EndsWith(nameof(DependencyValidator)));

                await host.StopAsync();
            }

            return(invalidServicesMessage);
        }
Beispiel #3
0
        private async Task <LogMessage> RunTest(Action <IServiceCollection> configure = null)
        {
            LogMessage         invalidServicesMessage = null;
            TestLoggerProvider loggerProvider         = new TestLoggerProvider();

            var builder = Program.CreateWebHostBuilder(null)
                          .ConfigureLogging(b =>
            {
                b.AddProvider(loggerProvider);
            })
                          .ConfigureServices(s =>
            {
                s.AddSingleton <IConfigureBuilder <ILoggingBuilder> >(new DelegatedConfigureBuilder <ILoggingBuilder>(b =>
                {
                    b.AddProvider(loggerProvider);
                    configure?.Invoke(b.Services);
                }));

                string uniqueTestRootPath = Path.Combine(Path.GetTempPath(), "FunctionsTest", "DependencyValidatorTests");

                s.PostConfigureAll <ScriptApplicationHostOptions>(o =>
                {
                    o.IsSelfHost  = true;
                    o.LogPath     = Path.Combine(uniqueTestRootPath, "logs");
                    o.SecretsPath = Path.Combine(uniqueTestRootPath, "secrets");
                    o.ScriptPath  = uniqueTestRootPath;
                });
            });

            using (var host = builder.Build())
            {
                await host.StartAsync();

                await TestHelpers.Await(() =>
                {
                    return(loggerProvider.GetAllLogMessages()
                           .FirstOrDefault(p => p.FormattedMessage.StartsWith("Host initialization")) != null);
                }, userMessageCallback : () => loggerProvider.GetLog());

                invalidServicesMessage = loggerProvider.GetAllLogMessages()
                                         .FirstOrDefault(m => m.Category.EndsWith(nameof(DependencyValidator)));

                await host.StopAsync();
            }

            return(invalidServicesMessage);
        }
        public async Task InvocationsContainDifferentOperationIds()
        {
            // Verify that when a request specializes the host we don't capture the context
            // of that request. Application Insights uses this context to correlate telemetry
            // so it had a confusing effect. Previously all TimerTrigger traces would have the
            // operation id of this request and all host logs would as well.

            // Start a host in standby mode.
            StandbyManager.ResetChangeToken();

            string standbyPath           = Path.Combine(Path.GetTempPath(), "functions", "standby", "wwwroot");
            string specializedScriptRoot = @"TestScripts\CSharp";
            string scriptRootConfigPath  = ConfigurationPath.Combine(ConfigurationSectionNames.WebHost, nameof(ScriptApplicationHostOptions.ScriptPath));

            var settings = new Dictionary <string, string>()
            {
                { EnvironmentSettingNames.AzureWebsitePlaceholderMode, "1" },
                { EnvironmentSettingNames.AzureWebsiteContainerReady, null },
            };

            var environment    = new TestEnvironment(settings);
            var loggerProvider = new TestLoggerProvider();
            var channel        = new TestTelemetryChannel();

            var builder = Program.CreateWebHostBuilder()
                          .ConfigureLogging(b =>
            {
                b.AddProvider(loggerProvider);
            })
                          .ConfigureAppConfiguration(c =>
            {
                c.AddInMemoryCollection(new Dictionary <string, string>
                {
                    { scriptRootConfigPath, specializedScriptRoot }
                });
            })
                          .ConfigureServices((bc, s) =>
            {
                s.AddSingleton <IEnvironment>(environment);

                // Ensure that we don't have a race between the timer and the
                // request for triggering specialization.
                s.AddSingleton <IStandbyManager, InfiniteTimerStandbyManager>();
            })
                          .AddScriptHostBuilder(webJobsBuilder =>
            {
                webJobsBuilder.Services.AddSingleton <ITelemetryChannel>(_ => channel);

                webJobsBuilder.Services.Configure <FunctionResultAggregatorOptions>(o =>
                {
                    o.IsEnabled = false;
                });

                webJobsBuilder.Services.PostConfigure <ApplicationInsightsLoggerOptions>(o =>
                {
                    o.SamplingSettings = null;
                });

                webJobsBuilder.Services.PostConfigure <ScriptJobHostOptions>(o =>
                {
                    // Only load the function we care about, but not during standby
                    if (o.RootScriptPath != standbyPath)
                    {
                        o.Functions = new[]
                        {
                            "OneSecondTimer",
                            "FunctionExecutionContext"
                        };
                    }
                });
            })
                          .ConfigureScriptHostAppConfiguration(c =>
            {
                c.AddInMemoryCollection(new Dictionary <string, string>
                {
                    [EnvironmentSettingNames.AppInsightsInstrumentationKey] = "some_key"
                });
            });

            using (var testServer = new TestServer(builder))
            {
                var client = testServer.CreateClient();

                HttpResponseMessage response = await client.GetAsync("api/warmup");

                Assert.True(response.IsSuccessStatusCode, loggerProvider.GetLog());

                // Now that standby mode is warmed up, set the specialization properties...
                environment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebsiteContainerReady, "1");
                environment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebsitePlaceholderMode, "0");

                // ...and issue a request which will force specialization.
                response = await client.GetAsync("api/functionexecutioncontext");

                Assert.True(response.IsSuccessStatusCode, loggerProvider.GetLog());

                // Wait until we have a few logs from the timer trigger.
                IEnumerable <TraceTelemetry> timerLogs = null;
                await TestHelpers.Await(() =>
                {
                    timerLogs = channel.Telemetries
                                .OfType <TraceTelemetry>()
                                .Where(p => p.Message == "OneSecondTimer fired!");

                    return(timerLogs.Count() >= 3);
                });

                var startupRequest = channel.Telemetries
                                     .OfType <RequestTelemetry>()
                                     .Where(p => p.Name == "FunctionExecutionContext")
                                     .Single();

                // Make sure that auto-Http tracking worked with this request.
                Assert.Equal("200", startupRequest.ResponseCode);

                // The host logs should not be associated with this request.
                var logsWithRequestId = channel.Telemetries
                                        .OfType <TraceTelemetry>()
                                        .Select(p => p.Context.Operation.Id)
                                        .Where(p => p == startupRequest.Context.Operation.Id);

                // Just expect the "Executing" and "Executed" logs from the actual request.
                Assert.Equal(2, logsWithRequestId.Count());

                // And each of the timer invocations should have a different operation id, and none
                // should match the request id.
                var distinctOpIds = timerLogs.Select(p => p.Context.Operation.Id).Distinct();
                Assert.Equal(timerLogs.Count(), distinctOpIds.Count());
                Assert.Empty(timerLogs.Where(p => p.Context.Operation.Id == startupRequest.Context.Operation.Id));
            }
        }
Beispiel #5
0
        public async Task HostRestart_DuringInitialization_Cancels()
        {
            SemaphoreSlim semaphore = new SemaphoreSlim(1, 1);
            await semaphore.WaitAsync();

            bool waitingInHostBuilder = false;
            var  metricsLogger        = new TestMetricsLogger();

            // Have the first host start, but pause. We'll issue a restart, then
            // let this first host continue and see the cancelation token has fired.
            var hostA = CreateMockHost();

            hostA
            .Setup(h => h.StartAsync(It.IsAny <CancellationToken>()))
            .Returns <CancellationToken>(async ct =>
            {
                waitingInHostBuilder = true;
                await semaphore.WaitAsync();
                ct.ThrowIfCancellationRequested();
            });

            var hostB = CreateMockHost();

            hostB
            .Setup(h => h.StartAsync(It.IsAny <CancellationToken>()))
            .Returns(() => Task.CompletedTask);

            var hostBuilder = new Mock <IScriptHostBuilder>();

            hostBuilder.SetupSequence(b => b.BuildHost(It.IsAny <bool>(), It.IsAny <bool>()))
            .Returns(hostA.Object)
            .Returns(hostB.Object);

            _webHostLoggerProvider = new TestLoggerProvider();
            _loggerFactory         = new LoggerFactory();
            _loggerFactory.AddProvider(_webHostLoggerProvider);

            _hostService = new WebJobsScriptHostService(
                _monitor, hostBuilder.Object, _loggerFactory,
                _mockScriptWebHostEnvironment.Object, _mockEnvironment.Object, _hostPerformanceManager, _healthMonitorOptions, metricsLogger, new Mock <IApplicationLifetime>().Object);

            TestLoggerProvider hostALogger = hostA.Object.GetTestLoggerProvider();

            Task initialStart = _hostService.StartAsync(CancellationToken.None);

            Thread restartHostThread = new Thread(new ThreadStart(RestartHost));

            restartHostThread.Start();
            await TestHelpers.Await(() => waitingInHostBuilder);

            await TestHelpers.Await(() => _webHostLoggerProvider.GetLog().Contains("Restart requested."));

            // Now let the first host continue. It will see that startup is canceled.
            semaphore.Release();

            await TestHelpers.Await(() => _hostService.State == ScriptHostState.Running);

            restartHostThread.Join();

            await initialStart;

            Assert.True(AreRequiredMetricsGenerated(metricsLogger));

            // Make sure the error was logged to the correct logger
            Assert.Contains(hostALogger.GetAllLogMessages(), m => m.FormattedMessage != null && m.FormattedMessage.StartsWith("Host startup operation '") && m.FormattedMessage.EndsWith("' was canceled."));

            // Make sure we orphaned the correct host
            hostA.Verify(m => m.StopAsync(It.IsAny <CancellationToken>()), Times.Exactly(1));
            hostA.Verify(m => m.Dispose(), Times.Exactly(1));

            // We should not be calling Orphan on the good host
            hostB.Verify(m => m.StopAsync(It.IsAny <CancellationToken>()), Times.Never);
            hostB.Verify(m => m.Dispose(), Times.Never);

            Assert.Equal(ScriptHostState.Running, _hostService.State);
        }
Beispiel #6
0
        protected async Task VerifyWarmupSucceeds(bool restart = false)
        {
            string uri = "api/warmup";

            if (restart)
            {
                uri += "?restart=1";
            }

            // issue warmup request and verify
            var request  = new HttpRequestMessage(HttpMethod.Get, uri);
            var response = await _httpClient.SendAsync(request);

            Assert.True(response.StatusCode == HttpStatusCode.OK, $"Expected 'OK'. Actual '{response.StatusCode}'. {_loggerProvider.GetLog()}");
            string responseBody = await response.Content.ReadAsStringAsync();

            Assert.Equal("WarmUp complete.", responseBody);
        }
        public async Task IsPlaceholderMode_ThroughoutInitialization_EvaluatesCorrectly()
        {
            StandbyManager.ResetChangeToken();

            string standbyPath           = Path.Combine(Path.GetTempPath(), "functions", "standby", "wwwroot");
            string specializedScriptRoot = @"TestScripts\CSharp";
            string scriptRootConfigPath  = ConfigurationPath.Combine(ConfigurationSectionNames.WebHost, nameof(ScriptApplicationHostOptions.ScriptPath));

            var settings = new Dictionary <string, string>()
            {
                { EnvironmentSettingNames.AzureWebsitePlaceholderMode, "1" },
                { EnvironmentSettingNames.AzureWebsiteContainerReady, null },
            };

            var environment    = new TestEnvironment(settings);
            var loggerProvider = new TestLoggerProvider();

            var builder = Program.CreateWebHostBuilder()
                          .ConfigureLogging(b =>
            {
                b.AddProvider(loggerProvider);
            })
                          .ConfigureAppConfiguration(c =>
            {
                c.AddInMemoryCollection(new Dictionary <string, string>
                {
                    { scriptRootConfigPath, specializedScriptRoot }
                });
            })
                          .ConfigureServices((bc, s) =>
            {
                s.AddSingleton <IEnvironment>(environment);

                // Simulate the environment becoming specialized after these options have been
                // initialized with standby paths.
                s.AddOptions <ScriptApplicationHostOptions>()
                .PostConfigure <IEnvironment>((o, e) =>
                {
                    Specialize(e);
                });
            })
                          .AddScriptHostBuilder(webJobsBuilder =>
            {
                webJobsBuilder.Services.PostConfigure <ScriptJobHostOptions>(o =>
                {
                    // Only load the function we care about, but not during standby
                    if (o.RootScriptPath != standbyPath)
                    {
                        o.Functions = new[]
                        {
                            "HttpTrigger-Dynamic"
                        };
                    }
                });
            });

            var server = new TestServer(builder);
            var client = server.CreateClient();

            // Force the specialization middleware to run
            HttpResponseMessage response = await InvokeFunction(client);

            response.EnsureSuccessStatusCode();

            string log = loggerProvider.GetLog();

            Assert.Contains("Creating StandbyMode placeholder function directory", log);
            Assert.Contains("Starting host specialization", log);

            // Make sure this was registered.
            var hostedServices = server.Host.Services.GetServices <IHostedService>();

            Assert.Contains(hostedServices, p => p is StandbyInitializationService);
        }