Skip to content
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 Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@
<PackageVersion Include="Testcontainers" Version="$(TestcontainersVersion)" />
<PackageVersion Include="Testcontainers.ClickHouse" Version="$(TestcontainersVersion)" />
<PackageVersion Include="Testcontainers.Kafka" Version="$(TestcontainersVersion)" />
<PackageVersion Include="Testcontainers.LocalStack" Version="$(TestcontainersVersion)" />
<PackageVersion Include="Testcontainers.Milvus" Version="$(TestcontainersVersion)" />
<PackageVersion Include="Testcontainers.PostgreSql" Version="$(TestcontainersVersion)" />
<PackageVersion Include="TestContainers.MongoDb" Version="$(TestcontainersVersion)" />
Expand Down
23 changes: 19 additions & 4 deletions src/HealthChecks.Aws.Sqs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ With all of the following examples, you can additionally add the following param

### Basic

### Check existence of a queue and load credentials from the application's default configuration
### Check the existence of a queue and load credentials from the application's default configuration

```csharp
public void ConfigureServices(IServiceCollection services)
Expand All @@ -27,7 +27,7 @@ public void ConfigureServices(IServiceCollection services)
}
```

### Check existence of a queue and directly pass credentials
### Check the existence of a queue and directly pass credentials

```csharp
public void ConfigureServices(IServiceCollection services)
Expand All @@ -42,7 +42,7 @@ public void ConfigureServices(IServiceCollection services)
}
```

### Check existence of a queue and specify region endpoint
### Check the existence of a queue and specify region endpoint

```csharp
public void ConfigureServices(IServiceCollection services)
Expand All @@ -57,7 +57,7 @@ public void ConfigureServices(IServiceCollection services)
}
```

### Check existence of a queue and specify credentials with region endpoint
### Check the existence of a queue and specify credentials with region endpoint

```csharp
public void ConfigureServices(IServiceCollection services)
Expand All @@ -72,3 +72,18 @@ public void ConfigureServices(IServiceCollection services)
});
}
```

### Check the existence of a queue and specify service URL

```csharp
public void ConfigureServices(IServiceCollection services)
{
services
.AddHealthChecks()
.AddSqs(options =>
{
options.AddQueue("queueName");
options.ServiceURL = "http://localhost:4566";
});
}
```
37 changes: 22 additions & 15 deletions src/HealthChecks.Aws.Sqs/SqsHealthCheck.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ namespace HealthChecks.Aws.Sqs;

public class SqsHealthCheck : IHealthCheck
{
private readonly SqsOptions _sqsOptions;
private readonly SqsOptions _options;

public SqsHealthCheck(SqsOptions sqsOptions)
{
_sqsOptions = Guard.ThrowIfNull(sqsOptions);
_options = Guard.ThrowIfNull(sqsOptions);
}

/// <inheritdoc />
Expand All @@ -18,29 +18,36 @@ public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context
try
{
using var client = CreateSqsClient();
foreach (var queueName in _sqsOptions.Queues)

foreach (string queueName in _options.Queues)
{
_ = await client.GetQueueUrlAsync(queueName).ConfigureAwait(false);
await client.GetQueueUrlAsync(queueName, cancellationToken).ConfigureAwait(false);
}

return HealthCheckResult.Healthy();
}
catch (Exception ex)
catch (Exception e)
{
return new HealthCheckResult(context.Registration.FailureStatus, exception: ex);
return new HealthCheckResult(context.Registration.FailureStatus, exception: e);
}
}

private IAmazonSQS CreateSqsClient()
private AmazonSQSClient CreateSqsClient()
{
var credentialsProvided = _sqsOptions.Credentials is not null;
var regionProvided = _sqsOptions.RegionEndpoint is not null;
return (credentialsProvided, regionProvided) switch
var config = new AmazonSQSConfig();

if (_options.ServiceURL is not null)
{
(false, false) => new AmazonSQSClient(),
(false, true) => new AmazonSQSClient(_sqsOptions.RegionEndpoint),
(true, false) => new AmazonSQSClient(_sqsOptions.Credentials),
(true, true) => new AmazonSQSClient(_sqsOptions.Credentials, _sqsOptions.RegionEndpoint)
};
config.ServiceURL = _options.ServiceURL;
}

if (_options.RegionEndpoint is not null)
{
config.RegionEndpoint = _options.RegionEndpoint;
}

return _options.Credentials is not null
? new AmazonSQSClient(_options.Credentials, config)
: new AmazonSQSClient(config);
}
}
6 changes: 5 additions & 1 deletion src/HealthChecks.Aws.Sqs/SqsOptions.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Diagnostics.CodeAnalysis;
using Amazon;
using Amazon.Runtime;

Expand All @@ -8,11 +9,14 @@ namespace HealthChecks.Aws.Sqs;
/// </summary>
public class SqsOptions
{
[SuppressMessage("ReSharper", "InconsistentNaming")]
public string? ServiceURL { get; set; }

public AWSCredentials? Credentials { get; set; }

public RegionEndpoint? RegionEndpoint { get; set; }

internal HashSet<string> Queues { get; } = new HashSet<string>();
internal HashSet<string> Queues { get; } = [];

/// <summary>
/// Add an AWS SQS queue to be checked.
Expand Down
175 changes: 175 additions & 0 deletions test/HealthChecks.Aws.Sqs.Tests/Functional/SqsHealthCheckTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
using System.Net;
using Amazon.Runtime;

namespace HealthChecks.Aws.Sqs.Tests.Functional;

public class aws_sqs_healthcheck_should(LocalStackContainerFixture localStackFixture) : IClassFixture<LocalStackContainerFixture>
{
[Fact]
public async Task be_healthy_if_aws_sqs_queue_is_available()
{
string connectionString = localStackFixture.GetConnectionString();

var webHostBuilder = new WebHostBuilder()
.ConfigureServices(services =>
{
services.AddHealthChecks()
.AddSqs(
options =>
{
options.Credentials = new BasicAWSCredentials("test", "test");
options.ServiceURL = connectionString;

options.AddQueue("healthchecks");
},
tags: ["sqs"]);
})
.Configure(app =>
{
app.UseHealthChecks("/health", new HealthCheckOptions
{
Predicate = r => r.Tags.Contains("sqs")
});
});

using var server = new TestServer(webHostBuilder);

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

response.StatusCode.ShouldBe(HttpStatusCode.OK);
}

[Fact]
public async Task be_healthy_if_aws_sqs_multiple_queues_are_available()
{
string connectionString = localStackFixture.GetConnectionString();

var webHostBuilder = new WebHostBuilder()
.ConfigureServices(services =>
{
services.AddHealthChecks()
.AddSqs(
options =>
{
options.Credentials = new BasicAWSCredentials("test", "test");
options.ServiceURL = connectionString;

options.AddQueue("healthchecks");
options.AddQueue("healthchecks");
},
tags: ["sqs"]);
})
.Configure(app =>
{
app.UseHealthChecks("/health", new HealthCheckOptions
{
Predicate = r => r.Tags.Contains("sqs")
});
});

using var server = new TestServer(webHostBuilder);

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

response.StatusCode.ShouldBe(HttpStatusCode.OK);
}

[Fact]
public async Task be_unhealthy_if_aws_sqs_is_unavailable()
{
var webHostBuilder = new WebHostBuilder()
.ConfigureServices(services =>
{
services.AddHealthChecks()
.AddSqs(
options =>
{
options.Credentials = new BasicAWSCredentials("test", "test");
options.ServiceURL = "invalid";

options.AddQueue("healthchecks");
},
tags: ["sqs"]);
})
.Configure(app =>
{
app.UseHealthChecks("/health", new HealthCheckOptions
{
Predicate = r => r.Tags.Contains("sqs")
});
});

using var server = new TestServer(webHostBuilder);

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

response.StatusCode.ShouldBe(HttpStatusCode.ServiceUnavailable);
}

[Fact]
public async Task be_unhealthy_if_aws_sqs_credentials_not_provided()
{
string connectionString = localStackFixture.GetConnectionString();

var webHostBuilder = new WebHostBuilder()
.ConfigureServices(services =>
{
services.AddHealthChecks()
.AddSqs(
options =>
{
options.ServiceURL = connectionString;

options.AddQueue("healthchecks");
},
tags: ["sqs"]);
})
.Configure(app =>
{
app.UseHealthChecks("/health", new HealthCheckOptions
{
Predicate = r => r.Tags.Contains("sqs")
});
});

using var server = new TestServer(webHostBuilder);

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

response.StatusCode.ShouldBe(HttpStatusCode.ServiceUnavailable);
}

[Fact]
public async Task be_unhealthy_if_aws_sqs_queue_does_not_exist()
{
string connectionString = localStackFixture.GetConnectionString();

var webHostBuilder = new WebHostBuilder()
.ConfigureServices(services =>
{
services.AddHealthChecks()
.AddSqs(
options =>
{
options.Credentials = new BasicAWSCredentials("test", "test");
options.ServiceURL = connectionString;

options.AddQueue("invalid");
},
tags: ["sqs"]);
})
.Configure(app =>
{
app.UseHealthChecks("/health", new HealthCheckOptions
{
Predicate = r => r.Tags.Contains("sqs")
});
});

using var server = new TestServer(webHostBuilder);

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

response.StatusCode.ShouldBe(HttpStatusCode.ServiceUnavailable);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,8 @@
<ProjectReference Include="..\..\src\HealthChecks.Aws.Sqs\HealthChecks.Aws.Sqs.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Testcontainers.LocalStack" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ namespace HealthChecks.Aws.Sqs
public SqsOptions() { }
public Amazon.Runtime.AWSCredentials? Credentials { get; set; }
public Amazon.RegionEndpoint? RegionEndpoint { get; set; }
public string? ServiceURL { get; set; }
public HealthChecks.Aws.Sqs.SqsOptions AddQueue(string queueName) { }
}
}
Expand Down
44 changes: 44 additions & 0 deletions test/HealthChecks.Aws.Sqs.Tests/LocalStackContainerFixture.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using Testcontainers.LocalStack;

namespace HealthChecks.Aws.Sqs.Tests;

public class LocalStackContainerFixture : IAsyncLifetime
{
private const string Registry = "docker.io";

private const string Image = "localstack/localstack";

private const string Tag = "4.7.0";

public LocalStackContainer? Container { get; private set; }

public string GetConnectionString()
{
if (Container is null)
{
throw new InvalidOperationException("The test container was not initialized.");
}

return Container.GetConnectionString();
}

public async Task InitializeAsync()
{
Container = await CreateContainerAsync();

await Container.ExecAsync(["awslocal", "sqs", "create-queue", "--queue-name", "healthchecks"]);
}

public Task DisposeAsync() => Container?.DisposeAsync().AsTask() ?? Task.CompletedTask;

private static async Task<LocalStackContainer> CreateContainerAsync()
{
var container = new LocalStackBuilder()
.WithImage($"{Registry}/{Image}:{Tag}")
.Build();

await container.StartAsync();

return container;
}
}