Browse Source

added ServiceState based on replicas state

surfaced ServiceState in dashboard
made dashboard links more self-evident
pull/1566/head
Razvan Goga 4 years ago
parent
commit
1f5010280b
  1. 21
      src/Microsoft.Tye.Hosting/Dashboard/Pages/Index.razor
  2. 51
      src/Microsoft.Tye.Hosting/Model/Service.cs
  3. 8
      test/UnitTests/Microsoft.Tye.UnitTests.csproj
  4. 52
      test/UnitTests/ServiceUnitTests.cs

21
src/Microsoft.Tye.Hosting/Dashboard/Pages/Index.razor

@ -12,13 +12,14 @@
<table class="table service-table"> <table class="table service-table">
<thead> <thead>
<tr> <tr>
<th></th>
<th>Name</th> <th>Name</th>
<th>Type</th> <th>Type</th>
<th>Source</th> <th>Source</th>
<th>Bindings</th> <th>Bindings</th>
<th>Replicas</th> <th>Replicas</th>
<th>Restarts</th> <th>Restarts</th>
<th>Logs</th> <th></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -26,7 +27,11 @@
{ {
var logsPath = $"logs/{service.Description.Name}"; var logsPath = $"logs/{service.Description.Name}";
var servicePath = $"services/{service.Description.Name}"; var servicePath = $"services/{service.Description.Name}";
var serviceState = service.State;
<tr @key="service.Description.Name"> <tr @key="service.Description.Name">
<td>
<span class="badge @GetServiceStateClass(serviceState)">@serviceState</span>
</td>
<td> <td>
@if(service.ServiceType == ServiceType.External) @if(service.ServiceType == ServiceType.External)
{ {
@ -34,7 +39,7 @@
} }
else else
{ {
<a href="@servicePath">@service.Description.Name</a> <a href="@logsPath">@service.Description.Name</a>
} }
</td> </td>
<td> <td>
@ -91,7 +96,7 @@
{ {
<td>@service.Replicas.Count/@service.Description.Replicas</td> <td>@service.Replicas.Count/@service.Description.Replicas</td>
<td>@service.Restarts</td> <td>@service.Restarts</td>
<td><NavLink href="@logsPath">View</NavLink></td> <td><NavLink href="@logsPath">Logs</NavLink> | <NavLink href="@servicePath">Metrics</NavLink></td>
} }
</tr> </tr>
} }
@ -102,6 +107,16 @@
private List<IDisposable> _subscriptions = new List<IDisposable>(); private List<IDisposable> _subscriptions = new List<IDisposable>();
string GetServiceStateClass(ServiceState serviceState) => serviceState switch
{
ServiceState.Starting => "badge-secondary",
ServiceState.Started => "badge-success",
ServiceState.Degraded => "badge-danger",
ServiceState.Failed => "badge-warning",
ServiceState.Stopped => "badge-light",
_ => "badge-dark"
};
string GetUrl(ServiceBinding b) string GetUrl(ServiceBinding b)
{ {
return $"{(b.Protocol ?? "tcp")}://{b.Host ?? "localhost"}:{b.Port}"; return $"{(b.Protocol ?? "tcp")}://{b.Host ?? "localhost"}:{b.Port}";

51
src/Microsoft.Tye.Hosting/Model/Service.cs

@ -5,6 +5,7 @@
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Reactive.Subjects; using System.Reactive.Subjects;
namespace Microsoft.Tye.Hosting.Model namespace Microsoft.Tye.Hosting.Model
@ -83,5 +84,55 @@ namespace Microsoft.Tye.Hosting.Model
public Subject<string> Logs { get; } = new Subject<string>(); public Subject<string> Logs { get; } = new Subject<string>();
public Subject<ReplicaEvent> ReplicaEvents { get; } = new Subject<ReplicaEvent>(); public Subject<ReplicaEvent> ReplicaEvents { get; } = new Subject<ReplicaEvent>();
public ServiceState State
{
get
{
var replicaStates = Replicas.Values.Select(r => r.State);
int replicaCount = replicaStates.Count();
if (replicaCount == 0)
return ServiceState.Unknown;
if (replicaStates.Any(r => r == ReplicaState.Added))
return ServiceState.Starting;
if (replicaStates.All(r => r == ReplicaState.Started || r == ReplicaState.Ready || r == ReplicaState.Healthy))
return ServiceState.Started;
if (replicaCount == 1)
{
ReplicaState? replicaState = replicaStates.Single();
if (replicaState == ReplicaState.Removed)
return ServiceState.Failed;
if (replicaState == ReplicaState.Stopped)
return ServiceState.Stopped;
}
else
{
if (replicaStates.All(r => r == ReplicaState.Stopped))
return ServiceState.Stopped;
if (replicaStates.Any(r => r == ReplicaState.Removed || r == ReplicaState.Stopped))
return ServiceState.Degraded;
}
return ServiceState.Unknown;
}
}
}
public enum ServiceState
{
Unknown,
Starting,
Started,
Degraded,
Failed,
Stopped
} }
} }

8
test/UnitTests/Microsoft.Tye.UnitTests.csproj

@ -9,6 +9,14 @@
<TestRunnerName>XUnit</TestRunnerName> <TestRunnerName>XUnit</TestRunnerName>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<LangVersion>10.0</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<LangVersion>10.0</LangVersion>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="coverlet.collector" Version="1.0.1" /> <PackageReference Include="coverlet.collector" Version="1.0.1" />
<PackageReference Include="coverlet.collector" Version="1.0.1" /> <PackageReference Include="coverlet.collector" Version="1.0.1" />

52
test/UnitTests/ServiceUnitTests.cs

@ -0,0 +1,52 @@
using System.Collections.Generic;
using Microsoft.Tye.Hosting.Model;
using Xunit;
namespace Microsoft.Tye.UnitTests
{
public class ServiceUnitTests
{
[Theory]
[MemberData(nameof(ServiceStateTestData))]
public void ServiceStateIsBasedOnReplicaStates(ServiceState expected, List<ReplicaState> replicaStates)
{
Service service = new(new ServiceDescription("test", null), ServiceSource.Unknown);
for (int i = 0; i < replicaStates.Count; i++)
{
string replicaName = i.ToString();
service.Replicas.TryAdd(replicaName, new ReplicaStatus(service, replicaName)
{
State = replicaStates[i],
});
}
Assert.Equal(expected, service.State);
}
public static IEnumerable<object[]> ServiceStateTestData =>
new List<object[]>
{
//no replica - should not happen
new object[] { ServiceState.Unknown, new List<ReplicaState>() },
//one replica
new object[] { ServiceState.Starting, new List<ReplicaState>() { ReplicaState.Added } },
new object[] { ServiceState.Started, new List<ReplicaState>() { ReplicaState.Started } },
new object[] { ServiceState.Started, new List<ReplicaState>() { ReplicaState.Ready } },
new object[] { ServiceState.Started, new List<ReplicaState>() { ReplicaState.Healthy } },
new object[] { ServiceState.Failed, new List<ReplicaState>() { ReplicaState.Removed } },
new object[] { ServiceState.Stopped, new List<ReplicaState>() { ReplicaState.Stopped } },
//multiple replicas
new object[] { ServiceState.Starting, new List<ReplicaState>() { ReplicaState.Added, ReplicaState.Started, ReplicaState.Ready, ReplicaState.Healthy } },
new object[] { ServiceState.Started, new List<ReplicaState>() { ReplicaState.Started, ReplicaState.Ready, ReplicaState.Healthy } },
new object[] { ServiceState.Degraded, new List<ReplicaState>() { ReplicaState.Removed, ReplicaState.Started, ReplicaState.Ready, ReplicaState.Healthy } },
new object[] { ServiceState.Degraded, new List<ReplicaState>() { ReplicaState.Stopped, ReplicaState.Started, ReplicaState.Ready, ReplicaState.Healthy } },
new object[] { ServiceState.Degraded, new List<ReplicaState>() { ReplicaState.Removed, ReplicaState.Stopped, ReplicaState.Started, ReplicaState.Ready, ReplicaState.Healthy } },
new object[] { ServiceState.Stopped, new List<ReplicaState>() { ReplicaState.Stopped, ReplicaState.Stopped, ReplicaState.Stopped } },
};
}
}
Loading…
Cancel
Save