Prevent duplicating instance links

In receptor address post-save method:
- Fixed detecting if address was missing
a link from control nodes
- Use InstanceLink create_or_update to prevent
adding duplicate InstanceLink source and target
peers

In instance serializer create_or_update,
delete receptor addresses first before doing
instance create or update. This ensures that we don't
trigger unnecessary post-save methods that might
attempt to manipulate receptor addresses that
will just be removed later.

Signed-off-by: Seth Foster <fosterbseth@gmail.com>
This commit is contained in:
Seth Foster
2024-01-31 00:15:48 -05:00
committed by Seth Foster
parent b558397b67
commit 083c05f12a
2 changed files with 21 additions and 15 deletions

View File

@@ -5598,27 +5598,32 @@ class InstanceSerializer(BaseSerializer):
def create_or_update(self, validated_data, obj=None, create=True): def create_or_update(self, validated_data, obj=None, create=True):
# create a managed receptor address if listener port is defined # create a managed receptor address if listener port is defined
kwargs = dict() port = validated_data.pop('listener_port', -1)
if 'listener_port' in validated_data: peers_from_control_nodes = validated_data.pop('peers_from_control_nodes', -1)
kwargs['port'] = validated_data.pop('listener_port')
if 'peers_from_control_nodes' in validated_data: # delete the receptor address if the port is explicitly set to None
kwargs['peers_from_control_nodes'] = validated_data.pop('peers_from_control_nodes') if obj and port == None:
obj.receptor_addresses.filter(address=obj.hostname).delete()
if create: if create:
instance = super(InstanceSerializer, self).create(validated_data) instance = super(InstanceSerializer, self).create(validated_data)
else: else:
instance = super(InstanceSerializer, self).update(obj, validated_data) instance = super(InstanceSerializer, self).update(obj, validated_data)
instance.refresh_from_db() # instance canonical address lookup is deferred, so needs to be reloaded
# delete the receptor address if the port is expolisitly set to None
if 'port' in kwargs and not kwargs['port']:
instance.receptor_addresses.filter(address=instance.hostname).delete()
# only create or update if port is defined in validated_data or already exists in the # only create or update if port is defined in validated_data or already exists in the
# canonical address # canonical address
# this prevents creating a receptor address if peers_from_control_nodes is in # this prevents creating a receptor address if peers_from_control_nodes is in
# validated_data but a port is not set # validated_data but a port is not set
elif kwargs and ('port' in kwargs or instance.canonical_address_port): if (port != None and port != -1) or instance.canonical_address_port:
kwargs['canonical'] = True kwargs = {}
instance.receptor_addresses.update_or_create(address=instance.hostname, defaults=kwargs) if port != -1:
kwargs['port'] = port
if peers_from_control_nodes != -1:
kwargs['peers_from_control_nodes'] = peers_from_control_nodes
if kwargs:
kwargs['canonical'] = True
instance.receptor_addresses.update_or_create(address=instance.hostname, defaults=kwargs)
return instance return instance

View File

@@ -522,13 +522,14 @@ def receptor_address_saved(sender, instance, **kwargs):
control_instances = set(Instance.objects.filter(node_type__in=[Instance.Types.CONTROL, Instance.Types.HYBRID])) control_instances = set(Instance.objects.filter(node_type__in=[Instance.Types.CONTROL, Instance.Types.HYBRID]))
if address.peers_from_control_nodes: if address.peers_from_control_nodes:
# FIXME: you ought to be able to have more connections than just the control instances # if control_instances is not a subset of current peers of address, then
if set(address.peers_from.all()) != control_instances: # that means we need to add some InstanceLinks
if not control_instances <= set(address.peers_from.all()):
with disable_activity_stream(): with disable_activity_stream():
address.peers_from.add(*control_instances) for control_instance in control_instances:
InstanceLink.objects.update_or_create(source=control_instance, target=address)
schedule_write_receptor_config() schedule_write_receptor_config()
else: else:
# FIXME: you shouldn't unconditionally remove every peer when disabling peers_from_control_nodes
if address.peers_from.exists(): if address.peers_from.exists():
with disable_activity_stream(): with disable_activity_stream():
address.peers_from.remove(*control_instances) address.peers_from.remove(*control_instances)