mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-08-15 05:54:11 +02:00
cleanup
This commit is contained in:
parent
cfcdd8826c
commit
a0d6e1ec33
2 changed files with 31 additions and 35 deletions
|
@ -1847,10 +1847,9 @@ class Domain(TimeStampedModel, DomainHelper):
|
||||||
missingSecurity = True
|
missingSecurity = True
|
||||||
missingTech = True
|
missingTech = True
|
||||||
|
|
||||||
# Potential collision - mismatch between _contacts and contacts?
|
contacts = cleaned.get("_contacts", [])
|
||||||
# But the ID wouldnt match in this case because the default is being grabbed?
|
if len(contacts) < 3:
|
||||||
if len(cleaned.get("_contacts")) < 3:
|
for contact in contacts:
|
||||||
for contact in cleaned.get("_contacts"):
|
|
||||||
if contact.type == PublicContact.ContactTypeChoices.ADMINISTRATIVE:
|
if contact.type == PublicContact.ContactTypeChoices.ADMINISTRATIVE:
|
||||||
missingAdmin = False
|
missingAdmin = False
|
||||||
if contact.type == PublicContact.ContactTypeChoices.SECURITY:
|
if contact.type == PublicContact.ContactTypeChoices.SECURITY:
|
||||||
|
@ -1868,9 +1867,9 @@ class Domain(TimeStampedModel, DomainHelper):
|
||||||
if missingTech:
|
if missingTech:
|
||||||
technical_contact = self.get_default_technical_contact()
|
technical_contact = self.get_default_technical_contact()
|
||||||
technical_contact.save()
|
technical_contact.save()
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
"_add_missing_contacts_if_unknown => In function. Values are "
|
"_add_missing_contacts_if_unknown => Adding contacts. Values are "
|
||||||
f"missingAdmin: {missingAdmin}, missingSecurity: {missingSecurity}, missingTech: {missingTech}"
|
f"missingAdmin: {missingAdmin}, missingSecurity: {missingSecurity}, missingTech: {missingTech}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -2052,7 +2051,7 @@ class Domain(TimeStampedModel, DomainHelper):
|
||||||
if contacts and isinstance(contacts, list) and len(contacts) > 0:
|
if contacts and isinstance(contacts, list) and len(contacts) > 0:
|
||||||
cleaned_contacts = self._fetch_contacts(contacts)
|
cleaned_contacts = self._fetch_contacts(contacts)
|
||||||
return cleaned_contacts
|
return cleaned_contacts
|
||||||
|
|
||||||
def _fetch_contacts(self, contact_data):
|
def _fetch_contacts(self, contact_data):
|
||||||
"""Fetch contact info."""
|
"""Fetch contact info."""
|
||||||
choices = PublicContact.ContactTypeChoices
|
choices = PublicContact.ContactTypeChoices
|
||||||
|
@ -2086,13 +2085,11 @@ class Domain(TimeStampedModel, DomainHelper):
|
||||||
def _get_or_create_public_contact(self, public_contact: PublicContact):
|
def _get_or_create_public_contact(self, public_contact: PublicContact):
|
||||||
"""Tries to find a PublicContact object in our DB.
|
"""Tries to find a PublicContact object in our DB.
|
||||||
If it can't, it'll create it. Returns PublicContact"""
|
If it can't, it'll create it. Returns PublicContact"""
|
||||||
logger.info(f"in function")
|
|
||||||
db_contact = PublicContact.objects.filter(
|
db_contact = PublicContact.objects.filter(
|
||||||
registry_id=public_contact.registry_id,
|
registry_id=public_contact.registry_id,
|
||||||
contact_type=public_contact.contact_type,
|
contact_type=public_contact.contact_type,
|
||||||
domain=self,
|
domain=self,
|
||||||
)
|
)
|
||||||
logger.info(f"db_contact {db_contact}")
|
|
||||||
|
|
||||||
# If we find duplicates, log it and delete the oldest ones.
|
# If we find duplicates, log it and delete the oldest ones.
|
||||||
if db_contact.count() > 1:
|
if db_contact.count() > 1:
|
||||||
|
@ -2119,13 +2116,10 @@ class Domain(TimeStampedModel, DomainHelper):
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
public_contact.save(skip_epp_save=True)
|
public_contact.save(skip_epp_save=True)
|
||||||
logger.info(f"Created a new PublicContact: {public_contact}")
|
logger.info(f"Created a new PublicContact: {public_contact}")
|
||||||
# In rare cases, _add_missing_contacts_if_unknown will cause a race condition with this function.
|
|
||||||
# This is because it calls .save(), which is called here.
|
|
||||||
#
|
|
||||||
except IntegrityError as err:
|
except IntegrityError as err:
|
||||||
logger.error(
|
logger.error(
|
||||||
"_get_or_create_public_contact() => tried to create a duplicate public contact: "
|
"_get_or_create_public_contact() => tried to create a duplicate public contact: " f"{err}",
|
||||||
f"{err}", exc_info=True
|
exc_info=True,
|
||||||
)
|
)
|
||||||
return PublicContact.objects.get(
|
return PublicContact.objects.get(
|
||||||
registry_id=public_contact.registry_id,
|
registry_id=public_contact.registry_id,
|
||||||
|
@ -2142,7 +2136,7 @@ class Domain(TimeStampedModel, DomainHelper):
|
||||||
if existing_contact.email != public_contact.email or existing_contact.registry_id != public_contact.registry_id:
|
if existing_contact.email != public_contact.email or existing_contact.registry_id != public_contact.registry_id:
|
||||||
existing_contact.delete()
|
existing_contact.delete()
|
||||||
public_contact.save()
|
public_contact.save()
|
||||||
logger.warning("Requested PublicContact is out of sync " "with DB.")
|
logger.warning("Requested PublicContact is out of sync with our DB.")
|
||||||
return public_contact
|
return public_contact
|
||||||
|
|
||||||
# If it already exists, we can assume that the DB instance was updated during set, so we should just use that.
|
# If it already exists, we can assume that the DB instance was updated during set, so we should just use that.
|
||||||
|
|
|
@ -350,27 +350,28 @@ class TestDomainCreation(MockEppLib):
|
||||||
"""Rule: An approved domain request must result in a domain"""
|
"""Rule: An approved domain request must result in a domain"""
|
||||||
|
|
||||||
@less_console_noise_decorator
|
@less_console_noise_decorator
|
||||||
def test_get_security_email_race_condition(self):
|
def test_get_or_create_public_contact_race_condition(self):
|
||||||
"""
|
"""
|
||||||
Scenario: Two processes try to create the same security contact simultaneously
|
Scenario: Two processes try to create the same security contact simultaneously
|
||||||
Given a domain in UNKNOWN state
|
Given a domain in UNKNOWN state
|
||||||
When a race condition occurs during contact creation
|
When a race condition occurs during contact creation
|
||||||
Then no IntegrityError is raised
|
Then no IntegrityError is raised
|
||||||
And only one security contact exists in database
|
And only one security contact exists in database
|
||||||
And the correct security email is returned
|
And the correct public contact is returned
|
||||||
|
|
||||||
|
CONTEXT: We ran into an intermittent but somewhat rare issue where IntegrityError
|
||||||
|
was raised when creating PublicContact.
|
||||||
|
Per our logs, this seemed to appear during periods of high app activity.
|
||||||
"""
|
"""
|
||||||
domain, _ = Domain.objects.get_or_create(name="defaultsecurity.gov")
|
domain, _ = Domain.objects.get_or_create(name="defaultsecurity.gov")
|
||||||
|
|
||||||
# Store original method
|
|
||||||
original_filter = PublicContact.objects.filter
|
|
||||||
self.first_call = True
|
|
||||||
def mock_filter(*args, **kwargs):
|
|
||||||
""" Simulates the race condition by creating a
|
|
||||||
duplicate contact between filter check and save
|
|
||||||
"""
|
|
||||||
result = original_filter(*args, **kwargs)
|
|
||||||
|
|
||||||
# Return empty queryset for first call. Otherwise just proceed as normal.
|
self.first_call = True
|
||||||
|
|
||||||
|
def mock_filter(*args, **kwargs):
|
||||||
|
"""Simulates a race condition by creating a
|
||||||
|
duplicate contact between the first filter and save.
|
||||||
|
"""
|
||||||
|
# Return an empty queryset for the first call. Otherwise just proceed as normal.
|
||||||
if self.first_call:
|
if self.first_call:
|
||||||
self.first_call = False
|
self.first_call = False
|
||||||
duplicate = PublicContact(
|
duplicate = PublicContact(
|
||||||
|
@ -378,21 +379,21 @@ class TestDomainCreation(MockEppLib):
|
||||||
contact_type=PublicContact.ContactTypeChoices.SECURITY,
|
contact_type=PublicContact.ContactTypeChoices.SECURITY,
|
||||||
registry_id="defaultSec",
|
registry_id="defaultSec",
|
||||||
email="dotgov@cisa.dhs.gov",
|
email="dotgov@cisa.dhs.gov",
|
||||||
name="Registry Customer Service"
|
name="Registry Customer Service",
|
||||||
)
|
)
|
||||||
duplicate.save(skip_epp_save=True)
|
duplicate.save(skip_epp_save=True)
|
||||||
return PublicContact.objects.none()
|
return PublicContact.objects.none()
|
||||||
|
|
||||||
return result
|
return PublicContact.objects.filter(*args, **kwargs)
|
||||||
|
|
||||||
with patch.object(PublicContact.objects, 'filter', side_effect=mock_filter):
|
with patch.object(PublicContact.objects, "filter", side_effect=mock_filter):
|
||||||
try:
|
try:
|
||||||
public_contact = PublicContact(
|
public_contact = PublicContact(
|
||||||
domain=domain,
|
domain=domain,
|
||||||
contact_type=PublicContact.ContactTypeChoices.SECURITY,
|
contact_type=PublicContact.ContactTypeChoices.SECURITY,
|
||||||
registry_id="defaultSec",
|
registry_id="defaultSec",
|
||||||
email="dotgov@cisa.dhs.gov",
|
email="dotgov@cisa.dhs.gov",
|
||||||
name="Registry Customer Service"
|
name="Registry Customer Service",
|
||||||
)
|
)
|
||||||
returned_public_contact = domain._get_or_create_public_contact(public_contact)
|
returned_public_contact = domain._get_or_create_public_contact(public_contact)
|
||||||
except IntegrityError:
|
except IntegrityError:
|
||||||
|
@ -403,13 +404,14 @@ class TestDomainCreation(MockEppLib):
|
||||||
"Expected behavior: gracefully handle duplicate creation and return existing contact."
|
"Expected behavior: gracefully handle duplicate creation and return existing contact."
|
||||||
)
|
)
|
||||||
|
|
||||||
# Verify only one contact exists
|
# Verify that only one contact exists and its correctness
|
||||||
security_contacts = PublicContact.objects.filter(
|
security_contacts = PublicContact.objects.filter(
|
||||||
domain=domain,
|
domain=domain, contact_type=PublicContact.ContactTypeChoices.SECURITY
|
||||||
contact_type=PublicContact.ContactTypeChoices.SECURITY
|
|
||||||
)
|
)
|
||||||
self.assertEqual(security_contacts.count(), 1)
|
self.assertEqual(security_contacts.count(), 1)
|
||||||
self.assertEqual(returned_public_contact, security_contacts.get())
|
self.assertEqual(returned_public_contact, security_contacts.get())
|
||||||
|
self.assertEqual(returned_public_contact.registry_id, "defaultSec")
|
||||||
|
self.assertEqual(returned_public_contact.email, "dotgov@cisa.dhs.gov")
|
||||||
|
|
||||||
@boto3_mocking.patching
|
@boto3_mocking.patching
|
||||||
def test_approved_domain_request_creates_domain_locally(self):
|
def test_approved_domain_request_creates_domain_locally(self):
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue