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
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Net.Sockets;
using static CommunityToolkit.Aspire.Hosting.Dapr.CommandLineArgs;

Expand Down Expand Up @@ -240,8 +239,8 @@ private async Task OnBeforeStartAsync(BeforeStartEvent @event, CancellationToken
var http = daprCli.GetEndpoint("http");
var grpc = daprCli.GetEndpoint("grpc");

context.EnvironmentVariables.TryAdd("DAPR_HTTP_PORT", http.Port.ToString(CultureInfo.InvariantCulture));
context.EnvironmentVariables.TryAdd("DAPR_GRPC_PORT", grpc.Port.ToString(CultureInfo.InvariantCulture));
context.EnvironmentVariables.TryAdd("DAPR_HTTP_PORT", http.Property(EndpointProperty.Port));
context.EnvironmentVariables.TryAdd("DAPR_GRPC_PORT", grpc.Property(EndpointProperty.Port));

context.EnvironmentVariables.TryAdd("DAPR_GRPC_ENDPOINT", grpc);
context.EnvironmentVariables.TryAdd("DAPR_HTTP_ENDPOINT", http);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Aspire.Hosting;
using Aspire.Hosting.Utils;
using CommunityToolkit.Aspire.Hosting.Dapr;
using System.Runtime.CompilerServices;

namespace CommunityToolkit.Aspire.Hosting.Azure.Dapr.Tests;

public class DaprAzureContainerAppEnvironmentTests
{
// End-to-end guard for microsoft/aspire#18242 in the topology that triggers it:
// AddAzureContainerAppEnvironment (which contributes the azure-prepare-resources walk) plus a
// Dapr sidecar. Invokes the preparer's core operation directly so the test needs no Docker or
// provisioning; the non-Azure Dapr.Tests test is the primary deterministic guard.
[Fact]
public async Task PrepareResourcesWalk_DoesNotThrow_WithAzureEnvironmentAndDaprSidecar()
{
using var builder = TestDistributedApplicationBuilder.Create();

// Fake path so the Dapr lifecycle hook does not fail locating the Dapr CLI.
builder.AddDapr(o => o.DaprPath = "dapr");
builder.AddAzureContainerAppEnvironment("cae");

var worker = builder.AddContainer("worker", "image")
.WithDaprSidecar();

using var app = builder.Build();

await ExecuteBeforeStartHooksAsync(app, default);

var executionContext = new DistributedApplicationExecutionContext(DistributedApplicationOperation.Run);

var exception = await Record.ExceptionAsync(() =>
worker.Resource.GetResourceDependenciesAsync(executionContext, ResourceDependencyDiscoveryMode.DirectOnly));

Assert.Null(exception);
}

[UnsafeAccessor(UnsafeAccessorKind.Method, Name = "ExecuteBeforeStartHooksAsync")]
private static extern Task ExecuteBeforeStartHooksAsync(DistributedApplication app, CancellationToken cancellationToken);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Aspire.Hosting;
using Aspire.Hosting.Utils;
using System.Runtime.CompilerServices;

namespace CommunityToolkit.Aspire.Hosting.Dapr.Tests;

public class DaprSidecarEndpointAllocationTests
{
// Regression test for microsoft/aspire#18242: the Dapr env callback must not eagerly read the
// sidecar CLI endpoint Port before allocation. GetResourceDependenciesAsync(DirectOnly) is the
// operation AzureResourcePreparer runs per compute resource, and reproduces the crash without Azure.
[Fact]
public async Task AppEnvironmentCallback_DoesNotThrow_WhenDaprSidecarEndpointsNotAllocated()
{
using var builder = TestDistributedApplicationBuilder.Create();

// Fake path so the lifecycle hook does not fail locating the Dapr CLI.
builder.AddDapr(o => o.DaprPath = "dapr");

var container = builder.AddContainer("worker", "image")
.WithDaprSidecar();

using var app = builder.Build();

// Runs the Dapr hook (creates "worker-dapr-cli" and attaches the callback); endpoints stay unallocated.
await ExecuteBeforeStartHooksAsync(app, default);

var executionContext = new DistributedApplicationExecutionContext(DistributedApplicationOperation.Run);

var dependencies = await container.Resource.GetResourceDependenciesAsync(
executionContext,
ResourceDependencyDiscoveryMode.DirectOnly);

Assert.Contains(dependencies, r => r.Name == "worker-dapr-cli");

// Fire the callback directly (do not resolve - a deferred port blocks until allocation) and
// assert the ports are recorded as deferred providers rather than eagerly-read values.
var callbackContext = new EnvironmentCallbackContext(executionContext, container.Resource);
foreach (var callback in container.Resource.Annotations.OfType<EnvironmentCallbackAnnotation>())
{
await callback.Callback(callbackContext);
}

Assert.True(callbackContext.EnvironmentVariables.ContainsKey("DAPR_HTTP_PORT"));
Assert.True(callbackContext.EnvironmentVariables.ContainsKey("DAPR_GRPC_PORT"));
Assert.IsAssignableFrom<IValueProvider>(callbackContext.EnvironmentVariables["DAPR_HTTP_PORT"]);
Assert.IsAssignableFrom<IValueProvider>(callbackContext.EnvironmentVariables["DAPR_GRPC_PORT"]);
}

[UnsafeAccessor(UnsafeAccessorKind.Method, Name = "ExecuteBeforeStartHooksAsync")]
private static extern Task ExecuteBeforeStartHooksAsync(DistributedApplication app, CancellationToken cancellationToken);
}
Loading