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 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
|
import os
import shutil
import subprocess
import sys
import tempfile
from typing import TYPE_CHECKING
from sentry_sdk.consts import VERSION as SDK_VERSION
if TYPE_CHECKING:
from typing import Optional
DIST_PATH = "dist" # created by "make dist" that is called by "make aws-lambda-layer"
PYTHON_SITE_PACKAGES = "python" # see https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html#configuration-layers-path
class LayerBuilder:
def __init__(
self,
base_dir, # type: str
out_zip_filename=None, # type: Optional[str]
):
# type: (...) -> None
self.base_dir = base_dir
self.python_site_packages = os.path.join(self.base_dir, PYTHON_SITE_PACKAGES)
self.out_zip_filename = (
f"sentry-python-serverless-{SDK_VERSION}.zip"
if out_zip_filename is None
else out_zip_filename
)
def make_directories(self):
# type: (...) -> None
os.makedirs(self.python_site_packages)
def install_python_packages(self):
# type: (...) -> None
# Install requirements for Lambda Layer (these are more limited than the SDK requirements,
# because Lambda does not support the newest versions of some packages)
subprocess.check_call(
[
sys.executable,
"-m",
"pip",
"install",
"-r",
"requirements-aws-lambda-layer.txt",
"--target",
self.python_site_packages,
],
)
sentry_python_sdk = os.path.join(
DIST_PATH,
f"sentry_sdk-{SDK_VERSION}-py2.py3-none-any.whl", # this is generated by "make dist" that is called by "make aws-lambda-layer"
)
subprocess.run(
[
"pip",
"install",
"--no-cache-dir", # always access PyPI
"--no-deps", # the right depencencies have been installed in the call above
"--quiet",
sentry_python_sdk,
"--target",
self.python_site_packages,
],
check=True,
)
def create_init_serverless_sdk_package(self):
# type: (...) -> None
"""
Method that creates the init_serverless_sdk pkg in the
sentry-python-serverless zip
"""
serverless_sdk_path = (
f"{self.python_site_packages}/sentry_sdk/"
f"integrations/init_serverless_sdk"
)
if not os.path.exists(serverless_sdk_path):
os.makedirs(serverless_sdk_path)
shutil.copy(
"scripts/init_serverless_sdk.py", f"{serverless_sdk_path}/__init__.py"
)
def zip(self):
# type: (...) -> None
subprocess.run(
[
"zip",
"-q", # Quiet
"-x", # Exclude files
"**/__pycache__/*", # Files to be excluded
"-r", # Recurse paths
self.out_zip_filename, # Output filename
PYTHON_SITE_PACKAGES, # Files to be zipped
],
cwd=self.base_dir,
check=True, # Raises CalledProcessError if exit status is non-zero
)
shutil.copy(
os.path.join(self.base_dir, self.out_zip_filename),
os.path.abspath(DIST_PATH),
)
def build_packaged_zip(base_dir=None, make_dist=False, out_zip_filename=None):
if base_dir is None:
base_dir = tempfile.mkdtemp()
if make_dist:
# Same thing that is done by "make dist"
# (which is a dependency of "make aws-lambda-layer")
subprocess.check_call(
[sys.executable, "setup.py", "sdist", "bdist_wheel", "-d", DIST_PATH],
)
layer_builder = LayerBuilder(base_dir, out_zip_filename=out_zip_filename)
layer_builder.make_directories()
layer_builder.install_python_packages()
layer_builder.create_init_serverless_sdk_package()
layer_builder.zip()
# Just for debugging
dist_path = os.path.abspath(DIST_PATH)
print("Created Lambda Layer package with this information:")
print(" - Base directory for generating package: {}".format(layer_builder.base_dir))
print(
" - Created Python SDK distribution (in `{}`): {}".format(dist_path, make_dist)
)
if not make_dist:
print(" If 'False' we assume it was already created (by 'make dist')")
print(" - Package zip filename: {}".format(layer_builder.out_zip_filename))
print(" - Copied package zip to: {}".format(dist_path))
if __name__ == "__main__":
build_packaged_zip()
|