Add dagger map for injecting DnsWriter implementations

This is one of several CLs in a sequence for allowing per-TLD DNS
implementations.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=129445641
This commit is contained in:
Greg Shikhman 2016-08-05 08:45:02 -07:00 committed by Justine Tunney
parent 770fd35e20
commit a620d06239
24 changed files with 173 additions and 215 deletions

View file

@ -36,6 +36,7 @@ java_library(
"//third_party/java/objectify:objectify-v4_1",
"//third_party/java/servlet/servlet_api",
"//java/google/registry/config",
"//java/google/registry/dns/writer",
"//java/google/registry/model",
"//java/google/registry/request",
"//java/google/registry/util",

View file

@ -30,7 +30,7 @@ import dagger.Binds;
import dagger.Module;
import dagger.Provides;
import google.registry.dns.DnsConstants.TargetType;
import google.registry.model.dns.DnsWriterZone;
import google.registry.dns.writer.DnsWriterZone;
import google.registry.request.Parameter;
import google.registry.request.RequestParameters;
import java.util.Set;

View file

@ -0,0 +1,42 @@
// Copyright 2016 The Domain Registry Authors. All Rights Reserved.
//
// 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 google.registry.dns;
import static com.google.common.base.Preconditions.checkState;
import com.google.common.collect.ImmutableMap;
import google.registry.dns.writer.DnsWriter;
import google.registry.model.registry.Registry;
import java.util.Map;
import javax.inject.Inject;
/** Proxy for retrieving {@link DnsWriter} implementations. */
public final class DnsWriterProxy {
private final ImmutableMap<String, DnsWriter> dnsWriters;
@Inject
DnsWriterProxy(Map<String, DnsWriter> dnsWriters) {
this.dnsWriters = ImmutableMap.copyOf(dnsWriters);
}
/** Return the {@link DnsWriter} for the given tld. */
public DnsWriter getForTld(String tld) {
String clazz = Registry.get(tld).getDnsWriter();
DnsWriter dnsWriter = dnsWriters.get(clazz);
checkState(dnsWriter != null, "Could not load DnsWriter %s for TLD %s", clazz, tld);
return dnsWriter;
}
}

View file

@ -20,7 +20,7 @@ import static google.registry.util.CollectionUtils.nullToEmpty;
import com.google.common.net.InternetDomainName;
import google.registry.config.ConfigModule.Config;
import google.registry.model.dns.DnsWriter;
import google.registry.dns.writer.DnsWriter;
import google.registry.request.Action;
import google.registry.request.HttpException.ServiceUnavailableException;
import google.registry.request.Parameter;
@ -30,7 +30,6 @@ import google.registry.util.FormattingLogger;
import java.util.Set;
import java.util.concurrent.Callable;
import javax.inject.Inject;
import javax.inject.Provider;
import org.joda.time.Duration;
/** Task that sends domain and host updates to the DNS server. */
@ -44,7 +43,7 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass();
@Inject DnsQueue dnsQueue;
@Inject Provider<DnsWriter> writerProvider;
@Inject DnsWriterProxy dnsWriterProxy;
@Inject @Config("dnsWriteLockTimeout") Duration timeout;
@Inject @Parameter(RequestParameters.PARAM_TLD) String tld;
@Inject @Parameter(DOMAINS_PARAM) Set<String> domains;
@ -73,7 +72,7 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
/** Steps through the domain and host refreshes contained in the parameters and processes them. */
private void processBatch() {
try (DnsWriter writer = writerProvider.get()) {
try (DnsWriter writer = dnsWriterProxy.getForTld(tld)) {
for (String domain : nullToEmpty(domains)) {
if (!DomainNameUtils.isUnder(
InternetDomainName.from(domain), InternetDomainName.from(tld))) {

View file

@ -1,18 +0,0 @@
package(
default_visibility = ["//java/google/registry:registry_project"],
)
licenses(["notice"]) # Apache 2.0
java_library(
name = "writer",
srcs = glob(["*.java"]),
deps = [
"//java/com/google/common/base",
"//third_party/java/dagger",
"//third_party/java/jsr305_annotations",
"//third_party/java/jsr330_inject",
"//java/google/registry/model",
],
)

View file

@ -1,49 +0,0 @@
// Copyright 2016 The Domain Registry Authors. All Rights Reserved.
//
// 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 google.registry.dns.writer;
import com.google.common.base.Joiner;
import google.registry.model.dns.DnsWriter;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Logger;
/**
* {@link DnsWriter} that doesn't actually update records in a DNS server.
*
* <p>All this class does is write its displeasure to the logs.
*/
public final class VoidDnsWriter implements DnsWriter {
private static final Logger logger = Logger.getLogger(VoidDnsWriter.class.getName());
private final Set<String> names = new HashSet<>();
@Override
public void publishDomain(String domainName) {
names.add(domainName);
}
@Override
public void publishHost(String hostName) {
names.add(hostName);
}
@Override
public void close() {
logger.warning("Ignoring DNS zone updates! No DnsWriterFactory implementation specified!\n"
+ Joiner.on('\n').join(names));
}
}

View file

@ -1,29 +0,0 @@
// Copyright 2016 The Domain Registry Authors. All Rights Reserved.
//
// 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 google.registry.dns.writer;
import dagger.Module;
import dagger.Provides;
import google.registry.model.dns.DnsWriter;
/** Dagger module that disables DNS updates. */
@Module
public final class VoidDnsWriterModule {
@Provides
static DnsWriter provideDnsWriter() {
return new VoidDnsWriter();
}
}

View file

@ -21,6 +21,7 @@ java_library(
"//third_party/java/dagger",
"//third_party/java/joda_time",
"//java/google/registry/config",
"//java/google/registry/dns/writer",
"//java/google/registry/model",
"//java/google/registry/util",
],

View file

@ -22,7 +22,10 @@ import com.google.api.services.dns.DnsScopes;
import com.google.common.base.Function;
import dagger.Module;
import dagger.Provides;
import dagger.multibindings.IntoMap;
import dagger.multibindings.StringKey;
import google.registry.config.ConfigModule.Config;
import google.registry.dns.writer.DnsWriter;
import java.util.Set;
/** Dagger module for Google Cloud DNS service connection objects. */
@ -39,4 +42,11 @@ public final class CloudDnsModule {
.setApplicationName(projectId)
.build();
}
@Provides
@IntoMap
@StringKey(CloudDnsWriter.NAME)
static DnsWriter provideCloudDnsWriter(CloudDnsWriter writer) {
return writer;
}
}

View file

@ -32,8 +32,8 @@ import com.google.common.collect.ImmutableSet.Builder;
import com.google.common.net.InternetDomainName;
import com.google.common.util.concurrent.RateLimiter;
import google.registry.config.ConfigModule.Config;
import google.registry.model.dns.DnsWriter;
import google.registry.model.dns.DnsWriterZone;
import google.registry.dns.writer.DnsWriter;
import google.registry.dns.writer.DnsWriterZone;
import google.registry.model.domain.DomainResource;
import google.registry.model.domain.secdns.DelegationSignerData;
import google.registry.model.host.HostResource;
@ -59,6 +59,12 @@ import org.joda.time.Duration;
*/
class CloudDnsWriter implements DnsWriter {
/**
* The name of the pricing engine, as used in {@code Registry.dnsWriter}. Remember to change
* the value on affected Registry objects to prevent runtime failures.
*/
public static final String NAME = "CloudDnsWriter";
private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass();
// This is the default max QPS for Cloud DNS. It can be increased by contacting the team

View file

@ -21,6 +21,7 @@ java_library(
"//third_party/java/jsr305_annotations",
"//third_party/java/jsr330_inject",
"//java/google/registry/config",
"//java/google/registry/dns/writer",
"//java/google/registry/model",
"//java/google/registry/util",
],

View file

@ -25,7 +25,7 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.net.InternetDomainName;
import google.registry.config.ConfigModule.Config;
import google.registry.model.dns.DnsWriter;
import google.registry.dns.writer.DnsWriter;
import google.registry.model.domain.DomainResource;
import google.registry.model.domain.secdns.DelegationSignerData;
import google.registry.model.host.HostResource;
@ -75,6 +75,12 @@ import org.xbill.DNS.Update;
*/
public class DnsUpdateWriter implements DnsWriter {
/**
* The name of the pricing engine, as used in {@code Registry.dnsWriter}. Remember to change
* the value on affected Registry objects to prevent runtime failures.
*/
public static final String NAME = "DnsUpdateWriter";
private final Duration dnsTimeToLive;
private final DnsMessageTransport transport;
private final Clock clock;

View file

@ -14,21 +14,26 @@
package google.registry.dns.writer.dnsupdate;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
import google.registry.model.dns.DnsWriter;
import dagger.multibindings.IntoMap;
import dagger.multibindings.StringKey;
import google.registry.dns.writer.DnsWriter;
import javax.net.SocketFactory;
/** Dagger module that provides a DnsUpdateWriter. */
@Module
public abstract class DnsUpdateWriterModule {
@Binds
abstract DnsWriter provideDnsWriter(DnsUpdateWriter dnsWriter);
@Provides
static SocketFactory provideSocketFactory() {
return SocketFactory.getDefault();
}
@Provides
@IntoMap
@StringKey(DnsUpdateWriter.NAME)
static DnsWriter provideDnsUpdateWriter(DnsUpdateWriter writer) {
return writer;
}
}

View file

@ -1,56 +0,0 @@
// Copyright 2016 The Domain Registry Authors. All Rights Reserved.
//
// 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 google.registry.model.dns;
/**
* Transaction object for sending an atomic batch of updates for a single zone to the DNS server.
*
* <p>Here's an example of how you would publish updates for a domain and host:
* <pre>
* &#064;Inject Provider&lt;DnsWriter&gt; dnsWriter;
* try (DnsWriter writer = dnsWriter.get()) {
* writer.publishDomain(domainName);
* writer.publishHost(hostName);
* }
* </pre>
*/
public interface DnsWriter extends AutoCloseable {
/**
* Loads {@code domainName} from datastore and publishes its NS/DS records to the DNS server.
* Replaces existing records for the exact name supplied with an NS record for each name server
* and a DS record for each delegation signer stored in the registry for the supplied domain name.
* If the domain is deleted or is in a "non-publish" state then any existing records are deleted.
*
* @param domainName the fully qualified domain name, with no trailing dot
*/
void publishDomain(String domainName);
/**
* Loads {@code hostName} from datastore and publishes its A/AAAA glue records to the DNS server,
* if it is used as an in-bailiwick nameserver. Orphaned glue records are prohibited. Replaces
* existing records for the exact name supplied, with an A or AAAA record (as appropriate) for
* each address stored in the registry, for the supplied host name. If the host is deleted then
* the existing records are deleted. Assumes that this method will only be called for in-bailiwick
* hosts. The registry does not have addresses for other hosts.
*
* @param hostName the fully qualified host name, with no trailing dot
*/
void publishHost(String hostName);
/** Commits the updates to the DNS server atomically. */
@Override
void close();
}

View file

@ -1,23 +0,0 @@
// Copyright 2016 The Domain Registry Authors. All Rights Reserved.
//
// 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 google.registry.model.dns;
import java.lang.annotation.Documented;
import javax.inject.Qualifier;
/** Dagger qualifier for the fully-qualified zone name that's being updated. */
@Qualifier
@Documented
public @interface DnsWriterZone {}

View file

@ -255,6 +255,16 @@ public class Registry extends ImmutableObject implements Buildable {
}
}
/** Backfill the Registry entities that were saved before this field was added. */
// TODO(shikhman): Remove this backfill once it is populated on all Registry entities.
@OnLoad
void backfillDnsWriter() {
if (dnsWriter == null) {
dnsWriter = "VoidDnsWriter";
}
}
/**
* The name of the pricing engine that this TLD uses.
*
@ -266,6 +276,14 @@ public class Registry extends ImmutableObject implements Buildable {
*/
String pricingEngineClassName;
/**
* The name of the DnsWriter that this TLD uses.
*
* <p>This must be a valid key for the map of DnsWriters injected by <code>
* @Inject Map<String, DnsWriter></code>
*/
String dnsWriter;
/**
* The unicode-aware representation of the TLD associated with this {@link Registry}.
*
@ -545,6 +563,10 @@ public class Registry extends ImmutableObject implements Buildable {
return pricingEngineClassName;
}
public String getDnsWriter() {
return dnsWriter;
}
public ImmutableSet<String> getAllowedRegistrantContactIds() {
return nullToEmptyImmutableCopy(allowedRegistrantContactIds);
}
@ -616,6 +638,12 @@ public class Registry extends ImmutableObject implements Buildable {
return this;
}
public Builder setDnsWriter(String dnsWriter) {
getInstance().dnsWriter = checkArgumentNotNull(dnsWriter);
return this;
}
public Builder setAddGracePeriodLength(Duration addGracePeriodLength) {
checkArgument(addGracePeriodLength.isLongerThan(Duration.ZERO),
"addGracePeriodLength must be non-zero");

View file

@ -13,6 +13,12 @@ java_library(
"//java/com/google/common/base",
"//java/com/google/common/collect",
"//java/com/google/common/net",
"//third_party/java/bouncycastle",
"//third_party/java/dagger",
"//third_party/java/joda_time",
"//third_party/java/jsr305_annotations",
"//third_party/java/jsr330_inject",
"//third_party/java/servlet/servlet_api",
"//java/google/registry/backup",
"//java/google/registry/bigquery",
"//java/google/registry/billing",
@ -20,6 +26,8 @@ java_library(
"//java/google/registry/cron",
"//java/google/registry/dns",
"//java/google/registry/dns/writer",
"//java/google/registry/dns/writer/clouddns",
"//java/google/registry/dns/writer/dnsupdate",
"//java/google/registry/export",
"//java/google/registry/export/sheet",
"//java/google/registry/flows",
@ -34,12 +42,6 @@ java_library(
"//java/google/registry/request:modules",
"//java/google/registry/tmch",
"//java/google/registry/util",
"//third_party/java/bouncycastle",
"//third_party/java/dagger",
"//third_party/java/joda_time",
"//third_party/java/jsr305_annotations",
"//third_party/java/jsr330_inject",
"//third_party/java/servlet/servlet_api",
],
)

View file

@ -28,6 +28,8 @@ import google.registry.dns.DnsModule;
import google.registry.dns.PublishDnsUpdatesAction;
import google.registry.dns.ReadDnsQueueAction;
import google.registry.dns.RefreshDnsAction;
import google.registry.dns.writer.clouddns.CloudDnsModule;
import google.registry.dns.writer.dnsupdate.DnsUpdateWriterModule;
import google.registry.export.BigqueryPollJobAction;
import google.registry.export.ExportDomainListsAction;
import google.registry.export.ExportRequestModule;
@ -67,8 +69,10 @@ import google.registry.tmch.TmchSmdrlAction;
AsyncFlowsModule.class,
BackendModule.class,
BackupModule.class,
CloudDnsModule.class,
CronModule.class,
DnsModule.class,
DnsUpdateWriterModule.class,
ExportRequestModule.class,
MapreduceModule.class,
RdeModule.class,

View file

@ -45,6 +45,7 @@ java_library(
"//java/com/google/common/util/concurrent",
"//java/google/registry/bigquery",
"//java/google/registry/config",
"//java/google/registry/dns/writer",
"//java/google/registry/export",
"//java/google/registry/flows",
"//java/google/registry/keyring/api",

View file

@ -15,6 +15,7 @@
package google.registry.tools;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static google.registry.model.RoidSuffixes.isRoidSuffixUsed;
import static google.registry.util.CollectionUtils.findDuplicates;
import static google.registry.util.DomainNameUtils.canonicalizeDomainName;
@ -26,6 +27,7 @@ import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
import google.registry.dns.writer.DnsWriter;
import google.registry.model.pricing.StaticPremiumListPricingEngine;
import google.registry.model.registry.Registries;
import google.registry.model.registry.Registry;
@ -39,6 +41,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nullable;
import javax.inject.Inject;
import org.joda.money.Money;
import org.joda.time.DateTime;
import org.joda.time.Duration;
@ -46,6 +49,8 @@ import org.joda.time.Duration;
/** Shared base class for commands to create or update a TLD. */
abstract class CreateOrUpdateTldCommand extends MutatingCommand {
@Inject Map<String, DnsWriter> dnsWriters;
@Parameter(
description = "Names of the TLDs",
required = true)
@ -205,6 +210,15 @@ abstract class CreateOrUpdateTldCommand extends MutatingCommand {
description = "The end of the claims period")
DateTime claimsPeriodEnd;
@Nullable
@Parameter(
names = "--dns_writer",
description = "The name of the DnsWriter implementation to use",
converter = OptionalStringParameter.class,
validateWith = OptionalStringParameter.class
)
Optional<String> dnsWriter;
/** Returns the existing registry (for update) or null (for creates). */
@Nullable
abstract Registry getOldRegistry(String tld);
@ -361,6 +375,16 @@ abstract class CreateOrUpdateTldCommand extends MutatingCommand {
}
}
if (dnsWriter != null) {
if (dnsWriter.isPresent()) {
checkNotNull(
dnsWriters.get(dnsWriter.get()),
"The DNS writer '%s' doesn't exist",
dnsWriter.get());
builder.setDnsWriter(dnsWriter.get());
}
}
ImmutableSet<String> newReservedListNames = getReservedLists(oldRegistry);
checkReservedListValidityForTld(tld, newReservedListNames);
builder.setReservedListsByName(newReservedListNames);

View file

@ -17,18 +17,20 @@ package google.registry.dns;
import static google.registry.testing.DatastoreHelper.createTld;
import static google.registry.testing.DatastoreHelper.persistActiveDomain;
import static google.registry.testing.DatastoreHelper.persistActiveSubordinateHost;
import static google.registry.testing.DatastoreHelper.persistResource;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import google.registry.model.dns.DnsWriter;
import google.registry.dns.writer.DnsWriter;
import google.registry.model.domain.DomainResource;
import google.registry.model.ofy.Ofy;
import google.registry.model.registry.Registry;
import google.registry.testing.AppEngineRule;
import google.registry.testing.FakeClock;
import google.registry.testing.InjectRule;
import javax.inject.Provider;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.junit.Before;
@ -58,6 +60,7 @@ public class PublishDnsUpdatesActionTest {
public void setUp() throws Exception {
inject.setStaticField(Ofy.class, "clock", clock);
createTld("xn--q9jyb4c");
persistResource(Registry.get("xn--q9jyb4c").asBuilder().setDnsWriter("mock").build());
DomainResource domain1 = persistActiveDomain("example.xn--q9jyb4c");
persistActiveSubordinateHost("ns1.example.xn--q9jyb4c", domain1);
persistActiveSubordinateHost("ns2.example.xn--q9jyb4c", domain1);
@ -66,17 +69,13 @@ public class PublishDnsUpdatesActionTest {
clock.advanceOneMilli();
}
private PublishDnsUpdatesAction createAction(String tld) {
private PublishDnsUpdatesAction createAction(String tld) throws Exception {
PublishDnsUpdatesAction action = new PublishDnsUpdatesAction();
action.timeout = Duration.standardSeconds(10);
action.tld = tld;
action.hosts = ImmutableSet.<String>of();
action.domains = ImmutableSet.<String>of();
action.writerProvider = new Provider<DnsWriter>() {
@Override
public DnsWriter get() {
return dnsWriter;
}};
action.dnsWriterProxy = new DnsWriterProxy(ImmutableMap.of("mock", dnsWriter));
return action;
}

View file

@ -675,6 +675,7 @@ class google.registry.model.registry.Registry {
google.registry.model.common.TimedTransitionProperty<org.joda.money.Money, google.registry.model.registry.Registry$BillingCostTransition> eapFeeSchedule;
google.registry.model.common.TimedTransitionProperty<org.joda.money.Money, google.registry.model.registry.Registry$BillingCostTransition> renewBillingCostTransitions;
google.registry.model.registry.Registry$TldType tldType;
java.lang.String dnsWriter;
java.lang.String driveFolderId;
java.lang.String lordnUsername;
java.lang.String pricingEngineClassName;

View file

@ -51,6 +51,7 @@ java_library(
"//third_party/java/truth",
"//java/google/registry/config",
"//java/google/registry/dns:constants",
"//java/google/registry/dns/writer",
"//java/google/registry/flows",
"//java/google/registry/model",
"//java/google/registry/pricing",

View file

@ -53,6 +53,7 @@ import com.googlecode.objectify.VoidWork;
import com.googlecode.objectify.Work;
import com.googlecode.objectify.cmd.Saver;
import google.registry.config.RegistryEnvironment;
import google.registry.dns.writer.VoidDnsWriter;
import google.registry.model.Buildable;
import google.registry.model.EppResource;
import google.registry.model.EppResource.ForeignKeyedEppResource;
@ -234,6 +235,7 @@ public class DatastoreHelper {
// Always set a default premium list. Tests that don't want it can delete it.
.setPremiumList(persistPremiumList(tld, DEFAULT_PREMIUM_LIST_CONTENTS.get()))
.setPremiumPricingEngine(StaticPremiumListPricingEngine.NAME)
.setDnsWriter(VoidDnsWriter.NAME)
.build();
}