Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add attrs for aws lambda entity #1227

Open
wants to merge 22 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
61c9ebb
Add attrs for aws lambda entity
hmstepanek Oct 7, 2024
deb74bf
Merge branch 'main' into support-lambda-entities
mergify[bot] Nov 8, 2024
b99c886
Merge branch 'main' into support-lambda-entities
mergify[bot] Nov 8, 2024
14e3e5e
Merge branch 'main' into support-lambda-entities
mergify[bot] Nov 11, 2024
a855845
Merge branch 'main' into support-lambda-entities
mergify[bot] Nov 11, 2024
088b8fb
Merge branch 'main' into support-lambda-entities
mergify[bot] Nov 14, 2024
91a2b75
Merge branch 'main' into support-lambda-entities
mergify[bot] Nov 18, 2024
84d977a
Store arn on events obj instead of transaction
hmstepanek Nov 19, 2024
616eed9
Merge branch 'support-lambda-entities' of github.com:newrelic/newreli…
hmstepanek Nov 19, 2024
456660a
Merge branch 'main' into support-lambda-entities
mergify[bot] Nov 21, 2024
678a35c
Reformat iam create role
hmstepanek Nov 22, 2024
d22eeb6
Merge branch 'main' into support-lambda-entities
mergify[bot] Nov 22, 2024
c2f4f20
Merge branch 'main' into support-lambda-entities
mergify[bot] Nov 22, 2024
50aebae
Merge branch 'main' into support-lambda-entities
mergify[bot] Nov 25, 2024
1b8425c
Merge branch 'main' into support-lambda-entities
mergify[bot] Nov 25, 2024
4e363ea
Merge branch 'main' into support-lambda-entities
mergify[bot] Nov 25, 2024
5c97248
Add attrs for aws lambda entity
hmstepanek Oct 7, 2024
71ce408
Store arn on events obj instead of transaction
hmstepanek Nov 19, 2024
86c8362
Reformat iam create role
hmstepanek Nov 22, 2024
d6347c9
Attach arn to api_params & request_dict
hmstepanek Nov 22, 2024
c1fde51
Merge branch 'support-lambda-entities' of github.com:newrelic/newreli…
hmstepanek Nov 26, 2024
ec94f91
Merge branch 'main' into support-lambda-entities
mergify[bot] Nov 26, 2024
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
1 change: 1 addition & 0 deletions newrelic/core/attribute.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"aws.operation",
"aws.requestId",
"cloud.account.id",
"cloud.platform",
"cloud.region",
"cloud.resource_id",
"code.filepath",
Expand Down
46 changes: 44 additions & 2 deletions newrelic/hooks/external_botocore.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
wrap_function_wrapper,
)
from newrelic.common.package_version_utils import get_package_version
from newrelic.common.signature import bind_args
from newrelic.core.config import global_settings

QUEUE_URL_PATTERN = re.compile(r"https://sqs.([\w\d-]+).amazonaws.com/(\d+)/([^/]+)")
Expand Down Expand Up @@ -1001,6 +1002,38 @@ def _nr_sqs_message_trace_wrapper_(wrapped, instance, args, kwargs):
return _nr_sqs_message_trace_wrapper_


def wrap_emit_api_params(wrapped, instance, args, kwargs):
transaction = current_transaction()
if not transaction:
return wrapped(*args, **kwargs)

bound_args = bind_args(wrapped, args, kwargs)

api_params = wrapped(*args, **kwargs)

arn = bound_args.get("api_params").get("FunctionName")
if arn and hasattr(arn, "startswith") and arn.startswith("arn:"):
api_params["_nr_arn"] = arn

return api_params


def wrap_convert_to_request_dict(wrapped, instance, args, kwargs):
transaction = current_transaction()
if not transaction:
return wrapped(*args, **kwargs)

bound_args = bind_args(wrapped, args, kwargs)
arn = bound_args.get("api_params").pop("_nr_arn", None)

request_dict = wrapped(*args, **kwargs)

if arn:
request_dict["_nr_arn"] = arn

return request_dict


CUSTOM_TRACE_POINTS = {
("sns", "publish"): message_trace("SNS", "Produce", "Topic", extract(("TopicArn", "TargetArn"), "PhoneNumber")),
("dynamodb", "put_item"): dynamodb_datastore_trace("DynamoDB", extract("TableName"), "put_item"),
Expand Down Expand Up @@ -1063,6 +1096,11 @@ def _nr_endpoint_make_request_(wrapped, instance, args, kwargs):
with ExternalTrace(library="botocore", url=url, method=method, source=wrapped) as trace:
try:
trace._add_agent_attribute("aws.operation", operation_model.name)
bound_args = bind_args(wrapped, args, kwargs)
lambda_arn = bound_args.get("request_dict").pop("_nr_arn", None)
if lambda_arn:
trace._add_agent_attribute("cloud.platform", "aws_lambda")
trace._add_agent_attribute("cloud.resource_id", lambda_arn)
except:
pass

Expand All @@ -1080,5 +1118,9 @@ def instrument_botocore_endpoint(module):


def instrument_botocore_client(module):
wrap_function_wrapper(module, "ClientCreator._create_api_method", _nr_clientcreator__create_api_method_)
wrap_function_wrapper(module, "ClientCreator._create_methods", _nr_clientcreator__create_methods)
if hasattr(module, "ClientCreator"):
wrap_function_wrapper(module, "ClientCreator._create_api_method", _nr_clientcreator__create_api_method_)
wrap_function_wrapper(module, "ClientCreator._create_methods", _nr_clientcreator__create_methods)
if hasattr(module, "BaseClient"):
wrap_function_wrapper(module, "BaseClient._convert_to_request_dict", wrap_convert_to_request_dict)
wrap_function_wrapper(module, "BaseClient._emit_api_params", wrap_emit_api_params)
122 changes: 122 additions & 0 deletions tests/external_botocore/test_boto3_lambda.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# Copyright 2010 New Relic, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import io
import json
import zipfile

import boto3
import pytest
from moto import mock_aws
from testing_support.fixtures import dt_enabled
from testing_support.validators.validate_span_events import validate_span_events
from testing_support.validators.validate_transaction_metrics import (
validate_transaction_metrics,
)

from newrelic.api.background_task import background_task
from newrelic.common.package_version_utils import get_package_version_tuple

MOTO_VERSION = get_package_version_tuple("moto")
BOTOCORE_VERSION = get_package_version_tuple("botocore")

AWS_ACCESS_KEY_ID = "AAAAAAAAAAAACCESSKEY"
AWS_SECRET_ACCESS_KEY = "AAAAAASECRETKEY" # nosec
AWS_REGION_NAME = "us-west-2"

LAMBDA_URL = "lambda.us-west-2.amazonaws.com"
EXPECTED_LAMBDA_URL = f"https://{LAMBDA_URL}/2015-03-31/functions"
LAMBDA_ARN = f"arn:aws:lambda:{AWS_REGION_NAME}:383735328703:function:lambdaFunction"


_lambda_scoped_metrics = [
(f"External/{LAMBDA_URL}/botocore/POST", 2),
]

_lambda_rollup_metrics = [
("External/all", 3),
("External/allOther", 3),
(f"External/{LAMBDA_URL}/all", 2),
(f"External/{LAMBDA_URL}/botocore/POST", 2),
]


@dt_enabled
@validate_span_events(exact_agents={"aws.operation": "CreateFunction"}, count=1)
@validate_span_events(
exact_agents={"aws.operation": "Invoke", "cloud.platform": "aws_lambda", "cloud.resource_id": LAMBDA_ARN}, count=1
)
@validate_span_events(exact_agents={"aws.operation": "Invoke"}, count=1)
@validate_span_events(exact_agents={"http.url": EXPECTED_LAMBDA_URL}, count=1)
@validate_transaction_metrics(
"test_boto3_lambda:test_lambda",
scoped_metrics=_lambda_scoped_metrics,
rollup_metrics=_lambda_rollup_metrics,
background_task=True,
)
@background_task()
@mock_aws
def test_lambda(iam_role_arn, lambda_zip):
role_arn = iam_role_arn()

client = boto3.client(
"lambda",
aws_access_key_id=AWS_ACCESS_KEY_ID,
aws_secret_access_key=AWS_SECRET_ACCESS_KEY,
region_name=AWS_REGION_NAME,
)

# Create lambda
resp = client.create_function(
FunctionName="lambdaFunction", Runtime="python3.9", Role=role_arn, Code={"ZipFile": lambda_zip}
)
assert resp["ResponseMetadata"]["HTTPStatusCode"] == 201

# Invoke lambda
client.invoke(FunctionName=LAMBDA_ARN, InvocationType="RequestResponse", Payload=json.dumps({}))
assert resp["ResponseMetadata"]["HTTPStatusCode"] == 201


@pytest.fixture
def lambda_zip():
code = """
def lambda_handler(event, context):
return event
"""
zip_output = io.BytesIO()
zip_file = zipfile.ZipFile(zip_output, "w", zipfile.ZIP_DEFLATED)
zip_file.writestr("lambda_function.py", code)
zip_file.close()
zip_output.seek(0)
return zip_output.read()


@pytest.fixture
def iam_role_arn():
def create_role():
iam = boto3.client(
"iam",
aws_access_key_id=AWS_ACCESS_KEY_ID,
aws_secret_access_key=AWS_SECRET_ACCESS_KEY,
region_name=AWS_REGION_NAME,
)
# Create IAM role
role = iam.create_role(
RoleName="my-role",
AssumeRolePolicyDocument="some policy",
Path="/my-path/",
)
return role["Role"]["Arn"]

return create_role
1 change: 1 addition & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,7 @@ deps =
external_botocore-botocore128: botocore<1.29
external_botocore-botocore0125: botocore<1.26
external_botocore: moto
external_botocore: docker
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the docker dependency for? Lambda/moto?

Copy link
Contributor Author

@hmstepanek hmstepanek Nov 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it's imported inside the lambda invoke in moto.

external_feedparser-feedparser06: feedparser<7
external_httplib2: httplib2<1.0
external_httpx: httpx[http2]
Expand Down
Loading