xds: reimplement bootstrapping with new bootstrap file format design (#6201)

This change reimplements Bootstrapper with new design of bootstrap file format, with no longer using ApiConfigSource proto. The new JSON format of the bootstrap file contains a top level "node" and a "xds_server" object, which contains a "server_uri" string and a `channel_creds` list for channel credentials.

Unknown fields in the bootstrap file are allowed by ignored so that the implementation does not break when new fields are added in the future. Therefore, we cannot simply create a custom proto file and use proto util to parse it.
This commit is contained in:
Chengyuan Zhang 2019-10-01 14:48:28 -07:00 committed by GitHub
parent 9668102f05
commit e448f9c7d3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 324 additions and 204 deletions

View File

@ -20,9 +20,8 @@ dependencies {
compile project(':grpc-protobuf'),
project(':grpc-stub'),
project(':grpc-core'),
project(':grpc-netty'),
project(':grpc-services'),
project(':grpc-auth')
project(':grpc-services')
compile (libraries.pgv) {
// PGV depends on com.google.protobuf:protobuf-java 3.6.1 conflicting with :grpc-protobuf
exclude group: 'com.google.protobuf'
@ -31,12 +30,6 @@ dependencies {
// prefer 26.0-android from libraries instead of 20.0
exclude group: 'com.google.guava', module: 'guava'
}
compile (libraries.google_auth_oauth2_http) {
// prefer 26.0-android from libraries instead of 25.1-android
exclude group: 'com.google.guava', module: 'guava'
// prefer 0.19.2 from libraries instead of 0.18.0
exclude group: 'io.opencensus', module: 'opencensus-api'
}
testCompile project(':grpc-core').sourceSets.test.output

View File

@ -16,19 +16,24 @@
package io.grpc.xds;
import com.google.auth.oauth2.ComputeEngineCredentials;
import com.google.common.annotations.VisibleForTesting;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.util.JsonFormat;
import io.envoyproxy.envoy.api.v2.core.ApiConfigSource;
import io.envoyproxy.envoy.api.v2.core.ApiConfigSource.ApiType;
import com.google.protobuf.ListValue;
import com.google.protobuf.NullValue;
import com.google.protobuf.Struct;
import com.google.protobuf.Value;
import io.envoyproxy.envoy.api.v2.core.Locality;
import io.envoyproxy.envoy.api.v2.core.Node;
import io.grpc.CallCredentials;
import io.grpc.auth.MoreCallCredentials;
import io.grpc.internal.JsonParser;
import io.grpc.internal.JsonUtil;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
/**
@ -47,9 +52,9 @@ abstract class Bootstrapper {
}
/**
* Returns the canonical name of the traffic director to be connected to.
* Returns the URI the traffic director to be connected to.
*/
abstract String getBalancerName();
abstract String getServerUri();
/**
* Returns a {@link Node} message with project/network metadata in it to be included in
@ -60,7 +65,7 @@ abstract class Bootstrapper {
/**
* Returns the credentials to use when communicating with the xDS server.
*/
abstract CallCredentials getCallCredentials();
abstract List<ChannelCreds> getChannelCredentials();
@VisibleForTesting
static final class FileBasedBootstrapper extends Bootstrapper {
@ -68,10 +73,9 @@ abstract class Bootstrapper {
private static final Exception failToBootstrapException;
private static final Bootstrapper defaultInstance;
private final String balancerName;
private final String serverUri;
private final Node node;
// TODO(chengyuanzhang): Add configuration for call credentials loaded from bootstrap file.
// hard-coded for alpha release.
private final List<ChannelCreds> channelCredsList;
static {
Bootstrapper instance = null;
@ -86,23 +90,15 @@ abstract class Bootstrapper {
}
@VisibleForTesting
FileBasedBootstrapper(Bootstrap bootstrapConfig) throws IOException {
ApiConfigSource serverConfig = bootstrapConfig.getXdsServer();
if (!serverConfig.getApiType().equals(ApiType.GRPC)) {
throw new IOException("Unexpected api type: " + serverConfig.getApiType().toString());
}
if (serverConfig.getGrpcServicesCount() != 1) {
throw new IOException(
"Unexpected number of gRPC services: expected: 1, actual: "
+ serverConfig.getGrpcServicesCount());
}
balancerName = serverConfig.getGrpcServices(0).getGoogleGrpc().getTargetUri();
node = bootstrapConfig.getNode();
FileBasedBootstrapper(BootstrapInfo bootstrapInfo) {
this.serverUri = bootstrapInfo.serverConfig.uri;
this.node = bootstrapInfo.node;
this.channelCredsList = bootstrapInfo.serverConfig.channelCredsList;
}
@Override
String getBalancerName() {
return balancerName;
String getServerUri() {
return serverUri;
}
@Override
@ -111,12 +107,12 @@ abstract class Bootstrapper {
}
@Override
CallCredentials getCallCredentials() {
return MoreCallCredentials.from(ComputeEngineCredentials.create());
List<ChannelCreds> getChannelCredentials() {
return Collections.unmodifiableList(channelCredsList);
}
}
private static Bootstrap readConfig() throws IOException {
private static BootstrapInfo readConfig() throws IOException {
String filePath = System.getenv(BOOTSTRAP_PATH_SYS_ENV_VAR);
if (filePath == null) {
throw new IOException("Environment variable " + BOOTSTRAP_PATH_SYS_ENV_VAR + " not found.");
@ -125,9 +121,161 @@ abstract class Bootstrapper {
}
@VisibleForTesting
static Bootstrap parseConfig(String rawData) throws InvalidProtocolBufferException {
Bootstrap.Builder bootstrapBuilder = Bootstrap.newBuilder();
JsonFormat.parser().merge(rawData, bootstrapBuilder);
return bootstrapBuilder.build();
static BootstrapInfo parseConfig(String rawData) throws IOException {
@SuppressWarnings("unchecked")
Map<String, ?> rawBootstrap = (Map<String, ?>) JsonParser.parse(rawData);
Map<String, ?> rawServerConfig = JsonUtil.getObject(rawBootstrap, "xds_server");
if (rawServerConfig == null) {
throw new IOException("Invalid bootstrap: 'xds_server' does not exist.");
}
// Field "server_uri" is required.
String serverUri = JsonUtil.getString(rawServerConfig, "server_uri");
if (serverUri == null) {
throw new IOException("Invalid bootstrap: 'xds_server : server_uri' does not exist.");
}
List<ChannelCreds> channelCredsOptions = new ArrayList<>();
List<?> rawChannelCredsList = JsonUtil.getList(rawServerConfig, "channel_creds");
// List of channel creds is optional.
if (rawChannelCredsList != null) {
List<Map<String, ?>> channelCredsList = JsonUtil.checkObjectList(rawChannelCredsList);
for (Map<String, ?> channelCreds : channelCredsList) {
String type = JsonUtil.getString(channelCreds, "type");
if (type == null) {
throw new IOException("Invalid bootstrap: 'channel_creds' contains unknown type.");
}
ChannelCreds creds = new ChannelCreds(type, JsonUtil.getObject(channelCreds, "config"));
channelCredsOptions.add(creds);
}
}
ServerConfig serverConfig = new ServerConfig(serverUri, channelCredsOptions);
Map<String, ?> rawNode = JsonUtil.getObject(rawBootstrap, "node");
if (rawNode == null) {
throw new IOException("Invalid bootstrap: 'node' does not exist.");
}
// Fields in "node" are not checked.
Node.Builder nodeBuilder = Node.newBuilder();
String id = JsonUtil.getString(rawNode, "id");
if (id != null) {
nodeBuilder.setId(id);
}
String cluster = JsonUtil.getString(rawNode, "cluster");
if (cluster != null) {
nodeBuilder.setCluster(cluster);
}
Map<String, ?> metadata = JsonUtil.getObject(rawNode, "metadata");
if (metadata != null) {
Struct.Builder structBuilder = Struct.newBuilder();
for (Map.Entry<String, ?> entry : metadata.entrySet()) {
structBuilder.putFields(entry.getKey(), convertToValue(entry.getValue()));
}
nodeBuilder.setMetadata(structBuilder);
}
Map<String, ?> rawLocality = JsonUtil.getObject(rawNode, "locality");
if (rawLocality != null) {
Locality.Builder localityBuilder = Locality.newBuilder();
String region = JsonUtil.getString(rawLocality, "region");
if (region == null) {
throw new IOException("Invalid bootstrap: malformed 'node : locality'.");
}
localityBuilder.setRegion(region);
if (rawLocality.containsKey("zone")) {
localityBuilder.setZone(JsonUtil.getString(rawLocality, "zone"));
}
if (rawLocality.containsKey("sub_zone")) {
localityBuilder.setSubZone(JsonUtil.getString(rawLocality, "sub_zone"));
}
nodeBuilder.setLocality(localityBuilder);
}
String buildVersion = JsonUtil.getString(rawNode, "build_version");
if (buildVersion != null) {
nodeBuilder.setBuildVersion(buildVersion);
}
return new BootstrapInfo(serverConfig, nodeBuilder.build());
}
/**
* Converts Java representation of the given JSON value to protobuf's {@link
* com.google.protobuf.Value} representation.
*
* <p>The given {@code rawObject} must be a valid JSON value in Java representation, which is
* either a {@code Map<String, ?>}, {@code List<?>}, {@code String}, {@code Double},
* {@code Boolean}, or {@code null}.
*/
private static Value convertToValue(Object rawObject) {
Value.Builder valueBuilder = Value.newBuilder();
if (rawObject == null) {
valueBuilder.setNullValue(NullValue.NULL_VALUE);
} else if (rawObject instanceof Double) {
valueBuilder.setNumberValue((Double) rawObject);
} else if (rawObject instanceof String) {
valueBuilder.setStringValue((String) rawObject);
} else if (rawObject instanceof Boolean) {
valueBuilder.setBoolValue((Boolean) rawObject);
} else if (rawObject instanceof Map) {
Struct.Builder structBuilder = Struct.newBuilder();
@SuppressWarnings("unchecked")
Map<String, ?> map = (Map<String, ?>) rawObject;
for (Map.Entry<String, ?> entry : map.entrySet()) {
structBuilder.putFields(entry.getKey(), convertToValue(entry.getValue()));
}
valueBuilder.setStructValue(structBuilder);
} else if (rawObject instanceof List) {
ListValue.Builder listBuilder = ListValue.newBuilder();
List<?> list = (List<?>) rawObject;
for (Object obj : list) {
listBuilder.addValues(convertToValue(obj));
}
valueBuilder.setListValue(listBuilder);
}
return valueBuilder.build();
}
// TODO(chengyuanzhang): May need more complex structure for channel creds config representation.
static class ChannelCreds {
private final String type;
@Nullable
private final Map<String, ?> config;
@VisibleForTesting
ChannelCreds(String type, @Nullable Map<String, ?> config) {
this.type = type;
this.config = config;
}
String getType() {
return type;
}
@Nullable
Map<String, ?> getConfig() {
return config;
}
}
@VisibleForTesting
static class BootstrapInfo {
final ServerConfig serverConfig;
final Node node;
@VisibleForTesting
BootstrapInfo(ServerConfig serverConfig, Node node) {
this.serverConfig = serverConfig;
this.node = node;
}
}
@VisibleForTesting
static class ServerConfig {
final String uri;
final List<ChannelCreds> channelCredsList;
@VisibleForTesting
ServerConfig(String uri, List<ChannelCreds> channelCredsList) {
this.uri = uri;
this.channelCredsList = channelCredsList;
}
}
}

View File

@ -1,39 +0,0 @@
// Copyright 2019 The gRPC Authors
// All rights reserved.
//
// 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.
// An integration test service that covers all the method signature permutations
// of unary/streaming requests/responses.
syntax = "proto3";
package io.grpc.xds;
option java_outer_classname = "BootstrapProto";
option java_multiple_files = true;
option java_package = "io.grpc.xds";
import "envoy/api/v2/core/base.proto";
import "envoy/api/v2/core/config_source.proto";
// Configurations containing the information needed for xDS load balancer to bootstrap its
// communication with the xDS server.
// This proto message is defined for the convenience of parsing JSON bootstrap file in xDS load
// balancing policy only. It should not be used for any other purposes.
message Bootstrap {
// Metadata to be added to the Node message in xDS requests.
envoy.api.v2.core.Node node = 1 [json_name = "node"];
// Configurations including the name of the xDS server to contact, the credentials to use, etc.
envoy.api.v2.core.ApiConfigSource xds_server = 2 [json_name = "xds_server"];
}

View File

@ -18,17 +18,17 @@ package io.grpc.xds;
import static com.google.common.truth.Truth.assertThat;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.common.collect.ImmutableList;
import com.google.protobuf.Struct;
import com.google.protobuf.Value;
import io.envoyproxy.envoy.api.v2.core.ApiConfigSource;
import io.envoyproxy.envoy.api.v2.core.ApiConfigSource.ApiType;
import io.envoyproxy.envoy.api.v2.core.GrpcService;
import io.envoyproxy.envoy.api.v2.core.GrpcService.GoogleGrpc;
import io.envoyproxy.envoy.api.v2.core.Locality;
import io.envoyproxy.envoy.api.v2.core.Node;
import io.grpc.xds.Bootstrapper.BootstrapInfo;
import io.grpc.xds.Bootstrapper.ChannelCreds;
import io.grpc.xds.Bootstrapper.FileBasedBootstrapper;
import io.grpc.xds.Bootstrapper.ServerConfig;
import java.io.IOException;
import java.util.List;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
@ -42,162 +42,180 @@ public class BootstrapperTest {
@Rule public ExpectedException thrown = ExpectedException.none();
@Test
public void validBootstrap() throws IOException {
Bootstrap config =
Bootstrap.newBuilder()
.setNode(
Node.newBuilder()
.setId("ENVOY_NODE_ID")
.setLocality(
Locality.newBuilder().setZone("ENVOY_ZONE").setRegion("ENVOY_REGION"))
.setMetadata(
Struct.newBuilder()
.putFields("TRAFFICDIRECTOR_INTERCEPTION_PORT",
Value.newBuilder().setStringValue("ENVOY_PORT").build())
.putFields("TRAFFICDIRECTOR_NETWORK_NAME",
Value.newBuilder().setStringValue("VPC_NETWORK_NAME").build())))
.setXdsServer(ApiConfigSource.newBuilder()
.setApiType(ApiType.GRPC)
.addGrpcServices(
GrpcService.newBuilder()
.setGoogleGrpc(
GoogleGrpc.newBuilder()
.setTargetUri("trafficdirector.googleapis.com:443").build())))
public void validBootstrap() {
List<ChannelCreds> channelCredsList =
ImmutableList.of(new ChannelCreds("TLS", null), new ChannelCreds("LOAS", null));
ServerConfig serverConfig =
new ServerConfig("trafficdirector.googleapis.com:443", channelCredsList);
Node node =
Node.newBuilder()
.setId("ENVOY_NODE_ID")
.setCluster("ENVOY_CLUSTER")
.setLocality(
Locality.newBuilder()
.setRegion("ENVOY_REGION").setZone("ENVOY_ZONE").setSubZone("ENVOY_SUBZONE"))
.setMetadata(
Struct.newBuilder()
.putFields("TRAFFICDIRECTOR_INTERCEPTION_PORT",
Value.newBuilder().setStringValue("ENVOY_PORT").build())
.putFields("TRAFFICDIRECTOR_NETWORK_NAME",
Value.newBuilder().setStringValue("VPC_NETWORK_NAME").build()))
.build();
BootstrapInfo config = new BootstrapInfo(serverConfig, node);
Bootstrapper bootstrapper = new FileBasedBootstrapper(config);
assertThat(bootstrapper.getBalancerName()).isEqualTo("trafficdirector.googleapis.com:443");
assertThat(bootstrapper.getServerUri()).isEqualTo("trafficdirector.googleapis.com:443");
assertThat(bootstrapper.getNode())
.isEqualTo(
Node.newBuilder()
.setId("ENVOY_NODE_ID")
.setLocality(Locality.newBuilder().setZone("ENVOY_ZONE").setRegion("ENVOY_REGION"))
.setCluster("ENVOY_CLUSTER")
.setLocality(
Locality.newBuilder()
.setRegion("ENVOY_REGION")
.setZone("ENVOY_ZONE")
.setSubZone("ENVOY_SUBZONE"))
.setMetadata(
Struct.newBuilder()
.putFields("TRAFFICDIRECTOR_INTERCEPTION_PORT",
Value.newBuilder().setStringValue("ENVOY_PORT").build())
.putFields("TRAFFICDIRECTOR_NETWORK_NAME",
Value.newBuilder().setStringValue("VPC_NETWORK_NAME").build())
.build()).build());
.build())
.build());
assertThat(bootstrapper.getChannelCredentials()).hasSize(2);
assertThat(bootstrapper.getChannelCredentials().get(0).getType()).isEqualTo("TLS");
assertThat(bootstrapper.getChannelCredentials().get(0).getConfig()).isNull();
assertThat(bootstrapper.getChannelCredentials().get(1).getType()).isEqualTo("LOAS");
assertThat(bootstrapper.getChannelCredentials().get(1).getConfig()).isNull();
}
@Test
public void unsupportedApiType() throws IOException {
Bootstrap config =
Bootstrap.newBuilder()
.setNode(
Node.newBuilder()
.setId("ENVOY_NODE_ID")
.setLocality(
Locality.newBuilder().setZone("ENVOY_ZONE").setRegion("ENVOY_REGION"))
.setMetadata(
Struct.newBuilder()
.putFields("TRAFFICDIRECTOR_INTERCEPTION_PORT",
Value.newBuilder().setStringValue("ENVOY_PORT").build())
.putFields("TRAFFICDIRECTOR_NETWORK_NAME",
Value.newBuilder().setStringValue("VPC_NETWORK_NAME").build())))
.setXdsServer(ApiConfigSource.newBuilder()
.setApiType(ApiType.REST)
.addGrpcServices(
GrpcService.newBuilder()
.setGoogleGrpc(
GoogleGrpc.newBuilder()
.setTargetUri("trafficdirector.googleapis.com:443").build())))
.build();
public void parseBootstrap_validData() throws IOException {
String rawData = "{"
+ "\"node\": {"
+ "\"id\": \"ENVOY_NODE_ID\","
+ "\"cluster\": \"ENVOY_CLUSTER\","
+ "\"locality\": {"
+ "\"region\": \"ENVOY_REGION\", \"zone\": \"ENVOY_ZONE\", \"sub_zone\": \"ENVOY_SUBZONE\""
+ "},"
+ "\"metadata\": {"
+ "\"TRAFFICDIRECTOR_INTERCEPTION_PORT\": \"ENVOY_PORT\", "
+ "\"TRAFFICDIRECTOR_NETWORK_NAME\": \"VPC_NETWORK_NAME\""
+ "}"
+ "},"
+ "\"xds_server\": {"
+ "\"server_uri\": \"trafficdirector.googleapis.com:443\","
+ "\"channel_creds\": "
+ "[ {\"type\": \"TLS\"}, {\"type\": \"LOAS\"} ]"
+ "} "
+ "}";
thrown.expect(IOException.class);
thrown.expectMessage("Unexpected api type: REST");
new FileBasedBootstrapper(config);
BootstrapInfo info = Bootstrapper.parseConfig(rawData);
assertThat(info.serverConfig.uri).isEqualTo("trafficdirector.googleapis.com:443");
assertThat(info.serverConfig.channelCredsList).hasSize(2);
assertThat(info.serverConfig.channelCredsList.get(0).getType()).isEqualTo("TLS");
assertThat(info.serverConfig.channelCredsList.get(0).getConfig()).isNull();
assertThat(info.serverConfig.channelCredsList.get(1).getType()).isEqualTo("LOAS");
assertThat(info.serverConfig.channelCredsList.get(1).getConfig()).isNull();
assertThat(info.node).isEqualTo(
Node.newBuilder()
.setId("ENVOY_NODE_ID")
.setCluster("ENVOY_CLUSTER")
.setLocality(
Locality.newBuilder()
.setRegion("ENVOY_REGION").setZone("ENVOY_ZONE").setSubZone("ENVOY_SUBZONE"))
.setMetadata(
Struct.newBuilder()
.putFields("TRAFFICDIRECTOR_INTERCEPTION_PORT",
Value.newBuilder().setStringValue("ENVOY_PORT").build())
.putFields("TRAFFICDIRECTOR_NETWORK_NAME",
Value.newBuilder().setStringValue("VPC_NETWORK_NAME").build())
.build())
.build());
}
@Test
public void tooManyGrpcServices() throws IOException {
Bootstrap config =
Bootstrap.newBuilder()
.setNode(
Node.newBuilder()
.setId("ENVOY_NODE_ID")
.setLocality(
Locality.newBuilder().setZone("ENVOY_ZONE").setRegion("ENVOY_REGION"))
.setMetadata(
Struct.newBuilder()
.putFields("TRAFFICDIRECTOR_INTERCEPTION_PORT",
Value.newBuilder().setStringValue("ENVOY_PORT").build())
.putFields("TRAFFICDIRECTOR_NETWORK_NAME",
Value.newBuilder().setStringValue("VPC_NETWORK_NAME").build())))
.setXdsServer(ApiConfigSource.newBuilder()
.setApiType(ApiType.GRPC)
.addGrpcServices(
GrpcService.newBuilder()
.setGoogleGrpc(
GoogleGrpc.newBuilder()
.setTargetUri("trafficdirector.googleapis.com:443").build()))
.addGrpcServices(
GrpcService.newBuilder()
.setGoogleGrpc(
GoogleGrpc.newBuilder()
.setTargetUri("foobar.googleapis.com:443").build()))
)
.build();
thrown.expect(IOException.class);
thrown.expectMessage("Unexpected number of gRPC services: expected: 1, actual: 2");
new FileBasedBootstrapper(config);
}
@Test
public void parseBootstrap_emptyData() throws InvalidProtocolBufferException {
public void parseBootstrap_emptyData() throws IOException {
String rawData = "";
thrown.expect(InvalidProtocolBufferException.class);
thrown.expect(IOException.class);
Bootstrapper.parseConfig(rawData);
}
@Test
public void parseBootstrap_invalidNodeProto() throws InvalidProtocolBufferException {
public void parseBootstrap_minimumRequiredFields() throws IOException {
String rawData = "{"
+ "\"node\": {},"
+ "\"xds_server\": {"
+ "\"server_uri\": \"trafficdirector.googleapis.com:443\""
+ "}"
+ "}";
BootstrapInfo info = Bootstrapper.parseConfig(rawData);
assertThat(info.serverConfig.uri).isEqualTo("trafficdirector.googleapis.com:443");
assertThat(info.node).isEqualTo(Node.getDefaultInstance());
}
@Test
public void parseBootstrap_noNode() throws IOException {
String rawData = "{"
+ "\"xds_server\": {"
+ "\"server_uri\": \"trafficdirector.googleapis.com:443\","
+ "\"channel_creds\": "
+ "[ {\"type\": \"TLS\"}, {\"type\": \"LOAS\"} ]"
+ "} "
+ "}";
thrown.expect(IOException.class);
thrown.expectMessage("Invalid bootstrap: 'node' does not exist.");
Bootstrapper.parseConfig(rawData);
}
@Test
public void parseBootstrap_noXdsServer() throws IOException {
String rawData = "{"
+ "\"node\": {"
+ "\"id\": \"ENVOY_NODE_ID\","
+ "\"bad_field\": \"bad_value\""
+ "\"cluster\": \"ENVOY_CLUSTER\","
+ "\"locality\": {"
+ "\"zone\": \"ENVOY_ZONE\"},"
+ "\"region\": \"ENVOY_REGION\", \"zone\": \"ENVOY_ZONE\", \"sub_zone\": \"ENVOY_SUBZONE\""
+ "},"
+ "\"metadata\": {"
+ "\"TRAFFICDIRECTOR_INTERCEPTION_PORT\": \"ENVOY_PORT\", "
+ "\"TRAFFICDIRECTOR_NETWORK_NAME\": \"VPC_NETWORK_NAME\""
+ "}"
+ "}"
+ "}";
thrown.expect(IOException.class);
thrown.expectMessage("Invalid bootstrap: 'xds_server' does not exist.");
Bootstrapper.parseConfig(rawData);
}
@Test
public void parseBootstrap_noServerUri() throws IOException {
String rawData = "{"
+ "\"node\": {"
+ "\"id\": \"ENVOY_NODE_ID\","
+ "\"cluster\": \"ENVOY_CLUSTER\","
+ "\"locality\": {"
+ "\"region\": \"ENVOY_REGION\", \"zone\": \"ENVOY_ZONE\", \"sub_zone\": \"ENVOY_SUBZONE\""
+ "},"
+ "\"metadata\": {"
+ "\"TRAFFICDIRECTOR_INTERCEPTION_PORT\": \"ENVOY_PORT\", "
+ "\"TRAFFICDIRECTOR_NETWORK_NAME\": \"VPC_NETWORK_NAME\""
+ "}"
+ "},"
+ "\"xds_server\": {"
+ "\"api_type\": \"GRPC\","
+ "\"grpc_services\": "
+ "[ {\"google_grpc\": {\"target_uri\": \"trafficdirector.googleapis.com:443\"} } ]"
+ "\"channel_creds\": "
+ "[ {\"type\": \"TLS\"}, {\"type\": \"LOAS\"} ]"
+ "} "
+ "}";
thrown.expect(InvalidProtocolBufferException.class);
Bootstrapper.parseConfig(rawData);
}
@Test
public void parseBootstrap_invalidApiConfigSourceProto() throws InvalidProtocolBufferException {
String rawData = "{"
+ "\"node\": {"
+ "\"id\": \"ENVOY_NODE_ID\","
+ "\"locality\": {"
+ "\"zone\": \"ENVOY_ZONE\"},"
+ "\"metadata\": {"
+ "\"TRAFFICDIRECTOR_INTERCEPTION_PORT\": \"ENVOY_PORT\", "
+ "\"TRAFFICDIRECTOR_NETWORK_NAME\": \"VPC_NETWORK_NAME\""
+ "}"
+ "},"
+ "\"xds_server\": {"
+ "\"api_type\": \"GRPC\","
+ "\"bad_field\": \"bad_value\""
+ "\"grpc_services\": "
+ "[ {\"google_grpc\": {\"target_uri\": \"trafficdirector.googleapis.com:443\"} } ]"
+ "} "
+ "}";
thrown.expect(InvalidProtocolBufferException.class);
thrown.expect(IOException.class);
thrown.expectMessage("Invalid bootstrap: 'xds_server : server_uri' does not exist.");
Bootstrapper.parseConfig(rawData);
}
}