Skip to content
Merged
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
6 changes: 5 additions & 1 deletion lib/mongo/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -625,7 +625,11 @@ def initialize(addresses_or_uri, options = nil)
build_encrypter
end
end
rescue StandardError
# ScriptError is rescued in addition to StandardError because
# Mongo::Crypt::Binding raises a LoadError when libmongocrypt cannot be
# found. Without it the cluster built above would be left open, leaking
# its monitoring and connection pool threads.
rescue StandardError, ScriptError
begin
@cluster.close
rescue StandardError => e
Expand Down
26 changes: 26 additions & 0 deletions spec/mongo/client_construction_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,32 @@
end
end

# Mongo::Crypt::Binding raises a LoadError, which is a ScriptError and
# not a StandardError, when libmongocrypt is not available. The cluster
# is built before the encrypter, so it must be closed before the error
# propagates out of the constructor. This context deliberately sits
# outside the require_libmongocrypt guard above: the scenario it covers
# only happens when libmongocrypt is missing.
context 'when building the encrypter raises a LoadError' do
before do
allow(Mongo::Crypt::AutoEncrypter).to receive(:new).and_raise(LoadError, 'no libmongocrypt')
end

it 'closes the cluster and propagates the error' do
expect_any_instance_of(Mongo::Cluster).to receive(:close).and_call_original

expect do
new_local_client_nmio(
SpecConfig.instance.addresses,
auto_encryption_options: {
key_vault_namespace: 'keyvault.datakeys',
kms_providers: { local: { key: 'x' * 96 } },
}
)
end.to raise_error(LoadError, /no libmongocrypt/)
end
end

context 'timeout options' do
let(:client) do
new_local_client(
Expand Down
6 changes: 5 additions & 1 deletion spec/runners/crud/requirement.rb
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,11 @@ def satisfied?
ok &&= !SpecConfig.instance.auth?
end
if @csfle
ok &&= !!(ENV['LIBMONGOCRYPT_PATH'] || ENV['FLE'])
# Evergreen exports FLE="" on variants that do not exercise FLE, and
# an empty string is truthy in Ruby. Treat an empty value as
# "libmongocrypt is not available", the same way
# Mrss::LiteConstraints#require_libmongocrypt does.
ok &&= !(ENV['LIBMONGOCRYPT_PATH'] || '').empty? || !(ENV['FLE'] || '').empty?
if ok && @csfle.is_a?(Hash) && (min_version = @csfle['minLibmongocryptVersion'])
actual_version = Mongo::Crypt::Binding.mongocrypt_version(nil)
ok &&= Mongo::Crypt::Binding.parse_version(actual_version) >= Gem::Version.new(min_version)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
description: poc-queryable-encryption

schemaVersion: "1.23"

runOnRequirements:
- minServerVersion: "7.0"
csfle: true
# QE is not supported on standalone servers
topologies: [ replicaset, load-balanced, sharded ]

createEntities:
- client:
id: &client0 client0
autoEncryptOpts:
keyVaultNamespace: keyvault.datakeys
kmsProviders:
local:
key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk
- database:
id: &encryptedDB encryptedDB
client: *client0
databaseName: &encryptedDBName poc-queryable-encryption
- collection:
id: &encryptedColl encryptedColl
database: *encryptedDB
collectionName: &encryptedCollName encrypted
- client:
id: &client1 client1
- database:
id: &unencryptedDB unencryptedDB
client: *client1
databaseName: *encryptedDBName
- collection:
id: &unencryptedColl unencryptedColl
database: *unencryptedDB
collectionName: *encryptedCollName

initialData:
- databaseName: keyvault
collectionName: datakeys
documents:
- _id: &keyid { $binary: { base64: EjRWeBI0mHYSNBI0VniQEg==, subType: "04" } }
keyMaterial: { $binary: { base64: sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==, subType: "00" } }
creationDate: { $date: { $numberLong: "1641024000000" } }
updateDate: { $date: { $numberLong: "1641024000000" } }
status: 1
masterKey:
provider: local
- databaseName: *encryptedDBName
collectionName: *encryptedCollName
documents: []
createOptions:
encryptedFields:
fields:
- keyId: *keyid
path: 'encryptedInt'
bsonType: 'int'
queries: {'queryType': 'equality', 'contention': {'$numberLong': '0'}}

tests:
- description: insert, replace, and find with queryable encryption
operations:
- object: *encryptedColl
name: insertOne
arguments:
document:
_id: 1
encryptedInt: 11
- object: *encryptedColl
name: replaceOne
arguments:
filter: { encryptedInt: 11 }
replacement: { encryptedInt: 22 }
- object: *encryptedColl
name: find
arguments:
filter: { encryptedInt: 22 }
expectResult:
- _id: 1
encryptedInt: 22
- object: *unencryptedColl
name: find
arguments:
filter: {}
expectResult:
- { _id: 1, encryptedInt: { $$type: binData }, __safeContent__: [ { "$binary" : { "base64" : "rhS16TJojgDDBtbluxBokvcotP1mQTGeYpNt8xd3MJQ=", "subType" : "00" } } ] }
Loading