Skip to content

Commit a2c9e34

Browse files
author
Michal Ploski
committed
Refactor asset uploading to use asset manifest
1 parent 0c49b66 commit a2c9e34

File tree

3 files changed

+41
-33
lines changed

3 files changed

+41
-33
lines changed

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,14 @@ lint: format
1717
poetry run flake8 aws_lambda_powertools/* tests/*
1818

1919
test:
20-
poetry run pytest -m "not (perf or e2e)" --cov=aws_lambda_powertools --cov-report=xml
20+
poetry run pytest -m "not perf" --ignore tests/e2e --cov=aws_lambda_powertools --cov-report=xml
2121
poetry run pytest --cache-clear tests/performance
2222

2323
unit-test:
2424
poetry run pytest tests/unit
2525

2626
e2e-test:
27-
poetry run pytest -n 3 --dist loadscope --durations=0 --durations-min=1 tests/e2e
27+
poetry run pytest -rP -n 3 --dist loadscope --durations=0 --durations-min=1 tests/e2e
2828

2929
coverage-html:
3030
poetry run pytest -m "not (perf or e2e)" --cov=aws_lambda_powertools --cov-report=html

tests/e2e/utils/helpers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def trigger_lambda(lambda_arn: str, client: LambdaClient):
3838

3939

4040
@lru_cache(maxsize=10, typed=False)
41-
@retry(ValueError, delay=1, jitter=1, tries=10)
41+
@retry(ValueError, delay=1, jitter=1, tries=20)
4242
def get_logs(lambda_function_name: str, log_client: CloudWatchClient, start_time: int, **kwargs: dict) -> List[Log]:
4343
response = log_client.filter_log_events(logGroupName=f"/aws/lambda/{lambda_function_name}", startTime=start_time)
4444
if not response["events"]:

tests/e2e/utils/infrastructure.py

Lines changed: 38 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import io
2+
import json
23
import os
34
import sys
45
import zipfile
@@ -57,8 +58,7 @@ def _create_layer(self, stack: Stack):
5758
"-c",
5859
rf"poetry export --with-credentials --format requirements.txt --output /tmp/requirements.txt &&\
5960
pip install -r /tmp/requirements.txt -t {output_dir} &&\
60-
cp -R {input_dir} {output_dir} &&\
61-
find {output_dir}/ -type d -name __pycache__ -prune -exec rm -rf {{}} \;",
61+
cp -R {input_dir} {output_dir}",
6262
],
6363
),
6464
),
@@ -69,7 +69,7 @@ def _find_handlers(self, directory: str) -> List:
6969
for root, _, files in os.walk(directory):
7070
return [os.path.join(root, filename) for filename in files if filename.endswith(".py")]
7171

72-
def synthesize(self, handlers: List[str]) -> Tuple[dict, str]:
72+
def synthesize(self, handlers: List[str]) -> Tuple[dict, str, str]:
7373
integration_test_app = App()
7474
stack = Stack(integration_test_app, self.stack_name)
7575
powertools_layer = self._create_layer(stack)
@@ -98,10 +98,12 @@ def synthesize(self, handlers: List[str]) -> Tuple[dict, str]:
9898
removal_policy=RemovalPolicy.DESTROY,
9999
)
100100
CfnOutput(stack, f"{filename}_arn", value=function_python.function_arn)
101-
return (
102-
integration_test_app.synth().get_stack_by_name(self.stack_name).template,
103-
integration_test_app.synth().directory,
104-
)
101+
cloud_assembly = integration_test_app.synth()
102+
cf_template = cloud_assembly.get_stack_by_name(self.stack_name).template
103+
cloud_assembly_directory = cloud_assembly.directory
104+
cloud_assembly_assets_manifest_path = cloud_assembly.get_stack_by_name(self.stack_name).dependencies[0].file
105+
106+
return (cf_template, cloud_assembly_directory, cloud_assembly_assets_manifest_path)
105107

106108
def __call__(self) -> Tuple[dict, str]:
107109
handlers = self._find_handlers(directory=self.handlers_dir)
@@ -124,8 +126,8 @@ def __init__(self, stack_name: str, handlers_dir: str, config: dict) -> None:
124126
def deploy(self, Stack: Type[InfrastructureStackInterface]) -> Dict[str, str]:
125127

126128
stack = Stack(handlers_dir=self.handlers_dir, stack_name=self.stack_name, config=self.config)
127-
template, asset_root_dir = stack()
128-
self._upload_assets(template, asset_root_dir)
129+
template, asset_root_dir, asset_manifest_file = stack()
130+
self._upload_assets(asset_root_dir, asset_manifest_file)
129131

130132
response = self._deploy_stack(self.stack_name, template)
131133

@@ -134,25 +136,31 @@ def deploy(self, Stack: Type[InfrastructureStackInterface]) -> Dict[str, str]:
134136
def delete(self):
135137
self.cf_client.delete_stack(StackName=self.stack_name)
136138

137-
def _upload_assets(self, template: dict, asset_root_dir: str):
139+
def _upload_assets(self, asset_root_dir: str, asset_manifest_file: str):
140+
141+
assets = self._find_assets(asset_manifest_file, self.account_id, self.region)
138142

139-
assets = self._find_assets(template, self.account_id, self.region)
143+
for s3_key, config in assets.items():
144+
print(config)
145+
s3_bucket = self.s3_resource.Bucket(config["bucket_name"])
146+
147+
if config["asset_packaging"] != "zip":
148+
print("Asset is not a zip file. Skipping upload")
149+
continue
140150

141-
for s3_key, bucket in assets.items():
142-
s3_bucket = self.s3_resource.Bucket(bucket)
143151
if bool(list(s3_bucket.objects.filter(Prefix=s3_key))):
144152
print("object exists, skipping")
145153
continue
146154

147155
buf = io.BytesIO()
148-
asset_dir = f"{asset_root_dir}/asset.{Path(s3_key).with_suffix('')}"
156+
asset_dir = f"{asset_root_dir}/{config['asset_path']}"
149157
os.chdir(asset_dir)
150158
asset_files = self._find_files(directory=".")
151159
with zipfile.ZipFile(buf, "w", compression=zipfile.ZIP_DEFLATED) as zf:
152160
for asset_file in asset_files:
153161
zf.write(os.path.join(asset_file))
154162
buf.seek(0)
155-
self.s3_client.upload_fileobj(Fileobj=buf, Bucket=bucket, Key=s3_key)
163+
self.s3_client.upload_fileobj(Fileobj=buf, Bucket=config["bucket_name"], Key=s3_key)
156164

157165
def _find_files(self, directory: str) -> List:
158166
file_paths = []
@@ -174,22 +182,22 @@ def _deploy_stack(self, stack_name: str, template: dict):
174182
response = self.cf_client.describe_stacks(StackName=stack_name)
175183
return response
176184

177-
def _find_assets(self, template: dict, account_id: str, region: str):
185+
def _find_assets(self, asset_template: str, account_id: str, region: str):
178186
assets = {}
179-
for _, resource in template["Resources"].items():
180-
bucket = None
181-
S3Key = None
182-
183-
if resource["Properties"].get("Code"):
184-
bucket = resource["Properties"]["Code"]["S3Bucket"]
185-
S3Key = resource["Properties"]["Code"]["S3Key"]
186-
elif resource["Properties"].get("Content"):
187-
bucket = resource["Properties"]["Content"]["S3Bucket"]
188-
S3Key = resource["Properties"]["Content"]["S3Key"]
189-
if S3Key and bucket:
190-
assets[S3Key] = (
191-
bucket["Fn::Sub"].replace("${AWS::AccountId}", account_id).replace("${AWS::Region}", region)
192-
)
187+
with open(asset_template, mode="r") as template:
188+
for _, config in json.loads(template.read())["files"].items():
189+
asset_path = config["source"]["path"]
190+
asset_packaging = config["source"]["packaging"]
191+
bucket_name = config["destinations"]["current_account-current_region"]["bucketName"]
192+
object_key = config["destinations"]["current_account-current_region"]["objectKey"]
193+
194+
assets[object_key] = {
195+
"bucket_name": bucket_name.replace("${AWS::AccountId}", account_id).replace(
196+
"${AWS::Region}", region
197+
),
198+
"asset_path": asset_path,
199+
"asset_packaging": asset_packaging,
200+
}
193201

194202
return assets
195203

0 commit comments

Comments
 (0)