xds: xdsNameResolver match channel overrideAuthority in virtualHost matching (#9405)

This commit is contained in:
yifeizhuang 2022-07-22 12:41:16 -07:00 committed by GitHub
parent 58cd6e1a7f
commit 027d36eee7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 119 additions and 18 deletions

View File

@ -261,6 +261,7 @@ public abstract class NameResolver {
@Nullable private final ScheduledExecutorService scheduledExecutorService;
@Nullable private final ChannelLogger channelLogger;
@Nullable private final Executor executor;
@Nullable private final String overrideAuthority;
private Args(
Integer defaultPort,
@ -269,7 +270,8 @@ public abstract class NameResolver {
ServiceConfigParser serviceConfigParser,
@Nullable ScheduledExecutorService scheduledExecutorService,
@Nullable ChannelLogger channelLogger,
@Nullable Executor executor) {
@Nullable Executor executor,
@Nullable String overrideAuthority) {
this.defaultPort = checkNotNull(defaultPort, "defaultPort not set");
this.proxyDetector = checkNotNull(proxyDetector, "proxyDetector not set");
this.syncContext = checkNotNull(syncContext, "syncContext not set");
@ -277,6 +279,7 @@ public abstract class NameResolver {
this.scheduledExecutorService = scheduledExecutorService;
this.channelLogger = channelLogger;
this.executor = executor;
this.overrideAuthority = overrideAuthority;
}
/**
@ -362,6 +365,20 @@ public abstract class NameResolver {
return executor;
}
/**
* Returns the overrideAuthority from channel {@link ManagedChannelBuilder#overrideAuthority}.
* Overrides the host name for L7 HTTP virtual host matching. Almost all name resolvers should
* not use this.
*
* @since 1.49.0
*/
@Nullable
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/9406")
public String getOverrideAuthority() {
return overrideAuthority;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
@ -372,6 +389,7 @@ public abstract class NameResolver {
.add("scheduledExecutorService", scheduledExecutorService)
.add("channelLogger", channelLogger)
.add("executor", executor)
.add("overrideAuthority", overrideAuthority)
.toString();
}
@ -389,6 +407,7 @@ public abstract class NameResolver {
builder.setScheduledExecutorService(scheduledExecutorService);
builder.setChannelLogger(channelLogger);
builder.setOffloadExecutor(executor);
builder.setOverrideAuthority(overrideAuthority);
return builder;
}
@ -414,6 +433,7 @@ public abstract class NameResolver {
private ScheduledExecutorService scheduledExecutorService;
private ChannelLogger channelLogger;
private Executor executor;
private String overrideAuthority;
Builder() {
}
@ -490,6 +510,17 @@ public abstract class NameResolver {
return this;
}
/**
* See {@link Args#getOverrideAuthority()}. This is an optional field.
*
* @since 1.49.0
*/
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/9406")
public Builder setOverrideAuthority(String authority) {
this.overrideAuthority = authority;
return this;
}
/**
* Builds an {@link Args}.
*
@ -499,7 +530,7 @@ public abstract class NameResolver {
return
new Args(
defaultPort, proxyDetector, syncContext, serviceConfigParser,
scheduledExecutorService, channelLogger, executor);
scheduledExecutorService, channelLogger, executor, overrideAuthority);
}
}
}

View File

@ -40,6 +40,7 @@ public class NameResolverTest {
mock(ScheduledExecutorService.class);
private final ChannelLogger channelLogger = mock(ChannelLogger.class);
private final Executor executor = Executors.newSingleThreadExecutor();
private final String overrideAuthority = "grpc.io";
@Test
public void args() {
@ -51,6 +52,7 @@ public class NameResolverTest {
assertThat(args.getScheduledExecutorService()).isSameInstanceAs(scheduledExecutorService);
assertThat(args.getChannelLogger()).isSameInstanceAs(channelLogger);
assertThat(args.getOffloadExecutor()).isSameInstanceAs(executor);
assertThat(args.getOverrideAuthority()).isSameInstanceAs(overrideAuthority);
NameResolver.Args args2 = args.toBuilder().build();
assertThat(args2.getDefaultPort()).isEqualTo(defaultPort);
@ -60,6 +62,7 @@ public class NameResolverTest {
assertThat(args2.getScheduledExecutorService()).isSameInstanceAs(scheduledExecutorService);
assertThat(args2.getChannelLogger()).isSameInstanceAs(channelLogger);
assertThat(args2.getOffloadExecutor()).isSameInstanceAs(executor);
assertThat(args2.getOverrideAuthority()).isSameInstanceAs(overrideAuthority);
assertThat(args2).isNotSameInstanceAs(args);
assertThat(args2).isNotEqualTo(args);
@ -74,6 +77,7 @@ public class NameResolverTest {
.setScheduledExecutorService(scheduledExecutorService)
.setChannelLogger(channelLogger)
.setOffloadExecutor(executor)
.setOverrideAuthority(overrideAuthority)
.build();
}
}

View File

@ -645,6 +645,7 @@ final class ManagedChannelImpl extends ManagedChannel implements
builder.maxRetryAttempts,
builder.maxHedgedAttempts,
loadBalancerFactory);
this.authorityOverride = builder.authorityOverride;
this.nameResolverArgs =
NameResolver.Args.newBuilder()
.setDefaultPort(builder.getDefaultPort())
@ -654,8 +655,8 @@ final class ManagedChannelImpl extends ManagedChannel implements
.setServiceConfigParser(serviceConfigParser)
.setChannelLogger(channelLogger)
.setOffloadExecutor(this.offloadExecutorHolder)
.setOverrideAuthority(this.authorityOverride)
.build();
this.authorityOverride = builder.authorityOverride;
this.nameResolverFactory = builder.nameResolverFactory;
this.nameResolver = getNameResolver(
target, authorityOverride, nameResolverFactory, nameResolverArgs);

View File

@ -111,6 +111,7 @@ final class XdsNameResolver extends NameResolver {
@Nullable
private final String targetAuthority;
private final String serviceAuthority;
private final String overrideAuthority;
private final ServiceConfigParser serviceConfigParser;
private final SynchronizationContext syncContext;
private final ScheduledExecutorService scheduler;
@ -134,22 +135,25 @@ final class XdsNameResolver extends NameResolver {
private boolean receivedConfig;
XdsNameResolver(
@Nullable String targetAuthority, String name, ServiceConfigParser serviceConfigParser,
@Nullable String targetAuthority, String name, @Nullable String overrideAuthority,
ServiceConfigParser serviceConfigParser,
SynchronizationContext syncContext, ScheduledExecutorService scheduler,
@Nullable Map<String, ?> bootstrapOverride) {
this(targetAuthority, name, serviceConfigParser, syncContext, scheduler,
this(targetAuthority, name, overrideAuthority, serviceConfigParser, syncContext, scheduler,
SharedXdsClientPoolProvider.getDefaultProvider(), ThreadSafeRandomImpl.instance,
FilterRegistry.getDefaultRegistry(), bootstrapOverride);
}
@VisibleForTesting
XdsNameResolver(
@Nullable String targetAuthority, String name, ServiceConfigParser serviceConfigParser,
@Nullable String targetAuthority, String name, @Nullable String overrideAuthority,
ServiceConfigParser serviceConfigParser,
SynchronizationContext syncContext, ScheduledExecutorService scheduler,
XdsClientPoolFactory xdsClientPoolFactory, ThreadSafeRandom random,
FilterRegistry filterRegistry, @Nullable Map<String, ?> bootstrapOverride) {
this.targetAuthority = targetAuthority;
serviceAuthority = GrpcUtil.checkAuthority(checkNotNull(name, "name"));
this.overrideAuthority = overrideAuthority;
this.serviceConfigParser = checkNotNull(serviceConfigParser, "serviceConfigParser");
this.syncContext = checkNotNull(syncContext, "syncContext");
this.scheduler = checkNotNull(scheduler, "scheduler");
@ -769,9 +773,10 @@ final class XdsNameResolver extends NameResolver {
// called in syncContext
private void updateRoutes(List<VirtualHost> virtualHosts, long httpMaxStreamDurationNano,
@Nullable List<NamedFilterConfig> filterConfigs) {
VirtualHost virtualHost = findVirtualHostForHostName(virtualHosts, ldsResourceName);
String authority = overrideAuthority != null ? overrideAuthority : ldsResourceName;
VirtualHost virtualHost = findVirtualHostForHostName(virtualHosts, authority);
if (virtualHost == null) {
String error = "Failed to find virtual host matching hostname: " + ldsResourceName;
String error = "Failed to find virtual host matching hostname: " + authority;
logger.log(XdsLogLevel.WARNING, error);
cleanUpRoutes(error);
return;

View File

@ -79,8 +79,9 @@ public final class XdsNameResolverProvider extends NameResolverProvider {
targetUri);
String name = targetPath.substring(1);
return new XdsNameResolver(
targetUri.getAuthority(), name, args.getServiceConfigParser(),
args.getSynchronizationContext(), args.getScheduledExecutorService(),
targetUri.getAuthority(), name, args.getOverrideAuthority(),
args.getServiceConfigParser(), args.getSynchronizationContext(),
args.getScheduledExecutorService(),
bootstrapOverride);
}
return null;

View File

@ -167,7 +167,8 @@ public class XdsNameResolverTest {
FilterRegistry filterRegistry = FilterRegistry.newRegistry().register(
new FaultFilter(mockRandom, new AtomicLong()),
RouterFilter.INSTANCE);
resolver = new XdsNameResolver(null, AUTHORITY, serviceConfigParser, syncContext, scheduler,
resolver = new XdsNameResolver(null, AUTHORITY, null,
serviceConfigParser, syncContext, scheduler,
xdsClientPoolFactory, mockRandom, filterRegistry, null);
}
@ -200,7 +201,8 @@ public class XdsNameResolverTest {
throw new XdsInitializationException("Fail to read bootstrap file");
}
};
resolver = new XdsNameResolver(null, AUTHORITY, serviceConfigParser, syncContext, scheduler,
resolver = new XdsNameResolver(null, AUTHORITY, null,
serviceConfigParser, syncContext, scheduler,
xdsClientPoolFactory, mockRandom, FilterRegistry.getDefaultRegistry(), null);
resolver.start(mockListener);
verify(mockListener).onError(errorCaptor.capture());
@ -213,7 +215,7 @@ public class XdsNameResolverTest {
@Test
public void resolving_withTargetAuthorityNotFound() {
resolver = new XdsNameResolver(
"notfound.google.com", AUTHORITY, serviceConfigParser, syncContext, scheduler,
"notfound.google.com", AUTHORITY, null, serviceConfigParser, syncContext, scheduler,
xdsClientPoolFactory, mockRandom, FilterRegistry.getDefaultRegistry(), null);
resolver.start(mockListener);
verify(mockListener).onError(errorCaptor.capture());
@ -234,7 +236,8 @@ public class XdsNameResolverTest {
String serviceAuthority = "[::FFFF:129.144.52.38]:80";
expectedLdsResourceName = "[::FFFF:129.144.52.38]:80/id=1";
resolver = new XdsNameResolver(
null, serviceAuthority, serviceConfigParser, syncContext, scheduler, xdsClientPoolFactory,
null, serviceAuthority, null, serviceConfigParser, syncContext,
scheduler, xdsClientPoolFactory,
mockRandom, FilterRegistry.getDefaultRegistry(), null);
resolver.start(mockListener);
verify(mockListener, never()).onError(any(Status.class));
@ -254,7 +257,7 @@ public class XdsNameResolverTest {
"xdstp://xds.authority.com/envoy.config.listener.v3.Listener/"
+ "%5B::FFFF:129.144.52.38%5D:80?id=1";
resolver = new XdsNameResolver(
null, serviceAuthority, serviceConfigParser, syncContext, scheduler,
null, serviceAuthority, null, serviceConfigParser, syncContext, scheduler,
xdsClientPoolFactory, mockRandom, FilterRegistry.getDefaultRegistry(), null);
resolver.start(mockListener);
verify(mockListener, never()).onError(any(Status.class));
@ -277,7 +280,7 @@ public class XdsNameResolverTest {
expectedLdsResourceName = "xdstp://xds.authority.com/envoy.config.listener.v3.Listener/"
+ "%5B::FFFF:129.144.52.38%5D:80?bar=2&foo=1"; // query param canonified
resolver = new XdsNameResolver(
"xds.authority.com", serviceAuthority, serviceConfigParser, syncContext, scheduler,
"xds.authority.com", serviceAuthority, null, serviceConfigParser, syncContext, scheduler,
xdsClientPoolFactory, mockRandom, FilterRegistry.getDefaultRegistry(), null);
resolver.start(mockListener);
verify(mockListener, never()).onError(any(Status.class));
@ -465,6 +468,61 @@ public class XdsNameResolverTest {
+ ". xDS server returned: UNAVAILABLE: server unreachable");
}
@SuppressWarnings("unchecked")
@Test
public void resolving_matchingVirtualHostNotFound_matchingOverrideAuthority() {
Route route = Route.forAction(RouteMatch.withPathExactOnly(call1.getFullMethodNameForPath()),
RouteAction.forCluster(
cluster1, Collections.<HashPolicy>emptyList(), TimeUnit.SECONDS.toNanos(15L), null),
ImmutableMap.<String, FilterConfig>of());
VirtualHost virtualHost =
VirtualHost.create("virtualhost", Collections.singletonList("random"),
Collections.singletonList(route),
ImmutableMap.<String, FilterConfig>of());
resolver = new XdsNameResolver(null, AUTHORITY, "random",
serviceConfigParser, syncContext, scheduler,
xdsClientPoolFactory, mockRandom, FilterRegistry.getDefaultRegistry(), null);
resolver.start(mockListener);
FakeXdsClient xdsClient = (FakeXdsClient) resolver.getXdsClient();
xdsClient.deliverLdsUpdate(0L, Arrays.asList(virtualHost));
verify(mockListener).onResult(resolutionResultCaptor.capture());
assertServiceConfigForLoadBalancingConfig(
Collections.singletonList(cluster1),
(Map<String, ?>) resolutionResultCaptor.getValue().getServiceConfig().getConfig());
}
@Test
public void resolving_matchingVirtualHostNotFound_notMatchingOverrideAuthority() {
Route route = Route.forAction(RouteMatch.withPathExactOnly(call1.getFullMethodNameForPath()),
RouteAction.forCluster(
cluster1, Collections.<HashPolicy>emptyList(), TimeUnit.SECONDS.toNanos(15L), null),
ImmutableMap.<String, FilterConfig>of());
VirtualHost virtualHost =
VirtualHost.create("virtualhost", Collections.singletonList(AUTHORITY),
Collections.singletonList(route),
ImmutableMap.<String, FilterConfig>of());
resolver = new XdsNameResolver(null, AUTHORITY, "random",
serviceConfigParser, syncContext, scheduler,
xdsClientPoolFactory, mockRandom, FilterRegistry.getDefaultRegistry(), null);
resolver.start(mockListener);
FakeXdsClient xdsClient = (FakeXdsClient) resolver.getXdsClient();
xdsClient.deliverLdsUpdate(0L, Arrays.asList(virtualHost));
assertEmptyResolutionResult("random");
}
@Test
public void resolving_matchingVirtualHostNotFoundForOverrideAuthority() {
resolver = new XdsNameResolver(null, AUTHORITY, AUTHORITY,
serviceConfigParser, syncContext, scheduler,
xdsClientPoolFactory, mockRandom, FilterRegistry.getDefaultRegistry(), null);
resolver.start(mockListener);
FakeXdsClient xdsClient = (FakeXdsClient) resolver.getXdsClient();
xdsClient.deliverLdsUpdate(0L, buildUnmatchedVirtualHosts());
assertEmptyResolutionResult(expectedLdsResourceName);
}
@Test
public void resolving_matchingVirtualHostNotFoundInLdsResource() {
resolver.start(mockListener);
@ -541,7 +599,7 @@ public class XdsNameResolverTest {
public void retryPolicyInPerMethodConfigGeneratedByResolverIsValid() {
ServiceConfigParser realParser = new ScParser(
true, 5, 5, new AutoConfiguredLoadBalancerFactory("pick-first"));
resolver = new XdsNameResolver(null, AUTHORITY, realParser, syncContext, scheduler,
resolver = new XdsNameResolver(null, AUTHORITY, null, realParser, syncContext, scheduler,
xdsClientPoolFactory, mockRandom, FilterRegistry.getDefaultRegistry(), null);
resolver.start(mockListener);
FakeXdsClient xdsClient = (FakeXdsClient) resolver.getXdsClient();
@ -744,7 +802,8 @@ public class XdsNameResolverTest {
// A different resolver/Channel.
resolver.shutdown();
reset(mockListener);
resolver = new XdsNameResolver(null, AUTHORITY, serviceConfigParser, syncContext, scheduler,
resolver = new XdsNameResolver(null, AUTHORITY, null, serviceConfigParser,
syncContext, scheduler,
xdsClientPoolFactory, mockRandom, FilterRegistry.getDefaultRegistry(), null);
resolver.start(mockListener);
xdsClient = (FakeXdsClient) resolver.getXdsClient();