rawLocality = JsonUtil.getObject(rawNode, "locality");
if (rawLocality != null) {
- Locality.Builder localityBuilder = Locality.newBuilder();
- if (rawLocality.containsKey("region")) {
- String region = JsonUtil.getString(rawLocality, "region");
+ String region = JsonUtil.getString(rawLocality, "region");
+ String zone = JsonUtil.getString(rawLocality, "zone");
+ String subZone = JsonUtil.getString(rawLocality, "sub_zone");
+ if (region != null) {
logger.log(XdsLogLevel.INFO, "Locality region: {0}", region);
- localityBuilder.setRegion(region);
}
if (rawLocality.containsKey("zone")) {
- String zone = JsonUtil.getString(rawLocality, "zone");
logger.log(XdsLogLevel.INFO, "Locality zone: {0}", zone);
- localityBuilder.setZone(zone);
}
if (rawLocality.containsKey("sub_zone")) {
- String subZone = JsonUtil.getString(rawLocality, "sub_zone");
logger.log(XdsLogLevel.INFO, "Locality sub_zone: {0}", subZone);
- localityBuilder.setSubZone(subZone);
}
- nodeBuilder.setLocality(localityBuilder);
+ Locality locality = new Locality(region, zone, subZone);
+ nodeBuilder.setLocality(locality);
}
}
GrpcBuildVersion buildVersion = GrpcUtil.getGrpcBuildVersion();
@@ -173,43 +163,6 @@ static BootstrapInfo parseConfig(String rawData) throws IOException {
return new BootstrapInfo(servers, nodeBuilder.build());
}
- /**
- * Converts Java representation of the given JSON value to protobuf's {@link
- * com.google.protobuf.Value} representation.
- *
- * The given {@code rawObject} must be a valid JSON value in Java representation, which is
- * either a {@code Map}, {@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 map = (Map) rawObject;
- for (Map.Entry 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();
- }
-
/**
* Data class containing channel credentials configurations for xDS protocol communication.
*/
@@ -247,11 +200,14 @@ String getType() {
static class ServerInfo {
private final String serverUri;
private final List channelCredsList;
+ @Nullable
+ private final List serverFeatures;
@VisibleForTesting
- ServerInfo(String serverUri, List channelCredsList) {
+ ServerInfo(String serverUri, List channelCredsList, List serverFeatures) {
this.serverUri = serverUri;
this.channelCredsList = channelCredsList;
+ this.serverFeatures = serverFeatures;
}
String getServerUri() {
@@ -261,6 +217,12 @@ String getServerUri() {
List getChannelCredentials() {
return Collections.unmodifiableList(channelCredsList);
}
+
+ List getServerFeatures() {
+ return serverFeatures == null
+ ? Collections.emptyList()
+ : Collections.unmodifiableList(serverFeatures);
+ }
}
/**
@@ -291,6 +253,5 @@ List getServers() {
public Node getNode() {
return node;
}
-
}
}
diff --git a/xds/src/main/java/io/grpc/xds/ClientLoadCounter.java b/xds/src/main/java/io/grpc/xds/ClientLoadCounter.java
index 8e8754d2542..f86002b683f 100644
--- a/xds/src/main/java/io/grpc/xds/ClientLoadCounter.java
+++ b/xds/src/main/java/io/grpc/xds/ClientLoadCounter.java
@@ -54,27 +54,10 @@ final class ClientLoadCounter {
private final AtomicLong callsIssued = new AtomicLong();
private final MetricRecorder[] metricRecorders = new MetricRecorder[THREAD_BALANCING_FACTOR];
- // True if this counter continues to record stats after next snapshot. Otherwise, it will be
- // discarded.
- private boolean active;
-
ClientLoadCounter() {
for (int i = 0; i < THREAD_BALANCING_FACTOR; i++) {
metricRecorders[i] = new MetricRecorder();
}
- active = true;
- }
-
- /**
- * Must only be used for testing.
- */
- @VisibleForTesting
- ClientLoadCounter(long callsSucceeded, long callsInProgress, long callsFailed, long callsIssued) {
- this();
- this.callsSucceeded.set(callsSucceeded);
- this.callsInProgress.set(callsInProgress);
- this.callsFailed.set(callsFailed);
- this.callsIssued.set(callsIssued);
}
void recordCallStarted() {
@@ -98,12 +81,8 @@ void recordMetric(String name, double value) {
}
/**
- * Generates a snapshot for load stats recorded in this counter. Successive snapshots represent
- * load stats recorded for the interval since the previous snapshot. So taking a snapshot clears
- * the counter state except for ongoing RPC recordings.
- *
- * This method is not thread-safe and must be called from {@link
- * io.grpc.LoadBalancer.Helper#getSynchronizationContext()}.
+ * Generates a snapshot for load stats recorded in this counter for the interval between calls
+ * of this method.
*/
ClientLoadSnapshot snapshot() {
Map aggregatedValues = new HashMap<>();
@@ -127,12 +106,24 @@ ClientLoadSnapshot snapshot() {
aggregatedValues);
}
- void setActive(boolean value) {
- active = value;
+ @VisibleForTesting
+ void setCallsIssued(long callsIssued) {
+ this.callsIssued.set(callsIssued);
+ }
+
+ @VisibleForTesting
+ void setCallsInProgress(long callsInProgress) {
+ this.callsInProgress.set(callsInProgress);
}
- boolean isActive() {
- return active;
+ @VisibleForTesting
+ void setCallsSucceeded(long callsSucceeded) {
+ this.callsSucceeded.set(callsSucceeded);
+ }
+
+ @VisibleForTesting
+ void setCallsFailed(long callsFailed) {
+ this.callsFailed.set(callsFailed);
}
/**
diff --git a/xds/src/main/java/io/grpc/xds/EdsLoadBalancer.java b/xds/src/main/java/io/grpc/xds/EdsLoadBalancer.java
index 34e90c7c7a1..b009ea7d530 100644
--- a/xds/src/main/java/io/grpc/xds/EdsLoadBalancer.java
+++ b/xds/src/main/java/io/grpc/xds/EdsLoadBalancer.java
@@ -22,7 +22,6 @@
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
-import io.envoyproxy.envoy.api.v2.core.Node;
import io.grpc.Attributes;
import io.grpc.InternalLogId;
import io.grpc.LoadBalancer;
@@ -38,6 +37,8 @@
import io.grpc.xds.EnvoyProtoData.DropOverload;
import io.grpc.xds.EnvoyProtoData.Locality;
import io.grpc.xds.EnvoyProtoData.LocalityLbEndpoints;
+import io.grpc.xds.EnvoyProtoData.Node;
+import io.grpc.xds.LoadStatsManager.LoadStatsStore;
import io.grpc.xds.LocalityStore.LocalityStoreFactory;
import io.grpc.xds.XdsClient.EndpointUpdate;
import io.grpc.xds.XdsClient.EndpointWatcher;
@@ -208,11 +209,9 @@ public void shutdown() {
*/
private final class ClusterEndpointsBalancerFactory extends LoadBalancer.Factory {
@Nullable final String clusterServiceName;
- final LoadStatsStore loadStatsStore;
ClusterEndpointsBalancerFactory(@Nullable String clusterServiceName) {
this.clusterServiceName = clusterServiceName;
- loadStatsStore = new LoadStatsStoreImpl(clusterName, clusterServiceName);
}
@Override
@@ -248,6 +247,7 @@ final class ClusterEndpointsBalancer extends LoadBalancer {
ClusterEndpointsBalancer(Helper helper) {
this.helper = helper;
resourceName = clusterServiceName != null ? clusterServiceName : clusterName;
+ LoadStatsStore loadStatsStore = xdsClient.addClientStats(clusterName, clusterServiceName);
localityStore =
localityStoreFactory.newLocalityStore(logId, helper, lbRegistry, loadStatsStore);
endpointWatcher = new EndpointWatcherImpl();
@@ -267,22 +267,12 @@ public void handleResolvedAddresses(ResolvedAddresses resolvedAddresses) {
throw new AssertionError("Can only report load to the same management server");
}
if (!isReportingLoad) {
- logger.log(
- XdsLogLevel.INFO,
- "Start reporting loads for cluster: {0}, cluster_service: {1}",
- clusterName,
- clusterServiceName);
- xdsClient.reportClientStats(clusterName, clusterServiceName, loadStatsStore);
+ xdsClient.reportClientStats();
isReportingLoad = true;
}
} else {
if (isReportingLoad) {
- logger.log(
- XdsLogLevel.INFO,
- "Stop reporting loads for cluster: {0}, cluster_service: {1}",
- clusterName,
- clusterServiceName);
- xdsClient.cancelClientStatsReport(clusterName, clusterServiceName);
+ xdsClient.cancelClientStatsReport();
isReportingLoad = false;
}
}
@@ -304,15 +294,11 @@ public boolean canHandleEmptyAddressListFromNameResolution() {
@Override
public void shutdown() {
if (isReportingLoad) {
- logger.log(
- XdsLogLevel.INFO,
- "Stop reporting loads for cluster: {0}, cluster_service: {1}",
- clusterName,
- clusterServiceName);
- xdsClient.cancelClientStatsReport(clusterName, clusterServiceName);
+ xdsClient.cancelClientStatsReport();
isReportingLoad = false;
}
localityStore.reset();
+ xdsClient.removeClientStats(clusterName, clusterServiceName);
xdsClient.cancelEndpointDataWatch(resourceName, endpointWatcher);
logger.log(
XdsLogLevel.INFO,
@@ -365,12 +351,7 @@ public void onEndpointChanged(EndpointUpdate endpointUpdate) {
public void onResourceDoesNotExist(String resourceName) {
logger.log(XdsLogLevel.INFO, "Resource {0} is unavailable", resourceName);
if (isReportingLoad) {
- logger.log(
- XdsLogLevel.INFO,
- "Stop reporting loads for cluster: {0}, cluster_service: {1}",
- clusterName,
- clusterServiceName);
- xdsClient.cancelClientStatsReport(clusterName, clusterServiceName);
+ xdsClient.cancelClientStatsReport();
isReportingLoad = false;
}
localityStore.reset();
diff --git a/xds/src/main/java/io/grpc/xds/EnvoyProtoData.java b/xds/src/main/java/io/grpc/xds/EnvoyProtoData.java
index be691337f22..14a62f29f5a 100644
--- a/xds/src/main/java/io/grpc/xds/EnvoyProtoData.java
+++ b/xds/src/main/java/io/grpc/xds/EnvoyProtoData.java
@@ -22,6 +22,11 @@
import com.google.common.base.MoreObjects;
import com.google.common.base.MoreObjects.ToStringHelper;
import com.google.common.collect.ImmutableList;
+import com.google.protobuf.ListValue;
+import com.google.protobuf.NullValue;
+import com.google.protobuf.Struct;
+import com.google.protobuf.Value;
+import com.google.protobuf.util.Durations;
import com.google.re2j.Pattern;
import com.google.re2j.PatternSyntaxException;
import io.envoyproxy.envoy.type.v3.FractionalPercent;
@@ -34,7 +39,9 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
+import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
/**
@@ -135,17 +142,345 @@ public String toString() {
}
/**
- * See corresponding Envoy proto message {@link io.envoyproxy.envoy.api.v2.core.Locality}.
+ * See corresponding Envoy proto message {@link io.envoyproxy.envoy.config.core.v3.Node}.
+ */
+ public static final class Node {
+
+ private final String id;
+ private final String cluster;
+ @Nullable
+ private final Map metadata;
+ @Nullable
+ private final Locality locality;
+ private final List listeningAddresses;
+ private final String buildVersion;
+ private final String userAgentName;
+ @Nullable
+ private final String userAgentVersion;
+ private final List clientFeatures;
+
+ private Node(
+ String id, String cluster, @Nullable Map metadata, @Nullable Locality locality,
+ List listeningAddresses, String buildVersion, String userAgentName,
+ @Nullable String userAgentVersion, List clientFeatures) {
+ this.id = checkNotNull(id, "id");
+ this.cluster = checkNotNull(cluster, "cluster");
+ this.metadata = metadata;
+ this.locality = locality;
+ this.listeningAddresses = Collections.unmodifiableList(
+ checkNotNull(listeningAddresses, "listeningAddresses"));
+ this.buildVersion = checkNotNull(buildVersion, "buildVersion");
+ this.userAgentName = checkNotNull(userAgentName, "userAgentName");
+ this.userAgentVersion = userAgentVersion;
+ this.clientFeatures = Collections.unmodifiableList(
+ checkNotNull(clientFeatures, "clientFeatures"));
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("id", id)
+ .add("cluster", cluster)
+ .add("metadata", metadata)
+ .add("locality", locality)
+ .add("listeningAddresses", listeningAddresses)
+ .add("buildVersion", buildVersion)
+ .add("userAgentName", userAgentName)
+ .add("userAgentVersion", userAgentVersion)
+ .add("clientFeatures", clientFeatures)
+ .toString();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Node node = (Node) o;
+ return Objects.equals(id, node.id)
+ && Objects.equals(cluster, node.cluster)
+ && Objects.equals(metadata, node.metadata)
+ && Objects.equals(locality, node.locality)
+ && Objects.equals(listeningAddresses, node.listeningAddresses)
+ && Objects.equals(buildVersion, node.buildVersion)
+ && Objects.equals(userAgentName, node.userAgentName)
+ && Objects.equals(userAgentVersion, node.userAgentVersion)
+ && Objects.equals(clientFeatures, node.clientFeatures);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects
+ .hash(id, cluster, metadata, locality, listeningAddresses, buildVersion, userAgentName,
+ userAgentVersion, clientFeatures);
+ }
+
+ static final class Builder {
+ private String id = "";
+ private String cluster = "";
+ @Nullable
+ private Map metadata;
+ @Nullable
+ private Locality locality;
+ private final List listeningAddresses = new ArrayList<>();
+ private String buildVersion = "";
+ private String userAgentName = "";
+ @Nullable
+ private String userAgentVersion;
+ private final List clientFeatures = new ArrayList<>();
+
+ private Builder() {
+ }
+
+ Builder setId(String id) {
+ this.id = checkNotNull(id, "id");
+ return this;
+ }
+
+ Builder setCluster(String cluster) {
+ this.cluster = checkNotNull(cluster, "cluster");
+ return this;
+ }
+
+ Builder setMetadata(Map metadata) {
+ this.metadata = checkNotNull(metadata, "metadata");
+ return this;
+ }
+
+ Builder setLocality(Locality locality) {
+ this.locality = checkNotNull(locality, "locality");
+ return this;
+ }
+
+ Builder addListeningAddresses(Address address) {
+ listeningAddresses.add(checkNotNull(address, "address"));
+ return this;
+ }
+
+ Builder setBuildVersion(String buildVersion) {
+ this.buildVersion = checkNotNull(buildVersion, "buildVersion");
+ return this;
+ }
+
+ Builder setUserAgentName(String userAgentName) {
+ this.userAgentName = checkNotNull(userAgentName, "userAgentName");
+ return this;
+ }
+
+ Builder setUserAgentVersion(String userAgentVersion) {
+ this.userAgentVersion = checkNotNull(userAgentVersion, "userAgentVersion");
+ return this;
+ }
+
+ Builder addClientFeatures(String clientFeature) {
+ this.clientFeatures.add(checkNotNull(clientFeature, "clientFeature"));
+ return this;
+ }
+
+ Node build() {
+ return new Node(
+ id, cluster, metadata, locality, listeningAddresses, buildVersion, userAgentName,
+ userAgentVersion, clientFeatures);
+ }
+ }
+
+ static Builder newBuilder() {
+ return new Builder();
+ }
+
+ Builder toBuilder() {
+ Builder builder = new Builder().setId(id).setCluster(cluster);
+ if (metadata != null) {
+ builder.setMetadata(metadata);
+ }
+ if (locality != null) {
+ builder.setLocality(locality);
+ }
+ builder.listeningAddresses.addAll(listeningAddresses);
+ return builder;
+ }
+
+ String getId() {
+ return id;
+ }
+
+ String getCluster() {
+ return cluster;
+ }
+
+ @Nullable
+ Map getMetadata() {
+ return metadata;
+ }
+
+ @Nullable
+ Locality getLocality() {
+ return locality;
+ }
+
+ List getListeningAddresses() {
+ return listeningAddresses;
+ }
+
+ io.envoyproxy.envoy.config.core.v3.Node toEnvoyProtoNode() {
+ io.envoyproxy.envoy.config.core.v3.Node.Builder builder =
+ io.envoyproxy.envoy.config.core.v3.Node.newBuilder();
+ builder.setId(id);
+ builder.setCluster(cluster);
+ if (metadata != null) {
+ Struct.Builder structBuilder = Struct.newBuilder();
+ for (Map.Entry entry : metadata.entrySet()) {
+ structBuilder.putFields(entry.getKey(), convertToValue(entry.getValue()));
+ }
+ builder.setMetadata(structBuilder);
+ }
+ if (locality != null) {
+ builder.setLocality(locality.toEnvoyProtoLocality());
+ }
+ for (Address address : listeningAddresses) {
+ builder.addListeningAddresses(address.toEnvoyProtoAddress());
+ }
+ builder.setUserAgentName(userAgentName);
+ if (userAgentVersion != null) {
+ builder.setUserAgentVersion(userAgentVersion);
+ }
+ builder.addAllClientFeatures(clientFeatures);
+ return builder.build();
+ }
+
+ @SuppressWarnings("deprecation") // Deprecated v2 API setBuildVersion().
+ public io.envoyproxy.envoy.api.v2.core.Node toEnvoyProtoNodeV2() {
+ io.envoyproxy.envoy.api.v2.core.Node.Builder builder =
+ io.envoyproxy.envoy.api.v2.core.Node.newBuilder();
+ builder.setId(id);
+ builder.setCluster(cluster);
+ if (metadata != null) {
+ Struct.Builder structBuilder = Struct.newBuilder();
+ for (Map.Entry entry : metadata.entrySet()) {
+ structBuilder.putFields(entry.getKey(), convertToValue(entry.getValue()));
+ }
+ builder.setMetadata(structBuilder);
+ }
+ if (locality != null) {
+ builder.setLocality(locality.toEnvoyProtoLocalityV2());
+ }
+ for (Address address : listeningAddresses) {
+ builder.addListeningAddresses(address.toEnvoyProtoAddressV2());
+ }
+ builder.setBuildVersion(buildVersion);
+ builder.setUserAgentName(userAgentName);
+ if (userAgentVersion != null) {
+ builder.setUserAgentVersion(userAgentVersion);
+ }
+ builder.addAllClientFeatures(clientFeatures);
+ return builder.build();
+ }
+ }
+
+ /**
+ * Converts Java representation of the given JSON value to protobuf's {@link
+ * com.google.protobuf.Value} representation.
+ *
+ * The given {@code rawObject} must be a valid JSON value in Java representation, which is
+ * either a {@code Map}, {@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 map = (Map) rawObject;
+ for (Map.Entry 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();
+ }
+
+ /**
+ * See corresponding Envoy proto message {@link io.envoyproxy.envoy.config.core.v3.Address}.
+ */
+ static final class Address {
+ private final String address;
+ private final int port;
+
+ Address(String address, int port) {
+ this.address = checkNotNull(address, "address");
+ this.port = port;
+ }
+
+ io.envoyproxy.envoy.config.core.v3.Address toEnvoyProtoAddress() {
+ return
+ io.envoyproxy.envoy.config.core.v3.Address.newBuilder().setSocketAddress(
+ io.envoyproxy.envoy.config.core.v3.SocketAddress.newBuilder().setAddress(address)
+ .setPortValue(port)).build();
+ }
+
+ io.envoyproxy.envoy.api.v2.core.Address toEnvoyProtoAddressV2() {
+ return
+ io.envoyproxy.envoy.api.v2.core.Address.newBuilder().setSocketAddress(
+ io.envoyproxy.envoy.api.v2.core.SocketAddress.newBuilder().setAddress(address)
+ .setPortValue(port)).build();
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("address", address)
+ .add("port", port)
+ .toString();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Address address1 = (Address) o;
+ return port == address1.port && Objects.equals(address, address1.address);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(address, port);
+ }
+ }
+
+ /**
+ * See corresponding Envoy proto message {@link io.envoyproxy.envoy.config.core.v3.Locality}.
*/
static final class Locality {
private final String region;
private final String zone;
private final String subZone;
- Locality(String region, String zone, String subZone) {
- this.region = region;
- this.zone = zone;
- this.subZone = subZone;
+ Locality(@Nullable String region, @Nullable String zone, @Nullable String subZone) {
+ this.region = region == null ? "" : region;
+ this.zone = zone == null ? "" : zone;
+ this.subZone = subZone == null ? "" : subZone;
}
static Locality fromEnvoyProtoLocality(io.envoyproxy.envoy.config.core.v3.Locality locality) {
@@ -163,7 +498,15 @@ static Locality fromEnvoyProtoLocalityV2(io.envoyproxy.envoy.api.v2.core.Localit
/* subZone = */ locality.getSubZone());
}
- io.envoyproxy.envoy.api.v2.core.Locality toEnvoyProtoLocality() {
+ io.envoyproxy.envoy.config.core.v3.Locality toEnvoyProtoLocality() {
+ return io.envoyproxy.envoy.config.core.v3.Locality.newBuilder()
+ .setRegion(region)
+ .setZone(zone)
+ .setSubZone(subZone)
+ .build();
+ }
+
+ io.envoyproxy.envoy.api.v2.core.Locality toEnvoyProtoLocalityV2() {
return io.envoyproxy.envoy.api.v2.core.Locality.newBuilder()
.setRegion(region)
.setZone(zone)
@@ -726,6 +1069,7 @@ static StructOrError convertEnvoyProtoHeaderMatcher(
/** See corresponding Envoy proto message {@link io.envoyproxy.envoy.api.v2.route.RouteAction}. */
static final class RouteAction {
+ private final long timeoutNano;
// Exactly one of the following fields is non-null.
@Nullable
private final String cluster;
@@ -733,11 +1077,20 @@ static final class RouteAction {
private final List weightedClusters;
@VisibleForTesting
- RouteAction(@Nullable String cluster, @Nullable List weightedClusters) {
+ RouteAction(
+ long timeoutNano,
+ @Nullable String cluster,
+ @Nullable List weightedClusters) {
+ this.timeoutNano = timeoutNano;
this.cluster = cluster;
this.weightedClusters = weightedClusters;
}
+
+ Long getTimeoutNano() {
+ return timeoutNano;
+ }
+
@Nullable
String getCluster() {
return cluster;
@@ -757,18 +1110,20 @@ public boolean equals(Object o) {
return false;
}
RouteAction that = (RouteAction) o;
- return Objects.equals(cluster, that.cluster)
+ return Objects.equals(timeoutNano, that.timeoutNano)
+ && Objects.equals(cluster, that.cluster)
&& Objects.equals(weightedClusters, that.weightedClusters);
}
@Override
public int hashCode() {
- return Objects.hash(cluster, weightedClusters);
+ return Objects.hash(timeoutNano, cluster, weightedClusters);
}
@Override
public String toString() {
ToStringHelper toStringHelper = MoreObjects.toStringHelper(this);
+ toStringHelper.add("timeout", timeoutNano + "ns");
if (cluster != null) {
toStringHelper.add("cluster", cluster);
}
@@ -805,7 +1160,16 @@ static StructOrError fromEnvoyProtoRouteAction(
return StructOrError.fromError(
"Unknown cluster specifier: " + proto.getClusterSpecifierCase());
}
- return StructOrError.fromStruct(new RouteAction(cluster, weightedClusters));
+ long timeoutNano = TimeUnit.SECONDS.toNanos(15L); // default 15s
+ if (proto.hasMaxGrpcTimeout()) {
+ timeoutNano = Durations.toNanos(proto.getMaxGrpcTimeout());
+ } else if (proto.hasTimeout()) {
+ timeoutNano = Durations.toNanos(proto.getTimeout());
+ }
+ if (timeoutNano == 0) {
+ timeoutNano = Long.MAX_VALUE;
+ }
+ return StructOrError.fromStruct(new RouteAction(timeoutNano, cluster, weightedClusters));
}
}
diff --git a/xds/src/main/java/io/grpc/xds/LoadReportClient.java b/xds/src/main/java/io/grpc/xds/LoadReportClient.java
index 525b645d610..84d2b340cbd 100644
--- a/xds/src/main/java/io/grpc/xds/LoadReportClient.java
+++ b/xds/src/main/java/io/grpc/xds/LoadReportClient.java
@@ -27,7 +27,6 @@
import com.google.protobuf.Value;
import com.google.protobuf.util.Durations;
import io.envoyproxy.envoy.api.v2.core.Node;
-import io.envoyproxy.envoy.api.v2.endpoint.ClusterStats;
import io.envoyproxy.envoy.service.load_stats.v2.LoadReportingServiceGrpc;
import io.envoyproxy.envoy.service.load_stats.v2.LoadStatsRequest;
import io.envoyproxy.envoy.service.load_stats.v2.LoadStatsResponse;
@@ -39,10 +38,7 @@
import io.grpc.internal.BackoffPolicy;
import io.grpc.stub.StreamObserver;
import io.grpc.xds.XdsLogger.XdsLogLevel;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
+import java.util.List;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
@@ -57,6 +53,7 @@ final class LoadReportClient {
@VisibleForTesting
static final String TARGET_NAME_METADATA_KEY = "PROXYLESS_CLIENT_HOSTNAME";
+ private final InternalLogId logId;
private final XdsLogger logger;
private final ManagedChannel channel;
private final Node node;
@@ -65,9 +62,8 @@ final class LoadReportClient {
private final Supplier stopwatchSupplier;
private final Stopwatch retryStopwatch;
private final BackoffPolicy.Provider backoffPolicyProvider;
+ private final LoadStatsManager loadStatsManager;
- // Sources of load stats data for each cluster:cluster_service.
- private final Map> loadStatsStoreMap = new HashMap<>();
private boolean started;
@Nullable
@@ -76,18 +72,17 @@ final class LoadReportClient {
private ScheduledHandle lrsRpcRetryTimer;
@Nullable
private LrsStream lrsStream;
- @Nullable
- private LoadReportCallback callback;
LoadReportClient(
- InternalLogId logId,
String targetName,
+ LoadStatsManager loadStatsManager,
ManagedChannel channel,
Node node,
SynchronizationContext syncContext,
ScheduledExecutorService scheduledExecutorService,
BackoffPolicy.Provider backoffPolicyProvider,
Supplier stopwatchSupplier) {
+ this.loadStatsManager = checkNotNull(loadStatsManager, "loadStatsManager");
this.channel = checkNotNull(channel, "channel");
this.syncContext = checkNotNull(syncContext, "syncContext");
this.timerService = checkNotNull(scheduledExecutorService, "timeService");
@@ -104,8 +99,9 @@ final class LoadReportClient {
Value.newBuilder().setStringValue(targetName).build())
.build();
this.node = node.toBuilder().setMetadata(metadata).build();
- String logPrefix = checkNotNull(logId, "logId").toString().concat("-lrs-client");
- logger = XdsLogger.withPrefix(logPrefix);
+ logId = InternalLogId.allocate("lrs-client", targetName);
+ logger = XdsLogger.withLogId(logId);
+ logger.log(XdsLogLevel.INFO, "Created");
}
/**
@@ -113,12 +109,12 @@ final class LoadReportClient {
* stats periodically. Calling this method on an already started {@link LoadReportClient} is
* no-op.
*/
- void startLoadReporting(LoadReportCallback callback) {
+ void startLoadReporting() {
if (started) {
return;
}
- this.callback = callback;
started = true;
+ logger.log(XdsLogLevel.INFO, "Starting load reporting RPC");
startLrsRpc();
}
@@ -130,6 +126,7 @@ void stopLoadReporting() {
if (!started) {
return;
}
+ logger.log(XdsLogLevel.INFO, "Stopping load reporting RPC");
if (lrsRpcRetryTimer != null) {
lrsRpcRetryTimer.cancel();
}
@@ -140,49 +137,6 @@ void stopLoadReporting() {
// Do not shutdown channel as it is not owned by LrsClient.
}
- /**
- * Provides this LoadReportClient source of load stats data for the given
- * cluster:cluster_service. If requested, data from the given loadStatsStore is
- * periodically queried and sent to traffic director by this LoadReportClient.
- */
- void addLoadStatsStore(
- String clusterName, @Nullable String clusterServiceName, LoadStatsStore loadStatsStore) {
- checkState(
- !loadStatsStoreMap.containsKey(clusterName)
- || !loadStatsStoreMap.get(clusterName).containsKey(clusterServiceName),
- "load stats for cluster: %s, cluster service: %s already exists",
- clusterName, clusterServiceName);
- logger.log(
- XdsLogLevel.INFO,
- "Add load stats for cluster: {0}, cluster_service: {1}", clusterName, clusterServiceName);
- if (!loadStatsStoreMap.containsKey(clusterName)) {
- loadStatsStoreMap.put(clusterName, new HashMap());
- }
- Map clusterLoadStatsStores = loadStatsStoreMap.get(clusterName);
- clusterLoadStatsStores.put(clusterServiceName, loadStatsStore);
- }
-
- /**
- * Stops providing load stats data for the given cluster:cluster_service.
- */
- void removeLoadStatsStore(String clusterName, @Nullable String clusterServiceName) {
- checkState(
- loadStatsStoreMap.containsKey(clusterName)
- && loadStatsStoreMap.get(clusterName).containsKey(clusterServiceName),
- "load stats for cluster: %s, cluster service: %s does not exist",
- clusterName, clusterServiceName);
- logger.log(
- XdsLogLevel.INFO,
- "Remove load stats for cluster: {0}, cluster_service: {1}",
- clusterName,
- clusterServiceName);
- Map clusterLoadStatsStores = loadStatsStoreMap.get(clusterName);
- clusterLoadStatsStores.remove(clusterServiceName);
- if (clusterLoadStatsStores.isEmpty()) {
- loadStatsStoreMap.remove(clusterName);
- }
- }
-
@VisibleForTesting
static class LoadReportingTask implements Runnable {
private final LrsStream stream;
@@ -217,27 +171,21 @@ private void startLrsRpc() {
private class LrsStream implements StreamObserver {
- // Cluster to report loads for asked by management server.
- final Set clusterNames = new HashSet<>();
final LoadReportingServiceGrpc.LoadReportingServiceStub stub;
- final Stopwatch reportStopwatch;
StreamObserver lrsRequestWriter;
boolean initialResponseReceived;
boolean closed;
long loadReportIntervalNano = -1;
+ boolean reportAllClusters;
+ List clusterNames; // clusters to report loads for, if not report all.
ScheduledHandle loadReportTimer;
LrsStream(LoadReportingServiceGrpc.LoadReportingServiceStub stub, Stopwatch stopwatch) {
this.stub = checkNotNull(stub, "stub");
- reportStopwatch = checkNotNull(stopwatch, "stopwatch");
}
void start() {
lrsRequestWriter = stub.withWaitForReady().streamLoadStats(this);
- reportStopwatch.reset().start();
-
- // Send an initial LRS request with empty cluster stats. Management server is able to
- // infer clusters the gRPC client sending loads to.
LoadStatsRequest initRequest =
LoadStatsRequest.newBuilder()
.setNode(node)
@@ -278,20 +226,12 @@ public void run() {
}
private void sendLoadReport() {
- long interval = reportStopwatch.elapsed(TimeUnit.NANOSECONDS);
- reportStopwatch.reset().start();
LoadStatsRequest.Builder requestBuilder = LoadStatsRequest.newBuilder().setNode(node);
- for (String name : clusterNames) {
- if (loadStatsStoreMap.containsKey(name)) {
- Map clusterLoadStatsStores = loadStatsStoreMap.get(name);
- for (LoadStatsStore statsStore : clusterLoadStatsStores.values()) {
- ClusterStats report =
- statsStore.generateLoadReport()
- .toBuilder()
- .setLoadReportInterval(Durations.fromNanos(interval))
- .build();
- requestBuilder.addClusterStats(report);
- }
+ if (reportAllClusters) {
+ requestBuilder.addAllClusterStats(loadStatsManager.getAllLoadReports());
+ } else {
+ for (String name : clusterNames) {
+ requestBuilder.addAllClusterStats(loadStatsManager.getClusterLoadReports(name));
}
}
LoadStatsRequest request = requestBuilder.build();
@@ -317,27 +257,22 @@ private void handleResponse(LoadStatsResponse response) {
if (closed) {
return;
}
-
if (!initialResponseReceived) {
logger.log(XdsLogLevel.DEBUG, "Received LRS initial response:\n{0}", response);
initialResponseReceived = true;
} else {
logger.log(XdsLogLevel.DEBUG, "Received LRS response:\n{0}", response);
}
- long interval = Durations.toNanos(response.getLoadReportingInterval());
- if (interval != loadReportIntervalNano) {
- logger.log(XdsLogLevel.INFO, "Update load reporting interval to {0} ns", interval);
- loadReportIntervalNano = interval;
- callback.onReportResponse(loadReportIntervalNano);
- }
- if (clusterNames.size() != response.getClustersCount()
- || !clusterNames.containsAll(response.getClustersList())) {
- logger.log(
- XdsLogLevel.INFO,
- "Update load reporting clusters to {0}", response.getClustersList());
- clusterNames.clear();
- clusterNames.addAll(response.getClustersList());
+ reportAllClusters = response.getSendAllClusters();
+ if (reportAllClusters) {
+ logger.log(XdsLogLevel.INFO, "Report loads for all clusters");
+ } else {
+ logger.log(XdsLogLevel.INFO, "Report loads for clusters: ", response.getClustersList());
+ clusterNames = response.getClustersList();
}
+ long interval = Durations.toNanos(response.getLoadReportingInterval());
+ logger.log(XdsLogLevel.INFO, "Update load reporting interval to {0} ns", interval);
+ loadReportIntervalNano = interval;
scheduleNextLoadReport();
}
@@ -400,21 +335,4 @@ private void cleanUp() {
}
}
}
-
- /**
- * Callbacks for passing information received from client load reporting responses to xDS load
- * balancer, such as the load reporting interval requested by the traffic director.
- *
- * Implementations are not required to be thread-safe as callbacks will be invoked in xDS load
- * balancer's {@link io.grpc.SynchronizationContext}.
- */
- interface LoadReportCallback {
-
- /**
- * The load reporting interval has been received.
- *
- * @param reportIntervalNano load reporting interval requested by remote traffic director.
- */
- void onReportResponse(long reportIntervalNano);
- }
}
diff --git a/xds/src/main/java/io/grpc/xds/LoadStatsManager.java b/xds/src/main/java/io/grpc/xds/LoadStatsManager.java
new file mode 100644
index 00000000000..5372d37b0ea
--- /dev/null
+++ b/xds/src/main/java/io/grpc/xds/LoadStatsManager.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2019 The gRPC Authors
+ *
+ * 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.
+ */
+
+package io.grpc.xds;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.common.annotations.VisibleForTesting;
+import io.envoyproxy.envoy.api.v2.endpoint.ClusterStats;
+import io.grpc.xds.EnvoyProtoData.Locality;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import javax.annotation.Nullable;
+
+/**
+ * Manages all stats for client side load.
+ */
+final class LoadStatsManager {
+ private final LoadStatsStoreFactory loadStatsStoreFactory;
+ private final Map>> loadStatsStores
+ = new HashMap<>();
+
+ LoadStatsManager() {
+ this(LoadStatsStoreImpl.getDefaultFactory());
+ }
+
+ @VisibleForTesting
+ LoadStatsManager(LoadStatsStoreFactory factory) {
+ this.loadStatsStoreFactory = factory;
+ }
+
+ /**
+ * Adds and retrieves the stats object for tracking loads for the given cluster:cluster_service.
+ * The returned {@link LoadStatsStore} is reference-counted, caller should use
+ * {@link #removeLoadStats} to release the reference when it is no longer used.
+ */
+ LoadStatsStore addLoadStats(String cluster, @Nullable String clusterService) {
+ if (!loadStatsStores.containsKey(cluster)) {
+ loadStatsStores.put(cluster, new HashMap>());
+ }
+ Map> clusterLoadStatsStores
+ = loadStatsStores.get(cluster);
+ if (!clusterLoadStatsStores.containsKey(clusterService)) {
+ clusterLoadStatsStores.put(
+ clusterService,
+ ReferenceCounted.wrap(loadStatsStoreFactory.newLoadStatsStore(cluster, clusterService)));
+ }
+ ReferenceCounted ref = clusterLoadStatsStores.get(clusterService);
+ ref.retain();
+ return ref.get();
+ }
+
+ /**
+ * Discards stats object used for tracking loads for the given cluster:cluster_service.
+ */
+ void removeLoadStats(String cluster, @Nullable String clusterService) {
+ checkState(
+ loadStatsStores.containsKey(cluster)
+ && loadStatsStores.get(cluster).containsKey(clusterService),
+ "stats for cluster %s, cluster service %s not exits");
+ Map> clusterLoadStatsStores =
+ loadStatsStores.get(cluster);
+ ReferenceCounted ref = clusterLoadStatsStores.get(clusterService);
+ ref.release();
+ if (ref.getReferenceCount() == 0) {
+ clusterLoadStatsStores.remove(clusterService);
+ }
+ if (clusterLoadStatsStores.isEmpty()) {
+ loadStatsStores.remove(cluster);
+ }
+ }
+
+ /**
+ * Generates reports summarizing the stats recorded for loads sent to the given cluster for
+ * the interval between calls of this method or {@link #getAllLoadReports}. A cluster may send
+ * loads to more than one cluster_service, they are included in separate stats reports.
+ */
+ // TODO(chengyuanzhang): do not use proto type directly.
+ List getClusterLoadReports(String cluster) {
+ List res = new ArrayList<>();
+ Map> clusterLoadStatsStores =
+ loadStatsStores.get(cluster);
+ if (clusterLoadStatsStores == null) {
+ return res;
+ }
+ for (ReferenceCounted ref : clusterLoadStatsStores.values()) {
+ res.add(ref.get().generateLoadReport());
+ }
+ return res;
+ }
+
+ /**
+ * Generates reports summarized the stats recorded for loads sent to all clusters for the
+ * interval between calls of this method or {@link #getClusterLoadReports}. Each report
+ * includes stats for one cluster:cluster_service.
+ */
+ // TODO(chengyuanzhang): do not use proto type directly.
+ List getAllLoadReports() {
+ List res = new ArrayList<>();
+ for (Map> clusterLoadStatsStores
+ : loadStatsStores.values()) {
+ for (ReferenceCounted ref : clusterLoadStatsStores.values()) {
+ res.add(ref.get().generateLoadReport());
+ }
+ }
+ return res;
+ }
+
+ @VisibleForTesting
+ interface LoadStatsStoreFactory {
+ LoadStatsStore newLoadStatsStore(String cluster, String clusterService);
+ }
+
+ /**
+ * Interface for client side load stats store. An {@code LoadStatsStore} maintains load stats per
+ * cluster:cluster_service exposed by traffic director from a gRPC client's perspective,
+ * including dropped calls. Load stats for endpoints are aggregated in locality granularity
+ * while the numbers of dropped calls are aggregated in cluster:cluster_service granularity.
+ */
+ interface LoadStatsStore {
+
+ /**
+ * Generates a report based on recorded load stats (including RPC counts, backend metrics and
+ * dropped calls) for the interval since the previous call of this method.
+ */
+ // TODO(chengyuanzhang): do not use proto type directly.
+ ClusterStats generateLoadReport();
+
+ /**
+ * Track load stats for endpoints in the provided locality. Only load stats for endpoints
+ * in tracked localities will be included in generated load reports.
+ */
+ ClientLoadCounter addLocality(Locality locality);
+
+ /**
+ * Drop tracking load stats for endpoints in the provided locality. Load stats for endpoints
+ * in removed localities will no longer be included in future generated load reports after
+ * their currently recording stats have been fully reported.
+ */
+ void removeLocality(Locality locality);
+
+ /**
+ * Records a drop decision.
+ *
+ * This method is thread-safe.
+ */
+ void recordDroppedRequest(String category);
+ }
+}
diff --git a/xds/src/main/java/io/grpc/xds/LoadStatsStore.java b/xds/src/main/java/io/grpc/xds/LoadStatsStore.java
deleted file mode 100644
index cd76be41edf..00000000000
--- a/xds/src/main/java/io/grpc/xds/LoadStatsStore.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright 2019 The gRPC Authors
- *
- * 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.
- */
-
-package io.grpc.xds;
-
-import io.envoyproxy.envoy.api.v2.endpoint.ClusterStats;
-import io.grpc.xds.EnvoyProtoData.Locality;
-import javax.annotation.Nullable;
-
-/**
- * Interface for client side load stats store. An {@code LoadStatsStore} maintains load stats per
- * cluster:cluster_service exposed by traffic director from a gRPC client's perspective,
- * including dropped calls. Load stats for endpoints (i.e., Google backends) are aggregated in
- * locality granularity (i.e., Google cluster) while the numbers of dropped calls are aggregated
- * in cluster:cluster_service granularity.
- *
- *
An {@code LoadStatsStore} only tracks loads for localities exposed by remote traffic
- * director. A proper usage should be
- *
- *
- * - Let {@link LoadStatsStore} track the locality newly exposed by traffic director by
- * calling {@link #addLocality(Locality)}.
- *
- Use the locality counter returned by {@link #getLocalityCounter(Locality)} to record
- * load stats for the corresponding locality.
- *
- Tell {@link LoadStatsStore} to stop tracking the locality no longer exposed by traffic
- * director by calling {@link #removeLocality(Locality)}.
- *
- *
- * No locality information is needed for recording dropped calls since they are aggregated in
- * cluster granularity.
- */
-interface LoadStatsStore {
-
- /**
- * Generates a {@link ClusterStats} proto message as the load report based on recorded load stats
- * (including RPC * counts, backend metrics and dropped calls) for the interval since the previous
- * call of this method.
- *
- *
Loads for localities no longer under tracking will not be included in generated load reports
- * once all of theirs loads are completed and reported.
- *
- *
The fields {@code cluster_name} and {@code load_report_interval} in the returned {@link
- * ClusterStats} needs to be set before it is ready to be sent to the traffic director for load
- * reporting.
- *
- *
This method is not thread-safe and should be called from the same synchronized context
- * used by {@link LoadReportClient}.
- */
- ClusterStats generateLoadReport();
-
- /**
- * Starts tracking load stats for endpoints in the provided locality. Only load stats for
- * endpoints in added localities will be recorded and included in generated load reports.
- *
- *
This method needs to be called at locality updates only for newly assigned localities in
- * endpoint discovery responses before recording loads for those localities.
- *
- *
This method is not thread-safe and should be called from the same synchronized context
- * used by {@link LoadReportClient}.
- */
- void addLocality(Locality locality);
-
- /**
- * Stops tracking load stats for endpoints in the provided locality. gRPC clients are expected not
- * to send loads to localities no longer exposed by traffic director. Load stats for endpoints in
- * removed localities will no longer be included in future generated load reports after their
- * recorded and ongoing loads have been reported.
- *
- *
This method needs to be called at locality updates only for newly removed localities.
- * Forgetting calling this method for localities no longer under track will result in memory
- * waste and keep including zero-load upstream locality stats in generated load reports.
- *
- *
This method is not thread-safe and should be called from the same synchronized context
- * used by {@link LoadReportClient}.
- */
- void removeLocality(Locality locality);
-
- /**
- * Returns the locality counter that does locality level stats aggregation for the provided
- * locality. If the provided locality is not tracked, {@code null} will be returned.
- *
- *
This method is thread-safe.
- */
- @Nullable
- ClientLoadCounter getLocalityCounter(Locality locality);
-
- /**
- * Records a drop decision made by a {@link io.grpc.LoadBalancer.SubchannelPicker} instance
- * with the provided category. Drops are aggregated in cluster granularity.
- *
- *
This method is thread-safe.
- */
- void recordDroppedRequest(String category);
-}
diff --git a/xds/src/main/java/io/grpc/xds/LoadStatsStoreImpl.java b/xds/src/main/java/io/grpc/xds/LoadStatsStoreImpl.java
index 37ff09d91be..ad597d5484c 100644
--- a/xds/src/main/java/io/grpc/xds/LoadStatsStoreImpl.java
+++ b/xds/src/main/java/io/grpc/xds/LoadStatsStoreImpl.java
@@ -17,19 +17,24 @@
package io.grpc.xds;
import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Preconditions.checkState;
import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Stopwatch;
+import com.google.protobuf.util.Durations;
import io.envoyproxy.envoy.api.v2.endpoint.ClusterStats;
import io.envoyproxy.envoy.api.v2.endpoint.ClusterStats.DroppedRequests;
import io.envoyproxy.envoy.api.v2.endpoint.EndpointLoadMetricStats;
import io.envoyproxy.envoy.api.v2.endpoint.UpstreamLocalityStats;
+import io.grpc.internal.GrpcUtil;
import io.grpc.xds.ClientLoadCounter.ClientLoadSnapshot;
import io.grpc.xds.ClientLoadCounter.MetricValue;
import io.grpc.xds.EnvoyProtoData.Locality;
+import io.grpc.xds.LoadStatsManager.LoadStatsStore;
+import io.grpc.xds.LoadStatsManager.LoadStatsStoreFactory;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import javax.annotation.Nullable;
import javax.annotation.concurrent.NotThreadSafe;
@@ -45,12 +50,14 @@ final class LoadStatsStoreImpl implements LoadStatsStore {
@Nullable
@SuppressWarnings("unused")
private final String clusterServiceName;
- private final ConcurrentMap localityLoadCounters;
+ private final ConcurrentMap> localityLoadCounters
+ = new ConcurrentHashMap<>();
// Cluster level dropped request counts for each category decision made by xDS load balancer.
private final ConcurrentMap dropCounters;
+ private final Stopwatch stopwatch;
LoadStatsStoreImpl(String clusterName, @Nullable String clusterServiceName) {
- this(clusterName, clusterServiceName, new ConcurrentHashMap(),
+ this(clusterName, clusterServiceName, GrpcUtil.STOPWATCH_SUPPLIER.get(),
new ConcurrentHashMap());
}
@@ -58,12 +65,13 @@ final class LoadStatsStoreImpl implements LoadStatsStore {
LoadStatsStoreImpl(
String clusterName,
@Nullable String clusterServiceName,
- ConcurrentMap localityLoadCounters,
+ Stopwatch stopwatch,
ConcurrentMap dropCounters) {
this.clusterName = checkNotNull(clusterName, "clusterName");
this.clusterServiceName = clusterServiceName;
- this.localityLoadCounters = checkNotNull(localityLoadCounters, "localityLoadCounters");
+ this.stopwatch = checkNotNull(stopwatch, "stopwatch");
this.dropCounters = checkNotNull(dropCounters, "dropCounters");
+ stopwatch.reset().start();
}
@Override
@@ -71,10 +79,11 @@ public ClusterStats generateLoadReport() {
ClusterStats.Builder statsBuilder = ClusterStats.newBuilder();
statsBuilder.setClusterName(clusterName);
// TODO(chengyuangzhang): also set cluster_service_name if provided.
- for (Map.Entry entry : localityLoadCounters.entrySet()) {
- ClientLoadSnapshot snapshot = entry.getValue().snapshot();
+ for (Map.Entry> entry
+ : localityLoadCounters.entrySet()) {
+ ClientLoadSnapshot snapshot = entry.getValue().get().snapshot();
UpstreamLocalityStats.Builder localityStatsBuilder =
- UpstreamLocalityStats.newBuilder().setLocality(entry.getKey().toEnvoyProtoLocality());
+ UpstreamLocalityStats.newBuilder().setLocality(entry.getKey().toEnvoyProtoLocalityV2());
localityStatsBuilder
.setTotalSuccessfulRequests(snapshot.getCallsSucceeded())
.setTotalErrorRequests(snapshot.getCallsFailed())
@@ -90,7 +99,7 @@ public ClusterStats generateLoadReport() {
statsBuilder.addUpstreamLocalityStats(localityStatsBuilder);
// Discard counters for localities that are no longer exposed by the remote balancer and
// no RPCs ongoing.
- if (!entry.getValue().isActive() && snapshot.getCallsInProgress() == 0) {
+ if (entry.getValue().getReferenceCount() == 0 && snapshot.getCallsInProgress() == 0) {
localityLoadCounters.remove(entry.getKey());
}
}
@@ -103,32 +112,27 @@ public ClusterStats generateLoadReport() {
.setDroppedCount(drops));
}
statsBuilder.setTotalDroppedRequests(totalDrops);
+ statsBuilder.setLoadReportInterval(
+ Durations.fromNanos(stopwatch.elapsed(TimeUnit.NANOSECONDS)));
+ stopwatch.reset().start();
return statsBuilder.build();
}
@Override
- public void addLocality(final Locality locality) {
- ClientLoadCounter counter = localityLoadCounters.get(locality);
- checkState(counter == null || !counter.isActive(),
- "An active counter for locality %s already exists", locality);
+ public ClientLoadCounter addLocality(final Locality locality) {
+ ReferenceCounted counter = localityLoadCounters.get(locality);
if (counter == null) {
- localityLoadCounters.put(locality, new ClientLoadCounter());
- } else {
- counter.setActive(true);
+ counter = ReferenceCounted.wrap(new ClientLoadCounter());
+ localityLoadCounters.put(locality, counter);
}
+ counter.retain();
+ return counter.get();
}
@Override
public void removeLocality(final Locality locality) {
- ClientLoadCounter counter = localityLoadCounters.get(locality);
- checkState(counter != null && counter.isActive(),
- "No active counter for locality %s exists", locality);
- counter.setActive(false);
- }
-
- @Override
- public ClientLoadCounter getLocalityCounter(final Locality locality) {
- return localityLoadCounters.get(locality);
+ ReferenceCounted counter = localityLoadCounters.get(locality);
+ counter.release();
}
@Override
@@ -142,4 +146,13 @@ public void recordDroppedRequest(String category) {
}
counter.getAndIncrement();
}
+
+ static LoadStatsStoreFactory getDefaultFactory() {
+ return new LoadStatsStoreFactory() {
+ @Override
+ public LoadStatsStore newLoadStatsStore(String cluster, String clusterService) {
+ return new LoadStatsStoreImpl(cluster, clusterService);
+ }
+ };
+ }
}
diff --git a/xds/src/main/java/io/grpc/xds/LocalityStore.java b/xds/src/main/java/io/grpc/xds/LocalityStore.java
index 183b4bbb7f5..4fa507e6a12 100644
--- a/xds/src/main/java/io/grpc/xds/LocalityStore.java
+++ b/xds/src/main/java/io/grpc/xds/LocalityStore.java
@@ -47,6 +47,7 @@
import io.grpc.xds.EnvoyProtoData.LbEndpoint;
import io.grpc.xds.EnvoyProtoData.Locality;
import io.grpc.xds.EnvoyProtoData.LocalityLbEndpoints;
+import io.grpc.xds.LoadStatsManager.LoadStatsStore;
import io.grpc.xds.OrcaOobUtil.OrcaReportingConfig;
import io.grpc.xds.OrcaOobUtil.OrcaReportingHelperWrapper;
import io.grpc.xds.WeightedRandomPicker.WeightedChildPicker;
@@ -311,8 +312,8 @@ private final class LocalityLbInfo {
LocalityLbInfo(Locality locality) {
this.locality = checkNotNull(locality, "locality");
- loadStatsStore.addLocality(locality);
- childHelper = new ChildHelper();
+ ClientLoadCounter counter = loadStatsStore.addLocality(locality);
+ childHelper = new ChildHelper(counter);
childBalancer = loadBalancerProvider.newLoadBalancer(childHelper);
}
@@ -368,8 +369,7 @@ class ChildHelper extends ForwardingLoadBalancerHelper {
private SubchannelPicker currentChildPicker = XdsSubchannelPickers.BUFFER_PICKER;
private ConnectivityState currentChildState = CONNECTING;
- ChildHelper() {
- final ClientLoadCounter counter = loadStatsStore.getLocalityCounter(locality);
+ ChildHelper(final ClientLoadCounter counter) {
Helper delegate = new ForwardingLoadBalancerHelper() {
@Override
protected Helper delegate() {
diff --git a/xds/src/main/java/io/grpc/xds/LrsLoadBalancer.java b/xds/src/main/java/io/grpc/xds/LrsLoadBalancer.java
index 55ca451271b..edcfcc0cc39 100644
--- a/xds/src/main/java/io/grpc/xds/LrsLoadBalancer.java
+++ b/xds/src/main/java/io/grpc/xds/LrsLoadBalancer.java
@@ -26,6 +26,7 @@
import io.grpc.util.GracefulSwitchLoadBalancer;
import io.grpc.xds.ClientLoadCounter.LoadRecordingSubchannelPicker;
import io.grpc.xds.EnvoyProtoData.Locality;
+import io.grpc.xds.LoadStatsManager.LoadStatsStore;
import io.grpc.xds.LrsLoadBalancerProvider.LrsConfig;
import io.grpc.xds.XdsSubchannelPickers.ErrorPicker;
import java.util.Objects;
@@ -58,8 +59,7 @@ public void handleResolvedAddresses(ResolvedAddresses resolvedAddresses) {
checkAndSetUp(config, store);
if (switchingLoadBalancer == null) {
- loadStatsStore.addLocality(config.locality);
- final ClientLoadCounter counter = loadStatsStore.getLocalityCounter(config.locality);
+ final ClientLoadCounter counter = loadStatsStore.addLocality(config.locality);
LoadBalancer.Helper loadRecordingHelper = new ForwardingLoadBalancerHelper() {
@Override
protected Helper delegate() {
diff --git a/xds/src/main/java/io/grpc/xds/ReferenceCounted.java b/xds/src/main/java/io/grpc/xds/ReferenceCounted.java
new file mode 100644
index 00000000000..b62340ffa0a
--- /dev/null
+++ b/xds/src/main/java/io/grpc/xds/ReferenceCounted.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2020 The gRPC Authors
+ *
+ * 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.
+ */
+
+package io.grpc.xds;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+/**
+ * A reference count wrapper for objects. This class does not take the ownership for the object,
+ * but only provides usage counting. The real owner of the wrapped object is responsible for
+ * managing the lifecycle of the object.
+ *
+ * Intended for a container class to keep track of lifecycle for elements it contains. This
+ * wrapper itself should never be returned to the consumers of the elements to avoid reference
+ * counts being leaked.
+ */
+final class ReferenceCounted {
+ private final T instance;
+ private int refs;
+
+ private ReferenceCounted(T instance) {
+ this.instance = instance;
+ }
+
+ static ReferenceCounted wrap(T instance) {
+ checkNotNull(instance, "instance");
+ return new ReferenceCounted<>(instance);
+ }
+
+ void retain() {
+ refs++;
+ }
+
+ void release() {
+ checkState(refs > 0, "reference reached 0");
+ refs--;
+ }
+
+ int getReferenceCount() {
+ return refs;
+ }
+
+ T get() {
+ return instance;
+ }
+}
diff --git a/xds/src/main/java/io/grpc/xds/XdsAttributes.java b/xds/src/main/java/io/grpc/xds/XdsAttributes.java
index 662c9a69a5a..57796658636 100644
--- a/xds/src/main/java/io/grpc/xds/XdsAttributes.java
+++ b/xds/src/main/java/io/grpc/xds/XdsAttributes.java
@@ -22,6 +22,7 @@
import io.grpc.NameResolver;
import io.grpc.internal.ObjectPool;
import io.grpc.xds.EnvoyServerProtoData.UpstreamTlsContext;
+import io.grpc.xds.LoadStatsManager.LoadStatsStore;
/**
* Special attributes that are only useful to gRPC in the XDS context.
diff --git a/xds/src/main/java/io/grpc/xds/XdsClient.java b/xds/src/main/java/io/grpc/xds/XdsClient.java
index b74ec3360b3..69a12843003 100644
--- a/xds/src/main/java/io/grpc/xds/XdsClient.java
+++ b/xds/src/main/java/io/grpc/xds/XdsClient.java
@@ -37,6 +37,7 @@
import io.grpc.xds.EnvoyProtoData.Route;
import io.grpc.xds.EnvoyServerProtoData.Listener;
import io.grpc.xds.EnvoyServerProtoData.UpstreamTlsContext;
+import io.grpc.xds.LoadStatsManager.LoadStatsStore;
import io.grpc.xds.XdsLogger.XdsLogLevel;
import java.util.ArrayList;
import java.util.Collection;
@@ -518,21 +519,34 @@ void watchListenerData(int port, ListenerWatcher watcher) {
}
/**
- * Report client load stats to a remote server for the given cluster:cluster_service.
- *
- * Note: currently we can only report loads for a single cluster:cluster_service,
- * as the design for adding clusters to report loads for while load reporting is
- * happening is undefined.
+ * Starts client side load reporting via LRS. All clusters report load through one LRS stream,
+ * only the first call of this method effectively starts the LRS stream.
+ */
+ void reportClientStats() {
+ }
+
+ /**
+ * Stops client side load reporting via LRS. All clusters report load through one LRS stream,
+ * only the last call of this method effectively stops the LRS stream.
+ */
+ void cancelClientStatsReport() {
+ }
+
+ /**
+ * Starts recording client load stats for the given cluster:cluster_service. Caller should use
+ * the returned {@link LoadStatsStore} to record and aggregate stats for load sent to the given
+ * cluster:cluster_service. Recorded stats may be reported to a load reporting server if enabled.
*/
- void reportClientStats(
- String clusterName, @Nullable String clusterServiceName, LoadStatsStore loadStatsStore) {
+ LoadStatsStore addClientStats(String clusterName, @Nullable String clusterServiceName) {
throw new UnsupportedOperationException();
}
/**
- * Stops reporting client load stats to the remote server for the given cluster:cluster_service.
+ * Stops recording client load stats for the given cluster:cluster_service. The load reporting
+ * server will no longer receive stats for the given cluster:cluster_service after this call.
*/
- void cancelClientStatsReport(String clusterName, @Nullable String clusterServiceName) {
+ void removeClientStats(String clusterName, @Nullable String clusterServiceName) {
+ throw new UnsupportedOperationException();
}
abstract static class XdsClientFactory {
@@ -601,13 +615,17 @@ public synchronized XdsClient returnObject(Object object) {
* Factory for creating channels to xDS severs.
*/
abstract static class XdsChannelFactory {
- private static final XdsChannelFactory DEFAULT_INSTANCE = new XdsChannelFactory() {
+ @VisibleForTesting
+ static boolean experimentalV3SupportEnvVar = Boolean.parseBoolean(
+ System.getenv("GRPC_XDS_EXPERIMENTAL_V3_SUPPORT"));
+ private static final String XDS_V3_SERVER_FEATURE = "xds_v3";
+ private static final XdsChannelFactory DEFAULT_INSTANCE = new XdsChannelFactory() {
/**
* Creates a channel to the first server in the given list.
*/
@Override
- ManagedChannel createChannel(List servers) {
+ XdsChannel createChannel(List servers) {
checkArgument(!servers.isEmpty(), "No management server provided.");
XdsLogger logger = XdsLogger.withPrefix("xds-client-channel-factory");
ServerInfo serverInfo = servers.get(0);
@@ -629,9 +647,13 @@ ManagedChannel createChannel(List servers) {
channelBuilder = ManagedChannelBuilder.forTarget(serverUri);
}
- return channelBuilder
+ ManagedChannel channel = channelBuilder
.keepAliveTime(5, TimeUnit.MINUTES)
.build();
+ boolean useProtocolV3 = experimentalV3SupportEnvVar
+ && serverInfo.getServerFeatures().contains(XDS_V3_SERVER_FEATURE);
+
+ return new XdsChannel(channel, useProtocolV3);
}
};
@@ -642,6 +664,25 @@ static XdsChannelFactory getInstance() {
/**
* Creates a channel to one of the provided management servers.
*/
- abstract ManagedChannel createChannel(List servers);
+ abstract XdsChannel createChannel(List servers);
+ }
+
+ static final class XdsChannel {
+ private final ManagedChannel managedChannel;
+ private final boolean useProtocolV3;
+
+ @VisibleForTesting
+ XdsChannel(ManagedChannel managedChannel, boolean useProtocolV3) {
+ this.managedChannel = managedChannel;
+ this.useProtocolV3 = useProtocolV3;
+ }
+
+ ManagedChannel getManagedChannel() {
+ return managedChannel;
+ }
+
+ boolean isUseProtocolV3() {
+ return useProtocolV3;
+ }
}
}
diff --git a/xds/src/main/java/io/grpc/xds/XdsClientImpl.java b/xds/src/main/java/io/grpc/xds/XdsClientImpl.java
index e8bd49906b3..c386daca3c6 100644
--- a/xds/src/main/java/io/grpc/xds/XdsClientImpl.java
+++ b/xds/src/main/java/io/grpc/xds/XdsClientImpl.java
@@ -27,14 +27,8 @@
import com.google.protobuf.Any;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.MessageOrBuilder;
-import com.google.protobuf.Struct;
-import com.google.protobuf.Value;
import com.google.protobuf.util.JsonFormat;
import com.google.rpc.Code;
-import io.envoyproxy.envoy.api.v2.DiscoveryRequest;
-import io.envoyproxy.envoy.api.v2.DiscoveryResponse;
-import io.envoyproxy.envoy.api.v2.core.Node;
-import io.envoyproxy.envoy.api.v2.core.SocketAddress;
import io.envoyproxy.envoy.config.cluster.v3.Cluster;
import io.envoyproxy.envoy.config.cluster.v3.Cluster.DiscoveryType;
import io.envoyproxy.envoy.config.cluster.v3.Cluster.EdsClusterConfig;
@@ -50,7 +44,9 @@
import io.envoyproxy.envoy.config.route.v3.VirtualHost;
import io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager;
import io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.Rds;
-import io.envoyproxy.envoy.service.discovery.v2.AggregatedDiscoveryServiceGrpc;
+import io.envoyproxy.envoy.service.discovery.v3.AggregatedDiscoveryServiceGrpc;
+import io.envoyproxy.envoy.service.discovery.v3.DiscoveryRequest;
+import io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse;
import io.grpc.InternalLogId;
import io.grpc.ManagedChannel;
import io.grpc.Status;
@@ -62,9 +58,10 @@
import io.grpc.xds.EnvoyProtoData.DropOverload;
import io.grpc.xds.EnvoyProtoData.Locality;
import io.grpc.xds.EnvoyProtoData.LocalityLbEndpoints;
+import io.grpc.xds.EnvoyProtoData.Node;
import io.grpc.xds.EnvoyProtoData.StructOrError;
import io.grpc.xds.EnvoyServerProtoData.UpstreamTlsContext;
-import io.grpc.xds.LoadReportClient.LoadReportCallback;
+import io.grpc.xds.LoadStatsManager.LoadStatsStore;
import io.grpc.xds.XdsLogger.XdsLogLevel;
import java.util.ArrayList;
import java.util.Collection;
@@ -87,12 +84,14 @@ final class XdsClientImpl extends XdsClient {
@VisibleForTesting
static final String ADS_TYPE_URL_LDS_V2 = "type.googleapis.com/envoy.api.v2.Listener";
- private static final String ADS_TYPE_URL_LDS =
+ @VisibleForTesting
+ static final String ADS_TYPE_URL_LDS =
"type.googleapis.com/envoy.config.listener.v3.Listener";
@VisibleForTesting
static final String ADS_TYPE_URL_RDS_V2 =
"type.googleapis.com/envoy.api.v2.RouteConfiguration";
- private static final String ADS_TYPE_URL_RDS =
+ @VisibleForTesting
+ static final String ADS_TYPE_URL_RDS =
"type.googleapis.com/envoy.config.route.v3.RouteConfiguration";
private static final String TYPE_URL_HTTP_CONNECTION_MANAGER_V2 =
"type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2"
@@ -102,12 +101,14 @@ final class XdsClientImpl extends XdsClient {
+ ".HttpConnectionManager";
@VisibleForTesting
static final String ADS_TYPE_URL_CDS_V2 = "type.googleapis.com/envoy.api.v2.Cluster";
- private static final String ADS_TYPE_URL_CDS =
+ @VisibleForTesting
+ static final String ADS_TYPE_URL_CDS =
"type.googleapis.com/envoy.config.cluster.v3.Cluster";
@VisibleForTesting
static final String ADS_TYPE_URL_EDS_V2 =
"type.googleapis.com/envoy.api.v2.ClusterLoadAssignment";
- private static final String ADS_TYPE_URL_EDS =
+ @VisibleForTesting
+ static final String ADS_TYPE_URL_EDS =
"type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment";
private final MessagePrinter respPrinter = new MessagePrinter();
@@ -117,6 +118,7 @@ final class XdsClientImpl extends XdsClient {
// Name of the target server this gRPC client is trying to talk to.
private final String targetName;
private final ManagedChannel channel;
+ private final boolean useProtocolV3;
private final SynchronizationContext syncContext;
private final ScheduledExecutorService timeService;
private final BackoffPolicy.Provider backoffPolicyProvider;
@@ -161,6 +163,8 @@ final class XdsClientImpl extends XdsClient {
// Timers for concluding EDS resources not found.
private final Map edsRespTimers = new HashMap<>();
+ private final LoadStatsManager loadStatsManager = new LoadStatsManager();
+
// Timer for concluding the currently requesting LDS resource not found.
@Nullable
private ScheduledHandle ldsRespTimer;
@@ -170,13 +174,14 @@ final class XdsClientImpl extends XdsClient {
private ScheduledHandle rdsRespTimer;
@Nullable
- private AdsStream adsStream;
+ private AbstractAdsStream adsStream;
@Nullable
private BackoffPolicy retryBackoffPolicy;
@Nullable
private ScheduledHandle rpcRetryTimer;
@Nullable
private LoadReportClient lrsClient;
+ private int loadReportCount; // number of clusters enabling load reporting
// Following fields are set only after the ConfigWatcher registered. Once set, they should
// never change. Only a ConfigWatcher or ListenerWatcher can be registered.
@@ -201,9 +206,11 @@ final class XdsClientImpl extends XdsClient {
BackoffPolicy.Provider backoffPolicyProvider,
Supplier stopwatchSupplier) {
this.targetName = checkNotNull(targetName, "targetName");
- this.channel =
+ XdsChannel xdsChannel =
checkNotNull(channelFactory, "channelFactory")
.createChannel(checkNotNull(servers, "servers"));
+ this.channel = xdsChannel.getManagedChannel();
+ this.useProtocolV3 = xdsChannel.isUseProtocolV3();
this.node = checkNotNull(node, "node");
this.syncContext = checkNotNull(syncContext, "syncContext");
this.timeService = checkNotNull(timeService, "timeService");
@@ -273,7 +280,7 @@ void watchConfigData(String targetAuthority, ConfigWatcher watcher) {
if (adsStream == null) {
startRpcStream();
}
- adsStream.sendXdsRequest(ADS_TYPE_URL_LDS_V2, ImmutableList.of(ldsResourceName));
+ adsStream.sendXdsRequest(ResourceType.LDS, ImmutableList.of(ldsResourceName));
ldsRespTimer =
syncContext
.schedule(
@@ -314,7 +321,7 @@ void watchClusterData(String clusterName, ClusterWatcher watcher) {
if (adsStream == null) {
startRpcStream();
}
- adsStream.sendXdsRequest(ADS_TYPE_URL_CDS_V2, clusterWatchers.keySet());
+ adsStream.sendXdsRequest(ResourceType.CDS, clusterWatchers.keySet());
ScheduledHandle timeoutHandle =
syncContext
.schedule(
@@ -351,7 +358,7 @@ void cancelClusterDataWatch(String clusterName, ClusterWatcher watcher) {
}
checkState(adsStream != null,
"Severe bug: ADS stream was not created while an endpoint watcher was registered");
- adsStream.sendXdsRequest(ADS_TYPE_URL_CDS_V2, clusterWatchers.keySet());
+ adsStream.sendXdsRequest(ResourceType.CDS, clusterWatchers.keySet());
}
}
@@ -392,7 +399,7 @@ void watchEndpointData(String clusterName, EndpointWatcher watcher) {
if (adsStream == null) {
startRpcStream();
}
- adsStream.sendXdsRequest(ADS_TYPE_URL_EDS_V2, endpointWatchers.keySet());
+ adsStream.sendXdsRequest(ResourceType.EDS, endpointWatchers.keySet());
ScheduledHandle timeoutHandle =
syncContext
.schedule(
@@ -427,7 +434,7 @@ void cancelEndpointDataWatch(String clusterName, EndpointWatcher watcher) {
// Currently in retry backoff.
return;
}
- adsStream.sendXdsRequest(ADS_TYPE_URL_EDS_V2, endpointWatchers.keySet());
+ adsStream.sendXdsRequest(ResourceType.EDS, endpointWatchers.keySet());
}
}
@@ -448,7 +455,7 @@ void watchListenerData(int port, ListenerWatcher watcher) {
startRpcStream();
}
updateNodeMetadataForListenerRequest(port);
- adsStream.sendXdsRequest(ADS_TYPE_URL_LDS_V2, ImmutableList.of());
+ adsStream.sendXdsRequest(ResourceType.LDS, ImmutableList.of());
ldsRespTimer =
syncContext
.schedule(
@@ -458,58 +465,57 @@ void watchListenerData(int port, ListenerWatcher watcher) {
/** In case of Listener watcher metadata to be updated to include port. */
private void updateNodeMetadataForListenerRequest(int port) {
- Struct newMetadata = node.getMetadata().toBuilder()
- .putFields("TRAFFICDIRECTOR_PROXYLESS",
- Value.newBuilder().setStringValue("1").build())
- .build();
- io.envoyproxy.envoy.api.v2.core.Address listeningAddress =
- io.envoyproxy.envoy.api.v2.core.Address.newBuilder()
- .setSocketAddress(
- SocketAddress.newBuilder()
- .setAddress("0.0.0.0")
- .setPortValue(port)
- .build())
- .build();
+ Map newMetadata = new HashMap<>();
+ if (node.getMetadata() != null) {
+ newMetadata.putAll(node.getMetadata());
+ }
+ newMetadata.put("TRAFFICDIRECTOR_PROXYLESS", "1");
+ EnvoyProtoData.Address listeningAddress =
+ new EnvoyProtoData.Address("0.0.0.0", port);
node =
node.toBuilder().setMetadata(newMetadata).addListeningAddresses(listeningAddress).build();
}
@Override
- void reportClientStats(
- String clusterName, @Nullable String clusterServiceName, LoadStatsStore loadStatsStore) {
+ void reportClientStats() {
if (lrsClient == null) {
+ logger.log(XdsLogLevel.INFO, "Turning on load reporting");
lrsClient =
new LoadReportClient(
- logId,
targetName,
+ loadStatsManager,
channel,
- node,
+ node.toEnvoyProtoNodeV2(),
syncContext,
timeService,
backoffPolicyProvider,
stopwatchSupplier);
- lrsClient.startLoadReporting(new LoadReportCallback() {
- @Override
- public void onReportResponse(long reportIntervalNano) {}
- });
}
- logger.log(
- XdsLogLevel.INFO,
- "Report loads for cluster: {0}, cluster_service: {1}", clusterName, clusterServiceName);
- lrsClient.addLoadStatsStore(clusterName, clusterServiceName, loadStatsStore);
+ if (loadReportCount == 0) {
+ lrsClient.startLoadReporting();
+ }
+ loadReportCount++;
}
@Override
- void cancelClientStatsReport(String clusterName, @Nullable String clusterServiceName) {
- checkState(lrsClient != null, "load reporting was never started");
- logger.log(
- XdsLogLevel.INFO,
- "Stop reporting loads for cluster: {0}, cluster_service: {1}",
- clusterName,
- clusterServiceName);
- lrsClient.removeLoadStatsStore(clusterName, clusterServiceName);
- // TODO(chengyuanzhang): can be optimized to stop load reporting if no more loads need
- // to be reported.
+ void cancelClientStatsReport() {
+ checkState(loadReportCount > 0, "load reporting was never started");
+ loadReportCount--;
+ if (loadReportCount == 0) {
+ logger.log(XdsLogLevel.INFO, "Turning off load reporting");
+ lrsClient.stopLoadReporting();
+ lrsClient = null;
+ }
+ }
+
+ @Override
+ LoadStatsStore addClientStats(String clusterName, @Nullable String clusterServiceName) {
+ return loadStatsManager.addLoadStats(clusterName, clusterServiceName);
+ }
+
+ @Override
+ void removeClientStats(String clusterName, @Nullable String clusterServiceName) {
+ loadStatsManager.removeLoadStats(clusterName, clusterServiceName);
}
@Override
@@ -523,9 +529,11 @@ public String toString() {
*/
private void startRpcStream() {
checkState(adsStream == null, "Previous adsStream has not been cleared yet");
- AggregatedDiscoveryServiceGrpc.AggregatedDiscoveryServiceStub stub =
- AggregatedDiscoveryServiceGrpc.newStub(channel);
- adsStream = new AdsStream(stub);
+ if (useProtocolV3) {
+ adsStream = new AdsStream();
+ } else {
+ adsStream = new AdsStreamV2();
+ }
adsStream.start();
logger.log(XdsLogLevel.INFO, "ADS stream started");
adsStreamRetryStopwatch.reset().start();
@@ -535,13 +543,9 @@ private void startRpcStream() {
* Calls handleLdsResponseForListener or handleLdsResponseForConfigUpdate based on which watcher
* was set.
*/
- private void handleLdsResponse(DiscoveryResponse ldsResponse) {
+ private void handleLdsResponse(DiscoveryResponseData ldsResponse) {
checkState((configWatcher != null) != (listenerWatcher != null),
"No LDS request was ever sent. Management server is doing something wrong");
- if (logger.isLoggable(XdsLogLevel.DEBUG)) {
- logger.log(
- XdsLogLevel.DEBUG, "Received LDS response:\n{0}", respPrinter.print(ldsResponse));
- }
if (listenerWatcher != null) {
handleLdsResponseForListener(ldsResponse);
} else {
@@ -557,13 +561,13 @@ private void handleLdsResponse(DiscoveryResponse ldsResponse) {
* resolution. The response is NACKed if contains invalid data for gRPC's usage. Otherwise, an
* ACK request is sent to management server.
*/
- private void handleLdsResponseForConfigUpdate(DiscoveryResponse ldsResponse) {
+ private void handleLdsResponseForConfigUpdate(DiscoveryResponseData ldsResponse) {
checkState(ldsResourceName != null && configWatcher != null,
"LDS request for ConfigWatcher was never sent!");
// Unpack Listener messages.
- List listeners = new ArrayList<>(ldsResponse.getResourcesCount());
- List listenerNames = new ArrayList<>(ldsResponse.getResourcesCount());
+ List listeners = new ArrayList<>(ldsResponse.getResourcesList().size());
+ List listenerNames = new ArrayList<>(ldsResponse.getResourcesList().size());
try {
for (com.google.protobuf.Any res : ldsResponse.getResourcesList()) {
if (res.getTypeUrl().equals(ADS_TYPE_URL_LDS_V2)) {
@@ -576,7 +580,7 @@ private void handleLdsResponseForConfigUpdate(DiscoveryResponse ldsResponse) {
} catch (InvalidProtocolBufferException e) {
logger.log(XdsLogLevel.WARNING, "Failed to unpack Listeners in LDS response {0}", e);
adsStream.sendNackRequest(
- ADS_TYPE_URL_LDS_V2, ImmutableList.of(ldsResourceName),
+ ResourceType.LDS, ImmutableList.of(ldsResourceName),
ldsResponse.getVersionInfo(), "Malformed LDS response: " + e);
return;
}
@@ -601,7 +605,7 @@ private void handleLdsResponseForConfigUpdate(DiscoveryResponse ldsResponse) {
XdsLogLevel.WARNING,
"Failed to unpack HttpConnectionManagers in Listeners of LDS response {0}", e);
adsStream.sendNackRequest(
- ADS_TYPE_URL_LDS_V2, ImmutableList.of(ldsResourceName),
+ ResourceType.LDS, ImmutableList.of(ldsResourceName),
ldsResponse.getVersionInfo(), "Malformed LDS response: " + e);
return;
}
@@ -647,11 +651,11 @@ private void handleLdsResponseForConfigUpdate(DiscoveryResponse ldsResponse) {
if (errorMessage != null) {
adsStream.sendNackRequest(
- ADS_TYPE_URL_LDS_V2, ImmutableList.of(ldsResourceName),
+ ResourceType.LDS, ImmutableList.of(ldsResourceName),
ldsResponse.getVersionInfo(), errorMessage);
return;
}
- adsStream.sendAckRequest(ADS_TYPE_URL_LDS_V2, ImmutableList.of(ldsResourceName),
+ adsStream.sendAckRequest(ResourceType.LDS, ImmutableList.of(ldsResourceName),
ldsResponse.getVersionInfo());
if (routes != null || rdsRouteConfigName != null) {
@@ -673,7 +677,7 @@ private void handleLdsResponseForConfigUpdate(DiscoveryResponse ldsResponse) {
logger.log(
XdsLogLevel.INFO,
"Use RDS to dynamically resolve route config, resource name: {0}", rdsRouteConfigName);
- adsStream.sendXdsRequest(ADS_TYPE_URL_RDS_V2, ImmutableList.of(rdsRouteConfigName));
+ adsStream.sendXdsRequest(ResourceType.RDS, ImmutableList.of(rdsRouteConfigName));
// Cancel the timer for fetching the previous RDS resource.
if (rdsRespTimer != null) {
rdsRespTimer.cancel();
@@ -692,13 +696,13 @@ private void handleLdsResponseForConfigUpdate(DiscoveryResponse ldsResponse) {
}
}
- private void handleLdsResponseForListener(DiscoveryResponse ldsResponse) {
+ private void handleLdsResponseForListener(DiscoveryResponseData ldsResponse) {
checkState(ldsResourceName == null && listenerPort > 0 && listenerWatcher != null,
"LDS request for ListenerWatcher was never sent!");
// Unpack Listener messages.
Listener requestedListener = null;
- logger.log(XdsLogLevel.DEBUG, "Listener count: {0}", ldsResponse.getResourcesCount());
+ logger.log(XdsLogLevel.DEBUG, "Listener count: {0}", ldsResponse.getResourcesList().size());
try {
for (com.google.protobuf.Any res : ldsResponse.getResourcesList()) {
if (res.getTypeUrl().equals(ADS_TYPE_URL_LDS_V2)) {
@@ -714,7 +718,7 @@ private void handleLdsResponseForListener(DiscoveryResponse ldsResponse) {
} catch (InvalidProtocolBufferException e) {
logger.log(XdsLogLevel.WARNING, "Failed to unpack Listeners in LDS response {0}", e);
adsStream.sendNackRequest(
- ADS_TYPE_URL_LDS_V2, ImmutableList.of(),
+ ResourceType.LDS, ImmutableList.of(),
ldsResponse.getVersionInfo(), "Malformed LDS response: " + e);
return;
}
@@ -731,7 +735,7 @@ private void handleLdsResponseForListener(DiscoveryResponse ldsResponse) {
} catch (InvalidProtocolBufferException e) {
logger.log(XdsLogLevel.WARNING, "Failed to unpack Listener in LDS response {0}", e);
adsStream.sendNackRequest(
- ADS_TYPE_URL_LDS_V2, ImmutableList.of(),
+ ResourceType.LDS, ImmutableList.of(),
ldsResponse.getVersionInfo(), "Malformed LDS response: " + e);
return;
}
@@ -740,7 +744,7 @@ private void handleLdsResponseForListener(DiscoveryResponse ldsResponse) {
listenerWatcher.onResourceDoesNotExist(":" + listenerPort);
}
}
- adsStream.sendAckRequest(ADS_TYPE_URL_LDS_V2, ImmutableList.of(),
+ adsStream.sendAckRequest(ResourceType.LDS, ImmutableList.of(),
ldsResponse.getVersionInfo());
if (listenerUpdate != null) {
listenerWatcher.onListenerChanged(listenerUpdate);
@@ -777,15 +781,12 @@ private boolean hasMatchingFilter(List filterChainsList) {
* for the "xds:" URI (with the port, if any, stripped off). The response is NACKed if contains
* invalid data for gRPC's usage. Otherwise, an ACK request is sent to management server.
*/
- private void handleRdsResponse(DiscoveryResponse rdsResponse) {
- if (logger.isLoggable(XdsLogLevel.DEBUG)) {
- logger.log(XdsLogLevel.DEBUG, "Received RDS response:\n{0}", respPrinter.print(rdsResponse));
- }
+ private void handleRdsResponse(DiscoveryResponseData rdsResponse) {
checkState(adsStream.rdsResourceName != null,
"Never requested for RDS resources, management server is doing something wrong");
// Unpack RouteConfiguration messages.
- List routeConfigNames = new ArrayList<>(rdsResponse.getResourcesCount());
+ List routeConfigNames = new ArrayList<>(rdsResponse.getResourcesList().size());
RouteConfiguration requestedRouteConfig = null;
try {
for (com.google.protobuf.Any res : rdsResponse.getResourcesList()) {
@@ -802,7 +803,7 @@ private void handleRdsResponse(DiscoveryResponse rdsResponse) {
logger.log(
XdsLogLevel.WARNING, "Failed to unpack RouteConfiguration in RDS response {0}", e);
adsStream.sendNackRequest(
- ADS_TYPE_URL_RDS_V2, ImmutableList.of(adsStream.rdsResourceName),
+ ResourceType.RDS, ImmutableList.of(adsStream.rdsResourceName),
rdsResponse.getVersionInfo(), "Malformed RDS response: " + e);
return;
}
@@ -817,7 +818,7 @@ private void handleRdsResponse(DiscoveryResponse rdsResponse) {
} catch (InvalidProtoDataException e) {
String errorDetail = e.getMessage();
adsStream.sendNackRequest(
- ADS_TYPE_URL_RDS_V2, ImmutableList.of(adsStream.rdsResourceName),
+ ResourceType.RDS, ImmutableList.of(adsStream.rdsResourceName),
rdsResponse.getVersionInfo(),
"RouteConfiguration " + requestedRouteConfig.getName() + ": cannot find a "
+ "valid cluster name in any virtual hosts with domains matching: "
@@ -827,7 +828,7 @@ private void handleRdsResponse(DiscoveryResponse rdsResponse) {
}
}
- adsStream.sendAckRequest(ADS_TYPE_URL_RDS_V2, ImmutableList.of(adsStream.rdsResourceName),
+ adsStream.sendAckRequest(ResourceType.RDS, ImmutableList.of(adsStream.rdsResourceName),
rdsResponse.getVersionInfo());
// Notify the ConfigWatcher if this RDS response contains the most recently requested
@@ -937,15 +938,12 @@ static VirtualHost findVirtualHostForHostName(
* Response data for requested clusters is cached locally, in case of new cluster watchers
* interested in the same clusters are added later.
*/
- private void handleCdsResponse(DiscoveryResponse cdsResponse) {
- if (logger.isLoggable(XdsLogLevel.DEBUG)) {
- logger.log(XdsLogLevel.DEBUG, "Received CDS response:\n{0}", respPrinter.print(cdsResponse));
- }
+ private void handleCdsResponse(DiscoveryResponseData cdsResponse) {
adsStream.cdsRespNonce = cdsResponse.getNonce();
// Unpack Cluster messages.
- List clusters = new ArrayList<>(cdsResponse.getResourcesCount());
- List clusterNames = new ArrayList<>(cdsResponse.getResourcesCount());
+ List clusters = new ArrayList<>(cdsResponse.getResourcesList().size());
+ List clusterNames = new ArrayList<>(cdsResponse.getResourcesList().size());
try {
for (com.google.protobuf.Any res : cdsResponse.getResourcesList()) {
if (res.getTypeUrl().equals(ADS_TYPE_URL_CDS_V2)) {
@@ -958,7 +956,7 @@ private void handleCdsResponse(DiscoveryResponse cdsResponse) {
} catch (InvalidProtocolBufferException e) {
logger.log(XdsLogLevel.WARNING, "Failed to unpack Clusters in CDS response {0}", e);
adsStream.sendNackRequest(
- ADS_TYPE_URL_CDS_V2, clusterWatchers.keySet(),
+ ResourceType.CDS, clusterWatchers.keySet(),
cdsResponse.getVersionInfo(), "Malformed CDS response: " + e);
return;
}
@@ -1033,13 +1031,13 @@ private void handleCdsResponse(DiscoveryResponse cdsResponse) {
}
if (errorMessage != null) {
adsStream.sendNackRequest(
- ADS_TYPE_URL_CDS_V2,
+ ResourceType.CDS,
clusterWatchers.keySet(),
cdsResponse.getVersionInfo(),
errorMessage);
return;
}
- adsStream.sendAckRequest(ADS_TYPE_URL_CDS_V2, clusterWatchers.keySet(),
+ adsStream.sendAckRequest(ResourceType.CDS, clusterWatchers.keySet(),
cdsResponse.getVersionInfo());
// Update local CDS cache with data in this response.
@@ -1115,15 +1113,11 @@ private static UpstreamTlsContext getTlsContextFromCluster(Cluster cluster)
* cached locally, in case of new endpoint watchers interested in the same clusters
* are added later.
*/
- private void handleEdsResponse(DiscoveryResponse edsResponse) {
- if (logger.isLoggable(XdsLogLevel.DEBUG)) {
- logger.log(XdsLogLevel.DEBUG, "Received EDS response:\n{0}", respPrinter.print(edsResponse));
- }
-
+ private void handleEdsResponse(DiscoveryResponseData edsResponse) {
// Unpack ClusterLoadAssignment messages.
List clusterLoadAssignments =
- new ArrayList<>(edsResponse.getResourcesCount());
- List claNames = new ArrayList<>(edsResponse.getResourcesCount());
+ new ArrayList<>(edsResponse.getResourcesList().size());
+ List claNames = new ArrayList<>(edsResponse.getResourcesList().size());
try {
for (com.google.protobuf.Any res : edsResponse.getResourcesList()) {
if (res.getTypeUrl().equals(ADS_TYPE_URL_EDS_V2)) {
@@ -1137,7 +1131,7 @@ private void handleEdsResponse(DiscoveryResponse edsResponse) {
logger.log(
XdsLogLevel.WARNING, "Failed to unpack ClusterLoadAssignments in EDS response {0}", e);
adsStream.sendNackRequest(
- ADS_TYPE_URL_EDS_V2, endpointWatchers.keySet(),
+ ResourceType.EDS, endpointWatchers.keySet(),
edsResponse.getVersionInfo(), "Malformed EDS response: " + e);
return;
}
@@ -1210,13 +1204,13 @@ private void handleEdsResponse(DiscoveryResponse edsResponse) {
}
if (errorMessage != null) {
adsStream.sendNackRequest(
- ADS_TYPE_URL_EDS_V2,
+ ResourceType.EDS,
endpointWatchers.keySet(),
edsResponse.getVersionInfo(),
errorMessage);
return;
}
- adsStream.sendAckRequest(ADS_TYPE_URL_EDS_V2, endpointWatchers.keySet(),
+ adsStream.sendAckRequest(ResourceType.EDS, endpointWatchers.keySet(),
edsResponse.getVersionInfo());
// Update local EDS cache by inserting updated endpoint information.
@@ -1247,7 +1241,7 @@ final class RpcRetryTask implements Runnable {
public void run() {
startRpcStream();
if (configWatcher != null) {
- adsStream.sendXdsRequest(ADS_TYPE_URL_LDS_V2, ImmutableList.of(ldsResourceName));
+ adsStream.sendXdsRequest(ResourceType.LDS, ImmutableList.of(ldsResourceName));
ldsRespTimer =
syncContext
.schedule(
@@ -1255,7 +1249,7 @@ public void run() {
INITIAL_RESOURCE_FETCH_TIMEOUT_SEC, TimeUnit.SECONDS, timeService);
}
if (listenerWatcher != null) {
- adsStream.sendXdsRequest(ADS_TYPE_URL_LDS_V2, ImmutableList.of());
+ adsStream.sendXdsRequest(ResourceType.LDS, ImmutableList.of());
ldsRespTimer =
syncContext
.schedule(
@@ -1263,7 +1257,7 @@ public void run() {
INITIAL_RESOURCE_FETCH_TIMEOUT_SEC, TimeUnit.SECONDS, timeService);
}
if (!clusterWatchers.isEmpty()) {
- adsStream.sendXdsRequest(ADS_TYPE_URL_CDS_V2, clusterWatchers.keySet());
+ adsStream.sendXdsRequest(ResourceType.CDS, clusterWatchers.keySet());
for (String clusterName : clusterWatchers.keySet()) {
ScheduledHandle timeoutHandle =
syncContext
@@ -1274,7 +1268,7 @@ public void run() {
}
}
if (!endpointWatchers.isEmpty()) {
- adsStream.sendXdsRequest(ADS_TYPE_URL_EDS_V2, endpointWatchers.keySet());
+ adsStream.sendXdsRequest(ResourceType.EDS, endpointWatchers.keySet());
for (String clusterName : endpointWatchers.keySet()) {
ScheduledHandle timeoutHandle =
syncContext
@@ -1287,10 +1281,159 @@ public void run() {
}
}
- private final class AdsStream implements StreamObserver {
- private final AggregatedDiscoveryServiceGrpc.AggregatedDiscoveryServiceStub stub;
+ private enum ResourceType {
+ UNKNOWN, LDS, RDS, CDS, EDS;
+
+ String typeUrl() {
+ switch (this) {
+ case LDS:
+ return ADS_TYPE_URL_LDS;
+ case RDS:
+ return ADS_TYPE_URL_RDS;
+ case CDS:
+ return ADS_TYPE_URL_CDS;
+ case EDS:
+ return ADS_TYPE_URL_EDS;
+ case UNKNOWN:
+ default:
+ throw new AssertionError("Unknown or missing case in enum switch: " + this);
+ }
+ }
+
+ String typeUrlV2() {
+ switch (this) {
+ case LDS:
+ return ADS_TYPE_URL_LDS_V2;
+ case RDS:
+ return ADS_TYPE_URL_RDS_V2;
+ case CDS:
+ return ADS_TYPE_URL_CDS_V2;
+ case EDS:
+ return ADS_TYPE_URL_EDS_V2;
+ case UNKNOWN:
+ default:
+ throw new AssertionError("Unknown or missing case in enum switch: " + this);
+ }
+ }
+
+ static ResourceType fromTypeUrl(String typeUrl) {
+ switch (typeUrl) {
+ case ADS_TYPE_URL_LDS:
+ // fall trough
+ case ADS_TYPE_URL_LDS_V2:
+ return LDS;
+ case ADS_TYPE_URL_RDS:
+ // fall through
+ case ADS_TYPE_URL_RDS_V2:
+ return RDS;
+ case ADS_TYPE_URL_CDS:
+ // fall through
+ case ADS_TYPE_URL_CDS_V2:
+ return CDS;
+ case ADS_TYPE_URL_EDS:
+ // fall through
+ case ADS_TYPE_URL_EDS_V2:
+ return EDS;
+ default:
+ return UNKNOWN;
+ }
+ }
+ }
- private StreamObserver requestWriter;
+ private static final class DiscoveryRequestData {
+ private final ResourceType resourceType;
+ private final Collection resourceNames;
+ private final String versionInfo;
+ private final String responseNonce;
+ private final Node node;
+ @Nullable
+ private final com.google.rpc.Status errorDetail;
+
+ DiscoveryRequestData(
+ ResourceType resourceType, Collection resourceNames, String versionInfo,
+ String responseNonce, Node node, @Nullable com.google.rpc.Status errorDetail) {
+ this.resourceType = resourceType;
+ this.resourceNames = resourceNames;
+ this.versionInfo = versionInfo;
+ this.responseNonce = responseNonce;
+ this.node = node;
+ this.errorDetail = errorDetail;
+ }
+
+ DiscoveryRequest toEnvoyProto() {
+ DiscoveryRequest.Builder builder =
+ DiscoveryRequest.newBuilder()
+ .setVersionInfo(versionInfo)
+ .setNode(node.toEnvoyProtoNode())
+ .addAllResourceNames(resourceNames)
+ .setTypeUrl(resourceType.typeUrl())
+ .setResponseNonce(responseNonce);
+ if (errorDetail != null) {
+ builder.setErrorDetail(errorDetail);
+ }
+ return builder.build();
+ }
+
+ io.envoyproxy.envoy.api.v2.DiscoveryRequest toEnvoyProtoV2() {
+ io.envoyproxy.envoy.api.v2.DiscoveryRequest.Builder builder =
+ io.envoyproxy.envoy.api.v2.DiscoveryRequest.newBuilder()
+ .setVersionInfo(versionInfo)
+ .setNode(node.toEnvoyProtoNodeV2())
+ .addAllResourceNames(resourceNames)
+ .setTypeUrl(resourceType.typeUrlV2())
+ .setResponseNonce(responseNonce);
+ if (errorDetail != null) {
+ builder.setErrorDetail(errorDetail);
+ }
+ return builder.build();
+ }
+ }
+
+ private static final class DiscoveryResponseData {
+ private final ResourceType resourceType;
+ private final List resources;
+ private final String versionInfo;
+ private final String nonce;
+
+ DiscoveryResponseData(
+ ResourceType resourceType, List resources, String versionInfo, String nonce) {
+ this.resourceType = resourceType;
+ this.resources = resources;
+ this.versionInfo = versionInfo;
+ this.nonce = nonce;
+ }
+
+ ResourceType getResourceType() {
+ return resourceType;
+ }
+
+ List getResourcesList() {
+ return resources;
+ }
+
+ String getVersionInfo() {
+ return versionInfo;
+ }
+
+ String getNonce() {
+ return nonce;
+ }
+
+ static DiscoveryResponseData fromEnvoyProto(DiscoveryResponse proto) {
+ return new DiscoveryResponseData(
+ ResourceType.fromTypeUrl(proto.getTypeUrl()), proto.getResourcesList(),
+ proto.getVersionInfo(), proto.getNonce());
+ }
+
+ static DiscoveryResponseData fromEnvoyProtoV2(
+ io.envoyproxy.envoy.api.v2.DiscoveryResponse proto) {
+ return new DiscoveryResponseData(
+ ResourceType.fromTypeUrl(proto.getTypeUrl()), proto.getResourcesList(),
+ proto.getVersionInfo(), proto.getNonce());
+ }
+ }
+
+ private abstract class AbstractAdsStream {
private boolean responseReceived;
private boolean closed;
@@ -1321,51 +1464,58 @@ private final class AdsStream implements StreamObserver {
@Nullable
private String rdsResourceName;
- private AdsStream(AggregatedDiscoveryServiceGrpc.AggregatedDiscoveryServiceStub stub) {
- this.stub = checkNotNull(stub, "stub");
- }
-
- private void start() {
- requestWriter = stub.withWaitForReady().streamAggregatedResources(this);
- }
-
- @Override
- public void onNext(final DiscoveryResponse response) {
- syncContext.execute(new Runnable() {
- @Override
- public void run() {
- if (closed) {
- return;
- }
- responseReceived = true;
- String typeUrl = response.getTypeUrl();
- // Nonce in each response is echoed back in the following ACK/NACK request. It is
- // used for management server to identify which response the client is ACKing/NACking.
- // To avoid confusion, client-initiated requests will always use the nonce in
- // most recently received responses of each resource type.
- if (typeUrl.equals(ADS_TYPE_URL_LDS_V2) || typeUrl.equals(ADS_TYPE_URL_LDS)) {
- ldsRespNonce = response.getNonce();
- handleLdsResponse(response);
- } else if (typeUrl.equals(ADS_TYPE_URL_RDS_V2) || typeUrl.equals(ADS_TYPE_URL_LDS)) {
- rdsRespNonce = response.getNonce();
- handleRdsResponse(response);
- } else if (typeUrl.equals(ADS_TYPE_URL_CDS_V2) || typeUrl.equals(ADS_TYPE_URL_CDS)) {
- cdsRespNonce = response.getNonce();
- handleCdsResponse(response);
- } else if (typeUrl.equals(ADS_TYPE_URL_EDS_V2) || typeUrl.equals(ADS_TYPE_URL_EDS)) {
- edsRespNonce = response.getNonce();
- handleEdsResponse(response);
- } else {
- logger.log(
- XdsLogLevel.WARNING,
- "Received an unknown type of DiscoveryResponse\n{0}", response);
- }
- }
- });
- }
-
- @Override
- public void onError(final Throwable t) {
+ abstract void start();
+
+ abstract void sendDiscoveryRequest(DiscoveryRequestData request);
+
+ abstract void sendError(Exception error);
+
+ final void onDiscoveryResponse(final DiscoveryResponseData response) {
+ syncContext.execute(
+ new Runnable() {
+ @Override
+ public void run() {
+ if (closed) {
+ return;
+ }
+ responseReceived = true;
+ String respNonce = response.getNonce();
+ // Nonce in each response is echoed back in the following ACK/NACK request. It is
+ // used for management server to identify which response the client is ACKing/NACking.
+ // To avoid confusion, client-initiated requests will always use the nonce in
+ // most recently received responses of each resource type.
+ ResourceType resourceType = response.getResourceType();
+ switch (resourceType) {
+ case LDS:
+ ldsRespNonce = respNonce;
+ handleLdsResponse(response);
+ break;
+ case RDS:
+ rdsRespNonce = respNonce;
+ handleRdsResponse(response);
+ break;
+ case CDS:
+ cdsRespNonce = respNonce;
+ handleCdsResponse(response);
+ break;
+ case EDS:
+ edsRespNonce = respNonce;
+ handleEdsResponse(response);
+ break;
+ case UNKNOWN:
+ logger.log(
+ XdsLogLevel.WARNING,
+ "Received an unknown type of DiscoveryResponse\n{0}",
+ respNonce);
+ break;
+ default:
+ throw new AssertionError("Missing case in enum switch: " + resourceType);
+ }
+ }
+ });
+ }
+
+ final void onError(final Throwable t) {
syncContext.execute(new Runnable() {
@Override
public void run() {
@@ -1374,8 +1524,7 @@ public void run() {
});
}
- @Override
- public void onCompleted() {
+ final void onCompleted() {
syncContext.execute(new Runnable() {
@Override
public void run() {
@@ -1438,7 +1587,7 @@ private void close(Exception error) {
}
closed = true;
cleanUp();
- requestWriter.onError(error);
+ sendError(error);
}
private void cleanUp() {
@@ -1452,126 +1601,244 @@ private void cleanUp() {
* requested resource name (except for LDS as we always request for the singleton Listener)
* as we need it to find resources in responses.
*/
- private void sendXdsRequest(String typeUrl, Collection resourceNames) {
- checkState(requestWriter != null, "ADS stream has not been started");
- String version = "";
- String nonce = "";
- if (typeUrl.equals(ADS_TYPE_URL_LDS_V2)) {
- version = ldsVersion;
- nonce = ldsRespNonce;
- logger.log(XdsLogLevel.INFO, "Sending LDS request for resources: {0}", resourceNames);
- } else if (typeUrl.equals(ADS_TYPE_URL_RDS_V2)) {
- checkArgument(resourceNames.size() == 1,
- "RDS request requesting for more than one resource");
- version = rdsVersion;
- nonce = rdsRespNonce;
- rdsResourceName = resourceNames.iterator().next();
- logger.log(XdsLogLevel.INFO, "Sending RDS request for resources: {0}", resourceNames);
- } else if (typeUrl.equals(ADS_TYPE_URL_CDS_V2)) {
- version = cdsVersion;
- nonce = cdsRespNonce;
- logger.log(XdsLogLevel.INFO, "Sending CDS request for resources: {0}", resourceNames);
- } else if (typeUrl.equals(ADS_TYPE_URL_EDS_V2)) {
- version = edsVersion;
- nonce = edsRespNonce;
- logger.log(XdsLogLevel.INFO, "Sending EDS request for resources: {0}", resourceNames);
- }
- DiscoveryRequest request =
- DiscoveryRequest
- .newBuilder()
- .setVersionInfo(version)
- .setNode(node)
- .addAllResourceNames(resourceNames)
- .setTypeUrl(typeUrl)
- .setResponseNonce(nonce)
- .build();
- requestWriter.onNext(request);
- logger.log(XdsLogLevel.DEBUG, "Sent DiscoveryRequest\n{0}", request);
+ private void sendXdsRequest(ResourceType resourceType, Collection resourceNames) {
+ String version;
+ String nonce;
+ switch (resourceType) {
+ case LDS:
+ version = ldsVersion;
+ nonce = ldsRespNonce;
+ logger.log(XdsLogLevel.INFO, "Sending LDS request for resources: {0}", resourceNames);
+ break;
+ case RDS:
+ checkArgument(
+ resourceNames.size() == 1, "RDS request requesting for more than one resource");
+ version = rdsVersion;
+ nonce = rdsRespNonce;
+ rdsResourceName = resourceNames.iterator().next();
+ logger.log(XdsLogLevel.INFO, "Sending RDS request for resources: {0}", resourceNames);
+ break;
+ case CDS:
+ version = cdsVersion;
+ nonce = cdsRespNonce;
+ logger.log(XdsLogLevel.INFO, "Sending CDS request for resources: {0}", resourceNames);
+ break;
+ case EDS:
+ version = edsVersion;
+ nonce = edsRespNonce;
+ logger.log(XdsLogLevel.INFO, "Sending EDS request for resources: {0}", resourceNames);
+ break;
+ case UNKNOWN:
+ default:
+ throw new AssertionError("Unknown or missing case in enum switch: " + resourceType);
+ }
+ DiscoveryRequestData request =
+ new DiscoveryRequestData(resourceType, resourceNames, version, nonce, node, null);
+ sendDiscoveryRequest(request);
}
/**
* Sends a DiscoveryRequest with the given information as an ACK. Updates the latest accepted
* version for the corresponding resource type.
*/
- private void sendAckRequest(String typeUrl, Collection resourceNames,
+ private void sendAckRequest(ResourceType resourceType, Collection resourceNames,
String versionInfo) {
- checkState(requestWriter != null, "ADS stream has not been started");
- String nonce = "";
- if (typeUrl.equals(ADS_TYPE_URL_LDS_V2)) {
- ldsVersion = versionInfo;
- nonce = ldsRespNonce;
- } else if (typeUrl.equals(ADS_TYPE_URL_RDS_V2)) {
- rdsVersion = versionInfo;
- nonce = rdsRespNonce;
- } else if (typeUrl.equals(ADS_TYPE_URL_CDS_V2)) {
- cdsVersion = versionInfo;
- nonce = cdsRespNonce;
- } else if (typeUrl.equals(ADS_TYPE_URL_EDS_V2)) {
- edsVersion = versionInfo;
- nonce = edsRespNonce;
- }
- DiscoveryRequest request =
- DiscoveryRequest
- .newBuilder()
- .setVersionInfo(versionInfo)
- .setNode(node)
- .addAllResourceNames(resourceNames)
- .setTypeUrl(typeUrl)
- .setResponseNonce(nonce)
- .build();
- requestWriter.onNext(request);
- logger.log(XdsLogLevel.DEBUG, "Sent ACK request\n{0}", request);
+ String nonce;
+ switch (resourceType) {
+ case LDS:
+ ldsVersion = versionInfo;
+ nonce = ldsRespNonce;
+ logger.log(XdsLogLevel.WARNING, "Sending ACK for LDS update, version: {0}", versionInfo);
+ break;
+ case RDS:
+ rdsVersion = versionInfo;
+ nonce = rdsRespNonce;
+ logger.log(XdsLogLevel.WARNING, "Sending ACK for RDS update, version: {0}", versionInfo);
+ break;
+ case CDS:
+ cdsVersion = versionInfo;
+ nonce = cdsRespNonce;
+ logger.log(XdsLogLevel.WARNING, "Sending ACK for CDS update, version: {0}", versionInfo);
+ break;
+ case EDS:
+ edsVersion = versionInfo;
+ nonce = edsRespNonce;
+ logger.log(XdsLogLevel.WARNING, "Sending ACK for EDS update, version: {0}", versionInfo);
+ break;
+ case UNKNOWN:
+ default:
+ throw new AssertionError("Unknown or missing case in enum switch: " + resourceType);
+ }
+ DiscoveryRequestData request =
+ new DiscoveryRequestData(resourceType, resourceNames, versionInfo, nonce, node, null);
+ sendDiscoveryRequest(request);
}
/**
* Sends a DiscoveryRequest with the given information as an NACK. NACK takes the previous
* accepted version.
*/
- private void sendNackRequest(String typeUrl, Collection resourceNames,
+ private void sendNackRequest(ResourceType resourceType, Collection resourceNames,
String rejectVersion, String message) {
+ String versionInfo;
+ String nonce;
+ switch (resourceType) {
+ case LDS:
+ versionInfo = ldsVersion;
+ nonce = ldsRespNonce;
+ logger.log(
+ XdsLogLevel.WARNING,
+ "Sending NACK for LDS update, version: {0}, reason: {1}",
+ rejectVersion,
+ message);
+ break;
+ case RDS:
+ versionInfo = rdsVersion;
+ nonce = rdsRespNonce;
+ logger.log(
+ XdsLogLevel.WARNING,
+ "Sending NACK for RDS update, version: {0}, reason: {1}",
+ rejectVersion,
+ message);
+ break;
+ case CDS:
+ versionInfo = cdsVersion;
+ nonce = cdsRespNonce;
+ logger.log(
+ XdsLogLevel.WARNING,
+ "Sending NACK for CDS update, version: {0}, reason: {1}",
+ rejectVersion,
+ message);
+ break;
+ case EDS:
+ versionInfo = edsVersion;
+ nonce = edsRespNonce;
+ logger.log(
+ XdsLogLevel.WARNING,
+ "Sending NACK for EDS update, version: {0}, reason: {1}",
+ rejectVersion,
+ message);
+ break;
+ case UNKNOWN:
+ default:
+ throw new AssertionError("Unknown or missing case in enum switch: " + resourceType);
+ }
+ com.google.rpc.Status error = com.google.rpc.Status.newBuilder()
+ .setCode(Code.INVALID_ARGUMENT_VALUE)
+ .setMessage(message)
+ .build();
+ DiscoveryRequestData request =
+ new DiscoveryRequestData(resourceType, resourceNames, versionInfo, nonce, node, error);
+ sendDiscoveryRequest(request);
+ }
+ }
+
+ private final class AdsStreamV2 extends AbstractAdsStream {
+ private final io.envoyproxy.envoy.service.discovery.v2.AggregatedDiscoveryServiceGrpc
+ .AggregatedDiscoveryServiceStub stubV2;
+ private StreamObserver requestWriterV2;
+
+ AdsStreamV2() {
+ stubV2 =
+ io.envoyproxy.envoy.service.discovery.v2.AggregatedDiscoveryServiceGrpc.newStub(channel);
+ }
+
+ @Override
+ void start() {
+ StreamObserver responseReaderV2 =
+ new StreamObserver() {
+ @Override
+ public void onNext(io.envoyproxy.envoy.api.v2.DiscoveryResponse response) {
+ DiscoveryResponseData responseData =
+ DiscoveryResponseData.fromEnvoyProtoV2(response);
+ if (logger.isLoggable(XdsLogLevel.DEBUG)) {
+ logger.log(
+ XdsLogLevel.DEBUG,
+ "Received {0} response:\n{1}",
+ responseData.getResourceType(),
+ respPrinter.print(response));
+ }
+ onDiscoveryResponse(responseData);
+ }
+
+ @Override
+ public void onError(Throwable t) {
+ AdsStreamV2.this.onError(t);
+ }
+
+ @Override
+ public void onCompleted() {
+ AdsStreamV2.this.onCompleted();
+ }
+ };
+ requestWriterV2 = stubV2.withWaitForReady().streamAggregatedResources(responseReaderV2);
+ }
+
+ @Override
+ void sendDiscoveryRequest(DiscoveryRequestData request) {
+ checkState(requestWriterV2 != null, "ADS stream has not been started");
+ io.envoyproxy.envoy.api.v2.DiscoveryRequest requestProto =
+ request.toEnvoyProtoV2();
+ requestWriterV2.onNext(requestProto);
+ logger.log(XdsLogLevel.DEBUG, "Sent DiscoveryRequest\n{0}", requestProto);
+ }
+
+ @Override
+ void sendError(Exception error) {
+ requestWriterV2.onError(error);
+ }
+ }
+
+ // AdsStream V3
+ private final class AdsStream extends AbstractAdsStream {
+ private final AggregatedDiscoveryServiceGrpc.AggregatedDiscoveryServiceStub stub;
+ private StreamObserver requestWriter;
+
+ AdsStream() {
+ stub = AggregatedDiscoveryServiceGrpc.newStub(channel);
+ }
+
+ @Override
+ void start() {
+ StreamObserver responseReader = new StreamObserver() {
+ @Override
+ public void onNext(DiscoveryResponse response) {
+ DiscoveryResponseData responseData =
+ DiscoveryResponseData.fromEnvoyProto(response);
+ if (logger.isLoggable(XdsLogLevel.DEBUG)) {
+ logger.log(
+ XdsLogLevel.DEBUG,
+ "Received {0} response:\n{1}",
+ responseData.getResourceType(),
+ respPrinter.print(response));
+ }
+ onDiscoveryResponse(responseData);
+ }
+
+ @Override
+ public void onError(Throwable t) {
+ AdsStream.this.onError(t);
+ }
+
+ @Override
+ public void onCompleted() {
+ AdsStream.this.onCompleted();
+ }
+ };
+ requestWriter = stub.withWaitForReady().streamAggregatedResources(responseReader);
+ }
+
+ @Override
+ void sendDiscoveryRequest(DiscoveryRequestData request) {
checkState(requestWriter != null, "ADS stream has not been started");
- String versionInfo = "";
- String nonce = "";
- if (typeUrl.equals(ADS_TYPE_URL_LDS_V2)) {
- versionInfo = ldsVersion;
- nonce = ldsRespNonce;
- logger.log(
- XdsLogLevel.WARNING,
- "Rejecting LDS update, version: {0}, reason: {1}", rejectVersion, message);
- } else if (typeUrl.equals(ADS_TYPE_URL_RDS_V2)) {
- versionInfo = rdsVersion;
- nonce = rdsRespNonce;
- logger.log(
- XdsLogLevel.WARNING,
- "Rejecting RDS update, version: {0}, reason: {1}", rejectVersion, message);
- } else if (typeUrl.equals(ADS_TYPE_URL_CDS_V2)) {
- versionInfo = cdsVersion;
- nonce = cdsRespNonce;
- logger.log(
- XdsLogLevel.WARNING,
- "Rejecting CDS update, version: {0}, reason: {1}", rejectVersion, message);
- } else if (typeUrl.equals(ADS_TYPE_URL_EDS_V2)) {
- versionInfo = edsVersion;
- nonce = edsRespNonce;
- logger.log(
- XdsLogLevel.WARNING,
- "Rejecting EDS update, version: {0}, reason: {1}", rejectVersion, message);
- }
- DiscoveryRequest request =
- DiscoveryRequest
- .newBuilder()
- .setVersionInfo(versionInfo)
- .setNode(node)
- .addAllResourceNames(resourceNames)
- .setTypeUrl(typeUrl)
- .setResponseNonce(nonce)
- .setErrorDetail(
- com.google.rpc.Status.newBuilder()
- .setCode(Code.INVALID_ARGUMENT_VALUE)
- .setMessage(message))
- .build();
- requestWriter.onNext(request);
- logger.log(XdsLogLevel.DEBUG, "Sent NACK request\n{0}", request);
+ DiscoveryRequest requestProto = request.toEnvoyProto();
+ requestWriter.onNext(requestProto);
+ logger.log(XdsLogLevel.DEBUG, "Sent DiscoveryRequest\n{0}", requestProto);
+ }
+
+ @Override
+ void sendError(Exception error) {
+ requestWriter.onError(error);
}
}
diff --git a/xds/src/main/java/io/grpc/xds/XdsClientWrapperForServerSds.java b/xds/src/main/java/io/grpc/xds/XdsClientWrapperForServerSds.java
index 79873e9dfa0..95e7cb9e06f 100644
--- a/xds/src/main/java/io/grpc/xds/XdsClientWrapperForServerSds.java
+++ b/xds/src/main/java/io/grpc/xds/XdsClientWrapperForServerSds.java
@@ -20,7 +20,6 @@
import static com.google.common.base.Preconditions.checkState;
import com.google.common.annotations.VisibleForTesting;
-import io.envoyproxy.envoy.api.v2.core.Node;
import io.grpc.Internal;
import io.grpc.InternalLogId;
import io.grpc.Status;
@@ -28,6 +27,7 @@
import io.grpc.internal.ExponentialBackoffPolicy;
import io.grpc.internal.GrpcUtil;
import io.grpc.internal.SharedResourceHolder;
+import io.grpc.xds.EnvoyProtoData.Node;
import io.grpc.xds.EnvoyServerProtoData.CidrRange;
import io.grpc.xds.EnvoyServerProtoData.DownstreamTlsContext;
import io.grpc.xds.EnvoyServerProtoData.FilterChain;
diff --git a/xds/src/main/java/io/grpc/xds/XdsNameResolver.java b/xds/src/main/java/io/grpc/xds/XdsNameResolver.java
index 47946949b49..e0c03e5f907 100644
--- a/xds/src/main/java/io/grpc/xds/XdsNameResolver.java
+++ b/xds/src/main/java/io/grpc/xds/XdsNameResolver.java
@@ -26,7 +26,6 @@
import com.google.common.collect.Iterables;
import com.google.gson.Gson;
import com.google.re2j.Pattern;
-import io.envoyproxy.envoy.api.v2.core.Node;
import io.grpc.Attributes;
import io.grpc.EquivalentAddressGroup;
import io.grpc.InternalLogId;
@@ -39,6 +38,7 @@
import io.grpc.xds.Bootstrapper.BootstrapInfo;
import io.grpc.xds.Bootstrapper.ServerInfo;
import io.grpc.xds.EnvoyProtoData.ClusterWeight;
+import io.grpc.xds.EnvoyProtoData.Node;
import io.grpc.xds.EnvoyProtoData.Route;
import io.grpc.xds.EnvoyProtoData.RouteAction;
import io.grpc.xds.RouteMatch.FractionMatcher;
diff --git a/xds/src/main/java/io/grpc/xds/internal/sds/ClientSslContextProviderFactory.java b/xds/src/main/java/io/grpc/xds/internal/sds/ClientSslContextProviderFactory.java
index bb6a636e969..84b2f8284aa 100644
--- a/xds/src/main/java/io/grpc/xds/internal/sds/ClientSslContextProviderFactory.java
+++ b/xds/src/main/java/io/grpc/xds/internal/sds/ClientSslContextProviderFactory.java
@@ -43,7 +43,7 @@ public SslContextProvider create(UpstreamTlsContext upstreamTlsContext) {
try {
return SdsClientSslContextProvider.getProvider(
upstreamTlsContext,
- Bootstrapper.getInstance().readBootstrap().getNode(),
+ Bootstrapper.getInstance().readBootstrap().getNode().toEnvoyProtoNodeV2(),
Executors.newSingleThreadExecutor(new ThreadFactoryBuilder()
.setNameFormat("client-sds-sslcontext-provider-%d")
.setDaemon(true)
diff --git a/xds/src/main/java/io/grpc/xds/internal/sds/ServerSslContextProviderFactory.java b/xds/src/main/java/io/grpc/xds/internal/sds/ServerSslContextProviderFactory.java
index fb271a32dc7..1cd2cfa8e9b 100644
--- a/xds/src/main/java/io/grpc/xds/internal/sds/ServerSslContextProviderFactory.java
+++ b/xds/src/main/java/io/grpc/xds/internal/sds/ServerSslContextProviderFactory.java
@@ -45,7 +45,7 @@ public SslContextProvider create(
try {
return SdsServerSslContextProvider.getProvider(
downstreamTlsContext,
- Bootstrapper.getInstance().readBootstrap().getNode(),
+ Bootstrapper.getInstance().readBootstrap().getNode().toEnvoyProtoNodeV2(),
Executors.newSingleThreadExecutor(new ThreadFactoryBuilder()
.setNameFormat("server-sds-sslcontext-provider-%d")
.setDaemon(true)
diff --git a/xds/src/test/java/io/grpc/xds/BootstrapperTest.java b/xds/src/test/java/io/grpc/xds/BootstrapperTest.java
index 37ef4161259..6c1fd2111ba 100644
--- a/xds/src/test/java/io/grpc/xds/BootstrapperTest.java
+++ b/xds/src/test/java/io/grpc/xds/BootstrapperTest.java
@@ -18,15 +18,14 @@
import static com.google.common.truth.Truth.assertThat;
+import com.google.common.collect.ImmutableMap;
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.internal.GrpcUtil.GrpcBuildVersion;
import io.grpc.xds.Bootstrapper.BootstrapInfo;
import io.grpc.xds.Bootstrapper.ServerInfo;
+import io.grpc.xds.EnvoyProtoData.Locality;
+import io.grpc.xds.EnvoyProtoData.Node;
import java.io.IOException;
import java.util.List;
import org.junit.Rule;
@@ -82,16 +81,13 @@ public void parseBootstrap_validData_singleXdsServer() throws IOException {
getNodeBuilder()
.setId("ENVOY_NODE_ID")
.setCluster("ENVOY_CLUSTER")
- .setLocality(
- Locality.newBuilder()
- .setRegion("ENVOY_REGION").setZone("ENVOY_ZONE").setSubZone("ENVOY_SUBZONE"))
+ .setLocality(new Locality("ENVOY_REGION", "ENVOY_ZONE", "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())
+ ImmutableMap.of(
+ "TRAFFICDIRECTOR_INTERCEPTION_PORT",
+ "ENVOY_PORT",
+ "TRAFFICDIRECTOR_NETWORK_NAME",
+ "VPC_NETWORK_NAME"))
.build());
}
@@ -116,6 +112,9 @@ public void parseBootstrap_validData_multipleXdsServers() throws IOException {
+ " \"server_uri\": \"trafficdirector-foo.googleapis.com:443\",\n"
+ " \"channel_creds\": [\n"
+ " {\"type\": \"tls\"}, {\"type\": \"loas\"}, {\"type\": \"google_default\"}\n"
+ + " ],\n"
+ + " \"server_features\": [\n"
+ + " \"xds_v3\", \"foo\", \"bar\"\n"
+ " ]\n"
+ " },\n"
+ " {\n"
@@ -138,6 +137,7 @@ public void parseBootstrap_validData_multipleXdsServers() throws IOException {
assertThat(serverInfoList.get(0).getChannelCredentials().get(2).getType())
.isEqualTo("google_default");
assertThat(serverInfoList.get(0).getChannelCredentials().get(2).getConfig()).isNull();
+ assertThat(serverInfoList.get(0).getServerFeatures()).contains("xds_v3");
assertThat(serverInfoList.get(1).getServerUri())
.isEqualTo("trafficdirector-bar.googleapis.com:443");
assertThat(serverInfoList.get(1).getChannelCredentials()).isEmpty();
@@ -145,16 +145,13 @@ public void parseBootstrap_validData_multipleXdsServers() throws IOException {
getNodeBuilder()
.setId("ENVOY_NODE_ID")
.setCluster("ENVOY_CLUSTER")
- .setLocality(
- Locality.newBuilder()
- .setRegion("ENVOY_REGION").setZone("ENVOY_ZONE").setSubZone("ENVOY_SUBZONE"))
+ .setLocality(new Locality("ENVOY_REGION", "ENVOY_ZONE", "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())
+ ImmutableMap.of(
+ "TRAFFICDIRECTOR_INTERCEPTION_PORT",
+ "ENVOY_PORT",
+ "TRAFFICDIRECTOR_NETWORK_NAME",
+ "VPC_NETWORK_NAME"))
.build());
}
@@ -201,16 +198,13 @@ public void parseBootstrap_IgnoreIrrelevantFields() throws IOException {
getNodeBuilder()
.setId("ENVOY_NODE_ID")
.setCluster("ENVOY_CLUSTER")
- .setLocality(
- Locality.newBuilder()
- .setRegion("ENVOY_REGION").setZone("ENVOY_ZONE").setSubZone("ENVOY_SUBZONE"))
+ .setLocality(new Locality("ENVOY_REGION", "ENVOY_ZONE", "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())
+ ImmutableMap.of(
+ "TRAFFICDIRECTOR_INTERCEPTION_PORT",
+ "ENVOY_PORT",
+ "TRAFFICDIRECTOR_NETWORK_NAME",
+ "VPC_NETWORK_NAME"))
.build());
}
@@ -304,7 +298,6 @@ public void parseBootstrap_serverWithoutServerUri() throws IOException {
Bootstrapper.parseConfig(rawData);
}
- @SuppressWarnings("deprecation")
private static Node.Builder getNodeBuilder() {
GrpcBuildVersion buildVersion = GrpcUtil.getGrpcBuildVersion();
return
diff --git a/xds/src/test/java/io/grpc/xds/CdsLoadBalancerTest.java b/xds/src/test/java/io/grpc/xds/CdsLoadBalancerTest.java
index 34d57be999b..9a9bb69241f 100644
--- a/xds/src/test/java/io/grpc/xds/CdsLoadBalancerTest.java
+++ b/xds/src/test/java/io/grpc/xds/CdsLoadBalancerTest.java
@@ -29,7 +29,6 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.same;
-import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -61,8 +60,6 @@
import io.grpc.xds.EnvoyServerProtoData.UpstreamTlsContext;
import io.grpc.xds.XdsClient.ClusterUpdate;
import io.grpc.xds.XdsClient.ClusterWatcher;
-import io.grpc.xds.XdsClient.EndpointUpdate;
-import io.grpc.xds.XdsClient.EndpointWatcher;
import io.grpc.xds.XdsClient.RefCountedXdsClientObjectPool;
import io.grpc.xds.XdsClient.XdsClientFactory;
import io.grpc.xds.internal.sds.CommonTlsContextTestsUtil;
@@ -592,52 +589,4 @@ public void clusterWatcher_onErrorCalledBeforeAndAfterOnClusterChanged() {
verify(helper, times(1))
.updateBalancingState(eq(TRANSIENT_FAILURE), any(SubchannelPicker.class));
}
-
- @Test
- public void cdsBalancerIntegrateWithEdsBalancer() {
- lbRegistry.deregister(fakeEdsLoadBlancerProvider);
- lbRegistry.register(new EdsLoadBalancerProvider());
-
- ResolvedAddresses resolvedAddresses1 = ResolvedAddresses.newBuilder()
- .setAddresses(ImmutableList.of())
- .setAttributes(Attributes.newBuilder()
- .set(XdsAttributes.XDS_CLIENT_POOL, xdsClientPool)
- .build())
- .setLoadBalancingPolicyConfig(new CdsConfig("foo.googleapis.com"))
- .build();
- cdsLoadBalancer.handleResolvedAddresses(resolvedAddresses1);
- ArgumentCaptor clusterWatcherCaptor = ArgumentCaptor.forClass(null);
- verify(xdsClient).watchClusterData(eq("foo.googleapis.com"), clusterWatcherCaptor.capture());
- ClusterWatcher clusterWatcher = clusterWatcherCaptor.getValue();
- clusterWatcher.onClusterChanged(
- ClusterUpdate.newBuilder()
- .setClusterName("foo.googleapis.com")
- .setEdsServiceName("edsServiceFoo.googleapis.com")
- .setLbPolicy("round_robin")
- .build());
-
- ArgumentCaptor endpointWatcherCaptor = ArgumentCaptor.forClass(null);
- verify(xdsClient).watchEndpointData(
- eq("edsServiceFoo.googleapis.com"), endpointWatcherCaptor.capture());
- EndpointWatcher endpointWatcher = endpointWatcherCaptor.getValue();
-
- verify(helper, never()).updateBalancingState(
- eq(TRANSIENT_FAILURE), any(SubchannelPicker.class));
- // Update endpoints with all backends unhealthy, the EDS will update channel state to
- // TRANSIENT_FAILURE.
- // Not able to test with healthy endpoints because the real EDS balancer is using real
- // round-robin balancer to balance endpoints.
- endpointWatcher.onEndpointChanged(EndpointUpdate.newBuilder()
- .setClusterName("edsServiceFoo.googleapis.com")
- .addLocalityLbEndpoints(
- new EnvoyProtoData.Locality("region", "zone", "subzone"),
- new EnvoyProtoData.LocalityLbEndpoints(
- // All unhealthy.
- ImmutableList.of(new EnvoyProtoData.LbEndpoint("127.0.0.1", 8080, 1, false)), 1, 0))
- .build());
- verify(helper, atLeastOnce()).updateBalancingState(
- eq(TRANSIENT_FAILURE), any(SubchannelPicker.class));
-
- cdsLoadBalancer.shutdown();
- }
}
diff --git a/xds/src/test/java/io/grpc/xds/ClientLoadCounterTest.java b/xds/src/test/java/io/grpc/xds/ClientLoadCounterTest.java
index bedeaf4eaea..db3fbb818d2 100644
--- a/xds/src/test/java/io/grpc/xds/ClientLoadCounterTest.java
+++ b/xds/src/test/java/io/grpc/xds/ClientLoadCounterTest.java
@@ -77,9 +77,11 @@ public void snapshotContainsDataInCounter() {
long numInProgressCalls = ThreadLocalRandom.current().nextLong(Long.MAX_VALUE);
long numFailedCalls = ThreadLocalRandom.current().nextLong(Long.MAX_VALUE);
long numIssuedCalls = ThreadLocalRandom.current().nextLong(Long.MAX_VALUE);
- counter =
- new ClientLoadCounter(numSucceededCalls, numInProgressCalls, numFailedCalls,
- numIssuedCalls);
+ counter = new ClientLoadCounter();
+ counter.setCallsSucceeded(numSucceededCalls);
+ counter.setCallsInProgress(numInProgressCalls);
+ counter.setCallsFailed(numFailedCalls);
+ counter.setCallsIssued(numIssuedCalls);
ClientLoadSnapshot snapshot = counter.snapshot();
assertQueryCounts(snapshot, numSucceededCalls, numInProgressCalls, numFailedCalls,
numIssuedCalls);
diff --git a/xds/src/test/java/io/grpc/xds/EdsLoadBalancerTest.java b/xds/src/test/java/io/grpc/xds/EdsLoadBalancerTest.java
index ff9101b5a60..2edfd673f72 100644
--- a/xds/src/test/java/io/grpc/xds/EdsLoadBalancerTest.java
+++ b/xds/src/test/java/io/grpc/xds/EdsLoadBalancerTest.java
@@ -22,7 +22,7 @@
import static io.grpc.ConnectivityState.READY;
import static io.grpc.ConnectivityState.TRANSIENT_FAILURE;
import static io.grpc.xds.XdsClientTestHelper.buildClusterLoadAssignment;
-import static io.grpc.xds.XdsClientTestHelper.buildDiscoveryResponse;
+import static io.grpc.xds.XdsClientTestHelper.buildDiscoveryResponseV2;
import static io.grpc.xds.XdsClientTestHelper.buildDropOverload;
import static io.grpc.xds.XdsClientTestHelper.buildLbEndpoint;
import static io.grpc.xds.XdsClientTestHelper.buildLocalityLbEndpoints;
@@ -43,7 +43,6 @@
import io.envoyproxy.envoy.api.v2.ClusterLoadAssignment.Policy.DropOverload;
import io.envoyproxy.envoy.api.v2.DiscoveryRequest;
import io.envoyproxy.envoy.api.v2.DiscoveryResponse;
-import io.envoyproxy.envoy.api.v2.core.Node;
import io.envoyproxy.envoy.api.v2.endpoint.LbEndpoint;
import io.envoyproxy.envoy.api.v2.endpoint.LocalityLbEndpoints;
import io.envoyproxy.envoy.service.discovery.v2.AggregatedDiscoveryServiceGrpc.AggregatedDiscoveryServiceImplBase;
@@ -76,8 +75,11 @@
import io.grpc.xds.Bootstrapper.ChannelCreds;
import io.grpc.xds.Bootstrapper.ServerInfo;
import io.grpc.xds.EdsLoadBalancerProvider.EdsConfig;
+import io.grpc.xds.EnvoyProtoData.Node;
+import io.grpc.xds.LoadStatsManager.LoadStatsStore;
import io.grpc.xds.LocalityStore.LocalityStoreFactory;
import io.grpc.xds.XdsClient.EndpointUpdate;
+import io.grpc.xds.XdsClient.XdsChannel;
import io.grpc.xds.XdsClient.XdsChannelFactory;
import java.net.InetSocketAddress;
import java.util.ArrayDeque;
@@ -135,10 +137,10 @@ public void uncaughtException(Thread t, Throwable e) {
private final Map childBalancers = new HashMap<>();
private final XdsChannelFactory channelFactory = new XdsChannelFactory() {
@Override
- ManagedChannel createChannel(List servers) {
+ XdsChannel createChannel(List servers) {
assertThat(Iterables.getOnlyElement(servers).getServerUri())
.isEqualTo("trafficdirector.googleapis.com");
- return channel;
+ return new XdsChannel(channel, false);
}
};
@@ -228,10 +230,10 @@ public StreamObserver streamAggregatedResources(
.forName(serverName)
.directExecutor()
.build());
- final List serverList =
- ImmutableList.of(
- new ServerInfo("trafficdirector.googleapis.com", ImmutableList.of()));
- BootstrapInfo bootstrapInfo = new BootstrapInfo(serverList, Node.getDefaultInstance());
+ final List serverList = ImmutableList.of(
+ new ServerInfo("trafficdirector.googleapis.com", ImmutableList.of(), null));
+ Node node = Node.newBuilder().build();
+ BootstrapInfo bootstrapInfo = new BootstrapInfo(serverList, node);
doReturn(bootstrapInfo).when(bootstrapper).readBootstrap();
if (isFullFlow) {
@@ -240,7 +242,7 @@ public StreamObserver streamAggregatedResources(
SERVICE_AUTHORITY,
serverList,
channelFactory,
- Node.getDefaultInstance(),
+ node,
syncContext,
fakeClock.getScheduledExecutorService(),
mock(BackoffPolicy.Provider.class),
@@ -679,7 +681,7 @@ public PickResult pickSubchannel(PickSubchannelArgs args) {
// The whole cluster is no longer accessible.
// Note that EDS resource removal is achieved by CDS resource update.
responseObserver.onNext(
- buildDiscoveryResponse(
+ buildDiscoveryResponseV2(
String.valueOf(versionIno++),
Collections.emptyList(),
XdsClientImpl.ADS_TYPE_URL_CDS_V2,
@@ -767,7 +769,7 @@ private void deliverResolvedAddresses(
private void deliverClusterLoadAssignments(ClusterLoadAssignment clusterLoadAssignment) {
responseObserver.onNext(
- buildDiscoveryResponse(
+ buildDiscoveryResponseV2(
String.valueOf(versionIno++),
ImmutableList.of(Any.pack(clusterLoadAssignment)),
XdsClientImpl.ADS_TYPE_URL_EDS_V2,
diff --git a/xds/src/test/java/io/grpc/xds/EnvoyProtoDataTest.java b/xds/src/test/java/io/grpc/xds/EnvoyProtoDataTest.java
index 7e633d55bd5..24e739bf560 100644
--- a/xds/src/test/java/io/grpc/xds/EnvoyProtoDataTest.java
+++ b/xds/src/test/java/io/grpc/xds/EnvoyProtoDataTest.java
@@ -18,9 +18,13 @@
import static com.google.common.truth.Truth.assertThat;
+import com.google.common.collect.ImmutableMap;
import com.google.common.testing.EqualsTester;
import com.google.protobuf.BoolValue;
+import com.google.protobuf.Struct;
import com.google.protobuf.UInt32Value;
+import com.google.protobuf.Value;
+import com.google.protobuf.util.Durations;
import com.google.re2j.Pattern;
import io.envoyproxy.envoy.config.core.v3.RuntimeFractionalPercent;
import io.envoyproxy.envoy.config.route.v3.QueryParameterMatcher;
@@ -29,8 +33,10 @@
import io.envoyproxy.envoy.type.matcher.v3.RegexMatcher;
import io.envoyproxy.envoy.type.v3.FractionalPercent;
import io.envoyproxy.envoy.type.v3.Int64Range;
+import io.grpc.xds.EnvoyProtoData.Address;
import io.grpc.xds.EnvoyProtoData.ClusterWeight;
import io.grpc.xds.EnvoyProtoData.Locality;
+import io.grpc.xds.EnvoyProtoData.Node;
import io.grpc.xds.EnvoyProtoData.Route;
import io.grpc.xds.EnvoyProtoData.RouteAction;
import io.grpc.xds.EnvoyProtoData.StructOrError;
@@ -39,6 +45,7 @@
import io.grpc.xds.RouteMatch.PathMatcher;
import java.util.Arrays;
import java.util.Collections;
+import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -63,7 +70,8 @@ public void locality_convertToAndFromLocalityProto() {
assertThat(xdsLocality.getZone()).isEqualTo("test_zone");
assertThat(xdsLocality.getSubZone()).isEqualTo("test_subzone");
- io.envoyproxy.envoy.api.v2.core.Locality convertedLocality = xdsLocality.toEnvoyProtoLocality();
+ io.envoyproxy.envoy.api.v2.core.Locality convertedLocality =
+ xdsLocality.toEnvoyProtoLocalityV2();
assertThat(convertedLocality.getRegion()).isEqualTo("test_region");
assertThat(convertedLocality.getZone()).isEqualTo("test_zone");
assertThat(convertedLocality.getSubZone()).isEqualTo("test_subzone");
@@ -84,6 +92,95 @@ public void locality_equal() {
.testEquals();
}
+ @Test
+ public void convertNode() {
+ Node node = Node.newBuilder()
+ .setId("node-id")
+ .setCluster("cluster")
+ .setMetadata(
+ ImmutableMap.of(
+ "TRAFFICDIRECTOR_INTERCEPTION_PORT",
+ "ENVOY_PORT",
+ "TRAFFICDIRECTOR_NETWORK_NAME",
+ "VPC_NETWORK_NAME"))
+ .setLocality(new Locality("region", "zone", "subzone"))
+ .addListeningAddresses(new Address("www.foo.com", 8080))
+ .addListeningAddresses(new Address("www.bar.com", 8088))
+ .setBuildVersion("v1")
+ .setUserAgentName("agent")
+ .setUserAgentVersion("1.1")
+ .addClientFeatures("feature-1")
+ .addClientFeatures("feature-2")
+ .build();
+ io.envoyproxy.envoy.config.core.v3.Node nodeProto =
+ io.envoyproxy.envoy.config.core.v3.Node.newBuilder()
+ .setId("node-id")
+ .setCluster("cluster")
+ .setMetadata(Struct.newBuilder()
+ .putFields("TRAFFICDIRECTOR_INTERCEPTION_PORT",
+ Value.newBuilder().setStringValue("ENVOY_PORT").build())
+ .putFields("TRAFFICDIRECTOR_NETWORK_NAME",
+ Value.newBuilder().setStringValue("VPC_NETWORK_NAME").build()))
+ .setLocality(
+ io.envoyproxy.envoy.config.core.v3.Locality.newBuilder()
+ .setRegion("region")
+ .setZone("zone")
+ .setSubZone("subzone"))
+ .addListeningAddresses(
+ io.envoyproxy.envoy.config.core.v3.Address.newBuilder()
+ .setSocketAddress(
+ io.envoyproxy.envoy.config.core.v3.SocketAddress.newBuilder()
+ .setAddress("www.foo.com")
+ .setPortValue(8080)))
+ .addListeningAddresses(
+ io.envoyproxy.envoy.config.core.v3.Address.newBuilder()
+ .setSocketAddress(
+ io.envoyproxy.envoy.config.core.v3.SocketAddress.newBuilder()
+ .setAddress("www.bar.com")
+ .setPortValue(8088)))
+ .setUserAgentName("agent")
+ .setUserAgentVersion("1.1")
+ .addClientFeatures("feature-1")
+ .addClientFeatures("feature-2")
+ .build();
+ assertThat(node.toEnvoyProtoNode()).isEqualTo(nodeProto);
+
+ @SuppressWarnings("deprecation") // Deprecated v2 API setBuildVersion().
+ io.envoyproxy.envoy.api.v2.core.Node nodeProtoV2 =
+ io.envoyproxy.envoy.api.v2.core.Node.newBuilder()
+ .setId("node-id")
+ .setCluster("cluster")
+ .setMetadata(Struct.newBuilder()
+ .putFields("TRAFFICDIRECTOR_INTERCEPTION_PORT",
+ Value.newBuilder().setStringValue("ENVOY_PORT").build())
+ .putFields("TRAFFICDIRECTOR_NETWORK_NAME",
+ Value.newBuilder().setStringValue("VPC_NETWORK_NAME").build()))
+ .setLocality(
+ io.envoyproxy.envoy.api.v2.core.Locality.newBuilder()
+ .setRegion("region")
+ .setZone("zone")
+ .setSubZone("subzone"))
+ .addListeningAddresses(
+ io.envoyproxy.envoy.api.v2.core.Address.newBuilder()
+ .setSocketAddress(
+ io.envoyproxy.envoy.api.v2.core.SocketAddress.newBuilder()
+ .setAddress("www.foo.com")
+ .setPortValue(8080)))
+ .addListeningAddresses(
+ io.envoyproxy.envoy.api.v2.core.Address.newBuilder()
+ .setSocketAddress(
+ io.envoyproxy.envoy.api.v2.core.SocketAddress.newBuilder()
+ .setAddress("www.bar.com")
+ .setPortValue(8088)))
+ .setBuildVersion("v1")
+ .setUserAgentName("agent")
+ .setUserAgentVersion("1.1")
+ .addClientFeatures("feature-1")
+ .addClientFeatures("feature-2")
+ .build();
+ assertThat(node.toEnvoyProtoNodeV2()).isEqualTo(nodeProtoV2);
+ }
+
@Test
public void locality_hash() {
assertThat(new Locality("region", "zone", "subzone").hashCode())
@@ -111,7 +208,7 @@ public void convertRoute() {
new Route(
new RouteMatch(new PathMatcher("/service/method", null, null),
Collections.emptyList(), null),
- new RouteAction("cluster-foo", null)));
+ new RouteAction(TimeUnit.SECONDS.toNanos(15L), "cluster-foo", null)));
io.envoyproxy.envoy.config.route.v3.Route unsupportedProto =
io.envoyproxy.envoy.config.route.v3.Route.newBuilder()
@@ -298,27 +395,51 @@ public void convertRouteMatch_withRuntimeFraction() {
@Test
public void convertRouteAction() {
- // cluster_specifier = cluster
+ // cluster_specifier = cluster, default timeout
io.envoyproxy.envoy.config.route.v3.RouteAction proto1 =
io.envoyproxy.envoy.config.route.v3.RouteAction.newBuilder()
.setCluster("cluster-foo")
.build();
StructOrError struct1 = RouteAction.fromEnvoyProtoRouteAction(proto1);
assertThat(struct1.getErrorDetail()).isNull();
+ assertThat(struct1.getStruct().getTimeoutNano())
+ .isEqualTo(TimeUnit.SECONDS.toNanos(15L)); // default value
assertThat(struct1.getStruct().getCluster()).isEqualTo("cluster-foo");
assertThat(struct1.getStruct().getWeightedCluster()).isNull();
- // cluster_specifier = cluster_header
+ // cluster_specifier = cluster, infinity timeout
io.envoyproxy.envoy.config.route.v3.RouteAction proto2 =
io.envoyproxy.envoy.config.route.v3.RouteAction.newBuilder()
- .setClusterHeader("cluster-bar")
+ .setMaxGrpcTimeout(Durations.fromNanos(0))
+ .setTimeout(Durations.fromMicros(20L))
+ .setCluster("cluster-foo")
.build();
StructOrError struct2 = RouteAction.fromEnvoyProtoRouteAction(proto2);
- assertThat(struct2).isNull();
+ assertThat(struct2.getStruct().getTimeoutNano())
+ .isEqualTo(Long.MAX_VALUE); // infinite
- // cluster_specifier = weighted_cluster
+ // cluster_specifier = cluster, infinity timeout
io.envoyproxy.envoy.config.route.v3.RouteAction proto3 =
io.envoyproxy.envoy.config.route.v3.RouteAction.newBuilder()
+ .setTimeout(Durations.fromNanos(0))
+ .setCluster("cluster-foo")
+ .build();
+ StructOrError struct3 = RouteAction.fromEnvoyProtoRouteAction(proto3);
+ assertThat(struct3.getStruct().getTimeoutNano()).isEqualTo(Long.MAX_VALUE); // infinite
+
+ // cluster_specifier = cluster_header
+ io.envoyproxy.envoy.config.route.v3.RouteAction proto4 =
+ io.envoyproxy.envoy.config.route.v3.RouteAction.newBuilder()
+ .setClusterHeader("cluster-bar")
+ .build();
+ StructOrError struct4 = RouteAction.fromEnvoyProtoRouteAction(proto4);
+ assertThat(struct4).isNull();
+
+ // cluster_specifier = weighted_cluster
+ io.envoyproxy.envoy.config.route.v3.RouteAction proto5 =
+ io.envoyproxy.envoy.config.route.v3.RouteAction.newBuilder()
+ .setMaxGrpcTimeout(Durations.fromSeconds(6L))
+ .setTimeout(Durations.fromMicros(20L))
.setWeightedClusters(
WeightedCluster.newBuilder()
.addClusters(
@@ -327,10 +448,12 @@ public void convertRouteAction() {
.setName("cluster-baz")
.setWeight(UInt32Value.newBuilder().setValue(100))))
.build();
- StructOrError struct3 = RouteAction.fromEnvoyProtoRouteAction(proto3);
- assertThat(struct3.getErrorDetail()).isNull();
- assertThat(struct3.getStruct().getCluster()).isNull();
- assertThat(struct3.getStruct().getWeightedCluster())
+ StructOrError struct5 = RouteAction.fromEnvoyProtoRouteAction(proto5);
+ assertThat(struct5.getErrorDetail()).isNull();
+ assertThat(struct5.getStruct().getTimeoutNano())
+ .isEqualTo(TimeUnit.SECONDS.toNanos(6L));
+ assertThat(struct5.getStruct().getCluster()).isNull();
+ assertThat(struct5.getStruct().getWeightedCluster())
.containsExactly(new ClusterWeight("cluster-baz", 100));
// cluster_specifier unset
diff --git a/xds/src/test/java/io/grpc/xds/LoadReportClientTest.java b/xds/src/test/java/io/grpc/xds/LoadReportClientTest.java
index 8c2e6f5a9fc..be68f08b110 100644
--- a/xds/src/test/java/io/grpc/xds/LoadReportClientTest.java
+++ b/xds/src/test/java/io/grpc/xds/LoadReportClientTest.java
@@ -28,6 +28,7 @@
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
+import com.google.common.base.Stopwatch;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.util.concurrent.MoreExecutors;
@@ -44,7 +45,6 @@
import io.envoyproxy.envoy.service.load_stats.v2.LoadStatsResponse;
import io.grpc.Context;
import io.grpc.Context.CancellationListener;
-import io.grpc.InternalLogId;
import io.grpc.ManagedChannel;
import io.grpc.Status;
import io.grpc.SynchronizationContext;
@@ -54,16 +54,19 @@
import io.grpc.internal.FakeClock;
import io.grpc.stub.StreamObserver;
import io.grpc.testing.GrpcCleanupRule;
-import io.grpc.xds.LoadReportClient.LoadReportCallback;
+import io.grpc.xds.LoadStatsManager.LoadStatsStore;
+import io.grpc.xds.LoadStatsManager.LoadStatsStoreFactory;
import java.util.ArrayDeque;
+import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Queue;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
-import javax.annotation.Nullable;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
@@ -93,6 +96,8 @@ public class LoadReportClientTest {
"TRAFFICDIRECTOR_NETWORK_HOSTNAME",
Value.newBuilder().setStringValue("default").build()))
.build();
+ private static final String CLUSTER1 = "cluster-foo.googleapis.com";
+ private static final String CLUSTER2 = "cluster-bar.googleapis.com";
private static final FakeClock.TaskFilter LOAD_REPORTING_TASK_FILTER =
new FakeClock.TaskFilter() {
@Override
@@ -119,11 +124,18 @@ public void uncaughtException(Thread t, Throwable e) {
throw new AssertionError(e);
}
});
- private final InternalLogId logId = InternalLogId.allocate("lrs-client-test", null);
private final FakeClock fakeClock = new FakeClock();
private final ArrayDeque> lrsRequestObservers =
new ArrayDeque<>();
private final AtomicBoolean callEnded = new AtomicBoolean(true);
+ private final LoadStatsManager loadStatsManager =
+ new LoadStatsManager(new LoadStatsStoreFactory() {
+ @Override
+ public LoadStatsStore newLoadStatsStore(String cluster, String clusterService) {
+ return new FakeLoadStatsStore(
+ cluster, clusterService, fakeClock.getStopwatchSupplier().get());
+ }
+ });
@Mock
private BackoffPolicy.Provider backoffPolicyProvider;
@@ -131,12 +143,6 @@ public void uncaughtException(Thread t, Throwable e) {
private BackoffPolicy backoffPolicy1;
@Mock
private BackoffPolicy backoffPolicy2;
- @Mock
- private LoadStatsStore loadStatsStore1;
- @Mock
- private LoadStatsStore loadStatsStore2;
- @Mock
- private LoadReportCallback callback;
@Captor
private ArgumentCaptor> lrsResponseObserverCaptor;
@@ -178,18 +184,18 @@ public void cancelled(Context context) {
when(backoffPolicy1.nextBackoffNanos())
.thenReturn(TimeUnit.SECONDS.toNanos(1L), TimeUnit.SECONDS.toNanos(10L));
when(backoffPolicy2.nextBackoffNanos())
- .thenReturn(TimeUnit.SECONDS.toNanos(1L), TimeUnit.SECONDS.toNanos(10L));
+ .thenReturn(TimeUnit.SECONDS.toNanos(2L), TimeUnit.SECONDS.toNanos(20L));
lrsClient =
new LoadReportClient(
- logId,
TARGET_NAME,
+ loadStatsManager,
channel,
NODE,
syncContext,
fakeClock.getScheduledExecutorService(),
backoffPolicyProvider,
fakeClock.getStopwatchSupplier());
- lrsClient.startLoadReporting(callback);
+ lrsClient.startLoadReporting();
}
@After
@@ -199,71 +205,95 @@ public void tearDown() {
}
@Test
- public void typicalWorkflow() {
+ public void periodicLoadReporting() {
verify(mockLoadReportingService).streamLoadStats(lrsResponseObserverCaptor.capture());
StreamObserver responseObserver = lrsResponseObserverCaptor.getValue();
StreamObserver requestObserver =
Iterables.getOnlyElement(lrsRequestObservers);
- InOrder inOrder = inOrder(requestObserver, callback);
+ InOrder inOrder = inOrder(requestObserver);
inOrder.verify(requestObserver).onNext(eq(buildInitialRequest()));
- String cluster1 = "cluster-foo.googleapis.com";
- ClusterStats rawStats1 = generateClusterLoadStats(cluster1, null);
- when(loadStatsStore1.generateLoadReport()).thenReturn(rawStats1);
- lrsClient.addLoadStatsStore(cluster1, null, loadStatsStore1);
+ FakeLoadStatsStore loadStatsStore1 =
+ (FakeLoadStatsStore) loadStatsManager.addLoadStats(CLUSTER1, null);
+ loadStatsStore1.refresh();
// Management server asks to report loads for cluster1.
- responseObserver.onNext(buildLrsResponse(ImmutableList.of(cluster1), 1000));
- inOrder.verify(callback).onReportResponse(1000);
+ responseObserver.onNext(buildLrsResponse(ImmutableList.of(CLUSTER1), 1000));
- ArgumentMatcher expectedLoadReportMatcher =
- new LoadStatsRequestMatcher(ImmutableList.of(rawStats1), 1000);
fakeClock.forwardNanos(999);
inOrder.verifyNoMoreInteractions();
fakeClock.forwardNanos(1);
- inOrder.verify(requestObserver).onNext(argThat(expectedLoadReportMatcher));
+ assertThat(loadStatsStore1.reported).hasSize(1);
+ ClusterStats report1 = loadStatsStore1.reported.poll();
+ assertThat(Durations.toNanos(report1.getLoadReportInterval())).isEqualTo(1000);
+ inOrder.verify(requestObserver)
+ .onNext(argThat(new LoadStatsRequestMatcher(Collections.singletonList(report1))));
+ loadStatsStore1.refresh();
fakeClock.forwardNanos(1000);
- inOrder.verify(requestObserver).onNext(argThat(expectedLoadReportMatcher));
+ assertThat(loadStatsStore1.reported).hasSize(1);
+ report1 = loadStatsStore1.reported.poll();
+ assertThat(Durations.toNanos(report1.getLoadReportInterval())).isEqualTo(1000);
+ inOrder.verify(requestObserver)
+ .onNext(argThat(new LoadStatsRequestMatcher(Collections.singletonList(report1))));
- String cluster2 = "cluster-bar.googleapis.com";
- ClusterStats rawStats2 = generateClusterLoadStats(cluster2, null);
- when(loadStatsStore2.generateLoadReport()).thenReturn(rawStats2);
- lrsClient.addLoadStatsStore(cluster2, null, loadStatsStore2);
+ FakeLoadStatsStore loadStatsStore2 =
+ (FakeLoadStatsStore) loadStatsManager.addLoadStats(CLUSTER2, null);
+ loadStatsStore2.refresh();
// Management server updates the interval of sending load reports, while still asking for
// loads to cluster1 only.
- responseObserver.onNext(buildLrsResponse(ImmutableList.of(cluster1), 2000));
- inOrder.verify(callback).onReportResponse(2000);
+ responseObserver.onNext(buildLrsResponse(ImmutableList.of(CLUSTER1), 2000));
fakeClock.forwardNanos(1000);
inOrder.verifyNoMoreInteractions();
-
fakeClock.forwardNanos(1000);
+ assertThat(loadStatsStore1.reported).hasSize(1);
+ report1 = loadStatsStore1.reported.poll();
+ assertThat(Durations.toNanos(report1.getLoadReportInterval())).isEqualTo(2000);
+ assertThat(loadStatsStore2.reported).isEmpty();
inOrder.verify(requestObserver)
- .onNext(argThat(new LoadStatsRequestMatcher(ImmutableList.of(rawStats1), 2000)));
+ .onNext(argThat(new LoadStatsRequestMatcher(Collections.singletonList(report1))));
- // Management server asks to report loads for cluster1 and cluster2.
- responseObserver.onNext(buildLrsResponse(ImmutableList.of(cluster1, cluster2), 2000));
+ // Management server asks to report loads for all clusters.
+ responseObserver.onNext(
+ LoadStatsResponse.newBuilder()
+ .setSendAllClusters(true)
+ .setLoadReportingInterval(Durations.fromNanos(2000))
+ .build());
+ loadStatsStore1.refresh();
+ loadStatsStore2.refresh();
fakeClock.forwardNanos(2000);
+ assertThat(loadStatsStore1.reported).hasSize(1);
+ report1 = loadStatsStore1.reported.poll();
+ assertThat(loadStatsStore2.reported).hasSize(1);
+ ClusterStats report2 = loadStatsStore2.reported.poll();
+ assertThat(Durations.toNanos(report1.getLoadReportInterval())).isEqualTo(2000);
+ assertThat(Durations.toNanos(report2.getLoadReportInterval())).isEqualTo(2000 + 2000);
inOrder.verify(requestObserver)
- .onNext(
- argThat(
- new LoadStatsRequestMatcher(ImmutableList.of(rawStats1, rawStats2), 2000)));
+ .onNext(argThat(new LoadStatsRequestMatcher(Arrays.asList(report1, report2))));
// Load reports for cluster1 is no longer wanted.
- responseObserver.onNext(buildLrsResponse(ImmutableList.of(cluster2), 2000));
+ responseObserver.onNext(buildLrsResponse(Collections.singletonList(CLUSTER2), 2000));
+ loadStatsStore1.refresh();
+ loadStatsStore2.refresh();
fakeClock.forwardNanos(2000);
+ assertThat(loadStatsStore1.reported).isEmpty();
+ assertThat(loadStatsStore2.reported).hasSize(1);
+ report2 = loadStatsStore2.reported.poll();
+ assertThat(Durations.toNanos(report2.getLoadReportInterval())).isEqualTo(2000);
inOrder.verify(requestObserver)
- .onNext(argThat(new LoadStatsRequestMatcher(ImmutableList.of(rawStats2), 2000)));
+ .onNext(argThat(new LoadStatsRequestMatcher(Collections.singletonList(report2))));
// Management server asks loads for a cluster that client has no load data.
responseObserver
.onNext(buildLrsResponse(ImmutableList.of("cluster-unknown.googleapis.com"), 2000));
fakeClock.forwardNanos(2000);
+ assertThat(loadStatsStore1.reported).isEmpty();
+ assertThat(loadStatsStore2.reported).isEmpty();
ArgumentCaptor reportCaptor = ArgumentCaptor.forClass(null);
inOrder.verify(requestObserver).onNext(reportCaptor.capture());
assertThat(reportCaptor.getValue().getClusterStatsCount()).isEqualTo(0);
@@ -282,9 +312,9 @@ public void lrsStreamClosedAndRetried() {
String clusterName = "cluster-foo.googleapis.com";
String clusterServiceName = "service-blade.googleapis.com";
- ClusterStats stats = generateClusterLoadStats(clusterName, clusterServiceName);
- when(loadStatsStore1.generateLoadReport()).thenReturn(stats);
- lrsClient.addLoadStatsStore(clusterName, null, loadStatsStore1);
+ FakeLoadStatsStore loadStatsStore =
+ (FakeLoadStatsStore) loadStatsManager.addLoadStats(clusterName, clusterServiceName);
+ loadStatsStore.refresh();
// First balancer RPC
verify(requestObserver).onNext(eq(buildInitialRequest()));
@@ -331,7 +361,7 @@ public void lrsStreamClosedAndRetried() {
// Balancer sends a response asking for loads of the cluster.
responseObserver
- .onNext(buildLrsResponse(ImmutableList.of(clusterName), 0));
+ .onNext(buildLrsResponse(ImmutableList.of(clusterName), 5));
// Then breaks the RPC
responseObserver.onError(Status.UNAVAILABLE.asException());
@@ -348,12 +378,12 @@ public void lrsStreamClosedAndRetried() {
fakeClock.forwardNanos(4);
responseObserver.onError(Status.UNAVAILABLE.asException());
- // Will be on the first retry (1s) of backoff sequence 2.
+ // Will be on the first retry (2s) of backoff sequence 2.
inOrder.verify(backoffPolicy2).nextBackoffNanos();
assertEquals(1, fakeClock.numPendingTasks(LRS_RPC_RETRY_TASK_FILTER));
// Fast-forward to a moment before the retry, the time spent in the last try is deducted.
- fakeClock.forwardNanos(TimeUnit.SECONDS.toNanos(1) - 4 - 1);
+ fakeClock.forwardNanos(TimeUnit.SECONDS.toNanos(2) - 4 - 1);
verifyNoMoreInteractions(mockLoadReportingService);
// Then time for retry
fakeClock.forwardNanos(1);
@@ -368,8 +398,11 @@ public void lrsStreamClosedAndRetried() {
responseObserver
.onNext(buildLrsResponse(ImmutableList.of(clusterName), 10));
fakeClock.forwardNanos(10);
+ ClusterStats report = Iterables.getOnlyElement(loadStatsStore.reported);
+ assertThat(Durations.toNanos(report.getLoadReportInterval()))
+ .isEqualTo(TimeUnit.SECONDS.toNanos(1 + 10 + 2) + 10);
verify(requestObserver)
- .onNext(argThat(new LoadStatsRequestMatcher(ImmutableList.of(stats), 10)));
+ .onNext(argThat(new LoadStatsRequestMatcher(Collections.singletonList(report))));
// Wrapping up
verify(backoffPolicyProvider, times(2)).get();
@@ -386,9 +419,9 @@ public void raceBetweenLoadReportingAndLbStreamClosure() {
String clusterName = "cluster-foo.googleapis.com";
String clusterServiceName = "service-blade.googleapis.com";
- ClusterStats stats = generateClusterLoadStats(clusterName, clusterServiceName);
- when(loadStatsStore1.generateLoadReport()).thenReturn(stats);
- lrsClient.addLoadStatsStore(clusterName, null, loadStatsStore1);
+ FakeLoadStatsStore loadStatsStore =
+ (FakeLoadStatsStore) loadStatsManager.addLoadStats(clusterName, clusterServiceName);
+ loadStatsStore.refresh();
// First balancer RPC
verify(requestObserver).onNext(eq(buildInitialRequest()));
@@ -445,59 +478,15 @@ private static LoadStatsRequest buildInitialRequest() {
.build();
}
- /**
- * Generates a raw service load stats report with random data.
- */
- private static ClusterStats generateClusterLoadStats(
- String clusterName, @Nullable String clusterServiceName) {
- long callsInProgress = ThreadLocalRandom.current().nextLong(Long.MAX_VALUE);
- long callsSucceeded = ThreadLocalRandom.current().nextLong(Long.MAX_VALUE);
- long callsFailed = ThreadLocalRandom.current().nextLong(Long.MAX_VALUE);
- long callsIssued = ThreadLocalRandom.current().nextLong(Long.MAX_VALUE);
- long numLbDrops = ThreadLocalRandom.current().nextLong(Long.MAX_VALUE);
- long numThrottleDrops = ThreadLocalRandom.current().nextLong(Long.MAX_VALUE);
-
- ClusterStats.Builder clusterStatsBuilder = ClusterStats.newBuilder();
- clusterStatsBuilder.setClusterName(clusterName);
- if (clusterServiceName != null) {
- clusterStatsBuilder.setClusterServiceName(clusterServiceName);
- }
- clusterStatsBuilder.addUpstreamLocalityStats(
- UpstreamLocalityStats.newBuilder()
- .setLocality(
- Locality.newBuilder()
- .setRegion("region-foo")
- .setZone("zone-bar")
- .setSubZone("subzone-baz"))
- .setTotalRequestsInProgress(callsInProgress)
- .setTotalSuccessfulRequests(callsSucceeded)
- .setTotalErrorRequests(callsFailed)
- .setTotalIssuedRequests(callsIssued))
- .addDroppedRequests(
- DroppedRequests.newBuilder()
- .setCategory("lb")
- .setDroppedCount(numLbDrops))
- .addDroppedRequests(
- DroppedRequests.newBuilder()
- .setCategory("throttle")
- .setDroppedCount(numThrottleDrops))
- .setTotalDroppedRequests(numLbDrops + numThrottleDrops);
- return clusterStatsBuilder.build();
- }
-
/**
* For comparing LoadStatsRequest stats data regardless of .
*/
private static class LoadStatsRequestMatcher implements ArgumentMatcher {
private final Map expectedStats = new HashMap<>();
- LoadStatsRequestMatcher(Collection clusterStats, long expectedIntervalNano) {
+ LoadStatsRequestMatcher(Collection clusterStats) {
for (ClusterStats stats : clusterStats) {
- ClusterStats statsWithInterval =
- stats.toBuilder()
- .setLoadReportInterval(Durations.fromNanos(expectedIntervalNano))
- .build();
- expectedStats.put(statsWithInterval.getClusterName(), statsWithInterval);
+ expectedStats.put(stats.getClusterName(), stats);
}
}
@@ -519,4 +508,82 @@ public boolean matches(LoadStatsRequest argument) {
return true;
}
}
+
+ private static final class FakeLoadStatsStore implements LoadStatsStore {
+ private final String cluster;
+ private final String clusterService;
+ private final Stopwatch stopwatch;
+ private final Queue reported = new ArrayDeque<>();
+ private ClusterStats stats;
+
+ private FakeLoadStatsStore(String cluster, String clusterService, Stopwatch stopwatch) {
+ this.cluster = cluster;
+ this.clusterService = clusterService;
+ this.stopwatch = stopwatch;
+ stopwatch.reset().start();
+ refresh();
+ }
+
+ @Override
+ public ClusterStats generateLoadReport() {
+ ClusterStats report =
+ stats.toBuilder()
+ .setLoadReportInterval(Durations.fromNanos(stopwatch.elapsed(TimeUnit.NANOSECONDS)))
+ .build();
+ stopwatch.reset().start();
+ reported.offer(report);
+ return report;
+ }
+
+ @Override
+ public ClientLoadCounter addLocality(EnvoyProtoData.Locality locality) {
+ throw new UnsupportedOperationException("should not used");
+ }
+
+ @Override
+ public void removeLocality(EnvoyProtoData.Locality locality) {
+ throw new UnsupportedOperationException("should not used");
+ }
+
+ @Override
+ public void recordDroppedRequest(String category) {
+ throw new UnsupportedOperationException("should not used");
+ }
+
+ private void refresh() {
+ long callsInProgress = ThreadLocalRandom.current().nextLong(Long.MAX_VALUE);
+ long callsSucceeded = ThreadLocalRandom.current().nextLong(Long.MAX_VALUE);
+ long callsFailed = ThreadLocalRandom.current().nextLong(Long.MAX_VALUE);
+ long callsIssued = ThreadLocalRandom.current().nextLong(Long.MAX_VALUE);
+ long numLbDrops = ThreadLocalRandom.current().nextLong(Long.MAX_VALUE);
+ long numThrottleDrops = ThreadLocalRandom.current().nextLong(Long.MAX_VALUE);
+
+ ClusterStats.Builder clusterStatsBuilder = ClusterStats.newBuilder();
+ clusterStatsBuilder.setClusterName(cluster);
+ if (clusterService != null) {
+ clusterStatsBuilder.setClusterServiceName(clusterService);
+ }
+ clusterStatsBuilder.addUpstreamLocalityStats(
+ UpstreamLocalityStats.newBuilder()
+ .setLocality(
+ Locality.newBuilder()
+ .setRegion(cluster + "-region-foo")
+ .setZone(cluster + "-zone-bar")
+ .setSubZone(cluster + "-subzone-baz"))
+ .setTotalRequestsInProgress(callsInProgress)
+ .setTotalSuccessfulRequests(callsSucceeded)
+ .setTotalErrorRequests(callsFailed)
+ .setTotalIssuedRequests(callsIssued))
+ .addDroppedRequests(
+ DroppedRequests.newBuilder()
+ .setCategory("lb")
+ .setDroppedCount(numLbDrops))
+ .addDroppedRequests(
+ DroppedRequests.newBuilder()
+ .setCategory("throttle")
+ .setDroppedCount(numThrottleDrops))
+ .setTotalDroppedRequests(numLbDrops + numThrottleDrops);
+ stats = clusterStatsBuilder.build();
+ }
+ }
}
diff --git a/xds/src/test/java/io/grpc/xds/LoadStatsStoreImplTest.java b/xds/src/test/java/io/grpc/xds/LoadStatsStoreImplTest.java
index d58fd4bb92e..4320ff279fd 100644
--- a/xds/src/test/java/io/grpc/xds/LoadStatsStoreImplTest.java
+++ b/xds/src/test/java/io/grpc/xds/LoadStatsStoreImplTest.java
@@ -18,13 +18,17 @@
import static com.google.common.truth.Truth.assertThat;
+import com.google.common.base.Stopwatch;
import com.google.common.collect.ImmutableMap;
+import com.google.protobuf.util.Durations;
import io.envoyproxy.envoy.api.v2.endpoint.ClusterStats;
import io.envoyproxy.envoy.api.v2.endpoint.ClusterStats.DroppedRequests;
import io.envoyproxy.envoy.api.v2.endpoint.EndpointLoadMetricStats;
import io.envoyproxy.envoy.api.v2.endpoint.UpstreamLocalityStats;
+import io.grpc.internal.FakeClock;
import io.grpc.xds.ClientLoadCounter.MetricValue;
import io.grpc.xds.EnvoyProtoData.Locality;
+import io.grpc.xds.LoadStatsManager.LoadStatsStore;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -35,7 +39,6 @@
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicLong;
import javax.annotation.Nullable;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -49,16 +52,15 @@ public class LoadStatsStoreImplTest {
new Locality("test_region1", "test_zone", "test_subzone");
private static final Locality LOCALITY2 =
new Locality("test_region2", "test_zone", "test_subzone");
- private ConcurrentMap localityLoadCounters;
+ private final FakeClock fakeClock = new FakeClock();
private ConcurrentMap dropCounters;
private LoadStatsStore loadStatsStore;
@Before
public void setUp() {
- localityLoadCounters = new ConcurrentHashMap<>();
dropCounters = new ConcurrentHashMap<>();
- loadStatsStore =
- new LoadStatsStoreImpl(CLUSTER_NAME, null, localityLoadCounters, dropCounters);
+ Stopwatch stopwatch = fakeClock.getStopwatchSupplier().get();
+ loadStatsStore = new LoadStatsStoreImpl(CLUSTER_NAME, null, stopwatch, dropCounters);
}
private static List buildEndpointLoadMetricStatsList(
@@ -83,7 +85,7 @@ private static UpstreamLocalityStats buildUpstreamLocalityStats(
@Nullable List metrics) {
UpstreamLocalityStats.Builder builder =
UpstreamLocalityStats.newBuilder()
- .setLocality(locality.toEnvoyProtoLocality())
+ .setLocality(locality.toEnvoyProtoLocalityV2())
.setTotalSuccessfulRequests(callsSucceed)
.setTotalErrorRequests(callsFailed)
.setTotalRequestsInProgress(callsInProgress)
@@ -103,7 +105,7 @@ private static DroppedRequests buildDroppedRequests(String category, long counts
private static ClusterStats buildClusterStats(
@Nullable List upstreamLocalityStatsList,
- @Nullable List droppedRequestsList) {
+ @Nullable List droppedRequestsList, long intervalNano) {
ClusterStats.Builder clusterStatsBuilder = ClusterStats.newBuilder();
clusterStatsBuilder.setClusterName(CLUSTER_NAME);
if (upstreamLocalityStatsList != null) {
@@ -117,6 +119,7 @@ private static ClusterStats buildClusterStats(
}
clusterStatsBuilder.setTotalDroppedRequests(dropCount);
}
+ clusterStatsBuilder.setLoadReportInterval(Durations.fromNanos(intervalNano));
return clusterStatsBuilder.build();
}
@@ -159,74 +162,47 @@ private static void assertUpstreamLocalityStatsEqual(UpstreamLocalityStats expec
}
@Test
- public void addAndGetAndRemoveLocality() {
- loadStatsStore.addLocality(LOCALITY1);
- assertThat(localityLoadCounters).containsKey(LOCALITY1);
-
- // Adding the same locality counter again causes an exception.
- try {
- loadStatsStore.addLocality(LOCALITY1);
- Assert.fail();
- } catch (IllegalStateException expected) {
- assertThat(expected).hasMessageThat()
- .contains("An active counter for locality " + LOCALITY1 + " already exists");
- }
-
- assertThat(loadStatsStore.getLocalityCounter(LOCALITY1))
- .isSameInstanceAs(localityLoadCounters.get(LOCALITY1));
- assertThat(loadStatsStore.getLocalityCounter(LOCALITY2)).isNull();
-
- // Removing an non-existing locality counter causes an exception.
- try {
- loadStatsStore.removeLocality(LOCALITY2);
- Assert.fail();
- } catch (IllegalStateException expected) {
- assertThat(expected).hasMessageThat()
- .contains("No active counter for locality " + LOCALITY2 + " exists");
- }
-
- // Removing the locality counter only mark it as inactive, but not throw it away.
- loadStatsStore.removeLocality(LOCALITY1);
- assertThat(localityLoadCounters.get(LOCALITY1).isActive()).isFalse();
-
- // Removing an inactive locality counter causes an exception.
- try {
- loadStatsStore.removeLocality(LOCALITY1);
- Assert.fail();
- } catch (IllegalStateException expected) {
- assertThat(expected).hasMessageThat()
- .contains("No active counter for locality " + LOCALITY1 + " exists");
- }
-
- // Adding it back simply mark it as active again.
+ public void removeInactiveCountersAfterGeneratingLoadReport() {
loadStatsStore.addLocality(LOCALITY1);
- assertThat(localityLoadCounters.get(LOCALITY1).isActive()).isTrue();
+ assertThat(loadStatsStore.generateLoadReport().getUpstreamLocalityStatsCount()).isEqualTo(1);
+ loadStatsStore.removeLocality(LOCALITY1); // becomes inactive
+ assertThat(loadStatsStore.generateLoadReport().getUpstreamLocalityStatsCount()).isEqualTo(1);
+ assertThat(loadStatsStore.generateLoadReport().getUpstreamLocalityStatsCount()).isEqualTo(0);
}
@Test
- public void removeInactiveCountersAfterGeneratingLoadReport() {
- localityLoadCounters.put(LOCALITY1, new ClientLoadCounter());
- ClientLoadCounter inactiveCounter = new ClientLoadCounter();
- inactiveCounter.setActive(false);
- localityLoadCounters.put(LOCALITY2, inactiveCounter);
- loadStatsStore.generateLoadReport();
- assertThat(localityLoadCounters).containsKey(LOCALITY1);
- assertThat(localityLoadCounters).doesNotContainKey(LOCALITY2);
+ public void localityCountersReferenceCounted() {
+ loadStatsStore.addLocality(LOCALITY1);
+ loadStatsStore.addLocality(LOCALITY1);
+ loadStatsStore.removeLocality(LOCALITY1);
+ assertThat(loadStatsStore.generateLoadReport().getUpstreamLocalityStatsCount()).isEqualTo(1);
+ assertThat(loadStatsStore.generateLoadReport().getUpstreamLocalityStatsCount())
+ .isEqualTo(1); // still active
+ loadStatsStore.removeLocality(LOCALITY1); // becomes inactive
+ assertThat(loadStatsStore.generateLoadReport().getUpstreamLocalityStatsCount()).isEqualTo(1);
+ assertThat(loadStatsStore.generateLoadReport().getUpstreamLocalityStatsCount()).isEqualTo(0);
}
@Test
public void loadReportContainsRecordedStats() {
- ClientLoadCounter counter1 = new ClientLoadCounter(4315, 3421, 23, 593);
+ ClientLoadCounter counter1 = loadStatsStore.addLocality(LOCALITY1);
+ counter1.setCallsSucceeded(4315);
+ counter1.setCallsInProgress(3421);
+ counter1.setCallsFailed(23);
+ counter1.setCallsIssued(593);
counter1.recordMetric("cpu_utilization", 0.3244);
counter1.recordMetric("mem_utilization", 0.01233);
counter1.recordMetric("named_cost_or_utilization", 3221.6543);
- ClientLoadCounter counter2 = new ClientLoadCounter(41234, 432, 431, 702);
+ ClientLoadCounter counter2 = loadStatsStore.addLocality(LOCALITY2);
+ counter2.setCallsSucceeded(41234);
+ counter2.setCallsInProgress(432);
+ counter2.setCallsFailed(431);
+ counter2.setCallsIssued(702);
counter2.recordMetric("cpu_utilization", 0.6526);
counter2.recordMetric("mem_utilization", 0.3473);
counter2.recordMetric("named_cost_or_utilization", 87653.4234);
- localityLoadCounters.put(LOCALITY1, counter1);
- localityLoadCounters.put(LOCALITY2, counter2);
+ fakeClock.forwardNanos(1000L);
Map metrics1 =
ImmutableMap.of(
"cpu_utilization", new MetricValue(1, 0.3244),
@@ -245,16 +221,17 @@ public void loadReportContainsRecordedStats() {
buildUpstreamLocalityStats(LOCALITY2, 41234, 432, 431, 702,
buildEndpointLoadMetricStatsList(metrics2))
),
- null);
+ null, 1000L);
assertClusterStatsEqual(expectedReport, loadStatsStore.generateLoadReport());
+ fakeClock.forwardNanos(2000L);
expectedReport =
buildClusterStats(
Arrays.asList(
buildUpstreamLocalityStats(LOCALITY1, 0, 3421, 0, 0, null),
buildUpstreamLocalityStats(LOCALITY2, 0, 432, 0, 0, null)
),
- null);
+ null, 2000L);
assertClusterStatsEqual(expectedReport, loadStatsStore.generateLoadReport());
}
@@ -270,10 +247,13 @@ public void recordingDroppedRequests() {
}
assertThat(dropCounters.get("lb").get()).isEqualTo(numLbDrop);
assertThat(dropCounters.get("throttle").get()).isEqualTo(numThrottleDrop);
+
+ fakeClock.forwardNanos(1000L);
ClusterStats expectedLoadReport =
buildClusterStats(null,
Arrays.asList(buildDroppedRequests("lb", numLbDrop),
- buildDroppedRequests("throttle", numThrottleDrop)));
+ buildDroppedRequests("throttle", numThrottleDrop)),
+ 1000L);
assertClusterStatsEqual(expectedLoadReport, loadStatsStore.generateLoadReport());
assertThat(dropCounters.get("lb").get()).isEqualTo(0);
assertThat(dropCounters.get("throttle").get()).isEqualTo(0);
diff --git a/xds/src/test/java/io/grpc/xds/LocalityStoreTest.java b/xds/src/test/java/io/grpc/xds/LocalityStoreTest.java
index 4f9c8f51753..6c079b54edb 100644
--- a/xds/src/test/java/io/grpc/xds/LocalityStoreTest.java
+++ b/xds/src/test/java/io/grpc/xds/LocalityStoreTest.java
@@ -69,6 +69,7 @@
import io.grpc.xds.EnvoyProtoData.LbEndpoint;
import io.grpc.xds.EnvoyProtoData.Locality;
import io.grpc.xds.EnvoyProtoData.LocalityLbEndpoints;
+import io.grpc.xds.LoadStatsManager.LoadStatsStore;
import io.grpc.xds.LocalityStore.LocalityStoreImpl;
import io.grpc.xds.OrcaOobUtil.OrcaOobReportListener;
import io.grpc.xds.OrcaOobUtil.OrcaReportingConfig;
@@ -84,7 +85,6 @@
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
-import javax.annotation.Nullable;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -1053,8 +1053,8 @@ public PickResult pickSubchannel(PickSubchannelArgs args) {
ArgumentCaptor subchannelPickerCaptor = ArgumentCaptor.forClass(null);
inOrder.verify(helper).updateBalancingState(same(READY), subchannelPickerCaptor.capture());
assertThat(subchannelPickerCaptor.getValue()
- .pickSubchannel(mock(PickSubchannelArgs.class))
- .getSubchannel())
+ .pickSubchannel(mock(PickSubchannelArgs.class))
+ .getSubchannel())
.isSameInstanceAs(mockSubchannel3);
// P0 gets READY - P0 R, P1 F&D, P2 R&D, P3 N/A
@@ -1078,8 +1078,8 @@ public PickResult pickSubchannel(PickSubchannelArgs args) {
assertThat(deactivationTasks).hasSize(2);
inOrder.verify(helper).updateBalancingState(same(READY), subchannelPickerCaptor.capture());
assertThat(subchannelPickerCaptor.getValue()
- .pickSubchannel(mock(PickSubchannelArgs.class))
- .getSubchannel())
+ .pickSubchannel(mock(PickSubchannelArgs.class))
+ .getSubchannel())
.isSameInstanceAs(mockSubchannel1);
// P1 gets READY - P0 R, P1 R&D, P2 R&D, P3 N/A
@@ -1188,8 +1188,8 @@ public PickResult pickSubchannel(PickSubchannelArgs args) {
assertThat(fakeClock.getPendingTasks(deactivationTaskFilter)).hasSize(2);
inOrder.verify(helper).updateBalancingState(same(READY), subchannelPickerCaptor.capture());
assertThat(subchannelPickerCaptor.getValue()
- .pickSubchannel(mock(PickSubchannelArgs.class))
- .getSubchannel())
+ .pickSubchannel(mock(PickSubchannelArgs.class))
+ .getSubchannel())
.isSameInstanceAs(mockSubchannel22);
// EDS update, localities moved: P0 sz1, sz3; P1 sz4; P2 sz2 - P0 C, P1 R, P2 R&D
@@ -1214,8 +1214,8 @@ public void run() {
assertThat(loadBalancers.values()).containsExactly(lb1, lb2, lb3, lb4);
inOrder.verify(helper).updateBalancingState(same(READY), subchannelPickerCaptor.capture());
assertThat(subchannelPickerCaptor.getValue()
- .pickSubchannel(mock(PickSubchannelArgs.class))
- .getSubchannel())
+ .pickSubchannel(mock(PickSubchannelArgs.class))
+ .getSubchannel())
.isSameInstanceAs(mockSubchannel4);
// The order of the following four handleResolvedAddresses() does not matter. We want to verify
// they are after helper.updateBalancingState()
@@ -1283,8 +1283,8 @@ public PickResult pickSubchannel(PickSubchannelArgs args) {
assertThat(fakeClock.getPendingTasks(deactivationTaskFilter)).hasSize(1);
inOrder.verify(helper).updateBalancingState(same(READY), subchannelPickerCaptor.capture());
assertThat(subchannelPickerCaptor.getValue()
- .pickSubchannel(mock(PickSubchannelArgs.class))
- .getSubchannel())
+ .pickSubchannel(mock(PickSubchannelArgs.class))
+ .getSubchannel())
.isSameInstanceAs(newMockSubchannel3);
inOrder.verifyNoMoreInteractions();
@@ -1337,9 +1337,11 @@ public ClusterStats generateLoadReport() {
}
@Override
- public void addLocality(Locality locality) {
+ public ClientLoadCounter addLocality(Locality locality) {
assertThat(localityCounters).doesNotContainKey(locality);
- localityCounters.put(locality, new ClientLoadCounter());
+ ClientLoadCounter counter = new ClientLoadCounter();
+ localityCounters.put(locality, counter);
+ return counter;
}
@Override
@@ -1348,12 +1350,6 @@ public void removeLocality(Locality locality) {
localityCounters.remove(locality);
}
- @Nullable
- @Override
- public ClientLoadCounter getLocalityCounter(Locality locality) {
- return localityCounters.get(locality);
- }
-
@Override
public void recordDroppedRequest(String category) {
// NO-OP, verify by invocations.
diff --git a/xds/src/test/java/io/grpc/xds/LrsLoadBalancerTest.java b/xds/src/test/java/io/grpc/xds/LrsLoadBalancerTest.java
index e845a2ccc97..bea3a2f2faa 100644
--- a/xds/src/test/java/io/grpc/xds/LrsLoadBalancerTest.java
+++ b/xds/src/test/java/io/grpc/xds/LrsLoadBalancerTest.java
@@ -40,6 +40,7 @@
import io.grpc.xds.ClientLoadCounter.LoadRecordingStreamTracerFactory;
import io.grpc.xds.ClientLoadCounter.LoadRecordingSubchannelPicker;
import io.grpc.xds.EnvoyProtoData.Locality;
+import io.grpc.xds.LoadStatsManager.LoadStatsStore;
import io.grpc.xds.LrsLoadBalancerProvider.LrsConfig;
import java.net.SocketAddress;
import java.util.ArrayDeque;
@@ -72,8 +73,6 @@ public class LrsLoadBalancerTest {
private static final String LRS_SERVER_NAME = "trafficdirector.googleapis.com";
private static final Locality TEST_LOCALITY =
new Locality("test-region", "test-zone", "test-subzone");
-
- private final ClientLoadCounter counter = new ClientLoadCounter();
private final LoadRecorder loadRecorder = new LoadRecorder();
private final Queue childBalancers = new ArrayDeque<>();
@@ -106,7 +105,7 @@ public void subchannelPickerInterceptedWithLoadRecording() {
PickResult result = picker.pickSubchannel(mock(PickSubchannelArgs.class));
ClientStreamTracer.Factory tracerFactory = result.getStreamTracerFactory();
assertThat(((LoadRecordingStreamTracerFactory) tracerFactory).getCounter())
- .isSameInstanceAs(counter);
+ .isSameInstanceAs(loadRecorder.counter);
loadBalancer.shutdown();
assertThat(childBalancer.shutdown).isTrue();
assertThat(loadRecorder.recording).isFalse();
@@ -300,7 +299,8 @@ public int hashCode() {
}
}
- private final class LoadRecorder implements LoadStatsStore {
+ private static final class LoadRecorder implements LoadStatsStore {
+ private final ClientLoadCounter counter = new ClientLoadCounter();
private boolean recording = false;
@Override
@@ -309,9 +309,10 @@ public ClusterStats generateLoadReport() {
}
@Override
- public void addLocality(Locality locality) {
+ public ClientLoadCounter addLocality(Locality locality) {
assertThat(locality).isEqualTo(TEST_LOCALITY);
recording = true;
+ return counter;
}
@Override
@@ -320,12 +321,6 @@ public void removeLocality(Locality locality) {
recording = false;
}
- @Override
- public ClientLoadCounter getLocalityCounter(Locality locality) {
- assertThat(locality).isEqualTo(TEST_LOCALITY);
- return counter;
- }
-
@Override
public void recordDroppedRequest(String category) {
throw new UnsupportedOperationException("should not be called");
diff --git a/xds/src/test/java/io/grpc/xds/XdsClientImplTest.java b/xds/src/test/java/io/grpc/xds/XdsClientImplTest.java
index 8e8002ecc0b..ec81022c924 100644
--- a/xds/src/test/java/io/grpc/xds/XdsClientImplTest.java
+++ b/xds/src/test/java/io/grpc/xds/XdsClientImplTest.java
@@ -39,7 +39,6 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import com.google.common.collect.ImmutableList;
@@ -51,13 +50,10 @@
import com.google.protobuf.util.Durations;
import io.envoyproxy.envoy.api.v2.ClusterLoadAssignment;
import io.envoyproxy.envoy.api.v2.ClusterLoadAssignment.Policy;
-import io.envoyproxy.envoy.api.v2.DiscoveryRequest;
-import io.envoyproxy.envoy.api.v2.DiscoveryResponse;
import io.envoyproxy.envoy.api.v2.auth.UpstreamTlsContext;
import io.envoyproxy.envoy.api.v2.core.AggregatedConfigSource;
import io.envoyproxy.envoy.api.v2.core.ConfigSource;
import io.envoyproxy.envoy.api.v2.core.HealthStatus;
-import io.envoyproxy.envoy.api.v2.core.Node;
import io.envoyproxy.envoy.api.v2.endpoint.ClusterStats;
import io.envoyproxy.envoy.api.v2.route.RedirectAction;
import io.envoyproxy.envoy.api.v2.route.WeightedCluster;
@@ -70,7 +66,9 @@
import io.envoyproxy.envoy.config.route.v3.RouteMatch;
import io.envoyproxy.envoy.config.route.v3.VirtualHost;
import io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.SdsSecretConfig;
-import io.envoyproxy.envoy.service.discovery.v2.AggregatedDiscoveryServiceGrpc.AggregatedDiscoveryServiceImplBase;
+import io.envoyproxy.envoy.service.discovery.v3.AggregatedDiscoveryServiceGrpc.AggregatedDiscoveryServiceImplBase;
+import io.envoyproxy.envoy.service.discovery.v3.DiscoveryRequest;
+import io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse;
import io.envoyproxy.envoy.service.load_stats.v2.LoadReportingServiceGrpc.LoadReportingServiceImplBase;
import io.envoyproxy.envoy.service.load_stats.v2.LoadStatsRequest;
import io.envoyproxy.envoy.service.load_stats.v2.LoadStatsResponse;
@@ -94,12 +92,14 @@
import io.grpc.xds.EnvoyProtoData.LbEndpoint;
import io.grpc.xds.EnvoyProtoData.Locality;
import io.grpc.xds.EnvoyProtoData.LocalityLbEndpoints;
+import io.grpc.xds.EnvoyProtoData.Node;
import io.grpc.xds.XdsClient.ClusterUpdate;
import io.grpc.xds.XdsClient.ClusterWatcher;
import io.grpc.xds.XdsClient.ConfigUpdate;
import io.grpc.xds.XdsClient.ConfigWatcher;
import io.grpc.xds.XdsClient.EndpointUpdate;
import io.grpc.xds.XdsClient.EndpointWatcher;
+import io.grpc.xds.XdsClient.XdsChannel;
import io.grpc.xds.XdsClient.XdsChannelFactory;
import io.grpc.xds.XdsClientImpl.MessagePrinter;
import java.io.IOException;
@@ -125,14 +125,15 @@
import org.mockito.MockitoAnnotations;
/**
- * Tests for {@link XdsClientImpl}.
+ * Tests for {@link XdsClientImpl} with xDS v3 protocol. However, the test xDS server still sends
+ * update with v2 resources for testing compatibility.
*/
@RunWith(JUnit4.class)
public class XdsClientImplTest {
private static final String TARGET_AUTHORITY = "foo.googleapis.com:8080";
- private static final Node NODE = Node.getDefaultInstance();
+ private static final Node NODE = Node.newBuilder().build();
private static final FakeClock.TaskFilter RPC_RETRY_TASK_FILTER =
new FakeClock.TaskFilter() {
@Override
@@ -279,13 +280,14 @@ public void cancelled(Context context) {
cleanupRule.register(InProcessChannelBuilder.forName(serverName).directExecutor().build());
List servers =
- ImmutableList.of(new ServerInfo(serverName, ImmutableList.of()));
+ ImmutableList.of(new ServerInfo(serverName, ImmutableList.of(), null));
XdsChannelFactory channelFactory = new XdsChannelFactory() {
@Override
- ManagedChannel createChannel(List servers) {
- assertThat(Iterables.getOnlyElement(servers).getServerUri()).isEqualTo(serverName);
- assertThat(Iterables.getOnlyElement(servers).getChannelCredentials()).isEmpty();
- return channel;
+ XdsChannel createChannel(List servers) {
+ ServerInfo serverInfo = Iterables.getOnlyElement(servers);
+ assertThat(serverInfo.getServerUri()).isEqualTo(serverName);
+ assertThat(serverInfo.getChannelCredentials()).isEmpty();
+ return new XdsChannel(channel, /* useProtocolV3= */ true);
}
};
@@ -294,7 +296,7 @@ ManagedChannel createChannel(List servers) {
TARGET_AUTHORITY,
servers,
channelFactory,
- NODE,
+ EnvoyProtoData.Node.newBuilder().build(),
syncContext,
fakeClock.getScheduledExecutorService(),
backoffPolicyProvider,
@@ -335,7 +337,7 @@ public void ldsResponseWithoutMatchingResource() {
// Client sends an LDS request for the host name (with port) to management server.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", TARGET_AUTHORITY,
- XdsClientImpl.ADS_TYPE_URL_LDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_LDS, "")));
assertThat(fakeClock.getPendingTasks(LDS_RESOURCE_FETCH_TIMEOUT_TASK_FILTER)).hasSize(1);
@@ -365,7 +367,7 @@ public void ldsResponseWithoutMatchingResource() {
// Client sends an ACK LDS request.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "0", TARGET_AUTHORITY,
- XdsClientImpl.ADS_TYPE_URL_LDS_V2, "0000")));
+ XdsClientImpl.ADS_TYPE_URL_LDS, "0000")));
verify(configWatcher, never()).onConfigChanged(any(ConfigUpdate.class));
verify(configWatcher, never()).onResourceDoesNotExist(TARGET_AUTHORITY);
@@ -391,7 +393,7 @@ public void failToFindVirtualHostInLdsResponseInLineRouteConfig() {
// Client sends an LDS request for the host name (with port) to management server.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", TARGET_AUTHORITY,
- XdsClientImpl.ADS_TYPE_URL_LDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_LDS, "")));
assertThat(fakeClock.getPendingTasks(LDS_RESOURCE_FETCH_TIMEOUT_TASK_FILTER)).hasSize(1);
io.envoyproxy.envoy.api.v2.RouteConfiguration routeConfig =
@@ -414,7 +416,7 @@ public void failToFindVirtualHostInLdsResponseInLineRouteConfig() {
verify(requestObserver)
.onNext(
argThat(new DiscoveryRequestMatcher("", TARGET_AUTHORITY,
- XdsClientImpl.ADS_TYPE_URL_LDS_V2, "0000")));
+ XdsClientImpl.ADS_TYPE_URL_LDS, "0000")));
verify(configWatcher, never()).onConfigChanged(any(ConfigUpdate.class));
verify(configWatcher, never()).onResourceDoesNotExist(TARGET_AUTHORITY);
@@ -440,7 +442,7 @@ public void resolveVirtualHostInLdsResponse() {
// Client sends an LDS request for the host name (with port) to management server.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", TARGET_AUTHORITY,
- XdsClientImpl.ADS_TYPE_URL_LDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_LDS, "")));
ScheduledTask ldsRespTimer =
Iterables.getOnlyElement(
fakeClock.getPendingTasks(LDS_RESOURCE_FETCH_TIMEOUT_TASK_FILTER));
@@ -487,7 +489,7 @@ public void resolveVirtualHostInLdsResponse() {
// Client sends an ACK request.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "0", TARGET_AUTHORITY,
- XdsClientImpl.ADS_TYPE_URL_LDS_V2, "0000")));
+ XdsClientImpl.ADS_TYPE_URL_LDS, "0000")));
ArgumentCaptor configUpdateCaptor = ArgumentCaptor.forClass(null);
verify(configWatcher).onConfigChanged(configUpdateCaptor.capture());
@@ -513,7 +515,7 @@ public void rdsResponseWithoutMatchingResource() {
// Client sends an LDS request for the host name (with port) to management server.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", TARGET_AUTHORITY,
- XdsClientImpl.ADS_TYPE_URL_LDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_LDS, "")));
Rds rdsConfig =
Rds.newBuilder()
@@ -533,12 +535,12 @@ public void rdsResponseWithoutMatchingResource() {
// Client sends an ACK LDS request.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "0", TARGET_AUTHORITY,
- XdsClientImpl.ADS_TYPE_URL_LDS_V2, "0000")));
+ XdsClientImpl.ADS_TYPE_URL_LDS, "0000")));
// Client sends an (first) RDS request.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", "route-foo.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_RDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_RDS, "")));
assertThat(fakeClock.getPendingTasks(RDS_RESOURCE_FETCH_TIMEOUT_TASK_FILTER)).hasSize(1);
@@ -565,7 +567,7 @@ public void rdsResponseWithoutMatchingResource() {
// Client sends an ACK RDS request.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "0", "route-foo.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_RDS_V2, "0000")));
+ XdsClientImpl.ADS_TYPE_URL_RDS, "0000")));
verify(configWatcher, never()).onConfigChanged(any(ConfigUpdate.class));
verify(configWatcher, never()).onResourceDoesNotExist(anyString());
@@ -631,7 +633,7 @@ public void resolveVirtualHostInRdsResponse() {
// Client sent an ACK RDS request.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "0", "route-foo.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_RDS_V2, "0000")));
+ XdsClientImpl.ADS_TYPE_URL_RDS, "0000")));
ArgumentCaptor configUpdateCaptor = ArgumentCaptor.forClass(null);
verify(configWatcher).onConfigChanged(configUpdateCaptor.capture());
@@ -736,7 +738,7 @@ public void resolveVirtualHostWithPathMatchingInRdsResponse() {
// Client sent an ACK RDS request.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "0", "route-foo.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_RDS_V2, "0000")));
+ XdsClientImpl.ADS_TYPE_URL_RDS, "0000")));
ArgumentCaptor configUpdateCaptor = ArgumentCaptor.forClass(null);
verify(configWatcher).onConfigChanged(configUpdateCaptor.capture());
@@ -748,7 +750,8 @@ public void resolveVirtualHostWithPathMatchingInRdsResponse() {
new io.grpc.xds.RouteMatch(
/* prefix= */ null,
/* path= */ "/service1/method1"),
- new EnvoyProtoData.RouteAction("cl1.googleapis.com", null)));
+ new EnvoyProtoData.RouteAction(
+ TimeUnit.SECONDS.toNanos(15L), "cl1.googleapis.com", null)));
assertThat(routes.get(1)).isEqualTo(
new EnvoyProtoData.Route(
// path match with weighted cluster route
@@ -756,6 +759,7 @@ public void resolveVirtualHostWithPathMatchingInRdsResponse() {
/* prefix= */ null,
/* path= */ "/service2/method2"),
new EnvoyProtoData.RouteAction(
+ TimeUnit.SECONDS.toNanos(15L),
null,
ImmutableList.of(
new EnvoyProtoData.ClusterWeight("cl21.googleapis.com", 30),
@@ -767,7 +771,8 @@ public void resolveVirtualHostWithPathMatchingInRdsResponse() {
new io.grpc.xds.RouteMatch(
/* prefix= */ "/service1/",
/* path= */ null),
- new EnvoyProtoData.RouteAction("cl1.googleapis.com", null)));
+ new EnvoyProtoData.RouteAction(
+ TimeUnit.SECONDS.toNanos(15L), "cl1.googleapis.com", null)));
assertThat(routes.get(3)).isEqualTo(
new EnvoyProtoData.Route(
// default match with cluster route
@@ -775,7 +780,7 @@ public void resolveVirtualHostWithPathMatchingInRdsResponse() {
/* prefix= */ "",
/* path= */ null),
new EnvoyProtoData.RouteAction(
- "cluster.googleapis.com", null)));
+ TimeUnit.SECONDS.toNanos(15L), "cluster.googleapis.com", null)));
}
/**
@@ -834,7 +839,7 @@ public void failToFindVirtualHostInRdsResponse() {
verify(requestObserver)
.onNext(
argThat(new DiscoveryRequestMatcher("", "route-foo.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_RDS_V2, "0000")));
+ XdsClientImpl.ADS_TYPE_URL_RDS, "0000")));
verify(configWatcher, never()).onConfigChanged(any(ConfigUpdate.class));
verify(configWatcher, never()).onResourceDoesNotExist(anyString());
@@ -901,7 +906,7 @@ public void matchingVirtualHostDoesNotContainRouteAction() {
verify(requestObserver)
.onNext(
argThat(new DiscoveryRequestMatcher("", "route-foo.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_RDS_V2, "0000")));
+ XdsClientImpl.ADS_TYPE_URL_RDS, "0000")));
verify(configWatcher, never()).onConfigChanged(any(ConfigUpdate.class));
verify(configWatcher, never()).onResourceDoesNotExist(anyString());
@@ -925,7 +930,7 @@ public void notifyUpdatedResources() {
// Client sends an LDS request for the host name (with port) to management server.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", TARGET_AUTHORITY,
- XdsClientImpl.ADS_TYPE_URL_LDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_LDS, "")));
// Management server sends back an LDS response containing a RouteConfiguration for the
// requested Listener directly in-line.
@@ -950,7 +955,7 @@ public void notifyUpdatedResources() {
// Client sends an ACK LDS request.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "0", TARGET_AUTHORITY,
- XdsClientImpl.ADS_TYPE_URL_LDS_V2, "0000")));
+ XdsClientImpl.ADS_TYPE_URL_LDS, "0000")));
// Cluster name is resolved and notified to config watcher.
ArgumentCaptor configUpdateCaptor = ArgumentCaptor.forClass(null);
@@ -979,7 +984,7 @@ public void notifyUpdatedResources() {
// Client sends an ACK LDS request.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "1", TARGET_AUTHORITY,
- XdsClientImpl.ADS_TYPE_URL_LDS_V2, "0001")));
+ XdsClientImpl.ADS_TYPE_URL_LDS, "0001")));
// Updated cluster name is notified to config watcher.
configUpdateCaptor = ArgumentCaptor.forClass(null);
@@ -1008,12 +1013,12 @@ public void notifyUpdatedResources() {
// Client sends an ACK LDS request.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "2", TARGET_AUTHORITY,
- XdsClientImpl.ADS_TYPE_URL_LDS_V2, "0002")));
+ XdsClientImpl.ADS_TYPE_URL_LDS, "0002")));
// Client sends an (first) RDS request.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", "some-route-to-foo.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_RDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_RDS, "")));
// Management server sends back an RDS response containing the RouteConfiguration
// for the requested resource.
@@ -1032,7 +1037,7 @@ public void notifyUpdatedResources() {
// Client sent an ACK RDS request.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "0", "some-route-to-foo.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_RDS_V2, "0000")));
+ XdsClientImpl.ADS_TYPE_URL_RDS, "0000")));
// Updated cluster name is notified to config watcher again.
configUpdateCaptor = ArgumentCaptor.forClass(null);
@@ -1055,7 +1060,7 @@ public void notifyUpdatedResources() {
// Client sent an ACK RDS request.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "1", "some-route-to-foo.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_RDS_V2, "0001")));
+ XdsClientImpl.ADS_TYPE_URL_RDS, "0001")));
// Updated cluster name is notified to config watcher again.
configUpdateCaptor = ArgumentCaptor.forClass(null);
@@ -1091,7 +1096,7 @@ public void waitRdsResponsesForRequestedResource() {
// Client sends an LDS request for the host name (with port) to management server.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", TARGET_AUTHORITY,
- XdsClientImpl.ADS_TYPE_URL_LDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_LDS, "")));
// Management sends back an LDS response telling client to do RDS.
Rds rdsConfig =
@@ -1113,12 +1118,12 @@ public void waitRdsResponsesForRequestedResource() {
// Client sends an ACK LDS request.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "0", TARGET_AUTHORITY,
- XdsClientImpl.ADS_TYPE_URL_LDS_V2, "0000")));
+ XdsClientImpl.ADS_TYPE_URL_LDS, "0000")));
// Client sends an (first) RDS request.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", "route-foo.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_RDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_RDS, "")));
ScheduledTask rdsRespTimer =
Iterables.getOnlyElement(
@@ -1143,7 +1148,7 @@ public void waitRdsResponsesForRequestedResource() {
// Client sent an ACK RDS request.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "0", "route-foo.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_RDS_V2, "0000")));
+ XdsClientImpl.ADS_TYPE_URL_RDS, "0000")));
// Client waits for future RDS responses silently.
verifyNoMoreInteractions(configWatcher);
@@ -1170,7 +1175,7 @@ public void waitRdsResponsesForRequestedResource() {
// Client sent an ACK RDS request.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "1", "route-foo.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_RDS_V2, "0001")));
+ XdsClientImpl.ADS_TYPE_URL_RDS, "0001")));
// Updated cluster name is notified to config watcher.
ArgumentCaptor configUpdateCaptor = ArgumentCaptor.forClass(null);
@@ -1193,7 +1198,7 @@ public void routeConfigurationRemovedNotifiedToWatcher() {
// Client sends an LDS request for the host name (with port) to management server.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", TARGET_AUTHORITY,
- XdsClientImpl.ADS_TYPE_URL_LDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_LDS, "")));
// Management sends back an LDS response telling client to do RDS.
Rds rdsConfig =
@@ -1215,12 +1220,12 @@ public void routeConfigurationRemovedNotifiedToWatcher() {
// Client sends an ACK LDS request.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "0", TARGET_AUTHORITY,
- XdsClientImpl.ADS_TYPE_URL_LDS_V2, "0000")));
+ XdsClientImpl.ADS_TYPE_URL_LDS, "0000")));
// Client sends an (first) RDS request.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", "route-foo.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_RDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_RDS, "")));
// Management server sends back an RDS response containing RouteConfiguration requested.
List routeConfigs = ImmutableList.of(
@@ -1237,7 +1242,7 @@ public void routeConfigurationRemovedNotifiedToWatcher() {
// Client sent an ACK RDS request.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "0", "route-foo.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_RDS_V2, "0000")));
+ XdsClientImpl.ADS_TYPE_URL_RDS, "0000")));
// Resolved cluster name is notified to config watcher.
ArgumentCaptor configUpdateCaptor = ArgumentCaptor.forClass(null);
@@ -1255,7 +1260,7 @@ public void routeConfigurationRemovedNotifiedToWatcher() {
// Client sent an ACK LDS request.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "1", TARGET_AUTHORITY,
- XdsClientImpl.ADS_TYPE_URL_LDS_V2, "0001")));
+ XdsClientImpl.ADS_TYPE_URL_LDS, "0001")));
verify(configWatcher).onResourceDoesNotExist(TARGET_AUTHORITY);
}
@@ -1290,7 +1295,7 @@ public void updateRdsRequestResourceWhileInitialResourceFetchInProgress() {
// Client sends an (first) RDS request.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", "route-foo.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_RDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_RDS, "")));
ScheduledTask rdsRespTimer =
Iterables.getOnlyElement(
@@ -1319,7 +1324,7 @@ public void updateRdsRequestResourceWhileInitialResourceFetchInProgress() {
// Client sent a new RDS request with updated resource name.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", "route-bar.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_RDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_RDS, "")));
assertThat(rdsRespTimer.isCancelled()).isTrue();
rdsRespTimer =
@@ -1356,7 +1361,7 @@ public void cdsResponseWithoutMatchingResource() {
// Client sends a CDS request for the only cluster being watched to management server.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", "cluster-foo.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_CDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_CDS, "")));
assertThat(fakeClock.getPendingTasks(CDS_RESOURCE_FETCH_TIMEOUT_TASK_FILTER)).hasSize(1);
// Management server sends back a CDS response without Cluster for the requested resource.
@@ -1370,7 +1375,7 @@ public void cdsResponseWithoutMatchingResource() {
// Client sent an ACK CDS request.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "0", "cluster-foo.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_CDS_V2, "0000")));
+ XdsClientImpl.ADS_TYPE_URL_CDS, "0000")));
verify(clusterWatcher, never()).onClusterChanged(any(ClusterUpdate.class));
verify(clusterWatcher, never()).onResourceDoesNotExist("cluster-foo.googleapis.com");
verify(clusterWatcher, never()).onError(any(Status.class));
@@ -1393,7 +1398,7 @@ public void cdsResponseWithMatchingResource() {
// Client sends a CDS request for the only cluster being watched to management server.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", "cluster-foo.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_CDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_CDS, "")));
ScheduledTask cdsRespTimer =
Iterables.getOnlyElement(
fakeClock.getPendingTasks(CDS_RESOURCE_FETCH_TIMEOUT_TASK_FILTER));
@@ -1410,7 +1415,7 @@ public void cdsResponseWithMatchingResource() {
// Client sent an ACK CDS request.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "0", "cluster-foo.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_CDS_V2, "0000")));
+ XdsClientImpl.ADS_TYPE_URL_CDS, "0000")));
assertThat(cdsRespTimer.isCancelled()).isTrue();
ArgumentCaptor clusterUpdateCaptor = ArgumentCaptor.forClass(null);
@@ -1434,7 +1439,7 @@ public void cdsResponseWithMatchingResource() {
// Client sent an ACK CDS request.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "1", "cluster-foo.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_CDS_V2, "0001")));
+ XdsClientImpl.ADS_TYPE_URL_CDS, "0001")));
verify(clusterWatcher, times(2)).onClusterChanged(clusterUpdateCaptor.capture());
clusterUpdate = clusterUpdateCaptor.getValue();
@@ -1469,7 +1474,7 @@ public void cdsResponseWithUpstreamTlsContext() {
// Client sent an ACK CDS request.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "0", "cluster-foo.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_CDS_V2, "0000")));
+ XdsClientImpl.ADS_TYPE_URL_CDS, "0000")));
ArgumentCaptor clusterUpdateCaptor = ArgumentCaptor.forClass(null);
verify(clusterWatcher, times(1)).onClusterChanged(clusterUpdateCaptor.capture());
ClusterUpdate clusterUpdate = clusterUpdateCaptor.getValue();
@@ -1507,7 +1512,7 @@ public void multipleClusterWatchers() {
argThat(
new DiscoveryRequestMatcher("",
ImmutableList.of("cluster-foo.googleapis.com", "cluster-bar.googleapis.com"),
- XdsClientImpl.ADS_TYPE_URL_CDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_CDS, "")));
assertThat(fakeClock.getPendingTasks(CDS_RESOURCE_FETCH_TIMEOUT_TASK_FILTER)).hasSize(2);
// Management server sends back a CDS response contains Cluster for only one of
@@ -1525,7 +1530,7 @@ public void multipleClusterWatchers() {
argThat(
new DiscoveryRequestMatcher("0",
ImmutableList.of("cluster-foo.googleapis.com", "cluster-bar.googleapis.com"),
- XdsClientImpl.ADS_TYPE_URL_CDS_V2, "0000")));
+ XdsClientImpl.ADS_TYPE_URL_CDS, "0000")));
// Two watchers get notification of cluster update for the cluster they are interested in.
ArgumentCaptor clusterUpdateCaptor1 = ArgumentCaptor.forClass(null);
@@ -1572,7 +1577,7 @@ public void multipleClusterWatchers() {
argThat(
new DiscoveryRequestMatcher("1",
ImmutableList.of("cluster-foo.googleapis.com", "cluster-bar.googleapis.com"),
- XdsClientImpl.ADS_TYPE_URL_CDS_V2, "0001")));
+ XdsClientImpl.ADS_TYPE_URL_CDS, "0001")));
verifyNoMoreInteractions(watcher1, watcher2); // resource has no change
ArgumentCaptor clusterUpdateCaptor3 = ArgumentCaptor.forClass(null);
@@ -1602,7 +1607,7 @@ public void watchClusterAlreadyBeingWatched() {
// Client sends an CDS request to management server.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", "cluster-foo.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_CDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_CDS, "")));
assertThat(fakeClock.getPendingTasks(CDS_RESOURCE_FETCH_TIMEOUT_TASK_FILTER)).hasSize(1);
// Management server sends back an CDS response with Cluster for the requested
@@ -1616,7 +1621,7 @@ public void watchClusterAlreadyBeingWatched() {
// Client sent an ACK CDS request.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "0", "cluster-foo.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_CDS_V2, "0000")));
+ XdsClientImpl.ADS_TYPE_URL_CDS, "0000")));
ArgumentCaptor clusterUpdateCaptor1 = ArgumentCaptor.forClass(null);
verify(watcher1).onClusterChanged(clusterUpdateCaptor1.capture());
@@ -1660,7 +1665,7 @@ public void addRemoveClusterWatchers() {
// Client sends an CDS request to management server.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", "cluster-foo.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_CDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_CDS, "")));
// Management server sends back a CDS response with Cluster for the requested
// cluster.
@@ -1673,7 +1678,7 @@ public void addRemoveClusterWatchers() {
// Client sent an ACK CDS request.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "0", "cluster-foo.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_CDS_V2, "0000")));
+ XdsClientImpl.ADS_TYPE_URL_CDS, "0000")));
ArgumentCaptor clusterUpdateCaptor1 = ArgumentCaptor.forClass(null);
verify(watcher1).onClusterChanged(clusterUpdateCaptor1.capture());
@@ -1693,7 +1698,7 @@ public void addRemoveClusterWatchers() {
argThat(
new DiscoveryRequestMatcher("0",
ImmutableList.of("cluster-foo.googleapis.com", "cluster-bar.googleapis.com"),
- XdsClientImpl.ADS_TYPE_URL_CDS_V2, "0000")));
+ XdsClientImpl.ADS_TYPE_URL_CDS, "0000")));
// Management server sends back a CDS response with Cluster for all requested cluster.
clusters = ImmutableList.of(
@@ -1711,7 +1716,7 @@ public void addRemoveClusterWatchers() {
argThat(
new DiscoveryRequestMatcher("1",
ImmutableList.of("cluster-foo.googleapis.com", "cluster-bar.googleapis.com"),
- XdsClientImpl.ADS_TYPE_URL_CDS_V2, "0001")));
+ XdsClientImpl.ADS_TYPE_URL_CDS, "0001")));
verifyNoMoreInteractions(watcher1); // resource has no change
ArgumentCaptor clusterUpdateCaptor2 = ArgumentCaptor.forClass(null);
verify(watcher2).onClusterChanged(clusterUpdateCaptor2.capture());
@@ -1730,7 +1735,7 @@ public void addRemoveClusterWatchers() {
// that cluster.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "1", "cluster-bar.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_CDS_V2, "0001")));
+ XdsClientImpl.ADS_TYPE_URL_CDS, "0001")));
// Management server has nothing to respond.
@@ -1741,7 +1746,7 @@ public void addRemoveClusterWatchers() {
.onNext(
argThat(
new DiscoveryRequestMatcher("1", ImmutableList.of(),
- XdsClientImpl.ADS_TYPE_URL_CDS_V2, "0001")));
+ XdsClientImpl.ADS_TYPE_URL_CDS, "0001")));
// Management server sends back a new CDS response.
clusters = ImmutableList.of(
@@ -1756,7 +1761,7 @@ public void addRemoveClusterWatchers() {
.onNext(
argThat(
new DiscoveryRequestMatcher("2", ImmutableList.of(),
- XdsClientImpl.ADS_TYPE_URL_CDS_V2, "0002")));
+ XdsClientImpl.ADS_TYPE_URL_CDS, "0002")));
// Cancelled watchers do not receive notification.
verifyNoMoreInteractions(watcher1, watcher2);
@@ -1769,7 +1774,7 @@ public void addRemoveClusterWatchers() {
// A CDS request is sent to indicate subscription of "cluster-foo.googleapis.com" only.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "2", "cluster-foo.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_CDS_V2, "0002")));
+ XdsClientImpl.ADS_TYPE_URL_CDS, "0002")));
// Management server sends back a new CDS response for at least newly requested resources
// (it is required to do so).
@@ -1795,7 +1800,7 @@ public void addRemoveClusterWatchers() {
// A CDS request is sent to re-subscribe the cluster again.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "3", "cluster-foo.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_CDS_V2, "0003")));
+ XdsClientImpl.ADS_TYPE_URL_CDS, "0003")));
}
@Test
@@ -1811,7 +1816,7 @@ public void addRemoveClusterWatcherWhileInitialResourceFetchInProgress() {
.onNext(
argThat(
new DiscoveryRequestMatcher("", "cluster-foo.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_CDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_CDS, "")));
assertThat(fakeClock.getPendingTasks(CDS_RESOURCE_FETCH_TIMEOUT_TASK_FILTER)).hasSize(1);
fakeClock.forwardTime(XdsClientImpl.INITIAL_RESOURCE_FETCH_TIMEOUT_SEC - 1, TimeUnit.SECONDS);
@@ -1829,7 +1834,7 @@ public void addRemoveClusterWatcherWhileInitialResourceFetchInProgress() {
argThat(
new DiscoveryRequestMatcher("",
ImmutableList.of("cluster-foo.googleapis.com", "cluster-bar.googleapis.com"),
- XdsClientImpl.ADS_TYPE_URL_CDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_CDS, "")));
assertThat(fakeClock.getPendingTasks(CDS_RESOURCE_FETCH_TIMEOUT_TASK_FILTER)).hasSize(2);
fakeClock.forwardTime(1, TimeUnit.SECONDS);
@@ -1860,7 +1865,8 @@ public void addRemoveClusterWatcherWhileInitialResourceFetchInProgress() {
assertThat(fakeClock.getPendingTasks(CDS_RESOURCE_FETCH_TIMEOUT_TASK_FILTER)).isEmpty();
assertThat(timeoutTask.isCancelled()).isTrue();
- verifyZeroInteractions(watcher3, watcher4);
+ // TODO(chengyuanzhang): migrate to verifyNoInteractions.
+ verifyNoMoreInteractions(watcher3, watcher4);
}
@Test
@@ -1871,7 +1877,7 @@ public void cdsUpdateForClusterBeingRemoved() {
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", "cluster-foo.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_CDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_CDS, "")));
assertThat(fakeClock.getPendingTasks(CDS_RESOURCE_FETCH_TIMEOUT_TASK_FILTER)).hasSize(1);
// Management server sends back a CDS response containing requested resource.
@@ -1917,7 +1923,7 @@ public void edsResponseWithoutMatchingResource() {
// Client sends an EDS request for the only cluster being watched to management server.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", "cluster-foo.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_EDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_EDS, "")));
assertThat(fakeClock.getPendingTasks(EDS_RESOURCE_FETCH_TIMEOUT_TASK_FILTER)).hasSize(1);
// Management server sends back an EDS response without ClusterLoadAssignment for the requested
@@ -1946,7 +1952,7 @@ public void edsResponseWithoutMatchingResource() {
// Client sent an ACK EDS request.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "0", "cluster-foo.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_EDS_V2, "0000")));
+ XdsClientImpl.ADS_TYPE_URL_EDS, "0000")));
verify(endpointWatcher, never()).onEndpointChanged(any(EndpointUpdate.class));
verify(endpointWatcher, never()).onResourceDoesNotExist("cluster-foo.googleapis.com");
@@ -1969,7 +1975,7 @@ public void edsResponseWithMatchingResource() {
// Client sends an EDS request for the only cluster being watched to management server.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", "cluster-foo.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_EDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_EDS, "")));
ScheduledTask edsRespTimeoutTask =
Iterables.getOnlyElement(
fakeClock.getPendingTasks(EDS_RESOURCE_FETCH_TIMEOUT_TASK_FILTER));
@@ -2012,7 +2018,7 @@ public void edsResponseWithMatchingResource() {
// Client sent an ACK EDS request.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "0", "cluster-foo.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_EDS_V2, "0000")));
+ XdsClientImpl.ADS_TYPE_URL_EDS, "0000")));
ArgumentCaptor endpointUpdateCaptor = ArgumentCaptor.forClass(null);
verify(endpointWatcher).onEndpointChanged(endpointUpdateCaptor.capture());
@@ -2045,7 +2051,7 @@ public void edsResponseWithMatchingResource() {
// Client sent an ACK EDS request.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "1", "cluster-foo.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_EDS_V2, "0001")));
+ XdsClientImpl.ADS_TYPE_URL_EDS, "0001")));
verify(endpointWatcher, times(2)).onEndpointChanged(endpointUpdateCaptor.capture());
endpointUpdate = endpointUpdateCaptor.getValue();
@@ -2072,7 +2078,7 @@ public void multipleEndpointWatchers() {
argThat(
new DiscoveryRequestMatcher("",
ImmutableList.of("cluster-foo.googleapis.com", "cluster-bar.googleapis.com"),
- XdsClientImpl.ADS_TYPE_URL_EDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_EDS, "")));
assertThat(fakeClock.getPendingTasks(EDS_RESOURCE_FETCH_TIMEOUT_TASK_FILTER)).hasSize(2);
@@ -2100,7 +2106,7 @@ public void multipleEndpointWatchers() {
argThat(
new DiscoveryRequestMatcher("0",
ImmutableList.of("cluster-foo.googleapis.com", "cluster-bar.googleapis.com"),
- XdsClientImpl.ADS_TYPE_URL_EDS_V2, "0000")));
+ XdsClientImpl.ADS_TYPE_URL_EDS, "0000")));
// Two watchers get notification of endpoint update for the cluster they are interested in.
ArgumentCaptor endpointUpdateCaptor1 = ArgumentCaptor.forClass(null);
@@ -2127,7 +2133,8 @@ public void multipleEndpointWatchers() {
new LbEndpoint("192.168.0.1", 8080,
2, true)), 1, 0));
- verifyZeroInteractions(watcher3);
+ // TODO(chengyuanzhang): migrate to verifyNoInteractions.
+ verifyNoMoreInteractions(watcher3);
// Management server sends back another EDS response contains ClusterLoadAssignment for the
// other requested cluster.
@@ -2150,7 +2157,7 @@ public void multipleEndpointWatchers() {
argThat(
new DiscoveryRequestMatcher("1",
ImmutableList.of("cluster-foo.googleapis.com", "cluster-bar.googleapis.com"),
- XdsClientImpl.ADS_TYPE_URL_EDS_V2, "0001")));
+ XdsClientImpl.ADS_TYPE_URL_EDS, "0001")));
// The corresponding watcher gets notified.
ArgumentCaptor endpointUpdateCaptor3 = ArgumentCaptor.forClass(null);
@@ -2181,7 +2188,7 @@ public void watchEndpointsForClusterAlreadyBeingWatched() {
// Client sends first EDS request.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", "cluster-foo.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_EDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_EDS, "")));
assertThat(fakeClock.getPendingTasks(EDS_RESOURCE_FETCH_TIMEOUT_TASK_FILTER)).hasSize(1);
@@ -2206,7 +2213,7 @@ public void watchEndpointsForClusterAlreadyBeingWatched() {
// Client sent an ACK EDS request.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "0", "cluster-foo.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_EDS_V2, "0000")));
+ XdsClientImpl.ADS_TYPE_URL_EDS, "0000")));
ArgumentCaptor endpointUpdateCaptor1 = ArgumentCaptor.forClass(null);
verify(watcher1).onEndpointChanged(endpointUpdateCaptor1.capture());
@@ -2259,7 +2266,7 @@ public void addRemoveEndpointWatchers() {
// Client sends an EDS request to management server.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", "cluster-foo.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_EDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_EDS, "")));
// Management server sends back an EDS response with ClusterLoadAssignment for the requested
// cluster.
@@ -2281,7 +2288,7 @@ public void addRemoveEndpointWatchers() {
// Client sent an ACK EDS request.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "0", "cluster-foo.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_EDS_V2, "0000")));
+ XdsClientImpl.ADS_TYPE_URL_EDS, "0000")));
ArgumentCaptor endpointUpdateCaptor1 = ArgumentCaptor.forClass(null);
verify(watcher1).onEndpointChanged(endpointUpdateCaptor1.capture());
@@ -2306,7 +2313,7 @@ public void addRemoveEndpointWatchers() {
argThat(
new DiscoveryRequestMatcher("0",
ImmutableList.of("cluster-foo.googleapis.com", "cluster-bar.googleapis.com"),
- XdsClientImpl.ADS_TYPE_URL_EDS_V2, "0000")));
+ XdsClientImpl.ADS_TYPE_URL_EDS, "0000")));
// Management server sends back an EDS response with ClusterLoadAssignment for one of requested
// cluster.
@@ -2329,7 +2336,7 @@ public void addRemoveEndpointWatchers() {
argThat(
new DiscoveryRequestMatcher("1",
ImmutableList.of("cluster-foo.googleapis.com", "cluster-bar.googleapis.com"),
- XdsClientImpl.ADS_TYPE_URL_EDS_V2, "0001")));
+ XdsClientImpl.ADS_TYPE_URL_EDS, "0001")));
ArgumentCaptor endpointUpdateCaptor2 = ArgumentCaptor.forClass(null);
verify(watcher2).onEndpointChanged(endpointUpdateCaptor2.capture());
@@ -2350,7 +2357,7 @@ public void addRemoveEndpointWatchers() {
// sent an new EDS request to unsubscribe from that cluster.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "1", "cluster-bar.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_EDS_V2, "0001")));
+ XdsClientImpl.ADS_TYPE_URL_EDS, "0001")));
// Management server should not respond as it had previously sent the requested resource.
@@ -2364,7 +2371,7 @@ public void addRemoveEndpointWatchers() {
argThat(
new DiscoveryRequestMatcher("1",
ImmutableList.of(), // empty resources
- XdsClientImpl.ADS_TYPE_URL_EDS_V2, "0001")));
+ XdsClientImpl.ADS_TYPE_URL_EDS, "0001")));
// All endpoint watchers have been cancelled.
@@ -2395,7 +2402,7 @@ public void addRemoveEndpointWatchers() {
argThat(
new DiscoveryRequestMatcher("2",
ImmutableList.of(), // empty resources
- XdsClientImpl.ADS_TYPE_URL_EDS_V2, "0002")));
+ XdsClientImpl.ADS_TYPE_URL_EDS, "0002")));
// Cancelled watchers do not receive notification.
verifyNoMoreInteractions(watcher1, watcher2);
@@ -2412,7 +2419,7 @@ public void addRemoveEndpointWatchers() {
// An EDS request is sent to re-subscribe the cluster again.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "2", "cluster-bar.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_EDS_V2, "0002")));
+ XdsClientImpl.ADS_TYPE_URL_EDS, "0002")));
// Management server sends back an EDS response for re-subscribed resource.
clusterLoadAssignments = ImmutableList.of(
@@ -2446,7 +2453,7 @@ public void addRemoveEndpointWatchers() {
argThat(
new DiscoveryRequestMatcher("3",
ImmutableList.of("cluster-bar.googleapis.com"),
- XdsClientImpl.ADS_TYPE_URL_EDS_V2, "0003")));
+ XdsClientImpl.ADS_TYPE_URL_EDS, "0003")));
}
@Test
@@ -2462,7 +2469,7 @@ public void addRemoveEndpointWatcherWhileInitialResourceFetchInProgress() {
.onNext(
argThat(
new DiscoveryRequestMatcher("", "cluster-foo.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_EDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_EDS, "")));
assertThat(fakeClock.getPendingTasks(EDS_RESOURCE_FETCH_TIMEOUT_TASK_FILTER)).hasSize(1);
fakeClock.forwardTime(XdsClientImpl.INITIAL_RESOURCE_FETCH_TIMEOUT_SEC - 1, TimeUnit.SECONDS);
@@ -2480,7 +2487,7 @@ public void addRemoveEndpointWatcherWhileInitialResourceFetchInProgress() {
argThat(
new DiscoveryRequestMatcher("",
ImmutableList.of("cluster-foo.googleapis.com", "cluster-bar.googleapis.com"),
- XdsClientImpl.ADS_TYPE_URL_EDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_EDS, "")));
assertThat(fakeClock.getPendingTasks(EDS_RESOURCE_FETCH_TIMEOUT_TASK_FILTER)).hasSize(2);
fakeClock.forwardTime(1, TimeUnit.SECONDS);
@@ -2511,7 +2518,8 @@ public void addRemoveEndpointWatcherWhileInitialResourceFetchInProgress() {
assertThat(fakeClock.getPendingTasks(EDS_RESOURCE_FETCH_TIMEOUT_TASK_FILTER)).isEmpty();
assertThat(timeoutTask.isCancelled()).isTrue();
- verifyZeroInteractions(watcher3, watcher4);
+ // TODO(chengyuanzhang): migrate to verifyNoInteractions.
+ verifyNoMoreInteractions(watcher3, watcher4);
}
@Test
@@ -2589,7 +2597,7 @@ public void streamClosedAndRetryWhenResolvingConfig() {
// Client sends an LDS request for the host name (with port) to management server.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", TARGET_AUTHORITY,
- XdsClientImpl.ADS_TYPE_URL_LDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_LDS, "")));
// Management server closes the RPC stream immediately.
responseObserver.onCompleted();
@@ -2609,7 +2617,7 @@ public void streamClosedAndRetryWhenResolvingConfig() {
// Client retried by sending an LDS request.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", TARGET_AUTHORITY,
- XdsClientImpl.ADS_TYPE_URL_LDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_LDS, "")));
// Management server closes the RPC stream with an error.
responseObserver.onError(Status.UNAVAILABLE.asException());
@@ -2629,7 +2637,7 @@ public void streamClosedAndRetryWhenResolvingConfig() {
// Client retried again by sending an LDS.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", TARGET_AUTHORITY,
- XdsClientImpl.ADS_TYPE_URL_LDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_LDS, "")));
// Management server responses with a listener for the requested resource.
Rds rdsConfig =
@@ -2650,12 +2658,12 @@ public void streamClosedAndRetryWhenResolvingConfig() {
// Client sent back an ACK LDS request.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "0", TARGET_AUTHORITY,
- XdsClientImpl.ADS_TYPE_URL_LDS_V2, "0000")));
+ XdsClientImpl.ADS_TYPE_URL_LDS, "0000")));
// Client sent an RDS request based on the received listener.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", "route-foo.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_RDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_RDS, "")));
// Management server encounters an error and closes the stream.
responseObserver.onError(Status.UNKNOWN.asException());
@@ -2669,7 +2677,7 @@ public void streamClosedAndRetryWhenResolvingConfig() {
requestObserver = requestObservers.poll();
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", TARGET_AUTHORITY,
- XdsClientImpl.ADS_TYPE_URL_LDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_LDS, "")));
// RPC stream closed immediately
responseObserver.onError(Status.UNKNOWN.asException());
@@ -2686,7 +2694,7 @@ public void streamClosedAndRetryWhenResolvingConfig() {
requestObserver = requestObservers.poll();
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", TARGET_AUTHORITY,
- XdsClientImpl.ADS_TYPE_URL_LDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_LDS, "")));
// Management server sends an LDS response.
responseObserver.onNext(ldsResponse);
@@ -2721,7 +2729,7 @@ public void streamClosedAndRetryWhenResolvingConfig() {
requestObserver = requestObservers.poll();
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", TARGET_AUTHORITY,
- XdsClientImpl.ADS_TYPE_URL_LDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_LDS, "")));
verifyNoMoreInteractions(backoffPolicyProvider, backoffPolicy1, backoffPolicy2);
}
@@ -2753,7 +2761,7 @@ public void streamClosedAndRetry() {
// Client sent first CDS request.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", "cluster.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_CDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_CDS, "")));
// Start watching endpoint information.
xdsClient.watchEndpointData("cluster.googleapis.com", endpointWatcher);
@@ -2761,7 +2769,7 @@ public void streamClosedAndRetry() {
// Client sent first EDS request.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", "cluster.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_EDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_EDS, "")));
// Management server closes the RPC stream with an error.
responseObserver.onError(Status.UNKNOWN.asException());
@@ -2783,13 +2791,13 @@ public void streamClosedAndRetry() {
// Retry resumes requests for all wanted resources.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", TARGET_AUTHORITY,
- XdsClientImpl.ADS_TYPE_URL_LDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_LDS, "")));
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", "cluster.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_CDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_CDS, "")));
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", "cluster.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_EDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_EDS, "")));
// Management server becomes unreachable.
responseObserver.onError(Status.UNAVAILABLE.asException());
@@ -2812,13 +2820,13 @@ public void streamClosedAndRetry() {
requestObserver = requestObservers.poll();
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", TARGET_AUTHORITY,
- XdsClientImpl.ADS_TYPE_URL_LDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_LDS, "")));
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", "cluster.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_CDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_CDS, "")));
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", "cluster.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_EDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_EDS, "")));
// Management server is still not reachable.
responseObserver.onError(Status.UNAVAILABLE.asException());
@@ -2841,13 +2849,13 @@ public void streamClosedAndRetry() {
requestObserver = requestObservers.poll();
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", TARGET_AUTHORITY,
- XdsClientImpl.ADS_TYPE_URL_LDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_LDS, "")));
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", "cluster.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_CDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_CDS, "")));
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", "cluster.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_EDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_EDS, "")));
// Management server sends back a CDS response.
List clusters = ImmutableList.of(
@@ -2874,13 +2882,13 @@ public void streamClosedAndRetry() {
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", TARGET_AUTHORITY,
- XdsClientImpl.ADS_TYPE_URL_LDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_LDS, "")));
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", "cluster.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_CDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_CDS, "")));
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", "cluster.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_EDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_EDS, "")));
// Management server becomes unreachable again.
responseObserver.onError(Status.UNAVAILABLE.asException());
@@ -2902,13 +2910,13 @@ public void streamClosedAndRetry() {
requestObserver = requestObservers.poll();
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", TARGET_AUTHORITY,
- XdsClientImpl.ADS_TYPE_URL_LDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_LDS, "")));
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", "cluster.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_CDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_CDS, "")));
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", "cluster.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_EDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_EDS, "")));
verifyNoMoreInteractions(mockedDiscoveryService, backoffPolicyProvider, backoffPolicy1,
backoffPolicy2);
@@ -2947,7 +2955,7 @@ public void streamClosedAndRetryRaceWithAddingAndRemovingWatchers() {
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", TARGET_AUTHORITY,
- XdsClientImpl.ADS_TYPE_URL_LDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_LDS, "")));
// Management server becomes unreachable.
responseObserver.onError(Status.UNAVAILABLE.asException());
@@ -2968,10 +2976,10 @@ public void streamClosedAndRetryRaceWithAddingAndRemovingWatchers() {
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", TARGET_AUTHORITY,
- XdsClientImpl.ADS_TYPE_URL_LDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_LDS, "")));
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", "cluster.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_CDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_CDS, "")));
// Management server is still unreachable.
responseObserver.onError(Status.UNAVAILABLE.asException());
@@ -2992,13 +3000,13 @@ public void streamClosedAndRetryRaceWithAddingAndRemovingWatchers() {
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", TARGET_AUTHORITY,
- XdsClientImpl.ADS_TYPE_URL_LDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_LDS, "")));
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", "cluster.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_CDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_CDS, "")));
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", "cluster.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_EDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_EDS, "")));
// Management server sends back a CDS response.
List clusters = ImmutableList.of(
@@ -3014,14 +3022,14 @@ public void streamClosedAndRetryRaceWithAddingAndRemovingWatchers() {
// Client updates EDS resource subscription immediately.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", ImmutableList.of(),
- XdsClientImpl.ADS_TYPE_URL_EDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_EDS, "")));
// Become interested in endpoints of another cluster.
xdsClient.watchEndpointData("cluster2.googleapis.com", endpointWatcher);
// Client updates EDS resource subscription immediately.
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", "cluster2.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_EDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_EDS, "")));
// Management server closes the RPC stream again.
responseObserver.onCompleted();
@@ -3035,13 +3043,13 @@ public void streamClosedAndRetryRaceWithAddingAndRemovingWatchers() {
requestObserver = requestObservers.poll();
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", TARGET_AUTHORITY,
- XdsClientImpl.ADS_TYPE_URL_LDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_LDS, "")));
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", "cluster.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_CDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_CDS, "")));
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", "cluster2.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_EDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_EDS, "")));
// Management server becomes unreachable again.
responseObserver.onError(Status.UNAVAILABLE.asException());
@@ -3062,13 +3070,13 @@ public void streamClosedAndRetryRaceWithAddingAndRemovingWatchers() {
verify(requestObserver)
.onNext(eq(buildDiscoveryRequest(NODE, "", TARGET_AUTHORITY,
- XdsClientImpl.ADS_TYPE_URL_LDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_LDS, "")));
verify(requestObserver, never())
.onNext(eq(buildDiscoveryRequest(NODE, "", "cluster.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_CDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_CDS, "")));
verify(requestObserver, never())
.onNext(eq(buildDiscoveryRequest(NODE, "", "cluster2.googleapis.com",
- XdsClientImpl.ADS_TYPE_URL_EDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_EDS, "")));
verifyNoMoreInteractions(mockedDiscoveryService, backoffPolicyProvider, backoffPolicy1,
backoffPolicy2);
@@ -3134,7 +3142,7 @@ public void streamClosedAndRetryReschedulesAllResourceFetchTimer() {
// Client resumed requests and management server sends back LDS resources again.
verify(requestObserver).onNext(
eq(buildDiscoveryRequest(NODE, "", TARGET_AUTHORITY,
- XdsClientImpl.ADS_TYPE_URL_LDS_V2, "")));
+ XdsClientImpl.ADS_TYPE_URL_LDS, "")));
responseObserver.onNext(response);
// Client sent an RDS request for resource "route-foo.googleapis.com" (Omitted).
@@ -3229,9 +3237,9 @@ public void streamClosedAndRetryReschedulesAllResourceFetchTimer() {
@Test
public void reportLoadStatsToServer() {
String clusterName = "cluster-foo.googleapis.com";
- LoadStatsStore loadStatsStore = new LoadStatsStoreImpl(clusterName, null);
+ xdsClient.addClientStats(clusterName, null);
ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(null);
- xdsClient.reportClientStats(clusterName, null, loadStatsStore);
+ xdsClient.reportClientStats();
LoadReportCall lrsCall = loadReportCalls.poll();
verify(lrsCall.requestObserver).onNext(requestCaptor.capture());
assertThat(requestCaptor.getValue().getClusterStatsCount())
@@ -3247,12 +3255,14 @@ public void reportLoadStatsToServer() {
ClusterStats report = Iterables.getOnlyElement(requestCaptor.getValue().getClusterStatsList());
assertThat(report.getClusterName()).isEqualTo(clusterName);
- xdsClient.cancelClientStatsReport(clusterName, null);
+ xdsClient.removeClientStats(clusterName, null);
fakeClock.forwardNanos(1000L);
verify(lrsCall.requestObserver, times(3)).onNext(requestCaptor.capture());
assertThat(requestCaptor.getValue().getClusterStatsCount())
.isEqualTo(0); // no more stats reported
+ xdsClient.cancelClientStatsReport();
+ assertThat(lrsEnded.get()).isTrue();
// See more test on LoadReportClientTest.java
}
@@ -3732,7 +3742,7 @@ public boolean matches(DiscoveryRequest argument) {
if (!resourceNames.equals(new HashSet<>(argument.getResourceNamesList()))) {
return false;
}
- return NODE.equals(argument.getNode());
+ return argument.getNode().equals(NODE.toEnvoyProtoNode());
}
}
diff --git a/xds/src/test/java/io/grpc/xds/XdsClientImplTestForListener.java b/xds/src/test/java/io/grpc/xds/XdsClientImplTestForListener.java
index fed260b7b11..6d1aeedaeb8 100644
--- a/xds/src/test/java/io/grpc/xds/XdsClientImplTestForListener.java
+++ b/xds/src/test/java/io/grpc/xds/XdsClientImplTestForListener.java
@@ -17,7 +17,7 @@
package io.grpc.xds;
import static com.google.common.truth.Truth.assertThat;
-import static io.grpc.xds.XdsClientTestHelper.buildDiscoveryResponse;
+import static io.grpc.xds.XdsClientTestHelper.buildDiscoveryResponseV2;
import static io.grpc.xds.XdsClientTestHelper.buildListener;
import static io.grpc.xds.XdsClientTestHelper.buildRouteConfiguration;
import static io.grpc.xds.XdsClientTestHelper.buildVirtualHost;
@@ -37,16 +37,12 @@
import com.google.common.util.concurrent.MoreExecutors;
import com.google.protobuf.Any;
import com.google.protobuf.InvalidProtocolBufferException;
-import com.google.protobuf.Struct;
import com.google.protobuf.UInt32Value;
-import com.google.protobuf.Value;
import io.envoyproxy.envoy.api.v2.DiscoveryRequest;
import io.envoyproxy.envoy.api.v2.DiscoveryResponse;
import io.envoyproxy.envoy.api.v2.Listener;
import io.envoyproxy.envoy.api.v2.auth.DownstreamTlsContext;
-import io.envoyproxy.envoy.api.v2.core.Address;
import io.envoyproxy.envoy.api.v2.core.CidrRange;
-import io.envoyproxy.envoy.api.v2.core.Node;
import io.envoyproxy.envoy.api.v2.core.SocketAddress;
import io.envoyproxy.envoy.api.v2.core.TransportSocket;
import io.envoyproxy.envoy.api.v2.listener.Filter;
@@ -70,19 +66,23 @@
import io.grpc.testing.GrpcCleanupRule;
import io.grpc.xds.Bootstrapper.ChannelCreds;
import io.grpc.xds.Bootstrapper.ServerInfo;
+import io.grpc.xds.EnvoyProtoData.Address;
+import io.grpc.xds.EnvoyProtoData.Node;
import io.grpc.xds.XdsClient.ConfigWatcher;
import io.grpc.xds.XdsClient.ListenerUpdate;
import io.grpc.xds.XdsClient.ListenerWatcher;
+import io.grpc.xds.XdsClient.XdsChannel;
import io.grpc.xds.XdsClient.XdsChannelFactory;
import io.grpc.xds.internal.sds.CommonTlsContextTestsUtil;
import java.io.IOException;
import java.util.ArrayDeque;
import java.util.Arrays;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Queue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
-
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
@@ -109,7 +109,7 @@ public class XdsClientImplTestForListener {
"type.googleapis.com/"
+ "envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager";
- private static final Node NODE = Node.getDefaultInstance();
+ private static final Node NODE = Node.newBuilder().build();
private static final FakeClock.TaskFilter RPC_RETRY_TASK_FILTER =
new FakeClock.TaskFilter() {
@Override
@@ -201,13 +201,14 @@ public void cancelled(Context context) {
cleanupRule.register(InProcessChannelBuilder.forName(serverName).directExecutor().build());
List servers =
- ImmutableList.of(new ServerInfo(serverName, ImmutableList.of()));
+ ImmutableList.of(new ServerInfo(serverName, ImmutableList.of(), null));
XdsChannelFactory channelFactory = new XdsChannelFactory() {
@Override
- ManagedChannel createChannel(List servers) {
- assertThat(Iterables.getOnlyElement(servers).getServerUri()).isEqualTo(serverName);
- assertThat(Iterables.getOnlyElement(servers).getChannelCredentials()).isEmpty();
- return channel;
+ XdsChannel createChannel(List