Inheritance: Microsoft.Azure.WebJobs.Host.TraceWriter
        protected EndToEndTestFixture(string rootPath)
        {
            string connectionString = AmbientConnectionStringProvider.Instance.GetConnectionString(ConnectionStringNames.Storage);
            CloudStorageAccount storageAccount = CloudStorageAccount.Parse(connectionString);
            _queueClient = storageAccount.CreateCloudQueueClient();
            _blobClient = storageAccount.CreateCloudBlobClient();

            CreateTestStorageEntities();
            TraceWriter = new TestTraceWriter(TraceLevel.Verbose);

            ScriptHostConfiguration config = new ScriptHostConfiguration()
            {
                RootScriptPath = rootPath,
                TraceWriter = TraceWriter,
                FileLoggingEnabled = true
            };

            HostManager = new ScriptHostManager(config);

            Thread t = new Thread(_ =>
            {
                HostManager.RunAndBlock();
            });
            t.Start();

            TestHelpers.Await(() => HostManager.IsRunning).Wait();
        }
        protected EndToEndTestFixture(string rootPath, string testId)
        {
            _settingsManager = ScriptSettingsManager.Instance;
            FixtureId = testId;
            string connectionString = AmbientConnectionStringProvider.Instance.GetConnectionString(ConnectionStringNames.Storage);
            CloudStorageAccount storageAccount = CloudStorageAccount.Parse(connectionString);
            QueueClient = storageAccount.CreateCloudQueueClient();
            BlobClient = storageAccount.CreateCloudBlobClient();
            TableClient = storageAccount.CreateCloudTableClient();

            CreateTestStorageEntities();
            TraceWriter = new TestTraceWriter(TraceLevel.Verbose);

            ApiHubTestHelper.SetDefaultConnectionFactory();

            ScriptHostConfiguration config = new ScriptHostConfiguration()
            {
                RootScriptPath = rootPath,
                TraceWriter = TraceWriter,
                FileLoggingMode = FileLoggingMode.Always
            };

            RequestConfiguration = new HttpConfiguration();
            RequestConfiguration.Formatters.Add(new PlaintextMediaTypeFormatter());

            // Reset the timer logs first, since one of the tests will
            // be checking them
            TestHelpers.ClearFunctionLogs("TimerTrigger");
            Host = ScriptHost.Create(_settingsManager, config);
            Host.Start();
        }
        public void Flush_FlushesInternalWriters()
        {
            var traceWriter = new TestTraceWriter(TraceLevel.Error);
            var compositeWriter = new CompositeTraceWriter(new[] { traceWriter });

            string message = "Test trace";
            compositeWriter.Error(message);
            compositeWriter.Flush();

            Assert.True(traceWriter.Flushed);
        }
        public void Trace_CallsInnerWriterTrace()
        {
            var traceWriter = new TestTraceWriter(TraceLevel.Verbose);
            var compositeWriter = new CompositeTraceWriter(new[] { traceWriter });

            string message = "Test trace";
            compositeWriter.Verbose(message);

            TraceEvent trace = traceWriter.Traces.FirstOrDefault();

            Assert.NotNull(trace);
            Assert.Equal(TraceLevel.Verbose, trace.Level);
            Assert.Equal(message, trace.Message);
        }
        public void Trace_RespectsInnerWriterLevel()
        {
            var traceWriter = new TestTraceWriter(TraceLevel.Error);
            var compositeWriter = new CompositeTraceWriter(new[] { traceWriter });

            string message = "Test trace";
            compositeWriter.Verbose(message);
            compositeWriter.Error(message);

            Assert.Equal(1, traceWriter.Traces.Count);

            TraceEvent trace = traceWriter.Traces.First();

            Assert.Equal(TraceLevel.Error, trace.Level);
            Assert.Equal(message, trace.Message);
        }
        public async Task HasLease_WhenLeaseIsAcquired_ReturnsTrue()
        {
            string connectionString = AmbientConnectionStringProvider.Instance.GetConnectionString(ConnectionStringNames.Storage);
            string hostId = Guid.NewGuid().ToString();
            string instanceId = Guid.NewGuid().ToString();
            var traceWriter = new TestTraceWriter(System.Diagnostics.TraceLevel.Verbose);

            using (var manager = await BlobLeaseManager.CreateAsync(connectionString, TimeSpan.FromSeconds(15), hostId, instanceId, traceWriter))
            {
                await TestHelpers.Await(() => manager.HasLease);

                Assert.Equal(instanceId, manager.LeaseId);
            }

            await ClearLeaseBlob(hostId);
        }
        public void ResolveAssembly_WithIndirectPrivateDependency_IsResolved()
        {
            var resolver = new FunctionAssemblyLoader("c:\\");

            var metadata1 = new FunctionMetadata { Name = "Test1", ScriptFile = @"c:\testroot\test1\test.tst" };
            var metadata2 = new FunctionMetadata { Name = "Test2", ScriptFile = @"c:\testroot\test2\test.tst" };
            var traceWriter = new TestTraceWriter(TraceLevel.Verbose);

            var mockResolver = new Mock<IFunctionMetadataResolver>();
            mockResolver.Setup(m => m.ResolveAssembly("MyTestAssembly.dll"))
              .Returns(new TestAssembly(new AssemblyName("MyTestAssembly")));
                
            resolver.CreateOrUpdateContext(metadata1, this.GetType().Assembly, new FunctionMetadataResolver(metadata1, new Collection<ScriptBindingProvider>(), traceWriter), traceWriter);
            resolver.CreateOrUpdateContext(metadata2, this.GetType().Assembly, mockResolver.Object, traceWriter);

            Assembly result = resolver.ResolveAssembly(null, new System.ResolveEventArgs("MyTestAssembly.dll",
                new TestAssembly(new AssemblyName("MyDirectReference"), @"file:///c:/testroot/test2/bin/MyDirectReference.dll")));

            Assert.NotNull(result);
        }
        public async Task HasLeaseChanged_WhenLeaseIsLostAndStateChanges_IsFired()
        {
            string hostId = Guid.NewGuid().ToString();
            string instanceId = Guid.NewGuid().ToString();
            string connectionString = AmbientConnectionStringProvider.Instance.GetConnectionString(ConnectionStringNames.Storage);
            ICloudBlob blob = await GetLockBlobAsync(connectionString, hostId);

            var traceWriter = new TestTraceWriter(TraceLevel.Verbose);
            var resetEvent = new ManualResetEventSlim();

            BlobLeaseManager manager = null;
            string tempLeaseId = null;

            using (manager = new BlobLeaseManager(blob, TimeSpan.FromSeconds(15), hostId, instanceId, traceWriter, TimeSpan.FromSeconds(3)))
            {
                try
                {
                    await TestHelpers.Await(() => manager.HasLease);

                    manager.HasLeaseChanged += (s, a) => resetEvent.Set();

                    // Release the manager's lease and acquire one with a different id
                    await blob.ReleaseLeaseAsync(new AccessCondition { LeaseId = manager.LeaseId });
                    tempLeaseId = await blob.AcquireLeaseAsync(TimeSpan.FromSeconds(30), Guid.NewGuid().ToString());
                }
                finally
                {
                    if (tempLeaseId != null)
                    {
                        await blob.ReleaseLeaseAsync(new AccessCondition { LeaseId = tempLeaseId });
                    }
                }

                resetEvent.Wait(TimeSpan.FromSeconds(15));
            }

            Assert.True(resetEvent.IsSet);
            Assert.False(manager.HasLease, $"{nameof(BlobLeaseManager.HasLease)} was not correctly set to 'false' when lease lost.");

            await ClearLeaseBlob(hostId);
        }
        public void PropertiesAreProperlySet()
        {
            var traceWriter = new TestTraceWriter(TraceLevel.Verbose);

            var properties = new Dictionary<string, object>
            {
                { "prop1", "prop1" },
                { "prop2", "prop2" },
                { "prop3", "prop3" }
            };

            traceWriter.Trace("test", TraceLevel.Verbose, properties);

            Assert.Equal(1, traceWriter.Traces.Count);

            var trace = traceWriter.Traces.First();
            foreach (var property in properties)
            {
                Assert.True(trace.Properties.ContainsKey(property.Key));
                Assert.Equal(property.Value, trace.Properties[property.Key]);
            }
        }
        public void Apply_CreatesInterceptingTraceWriter()
        {
            TestTraceWriter traceWriter = new TestTraceWriter(TraceLevel.Verbose);

            traceWriter.Info("Test message");
            var traceEvent = traceWriter.Traces.Single();
            Assert.Equal(0, traceEvent.Properties.Count);

            traceWriter.Traces.Clear();
            var properties = new Dictionary<string, object>
            {
                { "Foo", 123 },
                { "Bar", 456 }
            };
            var interceptingTraceWriter = traceWriter.Apply(properties);

            interceptingTraceWriter.Info("Test message");
            traceEvent = traceWriter.Traces.Single();
            Assert.Equal(2, traceEvent.Properties.Count);
            Assert.Equal(123, traceEvent.Properties["Foo"]);
            Assert.Equal(456, traceEvent.Properties["Bar"]);
        }
        public async Task HasLeaseChanged_WhenLeaseIsAcquiredAndStateChanges_IsFired()
        {
            string hostId = Guid.NewGuid().ToString();
            string instanceId = Guid.NewGuid().ToString();
            var traceWriter = new TestTraceWriter(TraceLevel.Verbose);
            var resetEvent = new ManualResetEventSlim();

            string connectionString = AmbientConnectionStringProvider.Instance.GetConnectionString(ConnectionStringNames.Storage);
            ICloudBlob blob = await GetLockBlobAsync(connectionString, hostId);

            // Acquire a lease on the host lock blob
            string leaseId = await blob.AcquireLeaseAsync(TimeSpan.FromSeconds(15));

            BlobLeaseManager manager = null;

            try
            {
                manager = await BlobLeaseManager.CreateAsync(connectionString, TimeSpan.FromSeconds(15), hostId, instanceId, traceWriter);
                manager.HasLeaseChanged += (s, a) => resetEvent.Set();
            }
            finally
            {
                await blob.ReleaseLeaseAsync(new AccessCondition { LeaseId = leaseId });
            }

            resetEvent.Wait(TimeSpan.FromSeconds(15));
            bool hasLease = manager.HasLease;
            string actualLeaseId = manager.LeaseId;
            manager.Dispose();

            Assert.True(resetEvent.IsSet);
            Assert.True(hasLease, $"{nameof(BlobLeaseManager.HasLease)} was not correctly set to 'true' when lease was acquired.");
            Assert.Equal(instanceId, actualLeaseId);

            await ClearLeaseBlob(hostId);
        }
        public async Task TraceOutputsMessagesWhenLeaseRenewalFails()
        {
            string hostId          = Guid.NewGuid().ToString();
            string instanceId      = Guid.NewGuid().ToString();
            var    traceWriter     = new TestTraceWriter(TraceLevel.Verbose);
            var    renewResetEvent = new ManualResetEventSlim();

            var blobMock = new Mock <ICloudBlob>();

            blobMock.Setup(b => b.AcquireLeaseAsync(It.IsAny <TimeSpan>(), It.IsAny <string>()))
            .Returns(() => Task.FromResult(hostId));

            blobMock.Setup(b => b.RenewLeaseAsync(It.IsAny <AccessCondition>()))
            .Returns(() => Task.FromException(new StorageException(new RequestResult {
                HttpStatusCode = 409
            }, "test", null)))
            .Callback(() => renewResetEvent.Set());

            using (var manager = new BlobLeaseManager(blobMock.Object, TimeSpan.FromSeconds(5), hostId, instanceId, traceWriter))
            {
                renewResetEvent.Wait(TimeSpan.FromSeconds(10));
                // Make sure we have enough time to trace the renewal
                await TestHelpers.Await(() => traceWriter.Traces.Count == 2, 5000, 500);
            }

            TraceEvent acquisitionEvent = traceWriter.Traces.First();

            Assert.Contains($"Host lock lease acquired by instance ID '{instanceId}'.", acquisitionEvent.Message);
            Assert.Equal(TraceLevel.Info, acquisitionEvent.Level);

            TraceEvent renewalEvent = traceWriter.Traces.Skip(1).First();
            string     pattern      = @"Failed to renew host lock lease: Another host has acquired the lease. The last successful renewal completed at (.+) \([0-9]+ milliseconds ago\) with a duration of [0-9]+ milliseconds.";

            Assert.True(Regex.IsMatch(renewalEvent.Message, pattern), $"Expected trace event {pattern} not found.");
            Assert.Equal(TraceLevel.Info, renewalEvent.Level);
        }
        public async Task DifferentHosts_UsingSameStorageAccount_CanObtainLease()
        {
            string hostId1 = Guid.NewGuid().ToString();
            string hostId2 = Guid.NewGuid().ToString();
            string instanceId = Guid.NewGuid().ToString();
            string connectionString = AmbientConnectionStringProvider.Instance.GetConnectionString(ConnectionStringNames.Storage);
            var traceWriter = new TestTraceWriter(TraceLevel.Verbose);

            using (var manager1 = await BlobLeaseManager.CreateAsync(connectionString, TimeSpan.FromSeconds(15), hostId1, instanceId, traceWriter))
            using (var manager2 = await BlobLeaseManager.CreateAsync(connectionString, TimeSpan.FromSeconds(15), hostId2, instanceId, traceWriter))
            {
                Task manager1Check = TestHelpers.Await(() => manager1.HasLease);
                Task manager2Check = TestHelpers.Await(() => manager2.HasLease);

                await Task.WhenAll(manager1Check, manager2Check);
            }

            await Task.WhenAll(ClearLeaseBlob(hostId1), ClearLeaseBlob(hostId2));
        }
        public async Task TraceOutputsMessagesWhenLeaseRenewalFails()
        {
            string hostId = Guid.NewGuid().ToString();
            string instanceId = Guid.NewGuid().ToString();
            var traceWriter = new TestTraceWriter(TraceLevel.Verbose);
            var renewResetEvent = new ManualResetEventSlim();

            var blobMock = new Mock<ICloudBlob>();
            blobMock.Setup(b => b.AcquireLeaseAsync(It.IsAny<TimeSpan>(), It.IsAny<string>()))
                .Returns(() => Task.FromResult(hostId));

            blobMock.Setup(b => b.RenewLeaseAsync(It.IsAny<AccessCondition>()))
                .Returns(() => Task.FromException(new StorageException(new RequestResult { HttpStatusCode = 409 }, "test", null)))
                .Callback(() => renewResetEvent.Set());

            using (var manager = new BlobLeaseManager(blobMock.Object, TimeSpan.FromSeconds(5), hostId, instanceId, traceWriter))
            {
                renewResetEvent.Wait(TimeSpan.FromSeconds(10));
                // Make sure we have enough time to trace the renewal
                await TestHelpers.Await(() => traceWriter.Traces.Count == 2, 5000, 500);
            }

            TraceEvent acquisitionEvent = traceWriter.Traces.First();
            Assert.Contains($"Host lock lease acquired by instance ID '{instanceId}'.", acquisitionEvent.Message);
            Assert.Equal(TraceLevel.Info, acquisitionEvent.Level);

            TraceEvent renewalEvent = traceWriter.Traces.Skip(1).First();            
            string pattern = @"Failed to renew host lock lease: Another host has acquired the lease. The last successful renewal completed at (.+) \([0-9]+ milliseconds ago\) with a duration of [0-9]+ milliseconds.";
            Assert.True(Regex.IsMatch(renewalEvent.Message, pattern), $"Expected trace event {pattern} not found.");            
            Assert.Equal(TraceLevel.Info, renewalEvent.Level);
        }
        public async Task TraceOutputsMessagesWhenLeaseIsAcquired()
        {
            string hostId = Guid.NewGuid().ToString();
            string instanceId = Guid.NewGuid().ToString();
            var traceWriter = new TestTraceWriter(TraceLevel.Verbose);
            var renewResetEvent = new ManualResetEventSlim();

            var blobMock = new Mock<ICloudBlob>();
            blobMock.Setup(b => b.AcquireLeaseAsync(It.IsAny<TimeSpan>(), It.IsAny<string>()))
                .Returns(() => Task.FromResult(hostId));            

            using (var manager = new BlobLeaseManager(blobMock.Object, TimeSpan.FromSeconds(5), hostId, instanceId, traceWriter))
            {
                renewResetEvent.Wait(TimeSpan.FromSeconds(10));

                // Make sure we have enough time to trace the renewal
                await TestHelpers.Await(() => traceWriter.Traces.Count == 1, 5000, 500);
            }

            TraceEvent acquisitionEvent = traceWriter.Traces.First();
            Assert.Contains($"Host lock lease acquired by instance ID '{instanceId}'.", acquisitionEvent.Message);
            Assert.Equal(TraceLevel.Info, acquisitionEvent.Level);
        }      
        internal async Task GeneratedMethods_WithOutParams_DoNotCauseDeadlocks(string fixture)
        {
            var traceWriter = new TestTraceWriter(TraceLevel.Verbose);

            ScriptHostConfiguration config = new ScriptHostConfiguration()
            {
                RootScriptPath = @"TestScripts\FunctionGeneration",
                TraceWriter = traceWriter
            };

            string secretsPath = Path.Combine(Path.GetTempPath(), @"FunctionTests\Secrets");
            WebHostSettings webHostSettings = new WebHostSettings();
            var secretManager = new SecretManager(SettingsManager, secretsPath, NullTraceWriter.Instance);

            using (var manager = new WebScriptHostManager(config, new TestSecretManagerFactory(secretManager), SettingsManager, webHostSettings))
            {
                Thread runLoopThread = new Thread(_ =>
                {
                    manager.RunAndBlock(CancellationToken.None);
                });
                runLoopThread.IsBackground = true;
                runLoopThread.Start();

                await TestHelpers.Await(() =>
                {
                    return manager.State == ScriptHostState.Running;
                });

                var request = new HttpRequestMessage(HttpMethod.Get, String.Format("http://localhost/api/httptrigger-{0}", fixture));
                FunctionDescriptor function = manager.GetHttpFunctionOrNull(request);

                SynchronizationContext currentContext = SynchronizationContext.Current;
                var resetEvent = new ManualResetEventSlim();

                try
                {
                    var requestThread = new Thread(() =>
                    {
                        var context = new SingleThreadSynchronizationContext();
                        SynchronizationContext.SetSynchronizationContext(context);

                        manager.HandleRequestAsync(function, request, CancellationToken.None)
                        .ContinueWith(task => resetEvent.Set());

                        Thread.Sleep(500);
                        context.Run();
                    });

                    requestThread.IsBackground = true;
                    requestThread.Start();

                    bool threadSignaled = resetEvent.Wait(TimeSpan.FromSeconds(10));

                    requestThread.Abort();

                    Assert.True(threadSignaled, "Thread execution did not complete");
                }
                finally
                {
                    SynchronizationContext.SetSynchronizationContext(currentContext);
                    manager.Stop();
                }
            }
        }
        public async Task Dispose_ReleasesBlobLease()
        {
            string hostId = Guid.NewGuid().ToString();
            string instanceId = Guid.NewGuid().ToString();
            string connectionString = AmbientConnectionStringProvider.Instance.GetConnectionString(ConnectionStringNames.Storage);

            var traceWriter = new TestTraceWriter(System.Diagnostics.TraceLevel.Verbose);

            using (var manager = await BlobLeaseManager.CreateAsync(connectionString, TimeSpan.FromSeconds(15), hostId, instanceId, traceWriter))
            {
                await TestHelpers.Await(() => manager.HasLease);
            }

            ICloudBlob blob = await GetLockBlobAsync(connectionString, hostId);

            string leaseId = null;
            try
            {
                // Acquire a lease on the host lock blob
                leaseId = await blob.AcquireLeaseAsync(TimeSpan.FromSeconds(15));

                await blob.ReleaseLeaseAsync(new AccessCondition { LeaseId = leaseId });
            }
            catch (StorageException exc) when (exc.RequestInformation.HttpStatusCode == 409)
            {
            }

            Assert.False(string.IsNullOrEmpty(leaseId), "Failed to acquire a blob lease. The lease was not properly released.");

            await ClearLeaseBlob(hostId);
        }
        public async Task Renew_WhenBlobIsDeleted_RecreatesBlob()
        {
            string hostId = Guid.NewGuid().ToString();
            string instanceId = Guid.NewGuid().ToString();
            var traceWriter = new TestTraceWriter(TraceLevel.Verbose);
            var renewResetEvent = new ManualResetEventSlim();
            string connectionString = AmbientConnectionStringProvider.Instance.GetConnectionString(ConnectionStringNames.Storage);

            ICloudBlob blob = await GetLockBlobAsync(connectionString, hostId);

            var blobMock = new Mock<ICloudBlob>();
            blobMock.Setup(b => b.AcquireLeaseAsync(It.IsAny<TimeSpan>(), It.IsAny<string>()))
                .Returns(() => Task.FromResult(hostId));

            blobMock.Setup(b => b.RenewLeaseAsync(It.IsAny<AccessCondition>()))
                .Returns(() => Task.FromException<string>(new StorageException(new RequestResult { HttpStatusCode = 404 }, "test", null)))
                .Callback(() => Task.Delay(1000).ContinueWith(t => renewResetEvent.Set()));

            blobMock.SetupGet(b => b.ServiceClient).Returns(blob.ServiceClient);

            // Delete the blob
            await blob.DeleteIfExistsAsync();

            using (var manager = new BlobLeaseManager(blobMock.Object, TimeSpan.FromSeconds(15), hostId, instanceId, traceWriter, TimeSpan.FromSeconds(3)))
            {
                renewResetEvent.Wait(TimeSpan.FromSeconds(10));

                await TestHelpers.Await(() => manager.HasLease);
            }

            bool blobExists = await blob.ExistsAsync();

            Assert.True(renewResetEvent.IsSet);
            Assert.True(blobExists);

            await ClearLeaseBlob(hostId);
        }
        private void TestMessageAndLevel(Action<TraceWriter> traceHandler, string message, TraceLevel level)
        {
            var traceWriter = new TestTraceWriter(level);
            traceHandler(traceWriter);

            Assert.Equal(1, traceWriter.Traces.Count);
            Assert.Equal(level, traceWriter.Traces.First().Level);
            Assert.Equal(message, traceWriter.Traces.First().Message);
        }
Exemple #20
0
 public StandbyManagerTests()
 {
     _settingsManager = ScriptSettingsManager.Instance;
     _traceWriter     = new TestTraceWriter(TraceLevel.Info);
     WebScriptHostManager.ResetStandbyMode();
 }
Exemple #21
0
        internal async Task GeneratedMethods_WithOutParams_DoNotCauseDeadlocks(string fixture)
        {
            var traceWriter = new TestTraceWriter(TraceLevel.Verbose);

            ScriptHostConfiguration config = new ScriptHostConfiguration()
            {
                RootScriptPath = @"TestScripts\FunctionGeneration",
                TraceWriter    = traceWriter
            };

            string             secretsPath     = Path.Combine(Path.GetTempPath(), @"FunctionTests\Secrets");
            ISecretsRepository repository      = new FileSystemSecretsRepository(secretsPath);
            WebHostSettings    webHostSettings = new WebHostSettings();

            webHostSettings.SecretsPath = secretsPath;

            var secretManager = new SecretManager(SettingsManager, repository, NullTraceWriter.Instance);

            using (var manager = new WebScriptHostManager(config, new TestSecretManagerFactory(secretManager), SettingsManager, webHostSettings))
            {
                Thread runLoopThread = new Thread(_ =>
                {
                    manager.RunAndBlock(CancellationToken.None);
                });
                runLoopThread.IsBackground = true;
                runLoopThread.Start();

                await TestHelpers.Await(() =>
                {
                    return(manager.State == ScriptHostState.Running);
                });

                var request = new HttpRequestMessage(HttpMethod.Get, String.Format("http://localhost/api/httptrigger-{0}", fixture));
                FunctionDescriptor function = manager.GetHttpFunctionOrNull(request);

                SynchronizationContext currentContext = SynchronizationContext.Current;
                var resetEvent = new ManualResetEventSlim();

                try
                {
                    var requestThread = new Thread(() =>
                    {
                        var context = new SingleThreadSynchronizationContext();
                        SynchronizationContext.SetSynchronizationContext(context);

                        manager.HandleRequestAsync(function, request, CancellationToken.None)
                        .ContinueWith(task => resetEvent.Set());

                        Thread.Sleep(500);
                        context.Run();
                    });

                    requestThread.IsBackground = true;
                    requestThread.Start();

                    bool threadSignaled = resetEvent.Wait(TimeSpan.FromSeconds(10));

                    requestThread.Abort();

                    Assert.True(threadSignaled, "Thread execution did not complete");
                }
                finally
                {
                    SynchronizationContext.SetSynchronizationContext(currentContext);
                    manager.Stop();
                }
            }
        }
Exemple #22
0
        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")));
            }
        }
Exemple #23
0
 public StandbyManagerTests()
 {
     _settingsManager = ScriptSettingsManager.Instance;
     _traceWriter     = new TestTraceWriter(TraceLevel.Info);
 }
Exemple #24
0
        public void Restart_CreatesNew_FunctionTraceWriter()
        {
            string functionDir             = @"TestScripts\CSharp";
            var    traceWriter             = new TestTraceWriter(System.Diagnostics.TraceLevel.Info);
            ScriptHostConfiguration config = new ScriptHostConfiguration
            {
                RootScriptPath  = functionDir,
                FileLoggingMode = FileLoggingMode.Always,
                TraceWriter     = traceWriter
            };

            string hostJsonPath     = Path.Combine(functionDir, ScriptConstants.HostMetadataFileName);
            string originalHostJson = File.ReadAllText(hostJsonPath);

            // Only load two functions to start:
            JObject hostConfig = new JObject
            {
                { "id", "123456" },
                { "functions", new JArray("ManualTrigger", "Scenarios") }
            };

            File.WriteAllText(hostJsonPath, hostConfig.ToString());

            CancellationTokenSource cts       = new CancellationTokenSource();
            ExceptionDispatchInfo   exception = null;

            try
            {
                using (var manager = new ScriptHostManager(config))
                {
                    // Background task to run while the main thread is pumping events at RunAndBlock().
                    Thread t = new Thread(_ =>
                    {
                        try
                        {
                            // don't start until the manager is running
                            TestHelpers.Await(() => manager.State == ScriptHostState.Running).Wait();

                            var firstFileWriters = GetRemovableTraceWriters(manager.Instance);
                            Assert.Equal(2, firstFileWriters.Count());

                            // update the host.json to only have one function
                            hostConfig["functions"] = new JArray("ManualTrigger");
                            traceWriter.Traces.Clear();
                            File.WriteAllText(hostJsonPath, hostConfig.ToString());
                            TestHelpers.Await(() => traceWriter.Traces.Select(p => p.Message).Contains("Job host started")).Wait();
                            TestHelpers.Await(() => manager.State == ScriptHostState.Running).Wait();

                            var secondFileWriters = GetRemovableTraceWriters(manager.Instance);
                            Assert.Equal(1, secondFileWriters.Count());

                            // make sure we have a new instance of the ManualTrigger writer and that it does
                            // not throw an ObjectDisposedException when we use it
                            Assert.DoesNotContain(secondFileWriters.Single(), firstFileWriters);
                            secondFileWriters.Single().Info("test");

                            // add back the other function -- make sure the writer is not disposed
                            hostConfig["functions"] = new JArray("ManualTrigger", "Scenarios");
                            traceWriter.Traces.Clear();
                            File.WriteAllText(hostJsonPath, hostConfig.ToString());
                            TestHelpers.Await(() => traceWriter.Traces.Select(p => p.Message).Contains("Job host started")).Wait();
                            TestHelpers.Await(() => manager.State == ScriptHostState.Running).Wait();

                            var thirdFileWriters = GetRemovableTraceWriters(manager.Instance);
                            Assert.Equal(2, thirdFileWriters.Count());

                            // make sure these are all new and that they also do not throw
                            var previousWriters = firstFileWriters.Concat(secondFileWriters);
                            Assert.DoesNotContain(thirdFileWriters.First(), previousWriters);
                            Assert.DoesNotContain(thirdFileWriters.Last(), previousWriters);
                            thirdFileWriters.First().Info("test");
                            thirdFileWriters.Last().Info("test");
                        }
                        catch (Exception ex)
                        {
                            exception = ExceptionDispatchInfo.Capture(ex);
                        }
                        finally
                        {
                            cts.Cancel();
                        }
                    });
                    t.Start();
                    manager.RunAndBlock(cts.Token);
                    t.Join();
                }

                Assert.True(exception == null, exception?.SourceException?.ToString());
            }
            finally
            {
                File.WriteAllText(hostJsonPath, originalHostJson);
            }
        }
        private async Task RunTimeoutTest(string scriptLang, string functionName)
        {
            TestHelpers.ClearFunctionLogs(functionName);
            TimeSpan testTimeout = TimeSpan.FromSeconds(3);
            var traceWriter = new TestTraceWriter(TraceLevel.Verbose);
            using (var manager = await CreateAndStartScriptHostManager(scriptLang, functionName, testTimeout, traceWriter))
            {
                string testData = Guid.NewGuid().ToString();

                Dictionary<string, object> arguments = new Dictionary<string, object>
                {
                    { "inputData", testData },
                };

                FunctionTimeoutException ex = await Assert.ThrowsAsync<FunctionTimeoutException>(() => manager.Instance.CallAsync(functionName, arguments));

                await TestHelpers.Await(() =>
                {
                    // make sure logging from within the function worked
                    // TODO: This doesn't appear to work for Powershell in AppVeyor. Need to investigate.
                    //bool hasTestData = inProgressLogs.Any(l => l.Contains(testData));
                    var expectedMessage = $"Timeout value of {testTimeout} was exceeded by function: Functions.{functionName}";
                    var traces = string.Join(Environment.NewLine, traceWriter.Traces);
                    return traces.Contains(expectedMessage);
                });

                var exception = GetExceptionHandler(manager).TimeoutExceptionInfos.Single().SourceException;
                Assert.IsType<FunctionTimeoutException>(exception);
            }
        }
        private RunDependencies CreateDependencies(TraceLevel traceLevel = TraceLevel.Info)
        {
            var dependencies = new RunDependencies();

            var functionTraceWriter = new TestTraceWriter(System.Diagnostics.TraceLevel.Verbose);
            var traceWriter = new TestTraceWriter(System.Diagnostics.TraceLevel.Verbose);
            var scriptHostConfiguration = new ScriptHostConfiguration
            {
                HostConfig = new JobHostConfiguration(),
                TraceWriter = traceWriter,
                FileLoggingMode = FileLoggingMode.Always,
                FileWatchingEnabled = true
            };

            scriptHostConfiguration.HostConfig.Tracing.ConsoleLevel = System.Diagnostics.TraceLevel.Verbose;

            var host = new Mock<ScriptHost>(scriptHostConfiguration);
            host.SetupGet(h => h.IsPrimary).Returns(true);

            var entrypointResolver = new Mock<IFunctionEntryPointResolver>();

            var compilation = new Mock<ICompilation>();
            compilation.Setup(c => c.GetDiagnostics())
                .Returns(ImmutableArray<Diagnostic>.Empty);

            var compilationService = new Mock<ICompilationService>();
            compilationService.Setup(s => s.SupportedFileTypes)
                .Returns(() => new[] { ".csx" });
            compilationService.Setup(s => s.GetFunctionCompilation(It.IsAny<FunctionMetadata>()))
                .Returns(compilation.Object);

            var compilationServiceFactory = new Mock<ICompilationServiceFactory>();
            compilationServiceFactory.Setup(f => f.CreateService(ScriptType.CSharp, It.IsAny<IFunctionMetadataResolver>()))
                .Returns(compilationService.Object);

            var traceWriterFactory = new Mock<ITraceWriterFactory>();
            traceWriterFactory.Setup(f => f.Create())
                .Returns(functionTraceWriter);

            var metricsLogger = new MetricsLogger();
            scriptHostConfiguration.HostConfig.AddService<IMetricsLogger>(metricsLogger);

            return new RunDependencies
            {
                Host = host,
                EntrypointResolver = entrypointResolver,
                Compilation = compilation,
                CompilationService = compilationService,
                CompilationServiceFactory = compilationServiceFactory,
                TraceWriterFactory = traceWriterFactory,
                TraceWriter = functionTraceWriter
            };
        }
        public void AcquiringLease_WithServerError_LogsAndRetries()
        {
            string hostId = Guid.NewGuid().ToString();
            string instanceId = Guid.NewGuid().ToString();
            var traceWriter = new TestTraceWriter(TraceLevel.Verbose);
            var renewResetEvent = new ManualResetEventSlim();

            var results = new Queue<Task<string>>();
            results.Enqueue(Task.FromException<string>(new StorageException(new RequestResult { HttpStatusCode = 500 }, "test", null)));
            results.Enqueue(Task.FromResult(hostId));

            var blobMock = new Mock<ICloudBlob>();
            blobMock.Setup(b => b.AcquireLeaseAsync(It.IsAny<TimeSpan>(), It.IsAny<string>()))
                .Returns(() => results.Dequeue());

            blobMock.Setup(b => b.RenewLeaseAsync(It.IsAny<AccessCondition>()))
                .Returns(() => Task.Delay(1000))
                .Callback(() => renewResetEvent.Set());

            BlobLeaseManager manager;
            using (manager = new BlobLeaseManager(blobMock.Object, TimeSpan.FromSeconds(5), hostId, instanceId, traceWriter))
            {
                renewResetEvent.Wait(TimeSpan.FromSeconds(10));
            }

            Assert.True(renewResetEvent.IsSet);
            Assert.True(manager.HasLease);
            Assert.True(traceWriter.Traces.Any(t => t.Message.Contains("Server error")));
        }
Exemple #28
0
        private RunDependencies CreateDependencies(TraceLevel traceLevel = TraceLevel.Info, IScriptHostEnvironment environment = null)
        {
            var dependencies = new RunDependencies();

            var functionTraceWriter     = new TestTraceWriter(System.Diagnostics.TraceLevel.Verbose);
            var traceWriter             = new TestTraceWriter(System.Diagnostics.TraceLevel.Verbose);
            var scriptHostConfiguration = new ScriptHostConfiguration
            {
                HostConfig          = new JobHostConfiguration(),
                TraceWriter         = traceWriter,
                FileLoggingMode     = FileLoggingMode.Always,
                FileWatchingEnabled = true
            };

            scriptHostConfiguration.HostConfig.Tracing.ConsoleLevel = System.Diagnostics.TraceLevel.Verbose;
            var eventManager = new ScriptEventManager();

            var host = new Mock <ScriptHost>(environment ?? new NullScriptHostEnvironment(), eventManager, scriptHostConfiguration, null, null, null);

            host.CallBase = true;

            var traceWriterFactory = new Mock <IFunctionTraceWriterFactory>();

            traceWriterFactory.Setup(f => f.Create(It.IsAny <string>(), null))
            .Returns(functionTraceWriter);

            host.SetupGet(h => h.IsPrimary).Returns(true);
            host.SetupGet(h => h.FunctionTraceWriterFactory).Returns(traceWriterFactory.Object);

            var entrypointResolver = new Mock <IFunctionEntryPointResolver>();

            var compilation = new Mock <IDotNetCompilation>();

            compilation.Setup(c => c.GetDiagnostics())
            .Returns(ImmutableArray <Diagnostic> .Empty);

            var compilationService = new Mock <ICompilationService <IDotNetCompilation> >();

            compilationService.Setup(s => s.SupportedFileTypes)
            .Returns(() => new[] { ".csx" });
            compilationService.Setup(s => s.GetFunctionCompilationAsync(It.IsAny <FunctionMetadata>()))
            .ReturnsAsync(compilation.Object);

            var compilationServiceFactory = new Mock <ICompilationServiceFactory <ICompilationService <IDotNetCompilation>, IFunctionMetadataResolver> >();

            compilationServiceFactory.Setup(f => f.CreateService(ScriptType.CSharp, It.IsAny <IFunctionMetadataResolver>()))
            .Returns(compilationService.Object);

            var metricsLogger = new MetricsLogger();

            scriptHostConfiguration.HostConfig.AddService <IMetricsLogger>(metricsLogger);

            return(new RunDependencies
            {
                Host = host,
                EntrypointResolver = entrypointResolver,
                Compilation = compilation,
                CompilationService = compilationService,
                CompilationServiceFactory = compilationServiceFactory,
                TraceWriterFactory = traceWriterFactory,
                TraceWriter = functionTraceWriter
            });
        }
        private async Task RunTokenTest(string scenario, Action<IEnumerable<string>> verify)
        {
            string functionName = "TimeoutToken";
            TestHelpers.ClearFunctionLogs(functionName);
            var traceWriter = new TestTraceWriter(TraceLevel.Info);
            using (var manager = await CreateAndStartScriptHostManager("CSharp", functionName, TimeSpan.FromSeconds(3), traceWriter))
            {
                Dictionary<string, object> arguments = new Dictionary<string, object>
                {
                    { "input", scenario },
                };

                FunctionTimeoutException ex = await Assert.ThrowsAsync<FunctionTimeoutException>(() => manager.Instance.CallAsync(functionName, arguments));

                var exception = GetExceptionHandler(manager).TimeoutExceptionInfos.Single().SourceException;
                Assert.IsType<FunctionTimeoutException>(exception);

                verify(traceWriter.Traces.Select(t => t.ToString()));
            }
        }
Exemple #30
0
        public void TryParseFunctionMetadata_ValidatesHttpRoutes()
        {
            // first add an http function
            JObject functionConfig = new JObject();

            functionConfig.Add("bindings", new JArray(new JObject
            {
                { "type", "httpTrigger" },
                { "name", "req" },
                { "direction", "in" },
                { "methods", new JArray("get") },
                { "route", "products/{category}/{id?}" }
            }));
            var mappedHttpFunctions           = new Dictionary <string, HttpTriggerBindingMetadata>();
            var traceWriter                   = new TestTraceWriter(TraceLevel.Verbose);
            var functionFilesProvider         = new Lazy <string[]>(() => new string[] { "run.csx" });
            FunctionMetadata functionMetadata = null;
            string           functionError    = null;
            bool             result           = ScriptHost.TryParseFunctionMetadata("test", functionConfig, mappedHttpFunctions, traceWriter, functionFilesProvider, out functionMetadata, out functionError);

            Assert.True(result);
            Assert.NotNull(functionMetadata);
            Assert.Null(functionError);
            Assert.Equal(1, mappedHttpFunctions.Count);
            Assert.True(mappedHttpFunctions.ContainsKey("test"));
            Assert.Equal("run.csx", functionMetadata.ScriptFile);

            // add another for a completely different route
            functionConfig["bindings"] = new JArray(new JObject
            {
                { "type", "httpTrigger" },
                { "name", "req" },
                { "direction", "in" },
                { "methods", new JArray("get") },
                { "route", "/foo/bar/baz/" }
            });
            functionMetadata = null;
            functionError    = null;
            result           = ScriptHost.TryParseFunctionMetadata("test2", functionConfig, mappedHttpFunctions, traceWriter, functionFilesProvider, out functionMetadata, out functionError);
            Assert.True(result);
            Assert.NotNull(functionMetadata);
            Assert.Null(functionError);
            Assert.True(mappedHttpFunctions.ContainsKey("test2"));
            Assert.Equal(2, mappedHttpFunctions.Count);

            // add another that varies from another only by http method
            functionConfig["bindings"] = new JArray(new JObject
            {
                { "type", "httpTrigger" },
                { "name", "req" },
                { "direction", "in" },
                { "methods", new JArray("put", "post") },
                { "route", "/foo/bar/baz" }
            });
            functionMetadata = null;
            functionError    = null;
            result           = ScriptHost.TryParseFunctionMetadata("test3", functionConfig, mappedHttpFunctions, traceWriter, functionFilesProvider, out functionMetadata, out functionError);
            Assert.True(result);
            Assert.NotNull(functionMetadata);
            Assert.Null(functionError);
            Assert.True(mappedHttpFunctions.ContainsKey("test3"));
            Assert.Equal(3, mappedHttpFunctions.Count);

            // now try to add a function for the same route
            // where the http methods overlap
            functionConfig["bindings"] = new JArray(new JObject
            {
                { "type", "httpTrigger" },
                { "name", "req" },
                { "direction", "in" },
                { "route", "foo/bar/baz" }
            });
            functionMetadata = null;
            functionError    = null;
            result           = ScriptHost.TryParseFunctionMetadata("test4", functionConfig, mappedHttpFunctions, traceWriter, functionFilesProvider, out functionMetadata, out functionError);
            Assert.False(result);
            Assert.NotNull(functionMetadata);
            Assert.True(functionError.StartsWith("The route specified conflicts with the route defined by function"));
            Assert.Equal(3, mappedHttpFunctions.Count);

            // try to add a route under reserved admin route
            functionConfig["bindings"] = new JArray(new JObject
            {
                { "type", "httpTrigger" },
                { "name", "req" },
                { "direction", "in" },
                { "route", "admin/foo/bar" }
            });
            functionMetadata = null;
            functionError    = null;
            result           = ScriptHost.TryParseFunctionMetadata("test5", functionConfig, mappedHttpFunctions, traceWriter, functionFilesProvider, out functionMetadata, out functionError);
            Assert.False(result);
            Assert.NotNull(functionMetadata);
            Assert.Equal(3, mappedHttpFunctions.Count);
            Assert.Equal("The specified route conflicts with one or more built in routes.", functionError);
        }
        public void GeneratedMethods_WithOutParams_DoNotCauseDeadlocks()
        {
            var traceWriter = new TestTraceWriter(TraceLevel.Verbose);

            ScriptHostConfiguration config = new ScriptHostConfiguration()
            {
                RootScriptPath = @"TestScripts\FunctionGeneration",
                TraceWriter = traceWriter
            };

            using (var manager = new WebScriptHostManager(config))
            {
                Thread runLoopThread = new Thread(_ =>
                {
                    manager.RunAndBlock(CancellationToken.None);
                });
                runLoopThread.IsBackground = true;
                runLoopThread.Start();
                
                while (!manager.IsRunning)
                {
                    Thread.Sleep(100);
                }

                FunctionDescriptor function = manager.GetHttpFunctionOrNull(new Uri("http://localhost/api/httptrigger-csharp"));
                var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/api/httptrigger-csharp");

                SynchronizationContext currentContext = SynchronizationContext.Current;
                var resetEvent = new ManualResetEventSlim();

                try
                {
                    var requestThread = new Thread(() =>
                    {
                        var context = new SingleThreadSynchronizationContext();
                        SynchronizationContext.SetSynchronizationContext(context);

                        manager.HandleRequestAsync(function, request, CancellationToken.None)
                        .ContinueWith(task => resetEvent.Set());

                        Thread.Sleep(500);
                        context.Run();
                    });

                    requestThread.IsBackground = true;
                    requestThread.Start();

                    bool threadSignaled = resetEvent.Wait(TimeSpan.FromSeconds(10));

                    requestThread.Abort();

                    Assert.True(threadSignaled, "Thread execution did not complete");
                }
                finally
                {
                    SynchronizationContext.SetSynchronizationContext(currentContext);
                    manager.Stop();
                }
            }
        }