Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

core,grpclb: DnsNameResolver will use srv record by default if grpclb #6298

Merged
merged 4 commits into from Oct 18, 2019
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 6 additions & 2 deletions api/src/test/java/io/grpc/NameResolverRegistryTest.java
Expand Up @@ -21,6 +21,7 @@
import static org.mockito.Mockito.mock;

import io.grpc.NameResolver.ServiceConfigParser;
import io.grpc.internal.BaseDnsNameResolverProvider;
import io.grpc.internal.DnsNameResolverProvider;
import java.lang.Thread.UncaughtExceptionHandler;
import java.net.URI;
Expand Down Expand Up @@ -152,8 +153,11 @@ public void newNameResolver_noProvider() {
@Test
public void baseProviders() {
List<NameResolverProvider> providers = NameResolverRegistry.getDefaultRegistry().providers();
assertThat(providers).hasSize(1);
assertThat(providers.get(0)).isInstanceOf(DnsNameResolverProvider.class);
assertThat(providers).hasSize(2);
// 2 name resolvers from grpclb and core
for (NameResolverProvider provider : providers) {
assertThat(provider).isInstanceOf(BaseDnsNameResolverProvider.class);
}
assertThat(NameResolverRegistry.getDefaultRegistry().asFactory().getDefaultScheme())
.isEqualTo("dns");
}
Expand Down
@@ -0,0 +1,72 @@
/*
* Copyright 2019 The gRPC Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.grpc.internal;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.base.Stopwatch;
import io.grpc.Internal;
import io.grpc.InternalServiceProviders;
import io.grpc.NameResolver;
import io.grpc.NameResolverProvider;
import java.net.URI;

/**
* Base provider of name resolvers for name agnostic consumption.
*/
@Internal
creamsoup marked this conversation as resolved.
Show resolved Hide resolved
public abstract class BaseDnsNameResolverProvider extends NameResolverProvider {

private static final String SCHEME = "dns";

@VisibleForTesting
public static final String ENABLE_GRPCLB_PROPERTY_NAME =
"io.grpc.internal.DnsNameResolverProvider.enable_grpclb";

/** Returns boolean value of system property {@link #ENABLE_GRPCLB_PROPERTY_NAME}. */
protected abstract boolean isSrvEnabled();

@Override
public DnsNameResolver newNameResolver(URI targetUri, NameResolver.Args args) {
if (SCHEME.equals(targetUri.getScheme())) {
String targetPath = Preconditions.checkNotNull(targetUri.getPath(), "targetPath");
Preconditions.checkArgument(targetPath.startsWith("/"),
"the path component (%s) of the target (%s) must start with '/'", targetPath, targetUri);
String name = targetPath.substring(1);
return new DnsNameResolver(
targetUri.getAuthority(),
name,
args,
GrpcUtil.SHARED_CHANNEL_EXECUTOR,
Stopwatch.createUnstarted(),
InternalServiceProviders.isAndroid(getClass().getClassLoader()),
isSrvEnabled());
} else {
return null;
}
}

@Override
public String getDefaultScheme() {
return SCHEME;
}

@Override
protected boolean isAvailable() {
return true;
}
}
16 changes: 10 additions & 6 deletions core/src/main/java/io/grpc/internal/DnsNameResolver.java
Expand Up @@ -92,8 +92,6 @@ final class DnsNameResolver extends NameResolver {
System.getProperty("io.grpc.internal.DnsNameResolverProvider.enable_jndi", "true");
private static final String JNDI_LOCALHOST_PROPERTY =
System.getProperty("io.grpc.internal.DnsNameResolverProvider.enable_jndi_localhost", "false");
private static final String JNDI_SRV_PROPERTY =
System.getProperty("io.grpc.internal.DnsNameResolverProvider.enable_grpclb", "false");
private static final String JNDI_TXT_PROPERTY =
System.getProperty("io.grpc.internal.DnsNameResolverProvider.enable_service_config", "false");

Expand All @@ -117,8 +115,6 @@ final class DnsNameResolver extends NameResolver {
@VisibleForTesting
static boolean enableJndiLocalhost = Boolean.parseBoolean(JNDI_LOCALHOST_PROPERTY);
@VisibleForTesting
static boolean enableSrv = Boolean.parseBoolean(JNDI_SRV_PROPERTY);
@VisibleForTesting
static boolean enableTxt = Boolean.parseBoolean(JNDI_TXT_PROPERTY);

private static final ResourceResolverFactory resourceResolverFactory =
Expand Down Expand Up @@ -152,15 +148,22 @@ final class DnsNameResolver extends NameResolver {

/** True if using an executor resource that should be released after use. */
private final boolean usingExecutorResource;
private final boolean enableSrv;

private boolean resolving;

// The field must be accessed from syncContext, although the methods on an Listener2 can be called
// from any thread.
private NameResolver.Listener2 listener;

DnsNameResolver(@Nullable String nsAuthority, String name, Args args,
Resource<Executor> executorResource, Stopwatch stopwatch, boolean isAndroid) {
DnsNameResolver(
@Nullable String nsAuthority,
String name,
Args args,
Resource<Executor> executorResource,
Stopwatch stopwatch,
boolean isAndroid,
boolean enableSrv) {
Preconditions.checkNotNull(args, "args");
// TODO: if a DNS server is provided as nsAuthority, use it.
// https://www.captechconsulting.com/blogs/accessing-the-dusty-corners-of-dns-with-java
Expand All @@ -184,6 +187,7 @@ final class DnsNameResolver extends NameResolver {
Preconditions.checkNotNull(args.getSynchronizationContext(), "syncContext");
this.executor = args.getBlockingExecutor();
this.usingExecutorResource = executor == null;
this.enableSrv = enableSrv;
}

@Override
Expand Down
39 changes: 3 additions & 36 deletions core/src/main/java/io/grpc/internal/DnsNameResolverProvider.java
Expand Up @@ -16,13 +16,6 @@

package io.grpc.internal;

import com.google.common.base.Preconditions;
import com.google.common.base.Stopwatch;
import io.grpc.InternalServiceProviders;
import io.grpc.NameResolver;
import io.grpc.NameResolverProvider;
import java.net.URI;

/**
* A provider for {@link DnsNameResolver}.
*
Expand All @@ -38,37 +31,11 @@
* <li>{@code "dns:///foo.googleapis.com"} (without port)</li>
* </ul>
*/
public final class DnsNameResolverProvider extends NameResolverProvider {

private static final String SCHEME = "dns";

@Override
public DnsNameResolver newNameResolver(URI targetUri, NameResolver.Args args) {
if (SCHEME.equals(targetUri.getScheme())) {
String targetPath = Preconditions.checkNotNull(targetUri.getPath(), "targetPath");
Preconditions.checkArgument(targetPath.startsWith("/"),
"the path component (%s) of the target (%s) must start with '/'", targetPath, targetUri);
String name = targetPath.substring(1);
return new DnsNameResolver(
targetUri.getAuthority(),
name,
args,
GrpcUtil.SHARED_CHANNEL_EXECUTOR,
Stopwatch.createUnstarted(),
InternalServiceProviders.isAndroid(getClass().getClassLoader()));
} else {
return null;
}
}

@Override
public String getDefaultScheme() {
return SCHEME;
}
public final class DnsNameResolverProvider extends BaseDnsNameResolverProvider {

@Override
protected boolean isAvailable() {
return true;
protected boolean isSrvEnabled() {
return Boolean.parseBoolean(System.getProperty(ENABLE_GRPCLB_PROPERTY_NAME, "false"));
creamsoup marked this conversation as resolved.
Show resolved Hide resolved
}

@Override
Expand Down
Expand Up @@ -16,6 +16,8 @@

package io.grpc.internal;

import static com.google.common.truth.Truth.assertThat;
import static io.grpc.internal.BaseDnsNameResolverProvider.ENABLE_GRPCLB_PROPERTY_NAME;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
Expand All @@ -25,6 +27,9 @@
import io.grpc.NameResolver.ServiceConfigParser;
import io.grpc.SynchronizationContext;
import java.net.URI;
import javax.annotation.Nullable;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
Expand All @@ -46,15 +51,52 @@ public void uncaughtException(Thread t, Throwable e) {
.setServiceConfigParser(mock(ServiceConfigParser.class))
.build();

private DnsNameResolverProvider provider = new DnsNameResolverProvider();
@Nullable
private String jndiSrvProperty;

@Before
public void memoizeSrvProperty() {
jndiSrvProperty = System.getProperty(ENABLE_GRPCLB_PROPERTY_NAME);
}

@After
public void restoreSrvProperty() {
if (jndiSrvProperty == null) {
System.clearProperty(ENABLE_GRPCLB_PROPERTY_NAME);
} else {
System.setProperty(ENABLE_GRPCLB_PROPERTY_NAME, jndiSrvProperty);
}
}

@Test
public void enableSrv_useDefault() {
System.clearProperty(ENABLE_GRPCLB_PROPERTY_NAME);

assertThat(new DnsNameResolverProvider().isSrvEnabled()).isFalse();
}

@Test
public void enableSrv_defaultEnabled() {
System.setProperty(ENABLE_GRPCLB_PROPERTY_NAME, "true");

assertThat(new DnsNameResolverProvider().isSrvEnabled()).isTrue();
}

@Test
public void enableSrv_defaultDisabled() {
System.setProperty(ENABLE_GRPCLB_PROPERTY_NAME, "false");

assertThat(new DnsNameResolverProvider().isSrvEnabled()).isFalse();
}
creamsoup marked this conversation as resolved.
Show resolved Hide resolved

@Test
public void isAvailable() {
assertTrue(provider.isAvailable());
assertTrue(new DnsNameResolverProvider().isAvailable());
}

@Test
public void newNameResolver() {
DnsNameResolverProvider provider = new DnsNameResolverProvider();
assertSame(DnsNameResolver.class,
provider.newNameResolver(URI.create("dns:///localhost:443"), args).getClass());
assertNull(
Expand Down
3 changes: 2 additions & 1 deletion core/src/test/java/io/grpc/internal/DnsNameResolverTest.java
Expand Up @@ -182,7 +182,8 @@ private DnsNameResolver newResolver(
private DnsNameResolver newResolver(
String name, Stopwatch stopwatch, boolean isAndroid, NameResolver.Args args) {
DnsNameResolver dnsResolver =
new DnsNameResolver(null, name, args, fakeExecutorResource, stopwatch, isAndroid);
new DnsNameResolver(
null, name, args, fakeExecutorResource, stopwatch, isAndroid, /* enableSrv= */ false);
// By default, using the mocked ResourceResolver to avoid I/O
dnsResolver.setResourceResolver(new JndiResourceResolver(recordFetcher));
return dnsResolver;
Expand Down
@@ -0,0 +1,53 @@
/*
* Copyright 2019 The gRPC Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.grpc.grpclb;

import io.grpc.Internal;
import io.grpc.internal.BaseDnsNameResolverProvider;

/**
* A provider for {@code io.grpc.internal.DnsNameResolver} for gRPC lb.
*
* <p>It resolves a target URI whose scheme is {@code "dns"}. The (optional) authority of the target
* URI is reserved for the address of alternative DNS server (not implemented yet). The path of the
* target URI, excluding the leading slash {@code '/'}, is treated as the host name and the optional
* port to be resolved by DNS. Example target URIs:
*
* <ul>
* <li>{@code "dns:///foo.googleapis.com:8080"} (using default DNS)</li>
* <li>{@code "dns://8.8.8.8/foo.googleapis.com:8080"} (using alternative DNS (not implemented
* yet))</li>
* <li>{@code "dns:///foo.googleapis.com"} (without port)</li>
* </ul>
*
* <p>Note: the main difference between {@code io.grpc.DnsNameResolver} is service record is enabled
* by default.
*/
@Internal
public class GrpclbNameResolverProvider extends BaseDnsNameResolverProvider {
creamsoup marked this conversation as resolved.
Show resolved Hide resolved

@Override
protected boolean isSrvEnabled() {
return Boolean.parseBoolean(System.getProperty(ENABLE_GRPCLB_PROPERTY_NAME, "true"));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to the design, GRPCLB will have SRV enabled "unconditionally". Just return "true" here, then the test can be largely simplified.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i discussed with @ejona86, we decided to make that conditional for debugging purpose etc.
i made a local change to make it testable, but it is pretty nasty (using class loader and reflection to reevaluate class... not many lines of code but yup....) i am not sure we want to use this approach or not.

}

@Override
protected int priority() {
// Must be higher than DnsNameResolverProvider#priority.
return 6;
}
}
@@ -0,0 +1 @@
io.grpc.grpclb.GrpclbNameResolverProvider