diff --git a/src/Microsoft.Tye.Hosting/Model/ConfigurationSource.cs b/src/Microsoft.Tye.Hosting/Model/ConfigurationSource.cs new file mode 100644 index 00000000..9e74fa2e --- /dev/null +++ b/src/Microsoft.Tye.Hosting/Model/ConfigurationSource.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Microsoft.Tye.Hosting.Model +{ + public class ConfigurationSource + { + public ConfigurationSource(string name, string value) + { + Name = name; + Value = value; + } + + public string Name { get; } + public string Value { get; } + } +} diff --git a/src/Microsoft.Tye.Hosting/Model/DockerStatus.cs b/src/Microsoft.Tye.Hosting/Model/DockerStatus.cs new file mode 100644 index 00000000..2c35b052 --- /dev/null +++ b/src/Microsoft.Tye.Hosting/Model/DockerStatus.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.Tye.Hosting.Model +{ + public class DockerStatus : ReplicaStatus + { + public DockerStatus(Service service, string name) : base(service, name) + { + } + + public string? DockerCommand { get; set; } + + public string? ContainerId { get; set; } + + public int? DockerLogsPid { get; set; } + } +} diff --git a/src/Microsoft.Tye.Hosting/Model/PortMapping.cs b/src/Microsoft.Tye.Hosting/Model/PortMapping.cs new file mode 100644 index 00000000..305c6e20 --- /dev/null +++ b/src/Microsoft.Tye.Hosting/Model/PortMapping.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; + +namespace Microsoft.Tye.Hosting.Model +{ + public class PortMapping + { + public int ExternalPort { get; set; } + + public List InternalPorts { get; set; } = new List(); + } +} diff --git a/src/Microsoft.Tye.Hosting/Model/ProcessStatus.cs b/src/Microsoft.Tye.Hosting/Model/ProcessStatus.cs new file mode 100644 index 00000000..b8794ce2 --- /dev/null +++ b/src/Microsoft.Tye.Hosting/Model/ProcessStatus.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; + +namespace Microsoft.Tye.Hosting.Model +{ + public class ProcessStatus : ReplicaStatus + { + public ProcessStatus(Service service, string name) + : base(service, name) + { + } + + public int? ExitCode { get; set; } + public int? Pid { get; set; } + public IDictionary? Environment { get; set; } + } +} diff --git a/src/Microsoft.Tye.Hosting/Model/ReplicaEvent.cs b/src/Microsoft.Tye.Hosting/Model/ReplicaEvent.cs new file mode 100644 index 00000000..ad841ba3 --- /dev/null +++ b/src/Microsoft.Tye.Hosting/Model/ReplicaEvent.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.Tye.Hosting.Model +{ + public readonly struct ReplicaEvent + { + public ReplicaState State { get; } + public ReplicaStatus Replica { get; } + + public ReplicaEvent(ReplicaState state, ReplicaStatus replica) + { + State = state; + Replica = replica; + } + } +} diff --git a/src/Microsoft.Tye.Hosting/Model/ReplicaState.cs b/src/Microsoft.Tye.Hosting/Model/ReplicaState.cs new file mode 100644 index 00000000..5e08bd9f --- /dev/null +++ b/src/Microsoft.Tye.Hosting/Model/ReplicaState.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.Tye.Hosting.Model +{ + public enum ReplicaState + { + Removed, + Added, + Started, + Stopped, + } +} diff --git a/src/Microsoft.Tye.Hosting/Model/ReplicaStatus.cs b/src/Microsoft.Tye.Hosting/Model/ReplicaStatus.cs new file mode 100644 index 00000000..f17ac285 --- /dev/null +++ b/src/Microsoft.Tye.Hosting/Model/ReplicaStatus.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; + +namespace Microsoft.Tye.Hosting.Model +{ + public class ReplicaStatus + { + public ReplicaStatus(Service service, string name) + { + Service = service; + Name = name; + } + + public string Name { get; } + + public IEnumerable? Ports { get; set; } + + public Service Service { get; } + + public Dictionary Items { get; } = new Dictionary(); + + public Dictionary Metrics { get; set; } = new Dictionary(); + } +} diff --git a/src/Microsoft.Tye.Hosting/Model/Service.cs b/src/Microsoft.Tye.Hosting/Model/Service.cs index 4323334c..6f035727 100644 --- a/src/Microsoft.Tye.Hosting/Model/Service.cs +++ b/src/Microsoft.Tye.Hosting/Model/Service.cs @@ -6,7 +6,6 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Reactive.Subjects; -using System.Text.Json; using System.Text.Json.Serialization; namespace Microsoft.Tye.Hosting.Model @@ -59,124 +58,14 @@ namespace Microsoft.Tye.Hosting.Model public ConcurrentDictionary Replicas { get; set; } = new ConcurrentDictionary(); - [JsonIgnore] public Dictionary> PortMap { get; set; } = new Dictionary>(); - [JsonIgnore] public Dictionary Items { get; } = new Dictionary(); - [JsonIgnore] public Queue CachedLogs { get; } = new Queue(); - [JsonIgnore] public Subject Logs { get; } = new Subject(); - [JsonIgnore] public Subject ReplicaEvents { get; } = new Subject(); } - - public readonly struct ReplicaEvent - { - public ReplicaState State { get; } - public ReplicaStatus Replica { get; } - - public ReplicaEvent(ReplicaState state, ReplicaStatus replica) - { - State = state; - Replica = replica; - } - } - - public enum ReplicaState - { - Removed, - Added, - Started, - Stopped, - } - - public class ServiceStatus - { - public string? ProjectFilePath { get; set; } - public string? ExecutablePath { get; set; } - public string? Args { get; set; } - public string? WorkingDirectory { get; set; } - } - - public class ProcessStatus : ReplicaStatus - { - public ProcessStatus(Service service, string name) - : base(service, name) - { - } - public int? ExitCode { get; set; } - public int? Pid { get; set; } - public IDictionary? Environment { get; set; } - } - - public class DockerStatus : ReplicaStatus - { - public DockerStatus(Service service, string name) : base(service, name) - { - } - - public string? DockerCommand { get; set; } - - public string? ContainerId { get; set; } - - public int? DockerLogsPid { get; set; } - } - - public class ReplicaStatus - { - public ReplicaStatus(Service service, string name) - { - Service = service; - Name = name; - } - - public string Name { get; } - - public static JsonConverter JsonConverter = new Converter(); - - public IEnumerable? Ports { get; set; } - - [JsonIgnore] - public Service Service { get; } - - [JsonIgnore] - public Dictionary Items { get; } = new Dictionary(); - - [JsonIgnore] - public Dictionary Metrics { get; set; } = new Dictionary(); - - private class Converter : JsonConverter - { - public override ReplicaStatus Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - throw new NotImplementedException(); - } - - public override void Write(Utf8JsonWriter writer, ReplicaStatus value, JsonSerializerOptions options) - { - // Use the runtime type since we really want to serialize either the DockerStatus or ProcessStatus - JsonSerializer.Serialize(writer, value, value.GetType(), options); - } - } - } - - public enum ServiceType - { - External, - Project, - Executable, - Container - } - - public class PortMapping - { - public int ExternalPort { get; set; } - - public List InteralPorts { get; set; } = new List(); - } } diff --git a/src/Microsoft.Tye.Hosting/Model/ServiceDescription.cs b/src/Microsoft.Tye.Hosting/Model/ServiceDescription.cs index 743148e4..d3f937c8 100644 --- a/src/Microsoft.Tye.Hosting/Model/ServiceDescription.cs +++ b/src/Microsoft.Tye.Hosting/Model/ServiceDescription.cs @@ -15,23 +15,9 @@ namespace Microsoft.Tye.Hosting.Model } public string Name { get; } - public RunInfo? RunInfo { get; set; } - public int Replicas { get; set; } = 1; public List Bindings { get; } = new List(); public List Configuration { get; } = new List(); } - - public class ConfigurationSource - { - public ConfigurationSource(string name, string value) - { - Name = name; - Value = value; - } - - public string Name { get; } - public string Value { get; } - } } diff --git a/src/Microsoft.Tye.Hosting/Model/ServiceStatus.cs b/src/Microsoft.Tye.Hosting/Model/ServiceStatus.cs new file mode 100644 index 00000000..69816c3a --- /dev/null +++ b/src/Microsoft.Tye.Hosting/Model/ServiceStatus.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.Tye.Hosting.Model +{ + public class ServiceStatus + { + public string? ProjectFilePath { get; set; } + public string? ExecutablePath { get; set; } + public string? Args { get; set; } + public string? WorkingDirectory { get; set; } + } +} diff --git a/src/Microsoft.Tye.Hosting/Model/ServiceType.cs b/src/Microsoft.Tye.Hosting/Model/ServiceType.cs new file mode 100644 index 00000000..e78f5e2f --- /dev/null +++ b/src/Microsoft.Tye.Hosting/Model/ServiceType.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.Tye.Hosting.Model +{ + public enum ServiceType + { + External, + Project, + Executable, + Container + } +} diff --git a/src/Microsoft.Tye.Hosting/Model/V1/V1ConfigurationSource.cs b/src/Microsoft.Tye.Hosting/Model/V1/V1ConfigurationSource.cs new file mode 100644 index 00000000..67398c6a --- /dev/null +++ b/src/Microsoft.Tye.Hosting/Model/V1/V1ConfigurationSource.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.Tye.Hosting.Model.V1 +{ + public class V1ConfigurationSource + { + public string? Name { get; set; } + public string? Value { get; set; } + } +} diff --git a/src/Microsoft.Tye.Hosting/Model/V1/V1ReplicaStatus.cs b/src/Microsoft.Tye.Hosting/Model/V1/V1ReplicaStatus.cs new file mode 100644 index 00000000..43353634 --- /dev/null +++ b/src/Microsoft.Tye.Hosting/Model/V1/V1ReplicaStatus.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Microsoft.Tye.Hosting.Model.V1 +{ + public class V1ReplicaStatus + { + public V1ReplicaType Type { get; set; } + public string? DockerCommand { get; set; } + public string? ContainerId { get; set; } + public int? DockerLogsPid { get; set; } + public string? Name { get; set; } + public IEnumerable? Ports { get; set; } + public int? ExitCode { get; set; } + public int? Pid { get; set; } + public IDictionary? Environment { get; set; } + } +} diff --git a/src/Microsoft.Tye.Hosting/Model/V1/V1ReplicaType.cs b/src/Microsoft.Tye.Hosting/Model/V1/V1ReplicaType.cs new file mode 100644 index 00000000..5f5eb2dd --- /dev/null +++ b/src/Microsoft.Tye.Hosting/Model/V1/V1ReplicaType.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.Tye.Hosting.Model.V1 +{ + public enum V1ReplicaType + { + Process, + Docker + } +} diff --git a/src/Microsoft.Tye.Hosting/Model/V1/V1RunInfo.cs b/src/Microsoft.Tye.Hosting/Model/V1/V1RunInfo.cs new file mode 100644 index 00000000..c47cb566 --- /dev/null +++ b/src/Microsoft.Tye.Hosting/Model/V1/V1RunInfo.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; + +namespace Microsoft.Tye.Hosting.Model.V1 +{ + public class V1RunInfo + { + public V1RunInfoType Type { get; set; } + public string? Args { get; set; } + public bool Build { get; set; } + public string? Project { get; set; } + public string? WorkingDirectory { get; set; } + public Dictionary? VolumeMappings { get; set; } + public string? Image { get; set; } + public string? Executable { get; set; } + } +} diff --git a/src/Microsoft.Tye.Hosting/Model/V1/V1RunInfoType.cs b/src/Microsoft.Tye.Hosting/Model/V1/V1RunInfoType.cs new file mode 100644 index 00000000..66d53359 --- /dev/null +++ b/src/Microsoft.Tye.Hosting/Model/V1/V1RunInfoType.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.Tye.Hosting.Model.V1 +{ + public enum V1RunInfoType + { + Project, + Executable, + Docker + } +} diff --git a/src/Microsoft.Tye.Hosting/Model/V1/V1Service.cs b/src/Microsoft.Tye.Hosting/Model/V1/V1Service.cs new file mode 100644 index 00000000..03dc904a --- /dev/null +++ b/src/Microsoft.Tye.Hosting/Model/V1/V1Service.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; + +namespace Microsoft.Tye.Hosting.Model.V1 +{ + public class V1Service + { + public V1ServiceDescription? Description { get; set; } + public ServiceType ServiceType { get; set; } + public int Restarts { get; set; } + public V1ServiceStatus? Status { get; set; } + public Dictionary? Replicas { get; set; } + } +} diff --git a/src/Microsoft.Tye.Hosting/Model/V1/V1ServiceBinding.cs b/src/Microsoft.Tye.Hosting/Model/V1/V1ServiceBinding.cs new file mode 100644 index 00000000..c4102194 --- /dev/null +++ b/src/Microsoft.Tye.Hosting/Model/V1/V1ServiceBinding.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.Tye.Hosting.Model.V1 +{ + public class V1ServiceBinding + { + public string? Name { get; set; } + public string? ConnectionString { get; set; } + public bool AutoAssignPort { get; set; } + public int? Port { get; set; } + public int? InternalPort { get; set; } + public string? Host { get; set; } + public string? Protocol { get; set; } + } +} diff --git a/src/Microsoft.Tye.Hosting/Model/V1/V1ServiceDescription.cs b/src/Microsoft.Tye.Hosting/Model/V1/V1ServiceDescription.cs new file mode 100644 index 00000000..e3a292d0 --- /dev/null +++ b/src/Microsoft.Tye.Hosting/Model/V1/V1ServiceDescription.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; + +namespace Microsoft.Tye.Hosting.Model.V1 +{ + public class V1ServiceDescription + { + public string? Name { get; set; } + public int Replicas { get; set; } + public V1RunInfo? RunInfo { get; set; } + public List? Bindings { get; set; } + public List? Configuration { get; set; } + } +} diff --git a/src/Microsoft.Tye.Hosting/Model/V1/V1ServiceStatus.cs b/src/Microsoft.Tye.Hosting/Model/V1/V1ServiceStatus.cs new file mode 100644 index 00000000..37c5d993 --- /dev/null +++ b/src/Microsoft.Tye.Hosting/Model/V1/V1ServiceStatus.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.Tye.Hosting.Model.V1 +{ + public class V1ServiceStatus + { + public string? ProjectFilePath { get; set; } + public string? ExecutablePath { get; set; } + public string? Args { get; set; } + public string? WorkingDirectory { get; set; } + } +} diff --git a/src/Microsoft.Tye.Hosting/TyeDashboardApi.cs b/src/Microsoft.Tye.Hosting/TyeDashboardApi.cs index c651d1a5..1ebb1371 100644 --- a/src/Microsoft.Tye.Hosting/TyeDashboardApi.cs +++ b/src/Microsoft.Tye.Hosting/TyeDashboardApi.cs @@ -11,6 +11,9 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection; +using System.Text.Json.Serialization; +using System.Collections.Generic; +using Microsoft.Tye.Hosting.Model.V1; namespace Microsoft.Tye.Hosting { @@ -27,7 +30,7 @@ namespace Microsoft.Tye.Hosting WriteIndented = true, }; - _options.Converters.Add(ReplicaStatus.JsonConverter); + _options.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase)); } public void MapRoutes(IEndpointRouteBuilder endpoints) @@ -61,7 +64,13 @@ namespace Microsoft.Tye.Hosting var services = app.Services.OrderBy(s => s.Key).Select(s => s.Value); - await JsonSerializer.SerializeAsync(context.Response.Body, services, _options); + var list = new List(); + foreach (var service in services) + { + list.Add(CreateServiceJson(service)); + } + + await JsonSerializer.SerializeAsync(context.Response.Body, list, _options); } private async Task Service(HttpContext context) @@ -83,7 +92,103 @@ namespace Microsoft.Tye.Hosting return; } - await JsonSerializer.SerializeAsync(context.Response.Body, service, _options); + var serviceJson = CreateServiceJson(service); + + await JsonSerializer.SerializeAsync(context.Response.Body, serviceJson, _options); + } + + private static V1Service CreateServiceJson(Model.Service service) + { + var description = service.Description; + var bindings = description.Bindings; + + var v1bindingList = new List(); + + foreach (var binding in bindings) + { + v1bindingList.Add(new V1ServiceBinding() + { + Name = binding.Name, + ConnectionString = binding.ConnectionString, + AutoAssignPort = binding.AutoAssignPort, + Port = binding.Port, + InternalPort = binding.InternalPort, + Host = binding.Host, + Protocol = binding.Protocol + }); + } + + var v1ConfigurationSourceList = new List(); + foreach (var configSource in description.Configuration) + { + v1ConfigurationSourceList.Add(new V1ConfigurationSource() + { + Name = configSource.Name, + Value = configSource.Value + }); + } + + var v1RunInfo = new V1RunInfo(); + if (description.RunInfo is DockerRunInfo dockerRunInfo) + { + v1RunInfo.Type = V1RunInfoType.Docker; + v1RunInfo.Image = dockerRunInfo.Image; + v1RunInfo.VolumeMappings = dockerRunInfo.VolumeMappings; + v1RunInfo.WorkingDirectory = dockerRunInfo.WorkingDirectory; + v1RunInfo.Args = dockerRunInfo.Args; + } + else if (description.RunInfo is ExecutableRunInfo executableRunInfo) + { + v1RunInfo.Type = V1RunInfoType.Executable; + v1RunInfo.Args = executableRunInfo.Args; + v1RunInfo.Executable = executableRunInfo.Executable; + v1RunInfo.WorkingDirectory = executableRunInfo.WorkingDirectory; + } + else if (description.RunInfo is ProjectRunInfo projectRunInfo) + { + v1RunInfo.Type = V1RunInfoType.Project; + v1RunInfo.Args = projectRunInfo.Args; + v1RunInfo.Build = projectRunInfo.Build; + v1RunInfo.Project = projectRunInfo.Project; + } + + var v1ServiceDescription = new V1ServiceDescription() + { + Bindings = v1bindingList, + Configuration = v1ConfigurationSourceList, + Name = description.Name, + Replicas = description.Replicas, + RunInfo = v1RunInfo + }; + + var replicateDictionary = new Dictionary(); + foreach (var replica in service.Replicas) + { + replicateDictionary[replica.Key] = new V1ReplicaStatus() + { + Name = replica.Value.Name, + Ports = replica.Value.Ports + }; + } + + var v1Status = new V1ServiceStatus() + { + ProjectFilePath = service.Status.ProjectFilePath, + ExecutablePath = service.Status.ExecutablePath, + Args = service.Status.Args, + WorkingDirectory = service.Status.WorkingDirectory, + }; + + var serviceJson = new V1Service() + { + ServiceType = service.ServiceType, + Status = v1Status, + Description = v1ServiceDescription, + Replicas = replicateDictionary, + Restarts = service.Restarts + }; + + return serviceJson; } private async Task Logs(HttpContext context) diff --git a/test/E2ETest/TyeRunTests.cs b/test/E2ETest/TyeRunTests.cs index 2ffb09e2..f5463103 100644 --- a/test/E2ETest/TyeRunTests.cs +++ b/test/E2ETest/TyeRunTests.cs @@ -8,11 +8,14 @@ using System.IO; using System.Linq; using System.Net; using System.Net.Http; +using System.Text.Json; +using System.Text.Json.Serialization; using System.Threading.Tasks; using Microsoft.Tye; using Microsoft.Tye.ConfigModel; using Microsoft.Tye.Hosting; using Microsoft.Tye.Hosting.Model; +using Microsoft.Tye.Hosting.Model.V1; using Xunit; using Xunit.Abstractions; @@ -22,11 +25,21 @@ namespace E2ETest { private readonly ITestOutputHelper output; private readonly TestOutputLogEventSink sink; + private readonly JsonSerializerOptions _options; public TyeRunTests(ITestOutputHelper output) { this.output = output; sink = new TestOutputLogEventSink(output); + + _options = new JsonSerializerOptions() + { + PropertyNameCaseInsensitive = true, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + WriteIndented = true, + }; + + _options.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase)); } [Fact] @@ -56,21 +69,11 @@ namespace E2ETest // 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 dashboardString = await client.GetStringAsync($"{dashboardUri}api/v1/services/test-project"); - var uriString = $"{protocol}://{hostName}:{binding.Port}"; - - // Confirm that the uri is in the dashboard response. - Assert.Contains(uriString, dashboardResponse); - - var uriBackendProcess = new Uri(uriString); + var service = JsonSerializer.Deserialize(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. @@ -122,7 +125,6 @@ namespace E2ETest var client = new HttpClient(new RetryHandler(handler)); var dashboardUri = new Uri(host.DashboardWebApplication!.Addresses.First()); - var dashboardResponse = await client.GetStringAsync(dashboardUri); await CheckServiceIsUp(host.Application, client, "backend", dashboardUri); await CheckServiceIsUp(host.Application, client, "frontend", dashboardUri); @@ -162,7 +164,6 @@ namespace E2ETest var client = new HttpClient(new RetryHandler(handler)); var dashboardUri = new Uri(host.DashboardWebApplication!.Addresses.First()); - var dashboardResponse = await client.GetStringAsync(dashboardUri); await CheckServiceIsUp(host.Application, client, "backend", dashboardUri); await CheckServiceIsUp(host.Application, client, "frontend", dashboardUri); @@ -176,20 +177,12 @@ namespace E2ETest private async Task CheckServiceIsUp(Microsoft.Tye.Hosting.Model.Application application, HttpClient client, string serviceName, Uri dashboardUri) { // make sure backend is up before frontend - var service = application.Services.Where(a => a.Value.Description.Name == serviceName).First().Value; - var binding = service.Description.Bindings.First(); - - var protocol = binding.Protocol != null && 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. + var dashboardString = await client.GetStringAsync($"{dashboardUri}api/v1/services/{serviceName}"); - var uriBackendProcess = new Uri(uriString); + var service = JsonSerializer.Deserialize(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);