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

feat(Cosmos): TrustLocalEmulator=true #431

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
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
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -681,6 +681,10 @@ NOTE There's [no Apple Silicon emulator available as yet](https://github.com/Azu
NOTE Have not tested with the Windows Emulator, but it should work with analogous steps.

```bash
# magic connection string value that CosmosStoreConnector supports to
# a) avoid having to copy values around
# b) having to add code to trust cert and/or assent to elevation primpts
export EQUINOX_COSMOS_CONNECTION="TrustLocalEmulator=true"
docker compose up equinox-cosmos -d
bash docker-compose-cosmos.sh
```
Expand Down
11 changes: 8 additions & 3 deletions docker-compose-cosmos.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,14 @@

set -e # exit on any non-zero exit code

tmpf=$(mktemp)
curl -k https://localhost:8081/_explorer/emulator.pem > $tmpf
sudo security add-trusted-cert -d -r trustRoot -k ~/Library/Keychains/login.keychain $tmpf
if [ "$EQUINOX_COSMOS_CONNECTION" == "TrustLocalEmulator=true" ]; then
echo "Skipping downloading/trusting CosmosDb Emulator Certificate as \$EQUINOX_COSMOS_CONNECTION == \"TrustLocalEmulator=true\""
else
echo "Downloading/trusting CosmosDb Emulator Certificate as \$EQUINOX_COSMOS_CONNECTION is not \"TrustLocalEmulator=true\""
tmpf=$(mktemp)
curl -k https://localhost:8081/_explorer/emulator.pem > $tmpf
sudo security add-trusted-cert -d -r trustRoot -k ~/Library/Keychains/login.keychain $tmpf
fi

dotnet run --project tools/Equinox.Tool -- init cosmos
dotnet run --project tools/Equinox.Tool -- init cosmos -c equinox-test-archive
24 changes: 21 additions & 3 deletions src/Equinox.CosmosStore/CosmosStore.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1102,6 +1102,8 @@ type internal StoreCategory<'event, 'state, 'req>

module ConnectionString =

let [<Literal>] DefaultEmulatorEndpoint = "https://localhost:8081"
let [<Literal>] DefaultEmulatorAccountKey = "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="
let (|AccountEndpoint|) connectionString =
match System.Data.Common.DbConnectionStringBuilder(ConnectionString = connectionString).TryGetValue "AccountEndpoint" with
| true, (:? string as s) when not (String.IsNullOrEmpty s) -> s
Expand Down Expand Up @@ -1143,19 +1145,31 @@ open Equinox.CosmosStore.Core
open Microsoft.Azure.Cosmos
open System

module Discovery =

/// <summary>Use <c>Equinox.CosmosStore.Core.ConnectionString.defaultEmulatorEndpoint</c> and <c>Equinox.CosmosStore.Core.ConnectionString.defaultEmulatorAccountKey</c>; force <c>bypassCertificateValidation</c>.</summary>
let [<Literal>] TrustLocalEmulatorConnectionString = "TrustLocalEmulator=true"

[<RequireQualifiedAccess; NoComparison>]
type Discovery =
/// Separated Account Uri and Key (for interop with previous versions)
| AccountUriAndKey of accountUri: Uri * key: string
/// Cosmos SDK Connection String
/// <summary>Cosmos SDK Connection String<br/>
/// NOTE the magic value <c>TrustLocalEmulator=true</c> overrides the mode to <c>Discovery.TrustLocalEmulator</c></summary>
| ConnectionString of connectionString: string
/// <summary>Use <c>Equinox.CosmosStore.Core.ConnectionString.DefaultEmulatorEndpoint</c> and <c>Equinox.CosmosStore.Core.ConnectionString.DefaultEmulatorAccountKey</c>; force <c>bypassCertificateValidation</c>.<br/>
/// See https://learn.microsoft.com/en-us/azure/cosmos-db/local-emulator for details.</summary>
| TrustLocalEmulator
member x.ToDiscoveryMode() = x |> function
| Discovery.AccountUriAndKey (u, k) -> DiscoveryMode.AccountUriAndKey (string u, k)
| Discovery.ConnectionString Discovery.TrustLocalEmulatorConnectionString
| Discovery.TrustLocalEmulator -> DiscoveryMode.AccountUriAndKey (Core.ConnectionString.DefaultEmulatorEndpoint, Core.ConnectionString.DefaultEmulatorAccountKey)
| Discovery.ConnectionString c -> DiscoveryMode.ConnectionString c

/// Manages establishing a CosmosClient, which is used by CosmosStoreClient to read from the underlying Cosmos DB Container.
type CosmosStoreConnector
( // CosmosDB endpoint/credentials specification.
// NOTE using the Default Local Emulator endpoint causes bypassCertificateValidation to default to true
discovery: Discovery,
// Timeout to apply to individual reads/write round-trips going to CosmosDB. CosmosDB Default: 1m.
requestTimeout: TimeSpan,
Expand All @@ -1168,16 +1182,20 @@ type CosmosStoreConnector
[<O; D null>] ?mode: ConnectionMode,
// consistency mode (default: use configuration specified for Database)
[<O; D null>] ?defaultConsistencyLevel: ConsistencyLevel,
// Inhibits certificate verification when set to `true`. Default: false.
// Inhibits certificate verification when set to `true`.
// Defaults to `true` when targeting Local Emulator, otherwise `false`.
[<O; D null>] ?bypassCertificateValidation: bool,
[<O; D null>] ?customize: Action<CosmosClientOptions>) =
let discoveryMode = discovery.ToDiscoveryMode()
let bypassCertificateValidation =
defaultArg bypassCertificateValidation false
|| (match discoveryMode with DiscoveryMode.AccountUriAndKey (ConnectionString.DefaultEmulatorEndpoint, _) -> true | _ -> false)
let factory =
let o = CosmosClientFactory.CreateDefaultOptions(requestTimeout, maxRetryAttemptsOnRateLimitedRequests, maxRetryWaitTimeOnRateLimitedRequests)
mode |> Option.iter (fun x -> o.ConnectionMode <- x)
defaultConsistencyLevel |> Option.iter (fun x -> o.ConsistencyLevel <- x)
// https://github.com/Azure/azure-cosmos-dotnet-v3/blob/1ef6e399f114a0fd580272d4cdca86b9f8732cf3/Microsoft.Azure.Cosmos.Samples/Usage/HttpClientFactory/Program.cs#L96
if defaultArg bypassCertificateValidation false then
if bypassCertificateValidation then
let cb = System.Net.Http.HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
let ch = new System.Net.Http.HttpClientHandler(ServerCertificateCustomValidationCallback = cb)
o.HttpClientFactory <- fun () -> new System.Net.Http.HttpClient(ch)
Expand Down
2 changes: 1 addition & 1 deletion tests/Equinox.CosmosStore.Integration/CosmosFixtures.fs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ let private archiveContainerId = tryRead "EQUINOX_COSMOS_CONTAINER_ARCHIVE" |> O
// see https://github.com/jet/equinox#provisioning-cosmosdb for details of what's expected in terms of provisioned containers etc
let discoverConnection () =
match tryRead "EQUINOX_COSMOS_CONNECTION" with
| None -> "localDocDbSim", Discovery.AccountUriAndKey(Uri "https://localhost:8081", "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==")
| None -> "localDocDbSim", Discovery.TrustLocalEmulator
| Some connectionString -> "EQUINOX_COSMOS_CONNECTION", Discovery.ConnectionString connectionString

let createConnector (log: Serilog.ILogger) =
Expand Down