-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpackager.py
100 lines (81 loc) · 3.49 KB
/
packager.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import hashlib
import json
import os
import platform
import shutil
import subprocess
import traceback
import urllib.error
import urllib.request
import venv
import zipfile
import boto3
SUCCESS = "SUCCESS"
FAILED = "FAILED"
def cfn_response(event, context, status, physical_resource_id, data, reason=None):
if reason:
reason += "\n\n"
else:
reason = ""
reason += f"See CloudWatch for details: {context.log_group_name} {context.log_stream_name}"
response = json.dumps({
"Status": status,
"Reason": reason,
"PhysicalResourceId": physical_resource_id,
"StackId": event["StackId"],
"RequestId": event["RequestId"],
"LogicalResourceId": event["LogicalResourceId"],
"Data": data or {}
}).encode("utf-8")
opener = urllib.request.build_opener(urllib.request.HTTPSHandler)
request = urllib.request.Request(
event["ResponseURL"],
data=response,
headers={
"Content-Type": "",
"Content-Length": f"{len(response)}"
},
method="PUT")
opener.open(request)
def handler(event, context):
pid = "BAD-PARAMETERS"
try:
requirements = event["ResourceProperties"]["Requirements"]
hashed_data = requirements + " XX_VERSION_XX " + platform.python_version()
rhash = hashlib.md5(hashed_data.encode("utf-8")).hexdigest()
pid = f"req-{rhash}"
key = f'{event["ResourceProperties"]["Prefix"]}/{rhash}.zip'
bucket = event["ResourceProperties"]["Bucket"]
if event["RequestType"] in ["Create", "Update"]:
print(f"Installing on Python {platform.python_version()}: \n{requirements}")
shutil.rmtree("/tmp/venv", ignore_errors=True)
shutil.rmtree("/tmp/python", ignore_errors=True)
# we create a venv so package upgrades don't attempt read-only /var/runtime libraries
venv.create("/tmp/venv", with_pip=True)
open("/tmp/requirements.txt", "w").write(requirements)
cmd = "/tmp/venv/bin/python -m pip --no-cache-dir --disable-pip-version-check install -t /tmp/python --progress-bar off -r /tmp/requirements.txt"
print(f"Running {cmd}")
pip_result = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)
if pip_result.returncode != 0:
# response is limited to 4096 bytes total
cfn_response(event, context, FAILED, pid, None,
f"pip failed [{pip_result.returncode}]:\n\n[...] {pip_result.stdout[-700:]}")
return
print(pip_result.stdout)
print(f"Building requirements package...")
with zipfile.ZipFile("/tmp/python.zip", "w") as z:
for root, folders, files in os.walk("/tmp/python"):
for f in files:
local_path = os.path.join(root, f)
zip_path = os.path.relpath(local_path, "/tmp")
z.write(local_path, zip_path, zipfile.ZIP_DEFLATED)
print(f"Uploading to s3://{bucket}/{key}")
boto3.client("s3").upload_file("/tmp/python.zip", bucket, key)
cfn_response(event, context, SUCCESS, pid, {"Key": key})
except Exception as e:
try:
traceback.print_last()
except ValueError:
print("Caught exception but unable to print stack trace")
print(e)
cfn_response(event, context, FAILED, pid, None, str(e))