Skip to content

Commit d1c5b74

Browse files
authored
Merge pull request #377 from KodrAus/feat/wait-until-healthy
Support waiting until the cluster reports a healthy status
2 parents ac2a311 + a24d543 commit d1c5b74

File tree

1 file changed

+64
-6
lines changed

1 file changed

+64
-6
lines changed

src/SeqCli/Cli/Commands/Cluster/HealthCommand.cs

Lines changed: 64 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
using System;
1616
using System.Globalization;
1717
using System.Linq;
18+
using System.Threading;
1819
using System.Threading.Tasks;
20+
using Seq.Api;
1921
using SeqCli.Cli.Features;
2022
using SeqCli.Config;
2123
using SeqCli.Connection;
@@ -26,19 +28,29 @@
2628
namespace SeqCli.Cli.Commands.Cluster;
2729

2830
[Command("cluster", "health",
29-
"Probe a Seq node's `/health/cluster` endpoint, and print the returned status",
30-
Example = "seqcli cluster health -s https://seq.example.com")]
31+
"Probe a Seq node's `/health/cluster` endpoint, and print the returned status. This command can also be used " +
32+
"to wait on a timeout until the cluster is healthy.",
33+
Example = "seqcli cluster health -s https://seq.example.com --wait-until-healthy")]
3134
class HealthCommand : Command
3235
{
3336
readonly SeqConnectionFactory _connectionFactory;
3437

3538
readonly ConnectionFeature _connection;
3639
readonly OutputFormatFeature _output;
40+
readonly TimeoutFeature _timeout;
3741

42+
bool _waitUntilHealthy;
43+
3844
public HealthCommand(SeqConnectionFactory connectionFactory, SeqCliOutputConfig outputConfig)
3945
{
4046
_connectionFactory = connectionFactory ?? throw new ArgumentNullException(nameof(connectionFactory));
47+
48+
Options.Add("wait-until-healthy", "Wait until the cluster returns a status of healthy", _ =>
49+
{
50+
_waitUntilHealthy = true;
51+
});
4152

53+
_timeout = Enable(new TimeoutFeature());
4254
_output = Enable(new OutputFormatFeature(outputConfig));
4355
_connection = Enable<ConnectionFeature>();
4456
}
@@ -47,6 +59,54 @@ protected override async Task<int> Run()
4759
{
4860
var connection = _connectionFactory.Connect(_connection);
4961

62+
var timeout = _timeout.ApplyTimeout(connection.Client.HttpClient);
63+
64+
if (_waitUntilHealthy)
65+
{
66+
return await RunUntilHealthy(connection, timeout ?? TimeSpan.FromSeconds(30));
67+
}
68+
69+
return await RunOnce(connection);
70+
}
71+
72+
async Task<int> RunUntilHealthy(SeqConnection connection, TimeSpan timeout)
73+
{
74+
using var ct = new CancellationTokenSource(timeout);
75+
76+
var tick = TimeSpan.FromSeconds(1);
77+
78+
connection.Client.HttpClient.Timeout = tick;
79+
80+
try
81+
{
82+
return await Task.Run(async () =>
83+
{
84+
while (true)
85+
{
86+
try
87+
{
88+
if (await RunOnce(connection) == 0)
89+
{
90+
return 0;
91+
}
92+
}
93+
catch (Exception ex)
94+
{
95+
Log.Error("{UnhandledExceptionMessage}", Presentation.FormattedMessage(ex));
96+
}
97+
98+
await Task.Delay(tick, ct.Token);
99+
}
100+
}, ct.Token);
101+
}
102+
catch (TaskCanceledException)
103+
{
104+
return 1;
105+
}
106+
}
107+
108+
async Task<int> RunOnce(SeqConnection connection)
109+
{
50110
var health = await connection.Cluster.CheckHealthAsync();
51111

52112
if (_output.Json)
@@ -58,14 +118,12 @@ protected override async Task<int> Run()
58118
Console.WriteLine($"{health.Status}");
59119
}
60120

61-
return (health.Status) switch
121+
return health.Status switch
62122
{
63123
HealthStatus.Healthy => 0,
64124
HealthStatus.Degraded => 101,
65125
HealthStatus.Unhealthy => 102,
66-
// Catch-all for any future statuses
67-
// We give the main ones well-defined exit codes
68-
_ => (int)health.Status
126+
_ => 103
69127
};
70128
}
71129
}

0 commit comments

Comments
 (0)