diff --git a/core/src/main/java/google/registry/batch/AsyncTaskEnqueuer.java b/core/src/main/java/google/registry/batch/AsyncTaskEnqueuer.java index 8de7d16c4..65f249e76 100644 --- a/core/src/main/java/google/registry/batch/AsyncTaskEnqueuer.java +++ b/core/src/main/java/google/registry/batch/AsyncTaskEnqueuer.java @@ -31,6 +31,7 @@ import google.registry.model.ImmutableObject; import google.registry.model.eppcommon.Trid; import google.registry.model.host.HostResource; import google.registry.persistence.VKey; +import google.registry.schema.domain.RegistryLock; import google.registry.util.AppEngineServiceUtils; import google.registry.util.Retrier; import javax.inject.Inject; @@ -158,6 +159,26 @@ public final class AsyncTaskEnqueuer { .param(PARAM_REQUESTED_TIME, now.toString())); } + /** + * Enqueues a task to asynchronously re-lock a registry-locked domain after it was unlocked. + * + *

Note: the relockDuration must be present on the lock object. + */ + public void enqueueDomainRelock(RegistryLock lock) { + checkArgument( + lock.getRelockDuration().isPresent(), + "Lock with ID %s not configured for relock", + lock.getRevisionId()); + addTaskToQueueWithRetry( + asyncActionsPushQueue, + TaskOptions.Builder.withUrl(RelockDomainAction.PATH) + .method(Method.POST) + .param( + RelockDomainAction.OLD_UNLOCK_REVISION_ID_PARAM, + String.valueOf(lock.getRevisionId())) + .countdownMillis(lock.getRelockDuration().get().getMillis())); + } + /** * Adds a task to a queue with retrying, to avoid aborting the entire flow over a transient issue * enqueuing a task. diff --git a/core/src/main/java/google/registry/batch/RelockDomainAction.java b/core/src/main/java/google/registry/batch/RelockDomainAction.java index ef27f68f2..cf7319ea4 100644 --- a/core/src/main/java/google/registry/batch/RelockDomainAction.java +++ b/core/src/main/java/google/registry/batch/RelockDomainAction.java @@ -50,6 +50,7 @@ import javax.inject.Inject; public class RelockDomainAction implements Runnable { public static final String PATH = "/_dr/task/relockDomain"; + public static final String OLD_UNLOCK_REVISION_ID_PARAM = "oldUnlockRevisionId"; private static final FluentLogger logger = FluentLogger.forEnclosingClass(); @@ -59,7 +60,7 @@ public class RelockDomainAction implements Runnable { @Inject public RelockDomainAction( - @Parameter("oldUnlockRevisionId") long oldUnlockRevisionId, + @Parameter(OLD_UNLOCK_REVISION_ID_PARAM) long oldUnlockRevisionId, DomainLockUtils domainLockUtils, Response response) { this.oldUnlockRevisionId = oldUnlockRevisionId; diff --git a/core/src/main/java/google/registry/module/frontend/FrontendRequestComponent.java b/core/src/main/java/google/registry/module/frontend/FrontendRequestComponent.java index 9dd327526..b812a7fbe 100644 --- a/core/src/main/java/google/registry/module/frontend/FrontendRequestComponent.java +++ b/core/src/main/java/google/registry/module/frontend/FrontendRequestComponent.java @@ -16,6 +16,7 @@ package google.registry.module.frontend; import dagger.Module; import dagger.Subcomponent; +import google.registry.batch.BatchModule; import google.registry.dns.DnsModule; import google.registry.flows.EppTlsAction; import google.registry.flows.FlowComponent; @@ -38,6 +39,7 @@ import google.registry.ui.server.registrar.RegistryLockVerifyAction; @RequestScope @Subcomponent( modules = { + BatchModule.class, DnsModule.class, EppTlsModule.class, RegistrarConsoleModule.class, diff --git a/core/src/main/java/google/registry/tools/DomainLockUtils.java b/core/src/main/java/google/registry/tools/DomainLockUtils.java index cd47f10c0..ee0bf4c4f 100644 --- a/core/src/main/java/google/registry/tools/DomainLockUtils.java +++ b/core/src/main/java/google/registry/tools/DomainLockUtils.java @@ -24,6 +24,7 @@ import static google.registry.tools.LockOrUnlockDomainCommand.REGISTRY_LOCK_STAT import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; import com.googlecode.objectify.Key; +import google.registry.batch.AsyncTaskEnqueuer; import google.registry.model.billing.BillingEvent; import google.registry.model.billing.BillingEvent.Reason; import google.registry.model.domain.DomainBase; @@ -51,10 +52,14 @@ public final class DomainLockUtils { private static final int VERIFICATION_CODE_LENGTH = 32; private final StringGenerator stringGenerator; + private final AsyncTaskEnqueuer asyncTaskEnqueuer; @Inject - public DomainLockUtils(@Named("base58StringGenerator") StringGenerator stringGenerator) { + public DomainLockUtils( + @Named("base58StringGenerator") StringGenerator stringGenerator, + AsyncTaskEnqueuer asyncTaskEnqueuer) { this.stringGenerator = stringGenerator; + this.asyncTaskEnqueuer = asyncTaskEnqueuer; } /** @@ -115,35 +120,41 @@ public final class DomainLockUtils { /** Verifies and applies the unlock request previously requested by a user. */ public RegistryLock verifyAndApplyUnlock(String verificationCode, boolean isAdmin) { - return jpaTm() - .transact( - () -> { - DateTime now = jpaTm().getTransactionTime(); - RegistryLock lock = getByVerificationCode(verificationCode); - checkArgument( - !lock.getUnlockCompletionTimestamp().isPresent(), - "Domain %s is already unlocked", - lock.getDomainName()); + RegistryLock lock = + jpaTm() + .transact( + () -> { + DateTime now = jpaTm().getTransactionTime(); + RegistryLock previousLock = getByVerificationCode(verificationCode); + checkArgument( + !previousLock.getUnlockCompletionTimestamp().isPresent(), + "Domain %s is already unlocked", + previousLock.getDomainName()); - checkArgument( - !lock.isUnlockRequestExpired(now), - "The pending unlock has expired; please try again"); + checkArgument( + !previousLock.isUnlockRequestExpired(now), + "The pending unlock has expired; please try again"); - checkArgument( - isAdmin || !lock.isSuperuser(), "Non-admin user cannot complete admin unlock"); + checkArgument( + isAdmin || !previousLock.isSuperuser(), + "Non-admin user cannot complete admin unlock"); - RegistryLock newLock = - RegistryLockDao.save(lock.asBuilder().setUnlockCompletionTimestamp(now).build()); - tm().transact(() -> removeLockStatuses(newLock, isAdmin, now)); - return newLock; - }); + RegistryLock newLock = + RegistryLockDao.save( + previousLock.asBuilder().setUnlockCompletionTimestamp(now).build()); + tm().transact(() -> removeLockStatuses(newLock, isAdmin, now)); + return newLock; + }); + // Submit relock outside of the transaction to make sure that it fully succeeded + submitRelockIfNecessary(lock); + return lock; } /** - * Creates and applies a lock in one step -- this should only be used for admin actions, e.g. - * Nomulus tool commands or relocks. + * Creates and applies a lock in one step. * - *

Note: in the case of relocks, isAdmin is determined by the previous lock. + *

This should only be used for admin actions, e.g. Nomulus tool commands or relocks. + * Note: in the case of relocks, isAdmin is determined by the previous lock. */ public RegistryLock administrativelyApplyLock( String domainName, String registrarId, @Nullable String registrarPocId, boolean isAdmin) { @@ -163,23 +174,34 @@ public final class DomainLockUtils { } /** - * Creates and applies an unlock in one step -- this should only be used for admin actions, e.g. - * Nomulus tool commands. + * Creates and applies an unlock in one step. + * + *

This should only be used for admin actions, e.g. Nomulus tool commands. */ public RegistryLock administrativelyApplyUnlock( String domainName, String registrarId, boolean isAdmin, Optional relockDuration) { - return jpaTm() - .transact( - () -> { - DateTime now = jpaTm().getTransactionTime(); - RegistryLock result = - RegistryLockDao.save( - createUnlockBuilder(domainName, registrarId, isAdmin, relockDuration) - .setUnlockCompletionTimestamp(now) - .build()); - tm().transact(() -> removeLockStatuses(result, isAdmin, now)); - return result; - }); + RegistryLock lock = + jpaTm() + .transact( + () -> { + DateTime now = jpaTm().getTransactionTime(); + RegistryLock result = + RegistryLockDao.save( + createUnlockBuilder(domainName, registrarId, isAdmin, relockDuration) + .setUnlockCompletionTimestamp(now) + .build()); + tm().transact(() -> removeLockStatuses(result, isAdmin, now)); + return result; + }); + // Submit relock outside of the transaction to make sure that it fully succeeded + submitRelockIfNecessary(lock); + return lock; + } + + private void submitRelockIfNecessary(RegistryLock lock) { + if (lock.getRelockDuration().isPresent()) { + asyncTaskEnqueuer.enqueueDomainRelock(lock); + } } private void setAsRelock(RegistryLock newLock) { diff --git a/core/src/main/java/google/registry/tools/RegistryToolComponent.java b/core/src/main/java/google/registry/tools/RegistryToolComponent.java index d855e9cd1..e2b18cfb9 100644 --- a/core/src/main/java/google/registry/tools/RegistryToolComponent.java +++ b/core/src/main/java/google/registry/tools/RegistryToolComponent.java @@ -16,6 +16,7 @@ package google.registry.tools; import dagger.BindsInstance; import dagger.Component; +import google.registry.batch.BatchModule; import google.registry.bigquery.BigqueryModule; import google.registry.config.CredentialModule.LocalCredentialJson; import google.registry.config.RegistryConfig.ConfigModule; @@ -54,6 +55,7 @@ import javax.inject.Singleton; modules = { AppEngineAdminApiModule.class, AuthModule.class, + BatchModule.class, BigqueryModule.class, ConfigModule.class, CloudDnsWriterModule.class, diff --git a/core/src/test/java/google/registry/batch/AsyncTaskEnqueuerTest.java b/core/src/test/java/google/registry/batch/AsyncTaskEnqueuerTest.java index d05d1c3b2..5f5d26c9b 100644 --- a/core/src/test/java/google/registry/batch/AsyncTaskEnqueuerTest.java +++ b/core/src/test/java/google/registry/batch/AsyncTaskEnqueuerTest.java @@ -15,6 +15,7 @@ package google.registry.batch; import static com.google.appengine.api.taskqueue.QueueFactory.getQueue; +import static com.google.common.truth.Truth.assertThat; import static google.registry.batch.AsyncTaskEnqueuer.PARAM_REQUESTED_TIME; import static google.registry.batch.AsyncTaskEnqueuer.PARAM_RESAVE_TIMES; import static google.registry.batch.AsyncTaskEnqueuer.PARAM_RESOURCE_KEY; @@ -23,18 +24,21 @@ import static google.registry.batch.AsyncTaskEnqueuer.QUEUE_ASYNC_ACTIONS; import static google.registry.batch.AsyncTaskEnqueuer.QUEUE_ASYNC_DELETE; import static google.registry.batch.AsyncTaskEnqueuer.QUEUE_ASYNC_HOST_RENAME; import static google.registry.testing.DatastoreHelper.persistActiveContact; +import static google.registry.testing.SqlHelper.saveRegistryLock; import static google.registry.testing.TaskQueueHelper.assertNoTasksEnqueued; import static google.registry.testing.TaskQueueHelper.assertTasksEnqueued; import static google.registry.testing.TestLogHandlerUtils.assertLogMessage; import static org.joda.time.Duration.standardDays; import static org.joda.time.Duration.standardHours; import static org.joda.time.Duration.standardSeconds; +import static org.junit.Assert.assertThrows; import static org.mockito.Mockito.when; import com.google.common.collect.ImmutableSortedSet; import com.google.common.flogger.LoggerConfig; import com.googlecode.objectify.Key; import google.registry.model.contact.ContactResource; +import google.registry.schema.domain.RegistryLock; import google.registry.testing.AppEngineRule; import google.registry.testing.FakeClock; import google.registry.testing.FakeSleeper; @@ -46,6 +50,7 @@ import google.registry.util.CapturingLogHandler; import google.registry.util.Retrier; import java.util.logging.Level; import org.joda.time.DateTime; +import org.joda.time.Duration; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -77,14 +82,18 @@ public class AsyncTaskEnqueuerTest extends ShardableTestCase { public void setUp() { LoggerConfig.getConfig(AsyncTaskEnqueuer.class).addHandler(logHandler); when(appEngineServiceUtils.getServiceHostname("backend")).thenReturn("backend.hostname.fake"); - asyncTaskEnqueuer = - new AsyncTaskEnqueuer( - getQueue(QUEUE_ASYNC_ACTIONS), - getQueue(QUEUE_ASYNC_DELETE), - getQueue(QUEUE_ASYNC_HOST_RENAME), - standardSeconds(90), - appEngineServiceUtils, - new Retrier(new FakeSleeper(clock), 1)); + asyncTaskEnqueuer = createForTesting(appEngineServiceUtils, clock, standardSeconds(90)); + } + + public static AsyncTaskEnqueuer createForTesting( + AppEngineServiceUtils appEngineServiceUtils, FakeClock clock, Duration asyncDeleteDelay) { + return new AsyncTaskEnqueuer( + getQueue(QUEUE_ASYNC_ACTIONS), + getQueue(QUEUE_ASYNC_DELETE), + getQueue(QUEUE_ASYNC_HOST_RENAME), + asyncDeleteDelay, + appEngineServiceUtils, + new Retrier(new FakeSleeper(clock), 1)); } @Test @@ -137,4 +146,56 @@ public class AsyncTaskEnqueuerTest extends ShardableTestCase { assertNoTasksEnqueued(QUEUE_ASYNC_ACTIONS); assertLogMessage(logHandler, Level.INFO, "Ignoring async re-save"); } + + @Test + public void testEnqueueRelock() { + RegistryLock lock = + saveRegistryLock( + new RegistryLock.Builder() + .setLockCompletionTimestamp(clock.nowUtc()) + .setUnlockRequestTimestamp(clock.nowUtc()) + .setUnlockCompletionTimestamp(clock.nowUtc()) + .isSuperuser(false) + .setDomainName("example.tld") + .setRepoId("repoId") + .setRelockDuration(standardHours(6)) + .setRegistrarId("TheRegistrar") + .setRegistrarPocId("someone@example.com") + .setVerificationCode("hi") + .build()); + asyncTaskEnqueuer.enqueueDomainRelock(lock); + assertTasksEnqueued( + QUEUE_ASYNC_ACTIONS, + new TaskMatcher() + .url(RelockDomainAction.PATH) + .method("POST") + .param( + RelockDomainAction.OLD_UNLOCK_REVISION_ID_PARAM, + String.valueOf(lock.getRevisionId())) + .etaDelta( + standardHours(6).minus(standardSeconds(30)), + standardHours(6).plus(standardSeconds(30)))); + } + + @Test + public void testFailure_enqueueRelock_noDuration() { + RegistryLock lockWithoutDuration = + saveRegistryLock( + new RegistryLock.Builder() + .isSuperuser(false) + .setDomainName("example.tld") + .setRepoId("repoId") + .setRegistrarId("TheRegistrar") + .setRegistrarPocId("someone@example.com") + .setVerificationCode("hi") + .build()); + assertThat( + assertThrows( + IllegalArgumentException.class, + () -> asyncTaskEnqueuer.enqueueDomainRelock(lockWithoutDuration))) + .hasMessageThat() + .isEqualTo( + String.format( + "Lock with ID %s not configured for relock", lockWithoutDuration.getRevisionId())); + } } diff --git a/core/src/test/java/google/registry/batch/DeleteContactsAndHostsActionTest.java b/core/src/test/java/google/registry/batch/DeleteContactsAndHostsActionTest.java index 54641cbbd..c244b8ecb 100644 --- a/core/src/test/java/google/registry/batch/DeleteContactsAndHostsActionTest.java +++ b/core/src/test/java/google/registry/batch/DeleteContactsAndHostsActionTest.java @@ -18,9 +18,7 @@ import static com.google.appengine.api.taskqueue.QueueFactory.getQueue; import static com.google.common.collect.MoreCollectors.onlyElement; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth8.assertThat; -import static google.registry.batch.AsyncTaskEnqueuer.QUEUE_ASYNC_ACTIONS; import static google.registry.batch.AsyncTaskEnqueuer.QUEUE_ASYNC_DELETE; -import static google.registry.batch.AsyncTaskEnqueuer.QUEUE_ASYNC_HOST_RENAME; import static google.registry.batch.AsyncTaskMetrics.OperationResult.STALE; import static google.registry.model.EppResourceUtils.loadByForeignKey; import static google.registry.model.eppcommon.StatusValue.PENDING_DELETE; @@ -151,13 +149,8 @@ public class DeleteContactsAndHostsActionTest public void setup() { inject.setStaticField(Ofy.class, "clock", clock); enqueuer = - new AsyncTaskEnqueuer( - getQueue(QUEUE_ASYNC_ACTIONS), - getQueue(QUEUE_ASYNC_DELETE), - getQueue(QUEUE_ASYNC_HOST_RENAME), - Duration.ZERO, - mock(AppEngineServiceUtils.class), - new Retrier(new FakeSleeper(clock), 1)); + AsyncTaskEnqueuerTest.createForTesting( + mock(AppEngineServiceUtils.class), clock, Duration.ZERO); AsyncTaskMetrics asyncTaskMetricsMock = mock(AsyncTaskMetrics.class); action = new DeleteContactsAndHostsAction(); action.asyncTaskMetrics = asyncTaskMetricsMock; diff --git a/core/src/test/java/google/registry/batch/RefreshDnsOnHostRenameActionTest.java b/core/src/test/java/google/registry/batch/RefreshDnsOnHostRenameActionTest.java index 22e90b697..a459813e9 100644 --- a/core/src/test/java/google/registry/batch/RefreshDnsOnHostRenameActionTest.java +++ b/core/src/test/java/google/registry/batch/RefreshDnsOnHostRenameActionTest.java @@ -17,8 +17,6 @@ package google.registry.batch; import static com.google.appengine.api.taskqueue.QueueFactory.getQueue; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth8.assertThat; -import static google.registry.batch.AsyncTaskEnqueuer.QUEUE_ASYNC_ACTIONS; -import static google.registry.batch.AsyncTaskEnqueuer.QUEUE_ASYNC_DELETE; import static google.registry.batch.AsyncTaskEnqueuer.QUEUE_ASYNC_HOST_RENAME; import static google.registry.batch.AsyncTaskMetrics.OperationType.DNS_REFRESH; import static google.registry.model.ofy.ObjectifyService.ofy; @@ -86,13 +84,8 @@ public class RefreshDnsOnHostRenameActionTest public void setup() { createTld("tld"); enqueuer = - new AsyncTaskEnqueuer( - getQueue(QUEUE_ASYNC_ACTIONS), - getQueue(QUEUE_ASYNC_DELETE), - getQueue(QUEUE_ASYNC_HOST_RENAME), - Duration.ZERO, - mock(AppEngineServiceUtils.class), - new Retrier(new FakeSleeper(clock), 1)); + AsyncTaskEnqueuerTest.createForTesting( + mock(AppEngineServiceUtils.class), clock, Duration.ZERO); AsyncTaskMetrics asyncTaskMetricsMock = mock(AsyncTaskMetrics.class); action = new RefreshDnsOnHostRenameAction(); action.asyncTaskMetrics = asyncTaskMetricsMock; diff --git a/core/src/test/java/google/registry/batch/RelockDomainActionTest.java b/core/src/test/java/google/registry/batch/RelockDomainActionTest.java index f8137d69d..1e7feea04 100644 --- a/core/src/test/java/google/registry/batch/RelockDomainActionTest.java +++ b/core/src/test/java/google/registry/batch/RelockDomainActionTest.java @@ -28,6 +28,7 @@ import static google.registry.testing.SqlHelper.getRegistryLockByVerificationCod import static google.registry.testing.SqlHelper.saveRegistryLock; import static google.registry.tools.LockOrUnlockDomainCommand.REGISTRY_LOCK_STATUSES; import static javax.servlet.http.HttpServletResponse.SC_NO_CONTENT; +import static org.mockito.Mockito.mock; import com.google.common.collect.ImmutableSet; import google.registry.model.domain.DomainBase; @@ -39,8 +40,10 @@ import google.registry.testing.FakeClock; import google.registry.testing.FakeResponse; import google.registry.testing.UserInfo; import google.registry.tools.DomainLockUtils; +import google.registry.util.AppEngineServiceUtils; import google.registry.util.StringGenerator.Alphabets; import java.util.Optional; +import org.joda.time.Duration; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -58,7 +61,10 @@ public class RelockDomainActionTest { private final FakeResponse response = new FakeResponse(); private final FakeClock clock = new FakeClock(); private final DomainLockUtils domainLockUtils = - new DomainLockUtils(new DeterministicStringGenerator(Alphabets.BASE_58)); + new DomainLockUtils( + new DeterministicStringGenerator(Alphabets.BASE_58), + AsyncTaskEnqueuerTest.createForTesting( + mock(AppEngineServiceUtils.class), clock, Duration.ZERO)); @Rule public final AppEngineRule appEngineRule = diff --git a/core/src/test/java/google/registry/batch/ResaveEntityActionTest.java b/core/src/test/java/google/registry/batch/ResaveEntityActionTest.java index aae0ff413..4804684f0 100644 --- a/core/src/test/java/google/registry/batch/ResaveEntityActionTest.java +++ b/core/src/test/java/google/registry/batch/ResaveEntityActionTest.java @@ -14,14 +14,11 @@ package google.registry.batch; -import static com.google.appengine.api.taskqueue.QueueFactory.getQueue; import static com.google.common.truth.Truth.assertThat; import static google.registry.batch.AsyncTaskEnqueuer.PARAM_REQUESTED_TIME; import static google.registry.batch.AsyncTaskEnqueuer.PARAM_RESOURCE_KEY; import static google.registry.batch.AsyncTaskEnqueuer.PATH_RESAVE_ENTITY; import static google.registry.batch.AsyncTaskEnqueuer.QUEUE_ASYNC_ACTIONS; -import static google.registry.batch.AsyncTaskEnqueuer.QUEUE_ASYNC_DELETE; -import static google.registry.batch.AsyncTaskEnqueuer.QUEUE_ASYNC_HOST_RENAME; import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.testing.DatastoreHelper.createTld; import static google.registry.testing.DatastoreHelper.newDomainBase; @@ -47,12 +44,10 @@ import google.registry.model.ofy.Ofy; import google.registry.request.Response; import google.registry.testing.AppEngineRule; import google.registry.testing.FakeClock; -import google.registry.testing.FakeSleeper; import google.registry.testing.InjectRule; import google.registry.testing.ShardableTestCase; import google.registry.testing.TaskQueueHelper.TaskMatcher; import google.registry.util.AppEngineServiceUtils; -import google.registry.util.Retrier; import org.joda.time.DateTime; import org.joda.time.Duration; import org.junit.Before; @@ -85,13 +80,7 @@ public class ResaveEntityActionTest extends ShardableTestCase { inject.setStaticField(Ofy.class, "clock", clock); when(appEngineServiceUtils.getServiceHostname("backend")).thenReturn("backend.hostname.fake"); asyncTaskEnqueuer = - new AsyncTaskEnqueuer( - getQueue(QUEUE_ASYNC_ACTIONS), - getQueue(QUEUE_ASYNC_DELETE), - getQueue(QUEUE_ASYNC_HOST_RENAME), - Duration.ZERO, - appEngineServiceUtils, - new Retrier(new FakeSleeper(clock), 1)); + AsyncTaskEnqueuerTest.createForTesting(appEngineServiceUtils, clock, Duration.ZERO); createTld("tld"); } diff --git a/core/src/test/java/google/registry/flows/EppTestComponent.java b/core/src/test/java/google/registry/flows/EppTestComponent.java index ac6b79ac5..80a278265 100644 --- a/core/src/test/java/google/registry/flows/EppTestComponent.java +++ b/core/src/test/java/google/registry/flows/EppTestComponent.java @@ -14,10 +14,7 @@ package google.registry.flows; -import static com.google.appengine.api.taskqueue.QueueFactory.getQueue; -import static google.registry.batch.AsyncTaskEnqueuer.QUEUE_ASYNC_ACTIONS; -import static google.registry.batch.AsyncTaskEnqueuer.QUEUE_ASYNC_DELETE; -import static google.registry.batch.AsyncTaskEnqueuer.QUEUE_ASYNC_HOST_RENAME; +import static org.joda.time.Duration.standardSeconds; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -26,6 +23,7 @@ import dagger.Module; import dagger.Provides; import dagger.Subcomponent; import google.registry.batch.AsyncTaskEnqueuer; +import google.registry.batch.AsyncTaskEnqueuerTest; import google.registry.config.RegistryConfig.ConfigModule; import google.registry.config.RegistryConfig.ConfigModule.TmchCaMode; import google.registry.dns.DnsQueue; @@ -42,10 +40,8 @@ import google.registry.tmch.TmchCertificateAuthority; import google.registry.tmch.TmchXmlSignature; import google.registry.util.AppEngineServiceUtils; import google.registry.util.Clock; -import google.registry.util.Retrier; import google.registry.util.Sleeper; import javax.inject.Singleton; -import org.joda.time.Duration; /** Dagger component for running EPP tests. */ @Singleton @@ -84,20 +80,12 @@ interface EppTestComponent { } public static FakesAndMocksModule create( - FakeClock clock, - EppMetric.Builder eppMetricBuilder, - TmchXmlSignature tmchXmlSignature) { + FakeClock clock, EppMetric.Builder eppMetricBuilder, TmchXmlSignature tmchXmlSignature) { FakesAndMocksModule instance = new FakesAndMocksModule(); AppEngineServiceUtils appEngineServiceUtils = mock(AppEngineServiceUtils.class); when(appEngineServiceUtils.getServiceHostname("backend")).thenReturn("backend.hostname.fake"); instance.asyncTaskEnqueuer = - new AsyncTaskEnqueuer( - getQueue(QUEUE_ASYNC_ACTIONS), - getQueue(QUEUE_ASYNC_DELETE), - getQueue(QUEUE_ASYNC_HOST_RENAME), - Duration.standardSeconds(90), - appEngineServiceUtils, - new Retrier(new FakeSleeper(clock), 1)); + AsyncTaskEnqueuerTest.createForTesting(appEngineServiceUtils, clock, standardSeconds(90)); instance.clock = clock; instance.domainFlowTmchUtils = new DomainFlowTmchUtils(tmchXmlSignature); instance.sleeper = new FakeSleeper(clock); diff --git a/core/src/test/java/google/registry/tools/DomainLockUtilsTest.java b/core/src/test/java/google/registry/tools/DomainLockUtilsTest.java index dfc03722b..3ee5706ae 100644 --- a/core/src/test/java/google/registry/tools/DomainLockUtilsTest.java +++ b/core/src/test/java/google/registry/tools/DomainLockUtilsTest.java @@ -15,6 +15,7 @@ package google.registry.tools; import static com.google.common.truth.Truth.assertThat; +import static google.registry.batch.AsyncTaskEnqueuer.QUEUE_ASYNC_ACTIONS; import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.testing.DatastoreHelper.assertNoBillingEvents; import static google.registry.testing.DatastoreHelper.createTlds; @@ -25,10 +26,17 @@ import static google.registry.testing.DatastoreHelper.persistActiveHost; import static google.registry.testing.DatastoreHelper.persistResource; import static google.registry.testing.SqlHelper.getRegistryLockByRevisionId; import static google.registry.testing.SqlHelper.getRegistryLockByVerificationCode; +import static google.registry.testing.TaskQueueHelper.assertTasksEnqueued; import static google.registry.tools.LockOrUnlockDomainCommand.REGISTRY_LOCK_STATUSES; +import static org.joda.time.Duration.standardDays; +import static org.joda.time.Duration.standardHours; +import static org.joda.time.Duration.standardSeconds; import static org.junit.Assert.assertThrows; +import static org.mockito.Mockito.mock; import com.google.common.collect.ImmutableList; +import google.registry.batch.AsyncTaskEnqueuerTest; +import google.registry.batch.RelockDomainAction; import google.registry.model.billing.BillingEvent; import google.registry.model.billing.BillingEvent.Reason; import google.registry.model.domain.DomainBase; @@ -40,12 +48,15 @@ import google.registry.testing.AppEngineRule; import google.registry.testing.DatastoreHelper; import google.registry.testing.DeterministicStringGenerator; import google.registry.testing.FakeClock; +import google.registry.testing.TaskQueueHelper.TaskMatcher; import google.registry.testing.UserInfo; +import google.registry.util.AppEngineServiceUtils; import google.registry.util.StringGenerator.Alphabets; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; -import org.joda.time.Duration; +import org.joda.time.DateTime; +import org.joda.time.DateTimeZone; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -59,15 +70,19 @@ public final class DomainLockUtilsTest { private static final String DOMAIN_NAME = "example.tld"; private static final String POC_ID = "marla.singer@example.com"; - private final FakeClock clock = new FakeClock(); + private final FakeClock clock = new FakeClock(DateTime.now(DateTimeZone.UTC)); private final DomainLockUtils domainLockUtils = - new DomainLockUtils(new DeterministicStringGenerator(Alphabets.BASE_58)); + new DomainLockUtils( + new DeterministicStringGenerator(Alphabets.BASE_58), + AsyncTaskEnqueuerTest.createForTesting( + mock(AppEngineServiceUtils.class), clock, standardSeconds(90))); @Rule public final AppEngineRule appEngineRule = AppEngineRule.builder() .withDatastoreAndCloudSql() .withClock(clock) + .withTaskQueue() .withUserService(UserInfo.create(POC_ID, "12345")) .build(); @@ -109,7 +124,7 @@ public final class DomainLockUtilsTest { @Test public void testSuccess_createLock_previousLockExpired() { domainLockUtils.saveNewRegistryLockRequest(DOMAIN_NAME, "TheRegistrar", POC_ID, false); - clock.advanceBy(Duration.standardDays(1)); + clock.advanceBy(standardDays(1)); RegistryLock lock = domainLockUtils.saveNewRegistryLockRequest(DOMAIN_NAME, "TheRegistrar", POC_ID, false); domainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), false); @@ -121,7 +136,7 @@ public final class DomainLockUtilsTest { domainLockUtils.administrativelyApplyLock(DOMAIN_NAME, "TheRegistrar", POC_ID, false); domainLockUtils.saveNewRegistryUnlockRequest( DOMAIN_NAME, "TheRegistrar", false, Optional.empty()); - clock.advanceBy(Duration.standardDays(1)); + clock.advanceBy(standardDays(1)); RegistryLock unlockRequest = domainLockUtils.saveNewRegistryUnlockRequest( DOMAIN_NAME, "TheRegistrar", false, Optional.empty()); @@ -232,8 +247,28 @@ public final class DomainLockUtilsTest { domainLockUtils.administrativelyApplyLock(DOMAIN_NAME, "TheRegistrar", POC_ID, false); RegistryLock lock = domainLockUtils.saveNewRegistryUnlockRequest( - DOMAIN_NAME, "TheRegistrar", false, Optional.of(Duration.standardDays(1))); - assertThat(lock.getRelockDuration()).isEqualTo(Optional.of(Duration.standardDays(1))); + DOMAIN_NAME, "TheRegistrar", false, Optional.of(standardDays(1))); + assertThat(lock.getRelockDuration()).isEqualTo(Optional.of(standardDays(1))); + } + + @Test + public void testSuccess_unlock_relockSubmitted() { + domainLockUtils.administrativelyApplyLock(DOMAIN_NAME, "TheRegistrar", POC_ID, false); + RegistryLock lock = + domainLockUtils.saveNewRegistryUnlockRequest( + DOMAIN_NAME, "TheRegistrar", false, Optional.of(standardHours(6))); + domainLockUtils.verifyAndApplyUnlock(lock.getVerificationCode(), false); + assertTasksEnqueued( + QUEUE_ASYNC_ACTIONS, + new TaskMatcher() + .url(RelockDomainAction.PATH) + .method("POST") + .param( + RelockDomainAction.OLD_UNLOCK_REVISION_ID_PARAM, + String.valueOf(lock.getRevisionId())) + .etaDelta( + standardHours(6).minus(standardSeconds(30)), + standardDays(6).plus(standardSeconds(30)))); } @Test @@ -338,7 +373,7 @@ public final class DomainLockUtilsTest { public void testFailure_applyLock_expired() { RegistryLock lock = domainLockUtils.saveNewRegistryLockRequest(DOMAIN_NAME, "TheRegistrar", POC_ID, false); - clock.advanceBy(Duration.standardDays(1)); + clock.advanceBy(standardDays(1)); assertThat( assertThrows( IllegalArgumentException.class, diff --git a/core/src/test/java/google/registry/tools/LockDomainCommandTest.java b/core/src/test/java/google/registry/tools/LockDomainCommandTest.java index b189fe5af..ea6fb6671 100644 --- a/core/src/test/java/google/registry/tools/LockDomainCommandTest.java +++ b/core/src/test/java/google/registry/tools/LockDomainCommandTest.java @@ -24,15 +24,19 @@ import static google.registry.testing.DatastoreHelper.persistResource; import static google.registry.testing.SqlHelper.getMostRecentRegistryLockByRepoId; import static google.registry.tools.LockOrUnlockDomainCommand.REGISTRY_LOCK_STATUSES; import static org.junit.Assert.assertThrows; +import static org.mockito.Mockito.mock; import com.google.common.collect.ImmutableList; +import google.registry.batch.AsyncTaskEnqueuerTest; import google.registry.model.domain.DomainBase; import google.registry.model.registrar.Registrar.Type; import google.registry.testing.DeterministicStringGenerator; +import google.registry.util.AppEngineServiceUtils; import google.registry.util.StringGenerator.Alphabets; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; +import org.joda.time.Duration; import org.junit.Before; import org.junit.Test; @@ -45,7 +49,10 @@ public class LockDomainCommandTest extends CommandTestCase { createTld("tld"); command.registryAdminClientId = "adminreg"; command.domainLockUtils = - new DomainLockUtils(new DeterministicStringGenerator(Alphabets.BASE_58)); + new DomainLockUtils( + new DeterministicStringGenerator(Alphabets.BASE_58), + AsyncTaskEnqueuerTest.createForTesting( + mock(AppEngineServiceUtils.class), fakeClock, Duration.ZERO)); } @Test diff --git a/core/src/test/java/google/registry/tools/UnlockDomainCommandTest.java b/core/src/test/java/google/registry/tools/UnlockDomainCommandTest.java index 5c91878ea..bfb7a2bdb 100644 --- a/core/src/test/java/google/registry/tools/UnlockDomainCommandTest.java +++ b/core/src/test/java/google/registry/tools/UnlockDomainCommandTest.java @@ -25,17 +25,21 @@ import static google.registry.testing.DatastoreHelper.persistResource; import static google.registry.testing.SqlHelper.getMostRecentRegistryLockByRepoId; import static google.registry.tools.LockOrUnlockDomainCommand.REGISTRY_LOCK_STATUSES; import static org.junit.Assert.assertThrows; +import static org.mockito.Mockito.mock; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; +import google.registry.batch.AsyncTaskEnqueuerTest; import google.registry.model.domain.DomainBase; import google.registry.model.registrar.Registrar.Type; import google.registry.schema.domain.RegistryLock; import google.registry.testing.DeterministicStringGenerator; +import google.registry.util.AppEngineServiceUtils; import google.registry.util.StringGenerator.Alphabets; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; +import org.joda.time.Duration; import org.junit.Before; import org.junit.Test; @@ -48,7 +52,10 @@ public class UnlockDomainCommandTest extends CommandTestCase