public static async Task StartHostAndWaitForReplicasToStart(TyeHost host) { var startedTask = new TaskCompletionSource <bool>(); var alreadyStarted = 0; var totalReplicas = host.Application.Services.Sum(s => s.Value.Description.Replicas); void OnReplicaChange(ReplicaEvent ev) { if (ev.State == ReplicaState.Started) { Interlocked.Increment(ref alreadyStarted); } else if (ev.State == ReplicaState.Stopped) { Interlocked.Decrement(ref alreadyStarted); } if (alreadyStarted == totalReplicas) { startedTask.TrySetResult(true); } } var servicesStateObserver = host.Application.Services.Select(srv => srv.Value.ReplicaEvents.Subscribe(OnReplicaChange)).ToList(); await host.StartAsync(); using var cancellation = new CancellationTokenSource(WaitForServicesTimeout); try { await using (cancellation.Token.Register(() => startedTask.TrySetCanceled())) { await startedTask.Task; } } catch (TaskCanceledException) { await host.StopAsync(); throw; } finally { foreach (var observer in servicesStateObserver) { observer.Dispose(); } } }
public async Task NullDebugTargetsDoesNotThrow() { using var projectDirectory = CopyTestProjectDirectory(Path.Combine("single-project", "test-project")); var projectFile = new FileInfo(Path.Combine(projectDirectory.DirectoryPath, "test-project.csproj")); // Debug targets can be null if not specified, so make sure calling host.Start does not throw. var outputContext = new OutputContext(_sink, Verbosity.Debug); var application = await ApplicationFactory.CreateAsync(outputContext, projectFile); await using var host = new TyeHost(application.ToHostingApplication(), Array.Empty <string>()) { Sink = _sink, }; await host.StartAsync(); }
public async Task FrontendBackendRunTestWithDocker() { var projectDirectory = new DirectoryInfo(Path.Combine(TestHelpers.GetSolutionRootDirectory("tye"), "samples", "frontend-backend")); using var tempDirectory = TempDirectory.Create(preferUserDirectoryOnMacOS: true); DirectoryCopy.Copy(projectDirectory.FullName, tempDirectory.DirectoryPath); var projectFile = new FileInfo(Path.Combine(tempDirectory.DirectoryPath, "tye.yaml")); var outputContext = new OutputContext(sink, Verbosity.Debug); var application = await ApplicationFactory.CreateAsync(outputContext, projectFile); using var host = new TyeHost(application.ToHostingApplication(), new[] { "--docker" }) { Sink = sink, }; await host.StartAsync(); try { // Make sure we're runningn containers Assert.True(host.Application.Services.All(s => s.Value.Description.RunInfo is DockerRunInfo)); var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (a, b, c, d) => true, AllowAutoRedirect = false }; var client = new HttpClient(new RetryHandler(handler)); var dashboardUri = new Uri(host.DashboardWebApplication !.Addresses.First()); await CheckServiceIsUp(host.Application, client, "backend", dashboardUri, timeout : TimeSpan.FromSeconds(60)); await CheckServiceIsUp(host.Application, client, "frontend", dashboardUri, timeout : TimeSpan.FromSeconds(60)); } finally { await host.StopAsync(); } }
public async Task NullDebugTargetsDoesNotThrow() { var projectDirectory = new DirectoryInfo(Path.Combine(TestHelpers.GetSolutionRootDirectory("tye"), "samples", "single-project", "test-project")); using var tempDirectory = TempDirectory.Create(); DirectoryCopy.Copy(projectDirectory.FullName, tempDirectory.DirectoryPath); var projectFile = new FileInfo(Path.Combine(tempDirectory.DirectoryPath, "test-project.csproj")); // Debug targets can be null if not specified, so make sure calling host.Start does not throw. var outputContext = new OutputContext(sink, Verbosity.Debug); var application = await ApplicationFactory.CreateAsync(outputContext, projectFile); using var host = new TyeHost(application.ToHostingApplication(), Array.Empty <string>()) { Sink = sink, }; await host.StartAsync(); await host.StopAsync(); }
public async Task FrontendBackendRunTest() { var projectDirectory = new DirectoryInfo(Path.Combine(TestHelpers.GetSolutionRootDirectory("tye"), "samples", "frontend-backend")); using var tempDirectory = TempDirectory.Create(); DirectoryCopy.Copy(projectDirectory.FullName, tempDirectory.DirectoryPath); var projectFile = new FileInfo(Path.Combine(tempDirectory.DirectoryPath, "tye.yaml")); var outputContext = new OutputContext(sink, Verbosity.Debug); var application = await ApplicationFactory.CreateAsync(outputContext, projectFile); using var host = new TyeHost(application.ToHostingApplication(), Array.Empty <string>()) { Sink = sink, }; await host.StartAsync(); try { var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (a, b, c, d) => true, AllowAutoRedirect = false }; var client = new HttpClient(new RetryHandler(handler)); var dashboardUri = new Uri(host.DashboardWebApplication !.Addresses.First()); await CheckServiceIsUp(host.Application, client, "backend", dashboardUri); await CheckServiceIsUp(host.Application, client, "frontend", dashboardUri); } finally { await host.StopAsync(); } }
public async Task SingleProjectRunTest() { var projectDirectory = new DirectoryInfo(Path.Combine(TestHelpers.GetSolutionRootDirectory("tye"), "samples", "single-project", "test-project")); using var tempDirectory = TempDirectory.Create(); DirectoryCopy.Copy(projectDirectory.FullName, tempDirectory.DirectoryPath); var projectFile = new FileInfo(Path.Combine(tempDirectory.DirectoryPath, "test-project.csproj")); var outputContext = new OutputContext(sink, Verbosity.Debug); var application = await ApplicationFactory.CreateAsync(outputContext, projectFile); using var host = new TyeHost(application.ToHostingApplication(), Array.Empty <string>()) { Sink = sink, }; await host.StartAsync(); try { var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (a, b, c, d) => true, AllowAutoRedirect = false }; var client = new HttpClient(new RetryHandler(handler)); // Make sure dashboard and applications are up. // Dashboard should be hosted in same process. var dashboardUri = new Uri(host.DashboardWebApplication !.Addresses.First()); var dashboardString = await client.GetStringAsync($"{dashboardUri}api/v1/services/test-project"); var service = JsonSerializer.Deserialize <V1Service>(dashboardString, _options); var binding = service.Description !.Bindings.Where(b => b.Protocol == "http").Single(); var uriBackendProcess = new Uri($"{binding.Protocol}://localhost:{binding.Port}"); // This isn't reliable right now because micronetes only guarantees the process starts, not that // that kestrel started. try { var appResponse = await client.GetAsync(uriBackendProcess); Assert.Equal(HttpStatusCode.OK, appResponse.StatusCode); } finally { // If we failed, there's a good chance the service isn't running. Let's get the logs either way and put // them in the output. var request = new HttpRequestMessage(HttpMethod.Get, new Uri(dashboardUri, $"/api/v1/logs/{service.Description.Name}")); var response = await client.SendAsync(request); var text = await response.Content.ReadAsStringAsync(); output.WriteLine($"Logs for service: {service.Description.Name}"); output.WriteLine(text); } } finally { await host.StopAsync(); } }
public async Task IngressRunTest() { var projectDirectory = new DirectoryInfo(Path.Combine(TestHelpers.GetSolutionRootDirectory("tye"), "samples", "apps-with-ingress")); using var tempDirectory = TempDirectory.Create(); DirectoryCopy.Copy(projectDirectory.FullName, tempDirectory.DirectoryPath); var projectFile = new FileInfo(Path.Combine(tempDirectory.DirectoryPath, "tye.yaml")); var outputContext = new OutputContext(sink, Verbosity.Debug); var application = await ApplicationFactory.CreateAsync(outputContext, projectFile); using var host = new TyeHost(application.ToHostingApplication(), Array.Empty <string>()) { Sink = sink, }; var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (a, b, c, d) => true, AllowAutoRedirect = false }; using var client = new HttpClient(new RetryHandler(handler)); await host.StartAsync(); var serviceApi = new Uri(host.DashboardWebApplication !.Addresses.First()); try { var ingressService = await client.GetStringAsync($"{serviceApi}api/v1/services/ingress"); var service = JsonSerializer.Deserialize <V1Service>(ingressService, _options); var binding = service.Description !.Bindings.Single(); var ingressUri = $"http://localhost:{binding.Port}"; var responseA = await client.GetAsync(ingressUri + "/A"); var responseB = await client.GetAsync(ingressUri + "/B"); Assert.StartsWith("Hello from Application A", await responseA.Content.ReadAsStringAsync()); Assert.StartsWith("Hello from Application B", await responseB.Content.ReadAsStringAsync()); var requestA = new HttpRequestMessage(HttpMethod.Get, ingressUri); requestA.Headers.Host = "a.example.com"; var requestB = new HttpRequestMessage(HttpMethod.Get, ingressUri); requestB.Headers.Host = "b.example.com"; responseA = await client.SendAsync(requestA); responseB = await client.SendAsync(requestB); Assert.StartsWith("Hello from Application A", await responseA.Content.ReadAsStringAsync()); Assert.StartsWith("Hello from Application B", await responseB.Content.ReadAsStringAsync()); } finally { // If we failed, there's a good chance the service isn't running. Let's get the logs either way and put // them in the output. foreach (var s in host.Application.Services.Values) { var request = new HttpRequestMessage(HttpMethod.Get, new Uri(serviceApi, $"/api/v1/logs/{s.Description.Name}")); var response = await client.SendAsync(request); var text = await response.Content.ReadAsStringAsync(); output.WriteLine($"Logs for service: {s.Description.Name}"); output.WriteLine(text); } await host.StopAsync(); } }
public async Task SingleProjectRunTest() { var projectDirectory = new DirectoryInfo(Path.Combine(TestHelpers.GetSolutionRootDirectory("tye"), "samples", "single-project", "test-project")); using var tempDirectory = TempDirectory.Create(); DirectoryCopy.Copy(projectDirectory.FullName, tempDirectory.DirectoryPath); var projectFile = new FileInfo(Path.Combine(tempDirectory.DirectoryPath, "test-project.csproj")); using var host = new TyeHost(ConfigFactory.FromFile(projectFile).ToHostingApplication(), Array.Empty <string>()) { Sink = sink, }; await host.StartAsync(); try { var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (a, b, c, d) => true, AllowAutoRedirect = false }; var client = new HttpClient(new RetryHandler(handler)); // Make sure dashboard and applications are up. // Dashboard should be hosted in same process. var dashboardUri = new Uri(host.DashboardWebApplication !.Addresses.First()); var dashboardResponse = await client.GetStringAsync(dashboardUri); // Only one service for single application. var service = host.Application.Services.First().Value; var binding = service.Description.Bindings.First(); var protocol = binding.Protocol?.Length != 0 ? binding.Protocol : "http"; var hostName = binding.Host != null && binding.Host.Length != 0 ? binding.Host : "localhost"; var uriString = $"{protocol}://{hostName}:{binding.Port}"; // Confirm that the uri is in the dashboard response. Assert.Contains(uriString, dashboardResponse); var uriBackendProcess = new Uri(uriString); // This isn't reliable right now because micronetes only guarantees the process starts, not that // that kestrel started. try { var appResponse = await client.GetAsync(uriBackendProcess); Assert.Equal(HttpStatusCode.OK, appResponse.StatusCode); } finally { // If we failed, there's a good chance the service isn't running. Let's get the logs either way and put // them in the output. var request = new HttpRequestMessage(HttpMethod.Get, new Uri(dashboardUri, $"/api/v1/logs/{service.Description.Name}")); var response = await client.SendAsync(request); var text = await response.Content.ReadAsStringAsync(); output.WriteLine($"Logs for service: {service.Description.Name}"); output.WriteLine(text); } } finally { await host.StopAsync(); } }