Skip to content

Commit d10b79e

Browse files
captainsafiaeerhardtdavidfowlradical
authored
Add AzureFunctionsEndToEnd with prototype implementation (#5418)
* Add AzureFunctionsEndToEnd with prototype implementation * Updat EventHub extension package to latest mirrored version * Fix RandomString implementation * Hack to make the emulators work * More cleanup * Primary constructors * More cleanup * Added Aspire.Hosting.Azure.Functions * Experiment with specifying the host storage * Separate host storage from the app storage * First cut of IResourceWithAzureFunctionsConfig and removed WithReference - We now scan all of the env variables and resolve references instead. * Tested with managed identity and worked around issues - Blob triggers require queues - There's a conflict with the connection string section * Implement the functions interface on storage as well * Simulate modeling Azure Functions as project resources * Make it work * Remove the port from launch settings. * Remove other interfaces from functions resource * Simplify functions sample - Delete the web app * Add smoke test for launching Functions app * Always run inner functions build * Wire up package version for Aspire.Hosting.Azure.Functions * Fix AppHost reference for test project * Move AzureFunctionsEndToEnd tests to project specific tests * Add Requires attributes to AzureFunctions tests * Skip AzureFunctionsTest on Helix until core tools is installed * Add deps on azure functions cli on helix * fix helix payload definition * Fix playground tests build * Fix cli payload for helix * Set destinationPath on helix payload, the default is the root directory * Fix typo * another round - fix path * simplify payload spec * Update docstrings and README for Aspire.Hosting.Azure.Functions * Appease the linter * Conditionally skip EventHubs emulation in CI * Skip AzureFunctionsTest * Use [ActiveIssue] to skip test in CI * More feedback * Update tests/helix/send-to-helix-inner.proj Co-authored-by: Ankit Jain <radical@gmail.com> * Disable eventhubs on codespaces which doesn't have docker desktop * Update HTTP trigger to use ASP.NET-based trigger * Try 1: patch extensions.json copy bug * Try 2: patch extensions.json copy bug * Try 3: patch extensions.json copy bug * Try only build AzureFunctionsEndToEnd project once in CI * Bring back manual patch for extensions.json * Enable include conditionally * Update README with workaround for build bug * Fix markdown lint warnings * Add note about lack of OTel support --------- Co-authored-by: Eric Erhardt <eric.erhardt@microsoft.com> Co-authored-by: David Fowler <davidfowl@gmail.com> Co-authored-by: Ankit Jain <radical@gmail.com>
1 parent edd3232 commit d10b79e

File tree

47 files changed

+1022
-11
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+1022
-11
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,4 +147,4 @@ node_modules/
147147
*.svclog
148148

149149
# Python virtual environments
150-
.venv
150+
.venv

Aspire.sln

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
12
Microsoft Visual Studio Solution File, Format Version 12.00
23
# Visual Studio Version 17
34
VisualStudioVersion = 17.0.31903.59
@@ -583,6 +584,16 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aspire.Hosting.Dapr.Tests",
583584
EndProject
584585
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aspire.Hosting.AWS.Tests", "tests\Aspire.Hosting.AWS.Tests\Aspire.Hosting.AWS.Tests.csproj", "{6F71BC73-B703-4E64-98E0-801781302E7A}"
585586
EndProject
587+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AzureFunctionsEndToEnd", "AzureFunctionsEndToEnd", "{305D5B56-8782-493C-BD44-9860F8616D92}"
588+
EndProject
589+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AzureFunctionsEndToEnd.ApiService", "playground\AzureFunctionsEndToEnd\AzureFunctionsEndToEnd.ApiService\AzureFunctionsEndToEnd.ApiService.csproj", "{659AF918-57A4-4616-B3D0-24FCE38DF12A}"
590+
EndProject
591+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AzureFunctionsEndToEnd.AppHost", "playground\AzureFunctionsEndToEnd\AzureFunctionsEndToEnd.AppHost\AzureFunctionsEndToEnd.AppHost.csproj", "{13025E2D-2E2B-4319-8754-0B12F324283E}"
592+
EndProject
593+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AzureFunctionsEndToEnd.Functions", "playground\AzureFunctionsEndToEnd\AzureFunctionsEndToEnd.Functions\AzureFunctionsEndToEnd.Functions.csproj", "{79D2E40E-95EC-4BAF-8382-E5669B8E6E42}"
594+
EndProject
595+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aspire.Hosting.Azure.Functions", "src\Aspire.Hosting.Azure.Functions\Aspire.Hosting.Azure.Functions.csproj", "{A8FFEB1F-B128-48D0-A114-5C94FF770551}"
596+
EndProject
586597
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "waitfor", "waitfor", "{3FF3F00C-95C0-46FC-B2BE-A3920C71E393}"
587598
EndProject
588599
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WaitForSandbox.AppHost", "playground\waitfor\WaitForSandbox.AppHost\WaitForSandbox.AppHost.csproj", "{415E011A-1C56-41A1-BAEB-CA5D5CED1A57}"
@@ -1547,6 +1558,22 @@ Global
15471558
{6F71BC73-B703-4E64-98E0-801781302E7A}.Debug|Any CPU.Build.0 = Debug|Any CPU
15481559
{6F71BC73-B703-4E64-98E0-801781302E7A}.Release|Any CPU.ActiveCfg = Release|Any CPU
15491560
{6F71BC73-B703-4E64-98E0-801781302E7A}.Release|Any CPU.Build.0 = Release|Any CPU
1561+
{659AF918-57A4-4616-B3D0-24FCE38DF12A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
1562+
{659AF918-57A4-4616-B3D0-24FCE38DF12A}.Debug|Any CPU.Build.0 = Debug|Any CPU
1563+
{659AF918-57A4-4616-B3D0-24FCE38DF12A}.Release|Any CPU.ActiveCfg = Release|Any CPU
1564+
{659AF918-57A4-4616-B3D0-24FCE38DF12A}.Release|Any CPU.Build.0 = Release|Any CPU
1565+
{13025E2D-2E2B-4319-8754-0B12F324283E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
1566+
{13025E2D-2E2B-4319-8754-0B12F324283E}.Debug|Any CPU.Build.0 = Debug|Any CPU
1567+
{13025E2D-2E2B-4319-8754-0B12F324283E}.Release|Any CPU.ActiveCfg = Release|Any CPU
1568+
{13025E2D-2E2B-4319-8754-0B12F324283E}.Release|Any CPU.Build.0 = Release|Any CPU
1569+
{79D2E40E-95EC-4BAF-8382-E5669B8E6E42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
1570+
{79D2E40E-95EC-4BAF-8382-E5669B8E6E42}.Debug|Any CPU.Build.0 = Debug|Any CPU
1571+
{79D2E40E-95EC-4BAF-8382-E5669B8E6E42}.Release|Any CPU.ActiveCfg = Release|Any CPU
1572+
{79D2E40E-95EC-4BAF-8382-E5669B8E6E42}.Release|Any CPU.Build.0 = Release|Any CPU
1573+
{A8FFEB1F-B128-48D0-A114-5C94FF770551}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
1574+
{A8FFEB1F-B128-48D0-A114-5C94FF770551}.Debug|Any CPU.Build.0 = Debug|Any CPU
1575+
{A8FFEB1F-B128-48D0-A114-5C94FF770551}.Release|Any CPU.ActiveCfg = Release|Any CPU
1576+
{A8FFEB1F-B128-48D0-A114-5C94FF770551}.Release|Any CPU.Build.0 = Release|Any CPU
15501577
{415E011A-1C56-41A1-BAEB-CA5D5CED1A57}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15511578
{415E011A-1C56-41A1-BAEB-CA5D5CED1A57}.Debug|Any CPU.Build.0 = Debug|Any CPU
15521579
{415E011A-1C56-41A1-BAEB-CA5D5CED1A57}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -1844,6 +1871,11 @@ Global
18441871
{091EA540-355B-4763-9980-5F83F0BB6F11} = {15966C27-17FA-4A46-A172-55985411540A}
18451872
{C60C5CFA-5B6D-4432-BFCD-54D1BEEC7DBE} = {830A89EC-4029-4753-B25A-068BAE37DEC7}
18461873
{6F71BC73-B703-4E64-98E0-801781302E7A} = {830A89EC-4029-4753-B25A-068BAE37DEC7}
1874+
{305D5B56-8782-493C-BD44-9860F8616D92} = {D173887B-AF42-4576-B9C1-96B9E9B3D9C0}
1875+
{659AF918-57A4-4616-B3D0-24FCE38DF12A} = {305D5B56-8782-493C-BD44-9860F8616D92}
1876+
{13025E2D-2E2B-4319-8754-0B12F324283E} = {305D5B56-8782-493C-BD44-9860F8616D92}
1877+
{79D2E40E-95EC-4BAF-8382-E5669B8E6E42} = {305D5B56-8782-493C-BD44-9860F8616D92}
1878+
{A8FFEB1F-B128-48D0-A114-5C94FF770551} = {77CFE74A-32EE-400C-8930-5025E8555256}
18471879
{3FF3F00C-95C0-46FC-B2BE-A3920C71E393} = {D173887B-AF42-4576-B9C1-96B9E9B3D9C0}
18481880
{415E011A-1C56-41A1-BAEB-CA5D5CED1A57} = {3FF3F00C-95C0-46FC-B2BE-A3920C71E393}
18491881
{C554C480-3DA7-4D62-A09A-3F3F743D7A66} = {3FF3F00C-95C0-46FC-B2BE-A3920C71E393}

Directory.Build.targets

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,21 @@
2424
</ItemGroup>
2525

2626
<Import Project="$(RepositoryEngineeringDir)/apicompat/PublicApiAnalyzer.targets" />
27+
28+
<!-- HACK: This resolves issues in CI builds where the extensions.json file is mapped to the incorrect directory. We only want
29+
to copy the updated extensions.json file once the `_FunctionsExtensionUpdateMetadata` target has completed. -->
30+
<Target Name="_FunctionsAssignTargetPathsFix" AfterTargets="_FunctionsAssignTargetPaths" BeforeTargets="_FunctionsExtensionUpdateMetadata">
31+
<ItemGroup>
32+
<None Remove="$(_FunctionsIntermediateExtensionUpdatedJsonPath)"
33+
CopyToOutputDirectory="PreserveNewest"
34+
CopyToPublishDirectory="PreserveNewest"
35+
TargetPath="$(_FunctionsExtensionsJsonName)" />
36+
<None
37+
Include="$(_FunctionsIntermediateExtensionUpdatedJsonPath)"
38+
CopyToOutputDirectory="PreserveNewest"
39+
CopyToPublishDirectory="PreserveNewest"
40+
TargetPath="$(_FunctionsExtensionsJsonName)"
41+
Condition="Exists('$(_FunctionsIntermediateExtensionUpdatedJsonPath)')"/>
42+
</ItemGroup>
43+
</Target>
2744
</Project>

Directory.Packages.props

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,9 +167,18 @@
167167
<PackageVersion Include="Microsoft.Orleans.Client" Version="8.2.0" />
168168
<PackageVersion Include="Microsoft.Orleans.Server" Version="8.2.0" />
169169
<PackageVersion Include="Microsoft.Orleans.Sdk" Version="8.2.0" />
170+
<!-- playground apps dependencies for AzureFunctionsEndToEnd -->
171+
<PackageVersion Include="Microsoft.Azure.Functions.Worker" Version="1.23.0" />
172+
<PackageVersion Include="Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore" Version="1.3.2" />
173+
<PackageVersion Include="Microsoft.Azure.Functions.Worker.Extensions.Storage.Blobs" Version="6.6.0" />
174+
<PackageVersion Include="Microsoft.Azure.Functions.Worker.Extensions.Storage.Queues" Version="5.5.0" />
175+
<PackageVersion Include="Microsoft.Azure.Functions.Worker.Extensions.EventHubs" Version="6.3.5"/>
176+
<PackageVersion Include="Microsoft.Azure.Functions.Worker.OpenTelemetry" Version="1.0.0-preview1" />
177+
<PackageVersion Include="Microsoft.Azure.Functions.Worker.Sdk" Version="1.17.4" />
178+
<PackageVersion Include="Microsoft.ApplicationInsights.WorkerService" Version="2.22.0" />
170179
<!-- Pinned version for Component Governance - Remove when root dependencies are updated -->
171180
<PackageVersion Include="Azure.Core" Version="1.42.0" />
172181
<PackageVersion Include="Azure.Identity" Version="1.12.0" />
173182
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" /> <!-- https://github.com/Azure/azure-cosmos-dotnet-v3/pull/3313 -->
174183
</ItemGroup>
175-
</Project>
184+
</Project>
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<Project Sdk="Microsoft.NET.Sdk.Web">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net8.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<ProjectReference Include="..\..\Playground.ServiceDefaults\Playground.ServiceDefaults.csproj" />
11+
</ItemGroup>
12+
13+
<ItemGroup>
14+
<AspireProjectOrPackageReference Include="Aspire.Azure.Storage.Blobs" />
15+
<AspireProjectOrPackageReference Include="Aspire.Azure.Storage.Queues" />
16+
<AspireProjectOrPackageReference Include="Aspire.Azure.Messaging.EventHubs" />
17+
</ItemGroup>
18+
19+
</Project>
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
using System.Security.Cryptography;
2+
using System.Text;
3+
#if !SKIP_EVENTHUBS_EMULATION
4+
using Azure.Messaging.EventHubs;
5+
using Azure.Messaging.EventHubs.Producer;
6+
#endif
7+
using Azure.Storage.Blobs;
8+
using Azure.Storage.Queues;
9+
10+
var builder = WebApplication.CreateBuilder(args);
11+
12+
// Add service defaults & Aspire components.
13+
builder.AddServiceDefaults();
14+
builder.AddAzureQueueClient("queue");
15+
builder.AddAzureBlobClient("blob");
16+
#if !SKIP_EVENTHUBS_EMULATION
17+
builder.AddAzureEventHubProducerClient("eventhubs", static settings => settings.EventHubName = "myhub");
18+
#endif
19+
20+
var app = builder.Build();
21+
22+
app.MapGet("/publish/asq", async (QueueServiceClient client, CancellationToken cancellationToken) =>
23+
{
24+
var queue = client.GetQueueClient("queue");
25+
await queue.CreateIfNotExistsAsync(cancellationToken: cancellationToken);
26+
var data = Convert.ToBase64String(Encoding.UTF8.GetBytes("Hello, World!"));
27+
await queue.SendMessageAsync(data, cancellationToken: cancellationToken);
28+
return Results.Ok("Message sent to Azure Storage Queue.");
29+
});
30+
31+
static string RandomString(int length)
32+
{
33+
const string chars = "abcdefghijklmnopqrstuvwxyz";
34+
return RandomNumberGenerator.GetString(chars, length);
35+
}
36+
37+
app.MapGet("/publish/blob", async (BlobServiceClient client, CancellationToken cancellationToken, int length = 20) =>
38+
{
39+
var container = client.GetBlobContainerClient("blobs");
40+
await container.CreateIfNotExistsAsync(cancellationToken: cancellationToken);
41+
42+
var entry = new { Id = Guid.NewGuid(), Text = RandomString(length) };
43+
var blob = container.GetBlobClient(entry.Id.ToString());
44+
45+
await blob.UploadAsync(new BinaryData(entry));
46+
47+
return Results.Ok("String uploaded to Azure Storage Blobs.");
48+
});
49+
50+
#if !SKIP_EVENTHUBS_EMULATION
51+
app.MapGet("/publish/eventhubs", async (EventHubProducerClient client, CancellationToken cancellationToken, int length = 20) =>
52+
{
53+
var data = new BinaryData(Encoding.UTF8.GetBytes(RandomString(length)));
54+
await client.SendAsync([new EventData(data)]);
55+
return Results.Ok("Message sent to Azure EventHubs.");
56+
});
57+
#endif
58+
59+
app.MapGet("/", async (HttpClient client) =>
60+
{
61+
var stream = await client.GetStreamAsync("http://funcapp/api/weatherforecast");
62+
return Results.Stream(stream, "application/json");
63+
});
64+
65+
app.MapDefaultEndpoints();
66+
67+
app.Run();
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"$schema": "https://json.schemastore.org/launchsettings.json",
3+
"profiles": {
4+
"http": {
5+
"commandName": "Project",
6+
"dotnetRunMessages": true,
7+
"launchBrowser": true,
8+
"launchUrl": "publish/asq",
9+
"applicationUrl": "http://localhost:5313",
10+
"environmentVariables": {
11+
"ASPNETCORE_ENVIRONMENT": "Development"
12+
}
13+
},
14+
"https": {
15+
"commandName": "Project",
16+
"dotnetRunMessages": true,
17+
"launchBrowser": true,
18+
"launchUrl": "publish/asq",
19+
"applicationUrl": "https://localhost:7314;http://localhost:5313",
20+
"environmentVariables": {
21+
"ASPNETCORE_ENVIRONMENT": "Development"
22+
}
23+
}
24+
}
25+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"Logging": {
3+
"LogLevel": {
4+
"Default": "Information",
5+
"Microsoft.AspNetCore": "Warning"
6+
}
7+
}
8+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"Logging": {
3+
"LogLevel": {
4+
"Default": "Information",
5+
"Microsoft.AspNetCore": "Warning"
6+
}
7+
},
8+
"AllowedHosts": "*"
9+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net8.0</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
<IsAspireHost>true</IsAspireHost>
9+
<UserSecretsId>d824db17-effc-4f8c-aa80-f0ae6aba93eb</UserSecretsId>
10+
</PropertyGroup>
11+
12+
<ItemGroup>
13+
<AspireProjectOrPackageReference Include="Aspire.Hosting.Azure.Functions" />
14+
<AspireProjectOrPackageReference Include="Aspire.Hosting.Azure.EventHubs" />
15+
<AspireProjectOrPackageReference Include="Aspire.Hosting.Azure.Storage" />
16+
<AspireProjectOrPackageReference Include="Aspire.Hosting.AppHost" />
17+
</ItemGroup>
18+
19+
<ItemGroup>
20+
<ProjectReference Include="..\AzureFunctionsEndToEnd.ApiService\AzureFunctionsEndToEnd.ApiService.csproj" />
21+
<ProjectReference Include="..\AzureFunctionsEndToEnd.Functions\AzureFunctionsEndToEnd.Functions.csproj" />
22+
</ItemGroup>
23+
24+
</Project>

0 commit comments

Comments
 (0)