mirror of https://github.com/grpc/grpc-java.git
xds: Use XdsDependencyManager for XdsNameResolver
Contributes to the gRFC A74 effort. https://github.com/grpc/proposal/blob/master/A74-xds-config-tears.md The alternative to using Mockito's ArgumentMatcher is to use Hamcrest. However, Hamcrest did not impress me. ArgumentMatcher is trivial if you don't care about the error message. This fixes a pre-existing issue where ConfigSelector.releaseCluster could revert the LB config back to using cluster manager after releasing all RPCs using a cluster have committed. Co-authored-by: Larry Safran <lsafran@google.com>
This commit is contained in:
parent
e388ef3975
commit
e80c197455
|
|
@ -0,0 +1,118 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2025 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;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
import static com.google.common.base.Preconditions.checkState;
|
||||||
|
|
||||||
|
import org.mockito.ArgumentMatcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mockito matcher for {@link Status}.
|
||||||
|
*/
|
||||||
|
public final class StatusMatcher implements ArgumentMatcher<Status> {
|
||||||
|
public static StatusMatcher statusHasCode(ArgumentMatcher<Status.Code> codeMatcher) {
|
||||||
|
return new StatusMatcher(codeMatcher, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static StatusMatcher statusHasCode(Status.Code code) {
|
||||||
|
return statusHasCode(new EqualsMatcher<>(code));
|
||||||
|
}
|
||||||
|
|
||||||
|
private final ArgumentMatcher<Status.Code> codeMatcher;
|
||||||
|
private final ArgumentMatcher<String> descriptionMatcher;
|
||||||
|
|
||||||
|
private StatusMatcher(
|
||||||
|
ArgumentMatcher<Status.Code> codeMatcher,
|
||||||
|
ArgumentMatcher<String> descriptionMatcher) {
|
||||||
|
this.codeMatcher = checkNotNull(codeMatcher, "codeMatcher");
|
||||||
|
this.descriptionMatcher = descriptionMatcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StatusMatcher andDescription(ArgumentMatcher<String> descriptionMatcher) {
|
||||||
|
checkState(this.descriptionMatcher == null, "Already has a description matcher");
|
||||||
|
return new StatusMatcher(codeMatcher, descriptionMatcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
public StatusMatcher andDescription(String description) {
|
||||||
|
return andDescription(new EqualsMatcher<>(description));
|
||||||
|
}
|
||||||
|
|
||||||
|
public StatusMatcher andDescriptionContains(String substring) {
|
||||||
|
return andDescription(new StringContainsMatcher(substring));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(Status status) {
|
||||||
|
return status != null
|
||||||
|
&& codeMatcher.matches(status.getCode())
|
||||||
|
&& (descriptionMatcher == null || descriptionMatcher.matches(status.getDescription()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("{code=");
|
||||||
|
sb.append(codeMatcher);
|
||||||
|
if (descriptionMatcher != null) {
|
||||||
|
sb.append(", description=");
|
||||||
|
sb.append(descriptionMatcher);
|
||||||
|
}
|
||||||
|
sb.append("}");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use instead of lambda for better error message.
|
||||||
|
static final class EqualsMatcher<T> implements ArgumentMatcher<T> {
|
||||||
|
private final T obj;
|
||||||
|
|
||||||
|
EqualsMatcher(T obj) {
|
||||||
|
this.obj = checkNotNull(obj, "obj");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(Object other) {
|
||||||
|
return obj.equals(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return obj.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static final class StringContainsMatcher implements ArgumentMatcher<String> {
|
||||||
|
private final String needle;
|
||||||
|
|
||||||
|
StringContainsMatcher(String needle) {
|
||||||
|
this.needle = checkNotNull(needle, "needle");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(String haystack) {
|
||||||
|
if (haystack == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return haystack.contains(needle);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "contains " + needle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2025 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;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
import org.mockito.ArgumentMatcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mockito matcher for {@link StatusOr}.
|
||||||
|
*/
|
||||||
|
public final class StatusOrMatcher<T> implements ArgumentMatcher<StatusOr<T>> {
|
||||||
|
public static <T> StatusOrMatcher<T> hasValue(ArgumentMatcher<T> valueMatcher) {
|
||||||
|
return new StatusOrMatcher<T>(checkNotNull(valueMatcher, "valueMatcher"), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> StatusOrMatcher<T> hasStatus(ArgumentMatcher<Status> statusMatcher) {
|
||||||
|
return new StatusOrMatcher<T>(null, checkNotNull(statusMatcher, "statusMatcher"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private final ArgumentMatcher<T> valueMatcher;
|
||||||
|
private final ArgumentMatcher<Status> statusMatcher;
|
||||||
|
|
||||||
|
private StatusOrMatcher(ArgumentMatcher<T> valueMatcher, ArgumentMatcher<Status> statusMatcher) {
|
||||||
|
this.valueMatcher = valueMatcher;
|
||||||
|
this.statusMatcher = statusMatcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(StatusOr<T> statusOr) {
|
||||||
|
if (statusOr == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (statusOr.hasValue() != (valueMatcher != null)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (valueMatcher != null) {
|
||||||
|
return valueMatcher.matches(statusOr.getValue());
|
||||||
|
} else {
|
||||||
|
return statusMatcher.matches(statusOr.getStatus());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
if (valueMatcher != null) {
|
||||||
|
return "{value=" + valueMatcher + "}";
|
||||||
|
} else {
|
||||||
|
return "{status=" + statusMatcher + "}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -36,6 +36,22 @@ final class XdsAttributes {
|
||||||
static final Attributes.Key<ObjectPool<XdsClient>> XDS_CLIENT_POOL =
|
static final Attributes.Key<ObjectPool<XdsClient>> XDS_CLIENT_POOL =
|
||||||
Attributes.Key.create("io.grpc.xds.XdsAttributes.xdsClientPool");
|
Attributes.Key.create("io.grpc.xds.XdsAttributes.xdsClientPool");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attribute key for passing around the latest XdsConfig across NameResolver/LoadBalancers.
|
||||||
|
*/
|
||||||
|
@NameResolver.ResolutionResultAttr
|
||||||
|
static final Attributes.Key<XdsConfig> XDS_CONFIG =
|
||||||
|
Attributes.Key.create("io.grpc.xds.XdsAttributes.xdsConfig");
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attribute key for passing around the XdsDependencyManager across NameResolver/LoadBalancers.
|
||||||
|
*/
|
||||||
|
@NameResolver.ResolutionResultAttr
|
||||||
|
static final Attributes.Key<XdsConfig.XdsClusterSubscriptionRegistry>
|
||||||
|
XDS_CLUSTER_SUBSCRIPT_REGISTRY =
|
||||||
|
Attributes.Key.create("io.grpc.xds.XdsAttributes.xdsConfig.XdsClusterSubscriptionRegistry");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attribute key for obtaining the global provider that provides atomics for aggregating
|
* Attribute key for obtaining the global provider that provides atomics for aggregating
|
||||||
* outstanding RPCs sent to each cluster.
|
* outstanding RPCs sent to each cluster.
|
||||||
|
|
|
||||||
|
|
@ -26,9 +26,9 @@ import io.grpc.xds.XdsListenerResource.LdsUpdate;
|
||||||
import io.grpc.xds.XdsRouteConfigureResource.RdsUpdate;
|
import io.grpc.xds.XdsRouteConfigureResource.RdsUpdate;
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the xDS configuration tree for a specified Listener.
|
* Represents the xDS configuration tree for a specified Listener.
|
||||||
|
|
@ -178,13 +178,22 @@ final class XdsConfig {
|
||||||
public StatusOr<EdsUpdate> getEndpoint() {
|
public StatusOr<EdsUpdate> getEndpoint() {
|
||||||
return endpoint;
|
return endpoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
if (endpoint.hasValue()) {
|
||||||
|
return "EndpointConfig{endpoint=" + endpoint.getValue() + "}";
|
||||||
|
} else {
|
||||||
|
return "EndpointConfig{error=" + endpoint.getStatus() + "}";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The list of leaf clusters for an aggregate cluster.
|
// The list of leaf clusters for an aggregate cluster.
|
||||||
static final class AggregateConfig implements ClusterChild {
|
static final class AggregateConfig implements ClusterChild {
|
||||||
private final List<String> leafNames;
|
private final Set<String> leafNames;
|
||||||
|
|
||||||
public AggregateConfig(List<String> leafNames) {
|
public AggregateConfig(Set<String> leafNames) {
|
||||||
this.leafNames = checkNotNull(leafNames, "leafNames");
|
this.leafNames = checkNotNull(leafNames, "leafNames");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -234,6 +243,7 @@ final class XdsConfig {
|
||||||
XdsConfig build() {
|
XdsConfig build() {
|
||||||
checkNotNull(listener, "listener");
|
checkNotNull(listener, "listener");
|
||||||
checkNotNull(route, "route");
|
checkNotNull(route, "route");
|
||||||
|
checkNotNull(virtualHost, "virtualHost");
|
||||||
return new XdsConfig(listener, route, clusters, virtualHost);
|
return new XdsConfig(listener, route, clusters, virtualHost);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,9 +22,9 @@ import static io.grpc.xds.client.XdsClient.ResourceUpdate;
|
||||||
import static io.grpc.xds.client.XdsLogger.XdsLogLevel.DEBUG;
|
import static io.grpc.xds.client.XdsLogger.XdsLogLevel.DEBUG;
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
import io.grpc.InternalLogId;
|
import io.grpc.InternalLogId;
|
||||||
|
import io.grpc.NameResolver;
|
||||||
import io.grpc.Status;
|
import io.grpc.Status;
|
||||||
import io.grpc.StatusOr;
|
import io.grpc.StatusOr;
|
||||||
import io.grpc.SynchronizationContext;
|
import io.grpc.SynchronizationContext;
|
||||||
|
|
@ -39,7 +39,6 @@ import io.grpc.xds.client.XdsLogger;
|
||||||
import io.grpc.xds.client.XdsResourceType;
|
import io.grpc.xds.client.XdsResourceType;
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
|
@ -47,6 +46,7 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
|
@ -67,25 +67,28 @@ final class XdsDependencyManager implements XdsConfig.XdsClusterSubscriptionRegi
|
||||||
|
|
||||||
private final InternalLogId logId;
|
private final InternalLogId logId;
|
||||||
private final XdsLogger logger;
|
private final XdsLogger logger;
|
||||||
private XdsConfig lastXdsConfig = null;
|
private StatusOr<XdsConfig> lastUpdate = null;
|
||||||
private final Map<XdsResourceType<?>, TypeWatchers<?>> resourceWatchers = new HashMap<>();
|
private final Map<XdsResourceType<?>, TypeWatchers<?>> resourceWatchers = new HashMap<>();
|
||||||
|
|
||||||
XdsDependencyManager(XdsClient xdsClient, XdsConfigWatcher xdsConfigWatcher,
|
XdsDependencyManager(XdsClient xdsClient, XdsConfigWatcher xdsConfigWatcher,
|
||||||
SynchronizationContext syncContext, String dataPlaneAuthority,
|
SynchronizationContext syncContext, String dataPlaneAuthority,
|
||||||
String listenerName) {
|
String listenerName, NameResolver.Args nameResolverArgs,
|
||||||
|
ScheduledExecutorService scheduler) {
|
||||||
logId = InternalLogId.allocate("xds-dependency-manager", listenerName);
|
logId = InternalLogId.allocate("xds-dependency-manager", listenerName);
|
||||||
logger = XdsLogger.withLogId(logId);
|
logger = XdsLogger.withLogId(logId);
|
||||||
this.xdsClient = checkNotNull(xdsClient, "xdsClient");
|
this.xdsClient = checkNotNull(xdsClient, "xdsClient");
|
||||||
this.xdsConfigWatcher = checkNotNull(xdsConfigWatcher, "xdsConfigWatcher");
|
this.xdsConfigWatcher = checkNotNull(xdsConfigWatcher, "xdsConfigWatcher");
|
||||||
this.syncContext = checkNotNull(syncContext, "syncContext");
|
this.syncContext = checkNotNull(syncContext, "syncContext");
|
||||||
this.dataPlaneAuthority = checkNotNull(dataPlaneAuthority, "dataPlaneAuthority");
|
this.dataPlaneAuthority = checkNotNull(dataPlaneAuthority, "dataPlaneAuthority");
|
||||||
|
checkNotNull(nameResolverArgs, "nameResolverArgs");
|
||||||
|
checkNotNull(scheduler, "scheduler");
|
||||||
|
|
||||||
// start the ball rolling
|
// start the ball rolling
|
||||||
syncContext.execute(() -> addWatcher(new LdsWatcher(listenerName)));
|
syncContext.execute(() -> addWatcher(new LdsWatcher(listenerName)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String toContextStr(String typeName, String resourceName) {
|
public static String toContextStr(String typeName, String resourceName) {
|
||||||
return typeName + " resource: " + resourceName;
|
return typeName + " resource " + resourceName;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -225,7 +228,7 @@ final class XdsDependencyManager implements XdsConfig.XdsClusterSubscriptionRegi
|
||||||
XdsClusterResource.CdsUpdate cdsUpdate = root.getData().getValue();
|
XdsClusterResource.CdsUpdate cdsUpdate = root.getData().getValue();
|
||||||
switch (cdsUpdate.clusterType()) {
|
switch (cdsUpdate.clusterType()) {
|
||||||
case EDS:
|
case EDS:
|
||||||
String edsServiceName = cdsUpdate.edsServiceName();
|
String edsServiceName = root.getEdsServiceName();
|
||||||
EdsWatcher edsWatcher =
|
EdsWatcher edsWatcher =
|
||||||
(EdsWatcher) resourceWatchers.get(ENDPOINT_RESOURCE).watchers.get(edsServiceName);
|
(EdsWatcher) resourceWatchers.get(ENDPOINT_RESOURCE).watchers.get(edsServiceName);
|
||||||
cancelEdsWatcher(edsWatcher, root);
|
cancelEdsWatcher(edsWatcher, root);
|
||||||
|
|
@ -240,7 +243,7 @@ final class XdsDependencyManager implements XdsConfig.XdsClusterSubscriptionRegi
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case LOGICAL_DNS:
|
case LOGICAL_DNS:
|
||||||
// no eds needed
|
// no eds needed, so everything happens in cancelCdsWatcher()
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new AssertionError("Unknown cluster type: " + cdsUpdate.clusterType());
|
throw new AssertionError("Unknown cluster type: " + cdsUpdate.clusterType());
|
||||||
|
|
@ -260,54 +263,61 @@ final class XdsDependencyManager implements XdsConfig.XdsClusterSubscriptionRegi
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
XdsConfig newConfig = buildConfig();
|
StatusOr<XdsConfig> newUpdate = buildUpdate();
|
||||||
if (Objects.equals(newConfig, lastXdsConfig)) {
|
if (Objects.equals(newUpdate, lastUpdate)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
lastXdsConfig = newConfig;
|
assert newUpdate.hasValue()
|
||||||
xdsConfigWatcher.onUpdate(lastXdsConfig);
|
|| (newUpdate.getStatus().getCode() == Status.Code.UNAVAILABLE
|
||||||
|
|| newUpdate.getStatus().getCode() == Status.Code.INTERNAL);
|
||||||
|
lastUpdate = newUpdate;
|
||||||
|
xdsConfigWatcher.onUpdate(lastUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
XdsConfig buildConfig() {
|
StatusOr<XdsConfig> buildUpdate() {
|
||||||
XdsConfig.XdsConfigBuilder builder = new XdsConfig.XdsConfigBuilder();
|
XdsConfig.XdsConfigBuilder builder = new XdsConfig.XdsConfigBuilder();
|
||||||
|
|
||||||
// Iterate watchers and build the XdsConfig
|
// Iterate watchers and build the XdsConfig
|
||||||
|
|
||||||
// Will only be 1 listener and 1 route resource
|
// Will only be 1 listener and 1 route resource
|
||||||
VirtualHost activeVirtualHost = getActiveVirtualHost();
|
RdsUpdateSupplier routeSource = null;
|
||||||
for (XdsWatcherBase<?> xdsWatcherBase :
|
for (XdsWatcherBase<XdsListenerResource.LdsUpdate> ldsWatcher :
|
||||||
resourceWatchers.get(XdsListenerResource.getInstance()).watchers.values()) {
|
getWatchers(XdsListenerResource.getInstance()).values()) {
|
||||||
XdsListenerResource.LdsUpdate ldsUpdate = ((LdsWatcher) xdsWatcherBase).getData().getValue();
|
if (!ldsWatcher.getData().hasValue()) {
|
||||||
|
return StatusOr.fromStatus(ldsWatcher.getData().getStatus());
|
||||||
|
}
|
||||||
|
XdsListenerResource.LdsUpdate ldsUpdate = ldsWatcher.getData().getValue();
|
||||||
builder.setListener(ldsUpdate);
|
builder.setListener(ldsUpdate);
|
||||||
if (activeVirtualHost == null) {
|
routeSource = ((LdsWatcher) ldsWatcher).getRouteSource();
|
||||||
activeVirtualHost = RoutingUtils.findVirtualHostForHostName(
|
|
||||||
ldsUpdate.httpConnectionManager().virtualHosts(), dataPlaneAuthority);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ldsUpdate.httpConnectionManager() != null
|
|
||||||
&& ldsUpdate.httpConnectionManager().virtualHosts() != null) {
|
|
||||||
RdsUpdate rdsUpdate = new RdsUpdate(ldsUpdate.httpConnectionManager().virtualHosts());
|
|
||||||
builder.setRoute(rdsUpdate);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resourceWatchers.get(XdsRouteConfigureResource.getInstance()).watchers.values().stream()
|
StatusOr<RdsUpdate> statusOrRdsUpdate = routeSource.getRdsUpdate();
|
||||||
.map(watcher -> (RdsWatcher) watcher)
|
if (!statusOrRdsUpdate.hasValue()) {
|
||||||
.forEach(watcher -> builder.setRoute(watcher.getData().getValue()));
|
return StatusOr.fromStatus(statusOrRdsUpdate.getStatus());
|
||||||
|
}
|
||||||
|
RdsUpdate rdsUpdate = statusOrRdsUpdate.getValue();
|
||||||
|
builder.setRoute(rdsUpdate);
|
||||||
|
|
||||||
|
VirtualHost activeVirtualHost =
|
||||||
|
RoutingUtils.findVirtualHostForHostName(rdsUpdate.virtualHosts, dataPlaneAuthority);
|
||||||
|
if (activeVirtualHost == null) {
|
||||||
|
String error = "Failed to find virtual host matching hostname: " + dataPlaneAuthority;
|
||||||
|
return StatusOr.fromStatus(Status.UNAVAILABLE.withDescription(error));
|
||||||
|
}
|
||||||
builder.setVirtualHost(activeVirtualHost);
|
builder.setVirtualHost(activeVirtualHost);
|
||||||
|
|
||||||
Map<String, ? extends XdsWatcherBase<?>> edsWatchers =
|
Map<String, XdsWatcherBase<XdsEndpointResource.EdsUpdate>> edsWatchers =
|
||||||
resourceWatchers.get(ENDPOINT_RESOURCE).watchers;
|
getWatchers(ENDPOINT_RESOURCE);
|
||||||
Map<String, ? extends XdsWatcherBase<?>> cdsWatchers =
|
Map<String, XdsWatcherBase<XdsClusterResource.CdsUpdate>> cdsWatchers =
|
||||||
resourceWatchers.get(CLUSTER_RESOURCE).watchers;
|
getWatchers(CLUSTER_RESOURCE);
|
||||||
|
|
||||||
// Only care about aggregates from LDS/RDS or subscriptions and the leaf clusters
|
// Only care about aggregates from LDS/RDS or subscriptions and the leaf clusters
|
||||||
List<String> topLevelClusters =
|
List<String> topLevelClusters =
|
||||||
cdsWatchers.values().stream()
|
cdsWatchers.values().stream()
|
||||||
.filter(XdsDependencyManager::isTopLevelCluster)
|
.filter(XdsDependencyManager::isTopLevelCluster)
|
||||||
.map(w -> w.resourceName())
|
.map(XdsWatcherBase<?>::resourceName)
|
||||||
|
.distinct()
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
// Flatten multi-level aggregates into lists of leaf clusters
|
// Flatten multi-level aggregates into lists of leaf clusters
|
||||||
|
|
@ -316,43 +326,60 @@ final class XdsDependencyManager implements XdsConfig.XdsClusterSubscriptionRegi
|
||||||
|
|
||||||
addLeavesToBuilder(builder, edsWatchers, leafNames);
|
addLeavesToBuilder(builder, edsWatchers, leafNames);
|
||||||
|
|
||||||
return builder.build();
|
return StatusOr.fromValue(builder.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addLeavesToBuilder(XdsConfig.XdsConfigBuilder builder,
|
private <T extends ResourceUpdate> Map<String, XdsWatcherBase<T>> getWatchers(
|
||||||
Map<String, ? extends XdsWatcherBase<?>> edsWatchers,
|
XdsResourceType<T> resourceType) {
|
||||||
Set<String> leafNames) {
|
TypeWatchers<?> typeWatchers = resourceWatchers.get(resourceType);
|
||||||
|
if (typeWatchers == null) {
|
||||||
|
return Collections.emptyMap();
|
||||||
|
}
|
||||||
|
assert typeWatchers.resourceType == resourceType;
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
TypeWatchers<T> tTypeWatchers = (TypeWatchers<T>) typeWatchers;
|
||||||
|
return tTypeWatchers.watchers;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addLeavesToBuilder(
|
||||||
|
XdsConfig.XdsConfigBuilder builder,
|
||||||
|
Map<String, XdsWatcherBase<XdsEndpointResource.EdsUpdate>> edsWatchers,
|
||||||
|
Set<String> leafNames) {
|
||||||
for (String clusterName : leafNames) {
|
for (String clusterName : leafNames) {
|
||||||
CdsWatcher cdsWatcher = getCluster(clusterName);
|
CdsWatcher cdsWatcher = getCluster(clusterName);
|
||||||
StatusOr<XdsClusterResource.CdsUpdate> cdsUpdateOr = cdsWatcher.getData();
|
StatusOr<XdsClusterResource.CdsUpdate> cdsUpdateOr = cdsWatcher.getData();
|
||||||
|
|
||||||
if (cdsUpdateOr.hasValue()) {
|
if (!cdsUpdateOr.hasValue()) {
|
||||||
XdsClusterResource.CdsUpdate cdsUpdate = cdsUpdateOr.getValue();
|
|
||||||
if (cdsUpdate.clusterType() == ClusterType.EDS) {
|
|
||||||
EdsWatcher edsWatcher = (EdsWatcher) edsWatchers.get(cdsUpdate.edsServiceName());
|
|
||||||
if (edsWatcher != null) {
|
|
||||||
EndpointConfig child = new EndpointConfig(edsWatcher.getData());
|
|
||||||
builder.addCluster(clusterName, StatusOr.fromValue(
|
|
||||||
new XdsConfig.XdsClusterConfig(clusterName, cdsUpdate, child)));
|
|
||||||
} else {
|
|
||||||
builder.addCluster(clusterName, StatusOr.fromStatus(Status.UNAVAILABLE.withDescription(
|
|
||||||
"EDS resource not found for cluster " + clusterName)));
|
|
||||||
}
|
|
||||||
} else if (cdsUpdate.clusterType() == ClusterType.LOGICAL_DNS) {
|
|
||||||
// TODO get the resolved endpoint configuration
|
|
||||||
builder.addCluster(clusterName, StatusOr.fromValue(
|
|
||||||
new XdsConfig.XdsClusterConfig(clusterName, cdsUpdate, new EndpointConfig(null))));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
builder.addCluster(clusterName, StatusOr.fromStatus(cdsUpdateOr.getStatus()));
|
builder.addCluster(clusterName, StatusOr.fromStatus(cdsUpdateOr.getStatus()));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
XdsClusterResource.CdsUpdate cdsUpdate = cdsUpdateOr.getValue();
|
||||||
|
if (cdsUpdate.clusterType() == ClusterType.EDS) {
|
||||||
|
XdsWatcherBase<XdsEndpointResource.EdsUpdate> edsWatcher =
|
||||||
|
edsWatchers.get(cdsWatcher.getEdsServiceName());
|
||||||
|
EndpointConfig child;
|
||||||
|
if (edsWatcher != null) {
|
||||||
|
child = new EndpointConfig(edsWatcher.getData());
|
||||||
|
} else {
|
||||||
|
child = new EndpointConfig(StatusOr.fromStatus(Status.INTERNAL.withDescription(
|
||||||
|
"EDS resource not found for cluster " + clusterName)));
|
||||||
|
}
|
||||||
|
builder.addCluster(clusterName, StatusOr.fromValue(
|
||||||
|
new XdsConfig.XdsClusterConfig(clusterName, cdsUpdate, child)));
|
||||||
|
} else if (cdsUpdate.clusterType() == ClusterType.LOGICAL_DNS) {
|
||||||
|
builder.addCluster(clusterName, StatusOr.fromStatus(
|
||||||
|
Status.INTERNAL.withDescription("Logical DNS in dependency manager unsupported")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds the top-level clusters to the builder and returns the leaf cluster names
|
// Adds the top-level clusters to the builder and returns the leaf cluster names
|
||||||
private Set<String> addTopLevelClustersToBuilder(
|
private Set<String> addTopLevelClustersToBuilder(
|
||||||
XdsConfig.XdsConfigBuilder builder, Map<String, ? extends XdsWatcherBase<?>> edsWatchers,
|
XdsConfig.XdsConfigBuilder builder,
|
||||||
Map<String, ? extends XdsWatcherBase<?>> cdsWatchers, List<String> topLevelClusters) {
|
Map<String, XdsWatcherBase<XdsEndpointResource.EdsUpdate>> edsWatchers,
|
||||||
|
Map<String, XdsWatcherBase<XdsClusterResource.CdsUpdate>> cdsWatchers,
|
||||||
|
List<String> topLevelClusters) {
|
||||||
|
|
||||||
Set<String> leafClusterNames = new HashSet<>();
|
Set<String> leafClusterNames = new HashSet<>();
|
||||||
for (String clusterName : topLevelClusters) {
|
for (String clusterName : topLevelClusters) {
|
||||||
|
|
@ -367,23 +394,25 @@ final class XdsDependencyManager implements XdsConfig.XdsClusterSubscriptionRegi
|
||||||
XdsConfig.XdsClusterConfig.ClusterChild child;
|
XdsConfig.XdsClusterConfig.ClusterChild child;
|
||||||
switch (cdsUpdate.clusterType()) {
|
switch (cdsUpdate.clusterType()) {
|
||||||
case AGGREGATE:
|
case AGGREGATE:
|
||||||
List<String> leafNames = getLeafNames(cdsUpdate);
|
Set<String> leafNames = new HashSet<>();
|
||||||
|
addLeafNames(leafNames, cdsUpdate);
|
||||||
child = new AggregateConfig(leafNames);
|
child = new AggregateConfig(leafNames);
|
||||||
leafClusterNames.addAll(leafNames);
|
leafClusterNames.addAll(leafNames);
|
||||||
break;
|
break;
|
||||||
case EDS:
|
case EDS:
|
||||||
EdsWatcher edsWatcher = (EdsWatcher) edsWatchers.get(cdsUpdate.edsServiceName());
|
XdsWatcherBase<XdsEndpointResource.EdsUpdate> edsWatcher =
|
||||||
|
edsWatchers.get(cdsWatcher.getEdsServiceName());
|
||||||
if (edsWatcher != null) {
|
if (edsWatcher != null) {
|
||||||
child = new EndpointConfig(edsWatcher.getData());
|
child = new EndpointConfig(edsWatcher.getData());
|
||||||
} else {
|
} else {
|
||||||
builder.addCluster(clusterName, StatusOr.fromStatus(Status.UNAVAILABLE.withDescription(
|
child = new EndpointConfig(StatusOr.fromStatus(Status.INTERNAL.withDescription(
|
||||||
"EDS resource not found for cluster " + clusterName)));
|
"EDS resource not found for cluster " + clusterName)));
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case LOGICAL_DNS:
|
case LOGICAL_DNS:
|
||||||
// TODO get the resolved endpoint configuration
|
// TODO get the resolved endpoint configuration
|
||||||
child = new EndpointConfig(null);
|
child = new EndpointConfig(StatusOr.fromStatus(
|
||||||
|
Status.INTERNAL.withDescription("Logical DNS in dependency manager unsupported")));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new IllegalStateException("Unexpected value: " + cdsUpdate.clusterType());
|
throw new IllegalStateException("Unexpected value: " + cdsUpdate.clusterType());
|
||||||
|
|
@ -395,29 +424,26 @@ final class XdsDependencyManager implements XdsConfig.XdsClusterSubscriptionRegi
|
||||||
return leafClusterNames;
|
return leafClusterNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<String> getLeafNames(XdsClusterResource.CdsUpdate cdsUpdate) {
|
private void addLeafNames(Set<String> leafNames, XdsClusterResource.CdsUpdate cdsUpdate) {
|
||||||
List<String> childNames = new ArrayList<>();
|
|
||||||
|
|
||||||
for (String cluster : cdsUpdate.prioritizedClusterNames()) {
|
for (String cluster : cdsUpdate.prioritizedClusterNames()) {
|
||||||
|
if (leafNames.contains(cluster)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
StatusOr<XdsClusterResource.CdsUpdate> data = getCluster(cluster).getData();
|
StatusOr<XdsClusterResource.CdsUpdate> data = getCluster(cluster).getData();
|
||||||
if (data == null || !data.hasValue() || data.getValue() == null) {
|
if (data == null || !data.hasValue() || data.getValue() == null) {
|
||||||
childNames.add(cluster);
|
leafNames.add(cluster);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (data.getValue().clusterType() == ClusterType.AGGREGATE) {
|
if (data.getValue().clusterType() == ClusterType.AGGREGATE) {
|
||||||
childNames.addAll(getLeafNames(data.getValue()));
|
addLeafNames(leafNames, data.getValue());
|
||||||
} else {
|
} else {
|
||||||
childNames.add(cluster);
|
leafNames.add(cluster);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return childNames;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isTopLevelCluster(XdsWatcherBase<?> cdsWatcher) {
|
private static boolean isTopLevelCluster(
|
||||||
if (! (cdsWatcher instanceof CdsWatcher)) {
|
XdsWatcherBase<XdsClusterResource.CdsUpdate> cdsWatcher) {
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return ((CdsWatcher)cdsWatcher).parentContexts.values().stream()
|
return ((CdsWatcher)cdsWatcher).parentContexts.values().stream()
|
||||||
.anyMatch(depth -> depth == 1);
|
.anyMatch(depth -> depth == 1);
|
||||||
}
|
}
|
||||||
|
|
@ -454,17 +480,11 @@ final class XdsDependencyManager implements XdsConfig.XdsClusterSubscriptionRegi
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateRoutes(List<VirtualHost> virtualHosts, Object newParentContext,
|
private void updateRoutes(List<VirtualHost> virtualHosts, Object newParentContext,
|
||||||
VirtualHost oldVirtualHost, boolean sameParentContext) {
|
List<VirtualHost> oldVirtualHosts, boolean sameParentContext) {
|
||||||
|
VirtualHost oldVirtualHost =
|
||||||
|
RoutingUtils.findVirtualHostForHostName(oldVirtualHosts, dataPlaneAuthority);
|
||||||
VirtualHost virtualHost =
|
VirtualHost virtualHost =
|
||||||
RoutingUtils.findVirtualHostForHostName(virtualHosts, dataPlaneAuthority);
|
RoutingUtils.findVirtualHostForHostName(virtualHosts, dataPlaneAuthority);
|
||||||
if (virtualHost == null) {
|
|
||||||
String error = "Failed to find virtual host matching hostname: " + dataPlaneAuthority;
|
|
||||||
logger.log(XdsLogger.XdsLogLevel.WARNING, error);
|
|
||||||
cleanUpRoutes();
|
|
||||||
xdsConfigWatcher.onError(
|
|
||||||
"xDS node ID:" + dataPlaneAuthority, Status.UNAVAILABLE.withDescription(error));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Set<String> newClusters = getClusterNamesFromVirtualHost(virtualHost);
|
Set<String> newClusters = getClusterNamesFromVirtualHost(virtualHost);
|
||||||
Set<String> oldClusters = getClusterNamesFromVirtualHost(oldVirtualHost);
|
Set<String> oldClusters = getClusterNamesFromVirtualHost(oldVirtualHost);
|
||||||
|
|
@ -482,6 +502,10 @@ final class XdsDependencyManager implements XdsConfig.XdsClusterSubscriptionRegi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String nodeInfo() {
|
||||||
|
return " nodeID: " + xdsClient.getBootstrapInfo().node().getId();
|
||||||
|
}
|
||||||
|
|
||||||
private static Set<String> getClusterNamesFromVirtualHost(VirtualHost virtualHost) {
|
private static Set<String> getClusterNamesFromVirtualHost(VirtualHost virtualHost) {
|
||||||
if (virtualHost == null) {
|
if (virtualHost == null) {
|
||||||
return Collections.emptySet();
|
return Collections.emptySet();
|
||||||
|
|
@ -506,46 +530,6 @@ final class XdsDependencyManager implements XdsConfig.XdsClusterSubscriptionRegi
|
||||||
return clusters;
|
return clusters;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private VirtualHost getActiveVirtualHost() {
|
|
||||||
TypeWatchers<?> rdsWatchers = resourceWatchers.get(XdsRouteConfigureResource.getInstance());
|
|
||||||
if (rdsWatchers == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
RdsWatcher activeRdsWatcher =
|
|
||||||
(RdsWatcher) rdsWatchers.watchers.values().stream().findFirst().orElse(null);
|
|
||||||
if (activeRdsWatcher == null || activeRdsWatcher.missingResult()
|
|
||||||
|| !activeRdsWatcher.getData().hasValue()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return RoutingUtils.findVirtualHostForHostName(
|
|
||||||
activeRdsWatcher.getData().getValue().virtualHosts, dataPlaneAuthority);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Must be in SyncContext
|
|
||||||
private void cleanUpRoutes() {
|
|
||||||
// Remove RdsWatcher & CDS Watchers
|
|
||||||
TypeWatchers<?> rdsResourceWatcher =
|
|
||||||
resourceWatchers.get(XdsRouteConfigureResource.getInstance());
|
|
||||||
if (rdsResourceWatcher == null || rdsResourceWatcher.watchers.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
XdsWatcherBase<?> watcher = rdsResourceWatcher.watchers.values().stream().findFirst().get();
|
|
||||||
cancelWatcher(watcher);
|
|
||||||
|
|
||||||
// Remove CdsWatchers pointed to by the RdsWatcher
|
|
||||||
RdsWatcher rdsWatcher = (RdsWatcher) watcher;
|
|
||||||
for (String cName : rdsWatcher.getCdsNames()) {
|
|
||||||
CdsWatcher cdsWatcher = getCluster(cName);
|
|
||||||
if (cdsWatcher != null) {
|
|
||||||
cancelClusterWatcherTree(cdsWatcher, rdsWatcher);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private CdsWatcher getCluster(String clusterName) {
|
private CdsWatcher getCluster(String clusterName) {
|
||||||
return (CdsWatcher) resourceWatchers.get(CLUSTER_RESOURCE).watchers.get(clusterName);
|
return (CdsWatcher) resourceWatchers.get(CLUSTER_RESOURCE).watchers.get(clusterName);
|
||||||
}
|
}
|
||||||
|
|
@ -565,16 +549,11 @@ final class XdsDependencyManager implements XdsConfig.XdsClusterSubscriptionRegi
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface XdsConfigWatcher {
|
public interface XdsConfigWatcher {
|
||||||
|
/**
|
||||||
void onUpdate(XdsConfig config);
|
* An updated XdsConfig or RPC-safe Status. The status code will be either UNAVAILABLE or
|
||||||
|
* INTERNAL.
|
||||||
// These 2 methods are invoked when there is an error or
|
*/
|
||||||
// does-not-exist on LDS or RDS only. The context will be a
|
void onUpdate(StatusOr<XdsConfig> config);
|
||||||
// human-readable string indicating the scope in which the error
|
|
||||||
// occurred (e.g., the resource type and name).
|
|
||||||
void onError(String resourceContext, Status status);
|
|
||||||
|
|
||||||
void onResourceDoesNotExist(String resourceContext);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ClusterSubscription implements Closeable {
|
private class ClusterSubscription implements Closeable {
|
||||||
|
|
@ -594,7 +573,7 @@ final class XdsDependencyManager implements XdsConfig.XdsClusterSubscriptionRegi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private abstract static class XdsWatcherBase<T extends ResourceUpdate>
|
private abstract class XdsWatcherBase<T extends ResourceUpdate>
|
||||||
implements ResourceWatcher<T> {
|
implements ResourceWatcher<T> {
|
||||||
private final XdsResourceType<T> type;
|
private final XdsResourceType<T> type;
|
||||||
private final String resourceName;
|
private final String resourceName;
|
||||||
|
|
@ -612,12 +591,25 @@ final class XdsDependencyManager implements XdsConfig.XdsClusterSubscriptionRegi
|
||||||
@Override
|
@Override
|
||||||
public void onError(Status error) {
|
public void onError(Status error) {
|
||||||
checkNotNull(error, "error");
|
checkNotNull(error, "error");
|
||||||
setDataAsStatus(error);
|
// Don't update configuration on error, if we've already received configuration
|
||||||
|
if (!hasDataValue()) {
|
||||||
|
setDataAsStatus(Status.UNAVAILABLE.withDescription(
|
||||||
|
String.format("Error retrieving %s: %s: %s",
|
||||||
|
toContextString(), error.getCode(), error.getDescription())));
|
||||||
|
maybePublishConfig();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void handleDoesNotExist(String resourceName) {
|
@Override
|
||||||
|
public void onResourceDoesNotExist(String resourceName) {
|
||||||
|
if (cancelled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
checkArgument(this.resourceName.equals(resourceName), "Resource name does not match");
|
checkArgument(this.resourceName.equals(resourceName), "Resource name does not match");
|
||||||
setDataAsStatus(Status.UNAVAILABLE.withDescription("No " + toContextString()));
|
setDataAsStatus(Status.UNAVAILABLE.withDescription(
|
||||||
|
toContextString() + " does not exist" + nodeInfo()));
|
||||||
|
maybePublishConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean missingResult() {
|
boolean missingResult() {
|
||||||
|
|
@ -647,12 +639,17 @@ final class XdsDependencyManager implements XdsConfig.XdsClusterSubscriptionRegi
|
||||||
this.data = StatusOr.fromStatus(status);
|
this.data = StatusOr.fromStatus(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
String toContextString() {
|
public String toContextString() {
|
||||||
return toContextStr(type.typeName(), resourceName);
|
return toContextStr(type.typeName(), resourceName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class LdsWatcher extends XdsWatcherBase<XdsListenerResource.LdsUpdate> {
|
private interface RdsUpdateSupplier {
|
||||||
|
StatusOr<RdsUpdate> getRdsUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class LdsWatcher extends XdsWatcherBase<XdsListenerResource.LdsUpdate>
|
||||||
|
implements RdsUpdateSupplier {
|
||||||
String rdsName;
|
String rdsName;
|
||||||
|
|
||||||
private LdsWatcher(String resourceName) {
|
private LdsWatcher(String resourceName) {
|
||||||
|
|
@ -664,9 +661,20 @@ final class XdsDependencyManager implements XdsConfig.XdsClusterSubscriptionRegi
|
||||||
checkNotNull(update, "update");
|
checkNotNull(update, "update");
|
||||||
|
|
||||||
HttpConnectionManager httpConnectionManager = update.httpConnectionManager();
|
HttpConnectionManager httpConnectionManager = update.httpConnectionManager();
|
||||||
List<VirtualHost> virtualHosts = httpConnectionManager.virtualHosts();
|
List<VirtualHost> virtualHosts;
|
||||||
String rdsName = httpConnectionManager.rdsName();
|
String rdsName;
|
||||||
VirtualHost activeVirtualHost = getActiveVirtualHost();
|
if (httpConnectionManager == null) {
|
||||||
|
// TCP listener. Unsupported config
|
||||||
|
virtualHosts = Collections.emptyList(); // Not null, to not delegate to RDS
|
||||||
|
rdsName = null;
|
||||||
|
} else {
|
||||||
|
virtualHosts = httpConnectionManager.virtualHosts();
|
||||||
|
rdsName = httpConnectionManager.rdsName();
|
||||||
|
}
|
||||||
|
StatusOr<RdsUpdate> activeRdsUpdate = getRouteSource().getRdsUpdate();
|
||||||
|
List<VirtualHost> activeVirtualHosts = activeRdsUpdate.hasValue()
|
||||||
|
? activeRdsUpdate.getValue().virtualHosts
|
||||||
|
: Collections.emptyList();
|
||||||
|
|
||||||
boolean changedRdsName = !Objects.equals(rdsName, this.rdsName);
|
boolean changedRdsName = !Objects.equals(rdsName, this.rdsName);
|
||||||
if (changedRdsName) {
|
if (changedRdsName) {
|
||||||
|
|
@ -675,10 +683,9 @@ final class XdsDependencyManager implements XdsConfig.XdsClusterSubscriptionRegi
|
||||||
|
|
||||||
if (virtualHosts != null) {
|
if (virtualHosts != null) {
|
||||||
// No RDS watcher since we are getting RDS updates via LDS
|
// No RDS watcher since we are getting RDS updates via LDS
|
||||||
updateRoutes(virtualHosts, this, activeVirtualHost, this.rdsName == null);
|
updateRoutes(virtualHosts, this, activeVirtualHosts, this.rdsName == null);
|
||||||
this.rdsName = null;
|
this.rdsName = null;
|
||||||
} else if (changedRdsName) {
|
} else if (changedRdsName) {
|
||||||
cleanUpRdsWatcher();
|
|
||||||
this.rdsName = rdsName;
|
this.rdsName = rdsName;
|
||||||
addWatcher(new RdsWatcher(rdsName));
|
addWatcher(new RdsWatcher(rdsName));
|
||||||
logger.log(XdsLogger.XdsLogLevel.INFO, "Start watching RDS resource {0}", rdsName);
|
logger.log(XdsLogger.XdsLogLevel.INFO, "Start watching RDS resource {0}", rdsName);
|
||||||
|
|
@ -688,20 +695,18 @@ final class XdsDependencyManager implements XdsConfig.XdsClusterSubscriptionRegi
|
||||||
maybePublishConfig();
|
maybePublishConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onError(Status error) {
|
|
||||||
super.onError(checkNotNull(error, "error"));
|
|
||||||
xdsConfigWatcher.onError(toContextString(), error);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onResourceDoesNotExist(String resourceName) {
|
public void onResourceDoesNotExist(String resourceName) {
|
||||||
if (cancelled) {
|
if (cancelled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDoesNotExist(resourceName);
|
checkArgument(resourceName().equals(resourceName), "Resource name does not match");
|
||||||
xdsConfigWatcher.onResourceDoesNotExist(toContextString());
|
setDataAsStatus(Status.UNAVAILABLE.withDescription(
|
||||||
|
toContextString() + " does not exist" + nodeInfo()));
|
||||||
|
cleanUpRdsWatcher();
|
||||||
|
rdsName = null;
|
||||||
|
maybePublishConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void cleanUpRdsWatcher() {
|
private void cleanUpRdsWatcher() {
|
||||||
|
|
@ -711,8 +716,7 @@ final class XdsDependencyManager implements XdsConfig.XdsClusterSubscriptionRegi
|
||||||
logger.log(XdsLogger.XdsLogLevel.DEBUG, "Stop watching RDS resource {0}", rdsName);
|
logger.log(XdsLogger.XdsLogLevel.DEBUG, "Stop watching RDS resource {0}", rdsName);
|
||||||
|
|
||||||
// Cleanup clusters (as appropriate) that had the old rds watcher as a parent
|
// Cleanup clusters (as appropriate) that had the old rds watcher as a parent
|
||||||
if (!oldRdsWatcher.hasDataValue() || !oldRdsWatcher.getData().hasValue()
|
if (!oldRdsWatcher.hasDataValue() || resourceWatchers.get(CLUSTER_RESOURCE) == null) {
|
||||||
|| resourceWatchers.get(CLUSTER_RESOURCE) == null) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (XdsWatcherBase<?> watcher :
|
for (XdsWatcherBase<?> watcher :
|
||||||
|
|
@ -723,16 +727,58 @@ final class XdsDependencyManager implements XdsConfig.XdsClusterSubscriptionRegi
|
||||||
}
|
}
|
||||||
|
|
||||||
private RdsWatcher getRdsWatcher() {
|
private RdsWatcher getRdsWatcher() {
|
||||||
TypeWatchers<?> watchers = resourceWatchers.get(XdsRouteConfigureResource.getInstance());
|
if (rdsName == null) {
|
||||||
if (watchers == null || rdsName == null || watchers.watchers.isEmpty()) {
|
return null;
|
||||||
|
}
|
||||||
|
TypeWatchers<?> watchers = resourceWatchers.get(XdsRouteConfigureResource.getInstance());
|
||||||
|
if (watchers == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (RdsWatcher) watchers.watchers.get(rdsName);
|
return (RdsWatcher) watchers.watchers.get(rdsName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public RdsUpdateSupplier getRouteSource() {
|
||||||
|
if (!hasDataValue()) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
HttpConnectionManager hcm = getData().getValue().httpConnectionManager();
|
||||||
|
if (hcm == null) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
List<VirtualHost> virtualHosts = hcm.virtualHosts();
|
||||||
|
if (virtualHosts != null) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
RdsWatcher rdsWatcher = getRdsWatcher();
|
||||||
|
assert rdsWatcher != null;
|
||||||
|
return rdsWatcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StatusOr<RdsUpdate> getRdsUpdate() {
|
||||||
|
if (missingResult()) {
|
||||||
|
return StatusOr.fromStatus(Status.UNAVAILABLE.withDescription("Not yet loaded"));
|
||||||
|
}
|
||||||
|
if (!getData().hasValue()) {
|
||||||
|
return StatusOr.fromStatus(getData().getStatus());
|
||||||
|
}
|
||||||
|
HttpConnectionManager hcm = getData().getValue().httpConnectionManager();
|
||||||
|
if (hcm == null) {
|
||||||
|
return StatusOr.fromStatus(
|
||||||
|
Status.UNAVAILABLE.withDescription("Not an API listener" + nodeInfo()));
|
||||||
|
}
|
||||||
|
List<VirtualHost> virtualHosts = hcm.virtualHosts();
|
||||||
|
if (virtualHosts == null) {
|
||||||
|
// Code shouldn't trigger this case, as it should be calling RdsWatcher instead. This would
|
||||||
|
// be easily implemented with getRdsWatcher().getRdsUpdate(), but getting here is likely a
|
||||||
|
// bug
|
||||||
|
return StatusOr.fromStatus(Status.INTERNAL.withDescription("Routes are in RDS, not LDS"));
|
||||||
|
}
|
||||||
|
return StatusOr.fromValue(new RdsUpdate(virtualHosts));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class RdsWatcher extends XdsWatcherBase<RdsUpdate> {
|
private class RdsWatcher extends XdsWatcherBase<RdsUpdate> implements RdsUpdateSupplier {
|
||||||
|
|
||||||
public RdsWatcher(String resourceName) {
|
public RdsWatcher(String resourceName) {
|
||||||
super(XdsRouteConfigureResource.getInstance(), checkNotNull(resourceName, "resourceName"));
|
super(XdsRouteConfigureResource.getInstance(), checkNotNull(resourceName, "resourceName"));
|
||||||
|
|
@ -741,37 +787,20 @@ final class XdsDependencyManager implements XdsConfig.XdsClusterSubscriptionRegi
|
||||||
@Override
|
@Override
|
||||||
public void onChanged(RdsUpdate update) {
|
public void onChanged(RdsUpdate update) {
|
||||||
checkNotNull(update, "update");
|
checkNotNull(update, "update");
|
||||||
RdsUpdate oldData = hasDataValue() ? getData().getValue() : null;
|
List<VirtualHost> oldVirtualHosts = hasDataValue()
|
||||||
VirtualHost oldVirtualHost =
|
? getData().getValue().virtualHosts
|
||||||
(oldData != null)
|
: Collections.emptyList();
|
||||||
? RoutingUtils.findVirtualHostForHostName(oldData.virtualHosts, dataPlaneAuthority)
|
|
||||||
: null;
|
|
||||||
setData(update);
|
setData(update);
|
||||||
updateRoutes(update.virtualHosts, this, oldVirtualHost, true);
|
updateRoutes(update.virtualHosts, this, oldVirtualHosts, true);
|
||||||
maybePublishConfig();
|
maybePublishConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(Status error) {
|
public StatusOr<RdsUpdate> getRdsUpdate() {
|
||||||
super.onError(checkNotNull(error, "error"));
|
if (missingResult()) {
|
||||||
xdsConfigWatcher.onError(toContextString(), error);
|
return StatusOr.fromStatus(Status.UNAVAILABLE.withDescription("Not yet loaded"));
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onResourceDoesNotExist(String resourceName) {
|
|
||||||
if (cancelled) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
handleDoesNotExist(checkNotNull(resourceName, "resourceName"));
|
return getData();
|
||||||
xdsConfigWatcher.onResourceDoesNotExist(toContextString());
|
|
||||||
}
|
|
||||||
|
|
||||||
ImmutableList<String> getCdsNames() {
|
|
||||||
if (!hasDataValue() || getData().getValue().virtualHosts == null) {
|
|
||||||
return ImmutableList.of();
|
|
||||||
}
|
|
||||||
|
|
||||||
return ImmutableList.copyOf(getClusterNamesFromVirtualHost(getActiveVirtualHost()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -789,7 +818,7 @@ final class XdsDependencyManager implements XdsConfig.XdsClusterSubscriptionRegi
|
||||||
switch (update.clusterType()) {
|
switch (update.clusterType()) {
|
||||||
case EDS:
|
case EDS:
|
||||||
setData(update);
|
setData(update);
|
||||||
if (!addEdsWatcher(update.edsServiceName(), this)) {
|
if (!addEdsWatcher(getEdsServiceName(), this)) {
|
||||||
maybePublishConfig();
|
maybePublishConfig();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
@ -805,14 +834,15 @@ final class XdsDependencyManager implements XdsConfig.XdsClusterSubscriptionRegi
|
||||||
logger.log(XdsLogger.XdsLogLevel.WARNING,
|
logger.log(XdsLogger.XdsLogLevel.WARNING,
|
||||||
"Cluster recursion depth limit exceeded for cluster {0}", resourceName());
|
"Cluster recursion depth limit exceeded for cluster {0}", resourceName());
|
||||||
Status error = Status.UNAVAILABLE.withDescription(
|
Status error = Status.UNAVAILABLE.withDescription(
|
||||||
"aggregate cluster graph exceeds max depth");
|
"aggregate cluster graph exceeds max depth at " + resourceName() + nodeInfo());
|
||||||
setDataAsStatus(error);
|
setDataAsStatus(error);
|
||||||
}
|
}
|
||||||
if (hasDataValue()) {
|
if (hasDataValue()) {
|
||||||
Set<String> oldNames = new HashSet<>(getData().getValue().prioritizedClusterNames());
|
Set<String> oldNames = getData().getValue().clusterType() == ClusterType.AGGREGATE
|
||||||
|
? new HashSet<>(getData().getValue().prioritizedClusterNames())
|
||||||
|
: Collections.emptySet();
|
||||||
Set<String> newNames = new HashSet<>(update.prioritizedClusterNames());
|
Set<String> newNames = new HashSet<>(update.prioritizedClusterNames());
|
||||||
|
|
||||||
|
|
||||||
Set<String> deletedClusters = Sets.difference(oldNames, newNames);
|
Set<String> deletedClusters = Sets.difference(oldNames, newNames);
|
||||||
deletedClusters.forEach((cluster)
|
deletedClusters.forEach((cluster)
|
||||||
-> cancelClusterWatcherTree(getCluster(cluster), parentContext));
|
-> cancelClusterWatcherTree(getCluster(cluster), parentContext));
|
||||||
|
|
@ -838,19 +868,20 @@ final class XdsDependencyManager implements XdsConfig.XdsClusterSubscriptionRegi
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Status error = Status.UNAVAILABLE.withDescription(
|
Status error = Status.UNAVAILABLE.withDescription(
|
||||||
"aggregate cluster graph exceeds max depth");
|
"aggregate cluster graph exceeds max depth at " + resourceName() + nodeInfo());
|
||||||
setDataAsStatus(error);
|
setDataAsStatus(error);
|
||||||
maybePublishConfig();
|
maybePublishConfig();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public String getEdsServiceName() {
|
||||||
public void onResourceDoesNotExist(String resourceName) {
|
XdsClusterResource.CdsUpdate cdsUpdate = getData().getValue();
|
||||||
if (cancelled) {
|
assert cdsUpdate.clusterType() == ClusterType.EDS;
|
||||||
return;
|
String edsServiceName = cdsUpdate.edsServiceName();
|
||||||
|
if (edsServiceName == null) {
|
||||||
|
edsServiceName = cdsUpdate.clusterName();
|
||||||
}
|
}
|
||||||
handleDoesNotExist(checkNotNull(resourceName, "resourceName"));
|
return edsServiceName;
|
||||||
maybePublishConfig();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -868,15 +899,6 @@ final class XdsDependencyManager implements XdsConfig.XdsClusterSubscriptionRegi
|
||||||
maybePublishConfig();
|
maybePublishConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onResourceDoesNotExist(String resourceName) {
|
|
||||||
if (cancelled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
handleDoesNotExist(checkNotNull(resourceName, "resourceName"));
|
|
||||||
maybePublishConfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
void addParentContext(CdsWatcher parentContext) {
|
void addParentContext(CdsWatcher parentContext) {
|
||||||
parentContexts.add(checkNotNull(parentContext, "parentContext"));
|
parentContexts.add(checkNotNull(parentContext, "parentContext"));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ import io.grpc.MetricRecorder;
|
||||||
import io.grpc.NameResolver;
|
import io.grpc.NameResolver;
|
||||||
import io.grpc.Status;
|
import io.grpc.Status;
|
||||||
import io.grpc.Status.Code;
|
import io.grpc.Status.Code;
|
||||||
|
import io.grpc.StatusOr;
|
||||||
import io.grpc.SynchronizationContext;
|
import io.grpc.SynchronizationContext;
|
||||||
import io.grpc.internal.GrpcUtil;
|
import io.grpc.internal.GrpcUtil;
|
||||||
import io.grpc.internal.ObjectPool;
|
import io.grpc.internal.ObjectPool;
|
||||||
|
|
@ -60,11 +61,9 @@ import io.grpc.xds.VirtualHost.Route.RouteAction.HashPolicy;
|
||||||
import io.grpc.xds.VirtualHost.Route.RouteAction.RetryPolicy;
|
import io.grpc.xds.VirtualHost.Route.RouteAction.RetryPolicy;
|
||||||
import io.grpc.xds.VirtualHost.Route.RouteMatch;
|
import io.grpc.xds.VirtualHost.Route.RouteMatch;
|
||||||
import io.grpc.xds.XdsNameResolverProvider.CallCounterProvider;
|
import io.grpc.xds.XdsNameResolverProvider.CallCounterProvider;
|
||||||
import io.grpc.xds.XdsRouteConfigureResource.RdsUpdate;
|
|
||||||
import io.grpc.xds.client.Bootstrapper.AuthorityInfo;
|
import io.grpc.xds.client.Bootstrapper.AuthorityInfo;
|
||||||
import io.grpc.xds.client.Bootstrapper.BootstrapInfo;
|
import io.grpc.xds.client.Bootstrapper.BootstrapInfo;
|
||||||
import io.grpc.xds.client.XdsClient;
|
import io.grpc.xds.client.XdsClient;
|
||||||
import io.grpc.xds.client.XdsClient.ResourceWatcher;
|
|
||||||
import io.grpc.xds.client.XdsLogger;
|
import io.grpc.xds.client.XdsLogger;
|
||||||
import io.grpc.xds.client.XdsLogger.XdsLogLevel;
|
import io.grpc.xds.client.XdsLogger.XdsLogLevel;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
|
@ -127,6 +126,7 @@ final class XdsNameResolver extends NameResolver {
|
||||||
private final ConfigSelector configSelector = new ConfigSelector();
|
private final ConfigSelector configSelector = new ConfigSelector();
|
||||||
private final long randomChannelId;
|
private final long randomChannelId;
|
||||||
private final MetricRecorder metricRecorder;
|
private final MetricRecorder metricRecorder;
|
||||||
|
private final Args nameResolverArgs;
|
||||||
// Must be accessed in syncContext.
|
// Must be accessed in syncContext.
|
||||||
// Filter instances are unique per channel, and per filter (name+typeUrl).
|
// Filter instances are unique per channel, and per filter (name+typeUrl).
|
||||||
// NamedFilterConfig.filterStateKey -> filter_instance.
|
// NamedFilterConfig.filterStateKey -> filter_instance.
|
||||||
|
|
@ -138,20 +138,17 @@ final class XdsNameResolver extends NameResolver {
|
||||||
private XdsClient xdsClient;
|
private XdsClient xdsClient;
|
||||||
private CallCounterProvider callCounterProvider;
|
private CallCounterProvider callCounterProvider;
|
||||||
private ResolveState resolveState;
|
private ResolveState resolveState;
|
||||||
// Workaround for https://github.com/grpc/grpc-java/issues/8886 . This should be handled in
|
|
||||||
// XdsClient instead of here.
|
|
||||||
private boolean receivedConfig;
|
|
||||||
|
|
||||||
XdsNameResolver(
|
XdsNameResolver(
|
||||||
URI targetUri, String name, @Nullable String overrideAuthority,
|
URI targetUri, String name, @Nullable String overrideAuthority,
|
||||||
ServiceConfigParser serviceConfigParser,
|
ServiceConfigParser serviceConfigParser,
|
||||||
SynchronizationContext syncContext, ScheduledExecutorService scheduler,
|
SynchronizationContext syncContext, ScheduledExecutorService scheduler,
|
||||||
@Nullable Map<String, ?> bootstrapOverride,
|
@Nullable Map<String, ?> bootstrapOverride,
|
||||||
MetricRecorder metricRecorder) {
|
MetricRecorder metricRecorder, Args nameResolverArgs) {
|
||||||
this(targetUri, targetUri.getAuthority(), name, overrideAuthority, serviceConfigParser,
|
this(targetUri, targetUri.getAuthority(), name, overrideAuthority, serviceConfigParser,
|
||||||
syncContext, scheduler, SharedXdsClientPoolProvider.getDefaultProvider(),
|
syncContext, scheduler, SharedXdsClientPoolProvider.getDefaultProvider(),
|
||||||
ThreadSafeRandomImpl.instance, FilterRegistry.getDefaultRegistry(), bootstrapOverride,
|
ThreadSafeRandomImpl.instance, FilterRegistry.getDefaultRegistry(), bootstrapOverride,
|
||||||
metricRecorder);
|
metricRecorder, nameResolverArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
|
|
@ -161,7 +158,7 @@ final class XdsNameResolver extends NameResolver {
|
||||||
SynchronizationContext syncContext, ScheduledExecutorService scheduler,
|
SynchronizationContext syncContext, ScheduledExecutorService scheduler,
|
||||||
XdsClientPoolFactory xdsClientPoolFactory, ThreadSafeRandom random,
|
XdsClientPoolFactory xdsClientPoolFactory, ThreadSafeRandom random,
|
||||||
FilterRegistry filterRegistry, @Nullable Map<String, ?> bootstrapOverride,
|
FilterRegistry filterRegistry, @Nullable Map<String, ?> bootstrapOverride,
|
||||||
MetricRecorder metricRecorder) {
|
MetricRecorder metricRecorder, Args nameResolverArgs) {
|
||||||
this.targetAuthority = targetAuthority;
|
this.targetAuthority = targetAuthority;
|
||||||
target = targetUri.toString();
|
target = targetUri.toString();
|
||||||
|
|
||||||
|
|
@ -180,6 +177,8 @@ final class XdsNameResolver extends NameResolver {
|
||||||
this.random = checkNotNull(random, "random");
|
this.random = checkNotNull(random, "random");
|
||||||
this.filterRegistry = checkNotNull(filterRegistry, "filterRegistry");
|
this.filterRegistry = checkNotNull(filterRegistry, "filterRegistry");
|
||||||
this.metricRecorder = metricRecorder;
|
this.metricRecorder = metricRecorder;
|
||||||
|
this.nameResolverArgs = checkNotNull(nameResolverArgs, "nameResolverArgs");
|
||||||
|
|
||||||
randomChannelId = random.nextLong();
|
randomChannelId = random.nextLong();
|
||||||
logId = InternalLogId.allocate("xds-resolver", name);
|
logId = InternalLogId.allocate("xds-resolver", name);
|
||||||
logger = XdsLogger.withLogId(logId);
|
logger = XdsLogger.withLogId(logId);
|
||||||
|
|
@ -228,9 +227,8 @@ final class XdsNameResolver extends NameResolver {
|
||||||
}
|
}
|
||||||
ldsResourceName = XdsClient.canonifyResourceName(ldsResourceName);
|
ldsResourceName = XdsClient.canonifyResourceName(ldsResourceName);
|
||||||
callCounterProvider = SharedCallCounterMap.getInstance();
|
callCounterProvider = SharedCallCounterMap.getInstance();
|
||||||
resolveState = new ResolveState(ldsResourceName);
|
|
||||||
|
|
||||||
resolveState.start();
|
resolveState = new ResolveState(ldsResourceName); // auto starts
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String expandPercentS(String template, String replacement) {
|
private static String expandPercentS(String template, String replacement) {
|
||||||
|
|
@ -241,7 +239,7 @@ final class XdsNameResolver extends NameResolver {
|
||||||
public void shutdown() {
|
public void shutdown() {
|
||||||
logger.log(XdsLogLevel.INFO, "Shutdown");
|
logger.log(XdsLogLevel.INFO, "Shutdown");
|
||||||
if (resolveState != null) {
|
if (resolveState != null) {
|
||||||
resolveState.stop();
|
resolveState.shutdown();
|
||||||
}
|
}
|
||||||
if (xdsClient != null) {
|
if (xdsClient != null) {
|
||||||
xdsClient = xdsClientPool.returnObject(xdsClient);
|
xdsClient = xdsClientPool.returnObject(xdsClient);
|
||||||
|
|
@ -290,7 +288,7 @@ final class XdsNameResolver extends NameResolver {
|
||||||
}
|
}
|
||||||
|
|
||||||
// called in syncContext
|
// called in syncContext
|
||||||
private void updateResolutionResult() {
|
private void updateResolutionResult(XdsConfig xdsConfig) {
|
||||||
syncContext.throwIfNotInThisSynchronizationContext();
|
syncContext.throwIfNotInThisSynchronizationContext();
|
||||||
|
|
||||||
ImmutableMap.Builder<String, Object> childPolicy = new ImmutableMap.Builder<>();
|
ImmutableMap.Builder<String, Object> childPolicy = new ImmutableMap.Builder<>();
|
||||||
|
|
@ -312,6 +310,8 @@ final class XdsNameResolver extends NameResolver {
|
||||||
Attributes attrs =
|
Attributes attrs =
|
||||||
Attributes.newBuilder()
|
Attributes.newBuilder()
|
||||||
.set(XdsAttributes.XDS_CLIENT_POOL, xdsClientPool)
|
.set(XdsAttributes.XDS_CLIENT_POOL, xdsClientPool)
|
||||||
|
.set(XdsAttributes.XDS_CONFIG, xdsConfig)
|
||||||
|
.set(XdsAttributes.XDS_CLUSTER_SUBSCRIPT_REGISTRY, resolveState.xdsDependencyManager)
|
||||||
.set(XdsAttributes.CALL_COUNTER_PROVIDER, callCounterProvider)
|
.set(XdsAttributes.CALL_COUNTER_PROVIDER, callCounterProvider)
|
||||||
.set(InternalConfigSelector.KEY, configSelector)
|
.set(InternalConfigSelector.KEY, configSelector)
|
||||||
.build();
|
.build();
|
||||||
|
|
@ -321,7 +321,6 @@ final class XdsNameResolver extends NameResolver {
|
||||||
.setServiceConfig(parsedServiceConfig)
|
.setServiceConfig(parsedServiceConfig)
|
||||||
.build();
|
.build();
|
||||||
listener.onResult2(result);
|
listener.onResult2(result);
|
||||||
receivedConfig = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -540,7 +539,11 @@ final class XdsNameResolver extends NameResolver {
|
||||||
public void run() {
|
public void run() {
|
||||||
if (clusterRefs.get(cluster).refCount.get() == 0) {
|
if (clusterRefs.get(cluster).refCount.get() == 0) {
|
||||||
clusterRefs.remove(cluster);
|
clusterRefs.remove(cluster);
|
||||||
updateResolutionResult();
|
if (resolveState.lastConfigOrStatus.hasValue()) {
|
||||||
|
updateResolutionResult(resolveState.lastConfigOrStatus.getValue());
|
||||||
|
} else {
|
||||||
|
resolveState.cleanUpRoutes(resolveState.lastConfigOrStatus.getStatus());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -629,82 +632,56 @@ final class XdsNameResolver extends NameResolver {
|
||||||
return "cluster_specifier_plugin:" + pluginName;
|
return "cluster_specifier_plugin:" + pluginName;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ResolveState implements ResourceWatcher<XdsListenerResource.LdsUpdate> {
|
class ResolveState implements XdsDependencyManager.XdsConfigWatcher {
|
||||||
private final ConfigOrError emptyServiceConfig =
|
private final ConfigOrError emptyServiceConfig =
|
||||||
serviceConfigParser.parseServiceConfig(Collections.<String, Object>emptyMap());
|
serviceConfigParser.parseServiceConfig(Collections.<String, Object>emptyMap());
|
||||||
private final String ldsResourceName;
|
private final String authority;
|
||||||
|
private final XdsDependencyManager xdsDependencyManager;
|
||||||
private boolean stopped;
|
private boolean stopped;
|
||||||
@Nullable
|
@Nullable
|
||||||
private Set<String> existingClusters; // clusters to which new requests can be routed
|
private Set<String> existingClusters; // clusters to which new requests can be routed
|
||||||
@Nullable
|
private StatusOr<XdsConfig> lastConfigOrStatus;
|
||||||
private RouteDiscoveryState routeDiscoveryState;
|
|
||||||
|
|
||||||
ResolveState(String ldsResourceName) {
|
private ResolveState(String ldsResourceName) {
|
||||||
this.ldsResourceName = ldsResourceName;
|
authority = overrideAuthority != null ? overrideAuthority : encodedServiceAuthority;
|
||||||
|
xdsDependencyManager =
|
||||||
|
new XdsDependencyManager(xdsClient, this, syncContext, authority, ldsResourceName,
|
||||||
|
nameResolverArgs, scheduler);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void shutdown() {
|
||||||
public void onChanged(final XdsListenerResource.LdsUpdate update) {
|
|
||||||
if (stopped) {
|
if (stopped) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
logger.log(XdsLogLevel.INFO, "Receive LDS resource update: {0}", update);
|
|
||||||
HttpConnectionManager httpConnectionManager = update.httpConnectionManager();
|
stopped = true;
|
||||||
List<VirtualHost> virtualHosts = httpConnectionManager.virtualHosts();
|
xdsDependencyManager.shutdown();
|
||||||
String rdsName = httpConnectionManager.rdsName();
|
updateActiveFilters(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUpdate(StatusOr<XdsConfig> updateOrStatus) {
|
||||||
|
if (stopped) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
logger.log(XdsLogLevel.INFO, "Receive XDS resource update: {0}", updateOrStatus);
|
||||||
|
|
||||||
|
lastConfigOrStatus = updateOrStatus;
|
||||||
|
if (!updateOrStatus.hasValue()) {
|
||||||
|
updateActiveFilters(null);
|
||||||
|
cleanUpRoutes(updateOrStatus.getStatus());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process Route
|
||||||
|
XdsConfig update = updateOrStatus.getValue();
|
||||||
|
HttpConnectionManager httpConnectionManager = update.getListener().httpConnectionManager();
|
||||||
|
VirtualHost virtualHost = update.getVirtualHost();
|
||||||
ImmutableList<NamedFilterConfig> filterConfigs = httpConnectionManager.httpFilterConfigs();
|
ImmutableList<NamedFilterConfig> filterConfigs = httpConnectionManager.httpFilterConfigs();
|
||||||
long streamDurationNano = httpConnectionManager.httpMaxStreamDurationNano();
|
long streamDurationNano = httpConnectionManager.httpMaxStreamDurationNano();
|
||||||
|
|
||||||
// Create/update HCM-bound state.
|
|
||||||
cleanUpRouteDiscoveryState();
|
|
||||||
updateActiveFilters(filterConfigs);
|
updateActiveFilters(filterConfigs);
|
||||||
|
updateRoutes(update, virtualHost, streamDurationNano, filterConfigs);
|
||||||
// Routes specified directly in LDS.
|
|
||||||
if (virtualHosts != null) {
|
|
||||||
updateRoutes(virtualHosts, streamDurationNano, filterConfigs);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Routes provided by RDS.
|
|
||||||
routeDiscoveryState = new RouteDiscoveryState(rdsName, streamDurationNano, filterConfigs);
|
|
||||||
logger.log(XdsLogLevel.INFO, "Start watching RDS resource {0}", rdsName);
|
|
||||||
xdsClient.watchXdsResource(XdsRouteConfigureResource.getInstance(),
|
|
||||||
rdsName, routeDiscoveryState, syncContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onError(final Status error) {
|
|
||||||
if (stopped || receivedConfig) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
listener.onError(Status.UNAVAILABLE.withCause(error.getCause()).withDescription(
|
|
||||||
String.format("Unable to load LDS %s. xDS server returned: %s: %s",
|
|
||||||
ldsResourceName, error.getCode(), error.getDescription())));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onResourceDoesNotExist(final String resourceName) {
|
|
||||||
if (stopped) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String error = "LDS resource does not exist: " + resourceName;
|
|
||||||
logger.log(XdsLogLevel.INFO, error);
|
|
||||||
cleanUpRouteDiscoveryState();
|
|
||||||
updateActiveFilters(null);
|
|
||||||
cleanUpRoutes(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void start() {
|
|
||||||
logger.log(XdsLogLevel.INFO, "Start watching LDS resource {0}", ldsResourceName);
|
|
||||||
xdsClient.watchXdsResource(XdsListenerResource.getInstance(),
|
|
||||||
ldsResourceName, this, syncContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void stop() {
|
|
||||||
logger.log(XdsLogLevel.INFO, "Stop watching LDS resource {0}", ldsResourceName);
|
|
||||||
stopped = true;
|
|
||||||
cleanUpRouteDiscoveryState();
|
|
||||||
updateActiveFilters(null);
|
|
||||||
xdsClient.cancelXdsResourceWatch(XdsListenerResource.getInstance(), ldsResourceName, this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// called in syncContext
|
// called in syncContext
|
||||||
|
|
@ -732,18 +709,11 @@ final class XdsNameResolver extends NameResolver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// called in syncContext
|
private void updateRoutes(
|
||||||
private void updateRoutes(List<VirtualHost> virtualHosts, long httpMaxStreamDurationNano,
|
XdsConfig xdsConfig,
|
||||||
|
@Nullable VirtualHost virtualHost,
|
||||||
|
long httpMaxStreamDurationNano,
|
||||||
@Nullable List<NamedFilterConfig> filterConfigs) {
|
@Nullable List<NamedFilterConfig> filterConfigs) {
|
||||||
String authority = overrideAuthority != null ? overrideAuthority : encodedServiceAuthority;
|
|
||||||
VirtualHost virtualHost = RoutingUtils.findVirtualHostForHostName(virtualHosts, authority);
|
|
||||||
if (virtualHost == null) {
|
|
||||||
String error = "Failed to find virtual host matching hostname: " + authority;
|
|
||||||
logger.log(XdsLogLevel.WARNING, error);
|
|
||||||
cleanUpRoutes(error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Route> routes = virtualHost.routes();
|
List<Route> routes = virtualHost.routes();
|
||||||
ImmutableList.Builder<RouteData> routesData = ImmutableList.builder();
|
ImmutableList.Builder<RouteData> routesData = ImmutableList.builder();
|
||||||
|
|
||||||
|
|
@ -826,7 +796,7 @@ final class XdsNameResolver extends NameResolver {
|
||||||
}
|
}
|
||||||
// Update service config to include newly added clusters.
|
// Update service config to include newly added clusters.
|
||||||
if (shouldUpdateResult && routingConfig != null) {
|
if (shouldUpdateResult && routingConfig != null) {
|
||||||
updateResolutionResult();
|
updateResolutionResult(xdsConfig);
|
||||||
shouldUpdateResult = false;
|
shouldUpdateResult = false;
|
||||||
}
|
}
|
||||||
// Make newly added clusters selectable by config selector and deleted clusters no longer
|
// Make newly added clusters selectable by config selector and deleted clusters no longer
|
||||||
|
|
@ -840,7 +810,7 @@ final class XdsNameResolver extends NameResolver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (shouldUpdateResult) {
|
if (shouldUpdateResult) {
|
||||||
updateResolutionResult();
|
updateResolutionResult(xdsConfig);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -882,10 +852,8 @@ final class XdsNameResolver extends NameResolver {
|
||||||
return combineInterceptors(filterInterceptors.build());
|
return combineInterceptors(filterInterceptors.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void cleanUpRoutes(String error) {
|
private void cleanUpRoutes(Status error) {
|
||||||
String errorWithNodeId =
|
routingConfig = new RoutingConfig(error);
|
||||||
error + ", xDS node ID: " + xdsClient.getBootstrapInfo().node().getId();
|
|
||||||
routingConfig = new RoutingConfig(Status.UNAVAILABLE.withDescription(errorWithNodeId));
|
|
||||||
if (existingClusters != null) {
|
if (existingClusters != null) {
|
||||||
for (String cluster : existingClusters) {
|
for (String cluster : existingClusters) {
|
||||||
int count = clusterRefs.get(cluster).refCount.decrementAndGet();
|
int count = clusterRefs.get(cluster).refCount.decrementAndGet();
|
||||||
|
|
@ -904,64 +872,6 @@ final class XdsNameResolver extends NameResolver {
|
||||||
.build())
|
.build())
|
||||||
.setServiceConfig(emptyServiceConfig)
|
.setServiceConfig(emptyServiceConfig)
|
||||||
.build());
|
.build());
|
||||||
receivedConfig = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void cleanUpRouteDiscoveryState() {
|
|
||||||
if (routeDiscoveryState != null) {
|
|
||||||
String rdsName = routeDiscoveryState.resourceName;
|
|
||||||
logger.log(XdsLogLevel.INFO, "Stop watching RDS resource {0}", rdsName);
|
|
||||||
xdsClient.cancelXdsResourceWatch(XdsRouteConfigureResource.getInstance(), rdsName,
|
|
||||||
routeDiscoveryState);
|
|
||||||
routeDiscoveryState = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Discovery state for RouteConfiguration resource. One instance for each Listener resource
|
|
||||||
* update.
|
|
||||||
*/
|
|
||||||
private class RouteDiscoveryState implements ResourceWatcher<RdsUpdate> {
|
|
||||||
private final String resourceName;
|
|
||||||
private final long httpMaxStreamDurationNano;
|
|
||||||
@Nullable
|
|
||||||
private final List<NamedFilterConfig> filterConfigs;
|
|
||||||
|
|
||||||
private RouteDiscoveryState(String resourceName, long httpMaxStreamDurationNano,
|
|
||||||
@Nullable List<NamedFilterConfig> filterConfigs) {
|
|
||||||
this.resourceName = resourceName;
|
|
||||||
this.httpMaxStreamDurationNano = httpMaxStreamDurationNano;
|
|
||||||
this.filterConfigs = filterConfigs;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onChanged(final RdsUpdate update) {
|
|
||||||
if (RouteDiscoveryState.this != routeDiscoveryState) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
logger.log(XdsLogLevel.INFO, "Received RDS resource update: {0}", update);
|
|
||||||
updateRoutes(update.virtualHosts, httpMaxStreamDurationNano, filterConfigs);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onError(final Status error) {
|
|
||||||
if (RouteDiscoveryState.this != routeDiscoveryState || receivedConfig) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
listener.onError(Status.UNAVAILABLE.withCause(error.getCause()).withDescription(
|
|
||||||
String.format("Unable to load RDS %s. xDS server returned: %s: %s",
|
|
||||||
resourceName, error.getCode(), error.getDescription())));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onResourceDoesNotExist(final String resourceName) {
|
|
||||||
if (RouteDiscoveryState.this != routeDiscoveryState) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String error = "RDS resource does not exist: " + resourceName;
|
|
||||||
logger.log(XdsLogLevel.INFO, error);
|
|
||||||
cleanUpRoutes(error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ public final class XdsNameResolverProvider extends NameResolverProvider {
|
||||||
args.getServiceConfigParser(), args.getSynchronizationContext(),
|
args.getServiceConfigParser(), args.getSynchronizationContext(),
|
||||||
args.getScheduledExecutorService(),
|
args.getScheduledExecutorService(),
|
||||||
bootstrapOverride,
|
bootstrapOverride,
|
||||||
args.getMetricRecorder());
|
args.getMetricRecorder(), args);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package io.grpc.xds;
|
package io.grpc.xds;
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
import static io.grpc.StatusMatcher.statusHasCode;
|
||||||
import static io.grpc.xds.XdsClusterResource.CdsUpdate.ClusterType.AGGREGATE;
|
import static io.grpc.xds.XdsClusterResource.CdsUpdate.ClusterType.AGGREGATE;
|
||||||
import static io.grpc.xds.XdsClusterResource.CdsUpdate.ClusterType.EDS;
|
import static io.grpc.xds.XdsClusterResource.CdsUpdate.ClusterType.EDS;
|
||||||
import static io.grpc.xds.XdsTestControlPlaneService.ADS_TYPE_URL_CDS;
|
import static io.grpc.xds.XdsTestControlPlaneService.ADS_TYPE_URL_CDS;
|
||||||
|
|
@ -32,7 +33,6 @@ import static io.grpc.xds.client.CommonBootstrapperTestUtils.SERVER_URI;
|
||||||
import static org.mockito.AdditionalAnswers.delegatesTo;
|
import static org.mockito.AdditionalAnswers.delegatesTo;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.argThat;
|
import static org.mockito.ArgumentMatchers.argThat;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
|
||||||
import static org.mockito.Mockito.atLeastOnce;
|
import static org.mockito.Mockito.atLeastOnce;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.never;
|
||||||
|
|
@ -47,24 +47,26 @@ import io.envoyproxy.envoy.config.endpoint.v3.ClusterLoadAssignment;
|
||||||
import io.envoyproxy.envoy.config.listener.v3.Listener;
|
import io.envoyproxy.envoy.config.listener.v3.Listener;
|
||||||
import io.envoyproxy.envoy.config.route.v3.RouteConfiguration;
|
import io.envoyproxy.envoy.config.route.v3.RouteConfiguration;
|
||||||
import io.grpc.BindableService;
|
import io.grpc.BindableService;
|
||||||
|
import io.grpc.ChannelLogger;
|
||||||
import io.grpc.ManagedChannel;
|
import io.grpc.ManagedChannel;
|
||||||
|
import io.grpc.NameResolver;
|
||||||
import io.grpc.Server;
|
import io.grpc.Server;
|
||||||
import io.grpc.Status;
|
import io.grpc.Status;
|
||||||
import io.grpc.StatusOr;
|
import io.grpc.StatusOr;
|
||||||
|
import io.grpc.StatusOrMatcher;
|
||||||
import io.grpc.SynchronizationContext;
|
import io.grpc.SynchronizationContext;
|
||||||
import io.grpc.inprocess.InProcessChannelBuilder;
|
import io.grpc.inprocess.InProcessChannelBuilder;
|
||||||
import io.grpc.inprocess.InProcessServerBuilder;
|
import io.grpc.inprocess.InProcessServerBuilder;
|
||||||
import io.grpc.internal.ExponentialBackoffPolicy;
|
import io.grpc.internal.ExponentialBackoffPolicy;
|
||||||
import io.grpc.internal.FakeClock;
|
import io.grpc.internal.FakeClock;
|
||||||
|
import io.grpc.internal.GrpcUtil;
|
||||||
import io.grpc.testing.GrpcCleanupRule;
|
import io.grpc.testing.GrpcCleanupRule;
|
||||||
|
import io.grpc.xds.XdsClusterResource.CdsUpdate;
|
||||||
import io.grpc.xds.XdsConfig.XdsClusterConfig;
|
import io.grpc.xds.XdsConfig.XdsClusterConfig;
|
||||||
import io.grpc.xds.XdsEndpointResource.EdsUpdate;
|
import io.grpc.xds.XdsEndpointResource.EdsUpdate;
|
||||||
import io.grpc.xds.XdsListenerResource.LdsUpdate;
|
|
||||||
import io.grpc.xds.client.CommonBootstrapperTestUtils;
|
import io.grpc.xds.client.CommonBootstrapperTestUtils;
|
||||||
import io.grpc.xds.client.XdsClient;
|
|
||||||
import io.grpc.xds.client.XdsClientImpl;
|
import io.grpc.xds.client.XdsClientImpl;
|
||||||
import io.grpc.xds.client.XdsClientMetricReporter;
|
import io.grpc.xds.client.XdsClientMetricReporter;
|
||||||
import io.grpc.xds.client.XdsResourceType;
|
|
||||||
import io.grpc.xds.client.XdsTransportFactory;
|
import io.grpc.xds.client.XdsTransportFactory;
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
@ -77,7 +79,7 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
@ -108,7 +110,9 @@ public class XdsDependencyManagerTest {
|
||||||
private XdsClientMetricReporter xdsClientMetricReporter;
|
private XdsClientMetricReporter xdsClientMetricReporter;
|
||||||
|
|
||||||
private final SynchronizationContext syncContext =
|
private final SynchronizationContext syncContext =
|
||||||
new SynchronizationContext(mock(Thread.UncaughtExceptionHandler.class));
|
new SynchronizationContext((t, e) -> {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
});
|
||||||
|
|
||||||
private ManagedChannel channel;
|
private ManagedChannel channel;
|
||||||
private XdsClientImpl xdsClient;
|
private XdsClientImpl xdsClient;
|
||||||
|
|
@ -133,9 +137,17 @@ public class XdsDependencyManagerTest {
|
||||||
private XdsConfig defaultXdsConfig; // set in setUp()
|
private XdsConfig defaultXdsConfig; // set in setUp()
|
||||||
|
|
||||||
@Captor
|
@Captor
|
||||||
private ArgumentCaptor<XdsConfig> xdsConfigCaptor;
|
private ArgumentCaptor<StatusOr<XdsConfig>> xdsUpdateCaptor;
|
||||||
@Captor
|
private final NameResolver.Args nameResolverArgs = NameResolver.Args.newBuilder()
|
||||||
private ArgumentCaptor<Status> statusCaptor;
|
.setDefaultPort(8080)
|
||||||
|
.setProxyDetector(GrpcUtil.DEFAULT_PROXY_DETECTOR)
|
||||||
|
.setSynchronizationContext(syncContext)
|
||||||
|
.setServiceConfigParser(mock(NameResolver.ServiceConfigParser.class))
|
||||||
|
.setChannelLogger(mock(ChannelLogger.class))
|
||||||
|
.setScheduledExecutorService(fakeClock.getScheduledExecutorService())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
private final ScheduledExecutorService scheduler = fakeClock.getScheduledExecutorService();
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
|
|
@ -180,36 +192,36 @@ public class XdsDependencyManagerTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void verify_basic_config() {
|
public void verify_basic_config() {
|
||||||
xdsDependencyManager = new XdsDependencyManager(
|
xdsDependencyManager = new XdsDependencyManager(xdsClient, xdsConfigWatcher, syncContext,
|
||||||
xdsClient, xdsConfigWatcher, syncContext, serverName, serverName);
|
serverName, serverName, nameResolverArgs, scheduler);
|
||||||
|
|
||||||
verify(xdsConfigWatcher, timeout(1000)).onUpdate(defaultXdsConfig);
|
verify(xdsConfigWatcher, timeout(1000)).onUpdate(StatusOr.fromValue(defaultXdsConfig));
|
||||||
testWatcher.verifyStats(1, 0, 0);
|
testWatcher.verifyStats(1, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void verify_config_update() {
|
public void verify_config_update() {
|
||||||
xdsDependencyManager = new XdsDependencyManager(
|
xdsDependencyManager = new XdsDependencyManager(xdsClient, xdsConfigWatcher, syncContext,
|
||||||
xdsClient, xdsConfigWatcher, syncContext, serverName, serverName);
|
serverName, serverName, nameResolverArgs, scheduler);
|
||||||
|
|
||||||
InOrder inOrder = Mockito.inOrder(xdsConfigWatcher);
|
InOrder inOrder = Mockito.inOrder(xdsConfigWatcher);
|
||||||
inOrder.verify(xdsConfigWatcher, timeout(1000)).onUpdate(defaultXdsConfig);
|
inOrder.verify(xdsConfigWatcher, timeout(1000)).onUpdate(StatusOr.fromValue(defaultXdsConfig));
|
||||||
testWatcher.verifyStats(1, 0, 0);
|
testWatcher.verifyStats(1, 0);
|
||||||
assertThat(testWatcher.lastConfig).isEqualTo(defaultXdsConfig);
|
assertThat(testWatcher.lastConfig).isEqualTo(defaultXdsConfig);
|
||||||
|
|
||||||
XdsTestUtils.setAdsConfig(controlPlaneService, serverName, "RDS2", "CDS2", "EDS2",
|
XdsTestUtils.setAdsConfig(controlPlaneService, serverName, "RDS2", "CDS2", "EDS2",
|
||||||
ENDPOINT_HOSTNAME + "2", ENDPOINT_PORT + 2);
|
ENDPOINT_HOSTNAME + "2", ENDPOINT_PORT + 2);
|
||||||
inOrder.verify(xdsConfigWatcher, timeout(1000)).onUpdate(ArgumentMatchers.notNull());
|
inOrder.verify(xdsConfigWatcher, timeout(1000)).onUpdate(ArgumentMatchers.notNull());
|
||||||
testWatcher.verifyStats(2, 0, 0);
|
testWatcher.verifyStats(2, 0);
|
||||||
assertThat(testWatcher.lastConfig).isNotEqualTo(defaultXdsConfig);
|
assertThat(testWatcher.lastConfig).isNotEqualTo(defaultXdsConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void verify_simple_aggregate() {
|
public void verify_simple_aggregate() {
|
||||||
InOrder inOrder = Mockito.inOrder(xdsConfigWatcher);
|
InOrder inOrder = Mockito.inOrder(xdsConfigWatcher);
|
||||||
xdsDependencyManager = new XdsDependencyManager(
|
xdsDependencyManager = new XdsDependencyManager(xdsClient, xdsConfigWatcher, syncContext,
|
||||||
xdsClient, xdsConfigWatcher, syncContext, serverName, serverName);
|
serverName, serverName, nameResolverArgs, scheduler);
|
||||||
inOrder.verify(xdsConfigWatcher, timeout(1000)).onUpdate(defaultXdsConfig);
|
inOrder.verify(xdsConfigWatcher, timeout(1000)).onUpdate(StatusOr.fromValue(defaultXdsConfig));
|
||||||
|
|
||||||
List<String> childNames = Arrays.asList("clusterC", "clusterB", "clusterA");
|
List<String> childNames = Arrays.asList("clusterC", "clusterB", "clusterA");
|
||||||
String rootName = "root_c";
|
String rootName = "root_c";
|
||||||
|
|
@ -226,14 +238,14 @@ public class XdsDependencyManagerTest {
|
||||||
testWatcher.lastConfig.getClusters();
|
testWatcher.lastConfig.getClusters();
|
||||||
assertThat(lastConfigClusters).hasSize(childNames.size() + 1);
|
assertThat(lastConfigClusters).hasSize(childNames.size() + 1);
|
||||||
StatusOr<XdsClusterConfig> rootC = lastConfigClusters.get(rootName);
|
StatusOr<XdsClusterConfig> rootC = lastConfigClusters.get(rootName);
|
||||||
XdsClusterResource.CdsUpdate rootUpdate = rootC.getValue().getClusterResource();
|
CdsUpdate rootUpdate = rootC.getValue().getClusterResource();
|
||||||
assertThat(rootUpdate.clusterType()).isEqualTo(AGGREGATE);
|
assertThat(rootUpdate.clusterType()).isEqualTo(AGGREGATE);
|
||||||
assertThat(rootUpdate.prioritizedClusterNames()).isEqualTo(childNames);
|
assertThat(rootUpdate.prioritizedClusterNames()).isEqualTo(childNames);
|
||||||
|
|
||||||
for (String childName : childNames) {
|
for (String childName : childNames) {
|
||||||
assertThat(lastConfigClusters).containsKey(childName);
|
assertThat(lastConfigClusters).containsKey(childName);
|
||||||
StatusOr<XdsClusterConfig> childConfigOr = lastConfigClusters.get(childName);
|
StatusOr<XdsClusterConfig> childConfigOr = lastConfigClusters.get(childName);
|
||||||
XdsClusterResource.CdsUpdate childResource =
|
CdsUpdate childResource =
|
||||||
childConfigOr.getValue().getClusterResource();
|
childConfigOr.getValue().getClusterResource();
|
||||||
assertThat(childResource.clusterType()).isEqualTo(EDS);
|
assertThat(childResource.clusterType()).isEqualTo(EDS);
|
||||||
assertThat(childResource.edsServiceName()).isEqualTo(getEdsNameForCluster(childName));
|
assertThat(childResource.edsServiceName()).isEqualTo(getEdsNameForCluster(childName));
|
||||||
|
|
@ -266,54 +278,57 @@ public class XdsDependencyManagerTest {
|
||||||
List<String> childNames2 = Arrays.asList("clusterA", "clusterX");
|
List<String> childNames2 = Arrays.asList("clusterA", "clusterX");
|
||||||
XdsTestUtils.addAggregateToExistingConfig(controlPlaneService, rootName2, childNames2);
|
XdsTestUtils.addAggregateToExistingConfig(controlPlaneService, rootName2, childNames2);
|
||||||
|
|
||||||
xdsDependencyManager = new XdsDependencyManager(
|
xdsDependencyManager = new XdsDependencyManager(xdsClient, xdsConfigWatcher, syncContext,
|
||||||
xdsClient, xdsConfigWatcher, syncContext, serverName, serverName);
|
serverName, serverName, nameResolverArgs, scheduler);
|
||||||
inOrder.verify(xdsConfigWatcher, timeout(1000)).onUpdate(any());
|
inOrder.verify(xdsConfigWatcher, timeout(1000)).onUpdate(any());
|
||||||
|
|
||||||
Closeable subscription1 = xdsDependencyManager.subscribeToCluster(rootName1);
|
Closeable subscription1 = xdsDependencyManager.subscribeToCluster(rootName1);
|
||||||
inOrder.verify(xdsConfigWatcher, timeout(1000)).onUpdate(any());
|
inOrder.verify(xdsConfigWatcher, timeout(1000)).onUpdate(any());
|
||||||
|
|
||||||
Closeable subscription2 = xdsDependencyManager.subscribeToCluster(rootName2);
|
Closeable subscription2 = xdsDependencyManager.subscribeToCluster(rootName2);
|
||||||
inOrder.verify(xdsConfigWatcher, timeout(1000)).onUpdate(xdsConfigCaptor.capture());
|
inOrder.verify(xdsConfigWatcher, timeout(1000)).onUpdate(xdsUpdateCaptor.capture());
|
||||||
testWatcher.verifyStats(3, 0, 0);
|
testWatcher.verifyStats(3, 0);
|
||||||
ImmutableSet.Builder<String> builder = ImmutableSet.builder();
|
ImmutableSet.Builder<String> builder = ImmutableSet.builder();
|
||||||
Set<String> expectedClusters = builder.add(rootName1).add(rootName2).add(CLUSTER_NAME)
|
Set<String> expectedClusters = builder.add(rootName1).add(rootName2).add(CLUSTER_NAME)
|
||||||
.addAll(childNames).addAll(childNames2).build();
|
.addAll(childNames).addAll(childNames2).build();
|
||||||
assertThat(xdsConfigCaptor.getValue().getClusters().keySet()).isEqualTo(expectedClusters);
|
assertThat(xdsUpdateCaptor.getValue().getValue().getClusters().keySet())
|
||||||
|
.isEqualTo(expectedClusters);
|
||||||
|
|
||||||
// Close 1 subscription shouldn't affect the other or RDS subscriptions
|
// Close 1 subscription shouldn't affect the other or RDS subscriptions
|
||||||
subscription1.close();
|
subscription1.close();
|
||||||
inOrder.verify(xdsConfigWatcher, timeout(1000)).onUpdate(xdsConfigCaptor.capture());
|
inOrder.verify(xdsConfigWatcher, timeout(1000)).onUpdate(xdsUpdateCaptor.capture());
|
||||||
builder = ImmutableSet.builder();
|
builder = ImmutableSet.builder();
|
||||||
Set<String> expectedClusters2 =
|
Set<String> expectedClusters2 =
|
||||||
builder.add(rootName2).add(CLUSTER_NAME).addAll(childNames2).build();
|
builder.add(rootName2).add(CLUSTER_NAME).addAll(childNames2).build();
|
||||||
assertThat(xdsConfigCaptor.getValue().getClusters().keySet()).isEqualTo(expectedClusters2);
|
assertThat(xdsUpdateCaptor.getValue().getValue().getClusters().keySet())
|
||||||
|
.isEqualTo(expectedClusters2);
|
||||||
|
|
||||||
subscription2.close();
|
subscription2.close();
|
||||||
inOrder.verify(xdsConfigWatcher, timeout(1000)).onUpdate(defaultXdsConfig);
|
inOrder.verify(xdsConfigWatcher, timeout(1000)).onUpdate(StatusOr.fromValue(defaultXdsConfig));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDelayedSubscription() {
|
public void testDelayedSubscription() {
|
||||||
InOrder inOrder = Mockito.inOrder(xdsConfigWatcher);
|
InOrder inOrder = Mockito.inOrder(xdsConfigWatcher);
|
||||||
xdsDependencyManager = new XdsDependencyManager(
|
xdsDependencyManager = new XdsDependencyManager(xdsClient, xdsConfigWatcher, syncContext,
|
||||||
xdsClient, xdsConfigWatcher, syncContext, serverName, serverName);
|
serverName, serverName, nameResolverArgs, scheduler);
|
||||||
inOrder.verify(xdsConfigWatcher, timeout(1000)).onUpdate(defaultXdsConfig);
|
inOrder.verify(xdsConfigWatcher, timeout(1000)).onUpdate(StatusOr.fromValue(defaultXdsConfig));
|
||||||
|
|
||||||
String rootName1 = "root_c";
|
String rootName1 = "root_c";
|
||||||
|
|
||||||
Closeable subscription1 = xdsDependencyManager.subscribeToCluster(rootName1);
|
Closeable subscription1 = xdsDependencyManager.subscribeToCluster(rootName1);
|
||||||
assertThat(subscription1).isNotNull();
|
assertThat(subscription1).isNotNull();
|
||||||
fakeClock.forwardTime(16, TimeUnit.SECONDS);
|
fakeClock.forwardTime(16, TimeUnit.SECONDS);
|
||||||
inOrder.verify(xdsConfigWatcher).onUpdate(xdsConfigCaptor.capture());
|
inOrder.verify(xdsConfigWatcher).onUpdate(xdsUpdateCaptor.capture());
|
||||||
assertThat(xdsConfigCaptor.getValue().getClusters().get(rootName1).toString()).isEqualTo(
|
Status status = xdsUpdateCaptor.getValue().getValue().getClusters().get(rootName1).getStatus();
|
||||||
StatusOr.fromStatus(Status.UNAVAILABLE.withDescription(
|
assertThat(status.getCode()).isEqualTo(Status.Code.UNAVAILABLE);
|
||||||
"No " + toContextStr(CLUSTER_TYPE_NAME, rootName1))).toString());
|
assertThat(status.getDescription()).contains(rootName1);
|
||||||
|
|
||||||
List<String> childNames = Arrays.asList("clusterC", "clusterB", "clusterA");
|
List<String> childNames = Arrays.asList("clusterC", "clusterB", "clusterA");
|
||||||
XdsTestUtils.addAggregateToExistingConfig(controlPlaneService, rootName1, childNames);
|
XdsTestUtils.addAggregateToExistingConfig(controlPlaneService, rootName1, childNames);
|
||||||
inOrder.verify(xdsConfigWatcher).onUpdate(xdsConfigCaptor.capture());
|
inOrder.verify(xdsConfigWatcher).onUpdate(xdsUpdateCaptor.capture());
|
||||||
assertThat(xdsConfigCaptor.getValue().getClusters().get(rootName1).hasValue()).isTrue();
|
assertThat(xdsUpdateCaptor.getValue().getValue().getClusters().get(rootName1).hasValue())
|
||||||
|
.isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -342,109 +357,99 @@ public class XdsDependencyManagerTest {
|
||||||
edsMap.put("garbageEds", clusterLoadAssignment);
|
edsMap.put("garbageEds", clusterLoadAssignment);
|
||||||
controlPlaneService.setXdsConfig(ADS_TYPE_URL_EDS, edsMap);
|
controlPlaneService.setXdsConfig(ADS_TYPE_URL_EDS, edsMap);
|
||||||
|
|
||||||
xdsDependencyManager = new XdsDependencyManager(
|
xdsDependencyManager = new XdsDependencyManager(xdsClient, xdsConfigWatcher, syncContext,
|
||||||
xdsClient, xdsConfigWatcher, syncContext, serverName, serverName);
|
serverName, serverName, nameResolverArgs, scheduler);
|
||||||
|
|
||||||
fakeClock.forwardTime(16, TimeUnit.SECONDS);
|
fakeClock.forwardTime(16, TimeUnit.SECONDS);
|
||||||
verify(xdsConfigWatcher, timeout(1000)).onUpdate(xdsConfigCaptor.capture());
|
verify(xdsConfigWatcher, timeout(1000)).onUpdate(xdsUpdateCaptor.capture());
|
||||||
|
|
||||||
List<StatusOr<XdsClusterConfig>> returnedClusters = new ArrayList<>();
|
List<StatusOr<XdsClusterConfig>> returnedClusters = new ArrayList<>();
|
||||||
for (String childName : childNames) {
|
for (String childName : childNames) {
|
||||||
returnedClusters.add(xdsConfigCaptor.getValue().getClusters().get(childName));
|
returnedClusters.add(xdsUpdateCaptor.getValue().getValue().getClusters().get(childName));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that missing cluster reported Status and the other 2 are present
|
// Check that missing cluster reported Status and the other 2 are present
|
||||||
Status expectedClusterStatus = Status.UNAVAILABLE.withDescription(
|
|
||||||
"No " + toContextStr(CLUSTER_TYPE_NAME, childNames.get(2)));
|
|
||||||
StatusOr<XdsClusterConfig> missingCluster = returnedClusters.get(2);
|
StatusOr<XdsClusterConfig> missingCluster = returnedClusters.get(2);
|
||||||
assertThat(missingCluster.getStatus().toString()).isEqualTo(expectedClusterStatus.toString());
|
assertThat(missingCluster.getStatus().getCode()).isEqualTo(Status.Code.UNAVAILABLE);
|
||||||
|
assertThat(missingCluster.getStatus().getDescription()).contains(childNames.get(2));
|
||||||
assertThat(returnedClusters.get(0).hasValue()).isTrue();
|
assertThat(returnedClusters.get(0).hasValue()).isTrue();
|
||||||
assertThat(returnedClusters.get(1).hasValue()).isTrue();
|
assertThat(returnedClusters.get(1).hasValue()).isTrue();
|
||||||
|
|
||||||
// Check that missing EDS reported Status, the other one is present and the garbage EDS is not
|
// Check that missing EDS reported Status, the other one is present and the garbage EDS is not
|
||||||
Status expectedEdsStatus = Status.UNAVAILABLE.withDescription(
|
|
||||||
"No " + toContextStr(ENDPOINT_TYPE_NAME, XdsTestUtils.EDS_NAME + 1));
|
|
||||||
assertThat(getEndpoint(returnedClusters.get(0)).hasValue()).isTrue();
|
assertThat(getEndpoint(returnedClusters.get(0)).hasValue()).isTrue();
|
||||||
assertThat(getEndpoint(returnedClusters.get(1)).hasValue()).isFalse();
|
assertThat(getEndpoint(returnedClusters.get(1)).getStatus().getCode())
|
||||||
assertThat(getEndpoint(returnedClusters.get(1)).getStatus().toString())
|
.isEqualTo(Status.Code.UNAVAILABLE);
|
||||||
.isEqualTo(expectedEdsStatus.toString());
|
assertThat(getEndpoint(returnedClusters.get(1)).getStatus().getDescription())
|
||||||
|
.contains(XdsTestUtils.EDS_NAME + 1);
|
||||||
|
|
||||||
verify(xdsConfigWatcher, never()).onResourceDoesNotExist(any());
|
verify(xdsConfigWatcher, never()).onUpdate(
|
||||||
testWatcher.verifyStats(1, 0, 0);
|
argThat(StatusOrMatcher.hasStatus(statusHasCode(Status.Code.UNAVAILABLE))));
|
||||||
|
testWatcher.verifyStats(1, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMissingLds() {
|
public void testMissingLds() {
|
||||||
xdsDependencyManager = new XdsDependencyManager(
|
String ldsName = "badLdsName";
|
||||||
xdsClient, xdsConfigWatcher, syncContext, serverName, "badLdsName");
|
xdsDependencyManager = new XdsDependencyManager(xdsClient, xdsConfigWatcher, syncContext,
|
||||||
|
serverName, ldsName, nameResolverArgs, scheduler);
|
||||||
|
|
||||||
fakeClock.forwardTime(16, TimeUnit.SECONDS);
|
fakeClock.forwardTime(16, TimeUnit.SECONDS);
|
||||||
verify(xdsConfigWatcher, timeout(1000)).onResourceDoesNotExist(
|
verify(xdsConfigWatcher, timeout(1000)).onUpdate(
|
||||||
toContextStr(XdsListenerResource.getInstance().typeName(), "badLdsName"));
|
argThat(StatusOrMatcher.hasStatus(statusHasCode(Status.Code.UNAVAILABLE)
|
||||||
|
.andDescriptionContains(ldsName))));
|
||||||
|
|
||||||
testWatcher.verifyStats(0, 0, 1);
|
testWatcher.verifyStats(0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTcpListenerErrors() {
|
||||||
|
Listener serverListener =
|
||||||
|
ControlPlaneRule.buildServerListener().toBuilder().setName(serverName).build();
|
||||||
|
controlPlaneService.setXdsConfig(ADS_TYPE_URL_LDS, ImmutableMap.of(serverName, serverListener));
|
||||||
|
xdsDependencyManager = new XdsDependencyManager(xdsClient, xdsConfigWatcher, syncContext,
|
||||||
|
serverName, serverName, nameResolverArgs, scheduler);
|
||||||
|
|
||||||
|
fakeClock.forwardTime(16, TimeUnit.SECONDS);
|
||||||
|
verify(xdsConfigWatcher, timeout(1000)).onUpdate(
|
||||||
|
argThat(StatusOrMatcher.hasStatus(
|
||||||
|
statusHasCode(Status.Code.UNAVAILABLE).andDescriptionContains("Not an API listener"))));
|
||||||
|
|
||||||
|
testWatcher.verifyStats(0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMissingRds() {
|
public void testMissingRds() {
|
||||||
Listener serverListener = ControlPlaneRule.buildServerListener();
|
String rdsName = "badRdsName";
|
||||||
Listener clientListener =
|
Listener clientListener = ControlPlaneRule.buildClientListener(serverName, serverName, rdsName);
|
||||||
ControlPlaneRule.buildClientListener(serverName, serverName, "badRdsName");
|
|
||||||
controlPlaneService.setXdsConfig(ADS_TYPE_URL_LDS,
|
controlPlaneService.setXdsConfig(ADS_TYPE_URL_LDS,
|
||||||
ImmutableMap.of(XdsTestUtils.SERVER_LISTENER, serverListener, serverName, clientListener));
|
ImmutableMap.of(serverName, clientListener));
|
||||||
|
|
||||||
xdsDependencyManager = new XdsDependencyManager(
|
xdsDependencyManager = new XdsDependencyManager(xdsClient, xdsConfigWatcher, syncContext,
|
||||||
xdsClient, xdsConfigWatcher, syncContext, serverName, serverName);
|
serverName, serverName, nameResolverArgs, scheduler);
|
||||||
|
|
||||||
fakeClock.forwardTime(16, TimeUnit.SECONDS);
|
fakeClock.forwardTime(16, TimeUnit.SECONDS);
|
||||||
verify(xdsConfigWatcher, timeout(1000)).onResourceDoesNotExist(
|
verify(xdsConfigWatcher, timeout(1000)).onUpdate(
|
||||||
toContextStr(XdsRouteConfigureResource.getInstance().typeName(), "badRdsName"));
|
argThat(StatusOrMatcher.hasStatus(statusHasCode(Status.Code.UNAVAILABLE)
|
||||||
|
.andDescriptionContains(rdsName))));
|
||||||
|
|
||||||
testWatcher.verifyStats(0, 0, 1);
|
testWatcher.verifyStats(0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUpdateToMissingVirtualHost() {
|
public void testUpdateToMissingVirtualHost() {
|
||||||
InOrder inOrder = Mockito.inOrder(xdsConfigWatcher);
|
RouteConfiguration routeConfig = XdsTestUtils.buildRouteConfiguration(
|
||||||
WrappedXdsClient wrappedXdsClient = new WrappedXdsClient(xdsClient, syncContext);
|
"wrong-virtual-host", XdsTestUtils.RDS_NAME, XdsTestUtils.CLUSTER_NAME);
|
||||||
xdsDependencyManager = new XdsDependencyManager(
|
controlPlaneService.setXdsConfig(
|
||||||
wrappedXdsClient, xdsConfigWatcher, syncContext, serverName, serverName);
|
ADS_TYPE_URL_RDS, ImmutableMap.of(XdsTestUtils.RDS_NAME, routeConfig));
|
||||||
inOrder.verify(xdsConfigWatcher, timeout(1000)).onUpdate(defaultXdsConfig);
|
xdsDependencyManager = new XdsDependencyManager(xdsClient, xdsConfigWatcher, syncContext,
|
||||||
|
serverName, serverName, nameResolverArgs, scheduler);
|
||||||
|
|
||||||
// Update with a config that has a virtual host that doesn't match the server name
|
// Update with a config that has a virtual host that doesn't match the server name
|
||||||
wrappedXdsClient.deliverLdsUpdate(0L, buildUnmatchedVirtualHosts());
|
verify(xdsConfigWatcher, timeout(1000)).onUpdate(xdsUpdateCaptor.capture());
|
||||||
inOrder.verify(xdsConfigWatcher, timeout(1000)).onError(any(), statusCaptor.capture());
|
assertThat(xdsUpdateCaptor.getValue().getStatus().getDescription())
|
||||||
assertThat(statusCaptor.getValue().getDescription())
|
.contains("Failed to find virtual host matching hostname: " + serverName);
|
||||||
.isEqualTo("Failed to find virtual host matching hostname: " + serverName);
|
|
||||||
|
|
||||||
testWatcher.verifyStats(1, 1, 0);
|
testWatcher.verifyStats(0, 1);
|
||||||
|
|
||||||
wrappedXdsClient.shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<io.grpc.xds.VirtualHost> buildUnmatchedVirtualHosts() {
|
|
||||||
io.grpc.xds.VirtualHost.Route route1 =
|
|
||||||
io.grpc.xds.VirtualHost.Route.forAction(
|
|
||||||
io.grpc.xds.VirtualHost.Route.RouteMatch.withPathExactOnly("/GreetService/bye"),
|
|
||||||
io.grpc.xds.VirtualHost.Route.RouteAction.forCluster(
|
|
||||||
"cluster-bar.googleapis.com", Collections.emptyList(),
|
|
||||||
TimeUnit.SECONDS.toNanos(15L), null, false), ImmutableMap.of());
|
|
||||||
io.grpc.xds.VirtualHost.Route route2 =
|
|
||||||
io.grpc.xds.VirtualHost.Route.forAction(
|
|
||||||
io.grpc.xds.VirtualHost.Route.RouteMatch.withPathExactOnly("/HelloService/hi"),
|
|
||||||
io.grpc.xds.VirtualHost.Route.RouteAction.forCluster(
|
|
||||||
"cluster-foo.googleapis.com", Collections.emptyList(),
|
|
||||||
TimeUnit.SECONDS.toNanos(15L), null, false),
|
|
||||||
ImmutableMap.of());
|
|
||||||
return Arrays.asList(
|
|
||||||
io.grpc.xds.VirtualHost.create("virtualhost-foo", Collections.singletonList("hello"
|
|
||||||
+ ".googleapis.com"),
|
|
||||||
Collections.singletonList(route1),
|
|
||||||
ImmutableMap.of()),
|
|
||||||
io.grpc.xds.VirtualHost.create("virtualhost-bar", Collections.singletonList("hi"
|
|
||||||
+ ".googleapis.com"),
|
|
||||||
Collections.singletonList(route2),
|
|
||||||
ImmutableMap.of()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -452,37 +457,32 @@ public class XdsDependencyManagerTest {
|
||||||
String ldsResourceName =
|
String ldsResourceName =
|
||||||
"xdstp://unknown.example.com/envoy.config.listener.v3.Listener/listener1";
|
"xdstp://unknown.example.com/envoy.config.listener.v3.Listener/listener1";
|
||||||
|
|
||||||
xdsDependencyManager = new XdsDependencyManager(
|
xdsDependencyManager = new XdsDependencyManager(xdsClient, xdsConfigWatcher, syncContext,
|
||||||
xdsClient, xdsConfigWatcher, syncContext, serverName, ldsResourceName);
|
serverName, ldsResourceName, nameResolverArgs, scheduler);
|
||||||
|
|
||||||
Status expectedStatus = Status.INVALID_ARGUMENT.withDescription(
|
verify(xdsConfigWatcher, timeout(1000)).onUpdate(
|
||||||
"Wrong configuration: xds server does not exist for resource " + ldsResourceName);
|
argThat(StatusOrMatcher.hasStatus(
|
||||||
String context = toContextStr(XdsListenerResource.getInstance().typeName(), ldsResourceName);
|
statusHasCode(Status.Code.UNAVAILABLE).andDescriptionContains(ldsResourceName))));
|
||||||
verify(xdsConfigWatcher, timeout(1000))
|
|
||||||
.onError(eq(context), argThat(new XdsTestUtils.StatusMatcher(expectedStatus)));
|
|
||||||
|
|
||||||
fakeClock.forwardTime(16, TimeUnit.SECONDS);
|
fakeClock.forwardTime(16, TimeUnit.SECONDS);
|
||||||
testWatcher.verifyStats(0, 1, 0);
|
testWatcher.verifyStats(0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testChangeRdsName_fromLds() {
|
public void testChangeRdsName_fromLds() {
|
||||||
// TODO implement
|
|
||||||
InOrder inOrder = Mockito.inOrder(xdsConfigWatcher);
|
InOrder inOrder = Mockito.inOrder(xdsConfigWatcher);
|
||||||
Listener serverListener = ControlPlaneRule.buildServerListener();
|
xdsDependencyManager = new XdsDependencyManager(xdsClient, xdsConfigWatcher, syncContext,
|
||||||
|
serverName, serverName, nameResolverArgs, scheduler);
|
||||||
xdsDependencyManager = new XdsDependencyManager(
|
inOrder.verify(xdsConfigWatcher, timeout(1000)).onUpdate(StatusOr.fromValue(defaultXdsConfig));
|
||||||
xdsClient, xdsConfigWatcher, syncContext, serverName, serverName);
|
|
||||||
inOrder.verify(xdsConfigWatcher, timeout(1000)).onUpdate(defaultXdsConfig);
|
|
||||||
|
|
||||||
String newRdsName = "newRdsName1";
|
String newRdsName = "newRdsName1";
|
||||||
|
|
||||||
Listener clientListener = buildInlineClientListener(newRdsName, CLUSTER_NAME);
|
Listener clientListener = buildInlineClientListener(newRdsName, CLUSTER_NAME);
|
||||||
controlPlaneService.setXdsConfig(ADS_TYPE_URL_LDS,
|
controlPlaneService.setXdsConfig(ADS_TYPE_URL_LDS,
|
||||||
ImmutableMap.of(XdsTestUtils.SERVER_LISTENER, serverListener, serverName, clientListener));
|
ImmutableMap.of(serverName, clientListener));
|
||||||
inOrder.verify(xdsConfigWatcher, timeout(1000)).onUpdate(xdsConfigCaptor.capture());
|
inOrder.verify(xdsConfigWatcher, timeout(1000)).onUpdate(xdsUpdateCaptor.capture());
|
||||||
assertThat(xdsConfigCaptor.getValue()).isNotEqualTo(defaultXdsConfig);
|
assertThat(xdsUpdateCaptor.getValue().getValue()).isNotEqualTo(defaultXdsConfig);
|
||||||
assertThat(xdsConfigCaptor.getValue().getVirtualHost().name()).isEqualTo(newRdsName);
|
assertThat(xdsUpdateCaptor.getValue().getValue().getVirtualHost().name()).isEqualTo(newRdsName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -527,22 +527,22 @@ public class XdsDependencyManagerTest {
|
||||||
|
|
||||||
// Start the actual test
|
// Start the actual test
|
||||||
InOrder inOrder = Mockito.inOrder(xdsConfigWatcher);
|
InOrder inOrder = Mockito.inOrder(xdsConfigWatcher);
|
||||||
xdsDependencyManager = new XdsDependencyManager(
|
xdsDependencyManager = new XdsDependencyManager(xdsClient, xdsConfigWatcher, syncContext,
|
||||||
xdsClient, xdsConfigWatcher, syncContext, serverName, serverName);
|
serverName, serverName, nameResolverArgs, scheduler);
|
||||||
inOrder.verify(xdsConfigWatcher, timeout(1000)).onUpdate(xdsConfigCaptor.capture());
|
inOrder.verify(xdsConfigWatcher, timeout(1000)).onUpdate(xdsUpdateCaptor.capture());
|
||||||
XdsConfig initialConfig = xdsConfigCaptor.getValue();
|
XdsConfig initialConfig = xdsUpdateCaptor.getValue().getValue();
|
||||||
|
|
||||||
// Make sure that adding subscriptions that rds points at doesn't change the config
|
// Make sure that adding subscriptions that rds points at doesn't change the config
|
||||||
Closeable rootSub = xdsDependencyManager.subscribeToCluster("root");
|
Closeable rootSub = xdsDependencyManager.subscribeToCluster("root");
|
||||||
assertThat(xdsDependencyManager.buildConfig()).isEqualTo(initialConfig);
|
assertThat(xdsDependencyManager.buildUpdate().getValue()).isEqualTo(initialConfig);
|
||||||
Closeable clusterAB11Sub = xdsDependencyManager.subscribeToCluster("clusterAB11");
|
Closeable clusterAB11Sub = xdsDependencyManager.subscribeToCluster("clusterAB11");
|
||||||
assertThat(xdsDependencyManager.buildConfig()).isEqualTo(initialConfig);
|
assertThat(xdsDependencyManager.buildUpdate().getValue()).isEqualTo(initialConfig);
|
||||||
|
|
||||||
// Make sure that closing subscriptions that rds points at doesn't change the config
|
// Make sure that closing subscriptions that rds points at doesn't change the config
|
||||||
rootSub.close();
|
rootSub.close();
|
||||||
assertThat(xdsDependencyManager.buildConfig()).isEqualTo(initialConfig);
|
assertThat(xdsDependencyManager.buildUpdate().getValue()).isEqualTo(initialConfig);
|
||||||
clusterAB11Sub.close();
|
clusterAB11Sub.close();
|
||||||
assertThat(xdsDependencyManager.buildConfig()).isEqualTo(initialConfig);
|
assertThat(xdsDependencyManager.buildUpdate().getValue()).isEqualTo(initialConfig);
|
||||||
|
|
||||||
// Make an explicit root subscription and then change RDS to point to A11
|
// Make an explicit root subscription and then change RDS to point to A11
|
||||||
rootSub = xdsDependencyManager.subscribeToCluster("root");
|
rootSub = xdsDependencyManager.subscribeToCluster("root");
|
||||||
|
|
@ -550,13 +550,14 @@ public class XdsDependencyManagerTest {
|
||||||
XdsTestUtils.buildRouteConfiguration(serverName, XdsTestUtils.RDS_NAME, "clusterA11");
|
XdsTestUtils.buildRouteConfiguration(serverName, XdsTestUtils.RDS_NAME, "clusterA11");
|
||||||
controlPlaneService.setXdsConfig(
|
controlPlaneService.setXdsConfig(
|
||||||
ADS_TYPE_URL_RDS, ImmutableMap.of(XdsTestUtils.RDS_NAME, newRouteConfig));
|
ADS_TYPE_URL_RDS, ImmutableMap.of(XdsTestUtils.RDS_NAME, newRouteConfig));
|
||||||
inOrder.verify(xdsConfigWatcher, timeout(1000)).onUpdate(xdsConfigCaptor.capture());
|
inOrder.verify(xdsConfigWatcher, timeout(1000)).onUpdate(xdsUpdateCaptor.capture());
|
||||||
assertThat(xdsConfigCaptor.getValue().getClusters().keySet().size()).isEqualTo(4);
|
assertThat(xdsUpdateCaptor.getValue().getValue().getClusters().keySet().size()).isEqualTo(4);
|
||||||
|
|
||||||
// Now that it is released, we should only have A11
|
// Now that it is released, we should only have A11
|
||||||
rootSub.close();
|
rootSub.close();
|
||||||
inOrder.verify(xdsConfigWatcher, timeout(1000)).onUpdate(xdsConfigCaptor.capture());
|
inOrder.verify(xdsConfigWatcher, timeout(1000)).onUpdate(xdsUpdateCaptor.capture());
|
||||||
assertThat(xdsConfigCaptor.getValue().getClusters().keySet()).containsExactly("clusterA11");
|
assertThat(xdsUpdateCaptor.getValue().getValue().getClusters().keySet())
|
||||||
|
.containsExactly("clusterA11");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -587,10 +588,10 @@ public class XdsDependencyManagerTest {
|
||||||
controlPlaneService.setXdsConfig(ADS_TYPE_URL_EDS, edsMap);
|
controlPlaneService.setXdsConfig(ADS_TYPE_URL_EDS, edsMap);
|
||||||
|
|
||||||
// Start the actual test
|
// Start the actual test
|
||||||
xdsDependencyManager = new XdsDependencyManager(
|
xdsDependencyManager = new XdsDependencyManager(xdsClient, xdsConfigWatcher, syncContext,
|
||||||
xdsClient, xdsConfigWatcher, syncContext, serverName, serverName);
|
serverName, serverName, nameResolverArgs, scheduler);
|
||||||
verify(xdsConfigWatcher, timeout(1000)).onUpdate(xdsConfigCaptor.capture());
|
verify(xdsConfigWatcher, timeout(1000)).onUpdate(xdsUpdateCaptor.capture());
|
||||||
XdsConfig initialConfig = xdsConfigCaptor.getValue();
|
XdsConfig initialConfig = xdsUpdateCaptor.getValue().getValue();
|
||||||
assertThat(initialConfig.getClusters().keySet())
|
assertThat(initialConfig.getClusters().keySet())
|
||||||
.containsExactly("root", "clusterA", "clusterB");
|
.containsExactly("root", "clusterA", "clusterB");
|
||||||
|
|
||||||
|
|
@ -605,8 +606,8 @@ public class XdsDependencyManagerTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testChangeRdsName_FromLds_complexTree() {
|
public void testChangeRdsName_FromLds_complexTree() {
|
||||||
xdsDependencyManager = new XdsDependencyManager(
|
xdsDependencyManager = new XdsDependencyManager(xdsClient, xdsConfigWatcher, syncContext,
|
||||||
xdsClient, xdsConfigWatcher, syncContext, serverName, serverName);
|
serverName, serverName, nameResolverArgs, scheduler);
|
||||||
|
|
||||||
// Create the same tree as in testMultipleParentsInCdsTree
|
// Create the same tree as in testMultipleParentsInCdsTree
|
||||||
Cluster rootCluster =
|
Cluster rootCluster =
|
||||||
|
|
@ -639,11 +640,10 @@ public class XdsDependencyManagerTest {
|
||||||
// Do the test
|
// Do the test
|
||||||
String newRdsName = "newRdsName1";
|
String newRdsName = "newRdsName1";
|
||||||
Listener clientListener = buildInlineClientListener(newRdsName, "root");
|
Listener clientListener = buildInlineClientListener(newRdsName, "root");
|
||||||
Listener serverListener = ControlPlaneRule.buildServerListener();
|
|
||||||
controlPlaneService.setXdsConfig(ADS_TYPE_URL_LDS,
|
controlPlaneService.setXdsConfig(ADS_TYPE_URL_LDS,
|
||||||
ImmutableMap.of(XdsTestUtils.SERVER_LISTENER, serverListener, serverName, clientListener));
|
ImmutableMap.of(serverName, clientListener));
|
||||||
inOrder.verify(xdsConfigWatcher, timeout(1000)).onUpdate(xdsConfigCaptor.capture());
|
inOrder.verify(xdsConfigWatcher, timeout(1000)).onUpdate(xdsUpdateCaptor.capture());
|
||||||
XdsConfig config = xdsConfigCaptor.getValue();
|
XdsConfig config = xdsUpdateCaptor.getValue().getValue();
|
||||||
assertThat(config.getVirtualHost().name()).isEqualTo(newRdsName);
|
assertThat(config.getVirtualHost().name()).isEqualTo(newRdsName);
|
||||||
assertThat(config.getClusters().size()).isEqualTo(4);
|
assertThat(config.getClusters().size()).isEqualTo(4);
|
||||||
}
|
}
|
||||||
|
|
@ -652,9 +652,9 @@ public class XdsDependencyManagerTest {
|
||||||
public void testChangeAggCluster() {
|
public void testChangeAggCluster() {
|
||||||
InOrder inOrder = Mockito.inOrder(xdsConfigWatcher);
|
InOrder inOrder = Mockito.inOrder(xdsConfigWatcher);
|
||||||
|
|
||||||
xdsDependencyManager = new XdsDependencyManager(
|
xdsDependencyManager = new XdsDependencyManager(xdsClient, xdsConfigWatcher, syncContext,
|
||||||
xdsClient, xdsConfigWatcher, syncContext, serverName, serverName);
|
serverName, serverName, nameResolverArgs, scheduler);
|
||||||
inOrder.verify(xdsConfigWatcher, atLeastOnce()).onUpdate(any());
|
inOrder.verify(xdsConfigWatcher, timeout(1000)).onUpdate(any());
|
||||||
|
|
||||||
// Setup initial config A -> A1 -> (A11, A12)
|
// Setup initial config A -> A1 -> (A11, A12)
|
||||||
Cluster rootCluster =
|
Cluster rootCluster =
|
||||||
|
|
@ -673,9 +673,8 @@ public class XdsDependencyManagerTest {
|
||||||
|
|
||||||
XdsTestUtils.addEdsClusters(clusterMap, edsMap, "clusterA11", "clusterA12");
|
XdsTestUtils.addEdsClusters(clusterMap, edsMap, "clusterA11", "clusterA12");
|
||||||
Listener clientListener = buildInlineClientListener(RDS_NAME, "root");
|
Listener clientListener = buildInlineClientListener(RDS_NAME, "root");
|
||||||
Listener serverListener = ControlPlaneRule.buildServerListener();
|
|
||||||
controlPlaneService.setXdsConfig(ADS_TYPE_URL_LDS,
|
controlPlaneService.setXdsConfig(ADS_TYPE_URL_LDS,
|
||||||
ImmutableMap.of(XdsTestUtils.SERVER_LISTENER, serverListener, serverName, clientListener));
|
ImmutableMap.of(serverName, clientListener));
|
||||||
|
|
||||||
controlPlaneService.setXdsConfig(ADS_TYPE_URL_CDS, clusterMap);
|
controlPlaneService.setXdsConfig(ADS_TYPE_URL_CDS, clusterMap);
|
||||||
controlPlaneService.setXdsConfig(ADS_TYPE_URL_EDS, edsMap);
|
controlPlaneService.setXdsConfig(ADS_TYPE_URL_EDS, edsMap);
|
||||||
|
|
@ -702,96 +701,50 @@ public class XdsDependencyManagerTest {
|
||||||
inOrder.verify(xdsConfigWatcher, timeout(1000)).onUpdate(argThat(nameMatcher));
|
inOrder.verify(xdsConfigWatcher, timeout(1000)).onUpdate(argThat(nameMatcher));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Listener buildInlineClientListener(String rdsName, String clusterName) {
|
@Test
|
||||||
return XdsTestUtils.buildInlineClientListener(rdsName, clusterName, serverName);
|
public void testCdsError() throws IOException {
|
||||||
|
controlPlaneService.setXdsConfig(
|
||||||
|
ADS_TYPE_URL_CDS, ImmutableMap.of(XdsTestUtils.CLUSTER_NAME,
|
||||||
|
Cluster.newBuilder().setName(XdsTestUtils.CLUSTER_NAME).build()));
|
||||||
|
xdsDependencyManager = new XdsDependencyManager(xdsClient, xdsConfigWatcher, syncContext,
|
||||||
|
serverName, serverName, nameResolverArgs, scheduler);
|
||||||
|
|
||||||
|
verify(xdsConfigWatcher, timeout(1000)).onUpdate(xdsUpdateCaptor.capture());
|
||||||
|
Status status = xdsUpdateCaptor.getValue().getValue()
|
||||||
|
.getClusters().get(CLUSTER_NAME).getStatus();
|
||||||
|
assertThat(status.getDescription()).contains(XdsTestUtils.CLUSTER_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Listener buildInlineClientListener(String rdsName, String clusterName) {
|
||||||
private static String toContextStr(String type, String resourceName) {
|
return XdsTestUtils.buildInlineClientListener(rdsName, clusterName, serverName);
|
||||||
return type + " resource: " + resourceName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class TestWatcher implements XdsDependencyManager.XdsConfigWatcher {
|
private static class TestWatcher implements XdsDependencyManager.XdsConfigWatcher {
|
||||||
XdsConfig lastConfig;
|
XdsConfig lastConfig;
|
||||||
int numUpdates = 0;
|
int numUpdates = 0;
|
||||||
int numError = 0;
|
int numError = 0;
|
||||||
int numDoesNotExist = 0;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onUpdate(XdsConfig config) {
|
public void onUpdate(StatusOr<XdsConfig> update) {
|
||||||
log.fine("Config changed: " + config);
|
log.fine("Config update: " + update);
|
||||||
lastConfig = config;
|
if (update.hasValue()) {
|
||||||
numUpdates++;
|
lastConfig = update.getValue();
|
||||||
}
|
numUpdates++;
|
||||||
|
} else {
|
||||||
@Override
|
numError++;
|
||||||
public void onError(String resourceContext, Status status) {
|
}
|
||||||
log.fine(String.format("Error %s for %s: ", status, resourceContext));
|
|
||||||
numError++;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onResourceDoesNotExist(String resourceName) {
|
|
||||||
log.fine("Resource does not exist: " + resourceName);
|
|
||||||
numDoesNotExist++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Integer> getStats() {
|
private List<Integer> getStats() {
|
||||||
return Arrays.asList(numUpdates, numError, numDoesNotExist);
|
return Arrays.asList(numUpdates, numError);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void verifyStats(int updt, int err, int notExist) {
|
private void verifyStats(int updt, int err) {
|
||||||
assertThat(getStats()).isEqualTo(Arrays.asList(updt, err, notExist));
|
assertThat(getStats()).isEqualTo(Arrays.asList(updt, err));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class WrappedXdsClient extends XdsClient {
|
static class ClusterNameMatcher implements ArgumentMatcher<StatusOr<XdsConfig>> {
|
||||||
private final XdsClient delegate;
|
|
||||||
private final SynchronizationContext syncContext;
|
|
||||||
private ResourceWatcher<LdsUpdate> ldsWatcher;
|
|
||||||
|
|
||||||
WrappedXdsClient(XdsClient delegate, SynchronizationContext syncContext) {
|
|
||||||
this.delegate = delegate;
|
|
||||||
this.syncContext = syncContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void shutdown() {
|
|
||||||
delegate.shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public <T extends ResourceUpdate> void watchXdsResource(
|
|
||||||
XdsResourceType<T> type, String resourceName, ResourceWatcher<T> watcher,
|
|
||||||
Executor executor) {
|
|
||||||
if (type.equals(XdsListenerResource.getInstance())) {
|
|
||||||
ldsWatcher = (ResourceWatcher<LdsUpdate>) watcher;
|
|
||||||
}
|
|
||||||
delegate.watchXdsResource(type, resourceName, watcher, executor);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T extends ResourceUpdate> void cancelXdsResourceWatch(XdsResourceType<T> type,
|
|
||||||
String resourceName,
|
|
||||||
ResourceWatcher<T> watcher) {
|
|
||||||
delegate.cancelXdsResourceWatch(type, resourceName, watcher);
|
|
||||||
}
|
|
||||||
|
|
||||||
void deliverLdsUpdate(long httpMaxStreamDurationNano,
|
|
||||||
List<io.grpc.xds.VirtualHost> virtualHosts) {
|
|
||||||
syncContext.execute(() -> {
|
|
||||||
LdsUpdate ldsUpdate = LdsUpdate.forApiListener(
|
|
||||||
io.grpc.xds.HttpConnectionManager.forVirtualHosts(
|
|
||||||
httpMaxStreamDurationNano, virtualHosts, null));
|
|
||||||
ldsWatcher.onChanged(ldsUpdate);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static class ClusterNameMatcher implements ArgumentMatcher<XdsConfig> {
|
|
||||||
private final List<String> expectedNames;
|
private final List<String> expectedNames;
|
||||||
|
|
||||||
ClusterNameMatcher(List<String> expectedNames) {
|
ClusterNameMatcher(List<String> expectedNames) {
|
||||||
|
|
@ -799,7 +752,11 @@ public class XdsDependencyManagerTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean matches(XdsConfig xdsConfig) {
|
public boolean matches(StatusOr<XdsConfig> update) {
|
||||||
|
if (!update.hasValue()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
XdsConfig xdsConfig = update.getValue();
|
||||||
if (xdsConfig == null || xdsConfig.getClusters() == null) {
|
if (xdsConfig == null || xdsConfig.getClusters() == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ import com.google.protobuf.util.Durations;
|
||||||
import com.google.re2j.Pattern;
|
import com.google.re2j.Pattern;
|
||||||
import io.grpc.CallOptions;
|
import io.grpc.CallOptions;
|
||||||
import io.grpc.Channel;
|
import io.grpc.Channel;
|
||||||
|
import io.grpc.ChannelLogger;
|
||||||
import io.grpc.ClientCall;
|
import io.grpc.ClientCall;
|
||||||
import io.grpc.ClientInterceptor;
|
import io.grpc.ClientInterceptor;
|
||||||
import io.grpc.ClientInterceptors;
|
import io.grpc.ClientInterceptors;
|
||||||
|
|
@ -70,6 +71,7 @@ import io.grpc.Status.Code;
|
||||||
import io.grpc.SynchronizationContext;
|
import io.grpc.SynchronizationContext;
|
||||||
import io.grpc.internal.AutoConfiguredLoadBalancerFactory;
|
import io.grpc.internal.AutoConfiguredLoadBalancerFactory;
|
||||||
import io.grpc.internal.FakeClock;
|
import io.grpc.internal.FakeClock;
|
||||||
|
import io.grpc.internal.GrpcUtil;
|
||||||
import io.grpc.internal.JsonParser;
|
import io.grpc.internal.JsonParser;
|
||||||
import io.grpc.internal.JsonUtil;
|
import io.grpc.internal.JsonUtil;
|
||||||
import io.grpc.internal.ObjectPool;
|
import io.grpc.internal.ObjectPool;
|
||||||
|
|
@ -89,6 +91,8 @@ import io.grpc.xds.VirtualHost.Route.RouteAction.HashPolicy;
|
||||||
import io.grpc.xds.VirtualHost.Route.RouteAction.RetryPolicy;
|
import io.grpc.xds.VirtualHost.Route.RouteAction.RetryPolicy;
|
||||||
import io.grpc.xds.VirtualHost.Route.RouteMatch;
|
import io.grpc.xds.VirtualHost.Route.RouteMatch;
|
||||||
import io.grpc.xds.VirtualHost.Route.RouteMatch.PathMatcher;
|
import io.grpc.xds.VirtualHost.Route.RouteMatch.PathMatcher;
|
||||||
|
import io.grpc.xds.XdsClusterResource.CdsUpdate;
|
||||||
|
import io.grpc.xds.XdsEndpointResource.EdsUpdate;
|
||||||
import io.grpc.xds.XdsListenerResource.LdsUpdate;
|
import io.grpc.xds.XdsListenerResource.LdsUpdate;
|
||||||
import io.grpc.xds.XdsRouteConfigureResource.RdsUpdate;
|
import io.grpc.xds.XdsRouteConfigureResource.RdsUpdate;
|
||||||
import io.grpc.xds.client.Bootstrapper.AuthorityInfo;
|
import io.grpc.xds.client.Bootstrapper.AuthorityInfo;
|
||||||
|
|
@ -104,6 +108,7 @@ import java.net.URISyntaxException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
@ -187,6 +192,15 @@ public class XdsNameResolverTest {
|
||||||
private TestCall<?, ?> testCall;
|
private TestCall<?, ?> testCall;
|
||||||
private boolean originalEnableTimeout;
|
private boolean originalEnableTimeout;
|
||||||
private URI targetUri;
|
private URI targetUri;
|
||||||
|
private final NameResolver.Args nameResolverArgs = NameResolver.Args.newBuilder()
|
||||||
|
.setDefaultPort(8080)
|
||||||
|
.setProxyDetector(GrpcUtil.DEFAULT_PROXY_DETECTOR)
|
||||||
|
.setSynchronizationContext(syncContext)
|
||||||
|
.setServiceConfigParser(mock(NameResolver.ServiceConfigParser.class))
|
||||||
|
.setChannelLogger(mock(ChannelLogger.class))
|
||||||
|
.setScheduledExecutorService(fakeClock.getScheduledExecutorService())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
|
|
@ -213,7 +227,7 @@ public class XdsNameResolverTest {
|
||||||
|
|
||||||
resolver = new XdsNameResolver(targetUri, null, AUTHORITY, null,
|
resolver = new XdsNameResolver(targetUri, null, AUTHORITY, null,
|
||||||
serviceConfigParser, syncContext, scheduler,
|
serviceConfigParser, syncContext, scheduler,
|
||||||
xdsClientPoolFactory, mockRandom, filterRegistry, null, metricRecorder);
|
xdsClientPoolFactory, mockRandom, filterRegistry, null, metricRecorder, nameResolverArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
|
|
@ -259,7 +273,7 @@ public class XdsNameResolverTest {
|
||||||
resolver = new XdsNameResolver(targetUri, null, AUTHORITY, null,
|
resolver = new XdsNameResolver(targetUri, null, AUTHORITY, null,
|
||||||
serviceConfigParser, syncContext, scheduler,
|
serviceConfigParser, syncContext, scheduler,
|
||||||
xdsClientPoolFactory, mockRandom, FilterRegistry.getDefaultRegistry(), null,
|
xdsClientPoolFactory, mockRandom, FilterRegistry.getDefaultRegistry(), null,
|
||||||
metricRecorder);
|
metricRecorder, nameResolverArgs);
|
||||||
resolver.start(mockListener);
|
resolver.start(mockListener);
|
||||||
verify(mockListener).onError(errorCaptor.capture());
|
verify(mockListener).onError(errorCaptor.capture());
|
||||||
Status error = errorCaptor.getValue();
|
Status error = errorCaptor.getValue();
|
||||||
|
|
@ -273,7 +287,7 @@ public class XdsNameResolverTest {
|
||||||
resolver = new XdsNameResolver(targetUri,
|
resolver = new XdsNameResolver(targetUri,
|
||||||
"notfound.google.com", AUTHORITY, null, serviceConfigParser, syncContext, scheduler,
|
"notfound.google.com", AUTHORITY, null, serviceConfigParser, syncContext, scheduler,
|
||||||
xdsClientPoolFactory, mockRandom, FilterRegistry.getDefaultRegistry(), null,
|
xdsClientPoolFactory, mockRandom, FilterRegistry.getDefaultRegistry(), null,
|
||||||
metricRecorder);
|
metricRecorder, nameResolverArgs);
|
||||||
resolver.start(mockListener);
|
resolver.start(mockListener);
|
||||||
verify(mockListener).onError(errorCaptor.capture());
|
verify(mockListener).onError(errorCaptor.capture());
|
||||||
Status error = errorCaptor.getValue();
|
Status error = errorCaptor.getValue();
|
||||||
|
|
@ -295,7 +309,7 @@ public class XdsNameResolverTest {
|
||||||
resolver = new XdsNameResolver(
|
resolver = new XdsNameResolver(
|
||||||
targetUri, null, serviceAuthority, null, serviceConfigParser, syncContext,
|
targetUri, null, serviceAuthority, null, serviceConfigParser, syncContext,
|
||||||
scheduler, xdsClientPoolFactory,
|
scheduler, xdsClientPoolFactory,
|
||||||
mockRandom, FilterRegistry.getDefaultRegistry(), null, metricRecorder);
|
mockRandom, FilterRegistry.getDefaultRegistry(), null, metricRecorder, nameResolverArgs);
|
||||||
resolver.start(mockListener);
|
resolver.start(mockListener);
|
||||||
verify(mockListener, never()).onError(any(Status.class));
|
verify(mockListener, never()).onError(any(Status.class));
|
||||||
}
|
}
|
||||||
|
|
@ -316,7 +330,7 @@ public class XdsNameResolverTest {
|
||||||
resolver = new XdsNameResolver(
|
resolver = new XdsNameResolver(
|
||||||
targetUri, null, serviceAuthority, null, serviceConfigParser, syncContext, scheduler,
|
targetUri, null, serviceAuthority, null, serviceConfigParser, syncContext, scheduler,
|
||||||
xdsClientPoolFactory, mockRandom, FilterRegistry.getDefaultRegistry(), null,
|
xdsClientPoolFactory, mockRandom, FilterRegistry.getDefaultRegistry(), null,
|
||||||
metricRecorder);
|
metricRecorder, nameResolverArgs);
|
||||||
resolver.start(mockListener);
|
resolver.start(mockListener);
|
||||||
verify(mockListener, never()).onError(any(Status.class));
|
verify(mockListener, never()).onError(any(Status.class));
|
||||||
}
|
}
|
||||||
|
|
@ -337,7 +351,7 @@ public class XdsNameResolverTest {
|
||||||
resolver = new XdsNameResolver(
|
resolver = new XdsNameResolver(
|
||||||
targetUri, null, serviceAuthority, null, serviceConfigParser, syncContext, scheduler,
|
targetUri, null, serviceAuthority, null, serviceConfigParser, syncContext, scheduler,
|
||||||
xdsClientPoolFactory, mockRandom, FilterRegistry.getDefaultRegistry(), null,
|
xdsClientPoolFactory, mockRandom, FilterRegistry.getDefaultRegistry(), null,
|
||||||
metricRecorder);
|
metricRecorder, nameResolverArgs);
|
||||||
|
|
||||||
|
|
||||||
// The Service Authority must be URL encoded, but unlike the LDS resource name.
|
// The Service Authority must be URL encoded, but unlike the LDS resource name.
|
||||||
|
|
@ -366,7 +380,7 @@ public class XdsNameResolverTest {
|
||||||
resolver = new XdsNameResolver(targetUri,
|
resolver = new XdsNameResolver(targetUri,
|
||||||
"xds.authority.com", serviceAuthority, null, serviceConfigParser, syncContext, scheduler,
|
"xds.authority.com", serviceAuthority, null, serviceConfigParser, syncContext, scheduler,
|
||||||
xdsClientPoolFactory, mockRandom, FilterRegistry.getDefaultRegistry(), null,
|
xdsClientPoolFactory, mockRandom, FilterRegistry.getDefaultRegistry(), null,
|
||||||
metricRecorder);
|
metricRecorder, nameResolverArgs);
|
||||||
resolver.start(mockListener);
|
resolver.start(mockListener);
|
||||||
verify(mockListener, never()).onError(any(Status.class));
|
verify(mockListener, never()).onError(any(Status.class));
|
||||||
}
|
}
|
||||||
|
|
@ -399,7 +413,7 @@ public class XdsNameResolverTest {
|
||||||
resolver = new XdsNameResolver(targetUri, null, AUTHORITY, null,
|
resolver = new XdsNameResolver(targetUri, null, AUTHORITY, null,
|
||||||
serviceConfigParser, syncContext, scheduler,
|
serviceConfigParser, syncContext, scheduler,
|
||||||
xdsClientPoolFactory, mockRandom, FilterRegistry.getDefaultRegistry(), null,
|
xdsClientPoolFactory, mockRandom, FilterRegistry.getDefaultRegistry(), null,
|
||||||
metricRecorder);
|
metricRecorder, nameResolverArgs);
|
||||||
// use different ldsResourceName and service authority. The virtualhost lookup should use
|
// use different ldsResourceName and service authority. The virtualhost lookup should use
|
||||||
// service authority.
|
// service authority.
|
||||||
expectedLdsResourceName = "test-" + expectedLdsResourceName;
|
expectedLdsResourceName = "test-" + expectedLdsResourceName;
|
||||||
|
|
@ -413,6 +427,7 @@ public class XdsNameResolverTest {
|
||||||
Collections.singletonList(route1),
|
Collections.singletonList(route1),
|
||||||
ImmutableMap.of());
|
ImmutableMap.of());
|
||||||
xdsClient.deliverRdsUpdate(RDS_RESOURCE_NAME, Collections.singletonList(virtualHost));
|
xdsClient.deliverRdsUpdate(RDS_RESOURCE_NAME, Collections.singletonList(virtualHost));
|
||||||
|
createAndDeliverClusterUpdates(xdsClient, cluster1);
|
||||||
verify(mockListener).onResult2(resolutionResultCaptor.capture());
|
verify(mockListener).onResult2(resolutionResultCaptor.capture());
|
||||||
assertServiceConfigForLoadBalancingConfig(
|
assertServiceConfigForLoadBalancingConfig(
|
||||||
Collections.singletonList(cluster1),
|
Collections.singletonList(cluster1),
|
||||||
|
|
@ -429,6 +444,7 @@ public class XdsNameResolverTest {
|
||||||
Collections.singletonList(route2),
|
Collections.singletonList(route2),
|
||||||
ImmutableMap.of());
|
ImmutableMap.of());
|
||||||
xdsClient.deliverRdsUpdate(alternativeRdsResource, Collections.singletonList(virtualHost));
|
xdsClient.deliverRdsUpdate(alternativeRdsResource, Collections.singletonList(virtualHost));
|
||||||
|
createAndDeliverClusterUpdates(xdsClient, cluster2);
|
||||||
// Two new service config updates triggered:
|
// Two new service config updates triggered:
|
||||||
// - with load balancing config being able to select cluster1 and cluster2
|
// - with load balancing config being able to select cluster1 and cluster2
|
||||||
// - with load balancing config being able to select cluster2 only
|
// - with load balancing config being able to select cluster2 only
|
||||||
|
|
@ -467,6 +483,7 @@ public class XdsNameResolverTest {
|
||||||
Collections.singletonList(route),
|
Collections.singletonList(route),
|
||||||
ImmutableMap.of());
|
ImmutableMap.of());
|
||||||
xdsClient.deliverRdsUpdate(RDS_RESOURCE_NAME, Collections.singletonList(virtualHost));
|
xdsClient.deliverRdsUpdate(RDS_RESOURCE_NAME, Collections.singletonList(virtualHost));
|
||||||
|
createAndDeliverClusterUpdates(xdsClient, cluster1);
|
||||||
verify(mockListener).onResult2(resolutionResultCaptor.capture());
|
verify(mockListener).onResult2(resolutionResultCaptor.capture());
|
||||||
assertServiceConfigForLoadBalancingConfig(
|
assertServiceConfigForLoadBalancingConfig(
|
||||||
Collections.singletonList(cluster1),
|
Collections.singletonList(cluster1),
|
||||||
|
|
@ -483,6 +500,7 @@ public class XdsNameResolverTest {
|
||||||
verifyNoInteractions(mockListener);
|
verifyNoInteractions(mockListener);
|
||||||
assertThat(xdsClient.rdsResource).isEqualTo(RDS_RESOURCE_NAME);
|
assertThat(xdsClient.rdsResource).isEqualTo(RDS_RESOURCE_NAME);
|
||||||
xdsClient.deliverRdsUpdate(RDS_RESOURCE_NAME, Collections.singletonList(virtualHost));
|
xdsClient.deliverRdsUpdate(RDS_RESOURCE_NAME, Collections.singletonList(virtualHost));
|
||||||
|
createAndDeliverClusterUpdates(xdsClient, cluster1);
|
||||||
verify(mockListener).onResult2(resolutionResultCaptor.capture());
|
verify(mockListener).onResult2(resolutionResultCaptor.capture());
|
||||||
assertServiceConfigForLoadBalancingConfig(
|
assertServiceConfigForLoadBalancingConfig(
|
||||||
Collections.singletonList(cluster1),
|
Collections.singletonList(cluster1),
|
||||||
|
|
@ -506,6 +524,7 @@ public class XdsNameResolverTest {
|
||||||
Collections.singletonList(route),
|
Collections.singletonList(route),
|
||||||
ImmutableMap.of());
|
ImmutableMap.of());
|
||||||
xdsClient.deliverRdsUpdate(RDS_RESOURCE_NAME, Collections.singletonList(virtualHost));
|
xdsClient.deliverRdsUpdate(RDS_RESOURCE_NAME, Collections.singletonList(virtualHost));
|
||||||
|
createAndDeliverClusterUpdates(xdsClient, cluster1);
|
||||||
verify(mockListener).onResult2(resolutionResultCaptor.capture());
|
verify(mockListener).onResult2(resolutionResultCaptor.capture());
|
||||||
assertServiceConfigForLoadBalancingConfig(
|
assertServiceConfigForLoadBalancingConfig(
|
||||||
Collections.singletonList(cluster1),
|
Collections.singletonList(cluster1),
|
||||||
|
|
@ -529,11 +548,15 @@ public class XdsNameResolverTest {
|
||||||
resolver.start(mockListener);
|
resolver.start(mockListener);
|
||||||
FakeXdsClient xdsClient = (FakeXdsClient) resolver.getXdsClient();
|
FakeXdsClient xdsClient = (FakeXdsClient) resolver.getXdsClient();
|
||||||
xdsClient.deliverError(Status.UNAVAILABLE.withDescription("server unreachable"));
|
xdsClient.deliverError(Status.UNAVAILABLE.withDescription("server unreachable"));
|
||||||
verify(mockListener).onError(errorCaptor.capture());
|
verify(mockListener).onResult2(resolutionResultCaptor.capture());
|
||||||
Status error = errorCaptor.getValue();
|
InternalConfigSelector configSelector = resolutionResultCaptor.getValue()
|
||||||
|
.getAttributes().get(InternalConfigSelector.KEY);
|
||||||
|
Result selectResult = configSelector.selectConfig(
|
||||||
|
newPickSubchannelArgs(call1.methodDescriptor, new Metadata(), CallOptions.DEFAULT));
|
||||||
|
Status error = selectResult.getStatus();
|
||||||
assertThat(error.getCode()).isEqualTo(Code.UNAVAILABLE);
|
assertThat(error.getCode()).isEqualTo(Code.UNAVAILABLE);
|
||||||
assertThat(error.getDescription()).isEqualTo("Unable to load LDS " + AUTHORITY
|
assertThat(error.getDescription()).contains(AUTHORITY);
|
||||||
+ ". xDS server returned: UNAVAILABLE: server unreachable");
|
assertThat(error.getDescription()).contains("UNAVAILABLE: server unreachable");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -541,11 +564,15 @@ public class XdsNameResolverTest {
|
||||||
resolver.start(mockListener);
|
resolver.start(mockListener);
|
||||||
FakeXdsClient xdsClient = (FakeXdsClient) resolver.getXdsClient();
|
FakeXdsClient xdsClient = (FakeXdsClient) resolver.getXdsClient();
|
||||||
xdsClient.deliverError(Status.NOT_FOUND.withDescription("server unreachable"));
|
xdsClient.deliverError(Status.NOT_FOUND.withDescription("server unreachable"));
|
||||||
verify(mockListener).onError(errorCaptor.capture());
|
verify(mockListener).onResult2(resolutionResultCaptor.capture());
|
||||||
Status error = errorCaptor.getValue();
|
InternalConfigSelector configSelector = resolutionResultCaptor.getValue()
|
||||||
|
.getAttributes().get(InternalConfigSelector.KEY);
|
||||||
|
Result selectResult = configSelector.selectConfig(
|
||||||
|
newPickSubchannelArgs(call1.methodDescriptor, new Metadata(), CallOptions.DEFAULT));
|
||||||
|
Status error = selectResult.getStatus();
|
||||||
assertThat(error.getCode()).isEqualTo(Code.UNAVAILABLE);
|
assertThat(error.getCode()).isEqualTo(Code.UNAVAILABLE);
|
||||||
assertThat(error.getDescription()).isEqualTo("Unable to load LDS " + AUTHORITY
|
assertThat(error.getDescription()).contains(AUTHORITY);
|
||||||
+ ". xDS server returned: NOT_FOUND: server unreachable");
|
assertThat(error.getDescription()).contains("NOT_FOUND: server unreachable");
|
||||||
assertThat(error.getCause()).isNull();
|
assertThat(error.getCause()).isNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -555,15 +582,15 @@ public class XdsNameResolverTest {
|
||||||
FakeXdsClient xdsClient = (FakeXdsClient) resolver.getXdsClient();
|
FakeXdsClient xdsClient = (FakeXdsClient) resolver.getXdsClient();
|
||||||
xdsClient.deliverLdsUpdateForRdsName(RDS_RESOURCE_NAME);
|
xdsClient.deliverLdsUpdateForRdsName(RDS_RESOURCE_NAME);
|
||||||
xdsClient.deliverError(Status.UNAVAILABLE.withDescription("server unreachable"));
|
xdsClient.deliverError(Status.UNAVAILABLE.withDescription("server unreachable"));
|
||||||
verify(mockListener, times(2)).onError(errorCaptor.capture());
|
verify(mockListener).onResult2(resolutionResultCaptor.capture());
|
||||||
Status error = errorCaptor.getAllValues().get(0);
|
InternalConfigSelector configSelector = resolutionResultCaptor.getValue()
|
||||||
|
.getAttributes().get(InternalConfigSelector.KEY);
|
||||||
|
Result selectResult = configSelector.selectConfig(
|
||||||
|
newPickSubchannelArgs(call1.methodDescriptor, new Metadata(), CallOptions.DEFAULT));
|
||||||
|
Status error = selectResult.getStatus();
|
||||||
assertThat(error.getCode()).isEqualTo(Code.UNAVAILABLE);
|
assertThat(error.getCode()).isEqualTo(Code.UNAVAILABLE);
|
||||||
assertThat(error.getDescription()).isEqualTo("Unable to load LDS " + AUTHORITY
|
assertThat(error.getDescription()).contains(RDS_RESOURCE_NAME);
|
||||||
+ ". xDS server returned: UNAVAILABLE: server unreachable");
|
assertThat(error.getDescription()).contains("UNAVAILABLE: server unreachable");
|
||||||
error = errorCaptor.getAllValues().get(1);
|
|
||||||
assertThat(error.getCode()).isEqualTo(Code.UNAVAILABLE);
|
|
||||||
assertThat(error.getDescription()).isEqualTo("Unable to load RDS " + RDS_RESOURCE_NAME
|
|
||||||
+ ". xDS server returned: UNAVAILABLE: server unreachable");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
|
@ -581,10 +608,11 @@ public class XdsNameResolverTest {
|
||||||
resolver = new XdsNameResolver(targetUri, null, AUTHORITY, "random",
|
resolver = new XdsNameResolver(targetUri, null, AUTHORITY, "random",
|
||||||
serviceConfigParser, syncContext, scheduler,
|
serviceConfigParser, syncContext, scheduler,
|
||||||
xdsClientPoolFactory, mockRandom, FilterRegistry.getDefaultRegistry(), null,
|
xdsClientPoolFactory, mockRandom, FilterRegistry.getDefaultRegistry(), null,
|
||||||
metricRecorder);
|
metricRecorder, nameResolverArgs);
|
||||||
resolver.start(mockListener);
|
resolver.start(mockListener);
|
||||||
FakeXdsClient xdsClient = (FakeXdsClient) resolver.getXdsClient();
|
FakeXdsClient xdsClient = (FakeXdsClient) resolver.getXdsClient();
|
||||||
xdsClient.deliverLdsUpdate(0L, Arrays.asList(virtualHost));
|
xdsClient.deliverLdsUpdate(0L, Arrays.asList(virtualHost));
|
||||||
|
createAndDeliverClusterUpdates(xdsClient, cluster1);
|
||||||
verify(mockListener).onResult2(resolutionResultCaptor.capture());
|
verify(mockListener).onResult2(resolutionResultCaptor.capture());
|
||||||
assertServiceConfigForLoadBalancingConfig(
|
assertServiceConfigForLoadBalancingConfig(
|
||||||
Collections.singletonList(cluster1),
|
Collections.singletonList(cluster1),
|
||||||
|
|
@ -605,10 +633,11 @@ public class XdsNameResolverTest {
|
||||||
resolver = new XdsNameResolver(targetUri, null, AUTHORITY, "random",
|
resolver = new XdsNameResolver(targetUri, null, AUTHORITY, "random",
|
||||||
serviceConfigParser, syncContext, scheduler,
|
serviceConfigParser, syncContext, scheduler,
|
||||||
xdsClientPoolFactory, mockRandom, FilterRegistry.getDefaultRegistry(), null,
|
xdsClientPoolFactory, mockRandom, FilterRegistry.getDefaultRegistry(), null,
|
||||||
metricRecorder);
|
metricRecorder, nameResolverArgs);
|
||||||
resolver.start(mockListener);
|
resolver.start(mockListener);
|
||||||
FakeXdsClient xdsClient = (FakeXdsClient) resolver.getXdsClient();
|
FakeXdsClient xdsClient = (FakeXdsClient) resolver.getXdsClient();
|
||||||
xdsClient.deliverLdsUpdate(0L, Arrays.asList(virtualHost));
|
xdsClient.deliverLdsUpdateOnly(0L, Arrays.asList(virtualHost));
|
||||||
|
fakeClock.forwardTime(15, TimeUnit.SECONDS);
|
||||||
assertEmptyResolutionResult("random");
|
assertEmptyResolutionResult("random");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -617,7 +646,7 @@ public class XdsNameResolverTest {
|
||||||
resolver = new XdsNameResolver(targetUri, null, AUTHORITY, AUTHORITY,
|
resolver = new XdsNameResolver(targetUri, null, AUTHORITY, AUTHORITY,
|
||||||
serviceConfigParser, syncContext, scheduler,
|
serviceConfigParser, syncContext, scheduler,
|
||||||
xdsClientPoolFactory, mockRandom, FilterRegistry.getDefaultRegistry(), null,
|
xdsClientPoolFactory, mockRandom, FilterRegistry.getDefaultRegistry(), null,
|
||||||
metricRecorder);
|
metricRecorder, nameResolverArgs);
|
||||||
resolver.start(mockListener);
|
resolver.start(mockListener);
|
||||||
FakeXdsClient xdsClient = (FakeXdsClient) resolver.getXdsClient();
|
FakeXdsClient xdsClient = (FakeXdsClient) resolver.getXdsClient();
|
||||||
xdsClient.deliverLdsUpdate(0L, buildUnmatchedVirtualHosts());
|
xdsClient.deliverLdsUpdate(0L, buildUnmatchedVirtualHosts());
|
||||||
|
|
@ -702,7 +731,7 @@ public class XdsNameResolverTest {
|
||||||
true, 5, 5, new AutoConfiguredLoadBalancerFactory("pick-first"));
|
true, 5, 5, new AutoConfiguredLoadBalancerFactory("pick-first"));
|
||||||
resolver = new XdsNameResolver(targetUri, null, AUTHORITY, null, realParser, syncContext,
|
resolver = new XdsNameResolver(targetUri, null, AUTHORITY, null, realParser, syncContext,
|
||||||
scheduler, xdsClientPoolFactory, mockRandom, FilterRegistry.getDefaultRegistry(), null,
|
scheduler, xdsClientPoolFactory, mockRandom, FilterRegistry.getDefaultRegistry(), null,
|
||||||
metricRecorder);
|
metricRecorder, nameResolverArgs);
|
||||||
resolver.start(mockListener);
|
resolver.start(mockListener);
|
||||||
FakeXdsClient xdsClient = (FakeXdsClient) resolver.getXdsClient();
|
FakeXdsClient xdsClient = (FakeXdsClient) resolver.getXdsClient();
|
||||||
RetryPolicy retryPolicy = RetryPolicy.create(
|
RetryPolicy retryPolicy = RetryPolicy.create(
|
||||||
|
|
@ -913,7 +942,7 @@ public class XdsNameResolverTest {
|
||||||
resolver = new XdsNameResolver(targetUri, null, AUTHORITY, null, serviceConfigParser,
|
resolver = new XdsNameResolver(targetUri, null, AUTHORITY, null, serviceConfigParser,
|
||||||
syncContext, scheduler,
|
syncContext, scheduler,
|
||||||
xdsClientPoolFactory, mockRandom, FilterRegistry.getDefaultRegistry(), null,
|
xdsClientPoolFactory, mockRandom, FilterRegistry.getDefaultRegistry(), null,
|
||||||
metricRecorder);
|
metricRecorder, nameResolverArgs);
|
||||||
resolver.start(mockListener);
|
resolver.start(mockListener);
|
||||||
xdsClient = (FakeXdsClient) resolver.getXdsClient();
|
xdsClient = (FakeXdsClient) resolver.getXdsClient();
|
||||||
xdsClient.deliverLdsUpdate(
|
xdsClient.deliverLdsUpdate(
|
||||||
|
|
@ -946,7 +975,7 @@ public class XdsNameResolverTest {
|
||||||
public void resolved_routeActionHasAutoHostRewrite_emitsCallOptionForTheSame() {
|
public void resolved_routeActionHasAutoHostRewrite_emitsCallOptionForTheSame() {
|
||||||
resolver = new XdsNameResolver(targetUri, null, AUTHORITY, null, serviceConfigParser,
|
resolver = new XdsNameResolver(targetUri, null, AUTHORITY, null, serviceConfigParser,
|
||||||
syncContext, scheduler, xdsClientPoolFactory, mockRandom,
|
syncContext, scheduler, xdsClientPoolFactory, mockRandom,
|
||||||
FilterRegistry.getDefaultRegistry(), null, metricRecorder);
|
FilterRegistry.getDefaultRegistry(), null, metricRecorder, nameResolverArgs);
|
||||||
resolver.start(mockListener);
|
resolver.start(mockListener);
|
||||||
FakeXdsClient xdsClient = (FakeXdsClient) resolver.getXdsClient();
|
FakeXdsClient xdsClient = (FakeXdsClient) resolver.getXdsClient();
|
||||||
xdsClient.deliverLdsUpdate(
|
xdsClient.deliverLdsUpdate(
|
||||||
|
|
@ -977,7 +1006,7 @@ public class XdsNameResolverTest {
|
||||||
public void resolved_routeActionNoAutoHostRewrite_doesntEmitCallOptionForTheSame() {
|
public void resolved_routeActionNoAutoHostRewrite_doesntEmitCallOptionForTheSame() {
|
||||||
resolver = new XdsNameResolver(targetUri, null, AUTHORITY, null, serviceConfigParser,
|
resolver = new XdsNameResolver(targetUri, null, AUTHORITY, null, serviceConfigParser,
|
||||||
syncContext, scheduler, xdsClientPoolFactory, mockRandom,
|
syncContext, scheduler, xdsClientPoolFactory, mockRandom,
|
||||||
FilterRegistry.getDefaultRegistry(), null, metricRecorder);
|
FilterRegistry.getDefaultRegistry(), null, metricRecorder, nameResolverArgs);
|
||||||
resolver.start(mockListener);
|
resolver.start(mockListener);
|
||||||
FakeXdsClient xdsClient = (FakeXdsClient) resolver.getXdsClient();
|
FakeXdsClient xdsClient = (FakeXdsClient) resolver.getXdsClient();
|
||||||
xdsClient.deliverLdsUpdate(
|
xdsClient.deliverLdsUpdate(
|
||||||
|
|
@ -1190,6 +1219,20 @@ public class XdsNameResolverTest {
|
||||||
assertCallSelectClusterResult(call1, configSelector, cluster1, 20.0);
|
assertCallSelectClusterResult(call1, configSelector, cluster1, 20.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Creates and delivers both CDS and EDS updates for the given clusters. */
|
||||||
|
private static void createAndDeliverClusterUpdates(
|
||||||
|
FakeXdsClient xdsClient, String... clusterNames) {
|
||||||
|
for (String clusterName : clusterNames) {
|
||||||
|
CdsUpdate.Builder forEds = CdsUpdate
|
||||||
|
.forEds(clusterName, clusterName, null, null, null, null, false)
|
||||||
|
.roundRobinLbPolicy();
|
||||||
|
xdsClient.deliverCdsUpdate(clusterName, forEds.build());
|
||||||
|
EdsUpdate edsUpdate = new EdsUpdate(clusterName,
|
||||||
|
XdsTestUtils.createMinimalLbEndpointsMap("host"), Collections.emptyList());
|
||||||
|
xdsClient.deliverEdsUpdate(clusterName, edsUpdate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void resolved_simpleCallSucceeds_routeToRls() {
|
public void resolved_simpleCallSucceeds_routeToRls() {
|
||||||
when(mockRandom.nextInt(anyInt())).thenReturn(90, 10);
|
when(mockRandom.nextInt(anyInt())).thenReturn(90, 10);
|
||||||
|
|
@ -1305,6 +1348,7 @@ public class XdsNameResolverTest {
|
||||||
|
|
||||||
// LDS 1.
|
// LDS 1.
|
||||||
xdsClient.deliverLdsUpdateWithFilters(vhost, filterStateTestConfigs(STATEFUL_1, STATEFUL_2));
|
xdsClient.deliverLdsUpdateWithFilters(vhost, filterStateTestConfigs(STATEFUL_1, STATEFUL_2));
|
||||||
|
createAndDeliverClusterUpdates(xdsClient, cluster1);
|
||||||
assertClusterResolutionResult(call1, cluster1);
|
assertClusterResolutionResult(call1, cluster1);
|
||||||
ImmutableList<StatefulFilter> lds1Snapshot = statefulFilterProvider.getAllInstances();
|
ImmutableList<StatefulFilter> lds1Snapshot = statefulFilterProvider.getAllInstances();
|
||||||
// Verify that StatefulFilter with different filter names result in different Filter instances.
|
// Verify that StatefulFilter with different filter names result in different Filter instances.
|
||||||
|
|
@ -1359,7 +1403,7 @@ public class XdsNameResolverTest {
|
||||||
* Verifies the lifecycle of HCM filter instances across RDS updates.
|
* Verifies the lifecycle of HCM filter instances across RDS updates.
|
||||||
*
|
*
|
||||||
* <p>Filter instances:
|
* <p>Filter instances:
|
||||||
* 1. Must have instantiated by the initial LDS.
|
* 1. Must have instantiated by the initial LDS/RDS.
|
||||||
* 2. Must be reused by all subsequent RDS updates.
|
* 2. Must be reused by all subsequent RDS updates.
|
||||||
* 3. Must be not shutdown (closed) by valid RDS updates.
|
* 3. Must be not shutdown (closed) by valid RDS updates.
|
||||||
*/
|
*/
|
||||||
|
|
@ -1371,22 +1415,19 @@ public class XdsNameResolverTest {
|
||||||
// LDS 1.
|
// LDS 1.
|
||||||
xdsClient.deliverLdsUpdateForRdsNameWithFilters(RDS_RESOURCE_NAME,
|
xdsClient.deliverLdsUpdateForRdsNameWithFilters(RDS_RESOURCE_NAME,
|
||||||
filterStateTestConfigs(STATEFUL_1, STATEFUL_2));
|
filterStateTestConfigs(STATEFUL_1, STATEFUL_2));
|
||||||
ImmutableList<StatefulFilter> lds1Snapshot = statefulFilterProvider.getAllInstances();
|
|
||||||
// Verify that StatefulFilter with different filter names result in different Filter instances.
|
|
||||||
assertWithMessage("LDS 1: expected to create filter instances").that(lds1Snapshot).hasSize(2);
|
|
||||||
// Naming: lds<LDS#>Filter<name#>
|
|
||||||
StatefulFilter lds1Filter1 = lds1Snapshot.get(0);
|
|
||||||
StatefulFilter lds1Filter2 = lds1Snapshot.get(1);
|
|
||||||
assertThat(lds1Filter1).isNotSameInstanceAs(lds1Filter2);
|
|
||||||
|
|
||||||
// RDS 1.
|
// RDS 1.
|
||||||
VirtualHost vhost1 = filterStateTestVhost();
|
VirtualHost vhost1 = filterStateTestVhost();
|
||||||
xdsClient.deliverRdsUpdate(RDS_RESOURCE_NAME, vhost1);
|
xdsClient.deliverRdsUpdate(RDS_RESOURCE_NAME, vhost1);
|
||||||
|
createAndDeliverClusterUpdates(xdsClient, cluster1);
|
||||||
assertClusterResolutionResult(call1, cluster1);
|
assertClusterResolutionResult(call1, cluster1);
|
||||||
// Initial RDS update should not generate Filter instances.
|
// Initial RDS update should not generate Filter instances.
|
||||||
ImmutableList<StatefulFilter> rds1Snapshot = statefulFilterProvider.getAllInstances();
|
ImmutableList<StatefulFilter> rds1Snapshot = statefulFilterProvider.getAllInstances();
|
||||||
assertWithMessage("RDS 1: Expected Filter instances to be reused across RDS route updates")
|
// Verify that StatefulFilter with different filter names result in different Filter instances.
|
||||||
.that(rds1Snapshot).isEqualTo(lds1Snapshot);
|
assertWithMessage("RDS 1: expected to create filter instances").that(rds1Snapshot).hasSize(2);
|
||||||
|
// Naming: lds<LDS#>Filter<name#>
|
||||||
|
StatefulFilter lds1Filter1 = rds1Snapshot.get(0);
|
||||||
|
StatefulFilter lds1Filter2 = rds1Snapshot.get(1);
|
||||||
|
assertThat(lds1Filter1).isNotSameInstanceAs(lds1Filter2);
|
||||||
|
|
||||||
// RDS 2: exactly the same as RDS 1.
|
// RDS 2: exactly the same as RDS 1.
|
||||||
xdsClient.deliverRdsUpdate(RDS_RESOURCE_NAME, vhost1);
|
xdsClient.deliverRdsUpdate(RDS_RESOURCE_NAME, vhost1);
|
||||||
|
|
@ -1394,7 +1435,7 @@ public class XdsNameResolverTest {
|
||||||
ImmutableList<StatefulFilter> rds2Snapshot = statefulFilterProvider.getAllInstances();
|
ImmutableList<StatefulFilter> rds2Snapshot = statefulFilterProvider.getAllInstances();
|
||||||
// Neither should any subsequent RDS updates.
|
// Neither should any subsequent RDS updates.
|
||||||
assertWithMessage("RDS 2: Expected Filter instances to be reused across RDS route updates")
|
assertWithMessage("RDS 2: Expected Filter instances to be reused across RDS route updates")
|
||||||
.that(rds2Snapshot).isEqualTo(lds1Snapshot);
|
.that(rds2Snapshot).isEqualTo(rds1Snapshot);
|
||||||
|
|
||||||
// RDS 3: Contains a per-route override for STATEFUL_1.
|
// RDS 3: Contains a per-route override for STATEFUL_1.
|
||||||
VirtualHost vhost3 = filterStateTestVhost(ImmutableMap.of(
|
VirtualHost vhost3 = filterStateTestVhost(ImmutableMap.of(
|
||||||
|
|
@ -1406,7 +1447,7 @@ public class XdsNameResolverTest {
|
||||||
// As with any other Route update, typed_per_filter_config overrides should not result in
|
// As with any other Route update, typed_per_filter_config overrides should not result in
|
||||||
// creating new filter instances.
|
// creating new filter instances.
|
||||||
assertWithMessage("RDS 3: Expected Filter instances to be reused on per-route filter overrides")
|
assertWithMessage("RDS 3: Expected Filter instances to be reused on per-route filter overrides")
|
||||||
.that(rds3Snapshot).isEqualTo(lds1Snapshot);
|
.that(rds3Snapshot).isEqualTo(rds1Snapshot);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -1427,7 +1468,7 @@ public class XdsNameResolverTest {
|
||||||
.register(statefulFilterProvider, altStatefulFilterProvider, ROUTER_FILTER_PROVIDER);
|
.register(statefulFilterProvider, altStatefulFilterProvider, ROUTER_FILTER_PROVIDER);
|
||||||
resolver = new XdsNameResolver(targetUri, null, AUTHORITY, null, serviceConfigParser,
|
resolver = new XdsNameResolver(targetUri, null, AUTHORITY, null, serviceConfigParser,
|
||||||
syncContext, scheduler, xdsClientPoolFactory, mockRandom, filterRegistry, null,
|
syncContext, scheduler, xdsClientPoolFactory, mockRandom, filterRegistry, null,
|
||||||
metricRecorder);
|
metricRecorder, nameResolverArgs);
|
||||||
resolver.start(mockListener);
|
resolver.start(mockListener);
|
||||||
|
|
||||||
FakeXdsClient xdsClient = (FakeXdsClient) resolver.getXdsClient();
|
FakeXdsClient xdsClient = (FakeXdsClient) resolver.getXdsClient();
|
||||||
|
|
@ -1435,6 +1476,7 @@ public class XdsNameResolverTest {
|
||||||
|
|
||||||
// LDS 1.
|
// LDS 1.
|
||||||
xdsClient.deliverLdsUpdateWithFilters(vhost, filterStateTestConfigs(STATEFUL_1, STATEFUL_2));
|
xdsClient.deliverLdsUpdateWithFilters(vhost, filterStateTestConfigs(STATEFUL_1, STATEFUL_2));
|
||||||
|
createAndDeliverClusterUpdates(xdsClient, cluster1);
|
||||||
assertClusterResolutionResult(call1, cluster1);
|
assertClusterResolutionResult(call1, cluster1);
|
||||||
ImmutableList<StatefulFilter> lds1Snapshot = statefulFilterProvider.getAllInstances();
|
ImmutableList<StatefulFilter> lds1Snapshot = statefulFilterProvider.getAllInstances();
|
||||||
ImmutableList<StatefulFilter> lds1SnapshotAlt = altStatefulFilterProvider.getAllInstances();
|
ImmutableList<StatefulFilter> lds1SnapshotAlt = altStatefulFilterProvider.getAllInstances();
|
||||||
|
|
@ -1483,6 +1525,7 @@ public class XdsNameResolverTest {
|
||||||
|
|
||||||
// LDS 1.
|
// LDS 1.
|
||||||
xdsClient.deliverLdsUpdateWithFilters(vhost, filterStateTestConfigs(STATEFUL_1, STATEFUL_2));
|
xdsClient.deliverLdsUpdateWithFilters(vhost, filterStateTestConfigs(STATEFUL_1, STATEFUL_2));
|
||||||
|
createAndDeliverClusterUpdates(xdsClient, cluster1);
|
||||||
assertClusterResolutionResult(call1, cluster1);
|
assertClusterResolutionResult(call1, cluster1);
|
||||||
ImmutableList<StatefulFilter> lds1Snapshot = statefulFilterProvider.getAllInstances();
|
ImmutableList<StatefulFilter> lds1Snapshot = statefulFilterProvider.getAllInstances();
|
||||||
assertWithMessage("LDS 1: expected to create filter instances").that(lds1Snapshot).hasSize(2);
|
assertWithMessage("LDS 1: expected to create filter instances").that(lds1Snapshot).hasSize(2);
|
||||||
|
|
@ -1510,6 +1553,7 @@ public class XdsNameResolverTest {
|
||||||
|
|
||||||
// LDS 1.
|
// LDS 1.
|
||||||
xdsClient.deliverLdsUpdateWithFilters(vhost, filterStateTestConfigs(STATEFUL_1, STATEFUL_2));
|
xdsClient.deliverLdsUpdateWithFilters(vhost, filterStateTestConfigs(STATEFUL_1, STATEFUL_2));
|
||||||
|
createAndDeliverClusterUpdates(xdsClient, cluster1);
|
||||||
assertClusterResolutionResult(call1, cluster1);
|
assertClusterResolutionResult(call1, cluster1);
|
||||||
ImmutableList<StatefulFilter> lds1Snapshot = statefulFilterProvider.getAllInstances();
|
ImmutableList<StatefulFilter> lds1Snapshot = statefulFilterProvider.getAllInstances();
|
||||||
assertWithMessage("LDS 1: expected to create filter instances").that(lds1Snapshot).hasSize(2);
|
assertWithMessage("LDS 1: expected to create filter instances").that(lds1Snapshot).hasSize(2);
|
||||||
|
|
@ -1526,33 +1570,32 @@ public class XdsNameResolverTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verifies that filter instances are NOT shutdown on RDS_RESOURCE_NAME not found.
|
* Verifies that all filter instances are shutdown (closed) on RDS resource not found.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void filterState_shutdown_noShutdownOnRdsNotFound() {
|
public void filterState_shutdown_onRdsNotFound() {
|
||||||
StatefulFilter.Provider statefulFilterProvider = filterStateTestSetupResolver();
|
StatefulFilter.Provider statefulFilterProvider = filterStateTestSetupResolver();
|
||||||
FakeXdsClient xdsClient = (FakeXdsClient) resolver.getXdsClient();
|
FakeXdsClient xdsClient = (FakeXdsClient) resolver.getXdsClient();
|
||||||
|
|
||||||
// LDS 1.
|
// LDS 1.
|
||||||
xdsClient.deliverLdsUpdateForRdsNameWithFilters(RDS_RESOURCE_NAME,
|
xdsClient.deliverLdsUpdateForRdsNameWithFilters(RDS_RESOURCE_NAME,
|
||||||
filterStateTestConfigs(STATEFUL_1, STATEFUL_2));
|
filterStateTestConfigs(STATEFUL_1, STATEFUL_2));
|
||||||
ImmutableList<StatefulFilter> lds1Snapshot = statefulFilterProvider.getAllInstances();
|
|
||||||
assertWithMessage("LDS 1: expected to create filter instances").that(lds1Snapshot).hasSize(2);
|
|
||||||
// Naming: lds<LDS#>Filter<name#>
|
|
||||||
StatefulFilter lds1Filter1 = lds1Snapshot.get(0);
|
|
||||||
StatefulFilter lds1Filter2 = lds1Snapshot.get(1);
|
|
||||||
|
|
||||||
// RDS 1: Standard vhost with a route.
|
// RDS 1: Standard vhost with a route.
|
||||||
xdsClient.deliverRdsUpdate(RDS_RESOURCE_NAME, filterStateTestVhost());
|
xdsClient.deliverRdsUpdate(RDS_RESOURCE_NAME, filterStateTestVhost());
|
||||||
|
createAndDeliverClusterUpdates(xdsClient, cluster1);
|
||||||
assertClusterResolutionResult(call1, cluster1);
|
assertClusterResolutionResult(call1, cluster1);
|
||||||
assertThat(statefulFilterProvider.getAllInstances()).isEqualTo(lds1Snapshot);
|
ImmutableList<StatefulFilter> rds1Snapshot = statefulFilterProvider.getAllInstances();
|
||||||
|
assertWithMessage("RDS 1: expected to create filter instances").that(rds1Snapshot).hasSize(2);
|
||||||
|
// Naming: lds<LDS#>Filter<name#>
|
||||||
|
StatefulFilter lds1Filter1 = rds1Snapshot.get(0);
|
||||||
|
StatefulFilter lds1Filter2 = rds1Snapshot.get(1);
|
||||||
|
|
||||||
// RDS 2: RDS_RESOURCE_NAME not found.
|
// RDS 2: RDS_RESOURCE_NAME not found.
|
||||||
reset(mockListener);
|
reset(mockListener);
|
||||||
xdsClient.deliverRdsResourceNotFound(RDS_RESOURCE_NAME);
|
xdsClient.deliverRdsResourceNotFound(RDS_RESOURCE_NAME);
|
||||||
assertEmptyResolutionResult(RDS_RESOURCE_NAME);
|
assertEmptyResolutionResult(RDS_RESOURCE_NAME);
|
||||||
assertThat(lds1Filter1.isShutdown()).isFalse();
|
assertThat(lds1Filter1.isShutdown()).isTrue();
|
||||||
assertThat(lds1Filter2.isShutdown()).isFalse();
|
assertThat(lds1Filter2.isShutdown()).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
private StatefulFilter.Provider filterStateTestSetupResolver() {
|
private StatefulFilter.Provider filterStateTestSetupResolver() {
|
||||||
|
|
@ -1561,7 +1604,7 @@ public class XdsNameResolverTest {
|
||||||
.register(statefulFilterProvider, ROUTER_FILTER_PROVIDER);
|
.register(statefulFilterProvider, ROUTER_FILTER_PROVIDER);
|
||||||
resolver = new XdsNameResolver(targetUri, null, AUTHORITY, null, serviceConfigParser,
|
resolver = new XdsNameResolver(targetUri, null, AUTHORITY, null, serviceConfigParser,
|
||||||
syncContext, scheduler, xdsClientPoolFactory, mockRandom, filterRegistry, null,
|
syncContext, scheduler, xdsClientPoolFactory, mockRandom, filterRegistry, null,
|
||||||
metricRecorder);
|
metricRecorder, nameResolverArgs);
|
||||||
resolver.start(mockListener);
|
resolver.start(mockListener);
|
||||||
return statefulFilterProvider;
|
return statefulFilterProvider;
|
||||||
}
|
}
|
||||||
|
|
@ -1762,6 +1805,7 @@ public class XdsNameResolverTest {
|
||||||
ImmutableList.of(route1, route2, route3),
|
ImmutableList.of(route1, route2, route3),
|
||||||
ImmutableMap.of());
|
ImmutableMap.of());
|
||||||
xdsClient.deliverRdsUpdate(RDS_RESOURCE_NAME, Collections.singletonList(virtualHost));
|
xdsClient.deliverRdsUpdate(RDS_RESOURCE_NAME, Collections.singletonList(virtualHost));
|
||||||
|
createAndDeliverClusterUpdates(xdsClient, "cluster-foo", "cluster-bar", "cluster-baz");
|
||||||
|
|
||||||
verify(mockListener).onResult2(resolutionResultCaptor.capture());
|
verify(mockListener).onResult2(resolutionResultCaptor.capture());
|
||||||
String expectedServiceConfigJson =
|
String expectedServiceConfigJson =
|
||||||
|
|
@ -2385,6 +2429,8 @@ public class XdsNameResolverTest {
|
||||||
private String rdsResource;
|
private String rdsResource;
|
||||||
private ResourceWatcher<LdsUpdate> ldsWatcher;
|
private ResourceWatcher<LdsUpdate> ldsWatcher;
|
||||||
private ResourceWatcher<RdsUpdate> rdsWatcher;
|
private ResourceWatcher<RdsUpdate> rdsWatcher;
|
||||||
|
private final Map<String, List<ResourceWatcher<CdsUpdate>>> cdsWatchers = new HashMap<>();
|
||||||
|
private final Map<String, List<ResourceWatcher<EdsUpdate>>> edsWatchers = new HashMap<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BootstrapInfo getBootstrapInfo() {
|
public BootstrapInfo getBootstrapInfo() {
|
||||||
|
|
@ -2412,10 +2458,19 @@ public class XdsNameResolverTest {
|
||||||
rdsResource = resourceName;
|
rdsResource = resourceName;
|
||||||
rdsWatcher = (ResourceWatcher<RdsUpdate>) watcher;
|
rdsWatcher = (ResourceWatcher<RdsUpdate>) watcher;
|
||||||
break;
|
break;
|
||||||
|
case "CDS":
|
||||||
|
cdsWatchers.computeIfAbsent(resourceName, k -> new ArrayList<>())
|
||||||
|
.add((ResourceWatcher<CdsUpdate>) watcher);
|
||||||
|
break;
|
||||||
|
case "EDS":
|
||||||
|
edsWatchers.computeIfAbsent(resourceName, k -> new ArrayList<>())
|
||||||
|
.add((ResourceWatcher<EdsUpdate>) watcher);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
@Override
|
@Override
|
||||||
public <T extends ResourceUpdate> void cancelXdsResourceWatch(XdsResourceType<T> type,
|
public <T extends ResourceUpdate> void cancelXdsResourceWatch(XdsResourceType<T> type,
|
||||||
String resourceName,
|
String resourceName,
|
||||||
|
|
@ -2434,25 +2489,53 @@ public class XdsNameResolverTest {
|
||||||
rdsResource = null;
|
rdsResource = null;
|
||||||
rdsWatcher = null;
|
rdsWatcher = null;
|
||||||
break;
|
break;
|
||||||
|
case "CDS":
|
||||||
|
assertThat(cdsWatchers).containsKey(resourceName);
|
||||||
|
assertThat(cdsWatchers.get(resourceName)).contains(watcher);
|
||||||
|
cdsWatchers.get(resourceName).remove((ResourceWatcher<CdsUpdate>) watcher);
|
||||||
|
break;
|
||||||
|
case "EDS":
|
||||||
|
assertThat(edsWatchers).containsKey(resourceName);
|
||||||
|
assertThat(edsWatchers.get(resourceName)).contains(watcher);
|
||||||
|
edsWatchers.get(resourceName).remove((ResourceWatcher<EdsUpdate>) watcher);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void deliverLdsUpdate(long httpMaxStreamDurationNano, List<VirtualHost> virtualHosts) {
|
void deliverLdsUpdateOnly(long httpMaxStreamDurationNano, List<VirtualHost> virtualHosts) {
|
||||||
syncContext.execute(() -> {
|
syncContext.execute(() -> {
|
||||||
ldsWatcher.onChanged(LdsUpdate.forApiListener(HttpConnectionManager.forVirtualHosts(
|
ldsWatcher.onChanged(LdsUpdate.forApiListener(HttpConnectionManager.forVirtualHosts(
|
||||||
httpMaxStreamDurationNano, virtualHosts, null)));
|
httpMaxStreamDurationNano, virtualHosts, null)));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void deliverLdsUpdate(long httpMaxStreamDurationNano, List<VirtualHost> virtualHosts) {
|
||||||
|
List<String> clusterNames = new ArrayList<>();
|
||||||
|
for (VirtualHost vh : virtualHosts) {
|
||||||
|
clusterNames.addAll(getClusterNames(vh.routes()));
|
||||||
|
}
|
||||||
|
|
||||||
|
syncContext.execute(() -> {
|
||||||
|
ldsWatcher.onChanged(LdsUpdate.forApiListener(HttpConnectionManager.forVirtualHosts(
|
||||||
|
httpMaxStreamDurationNano, virtualHosts, null)));
|
||||||
|
createAndDeliverClusterUpdates(this, clusterNames.toArray(new String[0]));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void deliverLdsUpdate(final List<Route> routes) {
|
void deliverLdsUpdate(final List<Route> routes) {
|
||||||
VirtualHost virtualHost =
|
VirtualHost virtualHost =
|
||||||
VirtualHost.create(
|
VirtualHost.create(
|
||||||
"virtual-host", Collections.singletonList(expectedLdsResourceName), routes,
|
"virtual-host", Collections.singletonList(expectedLdsResourceName), routes,
|
||||||
ImmutableMap.of());
|
ImmutableMap.of());
|
||||||
|
List<String> clusterNames = getClusterNames(routes);
|
||||||
|
|
||||||
syncContext.execute(() -> {
|
syncContext.execute(() -> {
|
||||||
ldsWatcher.onChanged(LdsUpdate.forApiListener(HttpConnectionManager.forVirtualHosts(
|
ldsWatcher.onChanged(LdsUpdate.forApiListener(HttpConnectionManager.forVirtualHosts(
|
||||||
0L, Collections.singletonList(virtualHost), null)));
|
0L, Collections.singletonList(virtualHost), null)));
|
||||||
|
if (!clusterNames.isEmpty()) {
|
||||||
|
createAndDeliverClusterUpdates(this, clusterNames.toArray(new String[0]));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2508,6 +2591,7 @@ public class XdsNameResolverTest {
|
||||||
syncContext.execute(() -> {
|
syncContext.execute(() -> {
|
||||||
ldsWatcher.onChanged(LdsUpdate.forApiListener(HttpConnectionManager.forVirtualHosts(
|
ldsWatcher.onChanged(LdsUpdate.forApiListener(HttpConnectionManager.forVirtualHosts(
|
||||||
0L, Collections.singletonList(virtualHost), filterChain)));
|
0L, Collections.singletonList(virtualHost), filterChain)));
|
||||||
|
createAndDeliverClusterUpdates(this, cluster);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2545,6 +2629,29 @@ public class XdsNameResolverTest {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<String> getClusterNames(List<Route> routes) {
|
||||||
|
List<String> clusterNames = new ArrayList<>();
|
||||||
|
for (Route r : routes) {
|
||||||
|
if (r.routeAction() == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String cluster = r.routeAction().cluster();
|
||||||
|
if (cluster != null) {
|
||||||
|
clusterNames.add(cluster);
|
||||||
|
} else {
|
||||||
|
List<ClusterWeight> weightedClusters = r.routeAction().weightedClusters();
|
||||||
|
if (weightedClusters == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (ClusterWeight wc : weightedClusters) {
|
||||||
|
clusterNames.add(wc.name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return clusterNames;
|
||||||
|
}
|
||||||
|
|
||||||
void deliverRdsUpdateWithFaultInjection(
|
void deliverRdsUpdateWithFaultInjection(
|
||||||
String resourceName, @Nullable FaultConfig virtualHostFaultConfig,
|
String resourceName, @Nullable FaultConfig virtualHostFaultConfig,
|
||||||
@Nullable FaultConfig routFaultConfig, @Nullable FaultConfig weightedClusterFaultConfig) {
|
@Nullable FaultConfig routFaultConfig, @Nullable FaultConfig weightedClusterFaultConfig) {
|
||||||
|
|
@ -2581,6 +2688,7 @@ public class XdsNameResolverTest {
|
||||||
overrideConfig);
|
overrideConfig);
|
||||||
syncContext.execute(() -> {
|
syncContext.execute(() -> {
|
||||||
rdsWatcher.onChanged(new RdsUpdate(Collections.singletonList(virtualHost)));
|
rdsWatcher.onChanged(new RdsUpdate(Collections.singletonList(virtualHost)));
|
||||||
|
createAndDeliverClusterUpdates(this, cluster1);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2606,6 +2714,29 @@ public class XdsNameResolverTest {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void deliverCdsUpdate(String clusterName, CdsUpdate update) {
|
||||||
|
if (!cdsWatchers.containsKey(clusterName)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
syncContext.execute(() -> {
|
||||||
|
List<ResourceWatcher<CdsUpdate>> resourceWatchers =
|
||||||
|
ImmutableList.copyOf(cdsWatchers.get(clusterName));
|
||||||
|
resourceWatchers.forEach(w -> w.onChanged(update));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deliverEdsUpdate(String name, EdsUpdate update) {
|
||||||
|
syncContext.execute(() -> {
|
||||||
|
if (!edsWatchers.containsKey(name)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
List<ResourceWatcher<EdsUpdate>> resourceWatchers =
|
||||||
|
ImmutableList.copyOf(edsWatchers.get(name));
|
||||||
|
resourceWatchers.forEach(w -> w.onChanged(update));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void deliverError(final Status error) {
|
void deliverError(final Status error) {
|
||||||
if (ldsWatcher != null) {
|
if (ldsWatcher != null) {
|
||||||
syncContext.execute(() -> {
|
syncContext.execute(() -> {
|
||||||
|
|
@ -2617,6 +2748,11 @@ public class XdsNameResolverTest {
|
||||||
rdsWatcher.onError(error);
|
rdsWatcher.onError(error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
syncContext.execute(() -> {
|
||||||
|
cdsWatchers.values().stream()
|
||||||
|
.flatMap(List::stream)
|
||||||
|
.forEach(w -> w.onError(error));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,6 @@ import io.envoyproxy.envoy.service.load_stats.v3.LoadStatsResponse;
|
||||||
import io.grpc.BindableService;
|
import io.grpc.BindableService;
|
||||||
import io.grpc.Context;
|
import io.grpc.Context;
|
||||||
import io.grpc.Context.CancellationListener;
|
import io.grpc.Context.CancellationListener;
|
||||||
import io.grpc.Status;
|
|
||||||
import io.grpc.StatusOr;
|
import io.grpc.StatusOr;
|
||||||
import io.grpc.internal.JsonParser;
|
import io.grpc.internal.JsonParser;
|
||||||
import io.grpc.stub.StreamObserver;
|
import io.grpc.stub.StreamObserver;
|
||||||
|
|
@ -281,6 +280,16 @@ public class XdsTestUtils {
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Map<Locality, LocalityLbEndpoints> createMinimalLbEndpointsMap(String serverHostName) {
|
||||||
|
Map<Locality, LocalityLbEndpoints> lbEndpointsMap = new HashMap<>();
|
||||||
|
LbEndpoint lbEndpoint = LbEndpoint.create(
|
||||||
|
serverHostName, ENDPOINT_PORT, 0, true, ENDPOINT_HOSTNAME, ImmutableMap.of());
|
||||||
|
lbEndpointsMap.put(
|
||||||
|
Locality.create("", "", ""),
|
||||||
|
LocalityLbEndpoints.create(ImmutableList.of(lbEndpoint), 10, 0, ImmutableMap.of()));
|
||||||
|
return lbEndpointsMap;
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private static ImmutableMap<String, ?> getWrrLbConfigAsMap() throws IOException {
|
private static ImmutableMap<String, ?> getWrrLbConfigAsMap() throws IOException {
|
||||||
String lbConfigStr = "{\"wrr_locality_experimental\" : "
|
String lbConfigStr = "{\"wrr_locality_experimental\" : "
|
||||||
|
|
@ -353,7 +362,6 @@ public class XdsTestUtils {
|
||||||
return Listener.newBuilder()
|
return Listener.newBuilder()
|
||||||
.setName(serverName)
|
.setName(serverName)
|
||||||
.setApiListener(clientListenerBuilder.build()).build();
|
.setApiListener(clientListenerBuilder.build()).build();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -407,18 +415,4 @@ public class XdsTestUtils {
|
||||||
responseObserver.onNext(response);
|
responseObserver.onNext(response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static class StatusMatcher implements ArgumentMatcher<Status> {
|
|
||||||
private final Status expectedStatus;
|
|
||||||
|
|
||||||
StatusMatcher(Status expectedStatus) {
|
|
||||||
this.expectedStatus = expectedStatus;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean matches(Status status) {
|
|
||||||
return status != null && expectedStatus.getCode().equals(status.getCode())
|
|
||||||
&& expectedStatus.getDescription().equals(status.getDescription());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue