Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integrate LocalStack for Developing AWS Applications Locally #875

Draft
wants to merge 26 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
9e566b4
start integration of AWS services fixes dotnet#874
Blind-Striker Nov 16, 2023
6686fd8
create sample application with Aspire
Blind-Striker Nov 17, 2023
0a4e55b
create Aspire.AWS project and base for AWS Components
Blind-Striker Nov 17, 2023
ec5ac33
add AWS provisioning support
Blind-Striker Nov 17, 2023
295adf5
fix s3 provision configuration section
Blind-Striker Nov 17, 2023
1b78735
fix project path in the solution
Blind-Striker Nov 17, 2023
6008597
change Aspire.Hosting.AWS project description
Blind-Striker Nov 17, 2023
ee0f58b
remove .dockerignore file
Blind-Striker Nov 17, 2023
e4e5388
fix aws sample applications
Blind-Striker Nov 17, 2023
1cf1b59
add aws provisioner support
Blind-Striker Nov 17, 2023
2323186
add localstack support for provisioning
Blind-Striker Nov 17, 2023
30b4aaf
fix S3Provisioner to work with both AWS and LocalStack
Blind-Striker Nov 17, 2023
eb8305f
Merge branch 'main' into aws-basic-services
Blind-Striker Nov 17, 2023
233f9f4
Merge branch 'main' into aws-basic-services
danmoseley Nov 17, 2023
7c5f7b1
add constructs for aws cloud formation template
Blind-Striker Nov 19, 2023
71aa16d
add connection string support for AWS resources to make aspire to inj…
Blind-Striker Nov 19, 2023
9705daf
add necessary AWS SDKs POC
Blind-Striker Nov 19, 2023
52f23f4
make aws constructs public to use them in Provisioners
Blind-Striker Nov 19, 2023
85f4fa6
replace AWS SDK with CloudFormation for provisioning
Blind-Striker Nov 19, 2023
cad2318
Merge branch 'aws-basic-services' of github.com:Blind-Striker/aspire …
Blind-Striker Nov 19, 2023
e40e28d
add output support to constructs
Blind-Striker Nov 20, 2023
eb18327
add set resource outputs to resources feature
Blind-Striker Nov 20, 2023
b7861eb
add well-known resource properties to resources
Blind-Striker Nov 20, 2023
a2eb1f8
update the sample service to utilizes S3 and SNS
Blind-Striker Nov 20, 2023
fc9ac7c
Merge branch 'main' into aws-basic-services
Blind-Striker Nov 21, 2023
9d90857
Merge branch 'dotnet:main' into aws-basic-services
Blind-Striker Nov 22, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
30 changes: 30 additions & 0 deletions .dockerignore
@@ -0,0 +1,30 @@
**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/azds.yaml
**/bin
**/charts
**/docker-compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md
!**/.gitignore
!.git/HEAD
!.git/config
!.git/packed-refs
!.git/refs/heads/**
45 changes: 45 additions & 0 deletions Aspire.sln
Expand Up @@ -152,6 +152,20 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aspire.RabbitMQ.Client", "s
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aspire.RabbitMQ.Client.Tests", "tests\Aspire.RabbitMQ.Client.Tests\Aspire.RabbitMQ.Client.Tests.csproj", "{165411FE-755E-4869-A756-F87F455860AC}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aspire.Hosting.AWS", "src\Aspire.Hosting.AWS\Aspire.Hosting.AWS.csproj", "{7738E898-5D00-45C2-A498-EA7C56E31A27}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aspire.AWS", "src\Components\Aspire.AWS\Aspire.AWS.csproj", "{B2B9CCC6-3DC5-45AA-BBC0-A4258286828B}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "aws", "aws", "{4FE4E999-1D3B-466F-802E-0389EF07EC8E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AwsAppHost.AppHost", "samples\aws\AwsAppHost\AwsAppHost.AppHost\AwsAppHost.AppHost.csproj", "{CF63B177-463A-42F4-BC34-6AD05702E754}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AwsAppHost.ServiceDefaults", "samples\aws\AwsAppHost\AwsAppHost.ServiceDefaults\AwsAppHost.ServiceDefaults.csproj", "{E320453A-DF78-4676-94DF-E5568FC4A548}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UserService", "samples\aws\AwsAppHost\UserService\UserService.csproj", "{202D78B2-CC0D-4F18-A7E1-A20F12D686A7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aspire.Hosting.AWS.Provisioning", "src\Aspire.Hosting.AWS.Provisioning\Aspire.Hosting.AWS.Provisioning.csproj", "{A2A42422-6FC1-4B3C-A495-DAC86829A381}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -406,6 +420,30 @@ Global
{165411FE-755E-4869-A756-F87F455860AC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{165411FE-755E-4869-A756-F87F455860AC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{165411FE-755E-4869-A756-F87F455860AC}.Release|Any CPU.Build.0 = Release|Any CPU
{7738E898-5D00-45C2-A498-EA7C56E31A27}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7738E898-5D00-45C2-A498-EA7C56E31A27}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7738E898-5D00-45C2-A498-EA7C56E31A27}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7738E898-5D00-45C2-A498-EA7C56E31A27}.Release|Any CPU.Build.0 = Release|Any CPU
{B2B9CCC6-3DC5-45AA-BBC0-A4258286828B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B2B9CCC6-3DC5-45AA-BBC0-A4258286828B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B2B9CCC6-3DC5-45AA-BBC0-A4258286828B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B2B9CCC6-3DC5-45AA-BBC0-A4258286828B}.Release|Any CPU.Build.0 = Release|Any CPU
{CF63B177-463A-42F4-BC34-6AD05702E754}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CF63B177-463A-42F4-BC34-6AD05702E754}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CF63B177-463A-42F4-BC34-6AD05702E754}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CF63B177-463A-42F4-BC34-6AD05702E754}.Release|Any CPU.Build.0 = Release|Any CPU
{E320453A-DF78-4676-94DF-E5568FC4A548}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E320453A-DF78-4676-94DF-E5568FC4A548}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E320453A-DF78-4676-94DF-E5568FC4A548}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E320453A-DF78-4676-94DF-E5568FC4A548}.Release|Any CPU.Build.0 = Release|Any CPU
{202D78B2-CC0D-4F18-A7E1-A20F12D686A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{202D78B2-CC0D-4F18-A7E1-A20F12D686A7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{202D78B2-CC0D-4F18-A7E1-A20F12D686A7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{202D78B2-CC0D-4F18-A7E1-A20F12D686A7}.Release|Any CPU.Build.0 = Release|Any CPU
{A2A42422-6FC1-4B3C-A495-DAC86829A381}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A2A42422-6FC1-4B3C-A495-DAC86829A381}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A2A42422-6FC1-4B3C-A495-DAC86829A381}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A2A42422-6FC1-4B3C-A495-DAC86829A381}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -477,6 +515,13 @@ Global
{A84C4EE3-2601-4804-BCDC-E9948E164A22} = {A68BA1A5-1604-433D-9778-DC0199831C2A}
{4D8A92AB-4E77-4965-AD8E-8E206DCE66A4} = {27381127-6C45-4B4C-8F18-41FF48DFE4B2}
{165411FE-755E-4869-A756-F87F455860AC} = {4981B3A5-4AFD-4191-BF7D-8692D9783D60}
{7738E898-5D00-45C2-A498-EA7C56E31A27} = {B80354C7-BE58-43F6-8928-9F3A74AB7F47}
{B2B9CCC6-3DC5-45AA-BBC0-A4258286828B} = {27381127-6C45-4B4C-8F18-41FF48DFE4B2}
{4FE4E999-1D3B-466F-802E-0389EF07EC8E} = {D173887B-AF42-4576-B9C1-96B9E9B3D9C0}
{CF63B177-463A-42F4-BC34-6AD05702E754} = {4FE4E999-1D3B-466F-802E-0389EF07EC8E}
{E320453A-DF78-4676-94DF-E5568FC4A548} = {4FE4E999-1D3B-466F-802E-0389EF07EC8E}
{202D78B2-CC0D-4F18-A7E1-A20F12D686A7} = {4FE4E999-1D3B-466F-802E-0389EF07EC8E}
{A2A42422-6FC1-4B3C-A495-DAC86829A381} = {B80354C7-BE58-43F6-8928-9F3A74AB7F47}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {6DCEDFEC-988E-4CB3-B45B-191EB5086E0C}
Expand Down
6 changes: 5 additions & 1 deletion Directory.Packages.props
Expand Up @@ -16,6 +16,10 @@
<PackageVersion Include="Azure.Storage.Queues" Version="12.16.0" />
<PackageVersion Include="Microsoft.Azure.Cosmos" Version="3.36.0-preview" />
<PackageVersion Include="Microsoft.Extensions.Azure" Version="1.7.1" />
<!-- AWS SDK for .NET dependencies -->
<PackageVersion Include="AWSSDK.Core" Version="3.7.300.3" />
<PackageVersion Include="AWSSDK.S3" Version="3.7.300.3" />
<PackageVersion Include="AWSSDK.Extensions.NETCore.Setup" Version="3.7.300" />
<!-- Azure Management SDK for .NET dependencies -->
<PackageVersion Include="Azure.ResourceManager.CosmosDB" Version="1.4.0-beta.4" />
<PackageVersion Include="Azure.ResourceManager.KeyVault" Version="1.2.0-beta.2" />
Expand Down Expand Up @@ -100,4 +104,4 @@
<PackageVersion Include="Microsoft.Signed.Wix" Version="1.0.0-v3.14.0.5722" />
<PackageVersion Include="Microsoft.DotNet.Build.Tasks.Installers" Version="8.0.0-beta.23371.1" />
</ItemGroup>
</Project>
</Project>
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsAspireHost>true</IsAspireHost>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\..\src\Aspire.Hosting.AWS\Aspire.Hosting.AWS.csproj" />
<ProjectReference Include="..\..\..\..\src\Aspire.Hosting\Aspire.Hosting.csproj" />
</ItemGroup>

</Project>
8 changes: 8 additions & 0 deletions samples/aws/AwsAppHost/AwsAppHost.AppHost/Program.cs
@@ -0,0 +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 Aspire.Hosting;

var builder = DistributedApplication.CreateBuilder(args);

builder.Build().Run();
@@ -0,0 +1,28 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "http://localhost:15180",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"DOTNET_ENVIRONMENT": "Development",
"DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:16031"
}
}
},
"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:16031"
}
}
}
@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
9 changes: 9 additions & 0 deletions samples/aws/AwsAppHost/AwsAppHost.AppHost/appsettings.json
@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Aspire.Hosting.Dcp": "Warning"
}
}
}
@@ -0,0 +1,3 @@
{
"resources": {}
}
@@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsAspireSharedProject>true</IsAspireSharedProject>
</PropertyGroup>

<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />

<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" />
<PackageReference Include="OpenTelemetry.Exporter.Prometheus.AspNetCore" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" />
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" />
<PackageReference Include="OpenTelemetry.Instrumentation.GrpcNetClient" />
<PackageReference Include="OpenTelemetry.Instrumentation.Http" />
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" />

<PackageReference Include="Microsoft.Extensions.Http.Resilience" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\..\src\Microsoft.Extensions.ServiceDiscovery.Dns\Microsoft.Extensions.ServiceDiscovery.Dns.csproj" />
<ProjectReference Include="..\..\..\..\src\Microsoft.Extensions.ServiceDiscovery\Microsoft.Extensions.ServiceDiscovery.csproj" />
</ItemGroup>

</Project>
119 changes: 119 additions & 0 deletions samples/aws/AwsAppHost/AwsAppHost.ServiceDefaults/Extensions.cs
@@ -0,0 +1,119 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.Extensions.Logging;
using OpenTelemetry.Logs;
using OpenTelemetry.Metrics;
using OpenTelemetry.Trace;

namespace Microsoft.Extensions.Hosting;

public static class Extensions
{
public static IHostApplicationBuilder AddServiceDefaults(this IHostApplicationBuilder builder)
{
builder.ConfigureOpenTelemetry();

builder.AddDefaultHealthChecks();

builder.Services.AddServiceDiscovery();

builder.Services.ConfigureHttpClientDefaults(http =>
{
// Turn on resilience by default
http.AddStandardResilienceHandler();

// Turn on service discovery by default
http.UseServiceDiscovery();
});

return builder;
}

public static IHostApplicationBuilder ConfigureOpenTelemetry(this IHostApplicationBuilder builder)
{
builder.Logging.AddOpenTelemetry(logging =>
{
logging.IncludeFormattedMessage = true;
logging.IncludeScopes = true;
});

builder.Services.AddOpenTelemetry()
.WithMetrics(metrics =>
{
metrics.AddRuntimeInstrumentation()
.AddBuiltInMeters();
})
.WithTracing(tracing =>
{
if (builder.Environment.IsDevelopment())
{
// We want to view all traces in development
tracing.SetSampler(new AlwaysOnSampler());
}

tracing.AddAspNetCoreInstrumentation()
.AddGrpcClientInstrumentation()
.AddHttpClientInstrumentation();
});

builder.AddOpenTelemetryExporters();

return builder;
}

private static IHostApplicationBuilder AddOpenTelemetryExporters(this IHostApplicationBuilder builder)
{
var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]);

if (useOtlpExporter)
{
builder.Services.Configure<OpenTelemetryLoggerOptions>(logging => logging.AddOtlpExporter());
builder.Services.ConfigureOpenTelemetryMeterProvider(metrics => metrics.AddOtlpExporter());
builder.Services.ConfigureOpenTelemetryTracerProvider(tracing => tracing.AddOtlpExporter());
}

// Uncomment the following lines to enable the Prometheus exporter (requires the OpenTelemetry.Exporter.Prometheus.AspNetCore package)
// builder.Services.AddOpenTelemetry()
// .WithMetrics(metrics => metrics.AddPrometheusExporter());

// Uncomment the following lines to enable the Azure Monitor exporter (requires the Azure.Monitor.OpenTelemetry.Exporter package)
// builder.Services.AddOpenTelemetry()
// .UseAzureMonitor();

return builder;
}

public static IHostApplicationBuilder AddDefaultHealthChecks(this IHostApplicationBuilder builder)
{
builder.Services.AddHealthChecks()
// Add a default liveness check to ensure app is responsive
.AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]);

return builder;
}

public static WebApplication MapDefaultEndpoints(this WebApplication app)
{
// Uncomment the following line to enable the Prometheus endpoint (requires the OpenTelemetry.Exporter.Prometheus.AspNetCore package)
// app.MapPrometheusScrapingEndpoint();

// All health checks must pass for app to be considered ready to accept traffic after starting
app.MapHealthChecks("/health");

// Only health checks tagged with the "live" tag must pass for app to be considered alive
app.MapHealthChecks("/alive", new HealthCheckOptions
{
Predicate = r => r.Tags.Contains("live")
});

return app;
}

private static MeterProviderBuilder AddBuiltInMeters(this MeterProviderBuilder meterProviderBuilder) =>
meterProviderBuilder.AddMeter(
"Microsoft.AspNetCore.Hosting",
"Microsoft.AspNetCore.Server.Kestrel",
"System.Net.Http");
}
@@ -0,0 +1,32 @@
using Microsoft.AspNetCore.Mvc;

namespace UserService.Controllers;

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] s_summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};

private readonly ILogger<WeatherForecastController> _logger;

public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}

[HttpGet(Name = "GetWeatherForecast")]
public IEnumerable<WeatherForecast> Get()
{
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = s_summaries[Random.Shared.Next(s_summaries.Length)]
})
.ToArray();
}
}