Skip to content

Commit a30fede

Browse files
authored
GODRIVER-3486 Support auto encryption in unified tests. (mongodb#2240)
1 parent 1c1358d commit a30fede

File tree

11 files changed

+271
-146
lines changed

11 files changed

+271
-146
lines changed

etc/install-libmongocrypt.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# This script installs libmongocrypt into an "install" directory.
44
set -eux
55

6-
LIBMONGOCRYPT_TAG="1.12.0"
6+
LIBMONGOCRYPT_TAG="1.15.1"
77

88
# Install libmongocrypt based on OS.
99
if [ "Windows_NT" = "${OS:-}" ]; then

internal/integration/mtest/mongotest.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -797,11 +797,6 @@ func verifyRunOnBlockConstraint(rob RunOnBlock) error {
797797
return err
798798
}
799799

800-
// TODO(GODRIVER-3486): Once auto encryption is supported by the unified test
801-
// format,this check should be removed.
802-
if rob.CSFLEEnabled() && rob.CSFLE.Options != nil {
803-
return fmt.Errorf("auto encryption required (GODRIVER-3486)")
804-
}
805800
if rob.CSFLEEnabled() && !IsCSFLEEnabled() {
806801
return fmt.Errorf("runOnBlock requires CSFLE to be enabled. Build with the cse tag to enable")
807802
}

internal/integration/mtest/options.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@ var (
5151

5252
// CSFLEOptions holds configuration for Client-Side Field Level Encryption
5353
// (CSFLE).
54-
type CSFLEOptions struct{}
54+
type CSFLEOptions struct {
55+
MinVer string `bson:"minLibmongocryptVersion"`
56+
}
5557

5658
// CSFLE models the runOnRequirements.csfle field in Unified Test Format tests.
5759
//
@@ -67,12 +69,12 @@ type CSFLE struct {
6769

6870
// UnmarshalBSON implements custom BSON unmarshalling for CSFLE, accepting
6971
// either a boolean or an embedded document. If a document is provided, Options
70-
// is set and Boolean is false. If a boolean is provided, Boolean is set and
72+
// is set and Boolean is true. If a boolean is provided, Boolean is set and
7173
// Options is nil.
7274
func (csfle *CSFLE) UnmarshalBSON(data []byte) error {
7375
embRawValue := bson.RawValue{Type: bson.TypeEmbeddedDocument, Value: data}
7476
if err := embRawValue.Unmarshal(&csfle.Options); err == nil {
75-
csfle.Boolean = false
77+
csfle.Boolean = true
7678

7779
return nil
7880
}

internal/integration/unified/client_entity.go

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ package unified
88

99
import (
1010
"context"
11+
"crypto/tls"
1112
"fmt"
1213
"strings"
1314
"sync"
@@ -198,6 +199,13 @@ func newClientEntity(ctx context.Context, em *EntityMap, entityOptions *entityOp
198199
} else {
199200
integtest.AddTestServerAPIVersion(clientOpts)
200201
}
202+
if entityOptions.AutoEncryptOpts != nil {
203+
aeo, err := createAutoEncryptionOptions(entityOptions.AutoEncryptOpts)
204+
if err != nil {
205+
return nil, fmt.Errorf("error parsing auto encryption options: %w", err)
206+
}
207+
clientOpts.SetAutoEncryptionOptions(aeo)
208+
}
201209
for _, cmd := range entityOptions.IgnoredCommands {
202210
entity.ignoredCommands[cmd] = struct{}{}
203211
}
@@ -241,6 +249,83 @@ func getURIForClient(opts *entityOptions) string {
241249
}
242250
}
243251

252+
// TODO(GODRIVER-3726): Need to update the logic to an Unmarshal method.
253+
func createAutoEncryptionOptions(opts bson.Raw) (*options.AutoEncryptionOptions, error) {
254+
aeo := options.AutoEncryption()
255+
var kvnsFound bool
256+
elems, err := opts.Elements()
257+
if err != nil {
258+
return nil, err
259+
}
260+
261+
for _, elem := range elems {
262+
name := elem.Key()
263+
opt := elem.Value()
264+
265+
switch name {
266+
case "kmsProviders":
267+
providers := make(map[string]map[string]any)
268+
elems, err := opt.Document().Elements()
269+
if err != nil {
270+
return nil, err
271+
}
272+
for _, elem := range elems {
273+
key := elem.Key()
274+
opt := elem.Value().Document()
275+
provider, err := getKmsProvider(key, opt)
276+
if err != nil {
277+
return nil, err
278+
}
279+
if provider == nil {
280+
continue
281+
}
282+
providers[key] = provider
283+
if key == "kmip" && tlsClientCertificateKeyFile != "" && tlsCAFile != "" {
284+
cfg, err := options.BuildTLSConfig(map[string]any{
285+
"tlsCertificateKeyFile": tlsClientCertificateKeyFile,
286+
"tlsCAFile": tlsCAFile,
287+
})
288+
if err != nil {
289+
return nil, fmt.Errorf("error constructing tls config: %w", err)
290+
}
291+
aeo.SetTLSConfig(map[string]*tls.Config{
292+
"kmip": cfg,
293+
})
294+
}
295+
}
296+
aeo.SetKmsProviders(providers)
297+
case "schemaMap":
298+
var schemaMap map[string]any
299+
err := bson.Unmarshal(opt.Document(), &schemaMap)
300+
if err != nil {
301+
return nil, fmt.Errorf("error creating schema map: %v", err)
302+
}
303+
aeo.SetSchemaMap(schemaMap)
304+
case "keyVaultNamespace":
305+
kvnsFound = true
306+
aeo.SetKeyVaultNamespace(opt.StringValue())
307+
case "bypassAutoEncryption":
308+
aeo.SetBypassAutoEncryption(opt.Boolean())
309+
case "encryptedFieldsMap":
310+
var encryptedFieldsMap map[string]any
311+
err := bson.Unmarshal(opt.Document(), &encryptedFieldsMap)
312+
if err != nil {
313+
return nil, fmt.Errorf("error creating encryptedFieldsMap: %v", err)
314+
}
315+
aeo.SetEncryptedFieldsMap(encryptedFieldsMap)
316+
case "bypassQueryAnalysis":
317+
aeo.SetBypassQueryAnalysis(opt.Boolean())
318+
default:
319+
return nil, fmt.Errorf("unrecognized option: %v", name)
320+
}
321+
}
322+
if !kvnsFound {
323+
aeo.SetKeyVaultNamespace("keyvault.datakeys")
324+
}
325+
326+
return aeo, nil
327+
}
328+
244329
// disconnect disconnects the client associated with this entity. It is an
245330
// idempotent operation, unlike the mongo client's disconnect method. This
246331
// property will help avoid unnecessary errors when calling disconnect on a

internal/integration/unified/collection_data.go

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,10 @@ type collectionData struct {
2727
}
2828

2929
type createOptions struct {
30-
Capped *bool `bson:"capped"`
31-
SizeInBytes *int64 `bson:"size"`
30+
Capped *bool `bson:"capped"`
31+
SizeInBytes *int64 `bson:"size"`
32+
EncryptedFields bson.Raw `bson:"encryptedFields"`
33+
Validator bson.Raw `bson:"validator"`
3234
}
3335

3436
// createCollection configures the collection represented by the receiver using the internal client. This function
@@ -49,14 +51,18 @@ func (c *collectionData) createCollection(ctx context.Context) error {
4951
if c.Options.SizeInBytes != nil {
5052
createOpts = createOpts.SetSizeInBytes(*c.Options.SizeInBytes)
5153
}
54+
if c.Options.EncryptedFields != nil {
55+
createOpts = createOpts.SetEncryptedFields(c.Options.EncryptedFields)
56+
}
57+
if c.Options.Validator != nil {
58+
createOpts = createOpts.SetValidator(c.Options.Validator)
59+
}
5260

5361
if err := db.CreateCollection(ctx, c.CollectionName, createOpts); err != nil {
5462
return fmt.Errorf("error creating collection: %w", err)
5563
}
56-
}
57-
58-
// If neither documents nor options are provided, still create the collection with write concern "majority".
59-
if len(c.Documents) == 0 && c.Options == nil {
64+
} else {
65+
// If no options are provided, still create the collection with write concern "majority".
6066
// The write concern has to be manually specified in the command document because RunCommand does not honor
6167
// the database's write concern.
6268
create := bson.D{
@@ -68,13 +74,15 @@ func (c *collectionData) createCollection(ctx context.Context) error {
6874
if err := db.RunCommand(ctx, create).Err(); err != nil {
6975
return fmt.Errorf("error creating collection: %w", err)
7076
}
71-
return nil
7277
}
7378

74-
docs := bsonutil.RawToInterfaces(c.Documents...)
75-
if _, err := coll.InsertMany(ctx, docs); err != nil {
76-
return fmt.Errorf("error inserting data: %w", err)
79+
if len(c.Documents) != 0 {
80+
docs := bsonutil.RawToInterfaces(c.Documents...)
81+
if _, err := coll.InsertMany(ctx, docs); err != nil {
82+
return fmt.Errorf("error inserting data: %w", err)
83+
}
7784
}
85+
7886
return nil
7987
}
8088

internal/integration/unified/database_operation_execution.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,10 @@ func executeCreateCollection(ctx context.Context, operation *operation) (*operat
125125
cco.SetTimeSeriesOptions(tso)
126126
case "clusteredIndex":
127127
cco.SetClusteredIndex(val.Document())
128+
case "validator":
129+
cco.SetValidator(val.Document())
130+
case "encryptedFields":
131+
cco.SetEncryptedFields(val.Document())
128132
default:
129133
return nil, fmt.Errorf("unrecognized createCollection option %q", key)
130134
}
@@ -156,13 +160,17 @@ func executeDropCollection(ctx context.Context, operation *operation) (*operatio
156160
return nil, err
157161
}
158162

163+
dco := options.DropCollection()
164+
159165
var collName string
160166
elems, _ := operation.Arguments.Elements()
161167
for _, elem := range elems {
162168
key := elem.Key()
163169
val := elem.Value()
164170

165171
switch key {
172+
case "encryptedFields":
173+
dco.SetEncryptedFields(val.Document())
166174
case "collection":
167175
collName = val.StringValue()
168176
default:
@@ -173,7 +181,7 @@ func executeDropCollection(ctx context.Context, operation *operation) (*operatio
173181
return nil, newMissingArgumentError("collection")
174182
}
175183

176-
err = db.Collection(collName).Drop(ctx)
184+
err = db.Collection(collName).Drop(ctx, dco)
177185
return newErrorResult(err), nil
178186
}
179187

0 commit comments

Comments
 (0)