xds: support multiple xDS servers in bootstrap file (#6493)

Support bootstrap file containing multiple xDS servers, with each has its own server URI and channel credential options. Multiple xDS servers are provided in case of one not reachable. For now, we would only use the first one.

This change also formats JSON strings in bootstrap related tests and add several tests for parsing bootstrap JSON as completeness.

Implementation of XdsClient is changed to take in a list of xDS servers. But still, we only use the first one.
This commit is contained in:
Chengyuan Zhang 2019-12-05 16:45:28 -08:00 committed by GitHub
parent 29638780ae
commit d168632f82
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 353 additions and 121 deletions

View File

@ -73,29 +73,34 @@ public abstract class Bootstrapper {
@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.");
List<ServerInfo> servers = new ArrayList<>();
List<?> rawServerConfigs = JsonUtil.getList(rawBootstrap, "xds_servers");
if (rawServerConfigs == null) {
throw new IOException("Invalid bootstrap: 'xds_servers' does not exist.");
}
// Field "server_uri" is required.
String serverUri = JsonUtil.getString(rawServerConfig, "server_uri");
List<Map<String, ?>> serverConfigList = JsonUtil.checkObjectList(rawServerConfigs);
for (Map<String, ?> serverConfig : serverConfigList) {
String serverUri = JsonUtil.getString(serverConfig, "server_uri");
if (serverUri == null) {
throw new IOException("Invalid bootstrap: 'xds_server : server_uri' does not exist.");
throw new IOException("Invalid bootstrap: 'xds_servers' contains unknown server.");
}
List<ChannelCreds> channelCredsOptions = new ArrayList<>();
List<?> rawChannelCredsList = JsonUtil.getList(rawServerConfig, "channel_creds");
List<?> rawChannelCredsList = JsonUtil.getList(serverConfig, "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.");
throw new IOException("Invalid bootstrap: 'xds_servers' contains server with "
+ "unknown type 'channel_creds'.");
}
ChannelCreds creds = new ChannelCreds(type, JsonUtil.getObject(channelCreds, "config"));
channelCredsOptions.add(creds);
}
}
servers.add(new ServerInfo(serverUri, channelCredsOptions));
}
Node.Builder nodeBuilder = Node.newBuilder();
Map<String, ?> rawNode = JsonUtil.getObject(rawBootstrap, "node");
@ -133,7 +138,7 @@ public abstract class Bootstrapper {
}
nodeBuilder.setBuildVersion(GrpcUtil.getGrpcBuildVersion());
return new BootstrapInfo(serverUri, channelCredsOptions, nodeBuilder.build());
return new BootstrapInfo(servers, nodeBuilder.build());
}
/**
@ -202,27 +207,49 @@ public abstract class Bootstrapper {
}
}
/**
* Data class containing xDS server information, such as server URI and channel credential
* options to be used for communication.
*/
@Immutable
static class ServerInfo {
private final String serverUri;
private final List<ChannelCreds> channelCredsList;
@VisibleForTesting
ServerInfo(String serverUri, List<ChannelCreds> channelCredsList) {
this.serverUri = serverUri;
this.channelCredsList = channelCredsList;
}
String getServerUri() {
return serverUri;
}
List<ChannelCreds> getChannelCredentials() {
return Collections.unmodifiableList(channelCredsList);
}
}
/**
* Data class containing the results of reading bootstrap.
*/
@Immutable
public static class BootstrapInfo {
private final String serverUri;
private final List<ChannelCreds> channelCredsList;
private List<ServerInfo> servers;
private final Node node;
@VisibleForTesting
BootstrapInfo(String serverUri, List<ChannelCreds> channelCredsList, Node node) {
this.serverUri = serverUri;
this.channelCredsList = channelCredsList;
BootstrapInfo(List<ServerInfo> servers, Node node) {
this.servers = servers;
this.node = node;
}
/**
* Returns the URI the traffic director to be connected to.
* Returns the list of xDS servers to be connected to.
*/
String getServerUri() {
return serverUri;
List<ServerInfo> getServers() {
return Collections.unmodifiableList(servers);
}
/**
@ -232,11 +259,5 @@ public abstract class Bootstrapper {
return node;
}
/**
* Returns the credentials to use when communicating with the xDS server.
*/
List<ChannelCreds> getChannelCredentials() {
return Collections.unmodifiableList(channelCredsList);
}
}
}

View File

@ -41,6 +41,7 @@ import io.grpc.internal.ObjectPool;
import io.grpc.util.GracefulSwitchLoadBalancer;
import io.grpc.xds.Bootstrapper.BootstrapInfo;
import io.grpc.xds.Bootstrapper.ChannelCreds;
import io.grpc.xds.Bootstrapper.ServerInfo;
import io.grpc.xds.EnvoyProtoData.DropOverload;
import io.grpc.xds.EnvoyProtoData.Locality;
import io.grpc.xds.EnvoyProtoData.LocalityLbEndpoints;
@ -204,9 +205,21 @@ final class LookasideLb extends LoadBalancer {
new ErrorPicker(Status.UNAVAILABLE.withCause(e)));
return;
}
List<ServerInfo> serverList = bootstrapInfo.getServers();
if (serverList.isEmpty()) {
lookasideLbHelper.updateBalancingState(
TRANSIENT_FAILURE,
new ErrorPicker(
Status.UNAVAILABLE
.withDescription("No traffic director provided by bootstrap")));
return;
}
// Currently we only support using the first server from bootstrap.
ServerInfo serverInfo = serverList.get(0);
channel = initLbChannel(
lookasideLbHelper, bootstrapInfo.getServerUri(),
bootstrapInfo.getChannelCredentials());
lookasideLbHelper, serverInfo.getServerUri(),
serverInfo.getChannelCredentials());
xdsClientRef = new RefCountedXdsClientObjectPool(new XdsClientFactory() {
@Override
XdsClient createXdsClient() {

View File

@ -49,6 +49,7 @@ import io.grpc.alts.GoogleDefaultChannelBuilder;
import io.grpc.internal.BackoffPolicy;
import io.grpc.stub.StreamObserver;
import io.grpc.xds.Bootstrapper.ChannelCreds;
import io.grpc.xds.Bootstrapper.ServerInfo;
import io.grpc.xds.EnvoyProtoData.DropOverload;
import io.grpc.xds.EnvoyProtoData.Locality;
import io.grpc.xds.EnvoyProtoData.LocalityLbEndpoints;
@ -135,19 +136,14 @@ final class XdsClientImpl extends XdsClient {
private String ldsResourceName;
XdsClientImpl(
// URI of the management server to be connected to.
String serverUri,
List<ServerInfo> servers, // list of management servers
Node node,
// List of channel credential configurations for the channel to management server.
// Should pick the first supported one.
List<ChannelCreds> channelCredsList,
SynchronizationContext syncContext,
ScheduledExecutorService timeService,
BackoffPolicy.Provider backoffPolicyProvider,
Stopwatch stopwatch) {
this(
buildChannel(checkNotNull(serverUri, "serverUri"),
checkNotNull(channelCredsList, "channelCredsList")),
buildChannel(checkNotNull(servers, "servers")),
node,
syncContext,
timeService,
@ -319,9 +315,15 @@ final class XdsClientImpl extends XdsClient {
}
/**
* Builds a channel to the given server URI with the first supported channel creds config.
* Builds a channel to one of the provided management servers.
*
* <p>Note: currently we only support using the first server.
*/
private static ManagedChannel buildChannel(String serverUri,List<ChannelCreds> channelCredsList) {
private static ManagedChannel buildChannel(List<ServerInfo> servers) {
checkArgument(!servers.isEmpty(), "No management server provided.");
ServerInfo serverInfo = servers.get(0);
String serverUri = serverInfo.getServerUri();
List<ChannelCreds> channelCredsList = serverInfo.getChannelCredentials();
ManagedChannel ch = null;
// Use the first supported channel credentials configuration.
// Currently, only "google_default" is supported.

View File

@ -29,6 +29,7 @@ import io.grpc.internal.GrpcAttributes;
import io.grpc.internal.JsonParser;
import io.grpc.xds.Bootstrapper.BootstrapInfo;
import io.grpc.xds.Bootstrapper.ChannelCreds;
import io.grpc.xds.Bootstrapper.ServerInfo;
import java.io.IOException;
import java.net.URI;
import java.util.Collections;
@ -89,13 +90,26 @@ final class XdsNameResolver extends NameResolver {
return;
}
String serviceConfig = "{"
+ "\"loadBalancingConfig\": ["
+ "{\"xds_experimental\" : {"
+ "\"balancerName\" : \"" + bootstrapInfo.getServerUri() + "\","
+ "\"childPolicy\" : [{\"round_robin\" : {}}]"
+ "}}"
+ "]}";
List<ServerInfo> serverList = bootstrapInfo.getServers();
if (serverList.isEmpty()) {
listener.onError(
Status.UNAVAILABLE.withDescription("No traffic director provided by bootstrap"));
return;
}
// Currently we only support using the first server from bootstrap.
ServerInfo serverInfo = serverList.get(0);
String serviceConfig = "{\n"
+ " \"loadBalancingConfig\": [\n"
+ " {\n"
+ " \"xds_experimental\": {\n"
+ " \"balancerName\": \"" + serverInfo.getServerUri() + "\",\n"
+ " \"childPolicy\": [ {\"round_robin\": {} } ]\n"
+ " }\n"
+ " }"
+ " ]\n"
+ "}";
Map<String, ?> config;
try {
config = (Map<String, ?>) JsonParser.parse(serviceConfig);
@ -108,7 +122,7 @@ final class XdsNameResolver extends NameResolver {
Attributes.newBuilder()
.set(GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG, config)
.set(XDS_NODE, bootstrapInfo.getNode())
.set(XDS_CHANNEL_CREDS_LIST, bootstrapInfo.getChannelCredentials())
.set(XDS_CHANNEL_CREDS_LIST, serverInfo.getChannelCredentials())
.build();
ResolutionResult result =
ResolutionResult.newBuilder()

View File

@ -18,13 +18,16 @@ package io.grpc.xds;
import static com.google.common.truth.Truth.assertThat;
import com.google.common.collect.Iterables;
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.internal.GrpcUtil;
import io.grpc.xds.Bootstrapper.BootstrapInfo;
import io.grpc.xds.Bootstrapper.ServerInfo;
import java.io.IOException;
import java.util.List;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
@ -38,35 +41,163 @@ public class BootstrapperTest {
@Rule public ExpectedException thrown = ExpectedException.none();
@Test
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\"}, {\"type\": \"google_default\"} ]"
+ "} "
public void parseBootstrap_validData_singleXdsServer() throws IOException {
String rawData = "{\n"
+ " \"node\": {\n"
+ " \"id\": \"ENVOY_NODE_ID\",\n"
+ " \"cluster\": \"ENVOY_CLUSTER\",\n"
+ " \"locality\": {\n"
+ " \"region\": \"ENVOY_REGION\",\n"
+ " \"zone\": \"ENVOY_ZONE\",\n"
+ " \"sub_zone\": \"ENVOY_SUBZONE\"\n"
+ " },\n"
+ " \"metadata\": {\n"
+ " \"TRAFFICDIRECTOR_INTERCEPTION_PORT\": \"ENVOY_PORT\",\n"
+ " \"TRAFFICDIRECTOR_NETWORK_NAME\": \"VPC_NETWORK_NAME\"\n"
+ " }\n"
+ " },\n"
+ " \"xds_servers\": [\n"
+ " {\n"
+ " \"server_uri\": \"trafficdirector.googleapis.com:443\",\n"
+ " \"channel_creds\": [\n"
+ " {\"type\": \"tls\"}, {\"type\": \"loas\"}, {\"type\": \"google_default\"}\n"
+ " ]\n"
+ " }\n"
+ " ]\n"
+ "}";
BootstrapInfo info = Bootstrapper.parseConfig(rawData);
assertThat(info.getServerUri()).isEqualTo("trafficdirector.googleapis.com:443");
assertThat(info.getChannelCredentials()).hasSize(3);
assertThat(info.getChannelCredentials().get(0).getType()).isEqualTo("tls");
assertThat(info.getChannelCredentials().get(0).getConfig()).isNull();
assertThat(info.getChannelCredentials().get(1).getType()).isEqualTo("loas");
assertThat(info.getChannelCredentials().get(1).getConfig()).isNull();
assertThat(info.getChannelCredentials().get(2).getType()).isEqualTo("google_default");
assertThat(info.getChannelCredentials().get(2).getConfig()).isNull();
assertThat(info.getServers()).hasSize(1);
ServerInfo serverInfo = Iterables.getOnlyElement(info.getServers());
assertThat(serverInfo.getServerUri()).isEqualTo("trafficdirector.googleapis.com:443");
assertThat(serverInfo.getChannelCredentials()).hasSize(3);
assertThat(serverInfo.getChannelCredentials().get(0).getType()).isEqualTo("tls");
assertThat(serverInfo.getChannelCredentials().get(0).getConfig()).isNull();
assertThat(serverInfo.getChannelCredentials().get(1).getType()).isEqualTo("loas");
assertThat(serverInfo.getChannelCredentials().get(1).getConfig()).isNull();
assertThat(serverInfo.getChannelCredentials().get(2).getType()).isEqualTo("google_default");
assertThat(serverInfo.getChannelCredentials().get(2).getConfig()).isNull();
assertThat(info.getNode()).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())
.setBuildVersion(GrpcUtil.getGrpcBuildVersion())
.build());
}
@Test
public void parseBootstrap_validData_multipleXdsServers() throws IOException {
String rawData = "{\n"
+ " \"node\": {\n"
+ " \"id\": \"ENVOY_NODE_ID\",\n"
+ " \"cluster\": \"ENVOY_CLUSTER\",\n"
+ " \"locality\": {\n"
+ " \"region\": \"ENVOY_REGION\",\n"
+ " \"zone\": \"ENVOY_ZONE\",\n"
+ " \"sub_zone\": \"ENVOY_SUBZONE\"\n"
+ " },\n"
+ " \"metadata\": {\n"
+ " \"TRAFFICDIRECTOR_INTERCEPTION_PORT\": \"ENVOY_PORT\",\n"
+ " \"TRAFFICDIRECTOR_NETWORK_NAME\": \"VPC_NETWORK_NAME\"\n"
+ " }\n"
+ " },\n"
+ " \"xds_servers\": [\n"
+ " {\n"
+ " \"server_uri\": \"trafficdirector-foo.googleapis.com:443\",\n"
+ " \"channel_creds\": [\n"
+ " {\"type\": \"tls\"}, {\"type\": \"loas\"}, {\"type\": \"google_default\"}\n"
+ " ]\n"
+ " },\n"
+ " {\n"
+ " \"server_uri\": \"trafficdirector-bar.googleapis.com:443\",\n"
+ " \"channel_creds\": []\n"
+ " }\n"
+ " ]\n"
+ "}";
BootstrapInfo info = Bootstrapper.parseConfig(rawData);
assertThat(info.getServers()).hasSize(2);
List<ServerInfo> serverInfoList = info.getServers();
assertThat(serverInfoList.get(0).getServerUri())
.isEqualTo("trafficdirector-foo.googleapis.com:443");
assertThat(serverInfoList.get(0).getChannelCredentials()).hasSize(3);
assertThat(serverInfoList.get(0).getChannelCredentials().get(0).getType()).isEqualTo("tls");
assertThat(serverInfoList.get(0).getChannelCredentials().get(0).getConfig()).isNull();
assertThat(serverInfoList.get(0).getChannelCredentials().get(1).getType()).isEqualTo("loas");
assertThat(serverInfoList.get(0).getChannelCredentials().get(1).getConfig()).isNull();
assertThat(serverInfoList.get(0).getChannelCredentials().get(2).getType())
.isEqualTo("google_default");
assertThat(serverInfoList.get(0).getChannelCredentials().get(2).getConfig()).isNull();
assertThat(serverInfoList.get(1).getServerUri())
.isEqualTo("trafficdirector-bar.googleapis.com:443");
assertThat(serverInfoList.get(1).getChannelCredentials()).isEmpty();
assertThat(info.getNode()).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())
.setBuildVersion(GrpcUtil.getGrpcBuildVersion())
.build());
}
@Test
public void parseBootstrap_IgnoreIrrelevantFields() throws IOException {
String rawData = "{\n"
+ " \"node\": {\n"
+ " \"id\": \"ENVOY_NODE_ID\",\n"
+ " \"cluster\": \"ENVOY_CLUSTER\",\n"
+ " \"locality\": {\n"
+ " \"region\": \"ENVOY_REGION\",\n"
+ " \"zone\": \"ENVOY_ZONE\",\n"
+ " \"sub_zone\": \"ENVOY_SUBZONE\"\n"
+ " },\n"
+ " \"metadata\": {\n"
+ " \"TRAFFICDIRECTOR_INTERCEPTION_PORT\": \"ENVOY_PORT\",\n"
+ " \"TRAFFICDIRECTOR_NETWORK_NAME\": \"VPC_NETWORK_NAME\"\n"
+ " }\n"
+ " },\n"
+ " \"xds_servers\": [\n"
+ " {\n"
+ " \"server_uri\": \"trafficdirector.googleapis.com:443\",\n"
+ " \"ignore\": \"something irrelevant\","
+ " \"channel_creds\": [\n"
+ " {\"type\": \"tls\"}, {\"type\": \"loas\"}, {\"type\": \"google_default\"}\n"
+ " ]\n"
+ " }\n"
+ " ],\n"
+ " \"ignore\": \"something irrelevant\"\n"
+ "}";
BootstrapInfo info = Bootstrapper.parseConfig(rawData);
assertThat(info.getServers()).hasSize(1);
ServerInfo serverInfo = Iterables.getOnlyElement(info.getServers());
assertThat(serverInfo.getServerUri()).isEqualTo("trafficdirector.googleapis.com:443");
assertThat(serverInfo.getChannelCredentials()).hasSize(3);
assertThat(serverInfo.getChannelCredentials().get(0).getType()).isEqualTo("tls");
assertThat(serverInfo.getChannelCredentials().get(0).getConfig()).isNull();
assertThat(serverInfo.getChannelCredentials().get(1).getType()).isEqualTo("loas");
assertThat(serverInfo.getChannelCredentials().get(1).getConfig()).isNull();
assertThat(serverInfo.getChannelCredentials().get(2).getType()).isEqualTo("google_default");
assertThat(serverInfo.getChannelCredentials().get(2).getConfig()).isNull();
assertThat(info.getNode()).isEqualTo(
Node.newBuilder()
.setId("ENVOY_NODE_ID")
@ -95,14 +226,12 @@ public class BootstrapperTest {
@Test
public void parseBootstrap_minimumRequiredFields() throws IOException {
String rawData = "{"
+ "\"xds_server\": {"
+ "\"server_uri\": \"trafficdirector.googleapis.com:443\""
+ "}"
String rawData = "{\n"
+ " \"xds_servers\": []\n"
+ "}";
BootstrapInfo info = Bootstrapper.parseConfig(rawData);
assertThat(info.getServerUri()).isEqualTo("trafficdirector.googleapis.com:443");
assertThat(info.getServers()).isEmpty();
assertThat(info.getNode())
.isEqualTo(
Node.newBuilder()
@ -112,48 +241,78 @@ public class BootstrapperTest {
}
@Test
public void parseBootstrap_noXdsServer() 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\""
+ "}"
+ "}"
public void parseBootstrap_minimalUsableData() throws IOException {
String rawData = "{\n"
+ " \"xds_servers\": [\n"
+ " {\n"
+ " \"server_uri\": \"trafficdirector.googleapis.com:443\"\n"
+ " }\n"
+ " ]\n"
+ "}";
BootstrapInfo info = Bootstrapper.parseConfig(rawData);
assertThat(info.getServers()).hasSize(1);
ServerInfo serverInfo = Iterables.getOnlyElement(info.getServers());
assertThat(serverInfo.getServerUri()).isEqualTo("trafficdirector.googleapis.com:443");
assertThat(serverInfo.getChannelCredentials()).isEmpty();
assertThat(info.getNode())
.isEqualTo(
Node.newBuilder()
.setBuildVersion(
GrpcUtil.getGrpcBuildVersion())
.build());
}
@Test
public void parseBootstrap_noXdsServers() throws IOException {
String rawData = "{\n"
+ " \"node\": {\n"
+ " \"id\": \"ENVOY_NODE_ID\",\n"
+ " \"cluster\": \"ENVOY_CLUSTER\",\n"
+ " \"locality\": {\n"
+ " \"region\": \"ENVOY_REGION\",\n"
+ " \"zone\": \"ENVOY_ZONE\",\n"
+ " \"sub_zone\": \"ENVOY_SUBZONE\"\n"
+ " },\n"
+ " \"metadata\": {\n"
+ " \"TRAFFICDIRECTOR_INTERCEPTION_PORT\": \"ENVOY_PORT\",\n"
+ " \"TRAFFICDIRECTOR_NETWORK_NAME\": \"VPC_NETWORK_NAME\"\n"
+ " }\n"
+ " }\n"
+ "}";
thrown.expect(IOException.class);
thrown.expectMessage("Invalid bootstrap: 'xds_server' does not exist.");
thrown.expectMessage("Invalid bootstrap: 'xds_servers' does not exist.");
Bootstrapper.parseConfig(rawData);
}
@Test
public void parseBootstrap_noServerUri() throws IOException {
public void parseBootstrap_serverWithoutServerUri() 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\": {"
+ "\"channel_creds\": "
+ "[ {\"type\": \"tls\"}, {\"type\": \"loas\"} ]"
+ "} "
+ " \"node\": {\n"
+ " \"id\": \"ENVOY_NODE_ID\",\n"
+ " \"cluster\": \"ENVOY_CLUSTER\",\n"
+ " \"locality\": {\n"
+ " \"region\": \"ENVOY_REGION\",\n"
+ " \"zone\": \"ENVOY_ZONE\",\n"
+ " \"sub_zone\": \"ENVOY_SUBZONE\"\n"
+ " },\n"
+ " \"metadata\": {\n"
+ " \"TRAFFICDIRECTOR_INTERCEPTION_PORT\": \"ENVOY_PORT\",\n"
+ " \"TRAFFICDIRECTOR_NETWORK_NAME\": \"VPC_NETWORK_NAME\"\n"
+ " }\n"
+ " },\n"
+ " \"xds_servers\": [\n"
+ " {\n"
+ " \"channel_creds\": [\n"
+ " {\"type\": \"tls\"}, {\"type\": \"loas\"}\n"
+ " ]\n"
+ " }\n"
+ " ]\n "
+ "}";
thrown.expect(IOException.class);
thrown.expectMessage("Invalid bootstrap: 'xds_server : server_uri' does not exist.");
thrown.expectMessage("Invalid bootstrap: 'xds_servers' contains unknown server.");
Bootstrapper.parseConfig(rawData);
}
}

View File

@ -74,6 +74,7 @@ import io.grpc.stub.StreamObserver;
import io.grpc.testing.GrpcCleanupRule;
import io.grpc.xds.Bootstrapper.BootstrapInfo;
import io.grpc.xds.Bootstrapper.ChannelCreds;
import io.grpc.xds.Bootstrapper.ServerInfo;
import io.grpc.xds.EnvoyProtoData.DropOverload;
import io.grpc.xds.EnvoyProtoData.Locality;
import io.grpc.xds.EnvoyProtoData.LocalityLbEndpoints;
@ -489,9 +490,10 @@ public class LookasideLbTest {
@Test
public void handleResolvedAddress_withBootstrap() throws Exception {
BootstrapInfo bootstrapInfo = new BootstrapInfo(
"trafficdirector.googleapis.com", ImmutableList.<ChannelCreds>of(),
Node.getDefaultInstance());
List<ServerInfo> serverList =
ImmutableList.of(
new ServerInfo("trafficdirector.googleapis.com", ImmutableList.<ChannelCreds>of()));
BootstrapInfo bootstrapInfo = new BootstrapInfo(serverList, Node.getDefaultInstance());
doReturn(bootstrapInfo).when(bootstrapper).readBootstrap();
String lbConfigRaw =

View File

@ -111,8 +111,11 @@ public class XdsNameResolverTest {
Bootstrapper bootstrapper = new Bootstrapper() {
@Override
public BootstrapInfo readBootstrap() {
return new BootstrapInfo("trafficdirector.googleapis.com",
ImmutableList.of(loasCreds, googleDefaultCreds), FAKE_BOOTSTRAP_NODE);
List<ServerInfo> serverList =
ImmutableList.of(
new ServerInfo("trafficdirector.googleapis.com",
ImmutableList.of(loasCreds, googleDefaultCreds)));
return new BootstrapInfo(serverList, FAKE_BOOTSTRAP_NODE);
}
};
XdsNameResolver resolver = new XdsNameResolver("foo.googleapis.com", bootstrapper);
@ -143,6 +146,24 @@ public class XdsNameResolverTest {
.containsExactly(loasCreds, googleDefaultCreds);
}
@Test
public void resolve_bootstrapProvidesNoTrafficDirectorInfo() {
Bootstrapper bootstrapper = new Bootstrapper() {
@Override
public BootstrapInfo readBootstrap() {
return new BootstrapInfo(ImmutableList.<ServerInfo>of(), FAKE_BOOTSTRAP_NODE);
}
};
XdsNameResolver resolver = new XdsNameResolver("foo.googleapis.com", bootstrapper);
resolver.start(mockListener);
ArgumentCaptor<Status> statusCaptor = ArgumentCaptor.forClass(null);
verify(mockListener).onError(statusCaptor.capture());
assertThat(statusCaptor.getValue().getCode()).isEqualTo(Code.UNAVAILABLE);
assertThat(statusCaptor.getValue().getDescription())
.isEqualTo("No traffic director provided by bootstrap");
}
@Test
public void resolve_failToBootstrap() {
Bootstrapper bootstrapper = new Bootstrapper() {