diff --git a/.editorconfig b/.editorconfig
index 1e311974190..7b89a017365 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -476,3 +476,6 @@ dotnet_diagnostic.IDE0005.severity = silent
[{*.razor.cs,src/Aspire.Dashboard/Components/**.cs}]
# CA2007: Consider calling ConfigureAwait on the awaited task
dotnet_diagnostic.CA2007.severity = silent
+
+[{src/Playgrounds/**.cs}]
+dotnet_diagnostic.CA2007.severity = silent
diff --git a/Aspire.sln b/Aspire.sln
index 8ba2b43d1b2..2ae0159929a 100644
--- a/Aspire.sln
+++ b/Aspire.sln
@@ -1,4 +1,3 @@
-
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
@@ -194,6 +193,16 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aspire.Confluent.Kafka", "s
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aspire.Confluent.Kafka.Tests", "tests\Aspire.Confluent.Kafka.Tests\Aspire.Confluent.Kafka.Tests.csproj", "{A8CB331A-1247-41D9-8118-538E5A2CC9DF}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Playgrounds", "Playgrounds", "{DF8ADBC2-5B15-422F-A703-C60711D5552D}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CosmosEndToEnd", "CosmosEndToEnd", "{DBEDDF76-1C33-4943-8CCB-337A7D48AFF5}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CosmosEndToEnd.AppHost", "src\Playgrounds\CosmosEndToEnd\CosmosEndToEnd.AppHost\CosmosEndToEnd.AppHost.csproj", "{51DDD6BC-1D6C-466A-B509-FC49E3BD72E4}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CosmosEndToEnd.ApiService", "src\Playgrounds\CosmosEndToEnd\CosmosEndToEnd.ApiService\CosmosEndToEnd.ApiService.csproj", "{EABB20A8-CDA2-4AFE-A5B1-FB631200CD64}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Playground.ServiceDefaults", "src\Playgrounds\Playground.ServiceDefaults\Playground.ServiceDefaults.csproj", "{25208C6F-0A9D-4D60-9EDD-256C9891B1CD}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -520,6 +529,18 @@ Global
{A8CB331A-1247-41D9-8118-538E5A2CC9DF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A8CB331A-1247-41D9-8118-538E5A2CC9DF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A8CB331A-1247-41D9-8118-538E5A2CC9DF}.Release|Any CPU.Build.0 = Release|Any CPU
+ {51DDD6BC-1D6C-466A-B509-FC49E3BD72E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {51DDD6BC-1D6C-466A-B509-FC49E3BD72E4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {51DDD6BC-1D6C-466A-B509-FC49E3BD72E4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {51DDD6BC-1D6C-466A-B509-FC49E3BD72E4}.Release|Any CPU.Build.0 = Release|Any CPU
+ {EABB20A8-CDA2-4AFE-A5B1-FB631200CD64}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {EABB20A8-CDA2-4AFE-A5B1-FB631200CD64}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {EABB20A8-CDA2-4AFE-A5B1-FB631200CD64}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {EABB20A8-CDA2-4AFE-A5B1-FB631200CD64}.Release|Any CPU.Build.0 = Release|Any CPU
+ {25208C6F-0A9D-4D60-9EDD-256C9891B1CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {25208C6F-0A9D-4D60-9EDD-256C9891B1CD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {25208C6F-0A9D-4D60-9EDD-256C9891B1CD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {25208C6F-0A9D-4D60-9EDD-256C9891B1CD}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -610,6 +631,10 @@ Global
{F7D9FA54-1F64-4A36-961A-0087F8E88D07} = {8BAF2119-8370-4E9E-A887-D92506F8C727}
{174E0507-3BB0-4CDC-829E-9CA75DA66473} = {27381127-6C45-4B4C-8F18-41FF48DFE4B2}
{A8CB331A-1247-41D9-8118-538E5A2CC9DF} = {4981B3A5-4AFD-4191-BF7D-8692D9783D60}
+ {DBEDDF76-1C33-4943-8CCB-337A7D48AFF5} = {DF8ADBC2-5B15-422F-A703-C60711D5552D}
+ {51DDD6BC-1D6C-466A-B509-FC49E3BD72E4} = {DBEDDF76-1C33-4943-8CCB-337A7D48AFF5}
+ {EABB20A8-CDA2-4AFE-A5B1-FB631200CD64} = {DBEDDF76-1C33-4943-8CCB-337A7D48AFF5}
+ {25208C6F-0A9D-4D60-9EDD-256C9891B1CD} = {DF8ADBC2-5B15-422F-A703-C60711D5552D}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {6DCEDFEC-988E-4CB3-B45B-191EB5086E0C}
diff --git a/src/Aspire.Hosting.Azure/AzureCosmosDBDatabaseResource.cs b/src/Aspire.Hosting.Azure/AzureCosmosDBDatabaseResource.cs
index 12466cad656..2eb92d0d829 100644
--- a/src/Aspire.Hosting.Azure/AzureCosmosDBDatabaseResource.cs
+++ b/src/Aspire.Hosting.Azure/AzureCosmosDBDatabaseResource.cs
@@ -26,5 +26,5 @@ public AzureCosmosDBDatabaseResource(string name, AzureCosmosDBResource parent)
/// Gets the connection string to use for this database.
///
/// The connection string to use for this database.
- public string? GetConnectionString() => ConnectionString ?? Parent.GetConnectionString();
+ public string? GetConnectionString() => ConnectionString ?? Parent.GetConnectionString(); // HACK: Will go away when we get rid of Azure Provisioner package.
}
diff --git a/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/CosmosEndToEnd.ApiService.csproj b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/CosmosEndToEnd.ApiService.csproj
new file mode 100644
index 00000000000..211dd11a47b
--- /dev/null
+++ b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/CosmosEndToEnd.ApiService.csproj
@@ -0,0 +1,15 @@
+
+
+
+ net8.0
+ enable
+ enable
+ true
+
+
+
+
+
+
+
+
diff --git a/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/CosmosEndToEnd.ApiService.http b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/CosmosEndToEnd.ApiService.http
new file mode 100644
index 00000000000..ebb1db4fe79
--- /dev/null
+++ b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/CosmosEndToEnd.ApiService.http
@@ -0,0 +1,6 @@
+@CosmosEndToEnd.ApiService_HostAddress = http://localhost:5193
+
+GET {{CosmosEndToEnd.ApiService_HostAddress}}/weatherforecast/
+Accept: application/json
+
+###
diff --git a/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/Program.cs b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/Program.cs
new file mode 100644
index 00000000000..d678b1ece4d
--- /dev/null
+++ b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/Program.cs
@@ -0,0 +1,54 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.Azure.Cosmos;
+using Newtonsoft.Json;
+
+var builder = WebApplication.CreateBuilder(args);
+
+builder.AddServiceDefaults();
+builder.AddAzureCosmosDB("db", settings =>
+{
+ settings.IgnoreEmulatorCertificate = true;
+});
+
+var app = builder.Build();
+
+app.MapGet("/", async (CosmosClient cosmosClient) =>
+{
+ var db = (await cosmosClient.CreateDatabaseIfNotExistsAsync("db")).Database;
+ var container = (await db.CreateContainerIfNotExistsAsync("entries", "/Id")).Container;
+
+ // Add an entry to the database on each request.
+ var newEntry = new Entry() { Id = Guid.NewGuid().ToString() };
+ await container.CreateItemAsync(newEntry);
+
+ var entries = new List();
+ var iterator = container.GetItemQueryIterator(requestOptions: new QueryRequestOptions() { MaxItemCount = 5 });
+
+ var batchCount = 0;
+ while (iterator.HasMoreResults)
+ {
+ batchCount++;
+ var batch = await iterator.ReadNextAsync();
+ foreach (var entry in batch)
+ {
+ entries.Add(entry);
+ }
+ }
+
+ return new
+ {
+ batchCount = batchCount,
+ totalEntries = entries.Count,
+ entries = entries
+ };
+});
+
+app.Run();
+
+public class Entry
+{
+ [JsonProperty("id")]
+ public string? Id { get; set; }
+}
diff --git a/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/Properties/launchSettings.json b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/Properties/launchSettings.json
new file mode 100644
index 00000000000..de23e4696cf
--- /dev/null
+++ b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/Properties/launchSettings.json
@@ -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"
+ }
+ }
+ }
+}
diff --git a/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/appsettings.Development.json b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/appsettings.Development.json
new file mode 100644
index 00000000000..0c208ae9181
--- /dev/null
+++ b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/appsettings.Development.json
@@ -0,0 +1,8 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ }
+}
diff --git a/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/appsettings.json b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/appsettings.json
new file mode 100644
index 00000000000..10f68b8c8b4
--- /dev/null
+++ b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.ApiService/appsettings.json
@@ -0,0 +1,9 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "AllowedHosts": "*"
+}
diff --git a/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.AppHost/CosmosEndToEnd.AppHost.csproj b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.AppHost/CosmosEndToEnd.AppHost.csproj
new file mode 100644
index 00000000000..833f8567418
--- /dev/null
+++ b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.AppHost/CosmosEndToEnd.AppHost.csproj
@@ -0,0 +1,17 @@
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+ true
+
+
+
+
+
+
+
+
+
diff --git a/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.AppHost/Directory.Build.props b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.AppHost/Directory.Build.props
new file mode 100644
index 00000000000..dbc16a48c05
--- /dev/null
+++ b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.AppHost/Directory.Build.props
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.AppHost/Directory.Build.targets b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.AppHost/Directory.Build.targets
new file mode 100644
index 00000000000..ff4a3c400b3
--- /dev/null
+++ b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.AppHost/Directory.Build.targets
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.AppHost/Program.cs b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.AppHost/Program.cs
new file mode 100644
index 00000000000..bf20951d3a2
--- /dev/null
+++ b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.AppHost/Program.cs
@@ -0,0 +1,13 @@
+// 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);
+
+var db = builder.AddAzureCosmosDB("cosmos")
+ .UseEmulator()
+ .AddDatabase("db");
+
+builder.AddProject("api")
+ .WithReference(db);
+
+builder.Build().Run();
diff --git a/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.AppHost/Properties/launchSettings.json b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.AppHost/Properties/launchSettings.json
new file mode 100644
index 00000000000..539615401c1
--- /dev/null
+++ b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.AppHost/Properties/launchSettings.json
@@ -0,0 +1,28 @@
+{
+ "$schema": "http://json.schemastore.org/launchsettings.json",
+ "profiles": {
+ "http": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "applicationUrl": "http://localhost:15129",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development",
+ "DOTNET_ENVIRONMENT": "Development",
+ "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:16175"
+ }
+ },
+ "generate-manifest": {
+ "commandName": "Project",
+ "launchBrowser": true,
+ "dotnetRunMessages": true,
+ "commandLineArgs": "--publisher manifest --output-path aspire-manifest.json",
+ "applicationUrl": "http://localhost:15129",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development",
+ "DOTNET_ENVIRONMENT": "Development",
+ "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:16175"
+ }
+ }
+ }
+}
diff --git a/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.AppHost/appsettings.Development.json b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.AppHost/appsettings.Development.json
new file mode 100644
index 00000000000..0c208ae9181
--- /dev/null
+++ b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.AppHost/appsettings.Development.json
@@ -0,0 +1,8 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ }
+}
diff --git a/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.AppHost/appsettings.json b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.AppHost/appsettings.json
new file mode 100644
index 00000000000..31c092aa450
--- /dev/null
+++ b/src/Playgrounds/CosmosEndToEnd/CosmosEndToEnd.AppHost/appsettings.json
@@ -0,0 +1,9 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning",
+ "Aspire.Hosting.Dcp": "Warning"
+ }
+ }
+}
diff --git a/src/Playgrounds/Playground.ServiceDefaults/Extensions.cs b/src/Playgrounds/Playground.ServiceDefaults/Extensions.cs
new file mode 100644
index 00000000000..30ea4a2493f
--- /dev/null
+++ b/src/Playgrounds/Playground.ServiceDefaults/Extensions.cs
@@ -0,0 +1,122 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+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(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");
+}
diff --git a/src/Playgrounds/Playground.ServiceDefaults/Playground.ServiceDefaults.csproj b/src/Playgrounds/Playground.ServiceDefaults/Playground.ServiceDefaults.csproj
new file mode 100644
index 00000000000..dde733791d9
--- /dev/null
+++ b/src/Playgrounds/Playground.ServiceDefaults/Playground.ServiceDefaults.csproj
@@ -0,0 +1,27 @@
+
+
+
+ Library
+ net8.0
+ enable
+ enable
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+