Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,9 @@ public static IResourceBuilder<DevTunnelResource> AddDevTunnel(
var exception = new DistributedApplicationException($"Error trying to create the dev tunnel resource '{tunnelResource.TunnelId}' this port belongs to: {ex.Message}", ex);
foreach (var portResource in tunnelResource.Ports)
{
#pragma warning disable CS0618 // Type or member is obsolete
portResource.TunnelEndpointAnnotation.AllocatedEndpointSnapshot.SetException(exception);
#pragma warning restore CS0618 // Type or member is obsolete
}
throw;
}
Expand Down Expand Up @@ -209,7 +211,9 @@ await notifications.PublishUpdateAsync(portResource, snapshot => snapshot with
catch (Exception ex)
{
portLogger.LogError(ex, "Error trying to create dev tunnel port '{Port}' on tunnel '{Tunnel}': {Error}", portResource.TargetEndpoint.Port, portResource.DevTunnel.TunnelId, ex.Message);
#pragma warning disable CS0618 // Type or member is obsolete
portResource.TunnelEndpointAnnotation.AllocatedEndpointSnapshot.SetException(ex);
#pragma warning restore CS0618 // Type or member is obsolete
throw;
}

Expand Down
48 changes: 48 additions & 0 deletions src/Aspire.Hosting/ApplicationModel/EndpointAnnotation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,9 @@ public EndpointAnnotation(
IsExternal = isExternal ?? false;
IsProxied = isProxied;
_networkID = networkID ?? KnownNetworkIdentifiers.LocalhostNetwork;
#pragma warning disable CS0618 // Type or member is obsolete
AllAllocatedEndpoints.TryAdd(_networkID, AllocatedEndpointSnapshot);
#pragma warning restore CS0618 // Type or member is obsolete
}

/// <summary>
Expand Down Expand Up @@ -202,8 +204,10 @@ public string Transport
/// </summary>
public AllocatedEndpoint? AllocatedEndpoint
{
#pragma warning disable CS0618 // Type or member is obsolete (AllocatedEndpointSnapshot)
get
{

if (!AllocatedEndpointSnapshot.IsValueSet)
{
return null;
Expand All @@ -223,14 +227,20 @@ public AllocatedEndpoint? AllocatedEndpoint
}
else
{
if (_networkID != value.NetworkID)
{
throw new InvalidOperationException($"The default AllocatedEndpoint's network ID must match the EndpointAnnotation network ID ('{_networkID}'). The attempted AllocatedEndpoint belongs to '{value.NetworkID}'.");
}
AllocatedEndpointSnapshot.SetValue(value);
}
}
#pragma warning restore CS0618 // Type or member is obsolete
}

/// <summary>
/// Gets the <see cref="AllocatedEndpointSnapshot"/> for the default <see cref="AllocatedEndpoint"/>.
/// </summary>
[Obsolete("This property will be marked as internal in future Aspire release. Use AllocatedEndpoint and AllAllocatedEndpoints properties to access and change allocated endpoints associated with an EndpointAnnotation.")]
public ValueSnapshot<AllocatedEndpoint> AllocatedEndpointSnapshot { get; } = new();

/// <summary>
Expand Down Expand Up @@ -271,6 +281,7 @@ IEnumerator IEnumerable.GetEnumerator()
/// <summary>
/// Adds an AllocatedEndpoint snapshot for a specific network if one does not already exist.
/// </summary>
[Obsolete("This method is for internal use only and will be marked internal in a future Aspire release. Use AddOrUpdateAllocatedEndpoint instead.")]
public bool TryAdd(NetworkIdentifier networkID, ValueSnapshot<AllocatedEndpoint> snapshot)
{
lock (_snapshots)
Expand All @@ -283,4 +294,41 @@ public bool TryAdd(NetworkIdentifier networkID, ValueSnapshot<AllocatedEndpoint>
return true;
}
}

/// <summary>
/// Adds or updates an AllocatedEndpoint value associated with a specific network in the snapshot list.
/// </summary>
public void AddOrUpdateAllocatedEndpoint(NetworkIdentifier networkID, AllocatedEndpoint endpoint)
{
if (endpoint.NetworkID != networkID)
{
throw new ArgumentException($"AllocatedEndpoint must use the same network as the {nameof(networkID)} parameter", nameof(endpoint));
}
var nes = GetSnapshotFor(networkID);
nes.Snapshot.SetValue(endpoint);
}

/// <summary>
/// Gets an AllocatedEndpoint for a given network ID, waiting for it to appear if it is not already present.
/// </summary>
public Task<AllocatedEndpoint> GetAllocatedEndpointAsync(NetworkIdentifier networkID, CancellationToken cancellationToken = default)
{
var nes = GetSnapshotFor(networkID);
return nes.Snapshot.GetValueAsync(cancellationToken);
}

private NetworkEndpointSnapshot GetSnapshotFor(NetworkIdentifier networkID)
{
lock (_snapshots)
{
var nes = _snapshots.FirstOrDefault(s => s.NetworkID.Equals(networkID));
if (nes is null)
{
nes = new NetworkEndpointSnapshot(new ValueSnapshot<AllocatedEndpoint>(), networkID);
_snapshots.Add(nes);
}
return nes;
}
}

}
17 changes: 3 additions & 14 deletions src/Aspire.Hosting/ApplicationModel/EndpointReference.cs
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,10 @@ public EndpointReferenceExpression Property(EndpointProperty property)
/// </summary>
public string Url => AllocatedEndpoint.UriString;

#pragma warning disable CS0618 // Type or member is obsolete
internal ValueSnapshot<AllocatedEndpoint> AllocatedEndpointSnapshot =>
EndpointAnnotation.AllocatedEndpointSnapshot;
#pragma warning restore CS0618 // Type or member is obsolete

internal AllocatedEndpoint AllocatedEndpoint =>
GetAllocatedEndpoint()
Expand Down Expand Up @@ -307,21 +309,8 @@ public class EndpointReferenceExpression(EndpointReference endpointReference, En

async ValueTask<string?> ResolveValueWithAllocatedAddress()
{
// We are going to take the first snapshot that matches the context network ID. In general there might be multiple endpoints for a single service,
// and in future we might need some sort of policy to choose between them, but for now we just take the first one.
var endpointSnapshots = Endpoint.EndpointAnnotation.AllAllocatedEndpoints;
var nes = endpointSnapshots.Where(nes => nes.NetworkID == networkContext).FirstOrDefault();
if (nes is null)
{
nes = new NetworkEndpointSnapshot(new ValueSnapshot<AllocatedEndpoint>(), networkContext);
if (!endpointSnapshots.TryAdd(networkContext, nes.Snapshot))
{
// Someone else added it first, use theirs.
nes = endpointSnapshots.Where(nes => nes.NetworkID == networkContext).First();
}
}

var allocatedEndpoint = await nes.Snapshot.GetValueAsync(cancellationToken).ConfigureAwait(false);
var allocatedEndpoint = await endpointSnapshots.GetAllocatedEndpointAsync(networkContext, cancellationToken).ConfigureAwait(false);

return Property switch
{
Expand Down
8 changes: 2 additions & 6 deletions src/Aspire.Hosting/Dcp/DcpExecutor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -994,9 +994,7 @@ private void AddAllocatedEndpointInfo(IEnumerable<RenderedModelResource> resourc
targetPortExpression: $$$"""{{- portForServing "{{{svc.Metadata.Name}}}" -}}""",
KnownNetworkIdentifiers.DefaultAspireContainerNetwork
);
var snapshot = new ValueSnapshot<AllocatedEndpoint>();
snapshot.SetValue(allocatedEndpoint);
sp.EndpointAnnotation.AllAllocatedEndpoints.TryAdd(allocatedEndpoint.NetworkID, snapshot);
sp.EndpointAnnotation.AllAllocatedEndpoints.AddOrUpdateAllocatedEndpoint(allocatedEndpoint.NetworkID, allocatedEndpoint);
}
}
}
Expand Down Expand Up @@ -1056,9 +1054,7 @@ ts.Service is not null &&
targetPortExpression: $$$"""{{- portForServing "{{{ts.Service.Name}}}" -}}""",
networkID
);
var snapshot = new ValueSnapshot<AllocatedEndpoint>();
snapshot.SetValue(tunnelAllocatedEndpoint);
endpoint.AllAllocatedEndpoints.TryAdd(networkID, snapshot);
endpoint.AllAllocatedEndpoints.AddOrUpdateAllocatedEndpoint(networkID, tunnelAllocatedEndpoint);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -371,9 +371,7 @@ public async Task WithPostgresMcpOnAzureDatabaseRunAsContainerAddsMcpResource()
c.WithEndpoint("tcp", e =>
{
e.AllocatedEndpoint = new AllocatedEndpoint(e, "localhost", 5432);
var snapshot = new ValueSnapshot<AllocatedEndpoint>();
snapshot.SetValue(new AllocatedEndpoint(e, "postgres.dev.internal", 5432, EndpointBindingMode.SingleAddress, targetPortExpression: null, networkID: KnownNetworkIdentifiers.DefaultAspireContainerNetwork));
e.AllAllocatedEndpoints.TryAdd(KnownNetworkIdentifiers.DefaultAspireContainerNetwork, snapshot);
e.AllAllocatedEndpoints.AddOrUpdateAllocatedEndpoint(KnownNetworkIdentifiers.DefaultAspireContainerNetwork, new AllocatedEndpoint(e, "postgres.dev.internal", 5432, EndpointBindingMode.SingleAddress, targetPortExpression: null, networkID: KnownNetworkIdentifiers.DefaultAspireContainerNetwork));
});
});

Expand Down
10 changes: 2 additions & 8 deletions tests/Aspire.Hosting.Containers.Tests/ContainerResourceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,21 +102,15 @@ public async Task AddContainerWithArgs()
e.AllocatedEndpoint = new(e, "localhost", 1234, targetPortExpression: "1234");

// For container-container lookup we need to add an AllocatedEndpoint on the container network side
var ccae = new AllocatedEndpoint(e, "c1.dev.internal", 2234, EndpointBindingMode.SingleAddress, targetPortExpression: "2234", KnownNetworkIdentifiers.DefaultAspireContainerNetwork);
var snapshot = new ValueSnapshot<AllocatedEndpoint>();
snapshot.SetValue(ccae);
e.AllAllocatedEndpoints.TryAdd(KnownNetworkIdentifiers.DefaultAspireContainerNetwork, snapshot);
e.AllAllocatedEndpoints.AddOrUpdateAllocatedEndpoint(KnownNetworkIdentifiers.DefaultAspireContainerNetwork, new AllocatedEndpoint(e, "c1.dev.internal", 2234, EndpointBindingMode.SingleAddress, targetPortExpression: "2234", KnownNetworkIdentifiers.DefaultAspireContainerNetwork));
});

var c2 = appBuilder.AddContainer("container", "none")
.WithEndpoint("ep", e =>
{
e.UriScheme = "http";
// We only care about the container-side endpoint for this test
var snapshot = new ValueSnapshot<AllocatedEndpoint>();
var ae = new AllocatedEndpoint(e, "container.dev.internal", 5678, EndpointBindingMode.SingleAddress, targetPortExpression: "5678", KnownNetworkIdentifiers.DefaultAspireContainerNetwork);
snapshot.SetValue(ae);
e.AllAllocatedEndpoints.TryAdd(KnownNetworkIdentifiers.DefaultAspireContainerNetwork, snapshot);
e.AllAllocatedEndpoints.AddOrUpdateAllocatedEndpoint(KnownNetworkIdentifiers.DefaultAspireContainerNetwork, new AllocatedEndpoint(e, "container.dev.internal", 5678, EndpointBindingMode.SingleAddress, targetPortExpression: "5678", KnownNetworkIdentifiers.DefaultAspireContainerNetwork));
})
.WithArgs(context =>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -636,7 +636,7 @@ public async Task WithOtlpDevTunnel_CleansUpIntermediateEnvironmentVariables(Pla

foreach (var endpointAnnotation in endpointAnnotations)
{
endpointAnnotation.AllocatedEndpointSnapshot.SetValue(new AllocatedEndpoint(endpointAnnotation, "localhost", 1234));
endpointAnnotation.AllocatedEndpoint = new AllocatedEndpoint(endpointAnnotation, "localhost", 1234);
}

var envVars = await EnvironmentVariableEvaluator.GetEnvironmentVariablesAsync(
Expand Down
4 changes: 1 addition & 3 deletions tests/Aspire.Hosting.Milvus.Tests/AddMilvusTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,7 @@ public async Task MilvusClientAppWithReferenceContainsConnectionStrings()
.WithEndpoint("grpc", e =>
{
e.AllocatedEndpoint = new AllocatedEndpoint(e, "localhost", MilvusPortGrpc);
var snapshot = new ValueSnapshot<AllocatedEndpoint>();
snapshot.SetValue(new AllocatedEndpoint(e, "my-milvus.dev.internal", MilvusPortGrpc, EndpointBindingMode.SingleAddress, targetPortExpression: null, networkID: KnownNetworkIdentifiers.DefaultAspireContainerNetwork));
e.AllAllocatedEndpoints.TryAdd(KnownNetworkIdentifiers.DefaultAspireContainerNetwork, snapshot);
e.AllAllocatedEndpoints.AddOrUpdateAllocatedEndpoint(KnownNetworkIdentifiers.DefaultAspireContainerNetwork, new AllocatedEndpoint(e, "my-milvus.dev.internal", MilvusPortGrpc, EndpointBindingMode.SingleAddress, targetPortExpression: null, networkID: KnownNetworkIdentifiers.DefaultAspireContainerNetwork));
});

var projectA = appBuilder.AddProject<ProjectA>("projecta", o => o.ExcludeLaunchProfile = true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,7 @@ public async Task WithPostgresMcpOnDatabaseSetsDatabaseUriEnvironmentVariable()
.WithEndpoint("tcp", e =>
{
e.AllocatedEndpoint = new AllocatedEndpoint(e, "localhost", 5432);
var snapshot = new ValueSnapshot<AllocatedEndpoint>();
snapshot.SetValue(new AllocatedEndpoint(e, "postgres.dev.internal", 5432, EndpointBindingMode.SingleAddress, targetPortExpression: null, networkID: KnownNetworkIdentifiers.DefaultAspireContainerNetwork));
e.AllAllocatedEndpoints.TryAdd(KnownNetworkIdentifiers.DefaultAspireContainerNetwork, snapshot);
e.AllAllocatedEndpoints.AddOrUpdateAllocatedEndpoint(KnownNetworkIdentifiers.DefaultAspireContainerNetwork, new AllocatedEndpoint(e, "postgres.dev.internal", 5432, EndpointBindingMode.SingleAddress, targetPortExpression: null, networkID: KnownNetworkIdentifiers.DefaultAspireContainerNetwork));
})
.AddDatabase("db")
.WithPostgresMcp();
Expand Down
8 changes: 2 additions & 6 deletions tests/Aspire.Hosting.Qdrant.Tests/AddQdrantTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -172,16 +172,12 @@ public async Task QdrantClientAppWithReferenceContainsConnectionStrings()
.WithEndpoint("grpc", e =>
{
e.AllocatedEndpoint = new AllocatedEndpoint(e, "localhost", 6334);
var snapshot = new ValueSnapshot<AllocatedEndpoint>();
snapshot.SetValue(new AllocatedEndpoint(e, "my-qdrant.dev.internal", 6334, EndpointBindingMode.SingleAddress, targetPortExpression: null, networkID: KnownNetworkIdentifiers.DefaultAspireContainerNetwork));
e.AllAllocatedEndpoints.TryAdd(KnownNetworkIdentifiers.DefaultAspireContainerNetwork, snapshot);
e.AllAllocatedEndpoints.AddOrUpdateAllocatedEndpoint(KnownNetworkIdentifiers.DefaultAspireContainerNetwork, new AllocatedEndpoint(e, "my-qdrant.dev.internal", 6334, EndpointBindingMode.SingleAddress, targetPortExpression: null, networkID: KnownNetworkIdentifiers.DefaultAspireContainerNetwork));
})
.WithEndpoint("http", e =>
{
e.AllocatedEndpoint = new AllocatedEndpoint(e, "localhost", 6333);
var snapshot = new ValueSnapshot<AllocatedEndpoint>();
snapshot.SetValue(new AllocatedEndpoint(e, "my-qdrant.dev.internal", 6333, EndpointBindingMode.SingleAddress, targetPortExpression: null, networkID: KnownNetworkIdentifiers.DefaultAspireContainerNetwork));
e.AllAllocatedEndpoints.TryAdd(KnownNetworkIdentifiers.DefaultAspireContainerNetwork, snapshot);
e.AllAllocatedEndpoints.AddOrUpdateAllocatedEndpoint(KnownNetworkIdentifiers.DefaultAspireContainerNetwork, new AllocatedEndpoint(e, "my-qdrant.dev.internal", 6333, EndpointBindingMode.SingleAddress, targetPortExpression: null, networkID: KnownNetworkIdentifiers.DefaultAspireContainerNetwork));
});

var projectA = appBuilder.AddProject<ProjectA>("projecta", o => o.ExcludeLaunchProfile = true)
Expand Down
16 changes: 4 additions & 12 deletions tests/Aspire.Hosting.Redis.Tests/AddRedisTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -304,23 +304,17 @@ public async Task WithRedisInsightProducesCorrectEnvironmentVariables()
redis1.WithEndpoint("tcp", e =>
{
e.AllocatedEndpoint = new AllocatedEndpoint(e, "localhost", 5001);
var snapshot = new ValueSnapshot<AllocatedEndpoint>();
snapshot.SetValue(new AllocatedEndpoint(e, "myredis1.dev.internal", 5001, EndpointBindingMode.SingleAddress, targetPortExpression: null, networkID: KnownNetworkIdentifiers.DefaultAspireContainerNetwork));
e.AllAllocatedEndpoints.TryAdd(KnownNetworkIdentifiers.DefaultAspireContainerNetwork, snapshot);
e.AllAllocatedEndpoints.AddOrUpdateAllocatedEndpoint(KnownNetworkIdentifiers.DefaultAspireContainerNetwork, new AllocatedEndpoint(e, "myredis1.dev.internal", 5001, EndpointBindingMode.SingleAddress, targetPortExpression: null, networkID: KnownNetworkIdentifiers.DefaultAspireContainerNetwork));
});
redis2.WithEndpoint("tcp", e =>
{
e.AllocatedEndpoint = new AllocatedEndpoint(e, "localhost", 5002);
var snapshot = new ValueSnapshot<AllocatedEndpoint>();
snapshot.SetValue(new AllocatedEndpoint(e, "myredis2.dev.internal", 5002, EndpointBindingMode.SingleAddress, targetPortExpression: null, networkID: KnownNetworkIdentifiers.DefaultAspireContainerNetwork));
e.AllAllocatedEndpoints.TryAdd(KnownNetworkIdentifiers.DefaultAspireContainerNetwork, snapshot);
e.AllAllocatedEndpoints.AddOrUpdateAllocatedEndpoint(KnownNetworkIdentifiers.DefaultAspireContainerNetwork, new AllocatedEndpoint(e, "myredis2.dev.internal", 5002, EndpointBindingMode.SingleAddress, targetPortExpression: null, networkID: KnownNetworkIdentifiers.DefaultAspireContainerNetwork));
});
redis3.WithEndpoint("tcp", e =>
{
e.AllocatedEndpoint = new AllocatedEndpoint(e, "localhost", 5003);
var snapshot = new ValueSnapshot<AllocatedEndpoint>();
snapshot.SetValue(new AllocatedEndpoint(e, "myredis3.dev.internal", 5003, EndpointBindingMode.SingleAddress, targetPortExpression: null, networkID: KnownNetworkIdentifiers.DefaultAspireContainerNetwork));
e.AllAllocatedEndpoints.TryAdd(KnownNetworkIdentifiers.DefaultAspireContainerNetwork, snapshot);
e.AllAllocatedEndpoints.AddOrUpdateAllocatedEndpoint(KnownNetworkIdentifiers.DefaultAspireContainerNetwork, new AllocatedEndpoint(e, "myredis3.dev.internal", 5003, EndpointBindingMode.SingleAddress, targetPortExpression: null, networkID: KnownNetworkIdentifiers.DefaultAspireContainerNetwork));
});

var redisInsight = Assert.Single(builder.Resources.OfType<RedisInsightResource>());
Expand Down Expand Up @@ -734,9 +728,7 @@ public async Task RedisInsightEnvironmentCallbackIsIdempotent()
.WithEndpoint("tcp", e =>
{
e.AllocatedEndpoint = new AllocatedEndpoint(e, "localhost", 6379);
var snapshot = new ValueSnapshot<AllocatedEndpoint>();
snapshot.SetValue(new AllocatedEndpoint(e, "redis.dev.internal", 6379, EndpointBindingMode.SingleAddress, targetPortExpression: null, networkID: KnownNetworkIdentifiers.DefaultAspireContainerNetwork));
e.AllAllocatedEndpoints.TryAdd(KnownNetworkIdentifiers.DefaultAspireContainerNetwork, snapshot);
e.AllAllocatedEndpoints.AddOrUpdateAllocatedEndpoint(KnownNetworkIdentifiers.DefaultAspireContainerNetwork, new AllocatedEndpoint(e, "redis.dev.internal", 6379, EndpointBindingMode.SingleAddress, targetPortExpression: null, networkID: KnownNetworkIdentifiers.DefaultAspireContainerNetwork));
})
.WithRedisInsight();

Expand Down
Loading
Loading