Rework delete from epp

This commit is contained in:
matthewswspence 2024-11-26 13:56:45 -06:00
parent 7cf8b8a82e
commit 6891f5c8df
No known key found for this signature in database
GPG key ID: FB458202A7852BA4
3 changed files with 70 additions and 40 deletions

View file

@ -744,7 +744,12 @@ class Domain(TimeStampedModel, DomainHelper):
successTotalNameservers = len(oldNameservers) - deleteCount + addToDomainCount successTotalNameservers = len(oldNameservers) - deleteCount + addToDomainCount
self._delete_hosts_if_not_used(hostsToDelete=deleted_values) try:
self._delete_hosts_if_not_used(hostsToDelete=deleted_values)
except:
# in this case we don't care if there's an error, and it will be logged in the function.
pass
if successTotalNameservers < 2: if successTotalNameservers < 2:
try: try:
self.dns_needed() self.dns_needed()
@ -1032,19 +1037,28 @@ class Domain(TimeStampedModel, DomainHelper):
RegistryErrors will be logged and raised. Additional RegistryErrors will be logged and raised. Additional
error handling should be provided by the caller. error handling should be provided by the caller.
""" """
logger.info("Deleting contacts for %s", self.name)
contacts = self._cache.get("contacts") contacts = self._cache.get("contacts")
for contact in contacts: logger.debug("Contacts to delete for %s inside _delete_contacts -> %s", self.name, contacts)
self._delete_contact(contact) if contacts:
for contact in contacts:
self._delete_contact(contact)
def _delete_subdomains(self): def _delete_subdomains(self):
"""Subdomains of this domain should be deleted from the registry. """Subdomains of this domain should be deleted from the registry.
Subdomains which are used by other domains (eg as a hostname) will Subdomains which are used by other domains (eg as a hostname) will
not be deleted. not be deleted.
Supresses registry error, as registry can disallow delete for various reasons raises:
RegistryError: if any subdomain cannot be deleted
""" """
logger.info("Deleting nameservers for %s", self.name)
nameservers = [n[0] for n in self.nameservers] nameservers = [n[0] for n in self.nameservers]
hostsToDelete = self.createDeleteHostList(nameservers) logger.info("Nameservers found: %s", nameservers)
hostsToDelete, _ = self.createDeleteHostList(nameservers)
logger.debug("HostsToDelete from %s inside _delete_subdomains -> %s", self.name, hostsToDelete)
self._delete_hosts_if_not_used(hostsToDelete) self._delete_hosts_if_not_used(hostsToDelete)
def _delete_domain(self): def _delete_domain(self):
@ -1665,7 +1679,7 @@ class Domain(TimeStampedModel, DomainHelper):
raise e raise e
def _delete_contact(self, contact: PublicContact): def _delete_contact(self, contact: PublicContact):
"""Try to delete a contact. RegistryErrors will be logged. """Try to delete a contact from the registry.
raises: raises:
RegistryError: if the registry is unable to delete the contact RegistryError: if the registry is unable to delete the contact
@ -1790,7 +1804,6 @@ class Domain(TimeStampedModel, DomainHelper):
"""delete the host object in registry, """delete the host object in registry,
will only delete the host object, if it's not being used by another domain will only delete the host object, if it's not being used by another domain
Performs just the DeleteHost epp call Performs just the DeleteHost epp call
Supresses regstry error, as registry can disallow delete for various reasons
Args: Args:
hostsToDelete (list[str])- list of nameserver/host names to remove hostsToDelete (list[str])- list of nameserver/host names to remove
Returns: Returns:
@ -1808,6 +1821,8 @@ class Domain(TimeStampedModel, DomainHelper):
logger.info("Did not remove host %s because it is in use on another domain." % nameserver) logger.info("Did not remove host %s because it is in use on another domain." % nameserver)
else: else:
logger.error("Error _delete_hosts_if_not_used, code was %s error was %s" % (e.code, e)) logger.error("Error _delete_hosts_if_not_used, code was %s error was %s" % (e.code, e))
raise e
def _fix_unknown_state(self, cleaned): def _fix_unknown_state(self, cleaned):
""" """

View file

@ -1279,6 +1279,15 @@ class MockEppLib(TestCase):
hosts=["fake.host.com"], hosts=["fake.host.com"],
) )
infoDomainSharedHost = fakedEppObject(
"sharedHost.gov",
cr_date=make_aware(datetime(2023, 5, 25, 19, 45, 35)),
contacts=[],
hosts=[
"ns1.sharedhost.com",
],
)
infoDomainThreeHosts = fakedEppObject( infoDomainThreeHosts = fakedEppObject(
"my-nameserver.gov", "my-nameserver.gov",
cr_date=make_aware(datetime(2023, 5, 25, 19, 45, 35)), cr_date=make_aware(datetime(2023, 5, 25, 19, 45, 35)),
@ -1496,10 +1505,7 @@ class MockEppLib(TestCase):
case commands.UpdateHost: case commands.UpdateHost:
return self.mockUpdateHostCommands(_request, cleaned) return self.mockUpdateHostCommands(_request, cleaned)
case commands.DeleteHost: case commands.DeleteHost:
return MagicMock( return self.mockDeletHostCommands(_request, cleaned)
res_data=[self.mockDataHostChange],
code=ErrorCode.COMMAND_COMPLETED_SUCCESSFULLY,
)
case commands.CheckDomain: case commands.CheckDomain:
return self.mockCheckDomainCommand(_request, cleaned) return self.mockCheckDomainCommand(_request, cleaned)
case commands.DeleteDomain: case commands.DeleteDomain:
@ -1551,6 +1557,16 @@ class MockEppLib(TestCase):
res_data=[self.mockDataHostChange], res_data=[self.mockDataHostChange],
code=ErrorCode.COMMAND_COMPLETED_SUCCESSFULLY, code=ErrorCode.COMMAND_COMPLETED_SUCCESSFULLY,
) )
def mockDeletHostCommands(self, _request, cleaned):
hosts = getattr(_request, "name", None).hosts
for host in hosts:
if "sharedhost.com" in host:
raise RegistryError(code=ErrorCode.OBJECT_ASSOCIATION_PROHIBITS_OPERATION)
return MagicMock(
res_data=[self.mockDataHostChange],
code=ErrorCode.COMMAND_COMPLETED_SUCCESSFULLY,
)
def mockUpdateDomainCommands(self, _request, cleaned): def mockUpdateDomainCommands(self, _request, cleaned):
if getattr(_request, "name", None) == "dnssec-invalid.gov": if getattr(_request, "name", None) == "dnssec-invalid.gov":
@ -1563,10 +1579,7 @@ class MockEppLib(TestCase):
def mockDeleteDomainCommands(self, _request, cleaned): def mockDeleteDomainCommands(self, _request, cleaned):
if getattr(_request, "name", None) == "failDelete.gov": if getattr(_request, "name", None) == "failDelete.gov":
name = getattr(_request, "name", None) raise RegistryError(code=ErrorCode.OBJECT_ASSOCIATION_PROHIBITS_OPERATION)
fake_nameserver = "ns1.failDelete.gov"
if name in fake_nameserver:
raise RegistryError(code=ErrorCode.OBJECT_ASSOCIATION_PROHIBITS_OPERATION)
return None return None
def mockRenewDomainCommand(self, _request, cleaned): def mockRenewDomainCommand(self, _request, cleaned):
@ -1636,6 +1649,7 @@ class MockEppLib(TestCase):
"subdomainwoip.gov": (self.mockDataInfoDomainSubdomainNoIP, None), "subdomainwoip.gov": (self.mockDataInfoDomainSubdomainNoIP, None),
"ddomain3.gov": (self.InfoDomainWithContacts, None), "ddomain3.gov": (self.InfoDomainWithContacts, None),
"igorville.gov": (self.InfoDomainWithContacts, None), "igorville.gov": (self.InfoDomainWithContacts, None),
"sharingiscaring.gov": (self.infoDomainSharedHost, None),
} }
# Retrieve the corresponding values from the dictionary # Retrieve the corresponding values from the dictionary

View file

@ -2585,6 +2585,7 @@ class TestAnalystDelete(MockEppLib):
self.domain_on_hold, _ = Domain.objects.get_or_create(name="fake-on-hold.gov", state=Domain.State.ON_HOLD) self.domain_on_hold, _ = Domain.objects.get_or_create(name="fake-on-hold.gov", state=Domain.State.ON_HOLD)
def tearDown(self): def tearDown(self):
Host.objects.all().delete()
Domain.objects.all().delete() Domain.objects.all().delete()
super().tearDown() super().tearDown()
@ -2597,39 +2598,39 @@ class TestAnalystDelete(MockEppLib):
The deleted date is set. The deleted date is set.
""" """
with less_console_noise(): # with less_console_noise():
# Put the domain in client hold # Put the domain in client hold
self.domain.place_client_hold() self.domain.place_client_hold()
# Delete it... # Delete it...
self.domain.deletedInEpp() self.domain.deletedInEpp()
self.domain.save() self.domain.save()
self.mockedSendFunction.assert_has_calls( self.mockedSendFunction.assert_has_calls(
[ [
call( call(
commands.DeleteDomain(name="fake.gov"), commands.DeleteDomain(name="fake.gov"),
cleaned=True, cleaned=True,
) )
] ]
) )
# Domain itself should not be deleted # Domain itself should not be deleted
self.assertNotEqual(self.domain, None) self.assertNotEqual(self.domain, None)
# Domain should have the right state # Domain should have the right state
self.assertEqual(self.domain.state, Domain.State.DELETED) self.assertEqual(self.domain.state, Domain.State.DELETED)
# Domain should have a deleted # Domain should have a deleted
self.assertNotEqual(self.domain.deleted, None) self.assertNotEqual(self.domain.deleted, None)
# Cache should be invalidated # Cache should be invalidated
self.assertEqual(self.domain._cache, {}) self.assertEqual(self.domain._cache, {})
def test_deletion_is_unsuccessful(self): def test_deletion_is_unsuccessful(self):
""" """
Scenario: Domain deletion is unsuccessful Scenario: Domain deletion is unsuccessful
When a subdomain exists When a subdomain exists that is in use by another domain
Then a client error is returned of code 2305 Then a client error is returned of code 2305
And `state` is not set to `DELETED` And `state` is not set to `DELETED`
""" """
with less_console_noise(): with less_console_noise():
# Desired domain # Desired domain
domain, _ = Domain.objects.get_or_create(name="failDelete.gov", state=Domain.State.ON_HOLD) domain, _ = Domain.objects.get_or_create(name="sharingiscaring.gov", state=Domain.State.ON_HOLD)
# Put the domain in client hold # Put the domain in client hold
domain.place_client_hold() domain.place_client_hold()
# Delete it # Delete it
@ -2640,7 +2641,7 @@ class TestAnalystDelete(MockEppLib):
self.mockedSendFunction.assert_has_calls( self.mockedSendFunction.assert_has_calls(
[ [
call( call(
commands.DeleteDomain(name="failDelete.gov"), commands.DeleteHost(name=common.HostObjSet(hosts=['ns1.sharedhost.com'])),
cleaned=True, cleaned=True,
) )
] ]