Don't throw entity integrity errors for renamed hosts

Old ForeignKeyIndex entities pointing to hosts are soft-deleted when a
host is renamed, and a new ForeignKeyIndex is created that correctly
points at the new host.  This is an expected state of the system, so
don't throw an error when it is observed.
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=122447608
This commit is contained in:
Ben McIlwain 2016-05-16 12:45:49 -07:00 committed by Justine Tunney
parent e38c87238e
commit 707e22f2d2
2 changed files with 63 additions and 20 deletions

View file

@ -22,6 +22,7 @@ import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.util.DateTimeUtils.END_OF_TIME; import static google.registry.util.DateTimeUtils.END_OF_TIME;
import static google.registry.util.DateTimeUtils.START_OF_TIME; import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static google.registry.util.DateTimeUtils.earliestOf; import static google.registry.util.DateTimeUtils.earliestOf;
import static google.registry.util.DateTimeUtils.isAtOrAfter;
import static google.registry.util.DateTimeUtils.isBeforeOrAt; import static google.registry.util.DateTimeUtils.isBeforeOrAt;
import static google.registry.util.FormattingLogger.getLoggerForCallerClass; import static google.registry.util.FormattingLogger.getLoggerForCallerClass;
import static google.registry.util.PipelineUtils.createJobPath; import static google.registry.util.PipelineUtils.createJobPath;
@ -305,11 +306,14 @@ public class VerifyEntityIntegrityAction implements Runnable {
@SuppressWarnings("cast") @SuppressWarnings("cast")
EppResource resource = verifyExistence(fkiKey, fki.getReference()); EppResource resource = verifyExistence(fkiKey, fki.getReference());
if (resource != null) { if (resource != null) {
integrity().check( // TODO(user): Traverse the chain of pointers to old FKIs instead once they are written.
fki.getForeignKey().equals(resource.getForeignKey()), if (isAtOrAfter(fki.getDeletionTime(), resource.getDeletionTime())) {
fkiKey, integrity().check(
Key.create(resource), fki.getForeignKey().equals(resource.getForeignKey()),
"Foreign key index points to EppResource with different foreign key"); fkiKey,
Key.create(resource),
"Foreign key index points to EppResource with different foreign key");
}
} }
if (fki instanceof ForeignKeyDomainIndex) { if (fki instanceof ForeignKeyDomainIndex) {
getContext().incrementCounter("domain foreign key indexes"); getContext().incrementCounter("domain foreign key indexes");
@ -539,7 +543,9 @@ public class VerifyEntityIntegrityAction implements Runnable {
foreignKey, foreignKey,
resourceKind, resourceKind,
"Missing foreign key index for EppResource"); "Missing foreign key index for EppResource");
if (thereCanBeOnlyOne) { // Skip the case where no resources were found because entity exceptions are already thrown in
// the mapper in invalid situations where FKIs point to non-existent entities.
if (thereCanBeOnlyOne && !resources.isEmpty()) {
verifyOnlyOneActiveResource(resources, getOnlyElement(foreignKeyIndexes)); verifyOnlyOneActiveResource(resources, getOnlyElement(foreignKeyIndexes));
} }
} }

View file

@ -24,6 +24,7 @@ import static google.registry.testing.DatastoreHelper.persistActiveHost;
import static google.registry.testing.DatastoreHelper.persistDeletedContact; import static google.registry.testing.DatastoreHelper.persistDeletedContact;
import static google.registry.testing.DatastoreHelper.persistDomainAsDeleted; import static google.registry.testing.DatastoreHelper.persistDomainAsDeleted;
import static google.registry.testing.DatastoreHelper.persistResource; import static google.registry.testing.DatastoreHelper.persistResource;
import static google.registry.testing.DatastoreHelper.persistSimpleResource;
import static org.mockito.Matchers.any; import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
@ -54,6 +55,7 @@ import google.registry.model.index.EppResourceIndex;
import google.registry.model.index.ForeignKeyIndex; import google.registry.model.index.ForeignKeyIndex;
import google.registry.model.index.ForeignKeyIndex.ForeignKeyContactIndex; import google.registry.model.index.ForeignKeyIndex.ForeignKeyContactIndex;
import google.registry.model.index.ForeignKeyIndex.ForeignKeyDomainIndex; import google.registry.model.index.ForeignKeyIndex.ForeignKeyDomainIndex;
import google.registry.model.index.ForeignKeyIndex.ForeignKeyHostIndex;
import google.registry.testing.FakeClock; import google.registry.testing.FakeClock;
import google.registry.testing.FakeResponse; import google.registry.testing.FakeResponse;
import google.registry.testing.FakeSleeper; import google.registry.testing.FakeSleeper;
@ -177,7 +179,7 @@ public class VerifyEntityIntegrityActionTest
deleteResource(EppResourceIndex.create(cooperKey)); deleteResource(EppResourceIndex.create(cooperKey));
runMapreduce(); runMapreduce();
assertIntegrityErrors(IntegrityError.create( assertIntegrityErrors(IntegrityError.create(
Data.NULL_STRING, cooperKey.toString(), "Missing EPP resource index for EPP resource")); Data.NULL_STRING, cooperKey, "Missing EPP resource index for EPP resource"));
} }
@Test @Test
@ -195,12 +197,12 @@ public class VerifyEntityIntegrityActionTest
Ref.create(missingHost2), Ref.create(missingHost2),
Ref.create(missingHost3))) Ref.create(missingHost3)))
.build()); .build());
String source = Key.create(domain).toString(); Key<DomainResource> domainKey = Key.create(domain);
runMapreduce(); runMapreduce();
assertIntegrityErrors( assertIntegrityErrors(
IntegrityError.create(source, missingHost1.toString(), "Target entity does not exist"), IntegrityError.create(domainKey, missingHost1, "Target entity does not exist"),
IntegrityError.create(source, missingHost2.toString(), "Target entity does not exist"), IntegrityError.create(domainKey, missingHost2, "Target entity does not exist"),
IntegrityError.create(source, missingHost3.toString(), "Target entity does not exist")); IntegrityError.create(domainKey, missingHost3, "Target entity does not exist"));
} }
@Test @Test
@ -214,8 +216,8 @@ public class VerifyEntityIntegrityActionTest
runMapreduce(); runMapreduce();
assertIntegrityErrors( assertIntegrityErrors(
IntegrityError.create( IntegrityError.create(
ForeignKeyDomainIndex.createKey(domain2).toString(), ForeignKeyDomainIndex.createKey(domain2),
Key.create(domain1).toString(), Key.create(domain1),
"Found inactive resource deleted more recently than when active resource was created")); "Found inactive resource deleted more recently than when active resource was created"));
} }
@ -226,25 +228,60 @@ public class VerifyEntityIntegrityActionTest
runMapreduce(); runMapreduce();
assertIntegrityErrors( assertIntegrityErrors(
IntegrityError.create( IntegrityError.create(
Key.create(ForeignKeyContactIndex.class, "dupeid").toString(), Key.create(ForeignKeyContactIndex.class, "dupeid"),
Key.create(contact1).toString(), Key.create(contact1),
"Multiple active EppResources with same foreign key"), "Multiple active EppResources with same foreign key"),
IntegrityError.create( IntegrityError.create(
Key.create(ForeignKeyContactIndex.class, "dupeid").toString(), Key.create(ForeignKeyContactIndex.class, "dupeid"),
Key.create(contact2).toString(), Key.create(contact2),
"Multiple active EppResources with same foreign key")); "Multiple active EppResources with same foreign key"));
} }
@Test
public void test_deletedFkiWithOldHostnameIsntAnError() throws Exception {
HostResource hostLosing = persistActiveHost("losing.example.tld");
persistResource(
hostLosing.asBuilder().setFullyQualifiedHostName("gaining.example.tld").build());
persistSimpleResource(
ForeignKeyHostIndex.create(hostLosing, DateTime.parse("2010-01-01T00:00:00Z")));
// The old FKI with a primary key of "losing.example.tld" still exists, and it points to the
// resource which has a hostname of "gaining.example.tld", but this isn't an error because the
// FKI is soft-deleted.
runMapreduce();
verifyZeroInteractions(bigquery);
}
@Test
public void test_fkiWithDifferentHostnameDeletedMoreRecentlyIsAnError() throws Exception {
HostResource hostLosing = persistActiveHost("losing.example.tld");
HostResource hostGaining = persistResource(
hostLosing
.asBuilder()
.setFullyQualifiedHostName("gaining.example.tld")
.setDeletionTime(DateTime.parse("2013-01-01T00:00:00Z"))
.build());
ForeignKeyIndex<?> fki = persistSimpleResource(
ForeignKeyHostIndex.create(hostLosing, DateTime.parse("2014-01-01T00:00:00Z")));
// The old FKI is pointing to the old name but has a deletion time more recent than the deleted
// host, so it should have the same foreign key.
runMapreduce();
assertIntegrityErrors(
IntegrityError.create(
Key.create(fki),
Key.create(hostGaining),
"Foreign key index points to EppResource with different foreign key"));
}
/** Encapsulates the data representing a single integrity error. */ /** Encapsulates the data representing a single integrity error. */
private static class IntegrityError { private static class IntegrityError {
String source; String source;
String target; String target;
String message; String message;
static IntegrityError create(String source, String target, String message) { static IntegrityError create(Object source, Object target, String message) {
IntegrityError instance = new IntegrityError(); IntegrityError instance = new IntegrityError();
instance.source = source; instance.source = source.toString();
instance.target = target; instance.target = target.toString();
instance.message = message; instance.message = message;
return instance; return instance;
} }