diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index c511bb0d0..dd2cee376 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -25,6 +25,7 @@ jobs: Hosting.Azure.Dapr.Redis.Tests, Hosting.Azure.Dapr.Tests, Hosting.Azure.DataApiBuilder.Tests, + Hosting.Azure.Extensions.Tests, Hosting.Bun.Tests, Hosting.Dapr.Tests, Hosting.DbGate.Tests, diff --git a/CommunityToolkit.Aspire.slnx b/CommunityToolkit.Aspire.slnx index de6a29fef..3f21a87d7 100644 --- a/CommunityToolkit.Aspire.slnx +++ b/CommunityToolkit.Aspire.slnx @@ -8,6 +8,9 @@ + + + @@ -184,6 +187,7 @@ + @@ -242,6 +246,7 @@ + diff --git a/README.md b/README.md index 977386955..2804746d9 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,7 @@ This repository contains the source code for the Aspire Community Toolkit, a col | - **Learn More**: [`Hosting.SurrealDb`][surrealdb-integration-docs]
- Stable 📦: [![CommunityToolkit.Aspire.Hosting.SurrealDb][surrealdb-shields]][surrealdb-nuget]
- Preview 📦: [![CommunityToolkit.Aspire.Hosting.SurrealDb][surrealdb-shields-preview]][surrealdb-nuget-preview] | An Aspire hosting integration leveraging the [SurrealDB](https://surrealdb.com/) container. | | - **Learn More**: [`SurrealDb`][surrealdb-integration-docs]
- Stable 📦: [![CommunityToolkit.Aspire.SurrealDb][surrealdb-client-shields]][surrealdb-client-nuget]
- Preview 📦: [![CommunityToolkit.Aspire.SurrealDb][surrealdb-client-shields-preview]][surrealdb-client-nuget-preview] | An Aspire client integration for the [SurrealDB](https://github.com/surrealdb/surrealdb.net/) package. | | - **Learn More**: [`Hosting.Elasticsearch.Extensions`][elasticsearch-ext-integration-docs]
- Stable 📦: [![CommunityToolkit.Aspire.Hosting.Elasticsearch.Extensions][elasticsearch-ext-shields]][elasticsearch-ext-nuget]
- Preview 📦: [![CommunityToolkit.Aspire.Hosting.Elasticsearch.Extensions][elasticsearch-ext-shields-preview]][elasticsearch-ext-nuget-preview] | An integration that contains some additional extensions for hosting Elasticsearch container. | +| - **Learn More**: [`Hosting.Azure.Extensions`][azure-ext-integration-docs]
- Stable 📦: [![CommunityToolkit.Aspire.Azure.Extensions][azure-ext-shields]][azure-ext-nuget]
- Preview 📦: [![CommunityToolkit.Aspire.Hosting.Azure.Extensions][azure-ext-shields-preview]][azure-ext-nuget-preview] | An integration that contains some additional extensions for hosting Azure container. | ## 🙌 Getting Started @@ -280,3 +281,8 @@ This project is supported by the [.NET Foundation](https://dotnetfoundation.org) [elasticsearch-ext-nuget]: https://nuget.org/packages/CommunityToolkit.Aspire.Hosting.Elasticsearch.Extensions/ [elasticsearch-ext-shields-preview]: https://img.shields.io/nuget/vpre/CommunityToolkit.Aspire.Hosting.Elasticsearch.Extensions?label=nuget%20(preview) [elasticsearch-ext-nuget-preview]: https://nuget.org/packages/CommunityToolkit.Aspire.Hosting.Elasticsearch.Extensions/absoluteLatest +[azure-ext-integration-docs]: https://learn.microsoft.com/dotnet/aspire/community-toolkit/hosting-azure-extensions +[azure-ext-shields]: https://img.shields.io/nuget/v/CommunityToolkit.Aspire.Hosting.Azure.Extensions +[azure-ext-nuget]: https://nuget.org/packages/CommunityToolkit.Aspire.Hosting.Azure.Extensions/ +[azure-ext-shields-preview]: https://img.shields.io/nuget/vpre/CommunityToolkit.Aspire.Hosting.Azure.Extensions?label=nuget%20(preview) +[azure-ext-nuget-preview]: https://nuget.org/packages/CommunityToolkit.Aspire.Hosting.Azure.Extensions/absoluteLatest diff --git a/examples/azure-ext/CommunityToolkit.Aspire.Azure.Extensions.AppHost/CommunityToolkit.Aspire.Azure.Extensions.AppHost.csproj b/examples/azure-ext/CommunityToolkit.Aspire.Azure.Extensions.AppHost/CommunityToolkit.Aspire.Azure.Extensions.AppHost.csproj new file mode 100644 index 000000000..36e400b5c --- /dev/null +++ b/examples/azure-ext/CommunityToolkit.Aspire.Azure.Extensions.AppHost/CommunityToolkit.Aspire.Azure.Extensions.AppHost.csproj @@ -0,0 +1,16 @@ + + + + Exe + true + + + + + + + + + + + diff --git a/examples/azure-ext/CommunityToolkit.Aspire.Azure.Extensions.AppHost/Program.cs b/examples/azure-ext/CommunityToolkit.Aspire.Azure.Extensions.AppHost/Program.cs new file mode 100644 index 000000000..8c2c47ecf --- /dev/null +++ b/examples/azure-ext/CommunityToolkit.Aspire.Azure.Extensions.AppHost/Program.cs @@ -0,0 +1,16 @@ +var builder = DistributedApplication.CreateBuilder(args); + +var storage = builder.AddAzureStorage("azure-storage") + .RunAsEmulator(azurite => + { + azurite + .WithArgs("--disableProductStyleUrl") + .WithBlobPort(27000) + .WithQueuePort(27001) + .WithTablePort(27002) + .WithDataVolume("storage"); + }); + +var blobs = storage.AddBlobs("blobs").WithAzureStorageExplorer(); + +builder.Build().Run(); \ No newline at end of file diff --git a/examples/azure-ext/CommunityToolkit.Aspire.Azure.Extensions.AppHost/Properties/launchSettings.json b/examples/azure-ext/CommunityToolkit.Aspire.Azure.Extensions.AppHost/Properties/launchSettings.json new file mode 100644 index 000000000..dd6a59f53 --- /dev/null +++ b/examples/azure-ext/CommunityToolkit.Aspire.Azure.Extensions.AppHost/Properties/launchSettings.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:17160;http://localhost:15192", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21259", + "ASPIRE_DASHBOARD_MCP_ENDPOINT_URL": "https://localhost:23002", + "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22192" + } + }, + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:15192", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19033", + "ASPIRE_DASHBOARD_MCP_ENDPOINT_URL": "http://localhost:18007", + "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20079" + } + } + } +} diff --git a/examples/azure-ext/CommunityToolkit.Aspire.Azure.Extensions.AppHost/appsettings.json b/examples/azure-ext/CommunityToolkit.Aspire.Azure.Extensions.AppHost/appsettings.json new file mode 100644 index 000000000..31c092aa4 --- /dev/null +++ b/examples/azure-ext/CommunityToolkit.Aspire.Azure.Extensions.AppHost/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning", + "Aspire.Hosting.Dcp": "Warning" + } + } +} diff --git a/src/CommunityToolkit.Aspire.Hosting.Azure.Extensions/AzureBlobStorageResourceBuilderExtensions.cs b/src/CommunityToolkit.Aspire.Hosting.Azure.Extensions/AzureBlobStorageResourceBuilderExtensions.cs new file mode 100644 index 000000000..44f3c31f1 --- /dev/null +++ b/src/CommunityToolkit.Aspire.Hosting.Azure.Extensions/AzureBlobStorageResourceBuilderExtensions.cs @@ -0,0 +1,63 @@ +// 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.ApplicationModel; +using Aspire.Hosting.Azure; + +namespace Aspire.Hosting; + +/// +/// Provides extension methods for adding Azure Storage Explorer resources to the application model. +/// +public static class AzureBlobStorageResourceBuilderExtensions +{ + /// + /// Adds an Azure Storage Explorer instance to a Blob storage resource. + /// + /// The builder for the . + /// Configuration callback for Azure Storage Explorer container resource. + /// The name of the resource. + /// A reference to the . + /// + /// + /// Add an Azure Storage Explorer container to the application model and reference it in a .NET project. + /// + /// var builder = DistributedApplication.CreateBuilder(args); + /// + /// var storage = builder.AddAzureStorage("storage") + /// .RunAsEmulator(azurite => + /// { + /// azurite + /// .WithBlobPort(27000) + /// .WithQueuePort(27001) + /// .WithTablePort(27002); + /// }); + /// var blobs = storage.AddBlobs("blobs") + /// .WithAzureStorageExplorer(); + /// + /// builder.Build().Run(); + /// + /// + /// + public static IResourceBuilder WithAzureStorageExplorer( + this IResourceBuilder blobs, + Action>? configureContainer = null, + [ResourceName] string? name = null + ) + { + string resourceNname = name ?? $"{blobs.Resource.Name}-explorer"; + var builder = blobs.ApplicationBuilder + .AddAzureStorageExplorer(resourceNname) + .WithStorageResource(blobs) + .WithParentRelationship(blobs); + + if (blobs.Resource.Parent.IsEmulator) + { + builder.WithAzurite(); + } + + configureContainer?.Invoke(builder); + + return blobs; + } +} \ No newline at end of file diff --git a/src/CommunityToolkit.Aspire.Hosting.Azure.Extensions/AzureQueueStorageResourceBuilderExtensions.cs b/src/CommunityToolkit.Aspire.Hosting.Azure.Extensions/AzureQueueStorageResourceBuilderExtensions.cs new file mode 100644 index 000000000..9fd690cb8 --- /dev/null +++ b/src/CommunityToolkit.Aspire.Hosting.Azure.Extensions/AzureQueueStorageResourceBuilderExtensions.cs @@ -0,0 +1,63 @@ +// 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.ApplicationModel; +using Aspire.Hosting.Azure; + +namespace Aspire.Hosting; + +/// +/// Provides extension methods for adding Azure Storage Explorer resources to the application model. +/// +public static class AzureQueueStorageResourceBuilderExtensions +{ + /// + /// Adds an Azure Storage Explorer instance to a Queue storage resource. + /// + /// The builder for the . + /// Configuration callback for Azure Storage Explorer container resource. + /// The name of the resource. + /// A reference to the . + /// + /// + /// Add an Azure Storage Explorer container to the application model and reference it in a .NET project. + /// + /// var builder = DistributedApplication.CreateBuilder(args); + /// + /// var storage = builder.AddAzureStorage("storage") + /// .RunAsEmulator(azurite => + /// { + /// azurite + /// .WithBlobPort(27000) + /// .WithQueuePort(27001) + /// .WithTablePort(27002); + /// }); + /// var queues = storage.AddQueues("queues") + /// .WithAzureStorageExplorer(); + /// + /// builder.Build().Run(); + /// + /// + /// + public static IResourceBuilder WithAzureStorageExplorer( + this IResourceBuilder queues, + Action>? configureContainer = null, + [ResourceName] string? name = null + ) + { + string resourceNname = name ?? $"{queues.Resource.Name}-explorer"; + var builder = queues.ApplicationBuilder + .AddAzureStorageExplorer(resourceNname) + .WithStorageResource(queues) + .WithParentRelationship(queues); + + if (queues.Resource.Parent.IsEmulator) + { + builder.WithAzurite(); + } + + configureContainer?.Invoke(builder); + + return queues; + } +} \ No newline at end of file diff --git a/src/CommunityToolkit.Aspire.Hosting.Azure.Extensions/AzureStorageExplorerBuilderExtensions.cs b/src/CommunityToolkit.Aspire.Hosting.Azure.Extensions/AzureStorageExplorerBuilderExtensions.cs new file mode 100644 index 000000000..50855c160 --- /dev/null +++ b/src/CommunityToolkit.Aspire.Hosting.Azure.Extensions/AzureStorageExplorerBuilderExtensions.cs @@ -0,0 +1,90 @@ +// 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.ApplicationModel; + +namespace Aspire.Hosting; + +/// +/// Provides extension methods for adding Azure Storage Explorer resources to the application model. +/// +public static class AzureStorageExplorerBuilderExtensions +{ + private const int AzureStorageExplorerPort = 8080; + + /// + /// Configures the host port that the Azure Storage Explorer resource is exposed on instead of using randomly assigned port. + /// + /// The resource builder for Azure Storage Explorer. + /// The port to bind on the host. If is used random port will be assigned. + /// The resource builder for Azure Storage Explorer. + public static IResourceBuilder WithHostPort(this IResourceBuilder builder, int? port) + { + ArgumentNullException.ThrowIfNull(builder); + + return builder.WithEndpoint(AzureStorageExplorerResource.PrimaryEndpointName, endpoint => + { + endpoint.Port = port; + }); + } + + /// + /// Adds an Azure Storage Explorer resource to the application model. + /// The default image is and the tag is . + /// + /// The . + /// The name of the resource. + /// The host port for the Azure Storage Explorer instance. + /// A reference to the . + internal static IResourceBuilder AddAzureStorageExplorer( + this IDistributedApplicationBuilder builder, + [ResourceName] string name, + int? port = null + ) + { + ArgumentNullException.ThrowIfNull(builder); + ArgumentException.ThrowIfNullOrEmpty(name); + + var explorer = new AzureStorageExplorerResource(name); + + return builder.AddResource(explorer) + .WithHttpEndpoint( + port: port, + targetPort: AzureStorageExplorerPort, + name: AzureStorageExplorerResource.PrimaryEndpointName + ) + .WithImage(AzureStorageExplorerContainerImageTags.Image, AzureStorageExplorerContainerImageTags.Tag) + .WithImageRegistry(AzureStorageExplorerContainerImageTags.Registry) + .ExcludeFromManifest(); + } + + /// + /// Allows Azure Storage Explorer to work with the Azure Emulator (Azurite). + /// + /// The builder for the . + /// A reference to the . + internal static IResourceBuilder WithAzurite( + this IResourceBuilder builder + ) + { + ArgumentNullException.ThrowIfNull(builder); + + return builder.WithEnvironment("AZURITE", "true"); + } + + internal static IResourceBuilder WithStorageResource( + this IResourceBuilder builder, + IResourceBuilder resourceBuilder + ) + { + ArgumentNullException.ThrowIfNull(builder); + + return builder + .WaitFor(resourceBuilder) + .WithEnvironment(e => + { + e.EnvironmentVariables["AZURE_STORAGE_CONNECTIONSTRING"] = + new ConnectionStringReference(resourceBuilder.Resource, optional: false); + }); + } +} \ No newline at end of file diff --git a/src/CommunityToolkit.Aspire.Hosting.Azure.Extensions/AzureStorageExplorerContainerImageTags.cs b/src/CommunityToolkit.Aspire.Hosting.Azure.Extensions/AzureStorageExplorerContainerImageTags.cs new file mode 100644 index 000000000..baa3e1d1c --- /dev/null +++ b/src/CommunityToolkit.Aspire.Hosting.Azure.Extensions/AzureStorageExplorerContainerImageTags.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Aspire.Hosting; + +internal sealed class AzureStorageExplorerContainerImageTags +{ + /// docker.io + public const string Registry = "docker.io"; + /// sebagomez/azurestorageexplorer + public const string Image = "sebagomez/azurestorageexplorer"; + /// 3.1.0 + public const string Tag = "3.1.0"; +} \ No newline at end of file diff --git a/src/CommunityToolkit.Aspire.Hosting.Azure.Extensions/AzureStorageExplorerResource.cs b/src/CommunityToolkit.Aspire.Hosting.Azure.Extensions/AzureStorageExplorerResource.cs new file mode 100644 index 000000000..b46933244 --- /dev/null +++ b/src/CommunityToolkit.Aspire.Hosting.Azure.Extensions/AzureStorageExplorerResource.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Aspire.Hosting.ApplicationModel; + +/// +/// A resource that represents an Azure Storage Explorer container. +/// +/// The name of the resource. +public class AzureStorageExplorerResource([ResourceName] string name) : ContainerResource(name) +{ + internal const string PrimaryEndpointName = "http"; + + private EndpointReference? _primaryEndpoint; + + /// + /// Gets the primary endpoint for the Azure Storage Explorer instance. + /// + public EndpointReference PrimaryEndpoint => _primaryEndpoint ??= new(this, PrimaryEndpointName); + + /// + /// Gets the host endpoint reference for this resource. + /// + public EndpointReferenceExpression Host => PrimaryEndpoint.Property(EndpointProperty.Host); + + /// + /// Gets the port endpoint reference for this resource. + /// + public EndpointReferenceExpression Port => PrimaryEndpoint.Property(EndpointProperty.Port); +} diff --git a/src/CommunityToolkit.Aspire.Hosting.Azure.Extensions/AzureTableStorageResourceBuilderExtensions.cs b/src/CommunityToolkit.Aspire.Hosting.Azure.Extensions/AzureTableStorageResourceBuilderExtensions.cs new file mode 100644 index 000000000..17ec3a282 --- /dev/null +++ b/src/CommunityToolkit.Aspire.Hosting.Azure.Extensions/AzureTableStorageResourceBuilderExtensions.cs @@ -0,0 +1,63 @@ +// 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.ApplicationModel; +using Aspire.Hosting.Azure; + +namespace Aspire.Hosting; + +/// +/// Provides extension methods for adding Azure Storage Explorer resources to the application model. +/// +public static class AzureTableStorageResourceBuilderExtensions +{ + /// + /// Adds an Azure Storage Explorer instance to a Table storage resource. + /// + /// The builder for the . + /// Configuration callback for Azure Storage Explorer container resource. + /// The name of the resource. + /// A reference to the . + /// + /// + /// Add an Azure Storage Explorer container to the application model and reference it in a .NET project. + /// + /// var builder = DistributedApplication.CreateBuilder(args); + /// + /// var storage = builder.AddAzureStorage("storage") + /// .RunAsEmulator(azurite => + /// { + /// azurite + /// .WithBlobPort(27000) + /// .WithQueuePort(27001) + /// .WithTablePort(27002); + /// }); + /// var tables = storage.AddTables("tables") + /// .WithAzureStorageExplorer(); + /// + /// builder.Build().Run(); + /// + /// + /// + public static IResourceBuilder WithAzureStorageExplorer( + this IResourceBuilder tables, + Action>? configureContainer = null, + [ResourceName] string? name = null + ) + { + string resourceNname = name ?? $"{tables.Resource.Name}-explorer"; + var builder = tables.ApplicationBuilder + .AddAzureStorageExplorer(resourceNname) + .WithStorageResource(tables) + .WithParentRelationship(tables); + + if (tables.Resource.Parent.IsEmulator) + { + builder.WithAzurite(); + } + + configureContainer?.Invoke(builder); + + return tables; + } +} \ No newline at end of file diff --git a/src/CommunityToolkit.Aspire.Hosting.Azure.Extensions/CommunityToolkit.Aspire.Hosting.Azure.Extensions.csproj b/src/CommunityToolkit.Aspire.Hosting.Azure.Extensions/CommunityToolkit.Aspire.Hosting.Azure.Extensions.csproj new file mode 100644 index 000000000..46ea1cbee --- /dev/null +++ b/src/CommunityToolkit.Aspire.Hosting.Azure.Extensions/CommunityToolkit.Aspire.Hosting.Azure.Extensions.csproj @@ -0,0 +1,13 @@ + + + + hosting azure extensions + Azure extensions support for .NET Aspire. + + + + + + + + diff --git a/src/CommunityToolkit.Aspire.Hosting.Azure.Extensions/README.md b/src/CommunityToolkit.Aspire.Hosting.Azure.Extensions/README.md new file mode 100644 index 000000000..72b9dba0a --- /dev/null +++ b/src/CommunityToolkit.Aspire.Hosting.Azure.Extensions/README.md @@ -0,0 +1,39 @@ +# CommunityToolkit.Aspire.Hosting.Azure.Extensions library + +This integration contains extensions for the [Azure Storage hosting package](https://nuget.org/packages/Aspire.Hosting.Azure.Storage) for .NET Aspire. + +The integration provides support for running [Azure Storage Explorer](https://github.com/sebagomez/azurestorageexplorer) to interact with the Azure Storage resource: blobs, queues and tables. + +## Getting Started + +### Install the package + +In your AppHost project, install the package using the following command: + +```dotnetcli +dotnet add package CommunityToolkit.Aspire.Hosting.Azure.Extensions +``` + +### Example usage + +Then, in the _Program.cs_ file of `AppHost`, define an Azure Storage resource, then call `AddAzureStorageExplorer`: + +```csharp +var storage = builder.AddAzureStorage("storage") + .RunAsEmulator(azurite => + { + azurite + .WithBlobPort(27000) + .WithQueuePort(27001) + .WithTablePort(27002); + }); +var blobs = storage.AddBlobs("blobs").WithAzureStorageExplorer(); +``` + +## Additional Information + +https://learn.microsoft.com/dotnet/aspire/community-toolkit/hosting-azure-extensions + +## Feedback & contributing + +https://github.com/CommunityToolkit/Aspire \ No newline at end of file diff --git a/tests/CommunityToolkit.Aspire.Hosting.Azure.Extensions.Tests/AppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.Azure.Extensions.Tests/AppHostTests.cs new file mode 100644 index 000000000..96fb0c6d0 --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.Azure.Extensions.Tests/AppHostTests.cs @@ -0,0 +1,21 @@ +using Aspire.Components.Common.Tests; +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.Azure.Extensions.Tests; + +[RequiresDocker] +public class AppHostTests(AspireIntegrationTestFixture fixture) : IClassFixture> +{ + [Fact] + public async Task ResourceStartsAndRespondsOk() + { + const string resourceName = "blobs-explorer"; + + await fixture.ResourceNotificationService.WaitForResourceHealthyAsync(resourceName).WaitAsync(TimeSpan.FromMinutes(5)); + + using var httpClient = fixture.CreateHttpClient(resourceName); + using var response = await httpClient.GetAsync("/"); + + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + } +} diff --git a/tests/CommunityToolkit.Aspire.Hosting.Azure.Extensions.Tests/CommunityToolkit.Aspire.Hosting.Azure.Extensions.Tests.csproj b/tests/CommunityToolkit.Aspire.Hosting.Azure.Extensions.Tests/CommunityToolkit.Aspire.Hosting.Azure.Extensions.Tests.csproj new file mode 100644 index 000000000..ce32482de --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.Azure.Extensions.Tests/CommunityToolkit.Aspire.Hosting.Azure.Extensions.Tests.csproj @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/tests/CommunityToolkit.Aspire.Hosting.Azure.Extensions.Tests/ResourceCreationTests.cs b/tests/CommunityToolkit.Aspire.Hosting.Azure.Extensions.Tests/ResourceCreationTests.cs new file mode 100644 index 000000000..f1898ca6f --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.Azure.Extensions.Tests/ResourceCreationTests.cs @@ -0,0 +1,135 @@ +using Aspire.Hosting; + +namespace CommunityToolkit.Aspire.Hosting.Azure.Extensions.Tests; + +public class ResourceCreationTests +{ + [Fact] + public async Task WithAzureStorageExplorerAddsAnnotations() + { + var builder = DistributedApplication.CreateBuilder(); + + var azureStorageResourceBuilder = builder.AddAzureStorage("storage") + .RunAsEmulator(azurite => + { + azurite + .WithBlobPort(27000) + .WithQueuePort(27001) + .WithTablePort(27002); + }); + var blobsResourceBuilder = azureStorageResourceBuilder.AddBlobs("blobs").WithAzureStorageExplorer(); + + using var app = builder.Build(); + + var appModel = app.Services.GetRequiredService(); + + var azureStorageExplorerResource = appModel.Resources.OfType().SingleOrDefault(); + + Assert.NotNull(azureStorageExplorerResource); + + Assert.Equal("blobs-explorer", azureStorageExplorerResource.Name); + +#pragma warning disable CS0618 // Type or member is obsolete + var envs = await azureStorageExplorerResource.GetEnvironmentVariableValuesAsync(); +#pragma warning restore CS0618 // Type or member is obsolete + + Assert.NotEmpty(envs); + + Assert.Collection(envs, + item => + { + Assert.Equal("AZURE_STORAGE_CONNECTIONSTRING", item.Key); + Assert.Equal("DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://storage.dev.internal:10000/devstoreaccount1;", item.Value); + }, + item => + { + Assert.Equal("AZURITE", item.Key); + Assert.Equal("true", item.Value); + }); + } + + [Fact] + public void MultipleWithAzureStorageExplorerCallsAddsOneAzureStorageExplorerResourceForEachResource() + { + var builder = DistributedApplication.CreateBuilder(); + + var azureStorageResourceBuilder = builder.AddAzureStorage("storage") + .RunAsEmulator(azurite => + { + azurite + .WithBlobPort(27000) + .WithQueuePort(27001) + .WithTablePort(27002); + }); + var blobsResourceBuilder = azureStorageResourceBuilder.AddBlobs("blobs").WithAzureStorageExplorer(); + var queuesResourceBuilder = azureStorageResourceBuilder.AddBlobs("queues").WithAzureStorageExplorer(); + var tablesResourceBuilder = azureStorageResourceBuilder.AddBlobs("tables").WithAzureStorageExplorer(); + + using var app = builder.Build(); + + var appModel = app.Services.GetRequiredService(); + + var azureStorageExplorerResources = appModel.Resources.OfType().ToList(); + + Assert.Equal(3, azureStorageExplorerResources.Count); + Assert.Equal("blobs-explorer", azureStorageExplorerResources[0].Name); + Assert.Equal("queues-explorer", azureStorageExplorerResources[1].Name); + Assert.Equal("tables-explorer", azureStorageExplorerResources[2].Name); + } + + [Fact] + public void WithAzureStorageExplorerShouldChangeAzureStorageExplorerHostPort() + { + var builder = DistributedApplication.CreateBuilder(); + + var azureStorageResourceBuilder = builder.AddAzureStorage("storage") + .RunAsEmulator(azurite => + { + azurite + .WithBlobPort(27000) + .WithQueuePort(27001) + .WithTablePort(27002); + }); + var blobsResourceBuilder = azureStorageResourceBuilder + .AddBlobs("blobs") + .WithAzureStorageExplorer(c => c.WithHostPort(8068)); + + using var app = builder.Build(); + + var appModel = app.Services.GetRequiredService(); + + var azureStorageExplorerResource = appModel.Resources.OfType().SingleOrDefault(); + Assert.NotNull(azureStorageExplorerResource); + + var primaryEndpoint = azureStorageExplorerResource.Annotations.OfType().Single(); + Assert.Equal(8068, primaryEndpoint.Port); + } + + [Fact] + public void WithAzureStorageExplorerShouldChangeAzureStorageExplorerContainerImageTag() + { + var builder = DistributedApplication.CreateBuilder(); + + var azureStorageResourceBuilder = builder.AddAzureStorage("storage") + .RunAsEmulator(azurite => + { + azurite + .WithBlobPort(27000) + .WithQueuePort(27001) + .WithTablePort(27002); + }); + var blobsResourceBuilder = azureStorageResourceBuilder + .AddBlobs("blobs") + .WithAzureStorageExplorer(c => c.WithImageTag("manualTag")); + + using var app = builder.Build(); + + var appModel = app.Services.GetRequiredService(); + + var azureStorageExplorerResource = appModel.Resources.OfType().SingleOrDefault(); + Assert.NotNull(azureStorageExplorerResource); + + var containerImageAnnotation = azureStorageExplorerResource.Annotations.OfType().Single(); + Assert.Equal("manualTag", containerImageAnnotation.Tag); + } +} \ No newline at end of file