diff --git a/.github/workflows/sdk_build.yml b/.github/workflows/sdk_build.yml
index 433a0735a..cdf2b241e 100644
--- a/.github/workflows/sdk_build.yml
+++ b/.github/workflows/sdk_build.yml
@@ -208,6 +208,7 @@ jobs:
name: ${{ matrix.prefix }}-${{ matrix['dapr-runtime-versions'].version }}-${{ matrix.projectName }}
needs: [ compute-integration-matrix ]
runs-on: ${{ matrix.os }}
+ timeout-minutes: 30
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.compute-integration-matrix.outputs.matrix) }}
diff --git a/src/Dapr.Testcontainers/Containers/Dapr/DaprPlacementContainer.cs b/src/Dapr.Testcontainers/Containers/Dapr/DaprPlacementContainer.cs
index 8be913db2..ae568a914 100644
--- a/src/Dapr.Testcontainers/Containers/Dapr/DaprPlacementContainer.cs
+++ b/src/Dapr.Testcontainers/Containers/Dapr/DaprPlacementContainer.cs
@@ -12,11 +12,14 @@
// ------------------------------------------------------------------------
using System;
+using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Dapr.Testcontainers.Common;
using Dapr.Testcontainers.Common.Options;
+using Docker.DotNet.Models;
using DotNet.Testcontainers.Builders;
+using DotNet.Testcontainers.Configurations;
using DotNet.Testcontainers.Containers;
using DotNet.Testcontainers.Networks;
@@ -44,9 +47,13 @@ public sealed class DaprPlacementContainer : IAsyncStartable
///
public int ExternalPort { get; private set; }
///
- /// THe contains' internal port.
+ /// The container's internal port.
///
public const int InternalPort = 50006;
+ ///
+ /// The container's internal health port.
+ ///
+ private const int HealthPort = 8080;
///
/// Initializes a new instance of the .
@@ -59,14 +66,24 @@ public DaprPlacementContainer(DaprRuntimeOptions options, INetwork network, stri
_logAttachment = ContainerLogAttachment.TryCreate(logDirectory, "placement", _containerName);
//Placement service runs via port 50006
- var containerBuilder = new ContainerBuilder()
- .WithImage(options.PlacementImageTag)
- .WithName(_containerName)
+ var containerBuilder = new ContainerBuilder()
+ .WithImage(options.PlacementImageTag)
+ .WithName(_containerName)
.WithNetwork(network)
- .WithCommand("./placement", "-port", InternalPort.ToString())
- .WithPortBinding(InternalPort, assignRandomHostPort: true)
- .WithWaitStrategy(Wait.ForUnixContainer().UntilMessageIsLogged("placement server leadership acquired"))
- ;
+ .WithCommand("./placement", "-port", InternalPort.ToString())
+ .WithPortBinding(InternalPort, assignRandomHostPort: true)
+ .WithPortBinding(HealthPort, assignRandomHostPort: true)
+ .WithWaitStrategy(Wait.ForUnixContainer()
+ .UntilHttpRequestIsSucceeded(endpoint =>
+ endpoint
+ .ForPort(HealthPort)
+ .ForPath("/healthz")
+ .ForStatusCodeMatching(code => (int)code >= 200 && (int)code < 300),
+ mod =>
+ mod
+ .WithTimeout(TimeSpan.FromMinutes(2))
+ .WithInterval(TimeSpan.FromSeconds(5))
+ .WithMode(WaitStrategyMode.Running)));
if (_logAttachment is not null)
{
diff --git a/src/Dapr.Testcontainers/Containers/Dapr/DaprSchedulerContainer.cs b/src/Dapr.Testcontainers/Containers/Dapr/DaprSchedulerContainer.cs
index bb81003ec..c0499e9c9 100644
--- a/src/Dapr.Testcontainers/Containers/Dapr/DaprSchedulerContainer.cs
+++ b/src/Dapr.Testcontainers/Containers/Dapr/DaprSchedulerContainer.cs
@@ -13,6 +13,8 @@
using System;
using System.Linq;
+using System.Net;
+using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Dapr.Testcontainers.Common;
@@ -52,6 +54,10 @@ public sealed class DaprSchedulerContainer : IAsyncStartable
/// The container's internal port.
///
public const int InternalPort = 51005;
+ ///
+ /// The container's internal health port.
+ ///
+ private const int HealthPort = 8080;
///
/// Creates a new instance of a .
@@ -70,17 +76,27 @@ public DaprSchedulerContainer(DaprRuntimeOptions options, INetwork network, stri
];
_testDirectory = TestDirectoryManager.CreateTestDirectory("scheduler");
-
+
var containerBuilder = new ContainerBuilder()
.WithImage(options.SchedulerImageTag)
- .WithName(_containerName)
+ .WithName(_containerName)
.WithNetwork(network)
.WithCommand(cmd.ToArray())
- .WithPortBinding(InternalPort, assignRandomHostPort: true)
+ .WithPortBinding(InternalPort, assignRandomHostPort: true)
+ .WithPortBinding(HealthPort, assignRandomHostPort: true) // Allows probes to reach healthz
// Mount an anonymous volume to /data to ensure the scheduler has write permissions
.WithBindMount(_testDirectory, containerDataDir, AccessMode.ReadWrite)
- .WithWaitStrategy(Wait.ForUnixContainer().UntilMessageIsLogged("api is ready"))
- ;
+ .WithWaitStrategy(Wait.ForUnixContainer()
+ .UntilHttpRequestIsSucceeded(endpoint =>
+ endpoint
+ .ForPort(HealthPort)
+ .ForPath("/healthz")
+ .ForStatusCodeMatching(code => (int)code >= 200 && (int)code < 300),
+ mod =>
+ mod
+ .WithTimeout(TimeSpan.FromMinutes(2))
+ .WithInterval(TimeSpan.FromSeconds(5))
+ .WithMode(WaitStrategyMode.Running)));
if (_logAttachment is not null)
{
diff --git a/src/Dapr.Testcontainers/Containers/Dapr/DaprdContainer.cs b/src/Dapr.Testcontainers/Containers/Dapr/DaprdContainer.cs
index 77018531b..b5965cb23 100644
--- a/src/Dapr.Testcontainers/Containers/Dapr/DaprdContainer.cs
+++ b/src/Dapr.Testcontainers/Containers/Dapr/DaprdContainer.cs
@@ -13,6 +13,7 @@
using System;
using System.Collections.Generic;
+using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
@@ -33,9 +34,10 @@ public sealed class DaprdContainer : IAsyncStartable
{
private const int InternalHttpPort = 3500;
private const int InternalGrpcPort = 50001;
+ private const int InternalHealthPort = 8080;
private readonly IContainer _container;
private readonly ContainerLogAttachment? _logAttachment;
- private string _containerName = $"dapr-{Guid.NewGuid():N}";
+ private readonly string _containerName = $"dapr-{Guid.NewGuid():N}";
///
/// The internal network alias/name of the container.
@@ -123,57 +125,74 @@ public DaprdContainer(
cmd.Add("-scheduler-host-address");
cmd.Add("");
}
-
- var containerBuilder = new ContainerBuilder()
- .WithImage(options.RuntimeImageTag)
- .WithName(_containerName)
+
+ var containerBuilder = new ContainerBuilder()
+ .WithImage(options.RuntimeImageTag)
+ .WithName(_containerName)
.WithLogger(ConsoleLogger.Instance)
- .WithCommand(cmd.ToArray())
+ .WithCommand(cmd.ToArray())
.WithNetwork(network)
.WithExtraHost(ContainerHostAlias, "host-gateway")
- .WithBindMount(componentsHostFolder, componentsPath, AccessMode.ReadOnly)
- .WithWaitStrategy(Wait.ForUnixContainer()
+ .WithBindMount(componentsHostFolder, componentsPath, AccessMode.ReadOnly)
+ .WithWaitStrategy(Wait.ForUnixContainer()
.UntilMessageIsLogged("Internal gRPC server is running"));
- //.UntilMessageIsLogged(@"^dapr initialized. Status: Running. Init Elapsed "))
+ // .UntilHttpRequestIsSucceeded(endpoint =>
+ // endpoint
+ // .ForPort(InternalHttpPort)
+ // .ForPath("/healthz")
+ // .ForStatusCodeMatching(code => (int)code >= 200 && (int)code < 300),
+ // mod =>
+ // mod
+ // .WithTimeout(TimeSpan.FromMinutes(2))
+ // .WithInterval(TimeSpan.FromSeconds(5))
+ // .WithMode(WaitStrategyMode.Running)));
if (_logAttachment is not null)
{
containerBuilder = containerBuilder.WithOutputConsumer(_logAttachment.OutputConsumer);
}
- containerBuilder = daprHttpPort is not null ? containerBuilder.WithPortBinding(containerPort: InternalHttpPort, hostPort: daprHttpPort.Value) : containerBuilder.WithPortBinding(port: InternalHttpPort, assignRandomHostPort: true);
- containerBuilder = daprGrpcPort is not null ? containerBuilder.WithPortBinding(containerPort: InternalGrpcPort, hostPort: daprGrpcPort.Value) : containerBuilder.WithPortBinding(port: InternalGrpcPort, assignRandomHostPort: true);
+ containerBuilder = daprHttpPort is not null ? containerBuilder.WithPortBinding(containerPort: InternalHttpPort, hostPort: daprHttpPort.Value) : containerBuilder.WithPortBinding(port: InternalHttpPort, assignRandomHostPort: true);
+ containerBuilder = daprGrpcPort is not null ? containerBuilder.WithPortBinding(containerPort: InternalGrpcPort, hostPort: daprGrpcPort.Value) : containerBuilder.WithPortBinding(port: InternalGrpcPort, assignRandomHostPort: true);
- _container = containerBuilder.Build();
+ _container = containerBuilder.Build();
}
///
public async Task StartAsync(CancellationToken cancellationToken = default)
{
- await _container.StartAsync(cancellationToken);
+ try
+ {
+ await _container.StartAsync(cancellationToken);
- var mappedHttpPort = _container.GetMappedPublicPort(InternalHttpPort);
- var mappedGrpcPort = _container.GetMappedPublicPort(InternalGrpcPort);
+ var mappedHttpPort = _container.GetMappedPublicPort(InternalHttpPort);
+ var mappedGrpcPort = _container.GetMappedPublicPort(InternalGrpcPort);
- if (_requestedHttpPort is not null && mappedHttpPort != _requestedHttpPort.Value)
- {
- throw new InvalidOperationException(
- $"Dapr HTTP port mapping mismatch. Requested {_requestedHttpPort.Value}, but Docker mapped {mappedHttpPort}");
- }
+ if (_requestedHttpPort is not null && mappedHttpPort != _requestedHttpPort.Value)
+ {
+ throw new InvalidOperationException(
+ $"Dapr HTTP port mapping mismatch. Requested {_requestedHttpPort.Value}, but Docker mapped {mappedHttpPort}");
+ }
- if (_requestedGrpcPort is not null && mappedGrpcPort != _requestedGrpcPort.Value)
- {
- throw new InvalidOperationException(
- $"Dapr gRPC port mapping mismatch. Requested {_requestedGrpcPort.Value}, but Docker mapped {mappedGrpcPort}");
- }
+ if (_requestedGrpcPort is not null && mappedGrpcPort != _requestedGrpcPort.Value)
+ {
+ throw new InvalidOperationException(
+ $"Dapr gRPC port mapping mismatch. Requested {_requestedGrpcPort.Value}, but Docker mapped {mappedGrpcPort}");
+ }
- HttpPort = mappedHttpPort;
- GrpcPort = mappedGrpcPort;
+ HttpPort = mappedHttpPort;
+ GrpcPort = mappedGrpcPort;
- // The container log wait strategy can fire before the host port is actually accepting connections
- // (especially on Windows). Ensure the ports are reachable from the test process.
- await WaitForTcpPortAsync("127.0.0.1", HttpPort, TimeSpan.FromSeconds(30), cancellationToken);
- await WaitForTcpPortAsync("127.0.0.1", GrpcPort, TimeSpan.FromSeconds(30), cancellationToken);
+ // The container log wait strategy can fire before the host port is actually accepting connections
+ // (especially on Windows). Ensure the ports are reachable from the test process.
+ await WaitForTcpPortAsync("127.0.0.1", HttpPort, TimeSpan.FromSeconds(30), cancellationToken);
+ await WaitForTcpPortAsync("127.0.0.1", GrpcPort, TimeSpan.FromSeconds(30), cancellationToken);
+ }
+ catch (Exception ex)
+ {
+ var msg = ex.Message;
+ throw;
+ }
}
private static async Task WaitForTcpPortAsync(
diff --git a/src/Dapr.Testcontainers/Harnesses/BaseHarness.cs b/src/Dapr.Testcontainers/Harnesses/BaseHarness.cs
index 93b6383af..240d6e463 100644
--- a/src/Dapr.Testcontainers/Harnesses/BaseHarness.cs
+++ b/src/Dapr.Testcontainers/Harnesses/BaseHarness.cs
@@ -229,7 +229,7 @@ DaprSchedulerExternalPort is null || DaprSchedulerAlias is null
await _daprd!.StartAsync(cancellationToken);
_sidecarPortsReady.TrySetResult();
}, cancellationToken);
-
+
Task? appTask = null;
if (startApp is not null)
{