Skip to content

Commit

Permalink
WaitFor for Elasticsearch (#5725)
Browse files Browse the repository at this point in the history
* Add waitfor Elasticsearch

* Bump timeout for Aspire.Hosting.Elasticsearch.Tests to 40 mins

* Bump timeout for Aspire.Hosting.Elasticsearch.Tests to 60 mins
  • Loading branch information
Alirexaa committed Sep 17, 2024
1 parent 3a4528a commit f177eb5
Show file tree
Hide file tree
Showing 7 changed files with 80 additions and 4 deletions.
1 change: 1 addition & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<LangVersion>preview</LangVersion>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<SharedDir>$(MSBuildThisFileDirectory)/src/Shared/</SharedDir>
<ComponentsDir>$(MSBuildThisFileDirectory)/src/Components/</ComponentsDir>
<TestsSharedDir>$([MSBuild]::NormalizeDirectory($(MSBuildThisFileDirectory), 'tests', 'Shared'))</TestsSharedDir>
<TestsSharedRepoTestingDir>$([MSBuild]::NormalizeDirectory($(TestsSharedDir), 'RepoTesting'))</TestsSharedRepoTestingDir>
<VendoringDir>$(MSBuildThisFileDirectory)/src/Vendoring/</VendoringDir>
Expand Down
3 changes: 2 additions & 1 deletion playground/Elasticsearch/Elasticsearch.AppHost/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
.WithDataVolume();

builder.AddProject<Projects.Elasticsearch_ApiService>("elasticsearch-apiservice")
.WithReference(elasticsearch);
.WithReference(elasticsearch)
.WaitFor(elasticsearch);

#if !SKIP_DASHBOARD_REFERENCE
// This project is only added in playground projects to support development/debugging
Expand Down
1 change: 1 addition & 0 deletions spelling.dic
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,4 @@ uris
urls
kubernetes
Pgweb
elasticsearch
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@

<ItemGroup>
<Compile Include="$(SharedDir)VolumeNameGenerator.cs" Link="Utils\VolumeNameGenerator.cs" />
<Compile Include="$(ComponentsDir)Aspire.Elastic.Clients.Elasticsearch\ElasticsearchHealthCheck.cs" Link="ElasticsearchHealthCheck.cs"></Compile>
</ItemGroup>

<ItemGroup>
<InternalsVisibleTo Include="Aspire.Hosting.Elasticsearch.Tests" />
<PackageReference Include="Elastic.Clients.Elasticsearch" />
</ItemGroup>

<ItemGroup>
Expand Down
33 changes: 31 additions & 2 deletions src/Aspire.Hosting.Elasticsearch/ElasticsearchBuilderExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Aspire.Elastic.Clients.Elasticsearch;
using Aspire.Hosting.ApplicationModel;
using Aspire.Hosting.Elasticsearch;
using Aspire.Hosting.Utils;
using Elastic.Clients.Elasticsearch;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks;

namespace Aspire.Hosting;

Expand All @@ -19,7 +23,7 @@ public static class ElasticsearchBuilderExtensions
/// Adds an Elasticsearch container resource to the application model.
/// </summary>
/// <remarks>
/// The default image is "elasticsearch" and the tag is "8.14.0".
/// The default image is "elasticsearch" and the tag is "8.15.1".
/// </remarks>
/// <param name="builder">The <see cref="IDistributedApplicationBuilder"/>.</param>
/// <param name="name">The name of the resource. This name will be used as the connection string name when referenced in a dependency.</param>
Expand Down Expand Up @@ -51,6 +55,30 @@ public static IResourceBuilder<ElasticsearchResource> AddElasticsearch(

var elasticsearch = new ElasticsearchResource(name, passwordParameter);

string? connectionString = null;
ElasticsearchClient? elasticsearchClient = null;

builder.Eventing.Subscribe<ConnectionStringAvailableEvent>(elasticsearch, async (@event, ct) =>
{
connectionString = await elasticsearch.ConnectionStringExpression.GetValueAsync(ct).ConfigureAwait(false);
if (connectionString is null)
{
throw new DistributedApplicationException($"ConnectionStringAvailableEvent was published for the '{elasticsearch.Name}' resource but the connection string was null.");
}
elasticsearchClient = new ElasticsearchClient(new Uri(connectionString));
});

var healthCheckKey = $"{name}_check";
// todo: Use health check from AspNetCore.Diagnostics.HealthChecks once following PR released:
// https://github.com/Xabaril/AspNetCore.Diagnostics.HealthChecks/pull/2244
builder.Services.AddHealthChecks()
.Add(new HealthCheckRegistration(
healthCheckKey,
sp => new ElasticsearchHealthCheck(elasticsearchClient!),
failureStatus: default,
tags: default,
timeout: default));

return builder.AddResource(elasticsearch)
.WithImage(ElasticsearchContainerImageTags.Image, ElasticsearchContainerImageTags.Tag)
.WithImageRegistry(ElasticsearchContainerImageTags.Registry)
Expand All @@ -61,7 +89,8 @@ public static IResourceBuilder<ElasticsearchResource> AddElasticsearch(
.WithEnvironment(context =>
{
context.EnvironmentVariables["ELASTIC_PASSWORD"] = elasticsearch.PasswordParameter;
});
})
.WithHealthCheck(healthCheckKey);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
// The .NET Foundation licenses this file to you under the MIT license.

using Aspire.Components.Common.Tests;
using Aspire.Hosting.ApplicationModel;
using Aspire.Hosting.Utils;
using Elastic.Clients.Elasticsearch;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.Extensions.Hosting;
using Polly;
using Xunit;
Expand Down Expand Up @@ -199,6 +201,46 @@ await pipeline.ExecuteAsync(
}
}

[Fact]
[RequiresDocker]
public async Task VerifyWaitForOnElasticsearchBlocksDependentResources()
{
var cts = new CancellationTokenSource(TimeSpan.FromMinutes(10));
using var builder = TestDistributedApplicationBuilder.CreateWithTestContainerRegistry(testOutputHelper);

var healthCheckTcs = new TaskCompletionSource<HealthCheckResult>();
builder.Services.AddHealthChecks().AddAsyncCheck("blocking_check", () =>
{
return healthCheckTcs.Task;
});

var resource = builder.AddElasticsearch("resource")
.WithHealthCheck("blocking_check");

var dependentResource = builder.AddElasticsearch("dependentresource")
.WaitFor(resource);

using var app = builder.Build();

var pendingStart = app.StartAsync(cts.Token);

var rns = app.Services.GetRequiredService<ResourceNotificationService>();

await rns.WaitForResourceAsync(resource.Resource.Name, KnownResourceStates.Running, cts.Token);

await rns.WaitForResourceAsync(dependentResource.Resource.Name, KnownResourceStates.Waiting, cts.Token);

healthCheckTcs.SetResult(HealthCheckResult.Healthy());

await rns.WaitForResourceAsync(resource.Resource.Name, (re => re.Snapshot.HealthStatus == Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus.Healthy), cts.Token);

await rns.WaitForResourceAsync(dependentResource.Resource.Name, KnownResourceStates.Running, cts.Token);

await pendingStart;

await app.StopAsync();
}

private static async Task CreateTestData(ElasticsearchClient elasticsearchClient, ITestOutputHelper testOutputHelper, CancellationToken cancellationToken)
{
var indexResponse = await elasticsearchClient.IndexAsync<Person>(s_person, IndexName, s_person.Id, cancellationToken);
Expand Down
2 changes: 1 addition & 1 deletion tests/helix/send-to-helix-basictests.targets
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
<!-- runsettings timeout in ms, default to 15 mins -->
<_DefaultWorkItems TimeoutMs="900000" />

<_DefaultWorkItems Condition="'%(FileName)' == 'Aspire.Hosting.Elasticsearch.Tests'" TimeoutMs="1200000" />
<_DefaultWorkItems Condition="'%(FileName)' == 'Aspire.Hosting.Elasticsearch.Tests'" TimeoutMs="3600000" />
<_DefaultWorkItems Condition="'%(FileName)' == 'Aspire.Hosting.Oracle.Tests'" TimeoutMs="1200000" />
<_DefaultWorkItems Condition="'%(FileName)' == 'Aspire.Pomelo.EntityFrameworkCore.MySql.Tests'" TimeoutMs="1200000" />

Expand Down

0 comments on commit f177eb5

Please sign in to comment.