Skip to content

Commit

Permalink
Added support for customizing container apps in ACA via the CDK
Browse files Browse the repository at this point in the history
- Added Aspire.Hosting.Azure.ContainerApps. This exposes 3 APIs used to configure and customize container app resources.
- Added deployment target support to project and container resources in the manifest writer. This allows developers to express that a project/container gets deployed using the nested resource type. This requires a branch of azd to wire up and test.
  • Loading branch information
davidfowl committed Sep 25, 2024
1 parent 69c1950 commit b0520ee
Show file tree
Hide file tree
Showing 29 changed files with 1,939 additions and 15 deletions.
36 changes: 25 additions & 11 deletions Aspire.sln
Original file line number Diff line number Diff line change
Expand Up @@ -554,8 +554,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aspire.Hosting.Keycloak.Tes
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aspire.Keycloak.Authentication.Tests", "tests\Aspire.Keycloak.Authentication.Tests\Aspire.Keycloak.Authentication.Tests.csproj", "{48FF09E9-7D33-4A3F-9FF2-4C43A219C7B7}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Garnet", "Garnet", "{39E23812-12FB-4E49-AA13-499332E49A5A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aspire.Hosting.MongoDB.Tests", "tests\Aspire.Hosting.MongoDB.Tests\Aspire.Hosting.MongoDB.Tests.csproj", "{DD9BC533-8072-481C-9A7E-F95DC36B34C0}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aspire.Hosting.Nats.Tests", "tests\Aspire.Hosting.Nats.Tests\Aspire.Hosting.Nats.Tests.csproj", "{F492357C-682E-4CBB-A374-1A124B3976A3}"
Expand Down Expand Up @@ -624,11 +622,17 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Redis.ApiService", "playgro
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Redis.AppHost", "playground\Redis\Redis.AppHost\Redis.AppHost.csproj", "{6249A193-3BF4-4FFB-AB81-6590A8318889}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aspire.Hosting.Analyzers.Tests", "tests\Aspire.Hosting.Analyzers.Tests\Aspire.Hosting.Analyzers.Tests.csproj", "{F21F921E-1930-4BD5-B665-5EF1B82BD4D2}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aspire.Hosting.Analyzers.Tests", "tests\Aspire.Hosting.Analyzers.Tests\Aspire.Hosting.Analyzers.Tests.csproj", "{F21F921E-1930-4BD5-B665-5EF1B82BD4D2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aspire.Hosting.Sdk.Tests", "tests\Aspire.Hosting.Sdk.Tests\Aspire.Hosting.Sdk.Tests.csproj", "{AEF07BC6-76D8-4A45-89D5-54FC4483863F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aspire.Hosting.Azure.ContainerApps", "src\Aspire.Hosting.Azure.ContainerApps\Aspire.Hosting.Azure.ContainerApps.csproj", "{21FBDB19-0B8B-4F0F-8ED6-98560AD5DDA7}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AzureContainerApps", "AzureContainerApps", "{D47C77F3-49F4-4741-9786-6F68FC50587E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aspire.Hosting.Sdk.Tests", "tests\Aspire.Hosting.Sdk.Tests\Aspire.Hosting.Sdk.Tests.csproj", "{AEF07BC6-76D8-4A45-89D5-54FC4483863F}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AzureContainerApps.AppHost", "playground\AzureContainerApps\AzureContainerApps.AppHost\AzureContainerApps.AppHost.csproj", "{96A5B854-2C72-49C8-A91B-2643B814DB2A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aspire.RuntimeIdentifier.Tool", "src\Aspire.AppHost.Sdk\Aspire.RuntimeIdentifier.Tool\Aspire.RuntimeIdentifier.Tool.csproj", "{76FCBF5C-4DB2-4F6C-BB3C-91FD89735AC7}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AzureContainerApps.ApiService", "playground\AzureContainerApps\AzureContainerApps.ApiService\AzureContainerApps.ApiService.csproj", "{683D8264-2755-4455-A6D4-14F7A6AC7B54}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -1652,10 +1656,18 @@ Global
{AEF07BC6-76D8-4A45-89D5-54FC4483863F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AEF07BC6-76D8-4A45-89D5-54FC4483863F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AEF07BC6-76D8-4A45-89D5-54FC4483863F}.Release|Any CPU.Build.0 = Release|Any CPU
{76FCBF5C-4DB2-4F6C-BB3C-91FD89735AC7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{76FCBF5C-4DB2-4F6C-BB3C-91FD89735AC7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{76FCBF5C-4DB2-4F6C-BB3C-91FD89735AC7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{76FCBF5C-4DB2-4F6C-BB3C-91FD89735AC7}.Release|Any CPU.Build.0 = Release|Any CPU
{21FBDB19-0B8B-4F0F-8ED6-98560AD5DDA7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{21FBDB19-0B8B-4F0F-8ED6-98560AD5DDA7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{21FBDB19-0B8B-4F0F-8ED6-98560AD5DDA7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{21FBDB19-0B8B-4F0F-8ED6-98560AD5DDA7}.Release|Any CPU.Build.0 = Release|Any CPU
{96A5B854-2C72-49C8-A91B-2643B814DB2A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{96A5B854-2C72-49C8-A91B-2643B814DB2A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{96A5B854-2C72-49C8-A91B-2643B814DB2A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{96A5B854-2C72-49C8-A91B-2643B814DB2A}.Release|Any CPU.Build.0 = Release|Any CPU
{683D8264-2755-4455-A6D4-14F7A6AC7B54}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{683D8264-2755-4455-A6D4-14F7A6AC7B54}.Debug|Any CPU.Build.0 = Debug|Any CPU
{683D8264-2755-4455-A6D4-14F7A6AC7B54}.Release|Any CPU.ActiveCfg = Release|Any CPU
{683D8264-2755-4455-A6D4-14F7A6AC7B54}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -1921,7 +1933,6 @@ Global
{C556D61C-7E11-43EC-9098-C8D170FEA905} = {EBC55A17-B0D6-4E0A-9DC2-7D264E96F631}
{5867BAF2-FEF0-4661-BFDE-9ADCDC2921CD} = {830A89EC-4029-4753-B25A-068BAE37DEC7}
{48FF09E9-7D33-4A3F-9FF2-4C43A219C7B7} = {C424395C-1235-41A4-BF55-07880A04368C}
{39E23812-12FB-4E49-AA13-499332E49A5A} = {D173887B-AF42-4576-B9C1-96B9E9B3D9C0}
{DD9BC533-8072-481C-9A7E-F95DC36B34C0} = {830A89EC-4029-4753-B25A-068BAE37DEC7}
{F492357C-682E-4CBB-A374-1A124B3976A3} = {830A89EC-4029-4753-B25A-068BAE37DEC7}
{D705FE42-CD54-4575-BA18-0431256B40B2} = {830A89EC-4029-4753-B25A-068BAE37DEC7}
Expand Down Expand Up @@ -1958,7 +1969,10 @@ Global
{6249A193-3BF4-4FFB-AB81-6590A8318889} = {874EA351-05EA-44F5-8B12-7A7F865ECEC5}
{F21F921E-1930-4BD5-B665-5EF1B82BD4D2} = {830A89EC-4029-4753-B25A-068BAE37DEC7}
{AEF07BC6-76D8-4A45-89D5-54FC4483863F} = {4981B3A5-4AFD-4191-BF7D-8692D9783D60}
{76FCBF5C-4DB2-4F6C-BB3C-91FD89735AC7} = {F534D4F8-5E3A-42FC-BCD7-4C2D6060F9C8}
{21FBDB19-0B8B-4F0F-8ED6-98560AD5DDA7} = {77CFE74A-32EE-400C-8930-5025E8555256}
{D47C77F3-49F4-4741-9786-6F68FC50587E} = {D173887B-AF42-4576-B9C1-96B9E9B3D9C0}
{96A5B854-2C72-49C8-A91B-2643B814DB2A} = {D47C77F3-49F4-4741-9786-6F68FC50587E}
{683D8264-2755-4455-A6D4-14F7A6AC7B54} = {D47C77F3-49F4-4741-9786-6F68FC50587E}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {6DCEDFEC-988E-4CB3-B45B-191EB5086E0C}
Expand Down
2 changes: 2 additions & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
<PackageVersion Include="Azure.Provisioning.AppConfiguration" Version="$(AzureProvisiongVersion)" />
<PackageVersion Include="Azure.Provisioning.ApplicationInsights" Version="1.0.0-alpha.20240918.1" />
<PackageVersion Include="Azure.Provisioning.CognitiveServices" Version="$(AzureProvisiongVersion)" />
<PackageVersion Include="Azure.Provisioning.ContainerRegistry" Version="1.0.0-alpha.20240912.5" />
<PackageVersion Include="Azure.Provisioning.AppContainers" Version="1.0.0-alpha.20240912.5" />
<PackageVersion Include="Azure.Provisioning.CosmosDB" Version="$(AzureProvisiongVersion)" />
<PackageVersion Include="Azure.Provisioning.EventHubs" Version="$(AzureProvisiongVersion)" />
<PackageVersion Include="Azure.Provisioning.KeyVault" Version="$(AzureProvisiongVersion)" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>$(DefaultTargetFramework)</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
<AspireProjectOrPackageReference Include="Aspire.Azure.Storage.Blobs" />
<AspireProjectOrPackageReference Include="Aspire.StackExchange.Redis" />
<AspireProjectOrPackageReference Include="Aspire.Microsoft.EntityFrameworkCore.Cosmos" />
<ProjectReference Include="..\..\Playground.ServiceDefaults\Playground.ServiceDefaults.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
@CosmosEndToEnd.ApiService_HostAddress = http://localhost:5193

GET {{CosmosEndToEnd.ApiService_HostAddress}}/
Accept: application/json

###
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Azure.Storage.Blobs;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
using StackExchange.Redis;

var builder = WebApplication.CreateBuilder(args);

builder.AddServiceDefaults();

builder.AddCosmosDbContext<TestCosmosContext>("account", "db");
builder.AddAzureBlobClient("blobs");
builder.AddRedisClient("cache");

var app = builder.Build();

app.MapDefaultEndpoints();
app.MapGet("/", async (BlobServiceClient bsc) =>
{
var container = bsc.GetBlobContainerClient("mycontainer");
await container.CreateIfNotExistsAsync();
var blobNameAndContent = Guid.NewGuid().ToString();
await container.UploadBlobAsync(blobNameAndContent, new BinaryData(blobNameAndContent));
var blobs = container.GetBlobsAsync();
var blobNames = new List<string>();
await foreach (var blob in blobs)
{
blobNames.Add(blob.Name);
}
return blobNames;
});

app.MapGet("/cosmos", async (TestCosmosContext context) =>
{
await context.Database.EnsureCreatedAsync();
context.Entries.Add(new EntityFrameworkEntry());
await context.SaveChangesAsync();
return await context.Entries.ToListAsync();
});

app.MapGet("/redis/ping", async (IConnectionMultiplexer connection) =>
{
return await connection.GetDatabase().PingAsync();
});

app.MapGet("/redis/set", async (IConnectionMultiplexer connection) =>
{
return await connection.GetDatabase().StringSetAsync("Key", $"{DateTime.Now}");
});

app.MapGet("/redis/get", async (IConnectionMultiplexer connection) =>
{
var redisValue = await connection.GetDatabase().StringGetAsync("Key");
return redisValue.HasValue ? redisValue.ToString() : "(null)";
});

app.Run();

public class Entry
{
[JsonProperty("id")]
public string? Id { get; set; }
}

public class TestCosmosContext(DbContextOptions<TestCosmosContext> options) : DbContext(options)
{
public DbSet<EntityFrameworkEntry> Entries { get; set; }
}

public class EntityFrameworkEntry
{
public Guid Id { get; set; } = Guid.NewGuid();
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "http://localhost:5193",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>$(DefaultTargetFramework)</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsAspireHost>true</IsAspireHost>
<UserSecretsId>9dc69458-f2b4-4306-9dc5-f7b8e398a3a9</UserSecretsId>
</PropertyGroup>

<ItemGroup>
<Compile Include="..\..\KnownResourceNames.cs" Link="KnownResourceNames.cs" />
</ItemGroup>

<ItemGroup>
<AspireProjectOrPackageReference Include="Aspire.Hosting.Azure.ContainerApps" />
<AspireProjectOrPackageReference Include="Aspire.Hosting.Azure.CosmosDB" />
<AspireProjectOrPackageReference Include="Aspire.Hosting.Azure.Storage" />
<AspireProjectOrPackageReference Include="Aspire.Hosting.Azure.Redis" />
<AspireProjectOrPackageReference Include="Aspire.Hosting.AppHost" />
<AspireProjectOrPackageReference Include="Aspire.Hosting.Redis" />

<ProjectReference Include="..\AzureContainerApps.ApiService\AzureContainerApps.ApiService.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#pragma warning disable AZPROVISION001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
// 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.Azure;

var builder = DistributedApplication.CreateBuilder(args);

// Testing secret parameters
var param = builder.AddParameter("secretparam", "fakeSecret", secret: true);

// Testing volumes
var redis = builder.AddRedis("cache")
.WithLifetime(ContainerLifetime.Persistent)
.WithDataVolume();

// Testing secret outputs
var cosmosDb = builder.AddAzureCosmosDB("account")
.RunAsEmulator(c => c.WithLifetime(ContainerLifetime.Persistent))
.AddDatabase("db");

// Testing a connection string
var blobs = builder.AddAzureStorage("storage")
.RunAsEmulator(c => c.WithLifetime(ContainerLifetime.Persistent))
.AddBlobs("blobs");

builder.AddProject<Projects.AzureContainerApps_ApiService>("api")
.WithExternalHttpEndpoints()
.WithReference(blobs)
.WithReference(redis)
.WithReference(cosmosDb)
.WithEnvironment("VALUE", param)
.PublishAsContainerApp((module, app) =>
{
// Scale to 0
app.Template.Value!.Scale.Value!.MinReplicas = 0;
});

#if !SKIP_DASHBOARD_REFERENCE
// This project is only added in playground projects to support development/debugging
// of the dashboard. It is not required in end developer code. Comment out this code
// or build with `/p:SkipDashboardReference=true`, to test end developer
// dashboard launch experience, Refer to Directory.Build.props for the path to
// the dashboard binary (defaults to the Aspire.Dashboard bin output in the
// artifacts dir).
builder.AddProject<Projects.Aspire_Dashboard>(KnownResourceNames.AspireDashboard);
#endif

builder.Build().Run();

Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"profiles": {
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "https://localhost:15887;http://localhost:15888",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"DOTNET_ENVIRONMENT": "Development",
"DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:16157",
"DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:17307",
"DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true"
}
},
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "http://localhost:15888",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"DOTNET_ENVIRONMENT": "Development",
"DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:16157",
"DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:17308",
"DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true",
"ASPIRE_ALLOW_UNSECURED_TRANSPORT": "true"
}
},
"generate-manifest": {
"commandName": "Project",
"launchBrowser": true,
"dotnetRunMessages": true,
"commandLineArgs": "--publisher manifest --output-path aspire-manifest.json",
"applicationUrl": "http://localhost:15888",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"DOTNET_ENVIRONMENT": "Development",
"DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:16157"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
@description('The location for the resource(s) to be deployed.')
param location string = resourceGroup().location

param keyVaultName string

resource keyVault 'Microsoft.KeyVault/vaults@2019-09-01' existing = {
name: keyVaultName
}

resource account 'Microsoft.DocumentDB/databaseAccounts@2024-05-15-preview' = {
name: take('account-${uniqueString(resourceGroup().id)}', 44)
location: location
properties: {
locations: [
{
locationName: location
failoverPriority: 0
}
]
consistencyPolicy: {
defaultConsistencyLevel: 'Session'
}
databaseAccountOfferType: 'Standard'
}
kind: 'GlobalDocumentDB'
tags: {
'aspire-resource-name': 'account'
}
}

resource db 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases@2024-05-15-preview' = {
name: 'db'
location: location
properties: {
resource: {
id: 'db'
}
}
parent: account
}

resource connectionString 'Microsoft.KeyVault/vaults/secrets@2019-09-01' = {
name: 'connectionString'
properties: {
value: 'AccountEndpoint=${account.properties.documentEndpoint};AccountKey=${account.listKeys().primaryMasterKey}'
}
parent: keyVault
}
Loading

0 comments on commit b0520ee

Please sign in to comment.