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

Azure Provisioning Integration #2429

Merged
merged 17 commits into from
Mar 1, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion Aspire.sln
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DatabaseMigration.AppHost",
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DatabaseMigration.MigrationService", "playground\DatabaseMigration\DatabaseMigration.MigrationService\DatabaseMigration.MigrationService.csproj", "{E7DB736B-C316-460E-A609-2200E58BF0C2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DatabaseMigration.ApiModel", "playground\DatabaseMigration\DatabaseMigration.ApiModel\DatabaseMigration.ApiModel.csproj", "{C15F3F13-AB63-47CF-AAFE-D319F02E7B33}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DatabaseMigration.ApiModel", "playground\DatabaseMigration\DatabaseMigration.ApiModel\DatabaseMigration.ApiModel.csproj", "{C15F3F13-AB63-47CF-AAFE-D319F02E7B33}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "signalr", "signalr", "{E6985EED-47E3-4EAC-8222-074E5410CEDC}"
EndProject
Expand All @@ -281,6 +281,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProxylessEndToEnd.ApiServic
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProxylessEndToEnd.AppHost", "playground\ProxylessEndToEnd\ProxylessEndToEnd.AppHost\ProxylessEndToEnd.AppHost.csproj", "{0244203D-7491-4414-9C88-10BFED9C5B2D}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "cdk", "cdk", "{C3F48531-87D9-4E52-90AC-715A3E55751A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CdkSample.AppHost", "playground\cdk\CdkSample.AppHost\CdkSample.AppHost.csproj", "{A357411A-5909-4A49-9519-12A935F84395}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CdkSample.ApiService", "playground\cdk\CdkSample.ApiService\CdkSample.ApiService.csproj", "{4601F5A2-E445-41B2-9C1F-2CE016642E62}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -735,6 +741,14 @@ Global
{0244203D-7491-4414-9C88-10BFED9C5B2D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0244203D-7491-4414-9C88-10BFED9C5B2D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0244203D-7491-4414-9C88-10BFED9C5B2D}.Release|Any CPU.Build.0 = Release|Any CPU
{A357411A-5909-4A49-9519-12A935F84395}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A357411A-5909-4A49-9519-12A935F84395}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A357411A-5909-4A49-9519-12A935F84395}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A357411A-5909-4A49-9519-12A935F84395}.Release|Any CPU.Build.0 = Release|Any CPU
{4601F5A2-E445-41B2-9C1F-2CE016642E62}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4601F5A2-E445-41B2-9C1F-2CE016642E62}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4601F5A2-E445-41B2-9C1F-2CE016642E62}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4601F5A2-E445-41B2-9C1F-2CE016642E62}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -869,6 +883,9 @@ Global
{9C30FFD6-2262-45E7-B010-24B30E0433C2} = {D173887B-AF42-4576-B9C1-96B9E9B3D9C0}
{51654CD7-2E05-4664-B2EB-95308A300609} = {9C30FFD6-2262-45E7-B010-24B30E0433C2}
{0244203D-7491-4414-9C88-10BFED9C5B2D} = {9C30FFD6-2262-45E7-B010-24B30E0433C2}
{C3F48531-87D9-4E52-90AC-715A3E55751A} = {D173887B-AF42-4576-B9C1-96B9E9B3D9C0}
{A357411A-5909-4A49-9519-12A935F84395} = {C3F48531-87D9-4E52-90AC-715A3E55751A}
{4601F5A2-E445-41B2-9C1F-2CE016642E62} = {C3F48531-87D9-4E52-90AC-715A3E55751A}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {6DCEDFEC-988E-4CB3-B45B-191EB5086E0C}
Expand Down
6 changes: 4 additions & 2 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@
<PackageVersion Include="Microsoft.Azure.SignalR" Version="1.25.0" />
<PackageVersion Include="Microsoft.Extensions.Azure" Version="1.7.2" />
<!-- Azure Management SDK for .NET dependencies -->
<PackageVersion Include="Azure.ResourceManager" Version="1.11.0-alpha.20240219.2" />
<PackageVersion Include="Azure.ResourceManager.Authorization" Version="1.1.0" />
<PackageVersion Include="Azure.ResourceManager.KeyVault" Version="1.2.0" />
<PackageVersion Include="Azure.ResourceManager.Resources" Version="1.7.0" />
<PackageVersion Include="Azure.ResourceManager.KeyVault" Version="1.3.0-alpha.20240219.2" />
<PackageVersion Include="Azure.ResourceManager.Resources" Version="1.8.0-alpha.20240219.2" />
<PackageVersion Include="Azure.Provisioning" Version="1.0.0-alpha.20240220.1" />
<!-- ASP.NET Core dependencies -->
<PackageVersion Include="Microsoft.AspNetCore.OpenApi" Version="$(MicrosoftAspNetCoreOpenApiPackageVersion)" />
<PackageVersion Include="Microsoft.AspNetCore.OutputCaching.StackExchangeRedis" Version="$(MicrosoftAspNetCoreOutputCachingStackExchangeRedisPackageVersion)" />
Expand Down
8 changes: 7 additions & 1 deletion NuGet.config
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,15 @@
<add key="dotnet8" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet8/nuget/v3/index.json" />
<add key="dotnet-libraries" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-libraries/nuget/v3/index.json" />
<add key="orleans-nightly" value="https://orleans.pkgs.visualstudio.com/orleans-public/_packaging/orleans-nightly/nuget/v3/index.json" />
<add key="azure-sdk-for-net-dev" value="https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-net/nuget/v3/index.json" />
</packageSources>
<packageSourceMapping>
<packageSource key="dotnet-public">
<packageSource key="azure-sdk-for-net-dev">
<package pattern="Azure.Provisioning" />
<package pattern="Azure.ResourceManager" />
<package pattern="Azure.ResourceManager.*" />
</packageSource>
<packageSource key="dotnet-public">
<package pattern="*" />
</packageSource>
<packageSource key="dotnet8">
Expand Down
13 changes: 13 additions & 0 deletions playground/cdk/CdkSample.ApiService/CdkSample.ApiService.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\Playground.ServiceDefaults\Playground.ServiceDefaults.csproj" />
</ItemGroup>

</Project>
6 changes: 6 additions & 0 deletions playground/cdk/CdkSample.ApiService/CdkSample.ApiService.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
@CosmosEndToEnd.ApiService_HostAddress = http://localhost:5193

GET {{SqlServerEndToEnd.ApiService_HostAddress}}/weatherforecast/
Accept: application/json

###
15 changes: 15 additions & 0 deletions playground/cdk/CdkSample.ApiService/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

var builder = WebApplication.CreateBuilder(args);

builder.AddServiceDefaults();

var app = builder.Build();

app.MapGet("/", () =>
{
return "Hello, Azure!";
});

app.Run();
14 changes: 14 additions & 0 deletions playground/cdk/CdkSample.ApiService/Properties/launchSettings.json
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:5180",
"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"
}
}
}
9 changes: 9 additions & 0 deletions playground/cdk/CdkSample.ApiService/appsettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
23 changes: 23 additions & 0 deletions playground/cdk/CdkSample.AppHost/CdkSample.AppHost.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsAspireHost>true</IsAspireHost>
<UserSecretsId>dafae173-3ac0-4100-8cab-602852cb28dd</UserSecretsId>
</PropertyGroup>

<ItemGroup>
<Compile Include="$(SharedDir)KnownResourceNames.cs" Link="KnownResourceNames.cs" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\src\Aspire.Dashboard\Aspire.Dashboard.csproj" />
<ProjectReference Include="..\..\..\src\Aspire.Hosting.Azure.Provisioning\Aspire.Hosting.Azure.Provisioning.csproj" IsAspireProjectResource="false" />

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

</Project>
8 changes: 8 additions & 0 deletions playground/cdk/CdkSample.AppHost/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<Project>

<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />

<!-- NOTE: This line is only required because we are using P2P references, not NuGet. It will not exist in real apps. -->
<Import Project="../../../src/Aspire.Hosting/build/Aspire.Hosting.props" />

</Project>
9 changes: 9 additions & 0 deletions playground/cdk/CdkSample.AppHost/Directory.Build.targets
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<Project>

<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.targets', '$(MSBuildThisFileDirectory)../'))" />

<!-- NOTE: These lines are only required because we are using P2P references, not NuGet. They will not exist in real apps. -->
<Import Project="..\..\..\src\Aspire.Hosting\build\Aspire.Hosting.targets" />
<Import Project="..\..\..\src\Aspire.Hosting.Sdk\SDK\Sdk.targets" />

</Project>
19 changes: 19 additions & 0 deletions playground/cdk/CdkSample.AppHost/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

var builder = DistributedApplication.CreateBuilder(args);

// This is just an empty resource. Not referenced, but
// just part of the .NET Aspire application model.
builder.AddCdkResource("empty");

builder.AddProject<Projects.CdkSample_ApiService>("api");

// 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
// 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);

builder.Build().Run();
29 changes: 29 additions & 0 deletions playground/cdk/CdkSample.AppHost/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"profiles": {
"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:16175",
"DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "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:16175"
}
}
}
}
8 changes: 8 additions & 0 deletions playground/cdk/CdkSample.AppHost/appsettings.Development.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
12 changes: 12 additions & 0 deletions playground/cdk/CdkSample.AppHost/appsettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Aspire.Hosting.Dcp": "Warning"
}
},
"Parameters": {
"val": "value from config"
}
}
1 change: 1 addition & 0 deletions src/Aspire.Hosting.Azure/Aspire.Hosting.Azure.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
<ItemGroup>
<ProjectReference Include="..\Aspire.Hosting\Aspire.Hosting.csproj" />
<PackageReference Include="System.IO.Hashing" />
<PackageReference Include="Azure.Provisioning" />
</ItemGroup>

<ItemGroup>
Expand Down
112 changes: 112 additions & 0 deletions src/Aspire.Hosting.Azure/CdkResource.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// 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.Lifecycle;
using Azure.Provisioning;
using Azure.Provisioning.ResourceManager;
using AspireResource = Aspire.Hosting.ApplicationModel.Resource;

namespace Aspire.Hosting;

/// <summary>
/// An Aspire resource which is also a CDK construct.
/// </summary>
/// <param name="name"></param>
public class CdkResource(string name) : AspireResource(name)
{

}

/// <summary>
/// TODO:
/// </summary>
/// <param name="name"></param>
public class AzureStorageCdkResource(string name) : CdkResource(name)
{

}

/// <summary>
/// Extensions for working with CDK resources in the .NET Aspire application model.
/// </summary>
public static class CdkResourceExtensions
{
/// <summary>
/// Adds a CDK resource to the application model.
/// </summary>
/// <param name="builder">The distributed application builder.</param>
/// <param name="name">The name of the resource being added.</param>
/// <param name="callback">A callback used to configure the resource.</param>
/// <returns></returns>
public static IResourceBuilder<CdkResource> AddCdkResource(this IDistributedApplicationBuilder builder, string name, Action<AspireResourceConstruct>? callback = null)
{
builder.Services.TryAddLifecycleHook<CdkLifecycleHook>();

callback ??= static (construct) => { };

var resource = new CdkResource(name);
return builder.AddResource(resource)
.WithAnnotation(new AspireResourceConstructCallbackAnnotation(callback), ResourceAnnotationMutationBehavior.Replace);
}
}

/// <summary>
/// An Azure.Provisioning construct which maps to an Aspire Resource.
/// </summary>
public class AspireResourceConstruct(IConstruct? scope, string name, ConstructScope constructScope = ConstructScope.ResourceGroup, Guid? tenantId = null, Guid? subscriptionId = null, string? envName = null, ResourceGroup? resourceGroup = null) : Construct(scope, name, constructScope, tenantId, subscriptionId, envName, resourceGroup)
{
}

internal class AspireInfrastructureConstruct(string envName) : Infrastructure(envName: envName)
{
}

/// <summary>
/// TODO: Doc comments.
/// </summary>
public class AspireResourceConstructCallbackAnnotation(Action<AspireResourceConstruct> callback) : IResourceAnnotation
{
/// <summary>
/// TODO: Doc comments.
/// </summary>
public Action<AspireResourceConstruct> Callback = callback;
}

internal class CdkLifecycleHook(DistributedApplicationExecutionContext executionContext) : IDistributedApplicationLifecycleHook
{

public Task BeforeStartAsync(DistributedApplicationModel appModel, CancellationToken cancellationToken = default)
{
if (executionContext.IsRunMode)
{
return Task.CompletedTask;
}

var cdkResources = appModel.Resources.OfType<CdkResource>();
var infrastructure = new AspireInfrastructureConstruct("test");

// Firstly suppress individual publishing of all CDK resources to the manifest.
foreach (var resource in cdkResources)
{
if (resource.Annotations.OfType<ManifestPublishingCallbackAnnotation>().SingleOrDefault() is { } existingAnnotation)
{
resource.Annotations.Remove(existingAnnotation);
}

resource.Annotations.Add(ManifestPublishingCallbackAnnotation.Ignore);

var resourceConstruct = new AspireResourceConstruct(infrastructure, resource.Name);
var callbackAnnotation = resource.Annotations.OfType<AspireResourceConstructCallbackAnnotation>().Single();
callbackAnnotation.Callback(resourceConstruct);
}

var path = Path.GetTempFileName();
infrastructure.Build(path);
// TODO: Insert code that spits out a Bicep resource to the manifest and generates
// the Bicep based on the CDK resources?

return Task.CompletedTask;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,7 @@ public async Task KubernetesHasResourceNameForContainersAndExes()
}
}

await foreach(var resource in s.WatchAsync<Executable>(cancellationToken: token))
await foreach (var resource in s.WatchAsync<Executable>(cancellationToken: token))
{
Assert.True(resource.Item2.Metadata.Annotations.TryGetValue(Executable.ResourceNameAnnotation, out var value));
if (expectedExeResources.Contains(value))
Expand Down