mirror of
https://github.com/google/nomulus.git
synced 2025-07-23 03:06:01 +02:00
Add delete expired domains action (#836)
* Add delete expired domains action This will be scheduled to run daily via App Engine cron in a subsequent PR. * Add test
This commit is contained in:
parent
98283a67ac
commit
90db60643e
7 changed files with 401 additions and 13 deletions
|
@ -0,0 +1,193 @@
|
||||||
|
// Copyright 2021 The Nomulus 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.batch;
|
||||||
|
|
||||||
|
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||||
|
import static com.google.common.net.MediaType.PLAIN_TEXT_UTF_8;
|
||||||
|
import static google.registry.flows.FlowUtils.marshalWithLenientRetry;
|
||||||
|
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||||
|
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||||
|
import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||||
|
import static google.registry.util.ResourceUtils.readResourceUtf8;
|
||||||
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
|
import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
|
||||||
|
import static javax.servlet.http.HttpServletResponse.SC_NO_CONTENT;
|
||||||
|
import static javax.servlet.http.HttpServletResponse.SC_OK;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.flogger.FluentLogger;
|
||||||
|
import google.registry.config.RegistryConfig.Config;
|
||||||
|
import google.registry.flows.EppController;
|
||||||
|
import google.registry.flows.EppRequestSource;
|
||||||
|
import google.registry.flows.PasswordOnlyTransportCredentials;
|
||||||
|
import google.registry.flows.StatelessRequestSessionMetadata;
|
||||||
|
import google.registry.model.domain.DomainBase;
|
||||||
|
import google.registry.model.eppcommon.ProtocolDefinition;
|
||||||
|
import google.registry.model.eppoutput.EppOutput;
|
||||||
|
import google.registry.request.Action;
|
||||||
|
import google.registry.request.Action.Method;
|
||||||
|
import google.registry.request.Response;
|
||||||
|
import google.registry.request.auth.Auth;
|
||||||
|
import google.registry.request.lock.LockHandler;
|
||||||
|
import google.registry.util.Clock;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import org.joda.time.DateTime;
|
||||||
|
import org.joda.time.Duration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An action that deletes all non-renewing domains whose expiration dates have now passed.
|
||||||
|
*
|
||||||
|
* <p>The registry runs on an autorenew domain model, so domains don't ever expire naturally; they
|
||||||
|
* are only ever autorenewed. However, in some situations (such as URS) we don't want this to
|
||||||
|
* happen. Thus, the domains are tagged as non-renewing and are deleted by the next daily invocation
|
||||||
|
* of this action once they are past the date at which they were to expire.
|
||||||
|
*
|
||||||
|
* <p>Note that this action works by running a superuser EPP domain delete command, and as a side
|
||||||
|
* effect of when domains are deleted (just past their expiration date), they are invariably in the
|
||||||
|
* autorenew grace period when this happens.
|
||||||
|
*/
|
||||||
|
@Action(
|
||||||
|
service = Action.Service.BACKEND,
|
||||||
|
path = DeleteExpiredDomainsAction.PATH,
|
||||||
|
auth = Auth.AUTH_INTERNAL_OR_ADMIN,
|
||||||
|
method = Method.POST)
|
||||||
|
public class DeleteExpiredDomainsAction implements Runnable {
|
||||||
|
|
||||||
|
public static final String PATH = "/_dr/task/deleteExpiredDomains";
|
||||||
|
private static final String LOCK_NAME = "Delete expired domains";
|
||||||
|
|
||||||
|
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||||
|
private final EppController eppController;
|
||||||
|
private final String registryAdminClientId;
|
||||||
|
private final Clock clock;
|
||||||
|
private final LockHandler lockHandler;
|
||||||
|
private final Response response;
|
||||||
|
private final String deleteXmlTmpl;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
DeleteExpiredDomainsAction(
|
||||||
|
EppController eppController,
|
||||||
|
@Config("registryAdminClientId") String registryAdminClientId,
|
||||||
|
Clock clock,
|
||||||
|
LockHandler lockHandler,
|
||||||
|
Response response) {
|
||||||
|
this.eppController = eppController;
|
||||||
|
this.registryAdminClientId = registryAdminClientId;
|
||||||
|
this.clock = clock;
|
||||||
|
this.lockHandler = lockHandler;
|
||||||
|
this.response = response;
|
||||||
|
this.deleteXmlTmpl =
|
||||||
|
readResourceUtf8(DeleteExpiredDomainsAction.class, "delete_expired_domain.xml");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
response.setContentType(PLAIN_TEXT_UTF_8);
|
||||||
|
|
||||||
|
Callable<Void> runner =
|
||||||
|
() -> {
|
||||||
|
try {
|
||||||
|
runLocked();
|
||||||
|
response.setStatus(SC_OK);
|
||||||
|
} catch (Exception e) {
|
||||||
|
response.setStatus(SC_INTERNAL_SERVER_ERROR);
|
||||||
|
response.setPayload("Encountered error; see GCP logs for full details.");
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!lockHandler.executeWithLocks(runner, null, Duration.standardHours(1), LOCK_NAME)) {
|
||||||
|
// Send a 200-series status code to prevent this conflicting action from retrying.
|
||||||
|
response.setStatus(SC_NO_CONTENT);
|
||||||
|
response.setPayload("Could not acquire lock; already running?");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runLocked() {
|
||||||
|
DateTime runTime = clock.nowUtc();
|
||||||
|
logger.atInfo().log(
|
||||||
|
"Deleting non-renewing domains with autorenew end times up through %s.", runTime);
|
||||||
|
|
||||||
|
// Note: This query is (and must be) non-transactional, and thus, is only eventually consistent.
|
||||||
|
ImmutableList<DomainBase> domainsToDelete =
|
||||||
|
ofy().load().type(DomainBase.class).filter("autorenewEndTime <=", runTime).list().stream()
|
||||||
|
// Datastore can't do two inequalities in one query, so the second happens in-memory.
|
||||||
|
.filter(d -> d.getDeletionTime().isEqual(END_OF_TIME))
|
||||||
|
.collect(toImmutableList());
|
||||||
|
if (domainsToDelete.isEmpty()) {
|
||||||
|
logger.atInfo().log("Found 0 domains to delete.");
|
||||||
|
response.setPayload("Found 0 domains to delete.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.atInfo().log(
|
||||||
|
"Found %d domains to delete: %s.",
|
||||||
|
domainsToDelete.size(),
|
||||||
|
String.join(
|
||||||
|
", ",
|
||||||
|
domainsToDelete.stream().map(DomainBase::getDomainName).collect(toImmutableList())));
|
||||||
|
domainsToDelete.forEach(this::runDomainDeleteFlow);
|
||||||
|
logger.atInfo().log("Finished deleting domains.");
|
||||||
|
response.setPayload("Finished deleting domains.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runDomainDeleteFlow(DomainBase domain) {
|
||||||
|
logger.atInfo().log("Attempting to delete domain %s", domain.getDomainName());
|
||||||
|
// Create a new transaction that the flow's execution will be enlisted in that loads the domain
|
||||||
|
// transactionally. This way we can ensure that nothing else has modified the domain in question
|
||||||
|
// in the intervening period since the query above found it.
|
||||||
|
Optional<EppOutput> eppOutput =
|
||||||
|
tm().transact(
|
||||||
|
() -> {
|
||||||
|
DomainBase transDomain = tm().loadByKey(domain.createVKey());
|
||||||
|
if (!domain.getAutorenewEndTime().isPresent()
|
||||||
|
|| domain.getAutorenewEndTime().get().isAfter(tm().getTransactionTime())) {
|
||||||
|
logger.atSevere().log(
|
||||||
|
"Failed to delete domain %s because of its autorenew end time: %s.",
|
||||||
|
transDomain.getDomainName(), transDomain.getAutorenewEndTime());
|
||||||
|
return Optional.empty();
|
||||||
|
} else if (domain.getDeletionTime().isBefore(END_OF_TIME)) {
|
||||||
|
logger.atSevere().log(
|
||||||
|
"Failed to delete domain %s because it was already deleted on %s.",
|
||||||
|
transDomain.getDomainName(), transDomain.getDeletionTime());
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
return Optional.of(
|
||||||
|
eppController.handleEppCommand(
|
||||||
|
new StatelessRequestSessionMetadata(
|
||||||
|
registryAdminClientId,
|
||||||
|
ProtocolDefinition.getVisibleServiceExtensionUris()),
|
||||||
|
new PasswordOnlyTransportCredentials(),
|
||||||
|
EppRequestSource.BACKEND,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
deleteXmlTmpl
|
||||||
|
.replace("%DOMAIN%", transDomain.getDomainName())
|
||||||
|
.getBytes(UTF_8)));
|
||||||
|
});
|
||||||
|
|
||||||
|
if (eppOutput.isPresent()) {
|
||||||
|
if (eppOutput.get().isSuccess()) {
|
||||||
|
logger.atInfo().log("Successfully deleted domain %s", domain.getDomainName());
|
||||||
|
} else {
|
||||||
|
logger.atWarning().log(
|
||||||
|
"Failed to delete domain %s; EPP response:\n\n%s",
|
||||||
|
domain.getDomainName(), new String(marshalWithLenientRetry(eppOutput.get()), UTF_8));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||||
|
<command>
|
||||||
|
<delete>
|
||||||
|
<domain:delete
|
||||||
|
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||||
|
<domain:name>%DOMAIN%</domain:name>
|
||||||
|
</domain:delete>
|
||||||
|
</delete>
|
||||||
|
<extension>
|
||||||
|
<metadata:metadata xmlns:metadata="urn:google:params:xml:ns:metadata-1.0">
|
||||||
|
<metadata:reason>Non-renewing domain has reached expiration date.</metadata:reason>
|
||||||
|
<metadata:requestedByRegistrar>false</metadata:requestedByRegistrar>
|
||||||
|
</metadata:metadata>
|
||||||
|
</extension>
|
||||||
|
<clTRID>ABC-12345</clTRID>
|
||||||
|
</command>
|
||||||
|
</epp>
|
|
@ -25,11 +25,6 @@
|
||||||
<property name="tld" direction="asc"/>
|
<property name="tld" direction="asc"/>
|
||||||
<property name="creationTime" direction="desc"/>
|
<property name="creationTime" direction="desc"/>
|
||||||
</datastore-index>
|
</datastore-index>
|
||||||
<!-- For finding non-autorenewing domains to be deleted. -->
|
|
||||||
<datastore-index kind="DomainBase" ancestor="false" source="manual">
|
|
||||||
<property name="autorenewEndTime" direction="asc"/>
|
|
||||||
<property name="deletionTime" direction="asc"/>
|
|
||||||
</datastore-index>
|
|
||||||
<!-- For finding host resources by registrar. -->
|
<!-- For finding host resources by registrar. -->
|
||||||
<datastore-index kind="HostResource" ancestor="false" source="manual">
|
<datastore-index kind="HostResource" ancestor="false" source="manual">
|
||||||
<property name="currentSponsorClientId" direction="asc"/>
|
<property name="currentSponsorClientId" direction="asc"/>
|
||||||
|
|
|
@ -22,5 +22,6 @@ public enum EppRequestSource {
|
||||||
TLS,
|
TLS,
|
||||||
TOOL,
|
TOOL,
|
||||||
CHECK_API,
|
CHECK_API,
|
||||||
UNIT_TEST
|
UNIT_TEST,
|
||||||
|
BACKEND
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,11 +104,14 @@ public final class ExtensionManager {
|
||||||
clientId, flowClass.getSimpleName(), undeclaredUris);
|
clientId, flowClass.getSimpleName(), undeclaredUris);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final ImmutableSet<EppRequestSource> ALLOWED_METADATA_EPP_REQUEST_SOURCES =
|
||||||
|
ImmutableSet.of(EppRequestSource.TOOL, EppRequestSource.BACKEND);
|
||||||
|
|
||||||
private void checkForRestrictedExtensions(
|
private void checkForRestrictedExtensions(
|
||||||
ImmutableSet<Class<? extends CommandExtension>> suppliedExtensions)
|
ImmutableSet<Class<? extends CommandExtension>> suppliedExtensions)
|
||||||
throws OnlyToolCanPassMetadataException, UnauthorizedForSuperuserExtensionException {
|
throws OnlyToolCanPassMetadataException, UnauthorizedForSuperuserExtensionException {
|
||||||
if (suppliedExtensions.contains(MetadataExtension.class)
|
if (suppliedExtensions.contains(MetadataExtension.class)
|
||||||
&& !eppRequestSource.equals(EppRequestSource.TOOL)) {
|
&& !ALLOWED_METADATA_EPP_REQUEST_SOURCES.contains(eppRequestSource)) {
|
||||||
throw new OnlyToolCanPassMetadataException();
|
throw new OnlyToolCanPassMetadataException();
|
||||||
}
|
}
|
||||||
// Can't use suppliedExtension.contains() here because the SuperuserExtension has child classes.
|
// Can't use suppliedExtension.contains() here because the SuperuserExtension has child classes.
|
||||||
|
|
|
@ -0,0 +1,183 @@
|
||||||
|
// Copyright 2021 The Nomulus 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.batch;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
import static google.registry.model.eppcommon.StatusValue.PENDING_DELETE;
|
||||||
|
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_CREATE;
|
||||||
|
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||||
|
import static google.registry.testing.DatabaseHelper.createTld;
|
||||||
|
import static google.registry.testing.DatabaseHelper.newDomainBase;
|
||||||
|
import static google.registry.testing.DatabaseHelper.persistActiveDomain;
|
||||||
|
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||||
|
import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import google.registry.flows.DaggerEppTestComponent;
|
||||||
|
import google.registry.flows.EppController;
|
||||||
|
import google.registry.flows.EppTestComponent.FakesAndMocksModule;
|
||||||
|
import google.registry.model.billing.BillingEvent;
|
||||||
|
import google.registry.model.billing.BillingEvent.Flag;
|
||||||
|
import google.registry.model.billing.BillingEvent.Reason;
|
||||||
|
import google.registry.model.domain.DomainBase;
|
||||||
|
import google.registry.model.ofy.Ofy;
|
||||||
|
import google.registry.model.poll.PollMessage;
|
||||||
|
import google.registry.model.reporting.HistoryEntry;
|
||||||
|
import google.registry.monitoring.whitebox.EppMetric;
|
||||||
|
import google.registry.testing.AppEngineExtension;
|
||||||
|
import google.registry.testing.FakeClock;
|
||||||
|
import google.registry.testing.FakeLockHandler;
|
||||||
|
import google.registry.testing.FakeResponse;
|
||||||
|
import google.registry.testing.InjectExtension;
|
||||||
|
import java.util.Optional;
|
||||||
|
import org.joda.time.DateTime;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
|
|
||||||
|
/** Unit tests for {@link DeleteExpiredDomainsAction}. */
|
||||||
|
class DeleteExpiredDomainsActionTest {
|
||||||
|
|
||||||
|
@RegisterExtension
|
||||||
|
public final AppEngineExtension appEngine =
|
||||||
|
AppEngineExtension.builder().withDatastoreAndCloudSql().withTaskQueue().build();
|
||||||
|
|
||||||
|
@RegisterExtension public final InjectExtension inject = new InjectExtension();
|
||||||
|
|
||||||
|
private final FakeClock clock = new FakeClock(DateTime.parse("2016-06-13T20:21:22Z"));
|
||||||
|
private final FakeResponse response = new FakeResponse();
|
||||||
|
private DeleteExpiredDomainsAction action;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void beforeEach() {
|
||||||
|
inject.setStaticField(Ofy.class, "clock", clock);
|
||||||
|
createTld("tld");
|
||||||
|
EppController eppController =
|
||||||
|
DaggerEppTestComponent.builder()
|
||||||
|
.fakesAndMocksModule(
|
||||||
|
FakesAndMocksModule.create(clock, EppMetric.builderForRequest(clock)))
|
||||||
|
.build()
|
||||||
|
.startRequest()
|
||||||
|
.eppController();
|
||||||
|
action =
|
||||||
|
new DeleteExpiredDomainsAction(
|
||||||
|
eppController, "NewRegistrar", clock, new FakeLockHandler(true), response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void test_deletesOnlyExpiredDomain() {
|
||||||
|
// A normal, active autorenewing domain that shouldn't be touched.
|
||||||
|
DomainBase activeDomain = persistActiveDomain("foo.tld");
|
||||||
|
clock.advanceOneMilli();
|
||||||
|
|
||||||
|
// A non-autorenewing domain that is already pending delete and shouldn't be touched.
|
||||||
|
DomainBase alreadyDeletedDomain =
|
||||||
|
persistResource(
|
||||||
|
newDomainBase("bar.tld")
|
||||||
|
.asBuilder()
|
||||||
|
.setAutorenewEndTime(Optional.of(clock.nowUtc().minusDays(10)))
|
||||||
|
.setDeletionTime(clock.nowUtc().plusDays(17))
|
||||||
|
.build());
|
||||||
|
clock.advanceOneMilli();
|
||||||
|
|
||||||
|
// A non-autorenewing domain that hasn't reached its expiration time and shouldn't be touched.
|
||||||
|
DomainBase notYetExpiredDomain =
|
||||||
|
persistResource(
|
||||||
|
newDomainBase("baz.tld")
|
||||||
|
.asBuilder()
|
||||||
|
.setAutorenewEndTime(Optional.of(clock.nowUtc().plusDays(15)))
|
||||||
|
.build());
|
||||||
|
clock.advanceOneMilli();
|
||||||
|
|
||||||
|
// A non-autorenewing domain that is past its expiration time and should be deleted.
|
||||||
|
// (This is the only one that needs a full set of subsidiary resources, for the delete flow to
|
||||||
|
// to operate on.)
|
||||||
|
DomainBase pendingExpirationDomain = persistNonAutorenewingDomain("fizz.tld");
|
||||||
|
|
||||||
|
assertThat(tm().loadByEntity(pendingExpirationDomain).getStatusValues())
|
||||||
|
.doesNotContain(PENDING_DELETE);
|
||||||
|
action.run();
|
||||||
|
|
||||||
|
DomainBase reloadedActiveDomain = tm().loadByEntity(activeDomain);
|
||||||
|
assertThat(reloadedActiveDomain).isEqualTo(activeDomain);
|
||||||
|
assertThat(reloadedActiveDomain.getStatusValues()).doesNotContain(PENDING_DELETE);
|
||||||
|
assertThat(tm().loadByEntity(alreadyDeletedDomain)).isEqualTo(alreadyDeletedDomain);
|
||||||
|
assertThat(tm().loadByEntity(notYetExpiredDomain)).isEqualTo(notYetExpiredDomain);
|
||||||
|
DomainBase reloadedExpiredDomain = tm().loadByEntity(pendingExpirationDomain);
|
||||||
|
assertThat(reloadedExpiredDomain.getStatusValues()).contains(PENDING_DELETE);
|
||||||
|
assertThat(reloadedExpiredDomain.getDeletionTime()).isEqualTo(clock.nowUtc().plusDays(35));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void test_deletesThreeDomainsInOneRun() {
|
||||||
|
DomainBase domain1 = persistNonAutorenewingDomain("ecck1.tld");
|
||||||
|
DomainBase domain2 = persistNonAutorenewingDomain("veee2.tld");
|
||||||
|
DomainBase domain3 = persistNonAutorenewingDomain("tarm3.tld");
|
||||||
|
|
||||||
|
action.run();
|
||||||
|
|
||||||
|
assertThat(tm().loadByEntity(domain1).getStatusValues()).contains(PENDING_DELETE);
|
||||||
|
assertThat(tm().loadByEntity(domain2).getStatusValues()).contains(PENDING_DELETE);
|
||||||
|
assertThat(tm().loadByEntity(domain3).getStatusValues()).contains(PENDING_DELETE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DomainBase persistNonAutorenewingDomain(String domainName) {
|
||||||
|
DomainBase pendingExpirationDomain = persistActiveDomain(domainName);
|
||||||
|
HistoryEntry createHistoryEntry =
|
||||||
|
persistResource(
|
||||||
|
new HistoryEntry.Builder()
|
||||||
|
.setType(DOMAIN_CREATE)
|
||||||
|
.setParent(pendingExpirationDomain)
|
||||||
|
.setModificationTime(clock.nowUtc().minusMonths(9))
|
||||||
|
.build());
|
||||||
|
BillingEvent.Recurring autorenewBillingEvent =
|
||||||
|
persistResource(createAutorenewBillingEvent(createHistoryEntry).build());
|
||||||
|
PollMessage.Autorenew autorenewPollMessage =
|
||||||
|
persistResource(createAutorenewPollMessage(createHistoryEntry).build());
|
||||||
|
pendingExpirationDomain =
|
||||||
|
persistResource(
|
||||||
|
pendingExpirationDomain
|
||||||
|
.asBuilder()
|
||||||
|
.setAutorenewEndTime(Optional.of(clock.nowUtc().minusDays(10)))
|
||||||
|
.setAutorenewBillingEvent(autorenewBillingEvent.createVKey())
|
||||||
|
.setAutorenewPollMessage(autorenewPollMessage.createVKey())
|
||||||
|
.build());
|
||||||
|
clock.advanceOneMilli();
|
||||||
|
|
||||||
|
return pendingExpirationDomain;
|
||||||
|
}
|
||||||
|
|
||||||
|
private BillingEvent.Recurring.Builder createAutorenewBillingEvent(
|
||||||
|
HistoryEntry createHistoryEntry) {
|
||||||
|
return new BillingEvent.Recurring.Builder()
|
||||||
|
.setReason(Reason.RENEW)
|
||||||
|
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
|
||||||
|
.setTargetId("fizz.tld")
|
||||||
|
.setClientId("TheRegistrar")
|
||||||
|
.setEventTime(clock.nowUtc().plusYears(1))
|
||||||
|
.setRecurrenceEndTime(END_OF_TIME)
|
||||||
|
.setParent(createHistoryEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
private PollMessage.Autorenew.Builder createAutorenewPollMessage(
|
||||||
|
HistoryEntry createHistoryEntry) {
|
||||||
|
return new PollMessage.Autorenew.Builder()
|
||||||
|
.setTargetId("fizz.tld")
|
||||||
|
.setClientId("TheRegistrar")
|
||||||
|
.setEventTime(clock.nowUtc().plusYears(1))
|
||||||
|
.setAutorenewEndTime(END_OF_TIME)
|
||||||
|
.setParent(createHistoryEntry);
|
||||||
|
}
|
||||||
|
}
|
|
@ -45,12 +45,8 @@ import javax.inject.Singleton;
|
||||||
|
|
||||||
/** Dagger component for running EPP tests. */
|
/** Dagger component for running EPP tests. */
|
||||||
@Singleton
|
@Singleton
|
||||||
@Component(
|
@Component(modules = {ConfigModule.class, EppTestComponent.FakesAndMocksModule.class})
|
||||||
modules = {
|
public interface EppTestComponent {
|
||||||
ConfigModule.class,
|
|
||||||
EppTestComponent.FakesAndMocksModule.class
|
|
||||||
})
|
|
||||||
interface EppTestComponent {
|
|
||||||
|
|
||||||
RequestComponent startRequest();
|
RequestComponent startRequest();
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue