Skip to content

add activemq healthcheck #2399

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ MYSQL_PASSWORD=Password12!
ZOOKEEPER_PORT=3000
KAFKA_PORT=9092
RABBITMQ_PORT=5672
ACTIVEMQ_PORT=61616
IDSVR_PORT=8888
ORACLE_PORT=1521
SFTP_PORT=22
Expand Down
1,187 changes: 1,187 additions & 0 deletions AspNetCore.Diagnostics.HealthChecks.sln

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<CentralPackageTransitivePinningEnabled>false</CentralPackageTransitivePinningEnabled>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Apache.NMS.AMQP" Version="2.3.0" />
<PackageVersion Include="ArangoDBNetStandard" Version="2.0.1" />
<PackageVersion Include="AWSSDK.CloudWatch" Version="3.7.300.7" />
<PackageVersion Include="AWSSDK.DynamoDBv2" Version="3.7.300.7" />
Expand Down Expand Up @@ -111,10 +112,11 @@
<PackageVersion Include="Testcontainers.MySql" Version="$(TestcontainersVersion)" />
<PackageVersion Include="Testcontainers.Redis" Version="$(TestcontainersVersion)" />
<PackageVersion Include="Testcontainers.RabbitMq" Version="$(TestcontainersVersion)" />
<PackageVersion Include="Testcontainers.ActiveMq" Version="$(TestcontainersVersion)" />
<PackageVersion Include="xunit" Version="2.9.2" />
<PackageVersion Include="xunit.runner.visualstudio" Version="2.8.2" />
</ItemGroup>
<ItemGroup Condition="'$(IsPackable)' == 'true'">
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
</ItemGroup>
</Project>
</Project>
4 changes: 4 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ services:
image: rabbitmq
ports:
- ${RABBITMQ_PORT}:5672
activemq:
image: apache/activemq-artemis
ports:
- ${ACTIVEMQ_PORT}:61616
idsvr:
image: nakah/identityserver4
ports:
Expand Down
45 changes: 45 additions & 0 deletions src/HealthChecks.ActiveMq/ActiveMQHealthCheck.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using Amqp;
using Microsoft.Extensions.Diagnostics.HealthChecks;


namespace HealthChecks.Activemq;

/// <summary>
/// A health check for ActiveMQ services.
/// </summary>
public class ActiveMqHealthCheck : IHealthCheck
{
private readonly IConnection? _connection;

public ActiveMqHealthCheck(IConnection connection)
{
_connection = Guard.ThrowIfNull(connection);
}
/// <summary>
/// CheckHealthAsync
/// </summary>
/// <param name="context"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
{
try
{
if (_connection == null)
{
return Task.FromResult(new HealthCheckResult(context.Registration.FailureStatus, description: "Connection is null."));
}

if (_connection.IsClosed)
{
return Task.FromResult(new HealthCheckResult(context.Registration.FailureStatus, description: "Connection is not established."));
}

return Task.FromResult(HealthCheckResult.Healthy());
}
catch (Exception ex)
{
return Task.FromResult(new HealthCheckResult(context.Registration.FailureStatus, exception: ex));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using Amqp;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks;

namespace HealthChecks.Activemq;

/// <summary>
/// Extension methods to configure <see cref="ActiveMqHealthCheck"/>.
/// </summary>
public static class ActiveMQHealthCheckBuilderExtensions
{
private const string NAME = "activemq";

public static IHealthChecksBuilder AddActiveMQ(
this IHealthChecksBuilder builder,
string? name,
HealthStatus? failureStatus = default,
IEnumerable<string>? tags = default,
TimeSpan? timeout = default)
{
// Register the health check service
builder.Services.AddKeyedSingleton<ActiveMqHealthCheck>(name);

return builder.Add(new HealthCheckRegistration(
name ?? NAME,
provider => provider.GetRequiredKeyedService<ActiveMqHealthCheck>(name),
failureStatus ?? HealthStatus.Unhealthy,
tags ?? new[] { "activemq" },
timeout));
}

public static IHealthChecksBuilder AddActiveMQ(
this IHealthChecksBuilder builder,
string? name,
IConnection connection,
HealthStatus? failureStatus = default,
IEnumerable<string>? tags = default,
TimeSpan? timeout = default)
{
// Register the health check service
builder.Services.AddKeyedSingleton<ActiveMqHealthCheck>(name, (provider, _) => new ActiveMqHealthCheck(connection));

return builder.Add(new HealthCheckRegistration(
name ?? NAME,
provider => provider.GetRequiredKeyedService<ActiveMqHealthCheck>(name),
failureStatus ?? HealthStatus.Unhealthy,
tags ?? new[] { "activemq" },
timeout));
}

}
17 changes: 17 additions & 0 deletions src/HealthChecks.ActiveMq/HealthChecks.ActiveMQ.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>$(DefaultLibraryTargetFrameworks)</TargetFrameworks>
<PackageTags>$(PackageTags);Activemq</PackageTags>
<Description>HealthChecks.Activemq is the health check package for ActiveMQ.Client.</Description>
<VersionPrefix>$(HealthCheckActivemq)</VersionPrefix>
<RootNamespace>HealthChecks.Activemq</RootNamespace> <!--For backward naming compatibility-->
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Apache.NMS.AMQP" />
</ItemGroup>



</Project>
48 changes: 48 additions & 0 deletions src/HealthChecks.ActiveMq/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# ActiveMq Health Check

This health check verifies the ability to communicate with a ActiveMQ server

## Example Usage

With all of the following examples, you can additionally add the following parameters:

- `name`: The health check name. Default if not specified is `activemq`.
- `failureStatus`: The `HealthStatus` that should be reported when the health check fails. Default is `HealthStatus Unhealthy`.
- `tags`: A list of tags that can be used to filter sets of health checks.
- `timeout`: A `System.TimeSpan` representing the timeout of the check.

### Dependency Injected `IConnection`

```csharp
var connectAddress = new Amqp.Address("activemqhost",
61616,
"artemis",
"artemis",
"/",
"amqp");

var connectionFactory = new ConnectionFactory();
var connection = await connectionFactory.CreateAsync(connectAddress);

var webHostBuilder = new WebHostBuilder()
.ConfigureServices(services =>
{
services
.AddSingleton<IConnection>(connection)
.AddHealthChecks()
.AddActiveMQ(name: "activemq", tags: ["activemq"]);
})
.Configure(app =>
{
app.UseHealthChecks("/health", new HealthCheckOptions
{
Predicate = r => r.Tags.Contains("activemq")
});
});
```






43 changes: 43 additions & 0 deletions test/HealthChecks.ActiveMQ.Tests/ActiveMqContainerFixture.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using Testcontainers.ActiveMq;

namespace HealthCheck.Activemq.Tests;

public sealed class ArtemisContainerTest : IAsyncLifetime
{
public const string Registry = "docker.io";
public const string Image = "apache/activemq-artemis";
public const string Tag = "latest";
public const string Username = "artemis";
public const string Password = "artemis";

public ArtemisContainer? Container { get; private set; }

public string GetHost() => Container?.Hostname ?? throw new InvalidOperationException("The test container was not initialized.");

public int GetPort() => Container?.GetMappedPublicPort(61616) ?? throw new InvalidOperationException("The test container was not initialized.");

public string GetUsername() => Username;

public string GetPassword() => Password;

public async Task InitializeAsync()
{
Container = new ArtemisBuilder()
.WithImage($"{Registry}/{Image}:{Tag}")
.WithUsername(Username)
.WithPassword(Password)
.Build();

await Container.StartAsync().ConfigureAwait(false);
}

public async Task DisposeAsync()
{
if (Container is not null)
{
await Container.DisposeAsync().ConfigureAwait(false);
}

GC.SuppressFinalize(this);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using Amqp;
using Amqp.Framing;
using HealthChecks.Activemq;

namespace HealthCheck.Activemq.Tests;

public class active_registration_should
{

private const string DEFAULT_CHECK_NAME = "activemq";

[Fact]
public void add_health_check_when_properly_configured()
{
var services = new ServiceCollection();
services.AddSingleton<IConnection>(new MockConnection());
services.AddHealthChecks()
.AddActiveMQ("activemq");

using var serviceProvider = services.BuildServiceProvider();
var options = serviceProvider.GetRequiredService<IOptions<HealthCheckServiceOptions>>();

var registration = options.Value.Registrations.First();
var check = registration.Factory(serviceProvider);

registration.Name.ShouldBe(DEFAULT_CHECK_NAME);
check.ShouldBeOfType<ActiveMqHealthCheck>();
}

[Fact]
public void add_named_health_check_when_properly_configured()
{
var services = new ServiceCollection();
var customCheckName = "my-" + DEFAULT_CHECK_NAME;

services.AddSingleton<IConnection>(new MockConnection());

services.AddHealthChecks()
.AddActiveMQ(name: customCheckName);

using var serviceProvider = services.BuildServiceProvider();
var options = serviceProvider.GetRequiredService<IOptions<HealthCheckServiceOptions>>();

var registration = options.Value.Registrations.First();
var check = registration.Factory(serviceProvider);

registration.Name.ShouldBe(customCheckName);
check.ShouldBeOfType<ActiveMqHealthCheck>();
}

internal class MockConnection : IConnection
{
public Error Error => throw new NotImplementedException();
public bool IsClosed => throw new NotImplementedException();

public event ClosedCallback? Closed
{
add { }
remove { }
}

public void AddClosedCallback(ClosedCallback callback) => throw new NotImplementedException();
public void Close() => throw new NotImplementedException();
public void Close(TimeSpan waitUntilEnded, Error error) => throw new NotImplementedException();
public Task CloseAsync() => throw new NotImplementedException();
public Task CloseAsync(TimeSpan timeout, Error error) => throw new NotImplementedException();
public ISession CreateSession() => throw new NotImplementedException();
}

}
Loading