Beispiel #1
0
        public StandbyManagerE2ETestBase()
        {
            _testRootPath = Path.Combine(Path.GetTempPath(), "StandbyManagerTests");
            CleanupTestDirectory();

            StandbyManager.ResetChangeToken();
        }
Beispiel #2
0
        public SpecializationE2ETests()
        {
            StandbyManager.ResetChangeToken();

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

            _environment    = new TestEnvironment(settings);
            _loggerProvider = new TestLoggerProvider();

            _pauseBeforeHostBuild       = new SemaphoreSlim(1, 1);
            _pauseAfterStandbyHostBuild = new SemaphoreSlim(1, 1);
            _buildCount = new SemaphoreSlim(2, 2);
        }
        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));
            }
        }
        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);
        }