Skip to content

Commit

Permalink
local testing (#201)
Browse files Browse the repository at this point in the history
* local testing
  • Loading branch information
chaudharysaket authored Mar 20, 2024
1 parent fcec11b commit d7d37c2
Show file tree
Hide file tree
Showing 9 changed files with 531 additions and 0 deletions.
94 changes: 94 additions & 0 deletions local_testing/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
.DS_Store
*.DS_Store
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
11 changes: 11 additions & 0 deletions local_testing/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
## 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. Update the environment.json file with the test account and other variables.
3. Modify the fetch_extension url in publish.sh to your forked repo's extension release url
4. Run `./publish.sh` to publish the layers to your test account `us-west-2` region
5. 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
6. Run `./test.sh` to create lambda with test layer published in step 2
7. Go to your AWS test account and check the logs of the lambda with suffix - `NR_EXTENSION_TEST_LAMBDA_` for any error in extension
8. After the tests, delete the test infra created, using `./delete_infra.sh`. It will delete the 4 test lambda functions.
19 changes: 19 additions & 0 deletions local_testing/delete_infra.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/bin/bash

# Variables
REGION="us-west-2" # Preferred AWS region

runtimes=("python3.11" "python3.12")
architectures=("x86_64" "arm64")

for arch in "${architectures[@]}"; do
for runtime in "${runtimes[@]}"; do
FUNCTION_NAME_SUFFIX="${arch}_${runtime//./}"
FUNCTION_NAME="NR_EXTENSION_TEST_LAMBDA_${FUNCTION_NAME_SUFFIX}"
if aws lambda delete-function --function-name "$FUNCTION_NAME" --region "$REGION"; then
echo "Successfully deleted $FUNCTION_NAME"
else
echo "Failed to delete $FUNCTION_NAME"
fi
done
done
11 changes: 11 additions & 0 deletions local_testing/environment.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"Variables": {
"NEW_RELIC_ACCOUNT_ID": "",
"NEW_RELIC_EXTENSION_SEND_FUNCTION_LOGS": "true",
"NEW_RELIC_LAMBDA_EXTENSION_ENABLED": "true",
"NEW_RELIC_LAMBDA_HANDLER": "lambda_function.lambda_handler",
"NEW_RELIC_LICENSE_KEY": "",
"NEW_RELIC_LOG_ENDPOINT": "",
"NEW_RELIC_TELEMETRY_ENDPOINT": ""
}
}
6 changes: 6 additions & 0 deletions local_testing/function.py
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!'
}
84 changes: 84 additions & 0 deletions local_testing/newrelic_lambda_wrapper.py
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)
Loading

0 comments on commit d7d37c2

Please sign in to comment.