diff --git a/.evergreen/.evg.yml b/.evergreen/.evg.yml index b1685115e7d..b28e40a0755 100644 --- a/.evergreen/.evg.yml +++ b/.evergreen/.evg.yml @@ -207,8 +207,12 @@ functions: file: mo-expansion.yml "bootstrap mongohoused": + - command: ec2.assume_role + params: + role_arn: ${aws_test_secrets_role} - command: shell.exec params: + include_expansions_in_env: [ "AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_SESSION_TOKEN" ] script: | DRIVERS_TOOLS="${DRIVERS_TOOLS}" bash ${DRIVERS_TOOLS}/.evergreen/atlas_data_lake/pull-mongohouse-image.sh - command: shell.exec @@ -838,6 +842,8 @@ functions: export K8S_DRIVERS_TAR_FILE=/tmp/mongo-java-driver.tar git archive -o $K8S_DRIVERS_TAR_FILE HEAD tar -rf $K8S_DRIVERS_TAR_FILE .git + # Loop through all submodule directories and append to the archive + git submodule status --recursive | awk '{ print $2 }' | xargs tar -rf "$K8S_DRIVERS_TAR_FILE" export K8S_TEST_CMD="OIDC_ENV=k8s VARIANT=${VARIANT} ./.evergreen/run-mongodb-oidc-test.sh" bash $DRIVERS_TOOLS/.evergreen/auth_oidc/k8s/setup-pod.sh bash $DRIVERS_TOOLS/.evergreen/auth_oidc/k8s/run-self-test.sh @@ -969,6 +975,8 @@ tasks: bash $DRIVERS_TOOLS/.evergreen/auth_oidc/gcp/run-driver-test.sh - name: "oidc-auth-test-k8s" + # Might exceed 1 hour of execution. + exec_timeout_secs: 7200 commands: - command: ec2.assume_role params: @@ -983,6 +991,8 @@ tasks: - func: "oidc-auth-test-k8s-func" vars: VARIANT: gke + params: + include_expansions_in_env: [ "AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_SESSION_TOKEN" ] - name: serverless-test commands: diff --git a/driver-core/src/main/com/mongodb/internal/connection/Authenticator.java b/driver-core/src/main/com/mongodb/internal/connection/Authenticator.java index cd1809966b0..2889a938709 100644 --- a/driver-core/src/main/com/mongodb/internal/connection/Authenticator.java +++ b/driver-core/src/main/com/mongodb/internal/connection/Authenticator.java @@ -103,13 +103,18 @@ abstract void authenticateAsync(InternalConnection connection, ConnectionDescrip OperationContext operationContext, SingleResultCallback callback); public void reauthenticate(final InternalConnection connection, final OperationContext operationContext) { - authenticate(connection, connection.getDescription(), operationContext); + authenticate(connection, connection.getDescription(), operationContextWithoutSession(operationContext)); } public void reauthenticateAsync(final InternalConnection connection, final OperationContext operationContext, final SingleResultCallback callback) { beginAsync().thenRun((c) -> { - authenticateAsync(connection, connection.getDescription(), operationContext, c); + authenticateAsync(connection, connection.getDescription(), operationContextWithoutSession(operationContext), c); }).finish(callback); } + + static OperationContext operationContextWithoutSession(final OperationContext operationContext) { + return operationContext.withSessionContext( + new ReadConcernAwareNoOpSessionContext(operationContext.getSessionContext().getReadConcern())); + } } diff --git a/driver-core/src/main/com/mongodb/internal/connection/OidcAuthenticator.java b/driver-core/src/main/com/mongodb/internal/connection/OidcAuthenticator.java index 99fcee788ed..1e67626d60d 100644 --- a/driver-core/src/main/com/mongodb/internal/connection/OidcAuthenticator.java +++ b/driver-core/src/main/com/mongodb/internal/connection/OidcAuthenticator.java @@ -253,7 +253,7 @@ static OidcCallback getGcpCallback(final MongoCredential credential) { @Override public void reauthenticate(final InternalConnection connection, final OperationContext operationContext) { assertTrue(connection.opened()); - authenticationLoop(connection, connection.getDescription(), operationContext); + authenticationLoop(connection, connection.getDescription(), operationContextWithoutSession(operationContext)); } @Override @@ -262,7 +262,7 @@ public void reauthenticateAsync(final InternalConnection connection, final SingleResultCallback callback) { beginAsync().thenRun(c -> { assertTrue(connection.opened()); - authenticationLoopAsync(connection, connection.getDescription(), operationContext, c); + authenticationLoopAsync(connection, connection.getDescription(), operationContextWithoutSession(operationContext), c); }).finish(callback); } diff --git a/driver-kotlin-extensions/src/main/kotlin/com/mongodb/kotlin/client/model/Properties.kt b/driver-kotlin-extensions/src/main/kotlin/com/mongodb/kotlin/client/model/Properties.kt index fc8a4e94e87..97ebae27d63 100644 --- a/driver-kotlin-extensions/src/main/kotlin/com/mongodb/kotlin/client/model/Properties.kt +++ b/driver-kotlin-extensions/src/main/kotlin/com/mongodb/kotlin/client/model/Properties.kt @@ -32,7 +32,7 @@ import kotlin.reflect.jvm.javaField import org.bson.codecs.pojo.annotations.BsonId import org.bson.codecs.pojo.annotations.BsonProperty -private val pathCache: MutableMap by lazySoft { ConcurrentHashMap() } +private val pathCache: MutableMap by lazySoft { ConcurrentHashMap() } /** Returns a composed property. For example Friend::address / Address::postalCode = "address.postalCode". */ public operator fun KProperty1.div(p2: KProperty1): KProperty1 = @@ -71,8 +71,7 @@ public fun KProperty.path(): String { return if (this is KPropertyPath<*, T>) { this.name } else { - pathCache.computeIfAbsent(this.toString()) { - + pathCache.computeIfAbsent(hashCode()) { // Check serial name - Note kotlinx.serialization.SerialName may not be on the class // path val serialName = diff --git a/driver-kotlin-extensions/src/main/kotlin/com/mongodb/kotlin/client/property/KPropertyPath.kt b/driver-kotlin-extensions/src/main/kotlin/com/mongodb/kotlin/client/property/KPropertyPath.kt index a460266f098..1aaa3f622e9 100644 --- a/driver-kotlin-extensions/src/main/kotlin/com/mongodb/kotlin/client/property/KPropertyPath.kt +++ b/driver-kotlin-extensions/src/main/kotlin/com/mongodb/kotlin/client/property/KPropertyPath.kt @@ -20,6 +20,7 @@ package com.mongodb.kotlin.client.property import com.mongodb.annotations.Sealed import com.mongodb.kotlin.client.model.path +import java.util.Objects import kotlin.reflect.KParameter import kotlin.reflect.KProperty1 import kotlin.reflect.KType @@ -84,6 +85,15 @@ public open class KPropertyPath( override fun callBy(args: Map): R = unSupportedOperation() override fun get(receiver: T): R = unSupportedOperation() override fun getDelegate(receiver: T): Any? = unSupportedOperation() + override fun hashCode(): Int = Objects.hash(previous, property, name) + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + other as KPropertyPath<*, *> + return Objects.equals(previous, other.previous) && + Objects.equals(property, other.property) && + Objects.equals(name, other.name) + } public companion object { @@ -121,6 +131,13 @@ public open class KPropertyPath( override fun get(receiver: T): R = unSupportedOperation() override fun getDelegate(receiver: T): Any? = unSupportedOperation() override fun invoke(p1: T): R = unSupportedOperation() + override fun hashCode(): Int = Objects.hash(previous, name) + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + other as CustomProperty<*, *> + return Objects.equals(previous, other.previous) && Objects.equals(name, other.name) + } } /** Provides "fake" property with custom name. */ diff --git a/driver-kotlin-extensions/src/test/kotlin/com/mongodb/kotlin/client/model/KPropertiesTest.kt b/driver-kotlin-extensions/src/test/kotlin/com/mongodb/kotlin/client/model/KPropertiesTest.kt index 0007c6251ea..51e09675d72 100644 --- a/driver-kotlin-extensions/src/test/kotlin/com/mongodb/kotlin/client/model/KPropertiesTest.kt +++ b/driver-kotlin-extensions/src/test/kotlin/com/mongodb/kotlin/client/model/KPropertiesTest.kt @@ -139,4 +139,20 @@ class KPropertiesTest { assertThrows { property.get(restaurant) } assertThrows { property.getDelegate(restaurant) } } + + @Test + fun testNoCacheCollisions() { + for (i in 1.rangeTo(25_000)) { + assertEquals("reviews.$i", Restaurant::reviews.pos(i).path()) + assertEquals("reviews.$[identifier$i]", Restaurant::reviews.filteredPosOp("identifier$i").path()) + assertEquals("localeMap.$i", Restaurant::localeMap.keyProjection(i).path()) + + val x = i / 2 + assertEquals( + "reviews.$[identifier$x].rating", + (Restaurant::reviews.filteredPosOp("identifier$x") / Review::score).path()) + assertEquals("reviews.$x.rating", (Restaurant::reviews.pos(x) / Review::score).path()) + assertEquals("localeMap.$x.rating", (Restaurant::localeMap.keyProjection(x) / Review::score).path()) + } + } } diff --git a/driver-sync/src/test/functional/com/mongodb/internal/connection/OidcAuthenticationProseTests.java b/driver-sync/src/test/functional/com/mongodb/internal/connection/OidcAuthenticationProseTests.java index b6a23a576ce..2b0544f0c5a 100644 --- a/driver-sync/src/test/functional/com/mongodb/internal/connection/OidcAuthenticationProseTests.java +++ b/driver-sync/src/test/functional/com/mongodb/internal/connection/OidcAuthenticationProseTests.java @@ -24,9 +24,12 @@ import com.mongodb.MongoSecurityException; import com.mongodb.MongoSocketException; import com.mongodb.assertions.Assertions; +import com.mongodb.client.ClientSession; +import com.mongodb.client.FindIterable; import com.mongodb.client.Fixture; import com.mongodb.client.MongoClient; import com.mongodb.client.MongoClients; +import com.mongodb.client.MongoCollection; import com.mongodb.client.TestListener; import com.mongodb.event.CommandListener; import com.mongodb.lang.Nullable; @@ -334,12 +337,17 @@ public void test3p3UnexpectedErrorDoesNotClearCache() { @Test public void test4p1Reauthentication() { + testReauthentication(false); + } + + private void testReauthentication(final boolean inSession) { TestCallback callback = createCallback(); MongoClientSettings clientSettings = createSettings(callback); - try (MongoClient mongoClient = createMongoClient(clientSettings)) { + try (MongoClient mongoClient = createMongoClient(clientSettings); + ClientSession session = inSession ? mongoClient.startSession() : null) { failCommand(391, 1, "find"); // #. Perform a find operation that succeeds. - performFind(mongoClient); + performFind(mongoClient, session); } assertEquals(2, callback.invocations.get()); } @@ -392,6 +400,11 @@ private static void performInsert(final MongoClient mongoClient) { .insertOne(Document.parse("{ x: 1 }")); } + @Test + public void test4p5ReauthenticationInSession() { + testReauthentication(true); + } + @Test public void test5p1AzureSucceedsWithNoUsername() { assumeAzure(); @@ -914,12 +927,14 @@ private void assertFindFails( } } - private void performFind(final MongoClient mongoClient) { - mongoClient - .getDatabase("test") - .getCollection("test") - .find() - .first(); + private static void performFind(final MongoClient mongoClient) { + performFind(mongoClient, null); + } + + private static void performFind(final MongoClient mongoClient, @Nullable final ClientSession session) { + MongoCollection collection = mongoClient.getDatabase("test").getCollection("test"); + FindIterable findIterable = session == null ? collection.find() : collection.find(session); + findIterable.first(); } protected void delayNextFind() { diff --git a/gradle.properties b/gradle.properties index 7da38a9cbc6..99e74d41c84 100644 --- a/gradle.properties +++ b/gradle.properties @@ -14,7 +14,7 @@ # limitations under the License. # -version=5.5.0-SNAPSHOT +version=5.5.2-SNAPSHOT org.gradle.daemon=true org.gradle.jvmargs=-Dfile.encoding=UTF-8 -Duser.country=US -Duser.language=en