Skip to content

Commit

Permalink
Rename to AddCustomDomain
Browse files Browse the repository at this point in the history
  • Loading branch information
matthebrown committed Feb 8, 2025
1 parent fae576f commit 8120aac
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
.WithEnvironment("VALUE", param)
.PublishAsAzureContainerApp((module, app) =>
{
app.ConfigureCustomDomain(customDomain, certificateName);
app.AddCustomDomain(customDomain, certificateName);

// Scale to 0
app.Template.Scale.MinReplicas = 0;
Expand Down
20 changes: 10 additions & 10 deletions src/Aspire.Hosting.Azure.AppContainers/ContainerAppExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,22 @@ namespace Aspire.Hosting;
public static class ContainerAppExtensions
{
/// <summary>
/// Configures the custom domain for the container app.
/// Adds a custom domain to the container app.
/// </summary>
/// <param name="app">The container app resource to configure for custom domain usage.</param>
/// <param name="app">The container app resource to add a custom domain.</param>
/// <param name="customDomain">A resource builder for a parameter resource capturing the name of the custom domain.</param>
/// <param name="certificateName">A resource builder for a parameter resource capturing the name of the certficate configured in the Azure Portal.</param>
/// <exception cref="ArgumentException">Throws if the container app resource is not parented to a <see cref="AzureResourceInfrastructure"/>.</exception>
/// <remarks>
/// <para>The <see cref="ConfigureCustomDomain(ContainerApp, IResourceBuilder{ParameterResource}, IResourceBuilder{ParameterResource})"/> extension method
/// <para>The <see cref="AddCustomDomain(ContainerApp, IResourceBuilder{ParameterResource}, IResourceBuilder{ParameterResource})"/> extension method
/// simplifies the process of assigning a custom domain to a container app resource when it is deployed. It has no impact on local development.</para>
/// <para>The <see cref="ConfigureCustomDomain(ContainerApp, IResourceBuilder{ParameterResource}, IResourceBuilder{ParameterResource})"/> method is used
/// <para>The <see cref="AddCustomDomain(ContainerApp, IResourceBuilder{ParameterResource}, IResourceBuilder{ParameterResource})"/> method is used
/// in conjunction with the <see cref="AzureContainerAppContainerExtensions.PublishAsAzureContainerApp{T}(IResourceBuilder{T}, Action{AzureResourceInfrastructure, ContainerApp})"/>
/// callback. Assigning a custom domain to a container app resource is a multi-step process and requires multiple deployments.</para>
/// <para>The <see cref="ConfigureCustomDomain(ContainerApp, IResourceBuilder{ParameterResource}, IResourceBuilder{ParameterResource})"/> method takes
/// <para>The <see cref="AddCustomDomain(ContainerApp, IResourceBuilder{ParameterResource}, IResourceBuilder{ParameterResource})"/> method takes
/// two arguments which are parameter resource builders. The first is a parameter that represents the custom domain and the second is a parameter that
/// represents the name of the managed certificate provisioned via the Azure Portal</para>
/// <para>When deploying with custom domains configured for the first time leave the <paramref name="certificateName"/> parameter empty (when prompted
/// <para>When deploying with custom domains added for the first time leave the <paramref name="certificateName"/> parameter empty (when prompted
/// by the Azure Developer CLI). Once the applicatio is deployed acucessfully access to the Azure Portal to bind the custom domain to a managed SSL
/// certificate. Once the certificate is successfully provisioned, subsequent deployments of the application can use this certificate name when the
/// <paramref name="certificateName"/> is prompted.</para>
Expand All @@ -40,7 +40,7 @@ public static class ContainerAppExtensions
/// </remarks>
/// <example>
/// This example shows declaring two parameters to capture the custom domain and certificate name and
/// passing them to the <see cref="ConfigureCustomDomain(ContainerApp, IResourceBuilder{ParameterResource}, IResourceBuilder{ParameterResource})"/>
/// passing them to the <see cref="AddCustomDomain(ContainerApp, IResourceBuilder{ParameterResource}, IResourceBuilder{ParameterResource})"/>
/// method via the <see cref="AzureContainerAppContainerExtensions.PublishAsAzureContainerApp{T}(IResourceBuilder{T}, Action{AzureResourceInfrastructure, ContainerApp})"/>
/// extension method.
/// <code lang="C#">
Expand All @@ -50,16 +50,16 @@ public static class ContainerAppExtensions
/// builder.AddProject&lt;Projects.InventoryService&gt;("inventory")
/// .PublishAsAzureContainerApp((module, app) =>
/// {
/// app.ConfigureCustomDomain(customDomain, certificateName);
/// app.AddCustomDomain(customDomain, certificateName);
/// });
/// </code>
/// </example>
[Experimental("ASPIREACADOMAINS001", UrlFormat = "https://aka.ms/dotnet/aspire/diagnostics#{0}")]
public static void ConfigureCustomDomain(this ContainerApp app, IResourceBuilder<ParameterResource> customDomain, IResourceBuilder<ParameterResource> certificateName)
public static void AddCustomDomain(this ContainerApp app, IResourceBuilder<ParameterResource> customDomain, IResourceBuilder<ParameterResource> certificateName)
{
if (app.ParentInfrastructure is not AzureResourceInfrastructure module)
{
throw new ArgumentException("Cannot configure custom domain when resource is not parented by ResourceModuleConstruct.", nameof(app));
throw new ArgumentException("Cannot add custom domain when resource is not parented by ResourceModuleConstruct.", nameof(app));
}

var containerAppManagedEnvironmentIdParameter = module.GetProvisionableResources().OfType<ProvisioningParameter>().Single(
Expand Down
135 changes: 133 additions & 2 deletions tests/Aspire.Hosting.Azure.Tests/AzureContainerAppsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1061,7 +1061,7 @@ param outputs_azure_container_apps_environment_id string
}

[Fact]
public async Task ConfigureCustomDomainsMutatesIngress()
public async Task AddCustomDomainMutatesIngress()
{
using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish);

Expand All @@ -1073,7 +1073,7 @@ public async Task ConfigureCustomDomainsMutatesIngress()
.WithHttpEndpoint(targetPort: 1111)
.PublishAsAzureContainerApp((module, c) =>
{
c.ConfigureCustomDomain(customDomain, certificateName);
c.AddCustomDomain(customDomain, certificateName);
});

using var app = builder.Build();
Expand Down Expand Up @@ -1176,6 +1176,137 @@ param customDomain string
Assert.Equal(expectedBicep, bicep);
}

[Fact]
public async Task AddMultipleCustomDomainsMutatesIngress()
{
using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish);

var customDomain1 = builder.AddParameter("customDomain1");
var certificateName1 = builder.AddParameter("certificateName1");

var customDomain2 = builder.AddParameter("customDomain2");
var certificateName2 = builder.AddParameter("certificateName2");

builder.AddAzureContainerAppsInfrastructure();
builder.AddContainer("api", "myimage")
.WithHttpEndpoint(targetPort: 1111)
.PublishAsAzureContainerApp((module, c) =>
{
c.AddCustomDomain(customDomain1, certificateName1);
c.AddCustomDomain(customDomain2, certificateName2);
});

using var app = builder.Build();

await ExecuteBeforeStartHooksAsync(app, default);

var model = app.Services.GetRequiredService<DistributedApplicationModel>();

var container = Assert.Single(model.GetContainerResources());

container.TryGetLastAnnotation<DeploymentTargetAnnotation>(out var target);

var resource = target?.DeploymentTarget as AzureBicepResource;

Assert.NotNull(resource);

var (manifest, bicep) = await ManifestUtils.GetManifestWithBicep(resource);

var m = manifest.ToString();

var expectedManifest =
"""
{
"type": "azure.bicep.v0",
"path": "api.module.bicep",
"params": {
"outputs_azure_container_registry_managed_identity_id": "{.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID}",
"outputs_managed_identity_client_id": "{.outputs.MANAGED_IDENTITY_CLIENT_ID}",
"outputs_azure_container_apps_environment_id": "{.outputs.AZURE_CONTAINER_APPS_ENVIRONMENT_ID}",
"certificateName1": "{certificateName1.value}",
"customDomain1": "{customDomain1.value}",
"certificateName2": "{certificateName2.value}",
"customDomain2": "{customDomain2.value}"
}
}
""";

Assert.Equal(expectedManifest, m);

var expectedBicep =
"""
@description('The location for the resource(s) to be deployed.')
param location string = resourceGroup().location
param outputs_azure_container_registry_managed_identity_id string
param outputs_managed_identity_client_id string
param outputs_azure_container_apps_environment_id string
param certificateName1 string
param customDomain1 string
param certificateName2 string
param customDomain2 string
resource api 'Microsoft.App/containerApps@2024-03-01' = {
name: 'api'
location: location
properties: {
configuration: {
activeRevisionsMode: 'Single'
ingress: {
external: false
targetPort: 1111
transport: 'http'
customDomains: [
{
name: customDomain1
bindingType: (certificateName1 != '') ? 'SniEnabled' : 'Disabled'
certificateId: (certificateName1 != '') ? '${outputs_azure_container_apps_environment_id}/managedCertificates/${certificateName1}' : null
}
{
name: customDomain2
bindingType: (certificateName2 != '') ? 'SniEnabled' : 'Disabled'
certificateId: (certificateName2 != '') ? '${outputs_azure_container_apps_environment_id}/managedCertificates/${certificateName2}' : null
}
]
}
}
environmentId: outputs_azure_container_apps_environment_id
template: {
containers: [
{
image: 'myimage:latest'
name: 'api'
env: [
{
name: 'AZURE_CLIENT_ID'
value: outputs_managed_identity_client_id
}
]
}
]
scale: {
minReplicas: 1
}
}
}
identity: {
type: 'UserAssigned'
userAssignedIdentities: {
'${outputs_azure_container_registry_managed_identity_id}': { }
}
}
}
""";
output.WriteLine(bicep);
Assert.Equal(expectedBicep, bicep);
}

[Fact]
public async Task VolumesAndBindMountsAreTranslation()
{
Expand Down

0 comments on commit 8120aac

Please sign in to comment.