Skip to content

Commit 37b53c7

Browse files
authored
release 3.3.8 (#125)
1 parent 15deb31 commit 37b53c7

File tree

11 files changed

+134
-42
lines changed

11 files changed

+134
-42
lines changed

.gitignore

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ reports
1616
*.pyc
1717
.eggs
1818
*.egg-info
19-
.venv
19+
.venv*
2020
.cache
2121
.pytest_cache
2222
.mypy_cache
@@ -30,5 +30,4 @@ node_modules
3030
.DS_Store
3131
.idea/
3232
*sonarlint*
33-
docs
3433
test.sh

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,24 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [3.3.8] - 2024-08-15
9+
10+
### Fixed
11+
12+
- [#116](https://github.com/aws-solutions/network-orchestration-for-aws-transit-gateway/issues/116)
13+
- [#117](https://github.com/aws-solutions/network-orchestration-for-aws-transit-gateway/issues/117)
14+
- IAM policy for _StateMachineLambdaFunctionRole_
15+
16+
### Changed
17+
18+
- `resource_exception_handler` decorator does not catch `IncorrectState`
19+
exception, allowing the exception to be raised as `ResourceBusyException `
20+
by `service_exception_handler` decorator
21+
22+
### Security
23+
24+
- Bumped axios to `1.7.4` to mitigate [CVE-2024-39338](https://github.com/advisories/GHSA-8hc4-vh64-cxmj)
25+
826
## [3.3.7] - 2024-08-02
927

1028
### Security

deployment/network-orchestration-hub.template

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1094,7 +1094,7 @@ Resources:
10941094
- logs:CreateLogStream
10951095
- logs:PutLogEvents
10961096
Resource:
1097-
- !Sub arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/{AWS::Partition}/lambda/*
1097+
- !Sub arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/${AWS::Partition}/lambda/*
10981098
- !Sub arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:${CloudWatchLogActions}*
10991099
- !Sub arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:${CloudWatchLogFailures}*
11001100
- Effect: Allow

source/cognito-trigger/package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

source/cognito-trigger/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "cognito-trigger",
3-
"version": "3.3.7",
3+
"version": "3.3.8",
44
"description": "Triggered when a new user is confirmed in the user pool to allow for custom actions to be taken",
55
"author": {
66
"name": "Amazon Web Services",
@@ -35,4 +35,4 @@
3535
"overrides": {
3636
"fast-xml-parser": "4.4.1"
3737
}
38-
}
38+
}

source/lambda/pyproject.toml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
[build-system]
2+
requires = ["setuptools"]
3+
build-backend = "setuptools.build_meta"
4+
5+
[project]
6+
name = "network-orchestration-for-tgw"
7+
version = "3.3.8-dev"
8+
description = "solution packages for network-orchestration-for-tgw"
9+
requires-python = ">=3.10"
10+
license = { text = "Apache Software License" }
11+
dependencies = [
12+
"boto3==1.34.91",
13+
"botocore==1.34.129",
14+
"aws-lambda-powertools==2.25.0",
15+
]
16+
[project.optional-dependencies]
17+
dev = [
18+
"moto==4.2.0",
19+
"pytest==7.4.2",
20+
"mypy-boto3-ec2==1.34.149",
21+
"mypy-boto3-dynamodb==1.34.148",
22+
"mypy-boto3-sts==1.34.0",
23+
"mypy-boto3-sns==1.34.121",
24+
"mypy-boto3-ram==1.34.0",
25+
"mypy-boto3-organizations==1.34.139"
26+
]
27+
28+
[tool.setuptools.packages.find]
29+
where = ["."]
30+
include = ["custom_resource*", "tgw_vpc_attachment*", "tgw_peering_attachment*"]

source/lambda/tgw_vpc_attachment/__tests__/transit_gateway_handler/test_transit_gateway_handler.py

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,16 @@
22

33
import pytest
44
from aws_lambda_powertools.utilities.typing import LambdaContext
5-
from moto import mock_sts
5+
from moto import mock_sts, mock_ec2
66
from mypy_boto3_ec2 import EC2Client
77

88
from tgw_vpc_attachment.__tests__.conftest import override_environment_variables
99
from tgw_vpc_attachment.lib.clients.ec2 import EC2
10+
from tgw_vpc_attachment.lib.exceptions import ResourceBusyException
1011
from tgw_vpc_attachment.main import lambda_handler
12+
from tgw_vpc_attachment.lib.handlers.tgw_vpc_attachment_handler import TransitGatewayVPCAttachments
13+
14+
from unittest.mock import patch
1115

1216

1317
@mock_sts
@@ -74,6 +78,33 @@ def test_disassociate_transit_gateway_route_table(vpc_setup_with_explicit_route_
7478
# throws exception because get_transit_gateway_route_table_associations is not implemented in moto
7579

7680

81+
@mock_sts
82+
@patch('tgw_vpc_attachment.lib.clients.ec2.EC2.get_transit_gateway_route_table_associations')
83+
def test_get_association_state(mock_get_tgw_rtb_associations, vpc_setup_with_explicit_route_table):
84+
tgw_attachments = TransitGatewayVPCAttachments(vpc_setup_with_explicit_route_table)
85+
86+
# disassociated state, returns empty list
87+
mock_get_tgw_rtb_associations.return_value = []
88+
assert tgw_attachments._get_association_state('myTable') == 'disassociated'
89+
90+
# state transition from associating -> associated
91+
mock_get_tgw_rtb_associations.side_effect = [[{'State': 'associating'}], [{'State': 'associated'}]]
92+
os.environ["WAIT_TIME"] = '1'
93+
assert tgw_attachments._get_association_state('myTable') == 'associated'
94+
95+
96+
@mock_sts
97+
@patch('tgw_vpc_attachment.lib.clients.ec2.EC2.get_transit_gateway_route_table_associations')
98+
def test_get_association_state_raises_exception(mock_get_tgw_rtb_associations, vpc_setup_with_explicit_route_table):
99+
tgw_attachments = TransitGatewayVPCAttachments(vpc_setup_with_explicit_route_table)
100+
101+
mock_get_tgw_rtb_associations.side_effect = [ResourceBusyException]
102+
103+
with pytest.raises(ResourceBusyException):
104+
tgw_attachments._get_association_state('myTable')
105+
106+
assert mock_get_tgw_rtb_associations.call_count == 1
107+
77108
@mock_sts
78109
def test_get_transit_gateway_attachment_propagations(vpc_setup_with_explicit_route_table):
79110
# ARRANGE
@@ -138,6 +169,26 @@ def test_enable_transit_gateway_route_table_propagation_skip(vpc_setup_with_expl
138169
assert response['AttachmentState'] == 'available'
139170

140171

172+
@mock_sts
173+
@mock_ec2
174+
@patch('tgw_vpc_attachment.lib.clients.ec2.EC2.enable_transit_gateway_route_table_propagation')
175+
@patch.object(TransitGatewayVPCAttachments, '_get_propagation_route_tables_to_enable')
176+
def test_enable_transit_gateway_route_table_propagation_raises_exception(
177+
mock_get_propagation_rtb, mock_enable_propagation, vpc_setup_with_explicit_route_table):
178+
179+
# ARRANGE
180+
vpc_setup_with_explicit_route_table['AttachmentState'] = "available"
181+
mock_get_propagation_rtb.return_value = ['rtb-0000']
182+
mock_enable_propagation.side_effect = [ResourceBusyException]
183+
184+
# ACT
185+
tgw_attachments = TransitGatewayVPCAttachments(vpc_setup_with_explicit_route_table)
186+
187+
# ASSERT
188+
with pytest.raises(ResourceBusyException):
189+
tgw_attachments.enable_transit_gateway_route_table_propagation()
190+
191+
141192
@mock_sts
142193
def test_enable_transit_gateway_route_table_propagation(vpc_setup_with_explicit_route_table):
143194
# ARRANGE

source/lambda/tgw_vpc_attachment/lib/exceptions.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ def wrapper_func(self, *args, **kwargs):
1616
response = func(self, *args, **kwargs)
1717
except ClientError as err:
1818
exception_codes = [
19-
'IncorrectState',
2019
'InsufficientSubnetsException',
2120
'OptInRequired',
2221
'DuplicateSubnetsInSameZone'

source/lambda/tgw_vpc_attachment/lib/handlers/tgw_vpc_attachment_handler.py

Lines changed: 21 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -186,9 +186,7 @@ def _add_subnet_to_tgw_attachment(self):
186186
self.event.get("TransitGatewayAttachmentId"),
187187
self.event.get('SubnetId'),
188188
)
189-
if response.get("Error") == "IncorrectState":
190-
raise ResourceBusyException
191-
elif response.get("Error") == "DuplicateSubnetsInSameZone":
189+
if response.get("Error") == "DuplicateSubnetsInSameZone":
192190
self.event.update({"Status": "auto-rejected"})
193191
comment = "You can only add one subnet in a TGW-VPC attachment per Availability Zone. Please delete and " \
194192
"create the tag with RoutingTag provided in the Hub Template"
@@ -218,10 +216,8 @@ def _remove_subnet_from_tgw_attachment(self):
218216
self.event.get("TransitGatewayAttachmentId"),
219217
self.event.get('SubnetId'),
220218
)
221-
if response.get("Error") == "IncorrectState":
222-
raise ResourceBusyException
223219
# this exception is caught if the last subnet in the attachment is being deleted
224-
elif response.get("Error") == "InsufficientSubnetsException":
220+
if response.get("Error") == "InsufficientSubnetsException":
225221
self.logger.info(
226222
"Insufficient Subnets when calling the ModifyTransitGatewayVpcAttachment operation, "
227223
"This is the last subnet in the TGW-VPC Attachment. Deleting TGW Attachment..."
@@ -545,14 +541,11 @@ def associate_transit_gateway_route_table(self):
545541
)
546542
self.event.update({"Action": "AssociateTgwRouteTable"})
547543
transit_gateway_attachment_id = self.event.get("TransitGatewayAttachmentId")
548-
response = self.hub_ec2_client.associate_transit_gateway_route_table(
544+
self.hub_ec2_client.associate_transit_gateway_route_table(
549545
association_route_table_id,
550546
transit_gateway_attachment_id,
551547
)
552-
state = self._get_association_state(
553-
association_route_table_id,
554-
response.get("Association").get("State"),
555-
)
548+
state = self._get_association_state(association_route_table_id)
556549
self.event.update({"AssociationState": state})
557550
self._create_tag(
558551
self.event.get("VpcId"),
@@ -570,14 +563,11 @@ def disassociate_transit_gateway_route_table(self):
570563
existing_association_route_table = self.event.get("ExistingAssociationRouteTableId")
571564
self.logger.info(f"Disassociating TGW Route Table Id: {existing_association_route_table}")
572565
self.event.update({"Action": "DisassociateTgwRouteTable"})
573-
response = self.hub_ec2_client.disassociate_transit_gateway_route_table(
566+
self.hub_ec2_client.disassociate_transit_gateway_route_table(
574567
existing_association_route_table,
575568
self.event.get("TransitGatewayAttachmentId"),
576569
)
577-
state = self._get_association_state(
578-
existing_association_route_table,
579-
response.get("Association").get("State"),
580-
)
570+
state = self._get_association_state(existing_association_route_table)
581571
self.event.update({"DisassociationState": state})
582572
self._create_tag(
583573
self.event.get("VpcId"),
@@ -588,28 +578,33 @@ def disassociate_transit_gateway_route_table(self):
588578
self.logger.info(TGW_VPC_ERROR)
589579
return self.event
590580

591-
def _get_association_state(self, rtb, state: TransitGatewayAssociationStateType):
592-
association_in_transient_state = False
593-
if state != "associated" or state != "disassociated":
594-
association_in_transient_state = True
581+
def _get_association_state(self, rtb):
582+
max_retries = int(environ.get("MAX_RETRY", 5)) # Default to 5 retries
583+
retry = 0
584+
vpc_id = self.event.get("VpcId")
585+
tgw_attachment_id = self.event.get("TransitGatewayAttachmentId")
586+
wait_time = int(environ.get("WAIT_TIME", 5)) # Default to 5 seconds if not set
595587

596-
while association_in_transient_state:
597-
vpc_id = self.event.get("VpcId")
598-
tgw_attachment_id = self.event.get("TransitGatewayAttachmentId")
588+
while retry < max_retries:
599589
response = self.hub_ec2_client.get_transit_gateway_route_table_associations(
600590
rtb,
601591
tgw_attachment_id,
602592
vpc_id,
603593
)
594+
604595
# once the TGW RT is disassociated the returned response is empty list
605596
state = "disassociated"
606597
if response:
607598
state = response[0].get("State")
608599
self.logger.info(f"Association Status: {state}")
609600
if state == "associated" or state == "disassociated":
610-
association_in_transient_state = False
611-
sleep(int(environ.get("WAIT_TIME")))
612-
return state
601+
return state
602+
retry += 1
603+
sleep(wait_time)
604+
605+
self.logger.error("Maximum retries reached, unable to determine association state.")
606+
raise ResourceBusyException
607+
613608

614609
@service_exception_handler
615610
def enable_transit_gateway_route_table_propagation(self):

source/ui/package-lock.json

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)