forked from newrelic/newrelic-lambda-extension
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
bc86c31
commit 97ffa20
Showing
7 changed files
with
469 additions
and
62 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
# Byte-compiled / optimized / DLL files | ||
__pycache__/ | ||
*.py[cod] | ||
*$py.class | ||
|
||
# C extensions | ||
*.so | ||
|
||
# Distribution / packaging | ||
.Python | ||
function.zip | ||
lambda_output.txt | ||
nr_tmp_env.sh | ||
env/ | ||
build/ | ||
develop-eggs/ | ||
dist/ | ||
downloads/ | ||
eggs/ | ||
.eggs/ | ||
lib/ | ||
lib64/ | ||
parts/ | ||
sdist/ | ||
var/ | ||
*.egg-info/ | ||
.installed.cfg | ||
*.egg | ||
|
||
# PyInstaller | ||
# Usually these files are written by a python script from a template | ||
# before PyInstaller builds the exe, so as to inject date/other infos into it. | ||
*.manifest | ||
*.spec | ||
|
||
# Installer logs | ||
pip-log.txt | ||
pip-delete-this-directory.txt | ||
|
||
# Unit test / coverage reports | ||
htmlcov/ | ||
.tox/ | ||
.coverage | ||
.coverage.* | ||
.cache | ||
nosetests.xml | ||
coverage.xml | ||
*,cover | ||
.hypothesis/ | ||
|
||
# Translations | ||
*.mo | ||
*.pot | ||
|
||
# Django stuff: | ||
*.log | ||
local_settings.py | ||
|
||
# Flask stuff: | ||
instance/ | ||
.webassets-cache | ||
|
||
# Scrapy stuff: | ||
.scrapy | ||
|
||
# Sphinx documentation | ||
docs/_build/ | ||
|
||
# PyBuilder | ||
target/ | ||
|
||
# IPython Notebook | ||
.ipynb_checkpoints | ||
|
||
# pyenv | ||
.python-version | ||
|
||
# celery beat schedule file | ||
celerybeat-schedule | ||
|
||
# dotenv | ||
.env | ||
|
||
# virtualenv | ||
venv/ | ||
ENV/ | ||
|
||
# Spyder project settings | ||
.spyderproject | ||
|
||
# Rope project settings | ||
.ropeproject |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
## Local Testing | ||
|
||
To test the extension on a AWS test account, follow the steps: | ||
1. Configure the credentials for the AWS test account `aws configure` | ||
2. Run `./publish.sh` to publish the layers to your test account `us-west-2` region | ||
3. Publish script will create 4 lambda layers for runtimes - Python 3.12 [[Amazon Linux 2023](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html)] & Python 3.11 [[Amazon Linux 2](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html)] and architectures - x86 & ARM | ||
3. Run `./test.sh` to create lambda with test layer published in step 2 | ||
4. Go to your AWS test account and check the logs of the lambda with suffix - `NR_EXTENSION_TEST_LAMBDA_` for any error in extension |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
def handler(event, context): | ||
# Your code here | ||
return { | ||
'statusCode': 200, | ||
'body': 'Hello from Lambda!' | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
# -*- coding: utf-8 -*- | ||
|
||
import importlib | ||
import os | ||
import sys | ||
import warnings | ||
|
||
os.environ.setdefault("NEW_RELIC_APP_NAME", os.getenv("AWS_LAMBDA_FUNCTION_NAME", "")) | ||
os.environ.setdefault("NEW_RELIC_NO_CONFIG_FILE", "true") | ||
os.environ.setdefault("NEW_RELIC_DISTRIBUTED_TRACING_ENABLED", "true") | ||
os.environ.setdefault("NEW_RELIC_SERVERLESS_MODE_ENABLED", "true") | ||
os.environ.setdefault( | ||
"NEW_RELIC_TRUSTED_ACCOUNT_KEY", os.getenv("NEW_RELIC_ACCOUNT_ID", "") | ||
) | ||
|
||
# The agent will load some environment variables on module import so we need | ||
# to perform the import after setting the necessary environment variables. | ||
import newrelic.agent # noqa | ||
from newrelic_lambda.lambda_handler import lambda_handler # noqa | ||
|
||
newrelic.agent.initialize() | ||
|
||
|
||
class IOpipeNoOp(object): | ||
def __call__(self, *args, **kwargs): | ||
warnings.warn( | ||
"Use of context.iopipe.* is no longer supported. " | ||
"Please see New Relic Python agent documentation here: " | ||
"https://docs.newrelic.com/docs/agents/python-agent" | ||
) | ||
|
||
def __getattr__(self, name): | ||
return IOpipeNoOp() | ||
|
||
|
||
def get_handler(): | ||
if ( | ||
"NEW_RELIC_LAMBDA_HANDLER" not in os.environ | ||
or not os.environ["NEW_RELIC_LAMBDA_HANDLER"] | ||
): | ||
raise ValueError( | ||
"No value specified in NEW_RELIC_LAMBDA_HANDLER environment variable" | ||
) | ||
|
||
try: | ||
module_path, handler_name = os.environ["NEW_RELIC_LAMBDA_HANDLER"].rsplit( | ||
".", 1 | ||
) | ||
except ValueError: | ||
raise ValueError( | ||
"Improperly formated handler value: %s" | ||
% os.environ["NEW_RELIC_LAMBDA_HANDLER"] | ||
) | ||
|
||
try: | ||
# Use the same check as | ||
# https://github.com/aws/aws-lambda-python-runtime-interface-client/blob/97dee252434edc56be4cafd54a9af1e7fa041eaf/awslambdaric/bootstrap.py#L33 | ||
if module_path.split(".")[0] in sys.builtin_module_names: | ||
raise ImportError( | ||
"Cannot use built-in module %s as a handler module" % module_path | ||
) | ||
|
||
module = importlib.import_module(module_path.replace("/", ".")) | ||
except Exception as e: | ||
raise ImportError("Failed to import module '%s': %s" % (module_path, e)) | ||
|
||
try: | ||
handler = getattr(module, handler_name) | ||
except AttributeError: | ||
raise AttributeError( | ||
"No handler '%s' in module '%s'" % (handler_name, module_path) | ||
) | ||
|
||
return handler | ||
|
||
|
||
# Greedily load the handler during cold start, so we don't pay for it on first invoke | ||
wrapped_handler = get_handler() | ||
|
||
|
||
@lambda_handler() | ||
def handler(event, context): | ||
context.iopipe = IOpipeNoOp() | ||
return wrapped_handler(event, context) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,197 @@ | ||
#!/usr/bin/env bash | ||
|
||
set -Eeuo pipefail | ||
|
||
# https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html | ||
# Python 3.12 | Amazon Linux 2023 | ||
# Python 3.11 | Amazon Linux 2 | ||
|
||
BUILD_DIR=python | ||
DIST_DIR=dist | ||
|
||
BUCKET_PREFIX=nr-extension-test-layers | ||
|
||
PY311_DIST_ARM64=$DIST_DIR/python311.arm64.zip | ||
PY311_DIST_X86_64=$DIST_DIR/python311.x86_64.zip | ||
|
||
PY312_DIST_ARM64=$DIST_DIR/python312.arm64.zip | ||
PY312_DIST_X86_64=$DIST_DIR/python312.x86_64.zip | ||
|
||
REGIONS_X86=(us-west-2) | ||
REGIONS_ARM=(us-west-2) | ||
|
||
EXTENSION_DIST_DIR=extensions | ||
EXTENSION_DIST_ZIP=extension.zip | ||
EXTENSION_DIST_PREVIEW_FILE=preview-extensions-ggqizro707 | ||
|
||
TMP_ENV_FILE_NAME=nr_tmp_env.sh | ||
|
||
function fetch_extension { | ||
arch=$1 | ||
url="https://github.com/newrelic/newrelic-lambda-extension/releases/download/v2.3.11/newrelic-lambda-extension.${arch}.zip" | ||
rm -rf $EXTENSION_DIST_DIR $EXTENSION_DIST_ZIP | ||
curl -L $url -o $EXTENSION_DIST_ZIP | ||
} | ||
|
||
function download_extension { | ||
fetch_extension $@ | ||
|
||
unzip $EXTENSION_DIST_ZIP -d . | ||
rm -f $EXTENSION_DIST_ZIP | ||
} | ||
|
||
function layer_name_str() { | ||
rt_part="LambdaExtension" | ||
arch_part="" | ||
|
||
case $1 in | ||
"python3.11") | ||
rt_part="Python311" | ||
;; | ||
"python3.12") | ||
rt_part="Python312" | ||
;; | ||
esac | ||
|
||
case $2 in | ||
"arm64") | ||
arch_part="ARM64" | ||
;; | ||
"x86_64") | ||
arch_part="X86" | ||
;; | ||
esac | ||
|
||
echo "NewRelic${rt_part}${arch_part}" | ||
} | ||
|
||
|
||
function hash_file() { | ||
if command -v md5sum &> /dev/null ; then | ||
md5sum $1 | awk '{ print $1 }' | ||
else | ||
md5 -q $1 | ||
fi | ||
} | ||
|
||
|
||
function s3_prefix() { | ||
name="nr-test-extension" | ||
|
||
case $1 in | ||
"python3.11") | ||
name="nr-python3.11" | ||
;; | ||
"python3.12") | ||
name="nr-python3.12" | ||
;; | ||
esac | ||
|
||
echo $name | ||
} | ||
|
||
function publish_layer { | ||
layer_archive=$1 | ||
region=$2 | ||
runtime_name=$3 | ||
arch=$4 | ||
|
||
layer_name=$( layer_name_str $runtime_name $arch ) | ||
|
||
hash=$( hash_file $layer_archive | awk '{ print $1 }' ) | ||
|
||
bucket_name="${BUCKET_PREFIX}-${region}" | ||
s3_key="$( s3_prefix $runtime_name )/${hash}.${arch}.zip" | ||
|
||
echo "Uploading ${layer_archive} to s3://${bucket_name}/${s3_key}" | ||
aws --region "$region" s3 cp $layer_archive "s3://${bucket_name}/${s3_key}" | ||
|
||
echo "Publishing ${runtime_name} layer to ${region}" | ||
layer_output=$(aws lambda publish-layer-version \ | ||
--layer-name ${layer_name} \ | ||
--content "S3Bucket=${bucket_name},S3Key=${s3_key}" \ | ||
--description "New Relic Test Layer for ${runtime_name} (${arch})" \ | ||
--license-info "Apache-2.0" \ | ||
--region "$region" \ | ||
--output json) | ||
|
||
layer_version=$(echo $layer_output | jq -r '.Version') | ||
layer_arn=$(echo $layer_output | jq -r '.LayerArn') | ||
|
||
echo "Published ${runtime_name} layer version ${layer_version} to ${region}" | ||
echo "Layer ARN: ${layer_arn}" | ||
full_layer_arn="${layer_arn}:${layer_version}" | ||
|
||
echo "Published ${runtime_name} layer version ${layer_version} to ${region}" | ||
echo "Full Layer ARN with version: ${full_layer_arn}" | ||
|
||
arch_upper=$(echo "$arch" | tr '[:lower:]' '[:upper:]') | ||
runtime_nodots=$(echo "${runtime_name//./}" | tr '[:lower:]' '[:upper:]') | ||
|
||
env_var_name="LAYER_ARN_${arch_upper}_${runtime_nodots}" | ||
echo $env_var_name | ||
declare "$env_var_name=$full_layer_arn" | ||
|
||
echo "export $env_var_name='$full_layer_arn'" >> $TMP_ENV_FILE_NAME | ||
} | ||
|
||
|
||
function build_python_version { | ||
version=$1 | ||
arch=$2 | ||
dist_dir=$3 | ||
|
||
echo "Building New Relic layer for python$version ($arch)" | ||
rm -rf $BUILD_DIR $dist_dir | ||
mkdir -p $DIST_DIR | ||
pip3 install --no-cache-dir -qU newrelic newrelic-lambda -t $BUILD_DIR/lib/python$version/site-packages | ||
cp newrelic_lambda_wrapper.py $BUILD_DIR/lib/python$version/site-packages/newrelic_lambda_wrapper.py | ||
find $BUILD_DIR -name '__pycache__' -exec rm -rf {} + | ||
download_extension $arch | ||
zip -rq $dist_dir $BUILD_DIR $EXTENSION_DIST_DIR $EXTENSION_DIST_PREVIEW_FILE | ||
rm -rf $BUILD_DIR $EXTENSION_DIST_DIR $EXTENSION_DIST_PREVIEW_FILE | ||
echo "Build complete: ${dist_dir}" | ||
} | ||
|
||
function publish_python_version { | ||
dist_dir=$1 | ||
arch=$2 | ||
version=$3 | ||
regions=("${@:4}") | ||
|
||
if [ ! -f $dist_dir ]; then | ||
echo "Package not found: ${dist_dir}" | ||
exit 1 | ||
fi | ||
|
||
for region in "${regions[@]}"; do | ||
publish_layer $dist_dir $region python$version $arch | ||
done | ||
} | ||
|
||
if [ -f "$TMP_ENV_FILE_NAME" ]; then | ||
rm -r "$TMP_ENV_FILE_NAME" | ||
else | ||
echo "File $TMP_ENV_FILE_NAME does not exist." | ||
fi | ||
|
||
|
||
# Build and publish for python3.11 arm64 | ||
echo "Building and publishing for Python 3.11 ARM64..." | ||
build_python_version "3.11" "arm64" $PY311_DIST_ARM64 | ||
publish_python_version $PY311_DIST_ARM64 "arm64" "3.11" "${REGIONS_ARM[@]}" | ||
|
||
# Build and publish for python3.11 x86_64 | ||
echo "Building and publishing for Python 3.11 x86_64..." | ||
build_python_version "3.11" "x86_64" $PY311_DIST_X86_64 | ||
publish_python_version $PY311_DIST_X86_64 "x86_64" "3.11" "${REGIONS_X86[@]}" | ||
|
||
# Build and publish for python3.12 arm64 | ||
echo "Building and publishing for Python 3.12 ARM64..." | ||
build_python_version "3.12" "arm64" $PY311_DIST_ARM64 | ||
publish_python_version $PY311_DIST_ARM64 "arm64" "3.12" "${REGIONS_ARM[@]}" | ||
|
||
# Build and publish for python3.12 x86_64 | ||
echo "Building and publishing for Python 3.11 x86_64..." | ||
build_python_version "3.12" "x86_64" $PY311_DIST_X86_64 | ||
publish_python_version $PY311_DIST_X86_64 "x86_64" "3.12" "${REGIONS_X86[@]}" |
Oops, something went wrong.