Skip to content
Merged
Show file tree
Hide file tree
Changes from 87 commits
Commits
Show all changes
92 commits
Select commit Hold shift + click to select a range
d11b66a
Initial addition of CosmosDB support, based on SqlServer
Pilchie Oct 3, 2023
3374fb6
Remove Healthchecks support from CosmosDB EF Component
Pilchie Oct 11, 2023
1e60bf5
DO NOT MERGE: Update sample to use CosmosDB instead of PostgreSQL
Pilchie Oct 1, 2023
4eaae64
Cleanup connection string handling in Cosmos EF
Pilchie Oct 17, 2023
6421e60
Cleanup connection string handling in Cosmos component
Pilchie Oct 17, 2023
07d8284
Update CosmosDB package to get OTel support
Pilchie Oct 17, 2023
8dae4bd
Use the parent name for the connection
Pilchie Oct 17, 2023
f706813
Revert last change
Pilchie Oct 17, 2023
3f8b528
Fix text build break
Pilchie Oct 17, 2023
0ac6633
Remove cruft around configuration schema
Pilchie Oct 17, 2023
ed5c74b
Udpate manifest strings
Pilchie Oct 18, 2023
8b72bf0
Add CosmosDB components to Progress and Telemetry
Pilchie Oct 18, 2023
a98aaee
Rename CosmosDB components to Aspire.Azure.Data.Cosmos[.EntityFramewo…
Pilchie Oct 18, 2023
3f0bb37
Rename Cosmos DB directories
Pilchie Oct 18, 2023
4bd8129
Rename some namespaces and related
Pilchie Oct 18, 2023
9d7442a
Rename options -> settings
Pilchie Oct 19, 2023
aff38a2
Rename Cosmos Components to follow naming guidelines
Pilchie Oct 20, 2023
9cf1d52
Merge remote-tracking branch 'upstream/main' into azure-cosmosdb
Pilchie Oct 24, 2023
a4a9b29
Fix mis-merges
Pilchie Oct 25, 2023
9b412bd
Update to CosmosDB preview package and pin to get OpenTelemetry support
Pilchie Oct 25, 2023
4709022
Merge remote-tracking branch 'upstream/main' into azure-cosmosdb
Pilchie Oct 26, 2023
dcab2df
Update comments and add Keyed DI to Aspire.Microsoft.Azure.Cosmos
Pilchie Oct 27, 2023
2067377
Update exception message
Pilchie Oct 27, 2023
ce8f094
Add log categories to Cosmos Component schemas
Pilchie Oct 27, 2023
46db475
Add basic support for CosmosClientOptions (no IConfiguration binding …
Pilchie Oct 27, 2023
49908bd
Remove healthchecks support from CosmosDB Component
Pilchie Oct 30, 2023
e5c96ac
Add README for Aspire.Microsoft.Azure.Cosmos
Pilchie Oct 30, 2023
8d04a70
Add README for Aspire.Microsoft.EntityFrameworkCore.Cosmos, and renam…
Pilchie Oct 30, 2023
0af6420
Update config schema to be nested for Aspire.Microsoft.EntityFramewor…
Pilchie Oct 30, 2023
c9f434c
Rename AzureDataCosmosSettings -> AzureCosmosDBSettings
Pilchie Oct 30, 2023
c880d96
Remove metrics support from Aspire.Microsoft.Azure.Cosmos
Pilchie Oct 30, 2023
e495d03
Remove WithAzureCosmosDB extension from hosting
Pilchie Oct 30, 2023
f2feb2b
Remove setting for DatabaseName from Aspire.Microsoft.EntityFramework…
Pilchie Oct 30, 2023
053e84b
Rename AzureEntityFrameworkCoreCosmosDBSettings -> EntityFrameworkCor…
Pilchie Oct 30, 2023
2765016
Update Aspire_Components_Progress.md
Pilchie Oct 30, 2023
6770b32
Prepare to make Aspire install by default on upgrades for web workloa…
danegsta Oct 26, 2023
77d4b68
Text alignment with ellipsis (#536)
tlmii Oct 26, 2023
e8a6bda
Metrics page alignment (#537)
tlmii Oct 26, 2023
447e1d3
Provide an explicit target to generate the Aspire manifest (#534)
baronfel Oct 27, 2023
64c0994
Fix one place where service discovery isn't AOT compatible (#540)
davidfowl Oct 27, 2023
735035e
Updated otel versions to latest (#542)
davidfowl Oct 27, 2023
362baf5
Add log level select (#541)
JamesNK Oct 27, 2023
254e82e
Display Container ID (#544)
tlmii Oct 27, 2023
b0db3db
Enable ConfigurationBinder source gen on components by default (#535)
eerhardt Oct 27, 2023
952f5fd
Service Discovery: make host name propagation opt-in (#548)
ReubenBond Oct 27, 2023
3f03c80
Fix Aspire.Npgsql.csproj to be packable (#549)
eerhardt Oct 27, 2023
0f413bd
Update dependencies from https://github.com/dotnet/arcade build 20231…
dotnet-maestro[bot] Oct 27, 2023
c491f20
Fix metrics applications list (#552)
tlmii Oct 27, 2023
bc01c19
Add unit test coverage for redis, postgres, containers (#551)
BrennanConroy Oct 27, 2023
bf0e4fa
Add new DistributedApplicationOptions object, refactor DCP location c…
danegsta Oct 27, 2023
e580324
Use correct metadata name (#555)
danegsta Oct 27, 2023
484f53b
Add RabbitMQ Component (#556)
eerhardt Oct 28, 2023
8573329
Support running multiple instances of DAB in the same process (#560)
davidfowl Oct 28, 2023
f1a8a79
Display new error logs count to projects page (#545)
JamesNK Oct 28, 2023
2c9699e
Update Azure dependencies to latest versions (#563)
eerhardt Oct 29, 2023
7474fd1
Fixes creation of user secrets when user secrets does not yet exist (…
bjorkstromm Oct 29, 2023
3584144
Various log filter fixes (#571)
JamesNK Oct 30, 2023
5ab2c0b
Update metrics page to use SelectViewModel (#573)
JamesNK Oct 30, 2023
1cb8dcd
Change Dashboard to use Fluent UI vNext (#575)
vnbaaij Oct 30, 2023
49af7e4
[main] Update dependencies from microsoft/usvc-apiserver (#577)
dotnet-maestro[bot] Oct 30, 2023
5653395
Simplify view model caching (#557)
smitpatel Oct 30, 2023
bce48ec
Details Views (#561)
tlmii Oct 30, 2023
828f5dd
Account for case sensitive paths on nix systems (#584)
danegsta Oct 31, 2023
54326ee
Adds Azure hosting support for App Configuration (#574)
bjorkstromm Oct 31, 2023
20138fa
Switch to using Path.GetRandomFileName() from uuid (#587)
danegsta Oct 31, 2023
6cbcac5
Fix trace order (#588)
JamesNK Oct 31, 2023
6ed5925
Add TokenCredential support to Aspire.Microsoft.EntityFrameworkCore.C…
Pilchie Oct 31, 2023
92ecb4c
Add TokenCredential support to Aspire.Microsoft.Azure.Cosmos
Pilchie Oct 31, 2023
61d05e9
Merge remote-tracking branch 'upstream/main' into azure-cosmosdb
Pilchie Oct 31, 2023
85a66a8
Add empty RatingsService
Pilchie Oct 31, 2023
4f01310
Add a RatingsService and use it from the frontend
Pilchie Nov 1, 2023
8d76cc1
Revert changes to catalogdb
Pilchie Nov 1, 2023
0130296
Update expected service lifetime to singleton
Pilchie Nov 1, 2023
f22856d
Revert "Add a RatingsService and use it from the frontend"
Pilchie Nov 1, 2023
c86dec1
Revert "Add empty RatingsService"
Pilchie Nov 1, 2023
97aa291
Apply suggestions from code review
Pilchie Nov 1, 2023
b372614
Merge remote-tracking branch 'upstream/main' into azure-cosmosdb
Pilchie Nov 1, 2023
7898ce9
Fix project type guid
Pilchie Nov 1, 2023
932723c
Remove TODO
Pilchie Nov 1, 2023
6214639
Fix typo in exception message
Pilchie Nov 1, 2023
24c71a4
Add PackageTags, Descriptions, and Icons
Pilchie Nov 1, 2023
138579d
Add AccountEndpoint to ConfigurationScheama.json
Pilchie Nov 1, 2023
3f53c4f
Remove healthcheck from comment
Pilchie Nov 1, 2023
0a40231
A couple more code review fixes
Pilchie Nov 1, 2023
3209cc5
WIP
Pilchie Nov 1, 2023
2f89327
Merge remote-tracking branch 'upstream/main' into azure-cosmosdb
Pilchie Nov 1, 2023
ea41461
Fix DB context builder config
Pilchie Nov 2, 2023
2a17893
Add xml doc comments for CosmosDB hosting methods and types
Pilchie Nov 2, 2023
0058fa1
Move Cosmos DB hosting to Aspire.Hosting.Azure
Pilchie Nov 2, 2023
2a3d1cc
Update manifest type names
Pilchie Nov 2, 2023
f58be30
Merge branch 'main' into azure-cosmosdb
mitchdenny Nov 2, 2023
9595b88
Respond to PR feedback
eerhardt Nov 2, 2023
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
28 changes: 28 additions & 0 deletions Aspire.sln
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aspire.Hosting.Azure.Provis
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "eShopLite", "eShopLite", "{A68BA1A5-1604-433D-9778-DC0199831C2A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aspire.Microsoft.Azure.Cosmos", "src\Components\Aspire.Microsoft.Azure.Cosmos\Aspire.Microsoft.Azure.Cosmos.csproj", "{23298562-C1D4-41CD-83FE-426C94FEE35F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aspire.Microsoft.EntityFrameworkCore.Cosmos", "src\Components\Aspire.Microsoft.EntityFrameworkCore.Cosmos\Aspire.Microsoft.EntityFrameworkCore.Cosmos.csproj", "{00C9BA50-2AFB-4D9C-A2D6-8154BCCD0A63}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aspire.Microsoft.Azure.Cosmos.Tests", "tests\Aspire.Microsoft.Azure.Cosmos.Tests\Aspire.Microsoft.Azure.Cosmos.Tests.csproj", "{A5836BC1-6A45-4BB6-9D22-A7F750890AB8}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aspire.Microsoft.EntityFrameworkCore.Cosmos.Tests", "tests\Aspire.Microsoft.EntityFrameworkCore.Cosmos.Tests\Aspire.Microsoft.EntityFrameworkCore.Cosmos.Tests.csproj", "{FDA02617-9C49-4DA8-A43A-A34DBA9B8596}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CatalogDb", "samples\eShopLite\CatalogDb\CatalogDb.csproj", "{A84C4EE3-2601-4804-BCDC-E9948E164A22}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{991DB378-6CB5-4441-BFC3-657400690FC3}"
Expand Down Expand Up @@ -370,6 +378,22 @@ Global
{D4BD974F-6505-43FC-A94E-2019F0DB5D5D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D4BD974F-6505-43FC-A94E-2019F0DB5D5D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D4BD974F-6505-43FC-A94E-2019F0DB5D5D}.Release|Any CPU.Build.0 = Release|Any CPU
{23298562-C1D4-41CD-83FE-426C94FEE35F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{23298562-C1D4-41CD-83FE-426C94FEE35F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{23298562-C1D4-41CD-83FE-426C94FEE35F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{23298562-C1D4-41CD-83FE-426C94FEE35F}.Release|Any CPU.Build.0 = Release|Any CPU
{00C9BA50-2AFB-4D9C-A2D6-8154BCCD0A63}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{00C9BA50-2AFB-4D9C-A2D6-8154BCCD0A63}.Debug|Any CPU.Build.0 = Debug|Any CPU
{00C9BA50-2AFB-4D9C-A2D6-8154BCCD0A63}.Release|Any CPU.ActiveCfg = Release|Any CPU
{00C9BA50-2AFB-4D9C-A2D6-8154BCCD0A63}.Release|Any CPU.Build.0 = Release|Any CPU
{A5836BC1-6A45-4BB6-9D22-A7F750890AB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A5836BC1-6A45-4BB6-9D22-A7F750890AB8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A5836BC1-6A45-4BB6-9D22-A7F750890AB8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A5836BC1-6A45-4BB6-9D22-A7F750890AB8}.Release|Any CPU.Build.0 = Release|Any CPU
{FDA02617-9C49-4DA8-A43A-A34DBA9B8596}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FDA02617-9C49-4DA8-A43A-A34DBA9B8596}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FDA02617-9C49-4DA8-A43A-A34DBA9B8596}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FDA02617-9C49-4DA8-A43A-A34DBA9B8596}.Release|Any CPU.Build.0 = Release|Any CPU
{A84C4EE3-2601-4804-BCDC-E9948E164A22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A84C4EE3-2601-4804-BCDC-E9948E164A22}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A84C4EE3-2601-4804-BCDC-E9948E164A22}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand Down Expand Up @@ -446,6 +470,10 @@ Global
{E2EC79D0-80F7-4471-9613-D7C8C3D52F95} = {B80354C7-BE58-43F6-8928-9F3A74AB7F47}
{D4BD974F-6505-43FC-A94E-2019F0DB5D5D} = {B80354C7-BE58-43F6-8928-9F3A74AB7F47}
{A68BA1A5-1604-433D-9778-DC0199831C2A} = {D173887B-AF42-4576-B9C1-96B9E9B3D9C0}
{23298562-C1D4-41CD-83FE-426C94FEE35F} = {27381127-6C45-4B4C-8F18-41FF48DFE4B2}
{00C9BA50-2AFB-4D9C-A2D6-8154BCCD0A63} = {27381127-6C45-4B4C-8F18-41FF48DFE4B2}
{A5836BC1-6A45-4BB6-9D22-A7F750890AB8} = {4981B3A5-4AFD-4191-BF7D-8692D9783D60}
{FDA02617-9C49-4DA8-A43A-A34DBA9B8596} = {4981B3A5-4AFD-4191-BF7D-8692D9783D60}
{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}
Expand Down
4 changes: 3 additions & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
<PackageVersion Include="Azure.Security.KeyVault.Secrets" Version="4.5.0" />
<PackageVersion Include="Azure.Storage.Blobs" Version="12.18.0" />
<PackageVersion Include="Azure.Storage.Queues" Version="12.16.0" />
<PackageVersion Include="Microsoft.Azure.Cosmos" Version="3.36.0-preview" />

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like 3.36.0 is out now.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CosmosDB does versioning wrong right now :(. 3.36.0-preview is actually 3.36.0 + preview stuff. In this case it's the difference between distributed tracing being on or off by default, so I'm happy to change if you want.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is some messed up versioning for sure. Definitely not how the rest of the ecosystem does it.

<PackageVersion Include="Microsoft.Extensions.Azure" Version="1.7.1" />
<!-- Azure Management SDK for .NET dependencies -->
<PackageVersion Include="Azure.ResourceManager.KeyVault" Version="1.2.0-beta.2" />
Expand Down Expand Up @@ -45,6 +46,7 @@
<!-- sql client dependencies -->
<PackageVersion Include="Microsoft.Data.SqlClient" Version="5.1.1" />
<!-- efcore dependencies -->
<PackageVersion Include="Microsoft.EntityFrameworkCore.Cosmos" Version="$(EfCoreVersion)" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="$(EfCoreVersion)" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="$(EfCoreVersion)" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="$(EfCoreVersion)" />
Expand Down Expand Up @@ -101,4 +103,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>
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Licensed to the .NET Foundation under one or more agreements.
Comment thread
Pilchie marked this conversation as resolved.
Outdated
// The .NET Foundation licenses this file to you under the MIT license.

using Aspire.Hosting.ApplicationModel;
using Aspire.Hosting.Azure.Data.Cosmos;
using System.Text.Json;

namespace Aspire.Hosting;

public static class AzureCosmosDBCloudApplicationBuilderExtensions

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have unit tests for these?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sigh - Now I moved them into Aspire.Hosting.Azure, and there is no test project at all for that :/

{
public static IResourceBuilder<CosmosDBConnectionResource> AddAzureCosmosDB(
Comment thread
Pilchie marked this conversation as resolved.
Outdated
this IDistributedApplicationBuilder builder,
string name,
string? connectionString = null)
{
var connection = new CosmosDBConnectionResource(name, connectionString);
return builder.AddResource(connection)
.WithAnnotation(new ManifestPublishingCallbackAnnotation(jsonWriter => WriteCosmosDBConnectionToManifest(jsonWriter, connection)));
}

private static void WriteCosmosDBConnectionToManifest(Utf8JsonWriter jsonWriter, CosmosDBConnectionResource cosmosDbConnection)
{
jsonWriter.WriteString("type", "azure.data.cosmos.connection.v1");
Comment thread
Pilchie marked this conversation as resolved.
Outdated
jsonWriter.WriteString("connectionString", cosmosDbConnection.GetConnectionString());
}

private static void WriteCosmosDBDatabaseToManifest(Utf8JsonWriter jsonWriter, CosmosDatabaseResource cosmosDatabase)
{
jsonWriter.WriteString("type", "azure.data.cosmos.server.v1");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this type name correct? If this resource has a parent, then the server should be the parent resource, and this should be database or collection or something like that.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No idea. I have no intuition about what should be in the manifest, or what it's used for. Any docs/specs/etc that would help me reason about it would be great.

@mitchdenny mitchdenny Nov 2, 2023

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Take a look at samples/eshoplite/aspire-manifest.json. We have a postgres server and postgres database. The database has a parent field that points to the server indicating a relationship between the two:

https://github.com/dotnet/aspire/blob/ae57cb153b48daf2b08e0b31d054548950e72dc2/samples/eShopLite/AppHost/aspire-manifest.json#L3-L9

The idea is that the tool that processes the manifest can use this information to pre-create the database for the application because typically code running on the app server wouldn't have permissions to perform these kinds of operations.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So in this case the hierarchy is:

  • Azure Subscription
  • one or more Azure Cosmos DB Accounts
  • one or more Databases
  • one or more Containers

It would be ideal to be able to model all the way down to the Container level and use the control plane SDK to provision these. (for example, Managed Identities in the data plane SDK don't support control plane operations, though they do with keys). Today it's a bit of a mix. You need a connection string to connect to an account, but then you need a name to get the database. That's why there is that weird extra parameter to the AddCosmosDbContext method, since EF needs to know the database name to use.

I initially tried to model this better, but couldn't figure out how to flow the connection string from the Connection resource (which should maybe be renamed Account?), as well. I'd love some advice on how to model this properly, though maybe we could do that in a separate PR?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we'll need to write a provisioner to give reasonable feedback here.

jsonWriter.WriteString("parent", cosmosDatabase.Parent.Name);
jsonWriter.WriteString("databaseName", cosmosDatabase.Name);
}

public static IResourceBuilder<CosmosDatabaseResource> AddDatabase(this IResourceBuilder<CosmosDBConnectionResource> builder, string name)
{
var cosmosDatabase = new CosmosDatabaseResource(name, builder.Resource);
return builder
.ApplicationBuilder
.AddResource(cosmosDatabase)
.WithAnnotation(new ManifestPublishingCallbackAnnotation(
(json) => WriteCosmosDBDatabaseToManifest(json, cosmosDatabase)));
}
}
12 changes: 12 additions & 0 deletions src/Aspire.Hosting/CosmosDB/CosmosDBConnectionResource.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// 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;

namespace Aspire.Hosting.Azure.Data.Cosmos;

public class CosmosDBConnectionResource(string name, string? connectionString)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll probably need to have CosmosDbAccountResource as well I suspect to represent a resource that we want the tool reading the manifest to interpret.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you elaborate on the difference and the scenario?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@eerhardt thoughts on this one?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure. @mitchdenny - can you elaborate?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When we are dealing with resource types like this where there is a child resource involved we'll typically end up dealing with three types:

  1. A type that represents a manually supplied connection string.
  2. A type that represents the "container type" ... e.g. a cosmos account.
  3. A type that represents the "contained type" ... e.g. a database.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per above, I think there is actually another level of nesting for Cosmos DB Account -> Database -> Container. However, the ConnectionString only uniquely identifies the Account, so I'm not sure how to fit that into Aspire right now.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, and to make this tricky - for the EF version, the containers it uses will have properties that depend on the code that sets up the (for example what the partition key is).

: Resource(name), IResourceWithConnectionString
{
public string? GetConnectionString() => connectionString;
}
17 changes: 17 additions & 0 deletions src/Aspire.Hosting/CosmosDB/CosmosDatabaseResource.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// 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;

namespace Aspire.Hosting.Azure.Data.Cosmos;

public class CosmosDatabaseResource(string name, CosmosDBConnectionResource parent)
: Resource(name), IResourceWithParent<CosmosDBConnectionResource>, IResourceWithConnectionString
{
public CosmosDBConnectionResource Parent { get; } = parent;

public string? GetConnectionString()
{
return Parent.GetConnectionString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>$(NetCurrent)</TargetFramework>
<IsPackable>true</IsPackable>
<EnableConfigurationBindingGenerator>false</EnableConfigurationBindingGenerator>
<IsAotCompatible>false</IsAotCompatible>
Comment thread
Pilchie marked this conversation as resolved.
<PackageTags>$(ComponentAzurePackageTags) cosmos cosmosdb data database db</PackageTags>
<Description>A client for Azure Cosmos DB that integrates with Aspire, including logging and telemetry.</Description>
<PackageIconFullPath>$(SharedDir)AzureCosmosDB_256x.png</PackageIconFullPath>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Azure.Identity" />
<PackageReference Include="Microsoft.Azure.Cosmos" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" />
</ItemGroup>

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

using Aspire.Microsoft.Azure.Cosmos;
using Azure.Identity;
using Microsoft.Azure.Cosmos;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace Microsoft.Extensions.Hosting;

/// <summary>
/// Azure CosmosDB extension
/// </summary>
public static class AspireAzureCosmosDBExtensions
{
private const string DefaultConfigSectionName = "Aspire:Microsoft:Azure:Cosmos";

/// <summary>
/// Registers <see cref="CosmosClient" /> as a singleton in the services provided by the <paramref name="builder"/>.
/// Configures logging and telemetry for the <see cref="CosmosClient" />.
/// </summary>
/// <param name="builder">The <see cref="IHostApplicationBuilder" /> to read config from and add services to.</param>
/// <param name="connectionName">The connection name to use to find a connection string.</param>
/// <param name="configureSettings">An optional method that can be used for customizing the <see cref="AzureCosmosDBSettings"/>. It's invoked after the settings are read from the configuration.</param>
/// <param name="configureClientOptions">An optional method that can be used for customizing the <see cref="CosmosClientOptions"/>.</param>
/// <remarks>Reads the configuration from "Aspire:Microsoft:Azure:Cosmos" section.</remarks>
/// <exception cref="InvalidOperationException">If required ConnectionString is not provided in configuration section</exception>
public static void AddAzureCosmosDB(
this IHostApplicationBuilder builder,
string connectionName,
Action<AzureCosmosDBSettings>? configureSettings = null,
Action<CosmosClientOptions>? configureClientOptions = null)
{
AddAzureCosmosDB(builder, DefaultConfigSectionName, configureSettings, configureClientOptions, connectionName, serviceKey: null);
}

/// <summary>
/// Registers <see cref="CosmosClient" /> as a singleton for given <paramref name="name" /> in the services provided by the <paramref name="builder"/>.
/// Configures logging and telemetry for the <see cref="CosmosClient" />.
/// </summary>
/// <param name="builder">The <see cref="IHostApplicationBuilder" /> to read config from and add services to.</param>
/// <param name="name">The name of the component, which is used as the <see cref="ServiceDescriptor.ServiceKey"/> of the service and also to retrieve the connection string from the ConnectionStrings configuration section.</param>
/// <param name="configureSettings">An optional method that can be used for customizing the <see cref="AzureCosmosDBSettings"/>. It's invoked after the settings are read from the configuration.</param>
/// <param name="configureClientOptions">An optional method that can be used for customizing the <see cref="CosmosClientOptions"/>.</param>
/// <remarks>Reads the configuration from "Aspire:Microsoft:Azure:Cosmos:{name}" section.</remarks>
/// <exception cref="InvalidOperationException">If required ConnectionString is not provided in configuration section</exception>
public static void AddKeyedAzureCosmosDB(
this IHostApplicationBuilder builder,
string name,
Action<AzureCosmosDBSettings>? configureSettings = null,
Action<CosmosClientOptions>? configureClientOptions = null)
{
AddAzureCosmosDB(builder, $"{DefaultConfigSectionName}:{name}", configureSettings, configureClientOptions, connectionName: name, serviceKey: name);
}

private static void AddAzureCosmosDB(
this IHostApplicationBuilder builder,
string configurationSectionName,
Action<AzureCosmosDBSettings>? configureSettings,
Action<CosmosClientOptions>? configureClientOptions,
string connectionName,
string? serviceKey)
{
ArgumentNullException.ThrowIfNull(builder);

AzureCosmosDBSettings settings = new();
builder.Configuration.GetSection(configurationSectionName).Bind(settings);

if (builder.Configuration.GetConnectionString(connectionName) is string connectionString)
{
if (Uri.TryCreate(connectionString, UriKind.Absolute, out var uri))
{
settings.AccountEndpoint = uri;
}
else
{
settings.ConnectionString = connectionString;
}
}

configureSettings?.Invoke(settings);
var clientOptions = new CosmosClientOptions();

// Needs to be enabled for either logging or tracing to work.
clientOptions.CosmosClientTelemetryOptions.DisableDistributedTracing = false;

if (settings.Tracing)
{

builder.Services.AddOpenTelemetry().WithTracing(tracerProviderBuilder =>
{
tracerProviderBuilder.AddSource("Azure.Cosmos.Operation");
});
}

configureClientOptions?.Invoke(clientOptions);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The clientOptions should be bound to the config, so users can configure them using normal configuration.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's on my todolist, but it's not easy, since I don't think many parts of the clientOptions type are actually serializable. Thoughts there, or it doesn't matter because those parts just won't be settable in config?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I've been doing is walking through the type and looking for simple properties. You can see all of them in the other Azure components schema, or in the RabbitMQ schema I just did a few days ago:

https://github.com/dotnet/aspire/blob/dd917e5daa91f855e101d87bc7e164a5c4f03e4e/src/Components/Aspire.RabbitMQ.Client/ConfigurationSchema.json#L12-L43

And then add a test for it like so:

https://github.com/dotnet/aspire/blob/dd917e5daa91f855e101d87bc7e164a5c4f03e4e/tests/Aspire.RabbitMQ.Client.Tests/AspireRabbitMQExtensionsTests.cs#L112-L158

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we consider merging this and I'll expand on that in a separate PR?


if (serviceKey is null)
{
builder.Services.AddSingleton(_ => ConfigureDb());
}
else
{
builder.Services.AddKeyedSingleton(serviceKey, (sp, key) => ConfigureDb());
}

CosmosClient ConfigureDb()
{
if (!string.IsNullOrEmpty(settings.ConnectionString))
{
return new CosmosClient(settings.ConnectionString, clientOptions);
}
else if (settings.AccountEndpoint is not null)
{
var credential = settings.Credential ?? new DefaultAzureCredential();
return new CosmosClient(settings.AccountEndpoint.OriginalString, credential, clientOptions);
}
else
{
throw new InvalidOperationException(
$"A CosmosClient could not be configured. Ensure valid connection information was provided in 'ConnectionStrings:{connectionName}' or either " +
$"{nameof(settings.ConnectionString)} or {nameof(settings.AccountEndpoint)} must be provided " +
$"in the '{configurationSectionName}' configuration section.");
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Azure.Core;

namespace Aspire.Microsoft.Azure.Cosmos;

/// <summary>
/// The settings relevant to accessing Azure Cosmos DB.
/// </summary>
public sealed class AzureCosmosDBSettings
{
/// <summary>
/// Gets or sets the connection string of the Azure Cosmos database to connect to.
/// </summary>
public string? ConnectionString { get; set; }

/// <summary>
/// <para>Gets or sets a boolean value that indicates whether the OpenTelemetry tracing is enabled or not.</para>
/// <para>Enabled by default.</para>
/// </summary>
public bool Tracing { get; set; } = true;

/// <summary>
/// A <see cref="Uri"/> referencing the Azure Cosmos DB Endpoint.
/// This is likely to be similar to "https://{account_name}.queue.core.windows.net".
/// </summary>
/// <remarks>
/// Must not contain shared access signature.
/// Used along with <see cref="Credential"/> to establish the connection.
/// </remarks>
public Uri? AccountEndpoint { get; set; }

/// <summary>
/// Gets or sets the credential used to authenticate to the Azure Cosmos DB endpoint.
/// </summary>
public TokenCredential? Credential { get; set; }
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"definitions": {
"logLevel": {
"properties": {
"Azure-Cosmos-Operation-Request-Diagnostics": {
"$ref": "#/definitions/logLevelThreshold"
}
}
}
},
"properties": {
Comment thread
Pilchie marked this conversation as resolved.
"Aspire": {
"type": "object",
"properties": {
"Microsoft": {
"type": "object",
"properties": {
"Azure": {
"type": "object",
"properties": {
"Cosmos": {
"type": "object",
"properties": {
"ConnectionString": {
"type": "string",
"description": "Gets or sets the connection string of the Azure Cosmos DB to connect to. If both are provided, 'ConnectionString' takes precedence over 'AccountEndpoint'."
},
"AccountEndpoint": {
"type": "string",
"description": "Gets or sets the account endpoint of the Azure Cosmos DB to connect to. If both are provided, 'ConnectionString' takes precedence over 'AccountEndpoint'."
},
"Tracing": {
Comment thread
Pilchie marked this conversation as resolved.
"type": "boolean",
"description": "Gets or sets a boolean value that indicates whether the OpenTelemetry tracing is enabled or not."
}
}
}
}
}
}
}
}
}
}
}
Loading