Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
version: 2
updates:
# Enable version updates for python
- package-ecosystem: "pip"
# Look for `requirements.txt` file in the `dev-utils` directory
directory: "/dev-utils/"
# Check for updates once a week
schedule:
interval: "weekly"
reviewers:
- "elastic/apm-agent-python"
4 changes: 4 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Pin to Alpine 3.17.3
FROM alpine@sha256:124c7d2707904eea7431fffe91522a01e5a861a624ee31d03372cc1d138a3126
ARG AGENT_DIR
COPY ${AGENT_DIR} /opt/python
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
BSD 3-Clause License

Copyright (c) 2009-2012, David Cramer and individual contributors
Copyright (c) 2013-2018, Elasticsearch BV
Copyright (c) 2013-2023, Elasticsearch BV
All rights reserved.

Redistribution and use in source and binary forms, with or without
Expand Down
404 changes: 67 additions & 337 deletions NOTICE.md

Large diffs are not rendered by default.

58 changes: 58 additions & 0 deletions dev-utils/make-distribution.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#!/bin/bash
#
# Make a Python APM agent distribution that is used as follows:
# - "build/dist/elastic-apm-python-lambda-layer.zip" is published to AWS as a
# Lambda layer (https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html)
# - "build/dist/package/python/..." is used to build a Docker image of the APM agent
#

if [ "$TRACE" != "" ]; then
export PS4='${BASH_SOURCE}:${LINENO}: ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'
set -o xtrace
fi
set -o errexit
set -o pipefail

# ---- support functions

function fatal {
echo "$(basename $0): error: $*"
exit 1
}

# ---- mainline

TOP=$(cd $(dirname $0)/../ >/dev/null; pwd)
BUILD_DIR="$TOP/build/dist"

if ! command -v pip >/dev/null 2>&1; then
fatal "pip is unavailable"
fi

rm -rf "$BUILD_DIR"
mkdir -p "$BUILD_DIR"
cd "$BUILD_DIR"

rm -f elastic-apm-python-lambda-layer.zip
rm -f requirements.txt
rm -rf package
mkdir package
cp "$TOP/dev-utils/requirements.txt" .
echo "$TOP" >> ./requirements.txt
pip install -r requirements.txt --target ./package/python
cd package
cp python/elasticapm/contrib/serverless/aws_wrapper/elasticapm-lambda.py ./python/bin/elasticapm-lambda
chmod +x ./python/bin/elasticapm-lambda
cp python/elasticapm/contrib/serverless/aws_wrapper/elasticapm_handler.py ./python/
cp "$TOP/elasticapm/contrib/serverless/aws_wrapper/NOTICE.md" ./python/

echo ""
zip -q -r ../elastic-apm-python-lambda-layer.zip .
echo "Created build/dist/elastic-apm-python-lambda-layer.zip"

cd ..


echo
echo "The lambda layer can be published as follows for dev work:"
echo " aws lambda --output json publish-layer-version --layer-name '$USER-dev-elastic-apm-python' --description '$USER dev Elastic APM Python agent lambda layer' --zip-file 'fileb://build/dist/elastic-apm-python-lambda-layer.zip'"
4 changes: 4 additions & 0 deletions dev-utils/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# These are the pinned requirements for the lambda layer/docker image
certifi==2022.12.7
urllib3==1.26.15
wrapt==1.15.0
39 changes: 24 additions & 15 deletions elasticapm/contrib/serverless/aws.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,19 +85,7 @@ def handler(event, context):

name = kwargs.pop("name", None)

# Disable all background threads except for transport
kwargs["metrics_interval"] = "0ms"
kwargs["breakdown_metrics"] = False
if "metrics_sets" not in kwargs and "ELASTIC_APM_METRICS_SETS" not in os.environ:
# Allow users to override metrics sets
kwargs["metrics_sets"] = []
kwargs["central_config"] = False
kwargs["cloud_provider"] = "none"
kwargs["framework_name"] = "AWS Lambda"
if "service_name" not in kwargs and "ELASTIC_APM_SERVICE_NAME" not in os.environ:
kwargs["service_name"] = os.environ["AWS_LAMBDA_FUNCTION_NAME"]
if "service_version" not in kwargs and "ELASTIC_APM_SERVICE_VERSION" not in os.environ:
kwargs["service_version"] = os.environ.get("AWS_LAMBDA_FUNCTION_VERSION")
kwargs = prep_kwargs(kwargs)

global INSTRUMENTED
client = get_client()
Expand All @@ -109,9 +97,9 @@ def handler(event, context):

@functools.wraps(func)
def decorated(*args, **kwds):
if len(args) == 2:
if len(args) >= 2:
# Saving these for request context later
event, context = args
event, context = args[0:2]
else:
event, context = {}, {}

Expand All @@ -125,6 +113,27 @@ def decorated(*args, **kwds):
return decorated


def prep_kwargs(kwargs=None):
if kwargs is None:
kwargs = {}

# Disable all background threads except for transport
kwargs["metrics_interval"] = "0ms"
kwargs["breakdown_metrics"] = False
if "metrics_sets" not in kwargs and "ELASTIC_APM_METRICS_SETS" not in os.environ:
# Allow users to override metrics sets
kwargs["metrics_sets"] = []
kwargs["central_config"] = False
kwargs["cloud_provider"] = "none"
kwargs["framework_name"] = "AWS Lambda"
if "service_name" not in kwargs and "ELASTIC_APM_SERVICE_NAME" not in os.environ:
kwargs["service_name"] = os.environ["AWS_LAMBDA_FUNCTION_NAME"]
if "service_version" not in kwargs and "ELASTIC_APM_SERVICE_VERSION" not in os.environ:
kwargs["service_version"] = os.environ.get("AWS_LAMBDA_FUNCTION_VERSION")

return kwargs


class _lambda_transaction(object):
"""
Context manager for creating transactions around AWS Lambda functions.
Expand Down
92 changes: 92 additions & 0 deletions elasticapm/contrib/serverless/aws_wrapper/NOTICE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
apm-agent-python Copyright 2013-2023 Elasticsearch BV

# Notice

This lambda layer contains several dependencies which have been vendored.

## urllib3

- **author:** Andrey Petrov
- **project url:** https://github.com/urllib3/urllib3
- **license:** MIT License, https://opensource.org/licenses/MIT

MIT License

Copyright (c) 2008-2019 Andrey Petrov and contributors (see CONTRIBUTORS.txt)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

## certifi

- **author:** Kenneth Reitz
- **project url:** https://github.com/certifi/python-certifi
- **license:** Mozilla Public License 2.0, https://opensource.org/licenses/MPL-2.0

This packge contains a modified version of ca-bundle.crt:

ca-bundle.crt -- Bundle of CA Root Certificates

Certificate data from Mozilla as of: Thu Nov 3 19:04:19 2011#
This is a bundle of X.509 certificates of public Certificate Authorities
(CA). These were automatically extracted from Mozilla's root certificates
file (certdata.txt). This file can be found in the mozilla source tree:
http://mxr.mozilla.org/mozilla/source/security/nss/lib/ckfw/builtins/certdata.txt?raw=1#
It contains the certificates in PEM format and therefore
can be directly used with curl / libcurl / php_curl, or with
an Apache+mod_ssl webserver for SSL client authentication.
Just configure this file as the SSLCACertificateFile.#

***** BEGIN LICENSE BLOCK *****
This Source Code Form is subject to the terms of the Mozilla Public License,
v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain
one at http://mozilla.org/MPL/2.0/.

***** END LICENSE BLOCK *****
@(#) $RCSfile: certdata.txt,v $ $Revision: 1.80 $ $Date: 2011/11/03 15:11:58 $

## wrapt

- **author:** Graham Dumpleton
- **project url:** https://github.com/GrahamDumpleton/wrapt
- **license:** BSD-2-Clause, http://opensource.org/licenses/BSD-2-Clause

Copyright (c) 2013, Graham Dumpleton
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
29 changes: 29 additions & 0 deletions elasticapm/contrib/serverless/aws_wrapper/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# BSD 3-Clause License
#
# Copyright (c) 2023, Elasticsearch BV
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# * Neither the name of the copyright holder nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
60 changes: 60 additions & 0 deletions elasticapm/contrib/serverless/aws_wrapper/elasticapm-lambda.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-

# BSD 3-Clause License
#
# Copyright (c) 2023, Elasticsearch BV
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# * Neither the name of the copyright holder nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

import os
import shutil
import sys


class LambdaError(Exception):
pass


if __name__ == "__main__":
original_handler = os.environ.get("_HANDLER", None)
if not original_handler:
raise LambdaError("Cannot find original handler. _HANDLER is not set correctly.")

# AWS Lambda's `/var/runtime/bootstrap.py` uses `imp.load_module` to load
# the handler from "_HANDLER". This means that the handler will be reloaded
# (even if it's already been loaded), and any instrumentation that we do
# will be lost. Thus, we can't use our normal wrapper script, and must
# replace the handler altogether and wrap it manually.
os.environ["ELASTICAPM_ORIGINAL_HANDLER"] = original_handler
os.environ["_HANDLER"] = "elasticapm_handler.lambda_handler"

# Invoke the runtime
args = sys.argv[1:]
runtime = shutil.which(args[0])
args = args[1:]
os.execl(runtime, runtime, *args)
Loading