Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable container and Kubernetes awareness for improved telemetry. #1235

Merged
merged 6 commits into from
Oct 30, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This useful addon to Mockito allows for the mocking of static methods.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice

testImplementation 'cglib:cglib-nodep:2.2.2'
testImplementation 'org.objenesis:objenesis:1.3'
testImplementation 'org.hamcrest:hamcrest-all:1.3'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;

/**
* <p>This class is not part of the public API and may be removed or changed at any time</p>
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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 {
Copy link
Member Author

@vbabanin vbabanin Oct 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the introduction of new parameters in the env section related to containers/orchestration, I've renamed this enum in accordance with the specification, as it contains metadata of FaaS environment.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ack

AWS_LAMBDA("aws.lambda"),
AZURE_FUNC("azure.func"),
GCP_FUNC("gcp.func"),
Expand All @@ -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;
}

Expand Down Expand Up @@ -225,6 +236,80 @@ 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 {
Expand All @@ -235,29 +320,29 @@ private static Integer getEnvInteger(final String name) {
}
}

static Environment getEnvironment() {
List<Environment> result = new ArrayList<>();
static FaasEnvironment getFaasEnvironment() {
List<FaasEnvironment> result = new ArrayList<>();
String awsExecutionEnv = System.getenv("AWS_EXECUTION_ENV");

if (System.getenv("VERCEL") != null) {
result.add(Environment.VERCEL);
result.add(FaasEnvironment.VERCEL);
}
if ((awsExecutionEnv != null && awsExecutionEnv.startsWith("AWS_Lambda_"))
|| System.getenv("AWS_LAMBDA_RUNTIME_API") != null) {
result.add(Environment.AWS_LAMBDA);
result.add(FaasEnvironment.AWS_LAMBDA);
}
if (System.getenv("FUNCTIONS_WORKER_RUNTIME") != null) {
result.add(Environment.AZURE_FUNC);
result.add(FaasEnvironment.AZURE_FUNC);
}
if (System.getenv("K_SERVICE") != null || System.getenv("FUNCTION_NAME") != null) {
result.add(Environment.GCP_FUNC);
result.add(FaasEnvironment.GCP_FUNC);
}
// vercel takes precedence over aws.lambda
if (result.equals(Arrays.asList(Environment.VERCEL, Environment.AWS_LAMBDA))) {
return Environment.VERCEL;
if (result.equals(Arrays.asList(FaasEnvironment.VERCEL, FaasEnvironment.AWS_LAMBDA))) {
return FaasEnvironment.VERCEL;
}
if (result.size() != 1) {
return Environment.UNKNOWN;
return FaasEnvironment.UNKNOWN;
}
return result.get(0);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,12 @@
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.mockito.MockedStatic;
import org.mockito.Mockito;

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

Expand All @@ -42,6 +47,9 @@

/**
* See <a href="https://github.com/mongodb/specifications/blob/master/source/mongodb-handshake/handshake.rst#test-plan">spec</a>
*
* <p>
* NOTE: This class also contains tests that aren't categorized as Prose tests.
*/
public class ClientMetadataHelperProseTest {
private static final String APP_NAME = "app name";
Expand Down Expand Up @@ -168,6 +176,43 @@ 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<Files> pathsMockedStatic = Mockito.mockStatic(Files.class)) {
Path path = Paths.get("/.dockerenv");
pathsMockedStatic.when(() -> Files.exists(path)).thenReturn(true);

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': {'runtime': 'docker', "
+ "'orchestrator': 'kubernetes'}}"));
BsonDocument actual = createActualClientMetadataDocument();
assertEquals(expected, actual);

performHello();
});
}
}

@Test
public void testLimitForDriverVersion() {
// should create client metadata document and exclude the extra driver info if its too verbose
Expand Down