Xds: Aggregate cluster fixes (A75) (#12186)

Instead of representing an aggregate cluster as a single cluster whose
priorities come from different underlying clusters, represent an aggregate cluster as an instance of a priority LB policy where each child is a cds LB policy for the underlying
cluster.
This commit is contained in:
Kannan J 2025-07-29 12:36:39 +00:00 committed by GitHub
parent c3ef1ab034
commit 7e982e48a1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 329 additions and 406 deletions

View File

@ -18,10 +18,10 @@ package io.grpc.xds;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static io.grpc.ConnectivityState.TRANSIENT_FAILURE; import static io.grpc.ConnectivityState.TRANSIENT_FAILURE;
import static io.grpc.xds.XdsLbPolicies.CDS_POLICY_NAME;
import static io.grpc.xds.XdsLbPolicies.CLUSTER_RESOLVER_POLICY_NAME; import static io.grpc.xds.XdsLbPolicies.CLUSTER_RESOLVER_POLICY_NAME;
import static io.grpc.xds.XdsLbPolicies.PRIORITY_POLICY_NAME;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.errorprone.annotations.CheckReturnValue; import com.google.errorprone.annotations.CheckReturnValue;
import io.grpc.InternalLogId; import io.grpc.InternalLogId;
import io.grpc.LoadBalancer; import io.grpc.LoadBalancer;
@ -33,6 +33,7 @@ import io.grpc.util.GracefulSwitchLoadBalancer;
import io.grpc.xds.CdsLoadBalancerProvider.CdsConfig; import io.grpc.xds.CdsLoadBalancerProvider.CdsConfig;
import io.grpc.xds.ClusterResolverLoadBalancerProvider.ClusterResolverConfig; import io.grpc.xds.ClusterResolverLoadBalancerProvider.ClusterResolverConfig;
import io.grpc.xds.ClusterResolverLoadBalancerProvider.ClusterResolverConfig.DiscoveryMechanism; import io.grpc.xds.ClusterResolverLoadBalancerProvider.ClusterResolverConfig.DiscoveryMechanism;
import io.grpc.xds.PriorityLoadBalancerProvider.PriorityLbConfig.PriorityChildConfig;
import io.grpc.xds.XdsClusterResource.CdsUpdate; import io.grpc.xds.XdsClusterResource.CdsUpdate;
import io.grpc.xds.XdsClusterResource.CdsUpdate.ClusterType; import io.grpc.xds.XdsClusterResource.CdsUpdate.ClusterType;
import io.grpc.xds.XdsConfig.Subscription; import io.grpc.xds.XdsConfig.Subscription;
@ -41,10 +42,11 @@ import io.grpc.xds.XdsConfig.XdsClusterConfig.AggregateConfig;
import io.grpc.xds.XdsConfig.XdsClusterConfig.EndpointConfig; import io.grpc.xds.XdsConfig.XdsClusterConfig.EndpointConfig;
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.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.List; import java.util.List;
import java.util.Map;
/** /**
* Load balancer for cds_experimental LB policy. One instance per top-level cluster. * Load balancer for cds_experimental LB policy. One instance per top-level cluster.
@ -55,19 +57,15 @@ final class CdsLoadBalancer2 extends LoadBalancer {
private final XdsLogger logger; private final XdsLogger logger;
private final Helper helper; private final Helper helper;
private final LoadBalancerRegistry lbRegistry; private final LoadBalancerRegistry lbRegistry;
private GracefulSwitchLoadBalancer delegate;
// Following fields are effectively final. // Following fields are effectively final.
private String clusterName; private String clusterName;
private Subscription clusterSubscription; private Subscription clusterSubscription;
private LoadBalancer childLb;
CdsLoadBalancer2(Helper helper) {
this(helper, LoadBalancerRegistry.getDefaultRegistry());
}
@VisibleForTesting
CdsLoadBalancer2(Helper helper, LoadBalancerRegistry lbRegistry) { CdsLoadBalancer2(Helper helper, LoadBalancerRegistry lbRegistry) {
this.helper = checkNotNull(helper, "helper"); this.helper = checkNotNull(helper, "helper");
this.lbRegistry = checkNotNull(lbRegistry, "lbRegistry"); this.lbRegistry = checkNotNull(lbRegistry, "lbRegistry");
this.delegate = new GracefulSwitchLoadBalancer(helper);
logger = XdsLogger.withLogId(InternalLogId.allocate("cds-lb", helper.getAuthority())); logger = XdsLogger.withLogId(InternalLogId.allocate("cds-lb", helper.getAuthority()));
logger.log(XdsLogLevel.INFO, "Created"); logger.log(XdsLogLevel.INFO, "Created");
} }
@ -91,7 +89,7 @@ final class CdsLoadBalancer2 extends LoadBalancer {
if (clusterSubscription == null) { if (clusterSubscription == null) {
// Should be impossible, because XdsDependencyManager wouldn't have generated this // Should be impossible, because XdsDependencyManager wouldn't have generated this
return fail(Status.INTERNAL.withDescription( return fail(Status.INTERNAL.withDescription(
errorPrefix() + "Unable to find non-dynamic root cluster")); errorPrefix() + "Unable to find non-dynamic cluster"));
} }
// The dynamic cluster must not have loaded yet // The dynamic cluster must not have loaded yet
return Status.OK; return Status.OK;
@ -100,42 +98,25 @@ final class CdsLoadBalancer2 extends LoadBalancer {
return fail(clusterConfigOr.getStatus()); return fail(clusterConfigOr.getStatus());
} }
XdsClusterConfig clusterConfig = clusterConfigOr.getValue(); XdsClusterConfig clusterConfig = clusterConfigOr.getValue();
List<String> leafNames;
if (clusterConfig.getChildren() instanceof AggregateConfig) {
leafNames = ((AggregateConfig) clusterConfig.getChildren()).getLeafNames();
} else if (clusterConfig.getChildren() instanceof EndpointConfig) {
leafNames = ImmutableList.of(clusterName);
} else {
return fail(Status.INTERNAL.withDescription(
errorPrefix() + "Unexpected cluster children type: "
+ clusterConfig.getChildren().getClass()));
}
if (leafNames.isEmpty()) {
// Should be impossible, because XdsClusterResource validated this
return fail(Status.UNAVAILABLE.withDescription(
errorPrefix() + "Zero leaf clusters for root cluster " + clusterName));
}
Status noneFoundError = Status.INTERNAL NameResolver.ConfigOrError configOrError;
.withDescription(errorPrefix() + "No leaves and no error; this is a bug"); Object gracefulConfig;
List<DiscoveryMechanism> instances = new ArrayList<>(); if (clusterConfig.getChildren() instanceof EndpointConfig) {
for (String leafName : leafNames) { // The LB policy config is provided in service_config.proto/JSON format.
StatusOr<XdsClusterConfig> leafConfigOr = xdsConfig.getClusters().get(leafName); configOrError =
if (!leafConfigOr.hasValue()) { GracefulSwitchLoadBalancer.parseLoadBalancingPolicyConfig(
noneFoundError = leafConfigOr.getStatus(); Arrays.asList(clusterConfig.getClusterResource().lbPolicyConfig()),
continue; lbRegistry);
if (configOrError.getError() != null) {
// Should be impossible, because XdsClusterResource validated this
return fail(Status.INTERNAL.withDescription(
errorPrefix() + "Unable to parse the LB config: " + configOrError.getError()));
} }
if (!(leafConfigOr.getValue().getChildren() instanceof EndpointConfig)) { CdsUpdate result = clusterConfig.getClusterResource();
noneFoundError = Status.INTERNAL.withDescription(
errorPrefix() + "Unexpected child " + leafName + " cluster children type: "
+ leafConfigOr.getValue().getChildren().getClass());
continue;
}
CdsUpdate result = leafConfigOr.getValue().getClusterResource();
DiscoveryMechanism instance; DiscoveryMechanism instance;
if (result.clusterType() == ClusterType.EDS) { if (result.clusterType() == ClusterType.EDS) {
instance = DiscoveryMechanism.forEds( instance = DiscoveryMechanism.forEds(
leafName, clusterName,
result.edsServiceName(), result.edsServiceName(),
result.lrsServerInfo(), result.lrsServerInfo(),
result.maxConcurrentRequests(), result.maxConcurrentRequests(),
@ -144,45 +125,49 @@ final class CdsLoadBalancer2 extends LoadBalancer {
result.outlierDetection()); result.outlierDetection());
} else { } else {
instance = DiscoveryMechanism.forLogicalDns( instance = DiscoveryMechanism.forLogicalDns(
leafName, clusterName,
result.dnsHostName(), result.dnsHostName(),
result.lrsServerInfo(), result.lrsServerInfo(),
result.maxConcurrentRequests(), result.maxConcurrentRequests(),
result.upstreamTlsContext(), result.upstreamTlsContext(),
result.filterMetadata()); result.filterMetadata());
} }
instances.add(instance); gracefulConfig = GracefulSwitchLoadBalancer.createLoadBalancingPolicyConfig(
} lbRegistry.getProvider(CLUSTER_RESOLVER_POLICY_NAME),
if (instances.isEmpty()) { new ClusterResolverConfig(
return fail(noneFoundError); instance,
} configOrError.getConfig(),
clusterConfig.getClusterResource().isHttp11ProxyAvailable()));
// The LB policy config is provided in service_config.proto/JSON format. } else if (clusterConfig.getChildren() instanceof AggregateConfig) {
NameResolver.ConfigOrError configOrError = Map<String, PriorityChildConfig> priorityChildConfigs = new HashMap<>();
GracefulSwitchLoadBalancer.parseLoadBalancingPolicyConfig( List<String> leafClusters = ((AggregateConfig) clusterConfig.getChildren()).getLeafNames();
Arrays.asList(clusterConfig.getClusterResource().lbPolicyConfig()), lbRegistry); for (String childCluster: leafClusters) {
if (configOrError.getError() != null) { priorityChildConfigs.put(childCluster,
// Should be impossible, because XdsClusterResource validated this new PriorityChildConfig(
GracefulSwitchLoadBalancer.createLoadBalancingPolicyConfig(
lbRegistry.getProvider(CDS_POLICY_NAME),
new CdsConfig(childCluster)),
false));
}
gracefulConfig = GracefulSwitchLoadBalancer.createLoadBalancingPolicyConfig(
lbRegistry.getProvider(PRIORITY_POLICY_NAME),
new PriorityLoadBalancerProvider.PriorityLbConfig(
Collections.unmodifiableMap(priorityChildConfigs), leafClusters));
} else {
return fail(Status.INTERNAL.withDescription( return fail(Status.INTERNAL.withDescription(
errorPrefix() + "Unable to parse the LB config: " + configOrError.getError())); errorPrefix() + "Unexpected cluster children type: "
+ clusterConfig.getChildren().getClass()));
} }
ClusterResolverConfig config = new ClusterResolverConfig( return delegate.acceptResolvedAddresses(
Collections.unmodifiableList(instances), resolvedAddresses.toBuilder().setLoadBalancingPolicyConfig(gracefulConfig).build());
configOrError.getConfig(),
clusterConfig.getClusterResource().isHttp11ProxyAvailable());
if (childLb == null) {
childLb = lbRegistry.getProvider(CLUSTER_RESOLVER_POLICY_NAME).newLoadBalancer(helper);
}
return childLb.acceptResolvedAddresses(
resolvedAddresses.toBuilder().setLoadBalancingPolicyConfig(config).build());
} }
@Override @Override
public void handleNameResolutionError(Status error) { public void handleNameResolutionError(Status error) {
logger.log(XdsLogLevel.WARNING, "Received name resolution error: {0}", error); logger.log(XdsLogLevel.WARNING, "Received name resolution error: {0}", error);
if (childLb != null) { if (delegate != null) {
childLb.handleNameResolutionError(error); delegate.handleNameResolutionError(error);
} else { } else {
helper.updateBalancingState( helper.updateBalancingState(
TRANSIENT_FAILURE, new FixedResultPicker(PickResult.withError(error))); TRANSIENT_FAILURE, new FixedResultPicker(PickResult.withError(error)));
@ -192,10 +177,8 @@ final class CdsLoadBalancer2 extends LoadBalancer {
@Override @Override
public void shutdown() { public void shutdown() {
logger.log(XdsLogLevel.INFO, "Shutdown"); logger.log(XdsLogLevel.INFO, "Shutdown");
if (childLb != null) { delegate.shutdown();
childLb.shutdown(); delegate = new GracefulSwitchLoadBalancer(helper);
childLb = null;
}
if (clusterSubscription != null) { if (clusterSubscription != null) {
clusterSubscription.close(); clusterSubscription.close();
clusterSubscription = null; clusterSubscription = null;
@ -204,10 +187,7 @@ final class CdsLoadBalancer2 extends LoadBalancer {
@CheckReturnValue // don't forget to return up the stack after the fail call @CheckReturnValue // don't forget to return up the stack after the fail call
private Status fail(Status error) { private Status fail(Status error) {
if (childLb != null) { delegate.shutdown();
childLb.shutdown();
childLb = null;
}
helper.updateBalancingState( helper.updateBalancingState(
TRANSIENT_FAILURE, new FixedResultPicker(PickResult.withError(error))); TRANSIENT_FAILURE, new FixedResultPicker(PickResult.withError(error)));
return Status.OK; // XdsNameResolver isn't a polling NR, so this value doesn't matter return Status.OK; // XdsNameResolver isn't a polling NR, so this value doesn't matter

View File

@ -23,6 +23,7 @@ import io.grpc.Internal;
import io.grpc.LoadBalancer; import io.grpc.LoadBalancer;
import io.grpc.LoadBalancer.Helper; import io.grpc.LoadBalancer.Helper;
import io.grpc.LoadBalancerProvider; import io.grpc.LoadBalancerProvider;
import io.grpc.LoadBalancerRegistry;
import io.grpc.NameResolver.ConfigOrError; import io.grpc.NameResolver.ConfigOrError;
import io.grpc.Status; import io.grpc.Status;
import io.grpc.internal.JsonUtil; import io.grpc.internal.JsonUtil;
@ -51,9 +52,24 @@ public class CdsLoadBalancerProvider extends LoadBalancerProvider {
return XdsLbPolicies.CDS_POLICY_NAME; return XdsLbPolicies.CDS_POLICY_NAME;
} }
private final LoadBalancerRegistry loadBalancerRegistry;
public CdsLoadBalancerProvider() {
this.loadBalancerRegistry = null;
}
public CdsLoadBalancerProvider(LoadBalancerRegistry loadBalancerRegistry) {
this.loadBalancerRegistry = loadBalancerRegistry;
}
@Override @Override
public LoadBalancer newLoadBalancer(Helper helper) { public LoadBalancer newLoadBalancer(Helper helper) {
return new CdsLoadBalancer2(helper); LoadBalancerRegistry loadBalancerRegistry = this.loadBalancerRegistry;
if (loadBalancerRegistry == null) {
loadBalancerRegistry = LoadBalancerRegistry.getDefaultRegistry();
}
return new CdsLoadBalancer2(helper, loadBalancerRegistry);
} }
@Override @Override

View File

@ -168,8 +168,8 @@ final class ClusterResolverLoadBalancer extends LoadBalancer {
*/ */
private final class ClusterResolverLbState extends LoadBalancer { private final class ClusterResolverLbState extends LoadBalancer {
private final Helper helper; private final Helper helper;
private final List<String> clusters = new ArrayList<>(); private ClusterState clusterState;
private final Map<String, ClusterState> clusterStates = new HashMap<>(); private String cluster;
private Object endpointLbConfig; private Object endpointLbConfig;
private ResolvedAddresses resolvedAddresses; private ResolvedAddresses resolvedAddresses;
private LoadBalancer childLb; private LoadBalancer childLb;
@ -185,21 +185,18 @@ final class ClusterResolverLoadBalancer extends LoadBalancer {
ClusterResolverConfig config = ClusterResolverConfig config =
(ClusterResolverConfig) resolvedAddresses.getLoadBalancingPolicyConfig(); (ClusterResolverConfig) resolvedAddresses.getLoadBalancingPolicyConfig();
endpointLbConfig = config.lbConfig; endpointLbConfig = config.lbConfig;
for (DiscoveryMechanism instance : config.discoveryMechanisms) { DiscoveryMechanism instance = config.discoveryMechanism;
clusters.add(instance.cluster); cluster = instance.cluster;
ClusterState state; if (instance.type == DiscoveryMechanism.Type.EDS) {
if (instance.type == DiscoveryMechanism.Type.EDS) { clusterState = new EdsClusterState(instance.cluster, instance.edsServiceName,
state = new EdsClusterState(instance.cluster, instance.edsServiceName, instance.lrsServerInfo, instance.maxConcurrentRequests, instance.tlsContext,
instance.lrsServerInfo, instance.maxConcurrentRequests, instance.tlsContext, instance.filterMetadata, instance.outlierDetection);
instance.filterMetadata, instance.outlierDetection); } else { // logical DNS
} else { // logical DNS clusterState = new LogicalDnsClusterState(instance.cluster, instance.dnsHostName,
state = new LogicalDnsClusterState(instance.cluster, instance.dnsHostName, instance.lrsServerInfo, instance.maxConcurrentRequests, instance.tlsContext,
instance.lrsServerInfo, instance.maxConcurrentRequests, instance.tlsContext, instance.filterMetadata);
instance.filterMetadata);
}
clusterStates.put(instance.cluster, state);
state.start();
} }
clusterState.start();
return Status.OK; return Status.OK;
} }
@ -215,9 +212,7 @@ final class ClusterResolverLoadBalancer extends LoadBalancer {
@Override @Override
public void shutdown() { public void shutdown() {
for (ClusterState state : clusterStates.values()) { clusterState.shutdown();
state.shutdown();
}
if (childLb != null) { if (childLb != null) {
childLb.shutdown(); childLb.shutdown();
} }
@ -229,24 +224,21 @@ final class ClusterResolverLoadBalancer extends LoadBalancer {
List<String> priorities = new ArrayList<>(); // totally ordered priority list List<String> priorities = new ArrayList<>(); // totally ordered priority list
Status endpointNotFound = Status.OK; Status endpointNotFound = Status.OK;
for (String cluster : clusters) { // Propagate endpoints to the child LB policy only after all clusters have been resolved.
ClusterState state = clusterStates.get(cluster); if (!clusterState.resolved && clusterState.status.isOk()) {
// Propagate endpoints to the child LB policy only after all clusters have been resolved. return;
if (!state.resolved && state.status.isOk()) { }
return; if (clusterState.result != null) {
} addresses.addAll(clusterState.result.addresses);
if (state.result != null) { priorityChildConfigs.putAll(clusterState.result.priorityChildConfigs);
addresses.addAll(state.result.addresses); priorities.addAll(clusterState.result.priorities);
priorityChildConfigs.putAll(state.result.priorityChildConfigs); } else {
priorities.addAll(state.result.priorities); endpointNotFound = clusterState.status;
} else {
endpointNotFound = state.status;
}
} }
if (addresses.isEmpty()) { if (addresses.isEmpty()) {
if (endpointNotFound.isOk()) { if (endpointNotFound.isOk()) {
endpointNotFound = Status.UNAVAILABLE.withDescription( endpointNotFound = Status.UNAVAILABLE.withDescription(
"No usable endpoint from cluster(s): " + clusters); "No usable endpoint from cluster: " + cluster);
} else { } else {
endpointNotFound = endpointNotFound =
Status.UNAVAILABLE.withCause(endpointNotFound.getCause()) Status.UNAVAILABLE.withCause(endpointNotFound.getCause())
@ -274,22 +266,12 @@ final class ClusterResolverLoadBalancer extends LoadBalancer {
} }
private void handleEndpointResolutionError() { private void handleEndpointResolutionError() {
boolean allInError = true; if (!clusterState.status.isOk()) {
Status error = null;
for (String cluster : clusters) {
ClusterState state = clusterStates.get(cluster);
if (state.status.isOk()) {
allInError = false;
} else {
error = state.status;
}
}
if (allInError) {
if (childLb != null) { if (childLb != null) {
childLb.handleNameResolutionError(error); childLb.handleNameResolutionError(clusterState.status);
} else { } else {
helper.updateBalancingState( helper.updateBalancingState(
TRANSIENT_FAILURE, new FixedResultPicker(PickResult.withError(error))); TRANSIENT_FAILURE, new FixedResultPicker(PickResult.withError(clusterState.status)));
} }
} }
} }
@ -306,10 +288,8 @@ final class ClusterResolverLoadBalancer extends LoadBalancer {
@Override @Override
public void refreshNameResolution() { public void refreshNameResolution() {
for (ClusterState state : clusterStates.values()) { if (clusterState instanceof LogicalDnsClusterState) {
if (state instanceof LogicalDnsClusterState) { ((LogicalDnsClusterState) clusterState).refresh();
((LogicalDnsClusterState) state).refresh();
}
} }
} }

View File

@ -30,7 +30,6 @@ import io.grpc.Status;
import io.grpc.xds.EnvoyServerProtoData.OutlierDetection; import io.grpc.xds.EnvoyServerProtoData.OutlierDetection;
import io.grpc.xds.EnvoyServerProtoData.UpstreamTlsContext; import io.grpc.xds.EnvoyServerProtoData.UpstreamTlsContext;
import io.grpc.xds.client.Bootstrapper.ServerInfo; import io.grpc.xds.client.Bootstrapper.ServerInfo;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -70,15 +69,15 @@ public final class ClusterResolverLoadBalancerProvider extends LoadBalancerProvi
} }
static final class ClusterResolverConfig { static final class ClusterResolverConfig {
// Ordered list of clusters to be resolved. // Cluster to be resolved.
final List<DiscoveryMechanism> discoveryMechanisms; final DiscoveryMechanism discoveryMechanism;
// GracefulSwitch configuration // GracefulSwitch configuration
final Object lbConfig; final Object lbConfig;
private final boolean isHttp11ProxyAvailable; private final boolean isHttp11ProxyAvailable;
ClusterResolverConfig(List<DiscoveryMechanism> discoveryMechanisms, Object lbConfig, ClusterResolverConfig(DiscoveryMechanism discoveryMechanism, Object lbConfig,
boolean isHttp11ProxyAvailable) { boolean isHttp11ProxyAvailable) {
this.discoveryMechanisms = checkNotNull(discoveryMechanisms, "discoveryMechanisms"); this.discoveryMechanism = checkNotNull(discoveryMechanism, "discoveryMechanism");
this.lbConfig = checkNotNull(lbConfig, "lbConfig"); this.lbConfig = checkNotNull(lbConfig, "lbConfig");
this.isHttp11ProxyAvailable = isHttp11ProxyAvailable; this.isHttp11ProxyAvailable = isHttp11ProxyAvailable;
} }
@ -89,7 +88,7 @@ public final class ClusterResolverLoadBalancerProvider extends LoadBalancerProvi
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hash(discoveryMechanisms, lbConfig, isHttp11ProxyAvailable); return Objects.hash(discoveryMechanism, lbConfig, isHttp11ProxyAvailable);
} }
@Override @Override
@ -101,7 +100,7 @@ public final class ClusterResolverLoadBalancerProvider extends LoadBalancerProvi
return false; return false;
} }
ClusterResolverConfig that = (ClusterResolverConfig) o; ClusterResolverConfig that = (ClusterResolverConfig) o;
return discoveryMechanisms.equals(that.discoveryMechanisms) return discoveryMechanism.equals(that.discoveryMechanism)
&& lbConfig.equals(that.lbConfig) && lbConfig.equals(that.lbConfig)
&& isHttp11ProxyAvailable == that.isHttp11ProxyAvailable; && isHttp11ProxyAvailable == that.isHttp11ProxyAvailable;
} }
@ -109,7 +108,7 @@ public final class ClusterResolverLoadBalancerProvider extends LoadBalancerProvi
@Override @Override
public String toString() { public String toString() {
return MoreObjects.toStringHelper(this) return MoreObjects.toStringHelper(this)
.add("discoveryMechanisms", discoveryMechanisms) .add("discoveryMechanism", discoveryMechanism)
.add("lbConfig", lbConfig) .add("lbConfig", lbConfig)
.add("isHttp11ProxyAvailable", isHttp11ProxyAvailable) .add("isHttp11ProxyAvailable", isHttp11ProxyAvailable)
.toString(); .toString();

View File

@ -18,6 +18,7 @@ package io.grpc.xds;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static io.grpc.xds.XdsLbPolicies.CLUSTER_RESOLVER_POLICY_NAME; import static io.grpc.xds.XdsLbPolicies.CLUSTER_RESOLVER_POLICY_NAME;
import static io.grpc.xds.XdsLbPolicies.PRIORITY_POLICY_NAME;
import static io.grpc.xds.XdsTestControlPlaneService.ADS_TYPE_URL_CDS; import static io.grpc.xds.XdsTestControlPlaneService.ADS_TYPE_URL_CDS;
import static io.grpc.xds.XdsTestControlPlaneService.ADS_TYPE_URL_EDS; import static io.grpc.xds.XdsTestControlPlaneService.ADS_TYPE_URL_EDS;
import static io.grpc.xds.XdsTestControlPlaneService.ADS_TYPE_URL_LDS; import static io.grpc.xds.XdsTestControlPlaneService.ADS_TYPE_URL_LDS;
@ -27,6 +28,7 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import com.github.xds.type.v3.TypedStruct; import com.github.xds.type.v3.TypedStruct;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
@ -157,7 +159,9 @@ public class CdsLoadBalancer2Test {
new LeastRequestLoadBalancerProvider())); new LeastRequestLoadBalancerProvider()));
lbRegistry.register(new FakeLoadBalancerProvider("wrr_locality_experimental", lbRegistry.register(new FakeLoadBalancerProvider("wrr_locality_experimental",
new WrrLocalityLoadBalancerProvider())); new WrrLocalityLoadBalancerProvider()));
loadBalancer = new CdsLoadBalancer2(helper, lbRegistry); CdsLoadBalancerProvider cdsLoadBalancerProvider = new CdsLoadBalancerProvider(lbRegistry);
lbRegistry.register(cdsLoadBalancerProvider);
loadBalancer = (CdsLoadBalancer2) cdsLoadBalancerProvider.newLoadBalancer(helper);
cleanupRule.register(InProcessServerBuilder cleanupRule.register(InProcessServerBuilder
.forName("control-plane.example.com") .forName("control-plane.example.com")
@ -169,6 +173,8 @@ public class CdsLoadBalancer2Test {
SynchronizationContext syncContext = new SynchronizationContext((t, e) -> { SynchronizationContext syncContext = new SynchronizationContext((t, e) -> {
throw new AssertionError(e); throw new AssertionError(e);
}); });
when(helper.getSynchronizationContext()).thenReturn(syncContext);
when(helper.getScheduledExecutorService()).thenReturn(fakeClock.getScheduledExecutorService());
NameResolver.Args nameResolverArgs = NameResolver.Args.newBuilder() NameResolver.Args nameResolverArgs = NameResolver.Args.newBuilder()
.setDefaultPort(8080) .setDefaultPort(8080)
@ -246,13 +252,12 @@ public class CdsLoadBalancer2Test {
FakeLoadBalancer childBalancer = Iterables.getOnlyElement(childBalancers); FakeLoadBalancer childBalancer = Iterables.getOnlyElement(childBalancers);
assertThat(childBalancer.name).isEqualTo(CLUSTER_RESOLVER_POLICY_NAME); assertThat(childBalancer.name).isEqualTo(CLUSTER_RESOLVER_POLICY_NAME);
ClusterResolverConfig childLbConfig = (ClusterResolverConfig) childBalancer.config; ClusterResolverConfig childLbConfig = (ClusterResolverConfig) childBalancer.config;
assertThat(childLbConfig.discoveryMechanisms).isEqualTo( assertThat(childLbConfig.discoveryMechanism).isEqualTo(
Arrays.asList( DiscoveryMechanism.forEds(
DiscoveryMechanism.forEds( CLUSTER, EDS_SERVICE_NAME, lrsServerInfo, 100L, upstreamTlsContext,
CLUSTER, EDS_SERVICE_NAME, lrsServerInfo, 100L, upstreamTlsContext, Collections.emptyMap(), io.grpc.xds.EnvoyServerProtoData.OutlierDetection.create(
Collections.emptyMap(), io.grpc.xds.EnvoyServerProtoData.OutlierDetection.create( null, null, null, null, SuccessRateEjection.create(null, null, null, null),
null, null, null, null, SuccessRateEjection.create(null, null, null, null), FailurePercentageEjection.create(null, null, null, null))));
FailurePercentageEjection.create(null, null, null, null)))));
assertThat( assertThat(
GracefulSwitchLoadBalancerAccessor.getChildProvider(childLbConfig.lbConfig).getPolicyName()) GracefulSwitchLoadBalancerAccessor.getChildProvider(childLbConfig.lbConfig).getPolicyName())
.isEqualTo("wrr_locality_experimental"); .isEqualTo("wrr_locality_experimental");
@ -296,11 +301,10 @@ public class CdsLoadBalancer2Test {
FakeLoadBalancer childBalancer = Iterables.getOnlyElement(childBalancers); FakeLoadBalancer childBalancer = Iterables.getOnlyElement(childBalancers);
assertThat(childBalancer.name).isEqualTo(CLUSTER_RESOLVER_POLICY_NAME); assertThat(childBalancer.name).isEqualTo(CLUSTER_RESOLVER_POLICY_NAME);
ClusterResolverConfig childLbConfig = (ClusterResolverConfig) childBalancer.config; ClusterResolverConfig childLbConfig = (ClusterResolverConfig) childBalancer.config;
assertThat(childLbConfig.discoveryMechanisms).isEqualTo( assertThat(childLbConfig.discoveryMechanism).isEqualTo(
Arrays.asList( DiscoveryMechanism.forLogicalDns(
DiscoveryMechanism.forLogicalDns( CLUSTER, "dns.example.com:1111", lrsServerInfo, 100L, upstreamTlsContext,
CLUSTER, "dns.example.com:1111", lrsServerInfo, 100L, upstreamTlsContext, Collections.emptyMap()));
Collections.emptyMap())));
assertThat( assertThat(
GracefulSwitchLoadBalancerAccessor.getChildProvider(childLbConfig.lbConfig).getPolicyName()) GracefulSwitchLoadBalancerAccessor.getChildProvider(childLbConfig.lbConfig).getPolicyName())
.isEqualTo("wrr_locality_experimental"); .isEqualTo("wrr_locality_experimental");
@ -332,10 +336,9 @@ public class CdsLoadBalancer2Test {
assertThat(childBalancers).hasSize(1); assertThat(childBalancers).hasSize(1);
FakeLoadBalancer childBalancer = Iterables.getOnlyElement(childBalancers); FakeLoadBalancer childBalancer = Iterables.getOnlyElement(childBalancers);
ClusterResolverConfig childLbConfig = (ClusterResolverConfig) childBalancer.config; ClusterResolverConfig childLbConfig = (ClusterResolverConfig) childBalancer.config;
assertThat(childLbConfig.discoveryMechanisms).isEqualTo( assertThat(childLbConfig.discoveryMechanism).isEqualTo(
Arrays.asList(
DiscoveryMechanism.forEds( DiscoveryMechanism.forEds(
CLUSTER, EDS_SERVICE_NAME, null, 100L, null, Collections.emptyMap(), null))); CLUSTER, EDS_SERVICE_NAME, null, 100L, null, Collections.emptyMap(), null));
cluster = EDS_CLUSTER.toBuilder() cluster = EDS_CLUSTER.toBuilder()
.setCircuitBreakers(CircuitBreakers.newBuilder() .setCircuitBreakers(CircuitBreakers.newBuilder()
@ -348,10 +351,9 @@ public class CdsLoadBalancer2Test {
assertThat(childBalancers).hasSize(1); assertThat(childBalancers).hasSize(1);
childBalancer = Iterables.getOnlyElement(childBalancers); childBalancer = Iterables.getOnlyElement(childBalancers);
childLbConfig = (ClusterResolverConfig) childBalancer.config; childLbConfig = (ClusterResolverConfig) childBalancer.config;
assertThat(childLbConfig.discoveryMechanisms).isEqualTo( assertThat(childLbConfig.discoveryMechanism).isEqualTo(
Arrays.asList(
DiscoveryMechanism.forEds( DiscoveryMechanism.forEds(
CLUSTER, EDS_SERVICE_NAME, null, 200L, null, Collections.emptyMap(), null))); CLUSTER, EDS_SERVICE_NAME, null, 200L, null, Collections.emptyMap(), null));
} }
@Test @Test
@ -363,10 +365,9 @@ public class CdsLoadBalancer2Test {
assertThat(childBalancers).hasSize(1); assertThat(childBalancers).hasSize(1);
FakeLoadBalancer childBalancer = Iterables.getOnlyElement(childBalancers); FakeLoadBalancer childBalancer = Iterables.getOnlyElement(childBalancers);
ClusterResolverConfig childLbConfig = (ClusterResolverConfig) childBalancer.config; ClusterResolverConfig childLbConfig = (ClusterResolverConfig) childBalancer.config;
assertThat(childLbConfig.discoveryMechanisms).isEqualTo( assertThat(childLbConfig.discoveryMechanism).isEqualTo(
Arrays.asList(
DiscoveryMechanism.forEds( DiscoveryMechanism.forEds(
CLUSTER, EDS_SERVICE_NAME, null, null, null, Collections.emptyMap(), null))); CLUSTER, EDS_SERVICE_NAME, null, null, null, Collections.emptyMap(), null));
controlPlaneService.setXdsConfig(ADS_TYPE_URL_CDS, ImmutableMap.of()); controlPlaneService.setXdsConfig(ADS_TYPE_URL_CDS, ImmutableMap.of());
@ -395,10 +396,9 @@ public class CdsLoadBalancer2Test {
assertThat(childBalancers).hasSize(1); assertThat(childBalancers).hasSize(1);
FakeLoadBalancer childBalancer = Iterables.getOnlyElement(childBalancers); FakeLoadBalancer childBalancer = Iterables.getOnlyElement(childBalancers);
ClusterResolverConfig childLbConfig = (ClusterResolverConfig) childBalancer.config; ClusterResolverConfig childLbConfig = (ClusterResolverConfig) childBalancer.config;
assertThat(childLbConfig.discoveryMechanisms).isEqualTo( assertThat(childLbConfig.discoveryMechanism).isEqualTo(
Arrays.asList(
DiscoveryMechanism.forEds( DiscoveryMechanism.forEds(
clusterName, EDS_SERVICE_NAME, null, null, null, Collections.emptyMap(), null))); clusterName, EDS_SERVICE_NAME, null, null, null, Collections.emptyMap(), null));
assertThat(this.lastXdsConfig.getClusters()).containsKey(clusterName); assertThat(this.lastXdsConfig.getClusters()).containsKey(clusterName);
shutdownLoadBalancer(); shutdownLoadBalancer();
@ -406,7 +406,12 @@ public class CdsLoadBalancer2Test {
} }
@Test @Test
public void discoverAggregateCluster() { public void discoverAggregateCluster_createsPriorityLbPolicy() {
lbRegistry.register(new FakeLoadBalancerProvider(PRIORITY_POLICY_NAME));
CdsLoadBalancerProvider cdsLoadBalancerProvider = new CdsLoadBalancerProvider(lbRegistry);
lbRegistry.register(cdsLoadBalancerProvider);
loadBalancer = (CdsLoadBalancer2) cdsLoadBalancerProvider.newLoadBalancer(helper);
String cluster1 = "cluster-01.googleapis.com"; String cluster1 = "cluster-01.googleapis.com";
String cluster2 = "cluster-02.googleapis.com"; String cluster2 = "cluster-02.googleapis.com";
String cluster3 = "cluster-03.googleapis.com"; String cluster3 = "cluster-03.googleapis.com";
@ -459,20 +464,77 @@ public class CdsLoadBalancer2Test {
verify(helper, never()).updateBalancingState(eq(ConnectivityState.TRANSIENT_FAILURE), any()); verify(helper, never()).updateBalancingState(eq(ConnectivityState.TRANSIENT_FAILURE), any());
assertThat(childBalancers).hasSize(1); assertThat(childBalancers).hasSize(1);
FakeLoadBalancer childBalancer = Iterables.getOnlyElement(childBalancers); FakeLoadBalancer childBalancer = Iterables.getOnlyElement(childBalancers);
assertThat(childBalancer.name).isEqualTo(CLUSTER_RESOLVER_POLICY_NAME); assertThat(childBalancer.name).isEqualTo(PRIORITY_POLICY_NAME);
ClusterResolverConfig childLbConfig = (ClusterResolverConfig) childBalancer.config; PriorityLoadBalancerProvider.PriorityLbConfig childLbConfig =
// Clusters are resolved recursively, later duplicates removed: [cluster3, cluster4, cluster2] (PriorityLoadBalancerProvider.PriorityLbConfig) childBalancer.config;
assertThat(childLbConfig.discoveryMechanisms).isEqualTo( assertThat(childLbConfig.priorities).hasSize(3);
Arrays.asList( assertThat(childLbConfig.priorities.get(0)).isEqualTo(cluster3);
DiscoveryMechanism.forEds( assertThat(childLbConfig.priorities.get(1)).isEqualTo(cluster4);
cluster3, EDS_SERVICE_NAME, null, 100L, null, Collections.emptyMap(), null), assertThat(childLbConfig.priorities.get(2)).isEqualTo(cluster2);
DiscoveryMechanism.forEds( assertThat(childLbConfig.childConfigs).hasSize(3);
cluster4, EDS_SERVICE_NAME, null, null, null, Collections.emptyMap(), null), PriorityLoadBalancerProvider.PriorityLbConfig.PriorityChildConfig childConfig3 =
DiscoveryMechanism.forLogicalDns( childLbConfig.childConfigs.get(cluster3);
cluster2, "dns.example.com:1111", null, null, null, Collections.emptyMap())));
assertThat( assertThat(
GracefulSwitchLoadBalancerAccessor.getChildProvider(childLbConfig.lbConfig).getPolicyName()) GracefulSwitchLoadBalancerAccessor.getChildProvider(childConfig3.childConfig)
.isEqualTo("ring_hash_experimental"); // dominated by top-level cluster's config .getPolicyName())
.isEqualTo("cds_experimental");
PriorityLoadBalancerProvider.PriorityLbConfig.PriorityChildConfig childConfig4 =
childLbConfig.childConfigs.get(cluster4);
assertThat(
GracefulSwitchLoadBalancerAccessor.getChildProvider(childConfig4.childConfig)
.getPolicyName())
.isEqualTo("cds_experimental");
PriorityLoadBalancerProvider.PriorityLbConfig.PriorityChildConfig childConfig2 =
childLbConfig.childConfigs.get(cluster2);
assertThat(
GracefulSwitchLoadBalancerAccessor.getChildProvider(childConfig2.childConfig)
.getPolicyName())
.isEqualTo("cds_experimental");
}
@Test
// Both priorities will get tried using real priority LB policy.
public void discoverAggregateCluster_testChildCdsLbPolicyParsing() {
lbRegistry.register(new PriorityLoadBalancerProvider());
CdsLoadBalancerProvider cdsLoadBalancerProvider = new CdsLoadBalancerProvider(lbRegistry);
lbRegistry.register(cdsLoadBalancerProvider);
loadBalancer = (CdsLoadBalancer2) cdsLoadBalancerProvider.newLoadBalancer(helper);
String cluster1 = "cluster-01.googleapis.com";
String cluster2 = "cluster-02.googleapis.com";
controlPlaneService.setXdsConfig(ADS_TYPE_URL_CDS, ImmutableMap.of(
// CLUSTER (aggr.) -> [cluster1 (EDS), cluster2 (EDS)]
CLUSTER, Cluster.newBuilder()
.setName(CLUSTER)
.setClusterType(Cluster.CustomClusterType.newBuilder()
.setName("envoy.clusters.aggregate")
.setTypedConfig(Any.pack(ClusterConfig.newBuilder()
.addClusters(cluster1)
.addClusters(cluster2)
.build())))
.build(),
cluster1, EDS_CLUSTER.toBuilder().setName(cluster1).build(),
cluster2, EDS_CLUSTER.toBuilder().setName(cluster2).build()));
startXdsDepManager();
verify(helper, never()).updateBalancingState(eq(ConnectivityState.TRANSIENT_FAILURE), any());
assertThat(childBalancers).hasSize(2);
ClusterResolverConfig cluster1ResolverConfig =
(ClusterResolverConfig) childBalancers.get(0).config;
assertThat(cluster1ResolverConfig.discoveryMechanism.cluster)
.isEqualTo("cluster-01.googleapis.com");
assertThat(cluster1ResolverConfig.discoveryMechanism.type)
.isEqualTo(DiscoveryMechanism.Type.EDS);
assertThat(cluster1ResolverConfig.discoveryMechanism.edsServiceName)
.isEqualTo("backend-service-1.googleapis.com");
ClusterResolverConfig cluster2ResolverConfig =
(ClusterResolverConfig) childBalancers.get(1).config;
assertThat(cluster2ResolverConfig.discoveryMechanism.cluster)
.isEqualTo("cluster-02.googleapis.com");
assertThat(cluster2ResolverConfig.discoveryMechanism.type)
.isEqualTo(DiscoveryMechanism.Type.EDS);
assertThat(cluster2ResolverConfig.discoveryMechanism.edsServiceName)
.isEqualTo("backend-service-1.googleapis.com");
} }
@Test @Test
@ -500,6 +562,11 @@ public class CdsLoadBalancer2Test {
@Test @Test
public void aggregateCluster_noNonAggregateClusterExits_returnErrorPicker() { public void aggregateCluster_noNonAggregateClusterExits_returnErrorPicker() {
lbRegistry.register(new PriorityLoadBalancerProvider());
CdsLoadBalancerProvider cdsLoadBalancerProvider = new CdsLoadBalancerProvider(lbRegistry);
lbRegistry.register(cdsLoadBalancerProvider);
loadBalancer = (CdsLoadBalancer2) cdsLoadBalancerProvider.newLoadBalancer(helper);
String cluster1 = "cluster-01.googleapis.com"; String cluster1 = "cluster-01.googleapis.com";
controlPlaneService.setXdsConfig(ADS_TYPE_URL_CDS, ImmutableMap.of( controlPlaneService.setXdsConfig(ADS_TYPE_URL_CDS, ImmutableMap.of(
// CLUSTER (aggr.) -> [cluster1 (missing)] // CLUSTER (aggr.) -> [cluster1 (missing)]
@ -550,8 +617,8 @@ public class CdsLoadBalancer2Test {
loadBalancer.handleNameResolutionError(Status.UNAVAILABLE.withDescription("unreachable")); loadBalancer.handleNameResolutionError(Status.UNAVAILABLE.withDescription("unreachable"));
assertThat(childBalancer.upstreamError.getCode()).isEqualTo(Code.UNAVAILABLE); assertThat(childBalancer.upstreamError.getCode()).isEqualTo(Code.UNAVAILABLE);
assertThat(childBalancer.upstreamError.getDescription()).isEqualTo("unreachable"); assertThat(childBalancer.upstreamError.getDescription()).isEqualTo("unreachable");
verify(helper, never()).updateBalancingState( verify(helper).updateBalancingState(
any(ConnectivityState.class), any(SubchannelPicker.class)); eq(ConnectivityState.CONNECTING), any(SubchannelPicker.class));
} }
@Test @Test

View File

@ -245,7 +245,7 @@ public class ClusterResolverLoadBalancerTest {
@Test @Test
public void edsClustersWithRingHashEndpointLbPolicy() { public void edsClustersWithRingHashEndpointLbPolicy() {
ClusterResolverConfig config = new ClusterResolverConfig( ClusterResolverConfig config = new ClusterResolverConfig(
Collections.singletonList(edsDiscoveryMechanism1), ringHash, false); edsDiscoveryMechanism1, ringHash, false);
deliverLbConfig(config); deliverLbConfig(config);
assertThat(xdsClient.watchers.keySet()).containsExactly(EDS_SERVICE_NAME1); assertThat(xdsClient.watchers.keySet()).containsExactly(EDS_SERVICE_NAME1);
assertThat(childBalancers).isEmpty(); assertThat(childBalancers).isEmpty();
@ -311,7 +311,7 @@ public class ClusterResolverLoadBalancerTest {
@Test @Test
public void edsClustersWithLeastRequestEndpointLbPolicy() { public void edsClustersWithLeastRequestEndpointLbPolicy() {
ClusterResolverConfig config = new ClusterResolverConfig( ClusterResolverConfig config = new ClusterResolverConfig(
Collections.singletonList(edsDiscoveryMechanism1), leastRequest, false); edsDiscoveryMechanism1, leastRequest, false);
deliverLbConfig(config); deliverLbConfig(config);
assertThat(xdsClient.watchers.keySet()).containsExactly(EDS_SERVICE_NAME1); assertThat(xdsClient.watchers.keySet()).containsExactly(EDS_SERVICE_NAME1);
assertThat(childBalancers).isEmpty(); assertThat(childBalancers).isEmpty();
@ -358,7 +358,7 @@ public class ClusterResolverLoadBalancerTest {
@Test @Test
public void edsClustersEndpointHostname_addedToAddressAttribute() { public void edsClustersEndpointHostname_addedToAddressAttribute() {
ClusterResolverConfig config = new ClusterResolverConfig( ClusterResolverConfig config = new ClusterResolverConfig(
Collections.singletonList(edsDiscoveryMechanismWithOutlierDetection), leastRequest, false); edsDiscoveryMechanismWithOutlierDetection, leastRequest, false);
deliverLbConfig(config); deliverLbConfig(config);
assertThat(xdsClient.watchers.keySet()).containsExactly(EDS_SERVICE_NAME1); assertThat(xdsClient.watchers.keySet()).containsExactly(EDS_SERVICE_NAME1);
assertThat(childBalancers).isEmpty(); assertThat(childBalancers).isEmpty();
@ -385,7 +385,7 @@ public class ClusterResolverLoadBalancerTest {
@Test @Test
public void endpointAddressRewritten_whenProxyMetadataIsInEndpointMetadata() { public void endpointAddressRewritten_whenProxyMetadataIsInEndpointMetadata() {
ClusterResolverConfig config = new ClusterResolverConfig( ClusterResolverConfig config = new ClusterResolverConfig(
Collections.singletonList(edsDiscoveryMechanismWithOutlierDetection), leastRequest, true); edsDiscoveryMechanismWithOutlierDetection, leastRequest, true);
deliverLbConfig(config); deliverLbConfig(config);
assertThat(xdsClient.watchers.keySet()).containsExactly(EDS_SERVICE_NAME1); assertThat(xdsClient.watchers.keySet()).containsExactly(EDS_SERVICE_NAME1);
assertThat(childBalancers).isEmpty(); assertThat(childBalancers).isEmpty();
@ -404,7 +404,7 @@ public class ClusterResolverLoadBalancerTest {
LocalityLbEndpoints localityLbEndpoints = LocalityLbEndpoints.create( LocalityLbEndpoints localityLbEndpoints = LocalityLbEndpoints.create(
Arrays.asList( Arrays.asList(
LbEndpoint.create(endpoint, 0 /* loadBalancingWeight */, true, LbEndpoint.create(endpoint, 0 /* loadBalancingWeight */, true,
"hostname1", endpointMetadata)), "hostname1", endpointMetadata)),
100 /* localityWeight */, 1 /* priority */, localityMetadata); 100 /* localityWeight */, 1 /* priority */, localityMetadata);
xdsClient.deliverClusterLoadAssignment( xdsClient.deliverClusterLoadAssignment(
@ -432,7 +432,7 @@ public class ClusterResolverLoadBalancerTest {
@Test @Test
public void endpointAddressRewritten_whenProxyMetadataIsInLocalityMetadata() { public void endpointAddressRewritten_whenProxyMetadataIsInLocalityMetadata() {
ClusterResolverConfig config = new ClusterResolverConfig( ClusterResolverConfig config = new ClusterResolverConfig(
Collections.singletonList(edsDiscoveryMechanismWithOutlierDetection), leastRequest, true); edsDiscoveryMechanismWithOutlierDetection, leastRequest, true);
deliverLbConfig(config); deliverLbConfig(config);
assertThat(xdsClient.watchers.keySet()).containsExactly(EDS_SERVICE_NAME1); assertThat(xdsClient.watchers.keySet()).containsExactly(EDS_SERVICE_NAME1);
assertThat(childBalancers).isEmpty(); assertThat(childBalancers).isEmpty();
@ -451,7 +451,7 @@ public class ClusterResolverLoadBalancerTest {
LocalityLbEndpoints localityLbEndpoints = LocalityLbEndpoints.create( LocalityLbEndpoints localityLbEndpoints = LocalityLbEndpoints.create(
Arrays.asList( Arrays.asList(
LbEndpoint.create(endpoint, 0 /* loadBalancingWeight */, true, LbEndpoint.create(endpoint, 0 /* loadBalancingWeight */, true,
"hostname2", endpointMetadata)), "hostname2", endpointMetadata)),
100 /* localityWeight */, 1 /* priority */, localityMetadata); 100 /* localityWeight */, 1 /* priority */, localityMetadata);
xdsClient.deliverClusterLoadAssignment( xdsClient.deliverClusterLoadAssignment(
@ -479,48 +479,36 @@ public class ClusterResolverLoadBalancerTest {
@Test @Test
public void onlyEdsClusters_receivedEndpoints() { public void onlyEdsClusters_receivedEndpoints() {
ClusterResolverConfig config = new ClusterResolverConfig( ClusterResolverConfig config = new ClusterResolverConfig(
Arrays.asList(edsDiscoveryMechanism1, edsDiscoveryMechanism2), roundRobin, false); edsDiscoveryMechanism2, roundRobin, false);
deliverLbConfig(config); deliverLbConfig(config);
assertThat(xdsClient.watchers.keySet()).containsExactly(EDS_SERVICE_NAME1, EDS_SERVICE_NAME2); assertThat(xdsClient.watchers.keySet()).containsExactly(EDS_SERVICE_NAME2);
assertThat(childBalancers).isEmpty(); assertThat(childBalancers).isEmpty();
// CLUSTER1 has priority 1 (priority3), which has locality 2, which has endpoint3. // CLUSTER1 has priority 1 (priority3), which has locality 2, which has endpoint3.
// CLUSTER2 has priority 1 (priority1) and 2 (priority2); priority1 has locality1, // CLUSTER2 has priority 1 (priority1) and 2 (priority2); priority1 has locality1,
// which has endpoint1 and endpoint2; priority2 has locality3, which has endpoint4. // which has endpoint1 and endpoint2; priority2 has locality3, which has endpoint4.
EquivalentAddressGroup endpoint1 = makeAddress("endpoint-addr-1"); EquivalentAddressGroup endpoint1 = makeAddress("endpoint-addr-1");
EquivalentAddressGroup endpoint2 = makeAddress("endpoint-addr-2"); EquivalentAddressGroup endpoint2 = makeAddress("endpoint-addr-2");
EquivalentAddressGroup endpoint3 = makeAddress("endpoint-addr-3");
EquivalentAddressGroup endpoint4 = makeAddress("endpoint-addr-4"); EquivalentAddressGroup endpoint4 = makeAddress("endpoint-addr-4");
LocalityLbEndpoints localityLbEndpoints1 = LocalityLbEndpoints localityLbEndpoints1 =
LocalityLbEndpoints.create( LocalityLbEndpoints.create(
Arrays.asList( Arrays.asList(
LbEndpoint.create(endpoint1, 100, LbEndpoint.create(endpoint1, 100,
true, "hostname1", ImmutableMap.of()), true, "hostname1", ImmutableMap.of()),
LbEndpoint.create(endpoint2, 100, LbEndpoint.create(endpoint2, 100,
true, "hostname1", ImmutableMap.of())), true, "hostname1", ImmutableMap.of())),
70 /* localityWeight */, 1 /* priority */, ImmutableMap.of()); 70 /* localityWeight */, 1 /* priority */, ImmutableMap.of());
LocalityLbEndpoints localityLbEndpoints2 =
LocalityLbEndpoints.create(
Collections.singletonList(LbEndpoint.create(endpoint3, 100, true,
"hostname2", ImmutableMap.of())),
10 /* localityWeight */, 1 /* priority */, ImmutableMap.of());
LocalityLbEndpoints localityLbEndpoints3 = LocalityLbEndpoints localityLbEndpoints3 =
LocalityLbEndpoints.create( LocalityLbEndpoints.create(
Collections.singletonList(LbEndpoint.create(endpoint4, 100, true, Collections.singletonList(LbEndpoint.create(endpoint4, 100, true,
"hostname3", ImmutableMap.of())), "hostname3", ImmutableMap.of())),
20 /* localityWeight */, 2 /* priority */, ImmutableMap.of()); 20 /* localityWeight */, 2 /* priority */, ImmutableMap.of());
String priority1 = CLUSTER2 + "[child1]"; String priority1 = CLUSTER2 + "[child1]";
String priority2 = CLUSTER2 + "[child2]"; String priority2 = CLUSTER2 + "[child2]";
String priority3 = CLUSTER1 + "[child1]";
// CLUSTER2: locality1 with priority 1 and locality3 with priority 2. // CLUSTER2: locality1 with priority 1 and locality3 with priority 2.
xdsClient.deliverClusterLoadAssignment( xdsClient.deliverClusterLoadAssignment(
EDS_SERVICE_NAME2, EDS_SERVICE_NAME2,
ImmutableMap.of(locality1, localityLbEndpoints1, locality3, localityLbEndpoints3)); ImmutableMap.of(locality1, localityLbEndpoints1, locality3, localityLbEndpoints3));
assertThat(childBalancers).isEmpty(); // not created until all clusters resolved
// CLUSTER1: locality2 with priority 1.
xdsClient.deliverClusterLoadAssignment(
EDS_SERVICE_NAME1, Collections.singletonMap(locality2, localityLbEndpoints2));
// Endpoints of all clusters have been resolved. // Endpoints of all clusters have been resolved.
assertThat(childBalancers).hasSize(1); assertThat(childBalancers).hasSize(1);
@ -528,12 +516,12 @@ public class ClusterResolverLoadBalancerTest {
assertThat(childBalancer.name).isEqualTo(PRIORITY_POLICY_NAME); assertThat(childBalancer.name).isEqualTo(PRIORITY_POLICY_NAME);
PriorityLbConfig priorityLbConfig = (PriorityLbConfig) childBalancer.config; PriorityLbConfig priorityLbConfig = (PriorityLbConfig) childBalancer.config;
assertThat(priorityLbConfig.priorities) assertThat(priorityLbConfig.priorities)
.containsExactly(priority3, priority1, priority2).inOrder(); .containsExactly(priority1, priority2).inOrder();
PriorityChildConfig priorityChildConfig1 = priorityLbConfig.childConfigs.get(priority1); PriorityChildConfig priorityChildConfig1 = priorityLbConfig.childConfigs.get(priority1);
assertThat(priorityChildConfig1.ignoreReresolution).isTrue(); assertThat(priorityChildConfig1.ignoreReresolution).isTrue();
assertThat(GracefulSwitchLoadBalancerAccessor.getChildProvider(priorityChildConfig1.childConfig) assertThat(GracefulSwitchLoadBalancerAccessor.getChildProvider(priorityChildConfig1.childConfig)
.getPolicyName()) .getPolicyName())
.isEqualTo(CLUSTER_IMPL_POLICY_NAME); .isEqualTo(CLUSTER_IMPL_POLICY_NAME);
ClusterImplConfig clusterImplConfig1 = (ClusterImplConfig) ClusterImplConfig clusterImplConfig1 = (ClusterImplConfig)
GracefulSwitchLoadBalancerAccessor.getChildConfig(priorityChildConfig1.childConfig); GracefulSwitchLoadBalancerAccessor.getChildConfig(priorityChildConfig1.childConfig);
@ -548,7 +536,7 @@ public class ClusterResolverLoadBalancerTest {
PriorityChildConfig priorityChildConfig2 = priorityLbConfig.childConfigs.get(priority2); PriorityChildConfig priorityChildConfig2 = priorityLbConfig.childConfigs.get(priority2);
assertThat(priorityChildConfig2.ignoreReresolution).isTrue(); assertThat(priorityChildConfig2.ignoreReresolution).isTrue();
assertThat(GracefulSwitchLoadBalancerAccessor.getChildProvider(priorityChildConfig2.childConfig) assertThat(GracefulSwitchLoadBalancerAccessor.getChildProvider(priorityChildConfig2.childConfig)
.getPolicyName()) .getPolicyName())
.isEqualTo(CLUSTER_IMPL_POLICY_NAME); .isEqualTo(CLUSTER_IMPL_POLICY_NAME);
ClusterImplConfig clusterImplConfig2 = (ClusterImplConfig) ClusterImplConfig clusterImplConfig2 = (ClusterImplConfig)
GracefulSwitchLoadBalancerAccessor.getChildConfig(priorityChildConfig2.childConfig); GracefulSwitchLoadBalancerAccessor.getChildConfig(priorityChildConfig2.childConfig);
@ -560,15 +548,6 @@ public class ClusterResolverLoadBalancerTest {
GracefulSwitchLoadBalancerAccessor.getChildProvider(wrrLocalityConfig2.childConfig); GracefulSwitchLoadBalancerAccessor.getChildProvider(wrrLocalityConfig2.childConfig);
assertThat(childProvider2.getPolicyName()).isEqualTo("round_robin"); assertThat(childProvider2.getPolicyName()).isEqualTo("round_robin");
PriorityChildConfig priorityChildConfig3 = priorityLbConfig.childConfigs.get(priority3);
assertThat(priorityChildConfig3.ignoreReresolution).isTrue();
assertThat(GracefulSwitchLoadBalancerAccessor.getChildProvider(priorityChildConfig3.childConfig)
.getPolicyName())
.isEqualTo(CLUSTER_IMPL_POLICY_NAME);
ClusterImplConfig clusterImplConfig3 = (ClusterImplConfig)
GracefulSwitchLoadBalancerAccessor.getChildConfig(priorityChildConfig3.childConfig);
assertClusterImplConfig(clusterImplConfig3, CLUSTER1, EDS_SERVICE_NAME1, LRS_SERVER_INFO, 100L,
tlsContext, Collections.<DropOverload>emptyList(), WRR_LOCALITY_POLICY_NAME);
WrrLocalityConfig wrrLocalityConfig3 = (WrrLocalityConfig) WrrLocalityConfig wrrLocalityConfig3 = (WrrLocalityConfig)
GracefulSwitchLoadBalancerAccessor.getChildConfig(clusterImplConfig1.childConfig); GracefulSwitchLoadBalancerAccessor.getChildConfig(clusterImplConfig1.childConfig);
LoadBalancerProvider childProvider3 = LoadBalancerProvider childProvider3 =
@ -595,7 +574,7 @@ public class ClusterResolverLoadBalancerTest {
private void verifyEdsPriorityNames(List<String> want, private void verifyEdsPriorityNames(List<String> want,
Map<Locality, LocalityLbEndpoints>... updates) { Map<Locality, LocalityLbEndpoints>... updates) {
ClusterResolverConfig config = new ClusterResolverConfig( ClusterResolverConfig config = new ClusterResolverConfig(
Arrays.asList(edsDiscoveryMechanism2), roundRobin, false); edsDiscoveryMechanism2, roundRobin, false);
deliverLbConfig(config); deliverLbConfig(config);
assertThat(xdsClient.watchers.keySet()).containsExactly(EDS_SERVICE_NAME2); assertThat(xdsClient.watchers.keySet()).containsExactly(EDS_SERVICE_NAME2);
assertThat(childBalancers).isEmpty(); assertThat(childBalancers).isEmpty();
@ -617,7 +596,7 @@ public class ClusterResolverLoadBalancerTest {
public void edsUpdatePriorityName_twoPriorities() { public void edsUpdatePriorityName_twoPriorities() {
verifyEdsPriorityNames(Arrays.asList(CLUSTER2 + "[child1]", CLUSTER2 + "[child2]"), verifyEdsPriorityNames(Arrays.asList(CLUSTER2 + "[child1]", CLUSTER2 + "[child2]"),
ImmutableMap.of(locality1, createEndpoints(1), ImmutableMap.of(locality1, createEndpoints(1),
locality2, createEndpoints(2) locality2, createEndpoints(2)
)); ));
} }
@ -653,8 +632,8 @@ public class ClusterResolverLoadBalancerTest {
locality3, createEndpoints(3), locality3, createEndpoints(3),
locality2, createEndpoints(2)), locality2, createEndpoints(2)),
ImmutableMap.of(locality1, createEndpoints(2), ImmutableMap.of(locality1, createEndpoints(2),
locality3, createEndpoints(1), locality3, createEndpoints(1),
locality2, createEndpoints(1) locality2, createEndpoints(1)
)); ));
} }
@ -662,76 +641,64 @@ public class ClusterResolverLoadBalancerTest {
return LocalityLbEndpoints.create( return LocalityLbEndpoints.create(
Arrays.asList( Arrays.asList(
LbEndpoint.create(makeAddress("endpoint-addr-1"), 100, LbEndpoint.create(makeAddress("endpoint-addr-1"), 100,
true, "hostname1", ImmutableMap.of()), true, "hostname1", ImmutableMap.of()),
LbEndpoint.create(makeAddress("endpoint-addr-2"), 100, LbEndpoint.create(makeAddress("endpoint-addr-2"), 100,
true, "hostname2", ImmutableMap.of())), true, "hostname2", ImmutableMap.of())),
70 /* localityWeight */, priority /* priority */, ImmutableMap.of()); 70 /* localityWeight */, priority /* priority */, ImmutableMap.of());
} }
@Test @Test
public void onlyEdsClusters_resourceNeverExist_returnErrorPicker() { public void onlyEdsClusters_resourceNeverExist_returnErrorPicker() {
ClusterResolverConfig config = new ClusterResolverConfig( ClusterResolverConfig config = new ClusterResolverConfig(
Arrays.asList(edsDiscoveryMechanism1, edsDiscoveryMechanism2), roundRobin, false); edsDiscoveryMechanism1, roundRobin, false);
deliverLbConfig(config); deliverLbConfig(config);
assertThat(xdsClient.watchers.keySet()).containsExactly(EDS_SERVICE_NAME1, EDS_SERVICE_NAME2); assertThat(xdsClient.watchers.keySet()).containsExactly(EDS_SERVICE_NAME1);
assertThat(childBalancers).isEmpty(); assertThat(childBalancers).isEmpty();
reset(helper); reset(helper);
xdsClient.deliverResourceNotFound(EDS_SERVICE_NAME1); xdsClient.deliverResourceNotFound(EDS_SERVICE_NAME1);
verify(helper, never()).updateBalancingState(
any(ConnectivityState.class), any(SubchannelPicker.class)); // wait for CLUSTER2's results
xdsClient.deliverResourceNotFound(EDS_SERVICE_NAME2);
verify(helper).updateBalancingState( verify(helper).updateBalancingState(
eq(ConnectivityState.TRANSIENT_FAILURE), pickerCaptor.capture()); eq(ConnectivityState.TRANSIENT_FAILURE), pickerCaptor.capture());
assertPicker( assertPicker(
pickerCaptor.getValue(), pickerCaptor.getValue(),
Status.UNAVAILABLE.withDescription( Status.UNAVAILABLE.withDescription(
"No usable endpoint from cluster(s): " + Arrays.asList(CLUSTER1, CLUSTER2)), "No usable endpoint from cluster: " + CLUSTER1),
null); null);
} }
@Test @Test
public void onlyEdsClusters_allResourcesRevoked_shutDownChildLbPolicy() { public void edsCluster_resourcesRevoked_shutDownChildLbPolicy() {
ClusterResolverConfig config = new ClusterResolverConfig( ClusterResolverConfig config = new ClusterResolverConfig(
Arrays.asList(edsDiscoveryMechanism1, edsDiscoveryMechanism2), roundRobin, false); edsDiscoveryMechanism1, roundRobin, false);
deliverLbConfig(config); deliverLbConfig(config);
assertThat(xdsClient.watchers.keySet()).containsExactly(EDS_SERVICE_NAME1, EDS_SERVICE_NAME2); assertThat(xdsClient.watchers.keySet()).containsExactly(EDS_SERVICE_NAME1);
assertThat(childBalancers).isEmpty(); assertThat(childBalancers).isEmpty();
reset(helper); reset(helper);
EquivalentAddressGroup endpoint1 = makeAddress("endpoint-addr-1"); EquivalentAddressGroup endpoint1 = makeAddress("endpoint-addr-1");
EquivalentAddressGroup endpoint2 = makeAddress("endpoint-addr-2");
LocalityLbEndpoints localityLbEndpoints1 = LocalityLbEndpoints localityLbEndpoints1 =
LocalityLbEndpoints.create( LocalityLbEndpoints.create(
Collections.singletonList(LbEndpoint.create(endpoint1, 100, true, Collections.singletonList(LbEndpoint.create(endpoint1, 100, true,
"hostname1", ImmutableMap.of())), "hostname1", ImmutableMap.of())),
10 /* localityWeight */, 1 /* priority */, ImmutableMap.of()); 10 /* localityWeight */, 1 /* priority */, ImmutableMap.of());
LocalityLbEndpoints localityLbEndpoints2 =
LocalityLbEndpoints.create(
Collections.singletonList(LbEndpoint.create(endpoint2, 100, true,
"hostname2", ImmutableMap.of())),
20 /* localityWeight */, 2 /* priority */, ImmutableMap.of());
xdsClient.deliverClusterLoadAssignment( xdsClient.deliverClusterLoadAssignment(
EDS_SERVICE_NAME1, Collections.singletonMap(locality1, localityLbEndpoints1)); EDS_SERVICE_NAME1, Collections.singletonMap(locality1, localityLbEndpoints1));
xdsClient.deliverClusterLoadAssignment(
EDS_SERVICE_NAME2, Collections.singletonMap(locality2, localityLbEndpoints2));
assertThat(childBalancers).hasSize(1); // child LB policy created assertThat(childBalancers).hasSize(1); // child LB policy created
FakeLoadBalancer childBalancer = Iterables.getOnlyElement(childBalancers); FakeLoadBalancer childBalancer = Iterables.getOnlyElement(childBalancers);
assertThat(((PriorityLbConfig) childBalancer.config).priorities).hasSize(2); assertThat(((PriorityLbConfig) childBalancer.config).priorities).hasSize(1);
assertAddressesEqual(Arrays.asList(endpoint1, endpoint2), childBalancer.addresses); assertAddressesEqual(Arrays.asList(endpoint1), childBalancer.addresses);
xdsClient.deliverResourceNotFound(EDS_SERVICE_NAME2);
xdsClient.deliverResourceNotFound(EDS_SERVICE_NAME1); xdsClient.deliverResourceNotFound(EDS_SERVICE_NAME1);
verify(helper).updateBalancingState( verify(helper).updateBalancingState(
eq(ConnectivityState.TRANSIENT_FAILURE), pickerCaptor.capture()); eq(ConnectivityState.TRANSIENT_FAILURE), pickerCaptor.capture());
Status expectedError = Status.UNAVAILABLE.withDescription( Status expectedError = Status.UNAVAILABLE.withDescription(
"No usable endpoint from cluster(s): " + Arrays.asList(CLUSTER1, CLUSTER2)); "No usable endpoint from cluster: " + CLUSTER1);
assertPicker(pickerCaptor.getValue(), expectedError, null); assertPicker(pickerCaptor.getValue(), expectedError, null);
} }
@Test @Test
public void handleEdsResource_ignoreUnhealthyEndpoints() { public void handleEdsResource_ignoreUnhealthyEndpoints() {
ClusterResolverConfig config = new ClusterResolverConfig( ClusterResolverConfig config = new ClusterResolverConfig(
Collections.singletonList(edsDiscoveryMechanism1), roundRobin, false); edsDiscoveryMechanism1, roundRobin, false);
deliverLbConfig(config); deliverLbConfig(config);
EquivalentAddressGroup endpoint1 = makeAddress("endpoint-addr-1"); EquivalentAddressGroup endpoint1 = makeAddress("endpoint-addr-1");
EquivalentAddressGroup endpoint2 = makeAddress("endpoint-addr-2"); EquivalentAddressGroup endpoint2 = makeAddress("endpoint-addr-2");
@ -739,9 +706,9 @@ public class ClusterResolverLoadBalancerTest {
LocalityLbEndpoints.create( LocalityLbEndpoints.create(
Arrays.asList( Arrays.asList(
LbEndpoint.create(endpoint1, 100, false /* isHealthy */, LbEndpoint.create(endpoint1, 100, false /* isHealthy */,
"hostname1", ImmutableMap.of()), "hostname1", ImmutableMap.of()),
LbEndpoint.create(endpoint2, 100, true /* isHealthy */, LbEndpoint.create(endpoint2, 100, true /* isHealthy */,
"hostname2", ImmutableMap.of())), "hostname2", ImmutableMap.of())),
10 /* localityWeight */, 1 /* priority */, ImmutableMap.of()); 10 /* localityWeight */, 1 /* priority */, ImmutableMap.of());
xdsClient.deliverClusterLoadAssignment( xdsClient.deliverClusterLoadAssignment(
EDS_SERVICE_NAME1, Collections.singletonMap(locality1, localityLbEndpoints)); EDS_SERVICE_NAME1, Collections.singletonMap(locality1, localityLbEndpoints));
@ -753,19 +720,19 @@ public class ClusterResolverLoadBalancerTest {
@Test @Test
public void handleEdsResource_ignoreLocalitiesWithNoHealthyEndpoints() { public void handleEdsResource_ignoreLocalitiesWithNoHealthyEndpoints() {
ClusterResolverConfig config = new ClusterResolverConfig( ClusterResolverConfig config = new ClusterResolverConfig(
Collections.singletonList(edsDiscoveryMechanism1), roundRobin, false); edsDiscoveryMechanism1, roundRobin, false);
deliverLbConfig(config); deliverLbConfig(config);
EquivalentAddressGroup endpoint1 = makeAddress("endpoint-addr-1"); EquivalentAddressGroup endpoint1 = makeAddress("endpoint-addr-1");
EquivalentAddressGroup endpoint2 = makeAddress("endpoint-addr-2"); EquivalentAddressGroup endpoint2 = makeAddress("endpoint-addr-2");
LocalityLbEndpoints localityLbEndpoints1 = LocalityLbEndpoints localityLbEndpoints1 =
LocalityLbEndpoints.create( LocalityLbEndpoints.create(
Collections.singletonList(LbEndpoint.create(endpoint1, 100, false /* isHealthy */, Collections.singletonList(LbEndpoint.create(endpoint1, 100, false /* isHealthy */,
"hostname1", ImmutableMap.of())), "hostname1", ImmutableMap.of())),
10 /* localityWeight */, 1 /* priority */, ImmutableMap.of()); 10 /* localityWeight */, 1 /* priority */, ImmutableMap.of());
LocalityLbEndpoints localityLbEndpoints2 = LocalityLbEndpoints localityLbEndpoints2 =
LocalityLbEndpoints.create( LocalityLbEndpoints.create(
Collections.singletonList(LbEndpoint.create(endpoint2, 100, true /* isHealthy */, Collections.singletonList(LbEndpoint.create(endpoint2, 100, true /* isHealthy */,
"hostname2", ImmutableMap.of())), "hostname2", ImmutableMap.of())),
10 /* localityWeight */, 1 /* priority */, ImmutableMap.of()); 10 /* localityWeight */, 1 /* priority */, ImmutableMap.of());
xdsClient.deliverClusterLoadAssignment( xdsClient.deliverClusterLoadAssignment(
EDS_SERVICE_NAME1, EDS_SERVICE_NAME1,
@ -780,20 +747,20 @@ public class ClusterResolverLoadBalancerTest {
@Test @Test
public void handleEdsResource_ignorePrioritiesWithNoHealthyEndpoints() { public void handleEdsResource_ignorePrioritiesWithNoHealthyEndpoints() {
ClusterResolverConfig config = new ClusterResolverConfig( ClusterResolverConfig config = new ClusterResolverConfig(
Collections.singletonList(edsDiscoveryMechanism1), roundRobin, false); edsDiscoveryMechanism1, roundRobin, false);
deliverLbConfig(config); deliverLbConfig(config);
EquivalentAddressGroup endpoint1 = makeAddress("endpoint-addr-1"); EquivalentAddressGroup endpoint1 = makeAddress("endpoint-addr-1");
EquivalentAddressGroup endpoint2 = makeAddress("endpoint-addr-2"); EquivalentAddressGroup endpoint2 = makeAddress("endpoint-addr-2");
LocalityLbEndpoints localityLbEndpoints1 = LocalityLbEndpoints localityLbEndpoints1 =
LocalityLbEndpoints.create( LocalityLbEndpoints.create(
Collections.singletonList(LbEndpoint.create(endpoint1, 100, false /* isHealthy */, Collections.singletonList(LbEndpoint.create(endpoint1, 100, false /* isHealthy */,
"hostname1", ImmutableMap.of())), "hostname1", ImmutableMap.of())),
10 /* localityWeight */, 1 /* priority */, ImmutableMap.of()); 10 /* localityWeight */, 1 /* priority */, ImmutableMap.of());
LocalityLbEndpoints localityLbEndpoints2 = LocalityLbEndpoints localityLbEndpoints2 =
LocalityLbEndpoints.create( LocalityLbEndpoints.create(
Collections.singletonList(LbEndpoint.create(endpoint2, 200, true /* isHealthy */, Collections.singletonList(LbEndpoint.create(endpoint2, 200, true /* isHealthy */,
"hostname2", ImmutableMap.of())), "hostname2", ImmutableMap.of())),
10 /* localityWeight */, 2 /* priority */, ImmutableMap.of()); 10 /* localityWeight */, 2 /* priority */, ImmutableMap.of());
String priority2 = CLUSTER1 + "[child2]"; String priority2 = CLUSTER1 + "[child2]";
xdsClient.deliverClusterLoadAssignment( xdsClient.deliverClusterLoadAssignment(
EDS_SERVICE_NAME1, EDS_SERVICE_NAME1,
@ -806,7 +773,7 @@ public class ClusterResolverLoadBalancerTest {
@Test @Test
public void handleEdsResource_noHealthyEndpoint() { public void handleEdsResource_noHealthyEndpoint() {
ClusterResolverConfig config = new ClusterResolverConfig( ClusterResolverConfig config = new ClusterResolverConfig(
Collections.singletonList(edsDiscoveryMechanism1), roundRobin, false); edsDiscoveryMechanism1, roundRobin, false);
deliverLbConfig(config); deliverLbConfig(config);
EquivalentAddressGroup endpoint = makeAddress("endpoint-addr-1"); EquivalentAddressGroup endpoint = makeAddress("endpoint-addr-1");
LocalityLbEndpoints localityLbEndpoints = LocalityLbEndpoints localityLbEndpoints =
@ -823,7 +790,7 @@ public class ClusterResolverLoadBalancerTest {
assertPicker( assertPicker(
pickerCaptor.getValue(), pickerCaptor.getValue(),
Status.UNAVAILABLE.withDescription( Status.UNAVAILABLE.withDescription(
"No usable endpoint from cluster(s): " + Collections.singleton(CLUSTER1)), "No usable endpoint from cluster: " + CLUSTER1),
null); null);
} }
@ -841,7 +808,7 @@ public class ClusterResolverLoadBalancerTest {
void do_onlyLogicalDnsCluster_endpointsResolved() { void do_onlyLogicalDnsCluster_endpointsResolved() {
ClusterResolverConfig config = new ClusterResolverConfig( ClusterResolverConfig config = new ClusterResolverConfig(
Collections.singletonList(logicalDnsDiscoveryMechanism), roundRobin, false); logicalDnsDiscoveryMechanism, roundRobin, false);
deliverLbConfig(config); deliverLbConfig(config);
FakeNameResolver resolver = assertResolverCreated("/" + DNS_HOST_NAME); FakeNameResolver resolver = assertResolverCreated("/" + DNS_HOST_NAME);
assertThat(childBalancers).isEmpty(); assertThat(childBalancers).isEmpty();
@ -857,7 +824,7 @@ public class ClusterResolverLoadBalancerTest {
PriorityChildConfig priorityChildConfig = priorityLbConfig.childConfigs.get(priority); PriorityChildConfig priorityChildConfig = priorityLbConfig.childConfigs.get(priority);
assertThat(priorityChildConfig.ignoreReresolution).isFalse(); assertThat(priorityChildConfig.ignoreReresolution).isFalse();
assertThat(GracefulSwitchLoadBalancerAccessor.getChildProvider(priorityChildConfig.childConfig) assertThat(GracefulSwitchLoadBalancerAccessor.getChildProvider(priorityChildConfig.childConfig)
.getPolicyName()) .getPolicyName())
.isEqualTo(CLUSTER_IMPL_POLICY_NAME); .isEqualTo(CLUSTER_IMPL_POLICY_NAME);
ClusterImplConfig clusterImplConfig = (ClusterImplConfig) ClusterImplConfig clusterImplConfig = (ClusterImplConfig)
GracefulSwitchLoadBalancerAccessor.getChildConfig(priorityChildConfig.childConfig); GracefulSwitchLoadBalancerAccessor.getChildConfig(priorityChildConfig.childConfig);
@ -873,7 +840,7 @@ public class ClusterResolverLoadBalancerTest {
@Test @Test
public void onlyLogicalDnsCluster_handleRefreshNameResolution() { public void onlyLogicalDnsCluster_handleRefreshNameResolution() {
ClusterResolverConfig config = new ClusterResolverConfig( ClusterResolverConfig config = new ClusterResolverConfig(
Collections.singletonList(logicalDnsDiscoveryMechanism), roundRobin, false); logicalDnsDiscoveryMechanism, roundRobin, false);
deliverLbConfig(config); deliverLbConfig(config);
FakeNameResolver resolver = assertResolverCreated("/" + DNS_HOST_NAME); FakeNameResolver resolver = assertResolverCreated("/" + DNS_HOST_NAME);
assertThat(childBalancers).isEmpty(); assertThat(childBalancers).isEmpty();
@ -900,9 +867,9 @@ public class ClusterResolverLoadBalancerTest {
void do_onlyLogicalDnsCluster_resolutionError_backoffAndRefresh() { void do_onlyLogicalDnsCluster_resolutionError_backoffAndRefresh() {
InOrder inOrder = Mockito.inOrder(helper, backoffPolicyProvider, InOrder inOrder = Mockito.inOrder(helper, backoffPolicyProvider,
backoffPolicy1, backoffPolicy2); backoffPolicy1, backoffPolicy2);
ClusterResolverConfig config = new ClusterResolverConfig( ClusterResolverConfig config = new ClusterResolverConfig(
Collections.singletonList(logicalDnsDiscoveryMechanism), roundRobin, false); logicalDnsDiscoveryMechanism, roundRobin, false);
deliverLbConfig(config); deliverLbConfig(config);
FakeNameResolver resolver = assertResolverCreated("/" + DNS_HOST_NAME); FakeNameResolver resolver = assertResolverCreated("/" + DNS_HOST_NAME);
assertThat(childBalancers).isEmpty(); assertThat(childBalancers).isEmpty();
@ -948,7 +915,7 @@ public class ClusterResolverLoadBalancerTest {
public void onlyLogicalDnsCluster_refreshNameResolutionRaceWithResolutionError() { public void onlyLogicalDnsCluster_refreshNameResolutionRaceWithResolutionError() {
InOrder inOrder = Mockito.inOrder(backoffPolicyProvider, backoffPolicy1, backoffPolicy2); InOrder inOrder = Mockito.inOrder(backoffPolicyProvider, backoffPolicy1, backoffPolicy2);
ClusterResolverConfig config = new ClusterResolverConfig( ClusterResolverConfig config = new ClusterResolverConfig(
Collections.singletonList(logicalDnsDiscoveryMechanism), roundRobin, false); logicalDnsDiscoveryMechanism, roundRobin, false);
deliverLbConfig(config); deliverLbConfig(config);
FakeNameResolver resolver = assertResolverCreated("/" + DNS_HOST_NAME); FakeNameResolver resolver = assertResolverCreated("/" + DNS_HOST_NAME);
assertThat(childBalancers).isEmpty(); assertThat(childBalancers).isEmpty();
@ -984,119 +951,22 @@ public class ClusterResolverLoadBalancerTest {
} }
@Test @Test
public void edsClustersAndLogicalDnsCluster_receivedEndpoints() { public void resolutionErrorAfterChildLbCreated_propagateError() {
ClusterResolverConfig config = new ClusterResolverConfig( ClusterResolverConfig config = new ClusterResolverConfig(
Arrays.asList(edsDiscoveryMechanism1, logicalDnsDiscoveryMechanism), roundRobin, false); edsDiscoveryMechanism1, roundRobin, false);
deliverLbConfig(config); deliverLbConfig(config);
assertThat(xdsClient.watchers.keySet()).containsExactly(EDS_SERVICE_NAME1); assertThat(xdsClient.watchers.keySet()).containsExactly(EDS_SERVICE_NAME1);
FakeNameResolver resolver = assertResolverCreated("/" + DNS_HOST_NAME);
assertThat(childBalancers).isEmpty();
EquivalentAddressGroup endpoint1 = makeAddress("endpoint-addr-1"); // DNS endpoint
EquivalentAddressGroup endpoint2 = makeAddress("endpoint-addr-2"); // DNS endpoint
EquivalentAddressGroup endpoint3 = makeAddress("endpoint-addr-3"); // EDS endpoint
resolver.deliverEndpointAddresses(Arrays.asList(endpoint1, endpoint2));
LocalityLbEndpoints localityLbEndpoints =
LocalityLbEndpoints.create(
Collections.singletonList(LbEndpoint.create(endpoint3, 100, true,
"hostname3", ImmutableMap.of())),
10 /* localityWeight */, 1 /* priority */, ImmutableMap.of());
xdsClient.deliverClusterLoadAssignment(
EDS_SERVICE_NAME1, Collections.singletonMap(locality1, localityLbEndpoints));
assertThat(childBalancers).hasSize(1);
FakeLoadBalancer childBalancer = Iterables.getOnlyElement(childBalancers);
assertThat(((PriorityLbConfig) childBalancer.config).priorities)
.containsExactly(CLUSTER1 + "[child1]", CLUSTER_DNS + "[child0]").inOrder();
assertAddressesEqual(Arrays.asList(endpoint3, endpoint1, endpoint2),
childBalancer.addresses); // ordered by cluster then addresses
assertAddressesEqual(AddressFilter.filter(AddressFilter.filter(
childBalancer.addresses, CLUSTER1 + "[child1]"),
"{region=\"test-region-1\", zone=\"test-zone-1\", sub_zone=\"test-subzone-1\"}"),
Collections.singletonList(endpoint3));
assertAddressesEqual(AddressFilter.filter(AddressFilter.filter(
childBalancer.addresses, CLUSTER_DNS + "[child0]"),
"{region=\"\", zone=\"\", sub_zone=\"\"}"),
Arrays.asList(endpoint1, endpoint2));
}
@Test
public void noEdsResourceExists_useDnsResolutionResults() {
ClusterResolverConfig config = new ClusterResolverConfig(
Arrays.asList(edsDiscoveryMechanism1, logicalDnsDiscoveryMechanism), roundRobin, false);
deliverLbConfig(config);
assertThat(xdsClient.watchers.keySet()).containsExactly(EDS_SERVICE_NAME1);
FakeNameResolver resolver = assertResolverCreated("/" + DNS_HOST_NAME);
assertThat(childBalancers).isEmpty();
reset(helper);
xdsClient.deliverResourceNotFound(EDS_SERVICE_NAME1);
verify(helper, never()).updateBalancingState(
any(ConnectivityState.class), any(SubchannelPicker.class)); // wait for DNS results
EquivalentAddressGroup endpoint1 = makeAddress("endpoint-addr-1");
EquivalentAddressGroup endpoint2 = makeAddress("endpoint-addr-2");
resolver.deliverEndpointAddresses(Arrays.asList(endpoint1, endpoint2));
assertThat(childBalancers).hasSize(1);
FakeLoadBalancer childBalancer = Iterables.getOnlyElement(childBalancers);
String priority = Iterables.getOnlyElement(
((PriorityLbConfig) childBalancer.config).priorities);
assertThat(priority).isEqualTo(CLUSTER_DNS + "[child0]");
assertAddressesEqual(Arrays.asList(endpoint1, endpoint2), childBalancer.addresses);
}
@Test
public void edsResourceRevoked_dnsResolutionError_shutDownChildLbPolicyAndReturnErrorPicker() {
ClusterResolverConfig config = new ClusterResolverConfig(
Arrays.asList(edsDiscoveryMechanism1, logicalDnsDiscoveryMechanism), roundRobin, false);
deliverLbConfig(config);
assertThat(xdsClient.watchers.keySet()).containsExactly(EDS_SERVICE_NAME1);
FakeNameResolver resolver = assertResolverCreated("/" + DNS_HOST_NAME);
assertThat(childBalancers).isEmpty(); assertThat(childBalancers).isEmpty();
reset(helper); reset(helper);
EquivalentAddressGroup endpoint = makeAddress("endpoint-addr-1"); EquivalentAddressGroup endpoint = makeAddress("endpoint-addr-1");
LocalityLbEndpoints localityLbEndpoints = LocalityLbEndpoints localityLbEndpoints =
LocalityLbEndpoints.create( LocalityLbEndpoints.create(
Collections.singletonList(LbEndpoint.create(endpoint, 100, true, Collections.singletonList(LbEndpoint.create(endpoint, 100, true,
"hostname1", ImmutableMap.of())), "hostname1", ImmutableMap.of())),
10 /* localityWeight */, 1 /* priority */, ImmutableMap.of()); 10 /* localityWeight */, 1 /* priority */, ImmutableMap.of());
xdsClient.deliverClusterLoadAssignment( xdsClient.deliverClusterLoadAssignment(
EDS_SERVICE_NAME1, Collections.singletonMap(locality1, localityLbEndpoints)); EDS_SERVICE_NAME1, Collections.singletonMap(locality1, localityLbEndpoints));
resolver.deliverError(Status.UNKNOWN.withDescription("I am lost"));
assertThat(childBalancers).hasSize(1);
FakeLoadBalancer childBalancer = Iterables.getOnlyElement(childBalancers);
assertThat(((PriorityLbConfig) childBalancer.config).priorities)
.containsExactly(CLUSTER1 + "[child1]");
assertAddressesEqual(Collections.singletonList(endpoint), childBalancer.addresses);
assertThat(childBalancer.shutdown).isFalse();
xdsClient.deliverResourceNotFound(EDS_SERVICE_NAME1);
assertThat(childBalancer.shutdown).isTrue();
verify(helper).updateBalancingState(
eq(ConnectivityState.TRANSIENT_FAILURE), pickerCaptor.capture());
assertPicker(pickerCaptor.getValue(),
Status.UNAVAILABLE.withDescription("I am lost"), null);
}
@Test
public void resolutionErrorAfterChildLbCreated_propagateErrorIfAllClustersEncounterError() {
ClusterResolverConfig config = new ClusterResolverConfig(
Arrays.asList(edsDiscoveryMechanism1, logicalDnsDiscoveryMechanism), roundRobin, false);
deliverLbConfig(config);
assertThat(xdsClient.watchers.keySet()).containsExactly(EDS_SERVICE_NAME1);
FakeNameResolver resolver = assertResolverCreated("/" + DNS_HOST_NAME);
assertThat(childBalancers).isEmpty();
reset(helper);
EquivalentAddressGroup endpoint = makeAddress("endpoint-addr-1");
LocalityLbEndpoints localityLbEndpoints =
LocalityLbEndpoints.create(
Collections.singletonList(LbEndpoint.create(endpoint, 100, true,
"hostname1", ImmutableMap.of())),
10 /* localityWeight */, 1 /* priority */, ImmutableMap.of());
xdsClient.deliverClusterLoadAssignment(
EDS_SERVICE_NAME1, Collections.singletonMap(locality1, localityLbEndpoints));
assertThat(childBalancers).isEmpty(); // not created until all clusters resolved.
resolver.deliverError(Status.UNKNOWN.withDescription("I am lost"));
// DNS resolution failed, but there are EDS endpoints can be used.
assertThat(childBalancers).hasSize(1); assertThat(childBalancers).hasSize(1);
FakeLoadBalancer childBalancer = Iterables.getOnlyElement(childBalancers); // child LB created FakeLoadBalancer childBalancer = Iterables.getOnlyElement(childBalancers); // child LB created
assertThat(childBalancer.upstreamError).isNull(); // should not propagate error to child LB assertThat(childBalancer.upstreamError).isNull(); // should not propagate error to child LB
@ -1104,40 +974,19 @@ public class ClusterResolverLoadBalancerTest {
xdsClient.deliverError(Status.RESOURCE_EXHAUSTED.withDescription("out of memory")); xdsClient.deliverError(Status.RESOURCE_EXHAUSTED.withDescription("out of memory"));
assertThat(childBalancer.upstreamError).isNotNull(); // last cluster's (DNS) error propagated assertThat(childBalancer.upstreamError).isNotNull(); // last cluster's (DNS) error propagated
assertThat(childBalancer.upstreamError.getCode()).isEqualTo(Code.UNKNOWN); assertThat(childBalancer.upstreamError.getCode()).isEqualTo(Code.UNAVAILABLE);
assertThat(childBalancer.upstreamError.getDescription()).isEqualTo("I am lost"); assertThat(childBalancer.upstreamError.getDescription())
.isEqualTo("Unable to load EDS backend-service-foo.googleapis.com. xDS server returned: "
+ "RESOURCE_EXHAUSTED: out of memory");
assertThat(childBalancer.shutdown).isFalse(); assertThat(childBalancer.shutdown).isFalse();
verify(helper, never()).updateBalancingState( verify(helper, never()).updateBalancingState(
eq(ConnectivityState.TRANSIENT_FAILURE), any(SubchannelPicker.class)); eq(ConnectivityState.TRANSIENT_FAILURE), any(SubchannelPicker.class));
} }
@Test @Test
public void resolutionErrorBeforeChildLbCreated_returnErrorPickerIfAllClustersEncounterError() { public void resolutionErrorBeforeChildLbCreated_returnErrorPicker() {
ClusterResolverConfig config = new ClusterResolverConfig( ClusterResolverConfig config = new ClusterResolverConfig(
Arrays.asList(edsDiscoveryMechanism1, logicalDnsDiscoveryMechanism), roundRobin, false); edsDiscoveryMechanism1, roundRobin, false);
deliverLbConfig(config);
assertThat(xdsClient.watchers.keySet()).containsExactly(EDS_SERVICE_NAME1);
FakeNameResolver resolver = assertResolverCreated("/" + DNS_HOST_NAME);
assertThat(childBalancers).isEmpty();
reset(helper);
xdsClient.deliverError(Status.UNIMPLEMENTED.withDescription("not found"));
assertThat(childBalancers).isEmpty();
verify(helper, never()).updateBalancingState(
eq(ConnectivityState.TRANSIENT_FAILURE), any(SubchannelPicker.class)); // wait for DNS
Status dnsError = Status.UNKNOWN.withDescription("I am lost");
resolver.deliverError(dnsError);
verify(helper).updateBalancingState(
eq(ConnectivityState.TRANSIENT_FAILURE), pickerCaptor.capture());
assertPicker(
pickerCaptor.getValue(),
Status.UNAVAILABLE.withDescription(dnsError.getDescription()),
null);
}
@Test
public void resolutionErrorBeforeChildLbCreated_edsOnly_returnErrorPicker() {
ClusterResolverConfig config = new ClusterResolverConfig(
Arrays.asList(edsDiscoveryMechanism1), roundRobin, false);
deliverLbConfig(config); deliverLbConfig(config);
assertThat(xdsClient.watchers.keySet()).containsExactly(EDS_SERVICE_NAME1); assertThat(xdsClient.watchers.keySet()).containsExactly(EDS_SERVICE_NAME1);
assertThat(childBalancers).isEmpty(); assertThat(childBalancers).isEmpty();
@ -1153,11 +1002,25 @@ public class ClusterResolverLoadBalancerTest {
} }
@Test @Test
public void handleNameResolutionErrorFromUpstream_beforeChildLbCreated_returnErrorPicker() { public void handleNameResolutionErrorFromUpstream_eds_beforeChildLbCreated_returnErrorPicker() {
ClusterResolverConfig config = new ClusterResolverConfig( ClusterResolverConfig config = new ClusterResolverConfig(
Arrays.asList(edsDiscoveryMechanism1, logicalDnsDiscoveryMechanism), roundRobin, false); edsDiscoveryMechanism1, roundRobin, false);
deliverLbConfig(config); deliverLbConfig(config);
assertThat(xdsClient.watchers.keySet()).containsExactly(EDS_SERVICE_NAME1); assertThat(xdsClient.watchers.keySet()).containsExactly(EDS_SERVICE_NAME1);
assertThat(childBalancers).isEmpty();
reset(helper);
Status upstreamError = Status.UNAVAILABLE.withDescription("unreachable");
loadBalancer.handleNameResolutionError(upstreamError);
verify(helper).updateBalancingState(
eq(ConnectivityState.TRANSIENT_FAILURE), pickerCaptor.capture());
assertPicker(pickerCaptor.getValue(), upstreamError, null);
}
@Test
public void handleNameResolutionErrorFromUpstream_lDns_beforeChildLbCreated_returnErrorPicker() {
ClusterResolverConfig config = new ClusterResolverConfig(
logicalDnsDiscoveryMechanism, roundRobin, false);
deliverLbConfig(config);
assertResolverCreated("/" + DNS_HOST_NAME); assertResolverCreated("/" + DNS_HOST_NAME);
assertThat(childBalancers).isEmpty(); assertThat(childBalancers).isEmpty();
reset(helper); reset(helper);
@ -1169,16 +1032,14 @@ public class ClusterResolverLoadBalancerTest {
} }
@Test @Test
public void handleNameResolutionErrorFromUpstream_afterChildLbCreated_fallThrough() { public void handleNameResolutionErrorFromUpstream_afterChildLbCreated_eds_fallThrough() {
ClusterResolverConfig config = new ClusterResolverConfig( ClusterResolverConfig config = new ClusterResolverConfig(
Arrays.asList(edsDiscoveryMechanism1, logicalDnsDiscoveryMechanism), roundRobin, false); edsDiscoveryMechanism1, roundRobin, false);
deliverLbConfig(config); deliverLbConfig(config);
assertThat(xdsClient.watchers.keySet()).containsExactly(EDS_SERVICE_NAME1); assertThat(xdsClient.watchers.keySet()).containsExactly(EDS_SERVICE_NAME1);
FakeNameResolver resolver = assertResolverCreated("/" + DNS_HOST_NAME);
assertThat(childBalancers).isEmpty(); assertThat(childBalancers).isEmpty();
reset(helper); reset(helper);
EquivalentAddressGroup endpoint1 = makeAddress("endpoint-addr-1"); EquivalentAddressGroup endpoint1 = makeAddress("endpoint-addr-1");
EquivalentAddressGroup endpoint2 = makeAddress("endpoint-addr-2");
LocalityLbEndpoints localityLbEndpoints = LocalityLbEndpoints localityLbEndpoints =
LocalityLbEndpoints.create( LocalityLbEndpoints.create(
Collections.singletonList(LbEndpoint.create(endpoint1, 100, true, Collections.singletonList(LbEndpoint.create(endpoint1, 100, true,
@ -1186,12 +1047,34 @@ public class ClusterResolverLoadBalancerTest {
10 /* localityWeight */, 1 /* priority */, ImmutableMap.of()); 10 /* localityWeight */, 1 /* priority */, ImmutableMap.of());
xdsClient.deliverClusterLoadAssignment( xdsClient.deliverClusterLoadAssignment(
EDS_SERVICE_NAME1, Collections.singletonMap(locality1, localityLbEndpoints)); EDS_SERVICE_NAME1, Collections.singletonMap(locality1, localityLbEndpoints));
assertThat(childBalancers).hasSize(1);
FakeLoadBalancer childBalancer = Iterables.getOnlyElement(childBalancers);
assertThat(((PriorityLbConfig) childBalancer.config).priorities)
.containsExactly(CLUSTER1 + "[child1]");
assertAddressesEqual(Arrays.asList(endpoint1), childBalancer.addresses);
loadBalancer.handleNameResolutionError(Status.UNAVAILABLE.withDescription("unreachable"));
assertThat(childBalancer.upstreamError.getCode()).isEqualTo(Code.UNAVAILABLE);
assertThat(childBalancer.upstreamError.getDescription()).isEqualTo("unreachable");
verify(helper, never()).updateBalancingState(
any(ConnectivityState.class), any(SubchannelPicker.class));
}
@Test
public void handleNameResolutionErrorFromUpstream_afterChildLbCreated_logicalDns_fallThrough() {
ClusterResolverConfig config = new ClusterResolverConfig(
logicalDnsDiscoveryMechanism, roundRobin, false);
deliverLbConfig(config);
FakeNameResolver resolver = assertResolverCreated("/" + DNS_HOST_NAME);
assertThat(childBalancers).isEmpty();
reset(helper);
EquivalentAddressGroup endpoint2 = makeAddress("endpoint-addr-2");
resolver.deliverEndpointAddresses(Collections.singletonList(endpoint2)); resolver.deliverEndpointAddresses(Collections.singletonList(endpoint2));
assertThat(childBalancers).hasSize(1); assertThat(childBalancers).hasSize(1);
FakeLoadBalancer childBalancer = Iterables.getOnlyElement(childBalancers); FakeLoadBalancer childBalancer = Iterables.getOnlyElement(childBalancers);
assertThat(((PriorityLbConfig) childBalancer.config).priorities) assertThat(((PriorityLbConfig) childBalancer.config).priorities)
.containsExactly(CLUSTER1 + "[child1]", CLUSTER_DNS + "[child0]"); .containsExactly(CLUSTER_DNS + "[child0]");
assertAddressesEqual(Arrays.asList(endpoint1, endpoint2), childBalancer.addresses); assertAddressesEqual(Arrays.asList(endpoint2), childBalancer.addresses);
loadBalancer.handleNameResolutionError(Status.UNAVAILABLE.withDescription("unreachable")); loadBalancer.handleNameResolutionError(Status.UNAVAILABLE.withDescription("unreachable"));
assertThat(childBalancer.upstreamError.getCode()).isEqualTo(Code.UNAVAILABLE); assertThat(childBalancer.upstreamError.getCode()).isEqualTo(Code.UNAVAILABLE);
@ -1205,19 +1088,17 @@ public class ClusterResolverLoadBalancerTest {
new EqualsTester() new EqualsTester()
.addEqualityGroup( .addEqualityGroup(
new ClusterResolverConfig( new ClusterResolverConfig(
Collections.singletonList(edsDiscoveryMechanism1), leastRequest, false), edsDiscoveryMechanism1, leastRequest, false),
new ClusterResolverConfig( new ClusterResolverConfig(
Collections.singletonList(edsDiscoveryMechanism1), leastRequest, false)) edsDiscoveryMechanism1, leastRequest, false))
.addEqualityGroup(new ClusterResolverConfig( .addEqualityGroup(new ClusterResolverConfig(
Collections.singletonList(edsDiscoveryMechanism1), roundRobin, false)) edsDiscoveryMechanism1, roundRobin, false))
.addEqualityGroup(new ClusterResolverConfig( .addEqualityGroup(new ClusterResolverConfig(
Collections.singletonList(edsDiscoveryMechanism1), leastRequest, true)) edsDiscoveryMechanism1, leastRequest, true))
.addEqualityGroup(new ClusterResolverConfig( .addEqualityGroup(new ClusterResolverConfig(
Collections.singletonList(edsDiscoveryMechanismWithOutlierDetection), edsDiscoveryMechanismWithOutlierDetection,
leastRequest, leastRequest,
false)) false))
.addEqualityGroup(new ClusterResolverConfig(
Arrays.asList(edsDiscoveryMechanism1, edsDiscoveryMechanism2), leastRequest, false))
.testEquals(); .testEquals();
} }