Beispiel #1
0
 public AssemblyAnalysisService(IEnvironment environment, WebJobsScriptHostService scriptHost, ILoggerFactory loggerFactory, IOptionsMonitor <StandbyOptions> standbyOptionsMonitor)
 {
     _environment           = environment;
     _scriptHost            = scriptHost;
     _loggerFactory         = loggerFactory;
     _standbyOptionsMonitor = standbyOptionsMonitor;
 }
Beispiel #2
0
        public TestFunctionHost(string scriptPath, string logPath,
                                Action <IWebJobsBuilder> configureJobHost = null,
                                Action <IConfigurationBuilder> configureAppConfiguration = null,
                                Action <IServiceCollection> configureServices            = null)
        {
            _appRoot = scriptPath;

            _hostOptions = new ScriptApplicationHostOptions
            {
                IsSelfHost     = true,
                ScriptPath     = _appRoot,
                LogPath        = logPath,
                SecretsPath    = Environment.CurrentDirectory, // not used
                HasParentScope = true
            };

            var optionsMonitor = TestHelpers.CreateOptionsMonitor(_hostOptions);
            var builder        = new WebHostBuilder()
                                 .ConfigureLogging(b =>
            {
                b.AddProvider(_loggerProvider);
            })
                                 .ConfigureServices(services =>
            {
                services.Replace(new ServiceDescriptor(typeof(ISecretManagerProvider), new TestSecretManagerProvider(new TestSecretManager())));
                services.Replace(ServiceDescriptor.Singleton <IServiceProviderFactory <IServiceCollection> >(new WebHostServiceProviderFactory()));
                services.Replace(new ServiceDescriptor(typeof(IOptions <ScriptApplicationHostOptions>), new OptionsWrapper <ScriptApplicationHostOptions>(_hostOptions)));
                services.Replace(new ServiceDescriptor(typeof(IOptionsMonitor <ScriptApplicationHostOptions>), optionsMonitor));

                services.AddSingleton <IConfigureBuilder <IConfigurationBuilder> >(_ => new DelegatedConfigureBuilder <IConfigurationBuilder>(c =>
                {
                    c.AddTestSettings();
                    configureAppConfiguration?.Invoke(c);
                }));

                configureServices?.Invoke(services);
            })
                                 .AddScriptHostBuilder(webJobsBuilder =>
            {
                var loggingBuilder = new LoggingBuilder(webJobsBuilder.Services);
                loggingBuilder.AddProvider(_loggerProvider);
                loggingBuilder.AddFilter <TestLoggerProvider>(_ => true);

                webJobsBuilder.AddAzureStorage();

                configureJobHost?.Invoke(webJobsBuilder);
            })
                                 .UseStartup <Startup>();

            _testServer = new TestServer(builder);

            HttpClient             = new HttpClient(new UpdateContentLengthHandler(_testServer.CreateHandler()));
            HttpClient.BaseAddress = new Uri("https://localhost/");

            var manager = _testServer.Host.Services.GetService <IScriptHostManager>();

            _hostService = manager as WebJobsScriptHostService;

            StartAsync().GetAwaiter().GetResult();
        }
        public async Task HostRestart_BeforeStart_WaitsForStartToContinue()
        {
            _host = CreateMockHost();

            var hostBuilder = new Mock <IScriptHostBuilder>();

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

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

            _hostService = new WebJobsScriptHostService(
                _monitor, hostBuilder.Object, _loggerFactory, _mockRootServiceProvider.Object, _mockRootScopeFactory.Object,
                _mockScriptWebHostEnvironment.Object, _mockEnvironment.Object, _hostPerformanceManager, _healthMonitorOptions);

            // Simulate a call to specialize coming from the PlaceholderSpecializationMiddleware. This
            // can happen before we ever start the service, which could create invalid state.
            Task restartTask = _hostService.RestartHostAsync(CancellationToken.None);

            await _hostService.StartAsync(CancellationToken.None);

            await restartTask;

            var messages = _webHostLoggerProvider.GetAllLogMessages();

            // The CancellationToken is canceled quick enough that we never build the initial host, so the
            // only one we start is the restarted/specialized one.
            Assert.NotNull(messages.Single(p => p.EventId.Id == 513)); // "Building" message
            Assert.NotNull(messages.Single(p => p.EventId.Id == 514)); // "StartupWasCanceled" message
            Assert.NotNull(messages.Single(p => p.EventId.Id == 520)); // "RestartBeforeStart" message
            _host.Verify(p => p.StartAsync(It.IsAny <CancellationToken>()), Times.Once);
        }
        public async Task StartAsync_Succeeds()
        {
            var hostBuilder = new Mock <IScriptHostBuilder>();

            hostBuilder.Setup(b => b.BuildHost(It.IsAny <bool>(), It.IsAny <bool>())).Returns(_host.Object);

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

            var mockEventManager = new Mock <IScriptEventManager>(MockBehavior.Strict);

            mockEventManager.Setup(p => p.Publish(It.IsAny <HostStartEvent>()));

            var metricsLogger = new TestMetricsLogger();

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

            await _hostService.StartAsync(CancellationToken.None);

            // add general post startup validations here
            mockEventManager.Verify(_ => _.Publish(It.IsAny <HostStartEvent>()), Times.Once());
        }
        public async Task HostInitialization_OnInitializationException_MaintainsErrorInformation()
        {
            // When an exception is thrown, we'll create a new host. Make sure
            // we don't return the same one (with disposed services) the second time.
            var hostA = CreateMockHost();

            hostA.Setup(h => h.StartAsync(It.IsAny <CancellationToken>()))
            .Throws(new HostInitializationException("boom"));

            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);

            _hostService = new WebJobsScriptHostService(
                _monitor, hostBuilder.Object, NullLoggerFactory.Instance, _mockRootServiceProvider.Object, _mockRootScopeFactory.Object,
                _mockScriptWebHostEnvironment.Object, _mockEnvironment.Object, _hostPerformanceManager, _healthMonitorOptions);

            await _hostService.StartAsync(CancellationToken.None);

            Assert.Equal(ScriptHostState.Error, _hostService.State);
            Assert.IsType <HostInitializationException>(_hostService.LastError);
        }
        public async Task HostRestart_Specialization_Succeeds()
        {
            _host.Setup(h => h.StartAsync(It.IsAny <CancellationToken>()))
            .Returns(Task.CompletedTask);

            var hostBuilder = new Mock <IScriptHostBuilder>();

            hostBuilder.Setup(b => b.BuildHost(It.IsAny <bool>(), It.IsAny <bool>()))
            .Returns(_host.Object);

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

            _hostService = new WebJobsScriptHostService(
                _monitor, hostBuilder.Object, _loggerFactory, _mockRootServiceProvider.Object, _mockRootScopeFactory.Object,
                _mockScriptWebHostEnvironment.Object, _mockEnvironment.Object, _hostPerformanceManager, _healthMonitorOptions);

            await _hostService.StartAsync(CancellationToken.None);

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

            restartHostThread.Start();
            specializeHostThread.Start();
            restartHostThread.Join();
            specializeHostThread.Join();

            var logMessages = _webHostLoggerProvider.GetAllLogMessages().Where(m => m.FormattedMessage.Contains("Restarting host."));

            Assert.Equal(2, logMessages.Count());
        }
        public void ShouldEnforceSequentialRestart_WithCorrectConfig(string value, bool expectedResult)
        {
            var metricsLogger = new TestMetricsLogger();

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

            var hostBuilder = new Mock <IScriptHostBuilder>();

            hostBuilder.Setup(b => b.BuildHost(It.IsAny <bool>(), It.IsAny <bool>()))
            .Returns(_host.Object);

            IConfiguration config = new ConfigurationBuilder()
                                    .AddInMemoryCollection(new Dictionary <string, string>
            {
                { "AzureFunctionsJobHost:SequentialRestart", value },
            })
                                    .Build();

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

            Assert.Equal(expectedResult, _hostService.ShouldEnforceSequentialRestart());
        }
        public WebJobsScriptHostServiceTests()
        {
            // configure the monitor so it will fail within a couple seconds
            _healthMonitorOptions = new HostHealthMonitorOptions
            {
                HealthCheckInterval  = TimeSpan.FromMilliseconds(100),
                HealthCheckWindow    = TimeSpan.FromSeconds(1),
                HealthCheckThreshold = 5
            };
            var wrappedHealthMonitorOptions = new OptionsWrapper <HostHealthMonitorOptions>(_healthMonitorOptions);

            _mockApplicationLifetime = new Mock <IApplicationLifetime>(MockBehavior.Loose);
            _mockApplicationLifetime.Setup(p => p.StopApplication())
            .Callback(() =>
            {
                _shutdownCalled = true;
            });

            _mockEnvironment = new Mock <IEnvironment>();
            var mockServiceProvider = new Mock <IServiceProvider>(MockBehavior.Strict);

            _mockEnvironment.Setup(p => p.GetEnvironmentVariable(EnvironmentSettingNames.AzureWebsiteInstanceId)).Returns("testapp");
            _mockEnvironment.Setup(p => p.GetEnvironmentVariable(EnvironmentSettingNames.AzureWebsiteHostName)).Returns("testapp");

            var mockHostPerformanceManager = new Mock <HostPerformanceManager>(_mockEnvironment.Object, wrappedHealthMonitorOptions, mockServiceProvider.Object, null);

            mockHostPerformanceManager.Setup(p => p.PerformanceCountersExceeded(It.IsAny <Collection <string> >(), It.IsAny <ILogger>()))
            .Callback <Collection <string>, ILogger>((c, l) =>
            {
                if (_countersExceeded)
                {
                    foreach (var counter in _exceededCounters)
                    {
                        c.Add(counter);
                    }
                }
            })
            .Returns(() => _countersExceeded);

            _testHost = new TestFunctionHost(TestScriptPath, TestLogPath,
                                             configureWebHostServices: services =>
            {
                services.AddSingleton <IOptions <HostHealthMonitorOptions> >(wrappedHealthMonitorOptions);
                services.AddSingleton <IApplicationLifetime>(_mockApplicationLifetime.Object);
                services.AddSingleton <IEnvironment>(_mockEnvironment.Object);
                services.AddSingleton <HostPerformanceManager>(mockHostPerformanceManager.Object);

                services.AddSingleton <IConfigureBuilder <IWebJobsBuilder> >(new DelegatedConfigureBuilder <IWebJobsBuilder>(b =>
                {
                    b.UseHostId("1234");
                    b.Services.Configure <ScriptJobHostOptions>(o => o.Functions = new[] { "ManualTrigger", "Scenarios" });
                }));
            },
                                             configureScriptHostWebJobsBuilder: builder =>
            {
                builder.AddExtension <TestWebHookExtension>();
            });

            _scriptHostService = _testHost.JobHostServices.GetService <IScriptHostManager>() as WebJobsScriptHostService;
        }
Beispiel #9
0
        public async Task <IActionResult> Warmup([FromServices] WebJobsScriptHostService hostService)
        {
            // Endpoint only for Windows Elastic Premium or Linux App Service plans
            if (!(_environment.IsLinuxAppService() || _environment.IsWindowsElasticPremium()))
            {
                return(BadRequest("This API is not available for the current hosting plan"));
            }

            if (Interlocked.CompareExchange(ref _warmupExecuted, 1, 0) != 0)
            {
                return(Ok());
            }

            var jobHost = hostService.Services?.GetService <IScriptJobHost>();

            if (jobHost == null)
            {
                _logger.LogError($"No active host available.");
                return(StatusCode(503));
            }

            await jobHost.TryInvokeWarmupAsync();

            return(Ok());
        }
Beispiel #10
0
        public WebJobsScriptHostServiceTests()
        {
            string testScriptPath = @"TestScripts\CSharp";
            string testLogPath    = Path.Combine(TestHelpers.FunctionsTestDirectory, "Logs", Guid.NewGuid().ToString(), @"Functions");

            // configure the monitor so it will fail within a couple seconds
            _healthMonitorOptions = new HostHealthMonitorOptions
            {
                HealthCheckInterval  = TimeSpan.FromMilliseconds(100),
                HealthCheckWindow    = TimeSpan.FromSeconds(1),
                HealthCheckThreshold = 5
            };
            var wrappedHealthMonitorOptions = new OptionsWrapper <HostHealthMonitorOptions>(_healthMonitorOptions);

            _mockJobHostEnvironment = new Mock <IScriptJobHostEnvironment>(MockBehavior.Strict);
            _mockJobHostEnvironment.Setup(p => p.Shutdown())
            .Callback(() =>
            {
                _shutdownCalled = true;
            });

            var mockEnvironment = new Mock <IEnvironment>();

            mockEnvironment.Setup(p => p.GetEnvironmentVariable(EnvironmentSettingNames.AzureWebsiteInstanceId)).Returns("testapp");

            var mockHostPerformanceManager = new Mock <HostPerformanceManager>(mockEnvironment.Object, wrappedHealthMonitorOptions);

            mockHostPerformanceManager.Setup(p => p.IsUnderHighLoad(It.IsAny <Collection <string> >(), It.IsAny <ILogger>()))
            .Callback <Collection <string>, ILogger>((c, l) =>
            {
                if (_underHighLoad)
                {
                    foreach (var counter in _exceededCounters)
                    {
                        c.Add(counter);
                    }
                }
            })
            .Returns(() => _underHighLoad);

            _testHost = new TestFunctionHost(testScriptPath, testLogPath,
                                             configureWebHostServices: services =>
            {
                services.AddSingleton <IOptions <HostHealthMonitorOptions> >(wrappedHealthMonitorOptions);
                services.AddSingleton <IScriptJobHostEnvironment>(_mockJobHostEnvironment.Object);
                services.AddSingleton <IEnvironment>(mockEnvironment.Object);
                services.AddSingleton <HostPerformanceManager>(mockHostPerformanceManager.Object);

                services.AddSingleton <IConfigureBuilder <IWebJobsBuilder> >(new DelegatedConfigureBuilder <IWebJobsBuilder>(b =>
                {
                    b.UseHostId("1234");
                    b.Services.Configure <ScriptJobHostOptions>(o => o.Functions = new[] { "ManualTrigger", "Scenarios" });
                }));
            });

            _scriptHostService = _testHost.JobHostServices.GetService <IScriptHostManager>() as WebJobsScriptHostService;
        }
        public async Task DisposesScriptHost()
        {
            var metricsLogger = new TestMetricsLogger();

            var services = new ServiceCollection()
                           .AddLogging(l =>
            {
                l.Services.AddSingleton <ILoggerProvider, TestLoggerProvider>();
                l.AddFilter(_ => true);
            })
                           .BuildServiceProvider();

            var host = new Mock <IHost>();

            host.Setup(h => h.Services)
            .Returns(services);

            host.Setup(h => h.Dispose())
            .Callback(() =>
            {
                services.Dispose();
            });

            var hostBuilder = new Mock <IScriptHostBuilder>();

            hostBuilder.Setup(b => b.BuildHost(It.IsAny <bool>(), It.IsAny <bool>()))
            .Returns(host.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,
                _mockConfig, new TestScriptEventManager());

            var hostLogger = host.Object.GetTestLoggerProvider();

            await _hostService.StartAsync(CancellationToken.None);

            await _hostService.RestartHostAsync(CancellationToken.None);

            Assert.True(AreRequiredMetricsGenerated(metricsLogger));

            host.Verify(m => m.StopAsync(It.IsAny <CancellationToken>()), Times.Exactly(1));
            host.Verify(m => m.Dispose(), Times.Exactly(1));

            var allLogMessages = _webHostLoggerProvider.GetAllLogMessages();

            Assert.Contains(allLogMessages,
                            m => m.FormattedMessage != null &&
                            m.FormattedMessage.Contains("ScriptHost disposed"));
        }
Beispiel #12
0
        public async Task Invoke(HttpContext httpContext, WebJobsScriptHostService manager)
        {
            if (manager.Services is IServiceScopeFactory scopedServiceProvider)
            {
                var features        = httpContext.Features;
                var servicesFeature = features.Get <IServiceProvidersFeature>();
                features.Set <IServiceProvidersFeature>(new RequestServicesFeature(httpContext, scopedServiceProvider));
            }

            await _next(httpContext);
        }
        public async Task DisposedHost_ServicesNotExposed()
        {
            SemaphoreSlim blockingSemaphore = new SemaphoreSlim(0, 1);
            SemaphoreSlim disposedSemaphore = new SemaphoreSlim(0, 1);
            var           metricsLogger     = new TestMetricsLogger();

            // Have the first host throw upon starting. Then pause while building the second
            // host. When accessing Services then, they should be null, rather than disposed.
            var hostA = CreateMockHost(disposedSemaphore);

            hostA
            .Setup(h => h.StartAsync(It.IsAny <CancellationToken>()))
            .Returns(() =>
            {
                throw new InvalidOperationException("Something happened at startup!");
            });

            var hostB = CreateMockHost();

            hostB
            .Setup(h => h.StartAsync(It.IsAny <CancellationToken>()))
            .Returns(async() =>
            {
                await blockingSemaphore.WaitAsync();
            });

            var hostBuilder = new Mock <IScriptHostBuilder>();

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

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

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

            await disposedSemaphore.WaitAsync();

            Assert.Null(_hostService.Services);

            blockingSemaphore.Release();

            await startTask;

            Assert.True(AreRequiredMetricsGenerated(metricsLogger));
        }
        public async Task Invoke(HttpContext httpContext, WebJobsScriptHostService manager)
        {
            // TODO: DI (FACAVAL) Remove this once the host check middleware is updated.
            //await Task.Delay(5000);

            if (manager.Services is IServiceScopeFactory scopedServiceProvider)
            {
                var features        = httpContext.Features;
                var servicesFeature = features.Get <IServiceProvidersFeature>();
                features.Set <IServiceProvidersFeature>(new RequestServicesFeature(httpContext, scopedServiceProvider));
            }

            await _next(httpContext);
        }
        public async Task DependencyTrackingTelemetryModule_Race()
        {
            var hostBuilder = new HostBuilder()
                              .ConfigureAppConfiguration(c =>
            {
                c.AddInMemoryCollection(new Dictionary <string, string>
                {
                    { "APPINSIGHTS_INSTRUMENTATIONKEY", "some_key" },
                });
            })
                              .ConfigureDefaultTestWebScriptHost();

            var scriptHostBuilder = new Mock <IScriptHostBuilder>();

            scriptHostBuilder.Setup(b => b.BuildHost(It.IsAny <bool>(), It.IsAny <bool>()))
            .Returns(hostBuilder.Build());

            bool done         = false;
            var  listenerTask = Task.Run(() =>
            {
                while (!done)
                {
                    using (new DiagnosticListener("Azure.SomeClient"))
                    {
                    }
                }
            });

            using (_hostService = new WebJobsScriptHostService(
                       _monitor, scriptHostBuilder.Object, NullLoggerFactory.Instance,
                       _mockScriptWebHostEnvironment.Object, _mockEnvironment.Object,
                       _hostPerformanceManager, _healthMonitorOptions, new TestMetricsLogger(),
                       new Mock <IApplicationLifetime>().Object, _mockConfig, new TestScriptEventManager()))
            {
                await _hostService.StartAsync(CancellationToken.None);

                await Task.Delay(500);

                await _hostService.StopAsync(CancellationToken.None);
            }

            done = true;
            await listenerTask;
        }
        public async Task HostInitialization_OnInitializationException_MaintainsErrorInformation()
        {
            var options = new ScriptApplicationHostOptions
            {
                ScriptPath = @"c:\tests",
                LogPath    = @"c:\tests\logs",
            };

            var monitor = new ScriptApplicationHostOptionsMonitor(options);

            var services = new ServiceCollection()
                           .AddLogging()
                           .BuildServiceProvider();

            var host = new Mock <IHost>();

            host.Setup(h => h.Services)
            .Returns(services);
            host.SetupSequence(h => h.StartAsync(It.IsAny <CancellationToken>()))
            .Throws(new HostInitializationException("boom"))
            .Returns(Task.CompletedTask);

            var hostBuilder = new Mock <IScriptHostBuilder>();

            hostBuilder.Setup(b => b.BuildHost(It.IsAny <bool>(), It.IsAny <bool>()))
            .Returns(host.Object);

            var mockRootServiceProvider      = new Mock <IServiceProvider>();
            var mockRootScopeFactory         = new Mock <IServiceScopeFactory>();
            var mockScriptWebHostEnvironment = new Mock <IScriptWebHostEnvironment>();
            var mockEnvironment        = new Mock <IEnvironment>();
            var healthMonitorOptions   = new OptionsWrapper <HostHealthMonitorOptions>(new HostHealthMonitorOptions());
            var hostPerformanceManager = new HostPerformanceManager(mockEnvironment.Object, healthMonitorOptions);

            var hostService = new WebJobsScriptHostService(
                monitor, hostBuilder.Object, NullLoggerFactory.Instance, mockRootServiceProvider.Object, mockRootScopeFactory.Object,
                mockScriptWebHostEnvironment.Object, mockEnvironment.Object, hostPerformanceManager, healthMonitorOptions);

            await hostService.StartAsync(CancellationToken.None);

            Assert.Equal(ScriptHostState.Error, hostService.State);
            Assert.IsType <HostInitializationException>(hostService.LastError);
        }
 public ScriptHostRequestServiceProviderMiddleware(RequestDelegate next, WebJobsScriptHostService scriptHostService)
 {
     _next = next;
     _scriptHostService = scriptHostService;
 }
        public async Task HostRestart_DuringInitialization_Recovers()
        {
            SemaphoreSlim semaphore = new SemaphoreSlim(1, 1);
            await semaphore.WaitAsync();

            // Have the first host start, but pause. We'll issue a restart, wait for the
            // second host to be running, then let this first host throw an exception.
            var hostA = CreateMockHost();

            hostA
            .Setup(h => h.StartAsync(It.IsAny <CancellationToken>()))
            .Returns(async() =>
            {
                await semaphore.WaitAsync();
                throw new InvalidOperationException("Something happened at startup!");
            });

            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, _mockRootServiceProvider.Object, _mockRootScopeFactory.Object,
                _mockScriptWebHostEnvironment.Object, _mockEnvironment.Object, _hostPerformanceManager, _healthMonitorOptions);

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

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

            restartHostThread.Start();
            restartHostThread.Join();

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

            // Now let the first host throw its startup exception.
            semaphore.Release();

            await initialStart;

            // Note that HostA is disposed so its services cannot be accessed. Logging will fall
            // back to using the WebHost's logger.
            Assert.Throws <ObjectDisposedException>(() => hostA.Object.GetTestLoggerProvider());
            TestLoggerProvider hostBLogger = hostB.Object.GetTestLoggerProvider();

            // Make sure the error was logged to the correct logger
            Assert.Contains(_webHostLoggerProvider.GetAllLogMessages(), m => m.FormattedMessage != null && m.FormattedMessage.Contains("A host error has occurred on an inactive host"));
            Assert.DoesNotContain(hostBLogger.GetAllLogMessages(), m => m.FormattedMessage != null && m.FormattedMessage.Contains("A host error has occurred"));

            // Make sure we orphaned the correct host when the exception was thrown. This will happen
            // twice: once during Restart and once during the Orphan call when handling the exception.
            hostA.Verify(m => m.StopAsync(It.IsAny <CancellationToken>()), Times.Exactly(2));
            hostA.Verify(m => m.Dispose(), Times.Exactly(2));

            // 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);

            // The "late" exception from the first host shouldn't bring things down
            Assert.Equal(ScriptHostState.Running, _hostService.State);
        }
Beispiel #19
0
        public TestFunctionHost(string scriptPath, string logPath,
                                Action <IServiceCollection> configureWebHostServices               = null,
                                Action <IWebJobsBuilder> configureScriptHostWebJobsBuilder         = null,
                                Action <IConfigurationBuilder> configureScriptHostAppConfiguration = null,
                                Action <ILoggingBuilder> configureScriptHostLogging     = null,
                                Action <IServiceCollection> configureScriptHostServices = null)
        {
            _appRoot = scriptPath;

            _hostOptions = new ScriptApplicationHostOptions
            {
                IsSelfHost     = true,
                ScriptPath     = _appRoot,
                LogPath        = logPath,
                SecretsPath    = Environment.CurrentDirectory, // not used
                HasParentScope = true
            };

            var builder = new WebHostBuilder()
                          .ConfigureLogging(b =>
            {
                b.AddProvider(_webHostLoggerProvider);
            })
                          .ConfigureServices(services =>
            {
                services.Replace(new ServiceDescriptor(typeof(ISecretManagerProvider), new TestSecretManagerProvider(new TestSecretManager())));
                services.Replace(ServiceDescriptor.Singleton <IServiceProviderFactory <IServiceCollection> >(new WebHostServiceProviderFactory()));
                services.Replace(new ServiceDescriptor(typeof(IOptions <ScriptApplicationHostOptions>), sp =>
                {
                    _hostOptions.RootServiceProvider = sp;
                    return(new OptionsWrapper <ScriptApplicationHostOptions>(_hostOptions));
                }, ServiceLifetime.Singleton));
                services.Replace(new ServiceDescriptor(typeof(IOptionsMonitor <ScriptApplicationHostOptions>), sp =>
                {
                    _hostOptions.RootServiceProvider = sp;
                    return(TestHelpers.CreateOptionsMonitor(_hostOptions));
                }, ServiceLifetime.Singleton));
                services.Replace(new ServiceDescriptor(typeof(IExtensionBundleManager), new TestExtensionBundleManager()));
                services.Replace(new ServiceDescriptor(typeof(IFunctionMetadataManager), sp =>
                {
                    var montior       = sp.GetService <IOptionsMonitor <ScriptApplicationHostOptions> >();
                    var scriptManager = sp.GetService <IScriptHostManager>();
                    var loggerFactory = sp.GetService <ILoggerFactory>();
                    var environment   = sp.GetService <IEnvironment>();

                    return(GetMetadataManager(montior, scriptManager, loggerFactory, environment));
                }, ServiceLifetime.Singleton));

                services.SkipDependencyValidation();

                // Allows us to configure services as the last step, thereby overriding anything
                services.AddSingleton(new PostConfigureServices(configureWebHostServices));
            })
                          .ConfigureScriptHostWebJobsBuilder(scriptHostWebJobsBuilder =>
            {
                /// REVIEW THIS
                scriptHostWebJobsBuilder.AddAzureStorage();
                configureScriptHostWebJobsBuilder?.Invoke(scriptHostWebJobsBuilder);
            })
                          .ConfigureScriptHostAppConfiguration(scriptHostConfigurationBuilder =>
            {
                scriptHostConfigurationBuilder.AddTestSettings();
                configureScriptHostAppConfiguration?.Invoke(scriptHostConfigurationBuilder);
            })
                          .ConfigureScriptHostLogging(scriptHostLoggingBuilder =>
            {
                scriptHostLoggingBuilder.AddProvider(_scriptHostLoggerProvider);
                scriptHostLoggingBuilder.AddFilter <TestLoggerProvider>(_ => true);
                configureScriptHostLogging?.Invoke(scriptHostLoggingBuilder);
            })
                          .ConfigureScriptHostServices(scriptHostServices =>
            {
                configureScriptHostServices?.Invoke(scriptHostServices);
            })
                          .ConfigureAppConfiguration((builderContext, config) =>
            {
                // replace the default environment source with our own
                IConfigurationSource envVarsSource = config.Sources.OfType <EnvironmentVariablesConfigurationSource>().FirstOrDefault();
                if (envVarsSource != null)
                {
                    config.Sources.Remove(envVarsSource);
                }

                config.Add(new ScriptEnvironmentVariablesConfigurationSource());
                config.AddTestSettings();
            })
                          .UseStartup <TestStartup>();

            _testServer = new TestServer(builder)
            {
                BaseAddress = new Uri("https://localhost/")
            };

            HttpClient         = _testServer.CreateClient();
            HttpClient.Timeout = TimeSpan.FromMinutes(5);

            var manager = _testServer.Host.Services.GetService <IScriptHostManager>();

            _hostService = manager as WebJobsScriptHostService;

            // Wire up StopApplication calls as they behave in hosted scenarios
            var lifetime = WebHostServices.GetService <IApplicationLifetime>();

            lifetime.ApplicationStopping.Register(async() => await _testServer.Host.StopAsync());

            StartAsync().GetAwaiter().GetResult();

            _stillRunningTimer = new Timer(StillRunningCallback, _testServer, TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(30));

            // store off a bit of the creation stack for easier debugging if this host doesn't shut down.
            var stack = new StackTrace(true).ToString().Split(Environment.NewLine).Take(5);

            _createdStack = string.Join($"{Environment.NewLine}    ", stack);

            // cache startup logs since tests clear logs from time to time
            StartupLogs = GetScriptHostLogMessages();
        }
        public TestFunctionHost(string scriptPath, string logPath,
                                Action <IServiceCollection> configureWebHostServices               = null,
                                Action <IWebJobsBuilder> configureScriptHostWebJobsBuilder         = null,
                                Action <IConfigurationBuilder> configureScriptHostAppConfiguration = null,
                                Action <ILoggingBuilder> configureScriptHostLogging     = null,
                                Action <IServiceCollection> configureScriptHostServices = null)
        {
            _appRoot = scriptPath;

            _hostOptions = new ScriptApplicationHostOptions
            {
                IsSelfHost     = true,
                ScriptPath     = _appRoot,
                LogPath        = logPath,
                SecretsPath    = Environment.CurrentDirectory, // not used
                HasParentScope = true
            };

            var optionsMonitor  = TestHelpers.CreateOptionsMonitor(_hostOptions);
            var serviceProvider = new TestServiceProvider(_hostOptions, optionsMonitor);

            _hostOptions.RootServiceProvider = serviceProvider;

            var builder = new WebHostBuilder()
                          .ConfigureLogging(b =>
            {
                b.AddProvider(_webHostLoggerProvider);
            })
                          .ConfigureServices(services =>
            {
                services.Replace(new ServiceDescriptor(typeof(ISecretManagerProvider), new TestSecretManagerProvider(new TestSecretManager())));
                services.Replace(ServiceDescriptor.Singleton <IServiceProviderFactory <IServiceCollection> >(new WebHostServiceProviderFactory()));
                services.Replace(new ServiceDescriptor(typeof(IOptions <ScriptApplicationHostOptions>), new OptionsWrapper <ScriptApplicationHostOptions>(_hostOptions)));
                services.Replace(new ServiceDescriptor(typeof(IOptionsMonitor <ScriptApplicationHostOptions>), optionsMonitor));
                services.Replace(new ServiceDescriptor(typeof(IExtensionBundleManager), new TestExtensionBundleManager()));


                // Allows us to configure services as the last step, thereby overriding anything
                services.AddSingleton(new PostConfigureServices(configureWebHostServices));
            })
                          .ConfigureScriptHostWebJobsBuilder(scriptHostWebJobsBuilder =>
            {
                scriptHostWebJobsBuilder.AddAzureStorage();
                configureScriptHostWebJobsBuilder?.Invoke(scriptHostWebJobsBuilder);
            })
                          .ConfigureScriptHostAppConfiguration(scriptHostConfigurationBuilder =>
            {
                scriptHostConfigurationBuilder.AddTestSettings();
                configureScriptHostAppConfiguration?.Invoke(scriptHostConfigurationBuilder);
            })
                          .ConfigureScriptHostLogging(scriptHostLoggingBuilder =>
            {
                scriptHostLoggingBuilder.AddProvider(_scriptHostLoggerProvider);
                scriptHostLoggingBuilder.AddFilter <TestLoggerProvider>(_ => true);
                configureScriptHostLogging?.Invoke(scriptHostLoggingBuilder);
            })
                          .ConfigureScriptHostServices(scriptHostServices =>
            {
                configureScriptHostServices?.Invoke(scriptHostServices);
            })
                          .UseStartup <TestStartup>();

            _testServer = new TestServer(builder);

            HttpClient = new HttpClient(new UpdateContentLengthHandler(_testServer.CreateHandler()))
            {
                BaseAddress = new Uri("https://localhost/")
            };

            var manager = _testServer.Host.Services.GetService <IScriptHostManager>();

            _hostService = manager as WebJobsScriptHostService;
            StartAsync().GetAwaiter().GetResult();
        }
Beispiel #21
0
        public async Task HostRestart_DuringInitializationWithError_Recovers()
        {
            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 throw an exception.
            var hostA = CreateMockHost();

            hostA
            .Setup(h => h.StartAsync(It.IsAny <CancellationToken>()))
            .Returns(async() =>
            {
                waitingInHostBuilder = true;
                await semaphore.WaitAsync();
                throw new InvalidOperationException("Something happened at startup!");
            });

            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();
            TestLoggerProvider hostBLogger = hostB.Object.GetTestLoggerProvider();

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

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

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

            // Now let the first host continue. It will throw, which should be correctly handled.
            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.Contains("A host error has occurred"));
            Assert.DoesNotContain(hostBLogger.GetAllLogMessages(), m => m.FormattedMessage != null && m.FormattedMessage.Contains("A host error has occurred"));

            // Make sure we orphaned the correct host when the exception was thrown.
            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);

            // The "late" exception from the first host shouldn't bring things down
            Assert.Equal(ScriptHostState.Running, _hostService.State);
        }
Beispiel #22
0
        public TestFunctionHost(string scriptPath, string logPath,
                                Action <IServiceCollection> configureWebHostServices               = null,
                                Action <IWebJobsBuilder> configureScriptHostWebJobsBuilder         = null,
                                Action <IConfigurationBuilder> configureScriptHostAppConfiguration = null,
                                Action <ILoggingBuilder> configureScriptHostLogging     = null,
                                Action <IServiceCollection> configureScriptHostServices = null)
        {
            _appRoot = scriptPath;

            _hostOptions = new ScriptApplicationHostOptions
            {
                IsSelfHost     = true,
                ScriptPath     = _appRoot,
                LogPath        = logPath,
                SecretsPath    = Environment.CurrentDirectory, // not used
                HasParentScope = true
            };

            var builder = new WebHostBuilder()
                          .ConfigureLogging(b =>
            {
                b.AddProvider(_webHostLoggerProvider);
            })
                          .ConfigureServices(services =>
            {
                services.Replace(new ServiceDescriptor(typeof(ISecretManagerProvider), new TestSecretManagerProvider(new TestSecretManager())));
                services.Replace(ServiceDescriptor.Singleton <IServiceProviderFactory <IServiceCollection> >(new WebHostServiceProviderFactory()));
                services.Replace(new ServiceDescriptor(typeof(IOptions <ScriptApplicationHostOptions>), sp =>
                {
                    _hostOptions.RootServiceProvider = sp;
                    return(new OptionsWrapper <ScriptApplicationHostOptions>(_hostOptions));
                }, ServiceLifetime.Singleton));
                services.Replace(new ServiceDescriptor(typeof(IOptionsMonitor <ScriptApplicationHostOptions>), sp =>
                {
                    _hostOptions.RootServiceProvider = sp;
                    return(TestHelpers.CreateOptionsMonitor(_hostOptions));
                }, ServiceLifetime.Singleton));
                services.Replace(new ServiceDescriptor(typeof(IExtensionBundleManager), new TestExtensionBundleManager()));
                services.Replace(new ServiceDescriptor(typeof(IFunctionMetadataManager), sp =>
                {
                    var montior       = sp.GetService <IOptionsMonitor <ScriptApplicationHostOptions> >();
                    var scriptManager = sp.GetService <IScriptHostManager>();
                    var loggerFactory = sp.GetService <ILoggerFactory>();

                    return(GetMetadataManager(montior, scriptManager, loggerFactory));
                }, ServiceLifetime.Singleton));

                // Allows us to configure services as the last step, thereby overriding anything
                services.AddSingleton(new PostConfigureServices(configureWebHostServices));
            })
                          .ConfigureScriptHostWebJobsBuilder(scriptHostWebJobsBuilder =>
            {
                scriptHostWebJobsBuilder.AddAzureStorage();
                configureScriptHostWebJobsBuilder?.Invoke(scriptHostWebJobsBuilder);
            })
                          .ConfigureScriptHostAppConfiguration(scriptHostConfigurationBuilder =>
            {
                scriptHostConfigurationBuilder.AddTestSettings();
                configureScriptHostAppConfiguration?.Invoke(scriptHostConfigurationBuilder);
            })
                          .ConfigureScriptHostLogging(scriptHostLoggingBuilder =>
            {
                scriptHostLoggingBuilder.AddProvider(_scriptHostLoggerProvider);
                scriptHostLoggingBuilder.AddFilter <TestLoggerProvider>(_ => true);
                configureScriptHostLogging?.Invoke(scriptHostLoggingBuilder);
            })
                          .ConfigureScriptHostServices(scriptHostServices =>
            {
                configureScriptHostServices?.Invoke(scriptHostServices);
            })
                          .UseStartup <TestStartup>();

            _testServer = new TestServer(builder)
            {
                BaseAddress = new Uri("https://localhost/")
            };

            HttpClient = _testServer.CreateClient();

            var manager = _testServer.Host.Services.GetService <IScriptHostManager>();

            _hostService = manager as WebJobsScriptHostService;

            // Wire up StopApplication calls as they behave in hosted scenarios
            var lifetime = WebHostServices.GetService <IApplicationLifetime>();

            lifetime.ApplicationStopping.Register(async() => await _testServer.Host.StopAsync());

            StartAsync().GetAwaiter().GetResult();
        }
Beispiel #23
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);
        }