Skip to content

Commit 395d6c6

Browse files
authored
Fix concurrent AWS security group creation (#4399)
Handle InvalidGroup.Duplicate and InvalidPermission.Duplicate errors when instances in the same fleet provision at once.
1 parent 4028fe8 commit 395d6c6

1 file changed

Lines changed: 48 additions & 22 deletions

File tree

‎src/dstack/_internal/core/backends/aws/resources.py‎

Lines changed: 48 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -82,20 +82,34 @@ def create_security_group(
8282
create_security_group_kwargs = {}
8383
if vpc_id is not None:
8484
create_security_group_kwargs["VpcId"] = vpc_id
85-
security_group = ec2_client.create_security_group(
86-
Description="Generated by dstack",
87-
GroupName=security_group_name,
88-
TagSpecifications=[
89-
{
90-
"ResourceType": "security-group",
91-
"Tags": [
92-
{"Key": "owner", "Value": "dstack"},
93-
{"Key": "dstack_project", "Value": project_id},
94-
],
95-
},
96-
],
97-
**create_security_group_kwargs,
98-
)
85+
try:
86+
security_group = ec2_client.create_security_group(
87+
Description="Generated by dstack",
88+
GroupName=security_group_name,
89+
TagSpecifications=[
90+
{
91+
"ResourceType": "security-group",
92+
"Tags": [
93+
{"Key": "owner", "Value": "dstack"},
94+
{"Key": "dstack_project", "Value": project_id},
95+
],
96+
},
97+
],
98+
**create_security_group_kwargs,
99+
)
100+
except botocore.exceptions.ClientError as e:
101+
if e.response.get("Error", {}).get("Code") != "InvalidGroup.Duplicate":
102+
raise
103+
# The group was created concurrently, e.g. by another instance provisioning
104+
response = ec2_client.describe_security_groups(
105+
Filters=describe_security_groups_filters
106+
)
107+
if not response.get("SecurityGroups"):
108+
raise ComputeError(
109+
f"Security group {security_group_name} reported as duplicate"
110+
" but not found when describing it"
111+
) from e
112+
security_group = response["SecurityGroups"][0]
99113
security_group_id = security_group["GroupId"]
100114

101115
_add_ingress_security_group_rule_if_missing(
@@ -486,10 +500,16 @@ def _add_ingress_security_group_rule_if_missing(
486500
) -> bool:
487501
if _rule_exists(rule, security_group.get("IpPermissions", [])):
488502
return False
489-
ec2_client.authorize_security_group_ingress(
490-
GroupId=security_group_id,
491-
IpPermissions=[rule],
492-
)
503+
try:
504+
ec2_client.authorize_security_group_ingress(
505+
GroupId=security_group_id,
506+
IpPermissions=[rule],
507+
)
508+
except botocore.exceptions.ClientError as e:
509+
if e.response.get("Error", {}).get("Code") == "InvalidPermission.Duplicate":
510+
# The rule was added concurrently
511+
return False
512+
raise
493513
return True
494514

495515

@@ -501,10 +521,16 @@ def _add_egress_security_group_rule_if_missing(
501521
) -> bool:
502522
if _rule_exists(rule, security_group.get("IpPermissionsEgress", [])):
503523
return False
504-
ec2_client.authorize_security_group_egress(
505-
GroupId=security_group_id,
506-
IpPermissions=[rule],
507-
)
524+
try:
525+
ec2_client.authorize_security_group_egress(
526+
GroupId=security_group_id,
527+
IpPermissions=[rule],
528+
)
529+
except botocore.exceptions.ClientError as e:
530+
if e.response.get("Error", {}).get("Code") == "InvalidPermission.Duplicate":
531+
# The rule was added concurrently
532+
return False
533+
raise
508534
return True
509535

510536

0 commit comments

Comments
 (0)