From 59d3173f0b0aaeae4ac9242eb92bcc0443c72c2f Mon Sep 17 00:00:00 2001 From: Piotr Rojek <73109174+piotr-rojek@users.noreply.github.com> Date: Sun, 12 Feb 2023 21:58:54 +0100 Subject: [PATCH] add health check to code and docker (#7) Co-authored-by: Piotr Rojek --- src/ServiceBusEmulator.Host/Dockerfile | 8 ++- src/ServiceBusEmulator.Host/Program.cs | 17 +++-- .../ServiceBusEmulator.Host.csproj | 2 +- src/ServiceBusEmulator.RabbitMq/Extensions.cs | 3 + .../Links/IRabbitMqChannelFactory.cs | 9 +++ .../Links/RabbitMqChannelFactory.cs | 68 +++++++++++++++++++ .../Links/RabbitMqLinkProcessor.cs | 37 ++-------- .../RabbitMqHealthCheck.cs | 28 ++++++++ .../ServiceBusEmulator.RabbitMq.csproj | 1 + 9 files changed, 128 insertions(+), 45 deletions(-) create mode 100644 src/ServiceBusEmulator.RabbitMq/Links/IRabbitMqChannelFactory.cs create mode 100644 src/ServiceBusEmulator.RabbitMq/Links/RabbitMqChannelFactory.cs create mode 100644 src/ServiceBusEmulator.RabbitMq/RabbitMqHealthCheck.cs diff --git a/src/ServiceBusEmulator.Host/Dockerfile b/src/ServiceBusEmulator.Host/Dockerfile index 252b333..679f188 100644 --- a/src/ServiceBusEmulator.Host/Dockerfile +++ b/src/ServiceBusEmulator.Host/Dockerfile @@ -23,13 +23,17 @@ FROM --platform=amd64 mcr.microsoft.com/dotnet/sdk:7.0 AS build-env WORKDIR /app/src/ServiceBusEmulator.Host RUN dotnet publish -c Release -o /app/out -FROM mcr.microsoft.com/dotnet/runtime:7.0 +FROM mcr.microsoft.com/dotnet/aspnet:7.0 ENV DOTNET_ENVIRONMENT=Docker + RUN apt-get update + RUN apt-get --yes install curl + HEALTHCHECK --interval=5s --timeout=10s --retries=3 CMD curl --fail http://localhost:80/health || exit 1 + WORKDIR /app COPY --from=build-env /app/out . COPY --from=server-cert /home/testca/cert.pfx . COPY --from=server-cert /home/testca/cacert.cer . - + ENTRYPOINT ["dotnet", "ServiceBusEmulator.Host.dll"] \ No newline at end of file diff --git a/src/ServiceBusEmulator.Host/Program.cs b/src/ServiceBusEmulator.Host/Program.cs index 38bad3c..b9f57b8 100644 --- a/src/ServiceBusEmulator.Host/Program.cs +++ b/src/ServiceBusEmulator.Host/Program.cs @@ -2,15 +2,14 @@ using ServiceBusEmulator; using ServiceBusEmulator.RabbitMq; -IHost host = Host.CreateDefaultBuilder(args) - .ConfigureServices(services => - { - _ = services.AddServiceBusEmulator(); - _ = services.AddServiceBusEmulatorRabbitMqBackend(); - }) - .Build(); - Trace.TraceLevel = TraceLevel.Frame; Trace.TraceListener = (l, f, a) => Console.WriteLine(DateTime.Now.ToString("[hh:mm:ss.fff]") + " " + string.Format(f, a)); -host.Run(); +var builder = WebApplication.CreateBuilder(args); +builder.Services.AddServiceBusEmulator(); +builder.Services.AddServiceBusEmulatorRabbitMqBackend(); + +var app = builder.Build(); +app.UseHealthChecks("/health"); +app.UseRouting(); +app.Run(); diff --git a/src/ServiceBusEmulator.Host/ServiceBusEmulator.Host.csproj b/src/ServiceBusEmulator.Host/ServiceBusEmulator.Host.csproj index defb0b7..0b2fefb 100644 --- a/src/ServiceBusEmulator.Host/ServiceBusEmulator.Host.csproj +++ b/src/ServiceBusEmulator.Host/ServiceBusEmulator.Host.csproj @@ -1,4 +1,4 @@ - + net7.0 diff --git a/src/ServiceBusEmulator.RabbitMq/Extensions.cs b/src/ServiceBusEmulator.RabbitMq/Extensions.cs index 3584a80..8d55751 100644 --- a/src/ServiceBusEmulator.RabbitMq/Extensions.cs +++ b/src/ServiceBusEmulator.RabbitMq/Extensions.cs @@ -15,6 +15,7 @@ public static IServiceCollection AddServiceBusEmulatorRabbitMqBackend(this IServ _ = services.AddSingleton(); _ = services.AddSingleton(); + _ = services.AddSingleton(); _ = services.AddTransient(); _ = services.AddTransient(); _ = services.AddTransient(); @@ -35,6 +36,8 @@ public static IServiceCollection AddServiceBusEmulatorRabbitMqBackend(this IServ _ = services.AddOptions().Configure(configure).BindConfiguration("Emulator:RabbitMq"); + _ = services.AddHealthChecks().AddCheck("rabbitmq"); + return services; } } diff --git a/src/ServiceBusEmulator.RabbitMq/Links/IRabbitMqChannelFactory.cs b/src/ServiceBusEmulator.RabbitMq/Links/IRabbitMqChannelFactory.cs new file mode 100644 index 0000000..3bb6eef --- /dev/null +++ b/src/ServiceBusEmulator.RabbitMq/Links/IRabbitMqChannelFactory.cs @@ -0,0 +1,9 @@ +using RabbitMQ.Client; + +namespace ServiceBusEmulator.RabbitMq.Links +{ + public interface IRabbitMqChannelFactory + { + IModel CreateChannel(); + } +} diff --git a/src/ServiceBusEmulator.RabbitMq/Links/RabbitMqChannelFactory.cs b/src/ServiceBusEmulator.RabbitMq/Links/RabbitMqChannelFactory.cs new file mode 100644 index 0000000..81b69bd --- /dev/null +++ b/src/ServiceBusEmulator.RabbitMq/Links/RabbitMqChannelFactory.cs @@ -0,0 +1,68 @@ +using Microsoft.Extensions.Options; +using RabbitMQ.Client; +using ServiceBusEmulator.RabbitMq.Options; + +namespace ServiceBusEmulator.RabbitMq.Links +{ + public class RabbitMqChannelFactory : IRabbitMqChannelFactory, IDisposable + { + private readonly RabbitMqBackendOptions _options; + + private IConnection? _connection; + private ConnectionFactory _connectionFactory; + + private bool _disposed; + + public RabbitMqChannelFactory(IOptions options) + { + _options = options.Value; + + _connectionFactory = new() + { + Password = _options.Password, + UserName = _options.Username, + HostName = _options.Host, + Port = _options.Port + }; + } + + protected IConnection Connection + { + get + { + if (_disposed) + { + throw new ObjectDisposedException(nameof(RabbitMqChannelFactory)); + } + + if (!(_connection?.IsOpen ?? false)) + { + lock (_connectionFactory) + { + if (!(_connection?.IsOpen ?? false)) + { + _connection = _connectionFactory.CreateConnection(); + } + } + } + + return _connection; + } + } + + public IModel CreateChannel() + { + return Connection.CreateModel(); + } + + public void Dispose() + { + if (!_disposed && _connection != null) + { + _connection.Dispose(); + _connection = null; + } + _disposed = true; + } + } +} diff --git a/src/ServiceBusEmulator.RabbitMq/Links/RabbitMqLinkProcessor.cs b/src/ServiceBusEmulator.RabbitMq/Links/RabbitMqLinkProcessor.cs index b7c2537..f63cb84 100644 --- a/src/ServiceBusEmulator.RabbitMq/Links/RabbitMqLinkProcessor.cs +++ b/src/ServiceBusEmulator.RabbitMq/Links/RabbitMqLinkProcessor.cs @@ -2,35 +2,24 @@ using Amqp.Framing; using Amqp.Listener; using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; using RabbitMQ.Client; using ServiceBusEmulator.Abstractions.Security; -using ServiceBusEmulator.RabbitMq.Options; namespace ServiceBusEmulator.RabbitMq.Links { public class RabbitMqLinkProcessor : ILinkProcessor { - private readonly RabbitMqBackendOptions _options; private readonly IRabbitMqLinkEndpointFactory _linkEndpointFactory; private readonly IRabbitMqLinkRegister _linkRegister; + private readonly IRabbitMqChannelFactory _channelFactory; private readonly IRabbitMqInitializer _initializer; private readonly ISecurityContext _securityContext; private readonly ILogger _logger; - private RabbitMQ.Client.IConnection? _connection; - private RabbitMQ.Client.ConnectionFactory _connectionFactory; - public RabbitMqLinkProcessor(IRabbitMqInitializer intializer, ISecurityContext securityContext, IOptions options, ILogger logger, IRabbitMqLinkRegister linkRegister, IRabbitMqLinkEndpointFactory linkEndpointFactory) + public RabbitMqLinkProcessor(IRabbitMqChannelFactory channelFactory,IRabbitMqInitializer intializer, ISecurityContext securityContext, ILogger logger, IRabbitMqLinkRegister linkRegister, IRabbitMqLinkEndpointFactory linkEndpointFactory) { - _options = options.Value; - _connectionFactory = new() - { - Password = _options.Password, - UserName = _options.Username, - HostName = _options.Host, - Port = _options.Port - }; + _channelFactory = channelFactory; _initializer = intializer; _securityContext = securityContext; _logger = logger; @@ -38,24 +27,6 @@ public RabbitMqLinkProcessor(IRabbitMqInitializer intializer, ISecurityContext s _linkEndpointFactory = linkEndpointFactory; } - protected RabbitMQ.Client.IConnection Connection - { - get - { - if (!(_connection?.IsOpen ?? false)) - { - lock (_connectionFactory) - { - if (!(_connection?.IsOpen ?? false)) - { - _connection = _connectionFactory.CreateConnection(); - } - } - } - - return _connection; - } - } public void Process(AttachContext attachContext) { @@ -73,7 +44,7 @@ public void Process(AttachContext attachContext) return; } - IModel channel = Connection.CreateModel(); + IModel channel = _channelFactory.CreateChannel(); _initializer.Initialize(channel); (var linkEndpoint, int initialCredit) = _linkEndpointFactory.CreateEndpoint(channel, attachContext.Attach, attachContext.Link.Role); diff --git a/src/ServiceBusEmulator.RabbitMq/RabbitMqHealthCheck.cs b/src/ServiceBusEmulator.RabbitMq/RabbitMqHealthCheck.cs new file mode 100644 index 0000000..96236f6 --- /dev/null +++ b/src/ServiceBusEmulator.RabbitMq/RabbitMqHealthCheck.cs @@ -0,0 +1,28 @@ +using Microsoft.Extensions.Diagnostics.HealthChecks; +using ServiceBusEmulator.RabbitMq.Links; + +namespace ServiceBusEmulator.RabbitMq +{ + public class RabbitMqHealthCheck : IHealthCheck + { + private readonly IRabbitMqChannelFactory _channelFactory; + + public RabbitMqHealthCheck(IRabbitMqChannelFactory channelFactory) + { + _channelFactory = channelFactory; + } + + public Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) + { + try + { + using var model = _channelFactory.CreateChannel(); + return Task.FromResult(HealthCheckResult.Healthy()); + } + catch (Exception ex) + { + return Task.FromResult(new HealthCheckResult(context.Registration.FailureStatus, exception: ex)); + } + } + } +} diff --git a/src/ServiceBusEmulator.RabbitMq/ServiceBusEmulator.RabbitMq.csproj b/src/ServiceBusEmulator.RabbitMq/ServiceBusEmulator.RabbitMq.csproj index 2136a1f..b4b0dd1 100644 --- a/src/ServiceBusEmulator.RabbitMq/ServiceBusEmulator.RabbitMq.csproj +++ b/src/ServiceBusEmulator.RabbitMq/ServiceBusEmulator.RabbitMq.csproj @@ -7,6 +7,7 @@ +