mirror of
https://github.com/google/nomulus.git
synced 2025-05-15 08:57:12 +02:00
Remove non-batch async contact/host deletion actions
They have been superseded by DeleteContactsAndHostsAction. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=134424453
This commit is contained in:
parent
6c610d49fe
commit
1dcc5e5cc6
13 changed files with 2 additions and 1150 deletions
|
@ -219,11 +219,6 @@ public final class ProductionRegistryConfigExample implements RegistryConfig {
|
||||||
return "TheRegistrar";
|
return "TheRegistrar";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Duration getAsyncDeleteFlowMapreduceDelay() {
|
|
||||||
return Duration.standardSeconds(90);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Duration getAsyncFlowFailureBackoff() {
|
public Duration getAsyncFlowFailureBackoff() {
|
||||||
return Duration.standardMinutes(10);
|
return Duration.standardMinutes(10);
|
||||||
|
|
|
@ -211,27 +211,6 @@ public interface RegistryConfig {
|
||||||
*/
|
*/
|
||||||
public String getCheckApiServletRegistrarClientId();
|
public String getCheckApiServletRegistrarClientId();
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the delay before executing async delete flow mapreduces.
|
|
||||||
*
|
|
||||||
* <p>This delay should be sufficiently longer than a transaction, to solve the following problem:
|
|
||||||
* <ul>
|
|
||||||
* <li>a domain mutation flow starts a transaction
|
|
||||||
* <li>the domain flow non-transactionally reads a resource and sees that it's not in
|
|
||||||
* PENDING_DELETE
|
|
||||||
* <li>the domain flow creates a new reference to this resource
|
|
||||||
* <li>a contact/host delete flow runs and marks the resource PENDING_DELETE and commits
|
|
||||||
* <li>the domain flow commits
|
|
||||||
* </ul>
|
|
||||||
*
|
|
||||||
* <p>Although we try not to add references to a PENDING_DELETE resource, strictly speaking that
|
|
||||||
* is ok as long as the mapreduce eventually sees the new reference (and therefore asynchronously
|
|
||||||
* fails the delete). Without this delay, the mapreduce might have started before the domain flow
|
|
||||||
* committed, and could potentially miss the reference.
|
|
||||||
*/
|
|
||||||
// TODO(b/26140521): Remove this configuration option along with non-batched async operations.
|
|
||||||
public Duration getAsyncDeleteFlowMapreduceDelay();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the amount of time to back off following an async flow task failure.
|
* Returns the amount of time to back off following an async flow task failure.
|
||||||
*
|
*
|
||||||
|
|
|
@ -167,11 +167,6 @@ public class TestRegistryConfig implements RegistryConfig {
|
||||||
return "TheRegistrar";
|
return "TheRegistrar";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Duration getAsyncDeleteFlowMapreduceDelay() {
|
|
||||||
return Duration.standardSeconds(90);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Duration getAsyncFlowFailureBackoff() {
|
public Duration getAsyncFlowFailureBackoff() {
|
||||||
return Duration.standardMinutes(10);
|
return Duration.standardMinutes(10);
|
||||||
|
|
|
@ -246,20 +246,6 @@
|
||||||
<url-pattern>/_dr/task/deleteProberData</url-pattern>
|
<url-pattern>/_dr/task/deleteProberData</url-pattern>
|
||||||
</servlet-mapping>
|
</servlet-mapping>
|
||||||
|
|
||||||
<!-- Deletes the specified contact resource if it is not referenced by any domains. -->
|
|
||||||
<!-- TODO(b/26140521): Delete this mapping once non-batched async operations are deleted. -->
|
|
||||||
<servlet-mapping>
|
|
||||||
<servlet-name>backend-servlet</servlet-name>
|
|
||||||
<url-pattern>/_dr/task/deleteContactResource</url-pattern>
|
|
||||||
</servlet-mapping>
|
|
||||||
|
|
||||||
<!-- Deletes the specified host resource if it is not referenced by any domains. -->
|
|
||||||
<!-- TODO(b/26140521): Delete this mapping once non-batched async operations are deleted. -->
|
|
||||||
<servlet-mapping>
|
|
||||||
<servlet-name>backend-servlet</servlet-name>
|
|
||||||
<url-pattern>/_dr/task/deleteHostResource</url-pattern>
|
|
||||||
</servlet-mapping>
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Deletes contacts and hosts enqueued for asynchronous deletion if they are
|
Deletes contacts and hosts enqueued for asynchronous deletion if they are
|
||||||
not referenced by any domain.
|
not referenced by any domain.
|
||||||
|
|
|
@ -14,11 +14,7 @@
|
||||||
|
|
||||||
package google.registry.flows.async;
|
package google.registry.flows.async;
|
||||||
import static google.registry.flows.async.DeleteContactsAndHostsAction.QUEUE_ASYNC_DELETE;
|
import static google.registry.flows.async.DeleteContactsAndHostsAction.QUEUE_ASYNC_DELETE;
|
||||||
import static google.registry.flows.async.DeleteEppResourceAction.PARAM_IS_SUPERUSER;
|
|
||||||
import static google.registry.flows.async.DeleteEppResourceAction.PARAM_REQUESTING_CLIENT_ID;
|
|
||||||
import static google.registry.flows.async.DeleteEppResourceAction.PARAM_RESOURCE_KEY;
|
|
||||||
import static google.registry.flows.async.DnsRefreshForHostRenameAction.PARAM_HOST_KEY;
|
import static google.registry.flows.async.DnsRefreshForHostRenameAction.PARAM_HOST_KEY;
|
||||||
import static google.registry.request.RequestParameters.extractBooleanParameter;
|
|
||||||
import static google.registry.request.RequestParameters.extractRequiredParameter;
|
import static google.registry.request.RequestParameters.extractRequiredParameter;
|
||||||
|
|
||||||
import com.google.appengine.api.taskqueue.Queue;
|
import com.google.appengine.api.taskqueue.Queue;
|
||||||
|
@ -39,27 +35,7 @@ public final class AsyncFlowsModule {
|
||||||
return QueueFactory.getQueue(QUEUE_ASYNC_DELETE);
|
return QueueFactory.getQueue(QUEUE_ASYNC_DELETE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
//TODO(b/26140521): Delete this method once non-batched DNS host refresh mapreduce is deleted.
|
||||||
@Parameter(PARAM_IS_SUPERUSER)
|
|
||||||
//TODO(b/26140521): Delete this method once non-batched async operations are deleted.
|
|
||||||
static boolean provideIsSuperuser(HttpServletRequest req) {
|
|
||||||
return extractBooleanParameter(req, PARAM_IS_SUPERUSER);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Parameter(PARAM_REQUESTING_CLIENT_ID)
|
|
||||||
//TODO(b/26140521): Delete this method once non-batched async operations are deleted.
|
|
||||||
static String provideRequestingClientId(HttpServletRequest req) {
|
|
||||||
return extractRequiredParameter(req, PARAM_REQUESTING_CLIENT_ID);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Parameter(PARAM_RESOURCE_KEY)
|
|
||||||
//TODO(b/26140521): Delete this method once non-batched async operations are deleted.
|
|
||||||
static String provideResourceKey(HttpServletRequest req) {
|
|
||||||
return extractRequiredParameter(req, PARAM_RESOURCE_KEY);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Parameter(PARAM_HOST_KEY)
|
@Parameter(PARAM_HOST_KEY)
|
||||||
static String provideHostKey(HttpServletRequest req) {
|
static String provideHostKey(HttpServletRequest req) {
|
||||||
|
|
|
@ -1,81 +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.flows.async;
|
|
||||||
|
|
||||||
import static google.registry.flows.ResourceFlowUtils.handlePendingTransferOnDelete;
|
|
||||||
|
|
||||||
import com.googlecode.objectify.Key;
|
|
||||||
import google.registry.model.contact.ContactResource;
|
|
||||||
import google.registry.model.domain.DomainBase;
|
|
||||||
import google.registry.model.reporting.HistoryEntry;
|
|
||||||
import google.registry.model.reporting.HistoryEntry.Type;
|
|
||||||
import google.registry.request.Action;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import org.joda.time.DateTime;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A mapreduce to delete the specified ContactResource, but ONLY if it is not referred to by any
|
|
||||||
* existing DomainBase entity.
|
|
||||||
*/
|
|
||||||
@Action(path = "/_dr/task/deleteContactResource")
|
|
||||||
// TODO(b/26140521): Delete this class once non-batched async operations are deleted.
|
|
||||||
public class DeleteContactResourceAction extends DeleteEppResourceAction<ContactResource> {
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public DeleteContactResourceAction() {
|
|
||||||
super(
|
|
||||||
new DeleteContactResourceMapper(),
|
|
||||||
new DeleteContactResourceReducer());
|
|
||||||
}
|
|
||||||
|
|
||||||
/** An async deletion mapper for {@link ContactResource}. */
|
|
||||||
public static class DeleteContactResourceMapper extends DeleteEppResourceMapper<ContactResource> {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = -5904009575877950342L;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean isLinked(
|
|
||||||
DomainBase domain, Key<ContactResource> targetResourceKey) {
|
|
||||||
return domain.getReferencedContacts().contains(targetResourceKey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** An async deletion reducer for {@link ContactResource}. */
|
|
||||||
public static class DeleteContactResourceReducer
|
|
||||||
extends DeleteEppResourceReducer<ContactResource> {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = -7633644054441045215L;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Type getHistoryType(boolean successfulDelete) {
|
|
||||||
return successfulDelete
|
|
||||||
? HistoryEntry.Type.CONTACT_DELETE
|
|
||||||
: HistoryEntry.Type.CONTACT_DELETE_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void performDeleteTasks(
|
|
||||||
ContactResource targetResource,
|
|
||||||
ContactResource deletedResource,
|
|
||||||
DateTime deletionTime,
|
|
||||||
HistoryEntry historyEntryForDelete) {
|
|
||||||
handlePendingTransferOnDelete(
|
|
||||||
targetResource,
|
|
||||||
deletedResource,
|
|
||||||
deletionTime,
|
|
||||||
historyEntryForDelete);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,264 +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.flows.async;
|
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkArgument;
|
|
||||||
import static com.google.common.base.Preconditions.checkState;
|
|
||||||
import static google.registry.flows.ResourceFlowUtils.prepareDeletedResourceAsBuilder;
|
|
||||||
import static google.registry.flows.ResourceFlowUtils.updateForeignKeyIndexDeletionTime;
|
|
||||||
import static google.registry.model.EppResourceUtils.isActive;
|
|
||||||
import static google.registry.model.EppResourceUtils.isDeleted;
|
|
||||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
|
||||||
import static google.registry.util.PipelineUtils.createJobPath;
|
|
||||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
|
||||||
|
|
||||||
import com.google.appengine.tools.mapreduce.Mapper;
|
|
||||||
import com.google.appengine.tools.mapreduce.Reducer;
|
|
||||||
import com.google.appengine.tools.mapreduce.ReducerInput;
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import com.google.common.collect.Iterators;
|
|
||||||
import com.googlecode.objectify.Key;
|
|
||||||
import com.googlecode.objectify.Work;
|
|
||||||
import google.registry.mapreduce.MapreduceRunner;
|
|
||||||
import google.registry.mapreduce.inputs.EppResourceInputs;
|
|
||||||
import google.registry.mapreduce.inputs.NullInput;
|
|
||||||
import google.registry.model.EppResource;
|
|
||||||
import google.registry.model.annotations.ExternalMessagingName;
|
|
||||||
import google.registry.model.domain.DomainBase;
|
|
||||||
import google.registry.model.eppcommon.StatusValue;
|
|
||||||
import google.registry.model.poll.PollMessage;
|
|
||||||
import google.registry.model.reporting.HistoryEntry;
|
|
||||||
import google.registry.request.HttpException.BadRequestException;
|
|
||||||
import google.registry.request.Parameter;
|
|
||||||
import google.registry.request.Response;
|
|
||||||
import google.registry.util.Clock;
|
|
||||||
import google.registry.util.FormattingLogger;
|
|
||||||
import google.registry.util.TypeUtils.TypeInstantiator;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import org.joda.time.DateTime;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A mapreduce to delete the specified EPP resource, but ONLY if it is not referred to by any
|
|
||||||
* existing DomainBase entity.
|
|
||||||
*/
|
|
||||||
// TODO(b/26140521): Delete this class once non-batched async operations are deleted.
|
|
||||||
public abstract class DeleteEppResourceAction<T extends EppResource> implements Runnable {
|
|
||||||
|
|
||||||
/** The HTTP parameter name used to specify the websafe key of the resource to delete. */
|
|
||||||
public static final String PARAM_RESOURCE_KEY = "resourceKey";
|
|
||||||
public static final String PARAM_REQUESTING_CLIENT_ID = "requestingClientId";
|
|
||||||
public static final String PARAM_IS_SUPERUSER = "superuser";
|
|
||||||
|
|
||||||
private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass();
|
|
||||||
|
|
||||||
@Inject @Parameter(PARAM_RESOURCE_KEY) String resourceKeyString;
|
|
||||||
@Inject @Parameter(PARAM_REQUESTING_CLIENT_ID) String requestingClientId;
|
|
||||||
@Inject @Parameter(PARAM_IS_SUPERUSER) boolean isSuperuser;
|
|
||||||
@Inject Clock clock;
|
|
||||||
@Inject MapreduceRunner mrRunner;
|
|
||||||
@Inject Response response;
|
|
||||||
|
|
||||||
DeleteEppResourceMapper<T> mapper;
|
|
||||||
DeleteEppResourceReducer<T> reducer;
|
|
||||||
|
|
||||||
protected DeleteEppResourceAction(
|
|
||||||
DeleteEppResourceMapper<T> mapper,
|
|
||||||
DeleteEppResourceReducer<T> reducer) {
|
|
||||||
this.mapper = mapper;
|
|
||||||
this.reducer = reducer;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
Key<T> resourceKey = null;
|
|
||||||
T resource;
|
|
||||||
try {
|
|
||||||
resourceKey = Key.create(resourceKeyString);
|
|
||||||
resource = checkArgumentNotNull(ofy().load().key(resourceKey).now());
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
throw new BadRequestException(resourceKey == null
|
|
||||||
? "Could not parse key string: " + resourceKeyString
|
|
||||||
: "Could not load resource for key: " + resourceKey);
|
|
||||||
}
|
|
||||||
checkArgument(
|
|
||||||
resource.getClass().equals(new TypeInstantiator<T>(getClass()){}.getExactType()),
|
|
||||||
String.format("Cannot delete a %s via this action.", resource.getClass().getSimpleName()));
|
|
||||||
checkState(
|
|
||||||
!isDeleted(resource, clock.nowUtc()),
|
|
||||||
"Resource %s is already deleted.", resource.getForeignKey());
|
|
||||||
checkState(
|
|
||||||
resource.getStatusValues().contains(StatusValue.PENDING_DELETE),
|
|
||||||
"Resource %s is not set as PENDING_DELETE", resource.getForeignKey());
|
|
||||||
mapper.setTargetResource(resourceKey);
|
|
||||||
reducer.setClient(requestingClientId, isSuperuser);
|
|
||||||
logger.infofmt("Executing Delete EPP resource mapreduce for %s", resourceKey);
|
|
||||||
try {
|
|
||||||
response.sendJavaScriptRedirect(createJobPath(mrRunner
|
|
||||||
.setJobName("Check for EPP resource references and then delete")
|
|
||||||
.setModuleName("backend")
|
|
||||||
.runMapreduce(
|
|
||||||
mapper,
|
|
||||||
reducer,
|
|
||||||
ImmutableList.of(
|
|
||||||
// Add an extra shard that maps over a null domain. See the mapper code for why.
|
|
||||||
new NullInput<DomainBase>(),
|
|
||||||
EppResourceInputs.createEntityInput(DomainBase.class)))));
|
|
||||||
} catch (Throwable t) {
|
|
||||||
logger.severefmt(
|
|
||||||
t, "Error while kicking off DeleteEppResource MR for %s", resource.getForeignKey());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A mapper that iterates over all {@link DomainBase} entities.
|
|
||||||
*
|
|
||||||
* <p>It emits the target key and {@code true} for domains referencing the target resource. For
|
|
||||||
* the special input of {@code null} it emits the target key and {@code false}.
|
|
||||||
*/
|
|
||||||
public abstract static class DeleteEppResourceMapper<T extends EppResource>
|
|
||||||
extends Mapper<DomainBase, Key<T>, Boolean> {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = -7355145176854995813L;
|
|
||||||
|
|
||||||
private DateTime targetResourceUpdateTimestamp;
|
|
||||||
private Key<T> targetEppResourceKey;
|
|
||||||
|
|
||||||
private void setTargetResource(Key<T> targetEppResourceKey) {
|
|
||||||
this.targetEppResourceKey = targetEppResourceKey;
|
|
||||||
this.targetResourceUpdateTimestamp =
|
|
||||||
ofy().load().key(targetEppResourceKey).now().getUpdateAutoTimestamp().getTimestamp();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Determine whether the target resource is a linked resource on the domain. */
|
|
||||||
protected abstract boolean isLinked(DomainBase domain, Key<T> targetResourceKey);
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void map(DomainBase domain) {
|
|
||||||
// The reducer only runs if at least one value is emitted. We add a null input to the
|
|
||||||
// mapreduce and always emit 'false' for it to force the reducer to run. We can then emit
|
|
||||||
// 'true' for linked domains and not emit anything for unlinked domains, which speeds up the
|
|
||||||
// reducer since it will only receive true keys, of which there will be few (usually none).
|
|
||||||
if (domain == null) {
|
|
||||||
emit(targetEppResourceKey, false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (isActive(domain, targetResourceUpdateTimestamp)
|
|
||||||
&& isLinked(domain, targetEppResourceKey)) {
|
|
||||||
emit(targetEppResourceKey, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A reducer that checks if the EPP resource to be deleted is referenced anywhere, and then
|
|
||||||
* deletes it if not and unmarks it for deletion if so.
|
|
||||||
*/
|
|
||||||
public abstract static class DeleteEppResourceReducer<T extends EppResource>
|
|
||||||
extends Reducer<Key<T>, Boolean, Void> {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 875017002097945151L;
|
|
||||||
|
|
||||||
private String requestingClientId;
|
|
||||||
private boolean isSuperuser;
|
|
||||||
|
|
||||||
private void setClient(String requestingClientId, boolean isSuperuser) {
|
|
||||||
this.requestingClientId = requestingClientId;
|
|
||||||
this.isSuperuser = isSuperuser;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine the proper history entry type for the delete operation, as a function of
|
|
||||||
* whether or not the delete was successful.
|
|
||||||
*/
|
|
||||||
protected abstract HistoryEntry.Type getHistoryType(boolean successfulDelete);
|
|
||||||
|
|
||||||
/** Perform any type-specific tasks on the resource to be deleted (and/or its dependencies). */
|
|
||||||
protected abstract void performDeleteTasks(
|
|
||||||
T targetResource,
|
|
||||||
T deletedResource,
|
|
||||||
DateTime deletionTime,
|
|
||||||
HistoryEntry historyEntryForDelete);
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void reduce(final Key<T> key, final ReducerInput<Boolean> values) {
|
|
||||||
final boolean hasNoActiveReferences = !Iterators.contains(values, true);
|
|
||||||
logger.infofmt("Processing delete request for %s", key.toString());
|
|
||||||
String pollMessageText = ofy().transactNew(new Work<String>() {
|
|
||||||
@Override
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public String run() {
|
|
||||||
DateTime now = ofy().getTransactionTime();
|
|
||||||
T targetResource = (T) ofy().load().key(key).now().cloneProjectedAtTime(now);
|
|
||||||
String resourceName = targetResource.getForeignKey();
|
|
||||||
// Double-check that the resource is still active and in PENDING_DELETE within the
|
|
||||||
// transaction.
|
|
||||||
checkState(
|
|
||||||
!isDeleted(targetResource, now),
|
|
||||||
"Resource %s is already deleted.", resourceName);
|
|
||||||
checkState(
|
|
||||||
targetResource.getStatusValues().contains(StatusValue.PENDING_DELETE),
|
|
||||||
"Resource %s is not in PENDING_DELETE.", resourceName);
|
|
||||||
|
|
||||||
targetResource = (T) targetResource.asBuilder()
|
|
||||||
.removeStatusValue(StatusValue.PENDING_DELETE)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
boolean requestedByCurrentOwner =
|
|
||||||
targetResource.getCurrentSponsorClientId().equals(requestingClientId);
|
|
||||||
boolean deleteAllowed = hasNoActiveReferences && (requestedByCurrentOwner || isSuperuser);
|
|
||||||
|
|
||||||
String resourceTypeName =
|
|
||||||
targetResource.getClass().getAnnotation(ExternalMessagingName.class).value();
|
|
||||||
HistoryEntry.Type historyType = getHistoryType(deleteAllowed);
|
|
||||||
|
|
||||||
String pollMessageText = deleteAllowed
|
|
||||||
? String.format("Deleted %s %s.", resourceTypeName, resourceName)
|
|
||||||
: String.format(
|
|
||||||
"Can't delete %s %s because %s.",
|
|
||||||
resourceTypeName,
|
|
||||||
resourceName,
|
|
||||||
requestedByCurrentOwner
|
|
||||||
? "it is referenced by a domain"
|
|
||||||
: "it was transferred prior to deletion");
|
|
||||||
|
|
||||||
HistoryEntry historyEntry = new HistoryEntry.Builder()
|
|
||||||
.setClientId(requestingClientId)
|
|
||||||
.setModificationTime(now)
|
|
||||||
.setType(historyType)
|
|
||||||
.setParent(key)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
PollMessage.OneTime deleteResultMessage = new PollMessage.OneTime.Builder()
|
|
||||||
.setClientId(requestingClientId)
|
|
||||||
.setMsg(pollMessageText)
|
|
||||||
.setParent(historyEntry)
|
|
||||||
.setEventTime(now)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
if (deleteAllowed) {
|
|
||||||
T deletedResource = prepareDeletedResourceAsBuilder(targetResource, now).build();
|
|
||||||
performDeleteTasks(targetResource, deletedResource, now, historyEntry);
|
|
||||||
updateForeignKeyIndexDeletionTime(deletedResource);
|
|
||||||
ofy().save().<Object>entities(deletedResource, historyEntry, deleteResultMessage);
|
|
||||||
} else {
|
|
||||||
ofy().save().<Object>entities(targetResource, historyEntry, deleteResultMessage);
|
|
||||||
}
|
|
||||||
return pollMessageText;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
logger.infofmt(pollMessageText);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,82 +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.flows.async;
|
|
||||||
|
|
||||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
|
||||||
|
|
||||||
import com.googlecode.objectify.Key;
|
|
||||||
import google.registry.dns.DnsQueue;
|
|
||||||
import google.registry.model.domain.DomainBase;
|
|
||||||
import google.registry.model.host.HostResource;
|
|
||||||
import google.registry.model.reporting.HistoryEntry;
|
|
||||||
import google.registry.model.reporting.HistoryEntry.Type;
|
|
||||||
import google.registry.request.Action;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import org.joda.time.DateTime;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A mapreduce to delete the specified HostResource, but ONLY if it is not referred to by any
|
|
||||||
* existing DomainBase entity.
|
|
||||||
*/
|
|
||||||
@Action(path = "/_dr/task/deleteHostResource")
|
|
||||||
// TODO(b/26140521): Delete this class once non-batched async operations are deleted.
|
|
||||||
public class DeleteHostResourceAction extends DeleteEppResourceAction<HostResource> {
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public DeleteHostResourceAction() {
|
|
||||||
super(
|
|
||||||
new DeleteHostResourceMapper(),
|
|
||||||
new DeleteHostResourceReducer());
|
|
||||||
}
|
|
||||||
|
|
||||||
/** An async deletion mapper for {@link HostResource}. */
|
|
||||||
public static class DeleteHostResourceMapper extends DeleteEppResourceMapper<HostResource> {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 1941092742903217194L;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean isLinked(DomainBase domain, Key<HostResource> targetResourceKey) {
|
|
||||||
return domain.getNameservers().contains(targetResourceKey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** An async deletion reducer for {@link HostResource}. */
|
|
||||||
public static class DeleteHostResourceReducer extends DeleteEppResourceReducer<HostResource> {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 555457935288867324L;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Type getHistoryType(boolean successfulDelete) {
|
|
||||||
return successfulDelete
|
|
||||||
? HistoryEntry.Type.HOST_DELETE
|
|
||||||
: HistoryEntry.Type.HOST_DELETE_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void performDeleteTasks(
|
|
||||||
HostResource targetResource,
|
|
||||||
HostResource deletedResource,
|
|
||||||
DateTime deletionTime,
|
|
||||||
HistoryEntry historyEntryForDelete) {
|
|
||||||
if (targetResource.getSuperordinateDomain() != null) {
|
|
||||||
DnsQueue.create().addHostRefreshTask(targetResource.getFullyQualifiedHostName());
|
|
||||||
ofy().save().entity(
|
|
||||||
ofy().load().key(targetResource.getSuperordinateDomain()).now().asBuilder()
|
|
||||||
.removeSubordinateHost(targetResource.getFullyQualifiedHostName())
|
|
||||||
.build());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -39,6 +39,7 @@ import org.joda.time.DateTime;
|
||||||
/**
|
/**
|
||||||
* Enqueues DNS refreshes for applicable domains following a host rename.
|
* Enqueues DNS refreshes for applicable domains following a host rename.
|
||||||
*/
|
*/
|
||||||
|
//TODO(b/26140521): Delete this once non-batched DNS host refresh mapreduce is deleted.
|
||||||
@Action(path = "/_dr/task/dnsRefreshForHostRename")
|
@Action(path = "/_dr/task/dnsRefreshForHostRename")
|
||||||
public class DnsRefreshForHostRenameAction implements Runnable {
|
public class DnsRefreshForHostRenameAction implements Runnable {
|
||||||
|
|
||||||
|
|
|
@ -42,9 +42,7 @@ import google.registry.export.UpdateSnapshotViewAction;
|
||||||
import google.registry.export.sheet.SheetModule;
|
import google.registry.export.sheet.SheetModule;
|
||||||
import google.registry.export.sheet.SyncRegistrarsSheetAction;
|
import google.registry.export.sheet.SyncRegistrarsSheetAction;
|
||||||
import google.registry.flows.async.AsyncFlowsModule;
|
import google.registry.flows.async.AsyncFlowsModule;
|
||||||
import google.registry.flows.async.DeleteContactResourceAction;
|
|
||||||
import google.registry.flows.async.DeleteContactsAndHostsAction;
|
import google.registry.flows.async.DeleteContactsAndHostsAction;
|
||||||
import google.registry.flows.async.DeleteHostResourceAction;
|
|
||||||
import google.registry.flows.async.DnsRefreshForHostRenameAction;
|
import google.registry.flows.async.DnsRefreshForHostRenameAction;
|
||||||
import google.registry.mapreduce.MapreduceModule;
|
import google.registry.mapreduce.MapreduceModule;
|
||||||
import google.registry.monitoring.whitebox.MetricsExportAction;
|
import google.registry.monitoring.whitebox.MetricsExportAction;
|
||||||
|
@ -91,10 +89,7 @@ interface BackendRequestComponent {
|
||||||
BrdaCopyAction brdaCopyAction();
|
BrdaCopyAction brdaCopyAction();
|
||||||
CommitLogCheckpointAction commitLogCheckpointAction();
|
CommitLogCheckpointAction commitLogCheckpointAction();
|
||||||
CommitLogFanoutAction commitLogFanoutAction();
|
CommitLogFanoutAction commitLogFanoutAction();
|
||||||
// TODO(b/26140521): Remove separate contact/host actions here.
|
|
||||||
DeleteContactResourceAction deleteContactResourceAction();
|
|
||||||
DeleteContactsAndHostsAction deleteContactsAndHostsAction();
|
DeleteContactsAndHostsAction deleteContactsAndHostsAction();
|
||||||
DeleteHostResourceAction deleteHostResourceAction();
|
|
||||||
DeleteOldCommitLogsAction deleteOldCommitLogsAction();
|
DeleteOldCommitLogsAction deleteOldCommitLogsAction();
|
||||||
DeleteProberDataAction deleteProberDataAction();
|
DeleteProberDataAction deleteProberDataAction();
|
||||||
DnsRefreshForHostRenameAction dnsRefreshForHostRenameAction();
|
DnsRefreshForHostRenameAction dnsRefreshForHostRenameAction();
|
||||||
|
|
|
@ -1,288 +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.flows.async;
|
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
|
||||||
import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
|
||||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
|
||||||
import static google.registry.testing.ContactResourceSubject.assertAboutContacts;
|
|
||||||
import static google.registry.testing.DatastoreHelper.assertNoBillingEvents;
|
|
||||||
import static google.registry.testing.DatastoreHelper.createTld;
|
|
||||||
import static google.registry.testing.DatastoreHelper.getOnlyHistoryEntryOfType;
|
|
||||||
import static google.registry.testing.DatastoreHelper.getPollMessages;
|
|
||||||
import static google.registry.testing.DatastoreHelper.newContactResource;
|
|
||||||
import static google.registry.testing.DatastoreHelper.persistActiveContact;
|
|
||||||
import static google.registry.testing.DatastoreHelper.persistContactWithPendingTransfer;
|
|
||||||
import static google.registry.testing.DatastoreHelper.persistResource;
|
|
||||||
import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
|
||||||
|
|
||||||
import com.google.common.collect.FluentIterable;
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import com.google.common.collect.Iterables;
|
|
||||||
import com.googlecode.objectify.Key;
|
|
||||||
import google.registry.model.contact.ContactAddress;
|
|
||||||
import google.registry.model.contact.ContactPhoneNumber;
|
|
||||||
import google.registry.model.contact.ContactResource;
|
|
||||||
import google.registry.model.contact.PostalInfo;
|
|
||||||
import google.registry.model.domain.DomainResource;
|
|
||||||
import google.registry.model.eppcommon.StatusValue;
|
|
||||||
import google.registry.model.eppcommon.Trid;
|
|
||||||
import google.registry.model.poll.PendingActionNotificationResponse;
|
|
||||||
import google.registry.model.poll.PollMessage;
|
|
||||||
import google.registry.model.reporting.HistoryEntry;
|
|
||||||
import google.registry.model.transfer.TransferResponse;
|
|
||||||
import google.registry.model.transfer.TransferStatus;
|
|
||||||
import google.registry.request.HttpException.BadRequestException;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.junit.runners.JUnit4;
|
|
||||||
|
|
||||||
/** Unit tests for {@link DeleteContactResourceAction}. */
|
|
||||||
@RunWith(JUnit4.class)
|
|
||||||
public class DeleteContactResourceActionTest
|
|
||||||
extends DeleteEppResourceActionTestCase<DeleteContactResourceAction> {
|
|
||||||
|
|
||||||
ContactResource contactUnused;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setup() throws Exception {
|
|
||||||
setupDeleteEppResourceAction(new DeleteContactResourceAction());
|
|
||||||
contactUnused = persistActiveContact("blah1235");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSuccess_contact_referencedByActiveDomain_doesNotGetDeleted() throws Exception {
|
|
||||||
contactUsed = persistResource(
|
|
||||||
contactUsed.asBuilder()
|
|
||||||
.addStatusValue(StatusValue.PENDING_DELETE)
|
|
||||||
.build());
|
|
||||||
runMapreduceWithKeyParam(Key.create(contactUsed).getString());
|
|
||||||
contactUsed = loadByForeignKey(ContactResource.class, "blah1234", now);
|
|
||||||
assertAboutContacts().that(contactUsed).doesNotHaveStatusValue(StatusValue.PENDING_DELETE)
|
|
||||||
.and().hasDeletionTime(END_OF_TIME);
|
|
||||||
domain = loadByForeignKey(DomainResource.class, "example.tld", now);
|
|
||||||
assertThat(domain.getReferencedContacts()).contains(Key.create(contactUsed));
|
|
||||||
HistoryEntry historyEntry =
|
|
||||||
getOnlyHistoryEntryOfType(contactUsed, HistoryEntry.Type.CONTACT_DELETE_FAILURE);
|
|
||||||
assertPollMessageFor(
|
|
||||||
historyEntry,
|
|
||||||
"TheRegistrar",
|
|
||||||
"Can't delete contact blah1234 because it is referenced by a domain.");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSuccess_contact_notReferenced_getsDeleted() throws Exception {
|
|
||||||
contactUnused = persistResource(
|
|
||||||
contactUnused.asBuilder()
|
|
||||||
.setLocalizedPostalInfo(
|
|
||||||
new PostalInfo.Builder()
|
|
||||||
.setType(PostalInfo.Type.LOCALIZED)
|
|
||||||
.setAddress(new ContactAddress.Builder()
|
|
||||||
.setStreet(ImmutableList.of("123 Grand Ave"))
|
|
||||||
.build())
|
|
||||||
.build())
|
|
||||||
.setInternationalizedPostalInfo(
|
|
||||||
new PostalInfo.Builder()
|
|
||||||
.setType(PostalInfo.Type.INTERNATIONALIZED)
|
|
||||||
.setAddress(new ContactAddress.Builder()
|
|
||||||
.setStreet(ImmutableList.of("123 Avenida Grande"))
|
|
||||||
.build())
|
|
||||||
.build())
|
|
||||||
.setEmailAddress("bob@bob.com")
|
|
||||||
.setVoiceNumber(new ContactPhoneNumber.Builder().setPhoneNumber("555-1212").build())
|
|
||||||
.setFaxNumber(new ContactPhoneNumber.Builder().setPhoneNumber("555-1212").build())
|
|
||||||
.addStatusValue(StatusValue.PENDING_DELETE)
|
|
||||||
.build());
|
|
||||||
assertAboutContacts().that(contactUnused).hasNonNullLocalizedPostalInfo()
|
|
||||||
.and().hasNonNullInternationalizedPostalInfo()
|
|
||||||
.and().hasNonNullEmailAddress()
|
|
||||||
.and().hasNonNullVoiceNumber()
|
|
||||||
.and().hasNonNullFaxNumber();
|
|
||||||
Key<ContactResource> key = Key.create(contactUnused);
|
|
||||||
runMapreduceWithKeyParam(key.getString());
|
|
||||||
assertThat(loadByForeignKey(ContactResource.class, "blah1235", now)).isNull();
|
|
||||||
ContactResource contactAfterDeletion = ofy().load().key(key).now();
|
|
||||||
assertAboutContacts().that(contactAfterDeletion).hasDeletionTime(now)
|
|
||||||
// Note that there will be another history entry of CONTACT_PENDING_DELETE, but this is
|
|
||||||
// added by the flow and not the mapreduce itself.
|
|
||||||
.and().hasOnlyOneHistoryEntryWhich().hasType(HistoryEntry.Type.CONTACT_DELETE);
|
|
||||||
assertAboutContacts().that(contactAfterDeletion).hasNullLocalizedPostalInfo()
|
|
||||||
.and().hasNullInternationalizedPostalInfo()
|
|
||||||
.and().hasNullEmailAddress()
|
|
||||||
.and().hasNullVoiceNumber()
|
|
||||||
.and().hasNullFaxNumber();
|
|
||||||
HistoryEntry historyEntry =
|
|
||||||
getOnlyHistoryEntryOfType(contactAfterDeletion, HistoryEntry.Type.CONTACT_DELETE);
|
|
||||||
assertPollMessageFor(historyEntry, "TheRegistrar", "Deleted contact blah1235.");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSuccess_contactWithPendingTransfer_getsDeleted() throws Exception {
|
|
||||||
ContactResource contact = persistContactWithPendingTransfer(
|
|
||||||
persistActiveContact("sh8013").asBuilder()
|
|
||||||
.addStatusValue(StatusValue.PENDING_DELETE)
|
|
||||||
.build(),
|
|
||||||
transferRequestTime,
|
|
||||||
transferExpirationTime,
|
|
||||||
clock.nowUtc());
|
|
||||||
runMapreduceWithKeyParam(Key.create(contact).getString());
|
|
||||||
// Check that the contact is deleted as of now.
|
|
||||||
assertThat(loadByForeignKey(ContactResource.class, "sh8013", now)).isNull();
|
|
||||||
// Check that it's still there (it wasn't deleted yesterday) and that it has history.
|
|
||||||
assertAboutContacts()
|
|
||||||
.that(loadByForeignKey(ContactResource.class, "sh8013", now.minusDays(1)))
|
|
||||||
.hasOneHistoryEntryEachOfTypes(
|
|
||||||
HistoryEntry.Type.CONTACT_TRANSFER_REQUEST,
|
|
||||||
HistoryEntry.Type.CONTACT_DELETE);
|
|
||||||
assertNoBillingEvents();
|
|
||||||
PollMessage deletePollMessage = Iterables.getOnlyElement(
|
|
||||||
getPollMessages("TheRegistrar", clock.nowUtc().plusMonths(1)));
|
|
||||||
assertThat(deletePollMessage.getMsg()).isEqualTo("Deleted contact sh8013.");
|
|
||||||
// The poll message in the future to the gaining registrar should be gone too, but there
|
|
||||||
// should be one at the current time to the gaining registrar.
|
|
||||||
PollMessage gainingPollMessage = Iterables.getOnlyElement(
|
|
||||||
getPollMessages("NewRegistrar", clock.nowUtc()));
|
|
||||||
System.out.println(gainingPollMessage);
|
|
||||||
assertThat(gainingPollMessage.getEventTime()).isEqualTo(clock.nowUtc());
|
|
||||||
assertThat(
|
|
||||||
Iterables.getOnlyElement(FluentIterable.from(gainingPollMessage.getResponseData())
|
|
||||||
.filter(TransferResponse.class))
|
|
||||||
.getTransferStatus())
|
|
||||||
.isEqualTo(TransferStatus.SERVER_CANCELLED);
|
|
||||||
PendingActionNotificationResponse panData = Iterables.getOnlyElement(FluentIterable
|
|
||||||
.from(gainingPollMessage.getResponseData())
|
|
||||||
.filter(PendingActionNotificationResponse.class));
|
|
||||||
assertThat(panData.getTrid())
|
|
||||||
.isEqualTo(Trid.create("transferClient-trid", "transferServer-trid"));
|
|
||||||
assertThat(panData.getActionResult()).isFalse();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSuccess_contact_referencedByDeleteDomain_getsDeleted() throws Exception {
|
|
||||||
contactUsed = persistResource(
|
|
||||||
contactUsed.asBuilder()
|
|
||||||
.addStatusValue(StatusValue.PENDING_DELETE)
|
|
||||||
.build());
|
|
||||||
domain = persistResource(
|
|
||||||
domain.asBuilder()
|
|
||||||
.setDeletionTime(now.minusDays(3))
|
|
||||||
.build());
|
|
||||||
runMapreduceWithKeyParam(Key.create(contactUsed).getString());
|
|
||||||
assertThat(loadByForeignKey(ContactResource.class, "blah1234", now)).isNull();
|
|
||||||
ContactResource contactBeforeDeletion =
|
|
||||||
loadByForeignKey(ContactResource.class, "blah1234", now.minusDays(1));
|
|
||||||
assertAboutContacts().that(contactBeforeDeletion).hasDeletionTime(now)
|
|
||||||
.and().hasExactlyStatusValues(StatusValue.OK)
|
|
||||||
// Note that there will be another history entry of CONTACT_PENDING_DELETE, but this is
|
|
||||||
// added by the flow and not the mapreduce itself.
|
|
||||||
.and().hasOnlyOneHistoryEntryWhich().hasType(HistoryEntry.Type.CONTACT_DELETE);
|
|
||||||
HistoryEntry historyEntry =
|
|
||||||
getOnlyHistoryEntryOfType(contactBeforeDeletion, HistoryEntry.Type.CONTACT_DELETE);
|
|
||||||
assertPollMessageFor(historyEntry, "TheRegistrar", "Deleted contact blah1234.");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testFailure_notPendingDelete() throws Exception {
|
|
||||||
thrown.expect(IllegalStateException.class, "Resource blah1235 is not set as PENDING_DELETE");
|
|
||||||
runMapreduceWithKeyParam(Key.create(contactUnused).getString());
|
|
||||||
assertThat(
|
|
||||||
loadByForeignKey(ContactResource.class, "blah1235", now)).isEqualTo(contactUnused);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSuccess_notRequestedByOwner_doesNotGetDeleted() throws Exception {
|
|
||||||
contactUnused = persistResource(
|
|
||||||
contactUnused.asBuilder()
|
|
||||||
.addStatusValue(StatusValue.PENDING_DELETE)
|
|
||||||
.build());
|
|
||||||
Key<ContactResource> key = Key.create(contactUnused);
|
|
||||||
runMapreduceWithParams(key.getString(), "OtherRegistrar", false);
|
|
||||||
contactUnused = loadByForeignKey(ContactResource.class, "blah1235", now);
|
|
||||||
assertAboutContacts().that(contactUnused).doesNotHaveStatusValue(StatusValue.PENDING_DELETE)
|
|
||||||
.and().hasDeletionTime(END_OF_TIME);
|
|
||||||
domain = loadByForeignKey(DomainResource.class, "example.tld", now);
|
|
||||||
HistoryEntry historyEntry =
|
|
||||||
getOnlyHistoryEntryOfType(contactUnused, HistoryEntry.Type.CONTACT_DELETE_FAILURE);
|
|
||||||
assertPollMessageFor(
|
|
||||||
historyEntry,
|
|
||||||
"OtherRegistrar",
|
|
||||||
"Can't delete contact blah1235 because it was transferred prior to deletion.");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSuccess_notRequestedByOwner_isSuperuser_getsDeleted() throws Exception {
|
|
||||||
contactUnused = persistResource(
|
|
||||||
contactUnused.asBuilder()
|
|
||||||
.setLocalizedPostalInfo(
|
|
||||||
new PostalInfo.Builder()
|
|
||||||
.setType(PostalInfo.Type.LOCALIZED)
|
|
||||||
.setAddress(new ContactAddress.Builder()
|
|
||||||
.setStreet(ImmutableList.of("123 Grand Ave"))
|
|
||||||
.build())
|
|
||||||
.build())
|
|
||||||
.setInternationalizedPostalInfo(
|
|
||||||
new PostalInfo.Builder()
|
|
||||||
.setType(PostalInfo.Type.INTERNATIONALIZED)
|
|
||||||
.setAddress(new ContactAddress.Builder()
|
|
||||||
.setStreet(ImmutableList.of("123 Avenida Grande"))
|
|
||||||
.build())
|
|
||||||
.build())
|
|
||||||
.setEmailAddress("bob@bob.com")
|
|
||||||
.setVoiceNumber(new ContactPhoneNumber.Builder().setPhoneNumber("555-1212").build())
|
|
||||||
.setFaxNumber(new ContactPhoneNumber.Builder().setPhoneNumber("555-1212").build())
|
|
||||||
.addStatusValue(StatusValue.PENDING_DELETE)
|
|
||||||
.build());
|
|
||||||
Key<ContactResource> key = Key.create(contactUnused);
|
|
||||||
runMapreduceWithParams(key.getString(), "OtherRegistrar", true);
|
|
||||||
assertThat(loadByForeignKey(ContactResource.class, "blah1235", now)).isNull();
|
|
||||||
ContactResource contactAfterDeletion = ofy().load().key(key).now();
|
|
||||||
assertAboutContacts().that(contactAfterDeletion).hasDeletionTime(now)
|
|
||||||
// Note that there will be another history entry of CONTACT_PENDING_DELETE, but this is
|
|
||||||
// added by the flow and not the mapreduce itself.
|
|
||||||
.and().hasOnlyOneHistoryEntryWhich().hasType(HistoryEntry.Type.CONTACT_DELETE);
|
|
||||||
assertAboutContacts().that(contactAfterDeletion).hasNullLocalizedPostalInfo()
|
|
||||||
.and().hasNullInternationalizedPostalInfo()
|
|
||||||
.and().hasNullEmailAddress()
|
|
||||||
.and().hasNullVoiceNumber()
|
|
||||||
.and().hasNullFaxNumber();
|
|
||||||
HistoryEntry historyEntry =
|
|
||||||
getOnlyHistoryEntryOfType(contactAfterDeletion, HistoryEntry.Type.CONTACT_DELETE);
|
|
||||||
assertPollMessageFor(historyEntry, "OtherRegistrar", "Deleted contact blah1235.");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testFailure_targetResourceDoesntExist() throws Exception {
|
|
||||||
createTld("tld");
|
|
||||||
ContactResource notPersisted = newContactResource("somecontact");
|
|
||||||
thrown.expect(
|
|
||||||
BadRequestException.class,
|
|
||||||
"Could not load resource for key: Key<?>(ContactResource(\"7-ROID\"))");
|
|
||||||
runMapreduceWithKeyParam(Key.create(notPersisted).getString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testFailure_contactAlreadyDeleted() throws Exception {
|
|
||||||
ContactResource contactDeleted = persistResource(
|
|
||||||
newContactResource("blah1236").asBuilder()
|
|
||||||
.setCreationTimeForTest(clock.nowUtc().minusDays(2))
|
|
||||||
.setDeletionTime(clock.nowUtc().minusDays(1))
|
|
||||||
.build());
|
|
||||||
thrown.expect(
|
|
||||||
IllegalStateException.class,
|
|
||||||
"Resource blah1236 is already deleted.");
|
|
||||||
runMapreduceWithKeyParam(Key.create(contactDeleted).getString());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,139 +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.flows.async;
|
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
|
||||||
import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
|
||||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
|
||||||
import static google.registry.testing.DatastoreHelper.createTld;
|
|
||||||
import static google.registry.testing.DatastoreHelper.getOnlyPollMessageForHistoryEntry;
|
|
||||||
import static google.registry.testing.DatastoreHelper.newDomainResource;
|
|
||||||
import static google.registry.testing.DatastoreHelper.persistActiveContact;
|
|
||||||
import static google.registry.testing.DatastoreHelper.persistActiveDomain;
|
|
||||||
import static google.registry.testing.DatastoreHelper.persistActiveHost;
|
|
||||||
import static google.registry.testing.DatastoreHelper.persistResource;
|
|
||||||
|
|
||||||
import com.google.common.base.Optional;
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
|
||||||
import com.googlecode.objectify.Key;
|
|
||||||
import google.registry.mapreduce.MapreduceRunner;
|
|
||||||
import google.registry.model.contact.ContactResource;
|
|
||||||
import google.registry.model.domain.DomainResource;
|
|
||||||
import google.registry.model.host.HostResource;
|
|
||||||
import google.registry.model.ofy.Ofy;
|
|
||||||
import google.registry.model.poll.PollMessage;
|
|
||||||
import google.registry.model.poll.PollMessage.OneTime;
|
|
||||||
import google.registry.model.registry.Registry;
|
|
||||||
import google.registry.model.reporting.HistoryEntry;
|
|
||||||
import google.registry.request.HttpException.BadRequestException;
|
|
||||||
import google.registry.testing.ExceptionRule;
|
|
||||||
import google.registry.testing.FakeClock;
|
|
||||||
import google.registry.testing.FakeResponse;
|
|
||||||
import google.registry.testing.InjectRule;
|
|
||||||
import google.registry.testing.mapreduce.MapreduceTestCase;
|
|
||||||
import org.joda.time.DateTime;
|
|
||||||
import org.joda.time.DateTimeZone;
|
|
||||||
import org.junit.Rule;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
/** Unit tests for {@link DeleteEppResourceAction}. */
|
|
||||||
public abstract class DeleteEppResourceActionTestCase<T extends DeleteEppResourceAction<?>>
|
|
||||||
extends MapreduceTestCase<T> {
|
|
||||||
|
|
||||||
@Rule
|
|
||||||
public final ExceptionRule thrown = new ExceptionRule();
|
|
||||||
|
|
||||||
@Rule
|
|
||||||
public final InjectRule inject = new InjectRule();
|
|
||||||
|
|
||||||
DateTime now = DateTime.now(DateTimeZone.UTC);
|
|
||||||
FakeClock clock = new FakeClock(now);
|
|
||||||
final DateTime transferRequestTime = now.minusDays(3);
|
|
||||||
final DateTime transferExpirationTime =
|
|
||||||
transferRequestTime.plus(Registry.DEFAULT_TRANSFER_GRACE_PERIOD);
|
|
||||||
|
|
||||||
ContactResource contactUsed;
|
|
||||||
HostResource hostUsed;
|
|
||||||
DomainResource domain;
|
|
||||||
|
|
||||||
public void setupDeleteEppResourceAction(T deleteEppResourceAction) throws Exception {
|
|
||||||
action = deleteEppResourceAction;
|
|
||||||
action.mrRunner = new MapreduceRunner(Optional.<Integer>of(5), Optional.<Integer>absent());
|
|
||||||
action.response = new FakeResponse();
|
|
||||||
action.clock = clock;
|
|
||||||
inject.setStaticField(Ofy.class, "clock", clock);
|
|
||||||
|
|
||||||
createTld("tld");
|
|
||||||
contactUsed = persistActiveContact("blah1234");
|
|
||||||
hostUsed = persistActiveHost("ns1.example.tld");
|
|
||||||
domain = persistResource(
|
|
||||||
newDomainResource("example.tld", contactUsed).asBuilder()
|
|
||||||
.setNameservers(ImmutableSet.of(Key.create(hostUsed)))
|
|
||||||
.build());
|
|
||||||
}
|
|
||||||
|
|
||||||
void runMapreduce() throws Exception {
|
|
||||||
clock.advanceOneMilli();
|
|
||||||
action.run();
|
|
||||||
executeTasksUntilEmpty("mapreduce");
|
|
||||||
ofy().clearSessionCache();
|
|
||||||
now = clock.nowUtc();
|
|
||||||
}
|
|
||||||
|
|
||||||
void runMapreduceWithParams(
|
|
||||||
String resourceKeyString,
|
|
||||||
String requestingClientId,
|
|
||||||
boolean isSuperuser) throws Exception {
|
|
||||||
action.resourceKeyString = resourceKeyString;
|
|
||||||
action.requestingClientId = requestingClientId;
|
|
||||||
action.isSuperuser = isSuperuser;
|
|
||||||
runMapreduce();
|
|
||||||
}
|
|
||||||
|
|
||||||
void runMapreduceWithKeyParam(String resourceKeyString) throws Exception {
|
|
||||||
runMapreduceWithParams(resourceKeyString, "TheRegistrar", false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper method to check that one poll message exists with a given history entry, resource,
|
|
||||||
* client id, and message.
|
|
||||||
*/
|
|
||||||
void assertPollMessageFor(
|
|
||||||
HistoryEntry historyEntry,
|
|
||||||
String clientId,
|
|
||||||
String msg) {
|
|
||||||
PollMessage.OneTime pollMessage = (OneTime) getOnlyPollMessageForHistoryEntry(historyEntry);
|
|
||||||
assertThat(msg).isEqualTo(pollMessage.getMsg());
|
|
||||||
assertThat(now).isEqualTo(pollMessage.getEventTime());
|
|
||||||
assertThat(clientId).isEqualTo(pollMessage.getClientId());
|
|
||||||
assertThat(pollMessage.getClientId()).isEqualTo(clientId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testFailure_domainKeyPassed() throws Exception {
|
|
||||||
DomainResource domain = persistActiveDomain("fail.tld");
|
|
||||||
thrown.expect(
|
|
||||||
IllegalArgumentException.class, "Cannot delete a DomainResource via this action.");
|
|
||||||
runMapreduceWithKeyParam(Key.create(domain).getString());
|
|
||||||
assertThat(loadByForeignKey(DomainResource.class, "fail.tld", now)).isEqualTo(domain);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testFailure_badKeyPassed() throws Exception {
|
|
||||||
createTld("tld");
|
|
||||||
thrown.expect(BadRequestException.class, "Could not parse key string: a bad key");
|
|
||||||
runMapreduceWithKeyParam("a bad key");
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,221 +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.flows.async;
|
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
|
||||||
import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
|
||||||
import static google.registry.testing.DatastoreHelper.assertNoBillingEvents;
|
|
||||||
import static google.registry.testing.DatastoreHelper.createTld;
|
|
||||||
import static google.registry.testing.DatastoreHelper.getOnlyHistoryEntryOfType;
|
|
||||||
import static google.registry.testing.DatastoreHelper.newDomainResource;
|
|
||||||
import static google.registry.testing.DatastoreHelper.newHostResource;
|
|
||||||
import static google.registry.testing.DatastoreHelper.persistActiveHost;
|
|
||||||
import static google.registry.testing.DatastoreHelper.persistResource;
|
|
||||||
import static google.registry.testing.HostResourceSubject.assertAboutHosts;
|
|
||||||
import static google.registry.testing.TaskQueueHelper.assertDnsTasksEnqueued;
|
|
||||||
import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
|
||||||
import com.googlecode.objectify.Key;
|
|
||||||
import google.registry.model.domain.DomainResource;
|
|
||||||
import google.registry.model.eppcommon.StatusValue;
|
|
||||||
import google.registry.model.host.HostResource;
|
|
||||||
import google.registry.model.reporting.HistoryEntry;
|
|
||||||
import google.registry.request.HttpException.BadRequestException;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.junit.runners.JUnit4;
|
|
||||||
|
|
||||||
/** Unit tests for {@link DeleteHostResourceAction}. */
|
|
||||||
@RunWith(JUnit4.class)
|
|
||||||
public class DeleteHostResourceActionTest
|
|
||||||
extends DeleteEppResourceActionTestCase<DeleteHostResourceAction> {
|
|
||||||
|
|
||||||
HostResource hostUnused;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setup() throws Exception {
|
|
||||||
setupDeleteEppResourceAction(new DeleteHostResourceAction());
|
|
||||||
hostUnused = persistActiveHost("ns2.example.tld");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSuccess_host_referencedByActiveDomain_doesNotGetDeleted() throws Exception {
|
|
||||||
hostUsed = persistResource(
|
|
||||||
hostUsed.asBuilder()
|
|
||||||
.addStatusValue(StatusValue.PENDING_DELETE)
|
|
||||||
.build());
|
|
||||||
runMapreduceWithKeyParam(Key.create(hostUsed).getString());
|
|
||||||
hostUsed = loadByForeignKey(HostResource.class, "ns1.example.tld", now);
|
|
||||||
assertAboutHosts().that(hostUsed).doesNotHaveStatusValue(StatusValue.PENDING_DELETE)
|
|
||||||
.and().hasDeletionTime(END_OF_TIME);
|
|
||||||
domain = loadByForeignKey(DomainResource.class, "example.tld", now);
|
|
||||||
assertThat(domain.getNameservers()).contains(Key.create(hostUsed));
|
|
||||||
HistoryEntry historyEntry =
|
|
||||||
getOnlyHistoryEntryOfType(hostUsed, HistoryEntry.Type.HOST_DELETE_FAILURE);
|
|
||||||
assertPollMessageFor(
|
|
||||||
historyEntry,
|
|
||||||
"TheRegistrar",
|
|
||||||
"Can't delete host ns1.example.tld because it is referenced by a domain.");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSuccess_host_notReferenced_getsDeleted() throws Exception {
|
|
||||||
hostUnused = persistResource(
|
|
||||||
hostUnused.asBuilder()
|
|
||||||
.addStatusValue(StatusValue.PENDING_DELETE)
|
|
||||||
.build());
|
|
||||||
runMapreduceWithKeyParam(Key.create(hostUnused).getString());
|
|
||||||
assertThat(loadByForeignKey(HostResource.class, "ns2.example.tld", now)).isNull();
|
|
||||||
HostResource hostBeforeDeletion =
|
|
||||||
loadByForeignKey(HostResource.class, "ns2.example.tld", now.minusDays(1));
|
|
||||||
assertAboutHosts().that(hostBeforeDeletion).hasDeletionTime(now)
|
|
||||||
.and().hasExactlyStatusValues(StatusValue.OK)
|
|
||||||
// Note that there will be another history entry of HOST_PENDING_DELETE, but this is
|
|
||||||
// added by the flow and not the mapreduce itself.
|
|
||||||
.and().hasOnlyOneHistoryEntryWhich().hasType(HistoryEntry.Type.HOST_DELETE);
|
|
||||||
HistoryEntry historyEntry =
|
|
||||||
getOnlyHistoryEntryOfType(hostBeforeDeletion, HistoryEntry.Type.HOST_DELETE);
|
|
||||||
assertPollMessageFor(historyEntry, "TheRegistrar", "Deleted host ns2.example.tld.");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSuccess_host_referencedByDeletedDomain_getsDeleted() throws Exception {
|
|
||||||
hostUsed = persistResource(
|
|
||||||
hostUsed.asBuilder()
|
|
||||||
.addStatusValue(StatusValue.PENDING_DELETE)
|
|
||||||
.build());
|
|
||||||
domain = persistResource(
|
|
||||||
domain.asBuilder()
|
|
||||||
.setDeletionTime(now.minusDays(3))
|
|
||||||
.build());
|
|
||||||
runMapreduceWithKeyParam(Key.create(hostUsed).getString());
|
|
||||||
assertThat(loadByForeignKey(HostResource.class, "ns1.example.tld", now)).isNull();
|
|
||||||
HostResource hostBeforeDeletion =
|
|
||||||
loadByForeignKey(HostResource.class, "ns1.example.tld", now.minusDays(1));
|
|
||||||
assertAboutHosts().that(hostBeforeDeletion).hasDeletionTime(now)
|
|
||||||
.and().hasExactlyStatusValues(StatusValue.OK)
|
|
||||||
// Note that there will be another history entry of HOST_PENDING_DELETE, but this is
|
|
||||||
// added by the flow and not the mapreduce itself.
|
|
||||||
.and().hasOnlyOneHistoryEntryWhich().hasType(HistoryEntry.Type.HOST_DELETE);
|
|
||||||
HistoryEntry historyEntry =
|
|
||||||
getOnlyHistoryEntryOfType(hostBeforeDeletion, HistoryEntry.Type.HOST_DELETE);
|
|
||||||
assertPollMessageFor(historyEntry, "TheRegistrar", "Deleted host ns1.example.tld.");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSuccess_subordinateHost_getsDeleted() throws Exception {
|
|
||||||
domain = persistResource(
|
|
||||||
newDomainResource("example.tld").asBuilder()
|
|
||||||
.setSubordinateHosts(ImmutableSet.of("ns2.example.tld"))
|
|
||||||
.build());
|
|
||||||
persistResource(
|
|
||||||
hostUnused.asBuilder()
|
|
||||||
.addStatusValue(StatusValue.PENDING_DELETE)
|
|
||||||
.setSuperordinateDomain(Key.create(domain))
|
|
||||||
.build());
|
|
||||||
runMapreduceWithKeyParam(Key.create(hostUnused).getString());
|
|
||||||
// Check that the host is deleted as of now.
|
|
||||||
assertThat(loadByForeignKey(HostResource.class, "ns2.example.tld", clock.nowUtc()))
|
|
||||||
.isNull();
|
|
||||||
assertNoBillingEvents();
|
|
||||||
assertThat(loadByForeignKey(DomainResource.class, "example.tld", clock.nowUtc())
|
|
||||||
.getSubordinateHosts())
|
|
||||||
.isEmpty();
|
|
||||||
assertDnsTasksEnqueued("ns2.example.tld");
|
|
||||||
HostResource hostBeforeDeletion =
|
|
||||||
loadByForeignKey(HostResource.class, "ns2.example.tld", now.minusDays(1));
|
|
||||||
assertAboutHosts().that(hostBeforeDeletion).hasDeletionTime(now)
|
|
||||||
.and().hasExactlyStatusValues(StatusValue.OK)
|
|
||||||
.and().hasOnlyOneHistoryEntryWhich().hasType(HistoryEntry.Type.HOST_DELETE);
|
|
||||||
HistoryEntry historyEntry =
|
|
||||||
getOnlyHistoryEntryOfType(hostBeforeDeletion, HistoryEntry.Type.HOST_DELETE);
|
|
||||||
assertPollMessageFor(historyEntry, "TheRegistrar", "Deleted host ns2.example.tld.");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testFailure_notPendingDelete() throws Exception {
|
|
||||||
thrown.expect(
|
|
||||||
IllegalStateException.class, "Resource ns2.example.tld is not set as PENDING_DELETE");
|
|
||||||
runMapreduceWithKeyParam(Key.create(hostUnused).getString());
|
|
||||||
assertThat(
|
|
||||||
loadByForeignKey(HostResource.class, "ns2.example.tld", now)).isEqualTo(hostUnused);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSuccess_notRequestedByOwner_doesNotGetDeleted() throws Exception {
|
|
||||||
hostUnused = persistResource(
|
|
||||||
hostUnused.asBuilder()
|
|
||||||
.addStatusValue(StatusValue.PENDING_DELETE)
|
|
||||||
.build());
|
|
||||||
Key<HostResource> key = Key.create(hostUnused);
|
|
||||||
runMapreduceWithParams(key.getString(), "OtherRegistrar", false);
|
|
||||||
hostUnused = loadByForeignKey(HostResource.class, "ns2.example.tld", now);
|
|
||||||
assertAboutHosts().that(hostUnused).doesNotHaveStatusValue(StatusValue.PENDING_DELETE)
|
|
||||||
.and().hasDeletionTime(END_OF_TIME);
|
|
||||||
domain = loadByForeignKey(DomainResource.class, "example.tld", now);
|
|
||||||
HistoryEntry historyEntry =
|
|
||||||
getOnlyHistoryEntryOfType(hostUnused, HistoryEntry.Type.HOST_DELETE_FAILURE);
|
|
||||||
assertPollMessageFor(
|
|
||||||
historyEntry,
|
|
||||||
"OtherRegistrar",
|
|
||||||
"Can't delete host ns2.example.tld because it was transferred prior to deletion.");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSuccess_notRequestedByOwner_isSuperuser_getsDeleted() throws Exception {
|
|
||||||
hostUnused = persistResource(
|
|
||||||
hostUnused.asBuilder()
|
|
||||||
.addStatusValue(StatusValue.PENDING_DELETE)
|
|
||||||
.build());
|
|
||||||
Key<HostResource> key = Key.create(hostUnused);
|
|
||||||
runMapreduceWithParams(key.getString(), "OtherRegistrar", true);
|
|
||||||
assertThat(loadByForeignKey(HostResource.class, "ns2.example.tld", now)).isNull();
|
|
||||||
HostResource hostBeforeDeletion =
|
|
||||||
loadByForeignKey(HostResource.class, "ns2.example.tld", now.minusDays(1));
|
|
||||||
assertAboutHosts().that(hostBeforeDeletion).hasDeletionTime(now)
|
|
||||||
.and().hasExactlyStatusValues(StatusValue.OK)
|
|
||||||
// Note that there will be another history entry of HOST_PENDING_DELETE, but this is
|
|
||||||
// added by the flow and not the mapreduce itself.
|
|
||||||
.and().hasOnlyOneHistoryEntryWhich().hasType(HistoryEntry.Type.HOST_DELETE);
|
|
||||||
HistoryEntry historyEntry =
|
|
||||||
getOnlyHistoryEntryOfType(hostBeforeDeletion, HistoryEntry.Type.HOST_DELETE);
|
|
||||||
assertPollMessageFor(historyEntry, "OtherRegistrar", "Deleted host ns2.example.tld.");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testFailure_targetResourceDoesntExist() throws Exception {
|
|
||||||
createTld("tld");
|
|
||||||
HostResource notPersisted = newHostResource("ns1.example.tld");
|
|
||||||
thrown.expect(
|
|
||||||
BadRequestException.class,
|
|
||||||
"Could not load resource for key: Key<?>(HostResource(\"7-ROID\"))");
|
|
||||||
runMapreduceWithKeyParam(Key.create(notPersisted).getString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testFailure_hostAlreadyDeleted() throws Exception {
|
|
||||||
HostResource hostDeleted = persistResource(
|
|
||||||
newHostResource("ns3.example.tld").asBuilder()
|
|
||||||
.setCreationTimeForTest(clock.nowUtc().minusDays(2))
|
|
||||||
.setDeletionTime(clock.nowUtc().minusDays(1))
|
|
||||||
.build());
|
|
||||||
thrown.expect(
|
|
||||||
IllegalStateException.class,
|
|
||||||
"Resource ns3.example.tld is already deleted.");
|
|
||||||
runMapreduceWithKeyParam(Key.create(hostDeleted).getString());
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Add table
Add a link
Reference in a new issue