Skip to content

Commit

Permalink
- Added a method overload to Raven Health check to allow additional R…
Browse files Browse the repository at this point in the history
…aven Options like array of Urls, certificate etc.

- Marked the existing Raven Health check method as `Obsolete`
  • Loading branch information
vijayankit committed Apr 3, 2019
1 parent de377b3 commit ee3adfc
Show file tree
Hide file tree
Showing 7 changed files with 289 additions and 20 deletions.
@@ -1,4 +1,5 @@
using HealthChecks.RavenDB;
using System;
using HealthChecks.RavenDB;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using System.Collections.Generic;

Expand All @@ -22,18 +23,62 @@ public static class RavenDBHealthCheckBuilderExtensions
/// 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>
/// <returns>The <see cref="IHealthChecksBuilder"/>.</returns></param>
/// <returns>The <see cref="IHealthChecksBuilder"/>.</returns>
[Obsolete("This method is deprecated.")]
public static IHealthChecksBuilder AddRavenDB(
this IHealthChecksBuilder builder,
string connectionString,
string databaseName = default,
string name = default,
HealthStatus? failureStatus = default,
IEnumerable<string> tags = default)
=> builder.Add(new HealthCheckRegistration(
{
if (connectionString == null)
{
throw new ArgumentNullException(nameof(connectionString));
}

return builder.AddRavenDB(
_ =>
{
_.Urls = new[] {connectionString};
_.Database = databaseName;
},
name,
failureStatus,
tags);
}


/// <summary>
/// Add a health check for RavenDB.
/// </summary>
/// <param name="builder">The <see cref="IHealthChecksBuilder"/>.</param>
/// <param name="setup">The action to configure the RavenDB setup.</param>
/// <param name="name">
/// The health check name. Optional. If <see langword="null"/> the type name 'ravendb' 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 <see langword="null"/> 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>
/// <returns>The <see cref="IHealthChecksBuilder"/>.</returns>
public static IHealthChecksBuilder AddRavenDB(
this IHealthChecksBuilder builder,
Action<RavenDBOptions> setup,
string name = default,
HealthStatus? failureStatus = default,
IEnumerable<string> tags = default)
{
var options = new RavenDBOptions();
setup?.Invoke(options);

return builder.Add(new HealthCheckRegistration(
name ?? NAME,
sp => new RavenDBHealthCheck(connectionString, databaseName),
sp => new RavenDBHealthCheck(options),
failureStatus,
tags));
}
}
}
38 changes: 25 additions & 13 deletions src/HealthChecks.RavenDB/RavenDBHealthCheck.cs
Expand Up @@ -10,37 +10,49 @@ namespace HealthChecks.RavenDB
{
public class RavenDBHealthCheck : IHealthCheck
{
private readonly string _connectionString;
private readonly string _specifiedDatabase;
private readonly RavenDBOptions _options;

public RavenDBHealthCheck(string connectionString, string databaseName = default)
public RavenDBHealthCheck(RavenDBOptions options)
{
_connectionString = connectionString ?? throw new ArgumentNullException(nameof(connectionString));
_specifiedDatabase = databaseName;
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}

if (options.Urls == null)
{
throw new ArgumentNullException(nameof(options.Urls));
}

_options = options;
}

public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
{
try
{
using (var store = new DocumentStore
{
Urls = new string[] { _connectionString }
Urls = _options.Urls,
})
{
if (_options.Certificate != null)
{
store.Certificate = _options.Certificate;
}

store.Initialize();
var databases = await store.Maintenance.Server.SendAsync(new GetDatabaseNamesOperation(start: 0, pageSize: 100));

if (!string.IsNullOrWhiteSpace(_specifiedDatabase)
&& !databases.Contains(_specifiedDatabase, StringComparer.OrdinalIgnoreCase))
if (!string.IsNullOrWhiteSpace(_options.Database)
&& !databases.Contains(_options.Database, StringComparer.OrdinalIgnoreCase))
{
return new HealthCheckResult(
context.Registration.FailureStatus,
$"RavenDB doesn't contains '{_specifiedDatabase}' database.");
}
else
{
return HealthCheckResult.Healthy();
$"RavenDB does not contain '{_options.Database}' database.");
}

return HealthCheckResult.Healthy();
}
}
catch (Exception ex)
Expand Down
13 changes: 13 additions & 0 deletions src/HealthChecks.RavenDB/RavenDBOptions.cs
@@ -0,0 +1,13 @@
using System.Security.Cryptography.X509Certificates;

namespace HealthChecks.RavenDB
{
public class RavenDBOptions
{
public string[] Urls { get; set; }

public X509Certificate2 Certificate { get; set; }

public string Database { get; set; }
}
}
@@ -0,0 +1,149 @@
using FluentAssertions;
using FunctionalTests.Base;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Net;
using System.Threading.Tasks;
using Xunit;

namespace FunctionalTests.HealthChecks.RavenDB
{
[Collection("execution")]
public class ravendb_with_options_healthcheck_should
{
private readonly ExecutionFixture _fixture;
private readonly string[] _urls = new[] {"http://live-test.ravendb.net:80"};

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

[SkipOnAppVeyor]
public async Task be_healthy_if_ravendb_is_available()
{

var webHostBuilder = new WebHostBuilder()
.UseStartup<DefaultStartup>()
.ConfigureServices(services =>
{
services
.AddHealthChecks()
.AddRavenDB(_ => { _.Urls = _urls; }, tags: new string[] {"ravendb"});
})
.Configure(app =>
{
app.UseHealthChecks("/health", new HealthCheckOptions()
{
Predicate = r => r.Tags.Contains("ravendb")
});
});

var server = new TestServer(webHostBuilder);

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

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

[SkipOnAppVeyor]
public async Task be_healthy_if_ravendb_is_available_and_contains_specific_database()
{
var webHostBuilder = new WebHostBuilder()
.UseStartup<DefaultStartup>()
.ConfigureServices(services =>
{
services
.AddHealthChecks()
.AddRavenDB(_ =>
{
_.Urls = _urls;
_.Database = "Demo";
}, tags: new string[] {"ravendb"});
})
.Configure(app =>
{
app.UseHealthChecks("/health", new HealthCheckOptions()
{
Predicate = r => r.Tags.Contains("ravendb")
});
});

var server = new TestServer(webHostBuilder);

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

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

[Fact]
public async Task be_unhealthy_if_ravendb_is_not_available()
{
var connectionString = "http://localhost:9999";

var webHostBuilder = new WebHostBuilder()
.UseStartup<DefaultStartup>()
.ConfigureServices(services =>
{
services
.AddHealthChecks()
.AddRavenDB(_ => { _.Urls = new string[] {connectionString}; }, tags: new string[] {"ravendb"});
})
.Configure(app =>
{
app.UseHealthChecks("/health", new HealthCheckOptions()
{
Predicate = r => r.Tags.Contains("ravendb")
});
});

var server = new TestServer(webHostBuilder);

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

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

[Fact]
public async Task be_unhealthy_if_ravendb_is_available_but_database_doesnot_exist()
{
var webHostBuilder = new WebHostBuilder()
.UseStartup<DefaultStartup>()
.ConfigureServices(services =>
{
services
.AddHealthChecks()
.AddRavenDB(_ =>
{
_.Urls = _urls;
_.Database = "ThisDatabaseReallyDoesnExist";
}, tags: new string[] {"ravendb"});
})
.Configure(app =>
{
app.UseHealthChecks("/health", new HealthCheckOptions()
{
Predicate = r => r.Tags.Contains("ravendb")
});
});

var server = new TestServer(webHostBuilder);

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

response.StatusCode
.Should().Be(HttpStatusCode.ServiceUnavailable);
}
}
}
Expand Up @@ -9,16 +9,17 @@
using System.Net;
using System.Threading.Tasks;
using Xunit;
#pragma warning disable 618

namespace FunctionalTests.HealthChecks.RavenDB
{
[Collection("execution")]
public class ravendb_healthcheck_should
public class ravendb_with_single_connection_string_healthcheck_should
{
private readonly ExecutionFixture _fixture;
private const string ConnectionString = "http://live-test.ravendb.net:80";

public ravendb_healthcheck_should(ExecutionFixture fixture)
public ravendb_with_single_connection_string_healthcheck_should(ExecutionFixture fixture)
{
_fixture = fixture ?? throw new ArgumentNullException(nameof(fixture));
}
Expand Down
@@ -0,0 +1,48 @@
using FluentAssertions;
using HealthChecks.RavenDB;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.Extensions.Options;
using System.Linq;
using Xunit;

namespace UnitTests.DependencyInjection.RavenDB
{
public class ravendb_with_options_registration_should
{
[Fact]
public void add_health_check_when_properly_configured()
{
var services = new ServiceCollection();
services.AddHealthChecks()
.AddRavenDB(_ => { _.Urls = new[] {"http://localhost:8080", "http://localhost:8081"}; });

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

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

registration.Name.Should().Be("ravendb");
check.GetType().Should().Be(typeof(RavenDBHealthCheck));
}

[Fact]
public void add_named_health_check_when_properly_configured()
{
var services = new ServiceCollection();
services.AddHealthChecks()
.AddRavenDB(_ => { _.Urls = new[] {"http://localhost:8080", "http://localhost:8081"}; },
name: "my-ravendb");

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

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

registration.Name.Should().Be("my-ravendb");
check.GetType().Should().Be(typeof(RavenDBHealthCheck));
}
}
}
Expand Up @@ -5,10 +5,11 @@
using Microsoft.Extensions.Options;
using System.Linq;
using Xunit;
#pragma warning disable 618

namespace UnitTests.DependencyInjection.RavenDB
{
public class ravendb_registration_should
public class ravendb_with_single_conection_string_registration_should
{
[Fact]
public void add_health_check_when_properly_configured()
Expand Down

0 comments on commit ee3adfc

Please sign in to comment.