Skip to content

Commit

Permalink
feat(Cosmos): TrustLocalEmulator=true
Browse files Browse the repository at this point in the history
  • Loading branch information
bartelink committed Dec 21, 2023
1 parent 07a8d79 commit 5a8a1da
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 7 deletions.
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

0 comments on commit 5a8a1da

Please sign in to comment.