Skip to content

Commit becbf73

Browse files
committed
Refactor testing setup
1 parent 0f63551 commit becbf73

File tree

18 files changed

+540
-172
lines changed

18 files changed

+540
-172
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#!/bin/bash
2+
set -euo pipefail
3+
4+
# Create build dir
5+
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
6+
BUILD_DIR="${TOX_ENV_DIR:-${SCRIPT_DIR}}/build/azure-functions-worker"
7+
rm -rf ${BUILD_DIR}
8+
mkdir -p ${BUILD_DIR}
9+
10+
# Clone repository
11+
git clone https://github.com/Azure/azure-functions-python-worker.git ${BUILD_DIR}
12+
13+
# Setup virtual environment and install dependencies
14+
python -m venv "${BUILD_DIR}/.venv"
15+
PYTHON="${BUILD_DIR}/.venv/bin/python"
16+
PIP="${BUILD_DIR}/.venv/bin/pip"
17+
PIPCOMPILE="${BUILD_DIR}/.venv/bin/pip-compile"
18+
INVOKE="${BUILD_DIR}/.venv/bin/invoke"
19+
${PIP} install pip-tools build invoke
20+
21+
# Install proto build dependencies
22+
$(cd ${BUILD_DIR} && ${PIPCOMPILE} >${BUILD_DIR}/requirements.txt)
23+
${PIP} install -r ${BUILD_DIR}/requirements.txt
24+
25+
# Build proto files into pb2 files
26+
cd ${BUILD_DIR}/tests && ${INVOKE} -c test_setup build-protos
27+
28+
# Build and install the package into the original environment (not the build venv)
29+
pip install ${BUILD_DIR}
30+
31+
# Clean up and return to the original directory
32+
rm -rf ${BUILD_DIR}

newrelic/common/utilization.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
AZURE_RESOURCE_GROUP_NAME_RE = re.compile(r"\+([a-zA-Z0-9\-]+)-[a-zA-Z0-9]+(?:-Linux)")
3030
AZURE_RESOURCE_GROUP_NAME_PARTIAL_RE = re.compile(r"\+([a-zA-Z0-9\-]+)(?:-Linux)?-[a-zA-Z0-9]+")
3131

32+
3233
class UtilizationHttpClient(InsecureHttpClient):
3334
SOCKET_TIMEOUT = 0.05
3435

@@ -228,9 +229,7 @@ def fetch():
228229
else:
229230
resource_group_name = AZURE_RESOURCE_GROUP_NAME_PARTIAL_RE.search(website_owner_name).group(1)
230231
subscription_id = re.search(r"(?:(?!\+).)*", website_owner_name).group(0)
231-
faas_app_name = "/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Web/sites/{2}".format(
232-
subscription_id, resource_group_name, azure_function_app_name
233-
)
232+
faas_app_name = f"/subscriptions/{subscription_id}/resourceGroups/{resource_group_name}/providers/Microsoft.Web/sites/{azure_function_app_name}"
234233
# Only send if all values are present
235234
return (faas_app_name, cloud_region)
236235

newrelic/config.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4092,7 +4092,9 @@ def _process_module_builtin_defaults():
40924092
)
40934093
_process_module_definition("tornado.routing", "newrelic.hooks.framework_tornado", "instrument_tornado_routing")
40944094
_process_module_definition("tornado.web", "newrelic.hooks.framework_tornado", "instrument_tornado_web")
4095-
_process_module_definition("azure.functions._http", "newrelic.hooks.serverless_azure", "instrument_azure__http")
4095+
_process_module_definition(
4096+
"azure.functions._http", "newrelic.hooks.serverless_azure", "instrument_azure_function__http"
4097+
)
40964098
_process_module_definition(
40974099
"azure_functions_worker.dispatcher",
40984100
"newrelic.hooks.serverless_azure",

newrelic/hooks/serverless_azure.py

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020
from newrelic.api.transaction import current_transaction
2121
from newrelic.api.web_transaction import WebTransaction
2222
from newrelic.common.object_wrapper import wrap_function_wrapper
23-
from newrelic.common.utilization import AZURE_RESOURCE_GROUP_NAME_RE, AZURE_RESOURCE_GROUP_NAME_PARTIAL_RE
2423
from newrelic.common.signature import bind_args
24+
from newrelic.common.utilization import AZURE_RESOURCE_GROUP_NAME_PARTIAL_RE, AZURE_RESOURCE_GROUP_NAME_RE
2525

2626

2727
def original_agent_instance():
@@ -65,7 +65,6 @@ def intrinsics_populator(application, context):
6565
# the application if not already registered with the collector as well
6666
# as determining if this invocation was a cold start or not.
6767
async def wrap_dispatcher__handle__invocation_request(wrapped, instance, args, kwargs):
68-
6968
# Logic to determine if this is a cold start since we are not
7069
# able to access the logic in the __init__ method of the Dispatcher
7170
# class with Python (in the Portal, this is not done in Python)
@@ -97,7 +96,7 @@ async def wrap_dispatcher__run_async_func(wrapped, instance, args, kwargs):
9796

9897
context = bound_args.get("context", None)
9998
func = bound_args.get("func", None)
100-
params = bound_args.get("args", None)
99+
params = bound_args.get("params", None)
101100

102101
application = original_agent_instance()
103102

@@ -108,7 +107,11 @@ async def wrap_dispatcher__run_async_func(wrapped, instance, args, kwargs):
108107
url_split = urlparse.urlsplit(getattr(http_request, "url", ":///?#"))
109108
scheme = getattr(url_split, "scheme", None)
110109
query = getattr(url_split, "query", None)
111-
host, port = (getattr(url_split, "netloc", None), None) if (":" not in getattr(url_split, "netloc", None)) else getattr(url_split, "netloc", None).split(":")
110+
host, port = (
111+
(getattr(url_split, "netloc", None), None)
112+
if (":" not in getattr(url_split, "netloc", None))
113+
else getattr(url_split, "netloc", None).split(":")
114+
)
112115
break
113116

114117
# If this is an HTTP http_request object, create a web transaction.
@@ -161,7 +164,7 @@ def wrap_dispatcher__run_sync_func(wrapped, instance, args, kwargs):
161164
context = bound_args.get("context", None)
162165
func = bound_args.get("func", None)
163166
params = bound_args.get("params", None)
164-
167+
165168
application = original_agent_instance()
166169

167170
http_request = None
@@ -171,7 +174,11 @@ def wrap_dispatcher__run_sync_func(wrapped, instance, args, kwargs):
171174
url_split = urlparse.urlsplit(getattr(http_request, "url", ":///?#"))
172175
scheme = getattr(url_split, "scheme", None)
173176
query = getattr(url_split, "query", None)
174-
host, port = (getattr(url_split, "netloc", None), None) if (":" not in getattr(url_split, "netloc", None)) else getattr(url_split, "netloc", None).split(":")
177+
host, port = (
178+
(getattr(url_split, "netloc", None), None)
179+
if (":" not in getattr(url_split, "netloc", None))
180+
else getattr(url_split, "netloc", None).split(":")
181+
)
175182
break
176183

177184
# If this is an HTTP Request object, we can create a web transaction
@@ -216,7 +223,6 @@ def wrap_dispatcher__run_sync_func(wrapped, instance, args, kwargs):
216223

217224

218225
def wrap_httpresponse__init__(wrapped, instance, args, kwargs):
219-
220226
transaction = current_transaction()
221227
if not transaction:
222228
return wrapped(*args, **kwargs)
@@ -225,14 +231,14 @@ def wrap_httpresponse__init__(wrapped, instance, args, kwargs):
225231

226232
status_code = bound_args.get("status_code", None)
227233
headers = bound_args.get("headers", None)
228-
234+
229235
if status_code:
230236
transaction.process_response(status_code, headers)
231237

232238
return wrapped(*args, **kwargs)
233239

234240

235-
def instrument_azure__http(module):
241+
def instrument_azure_function__http(module):
236242
if hasattr(module, "HttpResponse"):
237243
wrap_function_wrapper(module, "HttpResponse.__init__", wrap_httpresponse__init__)
238244

tests/agent_unittests/test_agent_protocol.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,7 @@ def connect_payload_asserts(
373373
def test_connect(
374374
with_aws, with_ecs, with_pcf, with_gcp, with_azure, with_azurefunction, with_docker, with_kubernetes, with_ip
375375
):
376-
global AWS, AZURE, AZUREFUNCTION, GCP, PCF, BOOT_ID, DOCKER, KUBERNETES, IP_ADDRESS
376+
global AWS, AZURE, AZUREFUNCTION, GCP, PCF, DOCKER, KUBERNETES, IP_ADDRESS
377377
if not with_aws:
378378
AWS = Exception
379379
if not with_pcf:
Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
#!/bin/bash
21
# Copyright 2010 New Relic, Inc.
32
#
43
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,4 +12,3 @@
1312
# See the License for the specific language governing permissions and
1413
# limitations under the License.
1514

16-
python ping_application.py &

tests/serverless_azure/conftest.py

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,10 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
import functools
1516

16-
from testing_support.fixtures import ( # noqa: F401; pylint: disable=W0611
17-
collector_agent_registration_fixture,
18-
collector_available_fixture,
19-
)
17+
import pytest
18+
from testing_support.fixtures import collector_agent_registration_fixture, collector_available_fixture
2019

2120
_default_settings = {
2221
"package_reporting.enabled": False, # Turn off package reporting for testing as it causes slow downs.
@@ -30,3 +29,32 @@
3029
collector_agent_registration = collector_agent_registration_fixture(
3130
app_name="Python Agent Test (serverless_azure)", default_settings=_default_settings
3231
)
32+
33+
34+
@pytest.fixture(scope="session", autouse=True)
35+
def dispatcher():
36+
"""Fixture that starts the worker on a separate thread and returns the dispatcher instance."""
37+
from .sample_app.worker import start_dispatcher_thread
38+
39+
# Start the dispatcher thread
40+
dispatcher_ = start_dispatcher_thread()
41+
42+
# Return the dispatcher instance for use in tests
43+
yield dispatcher_
44+
45+
# Stop worker gracefully
46+
dispatcher_.stop()
47+
48+
49+
@pytest.fixture(scope="session", params=["sync", "async"])
50+
def is_async(request):
51+
"""Fixture that parametrizes the endpoint type between sync and async."""
52+
return request.param == "async"
53+
54+
55+
@pytest.fixture(scope="session")
56+
def send_invocation(is_async, dispatcher):
57+
"""Fixture that returns the _send_event interface."""
58+
from .sample_app.messages import send_invocation_event
59+
60+
return functools.partial(send_invocation_event, dispatcher, is_async=is_async)

tests/serverless_azure/function_app.py

Lines changed: 0 additions & 35 deletions
This file was deleted.

tests/serverless_azure/host.json

Lines changed: 0 additions & 13 deletions
This file was deleted.

tests/serverless_azure/local.settings.json

Lines changed: 0 additions & 13 deletions
This file was deleted.

0 commit comments

Comments
 (0)