xds: More nuanced LoadBalancerConfigFactory tests (#9161)

Also some refactoring.
This commit is contained in:
Terry Wilson 2022-05-11 23:01:04 -07:00 committed by GitHub
parent b771403273
commit 1ee93758cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 103 additions and 146 deletions

View File

@ -20,6 +20,9 @@ import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import com.github.xds.type.v3.TypedStruct; import com.github.xds.type.v3.TypedStruct;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.protobuf.Any; import com.google.protobuf.Any;
import com.google.protobuf.Struct; import com.google.protobuf.Struct;
import com.google.protobuf.UInt32Value; import com.google.protobuf.UInt32Value;
@ -45,7 +48,6 @@ import io.grpc.internal.ServiceConfigUtil;
import io.grpc.internal.ServiceConfigUtil.LbConfig; import io.grpc.internal.ServiceConfigUtil.LbConfig;
import io.grpc.xds.ClientXdsClient.ResourceInvalidException; import io.grpc.xds.ClientXdsClient.ResourceInvalidException;
import java.util.List; import java.util.List;
import java.util.Map;
import org.junit.After; import org.junit.After;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@ -65,8 +67,7 @@ public class LoadBalancerConfigFactoryTest {
private static final long RING_HASH_MAX_RING_SIZE = 2; private static final long RING_HASH_MAX_RING_SIZE = 2;
private static final Policy RING_HASH_POLICY = Policy.newBuilder().setTypedExtensionConfig( private static final Policy RING_HASH_POLICY = Policy.newBuilder().setTypedExtensionConfig(
TypedExtensionConfig.newBuilder().setTypedConfig(Any.pack( TypedExtensionConfig.newBuilder().setTypedConfig(Any.pack(
RingHash.newBuilder() RingHash.newBuilder().setMinimumRingSize(UInt64Value.of(RING_HASH_MIN_RING_SIZE))
.setMinimumRingSize(UInt64Value.of(RING_HASH_MIN_RING_SIZE))
.setMaximumRingSize(UInt64Value.of(RING_HASH_MAX_RING_SIZE)) .setMaximumRingSize(UInt64Value.of(RING_HASH_MAX_RING_SIZE))
.setHashFunction(RingHash.HashFunction.XX_HASH).build()))).build(); .setHashFunction(RingHash.HashFunction.XX_HASH).build()))).build();
@ -74,21 +75,27 @@ public class LoadBalancerConfigFactoryTest {
private static final String CUSTOM_POLICY_FIELD_KEY = "choiceCount"; private static final String CUSTOM_POLICY_FIELD_KEY = "choiceCount";
private static final double CUSTOM_POLICY_FIELD_VALUE = 2; private static final double CUSTOM_POLICY_FIELD_VALUE = 2;
private static final Policy CUSTOM_POLICY = Policy.newBuilder().setTypedExtensionConfig( private static final Policy CUSTOM_POLICY = Policy.newBuilder().setTypedExtensionConfig(
TypedExtensionConfig.newBuilder().setTypedConfig(Any.pack(TypedStruct.newBuilder() TypedExtensionConfig.newBuilder().setTypedConfig(Any.pack(
.setTypeUrl("type.googleapis.com/" + CUSTOM_POLICY_NAME).setValue( TypedStruct.newBuilder().setTypeUrl(
Struct.newBuilder() "type.googleapis.com/" + CUSTOM_POLICY_NAME).setValue(
.putFields(CUSTOM_POLICY_FIELD_KEY, Struct.newBuilder().putFields(CUSTOM_POLICY_FIELD_KEY,
Value.newBuilder().setNumberValue(CUSTOM_POLICY_FIELD_VALUE).build())) Value.newBuilder().setNumberValue(CUSTOM_POLICY_FIELD_VALUE).build()))
.build()))).build(); .build()))).build();
private static final FakeCustomLoadBalancerProvider CUSTOM_POLICY_PROVIDER private static final FakeCustomLoadBalancerProvider CUSTOM_POLICY_PROVIDER
= new FakeCustomLoadBalancerProvider(); = new FakeCustomLoadBalancerProvider();
private static Policy buildWrrPolicy(Policy childPolicy) { private static final LbConfig VALID_ROUND_ROBIN_CONFIG = new LbConfig("wrr_locality_experimental",
return Policy.newBuilder().setTypedExtensionConfig(TypedExtensionConfig.newBuilder() ImmutableMap.of("childPolicy",
.setTypedConfig(Any.pack(WrrLocality.newBuilder() ImmutableList.of(ImmutableMap.of("round_robin", ImmutableMap.of()))));
.setEndpointPickingPolicy(LoadBalancingPolicy.newBuilder().addPolicies(childPolicy)) private static final LbConfig VALID_RING_HASH_CONFIG = new LbConfig("ring_hash_experimental",
.build()))).build(); ImmutableMap.of("minRingSize", (double) RING_HASH_MIN_RING_SIZE, "maxRingSize",
} (double) RING_HASH_MAX_RING_SIZE));
private static final LbConfig VALID_CUSTOM_CONFIG = new LbConfig(CUSTOM_POLICY_NAME,
ImmutableMap.of(CUSTOM_POLICY_FIELD_KEY, CUSTOM_POLICY_FIELD_VALUE));
private static final LbConfig VALID_CUSTOM_CONFIG_IN_WRR = new LbConfig(
"wrr_locality_experimental", ImmutableMap.of("childPolicy", ImmutableList.of(
ImmutableMap.of(VALID_CUSTOM_CONFIG.getPolicyName(),
VALID_CUSTOM_CONFIG.getRawConfigValue()))));
@After @After
public void deregisterCustomProvider() { public void deregisterCustomProvider() {
@ -97,32 +104,16 @@ public class LoadBalancerConfigFactoryTest {
@Test @Test
public void roundRobin() throws ResourceInvalidException { public void roundRobin() throws ResourceInvalidException {
Cluster cluster = Cluster.newBuilder() Cluster cluster = newCluster(buildWrrPolicy(ROUND_ROBIN_POLICY));
.setLoadBalancingPolicy(
LoadBalancingPolicy.newBuilder().addPolicies(buildWrrPolicy(ROUND_ROBIN_POLICY)))
.build();
assertValidRoundRobin(ServiceConfigUtil.unwrapLoadBalancingConfig( assertThat(newLbConfig(cluster, true)).isEqualTo(VALID_ROUND_ROBIN_CONFIG);
LoadBalancerConfigFactory.newConfig(cluster, true)));
} }
@Test @Test
public void roundRobin_legacy() throws ResourceInvalidException { public void roundRobin_legacy() throws ResourceInvalidException {
Cluster cluster = Cluster.newBuilder().setLbPolicy(LbPolicy.ROUND_ROBIN).build(); Cluster cluster = Cluster.newBuilder().setLbPolicy(LbPolicy.ROUND_ROBIN).build();
assertValidRoundRobin(ServiceConfigUtil.unwrapLoadBalancingConfig( assertThat(newLbConfig(cluster, true)).isEqualTo(VALID_ROUND_ROBIN_CONFIG);
LoadBalancerConfigFactory.newConfig(cluster, true)));
}
private void assertValidRoundRobin(LbConfig lbConfig) {
assertThat(lbConfig.getPolicyName()).isEqualTo("wrr_locality_experimental");
@SuppressWarnings("unchecked")
List<LbConfig> childConfigs = ServiceConfigUtil.unwrapLoadBalancingConfigList(
(List<Map<String, ?>>) lbConfig.getRawConfigValue().get("childPolicy"));
assertThat(childConfigs).hasSize(1);
assertThat(childConfigs.get(0).getPolicyName()).isEqualTo("round_robin");
assertThat(childConfigs.get(0).getRawConfigValue()).isEmpty();
} }
@Test @Test
@ -131,56 +122,29 @@ public class LoadBalancerConfigFactoryTest {
.setLoadBalancingPolicy(LoadBalancingPolicy.newBuilder().addPolicies(RING_HASH_POLICY)) .setLoadBalancingPolicy(LoadBalancingPolicy.newBuilder().addPolicies(RING_HASH_POLICY))
.build(); .build();
assertValidRingHash(ServiceConfigUtil.unwrapLoadBalancingConfig( assertThat(newLbConfig(cluster, true)).isEqualTo(VALID_RING_HASH_CONFIG);
LoadBalancerConfigFactory.newConfig(cluster, true)));
} }
@Test @Test
public void ringHash_legacy() throws ResourceInvalidException { public void ringHash_legacy() throws ResourceInvalidException {
Cluster cluster = Cluster.newBuilder() Cluster cluster = Cluster.newBuilder().setLbPolicy(LbPolicy.RING_HASH).setRingHashLbConfig(
.setLbPolicy(LbPolicy.RING_HASH) RingHashLbConfig.newBuilder().setMinimumRingSize(UInt64Value.of(RING_HASH_MIN_RING_SIZE))
.setRingHashLbConfig( .setMaximumRingSize(UInt64Value.of(RING_HASH_MAX_RING_SIZE))
RingHashLbConfig.newBuilder() .setHashFunction(HashFunction.XX_HASH)).build();
.setMinimumRingSize(UInt64Value.of(RING_HASH_MIN_RING_SIZE))
.setMaximumRingSize(UInt64Value.of(RING_HASH_MAX_RING_SIZE))
.setHashFunction(HashFunction.XX_HASH))
.build();
assertValidRingHash(ServiceConfigUtil.unwrapLoadBalancingConfig( assertThat(newLbConfig(cluster, true)).isEqualTo(VALID_RING_HASH_CONFIG);
LoadBalancerConfigFactory.newConfig(cluster, true)));
}
private void assertValidRingHash(LbConfig lbConfig) {
assertThat(lbConfig.getPolicyName()).isEqualTo("ring_hash_experimental");
assertThat(JsonUtil.getNumberAsLong(lbConfig.getRawConfigValue(), "minRingSize")).isEqualTo(
RING_HASH_MIN_RING_SIZE);
assertThat(JsonUtil.getNumberAsLong(lbConfig.getRawConfigValue(), "maxRingSize")).isEqualTo(
RING_HASH_MAX_RING_SIZE);
} }
@Test @Test
public void ringHash_invalidHash() { public void ringHash_invalidHash() {
Cluster cluster = Cluster.newBuilder() Cluster cluster = newCluster(
.setLoadBalancingPolicy(LoadBalancingPolicy.newBuilder() Policy.newBuilder().setTypedExtensionConfig(TypedExtensionConfig.newBuilder()
.addPolicies(Policy.newBuilder().setTypedExtensionConfig( .setTypedConfig(Any.pack(
TypedExtensionConfig.newBuilder().setTypedConfig(Any.pack( RingHash.newBuilder().setMinimumRingSize(UInt64Value.of(RING_HASH_MIN_RING_SIZE))
RingHash.newBuilder() .setMaximumRingSize(UInt64Value.of(RING_HASH_MAX_RING_SIZE))
.setMinimumRingSize(UInt64Value.of(RING_HASH_MIN_RING_SIZE)) .setHashFunction(RingHash.HashFunction.MURMUR_HASH_2).build()))).build());
.setMaximumRingSize(UInt64Value.of(RING_HASH_MAX_RING_SIZE))
.setHashFunction(RingHash.HashFunction.MURMUR_HASH_2).build()))).build()))
.build();
try { assertResourceInvalidExceptionThrown(cluster, true, "Invalid ring hash function");
ServiceConfigUtil.unwrapLoadBalancingConfig(
LoadBalancerConfigFactory.newConfig(cluster, true));
} catch (ResourceInvalidException e) {
// With the new config mechanism we get a more generic error than with the old one because the
// logic loops over potentially multiple configurations and only throws an exception at the
// end if there was no valid policies found.
assertThat(e).hasMessageThat().contains("Invalid ring hash function");
return;
}
fail("ResourceInvalidException not thrown");
} }
@Test @Test
@ -188,114 +152,83 @@ public class LoadBalancerConfigFactoryTest {
Cluster cluster = Cluster.newBuilder().setLbPolicy(LbPolicy.RING_HASH).setRingHashLbConfig( Cluster cluster = Cluster.newBuilder().setLbPolicy(LbPolicy.RING_HASH).setRingHashLbConfig(
RingHashLbConfig.newBuilder().setHashFunction(HashFunction.MURMUR_HASH_2)).build(); RingHashLbConfig.newBuilder().setHashFunction(HashFunction.MURMUR_HASH_2)).build();
try { assertResourceInvalidExceptionThrown(cluster, true, "invalid ring hash function");
ServiceConfigUtil.unwrapLoadBalancingConfig(
LoadBalancerConfigFactory.newConfig(cluster, true));
} catch (ResourceInvalidException e) {
assertThat(e).hasMessageThat().contains("invalid ring hash function");
return;
}
fail("ResourceInvalidException not thrown");
} }
@Test @Test
public void leastRequest() throws ResourceInvalidException { public void leastRequest_legacy() throws ResourceInvalidException {
System.setProperty("io.grpc.xds.experimentalEnableLeastRequest", "true"); System.setProperty("io.grpc.xds.experimentalEnableLeastRequest", "true");
Cluster cluster = Cluster.newBuilder() Cluster cluster = Cluster.newBuilder().setLbPolicy(LbPolicy.LEAST_REQUEST)
.setLbPolicy(LbPolicy.LEAST_REQUEST)
.setLeastRequestLbConfig( .setLeastRequestLbConfig(
LeastRequestLbConfig.newBuilder().setChoiceCount(UInt32Value.of(10))) LeastRequestLbConfig.newBuilder().setChoiceCount(UInt32Value.of(10))).build();
.build();
LbConfig lbConfig = ServiceConfigUtil.unwrapLoadBalancingConfig( LbConfig lbConfig = newLbConfig(cluster, true);
LoadBalancerConfigFactory.newConfig(cluster, true));
assertThat(lbConfig.getPolicyName()).isEqualTo("wrr_locality_experimental"); assertThat(lbConfig.getPolicyName()).isEqualTo("wrr_locality_experimental");
@SuppressWarnings("unchecked")
List<LbConfig> childConfigs = ServiceConfigUtil.unwrapLoadBalancingConfigList( List<LbConfig> childConfigs = ServiceConfigUtil.unwrapLoadBalancingConfigList(
(List<Map<String, ?>>) lbConfig.getRawConfigValue().get("childPolicy")); JsonUtil.getListOfObjects(lbConfig.getRawConfigValue(), "childPolicy"));
assertThat(childConfigs.get(0).getPolicyName()).isEqualTo("least_request_experimental"); assertThat(childConfigs.get(0).getPolicyName()).isEqualTo("least_request_experimental");
assertThat( assertThat(
JsonUtil.getNumberAsLong(childConfigs.get(0).getRawConfigValue(), "choiceCount")).isEqualTo( JsonUtil.getNumberAsLong(childConfigs.get(0).getRawConfigValue(), "choiceCount")).isEqualTo(
10); 10);
} }
@Test @Test
public void leastRequest_notEnabled() { public void leastRequest_notEnabled() {
System.setProperty("io.grpc.xds.experimentalEnableLeastRequest", "false"); System.setProperty("io.grpc.xds.experimentalEnableLeastRequest", "false");
Cluster cluster = Cluster.newBuilder().setLbPolicy(LbPolicy.LEAST_REQUEST).build(); Cluster cluster = Cluster.newBuilder().setLbPolicy(LbPolicy.LEAST_REQUEST).build();
try { assertResourceInvalidExceptionThrown(cluster, false, "unsupported lb policy");
ServiceConfigUtil.unwrapLoadBalancingConfig(
LoadBalancerConfigFactory.newConfig(cluster, false));
} catch (ResourceInvalidException e) {
assertThat(e).hasMessageThat().contains("unsupported lb policy");
return;
}
fail("ResourceInvalidException not thrown");
} }
@Test @Test
public void customConfiguration() throws ResourceInvalidException { public void customRootLb_providerRegistered() throws ResourceInvalidException {
LoadBalancerRegistry.getDefaultRegistry().register(CUSTOM_POLICY_PROVIDER); LoadBalancerRegistry.getDefaultRegistry().register(CUSTOM_POLICY_PROVIDER);
Cluster cluster = Cluster.newBuilder() assertThat(newLbConfig(newCluster(CUSTOM_POLICY), false)).isEqualTo(VALID_CUSTOM_CONFIG);
.setLoadBalancingPolicy(
LoadBalancingPolicy.newBuilder().addPolicies(buildWrrPolicy(CUSTOM_POLICY)))
.build();
assertValidCustomConfig(ServiceConfigUtil.unwrapLoadBalancingConfig(
LoadBalancerConfigFactory.newConfig(cluster, false)));
} }
// When a provider for the custom policy is available, the configuration should use it.
@Test @Test
public void complexCustomConfig_customProviderRegistered() throws ResourceInvalidException { public void customRootLb_providerNotRegistered() throws ResourceInvalidException {
Cluster cluster = Cluster.newBuilder()
.setLoadBalancingPolicy(LoadBalancingPolicy.newBuilder().addPolicies(CUSTOM_POLICY))
.build();
assertResourceInvalidExceptionThrown(cluster, false, "Invalid LoadBalancingPolicy");
}
// When a provider for the endpoint picking custom policy is available, the configuration should
// use it.
@Test
public void customLbInWrr_providerRegistered() throws ResourceInvalidException {
LoadBalancerRegistry.getDefaultRegistry().register(CUSTOM_POLICY_PROVIDER); LoadBalancerRegistry.getDefaultRegistry().register(CUSTOM_POLICY_PROVIDER);
Cluster cluster = Cluster.newBuilder() Cluster cluster = Cluster.newBuilder().setLoadBalancingPolicy(LoadBalancingPolicy.newBuilder()
.setLoadBalancingPolicy( .addPolicies(buildWrrPolicy(CUSTOM_POLICY, ROUND_ROBIN_POLICY))).build();
LoadBalancingPolicy.newBuilder().addPolicies(buildWrrPolicy(CUSTOM_POLICY))
.addPolicies(buildWrrPolicy(ROUND_ROBIN_POLICY)))
.build();
assertValidCustomConfig(ServiceConfigUtil.unwrapLoadBalancingConfig( assertThat(newLbConfig(cluster, false)).isEqualTo(VALID_CUSTOM_CONFIG_IN_WRR);
LoadBalancerConfigFactory.newConfig(cluster, false)));
} }
// When a provider for the custom policy is NOT available, we still fail even if there is another // When a provider for the custom wrr_locality child policy is NOT available, we should fall back
// round_robin configuration in the list as the wrr_locality the custom config is wrapped in is // to the round_robin that is also provided.
// a recognized type and expected to have a valid config.
@Test @Test
public void complexCustomConfig_customProviderNotRegistered() throws ResourceInvalidException { public void customLbInWrr_providerNotRegistered() throws ResourceInvalidException {
Cluster cluster = Cluster.newBuilder() Cluster cluster = Cluster.newBuilder().setLoadBalancingPolicy(LoadBalancingPolicy.newBuilder()
.setLoadBalancingPolicy( .addPolicies(buildWrrPolicy(CUSTOM_POLICY, ROUND_ROBIN_POLICY))).build();
LoadBalancingPolicy.newBuilder().addPolicies(buildWrrPolicy(CUSTOM_POLICY))
.addPolicies(buildWrrPolicy(ROUND_ROBIN_POLICY)))
.build();
try { assertThat(newLbConfig(cluster, false)).isEqualTo(VALID_ROUND_ROBIN_CONFIG);
ServiceConfigUtil.unwrapLoadBalancingConfig(
LoadBalancerConfigFactory.newConfig(cluster, false));
} catch (ResourceInvalidException e) {
assertThat(e).hasMessageThat().contains("Invalid LoadBalancingPolicy");
return;
}
fail("ResourceInvalidException not thrown");
} }
private void assertValidCustomConfig(LbConfig lbConfig) { // When a provider for the custom wrr_locality child policy is NOT available and no alternative
assertThat(lbConfig.getPolicyName()).isEqualTo("wrr_locality_experimental"); // child policy is provided, the configuration is invalid.
@SuppressWarnings("unchecked") @Test
List<LbConfig> childConfigs = ServiceConfigUtil.unwrapLoadBalancingConfigList( public void customLbInWrr_providerNotRegistered_noFallback() throws ResourceInvalidException {
(List<Map<String, ?>>) lbConfig.getRawConfigValue().get("childPolicy")); Cluster cluster = Cluster.newBuilder().setLoadBalancingPolicy(
assertThat(childConfigs).hasSize(1); LoadBalancingPolicy.newBuilder().addPolicies(buildWrrPolicy(CUSTOM_POLICY))).build();
assertThat(childConfigs.get(0).getPolicyName()).isEqualTo(CUSTOM_POLICY_NAME);
assertThat(childConfigs.get(0).getRawConfigValue().get(CUSTOM_POLICY_FIELD_KEY)).isEqualTo( assertResourceInvalidExceptionThrown(cluster, false, "Invalid LoadBalancingPolicy");
CUSTOM_POLICY_FIELD_VALUE);
} }
@Test @Test
@ -322,13 +255,37 @@ public class LoadBalancerConfigFactoryTest {
buildWrrPolicy( buildWrrPolicy(
ROUND_ROBIN_POLICY))))))))))))))))))).build(); ROUND_ROBIN_POLICY))))))))))))))))))).build();
assertResourceInvalidExceptionThrown(cluster, false,
"Maximum LB config recursion depth reached");
}
private Cluster newCluster(Policy... policies) {
return Cluster.newBuilder().setLoadBalancingPolicy(
LoadBalancingPolicy.newBuilder().addAllPolicies(Lists.newArrayList(policies))).build();
}
private static Policy buildWrrPolicy(Policy... childPolicy) {
return Policy.newBuilder().setTypedExtensionConfig(TypedExtensionConfig.newBuilder()
.setTypedConfig(Any.pack(WrrLocality.newBuilder().setEndpointPickingPolicy(
LoadBalancingPolicy.newBuilder().addAllPolicies(Lists.newArrayList(childPolicy)))
.build()))).build();
}
private LbConfig newLbConfig(Cluster cluster, boolean enableLeastRequest)
throws ResourceInvalidException {
return ServiceConfigUtil.unwrapLoadBalancingConfig(
LoadBalancerConfigFactory.newConfig(cluster, enableLeastRequest));
}
private void assertResourceInvalidExceptionThrown(Cluster cluster, boolean enableLeastRequest,
String expectedMessage) {
try { try {
LoadBalancerConfigFactory.newConfig(cluster, false); newLbConfig(cluster, enableLeastRequest);
} catch (ResourceInvalidException e) { } catch (ResourceInvalidException e) {
assertThat(e).hasMessageThat().contains("Maximum LB config recursion depth reached"); assertThat(e).hasMessageThat().contains(expectedMessage);
return; return;
} }
fail("Expected a ResourceInvalidException because of max recursion exceeded"); fail("ResourceInvalidException not thrown");
} }
private static class FakeCustomLoadBalancerProvider extends LoadBalancerProvider { private static class FakeCustomLoadBalancerProvider extends LoadBalancerProvider {