Skip to content

Commit

Permalink
Fix EventHubs GetNamespaceFromSettings (#7393)
Browse files Browse the repository at this point in the history
We were calling RandomNumberGenerator.GetHexString and not using the result. The original intention was to use a random container name if we couldn't see ".servicebus" in the host name.

Instead of using a random name, just sanitize the host name and use it, so it is deterministc.

Fix #4886
  • Loading branch information
eerhardt authored Feb 4, 2025
1 parent 4e1a0f2 commit c1c9666
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 14 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Security.Cryptography;
using Aspire.Azure.Common;
using Aspire.Azure.Messaging.EventHubs;
using Azure.Core;
Expand Down Expand Up @@ -56,15 +55,15 @@ protected static string GetNamespaceFromSettings(AzureMessagingEventHubsSettings
: new Uri(settings.FullyQualifiedNamespace).Host;

// This is likely to be similar to {yournamespace}.servicebus.windows.net or {yournamespace}.servicebus.chinacloudapi.cn
if (ns.Contains(".servicebus", StringComparison.OrdinalIgnoreCase))
var serviceBusIndex = ns.IndexOf(".servicebus", StringComparison.OrdinalIgnoreCase);
if (serviceBusIndex != -1)
{
ns = ns[..ns.IndexOf(".servicebus")];
ns = ns[..serviceBusIndex];
}
else
{
// Use a random prefix if no meaningful name is found e.g., "localhost", "127.0.0.1".
// This is used to create blob containers names that are unique in the referenced storage account.
RandomNumberGenerator.GetHexString(12, true);
// sanitize the namespace if it's not a servicebus namespace
ns = ns.Replace(".", "-");
}
}
catch (Exception ex) when (ex is FormatException or IndexOutOfRangeException)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,17 +97,14 @@ private static BlobContainerClient GetBlobContainerClient(
// name specified in the settings.
bool shouldTryCreateIfNotExists = false;

// Do we have a container name provided in the settings?
if (string.IsNullOrWhiteSpace(settings.BlobContainerName))
{
// If not, we'll create a container name based on the namespace, event hub name and consumer group
var ns = GetNamespaceFromSettings(settings);

// Do we have a container name provided in the settings?
if (string.IsNullOrWhiteSpace(settings.BlobContainerName))
{
// If not, we'll create a container name based on the namespace, event hub name and consumer group
settings.BlobContainerName = $"{ns}-{settings.EventHubName}-{consumerGroup}";
shouldTryCreateIfNotExists = true;
}
settings.BlobContainerName = $"{ns}-{settings.EventHubName}-{consumerGroup}";
shouldTryCreateIfNotExists = true;
}

var containerClient = blobClient.GetBlobContainerClient(settings.BlobContainerName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

<ProjectReference Include="..\..\src\Components\Aspire.Azure.Storage.Blobs\Aspire.Azure.Storage.Blobs.csproj" />
<ProjectReference Include="..\Aspire.Components.Common.Tests\Aspire.Components.Common.Tests.csproj" />

<Compile Include="..\Aspire.Azure.Security.KeyVault.Tests\MockTransport.cs" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Text;
using Azure.Core;
using Azure.Identity;
using Azure.Messaging.EventHubs;
using Azure.Messaging.EventHubs.Consumer;
Expand Down Expand Up @@ -476,7 +478,7 @@ private static void AssertFullyQualifiedNamespace(string expectedNamespace, obje
EventHubConsumerClient consumer => consumer.FullyQualifiedNamespace,
EventProcessorClient processor => processor.FullyQualifiedNamespace,
PartitionReceiver receiver => receiver.FullyQualifiedNamespace,
EventHubBufferedProducerClient producer => producer.FullyQualifiedNamespace,
EventHubBufferedProducerClient producer => producer.FullyQualifiedNamespace,
_ => throw new InvalidOperationException()
});
}
Expand Down Expand Up @@ -580,4 +582,48 @@ public void CanAddMultipleKeyedServices(int clientIndex)

public static string CreateConfigKey(string prefix, string? key, string suffix)
=> string.IsNullOrEmpty(key) ? $"{prefix}:{suffix}" : $"{prefix}:{key}:{suffix}";

/// <summary>
/// Tests that the BlobContainerName defaults correctly when the connection string doesn't contain ".servicebus" and
/// contains invalid container name characters.
/// </summary>
[Fact]
public void ProcessorBlobContainerNameDefaultsCorrectly()
{
var builder = Host.CreateEmptyApplicationBuilder(null);
builder.Configuration.AddInMemoryCollection([
new KeyValuePair<string, string?>("ConnectionStrings:eh1", "Endpoint=sb://127.0.0.1:53589;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=SAS_KEY_VALUE;UseDevelopmentEmulator=true;"),
new KeyValuePair<string, string?>("Aspire:Azure:Messaging:EventHubs:EventProcessorClient:EventHubName", "MyHub"),
]);

var mockTransport = new MockTransport(
CreateResponse("""{}"""));
var blobClient = new BlobServiceClient(new Uri(BlobsConnectionString), new BlobClientOptions() { Transport = mockTransport });
builder.Services.AddSingleton(blobClient);

builder.AddAzureEventProcessorClient("eh1");

using var host = builder.Build();

var client = host.Services.GetRequiredService<EventProcessorClient>();
Assert.NotNull(client);

Assert.Single(mockTransport.Requests);
// the container name should be based on the Endpoint, EventHubName, and ConsumerGroup
Assert.Equal("https://fake.blob.core.windows.net/127-0-0-1-MyHub-default?restype=container", mockTransport.Requests[0].Uri.ToString());
}

private static MockResponse CreateResponse(string content)
{
var buffer = Encoding.UTF8.GetBytes(content);
var response = new MockResponse(201)
{
ClientRequestId = Guid.NewGuid().ToString(),
ContentStream = new MemoryStream(buffer),
};

response.AddHeader(new HttpHeader("Content-Type", "application/json; charset=utf-8"));

return response;
}
}

0 comments on commit c1c9666

Please sign in to comment.