xds: Don't allow hostnames in address field (#12123)

* xds: Don't allow hostnames in address field

gRFC A27 specifies they must be IPv4 or IPv6 addresses. Certainly doing
a DNS lookup hidden inside the config object is asking for trouble.

The tests were accidentally doing a lot of failing DNS requests greatly
slowing them down. On my desktop, which made the problem most obvious
with five search paths in /etc/resolv.conf, :grpc-xds:test decreased
from 66s to 29s. The majority of that is XdsDependencyManagerTest which
went from 33s to .1s, as it generated a UUID for the in-process
transport each test and then used it as a hostname, which defeated
Java's DNS (negative) cache. The slowness was noticed because
XdsDependencyManagerTest should have run quickly as a single thread
without I/O, but was particularly slow on my desktop.

The cleanup caused me to audit serverName and the weird places it went.
I think some of them were tricks for XdsClientFallbackTest to squirrel
away something distinguishing, although reusing the serverName is asking
for confusion as is including the tricks in "shared" utilities.
XdsClientFallbackTest does have some non-trivial changes, but this seems
to fix some pre-existing bugs in the tests.

* Add failing hostname unit test
This commit is contained in:
Eric Anderson 2025-06-05 03:07:49 -07:00 committed by GitHub
parent efe9ccc22c
commit 482dc5c1c3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 71 additions and 45 deletions

View File

@ -22,6 +22,7 @@ import com.google.auto.value.AutoValue;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.net.InetAddresses;
import io.grpc.EquivalentAddressGroup;
import java.net.InetSocketAddress;
import java.util.List;
@ -78,7 +79,8 @@ final class Endpoints {
@VisibleForTesting
static LbEndpoint create(String address, int port, int loadBalancingWeight, boolean isHealthy,
String hostname, ImmutableMap<String, Object> endpointMetadata) {
return LbEndpoint.create(new EquivalentAddressGroup(new InetSocketAddress(address, port)),
return LbEndpoint.create(
new EquivalentAddressGroup(new InetSocketAddress(InetAddresses.forString(address), port)),
loadBalancingWeight, isHealthy, hostname, endpointMetadata);
}
}

View File

@ -40,6 +40,7 @@ import io.grpc.xds.XdsEndpointResource.EdsUpdate;
import io.grpc.xds.client.Locality;
import io.grpc.xds.client.XdsClient.ResourceUpdate;
import io.grpc.xds.client.XdsResourceType;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Collections;
@ -245,10 +246,16 @@ class XdsEndpointResource extends XdsResourceType<EdsUpdate> {
proto.getPriority(), localityMetadata));
}
private static InetSocketAddress getInetSocketAddress(Address address) {
private static InetSocketAddress getInetSocketAddress(Address address)
throws ResourceInvalidException {
io.envoyproxy.envoy.config.core.v3.SocketAddress socketAddress = address.getSocketAddress();
return new InetSocketAddress(socketAddress.getAddress(), socketAddress.getPortValue());
InetAddress parsedAddress;
try {
parsedAddress = InetAddresses.forString(socketAddress.getAddress());
} catch (IllegalArgumentException ex) {
throw new ResourceInvalidException("Address is not an IP", ex);
}
return new InetSocketAddress(parsedAddress, socketAddress.getPortValue());
}
static final class EdsUpdate implements ResourceUpdate {

View File

@ -266,17 +266,17 @@ public class ControlPlaneRule extends TestWatcher {
/**
* Builds a new default EDS configuration.
*/
static ClusterLoadAssignment buildClusterLoadAssignment(String hostName, String endpointHostname,
int port) {
return buildClusterLoadAssignment(hostName, endpointHostname, port, EDS_NAME);
static ClusterLoadAssignment buildClusterLoadAssignment(
String hostAddress, String endpointHostname, int port) {
return buildClusterLoadAssignment(hostAddress, endpointHostname, port, EDS_NAME);
}
static ClusterLoadAssignment buildClusterLoadAssignment(String hostName, String endpointHostname,
int port, String edsName) {
static ClusterLoadAssignment buildClusterLoadAssignment(
String hostAddress, String endpointHostname, int port, String edsName) {
Address address = Address.newBuilder()
.setSocketAddress(
SocketAddress.newBuilder().setAddress(hostName).setPortValue(port).build()).build();
SocketAddress.newBuilder().setAddress(hostAddress).setPortValue(port).build()).build();
LocalityLbEndpoints endpoints = LocalityLbEndpoints.newBuilder()
.setLoadBalancingWeight(UInt32Value.of(10))
.setPriority(0)
@ -297,17 +297,12 @@ public class ControlPlaneRule extends TestWatcher {
* Builds a new client listener.
*/
static Listener buildClientListener(String name) {
return buildClientListener(name, "terminal-filter");
return buildClientListener(name, RDS_NAME);
}
static Listener buildClientListener(String name, String identifier) {
return buildClientListener(name, identifier, RDS_NAME);
}
static Listener buildClientListener(String name, String identifier, String rdsName) {
static Listener buildClientListener(String name, String rdsName) {
HttpFilter httpFilter = HttpFilter.newBuilder()
.setName(identifier)
.setName("terminal-filter")
.setTypedConfig(Any.pack(Router.newBuilder().build()))
.setIsOptional(true)
.build();

View File

@ -53,7 +53,6 @@ import io.grpc.ClientInterceptor;
import io.grpc.MethodDescriptor;
import io.grpc.Status;
import io.grpc.StatusOr;
import io.grpc.inprocess.InProcessServerBuilder;
import io.grpc.testing.TestMethodDescriptors;
import io.grpc.xds.Endpoints.LbEndpoint;
import io.grpc.xds.Endpoints.LocalityLbEndpoints;
@ -84,7 +83,6 @@ import org.mockito.Mockito;
public class GcpAuthenticationFilterTest {
private static final GcpAuthenticationFilter.Provider FILTER_PROVIDER =
new GcpAuthenticationFilter.Provider();
private static final String serverName = InProcessServerBuilder.generateName();
private static final LdsUpdate ldsUpdate = getLdsUpdate();
private static final EdsUpdate edsUpdate = getEdsUpdate();
private static final RdsUpdate rdsUpdate = getRdsUpdate();
@ -461,7 +459,7 @@ public class GcpAuthenticationFilterTest {
private static LdsUpdate getLdsUpdate() {
Filter.NamedFilterConfig routerFilterConfig = new Filter.NamedFilterConfig(
serverName, RouterFilter.ROUTER_CONFIG);
"router", RouterFilter.ROUTER_CONFIG);
HttpConnectionManager httpConnectionManager = HttpConnectionManager.forRdsName(
0L, RDS_NAME, Collections.singletonList(routerFilterConfig));
return XdsListenerResource.LdsUpdate.forApiListener(httpConnectionManager);
@ -469,7 +467,7 @@ public class GcpAuthenticationFilterTest {
private static RdsUpdate getRdsUpdate() {
RouteConfiguration routeConfiguration =
buildRouteConfiguration(serverName, RDS_NAME, CLUSTER_NAME);
buildRouteConfiguration("my-server", RDS_NAME, CLUSTER_NAME);
XdsResourceType.Args args = new XdsResourceType.Args(null, "0", "0", null, null, null);
try {
return XdsRouteConfigureResource.getInstance().doParse(args, routeConfiguration);
@ -481,7 +479,7 @@ public class GcpAuthenticationFilterTest {
private static EdsUpdate getEdsUpdate() {
Map<Locality, LocalityLbEndpoints> lbEndpointsMap = new HashMap<>();
LbEndpoint lbEndpoint = LbEndpoint.create(
serverName, ENDPOINT_PORT, 0, true, ENDPOINT_HOSTNAME, ImmutableMap.of());
"127.0.0.5", ENDPOINT_PORT, 0, true, ENDPOINT_HOSTNAME, ImmutableMap.of());
lbEndpointsMap.put(
Locality.create("", "", ""),
LocalityLbEndpoints.create(ImmutableList.of(lbEndpoint), 10, 0, ImmutableMap.of()));

View File

@ -1077,6 +1077,30 @@ public class GrpcXdsClientImplDataTest {
100, 1, ImmutableMap.of()));
}
@Test
public void parseLocalityLbEndpoints_onlyPermitIp() {
io.envoyproxy.envoy.config.endpoint.v3.LocalityLbEndpoints proto =
io.envoyproxy.envoy.config.endpoint.v3.LocalityLbEndpoints.newBuilder()
.setLocality(Locality.newBuilder()
.setRegion("region-foo").setZone("zone-foo").setSubZone("subZone-foo"))
.setLoadBalancingWeight(UInt32Value.newBuilder().setValue(100)) // locality weight
.setPriority(1)
.addLbEndpoints(io.envoyproxy.envoy.config.endpoint.v3.LbEndpoint.newBuilder()
.setEndpoint(Endpoint.newBuilder()
.setAddress(Address.newBuilder()
.setSocketAddress(
SocketAddress.newBuilder()
.setAddress("example.com").setPortValue(8888))))
.setHealthStatus(io.envoyproxy.envoy.config.core.v3.HealthStatus.HEALTHY)
.setLoadBalancingWeight(UInt32Value.newBuilder().setValue(20))) // endpoint weight
.build();
ResourceInvalidException ex = assertThrows(
ResourceInvalidException.class,
() -> XdsEndpointResource.parseLocalityLbEndpoints(proto));
assertThat(ex.getMessage()).contains("IP");
assertThat(ex.getMessage()).contains("example.com");
}
@Test
public void parseLocalityLbEndpoints_treatUnknownHealthAsHealthy()
throws ResourceInvalidException {

View File

@ -84,10 +84,10 @@ public class XdsClientFallbackTest {
private static final String FALLBACK_EDS_NAME = "fallback-" + EDS_NAME;
private static final HttpConnectionManager MAIN_HTTP_CONNECTION_MANAGER =
HttpConnectionManager.forRdsName(0, RDS_NAME, ImmutableList.of(
new Filter.NamedFilterConfig(MAIN_SERVER, RouterFilter.ROUTER_CONFIG)));
new Filter.NamedFilterConfig("terminal-filter", RouterFilter.ROUTER_CONFIG)));
private static final HttpConnectionManager FALLBACK_HTTP_CONNECTION_MANAGER =
HttpConnectionManager.forRdsName(0, RDS_NAME, ImmutableList.of(
new Filter.NamedFilterConfig(FALLBACK_SERVER, RouterFilter.ROUTER_CONFIG)));
HttpConnectionManager.forRdsName(0, FALLBACK_RDS_NAME, ImmutableList.of(
new Filter.NamedFilterConfig("terminal-filter", RouterFilter.ROUTER_CONFIG)));
private ObjectPool<XdsClient> xdsClientPool;
private XdsClient xdsClient;
private boolean originalEnableXdsFallback;
@ -201,7 +201,7 @@ public class XdsClientFallbackTest {
String edsName = isMainServer ? EDS_NAME : FALLBACK_EDS_NAME;
controlPlane.setLdsConfig(ControlPlaneRule.buildServerListener(),
ControlPlaneRule.buildClientListener(MAIN_SERVER, serverName));
ControlPlaneRule.buildClientListener(MAIN_SERVER, rdsName));
controlPlane.setRdsConfig(rdsName,
XdsTestUtils.buildRouteConfiguration(MAIN_SERVER, rdsName, clusterName));

View File

@ -122,7 +122,7 @@ public class XdsDependencyManagerTest {
private Server xdsServer;
private final FakeClock fakeClock = new FakeClock();
private final String serverName = InProcessServerBuilder.generateName();
private final String serverName = "the-service-name";
private final Queue<XdsTestUtils.LrsRpcCall> loadReportCalls = new ArrayDeque<>();
private final AtomicBoolean adsEnded = new AtomicBoolean(true);
private final AtomicBoolean lrsEnded = new AtomicBoolean(true);
@ -153,7 +153,7 @@ public class XdsDependencyManagerTest {
@Before
public void setUp() throws Exception {
xdsServer = cleanupRule.register(InProcessServerBuilder
.forName(serverName)
.forName("control-plane")
.addService(controlPlaneService)
.addService(lrsService)
.directExecutor()
@ -163,7 +163,7 @@ public class XdsDependencyManagerTest {
XdsTestUtils.setAdsConfig(controlPlaneService, serverName);
channel = cleanupRule.register(
InProcessChannelBuilder.forName(serverName).directExecutor().build());
InProcessChannelBuilder.forName("control-plane").directExecutor().build());
XdsTransportFactory xdsTransportFactory =
ignore -> new GrpcXdsTransportFactory.GrpcXdsTransport(channel);
@ -351,10 +351,10 @@ public class XdsDependencyManagerTest {
// Update config so that one of the 2 "valid" clusters has an EDS resource, the other does not
// and there is an EDS that doesn't have matching clusters
ClusterLoadAssignment clusterLoadAssignment = ControlPlaneRule.buildClusterLoadAssignment(
serverName, ENDPOINT_HOSTNAME, ENDPOINT_PORT, XdsTestUtils.EDS_NAME + 0);
"127.0.1.1", ENDPOINT_HOSTNAME, ENDPOINT_PORT, XdsTestUtils.EDS_NAME + 0);
edsMap.put(XdsTestUtils.EDS_NAME + 0, clusterLoadAssignment);
clusterLoadAssignment = ControlPlaneRule.buildClusterLoadAssignment(
serverName, ENDPOINT_HOSTNAME, ENDPOINT_PORT, "garbageEds");
"127.0.1.2", ENDPOINT_HOSTNAME, ENDPOINT_PORT, "garbageEds");
edsMap.put("garbageEds", clusterLoadAssignment);
controlPlaneService.setXdsConfig(ADS_TYPE_URL_EDS, edsMap);
@ -421,7 +421,7 @@ public class XdsDependencyManagerTest {
@Test
public void testMissingRds() {
String rdsName = "badRdsName";
Listener clientListener = ControlPlaneRule.buildClientListener(serverName, serverName, rdsName);
Listener clientListener = ControlPlaneRule.buildClientListener(serverName, rdsName);
controlPlaneService.setXdsConfig(ADS_TYPE_URL_LDS,
ImmutableMap.of(serverName, clientListener));
@ -578,7 +578,7 @@ public class XdsDependencyManagerTest {
Map<String, Message> edsMap = new HashMap<>();
ClusterLoadAssignment clusterLoadAssignment = ControlPlaneRule.buildClusterLoadAssignment(
serverName, ENDPOINT_HOSTNAME, ENDPOINT_PORT, edsName);
"127.0.1.4", ENDPOINT_HOSTNAME, ENDPOINT_PORT, edsName);
edsMap.put(edsName, clusterLoadAssignment);
RouteConfiguration routeConfig =

View File

@ -1228,7 +1228,7 @@ public class XdsNameResolverTest {
.roundRobinLbPolicy();
xdsClient.deliverCdsUpdate(clusterName, forEds.build());
EdsUpdate edsUpdate = new EdsUpdate(clusterName,
XdsTestUtils.createMinimalLbEndpointsMap("host"), Collections.emptyList());
XdsTestUtils.createMinimalLbEndpointsMap("127.0.0.3"), Collections.emptyList());
xdsClient.deliverEdsUpdate(clusterName, edsUpdate);
}
}

View File

@ -136,7 +136,7 @@ public class XdsTestUtils {
int endpointPort) {
Listener serverListener = ControlPlaneRule.buildServerListener();
Listener clientListener = ControlPlaneRule.buildClientListener(serverName, serverName, rdsName);
Listener clientListener = ControlPlaneRule.buildClientListener(serverName, rdsName);
service.setXdsConfig(ADS_TYPE_URL_LDS,
ImmutableMap.of(SERVER_LISTENER, serverListener, serverName, clientListener));
@ -148,7 +148,7 @@ public class XdsTestUtils {
service.setXdsConfig(ADS_TYPE_URL_CDS, ImmutableMap.<String, Message>of(clusterName, cluster));
ClusterLoadAssignment clusterLoadAssignment = ControlPlaneRule.buildClusterLoadAssignment(
serverName, endpointHostname, endpointPort, edsName);
"127.0.0.11", endpointHostname, endpointPort, edsName);
service.setXdsConfig(ADS_TYPE_URL_EDS,
ImmutableMap.<String, Message>of(edsName, clusterLoadAssignment));
@ -186,7 +186,7 @@ public class XdsTestUtils {
Map<String, Message> edsMap = new HashMap<>();
for (String child : children) {
ClusterLoadAssignment clusterLoadAssignment = ControlPlaneRule.buildClusterLoadAssignment(
serverName, ENDPOINT_HOSTNAME, ENDPOINT_PORT, getEdsNameForCluster(child));
"127.0.0.16", ENDPOINT_HOSTNAME, ENDPOINT_PORT, getEdsNameForCluster(child));
edsMap.put(getEdsNameForCluster(child), clusterLoadAssignment);
}
service.setXdsConfig(ADS_TYPE_URL_EDS, edsMap);
@ -225,7 +225,7 @@ public class XdsTestUtils {
continue;
}
ClusterLoadAssignment clusterLoadAssignment = ControlPlaneRule.buildClusterLoadAssignment(
child, ENDPOINT_HOSTNAME, ENDPOINT_PORT, getEdsNameForCluster(child));
"127.0.0.15", ENDPOINT_HOSTNAME, ENDPOINT_PORT, getEdsNameForCluster(child));
edsMap.put(getEdsNameForCluster(child), clusterLoadAssignment);
}
service.setXdsConfig(ADS_TYPE_URL_EDS, edsMap);
@ -236,7 +236,7 @@ public class XdsTestUtils {
XdsConfig.XdsConfigBuilder builder = new XdsConfig.XdsConfigBuilder();
Filter.NamedFilterConfig routerFilterConfig = new Filter.NamedFilterConfig(
serverHostName, RouterFilter.ROUTER_CONFIG);
"terminal-filter", RouterFilter.ROUTER_CONFIG);
HttpConnectionManager httpConnectionManager = HttpConnectionManager.forRdsName(
0L, RDS_NAME, Collections.singletonList(routerFilterConfig));
@ -257,7 +257,7 @@ public class XdsTestUtils {
// Need to create endpoints to create locality endpoints map to create edsUpdate
Map<Locality, LocalityLbEndpoints> lbEndpointsMap = new HashMap<>();
LbEndpoint lbEndpoint = LbEndpoint.create(
serverHostName, ENDPOINT_PORT, 0, true, ENDPOINT_HOSTNAME, ImmutableMap.of());
"127.0.0.11", ENDPOINT_PORT, 0, true, ENDPOINT_HOSTNAME, ImmutableMap.of());
lbEndpointsMap.put(
Locality.create("", "", ""),
LocalityLbEndpoints.create(ImmutableList.of(lbEndpoint), 10, 0, ImmutableMap.of()));
@ -280,10 +280,10 @@ public class XdsTestUtils {
return builder.build();
}
static Map<Locality, LocalityLbEndpoints> createMinimalLbEndpointsMap(String serverHostName) {
static Map<Locality, LocalityLbEndpoints> createMinimalLbEndpointsMap(String serverAddress) {
Map<Locality, LocalityLbEndpoints> lbEndpointsMap = new HashMap<>();
LbEndpoint lbEndpoint = LbEndpoint.create(
serverHostName, ENDPOINT_PORT, 0, true, ENDPOINT_HOSTNAME, ImmutableMap.of());
serverAddress, ENDPOINT_PORT, 0, true, ENDPOINT_HOSTNAME, ImmutableMap.of());
lbEndpointsMap.put(
Locality.create("", "", ""),
LocalityLbEndpoints.create(ImmutableList.of(lbEndpoint), 10, 0, ImmutableMap.of()));
@ -338,7 +338,7 @@ public class XdsTestUtils {
clusterMap.put(clusterName, cluster);
ClusterLoadAssignment clusterLoadAssignment = ControlPlaneRule.buildClusterLoadAssignment(
clusterName, ENDPOINT_HOSTNAME, ENDPOINT_PORT, edsName);
"127.0.0.13", ENDPOINT_HOSTNAME, ENDPOINT_PORT, edsName);
edsMap.put(edsName, clusterLoadAssignment);
}
}
@ -346,7 +346,7 @@ public class XdsTestUtils {
static Listener buildInlineClientListener(String rdsName, String clusterName, String serverName) {
HttpFilter
httpFilter = HttpFilter.newBuilder()
.setName(serverName)
.setName("terminal-filter")
.setTypedConfig(Any.pack(Router.newBuilder().build()))
.setIsOptional(true)
.build();