From c803331cdfe95724c8f1f82bcb5ca04ab11e0956 Mon Sep 17 00:00:00 2001 From: Jeff Yemin Date: Wed, 4 Mar 2020 16:41:49 -0500 Subject: [PATCH 01/48] Bump to 3.12.3-SNAPSHOT --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 131e412c8d3..e4b776d71f6 100644 --- a/build.gradle +++ b/build.gradle @@ -54,7 +54,7 @@ def javaCodeCheckedProjects = subprojects.findAll { !['util', 'mongo-java-driver configure(coreProjects) { evaluationDependsOn(':util') group = 'org.mongodb' - version = '3.12.2' + version = '3.12.3-SNAPSHOT' repositories { mavenLocal() From a7a3a290364bb546a23d177d61910d1107be13ec Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Fri, 13 Mar 2020 14:39:40 +0000 Subject: [PATCH 02/48] Ensure getting the description doesn't require the pool to be open. JAVA-3657 JAVA-3616, JAVA-3647 --- .../com/mongodb/internal/connection/DefaultConnectionPool.java | 1 - 1 file changed, 1 deletion(-) diff --git a/driver-core/src/main/com/mongodb/internal/connection/DefaultConnectionPool.java b/driver-core/src/main/com/mongodb/internal/connection/DefaultConnectionPool.java index 966f77d8578..e2c0ed467eb 100644 --- a/driver-core/src/main/com/mongodb/internal/connection/DefaultConnectionPool.java +++ b/driver-core/src/main/com/mongodb/internal/connection/DefaultConnectionPool.java @@ -510,7 +510,6 @@ public void onResult(final ResponseBuffers result, final Throwable t) { @Override public ConnectionDescription getDescription() { - isTrue("open", !isClosed.get()); return wrapped.getDescription(); } } From 8128846102cd9550cc7f808309f99e2e6ac95d4d Mon Sep 17 00:00:00 2001 From: Evgeny Zakharov Date: Tue, 3 Mar 2020 17:36:32 +0300 Subject: [PATCH 03/48] Add to call of callback if error raise on decoding of document JAVA-3644 --- .../mongodb/operation/AsyncChangeStreamBatchCursor.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/driver-core/src/main/com/mongodb/operation/AsyncChangeStreamBatchCursor.java b/driver-core/src/main/com/mongodb/operation/AsyncChangeStreamBatchCursor.java index 383e3557e3c..bbb9294d098 100644 --- a/driver-core/src/main/com/mongodb/operation/AsyncChangeStreamBatchCursor.java +++ b/driver-core/src/main/com/mongodb/operation/AsyncChangeStreamBatchCursor.java @@ -173,7 +173,12 @@ public void onResult(final List rawDocuments, final Throwable t ); return; } - results.add(rawDocument.decode(changeStreamOperation.getDecoder())); + try { + results.add(rawDocument.decode(changeStreamOperation.getDecoder())); + } catch (Exception e) { + callback.onResult(null, e); + return; + } } resumeToken = rawDocuments.get(rawDocuments.size() - 1).getDocument("_id"); callback.onResult(results, null); From bdc80eb71154f81aa76fc71b20fe8098b86c55c9 Mon Sep 17 00:00:00 2001 From: Jeff Yemin Date: Sun, 5 Apr 2020 15:04:22 -0400 Subject: [PATCH 04/48] Use safe null-check for collection values JAVA-3635 --- .../com/mongodb/assertions/Assertions.java | 19 +++++++++++++++++++ .../model/geojson/GeometryCollection.java | 4 ++-- .../client/model/geojson/LineString.java | 3 ++- .../client/model/geojson/MultiLineString.java | 4 ++-- .../client/model/geojson/MultiPolygon.java | 4 ++-- .../model/geojson/PolygonCoordinates.java | 5 +++-- .../client/model/geojson/Position.java | 3 ++- 7 files changed, 32 insertions(+), 10 deletions(-) diff --git a/driver-core/src/main/com/mongodb/assertions/Assertions.java b/driver-core/src/main/com/mongodb/assertions/Assertions.java index 9b8ae8c40ce..d4968c9bbf6 100644 --- a/driver-core/src/main/com/mongodb/assertions/Assertions.java +++ b/driver-core/src/main/com/mongodb/assertions/Assertions.java @@ -19,6 +19,8 @@ import com.mongodb.async.SingleResultCallback; +import java.util.Collection; + /** *

Design by contract assertions.

This class is not part of the public API and may be removed or changed at any time.

*/ @@ -100,6 +102,23 @@ public static void isTrueArgument(final String name, final boolean condition) { } } + /** + * Throw IllegalArgumentException if the collection contains a null value. + * + * @param name the name of the collection + * @param collection the collection + * @throws java.lang.IllegalArgumentException if the collection contains a null value + */ + public static void doesNotContainNull(final String name, final Collection collection) { + // Use a loop instead of the contains method, as some implementations of that method will throw an exception if passed null as a + // parameter (in particular, lists returned by List.of methods) + for (Object o : collection) { + if (o == null) { + throw new IllegalArgumentException(name + " can not contain a null value"); + } + } + } + private Assertions() { } } diff --git a/driver-core/src/main/com/mongodb/client/model/geojson/GeometryCollection.java b/driver-core/src/main/com/mongodb/client/model/geojson/GeometryCollection.java index f688ca03246..ac1be2a86b3 100644 --- a/driver-core/src/main/com/mongodb/client/model/geojson/GeometryCollection.java +++ b/driver-core/src/main/com/mongodb/client/model/geojson/GeometryCollection.java @@ -21,7 +21,7 @@ import java.util.Collections; import java.util.List; -import static com.mongodb.assertions.Assertions.isTrueArgument; +import static com.mongodb.assertions.Assertions.doesNotContainNull; import static com.mongodb.assertions.Assertions.notNull; /** @@ -51,7 +51,7 @@ public GeometryCollection(@Nullable final CoordinateReferenceSystem coordinateRe final List geometries) { super(coordinateReferenceSystem); notNull("geometries", geometries); - isTrueArgument("geometries contains only non-null elements", !geometries.contains(null)); + doesNotContainNull("geometries", geometries); this.geometries = Collections.unmodifiableList(geometries); } diff --git a/driver-core/src/main/com/mongodb/client/model/geojson/LineString.java b/driver-core/src/main/com/mongodb/client/model/geojson/LineString.java index cca9bf79ce9..9545b2a83a3 100644 --- a/driver-core/src/main/com/mongodb/client/model/geojson/LineString.java +++ b/driver-core/src/main/com/mongodb/client/model/geojson/LineString.java @@ -21,6 +21,7 @@ import java.util.Collections; import java.util.List; +import static com.mongodb.assertions.Assertions.doesNotContainNull; import static com.mongodb.assertions.Assertions.isTrueArgument; import static com.mongodb.assertions.Assertions.notNull; @@ -53,7 +54,7 @@ public LineString(@Nullable final CoordinateReferenceSystem coordinateReferenceS super(coordinateReferenceSystem); notNull("coordinates", coordinates); isTrueArgument("coordinates must contain at least two positions", coordinates.size() >= 2); - isTrueArgument("coordinates contains only non-null positions", !coordinates.contains(null)); + doesNotContainNull("coordinates", coordinates); this.coordinates = Collections.unmodifiableList(coordinates); } diff --git a/driver-core/src/main/com/mongodb/client/model/geojson/MultiLineString.java b/driver-core/src/main/com/mongodb/client/model/geojson/MultiLineString.java index 6090574a660..4c4c1cb0287 100644 --- a/driver-core/src/main/com/mongodb/client/model/geojson/MultiLineString.java +++ b/driver-core/src/main/com/mongodb/client/model/geojson/MultiLineString.java @@ -21,7 +21,7 @@ import java.util.Collections; import java.util.List; -import static com.mongodb.assertions.Assertions.isTrueArgument; +import static com.mongodb.assertions.Assertions.doesNotContainNull; import static com.mongodb.assertions.Assertions.notNull; /** @@ -55,7 +55,7 @@ public MultiLineString(@Nullable final CoordinateReferenceSystem coordinateRefer for (List line : coordinates) { notNull("line", line); - isTrueArgument("line contains only non-null positions", !line.contains(null)); + doesNotContainNull("line", line); } this.coordinates = Collections.unmodifiableList(coordinates); diff --git a/driver-core/src/main/com/mongodb/client/model/geojson/MultiPolygon.java b/driver-core/src/main/com/mongodb/client/model/geojson/MultiPolygon.java index a4a7f9834fc..75f50141501 100644 --- a/driver-core/src/main/com/mongodb/client/model/geojson/MultiPolygon.java +++ b/driver-core/src/main/com/mongodb/client/model/geojson/MultiPolygon.java @@ -21,7 +21,7 @@ import java.util.Collections; import java.util.List; -import static com.mongodb.assertions.Assertions.isTrueArgument; +import static com.mongodb.assertions.Assertions.doesNotContainNull; import static com.mongodb.assertions.Assertions.notNull; /** @@ -51,7 +51,7 @@ public MultiPolygon(final List coordinates) { public MultiPolygon(@Nullable final CoordinateReferenceSystem coordinateReferenceSystem, final List coordinates) { super(coordinateReferenceSystem); notNull("coordinates", coordinates); - isTrueArgument("coordinates has no null elements", !coordinates.contains(null)); + doesNotContainNull("coordinates", coordinates); this.coordinates = Collections.unmodifiableList(coordinates); } diff --git a/driver-core/src/main/com/mongodb/client/model/geojson/PolygonCoordinates.java b/driver-core/src/main/com/mongodb/client/model/geojson/PolygonCoordinates.java index 1c4a08840b7..00008a00481 100644 --- a/driver-core/src/main/com/mongodb/client/model/geojson/PolygonCoordinates.java +++ b/driver-core/src/main/com/mongodb/client/model/geojson/PolygonCoordinates.java @@ -20,6 +20,7 @@ import java.util.Collections; import java.util.List; +import static com.mongodb.assertions.Assertions.doesNotContainNull; import static com.mongodb.assertions.Assertions.isTrueArgument; import static com.mongodb.assertions.Assertions.notNull; @@ -40,7 +41,7 @@ public final class PolygonCoordinates { */ public PolygonCoordinates(final List exterior, final List... holes) { notNull("exteriorRing", exterior); - isTrueArgument("ring contains only non-null positions", !exterior.contains(null)); + doesNotContainNull("exterior", exterior); isTrueArgument("ring must contain at least four positions", exterior.size() >= 4); isTrueArgument("first and last position must be the same", exterior.get(0).equals(exterior.get(exterior.size() - 1))); @@ -49,7 +50,7 @@ public PolygonCoordinates(final List exterior, final List... List> holesList = new ArrayList>(holes.length); for (List hole : holes) { notNull("interiorRing", hole); - isTrueArgument("ring contains only non-null positions", !hole.contains(null)); + doesNotContainNull("hole", hole); isTrueArgument("ring must contain at least four positions", hole.size() >= 4); isTrueArgument("first and last position must be the same", hole.get(0).equals(hole.get(hole.size() - 1))); holesList.add(Collections.unmodifiableList(hole)); diff --git a/driver-core/src/main/com/mongodb/client/model/geojson/Position.java b/driver-core/src/main/com/mongodb/client/model/geojson/Position.java index b66b6001fb6..36ed42c0233 100644 --- a/driver-core/src/main/com/mongodb/client/model/geojson/Position.java +++ b/driver-core/src/main/com/mongodb/client/model/geojson/Position.java @@ -22,6 +22,7 @@ import java.util.Collections; import java.util.List; +import static com.mongodb.assertions.Assertions.doesNotContainNull; import static com.mongodb.assertions.Assertions.isTrueArgument; import static com.mongodb.assertions.Assertions.notNull; @@ -41,7 +42,7 @@ public final class Position { */ public Position(final List values) { notNull("values", values); - isTrueArgument("value contains only non-null elements", !values.contains(null)); + doesNotContainNull("values", values); isTrueArgument("value must contain at least two elements", values.size() >= 2); this.values = Collections.unmodifiableList(values); } From 8b6364706cc21fb2ef2277712fab1406cd97e78d Mon Sep 17 00:00:00 2001 From: Jeff Yemin Date: Sun, 5 Apr 2020 15:34:06 -0400 Subject: [PATCH 05/48] Decode all integral value types in GeoJson decoders JAVA-3646 --- .../geojson/codecs/GeometryDecoderHelper.java | 18 ++++++++++++++---- ...GeometryCollectionCodecSpecification.groovy | 14 ++++++++++++++ 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/driver-core/src/main/com/mongodb/client/model/geojson/codecs/GeometryDecoderHelper.java b/driver-core/src/main/com/mongodb/client/model/geojson/codecs/GeometryDecoderHelper.java index 2700f2ce579..1d1837d3ed1 100644 --- a/driver-core/src/main/com/mongodb/client/model/geojson/codecs/GeometryDecoderHelper.java +++ b/driver-core/src/main/com/mongodb/client/model/geojson/codecs/GeometryDecoderHelper.java @@ -392,10 +392,7 @@ private static Position decodePosition(final BsonReader reader) { reader.readStartArray(); List values = new ArrayList(); while (reader.readBsonType() != BsonType.END_OF_DOCUMENT) { - if (reader.getCurrentBsonType() != BsonType.DOUBLE) { - throw new CodecConfigurationException("Invalid position"); - } - values.add(reader.readDouble()); + values.add(readAsDouble(reader)); } reader.readEndArray(); @@ -406,6 +403,19 @@ private static Position decodePosition(final BsonReader reader) { } } + private static double readAsDouble(final BsonReader reader) { + if (reader.getCurrentBsonType() == BsonType.DOUBLE) { + return reader.readDouble(); + } else if (reader.getCurrentBsonType() == BsonType.INT32) { + return reader.readInt32(); + } else if (reader.getCurrentBsonType() == BsonType.INT64) { + return reader.readInt64(); + } + + throw new CodecConfigurationException("A GeoJSON position value must be a numerical type, but the value is of type " + + reader.getCurrentBsonType()); + } + @Nullable static CoordinateReferenceSystem decodeCoordinateReferenceSystem(final BsonReader reader) { String crsName = null; diff --git a/driver-core/src/test/unit/com/mongodb/client/model/geojson/codecs/GeometryCollectionCodecSpecification.groovy b/driver-core/src/test/unit/com/mongodb/client/model/geojson/codecs/GeometryCollectionCodecSpecification.groovy index 1ccb978781b..e720f3dbf4b 100644 --- a/driver-core/src/test/unit/com/mongodb/client/model/geojson/codecs/GeometryCollectionCodecSpecification.groovy +++ b/driver-core/src/test/unit/com/mongodb/client/model/geojson/codecs/GeometryCollectionCodecSpecification.groovy @@ -31,6 +31,7 @@ import org.bson.BsonDocumentWriter import org.bson.codecs.DecoderContext import org.bson.codecs.EncoderContext import org.bson.codecs.configuration.CodecConfigurationException +import org.bson.json.JsonReader import spock.lang.Specification import static com.mongodb.client.model.geojson.NamedCoordinateReferenceSystem.EPSG_4326_STRICT_WINDING @@ -154,6 +155,19 @@ class GeometryCollectionCodecSpecification extends Specification { geometryCollection == decodedGeometryCollection } + def 'should decode integral value types'() { + given: + def jsonRepresentation = '{type: "LineString", coordinates: [ [101.0, 0], [102.0, 2147483648] ] }' + def expectedGeometry = new LineString([new Position(101d, 0d), new Position(102d, 2147483648d)]) + def codec = registry.get(LineString) + + when: + def decodedGeometry = codec.decode(new JsonReader(jsonRepresentation), DecoderContext.builder().build()) + + then: + decodedGeometry == expectedGeometry + } + def 'should throw when decoding invalid documents'() { when: codec.decode(new BsonDocumentReader(parse(invalidJson)), DecoderContext.builder().build()) From f346d2f41217626fa55480875022fa1848e5e62e Mon Sep 17 00:00:00 2001 From: Jeff Yemin Date: Fri, 3 Apr 2020 10:46:59 -0400 Subject: [PATCH 06/48] Ensure that socket is closed on handshake failure JAVA-3680 --- .../mongodb/connection/netty/NettyStream.java | 34 ++++++++++++------- .../connection/AsynchronousChannelStream.java | 24 +++++++++---- 2 files changed, 38 insertions(+), 20 deletions(-) diff --git a/driver-core/src/main/com/mongodb/connection/netty/NettyStream.java b/driver-core/src/main/com/mongodb/connection/netty/NettyStream.java index 9722193f12d..b8e5e8d0671 100644 --- a/driver-core/src/main/com/mongodb/connection/netty/NettyStream.java +++ b/driver-core/src/main/com/mongodb/connection/netty/NettyStream.java @@ -270,7 +270,7 @@ public ServerAddress getAddress() { } @Override - public void close() { + public synchronized void close() { isClosed = true; if (channel != null) { channel.close(); @@ -396,20 +396,28 @@ private class OpenChannelFutureListener implements ChannelFutureListener { @Override public void operationComplete(final ChannelFuture future) { - if (future.isSuccess()) { - channel = channelFuture.channel(); - channel.closeFuture().addListener(new ChannelFutureListener() { - @Override - public void operationComplete(final ChannelFuture future) { - handleReadResponse(null, new IOException("The connection to the server was closed")); + synchronized (NettyStream.this) { + if (future.isSuccess()) { + if (isClosed) { + channelFuture.channel().close(); + } else { + channel = channelFuture.channel(); + channel.closeFuture().addListener(new ChannelFutureListener() { + @Override + public void operationComplete(final ChannelFuture future) { + handleReadResponse(null, new IOException("The connection to the server was closed")); + } + }); } - }); - handler.completed(null); - } else { - if (socketAddressQueue.isEmpty()) { - handler.failed(new MongoSocketOpenException("Exception opening socket", getAddress(), future.cause())); + handler.completed(null); } else { - initializeChannel(handler, socketAddressQueue); + if (isClosed) { + handler.completed(null); + } else if (socketAddressQueue.isEmpty()) { + handler.failed(new MongoSocketOpenException("Exception opening socket", getAddress(), future.cause())); + } else { + initializeChannel(handler, socketAddressQueue); + } } } } diff --git a/driver-core/src/main/com/mongodb/internal/connection/AsynchronousChannelStream.java b/driver-core/src/main/com/mongodb/internal/connection/AsynchronousChannelStream.java index b4f18faff8b..b113acbed9e 100644 --- a/driver-core/src/main/com/mongodb/internal/connection/AsynchronousChannelStream.java +++ b/driver-core/src/main/com/mongodb/internal/connection/AsynchronousChannelStream.java @@ -69,13 +69,17 @@ public BufferProvider getBufferProvider() { return bufferProvider; } - public ExtendedAsynchronousByteChannel getChannel() { + public synchronized ExtendedAsynchronousByteChannel getChannel() { return channel; } - protected void setChannel(final ExtendedAsynchronousByteChannel channel) { + protected synchronized void setChannel(final ExtendedAsynchronousByteChannel channel) { isTrue("current channel is null", this.channel == null); - this.channel = channel; + if (isClosed) { + closeChannel(channel); + } else { + this.channel = channel; + } } @Override @@ -133,16 +137,22 @@ public ServerAddress getAddress() { } @Override - public void close() { + public synchronized void close() { + isClosed = true; + try { + closeChannel(channel); + } finally { + channel = null; + } + } + + private void closeChannel(final ExtendedAsynchronousByteChannel channel) { try { if (channel != null) { channel.close(); } } catch (IOException e) { // ignore - } finally { - channel = null; - isClosed = true; } } From 1df1f100ee5fcad318c255cbccdef77117d117f0 Mon Sep 17 00:00:00 2001 From: Jeff Yemin Date: Tue, 7 Apr 2020 14:27:09 -0400 Subject: [PATCH 07/48] Bump to 3.12.3 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index e4b776d71f6..91bf4cf31e4 100644 --- a/build.gradle +++ b/build.gradle @@ -54,7 +54,7 @@ def javaCodeCheckedProjects = subprojects.findAll { !['util', 'mongo-java-driver configure(coreProjects) { evaluationDependsOn(':util') group = 'org.mongodb' - version = '3.12.3-SNAPSHOT' + version = '3.12.3' repositories { mavenLocal() From 043b8024d33dff635cf28c69ae05bd8b69ecc7d4 Mon Sep 17 00:00:00 2001 From: Jeff Yemin Date: Tue, 7 Apr 2020 14:33:18 -0400 Subject: [PATCH 08/48] Bump to 3.12.4-SNAPSHOT --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 91bf4cf31e4..413a41c6137 100644 --- a/build.gradle +++ b/build.gradle @@ -54,7 +54,7 @@ def javaCodeCheckedProjects = subprojects.findAll { !['util', 'mongo-java-driver configure(coreProjects) { evaluationDependsOn(':util') group = 'org.mongodb' - version = '3.12.3' + version = '3.12.4-SNAPSHOT' repositories { mavenLocal() From 5e0cfaf385114571317fc8153f8fdf0cebabc035 Mon Sep 17 00:00:00 2001 From: Jeff Yemin Date: Tue, 7 Apr 2020 14:36:21 -0400 Subject: [PATCH 09/48] Update reference documentation to 3.12.3 --- docs/reference/content/bson/installation-guide.md | 2 +- .../content/driver-async/getting-started/installation.md | 2 +- .../content/driver/getting-started/installation.md | 8 ++++---- docs/reference/content/driver/tutorials/jndi.md | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/reference/content/bson/installation-guide.md b/docs/reference/content/bson/installation-guide.md index 6fdc0d5a648..fb837181c5a 100644 --- a/docs/reference/content/bson/installation-guide.md +++ b/docs/reference/content/bson/installation-guide.md @@ -22,4 +22,4 @@ This library comprehensively supports [BSON](http://www.bsonspec.org), the data storage and network transfer format that MongoDB uses for "documents". BSON is short for Binary [JSON](http://json.org/), is a binary-encoded serialization of JSON-like documents. -{{< install artifactId="bson" version="3.12.2" >}} +{{< install artifactId="bson" version="3.12.3" >}} diff --git a/docs/reference/content/driver-async/getting-started/installation.md b/docs/reference/content/driver-async/getting-started/installation.md index 91a62e6f0cc..73838dfef25 100644 --- a/docs/reference/content/driver-async/getting-started/installation.md +++ b/docs/reference/content/driver-async/getting-started/installation.md @@ -24,4 +24,4 @@ When TLS/SSL is enabled, the MongoDB Async Driver requires either [Netty](http:/ The MongoDB Async Driver provides asynchronous API that can leverage either Netty or Java 7's AsynchronousSocketChannel for fast and non-blocking I/O. -{{< install artifactId="mongodb-driver-async" version="3.12.2" dependencies="true">}} +{{< install artifactId="mongodb-driver-async" version="3.12.3" dependencies="true">}} diff --git a/docs/reference/content/driver/getting-started/installation.md b/docs/reference/content/driver/getting-started/installation.md index 507bc1fe682..ac7e155bc72 100644 --- a/docs/reference/content/driver/getting-started/installation.md +++ b/docs/reference/content/driver/getting-started/installation.md @@ -31,7 +31,7 @@ The `mongodb-driver-sync` artifact is a valid OSGi bundle whose symbolic name is {{% /note %}} -{{< install artifactId="mongodb-driver-sync" version="3.12.2" dependencies="true">}} +{{< install artifactId="mongodb-driver-sync" version="3.12.3" dependencies="true">}} ## MongoDB Driver Legacy @@ -44,7 +44,7 @@ While not deprecated, we recommend that new applications depend on the `mongodb- {{% /note %}} -{{< install artifactId="mongodb-driver-legacy" version="3.12.2" dependencies="true">}} +{{< install artifactId="mongodb-driver-legacy" version="3.12.3" dependencies="true">}} ## MongoDB Driver @@ -61,7 +61,7 @@ This module is deprecated and will no longer be published in the next major rele {{% /note %}} -{{< install artifactId="mongodb-driver" version="3.12.2" dependencies="true">}} +{{< install artifactId="mongodb-driver" version="3.12.3" dependencies="true">}} ## Uber Jar (Legacy) @@ -81,4 +81,4 @@ This module is deprecated and will no longer be published in the next major rele {{% /note %}} -{{< install artifactId="mongo-java-driver" version="3.12.2">}} +{{< install artifactId="mongo-java-driver" version="3.12.3">}} diff --git a/docs/reference/content/driver/tutorials/jndi.md b/docs/reference/content/driver/tutorials/jndi.md index 3396f8b0f99..67e8e1978da 100644 --- a/docs/reference/content/driver/tutorials/jndi.md +++ b/docs/reference/content/driver/tutorials/jndi.md @@ -28,7 +28,7 @@ The configuration of the `MongoClientFactory` differs depending on the applicati - + From d4b899f5046455115647f612e956dc63f7cea322 Mon Sep 17 00:00:00 2001 From: Jeff Yemin Date: Thu, 23 Apr 2020 14:25:27 -0400 Subject: [PATCH 10/48] Apply uuid representation from MongoClientURI to MongoClientOptions JAVA-3688 --- driver-legacy/src/main/com/mongodb/MongoClientURI.java | 5 +++++ .../unit/com/mongodb/MongoClientURISpecification.groovy | 7 ++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/driver-legacy/src/main/com/mongodb/MongoClientURI.java b/driver-legacy/src/main/com/mongodb/MongoClientURI.java index 7cfb2facfac..1639b382e42 100644 --- a/driver-legacy/src/main/com/mongodb/MongoClientURI.java +++ b/driver-legacy/src/main/com/mongodb/MongoClientURI.java @@ -17,6 +17,7 @@ package com.mongodb; import com.mongodb.lang.Nullable; +import org.bson.UuidRepresentation; import java.util.List; @@ -419,6 +420,10 @@ public MongoClientOptions getOptions() { if (!proxied.getCompressorList().isEmpty()) { builder.compressorList(proxied.getCompressorList()); } + UuidRepresentation uuidRepresentation = proxied.getUuidRepresentation(); + if (uuidRepresentation != null) { + builder.uuidRepresentation(uuidRepresentation); + } return builder.build(); } diff --git a/driver-legacy/src/test/unit/com/mongodb/MongoClientURISpecification.groovy b/driver-legacy/src/test/unit/com/mongodb/MongoClientURISpecification.groovy index 3ffb7de3e28..3deddf2ee8f 100644 --- a/driver-legacy/src/test/unit/com/mongodb/MongoClientURISpecification.groovy +++ b/driver-legacy/src/test/unit/com/mongodb/MongoClientURISpecification.groovy @@ -16,7 +16,7 @@ package com.mongodb - +import org.bson.UuidRepresentation import spock.lang.IgnoreIf import spock.lang.Specification import spock.lang.Unroll @@ -140,6 +140,7 @@ class MongoClientURISpecification extends Specification { + 'heartbeatFrequencyMS=20000&' + 'retryWrites=true&' + 'retryReads=true&' + + 'uuidRepresentation=csharpLegacy&' + 'appName=app1') when: @@ -164,6 +165,7 @@ class MongoClientURISpecification extends Specification { options.getHeartbeatFrequency() == 20000 options.getRetryWrites() options.getRetryReads() + options.getUuidRepresentation() == UuidRepresentation.C_SHARP_LEGACY options.getApplicationName() == 'app1' } @@ -183,6 +185,7 @@ class MongoClientURISpecification extends Specification { !options.isSslEnabled() options.getRetryWrites() options.getRetryReads() + options.getUuidRepresentation() == UuidRepresentation.JAVA_LEGACY } def 'should apply default uri to options'() { @@ -214,6 +217,7 @@ class MongoClientURISpecification extends Specification { .localThreshold(25) .requiredReplicaSetName('test') .compressorList([MongoCompressor.createZlibCompressor()]) + .uuidRepresentation(UuidRepresentation.C_SHARP_LEGACY) when: def options = new MongoClientURI('mongodb://localhost', optionsBuilder).getOptions() @@ -246,6 +250,7 @@ class MongoClientURISpecification extends Specification { options.getServerSettings().getHeartbeatFrequency(MILLISECONDS) == 5 options.getServerSettings().getMinHeartbeatFrequency(MILLISECONDS) == 11 options.compressorList == [MongoCompressor.createZlibCompressor()] + options.getUuidRepresentation() == UuidRepresentation.C_SHARP_LEGACY } @Unroll From 871df02c6e00ce83812cf2cdeb6d80e928ee794f Mon Sep 17 00:00:00 2001 From: Jeff Yemin Date: Tue, 5 May 2020 12:11:17 -0400 Subject: [PATCH 11/48] Bump to 3.12.4 --- build.gradle | 2 +- docs/reference/content/bson/installation-guide.md | 2 +- .../content/driver-async/getting-started/installation.md | 2 +- .../content/driver/getting-started/installation.md | 8 ++++---- docs/reference/content/driver/tutorials/jndi.md | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/build.gradle b/build.gradle index 413a41c6137..51eee66d1be 100644 --- a/build.gradle +++ b/build.gradle @@ -54,7 +54,7 @@ def javaCodeCheckedProjects = subprojects.findAll { !['util', 'mongo-java-driver configure(coreProjects) { evaluationDependsOn(':util') group = 'org.mongodb' - version = '3.12.4-SNAPSHOT' + version = '3.12.4' repositories { mavenLocal() diff --git a/docs/reference/content/bson/installation-guide.md b/docs/reference/content/bson/installation-guide.md index fb837181c5a..2e335d17c71 100644 --- a/docs/reference/content/bson/installation-guide.md +++ b/docs/reference/content/bson/installation-guide.md @@ -22,4 +22,4 @@ This library comprehensively supports [BSON](http://www.bsonspec.org), the data storage and network transfer format that MongoDB uses for "documents". BSON is short for Binary [JSON](http://json.org/), is a binary-encoded serialization of JSON-like documents. -{{< install artifactId="bson" version="3.12.3" >}} +{{< install artifactId="bson" version="3.12.4" >}} diff --git a/docs/reference/content/driver-async/getting-started/installation.md b/docs/reference/content/driver-async/getting-started/installation.md index 73838dfef25..1edad6c88a0 100644 --- a/docs/reference/content/driver-async/getting-started/installation.md +++ b/docs/reference/content/driver-async/getting-started/installation.md @@ -24,4 +24,4 @@ When TLS/SSL is enabled, the MongoDB Async Driver requires either [Netty](http:/ The MongoDB Async Driver provides asynchronous API that can leverage either Netty or Java 7's AsynchronousSocketChannel for fast and non-blocking I/O. -{{< install artifactId="mongodb-driver-async" version="3.12.3" dependencies="true">}} +{{< install artifactId="mongodb-driver-async" version="3.12.4" dependencies="true">}} diff --git a/docs/reference/content/driver/getting-started/installation.md b/docs/reference/content/driver/getting-started/installation.md index ac7e155bc72..80e1637131f 100644 --- a/docs/reference/content/driver/getting-started/installation.md +++ b/docs/reference/content/driver/getting-started/installation.md @@ -31,7 +31,7 @@ The `mongodb-driver-sync` artifact is a valid OSGi bundle whose symbolic name is {{% /note %}} -{{< install artifactId="mongodb-driver-sync" version="3.12.3" dependencies="true">}} +{{< install artifactId="mongodb-driver-sync" version="3.12.4" dependencies="true">}} ## MongoDB Driver Legacy @@ -44,7 +44,7 @@ While not deprecated, we recommend that new applications depend on the `mongodb- {{% /note %}} -{{< install artifactId="mongodb-driver-legacy" version="3.12.3" dependencies="true">}} +{{< install artifactId="mongodb-driver-legacy" version="3.12.4" dependencies="true">}} ## MongoDB Driver @@ -61,7 +61,7 @@ This module is deprecated and will no longer be published in the next major rele {{% /note %}} -{{< install artifactId="mongodb-driver" version="3.12.3" dependencies="true">}} +{{< install artifactId="mongodb-driver" version="3.12.4" dependencies="true">}} ## Uber Jar (Legacy) @@ -81,4 +81,4 @@ This module is deprecated and will no longer be published in the next major rele {{% /note %}} -{{< install artifactId="mongo-java-driver" version="3.12.3">}} +{{< install artifactId="mongo-java-driver" version="3.12.4">}} diff --git a/docs/reference/content/driver/tutorials/jndi.md b/docs/reference/content/driver/tutorials/jndi.md index 67e8e1978da..ec11fd54a85 100644 --- a/docs/reference/content/driver/tutorials/jndi.md +++ b/docs/reference/content/driver/tutorials/jndi.md @@ -28,7 +28,7 @@ The configuration of the `MongoClientFactory` differs depending on the applicati - + From 1656cf640c28afb0ae05039f7c42f2309b7be538 Mon Sep 17 00:00:00 2001 From: Jeff Yemin Date: Wed, 6 May 2020 13:39:28 -0400 Subject: [PATCH 12/48] Bump to 3.12.5-SNAPSHOT --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 51eee66d1be..6c124a0065f 100644 --- a/build.gradle +++ b/build.gradle @@ -54,7 +54,7 @@ def javaCodeCheckedProjects = subprojects.findAll { !['util', 'mongo-java-driver configure(coreProjects) { evaluationDependsOn(':util') group = 'org.mongodb' - version = '3.12.4' + version = '3.12.5-SNAPSHOT' repositories { mavenLocal() From 2af99b668759550c06b2f883bf6e483eac57cf10 Mon Sep 17 00:00:00 2001 From: Jeff Yemin Date: Wed, 6 May 2020 23:07:44 -0400 Subject: [PATCH 13/48] Make tests resilient to "ns not found" not being at the start of the error message --- .../src/test/functional/com/mongodb/async/client/Fixture.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/driver-async/src/test/functional/com/mongodb/async/client/Fixture.java b/driver-async/src/test/functional/com/mongodb/async/client/Fixture.java index 09bdc6a59bb..b55047f68ee 100644 --- a/driver-async/src/test/functional/com/mongodb/async/client/Fixture.java +++ b/driver-async/src/test/functional/com/mongodb/async/client/Fixture.java @@ -117,7 +117,7 @@ public static MongoCollection initializeCollection(final MongoNamespac database.runCommand(new Document("drop", namespace.getCollectionName()), futureResultCallback); futureResultCallback.get(60, SECONDS); } catch (MongoCommandException e) { - if (!e.getErrorMessage().startsWith("ns not found")) { + if (!e.getErrorMessage().contains("ns not found")) { throw e; } } catch (Throwable t) { @@ -141,7 +141,7 @@ public static void dropDatabase(final String name) { .runCommand(new Document("dropDatabase", 1), futureResultCallback); futureResultCallback.get(60, SECONDS); } catch (MongoCommandException e) { - if (!e.getErrorMessage().startsWith("ns not found")) { + if (!e.getErrorMessage().contains("ns not found")) { throw e; } } catch (Throwable t) { From f43892a9ff6d497dc546f208b2b2de32c35496f6 Mon Sep 17 00:00:00 2001 From: Jeff Yemin Date: Wed, 6 May 2020 23:10:52 -0400 Subject: [PATCH 14/48] Add MongoDB 4.4 to Evergreen matrix --- .evergreen/.evg.yml | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/.evergreen/.evg.yml b/.evergreen/.evg.yml index 49d1c6df521..3286301032b 100644 --- a/.evergreen/.evg.yml +++ b/.evergreen/.evg.yml @@ -460,6 +460,13 @@ axes: # Multiple mongos instances can be specified in the connection string # for this version. SAFE_FOR_MULTI_MONGOS: true + - id: "4.4" + display_name: "4.4" + variables: + VERSION: "4.4" + # Multiple mongos instances can be specified in the connection string + # for this version. + SAFE_FOR_MULTI_MONGOS: true - id: "4.2" display_name: "4.2" variables: @@ -596,21 +603,21 @@ buildvariants: - name: "static-analysis" - matrix_name: "tests-zlib-compression" - matrix_spec: { compressor : "zlib", auth: "noauth", ssl: "nossl", jdk: "jdk6", version: ["3.6", "4.0", "4.2", "latest"], topology: "standalone", os: "linux" } + matrix_spec: { compressor : "zlib", auth: "noauth", ssl: "nossl", jdk: "jdk6", version: ["3.6", "4.0", "4.2", "4.4", "latest"], topology: "standalone", os: "linux" } display_name: "${version} ${compressor} ${topology} ${auth} ${ssl} ${jdk} ${os} " tags: ["tests-variant"] tasks: - name: "test" - matrix_name: "tests-snappy-compression" - matrix_spec: { compressor : "snappy", auth: "noauth", ssl: "nossl", jdk: "jdk7", version: ["3.4", "3.6", "4.0", "4.2", "latest"], topology: "standalone", os: "linux" } + matrix_spec: { compressor : "snappy", auth: "noauth", ssl: "nossl", jdk: "jdk7", version: ["3.4", "3.6", "4.0", "4.2", "4.4", "latest"], topology: "standalone", os: "linux" } display_name: "${version} ${compressor} ${topology} ${auth} ${ssl} ${jdk} ${os} " tags: ["tests-variant"] tasks: - name: "test" - matrix_name: "tests-zstd-compression" - matrix_spec: { compressor : "zstd", auth: "noauth", ssl: "nossl", jdk: "jdk7", version: ["4.2", "latest"], topology: "standalone", os: "linux" } + matrix_spec: { compressor : "zstd", auth: "noauth", ssl: "nossl", jdk: "jdk7", version: ["4.2", "4.4", "latest"], topology: "standalone", os: "linux" } display_name: "${version} ${compressor} ${topology} ${auth} ${ssl} ${jdk} ${os} " tags: ["tests-variant"] tasks: @@ -625,14 +632,14 @@ buildvariants: - matrix_name: "tests-jdk6-secure" matrix_spec: { auth: "auth", ssl: "ssl", jdk: "jdk6", version: "*", topology: "*", os: "linux" } - exclude_spec: { auth: "auth", ssl: "ssl", jdk: "jdk6", version: ["4.0", "4.2", "latest"], topology: "*", os: "linux" } + exclude_spec: { auth: "auth", ssl: "ssl", jdk: "jdk6", version: ["4.0", "4.2", "4.4", "latest"], topology: "*", os: "linux" } display_name: "${version} ${topology} ${auth} ${ssl} ${jdk} ${os} " tags: ["tests-variant"] tasks: - name: "test" - matrix_name: "tests-jdk-newer-secure" - matrix_spec: { auth: "auth", ssl: "ssl", jdk: ["jdk8", "jdk11"], version: ["4.0", "4.2", "latest"], topology: "*", os: "linux" } + matrix_spec: { auth: "auth", ssl: "ssl", jdk: ["jdk8", "jdk11"], version: ["4.0", "4.2", "4.4", "latest"], topology: "*", os: "linux" } display_name: "${version} ${topology} ${auth} ${ssl} ${jdk} ${os} " tags: ["tests-variant"] tasks: @@ -646,28 +653,28 @@ buildvariants: - name: "test" - matrix_name: "tests-slow" - matrix_spec: { auth: "noauth", ssl: "*", jdk: "jdk8", version: ["4.2"], topology: "*", os: "linux" } + matrix_spec: { auth: "noauth", ssl: "*", jdk: "jdk8", version: ["4.4"], topology: "*", os: "linux" } display_name: "Slow: ${version} ${topology} ${ssl} ${jdk} ${os} " tags: ["tests-slow-variant"] tasks: - name: "slow-test" - matrix_name: "tests-socket" - matrix_spec: { auth: "*", ssl: "nossl", jdk: "jdk8", version: ["4.2"], topology: "standalone", os: "linux" } + matrix_spec: { auth: "*", ssl: "nossl", jdk: "jdk8", version: ["4.4"], topology: "standalone", os: "linux" } display_name: "Socket: ${version} ${topology} ${auth} ${jdk} ${os} " tags: ["tests-socket-variant"] tasks: - name: "socket-test" - matrix_name: "tests-socket-snappy-compression" - matrix_spec: { compressor : "snappy", auth: "noauth", ssl: "nossl", jdk: "jdk7", version: ["4.2"], topology: "standalone", os: "linux" } + matrix_spec: { compressor : "snappy", auth: "noauth", ssl: "nossl", jdk: "jdk7", version: ["4.4"], topology: "standalone", os: "linux" } display_name: "Socket: ${version} ${compressor} ${topology} ${auth} ${jdk} ${os} " tags: ["tests-socket-variant"] tasks: - name: "socket-test" - matrix_name: "tests-socket-zstd-compression" - matrix_spec: { compressor : "zstd", auth: "noauth", ssl: "nossl", jdk: "jdk7", version: ["4.2"], topology: "standalone", os: "linux" } + matrix_spec: { compressor : "zstd", auth: "noauth", ssl: "nossl", jdk: "jdk7", version: ["4.4"], topology: "standalone", os: "linux" } display_name: "Socket: ${version} ${compressor} ${topology} ${auth} ${jdk} ${os} " tags: ["tests-socket-variant"] tasks: From 3c8d9bc040efd4f6ccc0a1c4991222e0e71bc1ce Mon Sep 17 00:00:00 2001 From: Jeff Yemin Date: Tue, 5 May 2020 09:47:08 -0400 Subject: [PATCH 15/48] Make wildcard project test compatible with all server versions. --- .../operation/CreateIndexesOperationSpecification.groovy | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/driver-core/src/test/functional/com/mongodb/operation/CreateIndexesOperationSpecification.groovy b/driver-core/src/test/functional/com/mongodb/operation/CreateIndexesOperationSpecification.groovy index 91652a664e3..9eeee686d65 100644 --- a/driver-core/src/test/functional/com/mongodb/operation/CreateIndexesOperationSpecification.groovy +++ b/driver-core/src/test/functional/com/mongodb/operation/CreateIndexesOperationSpecification.groovy @@ -23,6 +23,7 @@ import com.mongodb.MongoWriteConcernException import com.mongodb.OperationFunctionalSpecification import com.mongodb.WriteConcern import com.mongodb.bulk.IndexRequest +import org.bson.BsonBoolean import org.bson.BsonDocument import org.bson.BsonDocumentWrapper import org.bson.BsonInt32 @@ -504,14 +505,14 @@ class CreateIndexesOperationSpecification extends OperationFunctionalSpecificati given: def operation = new CreateIndexesOperation(getNamespace(), [new IndexRequest(new BsonDocument('$**', new BsonInt32(1))) - .wildcardProjection(new BsonDocument('a', new BsonInt32(1)).append('b.c', new BsonInt32(1)))]) + .wildcardProjection(new BsonDocument('a', BsonBoolean.TRUE).append('_id', BsonBoolean.FALSE))]) when: execute(operation, async) then: getUserCreatedIndexes('key').contains(['$**': 1]) - getUserCreatedIndexes('wildcardProjection').contains(['a': 1, 'b.c': 1]) + getUserCreatedIndexes('wildcardProjection').contains(['a': true, '_id': false]) where: async << [true, false] From 3fe1ff2d74ca51242248910439cbb4cde7b3a1ad Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Tue, 21 Apr 2020 14:58:30 +0100 Subject: [PATCH 16/48] Use keyvault database for FLE tests JAVA-3702 --- .../ClientEncryptionCustomEndpointTest.java | 2 +- .../ClientSideEncryptionCorpusTest.java | 4 +- ...entSideEncryptionExternalKeyVaultTest.java | 4 +- .../limits-doc.json | 202 +- .../limits-key.json | 60 +- .../limits-schema.json | 2808 ++++++++--------- .../client-side-encryption/README.rst | 104 +- .../client-side-encryption/aggregate.json | 8 +- .../client-side-encryption/basic.json | 8 +- .../client-side-encryption/bulk.json | 4 +- .../bypassAutoEncryption.json | 4 +- .../client-side-encryption/count.json | 4 +- .../countDocuments.json | 4 +- .../client-side-encryption/delete.json | 8 +- .../client-side-encryption/distinct.json | 4 +- .../client-side-encryption/explain.json | 4 +- .../client-side-encryption/find.json | 8 +- .../findOneAndDelete.json | 4 +- .../findOneAndReplace.json | 4 +- .../findOneAndUpdate.json | 4 +- .../client-side-encryption/getMore.json | 4 +- .../client-side-encryption/insert.json | 8 +- .../client-side-encryption/keyAltName.json | 4 +- .../client-side-encryption/localKMS.json | 4 +- .../client-side-encryption/localSchema.json | 4 +- .../maxWireVersion.json | 1 - .../client-side-encryption/missingKey.json | 6 +- .../client-side-encryption/replaceOne.json | 4 +- .../client-side-encryption/types.json | 32 +- .../client-side-encryption/updateMany.json | 4 +- .../client-side-encryption/updateOne.json | 4 +- .../AbstractClientSideEncryptionTest.java | 6 +- .../ClientEncryptionCustomEndpointTest.java | 2 +- .../ClientSideEncryptionCorpusTest.java | 10 +- ...entSideEncryptionExternalKeyVaultTest.java | 8 +- ...ntSideEncryptionViewAreProhibitedTest.java | 2 +- 36 files changed, 1710 insertions(+), 1645 deletions(-) diff --git a/driver-async/src/test/functional/com/mongodb/async/client/ClientEncryptionCustomEndpointTest.java b/driver-async/src/test/functional/com/mongodb/async/client/ClientEncryptionCustomEndpointTest.java index 6e7aaabfb2e..1e7ced58a11 100644 --- a/driver-async/src/test/functional/com/mongodb/async/client/ClientEncryptionCustomEndpointTest.java +++ b/driver-async/src/test/functional/com/mongodb/async/client/ClientEncryptionCustomEndpointTest.java @@ -88,7 +88,7 @@ public void setUp() { ClientEncryptionSettings.Builder clientEncryptionSettingsBuilder = ClientEncryptionSettings.builder(). keyVaultMongoClientSettings(Fixture.getMongoClientSettings()) .kmsProviders(kmsProviders) - .keyVaultNamespace("admin.datakeys"); + .keyVaultNamespace("keyvault.datakeys"); ClientEncryptionSettings clientEncryptionSettings = clientEncryptionSettingsBuilder.build(); clientEncryption = ClientEncryptions.create(clientEncryptionSettings); diff --git a/driver-async/src/test/functional/com/mongodb/async/client/ClientSideEncryptionCorpusTest.java b/driver-async/src/test/functional/com/mongodb/async/client/ClientSideEncryptionCorpusTest.java index 0508fd31255..6aa2468d27c 100644 --- a/driver-async/src/test/functional/com/mongodb/async/client/ClientSideEncryptionCorpusTest.java +++ b/driver-async/src/test/functional/com/mongodb/async/client/ClientSideEncryptionCorpusTest.java @@ -100,7 +100,7 @@ public void setUp() throws IOException, URISyntaxException { .append("validator", new BsonDocument("$jsonSchema", schemaDocument)), documentCallback); documentCallback.get(); - // Step 3: Drop and create admin.datakeys + // Step 3: Drop and create keyvault.datakeys MongoDatabase adminDatabase = client.getDatabase("admin"); MongoCollection dataKeysCollection = adminDatabase.getCollection("datakeys", BsonDocument.class) .withWriteConcern(WriteConcern.MAJORITY); @@ -133,7 +133,7 @@ public void setUp() throws IOException, URISyntaxException { schemaMap.put("db.coll", schemaDocument); AutoEncryptionSettings.Builder autoEncryptionSettingsBuilder = AutoEncryptionSettings.builder() - .keyVaultNamespace("admin.datakeys") + .keyVaultNamespace("keyvault.datakeys") .kmsProviders(kmsProviders); if (useLocalSchema) { diff --git a/driver-async/src/test/functional/com/mongodb/async/client/ClientSideEncryptionExternalKeyVaultTest.java b/driver-async/src/test/functional/com/mongodb/async/client/ClientSideEncryptionExternalKeyVaultTest.java index 357d4bce220..2c4ef613d8d 100644 --- a/driver-async/src/test/functional/com/mongodb/async/client/ClientSideEncryptionExternalKeyVaultTest.java +++ b/driver-async/src/test/functional/com/mongodb/async/client/ClientSideEncryptionExternalKeyVaultTest.java @@ -97,7 +97,7 @@ public void setUp() throws IOException, URISyntaxException { schemaMap.put("db.coll", bsonDocumentFromPath("external-schema.json")); AutoEncryptionSettings.Builder autoEncryptionSettingsBuilder = AutoEncryptionSettings.builder() - .keyVaultNamespace("admin.datakeys") + .keyVaultNamespace("keyvault.datakeys") .kmsProviders(kmsProviders) .schemaMap(schemaMap); @@ -119,7 +119,7 @@ public void setUp() throws IOException, URISyntaxException { ClientEncryptionSettings.Builder clientEncryptionSettingsBuilder = ClientEncryptionSettings.builder(). keyVaultMongoClientSettings(getMongoClientBuilderFromConnectionString().build()) .kmsProviders(kmsProviders) - .keyVaultNamespace("admin.datakeys"); + .keyVaultNamespace("keyvault.datakeys"); if (withExternalKeyVault) { clientEncryptionSettingsBuilder.keyVaultMongoClientSettings(externalClientSettings); diff --git a/driver-core/src/test/resources/client-side-encryption-limits/limits-doc.json b/driver-core/src/test/resources/client-side-encryption-limits/limits-doc.json index 4deb0baabf9..53de52326c6 100644 --- a/driver-core/src/test/resources/client-side-encryption-limits/limits-doc.json +++ b/driver-core/src/test/resources/client-side-encryption-limits/limits-doc.json @@ -1,102 +1,102 @@ { - "00": "a", - "01": "a", - "02": "a", - "03": "a", - "04": "a", - "05": "a", - "06": "a", - "07": "a", - "08": "a", - "09": "a", - "10": "a", - "11": "a", - "12": "a", - "13": "a", - "14": "a", - "15": "a", - "16": "a", - "17": "a", - "18": "a", - "19": "a", - "20": "a", - "21": "a", - "22": "a", - "23": "a", - "24": "a", - "25": "a", - "26": "a", - "27": "a", - "28": "a", - "29": "a", - "30": "a", - "31": "a", - "32": "a", - "33": "a", - "34": "a", - "35": "a", - "36": "a", - "37": "a", - "38": "a", - "39": "a", - "40": "a", - "41": "a", - "42": "a", - "43": "a", - "44": "a", - "45": "a", - "46": "a", - "47": "a", - "48": "a", - "49": "a", - "50": "a", - "51": "a", - "52": "a", - "53": "a", - "54": "a", - "55": "a", - "56": "a", - "57": "a", - "58": "a", - "59": "a", - "60": "a", - "61": "a", - "62": "a", - "63": "a", - "64": "a", - "65": "a", - "66": "a", - "67": "a", - "68": "a", - "69": "a", - "70": "a", - "71": "a", - "72": "a", - "73": "a", - "74": "a", - "75": "a", - "76": "a", - "77": "a", - "78": "a", - "79": "a", - "80": "a", - "81": "a", - "82": "a", - "83": "a", - "84": "a", - "85": "a", - "86": "a", - "87": "a", - "88": "a", - "89": "a", - "90": "a", - "91": "a", - "92": "a", - "93": "a", - "94": "a", - "95": "a", - "96": "a", - "97": "a", - "98": "a", - "99": "a" -} + "00": "a", + "01": "a", + "02": "a", + "03": "a", + "04": "a", + "05": "a", + "06": "a", + "07": "a", + "08": "a", + "09": "a", + "10": "a", + "11": "a", + "12": "a", + "13": "a", + "14": "a", + "15": "a", + "16": "a", + "17": "a", + "18": "a", + "19": "a", + "20": "a", + "21": "a", + "22": "a", + "23": "a", + "24": "a", + "25": "a", + "26": "a", + "27": "a", + "28": "a", + "29": "a", + "30": "a", + "31": "a", + "32": "a", + "33": "a", + "34": "a", + "35": "a", + "36": "a", + "37": "a", + "38": "a", + "39": "a", + "40": "a", + "41": "a", + "42": "a", + "43": "a", + "44": "a", + "45": "a", + "46": "a", + "47": "a", + "48": "a", + "49": "a", + "50": "a", + "51": "a", + "52": "a", + "53": "a", + "54": "a", + "55": "a", + "56": "a", + "57": "a", + "58": "a", + "59": "a", + "60": "a", + "61": "a", + "62": "a", + "63": "a", + "64": "a", + "65": "a", + "66": "a", + "67": "a", + "68": "a", + "69": "a", + "70": "a", + "71": "a", + "72": "a", + "73": "a", + "74": "a", + "75": "a", + "76": "a", + "77": "a", + "78": "a", + "79": "a", + "80": "a", + "81": "a", + "82": "a", + "83": "a", + "84": "a", + "85": "a", + "86": "a", + "87": "a", + "88": "a", + "89": "a", + "90": "a", + "91": "a", + "92": "a", + "93": "a", + "94": "a", + "95": "a", + "96": "a", + "97": "a", + "98": "a", + "99": "a" +} \ No newline at end of file diff --git a/driver-core/src/test/resources/client-side-encryption-limits/limits-key.json b/driver-core/src/test/resources/client-side-encryption-limits/limits-key.json index 0d928fc5417..b3fe0723b06 100644 --- a/driver-core/src/test/resources/client-side-encryption-limits/limits-key.json +++ b/driver-core/src/test/resources/client-side-encryption-limits/limits-key.json @@ -1,31 +1,31 @@ { - "status": { - "$numberInt": "1" - }, - "_id": { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - }, - "masterKey": { - "provider": "local" - }, - "updateDate": { - "$date": { - "$numberLong": "1557827033449" - } - }, - "keyMaterial": { - "$binary": { - "base64": "Ce9HSz/HKKGkIt4uyy+jDuKGA+rLC2cycykMo6vc8jXxqa1UVDYHWq1r+vZKbnnSRBfB981akzRKZCFpC05CTyFqDhXv6OnMjpG97OZEREGIsHEYiJkBW0jJJvfLLgeLsEpBzsro9FztGGXASxyxFRZFhXvHxyiLOKrdWfs7X1O/iK3pEoHMx6uSNSfUOgbebLfIqW7TO++iQS5g1xovXA==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1557827033449" - } - }, - "keyAltNames": [ "local" ] -} + "status": { + "$numberInt": "1" + }, + "_id": { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + }, + "masterKey": { + "provider": "local" + }, + "updateDate": { + "$date": { + "$numberLong": "1557827033449" + } + }, + "keyMaterial": { + "$binary": { + "base64": "Ce9HSz/HKKGkIt4uyy+jDuKGA+rLC2cycykMo6vc8jXxqa1UVDYHWq1r+vZKbnnSRBfB981akzRKZCFpC05CTyFqDhXv6OnMjpG97OZEREGIsHEYiJkBW0jJJvfLLgeLsEpBzsro9FztGGXASxyxFRZFhXvHxyiLOKrdWfs7X1O/iK3pEoHMx6uSNSfUOgbebLfIqW7TO++iQS5g1xovXA==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1557827033449" + } + }, + "keyAltNames": [ "local" ] +} \ No newline at end of file diff --git a/driver-core/src/test/resources/client-side-encryption-limits/limits-schema.json b/driver-core/src/test/resources/client-side-encryption-limits/limits-schema.json index cd4cda847f3..c06908d9ce7 100644 --- a/driver-core/src/test/resources/client-side-encryption-limits/limits-schema.json +++ b/driver-core/src/test/resources/client-side-encryption-limits/limits-schema.json @@ -1,1405 +1,1405 @@ { - "properties": { - "00": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "01": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "02": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "03": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "04": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "05": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "06": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "07": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "08": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "09": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "10": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "11": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "12": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "13": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "14": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "15": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "16": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "17": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "18": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "19": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "20": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "21": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "22": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "23": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "24": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "25": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "26": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "27": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "28": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "29": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "30": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "31": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "32": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "33": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "34": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "35": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "36": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "37": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "38": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "39": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "40": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "41": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "42": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "43": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "44": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "45": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "46": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "47": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "48": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "49": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "50": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "51": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "52": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "53": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "54": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "55": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "56": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "57": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "58": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "59": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "60": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "61": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "62": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "63": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "64": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "65": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "66": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "67": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "68": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "69": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "70": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "71": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "72": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "73": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "74": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "75": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "76": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "77": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "78": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "79": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "80": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "81": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "82": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "83": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "84": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "85": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "86": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "87": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "88": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "89": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "90": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "91": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "92": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "93": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "94": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "95": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "96": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "97": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "98": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "99": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" -} + "properties": { + "00": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "01": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "02": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "03": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "04": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "05": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "06": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "07": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "08": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "09": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "10": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "11": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "12": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "13": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "14": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "15": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "16": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "17": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "18": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "19": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "20": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "21": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "22": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "23": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "24": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "25": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "26": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "27": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "28": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "29": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "30": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "31": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "32": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "33": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "34": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "35": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "36": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "37": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "38": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "39": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "40": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "41": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "42": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "43": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "44": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "45": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "46": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "47": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "48": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "49": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "50": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "51": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "52": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "53": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "54": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "55": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "56": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "57": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "58": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "59": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "60": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "61": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "62": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "63": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "64": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "65": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "66": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "67": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "68": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "69": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "70": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "71": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "72": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "73": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "74": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "75": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "76": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "77": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "78": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "79": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "80": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "81": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "82": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "83": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "84": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "85": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "86": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "87": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "88": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "89": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "90": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "91": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "92": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "93": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "94": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "95": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "96": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "97": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "98": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "99": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" +} \ No newline at end of file diff --git a/driver-core/src/test/resources/client-side-encryption/README.rst b/driver-core/src/test/resources/client-side-encryption/README.rst index c43c7f47378..00d6cf45c1d 100644 --- a/driver-core/src/test/resources/client-side-encryption/README.rst +++ b/driver-core/src/test/resources/client-side-encryption/README.rst @@ -83,7 +83,7 @@ Each YAML file has the following keys: - ``schemaMap``: Optional, a map from namespaces to local JSON schemas. - - ``keyVaultNamespace``: Optional, a namespace to the key vault collection. Defaults to "admin.datakeys". + - ``keyVaultNamespace``: Optional, a namespace to the key vault collection. Defaults to "keyvault.datakeys". - ``bypassAutoEncryption``: Optional, a boolean to indicate whether or not auto encryption should be bypassed. Defaults to ``false``. @@ -124,8 +124,8 @@ Then for each element in ``tests``: #. If the ``skipReason`` field is present, skip this test completely. #. If the ``key_vault_data`` field is present: - #. Drop the ``admin.datakeys`` collection using writeConcern "majority". - #. Insert the data specified into the ``admin.datakeys`` with write concern "majority". + #. Drop the ``keyvault.datakeys`` collection using writeConcern "majority". + #. Insert the data specified into the ``keyvault.datakeys`` with write concern "majority". #. Create a MongoClient. @@ -144,7 +144,7 @@ Then for each element in ``tests``: #. Create a **new** MongoClient using ``clientOptions``. #. If ``autoEncryptOpts`` includes ``aws`` as a KMS provider, pass in AWS credentials from the environment. - #. If ``autoEncryptOpts`` does not include ``keyVaultNamespace``, default it to ``admin.datakeys``. + #. If ``autoEncryptOpts`` does not include ``keyVaultNamespace``, default it to ``keyvault.datakeys``. #. For each element in ``operations``: @@ -208,6 +208,8 @@ In the prose tests LOCAL_MASTERKEY refers to the following base64: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk +Perform all applicable operations on key vault collections (e.g. inserting an example data key, or running a find command) with readConcern/writeConcern "majority". + Data key and double encryption ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -215,7 +217,7 @@ First, perform the setup. #. Create a MongoClient without encryption enabled (referred to as ``client``). Enable command monitoring to listen for command_started events. -#. Using ``client``, drop the collections ``admin.datakeys`` and ``db.coll``. +#. Using ``client``, drop the collections ``keyvault.datakeys`` and ``db.coll``. #. Create the following: @@ -231,7 +233,7 @@ First, perform the setup. "local": { "key": } } - Configure both objects with ``keyVaultNamespace`` set to ``admin.datakeys``. + Configure both objects with ``keyVaultNamespace`` set to ``keyvault.datakeys``. Configure the ``MongoClient`` with the following ``schema_map``: @@ -259,7 +261,7 @@ Then, test creating and using data keys from a ``local`` KMS provider: #. Call ``client_encryption.createDataKey()`` with the ``local`` KMS provider and keyAltNames set to ``["local_altname"]``. - Expect a BSON binary with subtype 4 to be returned, referred to as ``local_datakey_id``. - - Use ``client`` to run a ``find`` on ``admin.datakeys`` by querying with the ``_id`` set to the ``local_datakey_id``. + - Use ``client`` to run a ``find`` on ``keyvault.datakeys`` by querying with the ``_id`` set to the ``local_datakey_id``. - Expect that exactly one document is returned with the "masterKey.provider" equal to "local". - Check that ``client`` captured a command_started event for the ``insert`` command containing a majority writeConcern. @@ -286,7 +288,7 @@ Then, repeat the above tests with the ``aws`` KMS provider: - Expect a BSON binary with subtype 4 to be returned, referred to as ``aws_datakey_id``. - - Use ``client`` to run a ``find`` on ``admin.datakeys`` by querying with the ``_id`` set to the ``aws_datakey_id``. + - Use ``client`` to run a ``find`` on ``keyvault.datakeys`` by querying with the ``_id`` set to the ``aws_datakey_id``. - Expect that exactly one document is returned with the "masterKey.provider" equal to "aws". - Check that ``client`` captured a command_started event for the ``insert`` command containing a majority writeConcern. @@ -317,8 +319,8 @@ Run the following tests twice, parameterized by a boolean ``withExternalKeyVault #. Create a MongoClient without encryption enabled (referred to as ``client``). -#. Using ``client``, drop the collections ``admin.datakeys`` and ``db.coll``. - Insert the document `external/external-key.json <../external/external-key.json>`_ into ``admin.datakeys``. +#. Using ``client``, drop the collections ``keyvault.datakeys`` and ``db.coll``. + Insert the document `external/external-key.json <../external/external-key.json>`_ into ``keyvault.datakeys``. #. Create the following: @@ -331,7 +333,7 @@ Run the following tests twice, parameterized by a boolean ``withExternalKeyVault { "local": { "key": } } - Configure both objects with ``keyVaultNamespace`` set to ``admin.datakeys``. + Configure both objects with ``keyVaultNamespace`` set to ``keyvault.datakeys``. Configure ``client_encrypted`` to use the schema `external/external-schema.json <../external/external-schema.json>`_ for ``db.coll`` by setting a schema map like: ``{ "db.coll": }`` @@ -354,7 +356,7 @@ First, perform the setup. #. Using ``client``, drop and create the collection ``db.coll`` configured with the included JSON schema `limits/limits-schema.json <../limits/limits-schema.json>`_. -#. Using ``client``, drop the collection ``admin.datakeys``. Insert the document `limits/limits-key.json <../limits/limits-key.json>`_ +#. Using ``client``, drop the collection ``keyvault.datakeys``. Insert the document `limits/limits-key.json <../limits/limits-key.json>`_ #. Create a MongoClient configured with auto encryption (referred to as ``client_encrypted``) @@ -364,7 +366,7 @@ First, perform the setup. { "local": { "key": } } - Configure with the ``keyVaultNamespace`` set to ``admin.datakeys``. + Configure with the ``keyVaultNamespace`` set to ``keyvault.datakeys``. Using ``client_encrypted`` perform the following operations: @@ -420,13 +422,13 @@ Views are prohibited { "local": { "key": } } - Configure with the ``keyVaultNamespace`` set to ``admin.datakeys``. + Configure with the ``keyVaultNamespace`` set to ``keyvault.datakeys``. #. Using ``client_encrypted``, attempt to insert a document into ``db.view``. Expect an exception to be thrown containing the message: "cannot auto encrypt a view". Corpus Test -=========== +~~~~~~~~~~~ The corpus test exhaustively enumerates all ways to encrypt all BSON value types. Note, the test data includes BSON binary subtype 4 (or standard UUID), which MUST be decoded and encoded as subtype 4. Run the test as follows. @@ -434,7 +436,7 @@ The corpus test exhaustively enumerates all ways to encrypt all BSON value types 2. Using ``client``, drop and create the collection ``db.coll`` configured with the included JSON schema `corpus/corpus-schema.json <../corpus/corpus-schema.json>`_. -3. Using ``client``, drop the collection ``admin.datakeys``. Insert the documents `corpus/corpus-key-local.json <../corpus/corpus-key-local.json>`_ and `corpus/corpus-key-aws.json <../corpus/corpus-key-aws.json>`_. +3. Using ``client``, drop the collection ``keyvault.datakeys``. Insert the documents `corpus/corpus-key-local.json <../corpus/corpus-key-local.json>`_ and `corpus/corpus-key-aws.json <../corpus/corpus-key-aws.json>`_. 4. Create the following: @@ -456,7 +458,7 @@ The corpus test exhaustively enumerates all ways to encrypt all BSON value types Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk - Configure both objects with ``keyVaultNamespace`` set to ``admin.datakeys``. + Configure both objects with ``keyVaultNamespace`` set to ``keyvault.datakeys``. 5. Load `corpus/corpus.json <../corpus/corpus.json>`_ to a variable named ``corpus``. The corpus contains subdocuments with the following fields: @@ -507,7 +509,7 @@ The corpus test exhaustively enumerates all ways to encrypt all BSON value types 9. Repeat steps 1-8 with a local JSON schema. I.e. amend step 4 to configure the schema on ``client_encrypted`` with the ``schema_map`` option. Custom Endpoint Test -==================== +~~~~~~~~~~~~~~~~~~~~ Data keys created with AWS KMS may specify a custom endpoint to contact (instead of the default endpoint derived from the AWS region). @@ -521,7 +523,7 @@ Data keys created with AWS KMS may specify a custom endpoint to contact (instead "aws": { } } - Configure with ``keyVaultNamespace`` set to ``admin.datakeys``, and a default MongoClient as the ``keyVaultClient``. + Configure with ``keyVaultNamespace`` set to ``keyvault.datakeys``, and a default MongoClient as the ``keyVaultClient``. 2. Call `client_encryption.createDataKey()` with "aws" as the provider and the following masterKey: @@ -594,3 +596,67 @@ Data keys created with AWS KMS may specify a custom endpoint to contact (instead Expect this to fail with an exception with a message containing the string: "parse error" +Bypass spawning mongocryptd +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Via mongocryptdBypassSpawn +`````````````````````````` + +The following tests that setting ``mongocryptdBypassSpawn=true`` really does bypass spawning mongocryptd. + +#. Create a MongoClient configured with auto encryption (referred to as ``client_encrypted``) + + Configure the required options. Use the ``local`` KMS provider as follows: + + .. code:: javascript + + { "local": { "key": } } + + Configure with the ``keyVaultNamespace`` set to ``keyvault.datakeys``. + + Configure ``client_encrypted`` to use the schema `external/external-schema.json <../external/external-schema.json>`_ for ``db.coll`` by setting a schema map like: ``{ "db.coll": }`` + + Configure the following ``extraOptions``: + + .. code:: javascript + + { + "mongocryptdBypassSpawn": true + "mongocryptdURI": "mongodb://localhost:27021/db?serverSelectionTimeoutMS=1000", + "mongocryptdSpawnArgs": [ "--pidfilepath=bypass-spawning-mongocryptd.pid", "--port=27021"] + } + + Drivers MAY pass a different port if they expect their testing infrastructure to be using port 27021. Pass a port that should be free. + +#. Use ``client_encrypted`` to insert the document ``{"encrypted": "test"}`` into ``db.coll``. Expect a server selection error propagated from the internal MongoClient failing to connect to mongocryptd on port 27021. + +Via bypassAutoEncryption +```````````````````````` + +The following tests that setting ``bypassAutoEncryption=true`` really does bypass spawning mongocryptd. + +#. Create a MongoClient configured with auto encryption (referred to as ``client_encrypted``) + + Configure the required options. Use the ``local`` KMS provider as follows: + + .. code:: javascript + + { "local": { "key": } } + + Configure with the ``keyVaultNamespace`` set to ``keyvault.datakeys``. + + Configure with ``bypassAutoEncryption=true``. + + Configure the following ``extraOptions``: + + .. code:: javascript + + { + "mongocryptdSpawnArgs": [ "--pidfilepath=bypass-spawning-mongocryptd.pid", "--port=27021"] + } + + Drivers MAY pass a different value to ``--port`` if they expect their testing infrastructure to be using port 27021. Pass a port that should be free. + +#. Use ``client_encrypted`` to insert the document ``{"unencrypted": "test"}`` into ``db.coll``. Expect this to succeed. + +#. Validate that mongocryptd was not spawned. Create a MongoClient to localhost:27021 (or whatever was passed via ``--port``) with serverSelectionTimeoutMS=1000. Run an ``isMaster`` command and ensure it fails with a server selection timeout. \ No newline at end of file diff --git a/driver-core/src/test/resources/client-side-encryption/aggregate.json b/driver-core/src/test/resources/client-side-encryption/aggregate.json index 6bc92427178..a9e79f9edbf 100644 --- a/driver-core/src/test/resources/client-side-encryption/aggregate.json +++ b/driver-core/src/test/resources/client-side-encryption/aggregate.json @@ -157,7 +157,7 @@ "filter": { "name": "datakeys" }, - "$db": "admin" + "$db": "keyvault" }, "command_name": "listCollections" } @@ -187,7 +187,7 @@ } ] }, - "$db": "admin", + "$db": "keyvault", "readConcern": { "level": "majority" } @@ -280,7 +280,7 @@ "filter": { "name": "datakeys" }, - "$db": "admin" + "$db": "keyvault" }, "command_name": "listCollections" } @@ -310,7 +310,7 @@ } ] }, - "$db": "admin", + "$db": "keyvault", "readConcern": { "level": "majority" } diff --git a/driver-core/src/test/resources/client-side-encryption/basic.json b/driver-core/src/test/resources/client-side-encryption/basic.json index 371894e8ca2..3f9895fd5db 100644 --- a/driver-core/src/test/resources/client-side-encryption/basic.json +++ b/driver-core/src/test/resources/client-side-encryption/basic.json @@ -151,7 +151,7 @@ "filter": { "name": "datakeys" }, - "$db": "admin" + "$db": "keyvault" }, "command_name": "listCollections" } @@ -181,7 +181,7 @@ } ] }, - "$db": "admin", + "$db": "keyvault", "readConcern": { "level": "majority" } @@ -290,7 +290,7 @@ "filter": { "name": "datakeys" }, - "$db": "admin" + "$db": "keyvault" }, "command_name": "listCollections" } @@ -320,7 +320,7 @@ } ] }, - "$db": "admin", + "$db": "keyvault", "readConcern": { "level": "majority" } diff --git a/driver-core/src/test/resources/client-side-encryption/bulk.json b/driver-core/src/test/resources/client-side-encryption/bulk.json index 7a401d5e8ee..ead90985a16 100644 --- a/driver-core/src/test/resources/client-side-encryption/bulk.json +++ b/driver-core/src/test/resources/client-side-encryption/bulk.json @@ -185,7 +185,7 @@ "filter": { "name": "datakeys" }, - "$db": "admin" + "$db": "keyvault" }, "command_name": "listCollections" } @@ -215,7 +215,7 @@ } ] }, - "$db": "admin", + "$db": "keyvault", "readConcern": { "level": "majority" } diff --git a/driver-core/src/test/resources/client-side-encryption/bypassAutoEncryption.json b/driver-core/src/test/resources/client-side-encryption/bypassAutoEncryption.json index 42f44732235..9d09cb3fa92 100644 --- a/driver-core/src/test/resources/client-side-encryption/bypassAutoEncryption.json +++ b/driver-core/src/test/resources/client-side-encryption/bypassAutoEncryption.json @@ -196,7 +196,7 @@ } ] }, - "$db": "admin", + "$db": "keyvault", "readConcern": { "level": "majority" } @@ -369,7 +369,7 @@ } ] }, - "$db": "admin", + "$db": "keyvault", "readConcern": { "level": "majority" } diff --git a/driver-core/src/test/resources/client-side-encryption/count.json b/driver-core/src/test/resources/client-side-encryption/count.json index 9ac5104a09b..24f46a110a7 100644 --- a/driver-core/src/test/resources/client-side-encryption/count.json +++ b/driver-core/src/test/resources/client-side-encryption/count.json @@ -156,7 +156,7 @@ "filter": { "name": "datakeys" }, - "$db": "admin" + "$db": "keyvault" }, "command_name": "listCollections" } @@ -186,7 +186,7 @@ } ] }, - "$db": "admin", + "$db": "keyvault", "readConcern": { "level": "majority" } diff --git a/driver-core/src/test/resources/client-side-encryption/countDocuments.json b/driver-core/src/test/resources/client-side-encryption/countDocuments.json index d4ae0aeb469..3cf5fbca8b6 100644 --- a/driver-core/src/test/resources/client-side-encryption/countDocuments.json +++ b/driver-core/src/test/resources/client-side-encryption/countDocuments.json @@ -157,7 +157,7 @@ "filter": { "name": "datakeys" }, - "$db": "admin" + "$db": "keyvault" }, "command_name": "listCollections" } @@ -187,7 +187,7 @@ } ] }, - "$db": "admin", + "$db": "keyvault", "readConcern": { "level": "majority" } diff --git a/driver-core/src/test/resources/client-side-encryption/delete.json b/driver-core/src/test/resources/client-side-encryption/delete.json index bb9c0615560..30fb453a936 100644 --- a/driver-core/src/test/resources/client-side-encryption/delete.json +++ b/driver-core/src/test/resources/client-side-encryption/delete.json @@ -158,7 +158,7 @@ "filter": { "name": "datakeys" }, - "$db": "admin" + "$db": "keyvault" }, "command_name": "listCollections" } @@ -188,7 +188,7 @@ } ] }, - "$db": "admin", + "$db": "keyvault", "readConcern": { "level": "majority" } @@ -283,7 +283,7 @@ "filter": { "name": "datakeys" }, - "$db": "admin" + "$db": "keyvault" }, "command_name": "listCollections" } @@ -313,7 +313,7 @@ } ] }, - "$db": "admin", + "$db": "keyvault", "readConcern": { "level": "majority" } diff --git a/driver-core/src/test/resources/client-side-encryption/distinct.json b/driver-core/src/test/resources/client-side-encryption/distinct.json index c473030580e..7a5f75c4a51 100644 --- a/driver-core/src/test/resources/client-side-encryption/distinct.json +++ b/driver-core/src/test/resources/client-side-encryption/distinct.json @@ -168,7 +168,7 @@ "filter": { "name": "datakeys" }, - "$db": "admin" + "$db": "keyvault" }, "command_name": "listCollections" } @@ -198,7 +198,7 @@ } ] }, - "$db": "admin", + "$db": "keyvault", "readConcern": { "level": "majority" } diff --git a/driver-core/src/test/resources/client-side-encryption/explain.json b/driver-core/src/test/resources/client-side-encryption/explain.json index 6872cedf2b0..5ad46bc238b 100644 --- a/driver-core/src/test/resources/client-side-encryption/explain.json +++ b/driver-core/src/test/resources/client-side-encryption/explain.json @@ -162,7 +162,7 @@ "filter": { "name": "datakeys" }, - "$db": "admin" + "$db": "keyvault" }, "command_name": "listCollections" } @@ -192,7 +192,7 @@ } ] }, - "$db": "admin", + "$db": "keyvault", "readConcern": { "level": "majority" } diff --git a/driver-core/src/test/resources/client-side-encryption/find.json b/driver-core/src/test/resources/client-side-encryption/find.json index 93cef311c08..b7c5258a13c 100644 --- a/driver-core/src/test/resources/client-side-encryption/find.json +++ b/driver-core/src/test/resources/client-side-encryption/find.json @@ -167,7 +167,7 @@ "filter": { "name": "datakeys" }, - "$db": "admin" + "$db": "keyvault" }, "command_name": "listCollections" } @@ -197,7 +197,7 @@ } ] }, - "$db": "admin", + "$db": "keyvault", "readConcern": { "level": "majority" } @@ -309,7 +309,7 @@ "filter": { "name": "datakeys" }, - "$db": "admin" + "$db": "keyvault" }, "command_name": "listCollections" } @@ -339,7 +339,7 @@ } ] }, - "$db": "admin", + "$db": "keyvault", "readConcern": { "level": "majority" } diff --git a/driver-core/src/test/resources/client-side-encryption/findOneAndDelete.json b/driver-core/src/test/resources/client-side-encryption/findOneAndDelete.json index 2d9f963f23d..6261d8601ba 100644 --- a/driver-core/src/test/resources/client-side-encryption/findOneAndDelete.json +++ b/driver-core/src/test/resources/client-side-encryption/findOneAndDelete.json @@ -155,7 +155,7 @@ "filter": { "name": "datakeys" }, - "$db": "admin" + "$db": "keyvault" }, "command_name": "listCollections" } @@ -185,7 +185,7 @@ } ] }, - "$db": "admin", + "$db": "keyvault", "readConcern": { "level": "majority" } diff --git a/driver-core/src/test/resources/client-side-encryption/findOneAndReplace.json b/driver-core/src/test/resources/client-side-encryption/findOneAndReplace.json index 1512fb95525..d91bc059980 100644 --- a/driver-core/src/test/resources/client-side-encryption/findOneAndReplace.json +++ b/driver-core/src/test/resources/client-side-encryption/findOneAndReplace.json @@ -154,7 +154,7 @@ "filter": { "name": "datakeys" }, - "$db": "admin" + "$db": "keyvault" }, "command_name": "listCollections" } @@ -184,7 +184,7 @@ } ] }, - "$db": "admin", + "$db": "keyvault", "readConcern": { "level": "majority" } diff --git a/driver-core/src/test/resources/client-side-encryption/findOneAndUpdate.json b/driver-core/src/test/resources/client-side-encryption/findOneAndUpdate.json index a5b41f84558..fad70609ad7 100644 --- a/driver-core/src/test/resources/client-side-encryption/findOneAndUpdate.json +++ b/driver-core/src/test/resources/client-side-encryption/findOneAndUpdate.json @@ -156,7 +156,7 @@ "filter": { "name": "datakeys" }, - "$db": "admin" + "$db": "keyvault" }, "command_name": "listCollections" } @@ -186,7 +186,7 @@ } ] }, - "$db": "admin", + "$db": "keyvault", "readConcern": { "level": "majority" } diff --git a/driver-core/src/test/resources/client-side-encryption/getMore.json b/driver-core/src/test/resources/client-side-encryption/getMore.json index 637f69d5094..cf234422264 100644 --- a/driver-core/src/test/resources/client-side-encryption/getMore.json +++ b/driver-core/src/test/resources/client-side-encryption/getMore.json @@ -186,7 +186,7 @@ "filter": { "name": "datakeys" }, - "$db": "admin" + "$db": "keyvault" }, "command_name": "listCollections" } @@ -216,7 +216,7 @@ } ] }, - "$db": "admin", + "$db": "keyvault", "readConcern": { "level": "majority" } diff --git a/driver-core/src/test/resources/client-side-encryption/insert.json b/driver-core/src/test/resources/client-side-encryption/insert.json index beb98c5eb00..78fa8feba0e 100644 --- a/driver-core/src/test/resources/client-side-encryption/insert.json +++ b/driver-core/src/test/resources/client-side-encryption/insert.json @@ -138,7 +138,7 @@ "filter": { "name": "datakeys" }, - "$db": "admin" + "$db": "keyvault" }, "command_name": "listCollections" } @@ -168,7 +168,7 @@ } ] }, - "$db": "admin", + "$db": "keyvault", "readConcern": { "level": "majority" } @@ -265,7 +265,7 @@ "filter": { "name": "datakeys" }, - "$db": "admin" + "$db": "keyvault" }, "command_name": "listCollections" } @@ -295,7 +295,7 @@ } ] }, - "$db": "admin", + "$db": "keyvault", "readConcern": { "level": "majority" } diff --git a/driver-core/src/test/resources/client-side-encryption/keyAltName.json b/driver-core/src/test/resources/client-side-encryption/keyAltName.json index 7088d0b0be3..d062bed4537 100644 --- a/driver-core/src/test/resources/client-side-encryption/keyAltName.json +++ b/driver-core/src/test/resources/client-side-encryption/keyAltName.json @@ -138,7 +138,7 @@ "filter": { "name": "datakeys" }, - "$db": "admin" + "$db": "keyvault" }, "command_name": "listCollections" } @@ -163,7 +163,7 @@ } ] }, - "$db": "admin", + "$db": "keyvault", "readConcern": { "level": "majority" } diff --git a/driver-core/src/test/resources/client-side-encryption/localKMS.json b/driver-core/src/test/resources/client-side-encryption/localKMS.json index febc1ccfc88..e4d25309c44 100644 --- a/driver-core/src/test/resources/client-side-encryption/localKMS.json +++ b/driver-core/src/test/resources/client-side-encryption/localKMS.json @@ -121,7 +121,7 @@ "filter": { "name": "datakeys" }, - "$db": "admin" + "$db": "keyvault" }, "command_name": "listCollections" } @@ -151,7 +151,7 @@ } ] }, - "$db": "admin" + "$db": "keyvault" }, "command_name": "find" } diff --git a/driver-core/src/test/resources/client-side-encryption/localSchema.json b/driver-core/src/test/resources/client-side-encryption/localSchema.json index f939dbc1234..7071d6fefd1 100644 --- a/driver-core/src/test/resources/client-side-encryption/localSchema.json +++ b/driver-core/src/test/resources/client-side-encryption/localSchema.json @@ -143,7 +143,7 @@ "filter": { "name": "datakeys" }, - "$db": "admin" + "$db": "keyvault" }, "command_name": "listCollections" } @@ -173,7 +173,7 @@ } ] }, - "$db": "admin", + "$db": "keyvault", "readConcern": { "level": "majority" } diff --git a/driver-core/src/test/resources/client-side-encryption/maxWireVersion.json b/driver-core/src/test/resources/client-side-encryption/maxWireVersion.json index 39d8c6485c1..144786290dc 100644 --- a/driver-core/src/test/resources/client-side-encryption/maxWireVersion.json +++ b/driver-core/src/test/resources/client-side-encryption/maxWireVersion.json @@ -46,7 +46,6 @@ "tests": [ { "description": "operation fails with maxWireVersion < 8", - "skipReason": "waiting on SPEC-1403", "clientOptions": { "autoEncryptOpts": { "kmsProviders": { diff --git a/driver-core/src/test/resources/client-side-encryption/missingKey.json b/driver-core/src/test/resources/client-side-encryption/missingKey.json index a7237f17924..ac8e8320b0e 100644 --- a/driver-core/src/test/resources/client-side-encryption/missingKey.json +++ b/driver-core/src/test/resources/client-side-encryption/missingKey.json @@ -102,7 +102,7 @@ "description": "Insert with encryption on a missing key", "clientOptions": { "autoEncryptOpts": { - "keyVaultNamespace": "admin.different", + "keyVaultNamespace": "keyvault.different", "kmsProviders": { "aws": {} } @@ -147,7 +147,7 @@ "filter": { "name": "different" }, - "$db": "admin" + "$db": "keyvault" }, "command_name": "listCollections" } @@ -177,7 +177,7 @@ } ] }, - "$db": "admin", + "$db": "keyvault", "readConcern": { "level": "majority" } diff --git a/driver-core/src/test/resources/client-side-encryption/replaceOne.json b/driver-core/src/test/resources/client-side-encryption/replaceOne.json index 1287fdea14a..5cdb3d40f0d 100644 --- a/driver-core/src/test/resources/client-side-encryption/replaceOne.json +++ b/driver-core/src/test/resources/client-side-encryption/replaceOne.json @@ -155,7 +155,7 @@ "filter": { "name": "datakeys" }, - "$db": "admin" + "$db": "keyvault" }, "command_name": "listCollections" } @@ -185,7 +185,7 @@ } ] }, - "$db": "admin", + "$db": "keyvault", "readConcern": { "level": "majority" } diff --git a/driver-core/src/test/resources/client-side-encryption/types.json b/driver-core/src/test/resources/client-side-encryption/types.json index 08928381e15..47e4c27a2e2 100644 --- a/driver-core/src/test/resources/client-side-encryption/types.json +++ b/driver-core/src/test/resources/client-side-encryption/types.json @@ -110,7 +110,7 @@ "filter": { "name": "datakeys" }, - "$db": "admin" + "$db": "keyvault" }, "command_name": "listCollections" } @@ -140,7 +140,7 @@ } ] }, - "$db": "admin", + "$db": "keyvault", "readConcern": { "level": "majority" } @@ -261,7 +261,7 @@ "filter": { "name": "datakeys" }, - "$db": "admin" + "$db": "keyvault" }, "command_name": "listCollections" } @@ -291,7 +291,7 @@ } ] }, - "$db": "admin", + "$db": "keyvault", "readConcern": { "level": "majority" } @@ -412,7 +412,7 @@ "filter": { "name": "datakeys" }, - "$db": "admin" + "$db": "keyvault" }, "command_name": "listCollections" } @@ -442,7 +442,7 @@ } ] }, - "$db": "admin", + "$db": "keyvault", "readConcern": { "level": "majority" } @@ -663,7 +663,7 @@ "filter": { "name": "datakeys" }, - "$db": "admin" + "$db": "keyvault" }, "command_name": "listCollections" } @@ -693,7 +693,7 @@ } ] }, - "$db": "admin", + "$db": "keyvault", "readConcern": { "level": "majority" } @@ -814,7 +814,7 @@ "filter": { "name": "datakeys" }, - "$db": "admin" + "$db": "keyvault" }, "command_name": "listCollections" } @@ -844,7 +844,7 @@ } ] }, - "$db": "admin", + "$db": "keyvault", "readConcern": { "level": "majority" } @@ -1064,7 +1064,7 @@ "filter": { "name": "datakeys" }, - "$db": "admin" + "$db": "keyvault" }, "command_name": "listCollections" } @@ -1094,7 +1094,7 @@ } ] }, - "$db": "admin", + "$db": "keyvault", "readConcern": { "level": "majority" } @@ -1221,7 +1221,7 @@ "filter": { "name": "datakeys" }, - "$db": "admin" + "$db": "keyvault" }, "command_name": "listCollections" } @@ -1251,7 +1251,7 @@ } ] }, - "$db": "admin", + "$db": "keyvault", "readConcern": { "level": "majority" } @@ -1376,7 +1376,7 @@ "filter": { "name": "datakeys" }, - "$db": "admin" + "$db": "keyvault" }, "command_name": "listCollections" } @@ -1406,7 +1406,7 @@ } ] }, - "$db": "admin", + "$db": "keyvault", "readConcern": { "level": "majority" } diff --git a/driver-core/src/test/resources/client-side-encryption/updateMany.json b/driver-core/src/test/resources/client-side-encryption/updateMany.json index 43c6dd717c9..fd1f4d12bdf 100644 --- a/driver-core/src/test/resources/client-side-encryption/updateMany.json +++ b/driver-core/src/test/resources/client-side-encryption/updateMany.json @@ -171,7 +171,7 @@ "filter": { "name": "datakeys" }, - "$db": "admin" + "$db": "keyvault" }, "command_name": "listCollections" } @@ -201,7 +201,7 @@ } ] }, - "$db": "admin", + "$db": "keyvault", "readConcern": { "level": "majority" } diff --git a/driver-core/src/test/resources/client-side-encryption/updateOne.json b/driver-core/src/test/resources/client-side-encryption/updateOne.json index d6a6de79e2e..bed763d7205 100644 --- a/driver-core/src/test/resources/client-side-encryption/updateOne.json +++ b/driver-core/src/test/resources/client-side-encryption/updateOne.json @@ -157,7 +157,7 @@ "filter": { "name": "datakeys" }, - "$db": "admin" + "$db": "keyvault" }, "command_name": "listCollections" } @@ -187,7 +187,7 @@ } ] }, - "$db": "admin", + "$db": "keyvault", "readConcern": { "level": "majority" } diff --git a/driver-sync/src/test/functional/com/mongodb/client/AbstractClientSideEncryptionTest.java b/driver-sync/src/test/functional/com/mongodb/client/AbstractClientSideEncryptionTest.java index 91f402b5959..24842a98620 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/AbstractClientSideEncryptionTest.java +++ b/driver-sync/src/test/functional/com/mongodb/client/AbstractClientSideEncryptionTest.java @@ -146,9 +146,9 @@ public void setUp() { database.getCollection(collectionName, BsonDocument.class).insertMany(documents); } - /* Insert data into the "admin.datakeys" key vault. */ + /* Insert data into the "keyvault.datakeys" key vault. */ BsonArray data = specDocument.getArray("key_vault_data", new BsonArray()); - collection = getMongoClient().getDatabase("admin").getCollection("datakeys", BsonDocument.class) + collection = getMongoClient().getDatabase("keyvault").getCollection("datakeys", BsonDocument.class) .withWriteConcern(WriteConcern.MAJORITY); collection.drop(); if (!data.isEmpty()) { @@ -212,7 +212,7 @@ public void setUp() { } } - String keyVaultNamespace = "admin.datakeys"; + String keyVaultNamespace = "keyvault.datakeys"; if (cryptOptions.containsKey("keyVaultNamespace")) { keyVaultNamespace = cryptOptions.getString("keyVaultNamespace").getValue(); } diff --git a/driver-sync/src/test/functional/com/mongodb/client/ClientEncryptionCustomEndpointTest.java b/driver-sync/src/test/functional/com/mongodb/client/ClientEncryptionCustomEndpointTest.java index 626e35ec17e..3414c6b52a2 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/ClientEncryptionCustomEndpointTest.java +++ b/driver-sync/src/test/functional/com/mongodb/client/ClientEncryptionCustomEndpointTest.java @@ -86,7 +86,7 @@ public void setUp() { ClientEncryptionSettings.Builder clientEncryptionSettingsBuilder = ClientEncryptionSettings.builder(). keyVaultMongoClientSettings(getMongoClientSettings()) .kmsProviders(kmsProviders) - .keyVaultNamespace("admin.datakeys"); + .keyVaultNamespace("keyvault.datakeys"); ClientEncryptionSettings clientEncryptionSettings = clientEncryptionSettingsBuilder.build(); clientEncryption = ClientEncryptions.create(clientEncryptionSettings); diff --git a/driver-sync/src/test/functional/com/mongodb/client/ClientSideEncryptionCorpusTest.java b/driver-sync/src/test/functional/com/mongodb/client/ClientSideEncryptionCorpusTest.java index d42489ce282..106c3933c02 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/ClientSideEncryptionCorpusTest.java +++ b/driver-sync/src/test/functional/com/mongodb/client/ClientSideEncryptionCorpusTest.java @@ -93,9 +93,9 @@ public void setUp() throws IOException, URISyntaxException { db.runCommand(new BsonDocument("create", new BsonString("coll")) .append("validator", new BsonDocument("$jsonSchema", schemaDocument))); - // Step 3: Drop and create admin.datakeys - MongoDatabase adminDatabase = client.getDatabase("admin"); - MongoCollection dataKeysCollection = adminDatabase.getCollection("datakeys", BsonDocument.class) + // Step 3: Drop and create keyvault.datakeys + MongoDatabase keyvaultDatabase = client.getDatabase("keyvault"); + MongoCollection dataKeysCollection = keyvaultDatabase.getCollection("datakeys", BsonDocument.class) .withWriteConcern(WriteConcern.MAJORITY); dataKeysCollection.drop(); dataKeysCollection.insertOne(bsonDocumentFromPath("corpus-key-aws.json")); @@ -118,7 +118,7 @@ public void setUp() throws IOException, URISyntaxException { schemaMap.put("db.coll", schemaDocument); AutoEncryptionSettings.Builder autoEncryptionSettingsBuilder = AutoEncryptionSettings.builder() - .keyVaultNamespace("admin.datakeys") + .keyVaultNamespace("keyvault.datakeys") .kmsProviders(kmsProviders); if (useLocalSchema) { @@ -135,7 +135,7 @@ public void setUp() throws IOException, URISyntaxException { ClientEncryptionSettings clientEncryptionSettings = ClientEncryptionSettings.builder(). keyVaultMongoClientSettings(getMongoClientSettings()). kmsProviders(kmsProviders). - keyVaultNamespace("admin.datakeys").build(); + keyVaultNamespace("keyvault.datakeys").build(); clientEncryption = ClientEncryptions.create(clientEncryptionSettings); } diff --git a/driver-sync/src/test/functional/com/mongodb/client/ClientSideEncryptionExternalKeyVaultTest.java b/driver-sync/src/test/functional/com/mongodb/client/ClientSideEncryptionExternalKeyVaultTest.java index ea09d40fd71..9eb6f3f6193 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/ClientSideEncryptionExternalKeyVaultTest.java +++ b/driver-sync/src/test/functional/com/mongodb/client/ClientSideEncryptionExternalKeyVaultTest.java @@ -73,8 +73,8 @@ public void setUp() throws IOException, URISyntaxException { /* Step 1: get unencrypted client and recreate keys collection */ client = getMongoClient(); - MongoDatabase admin = client.getDatabase("admin"); - MongoCollection datakeys = admin.getCollection("datakeys", BsonDocument.class) + MongoDatabase keyvaultDatabase = client.getDatabase("keyvault"); + MongoCollection datakeys = keyvaultDatabase.getCollection("datakeys", BsonDocument.class) .withWriteConcern(WriteConcern.MAJORITY); datakeys.drop(); datakeys.insertOne(bsonDocumentFromPath("external-key.json")); @@ -91,7 +91,7 @@ public void setUp() throws IOException, URISyntaxException { schemaMap.put("db.coll", bsonDocumentFromPath("external-schema.json")); AutoEncryptionSettings.Builder autoEncryptionSettingsBuilder = AutoEncryptionSettings.builder() - .keyVaultNamespace("admin.datakeys") + .keyVaultNamespace("keyvault.datakeys") .kmsProviders(kmsProviders) .schemaMap(schemaMap); @@ -113,7 +113,7 @@ public void setUp() throws IOException, URISyntaxException { ClientEncryptionSettings.Builder clientEncryptionSettingsBuilder = ClientEncryptionSettings.builder(). keyVaultMongoClientSettings(getMongoClientSettingsBuilder().build()) .kmsProviders(kmsProviders) - .keyVaultNamespace("admin.datakeys"); + .keyVaultNamespace("keyvault.datakeys"); if (withExternalKeyVault) { clientEncryptionSettingsBuilder.keyVaultMongoClientSettings(externalClientSettings); diff --git a/driver-sync/src/test/functional/com/mongodb/client/ClientSideEncryptionViewAreProhibitedTest.java b/driver-sync/src/test/functional/com/mongodb/client/ClientSideEncryptionViewAreProhibitedTest.java index cd5b8491bb1..d2974393ed5 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/ClientSideEncryptionViewAreProhibitedTest.java +++ b/driver-sync/src/test/functional/com/mongodb/client/ClientSideEncryptionViewAreProhibitedTest.java @@ -65,7 +65,7 @@ public void setUp() { kmsProviders.put("local", localMasterkey); AutoEncryptionSettings.Builder autoEncryptionSettingsBuilder = AutoEncryptionSettings.builder() - .keyVaultNamespace("admin.datakeys") + .keyVaultNamespace("keyvault.datakeys") .kmsProviders(kmsProviders); AutoEncryptionSettings autoEncryptionSettings = autoEncryptionSettingsBuilder.build(); From d74655d536959b954920c7c899960187fc9d0145 Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Wed, 22 Apr 2020 15:21:55 +0100 Subject: [PATCH 17/48] Remove client-side-encryption/maxWireVersion.json failure test --- .../maxWireVersion.json | 71 ------------------- 1 file changed, 71 deletions(-) delete mode 100644 driver-core/src/test/resources/client-side-encryption/maxWireVersion.json diff --git a/driver-core/src/test/resources/client-side-encryption/maxWireVersion.json b/driver-core/src/test/resources/client-side-encryption/maxWireVersion.json deleted file mode 100644 index 144786290dc..00000000000 --- a/driver-core/src/test/resources/client-side-encryption/maxWireVersion.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "runOn": [ - { - "maxServerVersion": "4.0" - } - ], - "database_name": "default", - "collection_name": "default", - "data": [], - "key_vault_data": [ - { - "status": 1, - "_id": { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - }, - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", - "region": "us-east-1" - }, - "updateDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyMaterial": { - "$binary": { - "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEqnsxXlR51T5EbEVezUqqKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHa4jo6yp0Z18KgbUgIBEIB74sKxWtV8/YHje5lv5THTl0HIbhSwM6EqRlmBiFFatmEWaeMk4tO4xBX65eq670I5TWPSLMzpp8ncGHMmvHqRajNBnmFtbYxN3E3/WjxmdbOOe+OXpnGJPcGsftc7cB2shRfA4lICPnE26+oVNXT6p0Lo20nY5XC7jyCO", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyAltNames": [ - "altname", - "another_altname" - ] - } - ], - "tests": [ - { - "description": "operation fails with maxWireVersion < 8", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "encrypted_string": "string0" - } - }, - "result": { - "errorContains": "Auto-encryption requires a minimum MongoDB version of 4.2" - } - } - ] - } - ] -} From d14d40b0419d4ab3c03af46d12f3ab7cc1c47590 Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Wed, 22 Apr 2020 15:29:34 +0100 Subject: [PATCH 18/48] Reinstated client-side-encryption/maxWireVersion.json but indicate its waiting on SPEC-1403 --- .../maxWireVersion.json | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 driver-core/src/test/resources/client-side-encryption/maxWireVersion.json diff --git a/driver-core/src/test/resources/client-side-encryption/maxWireVersion.json b/driver-core/src/test/resources/client-side-encryption/maxWireVersion.json new file mode 100644 index 00000000000..144786290dc --- /dev/null +++ b/driver-core/src/test/resources/client-side-encryption/maxWireVersion.json @@ -0,0 +1,71 @@ +{ + "runOn": [ + { + "maxServerVersion": "4.0" + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "key_vault_data": [ + { + "status": 1, + "_id": { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + }, + "masterKey": { + "provider": "aws", + "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", + "region": "us-east-1" + }, + "updateDate": { + "$date": { + "$numberLong": "1552949630483" + } + }, + "keyMaterial": { + "$binary": { + "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEqnsxXlR51T5EbEVezUqqKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHa4jo6yp0Z18KgbUgIBEIB74sKxWtV8/YHje5lv5THTl0HIbhSwM6EqRlmBiFFatmEWaeMk4tO4xBX65eq670I5TWPSLMzpp8ncGHMmvHqRajNBnmFtbYxN3E3/WjxmdbOOe+OXpnGJPcGsftc7cB2shRfA4lICPnE26+oVNXT6p0Lo20nY5XC7jyCO", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1552949630483" + } + }, + "keyAltNames": [ + "altname", + "another_altname" + ] + } + ], + "tests": [ + { + "description": "operation fails with maxWireVersion < 8", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "encrypted_string": "string0" + } + }, + "result": { + "errorContains": "Auto-encryption requires a minimum MongoDB version of 4.2" + } + } + ] + } + ] +} From 41a1bad01c70298a4bef7b2e6caf07df38d0f8c1 Mon Sep 17 00:00:00 2001 From: Jeff Yemin Date: Wed, 22 Apr 2020 12:39:04 -0400 Subject: [PATCH 19/48] Add skipReason back to maxWireVersion.json test --- .../test/resources/client-side-encryption/maxWireVersion.json | 1 + 1 file changed, 1 insertion(+) diff --git a/driver-core/src/test/resources/client-side-encryption/maxWireVersion.json b/driver-core/src/test/resources/client-side-encryption/maxWireVersion.json index 144786290dc..39d8c6485c1 100644 --- a/driver-core/src/test/resources/client-side-encryption/maxWireVersion.json +++ b/driver-core/src/test/resources/client-side-encryption/maxWireVersion.json @@ -46,6 +46,7 @@ "tests": [ { "description": "operation fails with maxWireVersion < 8", + "skipReason": "waiting on SPEC-1403", "clientOptions": { "autoEncryptOpts": { "kmsProviders": { From c91ea1df52e3d0100a8508ec44bb5dc732619634 Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Mon, 27 Apr 2020 16:16:13 +0100 Subject: [PATCH 20/48] Ensure async bindings are released before calling callback JAVA-3662 --- .../async/client/OperationExecutorImpl.java | 29 ++++++++----------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/driver-async/src/main/com/mongodb/async/client/OperationExecutorImpl.java b/driver-async/src/main/com/mongodb/async/client/OperationExecutorImpl.java index 242cb2b3fa9..0a5e76555cc 100644 --- a/driver-async/src/main/com/mongodb/async/client/OperationExecutorImpl.java +++ b/driver-async/src/main/com/mongodb/async/client/OperationExecutorImpl.java @@ -88,13 +88,10 @@ public void onResult(final AsyncReadWriteBinding binding, final Throwable t) { operation.executeAsync(binding, new SingleResultCallback() { @Override public void onResult(final T result, final Throwable t) { - try { - labelException(t, session); - unpinServerAddressOnTransientTransactionError(session, t); - errHandlingCallback.onResult(result, t); - } finally { - binding.release(); - } + labelException(session, t); + unpinServerAddressOnTransientTransactionError(session, t); + binding.release(); + errHandlingCallback.onResult(result, t); } }); } @@ -134,13 +131,10 @@ public void onResult(final AsyncReadWriteBinding binding, final Throwable t) { operation.executeAsync(binding, new SingleResultCallback() { @Override public void onResult(final T result, final Throwable t) { - try { - labelException(t, session); - unpinServerAddressOnTransientTransactionError(session, t); - errHandlingCallback.onResult(result, t); - } finally { - binding.release(); - } + labelException(session, t); + unpinServerAddressOnTransientTransactionError(session, t); + binding.release(); + errHandlingCallback.onResult(result, t); } }); } @@ -151,7 +145,7 @@ public void onResult(final T result, final Throwable t) { }); } - private void labelException(final Throwable t, final ClientSession session) { + private void labelException(@Nullable final ClientSession session, @Nullable final Throwable t) { if (session != null && session.hasActiveTransaction() && (t instanceof MongoSocketException || t instanceof MongoTimeoutException || (t instanceof MongoQueryException && ((MongoQueryException) t).getErrorCode() == 91)) @@ -160,8 +154,9 @@ private void labelException(final Throwable t, final ClientSession session) { } } - private void unpinServerAddressOnTransientTransactionError(final @Nullable ClientSession session, final Throwable throwable) { - if (session != null && throwable != null && throwable instanceof MongoException + private void unpinServerAddressOnTransientTransactionError(@Nullable final ClientSession session, + @Nullable final Throwable throwable) { + if (session != null && throwable instanceof MongoException && ((MongoException) throwable).hasErrorLabel(TRANSIENT_TRANSACTION_ERROR_LABEL)) { session.setPinnedServerAddress(null); } From 868821a6472c354e89e0e8b55f7fbe413f967a11 Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Tue, 19 May 2020 08:44:00 +0100 Subject: [PATCH 21/48] Fix race condition causing an NPE in tlschannel JAVA-3714 --- .../tlschannel/async/AsynchronousTlsChannelGroup.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/driver-core/src/main/com/mongodb/internal/connection/tlschannel/async/AsynchronousTlsChannelGroup.java b/driver-core/src/main/com/mongodb/internal/connection/tlschannel/async/AsynchronousTlsChannelGroup.java index 70c47668e11..8bd80ad1de4 100644 --- a/driver-core/src/main/com/mongodb/internal/connection/tlschannel/async/AsynchronousTlsChannelGroup.java +++ b/driver-core/src/main/com/mongodb/internal/connection/tlschannel/async/AsynchronousTlsChannelGroup.java @@ -121,7 +121,9 @@ class RegisteredSocket { public void close() { doCancelRead(this, null); doCancelWrite(this, null); - key.cancel(); + if (key != null) { + key.cancel(); + } currentRegistrations.getAndDecrement(); /* * Actual de-registration from the selector will happen asynchronously. From 9810ce4dcc8cdcf6a262e3e3d89e57b8b29eb07b Mon Sep 17 00:00:00 2001 From: Jeff Yemin Date: Fri, 15 May 2020 18:06:49 -0400 Subject: [PATCH 22/48] Clean up TLS Socket Factory in key management service JAVA-3734 --- .../main/com/mongodb/async/client/internal/Crypt.java | 1 + .../async/client/internal/KeyManagementService.java | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/driver-async/src/main/com/mongodb/async/client/internal/Crypt.java b/driver-async/src/main/com/mongodb/async/client/internal/Crypt.java index d7c03666aa2..eadec516335 100644 --- a/driver-async/src/main/com/mongodb/async/client/internal/Crypt.java +++ b/driver-async/src/main/com/mongodb/async/client/internal/Crypt.java @@ -242,6 +242,7 @@ public void close() { commandMarker.close(); } keyRetriever.close(); + keyManagementService.close(); } private void executeStateMachine(final MongoCryptContext cryptContext, final String databaseName, diff --git a/driver-async/src/main/com/mongodb/async/client/internal/KeyManagementService.java b/driver-async/src/main/com/mongodb/async/client/internal/KeyManagementService.java index 697119c5701..21e2492ebfd 100644 --- a/driver-async/src/main/com/mongodb/async/client/internal/KeyManagementService.java +++ b/driver-async/src/main/com/mongodb/async/client/internal/KeyManagementService.java @@ -40,17 +40,24 @@ class KeyManagementService { private final int defaultPort; + private final TlsChannelStreamFactoryFactory tlsChannelStreamFactoryFactory; private final StreamFactory streamFactory; KeyManagementService(final SSLContext sslContext, final int defaultPort, final int timeoutMillis) { this.defaultPort = defaultPort; - this.streamFactory = new TlsChannelStreamFactoryFactory().create(SocketSettings.builder() + this.tlsChannelStreamFactoryFactory = new TlsChannelStreamFactoryFactory(); + this.streamFactory = tlsChannelStreamFactoryFactory.create(SocketSettings.builder() .connectTimeout(timeoutMillis, TimeUnit.MILLISECONDS) .readTimeout(timeoutMillis, TimeUnit.MILLISECONDS) .build(), SslSettings.builder().enabled(true).context(sslContext).build()); } + + public void close() { + tlsChannelStreamFactoryFactory.close(); + } + void decryptKey(final MongoKeyDecryptor keyDecryptor, final SingleResultCallback callback) { streamOpen(keyDecryptor, callback); } From f9d7705cc7b0948c0422ba664be65797424b0257 Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Wed, 22 Apr 2020 10:11:51 +0100 Subject: [PATCH 23/48] Updated retryable-reads tests to latest spec version --- .../async/client/RetryableReadsTest.java | 2 + .../client/CommandMonitoringTestHelper.java | 13 +- .../test/resources/retryable-reads/README.rst | 8 +- .../retryable-reads/aggregate-merge.json | 98 ++++ .../aggregate-serverErrors.json | 2 +- .../retryable-reads/count-serverErrors.json | 2 +- .../countDocuments-serverErrors.json | 2 +- .../distinct-serverErrors.json | 2 +- .../estimatedDocumentCount-serverErrors.json | 2 +- .../retryable-reads/find-serverErrors.json | 2 +- .../retryable-reads/findOne-serverErrors.json | 2 +- .../gridfs-download-serverErrors.json | 2 +- .../gridfs-downloadByName-serverErrors.json | 2 +- .../listCollectionNames-serverErrors.json | 2 +- .../listCollectionObjects-serverErrors.json | 2 +- .../listCollections-serverErrors.json | 2 +- .../listDatabaseNames-serverErrors.json | 4 +- .../retryable-reads/listDatabaseNames.json | 2 +- .../listDatabaseObjects-serverErrors.json | 4 +- .../retryable-reads/listDatabaseObjects.json | 2 +- .../listDatabases-serverErrors.json | 4 +- .../retryable-reads/listDatabases.json | 2 +- .../listIndexNames-serverErrors.json | 526 ++++++++++++++++++ .../retryable-reads/listIndexNames.json | 155 ++++++ .../listIndexes-serverErrors.json | 2 +- .../mongodb/client/RetryableReadsTest.java | 1 + 26 files changed, 821 insertions(+), 26 deletions(-) create mode 100644 driver-core/src/test/resources/retryable-reads/aggregate-merge.json create mode 100644 driver-core/src/test/resources/retryable-reads/listIndexNames-serverErrors.json create mode 100644 driver-core/src/test/resources/retryable-reads/listIndexNames.json diff --git a/driver-async/src/test/functional/com/mongodb/async/client/RetryableReadsTest.java b/driver-async/src/test/functional/com/mongodb/async/client/RetryableReadsTest.java index 9fb59738e3a..c87cece2611 100644 --- a/driver-async/src/test/functional/com/mongodb/async/client/RetryableReadsTest.java +++ b/driver-async/src/test/functional/com/mongodb/async/client/RetryableReadsTest.java @@ -116,6 +116,8 @@ public void setUp() { assumeFalse(skipTest); assumeTrue("Skipping test: " + definition.getString("skipReason", new BsonString("")).getValue(), !definition.containsKey("skipReason")); + assumeFalse("Skipping list index names tests", filename.startsWith("listIndexNames")); + collectionHelper = new CollectionHelper(new DocumentCodec(), new MongoNamespace(databaseName, collectionName)); collectionHelper.killAllSessions(); diff --git a/driver-core/src/test/functional/com/mongodb/client/CommandMonitoringTestHelper.java b/driver-core/src/test/functional/com/mongodb/client/CommandMonitoringTestHelper.java index bff39837081..8ffbcc29b36 100644 --- a/driver-core/src/test/functional/com/mongodb/client/CommandMonitoringTestHelper.java +++ b/driver-core/src/test/functional/com/mongodb/client/CommandMonitoringTestHelper.java @@ -80,12 +80,14 @@ public static List getExpectedEvents(final BsonArray expectedEvent // If the spec test supplies a $db field in the command, then use that database. if (commandDocument.containsKey("$db")) { actualDatabaseName = commandDocument.getString("$db").getValue(); - } - else if (commandName.equals("commitTransaction") || commandName.equals("abortTransaction")) { - actualDatabaseName = "admin"; } else if (commandName.equals("")) { commandName = commandDocument.keySet().iterator().next(); } + + if (isAdminCommand(commandName)) { + actualDatabaseName = "admin"; + } + // Not clear whether these global fields should be included, but also not clear how to efficiently exclude them if (ClusterFixture.serverVersionAtLeast(3, 6)) { commandDocument.put("$db", new BsonString(actualDatabaseName)); @@ -109,6 +111,11 @@ else if (commandName.equals("commitTransaction") || commandName.equals("abortTra return expectedEvents; } + private static final List ADMIN_COMMANDS = asList("commitTransaction", "abortTransaction", "listDatabases"); + static boolean isAdminCommand(final String commandName) { + return ADMIN_COMMANDS.contains(commandName); + } + static boolean isWriteCommand(final String commandName) { return asList("insert", "update", "delete").contains(commandName); } diff --git a/driver-core/src/test/resources/retryable-reads/README.rst b/driver-core/src/test/resources/retryable-reads/README.rst index 99636fb0413..ffb842c3031 100644 --- a/driver-core/src/test/resources/retryable-reads/README.rst +++ b/driver-core/src/test/resources/retryable-reads/README.rst @@ -84,7 +84,10 @@ Each YAML file has the following keys: - ``bucket_name``: Optional. The GridFS bucket name to use for testing. - ``data``: The data that should exist in the collection(s) under test before - each test run. + each test run. This will typically be an array of documents to be inserted + into the collection under test (i.e. ``collection_name``); however, this field + may also be an object mapping collection names to arrays of documents to be + inserted into the specified collection. - ``tests``: An array of tests that are to be run independently of each other. Each test will have some or all of the following fields: @@ -126,6 +129,9 @@ GridFS Tests ------------ GridFS tests are denoted by when the YAML file contains ``bucket_name``. +The ``data`` field will also be an object, which maps collection names +(e.g. ``fs.files``) to an array of documents that should be inserted into +the specified collection. ``fs.files`` and ``fs.chunks`` should be created in the database specified by ``database_name``. This could be done via inserts or by diff --git a/driver-core/src/test/resources/retryable-reads/aggregate-merge.json b/driver-core/src/test/resources/retryable-reads/aggregate-merge.json new file mode 100644 index 00000000000..b401d741ba5 --- /dev/null +++ b/driver-core/src/test/resources/retryable-reads/aggregate-merge.json @@ -0,0 +1,98 @@ +{ + "runOn": [ + { + "minServerVersion": "4.1.11" + } + ], + "database_name": "retryable-reads-tests", + "collection_name": "coll", + "data": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ], + "tests": [ + { + "description": "Aggregate with $merge does not retry", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "closeConnection": true + } + }, + "operations": [ + { + "object": "collection", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + }, + { + "$merge": { + "into": "output-collection" + } + } + ] + }, + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + }, + { + "$merge": { + "into": "output-collection" + } + } + ] + }, + "command_name": "aggregate", + "database_name": "retryable-reads-tests" + } + } + ] + } + ] +} diff --git a/driver-core/src/test/resources/retryable-reads/aggregate-serverErrors.json b/driver-core/src/test/resources/retryable-reads/aggregate-serverErrors.json index 211c318613c..04208bc95b8 100644 --- a/driver-core/src/test/resources/retryable-reads/aggregate-serverErrors.json +++ b/driver-core/src/test/resources/retryable-reads/aggregate-serverErrors.json @@ -125,7 +125,7 @@ ] }, { - "description": "Aggregate succeeds after InterruptedDueToStepDown", + "description": "Aggregate succeeds after InterruptedDueToReplStateChange", "failPoint": { "configureFailPoint": "failCommand", "mode": { diff --git a/driver-core/src/test/resources/retryable-reads/count-serverErrors.json b/driver-core/src/test/resources/retryable-reads/count-serverErrors.json index b13bb893dc5..839680fe59f 100644 --- a/driver-core/src/test/resources/retryable-reads/count-serverErrors.json +++ b/driver-core/src/test/resources/retryable-reads/count-serverErrors.json @@ -71,7 +71,7 @@ ] }, { - "description": "Count succeeds after InterruptedDueToStepDown", + "description": "Count succeeds after InterruptedDueToReplStateChange", "failPoint": { "configureFailPoint": "failCommand", "mode": { diff --git a/driver-core/src/test/resources/retryable-reads/countDocuments-serverErrors.json b/driver-core/src/test/resources/retryable-reads/countDocuments-serverErrors.json index 641fdc908a5..f45eadfa0c2 100644 --- a/driver-core/src/test/resources/retryable-reads/countDocuments-serverErrors.json +++ b/driver-core/src/test/resources/retryable-reads/countDocuments-serverErrors.json @@ -97,7 +97,7 @@ ] }, { - "description": "CountDocuments succeeds after InterruptedDueToStepDown", + "description": "CountDocuments succeeds after InterruptedDueToReplStateChange", "failPoint": { "configureFailPoint": "failCommand", "mode": { diff --git a/driver-core/src/test/resources/retryable-reads/distinct-serverErrors.json b/driver-core/src/test/resources/retryable-reads/distinct-serverErrors.json index 533a38c942e..50fd6a55051 100644 --- a/driver-core/src/test/resources/retryable-reads/distinct-serverErrors.json +++ b/driver-core/src/test/resources/retryable-reads/distinct-serverErrors.json @@ -95,7 +95,7 @@ ] }, { - "description": "Distinct succeeds after InterruptedDueToStepDown", + "description": "Distinct succeeds after InterruptedDueToReplStateChange", "failPoint": { "configureFailPoint": "failCommand", "mode": { diff --git a/driver-core/src/test/resources/retryable-reads/estimatedDocumentCount-serverErrors.json b/driver-core/src/test/resources/retryable-reads/estimatedDocumentCount-serverErrors.json index fd94d2183d9..1af21d1fe92 100644 --- a/driver-core/src/test/resources/retryable-reads/estimatedDocumentCount-serverErrors.json +++ b/driver-core/src/test/resources/retryable-reads/estimatedDocumentCount-serverErrors.json @@ -68,7 +68,7 @@ ] }, { - "description": "EstimatedDocumentCount succeeds after InterruptedDueToStepDown", + "description": "EstimatedDocumentCount succeeds after InterruptedDueToReplStateChange", "failPoint": { "configureFailPoint": "failCommand", "mode": { diff --git a/driver-core/src/test/resources/retryable-reads/find-serverErrors.json b/driver-core/src/test/resources/retryable-reads/find-serverErrors.json index e7d49f6eaac..44ecf34d2fb 100644 --- a/driver-core/src/test/resources/retryable-reads/find-serverErrors.json +++ b/driver-core/src/test/resources/retryable-reads/find-serverErrors.json @@ -114,7 +114,7 @@ ] }, { - "description": "Find succeeds after InterruptedDueToStepDown", + "description": "Find succeeds after InterruptedDueToReplStateChange", "failPoint": { "configureFailPoint": "failCommand", "mode": { diff --git a/driver-core/src/test/resources/retryable-reads/findOne-serverErrors.json b/driver-core/src/test/resources/retryable-reads/findOne-serverErrors.json index 7c235ef32c8..b8229483d24 100644 --- a/driver-core/src/test/resources/retryable-reads/findOne-serverErrors.json +++ b/driver-core/src/test/resources/retryable-reads/findOne-serverErrors.json @@ -94,7 +94,7 @@ ] }, { - "description": "FindOne succeeds after InterruptedDueToStepDown", + "description": "FindOne succeeds after InterruptedDueToReplStateChange", "failPoint": { "configureFailPoint": "failCommand", "mode": { diff --git a/driver-core/src/test/resources/retryable-reads/gridfs-download-serverErrors.json b/driver-core/src/test/resources/retryable-reads/gridfs-download-serverErrors.json index ae1c3c6919a..84e50e370c4 100644 --- a/driver-core/src/test/resources/retryable-reads/gridfs-download-serverErrors.json +++ b/driver-core/src/test/resources/retryable-reads/gridfs-download-serverErrors.json @@ -121,7 +121,7 @@ ] }, { - "description": "Download succeeds after InterruptedDueToStepDown", + "description": "Download succeeds after InterruptedDueToReplStateChange", "failPoint": { "configureFailPoint": "failCommand", "mode": { diff --git a/driver-core/src/test/resources/retryable-reads/gridfs-downloadByName-serverErrors.json b/driver-core/src/test/resources/retryable-reads/gridfs-downloadByName-serverErrors.json index 4e2441e7471..de439ce4b20 100644 --- a/driver-core/src/test/resources/retryable-reads/gridfs-downloadByName-serverErrors.json +++ b/driver-core/src/test/resources/retryable-reads/gridfs-downloadByName-serverErrors.json @@ -115,7 +115,7 @@ ] }, { - "description": "DownloadByName succeeds after InterruptedDueToStepDown", + "description": "DownloadByName succeeds after InterruptedDueToReplStateChange", "failPoint": { "configureFailPoint": "failCommand", "mode": { diff --git a/driver-core/src/test/resources/retryable-reads/listCollectionNames-serverErrors.json b/driver-core/src/test/resources/retryable-reads/listCollectionNames-serverErrors.json index 48a5db5ef50..27c13d6301c 100644 --- a/driver-core/src/test/resources/retryable-reads/listCollectionNames-serverErrors.json +++ b/driver-core/src/test/resources/retryable-reads/listCollectionNames-serverErrors.json @@ -56,7 +56,7 @@ ] }, { - "description": "ListCollectionNames succeeds after InterruptedDueToStepDown", + "description": "ListCollectionNames succeeds after InterruptedDueToReplStateChange", "failPoint": { "configureFailPoint": "failCommand", "mode": { diff --git a/driver-core/src/test/resources/retryable-reads/listCollectionObjects-serverErrors.json b/driver-core/src/test/resources/retryable-reads/listCollectionObjects-serverErrors.json index 8ce6a27c501..3922713df9c 100644 --- a/driver-core/src/test/resources/retryable-reads/listCollectionObjects-serverErrors.json +++ b/driver-core/src/test/resources/retryable-reads/listCollectionObjects-serverErrors.json @@ -56,7 +56,7 @@ ] }, { - "description": "ListCollectionObjects succeeds after InterruptedDueToStepDown", + "description": "ListCollectionObjects succeeds after InterruptedDueToReplStateChange", "failPoint": { "configureFailPoint": "failCommand", "mode": { diff --git a/driver-core/src/test/resources/retryable-reads/listCollections-serverErrors.json b/driver-core/src/test/resources/retryable-reads/listCollections-serverErrors.json index a2abf4acff3..6972073b181 100644 --- a/driver-core/src/test/resources/retryable-reads/listCollections-serverErrors.json +++ b/driver-core/src/test/resources/retryable-reads/listCollections-serverErrors.json @@ -56,7 +56,7 @@ ] }, { - "description": "ListCollections succeeds after InterruptedDueToStepDown", + "description": "ListCollections succeeds after InterruptedDueToReplStateChange", "failPoint": { "configureFailPoint": "failCommand", "mode": { diff --git a/driver-core/src/test/resources/retryable-reads/listDatabaseNames-serverErrors.json b/driver-core/src/test/resources/retryable-reads/listDatabaseNames-serverErrors.json index e7b5311a54a..11faf58bf04 100644 --- a/driver-core/src/test/resources/retryable-reads/listDatabaseNames-serverErrors.json +++ b/driver-core/src/test/resources/retryable-reads/listDatabaseNames-serverErrors.json @@ -14,7 +14,7 @@ ] } ], - "database_name": "admin", + "database_name": "retryable-reads-tests", "collection_name": "coll", "data": [], "tests": [ @@ -56,7 +56,7 @@ ] }, { - "description": "ListDatabaseNames succeeds after InterruptedDueToStepDown", + "description": "ListDatabaseNames succeeds after InterruptedDueToReplStateChange", "failPoint": { "configureFailPoint": "failCommand", "mode": { diff --git a/driver-core/src/test/resources/retryable-reads/listDatabaseNames.json b/driver-core/src/test/resources/retryable-reads/listDatabaseNames.json index d4f24ec7f9a..b35f7ab185f 100644 --- a/driver-core/src/test/resources/retryable-reads/listDatabaseNames.json +++ b/driver-core/src/test/resources/retryable-reads/listDatabaseNames.json @@ -14,7 +14,7 @@ ] } ], - "database_name": "admin", + "database_name": "retryable-reads-tests", "collection_name": "coll", "data": [], "tests": [ diff --git a/driver-core/src/test/resources/retryable-reads/listDatabaseObjects-serverErrors.json b/driver-core/src/test/resources/retryable-reads/listDatabaseObjects-serverErrors.json index 085c9a899f2..38082f2e28b 100644 --- a/driver-core/src/test/resources/retryable-reads/listDatabaseObjects-serverErrors.json +++ b/driver-core/src/test/resources/retryable-reads/listDatabaseObjects-serverErrors.json @@ -14,7 +14,7 @@ ] } ], - "database_name": "admin", + "database_name": "retryable-reads-tests", "collection_name": "coll", "data": [], "tests": [ @@ -56,7 +56,7 @@ ] }, { - "description": "ListDatabaseObjects succeeds after InterruptedDueToStepDown", + "description": "ListDatabaseObjects succeeds after InterruptedDueToReplStateChange", "failPoint": { "configureFailPoint": "failCommand", "mode": { diff --git a/driver-core/src/test/resources/retryable-reads/listDatabaseObjects.json b/driver-core/src/test/resources/retryable-reads/listDatabaseObjects.json index cb6029dcb76..cbd2c6763a3 100644 --- a/driver-core/src/test/resources/retryable-reads/listDatabaseObjects.json +++ b/driver-core/src/test/resources/retryable-reads/listDatabaseObjects.json @@ -14,7 +14,7 @@ ] } ], - "database_name": "admin", + "database_name": "retryable-reads-tests", "collection_name": "coll", "data": [], "tests": [ diff --git a/driver-core/src/test/resources/retryable-reads/listDatabases-serverErrors.json b/driver-core/src/test/resources/retryable-reads/listDatabases-serverErrors.json index fd10af962d8..4047f749ffc 100644 --- a/driver-core/src/test/resources/retryable-reads/listDatabases-serverErrors.json +++ b/driver-core/src/test/resources/retryable-reads/listDatabases-serverErrors.json @@ -14,7 +14,7 @@ ] } ], - "database_name": "admin", + "database_name": "retryable-reads-tests", "collection_name": "coll", "data": [], "tests": [ @@ -56,7 +56,7 @@ ] }, { - "description": "ListDatabases succeeds after InterruptedDueToStepDown", + "description": "ListDatabases succeeds after InterruptedDueToReplStateChange", "failPoint": { "configureFailPoint": "failCommand", "mode": { diff --git a/driver-core/src/test/resources/retryable-reads/listDatabases.json b/driver-core/src/test/resources/retryable-reads/listDatabases.json index 8dff53ce8e5..3cb8bbd083c 100644 --- a/driver-core/src/test/resources/retryable-reads/listDatabases.json +++ b/driver-core/src/test/resources/retryable-reads/listDatabases.json @@ -14,7 +14,7 @@ ] } ], - "database_name": "admin", + "database_name": "retryable-reads-tests", "collection_name": "coll", "data": [], "tests": [ diff --git a/driver-core/src/test/resources/retryable-reads/listIndexNames-serverErrors.json b/driver-core/src/test/resources/retryable-reads/listIndexNames-serverErrors.json new file mode 100644 index 00000000000..1a9ba83bc69 --- /dev/null +++ b/driver-core/src/test/resources/retryable-reads/listIndexNames-serverErrors.json @@ -0,0 +1,526 @@ +{ + "runOn": [ + { + "minServerVersion": "4.0", + "topology": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topology": [ + "sharded" + ] + } + ], + "database_name": "retryable-reads-tests", + "collection_name": "coll", + "data": [], + "tests": [ + { + "description": "ListIndexNames succeeds after InterruptedAtShutdown", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 11600 + } + }, + "operations": [ + { + "name": "listIndexNames", + "object": "collection" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "ListIndexNames succeeds after InterruptedDueToReplStateChange", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 11602 + } + }, + "operations": [ + { + "name": "listIndexNames", + "object": "collection" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "ListIndexNames succeeds after NotMaster", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "listIndexNames", + "object": "collection" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "ListIndexNames succeeds after NotMasterNoSlaveOk", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 13435 + } + }, + "operations": [ + { + "name": "listIndexNames", + "object": "collection" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "ListIndexNames succeeds after NotMasterOrSecondary", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 13436 + } + }, + "operations": [ + { + "name": "listIndexNames", + "object": "collection" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "ListIndexNames succeeds after PrimarySteppedDown", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 189 + } + }, + "operations": [ + { + "name": "listIndexNames", + "object": "collection" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "ListIndexNames succeeds after ShutdownInProgress", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 91 + } + }, + "operations": [ + { + "name": "listIndexNames", + "object": "collection" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "ListIndexNames succeeds after HostNotFound", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 7 + } + }, + "operations": [ + { + "name": "listIndexNames", + "object": "collection" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "ListIndexNames succeeds after HostUnreachable", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 6 + } + }, + "operations": [ + { + "name": "listIndexNames", + "object": "collection" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "ListIndexNames succeeds after NetworkTimeout", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 89 + } + }, + "operations": [ + { + "name": "listIndexNames", + "object": "collection" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "ListIndexNames succeeds after SocketException", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 9001 + } + }, + "operations": [ + { + "name": "listIndexNames", + "object": "collection" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "ListIndexNames fails after two NotMaster errors", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "listIndexNames", + "object": "collection", + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "ListIndexNames fails after NotMaster when retryReads is false", + "clientOptions": { + "retryReads": false + }, + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "listIndexNames", + "object": "collection", + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + } + ] +} diff --git a/driver-core/src/test/resources/retryable-reads/listIndexNames.json b/driver-core/src/test/resources/retryable-reads/listIndexNames.json new file mode 100644 index 00000000000..912c706015f --- /dev/null +++ b/driver-core/src/test/resources/retryable-reads/listIndexNames.json @@ -0,0 +1,155 @@ +{ + "runOn": [ + { + "minServerVersion": "4.0", + "topology": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topology": [ + "sharded" + ] + } + ], + "database_name": "retryable-reads-tests", + "collection_name": "coll", + "data": [], + "tests": [ + { + "description": "ListIndexNames succeeds on first attempt", + "operations": [ + { + "name": "listIndexNames", + "object": "collection" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "ListIndexNames succeeds on second attempt", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "listIndexNames", + "object": "collection" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "ListIndexNames fails on first attempt", + "clientOptions": { + "retryReads": false + }, + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "listIndexNames", + "object": "collection", + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "ListIndexNames fails on second attempt", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "listIndexNames", + "object": "collection", + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + } + ] +} diff --git a/driver-core/src/test/resources/retryable-reads/listIndexes-serverErrors.json b/driver-core/src/test/resources/retryable-reads/listIndexes-serverErrors.json index b1043310f87..16b61d535d6 100644 --- a/driver-core/src/test/resources/retryable-reads/listIndexes-serverErrors.json +++ b/driver-core/src/test/resources/retryable-reads/listIndexes-serverErrors.json @@ -58,7 +58,7 @@ ] }, { - "description": "ListIndexes succeeds after InterruptedDueToStepDown", + "description": "ListIndexes succeeds after InterruptedDueToReplStateChange", "failPoint": { "configureFailPoint": "failCommand", "mode": { diff --git a/driver-sync/src/test/functional/com/mongodb/client/RetryableReadsTest.java b/driver-sync/src/test/functional/com/mongodb/client/RetryableReadsTest.java index 53510e86917..9a560899842 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/RetryableReadsTest.java +++ b/driver-sync/src/test/functional/com/mongodb/client/RetryableReadsTest.java @@ -113,6 +113,7 @@ public void setUp() { assumeFalse(skipTest); assumeTrue("Skipping test: " + definition.getString("skipReason", new BsonString("")).getValue(), !definition.containsKey("skipReason")); + assumeFalse("Skipping list index names tests", filename.startsWith("listIndexNames")); collectionHelper = new CollectionHelper(new DocumentCodec(), new MongoNamespace(databaseName, collectionName)); final BsonDocument clientOptions = definition.getDocument("clientOptions", new BsonDocument()); From 095965c749b65493e91d14713e96eb9bcc896f8e Mon Sep 17 00:00:00 2001 From: Jeff Yemin Date: Fri, 22 May 2020 09:20:32 -0400 Subject: [PATCH 24/48] Update change stream retryable reads tests --- .../com/mongodb/client/CommandMonitoringTestHelper.java | 3 ++- .../changeStreams-client.watch-serverErrors.json | 2 +- .../resources/retryable-reads/changeStreams-client.watch.json | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/driver-core/src/test/functional/com/mongodb/client/CommandMonitoringTestHelper.java b/driver-core/src/test/functional/com/mongodb/client/CommandMonitoringTestHelper.java index 8ffbcc29b36..683380d5100 100644 --- a/driver-core/src/test/functional/com/mongodb/client/CommandMonitoringTestHelper.java +++ b/driver-core/src/test/functional/com/mongodb/client/CommandMonitoringTestHelper.java @@ -76,7 +76,8 @@ public static List getExpectedEvents(final BsonArray expectedEvent String commandName = eventDescriptionDocument.getString("command_name", new BsonString("")).getValue(); if (eventType.equals("command_started_event")) { BsonDocument commandDocument = eventDescriptionDocument.getDocument("command"); - String actualDatabaseName = databaseName; + String actualDatabaseName = eventDescriptionDocument.containsKey("database_name") + ? eventDescriptionDocument.getString("database_name").getValue() : databaseName; // If the spec test supplies a $db field in the command, then use that database. if (commandDocument.containsKey("$db")) { actualDatabaseName = commandDocument.getString("$db").getValue(); diff --git a/driver-core/src/test/resources/retryable-reads/changeStreams-client.watch-serverErrors.json b/driver-core/src/test/resources/retryable-reads/changeStreams-client.watch-serverErrors.json index 2ac543169e0..f70ba9dc1a9 100644 --- a/driver-core/src/test/resources/retryable-reads/changeStreams-client.watch-serverErrors.json +++ b/driver-core/src/test/resources/retryable-reads/changeStreams-client.watch-serverErrors.json @@ -13,7 +13,7 @@ ] } ], - "database_name": "admin", + "database_name": "retryable-reads-tests", "collection_name": "coll", "data": [ { diff --git a/driver-core/src/test/resources/retryable-reads/changeStreams-client.watch.json b/driver-core/src/test/resources/retryable-reads/changeStreams-client.watch.json index 18ff019e1b6..805835e8d7d 100644 --- a/driver-core/src/test/resources/retryable-reads/changeStreams-client.watch.json +++ b/driver-core/src/test/resources/retryable-reads/changeStreams-client.watch.json @@ -13,7 +13,7 @@ ] } ], - "database_name": "admin", + "database_name": "retryable-reads-tests", "collection_name": "coll", "data": [ { @@ -204,7 +204,7 @@ } ] }, - "database_name": "admin" + "database_name": "admin" } } ] From ecbce07535e01763e42bc3178e0b4b85218abb4e Mon Sep 17 00:00:00 2001 From: Jeff Yemin Date: Fri, 22 May 2020 14:43:05 -0400 Subject: [PATCH 25/48] Use database specified in spec test files rather than default database --- .../com/mongodb/async/client/AbstractUnifiedTest.java | 9 +++++---- .../mongodb/async/client/MainTransactionsTest.java | 10 ++++++++-- .../com/mongodb/async/client/SessionsTest.java | 11 ++++++++--- .../com/mongodb/client/AbstractUnifiedTest.java | 10 +++++----- .../com/mongodb/client/MainTransactionsTest.java | 10 ++++++++-- .../functional/com/mongodb/client/SessionsTest.java | 11 ++++++++--- .../client/WithTransactionHelperTransactionsTest.java | 10 ++++++++-- 7 files changed, 50 insertions(+), 21 deletions(-) diff --git a/driver-async/src/test/functional/com/mongodb/async/client/AbstractUnifiedTest.java b/driver-async/src/test/functional/com/mongodb/async/client/AbstractUnifiedTest.java index 283681de6e4..f0ff73146fb 100644 --- a/driver-async/src/test/functional/com/mongodb/async/client/AbstractUnifiedTest.java +++ b/driver-async/src/test/functional/com/mongodb/async/client/AbstractUnifiedTest.java @@ -62,7 +62,6 @@ import static com.mongodb.ClusterFixture.getMultiMongosConnectionString; import static com.mongodb.async.client.Fixture.getConnectionString; -import static com.mongodb.async.client.Fixture.getDefaultDatabaseName; import static com.mongodb.async.client.Fixture.isSharded; import static com.mongodb.client.CommandMonitoringTestHelper.assertEventsEquality; import static com.mongodb.client.CommandMonitoringTestHelper.getExpectedEvents; @@ -96,15 +95,17 @@ public abstract class AbstractUnifiedTest { private HashMap lsidMap; private boolean useMultipleMongoses = false; private ConnectionString connectionString; - private final String collectionName = "test"; + private final String collectionName; private static final long MIN_HEARTBEAT_FREQUENCY_MS = 50L; - public AbstractUnifiedTest(final String filename, final String description, final BsonArray data, final BsonDocument definition, + public AbstractUnifiedTest(final String filename, final String description, final String databaseName, + final String collectionName, final BsonArray data, final BsonDocument definition, final boolean skipTest) { this.filename = filename; this.description = description; - this.databaseName = getDefaultDatabaseName(); + this.databaseName = databaseName; + this.collectionName = collectionName; this.data = data; this.definition = definition; this.commandListener = new TestCommandListener(); diff --git a/driver-async/src/test/functional/com/mongodb/async/client/MainTransactionsTest.java b/driver-async/src/test/functional/com/mongodb/async/client/MainTransactionsTest.java index 838c1f5dcd3..879199c655a 100644 --- a/driver-async/src/test/functional/com/mongodb/async/client/MainTransactionsTest.java +++ b/driver-async/src/test/functional/com/mongodb/async/client/MainTransactionsTest.java @@ -18,6 +18,7 @@ import org.bson.BsonArray; import org.bson.BsonDocument; +import org.bson.BsonString; import org.bson.BsonValue; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -31,13 +32,15 @@ import java.util.List; import static com.mongodb.JsonTestServerVersionChecker.skipTest; +import static com.mongodb.async.client.Fixture.getDefaultDatabaseName; // See https://github.com/mongodb/specifications/tree/master/source/transactions/tests @RunWith(Parameterized.class) public class MainTransactionsTest extends AbstractUnifiedTest { - public MainTransactionsTest(final String filename, final String description, final BsonArray data, final BsonDocument definition, + public MainTransactionsTest(final String filename, final String description, final String databaseName, + final String collectionName, final BsonArray data, final BsonDocument definition, final boolean skipTest) { - super(filename, description, data, definition, skipTest); + super(filename, description, databaseName, collectionName, data, definition, skipTest); } @Parameterized.Parameters(name = "{0}: {1}") @@ -47,6 +50,9 @@ public static Collection data() throws URISyntaxException, IOException BsonDocument testDocument = JsonPoweredTestHelper.getTestDocument(file); for (BsonValue test : testDocument.getArray("tests")) { data.add(new Object[]{file.getName(), test.asDocument().getString("description").getValue(), + testDocument.getString("database_name", new BsonString(getDefaultDatabaseName())).getValue(), + testDocument.getString("collection_name", + new BsonString(file.getName().substring(0, file.getName().lastIndexOf(".")))).getValue(), testDocument.getArray("data"), test.asDocument(), skipTest(testDocument, test.asDocument())}); } } diff --git a/driver-async/src/test/functional/com/mongodb/async/client/SessionsTest.java b/driver-async/src/test/functional/com/mongodb/async/client/SessionsTest.java index 2518b4a7820..8bec7edc332 100644 --- a/driver-async/src/test/functional/com/mongodb/async/client/SessionsTest.java +++ b/driver-async/src/test/functional/com/mongodb/async/client/SessionsTest.java @@ -18,6 +18,7 @@ import org.bson.BsonArray; import org.bson.BsonDocument; +import org.bson.BsonString; import org.bson.BsonValue; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -31,13 +32,14 @@ import java.util.List; import static com.mongodb.JsonTestServerVersionChecker.skipTest; +import static com.mongodb.async.client.Fixture.getDefaultDatabaseName; // See https://github.com/mongodb/specifications/tree/master/source/sessions/tests @RunWith(Parameterized.class) public class SessionsTest extends AbstractUnifiedTest { - public SessionsTest(final String filename, final String description, final BsonArray data, final BsonDocument definition, - final boolean skipTest) { - super(filename, description, data, definition, skipTest); + public SessionsTest(final String filename, final String description, final String databaseName, final String collectionName, + final BsonArray data, final BsonDocument definition, final boolean skipTest) { + super(filename, description, databaseName, collectionName, data, definition, skipTest); } @Parameterized.Parameters(name = "{0}: {1}") @@ -47,6 +49,9 @@ public static Collection data() throws URISyntaxException, IOException BsonDocument testDocument = JsonPoweredTestHelper.getTestDocument(file); for (BsonValue test : testDocument.getArray("tests")) { data.add(new Object[]{file.getName(), test.asDocument().getString("description").getValue(), + testDocument.getString("database_name", new BsonString(getDefaultDatabaseName())).getValue(), + testDocument.getString("collection_name", + new BsonString(file.getName().substring(0, file.getName().lastIndexOf(".")))).getValue(), testDocument.getArray("data"), test.asDocument(), skipTest(testDocument, test.asDocument())}); } } diff --git a/driver-sync/src/test/functional/com/mongodb/client/AbstractUnifiedTest.java b/driver-sync/src/test/functional/com/mongodb/client/AbstractUnifiedTest.java index e7c9fad6806..eb2dbe90c96 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/AbstractUnifiedTest.java +++ b/driver-sync/src/test/functional/com/mongodb/client/AbstractUnifiedTest.java @@ -64,7 +64,6 @@ import static com.mongodb.ClusterFixture.isSharded; import static com.mongodb.client.CommandMonitoringTestHelper.assertEventsEquality; import static com.mongodb.client.CommandMonitoringTestHelper.getExpectedEvents; -import static com.mongodb.client.Fixture.getDefaultDatabaseName; import static com.mongodb.client.Fixture.getMongoClientSettingsBuilder; import static java.util.Collections.singletonList; import static java.util.concurrent.TimeUnit.MILLISECONDS; @@ -94,15 +93,16 @@ public abstract class AbstractUnifiedTest { private Map lsidMap; private boolean useMultipleMongoses = false; private ConnectionString connectionString = null; - private final String collectionName = "test"; + private final String collectionName; private static final long MIN_HEARTBEAT_FREQUENCY_MS = 50L; - public AbstractUnifiedTest(final String filename, final String description, final BsonArray data, final BsonDocument definition, - final boolean skipTest) { + public AbstractUnifiedTest(final String filename, final String description, final String databaseName, final String collectionName, + final BsonArray data, final BsonDocument definition, final boolean skipTest) { this.filename = filename; this.description = description; - this.databaseName = getDefaultDatabaseName(); + this.databaseName = databaseName; + this.collectionName = collectionName; this.data = data; this.definition = definition; this.commandListener = new TestCommandListener(); diff --git a/driver-sync/src/test/functional/com/mongodb/client/MainTransactionsTest.java b/driver-sync/src/test/functional/com/mongodb/client/MainTransactionsTest.java index a4e65f983ca..d2da8d911a4 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/MainTransactionsTest.java +++ b/driver-sync/src/test/functional/com/mongodb/client/MainTransactionsTest.java @@ -18,6 +18,7 @@ import org.bson.BsonArray; import org.bson.BsonDocument; +import org.bson.BsonString; import org.bson.BsonValue; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -31,13 +32,15 @@ import java.util.List; import static com.mongodb.JsonTestServerVersionChecker.skipTest; +import static com.mongodb.client.Fixture.getDefaultDatabaseName; // See https://github.com/mongodb/specifications/tree/master/source/transactions/tests @RunWith(Parameterized.class) public class MainTransactionsTest extends AbstractUnifiedTest { - public MainTransactionsTest(final String filename, final String description, final BsonArray data, final BsonDocument definition, + public MainTransactionsTest(final String filename, final String description, final String databaseName, + final String collectionName, final BsonArray data, final BsonDocument definition, final boolean skipTest) { - super(filename, description, data, definition, skipTest); + super(filename, description, databaseName, collectionName, data, definition, skipTest); } @Parameterized.Parameters(name = "{0}: {1}") @@ -48,6 +51,9 @@ public static Collection data() throws URISyntaxException, IOException for (BsonValue test : testDocument.getArray("tests")) { data.add(new Object[]{file.getName(), test.asDocument().getString("description").getValue(), + testDocument.getString("database_name", new BsonString(getDefaultDatabaseName())).getValue(), + testDocument.getString("collection_name", + new BsonString(file.getName().substring(0, file.getName().lastIndexOf(".")))).getValue(), testDocument.getArray("data"), test.asDocument(), skipTest(testDocument, test.asDocument())}); } } diff --git a/driver-sync/src/test/functional/com/mongodb/client/SessionsTest.java b/driver-sync/src/test/functional/com/mongodb/client/SessionsTest.java index ef67f1c1e4b..f8e557559fc 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/SessionsTest.java +++ b/driver-sync/src/test/functional/com/mongodb/client/SessionsTest.java @@ -18,6 +18,7 @@ import org.bson.BsonArray; import org.bson.BsonDocument; +import org.bson.BsonString; import org.bson.BsonValue; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -31,13 +32,14 @@ import java.util.List; import static com.mongodb.JsonTestServerVersionChecker.skipTest; +import static com.mongodb.client.Fixture.getDefaultDatabaseName; // See https://github.com/mongodb/specifications/tree/master/source/sessions/tests @RunWith(Parameterized.class) public class SessionsTest extends AbstractUnifiedTest { - public SessionsTest(final String filename, final String description, final BsonArray data, final BsonDocument definition, - final boolean skipTest) { - super(filename, description, data, definition, skipTest); + public SessionsTest(final String filename, final String description, final String databaseName, final String collectionName, + final BsonArray data, final BsonDocument definition, final boolean skipTest) { + super(filename, description, databaseName, collectionName, data, definition, skipTest); } @Parameterized.Parameters(name = "{0}: {1}") @@ -48,6 +50,9 @@ public static Collection data() throws URISyntaxException, IOException for (BsonValue test : testDocument.getArray("tests")) { data.add(new Object[]{file.getName(), test.asDocument().getString("description").getValue(), + testDocument.getString("database_name", new BsonString(getDefaultDatabaseName())).getValue(), + testDocument.getString("collection_name", + new BsonString(file.getName().substring(0, file.getName().lastIndexOf(".")))).getValue(), testDocument.getArray("data"), test.asDocument(), skipTest(testDocument, test.asDocument())}); } } diff --git a/driver-sync/src/test/functional/com/mongodb/client/WithTransactionHelperTransactionsTest.java b/driver-sync/src/test/functional/com/mongodb/client/WithTransactionHelperTransactionsTest.java index e5eff45a9a5..224c7f00b9a 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/WithTransactionHelperTransactionsTest.java +++ b/driver-sync/src/test/functional/com/mongodb/client/WithTransactionHelperTransactionsTest.java @@ -18,6 +18,7 @@ import org.bson.BsonArray; import org.bson.BsonDocument; +import org.bson.BsonString; import org.bson.BsonValue; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -31,13 +32,15 @@ import java.util.List; import static com.mongodb.JsonTestServerVersionChecker.skipTest; +import static com.mongodb.client.Fixture.getDefaultDatabaseName; // See https://github.com/mongodb/specifications/tree/master/source/transactions-convenient-api/tests @RunWith(Parameterized.class) public class WithTransactionHelperTransactionsTest extends AbstractUnifiedTest { - public WithTransactionHelperTransactionsTest(final String filename, final String description, final BsonArray data, + public WithTransactionHelperTransactionsTest(final String filename, final String description, final String databaseName, + final String collectionName, final BsonArray data, final BsonDocument definition, final boolean skipTest) { - super(filename, description, data, definition, skipTest); + super(filename, description, databaseName, collectionName, data, definition, skipTest); } @Parameterized.Parameters(name = "{0}: {1}") @@ -47,6 +50,9 @@ public static Collection data() throws URISyntaxException, IOException BsonDocument testDocument = JsonPoweredTestHelper.getTestDocument(file); for (BsonValue test : testDocument.getArray("tests")) { data.add(new Object[]{file.getName(), test.asDocument().getString("description").getValue(), + testDocument.getString("database_name", new BsonString(getDefaultDatabaseName())).getValue(), + testDocument.getString("collection_name", + new BsonString(file.getName().substring(0, file.getName().lastIndexOf(".")))).getValue(), testDocument.getArray("data"), test.asDocument(), skipTest(testDocument, test.asDocument())}); } } From 2c7dc576e92d398f924acc1d8c1446d41af5761e Mon Sep 17 00:00:00 2001 From: Jeff Yemin Date: Sun, 24 May 2020 12:50:00 -0400 Subject: [PATCH 26/48] Make ChangeStreamOperationSpecification more resilient Remove assumption that expected change stream documents are returned in a single batch. JAVA-3742 --- .../OperationFunctionalSpecification.groovy | 10 ++++ .../ChangeStreamOperationSpecification.groovy | 53 +++++++++---------- 2 files changed, 35 insertions(+), 28 deletions(-) diff --git a/driver-core/src/test/functional/com/mongodb/OperationFunctionalSpecification.groovy b/driver-core/src/test/functional/com/mongodb/OperationFunctionalSpecification.groovy index db9b6d2067d..3defca55427 100644 --- a/driver-core/src/test/functional/com/mongodb/OperationFunctionalSpecification.groovy +++ b/driver-core/src/test/functional/com/mongodb/OperationFunctionalSpecification.groovy @@ -156,6 +156,16 @@ class OperationFunctionalSpecification extends Specification { results } + def next(cursor, boolean async, int minimumCount) { + List retVal = [] + + while (retVal.size() < minimumCount) { + retVal.addAll(next(cursor, async)) + } + + retVal + } + def next(cursor, boolean async) { if (async) { def futureResultCallback = new FutureResultCallback>() diff --git a/driver-core/src/test/functional/com/mongodb/operation/ChangeStreamOperationSpecification.groovy b/driver-core/src/test/functional/com/mongodb/operation/ChangeStreamOperationSpecification.groovy index bc6c9613792..7128a93a901 100644 --- a/driver-core/src/test/functional/com/mongodb/operation/ChangeStreamOperationSpecification.groovy +++ b/driver-core/src/test/functional/com/mongodb/operation/ChangeStreamOperationSpecification.groovy @@ -16,7 +16,6 @@ package com.mongodb.operation - import com.mongodb.MongoException import com.mongodb.MongoNamespace import com.mongodb.OperationFunctionalSpecification @@ -155,7 +154,7 @@ class ChangeStreamOperationSpecification extends OperationFunctionalSpecificatio def expected = insertDocuments(helper, [1, 2]) then: - def next = nextAndClean(cursor, async) + def next = nextAndClean(cursor, async, expected.size()) next == expected when: @@ -164,7 +163,7 @@ class ChangeStreamOperationSpecification extends OperationFunctionalSpecificatio then: cursor.getBatchSize() == 5 - nextAndClean(cursor, async) == expected + nextAndClean(cursor, async, expected.size()) == expected then: if (async) { @@ -193,7 +192,7 @@ class ChangeStreamOperationSpecification extends OperationFunctionalSpecificatio when: def cursor = execute(operation, false) helper.insertDocuments(BsonDocument.parse('{ _id : 2, x : 2 }')) - ChangeStreamDocument next = next(cursor, false).get(0) + ChangeStreamDocument next = next(cursor, false, 1).get(0) then: next.getResumeToken() != null @@ -220,7 +219,7 @@ class ChangeStreamOperationSpecification extends OperationFunctionalSpecificatio when: def cursor = execute(operation, false) helper.updateOne(BsonDocument.parse('{ _id : 2}'), BsonDocument.parse('{ $set : {x : 3}, $unset : {y : 1}}')) - ChangeStreamDocument next = next(cursor, false).get(0) + ChangeStreamDocument next = next(cursor, false, 1).get(0) then: next.getResumeToken() != null @@ -247,7 +246,7 @@ class ChangeStreamOperationSpecification extends OperationFunctionalSpecificatio when: def cursor = execute(operation, false) helper.replaceOne(BsonDocument.parse('{ _id : 2}'), BsonDocument.parse('{ _id : 2, x : 3}'), false) - ChangeStreamDocument next = next(cursor, false).get(0) + ChangeStreamDocument next = next(cursor, false, 1).get(0) then: next.getResumeToken() != null @@ -274,7 +273,7 @@ class ChangeStreamOperationSpecification extends OperationFunctionalSpecificatio when: def cursor = execute(operation, false) helper.deleteOne(BsonDocument.parse('{ _id : 2}')) - ChangeStreamDocument next = next(cursor, false).get(0) + ChangeStreamDocument next = next(cursor, false, 1).get(0) then: next.getResumeToken() != null @@ -301,7 +300,7 @@ class ChangeStreamOperationSpecification extends OperationFunctionalSpecificatio when: def cursor = execute(operation, false) helper.drop() - ChangeStreamDocument next = next(cursor, false).get(0) + ChangeStreamDocument next = next(cursor, false, 1).get(0) then: next.getResumeToken() != null @@ -329,7 +328,7 @@ class ChangeStreamOperationSpecification extends OperationFunctionalSpecificatio when: def cursor = execute(operation, false) helper.drop() - ChangeStreamDocument next = next(cursor, false).get(0) + ChangeStreamDocument next = next(cursor, false, 1).get(0) then: next.getResumeToken() != null @@ -358,7 +357,7 @@ class ChangeStreamOperationSpecification extends OperationFunctionalSpecificatio when: def cursor = execute(operation, false) helper.dropDatabase('JavaDriverTest') - ChangeStreamDocument next = next(cursor, false).get(0) + ChangeStreamDocument next = next(cursor, false, 1).get(0) then: next.getResumeToken() != null @@ -387,7 +386,7 @@ class ChangeStreamOperationSpecification extends OperationFunctionalSpecificatio when: def cursor = execute(operation, false) helper.renameCollection(newNamespace) - ChangeStreamDocument next = next(cursor, false).get(0) + ChangeStreamDocument next = next(cursor, false, 1).get(0) then: next.getResumeToken() != null @@ -442,7 +441,7 @@ class ChangeStreamOperationSpecification extends OperationFunctionalSpecificatio def expected = insertDocuments(helper, [1, 2]) then: - nextAndClean(cursor, async) == expected + nextAndClean(cursor, async, expected.size()) == expected then: tryNextAndClean(cursor, async) == null @@ -451,7 +450,7 @@ class ChangeStreamOperationSpecification extends OperationFunctionalSpecificatio expected = insertDocuments(helper, [3, 4]) then: - nextAndClean(cursor, async) == expected + nextAndClean(cursor, async, expected.size()) == expected cleanup: cursor?.close() @@ -473,15 +472,12 @@ class ChangeStreamOperationSpecification extends OperationFunctionalSpecificatio def expected = insertDocuments(helper, [1, 2]) then: - nextAndClean(cursor, async) == expected + nextAndClean(cursor, async, expected.size()) == expected when: helper.killCursor(helper.getNamespace(), cursor.getWrapped().getServerCursor()) expected = insertDocuments(helper, [3, 4]) - def results = nextAndClean(cursor, async) - if (results.size() < expected.size()) { - results.addAll(nextAndClean(cursor, async)) - } + def results = nextAndClean(cursor, async, expected.size()) then: results == expected @@ -493,10 +489,7 @@ class ChangeStreamOperationSpecification extends OperationFunctionalSpecificatio expected = insertDocuments(helper, [5, 6]) helper.killCursor(helper.getNamespace(), cursor.getWrapped().getServerCursor()) - results = nextAndClean(cursor, async) - if (results.size() < expected.size()) { - results.addAll(nextAndClean(cursor, async)) - } + results = nextAndClean(cursor, async, expected.size()) then: results == expected @@ -521,7 +514,7 @@ class ChangeStreamOperationSpecification extends OperationFunctionalSpecificatio when: def expected = insertDocuments(helper, [1, 2]) - def result = next(cursor, async) + def result = next(cursor, async, 2) then: result.size() == 2 @@ -532,7 +525,7 @@ class ChangeStreamOperationSpecification extends OperationFunctionalSpecificatio operation.startAtOperationTime(result.last().getTimestamp('clusterTime')) cursor = execute(operation, async) - result = nextAndClean(cursor, async) + result = nextAndClean(cursor, async, expected.tail.size()) then: result == expected.tail() @@ -556,7 +549,7 @@ class ChangeStreamOperationSpecification extends OperationFunctionalSpecificatio when: def expected = insertDocuments(helper, [1, 2]) - def result = next(cursor, async) + def result = next(cursor, async, 2) then: result.size() == 2 @@ -567,7 +560,7 @@ class ChangeStreamOperationSpecification extends OperationFunctionalSpecificatio operation.resumeAfter(result.head().getDocument('_id')).startAtOperationTime(null) cursor = execute(operation, async) - result = nextAndClean(cursor, async) + result = nextAndClean(cursor, async, expected.tail().size()) then: result == expected.tail() @@ -592,7 +585,7 @@ class ChangeStreamOperationSpecification extends OperationFunctionalSpecificatio when: def expected = insertDocuments(helper, [1, 2]) - def result = next(cursor, async) + def result = next(cursor, async, 2) then: result.size() == 2 @@ -602,7 +595,7 @@ class ChangeStreamOperationSpecification extends OperationFunctionalSpecificatio waitForLastRelease(async ? getAsyncCluster() : getCluster()) cursor = execute(operation.startAfter(result.head().getDocument('_id')).startAtOperationTime(null), async) - result = nextAndClean(cursor, async) + result = nextAndClean(cursor, async, expected.tail().size()) then: result == expected.tail() @@ -762,6 +755,10 @@ class ChangeStreamOperationSpecification extends OperationFunctionalSpecificatio removeExtra(tryNext(cursor, async)) } + def nextAndClean(cursor, boolean async, int minimumCount) { + removeExtra(next(cursor, async, minimumCount)) + } + def nextAndClean(cursor, boolean async) { removeExtra(next(cursor, async)) } From 56345703bddb0773cd69b5aa95834c89d7dab009 Mon Sep 17 00:00:00 2001 From: Jeff Yemin Date: Tue, 26 May 2020 10:57:55 -0400 Subject: [PATCH 27/48] Fix test calling List.tail() List.tail is not the same as List.tail() in Groovy JAVA-3742 --- .../mongodb/operation/ChangeStreamOperationSpecification.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/driver-core/src/test/functional/com/mongodb/operation/ChangeStreamOperationSpecification.groovy b/driver-core/src/test/functional/com/mongodb/operation/ChangeStreamOperationSpecification.groovy index 7128a93a901..016e5a54ea3 100644 --- a/driver-core/src/test/functional/com/mongodb/operation/ChangeStreamOperationSpecification.groovy +++ b/driver-core/src/test/functional/com/mongodb/operation/ChangeStreamOperationSpecification.groovy @@ -525,7 +525,7 @@ class ChangeStreamOperationSpecification extends OperationFunctionalSpecificatio operation.startAtOperationTime(result.last().getTimestamp('clusterTime')) cursor = execute(operation, async) - result = nextAndClean(cursor, async, expected.tail.size()) + result = nextAndClean(cursor, async, expected.tail().size()) then: result == expected.tail() From 1ca6ab124a60d33f061ebe25dbecee13c443d396 Mon Sep 17 00:00:00 2001 From: Jeff Yemin Date: Fri, 29 May 2020 10:52:40 -0400 Subject: [PATCH 28/48] Remove performance tests --- .evergreen/.evg.yml | 30 ------------------------------ .evergreen/run-perf-tests.sh | 23 ----------------------- 2 files changed, 53 deletions(-) delete mode 100755 .evergreen/run-perf-tests.sh diff --git a/.evergreen/.evg.yml b/.evergreen/.evg.yml index 3286301032b..356ba5c4e1c 100644 --- a/.evergreen/.evg.yml +++ b/.evergreen/.evg.yml @@ -344,21 +344,6 @@ functions: ${PREPARE_SHELL} echo '{"results": [{ "status": "FAIL", "test_file": "Build", "log_raw": "No test-results.json found was created" } ]}' > ${PROJECT_DIRECTORY}/test-results.json - "run perf tests": - - command: shell.exec - type: test - params: - working_dir: "src" - script: | - ${PREPARE_SHELL} - PROJECT_DIRECTORY=${PROJECT_DIRECTORY} .evergreen/run-perf-tests.sh - - "send dashboard data": - - command: json.send - params: - name: perf - file: src/results.json - # Anchors hosts: &hosts @@ -437,13 +422,6 @@ tasks: commands: - func: "publish snapshot" - - name: "perf" - tags: ["perf"] - commands: - - func: "bootstrap mongo-orchestration" - - func: "run perf tests" - - func: "send dashboard data" - - name: "mmapv1-storage-test" commands: - func: "bootstrap mongo-orchestration" @@ -687,14 +665,6 @@ buildvariants: tasks: - name: "gssapi-auth-test" -- matrix_name: "perf" - matrix_spec: { auth: "noauth", ssl: "nossl", jdk: "jdk9", version: "*", topology: "standalone", os: "linux" } - batchtime: 1440 # run once a day - display_name: "Perf Tests ${version} " - tags: ["perf-variant"] - tasks: - - name: "perf" - - matrix_name: "tests-embedded" matrix_spec: { auth: "noauth", ssl: "nossl", jdk: "*", version: ["4.2"], topology: "standalone", os: "ubuntu" } exclude_spec: { auth: "*", ssl: "*", jdk: "jdk6", version: "*", topology: "*", os: "*" } diff --git a/.evergreen/run-perf-tests.sh b/.evergreen/run-perf-tests.sh deleted file mode 100755 index 7bf115383be..00000000000 --- a/.evergreen/run-perf-tests.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash - -set -o xtrace -set -o errexit - -rm -rf driver-performance-test-data -git clone https://github.com/mongodb-labs/driver-performance-test-data.git -cd driver-performance-test-data -tar xf extended_bson.tgz -tar xf parallel.tgz -tar xf single_and_multi_document.tgz -cd .. - -export JAVA_HOME="/opt/java/jdk11" - -export TEST_PATH="${PROJECT_DIRECTORY}/driver-performance-test-data/" -export OUTPUT_FILE="${PROJECT_DIRECTORY}/results.json" - -start_time=$(date +%s) -./gradlew -Dorg.mongodb.benchmarks.data=${TEST_PATH} -Dorg.mongodb.benchmarks.output=${OUTPUT_FILE} driver-benchmarks:run -end_time=$(date +%s) -elapsed_secs=$((end_time-start_time)) - From 5d4905e241c173993398bdc10aacffc379a8cbda Mon Sep 17 00:00:00 2001 From: Jeff Yemin Date: Tue, 2 Jun 2020 08:19:47 -0400 Subject: [PATCH 29/48] Bump version to 3.12.5 --- build.gradle | 2 +- docs/reference/content/bson/installation-guide.md | 2 +- .../content/driver-async/getting-started/installation.md | 2 +- .../content/driver/getting-started/installation.md | 8 ++++---- docs/reference/content/driver/tutorials/jndi.md | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/build.gradle b/build.gradle index 6c124a0065f..017ca7af428 100644 --- a/build.gradle +++ b/build.gradle @@ -54,7 +54,7 @@ def javaCodeCheckedProjects = subprojects.findAll { !['util', 'mongo-java-driver configure(coreProjects) { evaluationDependsOn(':util') group = 'org.mongodb' - version = '3.12.5-SNAPSHOT' + version = '3.12.5' repositories { mavenLocal() diff --git a/docs/reference/content/bson/installation-guide.md b/docs/reference/content/bson/installation-guide.md index 2e335d17c71..28a9a7d953f 100644 --- a/docs/reference/content/bson/installation-guide.md +++ b/docs/reference/content/bson/installation-guide.md @@ -22,4 +22,4 @@ This library comprehensively supports [BSON](http://www.bsonspec.org), the data storage and network transfer format that MongoDB uses for "documents". BSON is short for Binary [JSON](http://json.org/), is a binary-encoded serialization of JSON-like documents. -{{< install artifactId="bson" version="3.12.4" >}} +{{< install artifactId="bson" version="3.12.5" >}} diff --git a/docs/reference/content/driver-async/getting-started/installation.md b/docs/reference/content/driver-async/getting-started/installation.md index 1edad6c88a0..85fb6193b9a 100644 --- a/docs/reference/content/driver-async/getting-started/installation.md +++ b/docs/reference/content/driver-async/getting-started/installation.md @@ -24,4 +24,4 @@ When TLS/SSL is enabled, the MongoDB Async Driver requires either [Netty](http:/ The MongoDB Async Driver provides asynchronous API that can leverage either Netty or Java 7's AsynchronousSocketChannel for fast and non-blocking I/O. -{{< install artifactId="mongodb-driver-async" version="3.12.4" dependencies="true">}} +{{< install artifactId="mongodb-driver-async" version="3.12.5" dependencies="true">}} diff --git a/docs/reference/content/driver/getting-started/installation.md b/docs/reference/content/driver/getting-started/installation.md index 80e1637131f..49205239d91 100644 --- a/docs/reference/content/driver/getting-started/installation.md +++ b/docs/reference/content/driver/getting-started/installation.md @@ -31,7 +31,7 @@ The `mongodb-driver-sync` artifact is a valid OSGi bundle whose symbolic name is {{% /note %}} -{{< install artifactId="mongodb-driver-sync" version="3.12.4" dependencies="true">}} +{{< install artifactId="mongodb-driver-sync" version="3.12.5" dependencies="true">}} ## MongoDB Driver Legacy @@ -44,7 +44,7 @@ While not deprecated, we recommend that new applications depend on the `mongodb- {{% /note %}} -{{< install artifactId="mongodb-driver-legacy" version="3.12.4" dependencies="true">}} +{{< install artifactId="mongodb-driver-legacy" version="3.12.5" dependencies="true">}} ## MongoDB Driver @@ -61,7 +61,7 @@ This module is deprecated and will no longer be published in the next major rele {{% /note %}} -{{< install artifactId="mongodb-driver" version="3.12.4" dependencies="true">}} +{{< install artifactId="mongodb-driver" version="3.12.5" dependencies="true">}} ## Uber Jar (Legacy) @@ -81,4 +81,4 @@ This module is deprecated and will no longer be published in the next major rele {{% /note %}} -{{< install artifactId="mongo-java-driver" version="3.12.4">}} +{{< install artifactId="mongo-java-driver" version="3.12.5">}} diff --git a/docs/reference/content/driver/tutorials/jndi.md b/docs/reference/content/driver/tutorials/jndi.md index ec11fd54a85..62f6faefe19 100644 --- a/docs/reference/content/driver/tutorials/jndi.md +++ b/docs/reference/content/driver/tutorials/jndi.md @@ -28,7 +28,7 @@ The configuration of the `MongoClientFactory` differs depending on the applicati - + From b263d52a37a98b00ce7b4882993c02fd17a84c3b Mon Sep 17 00:00:00 2001 From: Jeff Yemin Date: Tue, 2 Jun 2020 08:32:37 -0400 Subject: [PATCH 30/48] Bump version to 3.12.6-SNAPSHOT --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 017ca7af428..9feee9bbfa9 100644 --- a/build.gradle +++ b/build.gradle @@ -54,7 +54,7 @@ def javaCodeCheckedProjects = subprojects.findAll { !['util', 'mongo-java-driver configure(coreProjects) { evaluationDependsOn(':util') group = 'org.mongodb' - version = '3.12.5' + version = '3.12.6-SNAPSHOT' repositories { mavenLocal() From 717ac88a3746c81ca054a485c04ac4b285fe17f5 Mon Sep 17 00:00:00 2001 From: Jeff Yemin Date: Thu, 4 Jun 2020 08:57:26 -0400 Subject: [PATCH 31/48] Apply client-side encryption in transactions on sharded clusters This fixes a bug in both sync and async drivers where client-side encryption is not applied when in a transaction. JAVA-3752 --- .../async/client/ClientSessionBinding.java | 98 +++++----- .../client/internal/AsyncCryptBinding.java | 15 ++ .../ClientSideEncryptionSessionTest.java | 172 ++++++++++++++++++ .../mongodb/binding/AsyncClusterBinding.java | 7 + .../com/mongodb/binding/ClusterBinding.java | 15 +- .../AsyncClusterAwareReadWriteBinding.java | 11 ++ .../binding/ClusterAwareReadWriteBinding.java | 8 + .../client/internal/ClientSessionBinding.java | 31 ++-- .../mongodb/client/internal/CryptBinding.java | 6 + .../ClientSideEncryptionSessionTest.java | 154 ++++++++++++++++ 10 files changed, 440 insertions(+), 77 deletions(-) create mode 100644 driver-async/src/test/functional/com/mongodb/async/client/ClientSideEncryptionSessionTest.java create mode 100644 driver-sync/src/test/functional/com/mongodb/client/ClientSideEncryptionSessionTest.java diff --git a/driver-async/src/main/com/mongodb/async/client/ClientSessionBinding.java b/driver-async/src/main/com/mongodb/async/client/ClientSessionBinding.java index 9a75818a135..e0878a821fd 100644 --- a/driver-async/src/main/com/mongodb/async/client/ClientSessionBinding.java +++ b/driver-async/src/main/com/mongodb/async/client/ClientSessionBinding.java @@ -21,7 +21,6 @@ import com.mongodb.async.SingleResultCallback; import com.mongodb.binding.AsyncConnectionSource; import com.mongodb.binding.AsyncReadWriteBinding; -import com.mongodb.binding.AsyncSingleServerBinding; import com.mongodb.connection.AsyncConnection; import com.mongodb.connection.ClusterType; import com.mongodb.connection.Server; @@ -53,29 +52,19 @@ public ReadPreference getReadPreference() { @Override public void getReadConnectionSource(final SingleResultCallback callback) { - wrapped.getReadConnectionSource(new SingleResultCallback() { - @Override - public void onResult(final AsyncConnectionSource result, final Throwable t) { - if (t != null) { - callback.onResult(null, t); - } else { - wrapConnectionSource(result, callback); - } - } - }); + if (isActiveShardedTxn()) { + getPinnedConnectionSource(callback); + } else { + wrapped.getReadConnectionSource(new WrappingCallback(callback)); + } } public void getWriteConnectionSource(final SingleResultCallback callback) { - wrapped.getWriteConnectionSource(new SingleResultCallback() { - @Override - public void onResult(final AsyncConnectionSource result, final Throwable t) { - if (t != null) { - callback.onResult(null, t); - } else { - wrapConnectionSource(result, callback); - } - } - }); + if (isActiveShardedTxn()) { + getPinnedConnectionSource(callback); + } else { + wrapped.getWriteConnectionSource(new WrappingCallback(callback)); + } } @Override @@ -83,47 +72,25 @@ public SessionContext getSessionContext() { return sessionContext; } - private void wrapConnectionSource(final AsyncConnectionSource connectionSource, - final SingleResultCallback callback) { - if (isActiveShardedTxn()) { - if (session.getPinnedServerAddress() == null) { - wrapped.getCluster().selectServerAsync( - new ReadPreferenceServerSelector(wrapped.getReadPreference()), - new SingleResultCallback() { - @Override - public void onResult(final Server server, final Throwable t) { - if (t != null) { - callback.onResult(null, t); - } else { - session.setPinnedServerAddress(server.getDescription().getAddress()); - setSingleServerBindingConnectionSource(callback); - } + private void getPinnedConnectionSource(final SingleResultCallback callback) { + if (session.getPinnedServerAddress() == null) { + wrapped.getCluster().selectServerAsync( + new ReadPreferenceServerSelector(wrapped.getReadPreference()), new SingleResultCallback() { + @Override + public void onResult(final Server server, final Throwable t) { + if (t != null) { + callback.onResult(null, t); + } else { + session.setPinnedServerAddress(server.getDescription().getAddress()); + wrapped.getConnectionSource(session.getPinnedServerAddress(), new WrappingCallback(callback)); } - }); - } else { - setSingleServerBindingConnectionSource(callback); - } + } + }); } else { - callback.onResult(new SessionBindingAsyncConnectionSource(connectionSource), null); + wrapped.getConnectionSource(session.getPinnedServerAddress(), new WrappingCallback(callback)); } } - private void setSingleServerBindingConnectionSource(final SingleResultCallback callback) { - final AsyncSingleServerBinding binding = - new AsyncSingleServerBinding(wrapped.getCluster(), session.getPinnedServerAddress(), wrapped.getReadPreference()); - binding.getWriteConnectionSource(new SingleResultCallback() { - @Override - public void onResult(final AsyncConnectionSource result, final Throwable t) { - binding.release(); - if (t != null) { - callback.onResult(null, t); - } else { - callback.onResult(new SessionBindingAsyncConnectionSource(result), null); - } - } - }); - } - @Override public int getCount() { return wrapped.getCount(); @@ -225,4 +192,21 @@ public ReadConcern getReadConcern() { } } } + + private class WrappingCallback implements SingleResultCallback { + private final SingleResultCallback callback; + + WrappingCallback(final SingleResultCallback callback) { + this.callback = callback; + } + + @Override + public void onResult(final AsyncConnectionSource result, final Throwable t) { + if (t != null) { + callback.onResult(null, t); + } else { + callback.onResult(new SessionBindingAsyncConnectionSource(result), null); + } + } + } } diff --git a/driver-async/src/main/com/mongodb/async/client/internal/AsyncCryptBinding.java b/driver-async/src/main/com/mongodb/async/client/internal/AsyncCryptBinding.java index 5d9ef119cee..251aa256b41 100644 --- a/driver-async/src/main/com/mongodb/async/client/internal/AsyncCryptBinding.java +++ b/driver-async/src/main/com/mongodb/async/client/internal/AsyncCryptBinding.java @@ -17,6 +17,7 @@ package com.mongodb.async.client.internal; import com.mongodb.ReadPreference; +import com.mongodb.ServerAddress; import com.mongodb.async.SingleResultCallback; import com.mongodb.binding.AsyncConnectionSource; import com.mongodb.binding.AsyncReadWriteBinding; @@ -74,6 +75,20 @@ public void onResult(final AsyncConnectionSource result, final Throwable t) { }); } + @Override + public void getConnectionSource(final ServerAddress serverAddress, final SingleResultCallback callback) { + wrapped.getConnectionSource(serverAddress, new SingleResultCallback() { + @Override + public void onResult(final AsyncConnectionSource result, final Throwable t) { + if (t != null) { + callback.onResult(null, t); + } else { + callback.onResult(new AsyncCryptConnectionSource(result), null); + } + } + }); + } + @Override public int getCount() { return wrapped.getCount(); diff --git a/driver-async/src/test/functional/com/mongodb/async/client/ClientSideEncryptionSessionTest.java b/driver-async/src/test/functional/com/mongodb/async/client/ClientSideEncryptionSessionTest.java new file mode 100644 index 00000000000..c760e0ff0f3 --- /dev/null +++ b/driver-async/src/test/functional/com/mongodb/async/client/ClientSideEncryptionSessionTest.java @@ -0,0 +1,172 @@ +/* + * Copyright 2008-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.mongodb.async.client; + +import com.mongodb.AutoEncryptionSettings; +import com.mongodb.MongoClientSettings; +import com.mongodb.MongoNamespace; +import com.mongodb.WriteConcern; +import com.mongodb.async.FutureResultCallback; +import com.mongodb.client.test.CollectionHelper; +import org.bson.BsonDocument; +import org.bson.BsonString; +import org.bson.codecs.BsonDocumentCodec; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.io.File; +import java.io.IOException; +import java.net.URISyntaxException; +import java.util.Arrays; +import java.util.Base64; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import static com.mongodb.ClusterFixture.isNotAtLeastJava8; +import static com.mongodb.ClusterFixture.isStandalone; +import static com.mongodb.ClusterFixture.serverVersionAtLeast; +import static com.mongodb.async.client.Fixture.getDefaultDatabaseName; +import static com.mongodb.async.client.Fixture.getMongoClient; +import static com.mongodb.async.client.Fixture.getMongoClientBuilderFromConnectionString; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeFalse; +import static org.junit.Assume.assumeTrue; +import static util.JsonPoweredTestHelper.getTestDocument; + +@RunWith(Parameterized.class) +public class ClientSideEncryptionSessionTest { + private static final String COLLECTION_NAME = "clientSideEncryptionSessionsTest"; + + private MongoClient client = getMongoClient(); + private MongoClient clientEncrypted; + private final boolean useTransaction; + + @Parameterized.Parameters(name = "useTransaction: {0}") + public static Collection data() { + return Arrays.asList(new Object[]{true}, new Object[]{false}); + } + + public ClientSideEncryptionSessionTest(final boolean useTransaction) { + this.useTransaction = useTransaction; + } + + @Before + public void setUp() throws Throwable { + assumeFalse(isNotAtLeastJava8()); + assumeTrue(serverVersionAtLeast(4, 2)); + assumeFalse(isStandalone()); + + /* Step 1: get unencrypted client and recreate keys collection */ + client = getMongoClient(); + MongoDatabase keyVaultDatabase = client.getDatabase("keyvault"); + MongoCollection dataKeys = keyVaultDatabase.getCollection("datakeys", BsonDocument.class) + .withWriteConcern(WriteConcern.MAJORITY); + FutureResultCallback voidCallback = new FutureResultCallback(); + dataKeys.drop(voidCallback); + voidCallback.get(); + + voidCallback = new FutureResultCallback(); + dataKeys.insertOne(bsonDocumentFromPath("external-key.json"), voidCallback); + voidCallback.get(); + + /* Step 2: create encryption objects. */ + Map> kmsProviders = new HashMap>(); + Map localMasterkey = new HashMap(); + Map schemaMap = new HashMap(); + + byte[] localMasterKeyBytes = Base64.getDecoder().decode("Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBM" + + "UN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk"); + localMasterkey.put("key", localMasterKeyBytes); + kmsProviders.put("local", localMasterkey); + schemaMap.put(getDefaultDatabaseName() + "." + COLLECTION_NAME, bsonDocumentFromPath("external-schema.json")); + + MongoClientSettings clientSettings = getMongoClientBuilderFromConnectionString() + .autoEncryptionSettings(AutoEncryptionSettings.builder() + .keyVaultNamespace("keyvault.datakeys") + .kmsProviders(kmsProviders) + .schemaMap(schemaMap).build()) + .build(); + clientEncrypted = MongoClients.create(clientSettings); + + CollectionHelper collectionHelper = + new CollectionHelper(new BsonDocumentCodec(), new MongoNamespace(getDefaultDatabaseName(), COLLECTION_NAME)); + collectionHelper.drop(); + collectionHelper.create(); + } + + @After + public void after() { + if (clientEncrypted != null) { + try { + clientEncrypted.close(); + } catch (Exception e) { + // ignore + } + } + } + + @Test + public void testWithExplicitSession() throws Throwable { + BsonString unencryptedValue = new BsonString("test"); + + FutureResultCallback clientSessionCallback = new FutureResultCallback(); + clientEncrypted.startSession(clientSessionCallback); + ClientSession clientSession = clientSessionCallback.get(); + try { + if (useTransaction) { + clientSession.startTransaction(); + } + MongoCollection autoEncryptedCollection = clientEncrypted.getDatabase(getDefaultDatabaseName()) + .getCollection(COLLECTION_NAME, BsonDocument.class); + FutureResultCallback insertCallback = new FutureResultCallback(); + autoEncryptedCollection.insertOne(clientSession, new BsonDocument().append("encrypted", new BsonString("test")), + insertCallback); + insertCallback.get(); + + FutureResultCallback findCallback = new FutureResultCallback(); + autoEncryptedCollection.find(clientSession).first(findCallback); + BsonDocument unencryptedDocument = findCallback.get(); + assertEquals(unencryptedValue, unencryptedDocument.getString("encrypted")); + + if (useTransaction) { + FutureResultCallback commitCallback = new FutureResultCallback(); + clientSession.commitTransaction(commitCallback); + commitCallback.get(); + } + } finally { + clientSession.close(); + } + + MongoCollection encryptedCollection = client.getDatabase(getDefaultDatabaseName()) + .getCollection(COLLECTION_NAME, BsonDocument.class); + FutureResultCallback findCallback = new FutureResultCallback(); + encryptedCollection.find().first(findCallback); + BsonDocument encryptedDocument = findCallback.get(); + assertTrue(encryptedDocument.isBinary("encrypted")); + assertEquals(6, encryptedDocument.getBinary("encrypted").getType()); + } + + private static BsonDocument bsonDocumentFromPath(final String path) throws IOException, URISyntaxException { + return getTestDocument(new File(ClientSideEncryptionSessionTest.class + .getResource("/client-side-encryption-external/" + path).toURI())); + } +} diff --git a/driver-core/src/main/com/mongodb/binding/AsyncClusterBinding.java b/driver-core/src/main/com/mongodb/binding/AsyncClusterBinding.java index 3216a8605a8..1c008441c87 100644 --- a/driver-core/src/main/com/mongodb/binding/AsyncClusterBinding.java +++ b/driver-core/src/main/com/mongodb/binding/AsyncClusterBinding.java @@ -18,6 +18,7 @@ import com.mongodb.ReadConcern; import com.mongodb.ReadPreference; +import com.mongodb.ServerAddress; import com.mongodb.async.SingleResultCallback; import com.mongodb.connection.AsyncConnection; import com.mongodb.connection.Cluster; @@ -27,6 +28,7 @@ import com.mongodb.internal.binding.AsyncClusterAwareReadWriteBinding; import com.mongodb.internal.connection.ReadConcernAwareNoOpSessionContext; import com.mongodb.selector.ReadPreferenceServerSelector; +import com.mongodb.selector.ServerAddressSelector; import com.mongodb.selector.ServerSelector; import com.mongodb.selector.WritableServerSelector; import com.mongodb.session.SessionContext; @@ -102,6 +104,11 @@ public void getWriteConnectionSource(final SingleResultCallback callback) { + getAsyncClusterBindingConnectionSource(new ServerAddressSelector(serverAddress), callback); + } + private void getAsyncClusterBindingConnectionSource(final ServerSelector serverSelector, final SingleResultCallback callback) { cluster.selectServerAsync(serverSelector, new SingleResultCallback() { diff --git a/driver-core/src/main/com/mongodb/binding/ClusterBinding.java b/driver-core/src/main/com/mongodb/binding/ClusterBinding.java index 9aa89a314e3..51460746642 100644 --- a/driver-core/src/main/com/mongodb/binding/ClusterBinding.java +++ b/driver-core/src/main/com/mongodb/binding/ClusterBinding.java @@ -18,6 +18,7 @@ import com.mongodb.ReadConcern; import com.mongodb.ReadPreference; +import com.mongodb.ServerAddress; import com.mongodb.connection.Cluster; import com.mongodb.connection.Connection; import com.mongodb.connection.Server; @@ -26,6 +27,7 @@ import com.mongodb.internal.binding.ClusterAwareReadWriteBinding; import com.mongodb.internal.connection.ReadConcernAwareNoOpSessionContext; import com.mongodb.selector.ReadPreferenceServerSelector; +import com.mongodb.selector.ServerAddressSelector; import com.mongodb.selector.ServerSelector; import com.mongodb.selector.WritableServerSelector; import com.mongodb.session.SessionContext; @@ -89,13 +91,13 @@ public ReadPreference getReadPreference() { } @Override - public ConnectionSource getReadConnectionSource() { - return new ClusterBindingConnectionSource(new ReadPreferenceServerSelector(readPreference)); + public SessionContext getSessionContext() { + return new ReadConcernAwareNoOpSessionContext(readConcern); } @Override - public SessionContext getSessionContext() { - return new ReadConcernAwareNoOpSessionContext(readConcern); + public ConnectionSource getReadConnectionSource() { + return new ClusterBindingConnectionSource(new ReadPreferenceServerSelector(readPreference)); } @Override @@ -103,6 +105,11 @@ public ConnectionSource getWriteConnectionSource() { return new ClusterBindingConnectionSource(new WritableServerSelector()); } + @Override + public ConnectionSource getConnectionSource(final ServerAddress serverAddress) { + return new ClusterBindingConnectionSource(new ServerAddressSelector(serverAddress)); + } + private final class ClusterBindingConnectionSource extends AbstractReferenceCounted implements ConnectionSource { private final Server server; diff --git a/driver-core/src/main/com/mongodb/internal/binding/AsyncClusterAwareReadWriteBinding.java b/driver-core/src/main/com/mongodb/internal/binding/AsyncClusterAwareReadWriteBinding.java index ec0523f23f7..c84df3d84e9 100644 --- a/driver-core/src/main/com/mongodb/internal/binding/AsyncClusterAwareReadWriteBinding.java +++ b/driver-core/src/main/com/mongodb/internal/binding/AsyncClusterAwareReadWriteBinding.java @@ -17,6 +17,9 @@ package com.mongodb.internal.binding; +import com.mongodb.ServerAddress; +import com.mongodb.async.SingleResultCallback; +import com.mongodb.binding.AsyncConnectionSource; import com.mongodb.binding.AsyncReadWriteBinding; import com.mongodb.connection.Cluster; @@ -25,4 +28,12 @@ */ public interface AsyncClusterAwareReadWriteBinding extends AsyncReadWriteBinding { Cluster getCluster(); + + /** + * Returns a connection source to the specified server + * + * @param serverAddress the server address + * @param callback the to be passed the connection source + */ + void getConnectionSource(ServerAddress serverAddress, SingleResultCallback callback); } diff --git a/driver-core/src/main/com/mongodb/internal/binding/ClusterAwareReadWriteBinding.java b/driver-core/src/main/com/mongodb/internal/binding/ClusterAwareReadWriteBinding.java index cc460620957..e3db0354be8 100644 --- a/driver-core/src/main/com/mongodb/internal/binding/ClusterAwareReadWriteBinding.java +++ b/driver-core/src/main/com/mongodb/internal/binding/ClusterAwareReadWriteBinding.java @@ -16,6 +16,8 @@ package com.mongodb.internal.binding; +import com.mongodb.ServerAddress; +import com.mongodb.binding.ConnectionSource; import com.mongodb.binding.ReadWriteBinding; import com.mongodb.connection.Cluster; @@ -24,4 +26,10 @@ */ public interface ClusterAwareReadWriteBinding extends ReadWriteBinding { Cluster getCluster(); + + /** + * Returns a connection source to the specified server address. + * @return the connection source + */ + ConnectionSource getConnectionSource(ServerAddress serverAddress); } diff --git a/driver-sync/src/main/com/mongodb/client/internal/ClientSessionBinding.java b/driver-sync/src/main/com/mongodb/client/internal/ClientSessionBinding.java index 5ee9028106d..ef956d2f06b 100644 --- a/driver-sync/src/main/com/mongodb/client/internal/ClientSessionBinding.java +++ b/driver-sync/src/main/com/mongodb/client/internal/ClientSessionBinding.java @@ -18,9 +18,9 @@ import com.mongodb.ReadConcern; import com.mongodb.ReadPreference; +import com.mongodb.ServerAddress; import com.mongodb.binding.ConnectionSource; import com.mongodb.binding.ReadWriteBinding; -import com.mongodb.binding.SingleServerBinding; import com.mongodb.client.ClientSession; import com.mongodb.connection.ClusterType; import com.mongodb.connection.Connection; @@ -79,23 +79,19 @@ private void closeSessionIfCountIsZero() { @Override public ConnectionSource getReadConnectionSource() { - return new SessionBindingConnectionSource(wrapConnectionSource(wrapped.getReadConnectionSource())); + if (isActiveShardedTxn()) { + return new SessionBindingConnectionSource(wrapped.getConnectionSource(pinServer())); + } else { + return new SessionBindingConnectionSource(wrapped.getReadConnectionSource()); + } } public ConnectionSource getWriteConnectionSource() { - return new SessionBindingConnectionSource(wrapConnectionSource(wrapped.getWriteConnectionSource())); - } - - private ConnectionSource wrapConnectionSource(final ConnectionSource connectionSource) { - ConnectionSource retVal = connectionSource; if (isActiveShardedTxn()) { - setPinnedServerAddress(); - SingleServerBinding binding = new SingleServerBinding(wrapped.getCluster(), session.getPinnedServerAddress(), - wrapped.getReadPreference()); - retVal = binding.getWriteConnectionSource(); - binding.release(); + return new SessionBindingConnectionSource(wrapped.getConnectionSource(pinServer())); + } else { + return new SessionBindingConnectionSource(wrapped.getWriteConnectionSource()); } - return retVal; } @Override @@ -107,11 +103,14 @@ private boolean isActiveShardedTxn() { return session.hasActiveTransaction() && wrapped.getCluster().getDescription().getType() == ClusterType.SHARDED; } - private void setPinnedServerAddress() { - if (session.getPinnedServerAddress() == null) { + private ServerAddress pinServer() { + ServerAddress pinnedServerAddress = session.getPinnedServerAddress(); + if (pinnedServerAddress == null) { Server server = wrapped.getCluster().selectServer(new ReadPreferenceServerSelector(wrapped.getReadPreference())); - session.setPinnedServerAddress(server.getDescription().getAddress()); + pinnedServerAddress = server.getDescription().getAddress(); + session.setPinnedServerAddress(pinnedServerAddress); } + return pinnedServerAddress; } private class SessionBindingConnectionSource implements ConnectionSource { diff --git a/driver-sync/src/main/com/mongodb/client/internal/CryptBinding.java b/driver-sync/src/main/com/mongodb/client/internal/CryptBinding.java index bd05f633a65..2a6a4b095fa 100644 --- a/driver-sync/src/main/com/mongodb/client/internal/CryptBinding.java +++ b/driver-sync/src/main/com/mongodb/client/internal/CryptBinding.java @@ -17,6 +17,7 @@ package com.mongodb.client.internal; import com.mongodb.ReadPreference; +import com.mongodb.ServerAddress; import com.mongodb.binding.ConnectionSource; import com.mongodb.binding.ReadWriteBinding; import com.mongodb.connection.Cluster; @@ -49,6 +50,11 @@ public ConnectionSource getWriteConnectionSource() { return new CryptConnectionSource(wrapped.getWriteConnectionSource()); } + @Override + public ConnectionSource getConnectionSource(final ServerAddress serverAddress) { + return new CryptConnectionSource(wrapped.getConnectionSource(serverAddress)); + } + @Override public SessionContext getSessionContext() { return wrapped.getSessionContext(); diff --git a/driver-sync/src/test/functional/com/mongodb/client/ClientSideEncryptionSessionTest.java b/driver-sync/src/test/functional/com/mongodb/client/ClientSideEncryptionSessionTest.java new file mode 100644 index 00000000000..a488d36df50 --- /dev/null +++ b/driver-sync/src/test/functional/com/mongodb/client/ClientSideEncryptionSessionTest.java @@ -0,0 +1,154 @@ +/* + * Copyright 2008-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.mongodb.client; + +import com.mongodb.AutoEncryptionSettings; +import com.mongodb.MongoClientSettings; +import com.mongodb.MongoNamespace; +import com.mongodb.WriteConcern; +import com.mongodb.client.test.CollectionHelper; +import org.bson.BsonDocument; +import org.bson.BsonString; +import org.bson.codecs.BsonDocumentCodec; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.io.File; +import java.io.IOException; +import java.net.URISyntaxException; +import java.util.Arrays; +import java.util.Base64; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import static com.mongodb.ClusterFixture.isNotAtLeastJava8; +import static com.mongodb.ClusterFixture.isStandalone; +import static com.mongodb.ClusterFixture.serverVersionAtLeast; +import static com.mongodb.client.Fixture.getDefaultDatabaseName; +import static com.mongodb.client.Fixture.getMongoClient; +import static com.mongodb.client.Fixture.getMongoClientSettingsBuilder; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeFalse; +import static org.junit.Assume.assumeTrue; +import static util.JsonPoweredTestHelper.getTestDocument; + +@RunWith(Parameterized.class) +public class ClientSideEncryptionSessionTest { + private static final String COLLECTION_NAME = "clientSideEncryptionSessionsTest"; + private MongoClient client, clientEncrypted; + private final boolean useTransaction; + + @Parameterized.Parameters(name = "useTransaction: {0}") + public static Collection data() { + return Arrays.asList(new Object[]{true}, new Object[]{false}); + } + + public ClientSideEncryptionSessionTest(final boolean useTransaction) { + this.useTransaction = useTransaction; + } + + @Before + public void setUp() throws IOException, URISyntaxException { + assumeFalse(isNotAtLeastJava8()); + assumeTrue(serverVersionAtLeast(4, 2)); + assumeFalse(isStandalone()); + + /* Step 1: get unencrypted client and recreate keys collection */ + client = getMongoClient(); + MongoDatabase keyvaultDatabase = client.getDatabase("keyvault"); + MongoCollection datakeys = keyvaultDatabase.getCollection("datakeys", BsonDocument.class) + .withWriteConcern(WriteConcern.MAJORITY); + datakeys.drop(); + datakeys.insertOne(bsonDocumentFromPath("external-key.json")); + + /* Step 2: create encryption objects. */ + Map> kmsProviders = new HashMap>(); + Map localMasterkey = new HashMap(); + Map schemaMap = new HashMap(); + + byte[] localMasterkeyBytes = Base64.getDecoder().decode("Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBM" + + "UN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk"); + localMasterkey.put("key", localMasterkeyBytes); + kmsProviders.put("local", localMasterkey); + schemaMap.put(getDefaultDatabaseName() + "." + COLLECTION_NAME, bsonDocumentFromPath("external-schema.json")); + + AutoEncryptionSettings autoEncryptionSettings = AutoEncryptionSettings.builder() + .keyVaultNamespace("keyvault.datakeys") + .kmsProviders(kmsProviders) + .schemaMap(schemaMap).build(); + + MongoClientSettings clientSettings = getMongoClientSettingsBuilder() + .autoEncryptionSettings(autoEncryptionSettings) + .build(); + clientEncrypted = MongoClients.create(clientSettings); + + CollectionHelper collectionHelper = + new CollectionHelper(new BsonDocumentCodec(), new MongoNamespace(getDefaultDatabaseName(), COLLECTION_NAME)); + collectionHelper.drop(); + collectionHelper.create(); + } + + @After + public void after() { + if (clientEncrypted != null) { + try { + clientEncrypted.close(); + } catch (Exception e) { + // ignore + } + } + } + + @Test + public void testWithExplicitSession() { + BsonString unencryptedValue = new BsonString("test"); + + ClientSession clientSession = clientEncrypted.startSession(); + try { + if (useTransaction) { + clientSession.startTransaction(); + } + MongoCollection encryptedCollection = clientEncrypted.getDatabase(getDefaultDatabaseName()) + .getCollection(COLLECTION_NAME, BsonDocument.class); + encryptedCollection.insertOne(clientSession, new BsonDocument().append("encrypted", unencryptedValue)); + BsonDocument unencryptedDocument = encryptedCollection.find(clientSession).first(); + assertEquals(unencryptedValue, unencryptedDocument.getString("encrypted")); + if (useTransaction) { + clientSession.commitTransaction(); + } + } finally { + clientSession.close(); + } + + MongoCollection unencryptedCollection = client.getDatabase(getDefaultDatabaseName()) + .getCollection(COLLECTION_NAME, BsonDocument.class); + BsonDocument encryptedDocument = unencryptedCollection.find().first(); + assertTrue(encryptedDocument.isBinary("encrypted")); + assertEquals(6, encryptedDocument.getBinary("encrypted").getType()); + } + + + private static BsonDocument bsonDocumentFromPath(final String path) throws IOException, URISyntaxException { + return getTestDocument(new File(ClientSideEncryptionSessionTest.class + .getResource("/client-side-encryption-external/" + path).toURI())); + } +} From f015023db246b8cec15e7bef711c9d8be3e02828 Mon Sep 17 00:00:00 2001 From: John Stewart Date: Tue, 12 May 2020 22:08:47 -0400 Subject: [PATCH 32/48] Avoid calling batch cursor when the cursor is closed JAVA-3710 --- .../main/com/mongodb/async/client/AbstractSubscription.java | 5 ++++- .../com/mongodb/async/client/MongoIterableSubscription.java | 2 +- ...ningSingleResultCallbackSubscriptionSpecification.groovy | 6 ++++-- .../client/MongoIterableSubscriptionSpecification.groovy | 5 +++-- .../SingleResultCallbackSubscriptionSpecification.groovy | 3 ++- 5 files changed, 14 insertions(+), 7 deletions(-) diff --git a/driver-async/src/main/com/mongodb/async/client/AbstractSubscription.java b/driver-async/src/main/com/mongodb/async/client/AbstractSubscription.java index ea48e2af044..2c5705ff5a8 100644 --- a/driver-async/src/main/com/mongodb/async/client/AbstractSubscription.java +++ b/driver-async/src/main/com/mongodb/async/client/AbstractSubscription.java @@ -69,6 +69,9 @@ public void request(final long n) { if (n < 1) { throw new IllegalArgumentException("Number requested must be > 0: " + n); } + if (isTerminated()) { + return; + } boolean requestData = false; synchronized (this) { @@ -132,7 +135,7 @@ void onError(final Throwable t) { throw MongoException.fromThrowableNonNull(t1); } } else { - throw MongoException.fromThrowableNonNull(t); + throw new MongoException("Subscription has already been terminated", t); } } diff --git a/driver-async/src/main/com/mongodb/async/client/MongoIterableSubscription.java b/driver-async/src/main/com/mongodb/async/client/MongoIterableSubscription.java index 0166cf0683d..162a7b75f1c 100644 --- a/driver-async/src/main/com/mongodb/async/client/MongoIterableSubscription.java +++ b/driver-async/src/main/com/mongodb/async/client/MongoIterableSubscription.java @@ -78,7 +78,7 @@ void postTerminate() { void requestMoreData() { boolean mustRead = false; synchronized (this) { - if (!isReading && !isTerminated() && batchCursor != null) { + if (!isReading && !isTerminated() && batchCursor != null && !batchCursor.isClosed()) { isReading = true; mustRead = true; } diff --git a/driver-async/src/test/unit/com/mongodb/async/client/FlatteningSingleResultCallbackSubscriptionSpecification.groovy b/driver-async/src/test/unit/com/mongodb/async/client/FlatteningSingleResultCallbackSubscriptionSpecification.groovy index c19af40aec0..7612651d4e1 100644 --- a/driver-async/src/test/unit/com/mongodb/async/client/FlatteningSingleResultCallbackSubscriptionSpecification.groovy +++ b/driver-async/src/test/unit/com/mongodb/async/client/FlatteningSingleResultCallbackSubscriptionSpecification.groovy @@ -279,7 +279,8 @@ class FlatteningSingleResultCallbackSubscriptionSpecification extends Specificat then: def ex = thrown(MongoException) - ex.message == 'exception calling onComplete' + ex.message == 'Subscription has already been terminated' + ex.cause.cause.message == 'exception calling onComplete' observer.assertTerminalEvent() observer.assertNoErrors() } @@ -312,7 +313,8 @@ class FlatteningSingleResultCallbackSubscriptionSpecification extends Specificat then: def ex = thrown(MongoException) - ex.message == 'exception calling onError' + ex.message == 'Subscription has already been terminated' + ex.cause.cause.message == 'exception calling onError' observer.assertTerminalEvent() observer.assertErrored() } diff --git a/driver-async/src/test/unit/com/mongodb/async/client/MongoIterableSubscriptionSpecification.groovy b/driver-async/src/test/unit/com/mongodb/async/client/MongoIterableSubscriptionSpecification.groovy index 18a6600d17b..853a11517a4 100644 --- a/driver-async/src/test/unit/com/mongodb/async/client/MongoIterableSubscriptionSpecification.groovy +++ b/driver-async/src/test/unit/com/mongodb/async/client/MongoIterableSubscriptionSpecification.groovy @@ -464,7 +464,7 @@ class MongoIterableSubscriptionSpecification extends Specification { then: def ex = thrown(MongoException) - ex.message == 'exception calling onComplete' + ex.message == 'Subscription has already been terminated' observer.assertTerminalEvent() observer.assertNoErrors() } @@ -497,7 +497,8 @@ class MongoIterableSubscriptionSpecification extends Specification { then: def ex = thrown(MongoException) - ex.message == 'exception calling onError' + ex.message == 'Subscription has already been terminated' + ex.cause.cause.message == 'exception calling onError' observer.assertTerminalEvent() observer.assertErrored() } diff --git a/driver-async/src/test/unit/com/mongodb/async/client/SingleResultCallbackSubscriptionSpecification.groovy b/driver-async/src/test/unit/com/mongodb/async/client/SingleResultCallbackSubscriptionSpecification.groovy index cb31f6195fe..9a29bdc6716 100644 --- a/driver-async/src/test/unit/com/mongodb/async/client/SingleResultCallbackSubscriptionSpecification.groovy +++ b/driver-async/src/test/unit/com/mongodb/async/client/SingleResultCallbackSubscriptionSpecification.groovy @@ -318,7 +318,8 @@ class SingleResultCallbackSubscriptionSpecification extends Specification { def ex = thrown(MongoException) observer.assertNoErrors() observer.assertTerminalEvent() - ex.message == 'exception calling onComplete' + ex.message == 'Subscription has already been terminated' + ex.cause.cause.message == 'exception calling onComplete' } def 'should throw the exception if calling onError raises one'() { From 685befac69291353ce5a5999c30b8988a70768f9 Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Wed, 8 Jul 2020 10:28:21 +0100 Subject: [PATCH 33/48] Bump version to 3.12.6 --- build.gradle | 2 +- docs/reference/content/bson/installation-guide.md | 2 +- .../content/driver-async/getting-started/installation.md | 2 +- .../content/driver/getting-started/installation.md | 8 ++++---- docs/reference/content/driver/tutorials/jndi.md | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/build.gradle b/build.gradle index 9feee9bbfa9..7a13b64add4 100644 --- a/build.gradle +++ b/build.gradle @@ -54,7 +54,7 @@ def javaCodeCheckedProjects = subprojects.findAll { !['util', 'mongo-java-driver configure(coreProjects) { evaluationDependsOn(':util') group = 'org.mongodb' - version = '3.12.6-SNAPSHOT' + version = '3.12.6' repositories { mavenLocal() diff --git a/docs/reference/content/bson/installation-guide.md b/docs/reference/content/bson/installation-guide.md index 28a9a7d953f..df794a89fbf 100644 --- a/docs/reference/content/bson/installation-guide.md +++ b/docs/reference/content/bson/installation-guide.md @@ -22,4 +22,4 @@ This library comprehensively supports [BSON](http://www.bsonspec.org), the data storage and network transfer format that MongoDB uses for "documents". BSON is short for Binary [JSON](http://json.org/), is a binary-encoded serialization of JSON-like documents. -{{< install artifactId="bson" version="3.12.5" >}} +{{< install artifactId="bson" version="3.12.6" >}} diff --git a/docs/reference/content/driver-async/getting-started/installation.md b/docs/reference/content/driver-async/getting-started/installation.md index 85fb6193b9a..31530a7fa2e 100644 --- a/docs/reference/content/driver-async/getting-started/installation.md +++ b/docs/reference/content/driver-async/getting-started/installation.md @@ -24,4 +24,4 @@ When TLS/SSL is enabled, the MongoDB Async Driver requires either [Netty](http:/ The MongoDB Async Driver provides asynchronous API that can leverage either Netty or Java 7's AsynchronousSocketChannel for fast and non-blocking I/O. -{{< install artifactId="mongodb-driver-async" version="3.12.5" dependencies="true">}} +{{< install artifactId="mongodb-driver-async" version="3.12.6" dependencies="true">}} diff --git a/docs/reference/content/driver/getting-started/installation.md b/docs/reference/content/driver/getting-started/installation.md index 49205239d91..6616918b04c 100644 --- a/docs/reference/content/driver/getting-started/installation.md +++ b/docs/reference/content/driver/getting-started/installation.md @@ -31,7 +31,7 @@ The `mongodb-driver-sync` artifact is a valid OSGi bundle whose symbolic name is {{% /note %}} -{{< install artifactId="mongodb-driver-sync" version="3.12.5" dependencies="true">}} +{{< install artifactId="mongodb-driver-sync" version="3.12.6" dependencies="true">}} ## MongoDB Driver Legacy @@ -44,7 +44,7 @@ While not deprecated, we recommend that new applications depend on the `mongodb- {{% /note %}} -{{< install artifactId="mongodb-driver-legacy" version="3.12.5" dependencies="true">}} +{{< install artifactId="mongodb-driver-legacy" version="3.12.6" dependencies="true">}} ## MongoDB Driver @@ -61,7 +61,7 @@ This module is deprecated and will no longer be published in the next major rele {{% /note %}} -{{< install artifactId="mongodb-driver" version="3.12.5" dependencies="true">}} +{{< install artifactId="mongodb-driver" version="3.12.6" dependencies="true">}} ## Uber Jar (Legacy) @@ -81,4 +81,4 @@ This module is deprecated and will no longer be published in the next major rele {{% /note %}} -{{< install artifactId="mongo-java-driver" version="3.12.5">}} +{{< install artifactId="mongo-java-driver" version="3.12.6">}} diff --git a/docs/reference/content/driver/tutorials/jndi.md b/docs/reference/content/driver/tutorials/jndi.md index 62f6faefe19..880aabee23e 100644 --- a/docs/reference/content/driver/tutorials/jndi.md +++ b/docs/reference/content/driver/tutorials/jndi.md @@ -28,7 +28,7 @@ The configuration of the `MongoClientFactory` differs depending on the applicati - + From ce8aef592c9aba8da93208241a3a885fa386387f Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Wed, 8 Jul 2020 10:47:34 +0100 Subject: [PATCH 34/48] Bump version to 3.12.7-SNAPSHOT --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 7a13b64add4..a5222997e1c 100644 --- a/build.gradle +++ b/build.gradle @@ -54,7 +54,7 @@ def javaCodeCheckedProjects = subprojects.findAll { !['util', 'mongo-java-driver configure(coreProjects) { evaluationDependsOn(':util') group = 'org.mongodb' - version = '3.12.6' + version = '3.12.7-SNAPSHOT' repositories { mavenLocal() From e0572db771b682f6c50006965f64c5de1f17a1b3 Mon Sep 17 00:00:00 2001 From: Jeff Yemin Date: Wed, 15 Jul 2020 08:19:36 -0400 Subject: [PATCH 35/48] Support explicit and implicit sessions on standalone servers JAVA-3745 --- .../async/client/ClientSessionHelper.java | 8 +- .../MongoClientSessionSpecification.groovy | 52 ++--- .../src/test/resources/sessions/README.rst | 84 ++++++++ .../sessions/dirty-session-errors.json | 167 +--------------- .../resources/sessions/server-support.json | 181 ++++++++++++++++++ .../MongoClientSessionSpecification.groovy | 56 ++---- .../client/internal/MongoClientDelegate.java | 4 +- .../MongoClientSessionSpecification.groovy | 54 +----- 8 files changed, 302 insertions(+), 304 deletions(-) create mode 100644 driver-core/src/test/resources/sessions/README.rst create mode 100644 driver-core/src/test/resources/sessions/server-support.json diff --git a/driver-async/src/main/com/mongodb/async/client/ClientSessionHelper.java b/driver-async/src/main/com/mongodb/async/client/ClientSessionHelper.java index 956cd36c2de..1dcfecb2f3b 100644 --- a/driver-async/src/main/com/mongodb/async/client/ClientSessionHelper.java +++ b/driver-async/src/main/com/mongodb/async/client/ClientSessionHelper.java @@ -21,10 +21,8 @@ import com.mongodb.async.SingleResultCallback; import com.mongodb.connection.ClusterConnectionMode; import com.mongodb.connection.ClusterDescription; -import com.mongodb.connection.ClusterType; import com.mongodb.connection.Server; import com.mongodb.connection.ServerDescription; -import com.mongodb.connection.ServerType; import com.mongodb.internal.session.ServerSessionPool; import com.mongodb.lang.Nullable; import com.mongodb.selector.ServerSelector; @@ -61,8 +59,7 @@ void createClientSession(final ClientSessionOptions options, final OperationExec } else { ClusterDescription clusterDescription = mongoClient.getCluster().getCurrentDescription(); if (!getServerDescriptionListToConsiderForSessionSupport(clusterDescription).isEmpty() - && clusterDescription.getLogicalSessionTimeoutMinutes() != null - && clusterDescription.getType() != ClusterType.STANDALONE) { + && clusterDescription.getLogicalSessionTimeoutMinutes() != null) { callback.onResult(createClientSession(options, executor), null); } else { mongoClient.getCluster().selectServerAsync(new ServerSelector() { @@ -75,8 +72,7 @@ public List select(final ClusterDescription clusterDescriptio public void onResult(final Server server, final Throwable t) { if (t != null) { callback.onResult(null, null); - } else if (server.getDescription().getLogicalSessionTimeoutMinutes() == null - || server.getDescription().getType() == ServerType.STANDALONE) { + } else if (server.getDescription().getLogicalSessionTimeoutMinutes() == null) { callback.onResult(null, null); } else { callback.onResult(createClientSession(options, executor), null); diff --git a/driver-async/src/test/functional/com/mongodb/async/client/MongoClientSessionSpecification.groovy b/driver-async/src/test/functional/com/mongodb/async/client/MongoClientSessionSpecification.groovy index 693f158b713..37f45cb9370 100644 --- a/driver-async/src/test/functional/com/mongodb/async/client/MongoClientSessionSpecification.groovy +++ b/driver-async/src/test/functional/com/mongodb/async/client/MongoClientSessionSpecification.groovy @@ -39,7 +39,6 @@ import spock.lang.IgnoreIf import java.util.concurrent.TimeUnit import static com.mongodb.ClusterFixture.isDiscoverableReplicaSet -import static com.mongodb.ClusterFixture.isStandalone import static com.mongodb.ClusterFixture.serverVersionAtLeast import static com.mongodb.async.client.Fixture.getMongoClient import static com.mongodb.async.client.TestHelper.run @@ -48,22 +47,13 @@ class MongoClientSessionSpecification extends FunctionalSpecification { def 'should throw IllegalArgumentException if options are null'() { when: - Fixture.getMongoClient().startSession(null, Stub(SingleResultCallback)) + getMongoClient().startSession(null, Stub(SingleResultCallback)) then: thrown(IllegalArgumentException) } - @IgnoreIf({ serverVersionAtLeast(3, 6) && !isStandalone() }) - def 'should throw MongoClientException starting a session when sessions are not supported'() { - when: - startSession(ClientSessionOptions.builder().build()) - - then: - thrown(MongoClientException) - } - - @IgnoreIf({ !serverVersionAtLeast(3, 6) || isStandalone() }) + @IgnoreIf({ !serverVersionAtLeast(3, 6) }) def 'should create session with correct defaults'() { when: def options = ClientSessionOptions.builder().build() @@ -71,7 +61,7 @@ class MongoClientSessionSpecification extends FunctionalSpecification { then: clientSession != null - clientSession.getOriginator() == Fixture.getMongoClient() + clientSession.getOriginator() == getMongoClient() clientSession.isCausallyConsistent() clientSession.getOptions() == ClientSessionOptions.builder() .defaultTransactionOptions(TransactionOptions.builder() @@ -88,7 +78,7 @@ class MongoClientSessionSpecification extends FunctionalSpecification { clientSession.close() } - @IgnoreIf({ !serverVersionAtLeast(3, 6) || isStandalone() }) + @IgnoreIf({ !serverVersionAtLeast(3, 6) }) def 'cluster time should advance'() { given: def firstOperationTime = new BsonTimestamp(42, 1) @@ -132,7 +122,7 @@ class MongoClientSessionSpecification extends FunctionalSpecification { clientSession.close() } - @IgnoreIf({ !serverVersionAtLeast(3, 6) || isStandalone() }) + @IgnoreIf({ !serverVersionAtLeast(3, 6) }) def 'operation time should advance'() { given: def firstOperationTime = new BsonTimestamp(42, 1) @@ -173,7 +163,7 @@ class MongoClientSessionSpecification extends FunctionalSpecification { clientSession.close() } - @IgnoreIf({ !serverVersionAtLeast(3, 6) || isStandalone() }) + @IgnoreIf({ !serverVersionAtLeast(3, 6) }) def 'methods that use the session should throw if the session is closed'() { given: def options = ClientSessionOptions.builder().build() @@ -202,7 +192,7 @@ class MongoClientSessionSpecification extends FunctionalSpecification { clientSession.close() } - @IgnoreIf({ !serverVersionAtLeast(3, 6) || isStandalone() }) + @IgnoreIf({ !serverVersionAtLeast(3, 6) }) def 'informational methods should not throw if the session is closed'() { given: def options = ClientSessionOptions.builder().build() @@ -219,7 +209,7 @@ class MongoClientSessionSpecification extends FunctionalSpecification { true } - @IgnoreIf({ !serverVersionAtLeast(3, 6) || isStandalone() }) + @IgnoreIf({ !serverVersionAtLeast(3, 6) }) def 'should apply causally consistent session option to client session'() { when: def clientSession = startSession(ClientSessionOptions.builder().causallyConsistent(causallyConsistent).build()) @@ -235,7 +225,7 @@ class MongoClientSessionSpecification extends FunctionalSpecification { causallyConsistent << [true, false] } - @IgnoreIf({ !serverVersionAtLeast(3, 6) || isStandalone() }) + @IgnoreIf({ !serverVersionAtLeast(3, 6) }) def 'client session should have server session with valid identifier'() { given: def clientSession = startSession(ClientSessionOptions.builder().build()) @@ -254,7 +244,7 @@ class MongoClientSessionSpecification extends FunctionalSpecification { clientSession.close() } - @IgnoreIf({ !serverVersionAtLeast(3, 6) || isStandalone() }) + @IgnoreIf({ !serverVersionAtLeast(3, 6) }) def 'should use a default session'() { given: def commandListener = new TestCommandListener() @@ -273,25 +263,7 @@ class MongoClientSessionSpecification extends FunctionalSpecification { client?.close() } - @IgnoreIf({ serverVersionAtLeast(3, 6) && !isStandalone() }) - def 'should not use a default session when sessions are not supported'() { - given: - def commandListener = new TestCommandListener() - def options = Fixture.getMongoClientBuilderFromConnectionString().addCommandListener(commandListener).build() - def client = MongoClients.create(options) - - when: - run(client.getDatabase('admin').&runCommand, new BsonDocument('ping', new BsonInt32(1))) - - then: - def pingCommandStartedEvent = commandListener.events.get(0) - !(pingCommandStartedEvent as CommandStartedEvent).command.containsKey('lsid') - - cleanup: - client?.close() - } - - @IgnoreIf({ !serverVersionAtLeast(3, 6) || isStandalone() }) + @IgnoreIf({ !serverVersionAtLeast(3, 6) }) def 'should throw exception if unacknowledged write used with explicit session'() { given: def session = run(getMongoClient().&startSession) @@ -334,7 +306,7 @@ class MongoClientSessionSpecification extends FunctionalSpecification { // This test is inherently racy as it's possible that the server _does_ replicate fast enough and therefore the test passes anyway // even if causal consistency was not actually in effect. For that reason the test iterates a number of times in order to increase // confidence that it's really causal consistency that is causing the test to succeed - @IgnoreIf({ !serverVersionAtLeast(3, 6) || isStandalone() }) + @IgnoreIf({ !serverVersionAtLeast(3, 6) }) @Category(Slow) def 'should find inserted document on a secondary when causal consistency is enabled'() { given: diff --git a/driver-core/src/test/resources/sessions/README.rst b/driver-core/src/test/resources/sessions/README.rst new file mode 100644 index 00000000000..3ed7eea96a4 --- /dev/null +++ b/driver-core/src/test/resources/sessions/README.rst @@ -0,0 +1,84 @@ +==================== +Driver Session Tests +==================== + +.. contents:: + +---- + +Introduction +============ + +The YAML and JSON files in this directory are platform-independent tests that +drivers can use to prove their conformance to the Driver Sessions Spec. They are +designed with the intention of sharing most test-runner code with the +Transactions spec tests. + +Several prose tests, which are not easily expressed in YAML, are also presented +in the Driver Sessions Spec. Those tests will need to be manually implemented +by each driver. + +Test Format +=========== + +The same as the `Transactions Spec Test format +<../../transactions/tests/README.rst#test-format>`_. + +Special Test Operations +``````````````````````` + +Certain operations that appear in the "operations" array do not correspond to +API methods but instead represent special test operations. Such operations are +defined on the "testRunner" object and are documented in the +`Transactions Spec Test +<../../transactions/tests/README.rst#special-test-operations>`_. +Additional, session test specific operations are documented here: + +assertDifferentLsidOnLastTwoCommands +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The "assertDifferentLsidOnLastTwoCommands" operation instructs the test runner +to assert that the last two command started events from the test's MongoClient +have different "lsid" fields. This assertion is used to ensure that dirty +server sessions are discarded from the pool:: + + - name: assertDifferentLsidOnLastTwoCommands + object: testRunner + +assertSameLsidOnLastTwoCommands +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The "assertSameLsidOnLastTwoCommands" operation instructs the test runner +to assert that the last two command started events from the test's MongoClient +have the same "lsid" field. This assertion is used to ensure that non-dirty +server sessions are not discarded from the pool:: + + - name: assertSameLsidOnLastTwoCommands + object: testRunner + +assertSessionDirty +~~~~~~~~~~~~~~~~~~ + +The "assertSessionDirty" operation instructs the test runner to assert that +the given session is marked dirty:: + + - name: assertSessionDirty + object: testRunner + arguments: + session: session0 + +assertSessionNotDirty +~~~~~~~~~~~~~~~~~~~~~ + +The "assertSessionNotDirty" operation instructs the test runner to assert that +the given session is *not* marked dirty:: + + - name: assertSessionNotDirty + object: testRunner + arguments: + session: session0 + +Changelog +========= + +:2019-05-15: Initial version. diff --git a/driver-core/src/test/resources/sessions/dirty-session-errors.json b/driver-core/src/test/resources/sessions/dirty-session-errors.json index a964b208c51..77f71c7623e 100644 --- a/driver-core/src/test/resources/sessions/dirty-session-errors.json +++ b/driver-core/src/test/resources/sessions/dirty-session-errors.json @@ -21,171 +21,6 @@ } ], "tests": [ - { - "description": "Clean explicit session is not discarded", - "operations": [ - { - "name": "assertSessionNotDirty", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 2 - } - }, - "result": { - "insertedId": 2 - } - }, - { - "name": "assertSessionNotDirty", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "endSession", - "object": "session0" - }, - { - "name": "find", - "object": "collection", - "arguments": { - "filter": { - "_id": -1 - } - }, - "result": [] - }, - { - "name": "assertSameLsidOnLastTwoCommands", - "object": "testRunner" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 2 - } - ], - "ordered": true, - "lsid": "session0" - }, - "command_name": "insert", - "database_name": "session-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "test", - "filter": { - "_id": -1 - }, - "lsid": "session0" - }, - "command_name": "find", - "database_name": "session-tests" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "Clean implicit session is not discarded", - "operations": [ - { - "name": "insertOne", - "object": "collection", - "arguments": { - "document": { - "_id": 2 - } - }, - "result": { - "insertedId": 2 - } - }, - { - "name": "find", - "object": "collection", - "arguments": { - "filter": { - "_id": -1 - } - }, - "result": [] - }, - { - "name": "assertSameLsidOnLastTwoCommands", - "object": "testRunner" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 2 - } - ], - "ordered": true - }, - "command_name": "insert", - "database_name": "session-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "test", - "filter": { - "_id": -1 - } - }, - "command_name": "find", - "database_name": "session-tests" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, { "description": "Dirty explicit session is discarded", "clientOptions": { @@ -833,4 +668,4 @@ } } ] -} \ No newline at end of file +} diff --git a/driver-core/src/test/resources/sessions/server-support.json b/driver-core/src/test/resources/sessions/server-support.json new file mode 100644 index 00000000000..967c9143fd9 --- /dev/null +++ b/driver-core/src/test/resources/sessions/server-support.json @@ -0,0 +1,181 @@ +{ + "runOn": [ + { + "minServerVersion": "3.6.0" + } + ], + "database_name": "session-tests", + "collection_name": "test", + "data": [ + { + "_id": 1 + } + ], + "tests": [ + { + "description": "Server supports explicit sessions", + "operations": [ + { + "name": "assertSessionNotDirty", + "object": "testRunner", + "arguments": { + "session": "session0" + } + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "session": "session0", + "document": { + "_id": 2 + } + }, + "result": { + "insertedId": 2 + } + }, + { + "name": "assertSessionNotDirty", + "object": "testRunner", + "arguments": { + "session": "session0" + } + }, + { + "name": "endSession", + "object": "session0" + }, + { + "name": "find", + "object": "collection", + "arguments": { + "filter": { + "_id": -1 + } + }, + "result": [] + }, + { + "name": "assertSameLsidOnLastTwoCommands", + "object": "testRunner" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 2 + } + ], + "ordered": true, + "lsid": "session0" + }, + "command_name": "insert", + "database_name": "session-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "test", + "filter": { + "_id": -1 + }, + "lsid": "session0" + }, + "command_name": "find", + "database_name": "session-tests" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + } + }, + { + "description": "Server supports implicit sessions", + "operations": [ + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "_id": 2 + } + }, + "result": { + "insertedId": 2 + } + }, + { + "name": "find", + "object": "collection", + "arguments": { + "filter": { + "_id": -1 + } + }, + "result": [] + }, + { + "name": "assertSameLsidOnLastTwoCommands", + "object": "testRunner" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 2 + } + ], + "ordered": true + }, + "command_name": "insert", + "database_name": "session-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "test", + "filter": { + "_id": -1 + } + }, + "command_name": "find", + "database_name": "session-tests" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + } + } + ] +} diff --git a/driver-legacy/src/test/functional/com/mongodb/MongoClientSessionSpecification.groovy b/driver-legacy/src/test/functional/com/mongodb/MongoClientSessionSpecification.groovy index 544b951a320..856111aef00 100644 --- a/driver-legacy/src/test/functional/com/mongodb/MongoClientSessionSpecification.groovy +++ b/driver-legacy/src/test/functional/com/mongodb/MongoClientSessionSpecification.groovy @@ -34,7 +34,6 @@ import static Fixture.getDefaultDatabaseName import static Fixture.getMongoClientURI import static com.mongodb.ClusterFixture.isAuthenticated import static com.mongodb.ClusterFixture.isDiscoverableReplicaSet -import static com.mongodb.ClusterFixture.isStandalone import static com.mongodb.ClusterFixture.serverVersionAtLeast import static com.mongodb.Fixture.getMongoClient import static com.mongodb.MongoCredential.createCredential @@ -49,22 +48,7 @@ class MongoClientSessionSpecification extends FunctionalSpecification { thrown(IllegalArgumentException) } - @IgnoreIf({ serverVersionAtLeast(3, 6) && !isStandalone() }) - def 'should throw MongoClientException starting a session when sessions are not supported'() { - when: - getMongoClient().startSession() - - then: - thrown(MongoClientException) - - when: - getMongoClient().startSession(ClientSessionOptions.builder().build()) - - then: - thrown(MongoClientException) - } - - @IgnoreIf({ !serverVersionAtLeast(3, 6) || isStandalone() }) + @IgnoreIf({ !serverVersionAtLeast(3, 6) }) def 'should create session with correct defaults'() { given: def clientSession = getMongoClient().startSession() @@ -87,7 +71,7 @@ class MongoClientSessionSpecification extends FunctionalSpecification { clientSession?.close() } - @IgnoreIf({ !serverVersionAtLeast(4, 0) || isStandalone() }) + @IgnoreIf({ !serverVersionAtLeast(4, 0) }) def 'should use mutated client properties for default transaction options'() { given: def originalWriteConcern = getMongoClient().getWriteConcern() @@ -111,7 +95,7 @@ class MongoClientSessionSpecification extends FunctionalSpecification { clientSession?.close() } - @IgnoreIf({ !serverVersionAtLeast(3, 6) || isStandalone() }) + @IgnoreIf({ !serverVersionAtLeast(3, 6) }) def 'cluster time should advance'() { given: def firstOperationTime = new BsonTimestamp(42, 1) @@ -155,7 +139,7 @@ class MongoClientSessionSpecification extends FunctionalSpecification { clientSession?.close() } - @IgnoreIf({ !serverVersionAtLeast(3, 6) || isStandalone() }) + @IgnoreIf({ !serverVersionAtLeast(3, 6) }) def 'operation time should advance'() { given: def firstOperationTime = new BsonTimestamp(42, 1) @@ -196,7 +180,7 @@ class MongoClientSessionSpecification extends FunctionalSpecification { clientSession?.close() } - @IgnoreIf({ !serverVersionAtLeast(3, 6) || isStandalone() }) + @IgnoreIf({ !serverVersionAtLeast(3, 6) }) def 'methods that use the session should throw if the session is closed'() { given: def options = ClientSessionOptions.builder().build() @@ -225,7 +209,7 @@ class MongoClientSessionSpecification extends FunctionalSpecification { clientSession?.close() } - @IgnoreIf({ !serverVersionAtLeast(3, 6) || isStandalone() }) + @IgnoreIf({ !serverVersionAtLeast(3, 6) }) def 'informational methods should not throw if the session is closed'() { given: def options = ClientSessionOptions.builder().build() @@ -245,7 +229,7 @@ class MongoClientSessionSpecification extends FunctionalSpecification { clientSession?.close() } - @IgnoreIf({ !serverVersionAtLeast(3, 6) || isStandalone() }) + @IgnoreIf({ !serverVersionAtLeast(3, 6) }) def 'should apply causally consistent session option to client session'() { when: def clientSession = getMongoClient().startSession(ClientSessionOptions.builder() @@ -263,7 +247,7 @@ class MongoClientSessionSpecification extends FunctionalSpecification { causallyConsistent << [true, false] } - @IgnoreIf({ !serverVersionAtLeast(3, 6) || isStandalone() }) + @IgnoreIf({ !serverVersionAtLeast(3, 6) }) def 'client session should have server session with valid identifier'() { given: def clientSession = getMongoClient().startSession(ClientSessionOptions.builder().build()) @@ -282,7 +266,7 @@ class MongoClientSessionSpecification extends FunctionalSpecification { clientSession?.close() } - @IgnoreIf({ !serverVersionAtLeast(3, 6) || isStandalone() }) + @IgnoreIf({ !serverVersionAtLeast(3, 6) }) def 'should use a default session'() { given: def commandListener = new TestCommandListener() @@ -302,24 +286,6 @@ class MongoClientSessionSpecification extends FunctionalSpecification { client?.close() } - @IgnoreIf({ serverVersionAtLeast(3, 6) && !isStandalone() }) - def 'should not use a default session when sessions are not supported'() { - given: - def commandListener = new TestCommandListener() - def optionsBuilder = MongoClientOptions.builder() - .addCommandListener(commandListener) - def client = new MongoClient(getMongoClientURI(optionsBuilder)) - - when: - client.getDatabase('admin').runCommand(new BsonDocument('ping', new BsonInt32(1))) - - then: - def pingCommandStartedEvent = commandListener.events.get(0) - !(pingCommandStartedEvent as CommandStartedEvent).command.containsKey('lsid') - cleanup: - client?.close() - } - // This test attempts attempts to demonstrate that causal consistency works correctly by inserting a document and then immediately // searching for that document on a secondary by its _id and failing the test if the document is not found. Without causal consistency // enabled the expectation is that eventually that test would fail since generally the find will execute on the secondary before @@ -327,7 +293,7 @@ class MongoClientSessionSpecification extends FunctionalSpecification { // This test is inherently racy as it's possible that the server _does_ replicate fast enough and therefore the test passes anyway // even if causal consistency was not actually in effect. For that reason the test iterates a number of times in order to increase // confidence that it's really causal consistency that is causing the test to succeed - @IgnoreIf({ !serverVersionAtLeast(3, 6) || isStandalone() }) + @IgnoreIf({ !serverVersionAtLeast(3, 6) }) @Category(Slow) def 'should find inserted document on a secondary when causal consistency is enabled'() { given: @@ -410,7 +376,7 @@ class MongoClientSessionSpecification extends FunctionalSpecification { client?.close() } - @IgnoreIf({ !serverVersionAtLeast(3, 6) || isStandalone() }) + @IgnoreIf({ !serverVersionAtLeast(3, 6) }) def 'should throw exception if unacknowledged write used with explicit session'() { given: def session = getMongoClient().startSession() diff --git a/driver-sync/src/main/com/mongodb/client/internal/MongoClientDelegate.java b/driver-sync/src/main/com/mongodb/client/internal/MongoClientDelegate.java index 254ce3e55d4..41837905dbe 100644 --- a/driver-sync/src/main/com/mongodb/client/internal/MongoClientDelegate.java +++ b/driver-sync/src/main/com/mongodb/client/internal/MongoClientDelegate.java @@ -37,7 +37,6 @@ import com.mongodb.connection.Cluster; import com.mongodb.connection.ClusterConnectionMode; import com.mongodb.connection.ClusterDescription; -import com.mongodb.connection.ClusterType; import com.mongodb.connection.ServerDescription; import com.mongodb.internal.binding.ClusterAwareReadWriteBinding; import com.mongodb.internal.session.ServerSessionPool; @@ -102,8 +101,7 @@ public ClientSession createClientSession(final ClientSessionOptions options, fin ClusterDescription connectedClusterDescription = getConnectedClusterDescription(); - if (connectedClusterDescription.getType() == ClusterType.STANDALONE - || connectedClusterDescription.getLogicalSessionTimeoutMinutes() == null) { + if (connectedClusterDescription.getLogicalSessionTimeoutMinutes() == null) { return null; } else { ClientSessionOptions mergedOptions = ClientSessionOptions.builder(options) diff --git a/driver-sync/src/test/functional/com/mongodb/client/MongoClientSessionSpecification.groovy b/driver-sync/src/test/functional/com/mongodb/client/MongoClientSessionSpecification.groovy index eddd26ae0b6..80360ccc353 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/MongoClientSessionSpecification.groovy +++ b/driver-sync/src/test/functional/com/mongodb/client/MongoClientSessionSpecification.groovy @@ -38,7 +38,6 @@ import spock.lang.IgnoreIf import java.util.concurrent.TimeUnit import static com.mongodb.ClusterFixture.isDiscoverableReplicaSet -import static com.mongodb.ClusterFixture.isStandalone import static com.mongodb.ClusterFixture.serverVersionAtLeast import static com.mongodb.client.Fixture.getDefaultDatabaseName import static com.mongodb.client.Fixture.getMongoClient @@ -54,22 +53,7 @@ class MongoClientSessionSpecification extends FunctionalSpecification { thrown(IllegalArgumentException) } - @IgnoreIf({ serverVersionAtLeast(3, 6) && !isStandalone() }) - def 'should throw MongoClientException starting a session when sessions are not supported'() { - when: - getMongoClient().startSession() - - then: - thrown(MongoClientException) - - when: - getMongoClient().startSession(ClientSessionOptions.builder().build()) - - then: - thrown(MongoClientException) - } - - @IgnoreIf({ !serverVersionAtLeast(3, 6) || isStandalone() }) + @IgnoreIf({ !serverVersionAtLeast(3, 6) }) def 'should create session with correct defaults'() { expect: clientSession.getOriginator() == getMongoClient() @@ -93,7 +77,7 @@ class MongoClientSessionSpecification extends FunctionalSpecification { getMongoClient().startSession(ClientSessionOptions.builder().build())] } - @IgnoreIf({ !serverVersionAtLeast(3, 6) || isStandalone() }) + @IgnoreIf({ !serverVersionAtLeast(3, 6) }) def 'cluster time should advance'() { given: def firstOperationTime = new BsonTimestamp(42, 1) @@ -134,7 +118,7 @@ class MongoClientSessionSpecification extends FunctionalSpecification { clientSession.getClusterTime() == secondClusterTime } - @IgnoreIf({ !serverVersionAtLeast(3, 6) || isStandalone() }) + @IgnoreIf({ !serverVersionAtLeast(3, 6) }) def 'operation time should advance'() { given: def firstOperationTime = new BsonTimestamp(42, 1) @@ -172,7 +156,7 @@ class MongoClientSessionSpecification extends FunctionalSpecification { clientSession.getOperationTime() == secondOperationTime } - @IgnoreIf({ !serverVersionAtLeast(3, 6) || isStandalone() }) + @IgnoreIf({ !serverVersionAtLeast(3, 6) }) def 'methods that use the session should throw if the session is closed'() { given: def options = ClientSessionOptions.builder().build() @@ -198,7 +182,7 @@ class MongoClientSessionSpecification extends FunctionalSpecification { thrown(IllegalStateException) } - @IgnoreIf({ !serverVersionAtLeast(3, 6) || isStandalone() }) + @IgnoreIf({ !serverVersionAtLeast(3, 6) }) def 'informational methods should not throw if the session is closed'() { given: def options = ClientSessionOptions.builder().build() @@ -215,7 +199,7 @@ class MongoClientSessionSpecification extends FunctionalSpecification { noExceptionThrown() } - @IgnoreIf({ !serverVersionAtLeast(3, 6) || isStandalone() }) + @IgnoreIf({ !serverVersionAtLeast(3, 6) }) def 'should apply causally consistent session option to client session'() { when: def clientSession = getMongoClient().startSession(ClientSessionOptions.builder() @@ -230,7 +214,7 @@ class MongoClientSessionSpecification extends FunctionalSpecification { causallyConsistent << [true, false] } - @IgnoreIf({ !serverVersionAtLeast(3, 6) || isStandalone() }) + @IgnoreIf({ !serverVersionAtLeast(3, 6) }) def 'client session should have server session with valid identifier'() { given: def clientSession = getMongoClient().startSession(ClientSessionOptions.builder().build()) @@ -246,7 +230,7 @@ class MongoClientSessionSpecification extends FunctionalSpecification { identifier.getBinary('id').data.length == 16 } - @IgnoreIf({ !serverVersionAtLeast(3, 6) || isStandalone() }) + @IgnoreIf({ !serverVersionAtLeast(3, 6) }) def 'should use a default session'() { given: def commandListener = new TestCommandListener() @@ -265,24 +249,6 @@ class MongoClientSessionSpecification extends FunctionalSpecification { client?.close() } - @IgnoreIf({ serverVersionAtLeast(3, 6) && !isStandalone() }) - def 'should not use a default session when sessions are not supported'() { - given: - def commandListener = new TestCommandListener() - def settings = MongoClientSettings.builder(getMongoClientSettings()).commandListenerList([commandListener]).build() - def client = MongoClients.create(settings) - - when: - client.getDatabase('admin').runCommand(new BsonDocument('ping', new BsonInt32(1))) - - then: - def pingCommandStartedEvent = commandListener.events.get(0) - !(pingCommandStartedEvent as CommandStartedEvent).command.containsKey('lsid') - - cleanup: - client?.close() - } - // This test attempts attempts to demonstrate that causal consistency works correctly by inserting a document and then immediately // searching for that document on a secondary by its _id and failing the test if the document is not found. Without causal consistency // enabled the expectation is that eventually that test would fail since generally the find will execute on the secondary before @@ -290,7 +256,7 @@ class MongoClientSessionSpecification extends FunctionalSpecification { // This test is inherently racy as it's possible that the server _does_ replicate fast enough and therefore the test passes anyway // even if causal consistency was not actually in effect. For that reason the test iterates a number of times in order to increase // confidence that it's really causal consistency that is causing the test to succeed - @IgnoreIf({ !serverVersionAtLeast(3, 6) || isStandalone() }) + @IgnoreIf({ !serverVersionAtLeast(3, 6) }) @Category(Slow) def 'should find inserted document on a secondary when causal consistency is enabled'() { given: @@ -342,7 +308,7 @@ class MongoClientSessionSpecification extends FunctionalSpecification { client?.close() } - @IgnoreIf({ !serverVersionAtLeast(3, 6) || isStandalone() }) + @IgnoreIf({ !serverVersionAtLeast(3, 6) }) def 'should throw exception if unacknowledged write used with explicit session'() { given: def session = getMongoClient().startSession() From 7eb7dd65a692433cc6a7ecf020bedfb4237ec534 Mon Sep 17 00:00:00 2001 From: Jeff Yemin Date: Tue, 4 Aug 2020 10:27:12 -0400 Subject: [PATCH 36/48] Bump to 3.12.7 --- build.gradle | 2 +- docs/reference/content/bson/installation-guide.md | 2 +- .../content/driver-async/getting-started/installation.md | 2 +- .../content/driver/getting-started/installation.md | 8 ++++---- docs/reference/content/driver/tutorials/jndi.md | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/build.gradle b/build.gradle index a5222997e1c..72687fa9d7b 100644 --- a/build.gradle +++ b/build.gradle @@ -54,7 +54,7 @@ def javaCodeCheckedProjects = subprojects.findAll { !['util', 'mongo-java-driver configure(coreProjects) { evaluationDependsOn(':util') group = 'org.mongodb' - version = '3.12.7-SNAPSHOT' + version = '3.12.7' repositories { mavenLocal() diff --git a/docs/reference/content/bson/installation-guide.md b/docs/reference/content/bson/installation-guide.md index df794a89fbf..1822581d806 100644 --- a/docs/reference/content/bson/installation-guide.md +++ b/docs/reference/content/bson/installation-guide.md @@ -22,4 +22,4 @@ This library comprehensively supports [BSON](http://www.bsonspec.org), the data storage and network transfer format that MongoDB uses for "documents". BSON is short for Binary [JSON](http://json.org/), is a binary-encoded serialization of JSON-like documents. -{{< install artifactId="bson" version="3.12.6" >}} +{{< install artifactId="bson" version="3.12.7" >}} diff --git a/docs/reference/content/driver-async/getting-started/installation.md b/docs/reference/content/driver-async/getting-started/installation.md index 31530a7fa2e..28b529cc0fd 100644 --- a/docs/reference/content/driver-async/getting-started/installation.md +++ b/docs/reference/content/driver-async/getting-started/installation.md @@ -24,4 +24,4 @@ When TLS/SSL is enabled, the MongoDB Async Driver requires either [Netty](http:/ The MongoDB Async Driver provides asynchronous API that can leverage either Netty or Java 7's AsynchronousSocketChannel for fast and non-blocking I/O. -{{< install artifactId="mongodb-driver-async" version="3.12.6" dependencies="true">}} +{{< install artifactId="mongodb-driver-async" version="3.12.7" dependencies="true">}} diff --git a/docs/reference/content/driver/getting-started/installation.md b/docs/reference/content/driver/getting-started/installation.md index 6616918b04c..29ccbb0919a 100644 --- a/docs/reference/content/driver/getting-started/installation.md +++ b/docs/reference/content/driver/getting-started/installation.md @@ -31,7 +31,7 @@ The `mongodb-driver-sync` artifact is a valid OSGi bundle whose symbolic name is {{% /note %}} -{{< install artifactId="mongodb-driver-sync" version="3.12.6" dependencies="true">}} +{{< install artifactId="mongodb-driver-sync" version="3.12.7" dependencies="true">}} ## MongoDB Driver Legacy @@ -44,7 +44,7 @@ While not deprecated, we recommend that new applications depend on the `mongodb- {{% /note %}} -{{< install artifactId="mongodb-driver-legacy" version="3.12.6" dependencies="true">}} +{{< install artifactId="mongodb-driver-legacy" version="3.12.7" dependencies="true">}} ## MongoDB Driver @@ -61,7 +61,7 @@ This module is deprecated and will no longer be published in the next major rele {{% /note %}} -{{< install artifactId="mongodb-driver" version="3.12.6" dependencies="true">}} +{{< install artifactId="mongodb-driver" version="3.12.7" dependencies="true">}} ## Uber Jar (Legacy) @@ -81,4 +81,4 @@ This module is deprecated and will no longer be published in the next major rele {{% /note %}} -{{< install artifactId="mongo-java-driver" version="3.12.6">}} +{{< install artifactId="mongo-java-driver" version="3.12.7">}} diff --git a/docs/reference/content/driver/tutorials/jndi.md b/docs/reference/content/driver/tutorials/jndi.md index 880aabee23e..d4e2281f2f3 100644 --- a/docs/reference/content/driver/tutorials/jndi.md +++ b/docs/reference/content/driver/tutorials/jndi.md @@ -28,7 +28,7 @@ The configuration of the `MongoClientFactory` differs depending on the applicati - + From 06785018ddd142b81fdaa1bb42e870beae6b9958 Mon Sep 17 00:00:00 2001 From: Jeff Yemin Date: Tue, 4 Aug 2020 10:29:35 -0400 Subject: [PATCH 37/48] Bump to 3.12.8-SNAPSHOT --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 72687fa9d7b..a79e5cc4d6b 100644 --- a/build.gradle +++ b/build.gradle @@ -54,7 +54,7 @@ def javaCodeCheckedProjects = subprojects.findAll { !['util', 'mongo-java-driver configure(coreProjects) { evaluationDependsOn(':util') group = 'org.mongodb' - version = '3.12.7' + version = '3.12.8-SNAPSHOT' repositories { mavenLocal() From cf79fa2b173f483287cd263a041a9e429b0bbcfd Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Thu, 24 Sep 2020 09:53:25 +0100 Subject: [PATCH 38/48] Update compatibility information for 3.12 (#577) JAVA-3822 JAVA-3837 --- docs/reference/content/upgrading.md | 36 +++++++++++++++-------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/docs/reference/content/upgrading.md b/docs/reference/content/upgrading.md index aea36df60c4..fd7df2ed0d8 100644 --- a/docs/reference/content/upgrading.md +++ b/docs/reference/content/upgrading.md @@ -11,7 +11,7 @@ title = "Upgrade Considerations" Please note that the 3.x series of releases (which includes support through MongoDB 4.2) will be the last releases that are compatible with *Java 6 or Java 7*. The 4.0 Java driver will require a minimum of Java 8. -Please also note that support for new features in MongoDB 4.4 will likely only be available via a 4.x driver release. +Please note that 3.12.x is tested against MongoDB 4.4, however support for all the new features is only available via a 4.x driver. {{% /note %}} ## Upgrading from 3.11.x @@ -95,19 +95,21 @@ the driver to use [Netty](http://netty.io/) instead. The following table specifies the compatibility of the MongoDB Java driver for use with a specific version of MongoDB. -|Java Driver Version|MongoDB 2.6|MongoDB 3.0 |MongoDB 3.2|MongoDB 3.4|MongoDB 3.6|MongoDB 4.0|MongoDB 4.2| -|-------------------|-----------|------------|-----------|-----------|-----------|-----------|-----------| -|Version 3.12 | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | -|Version 3.11 | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | -|Version 3.10 | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | | -|Version 3.9 | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | | -|Version 3.9 | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | | -|Version 3.8 | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | | -|Version 3.7 | ✓ | ✓ | ✓ | ✓ | ✓ | | | -|Version 3.6 | ✓ | ✓ | ✓ | ✓ | ✓ | | | -|Version 3.5 | ✓ | ✓ | ✓ | ✓ | | | | -|Version 3.4 | ✓ | ✓ | ✓ | ✓ | | | | -|Version 3.3 | ✓ | ✓ | ✓ | | | | | -|Version 3.2 | ✓ | ✓ | ✓ | | | | | -|Version 3.1 | ✓ | ✓ | | | | | | -|Version 3.0 | ✓ | ✓ | | | | | | +|Java Driver Version|MongoDB 3.0 |MongoDB 3.2|MongoDB 3.4|MongoDB 3.6|MongoDB 4.0|MongoDB 4.2|MongoDB 4.4| +|-------------------|------------|-----------|-----------|-----------|-----------|-----------|-----------| +|Version 3.12 | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓* | +|Version 3.11 | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | | +|Version 3.10 | ✓ | ✓ | ✓ | ✓ | ✓ | | | +|Version 3.9 | ✓ | ✓ | ✓ | ✓ | ✓ | | | +|Version 3.9 | ✓ | ✓ | ✓ | ✓ | ✓ | | | +|Version 3.8 | ✓ | ✓ | ✓ | ✓ | ✓ | | | +|Version 3.7 | ✓ | ✓ | ✓ | ✓ | | | | +|Version 3.6 | ✓ | ✓ | ✓ | ✓ | | | | +|Version 3.5 | ✓ | ✓ | ✓ | | | | | +|Version 3.4 | ✓ | ✓ | ✓ | | | | | +|Version 3.3 | ✓ | ✓ | | | | | | +|Version 3.2 | ✓ | ✓ | | | | | | +|Version 3.1 | ✓ | | | | | | | +|Version 3.0 | ✓ | | | | | | | + +\* The 3.12 driver is tested against MongoDB 4.4 but does not support all the new features \ No newline at end of file From 0e556f1db850d47bd890d144f3a48064af554b5c Mon Sep 17 00:00:00 2001 From: jyemin Date: Thu, 3 Dec 2020 08:43:51 -0500 Subject: [PATCH 39/48] Ensure DNS errors bubble up as errors in reactive streams Currently DNS lookups happen synchronously in the reactive streams driver. This isn't good, but with this change at least DNS errors will be properly reported. JAVA-3852 --- .../connection/InternalStreamConnection.java | 55 +++++++++---------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/driver-core/src/main/com/mongodb/internal/connection/InternalStreamConnection.java b/driver-core/src/main/com/mongodb/internal/connection/InternalStreamConnection.java index 63d6e05cc22..b1faa79fd6d 100644 --- a/driver-core/src/main/com/mongodb/internal/connection/InternalStreamConnection.java +++ b/driver-core/src/main/com/mongodb/internal/connection/InternalStreamConnection.java @@ -145,37 +145,36 @@ public void openAsync(final SingleResultCallback callback) { isTrue("Open already called", stream == null, callback); try { stream = streamFactory.create(serverId.getAddress()); - } catch (Throwable t) { - callback.onResult(null, t); - return; - } - stream.openAsync(new AsyncCompletionHandler() { - @Override - public void completed(final Void aVoid) { - connectionInitializer.initializeAsync(InternalStreamConnection.this, new SingleResultCallback() { - @Override - public void onResult(final ConnectionDescription result, final Throwable t) { - if (t != null) { - close(); - callback.onResult(null, t); - } else { - description = result; - opened.set(true); - sendCompressor = findSendCompressor(description); - if (LOGGER.isInfoEnabled()) { - LOGGER.info(format("Opened connection [%s] to %s", getId(), serverId.getAddress())); + stream.openAsync(new AsyncCompletionHandler() { + @Override + public void completed(final Void aVoid) { + connectionInitializer.initializeAsync(InternalStreamConnection.this, new SingleResultCallback() { + @Override + public void onResult(final ConnectionDescription result, final Throwable t) { + if (t != null) { + close(); + callback.onResult(null, t); + } else { + description = result; + opened.set(true); + sendCompressor = findSendCompressor(description); + if (LOGGER.isInfoEnabled()) { + LOGGER.info(format("Opened connection [%s] to %s", getId(), serverId.getAddress())); + } + callback.onResult(null, null); } - callback.onResult(null, null); } - } - }); - } + }); + } - @Override - public void failed(final Throwable t) { - callback.onResult(null, t); - } - }); + @Override + public void failed(final Throwable t) { + callback.onResult(null, t); + } + }); + } catch (Throwable t) { + callback.onResult(null, t); + } } private Map createCompressorMap(final List compressorList) { From 0b0ffc3254d4cc0b2bad01e9b2d73a6b5e637695 Mon Sep 17 00:00:00 2001 From: Jeff Yemin Date: Wed, 20 Jan 2021 09:31:28 -0500 Subject: [PATCH 40/48] Ensure that resumeToken is included on resume attempts (#634) JAVA-3871 --- .../operation/ChangeStreamBatchCursor.java | 22 +++++++++++++------ .../mongodb/operation/QueryBatchCursor.java | 1 + .../OperationFunctionalSpecification.groovy | 13 ++++++++++- .../mongodb/client/ChangeStreamProseTest.java | 6 +++-- 4 files changed, 32 insertions(+), 10 deletions(-) diff --git a/driver-core/src/main/com/mongodb/operation/ChangeStreamBatchCursor.java b/driver-core/src/main/com/mongodb/operation/ChangeStreamBatchCursor.java index 3f553893b71..16c373af866 100644 --- a/driver-core/src/main/com/mongodb/operation/ChangeStreamBatchCursor.java +++ b/driver-core/src/main/com/mongodb/operation/ChangeStreamBatchCursor.java @@ -61,7 +61,11 @@ public boolean hasNext() { return resumeableOperation(new Function, Boolean>() { @Override public Boolean apply(final AggregateResponseBatchCursor queryBatchCursor) { - return queryBatchCursor.hasNext(); + try { + return queryBatchCursor.hasNext(); + } finally { + cachePostBatchResumeToken(queryBatchCursor); + } } }); } @@ -71,9 +75,11 @@ public List next() { return resumeableOperation(new Function, List>() { @Override public List apply(final AggregateResponseBatchCursor queryBatchCursor) { - List results = convertResults(queryBatchCursor.next()); - cachePostBatchResumeToken(queryBatchCursor); - return results; + try { + return convertResults(queryBatchCursor.next()); + } finally { + cachePostBatchResumeToken(queryBatchCursor); + } } }); } @@ -83,9 +89,11 @@ public List tryNext() { return resumeableOperation(new Function, List>() { @Override public List apply(final AggregateResponseBatchCursor queryBatchCursor) { - List results = convertResults(queryBatchCursor.tryNext()); - cachePostBatchResumeToken(queryBatchCursor); - return results; + try { + return convertResults(queryBatchCursor.tryNext()); + } finally { + cachePostBatchResumeToken(queryBatchCursor); + } } }); } diff --git a/driver-core/src/main/com/mongodb/operation/QueryBatchCursor.java b/driver-core/src/main/com/mongodb/operation/QueryBatchCursor.java index ba3efdf053e..895fe68110b 100644 --- a/driver-core/src/main/com/mongodb/operation/QueryBatchCursor.java +++ b/driver-core/src/main/com/mongodb/operation/QueryBatchCursor.java @@ -99,6 +99,7 @@ class QueryBatchCursor implements AggregateResponseBatchCursor { this.decoder = notNull("decoder", decoder); if (result != null) { this.operationTime = result.getTimestamp(OPERATION_TIME, null); + this.postBatchResumeToken = getPostBatchResumeTokenFromResponse(result); } if (firstQueryResult.getCursor() != null) { notNull("connectionSource", connectionSource); diff --git a/driver-core/src/test/functional/com/mongodb/OperationFunctionalSpecification.groovy b/driver-core/src/test/functional/com/mongodb/OperationFunctionalSpecification.groovy index 3defca55427..1d968ceebd9 100644 --- a/driver-core/src/test/functional/com/mongodb/OperationFunctionalSpecification.groovy +++ b/driver-core/src/test/functional/com/mongodb/OperationFunctionalSpecification.groovy @@ -157,21 +157,32 @@ class OperationFunctionalSpecification extends Specification { } def next(cursor, boolean async, int minimumCount) { + next(cursor, async, false, minimumCount) + } + + def next(cursor, boolean async, boolean callHasNextBeforeNext, int minimumCount) { List retVal = [] while (retVal.size() < minimumCount) { - retVal.addAll(next(cursor, async)) + retVal.addAll(doNext(cursor, async, callHasNextBeforeNext)) } retVal } def next(cursor, boolean async) { + doNext(cursor, async, false) + } + + def doNext(cursor, boolean async, boolean callHasNextBeforeNext) { if (async) { def futureResultCallback = new FutureResultCallback>() cursor.next(futureResultCallback) futureResultCallback.get(TIMEOUT, TimeUnit.SECONDS) } else { + if (callHasNextBeforeNext) { + cursor.hasNext() + } cursor.next() } } diff --git a/driver-sync/src/test/functional/com/mongodb/client/ChangeStreamProseTest.java b/driver-sync/src/test/functional/com/mongodb/client/ChangeStreamProseTest.java index 17785a503ce..fb7da3bec27 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/ChangeStreamProseTest.java +++ b/driver-sync/src/test/functional/com/mongodb/client/ChangeStreamProseTest.java @@ -394,8 +394,10 @@ public void testGetResumeTokenReturnsPostBatchResumeTokenAfterGetMore() // use reflection to access the postBatchResumeToken AggregateResponseBatchCursor batchCursor = getBatchCursor(cursor); - // check equality in the case where the batch has not been iterated at all - assertEquals(cursor.getResumeToken(), batchCursor.getPostBatchResumeToken()); + assertNotNull(batchCursor.getPostBatchResumeToken()); + + // resume token should be null before iteration + assertNull(cursor.getResumeToken()); cursor.next(); assertEquals(cursor.getResumeToken(), batchCursor.getPostBatchResumeToken()); From 9ea346b18546d45c15c1c19247a489a52560ea10 Mon Sep 17 00:00:00 2001 From: Valentin Kovalenko Date: Wed, 20 Jan 2021 12:12:25 -0700 Subject: [PATCH 41/48] Backport a fix of the read timeout implementation in NettyStream The original PR https://github.com/mongodb/mongo-java-driver/pull/636 JAVA-3920 --- .../mongodb/connection/netty/NettyStream.java | 181 +++++++++++++----- .../connection/netty/ReadTimeoutHandler.java | 78 -------- 2 files changed, 130 insertions(+), 129 deletions(-) delete mode 100644 driver-core/src/main/com/mongodb/connection/netty/ReadTimeoutHandler.java diff --git a/driver-core/src/main/com/mongodb/connection/netty/NettyStream.java b/driver-core/src/main/com/mongodb/connection/netty/NettyStream.java index b8e5e8d0671..2eca8673730 100644 --- a/driver-core/src/main/com/mongodb/connection/netty/NettyStream.java +++ b/driver-core/src/main/com/mongodb/connection/netty/NettyStream.java @@ -24,10 +24,12 @@ import com.mongodb.MongoSocketOpenException; import com.mongodb.MongoSocketReadTimeoutException; import com.mongodb.ServerAddress; +import com.mongodb.annotations.ThreadSafe; import com.mongodb.connection.AsyncCompletionHandler; import com.mongodb.connection.SocketSettings; import com.mongodb.connection.SslSettings; import com.mongodb.connection.Stream; +import com.mongodb.lang.Nullable; import io.netty.bootstrap.Bootstrap; import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.CompositeByteBuf; @@ -35,16 +37,16 @@ import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; -import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; +import io.netty.channel.ChannelPipeline; import io.netty.channel.EventLoopGroup; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.socket.SocketChannel; import io.netty.handler.ssl.SslHandler; import io.netty.handler.timeout.ReadTimeoutException; -import io.netty.util.concurrent.EventExecutor; import org.bson.ByteBuf; import javax.net.ssl.SSLContext; @@ -58,6 +60,8 @@ import java.util.List; import java.util.Queue; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Future; +import java.util.concurrent.ScheduledFuture; import static com.mongodb.internal.connection.SslHelper.enableHostNameVerification; import static com.mongodb.internal.connection.SslHelper.enableSni; @@ -65,9 +69,39 @@ /** * A Stream implementation based on Netty 4.0. + * Just like it is for the {@link java.nio.channels.AsynchronousSocketChannel}, + * concurrent pending1 readers + * (whether {@linkplain #read(int, int) synchronous} or {@linkplain #readAsync(int, AsyncCompletionHandler) asynchronous}) + * are not supported by {@link NettyStream}. + * However, this class does not have a fail-fast mechanism checking for such situations. + *
+ * 1We cannot simply say that read methods are not allowed be run concurrently because strictly speaking they are allowed, + * as explained below. + *
{@code
+ * NettyStream stream = ...;
+ * stream.readAsync(1, new AsyncCompletionHandler() {//inv1
+ *  @Override
+ *  public void completed(ByteBuf o) {
+ *      stream.readAsync(//inv2
+ *              1, ...);//ret2
+ *  }
+ *
+ *  @Override
+ *  public void failed(Throwable t) {
+ *  }
+ * });//ret1
+ * }
+ * Arrows on the diagram below represent happens-before relations. + *
{@code
+ * int1 -> inv2 -> ret2
+ *      \--------> ret1
+ * }
+ * As shown on the diagram, the method {@link #readAsync(int, AsyncCompletionHandler)} runs concurrently with + * itself in the example above. However, there are no concurrent pending readers because the second operation + * is invoked after the first operation has completed reading despite the method has not returned yet. */ final class NettyStream implements Stream { - private static final String READ_HANDLER_NAME = "ReadTimeoutHandler"; + private static final byte NO_SCHEDULE_TIME = 0; private final ServerAddress address; private final SocketSettings settings; private final SslSettings sslSettings; @@ -79,8 +113,19 @@ final class NettyStream implements Stream { private volatile Channel channel; private final LinkedList pendingInboundBuffers = new LinkedList(); - private volatile PendingReader pendingReader; - private volatile Throwable pendingException; + /* The fields pendingReader, pendingException are always written/read inside synchronized blocks + * that use the same NettyStream object, so they can be plain.*/ + private PendingReader pendingReader; + private Throwable pendingException; + /* The fields readTimeoutTask, readTimeoutMillis are each written only in the ChannelInitializer.initChannel method + * (in addition to the write of the default value and the write by variable initializers), + * and read only when NettyStream users read data, or Netty event loop handles incoming data. + * Since actions done by the ChannelInitializer.initChannel method + * are ordered (in the happens-before order) before user read actions and before event loop actions that handle incoming data, + * these fields can be plain.*/ + @Nullable + private ReadTimeoutTask readTimeoutTask; + private long readTimeoutMillis = NO_SCHEDULE_TIME; NettyStream(final ServerAddress address, final SocketSettings settings, final SslSettings sslSettings, final EventLoopGroup workerGroup, final Class socketChannelClass, final ByteBufAllocator allocator) { @@ -135,6 +180,7 @@ private void initializeChannel(final AsyncCompletionHandler handler, final bootstrap.handler(new ChannelInitializer() { @Override public void initChannel(final SocketChannel ch) { + ChannelPipeline pipeline = ch.pipeline(); if (sslSettings.isEnabled()) { SSLEngine engine = getSslContext().createSSLEngine(address.getHost(), address.getPort()); engine.setUseClientMode(true); @@ -144,13 +190,20 @@ public void initChannel(final SocketChannel ch) { enableHostNameVerification(sslParameters); } engine.setSSLParameters(sslParameters); - ch.pipeline().addFirst("ssl", new SslHandler(engine, false)); + pipeline.addFirst("ssl", new SslHandler(engine, false)); } + int readTimeout = settings.getReadTimeout(MILLISECONDS); - if (readTimeout > 0) { - ch.pipeline().addLast(READ_HANDLER_NAME, new ReadTimeoutHandler(readTimeout)); + if (readTimeout > NO_SCHEDULE_TIME) { + readTimeoutMillis = readTimeout; + /* We need at least one handler before (in the inbound evaluation order) the InboundBufferHandler, + * so that we can fire exception events (they are inbound events) using its context and the InboundBufferHandler + * receives them. SslHandler is not always present, so adding a NOOP handler.*/ + pipeline.addLast(new ChannelInboundHandlerAdapter()); + readTimeoutTask = new ReadTimeoutTask(pipeline.lastContext()); } - ch.pipeline().addLast(new InboundBufferHandler()); + + pipeline.addLast(new InboundBufferHandler()); } }); final ChannelFuture channelFuture = bootstrap.connect(nextAddress); @@ -193,14 +246,27 @@ public void operationComplete(final ChannelFuture future) throws Exception { @Override public void readAsync(final int numBytes, final AsyncCompletionHandler handler) { - scheduleReadTimeout(); + readAsync(numBytes, handler, readTimeoutMillis); + } + + /** + * @param numBytes Must be equal to {@link #pendingReader}{@code .numBytes} when called by a Netty channel handler. + * @param handler Must be equal to {@link #pendingReader}{@code .handler} when called by a Netty channel handler. + * @param readTimeoutMillis Must be equal to {@link #NO_SCHEDULE_TIME} when called by a Netty channel handler. + * Timeouts may be scheduled only by the public read methods. Taking into account that concurrent pending + * readers are not allowed, there must not be a situation when threads attempt to schedule a timeout + * before the previous one is either cancelled or completed. + */ + private void readAsync(final int numBytes, final AsyncCompletionHandler handler, final long readTimeoutMillis) { ByteBuf buffer = null; Throwable exceptionResult = null; synchronized (this) { exceptionResult = pendingException; if (exceptionResult == null) { if (!hasBytesAvailable(numBytes)) { - pendingReader = new PendingReader(numBytes, handler); + if (pendingReader == null) {//called by a public read method + pendingReader = new PendingReader(numBytes, handler, scheduleReadTimeout(readTimeoutTask, readTimeoutMillis)); + } } else { CompositeByteBuf composite = allocator.compositeBuffer(pendingInboundBuffers.size()); int bytesNeeded = numBytes; @@ -223,13 +289,16 @@ public void readAsync(final int numBytes, final AsyncCompletionHandler buffer = new NettyByteBuf(composite).flip(); } } + if (!(exceptionResult == null && buffer == null)//the read operation has completed + && pendingReader != null) {//we need to clear the pending reader + cancel(pendingReader.timeout); + this.pendingReader = null; + } } if (exceptionResult != null) { - disableReadTimeout(); handler.failed(exceptionResult); } if (buffer != null) { - disableReadTimeout(); handler.completed(buffer); } } @@ -253,14 +322,12 @@ private void handleReadResponse(final io.netty.buffer.ByteBuf buffer, final Thro } else { pendingException = t; } - if (pendingReader != null) { - localPendingReader = pendingReader; - pendingReader = null; - } + localPendingReader = pendingReader; } if (localPendingReader != null) { - readAsync(localPendingReader.numBytes, localPendingReader.handler); + //timeouts may be scheduled only by the public read methods + readAsync(localPendingReader.numBytes, localPendingReader.handler, NO_SCHEDULE_TIME); } } @@ -336,10 +403,14 @@ public void exceptionCaught(final ChannelHandlerContext ctx, final Throwable t) private static final class PendingReader { private final int numBytes; private final AsyncCompletionHandler handler; + @Nullable + private final ScheduledFuture timeout; - private PendingReader(final int numBytes, final AsyncCompletionHandler handler) { + private PendingReader( + final int numBytes, final AsyncCompletionHandler handler, @Nullable final ScheduledFuture timeout) { this.numBytes = numBytes; this.handler = handler; + this.timeout = timeout; } } @@ -423,44 +494,52 @@ public void operationComplete(final ChannelFuture future) { } } - private void scheduleReadTimeout() { - adjustTimeout(false); + private static void cancel(@Nullable final Future f) { + if (f != null) { + f.cancel(false); + } } - private void disableReadTimeout() { - adjustTimeout(true); + private static long combinedTimeout(final long timeout, final int additionalTimeout) { + if (timeout == NO_SCHEDULE_TIME) { + return NO_SCHEDULE_TIME; + } else { + return Math.addExact(timeout, additionalTimeout); + } } - private void adjustTimeout(final boolean disable) { - ChannelHandler timeoutHandler = channel.pipeline().get(READ_HANDLER_NAME); - if (timeoutHandler != null) { - final ReadTimeoutHandler readTimeoutHandler = (ReadTimeoutHandler) timeoutHandler; - final ChannelHandlerContext handlerContext = channel.pipeline().context(timeoutHandler); - EventExecutor executor = handlerContext.executor(); + private static ScheduledFuture scheduleReadTimeout(@Nullable final ReadTimeoutTask readTimeoutTask, final long timeoutMillis) { + if (timeoutMillis == NO_SCHEDULE_TIME) { + return null; + } else { + //assert readTimeoutTask != null : "readTimeoutTask must be initialized if read timeouts are enabled"; + return readTimeoutTask.schedule(timeoutMillis); + } + } - if (disable) { - if (executor.inEventLoop()) { - readTimeoutHandler.removeTimeout(handlerContext); - } else { - executor.submit(new Runnable() { - @Override - public void run() { - readTimeoutHandler.removeTimeout(handlerContext); - } - }); - } - } else { - if (executor.inEventLoop()) { - readTimeoutHandler.scheduleTimeout(handlerContext); - } else { - executor.submit(new Runnable() { - @Override - public void run() { - readTimeoutHandler.scheduleTimeout(handlerContext); - } - }); - } + @ThreadSafe + private static final class ReadTimeoutTask implements Runnable { + private final ChannelHandlerContext ctx; + + private ReadTimeoutTask(final ChannelHandlerContext timeoutChannelHandlerContext) { + ctx = timeoutChannelHandlerContext; + } + + @Override + public void run() { + try { + if (ctx.channel().isOpen()) { + ctx.fireExceptionCaught(ReadTimeoutException.INSTANCE); + ctx.close(); } + } catch (final Throwable t) { + ctx.fireExceptionCaught(t); } + } + + private ScheduledFuture schedule(final long timeoutMillis) { + //assert timeoutMillis > 0 : timeoutMillis; + return ctx.executor().schedule(this, timeoutMillis, MILLISECONDS); + } } } diff --git a/driver-core/src/main/com/mongodb/connection/netty/ReadTimeoutHandler.java b/driver-core/src/main/com/mongodb/connection/netty/ReadTimeoutHandler.java deleted file mode 100644 index 4b4533f3cde..00000000000 --- a/driver-core/src/main/com/mongodb/connection/netty/ReadTimeoutHandler.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2008-present MongoDB, Inc. - * Copyright 2012 The Netty Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.mongodb.connection.netty; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundHandlerAdapter; -import io.netty.handler.timeout.ReadTimeoutException; - -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; - -import static com.mongodb.assertions.Assertions.isTrue; -import static com.mongodb.assertions.Assertions.isTrueArgument; - -/** - * Passes a {@link ReadTimeoutException} if the time between a {@link #scheduleTimeout} and {@link #removeTimeout} is longer than the set - * timeout. - */ -final class ReadTimeoutHandler extends ChannelInboundHandlerAdapter { - private final long readTimeout; - private volatile ScheduledFuture timeout; - - ReadTimeoutHandler(final long readTimeout) { - isTrueArgument("readTimeout must be greater than zero.", readTimeout > 0); - this.readTimeout = readTimeout; - } - - void scheduleTimeout(final ChannelHandlerContext ctx) { - isTrue("Handler called from the eventLoop", ctx.channel().eventLoop().inEventLoop()); - if (timeout == null) { - timeout = ctx.executor().schedule(new ReadTimeoutTask(ctx), readTimeout, TimeUnit.MILLISECONDS); - } - } - - void removeTimeout(final ChannelHandlerContext ctx) { - isTrue("Handler called from the eventLoop", ctx.channel().eventLoop().inEventLoop()); - if (timeout != null) { - timeout.cancel(false); - timeout = null; - } - } - - private static final class ReadTimeoutTask implements Runnable { - - private final ChannelHandlerContext ctx; - - ReadTimeoutTask(final ChannelHandlerContext ctx) { - this.ctx = ctx; - } - - @Override - public void run() { - if (ctx.channel().isOpen()) { - try { - ctx.fireExceptionCaught(ReadTimeoutException.INSTANCE); - ctx.close(); - } catch (Throwable t) { - ctx.fireExceptionCaught(t); - } - } - } - } -} From 351b524c535c0f9d0e560492a49eb4ffe570b15e Mon Sep 17 00:00:00 2001 From: Jeff Yemin Date: Thu, 4 Feb 2021 11:16:29 -0500 Subject: [PATCH 42/48] Use keyvault database for FLE tests This change was not done everywhere, so adding the rest of the places JAVA-3702 --- .../ClientSideEncryptionAutoEncryptionSettingsTour.java | 2 +- .../src/examples/tour/ClientSideEncryptionSimpleTour.java | 2 +- .../async/client/ClientSideEncryptionCorpusTest.java | 6 +++--- .../client/ClientSideEncryptionExternalKeyVaultTest.java | 4 ++-- .../ClientSideEncryptionMongocryptdSpawnBypassTest.java | 2 +- .../com/mongodb/async/client/ClientSideEncryptionTest.java | 6 +++--- .../client/ClientSideEncryptionViewAreProhibitedTest.java | 2 +- .../ClientSideEncryptionAutoEncryptionSettingsTour.java | 2 +- .../src/examples/tour/ClientSideEncryptionSimpleTour.java | 2 +- 9 files changed, 14 insertions(+), 14 deletions(-) diff --git a/driver-async/src/examples/tour/ClientSideEncryptionAutoEncryptionSettingsTour.java b/driver-async/src/examples/tour/ClientSideEncryptionAutoEncryptionSettingsTour.java index 1639e5f0d6c..8c7c2551a85 100644 --- a/driver-async/src/examples/tour/ClientSideEncryptionAutoEncryptionSettingsTour.java +++ b/driver-async/src/examples/tour/ClientSideEncryptionAutoEncryptionSettingsTour.java @@ -63,7 +63,7 @@ public static void main(final String[] args) throws InterruptedException { }}); }}; - String keyVaultNamespace = "admin.datakeys"; + String keyVaultNamespace = "keyvault.datakeys"; ClientEncryptionSettings clientEncryptionSettings = ClientEncryptionSettings.builder() .keyVaultMongoClientSettings(MongoClientSettings.builder() .applyConnectionString(new ConnectionString("mongodb://localhost")) diff --git a/driver-async/src/examples/tour/ClientSideEncryptionSimpleTour.java b/driver-async/src/examples/tour/ClientSideEncryptionSimpleTour.java index c60e2d1a454..94c470c5de6 100644 --- a/driver-async/src/examples/tour/ClientSideEncryptionSimpleTour.java +++ b/driver-async/src/examples/tour/ClientSideEncryptionSimpleTour.java @@ -55,7 +55,7 @@ public static void main(final String[] args) throws InterruptedException { }}); }}; - String keyVaultNamespace = "admin.datakeys"; + String keyVaultNamespace = "keyvault.datakeys"; AutoEncryptionSettings autoEncryptionSettings = AutoEncryptionSettings.builder() .keyVaultNamespace(keyVaultNamespace) diff --git a/driver-async/src/test/functional/com/mongodb/async/client/ClientSideEncryptionCorpusTest.java b/driver-async/src/test/functional/com/mongodb/async/client/ClientSideEncryptionCorpusTest.java index 6aa2468d27c..ebdb84f6448 100644 --- a/driver-async/src/test/functional/com/mongodb/async/client/ClientSideEncryptionCorpusTest.java +++ b/driver-async/src/test/functional/com/mongodb/async/client/ClientSideEncryptionCorpusTest.java @@ -101,8 +101,8 @@ public void setUp() throws IOException, URISyntaxException { documentCallback.get(); // Step 3: Drop and create keyvault.datakeys - MongoDatabase adminDatabase = client.getDatabase("admin"); - MongoCollection dataKeysCollection = adminDatabase.getCollection("datakeys", BsonDocument.class) + MongoDatabase keyvaultDatabase = client.getDatabase("keyvault"); + MongoCollection dataKeysCollection = keyvaultDatabase.getCollection("datakeys", BsonDocument.class) .withWriteConcern(WriteConcern.MAJORITY); voidCallback = new FutureResultCallback(); @@ -150,7 +150,7 @@ public void setUp() throws IOException, URISyntaxException { ClientEncryptionSettings clientEncryptionSettings = ClientEncryptionSettings.builder(). keyVaultMongoClientSettings(getMongoClientSettings()). kmsProviders(kmsProviders). - keyVaultNamespace("admin.datakeys").build(); + keyVaultNamespace("keyvault.datakeys").build(); clientEncryption = ClientEncryptions.create(clientEncryptionSettings); } diff --git a/driver-async/src/test/functional/com/mongodb/async/client/ClientSideEncryptionExternalKeyVaultTest.java b/driver-async/src/test/functional/com/mongodb/async/client/ClientSideEncryptionExternalKeyVaultTest.java index 2c4ef613d8d..c3a43a3589e 100644 --- a/driver-async/src/test/functional/com/mongodb/async/client/ClientSideEncryptionExternalKeyVaultTest.java +++ b/driver-async/src/test/functional/com/mongodb/async/client/ClientSideEncryptionExternalKeyVaultTest.java @@ -74,8 +74,8 @@ public void setUp() throws IOException, URISyntaxException { /* Step 1: get unencrypted client and recreate keys collection */ client = getMongoClient(); - MongoDatabase admin = client.getDatabase("admin"); - MongoCollection datakeys = admin.getCollection("datakeys", BsonDocument.class) + MongoDatabase keyvaultDatabase = client.getDatabase("keyvault"); + MongoCollection datakeys = keyvaultDatabase.getCollection("datakeys", BsonDocument.class) .withWriteConcern(WriteConcern.MAJORITY); FutureResultCallback voidCallback = new FutureResultCallback(); datakeys.drop(voidCallback); diff --git a/driver-async/src/test/functional/com/mongodb/async/client/ClientSideEncryptionMongocryptdSpawnBypassTest.java b/driver-async/src/test/functional/com/mongodb/async/client/ClientSideEncryptionMongocryptdSpawnBypassTest.java index 9218c3efd98..fd7f2b0284d 100644 --- a/driver-async/src/test/functional/com/mongodb/async/client/ClientSideEncryptionMongocryptdSpawnBypassTest.java +++ b/driver-async/src/test/functional/com/mongodb/async/client/ClientSideEncryptionMongocryptdSpawnBypassTest.java @@ -40,7 +40,7 @@ public class ClientSideEncryptionMongocryptdSpawnBypassTest extends DatabaseTestCase { private final File pidFile; private final Map> kmsProviders; - private final MongoNamespace keyVaultNamespace = new MongoNamespace("admin.datakeys"); + private final MongoNamespace keyVaultNamespace = new MongoNamespace("keyvault.datakeys"); public ClientSideEncryptionMongocryptdSpawnBypassTest() throws IOException { assumeFalse(isNotAtLeastJava8()); diff --git a/driver-async/src/test/functional/com/mongodb/async/client/ClientSideEncryptionTest.java b/driver-async/src/test/functional/com/mongodb/async/client/ClientSideEncryptionTest.java index f05ce661b01..b245602ea96 100644 --- a/driver-async/src/test/functional/com/mongodb/async/client/ClientSideEncryptionTest.java +++ b/driver-async/src/test/functional/com/mongodb/async/client/ClientSideEncryptionTest.java @@ -156,8 +156,8 @@ public void setUp() { callback.get(30, TimeUnit.SECONDS); } - /* Insert data into the "admin.datakeys" key vault. */ - collection = getMongoClient().getDatabase("admin").getCollection("datakeys", BsonDocument.class) + /* Insert data into the "keyvault.datakeys" key vault. */ + collection = getMongoClient().getDatabase("keyvault").getCollection("datakeys", BsonDocument.class) .withWriteConcern(WriteConcern.MAJORITY); callback = new FutureResultCallback(); collection.drop(callback); @@ -224,7 +224,7 @@ public void setUp() { } } - String keyVaultNamespace = "admin.datakeys"; + String keyVaultNamespace = "keyvault.datakeys"; if (cryptOptions.containsKey("keyVaultNamespace")) { keyVaultNamespace = cryptOptions.getString("keyVaultNamespace").getValue(); } diff --git a/driver-async/src/test/functional/com/mongodb/async/client/ClientSideEncryptionViewAreProhibitedTest.java b/driver-async/src/test/functional/com/mongodb/async/client/ClientSideEncryptionViewAreProhibitedTest.java index d22b835add3..bad9e58540c 100644 --- a/driver-async/src/test/functional/com/mongodb/async/client/ClientSideEncryptionViewAreProhibitedTest.java +++ b/driver-async/src/test/functional/com/mongodb/async/client/ClientSideEncryptionViewAreProhibitedTest.java @@ -71,7 +71,7 @@ public void setUp() { kmsProviders.put("local", localMasterkey); AutoEncryptionSettings.Builder autoEncryptionSettingsBuilder = AutoEncryptionSettings.builder() - .keyVaultNamespace("admin.datakeys") + .keyVaultNamespace("keyvault.datakeys") .kmsProviders(kmsProviders); AutoEncryptionSettings autoEncryptionSettings = autoEncryptionSettingsBuilder.build(); diff --git a/driver-sync/src/examples/tour/ClientSideEncryptionAutoEncryptionSettingsTour.java b/driver-sync/src/examples/tour/ClientSideEncryptionAutoEncryptionSettingsTour.java index c1680193c4d..398f89084c4 100644 --- a/driver-sync/src/examples/tour/ClientSideEncryptionAutoEncryptionSettingsTour.java +++ b/driver-sync/src/examples/tour/ClientSideEncryptionAutoEncryptionSettingsTour.java @@ -59,7 +59,7 @@ public static void main(final String[] args) { }}); }}; - String keyVaultNamespace = "admin.datakeys"; + String keyVaultNamespace = "keyvault.datakeys"; ClientEncryptionSettings clientEncryptionSettings = ClientEncryptionSettings.builder() .keyVaultMongoClientSettings(MongoClientSettings.builder() .applyConnectionString(new ConnectionString("mongodb://localhost")) diff --git a/driver-sync/src/examples/tour/ClientSideEncryptionSimpleTour.java b/driver-sync/src/examples/tour/ClientSideEncryptionSimpleTour.java index bf2cf668364..bc286ead954 100644 --- a/driver-sync/src/examples/tour/ClientSideEncryptionSimpleTour.java +++ b/driver-sync/src/examples/tour/ClientSideEncryptionSimpleTour.java @@ -52,7 +52,7 @@ public static void main(final String[] args) { }}); }}; - String keyVaultNamespace = "admin.datakeys"; + String keyVaultNamespace = "keyvault.datakeys"; AutoEncryptionSettings autoEncryptionSettings = AutoEncryptionSettings.builder() .keyVaultNamespace(keyVaultNamespace) From acb17d016401326b297ec3f73addb8397705a9e7 Mon Sep 17 00:00:00 2001 From: Jeff Yemin Date: Thu, 4 Feb 2021 11:17:57 -0500 Subject: [PATCH 43/48] Limit geoHaystack index testing to MongoDB 4.4 and below The next release after MongoDB 4.4 removes support for geoHaystack indexes, so we need to disable tests of them in the driver when running against server versions > 4.4. --- .../mongodb/client/model/IndexesFunctionalSpecification.groovy | 3 +++ .../operation/CreateIndexesOperationSpecification.groovy | 2 ++ 2 files changed, 5 insertions(+) diff --git a/driver-core/src/test/functional/com/mongodb/client/model/IndexesFunctionalSpecification.groovy b/driver-core/src/test/functional/com/mongodb/client/model/IndexesFunctionalSpecification.groovy index cc1f05ce364..7a33ccf3413 100644 --- a/driver-core/src/test/functional/com/mongodb/client/model/IndexesFunctionalSpecification.groovy +++ b/driver-core/src/test/functional/com/mongodb/client/model/IndexesFunctionalSpecification.groovy @@ -17,7 +17,9 @@ package com.mongodb.client.model import com.mongodb.OperationFunctionalSpecification +import spock.lang.IgnoreIf +import static com.mongodb.ClusterFixture.serverVersionGreaterThan import static com.mongodb.client.model.Indexes.ascending import static com.mongodb.client.model.Indexes.compoundIndex import static com.mongodb.client.model.Indexes.descending @@ -98,6 +100,7 @@ class IndexesFunctionalSpecification extends OperationFunctionalSpecification { getCollectionHelper().listIndexes()*.get('key').contains(parse('{x : "2d"}')) } + @IgnoreIf({ serverVersionGreaterThan('4.4') }) def 'geoHaystack'() { when: getCollectionHelper().createIndex(geoHaystack('x', descending('b')), 2.0) diff --git a/driver-core/src/test/functional/com/mongodb/operation/CreateIndexesOperationSpecification.groovy b/driver-core/src/test/functional/com/mongodb/operation/CreateIndexesOperationSpecification.groovy index 9eeee686d65..5ff94ccceb8 100644 --- a/driver-core/src/test/functional/com/mongodb/operation/CreateIndexesOperationSpecification.groovy +++ b/driver-core/src/test/functional/com/mongodb/operation/CreateIndexesOperationSpecification.groovy @@ -39,6 +39,7 @@ import static com.mongodb.ClusterFixture.getBinding import static com.mongodb.ClusterFixture.isDiscoverableReplicaSet import static com.mongodb.ClusterFixture.isSharded import static com.mongodb.ClusterFixture.serverVersionAtLeast +import static com.mongodb.ClusterFixture.serverVersionGreaterThan import static java.util.concurrent.TimeUnit.SECONDS class CreateIndexesOperationSpecification extends OperationFunctionalSpecification { @@ -291,6 +292,7 @@ class CreateIndexesOperationSpecification extends OperationFunctionalSpecificati async << [true, false] } + @IgnoreIf({ serverVersionGreaterThan('4.4') }) def 'should be able to create a geoHaystack indexes'() { given: def operation = new CreateIndexesOperation(getNamespace(), From 491e1c4f294b152a17ea2dc08764815cc2ba251b Mon Sep 17 00:00:00 2001 From: Jeff Yemin Date: Thu, 4 Feb 2021 16:13:39 -0500 Subject: [PATCH 44/48] Ignore test of $explain modifier on sharded clusters after 4.4 The response format has changed, so the test is failing. Decided to ignore it rather that try to fix it. --- .../com/mongodb/operation/FindOperationSpecification.groovy | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/driver-core/src/test/functional/com/mongodb/operation/FindOperationSpecification.groovy b/driver-core/src/test/functional/com/mongodb/operation/FindOperationSpecification.groovy index 15a91b5eead..b4f2260cb11 100644 --- a/driver-core/src/test/functional/com/mongodb/operation/FindOperationSpecification.groovy +++ b/driver-core/src/test/functional/com/mongodb/operation/FindOperationSpecification.groovy @@ -61,6 +61,7 @@ import static com.mongodb.ClusterFixture.getBinding import static com.mongodb.ClusterFixture.getCluster import static com.mongodb.ClusterFixture.isSharded import static com.mongodb.ClusterFixture.serverVersionAtLeast +import static com.mongodb.ClusterFixture.serverVersionGreaterThan import static com.mongodb.CursorType.NonTailable import static com.mongodb.CursorType.Tailable import static com.mongodb.CursorType.TailableAwait @@ -697,7 +698,7 @@ class FindOperationSpecification extends OperationFunctionalSpecification { ].combinations() } - + @IgnoreIf({ serverVersionGreaterThan('4.4') && isSharded() }) def 'should explain with $explain modifier'() { given: def operation = new FindOperation(getNamespace(), new BsonDocumentCodec()) From 504c0005cb85ed9d520c433a078705949d866b4e Mon Sep 17 00:00:00 2001 From: Jeff Yemin Date: Thu, 4 Feb 2021 17:35:07 -0500 Subject: [PATCH 45/48] Default to Java 8 in run-tests.sh --- .evergreen/run-tests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.evergreen/run-tests.sh b/.evergreen/run-tests.sh index 040ce207a03..bfbdb7b260a 100755 --- a/.evergreen/run-tests.sh +++ b/.evergreen/run-tests.sh @@ -19,7 +19,7 @@ set -o errexit # Exit the script with error if any of the commands fail AUTH=${AUTH:-noauth} SSL=${SSL:-nossl} MONGODB_URI=${MONGODB_URI:-} -JDK=${JDK:-jdk} +JDK=${JDK:-jdk8} TOPOLOGY=${TOPOLOGY:-server} COMPRESSOR=${COMPRESSOR:-} SLOW_TESTS_ONLY=${SLOW_TESTS_ONLY:-false} From 32052a1294fbc41b5e7e0e68b857bd1f359c1a48 Mon Sep 17 00:00:00 2001 From: Jeff Yemin Date: Thu, 4 Feb 2021 17:55:25 -0500 Subject: [PATCH 46/48] Skip test if multi-mongos connection string is null --- .../functional/com/mongodb/client/TransactionProseTest.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/driver-sync/src/test/functional/com/mongodb/client/TransactionProseTest.java b/driver-sync/src/test/functional/com/mongodb/client/TransactionProseTest.java index 35a293b3498..e3f121fc6b3 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/TransactionProseTest.java +++ b/driver-sync/src/test/functional/com/mongodb/client/TransactionProseTest.java @@ -35,6 +35,7 @@ import static com.mongodb.ClusterFixture.isSharded; import static com.mongodb.ClusterFixture.serverVersionAtLeast; import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeNotNull; import static org.junit.Assume.assumeTrue; // See https://github.com/mongodb/specifications/blob/master/source/transactions/tests/README.rst#mongos-pinning-prose-tests @@ -45,6 +46,8 @@ public class TransactionProseTest { @Before public void setUp() { assumeTrue(canRunTests()); + assumeNotNull(getMultiMongosConnectionString()); + MongoClientSettings.Builder builder = MongoClientSettings.builder() .applyConnectionString(getMultiMongosConnectionString()); if (System.getProperty("java.version").startsWith("1.6.")) { From ae5b1c0644456f1cf195846a37eea82f6248f812 Mon Sep 17 00:00:00 2001 From: jyemin Date: Thu, 11 Feb 2021 17:55:16 -0500 Subject: [PATCH 47/48] Fix CVE-2021-20328 JAVA-4017 --- config/findbugs-exclude.xml | 9 +++++++++ .../client/internal/KeyManagementService.java | 15 ++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/config/findbugs-exclude.xml b/config/findbugs-exclude.xml index eb6c479d606..799fcc219c0 100644 --- a/config/findbugs-exclude.xml +++ b/config/findbugs-exclude.xml @@ -25,6 +25,15 @@ + + + + + + + + diff --git a/driver-sync/src/main/com/mongodb/client/internal/KeyManagementService.java b/driver-sync/src/main/com/mongodb/client/internal/KeyManagementService.java index e1c786d27e0..1df021c44f5 100644 --- a/driver-sync/src/main/com/mongodb/client/internal/KeyManagementService.java +++ b/driver-sync/src/main/com/mongodb/client/internal/KeyManagementService.java @@ -17,8 +17,11 @@ package com.mongodb.client.internal; import com.mongodb.ServerAddress; +import com.mongodb.internal.connection.SslHelper; import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLSocket; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -38,9 +41,10 @@ class KeyManagementService { public InputStream stream(final String host, final ByteBuffer message) throws IOException { ServerAddress serverAddress = host.contains(":") ? new ServerAddress(host) : new ServerAddress(host, defaultPort); - Socket socket = sslContext.getSocketFactory().createSocket(); + SSLSocket socket = (SSLSocket) sslContext.getSocketFactory().createSocket(); try { + enableHostNameVerification(socket); socket.setSoTimeout(timeoutMillis); socket.connect(serverAddress.getSocketAddress(), timeoutMillis); } catch (IOException e) { @@ -68,6 +72,15 @@ public InputStream stream(final String host, final ByteBuffer message) throws IO } } + private void enableHostNameVerification(final SSLSocket socket) { + SSLParameters sslParameters = socket.getSSLParameters(); + if (sslParameters == null) { + sslParameters = new SSLParameters(); + } + SslHelper.enableHostNameVerification(sslParameters); + socket.setSSLParameters(sslParameters); + } + public int getDefaultPort() { return defaultPort; } From eb2ae257ff71514c839e3a0c9d23fda61da133de Mon Sep 17 00:00:00 2001 From: Jeff Yemin Date: Thu, 18 Feb 2021 15:53:43 -0500 Subject: [PATCH 48/48] Bump to 3.12.8 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index a79e5cc4d6b..c519d82bfab 100644 --- a/build.gradle +++ b/build.gradle @@ -54,7 +54,7 @@ def javaCodeCheckedProjects = subprojects.findAll { !['util', 'mongo-java-driver configure(coreProjects) { evaluationDependsOn(':util') group = 'org.mongodb' - version = '3.12.8-SNAPSHOT' + version = '3.12.8' repositories { mavenLocal()