diff --git a/build.gradle b/build.gradle index 73352795017..5d75c92139b 100644 --- a/build.gradle +++ b/build.gradle @@ -257,6 +257,7 @@ configure(javaCodeCheckedProjects) { testImplementation 'org.spockframework:spock-core' testImplementation 'org.spockframework:spock-junit4' testImplementation("org.mockito:mockito-core:3.8.0") + testImplementation("org.mockito:mockito-inline:3.8.0") testImplementation 'cglib:cglib-nodep:2.2.2' testImplementation 'org.objenesis:objenesis:1.3' testImplementation 'org.hamcrest:hamcrest-all:1.3' diff --git a/driver-core/src/main/com/mongodb/internal/connection/ClientMetadataHelper.java b/driver-core/src/main/com/mongodb/internal/connection/ClientMetadataHelper.java index 500e610889f..5ca1e53497f 100644 --- a/driver-core/src/main/com/mongodb/internal/connection/ClientMetadataHelper.java +++ b/driver-core/src/main/com/mongodb/internal/connection/ClientMetadataHelper.java @@ -29,7 +29,9 @@ import org.bson.codecs.EncoderContext; import org.bson.io.BasicOutputBuffer; +import java.io.File; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -38,6 +40,7 @@ import static com.mongodb.assertions.Assertions.isTrueArgument; import static java.lang.String.format; import static java.lang.System.getProperty; +import static java.nio.file.Paths.get; /** *
This class is not part of the public API and may be removed or changed at any time
@@ -98,17 +101,26 @@ public static BsonDocument createClientMetadataDocument(@Nullable final String a putAtPath(d, "driver.name", listToString(fullDriverInfo.getDriverNames())); putAtPath(d, "driver.version", listToString(fullDriverInfo.getDriverVersions())); }); + // optional fields: - Environment environment = getEnvironment(); + FaasEnvironment faasEnvironment = getFaasEnvironment(); + ContainerRuntime containerRuntime = ContainerRuntime.determineExecutionContainer(); + Orchestrator orchestrator = Orchestrator.determineExecutionOrchestrator(); + tryWithLimit(client, d -> putAtPath(d, "platform", listToString(baseDriverInfor.getDriverPlatforms()))); tryWithLimit(client, d -> putAtPath(d, "platform", listToString(fullDriverInfo.getDriverPlatforms()))); - tryWithLimit(client, d -> putAtPath(d, "env.name", environment.getName())); tryWithLimit(client, d -> putAtPath(d, "os.name", getOperatingSystemName())); tryWithLimit(client, d -> putAtPath(d, "os.architecture", getProperty("os.arch", "unknown"))); tryWithLimit(client, d -> putAtPath(d, "os.version", getProperty("os.version", "unknown"))); - tryWithLimit(client, d -> putAtPath(d, "env.timeout_sec", environment.getTimeoutSec())); - tryWithLimit(client, d -> putAtPath(d, "env.memory_mb", environment.getMemoryMb())); - tryWithLimit(client, d -> putAtPath(d, "env.region", environment.getRegion())); + + tryWithLimit(client, d -> putAtPath(d, "env.name", faasEnvironment.getName())); + tryWithLimit(client, d -> putAtPath(d, "env.timeout_sec", faasEnvironment.getTimeoutSec())); + tryWithLimit(client, d -> putAtPath(d, "env.memory_mb", faasEnvironment.getMemoryMb())); + tryWithLimit(client, d -> putAtPath(d, "env.region", faasEnvironment.getRegion())); + + tryWithLimit(client, d -> putAtPath(d, "env.container.runtime", containerRuntime.getName())); + tryWithLimit(client, d -> putAtPath(d, "env.container.orchestrator", orchestrator.getName())); + return client; } @@ -168,8 +180,7 @@ static boolean clientMetadataDocumentTooLarge(final BsonDocument document) { new BsonDocumentCodec().encode(new BsonBinaryWriter(buffer), document, EncoderContext.builder().build()); return buffer.getPosition() > MAXIMUM_CLIENT_METADATA_ENCODED_SIZE; } - - private enum Environment { + private enum FaasEnvironment { AWS_LAMBDA("aws.lambda"), AZURE_FUNC("azure.func"), GCP_FUNC("gcp.func"), @@ -179,7 +190,7 @@ private enum Environment { @Nullable private final String name; - Environment(@Nullable final String name) { + FaasEnvironment(@Nullable final String name) { this.name = name; } @@ -225,6 +236,81 @@ public String getRegion() { } } + public enum ContainerRuntime { + DOCKER("docker") { + @Override + boolean isCurrentRuntimeContainer() { + try { + return Files.exists(get(File.separator + ".dockerenv")); + } catch (Exception e) { + return false; + // NOOP. This could be a SecurityException. + } + } + }, + UNKNOWN(null); + + @Nullable + private final String name; + + ContainerRuntime(@Nullable final String name) { + this.name = name; + } + + @Nullable + public String getName() { + return name; + } + + boolean isCurrentRuntimeContainer() { + return false; + } + + static ContainerRuntime determineExecutionContainer() { + for (ContainerRuntime allegedContainer : ContainerRuntime.values()) { + if (allegedContainer.isCurrentRuntimeContainer()) { + return allegedContainer; + } + } + return UNKNOWN; + } + } + + private enum Orchestrator { + K8S("kubernetes") { + @Override + boolean isCurrentOrchestrator() { + return System.getenv("KUBERNETES_SERVICE_HOST") != null; + } + }, + UNKNOWN(null); + + @Nullable + private final String name; + + Orchestrator(@Nullable final String name) { + this.name = name; + } + + @Nullable + public String getName() { + return name; + } + + boolean isCurrentOrchestrator() { + return false; + } + + static Orchestrator determineExecutionOrchestrator() { + for (Orchestrator alledgedOrchestrator : Orchestrator.values()) { + if (alledgedOrchestrator.isCurrentOrchestrator()) { + return alledgedOrchestrator; + } + } + return UNKNOWN; + } + } + @Nullable private static Integer getEnvInteger(final String name) { try { @@ -235,29 +321,29 @@ private static Integer getEnvInteger(final String name) { } } - static Environment getEnvironment() { - List
+ * NOTE: This class also contains tests that aren't categorized as Prose tests.
*/
public class ClientMetadataHelperProseTest {
private static final String APP_NAME = "app name";
@@ -168,6 +177,61 @@ public void test08NotLambda() {
// Additional tests, not specified as prose tests:
+ @Test
+ void testKubernetesMetadataIncluded() {
+ withWrapper()
+ .withEnvironmentVariable("AWS_EXECUTION_ENV", "AWS_Lambda_java8")
+ .withEnvironmentVariable("KUBERNETES_SERVICE_HOST", "kubernetes.default.svc.cluster.local")
+ .run(() -> {
+ BsonDocument expected = createExpectedClientMetadataDocument(APP_NAME);
+ expected.put("env", BsonDocument.parse("{'name': 'aws.lambda', 'container': {'orchestrator': 'kubernetes'}}"));
+ BsonDocument actual = createActualClientMetadataDocument();
+ assertEquals(expected, actual);
+
+ performHello();
+ });
+ }
+
+ @Test
+ void testDockerMetadataIncluded() {
+ try (MockedStatic