Skip to content

Commit

Permalink
mobile: Add an API to set node metadata in the EngineBuilder (envoypr…
Browse files Browse the repository at this point in the history
…oxy#30357)

This adds EngineBuilder::setNodeMetadata to set node.metadata field in Bootstrap configuration in addition to those that are automatically set by Envoy Mobile, i.e. api_id, api_version, and device_os.

Risk Level: low (new API addition)
Testing: unit test + integration test
Docs Changes: n/a
Release Notes: n/a
Platform Specific Features: mobile

Signed-off-by: Fredy Wijaya <[email protected]>
  • Loading branch information
fredyw authored Oct 23, 2023
1 parent 084a9bc commit be87c04
Show file tree
Hide file tree
Showing 18 changed files with 149 additions and 39 deletions.
2 changes: 2 additions & 0 deletions mobile/bazel/envoy_mobile_dependencies.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ def kotlin_dependencies(extra_maven_dependencies = []):
maven_install(
artifacts = [
"com.google.code.findbugs:jsr305:3.0.2",
# Java Proto Lite
"com.google.protobuf:protobuf-javalite:3.24.4",
# Kotlin
"org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.21",
"org.jetbrains.kotlin:kotlin-stdlib-common:1.6.21",
Expand Down
1 change: 1 addition & 0 deletions mobile/library/cc/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ envoy_cc_library(
deps = [
":envoy_engine_cc_lib_no_stamp",
"@envoy//source/common/common:assert_lib",
"@envoy//source/common/protobuf",
"@envoy_api//envoy/config/bootstrap/v3:pkg_cc_proto",
"@envoy_api//envoy/config/metrics/v3:pkg_cc_proto",
"@envoy_api//envoy/extensions/compression/brotli/decompressor/v3:pkg_cc_proto",
Expand Down
8 changes: 8 additions & 0 deletions mobile/library/cc/engine_builder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,11 @@ EngineBuilder& EngineBuilder::setNodeLocality(std::string region, std::string zo
return *this;
}

EngineBuilder& EngineBuilder::setNodeMetadata(ProtobufWkt::Struct node_metadata) {
node_metadata_ = std::move(node_metadata);
return *this;
}

#ifdef ENVOY_GOOGLE_GRPC
EngineBuilder& EngineBuilder::setXds(XdsBuilder xds_builder) {
xds_builder_ = std::move(xds_builder);
Expand Down Expand Up @@ -873,6 +878,9 @@ std::unique_ptr<envoy::config::bootstrap::v3::Bootstrap> EngineBuilder::generate
node->mutable_locality()->set_sub_zone(node_locality_->sub_zone);
}
ProtobufWkt::Struct& metadata = *node->mutable_metadata();
if (node_metadata_.has_value()) {
*node->mutable_metadata() = *node_metadata_;
}
(*metadata.mutable_fields())["app_id"].set_string_value(app_id_);
(*metadata.mutable_fields())["app_version"].set_string_value(app_version_);
(*metadata.mutable_fields())["device_os"].set_string_value(device_os_);
Expand Down
5 changes: 5 additions & 0 deletions mobile/library/cc/engine_builder.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

#include "envoy/config/bootstrap/v3/bootstrap.pb.h"

#include "source/common/protobuf/protobuf.h"

#include "absl/container/flat_hash_map.h"
#include "absl/types/optional.h"
#include "direct_response_testing.h"
Expand Down Expand Up @@ -182,6 +184,8 @@ class EngineBuilder {
EngineBuilder& setNodeId(std::string node_id);
// Sets the node.locality field in the Bootstrap configuration.
EngineBuilder& setNodeLocality(std::string region, std::string zone, std::string sub_zone);
// Sets the node.metadata field in the Bootstrap configuration.
EngineBuilder& setNodeMetadata(ProtobufWkt::Struct node_metadata);
#ifdef ENVOY_GOOGLE_GRPC
// Sets the xDS configuration for the Envoy Mobile engine.
//
Expand Down Expand Up @@ -244,6 +248,7 @@ class EngineBuilder {
bool platform_certificates_validation_on_ = false;
std::string node_id_;
absl::optional<NodeLocality> node_locality_ = absl::nullopt;
absl::optional<ProtobufWkt::Struct> node_metadata_ = absl::nullopt;
bool dns_cache_on_ = false;
int dns_cache_save_interval_seconds_ = 1;

Expand Down
2 changes: 2 additions & 0 deletions mobile/library/common/jni/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ cc_library(
"//library/common/types:managed_types_lib",
"//library/common/types:matcher_data_lib",
"@envoy//source/common/common:assert_lib",
"@envoy//source/common/protobuf",
],
)

Expand All @@ -68,6 +69,7 @@ cc_library(
"//library/common/jni/types:jni_exception_lib",
"//library/common/jni/types:jni_javavm_lib",
"//library/common/types:managed_types_lib",
"@envoy//source/common/protobuf",
],
# We need this to ensure that we link this into the .so even though there are no code references.
alwayslink = True,
Expand Down
37 changes: 22 additions & 15 deletions mobile/library/common/jni/jni_interface.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
#include <string>
#include <utility>

#include "source/common/protobuf/protobuf.h"

#include "library/cc/engine_builder.h"
#include "library/common/api/c_types.h"
#include "library/common/data/utility.h"
Expand Down Expand Up @@ -1219,7 +1221,7 @@ void configureBuilder(JNIEnv* env, jstring grpc_stats_domain, jlong connect_time
jboolean trust_chain_verification, jobjectArray filter_chain,
jobjectArray stat_sinks, jboolean enable_platform_certificates_validation,
jobjectArray runtime_guards, jstring node_id, jstring node_region,
jstring node_zone, jstring node_sub_zone,
jstring node_zone, jstring node_sub_zone, jbyteArray serialized_node_metadata,
Envoy::Platform::EngineBuilder& builder) {
builder.addConnectTimeoutSeconds((connect_timeout_seconds));
builder.addDnsRefreshSeconds((dns_refresh_seconds));
Expand Down Expand Up @@ -1286,6 +1288,9 @@ void configureBuilder(JNIEnv* env, jstring grpc_stats_domain, jlong connect_time
builder.setNodeLocality(native_node_region, getCppString(env, node_zone),
getCppString(env, node_sub_zone));
}
Envoy::ProtobufWkt::Struct node_metadata;
javaByteArrayToProto(env, serialized_node_metadata, &node_metadata);
builder.setNodeMetadata(node_metadata);
}

extern "C" JNIEXPORT jlong JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibrary_createBootstrap(
Expand All @@ -1307,22 +1312,24 @@ extern "C" JNIEXPORT jlong JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibr
jstring rtds_resource_name, jlong rtds_timeout_seconds, jstring xds_address, jlong xds_port,
jstring xds_auth_header, jstring xds_auth_token, jstring xds_jwt_token,
jlong xds_jwt_token_lifetime, jstring xds_root_certs, jstring xds_sni, jstring node_id,
jstring node_region, jstring node_zone, jstring node_sub_zone, jstring cds_resources_locator,
jlong cds_timeout_seconds, jboolean enable_cds) {
jstring node_region, jstring node_zone, jstring node_sub_zone,
jbyteArray serialized_node_metadata, jstring cds_resources_locator, jlong cds_timeout_seconds,
jboolean enable_cds) {
Envoy::Platform::EngineBuilder builder;

configureBuilder(
env, grpc_stats_domain, connect_timeout_seconds, dns_refresh_seconds,
dns_failure_refresh_seconds_base, dns_failure_refresh_seconds_max, dns_query_timeout_seconds,
dns_min_refresh_seconds, dns_preresolve_hostnames, enable_dns_cache,
dns_cache_save_interval_seconds, enable_drain_post_dns_refresh, enable_http3,
http3_connection_options, http3_client_connection_options, quic_hints,
enable_gzip_decompression, enable_brotli_decompression, enable_socket_tagging,
enable_interface_binding, h2_connection_keepalive_idle_interval_milliseconds,
h2_connection_keepalive_timeout_seconds, max_connections_per_host, stats_flush_seconds,
stream_idle_timeout_seconds, per_try_idle_timeout_seconds, app_version, app_id,
trust_chain_verification, filter_chain, stat_sinks, enable_platform_certificates_validation,
runtime_guards, node_id, node_region, node_zone, node_sub_zone, builder);
configureBuilder(env, grpc_stats_domain, connect_timeout_seconds, dns_refresh_seconds,
dns_failure_refresh_seconds_base, dns_failure_refresh_seconds_max,
dns_query_timeout_seconds, dns_min_refresh_seconds, dns_preresolve_hostnames,
enable_dns_cache, dns_cache_save_interval_seconds, enable_drain_post_dns_refresh,
enable_http3, http3_connection_options, http3_client_connection_options,
quic_hints, enable_gzip_decompression, enable_brotli_decompression,
enable_socket_tagging, enable_interface_binding,
h2_connection_keepalive_idle_interval_milliseconds,
h2_connection_keepalive_timeout_seconds, max_connections_per_host,
stats_flush_seconds, stream_idle_timeout_seconds, per_try_idle_timeout_seconds,
app_version, app_id, trust_chain_verification, filter_chain, stat_sinks,
enable_platform_certificates_validation, runtime_guards, node_id, node_region,
node_zone, node_sub_zone, serialized_node_metadata, builder);

#ifdef ENVOY_GOOGLE_GRPC
std::string native_xds_address = getCppString(env, xds_address);
Expand Down
7 changes: 7 additions & 0 deletions mobile/library/common/jni/jni_utility.cc
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,13 @@ std::vector<MatcherData> javaObjectArrayToMatcherData(JNIEnv* env, jobjectArray
return ret;
}

void javaByteArrayToProto(JNIEnv* env, jbyteArray source, Envoy::Protobuf::MessageLite* dest) {
jbyte* bytes = env->GetByteArrayElements(source, /* isCopy= */ nullptr);
jsize size = env->GetArrayLength(source);
ASSERT(dest->ParseFromArray(bytes, size));
env->ReleaseByteArrayElements(source, bytes, 0);
}

#define DEFINE_CALL_METHOD(JAVA_TYPE, JNI_TYPE) \
JNI_TYPE call##JAVA_TYPE##Method(JNIEnv* env, jobject object, jmethodID method_id, ...) { \
va_list args; \
Expand Down
5 changes: 5 additions & 0 deletions mobile/library/common/jni/jni_utility.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#include <string>
#include <vector>

#include "source/common/protobuf/protobuf.h"

#include "library/common/jni/import/jni_import.h"
#include "library/common/types/c_types.h"
#include "library/common/types/managed_envoy_headers.h"
Expand Down Expand Up @@ -119,6 +121,9 @@ void JavaArrayOfByteToString(JNIEnv* env, jbyteArray jbytes, std::string* out);
std::vector<MatcherData> javaObjectArrayToMatcherData(JNIEnv* env, jobjectArray array,
std::string& cluster_out);

/** Parses the proto from Java's byte array and stores the output into `dest` proto. */
void javaByteArrayToProto(JNIEnv* env, jbyteArray source, Envoy::Protobuf::MessageLite* dest);

// Helper functions for JNI's `Call<Type>Method` with proper exception handling in order to satisfy
// -Xcheck:jni.
// See
Expand Down
1 change: 1 addition & 0 deletions mobile/library/java/io/envoyproxy/envoymobile/engine/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ java_library(
deps = [
"//library/java/io/envoyproxy/envoymobile/engine/types:envoy_c_types_lib",
"@maven//:com_google_code_findbugs_jsr305",
"@maven//:com_google_protobuf_protobuf_javalite",
],
)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
package io.envoyproxy.envoymobile.engine;

import com.google.protobuf.Struct;
import java.util.Collections;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import java.lang.StringBuilder;
import javax.annotation.Nullable;

import io.envoyproxy.envoymobile.engine.types.EnvoyHTTPFilterFactory;
import io.envoyproxy.envoymobile.engine.types.EnvoyStringAccessor;
import io.envoyproxy.envoymobile.engine.types.EnvoyKeyValueStore;
import io.envoyproxy.envoymobile.engine.JniLibrary;

/* Typed configuration that may be used for starting Envoy. */
public class EnvoyConfiguration {
Expand Down Expand Up @@ -77,6 +74,7 @@ public enum TrustChainVerification {
public final String nodeRegion;
public final String nodeZone;
public final String nodeSubZone;
public final Struct nodeMetadata;
public final String cdsResourcesLocator;
public final Integer cdsTimeoutSeconds;
public final Boolean enableCds;
Expand Down Expand Up @@ -151,7 +149,7 @@ public enum TrustChainVerification {
* xDS server.
* @param xdsJwtToken the JWT token to use for authenticating
* with the xDS server.
* @param xdsTokenLifetime the lifetime of the JWT token.
* @param xdsJwtTokenLifetime the lifetime of the JWT token.
* @param xdsRootCerts the root certificates to use for the TLS
* handshake during connection establishment
* with the xDS management server.
Expand All @@ -161,6 +159,7 @@ public enum TrustChainVerification {
* @param nodeRegion the node region in the Node metadata.
* @param nodeZone the node zone in the Node metadata.
* @param nodeSubZone the node sub-zone in the Node metadata.
* @param nodeMetadata the node metadata.
* @param cdsResourcesLocator the resources locator for CDS.
* @param cdsTimeoutSeconds the timeout for CDS fetches.
* @param enableCds enables CDS, used because all CDS params
Expand All @@ -186,8 +185,8 @@ public EnvoyConfiguration(
String rtdsResourceName, Integer rtdsTimeoutSeconds, String xdsAddress, Integer xdsPort,
String xdsAuthHeader, String xdsAuthToken, String xdsJwtToken, Integer xdsJwtTokenLifetime,
String xdsRootCerts, String xdsSni, String nodeId, String nodeRegion, String nodeZone,
String nodeSubZone, String cdsResourcesLocator, Integer cdsTimeoutSeconds,
boolean enableCds) {
String nodeSubZone, Struct nodeMetadata, String cdsResourcesLocator,
Integer cdsTimeoutSeconds, boolean enableCds) {
JniLibrary.load();
this.grpcStatsDomain = grpcStatsDomain;
this.connectTimeoutSeconds = connectTimeoutSeconds;
Expand Down Expand Up @@ -257,6 +256,7 @@ public EnvoyConfiguration(
this.nodeRegion = nodeRegion;
this.nodeZone = nodeZone;
this.nodeSubZone = nodeSubZone;
this.nodeMetadata = nodeMetadata;
this.cdsResourcesLocator = cdsResourcesLocator;
this.cdsTimeoutSeconds = cdsTimeoutSeconds;
this.enableCds = enableCds;
Expand All @@ -268,25 +268,25 @@ public long createBootstrap() {
List<EnvoyNativeFilterConfig> reverseFilterChain = new ArrayList<>(nativeFilterChain);
Collections.reverse(reverseFilterChain);

byte[][] filter_chain = JniBridgeUtility.toJniBytes(reverseFilterChain);
byte[][] stats_sinks = JniBridgeUtility.stringsToJniBytes(statSinks);
byte[][] dns_preresolve = JniBridgeUtility.stringsToJniBytes(dnsPreresolveHostnames);
byte[][] runtime_guards = JniBridgeUtility.mapToJniBytes(runtimeGuards);
byte[][] quic_hints = JniBridgeUtility.mapToJniBytes(quicHints);
byte[][] filterChain = JniBridgeUtility.toJniBytes(reverseFilterChain);
byte[][] statsSinks = JniBridgeUtility.stringsToJniBytes(statSinks);
byte[][] dnsPreresolve = JniBridgeUtility.stringsToJniBytes(dnsPreresolveHostnames);
byte[][] runtimeGuards = JniBridgeUtility.mapToJniBytes(this.runtimeGuards);
byte[][] quicHints = JniBridgeUtility.mapToJniBytes(this.quicHints);

return JniLibrary.createBootstrap(
grpcStatsDomain, connectTimeoutSeconds, dnsRefreshSeconds, dnsFailureRefreshSecondsBase,
dnsFailureRefreshSecondsMax, dnsQueryTimeoutSeconds, dnsMinRefreshSeconds, dns_preresolve,
dnsFailureRefreshSecondsMax, dnsQueryTimeoutSeconds, dnsMinRefreshSeconds, dnsPreresolve,
enableDNSCache, dnsCacheSaveIntervalSeconds, enableDrainPostDnsRefresh, enableHttp3,
http3ConnectionOptions, http3ClientConnectionOptions, quic_hints, enableGzipDecompression,
http3ConnectionOptions, http3ClientConnectionOptions, quicHints, enableGzipDecompression,
enableBrotliDecompression, enableSocketTagging, enableInterfaceBinding,
h2ConnectionKeepaliveIdleIntervalMilliseconds, h2ConnectionKeepaliveTimeoutSeconds,
maxConnectionsPerHost, statsFlushSeconds, streamIdleTimeoutSeconds,
perTryIdleTimeoutSeconds, appVersion, appId, enforceTrustChainVerification, filter_chain,
stats_sinks, enablePlatformCertificatesValidation, runtime_guards, rtdsResourceName,
perTryIdleTimeoutSeconds, appVersion, appId, enforceTrustChainVerification, filterChain,
statsSinks, enablePlatformCertificatesValidation, runtimeGuards, rtdsResourceName,
rtdsTimeoutSeconds, xdsAddress, xdsPort, xdsAuthHeader, xdsAuthToken, xdsJwtToken,
xdsJwtTokenLifetime, xdsRootCerts, xdsSni, nodeId, nodeRegion, nodeZone, nodeSubZone,
cdsResourcesLocator, cdsTimeoutSeconds, enableCds);
nodeMetadata.toByteArray(), cdsResourcesLocator, cdsTimeoutSeconds, enableCds);
}

static class ConfigurationException extends RuntimeException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import io.envoyproxy.envoymobile.engine.types.EnvoyEventTracker;
import io.envoyproxy.envoymobile.engine.types.EnvoyLogger;
import io.envoyproxy.envoymobile.engine.types.EnvoyOnEngineRunning;

import java.nio.ByteBuffer;

public class JniLibrary {
Expand Down Expand Up @@ -322,5 +321,6 @@ public static native long createBootstrap(
long rtdsTimeoutSeconds, String xdsAddress, long xdsPort, String xdsAuthenticationHeader,
String xdsAuthenticationToken, String xdsJwtToken, long xdsJwtTokenLifetime,
String xdsRootCerts, String xdsSni, String nodeId, String nodeRegion, String nodeZone,
String nodeSubZone, String cdsResourcesLocator, long cdsTimeoutSeconds, boolean enableCds);
String nodeSubZone, byte[] nodeMetadata, String cdsResourcesLocator, long cdsTimeoutSeconds,
boolean enableCds);
}
1 change: 1 addition & 0 deletions mobile/library/java/org/chromium/net/impl/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ android_library(
"//library/java/io/envoyproxy/envoymobile/utilities",
"//library/java/org/chromium/net",
"//library/java/org/chromium/net/urlconnection",
"@maven//:com_google_protobuf_protobuf_javalite",
artifact("androidx.annotation:annotation"),
],
)
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import android.content.Context;
import androidx.annotation.VisibleForTesting;
import com.google.protobuf.Struct;
import io.envoyproxy.envoymobile.engine.AndroidEngineImpl;
import io.envoyproxy.envoymobile.engine.AndroidJniLibrary;
import io.envoyproxy.envoymobile.engine.AndroidNetworkMonitor;
Expand Down Expand Up @@ -135,7 +136,8 @@ mEnableDrainPostDnsRefresh, quicEnabled(), quicConnectionOptions(),
/*rtdsResourceName=*/"", /*rtdsTimeoutSeconds=*/0, /*xdsAddress=*/"",
/*xdsPort=*/0, /*xdsAuthenticationHeader=*/"", /*xdsAuthenticationToken=*/"",
/*xdsJwtToken=*/"", /*xdsJwtTokenLifetime=*/0, /*xdsSslRootCerts=*/"",
/*xdsSni=*/"", mNodeId, mNodeRegion, mNodeZone, mNodeSubZone, /*cdsResourcesLocator=*/"",
/*xdsSni=*/"", mNodeId, mNodeRegion, mNodeZone, mNodeSubZone, Struct.getDefaultInstance(),
/*cdsResourcesLocator=*/"",
/*cdsTimeoutSeconds=*/0, /*enableCds=*/false);
}
}
14 changes: 14 additions & 0 deletions mobile/library/kotlin/io/envoyproxy/envoymobile/EngineBuilder.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.envoyproxy.envoymobile

import com.google.protobuf.Struct
import io.envoyproxy.envoymobile.engine.EnvoyConfiguration
import io.envoyproxy.envoymobile.engine.EnvoyConfiguration.TrustChainVerification
import io.envoyproxy.envoymobile.engine.EnvoyEngine
Expand Down Expand Up @@ -205,6 +206,7 @@ open class EngineBuilder(private val configuration: BaseConfiguration = Standard
private var nodeRegion: String = ""
private var nodeZone: String = ""
private var nodeSubZone: String = ""
private var nodeMetadata: Struct = Struct.getDefaultInstance()
private var xdsBuilder: XdsBuilder? = null

/**
Expand Down Expand Up @@ -636,6 +638,17 @@ open class EngineBuilder(private val configuration: BaseConfiguration = Standard
return this
}

/**
* Sets the node.metadata field in the Bootstrap configuration.
*
* @param metadata the metadata of the node.
* @return this builder.
*/
fun setNodeMetadata(metadata: Struct): EngineBuilder {
this.nodeMetadata = metadata
return this
}

/**
* Sets the xDS configuration for the Envoy Mobile engine.
*
Expand Down Expand Up @@ -729,6 +742,7 @@ open class EngineBuilder(private val configuration: BaseConfiguration = Standard
nodeRegion,
nodeZone,
nodeSubZone,
nodeMetadata,
xdsBuilder?.cdsResourcesLocator,
xdsBuilder?.cdsTimeoutInSeconds ?: 0,
xdsBuilder?.enableCds ?: false,
Expand Down
Loading

0 comments on commit be87c04

Please sign in to comment.