diff --git a/src/Microsoft.Tye.Hosting/Dashboard/Pages/ServiceDetails.razor b/src/Microsoft.Tye.Hosting/Dashboard/Pages/ServiceDetails.razor
index 875912c55..e8f3d4f38 100644
--- a/src/Microsoft.Tye.Hosting/Dashboard/Pages/ServiceDetails.razor
+++ b/src/Microsoft.Tye.Hosting/Dashboard/Pages/ServiceDetails.razor
@@ -1,5 +1,6 @@
@page "/services/{ServiceName}"
@inject Application application
+@inject HttpClient Http
@if (Service == null)
{
@@ -9,6 +10,14 @@ else
{
@Service.Description.Name
+ @if (_service?.Replicas != null) {
+ if (_service.Replicas.Any()) {
+
+ } else {
+
+ }
+ }
+
switch (Service.ServiceType)
{
case ServiceType.Container:
@@ -36,4 +45,14 @@ else
{
application.Services.TryGetValue(ServiceName, out _service);
}
+
+ private async Task OnServiceStop()
+ {
+ await Http.PostAsync($"/api/v1/services/{ServiceName}/stop", null);
+ }
+
+ private async Task OnServiceStart()
+ {
+ await Http.PostAsync($"/api/v1/services/{ServiceName}/start", null);
+ }
}
\ No newline at end of file
diff --git a/src/Microsoft.Tye.Hosting/ProcessRunner.cs b/src/Microsoft.Tye.Hosting/ProcessRunner.cs
index 4dc8f1248..6b9891851 100644
--- a/src/Microsoft.Tye.Hosting/ProcessRunner.cs
+++ b/src/Microsoft.Tye.Hosting/ProcessRunner.cs
@@ -178,7 +178,7 @@ service.Description.RunInfo is ProjectRunInfo project2 &&
}
}
- private void LaunchService(Application application, Service service)
+ public void LaunchService(Application application, Service service)
{
var serviceDescription = service.Description;
var processInfo = new ProcessInfo(new Task[service.Description.Replicas]);
@@ -460,7 +460,7 @@ async Task RunApplicationAsync(IEnumerable<(int ExternalPort, int Port, string?
service.Items[typeof(ProcessInfo)] = processInfo;
}
- private Task KillRunningProcesses(IDictionary services)
+ public Task KillRunningProcesses(IDictionary services)
{
static Task KillProcessAsync(Service service)
{
diff --git a/src/Microsoft.Tye.Hosting/TyeDashboardApi.cs b/src/Microsoft.Tye.Hosting/TyeDashboardApi.cs
index 90cc787b4..22b90f0cb 100644
--- a/src/Microsoft.Tye.Hosting/TyeDashboardApi.cs
+++ b/src/Microsoft.Tye.Hosting/TyeDashboardApi.cs
@@ -23,8 +23,9 @@ namespace Microsoft.Tye.Hosting
public class TyeDashboardApi
{
private readonly JsonSerializerOptions _options;
+ private readonly ProcessRunner _processRunner;
- public TyeDashboardApi()
+ public TyeDashboardApi(ProcessRunner processRunner)
{
_options = new JsonSerializerOptions()
{
@@ -34,6 +35,7 @@ public TyeDashboardApi()
};
_options.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase));
+ _processRunner = processRunner;
}
public void MapRoutes(IEndpointRouteBuilder endpoints)
@@ -42,6 +44,8 @@ public void MapRoutes(IEndpointRouteBuilder endpoints)
endpoints.MapGet("/api/v1/application", ApplicationIndex);
endpoints.MapDelete("/api/v1/control", ControlPlaneShutdown);
endpoints.MapGet("/api/v1/services", Services);
+ endpoints.MapPost("/api/v1/services/{name}/stop", ServiceStop);
+ endpoints.MapPost("/api/v1/services/{name}/start", ServiceStart);
endpoints.MapGet("/api/v1/services/{name}", Service);
endpoints.MapGet("/api/v1/logs/{name}", Logs);
endpoints.MapGet("/api/v1/metrics", AllMetrics);
@@ -128,6 +132,38 @@ private Task Service(HttpContext context)
return JsonSerializer.SerializeAsync(context.Response.Body, serviceJson, _options);
}
+ private Task ServiceStart(HttpContext context)
+ {
+ var app = context.RequestServices.GetRequiredService();
+
+ var name = (string?)context.Request.RouteValues["name"];
+ if (!string.IsNullOrEmpty(name) && app.Services.TryGetValue(name, out var service))
+ {
+ _processRunner.LaunchService(app, service);
+ }
+
+ context.Response.Redirect($"/services/{name}");
+
+ return Task.CompletedTask;
+ }
+
+ private async Task ServiceStop(HttpContext context)
+ {
+ var app = context.RequestServices.GetRequiredService();
+
+ var name = (string?)context.Request.RouteValues["name"];
+ if (!string.IsNullOrEmpty(name) && app.Services.TryGetValue(name, out var service))
+ {
+ var services = new Dictionary();
+ services.Add(name, service);
+ await _processRunner.KillRunningProcesses(services);
+ }
+
+ context.Response.Redirect($"/services/{name}");
+
+ return;
+ }
+
private static V1Service CreateServiceJson(Service service)
{
var description = service.Description;
diff --git a/src/Microsoft.Tye.Hosting/TyeHost.cs b/src/Microsoft.Tye.Hosting/TyeHost.cs
index 86b142a3a..70536fd39 100644
--- a/src/Microsoft.Tye.Hosting/TyeHost.cs
+++ b/src/Microsoft.Tye.Hosting/TyeHost.cs
@@ -204,6 +204,13 @@ private IHost BuildWebApplication(Application application, HostOptions options,
});
});
services.AddSingleton(application);
+ services.AddScoped(sp =>
+ {
+ var server = sp.GetRequiredService();
+ var addressFeature = server.Features.Get();
+ var baseAddress = addressFeature?.Addresses.FirstOrDefault() ?? string.Empty;
+ return new System.Net.Http.HttpClient { BaseAddress = new Uri(baseAddress) };
+ });
})
.Build();
}
@@ -218,14 +225,17 @@ private void ConfigureApplication(IApplicationBuilder app)
app.UseRouting();
- var api = new TyeDashboardApi();
-
- app.UseEndpoints(endpoints =>
+ if (_logger != null && _replicaRegistry != null)
{
- api.MapRoutes(endpoints);
- endpoints.MapBlazorHub();
- endpoints.MapFallbackToPage("/_Host");
- });
+ var api = new TyeDashboardApi(new ProcessRunner(_logger, _replicaRegistry, ProcessRunnerOptions.FromHostOptions(_options)));
+
+ app.UseEndpoints(endpoints =>
+ {
+ api.MapRoutes(endpoints);
+ endpoints.MapBlazorHub();
+ endpoints.MapFallbackToPage("/_Host");
+ });
+ }
}
private int ComputePort(int? port)