Skip to content

Commit

Permalink
Merge pull request #431 from fglaeser/ibmmq
Browse files Browse the repository at this point in the history
HealthChecks.IbmMQ
  • Loading branch information
CarlosLanderas authored Mar 11, 2020
2 parents 2016d6d + ecc3f6a commit 0bf908e
Show file tree
Hide file tree
Showing 10 changed files with 734 additions and 360 deletions.
725 changes: 366 additions & 359 deletions AspNetCore.Diagnostics.HealthChecks.sln

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -142,4 +142,5 @@ else {
exec { & dotnet pack .\src\HealthChecks.Kubernetes\HealthChecks.Kubernetes.csproj -c Release -o .\artifacts --include-symbols --no-build --version-suffix=$suffix }
exec { & dotnet pack .\src\HealthChecks.SignalR\HealthChecks.SignalR.csproj -c Release -o .\artifacts --include-symbols --no-build --version-suffix=$suffix }
exec { & dotnet pack .\src\HealthChecks.Gcp.CloudFirestore\HealthChecks.Gcp.CloudFirestore.csproj -c Release -o .\artifacts --include-symbols --no-build --version-suffix=$suffix }
exec { & dotnet pack .\src\HealthChecks.IbmMQ\HealthChecks.IbmMQ.csproj -c Release -o .\artifacts --include-symbols --no-build }
}
1 change: 1 addition & 0 deletions build/dependencies.props
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@
<HealthCheckSignalR>3.0.0</HealthCheckSignalR>
<HealthCheckCloudFirestore>3.0.0</HealthCheckCloudFirestore>
<HealthCheckIoTHub>3.0.0</HealthCheckIoTHub>
<HealthCheckIbmMQ>3.0.0</HealthCheckIbmMQ>
<HealthChecksUIK8sOperator>3.0.0-beta.1</HealthChecksUIK8sOperator>
</PropertyGroup>
</Project>
13 changes: 12 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -135,4 +135,15 @@ services:
image: consul:latest
ports:
- "8500:8500"
- "8600:8600"
- "8600:8600"
ibmmq:
image: ibmcom/mq
ports:
- "1414:1414"
- "9157:9157"
environment:
- LICENSE=accept
- MQ_QMGR_NAME=QM.TEST.01
- MQ_APP_PASSWORD=12345678
- MQ_ENABLE_METRICS=true

Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
using HealthChecks.IbmMQ;
using IBM.WMQ;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using System;
using System.Collections;
using System.Collections.Generic;


namespace Microsoft.Extensions.DependencyInjection
{
public static class IbmMQHealthCheckBuilderExtensions
{
const string NAME = "ibmmq";

/// <summary>
/// Add a health check for IbmMQ services using connection properties.
/// </summary>
/// <param name="builder">The <see cref="IHealthChecksBuilder"/>.</param>
/// <param name="queueManager">The name of the queue manager to use.</param>
/// <param name="connectionProperties">The list of properties that will be used for connection.</param>
/// <param name="name">The health check name. Optional. If <c>null</c> the type name 'ibmmq' will be used for the name.</param>
/// <param name="failureStatus">
/// The <see cref="HealthStatus"/> that should be reported when the health check fails. Optional. If <c>null</c> then
/// the default status of <see cref="HealthStatus.Unhealthy"/> will be reported.
/// </param>
/// <param name="tags">A list of tags that can be used to filter sets of health checks. Optional.</param>
/// <param name="timeout">An optional System.TimeSpan representing the timeout of the check.</param>
/// <returns>The <see cref="IHealthChecksBuilder"/>.</returns>
public static IHealthChecksBuilder AddIbmMQ(this IHealthChecksBuilder builder, string queueManager, Hashtable connectionProperties, string name = default, HealthStatus? failureStatus = default, IEnumerable<string> tags = default, TimeSpan? timeout = default)
{
return builder.Add(new HealthCheckRegistration(
name ?? NAME,
new IbmMQHealthCheck(queueManager, connectionProperties),
failureStatus,
tags,
timeout));
}

/// <summary>
/// Add a health check for IbmMQ services using a managed connection.
/// </summary>
/// <param name="builder">The <see cref="IHealthChecksBuilder"/>.</param>
/// <param name="queueManager">The name of the queue manager to use.</param>
/// <param name="channel">The name of the channel.</param>
/// <param name="connectionInfo">The connection information in the following format HOSTNAME(PORT).</param>
/// <param name="userName">The user name. Optional.</param>
/// <param name="password">The password. Optional</param>
/// <param name="name">The health check name. Optional. If <c>null</c> the type name 'ibmmq' will be used for the name.</param>
/// <param name="failureStatus">
/// The <see cref="HealthStatus"/> that should be reported when the health check fails. Optional. If <c>null</c> then
/// the default status of <see cref="HealthStatus.Unhealthy"/> will be reported.
/// </param>
/// <param name="tags">A list of tags that can be used to filter sets of health checks. Optional.</param>
/// <param name="timeout">An optional System.TimeSpan representing the timeout of the check.</param>
/// <returns>The <see cref="IHealthChecksBuilder"/>.</returns>
public static IHealthChecksBuilder AddIbmMQManagedConnection(this IHealthChecksBuilder builder, string queueManager, string channel, string connectionInfo, string userName = null, string password = null, string name = default, HealthStatus? failureStatus = default, IEnumerable<string> tags = default, TimeSpan? timeout = default)
{
return builder.Add(new HealthCheckRegistration(
name ?? NAME,
new IbmMQHealthCheck(queueManager, BuildProperties(channel, connectionInfo, userName, password)),
failureStatus,
tags,
timeout));
}

private static Hashtable BuildProperties(string channel, string connectionInfo, string userName = null, string password = null)
{
Hashtable properties = new Hashtable {
{MQC.CHANNEL_PROPERTY, channel},
{MQC.TRANSPORT_PROPERTY, MQC.TRANSPORT_MQSERIES_MANAGED},
{MQC.CONNECTION_NAME_PROPERTY, connectionInfo}
};

if (!string.IsNullOrEmpty(userName))
properties.Add(MQC.USER_ID_PROPERTY, userName);
if (!string.IsNullOrEmpty(password))
properties.Add(MQC.PASSWORD_PROPERTY, password);

return properties;
}
}
}
24 changes: 24 additions & 0 deletions src/HealthChecks.IbmMQ/HealthChecks.IbmMQ.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>$(NetStandardTargetVersion)</TargetFramework>
<PackageLicenseUrl>$(PackageLicenseUrl)</PackageLicenseUrl>
<PackageProjectUrl>$(PackageProjectUrl)</PackageProjectUrl>
<PackageTags>HealthCheck;IbmMQ</PackageTags>
<Description>HealthChecks.IbmMQ is the health check package for IbmMQ.</Description>
<Version>$(HealthCheckIbmMQ)</Version>
<RepositoryUrl>$(RepositoryUrl)</RepositoryUrl>
<Company>$(Company)</Company>
<Authors>$(Authors)</Authors>
<LangVersion>latest</LangVersion>
<PackageId>AspNetCore.HealthChecks.IbmMQ</PackageId>
<PublishRepositoryUrl>$(PublishRepositoryUrl)</PublishRepositoryUrl>
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder)</AllowedOutputExtensionsInPackageBuildOutputFolder>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="IBMMQDotnetClient" Version="9.1.4" />
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="$(MicrosoftExtensionsDiagnosticsHealthChecks)" />
</ItemGroup>

</Project>
38 changes: 38 additions & 0 deletions src/HealthChecks.IbmMQ/IbmMQHealthCheck.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using IBM.WMQ;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using System;
using System.Collections;
using System.Threading;
using System.Threading.Tasks;

namespace HealthChecks.IbmMQ
{
public class IbmMQHealthCheck : IHealthCheck
{
private readonly Hashtable _connectionProperties;
private readonly string _queueManager;

public IbmMQHealthCheck(string queueManager, Hashtable connectionProperties)
{
_queueManager = queueManager;
_connectionProperties = connectionProperties;
}

public Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
{
try
{
using (var connection = new MQQueueManager(_queueManager, _connectionProperties))
{
return Task.FromResult(
HealthCheckResult.Healthy());
}
}
catch (Exception ex)
{
return Task.FromResult(
new HealthCheckResult(context.Registration.FailureStatus, exception: ex));
}
}
}
}
41 changes: 41 additions & 0 deletions src/HealthChecks.IbmMQ/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# IbmMQ Health Check

This health check verifies the ability to communicate with a IbmMQ 9.0.+ 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 `ibmmq`.
- `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.

### Basic

Use the extension method where you provide the queue manager name and the connection properties.

```cs
public void ConfigureServices(IServiceCollection services)
{
Hashtable connectionProperties = new Hashtable {
{MQC.TRANSPORT_PROPERTY, MQC.TRANSPORT_MQSERIES_BINDINGS},
};
services
.AddHealthChecks()
.AddIbmMQ("QM.TEST.01", connectionProperties);
}
```

### Using Managed Client Connection

For `MQC.TRANSPORT_MQSERIES_MANAGED` connection you can use the following conveniece extension method where you need to specify the channel and the host(port) information. User and password are optional parameters.

```cs
public void ConfigureServices(IServiceCollection services)
{
services
.AddHealthChecks()
.AddIbmMQManagedConnection("QM.TEST.01", "DEV.APP.SVRCONN", "localhost(1417)", userName: "app", password: "xxx");
}
```
1 change: 1 addition & 0 deletions test/FunctionalTests/FunctionalTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<ProjectReference Include="..\..\src\HealthChecks.EventStore\HealthChecks.EventStore.csproj" />
<ProjectReference Include="..\..\src\HealthChecks.Elasticsearch\HealthChecks.Elasticsearch.csproj" />
<ProjectReference Include="..\..\src\HealthChecks.Solr\HealthChecks.Solr.csproj" />
<ProjectReference Include="..\..\src\HealthChecks.IbmMQ\HealthChecks.IbmMQ.csproj" />
<ProjectReference Include="..\..\src\HealthChecks.IdSvr\HealthChecks.IdSvr.csproj" />
<ProjectReference Include="..\..\src\HealthChecks.Kafka\HealthChecks.Kafka.csproj" />
<ProjectReference Include="..\..\src\HealthChecks.MongoDb\HealthChecks.MongoDb.csproj" />
Expand Down
168 changes: 168 additions & 0 deletions test/FunctionalTests/HealthChecks.IbmMQ/IbmMQHealthChecksTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
using FluentAssertions;
using FunctionalTests.Base;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using System;
using System.Net;
using System.Threading.Tasks;
using Xunit;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using System.Collections;
using IBM.WMQ;

namespace FunctionalTests.HealthChecks.IbmMQ
{

[Collection("execution")]
public class ibmmq_healthcheck_should
{
private readonly ExecutionFixture _fixture;

// Define the name of the queue manager to use (applies to all connections)
const string qManager = "QM.TEST.01";

// Define the name of your host connection (applies to client connections only)
const string hostName = "localhost(1414)";

const string wrongHostName = "localhost(1417)";

// Define the name of the channel to use (applies to client connections only)
const string channel = "DEV.APP.SVRCONN";

// Define the user name.
const string user = "app";

// Define the password.
const string password = "12345678";

public ibmmq_healthcheck_should(ExecutionFixture fixture)
{
_fixture = fixture ?? throw new ArgumentNullException(nameof(fixture));
}


[SkipOnAppVeyor]
public async Task be_healthy_if_ibmmq_is_available()
{
Hashtable properties = new Hashtable();
properties.Add(MQC.TRANSPORT_PROPERTY, MQC.TRANSPORT_MQSERIES_MANAGED);
properties.Add(MQC.CHANNEL_PROPERTY, channel);
properties.Add(MQC.CONNECTION_NAME_PROPERTY, hostName);
properties.Add(MQC.USER_ID_PROPERTY, user);
properties.Add(MQC.PASSWORD_PROPERTY, password);

var webHostBuilder = new WebHostBuilder()
.UseStartup<DefaultStartup>()
.ConfigureServices(services =>
{
services.AddHealthChecks().
AddIbmMQ(qManager, properties , tags: new string[] { "ibmmq" });
})
.Configure(app =>
{
app.UseHealthChecks("/health", new HealthCheckOptions()
{
Predicate = r => r.Tags.Contains("ibmmq")
});
});

var server = new TestServer(webHostBuilder);

var response = await server.CreateRequest("/health")
.GetAsync();

response.StatusCode
.Should().Be(HttpStatusCode.OK);
}

[SkipOnAppVeyor]
public async Task be_unhealthy_if_ibmmq_is_unavailable()
{
Hashtable properties = new Hashtable();
properties.Add(MQC.TRANSPORT_PROPERTY, MQC.TRANSPORT_MQSERIES_MANAGED);
properties.Add(MQC.CHANNEL_PROPERTY, channel);
properties.Add(MQC.CONNECTION_NAME_PROPERTY, wrongHostName);
properties.Add(MQC.USER_ID_PROPERTY, user);
properties.Add(MQC.PASSWORD_PROPERTY, password);

var webHostBuilder = new WebHostBuilder()
.UseStartup<DefaultStartup>()
.ConfigureServices(services =>
{
services.AddHealthChecks()
.AddIbmMQ(qManager, properties, tags: new string[] { "ibmmq" });
})
.Configure(app =>
{
app.UseHealthChecks("/health", new HealthCheckOptions()
{
Predicate = r => r.Tags.Contains("ibmmq")
});
});

var server = new TestServer(webHostBuilder);

var response = await server.CreateRequest("/health")
.GetAsync();

response.StatusCode
.Should().Be(HttpStatusCode.ServiceUnavailable);
}

[SkipOnAppVeyor]
public async Task be_unhealthy_if_ibmmq_managed_is_unavailable()
{
var webHostBuilder = new WebHostBuilder()
.UseStartup<DefaultStartup>()
.ConfigureServices(services =>
{
services.AddHealthChecks()
.AddIbmMQManagedConnection(qManager, channel, wrongHostName, user, password, tags: new string[] { "ibmmq" });
})
.Configure(app =>
{
app.UseHealthChecks("/health", new HealthCheckOptions()
{
Predicate = r => r.Tags.Contains("ibmmq")
});
});

var server = new TestServer(webHostBuilder);

var response = await server.CreateRequest("/health")
.GetAsync();

response.StatusCode
.Should().Be(HttpStatusCode.ServiceUnavailable);
}

[SkipOnAppVeyor]
public async Task be_healthy_if_ibmmq_managed_is_available()
{
var webHostBuilder = new WebHostBuilder()
.UseStartup<DefaultStartup>()
.ConfigureServices(services =>
{
services.AddHealthChecks().
AddIbmMQManagedConnection(qManager, channel, hostName, user, password, tags: new string[] { "ibmmq" });
})
.Configure(app =>
{
app.UseHealthChecks("/health", new HealthCheckOptions()
{
Predicate = r => r.Tags.Contains("ibmmq")
});
});

var server = new TestServer(webHostBuilder);

var response = await server.CreateRequest("/health")
.GetAsync();

response.StatusCode
.Should().Be(HttpStatusCode.OK);
}
}
}

0 comments on commit 0bf908e

Please sign in to comment.