[Aspire.Microsoft.Azure.Cosmos] How to access the CosmosDB emulator data explorer?
The Aspire.Microsoft.Azure.Cosmos component provides the
cosmosdb.RunAsEmulator();
method. Aspire.NET does recognize the 8081 port in the container (see screenshot). However, without any additional settings the emulator will only be exposed via a TCP endpoint which is not enough to access the data explorer dashboard from the emulator.
How can I expose the data explorer inside the emulator container best, during development? Can you please add this to the documentation?
A possible option I saw was doing the following:
var cosmos = builder.AddAzureCosmosDB("CosmosDb")
.AddDatabase("mydatabase");
if (builder.Environment.IsDevelopment())
{
cosmos.WithHttpsEndpoint(8081, 8081, "emulator-port")
.RunAsEmulator();
}
However, doing the thing above results in 8081 being listed twice and it adds another emulator-port target port than I specified (see screenshot).
I would like to be confirmed that this is the correct approach, and have it in the documentation for reference.
A possible option I saw was doing the following:
var cosmos = builder.AddAzureCosmosDB("CosmosDb") .AddDatabase("mydatabase");
if (builder.Environment.IsDevelopment()) { cosmos.WithHttpsEndpoint(8081, 8081, "emulator-port") .RunAsEmulator(); } ...
I did that but when I go to the endpoint in my browser it complains I need an authorization header. How? Can I disable that somewhere?
@obiwanjacobi per the docs for the Linux based emulator (which is what Aspire runs):
The emulator is comprised of two components:
- Data explorer - interactively explore the data in the emulator. By default this runs on port 1234
- Azure Cosmos DB emulator - a local version of the Azure Cosmos DB database service. By default, this runs on port 8081.
The emulator gateway endpoint is typically available on port 8081 at the address http://localhost:8081/. To navigate to the data explorer, use the address http://localhost:1234/ in your web browser. It may take a few seconds for data explorer to be available. The gateway endpoint is typically available immediately.
So mapping 8081 exposes emulator, not the dashboard, which is why you get that error. You have to map 1234 for the dashboard, using an HTTP endpoint, not an HTTPS endpoint. However, that still doesn't work, and it turns out it's due to an issue with the Cosmos DB emulator, not with Aspire. See: https://github.com/Azure/azure-cosmos-db-emulator-docker/issues/135
That problem has actually been fixed as of yesterday, however the version with the fix is tagged as vnext-preview. So you'll need to wait until that gets promoted to latest. Or in the meantime you can do this:
var cosmos = builder.AddAzureCosmosDB("cosmos")
.WithHttpEndpoint(51234, 1234, "explorer-port")
.WithExternalHttpEndpoints()
.RunAsEmulator(cfgContainer =>
{
cfgContainer
.WithImageRegistry("mcr.microsoft.com")
.WithImage("cosmosdb/linux/azure-cosmos-emulator")
.WithImageTag("vnext-preview");
});
Don't get me started on the kilometers of rabbit hole I've crawled through today to figure this out and get it to work - but it works:
Slight tweak:
var cosmos = builder.AddAzureCosmosDB("cosmos")
.RunAsEmulator(cfgContainer =>
{
cfgContainer
.WithHttpEndpoint(targetPort: 1234, "explorer-port")
.WithImageRegistry("mcr.microsoft.com")
.WithImage("cosmosdb/linux/azure-cosmos-emulator")
.WithImageTag("vnext-preview");
});
This is what I got (AppHost)
var cosmos = builder.AddAzureCosmosDB("myaccount");
var cosmosDb = cosmos.AddDatabase("mydatabase");
if (builder.Environment.IsDevelopment())
{
cosmos.RunAsEmulator(config => config
.WithHttpEndpoint(targetPort: 1234, name: "explorer-port")
.WithImageRegistry("mcr.microsoft.com")
.WithImage("cosmosdb/linux/azure-cosmos-emulator")
.WithImageTag("vnext-preview")
//.WithLifetime(ContainerLifetime.Persistent)
);
}
The container running cosmosDB is marked as unhealthy.
This is showing in the dashboard for the resource details:
I would expect to see port 1234 in there somewhere in the endpoints...?
(I commented out the WithLifetime call because at first it just hung on 'starting...')
https://learn.microsoft.com/en-us/dotnet/aspire/fundamentals/networking-overview
https://learn.microsoft.com/en-us/dotnet/aspire/fundamentals/networking-overview
Ah - so there's a proxy in front of it. Right, thanks.
But still, why does my container read unhealthy when I configure this? Any suggestions where I could start to look?
EDIT: I think this is probably wrong. It's more likely related to the SSL issue - I've spent a little bit of time on it but need to look into it more.
Original:
I'm still trying to get my head around why exactly this happens and what the fix might be, but the problem seems to be that when using a custom container image it doesn't publish the connection string, and Aspire is waiting for the publish event to mark it as healthy. Or more specifically, waiting for a CosmosClient to be created, which waits for the connection string publish event. See: https://github.com/dotnet/aspire/blob/28ded72163532b892e809a1e62e1f93bcea88b20/src/Aspire.Hosting.Azure.CosmosDB/AzureCosmosDBExtensions.cs#L127
Without debugging Apsire locally, I can't determine whether the issue is caused by a difference in the container image, or a difference in the way Aspire instantiates it when you provide a custom container spec. See: https://github.com/dotnet/aspire/blob/28ded72163532b892e809a1e62e1f93bcea88b20/src/Aspire.Hosting.Azure.CosmosDB/AzureCosmosDBExtensions.cs#L147
I can't see anything here that would cause it inherently, but it's possible that because this code is adding it as AzureCosmosDBEmulatorResource which doesn't implement IResourceWithConnectionString (required to publish the event, see: https://github.com/dotnet/aspire/blob/28ded72163532b892e809a1e62e1f93bcea88b20/src/Aspire.Hosting/Dcp/ApplicationExecutor.cs#L1285).
But I'm a little out of my depth here and mostly speculating. Either way I'm fairly sure that when using this image the connection string is either not published or the resource is not registered as an implementation of IResourceWithConnectionString and therefore even if it is published the event is not raised.
Maybe @davidfowl can help?
Obviously aside from the health check, we need the connection string and/or client to use the resource, but there's definitely a workaround for that. I don't have time to figure that out right now but I might get some time on the weekend to poke around at this a bit more.
Got it working for me with an update to set https on container startup as detailed in notes on the docs for the linux emulator. Both explorer, health check, persistent lifetime and also client side with connection string resolving work as expected. The explorer however gives unsafe warning. https://learn.microsoft.com/en-us/azure/cosmos-db/emulator-linux#running
Would be great for others to verify. Should any defaults change to use https or is this only part of the preview emulator container?
Example code:
var cosmos = builder.AddAzureCosmosDB("cosmos")
.RunAsEmulator(cfgContainer =>
{
cfgContainer
.WithHttpsEndpoint(targetPort: 1234, name: "explorer-port")
.WithImageRegistry("mcr.microsoft.com")
.WithImage("cosmosdb/linux/azure-cosmos-emulator")
.WithImageTag("vnext-preview")
.WithLifetime(ContainerLifetime.Persistent)
.WithArgs("--protocol", "https");
});
@trulsmp working for me too!
Ok so the dashboard works and the emu reports healthy. But I can't get any queries to work with the SDK. I spent a lot of time on this today and had to switch to an instance on Azure to get unblocked with my day job. Obviously not an Aspire issue, but @trulsmp just wondering whether you had this issue too? If it's not just me I'll try to find some time to create a small repro and report it on the emulator repo.
@matt-goldman I got queries to work and tested with item create and retrieval. Using the client integration in Azure Function isolated project orchestrated by Aspire.
I can share some example codebits, nothing special here just following standard setup.
Program.cs in Functions project using the client from Aspire. Using same name for the resource as in AppHost. Package used is Aspire.Microsoft.Azure.Cosmos according to docs: https://learn.microsoft.com/en-us/dotnet/aspire/database/azure-cosmos-db-integration?tabs=dotnet-cli#get-started
using Microsoft.Azure.Functions.Worker.Builder;
using Microsoft.Extensions.Hosting;
var builder = FunctionsApplication.CreateBuilder(args);
builder.AddServiceDefaults();
builder.AddAzureCosmosClient("cosmos");
builder.ConfigureFunctionsWebApplication();
var host = builder.Build();
await host.RunAsync();
Using the client in a function. Checking and possibly creating database/container if needed. Note that "id" needs to match case in cosmos db.
record User(string userId, string id, string name, string address);
public class HttpTriggers(CosmosClient client)
{
[Function("CreateItem")]
public async Task<IActionResult> CreateItem([HttpTrigger(AuthorizationLevel.Anonymous, "get")] HttpRequest req)
{
await client.CreateDatabaseIfNotExistsAsync("cosmosdb");
var db = client.GetDatabase("cosmosdb");
await db.CreateContainerIfNotExistsAsync("user", "/userId");
var container = db.GetContainer("user");
var partitionKey = Guid.CreateVersion7().ToString();
await container.CreateItemAsync(
item: new User(partitionKey, "profile", "John Doe", "123 Main St"),
partitionKey: new PartitionKey(partitionKey)
);
return new OkResult();
}
@trulsmp I can insert (or upsert) items, but I can't query anything. I think it's an SDK issue though, as I can via the explorer (with vnext-preview). But the SDK works with Azure Cosmos. So I'm not sure. This is all in a client project but if I get time over the weekend I'll see if I can replicate it in a minimal repro.
You guys got the DataExplorer to work (port 1234)?
I use the same code as posted above earlier with only setting the isProxied to false. This made the port appear in docker and the dashboard. But going there is a no-show (empty response).
var cosmos = builder.AddAzureCosmosDB("cosmos")
.RunAsEmulator(cfgContainer =>
{
cfgContainer
.WithHttpsEndpoint(targetPort: 1234, name: "explorer-port", isProxied: false)
.WithImageRegistry("mcr.microsoft.com")
.WithImage("cosmosdb/linux/azure-cosmos-emulator")
.WithImageTag("vnext-preview")
.WithArgs("--protocol", "https");
.WithLifetime(ContainerLifetime.Persistent)
});
Upsert and reading of a document works fine and the instance is also healthy now (not sure what I did wrong before).
I just noticed in the environment variables of the cosmos container that the protocol is set a weird value.
EXPLORER_PORT=1234
EXPLORER_PROTOCOL=PROTOCOL
Shouldn't that be http or https?
@davidortinau FYI
Adding .WithArgs("--explorer-protocol", "http") seem to have fixed it for me. The EXPLORER_PROTOCOL environment variable still has that weird PROTOCOL value - but the data explorer works now.
(Thanks to Egil Hansen on the Orleans Discord group.)