mirror of https://github.com/grpc/grpc-java.git
xds: xdsNameResolver match channel overrideAuthority in virtualHost matching (#9405)
This commit is contained in:
parent
58cd6e1a7f
commit
027d36eee7
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
Loading…
Reference in New Issue