From 064a1bd43231c2a4c20d6d503864b2798fd74f4b Mon Sep 17 00:00:00 2001 From: peterstone2017 <12449837+YunchuWang@users.noreply.github.com> Date: Mon, 6 May 2024 16:42:32 -0700 Subject: [PATCH 1/4] Http V2 route params update Worker Changes to get route parameters of http invocation request from invocation request "route_params" attributes after host made update in https://github.com/Azure/azure-functions-host/pull/9997. TODO: Update lc test after lc image released with host version containing the change. --- azure_functions_worker/constants.py | 2 +- azure_functions_worker/dispatcher.py | 11 ++++++++--- azure_functions_worker/http_v2.py | 7 ++----- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/azure_functions_worker/constants.py b/azure_functions_worker/constants.py index 69c59ad0b..9b975d7cd 100644 --- a/azure_functions_worker/constants.py +++ b/azure_functions_worker/constants.py @@ -11,7 +11,7 @@ SHARED_MEMORY_DATA_TRANSFER = "SharedMemoryDataTransfer" FUNCTION_DATA_CACHE = "FunctionDataCache" HTTP_URI = "HttpUri" - +REQUIRES_ROUTE_PARAMETERS = "RequiresRouteParameters" # When this capability is enabled, logs are not piped back to the # host from the worker. Logs will directly go to where the user has # configured them to go. This is to ensure that the logs are not diff --git a/azure_functions_worker/dispatcher.py b/azure_functions_worker/dispatcher.py index 902e9d86e..cff7ad6e7 100644 --- a/azure_functions_worker/dispatcher.py +++ b/azure_functions_worker/dispatcher.py @@ -32,7 +32,8 @@ PYTHON_LANGUAGE_RUNTIME, PYTHON_ENABLE_INIT_INDEXING, METADATA_PROPERTIES_WORKER_INDEXED, PYTHON_ENABLE_OPENTELEMETRY, - PYTHON_ENABLE_OPENTELEMETRY_DEFAULT) + PYTHON_ENABLE_OPENTELEMETRY_DEFAULT, + REQUIRES_ROUTE_PARAMETERS) from .extension import ExtensionManager from .http_v2 import http_coordinator, initialize_http_server, HttpV2Registry, \ sync_http_request, HttpServerInitError @@ -346,6 +347,7 @@ async def _handle__worker_init_request(self, request): if HttpV2Registry.http_v2_enabled(): capabilities[constants.HTTP_URI] = \ initialize_http_server(self._host) + capabilities[REQUIRES_ROUTE_PARAMETERS] = _TRUE except HttpServerInitError: raise @@ -578,8 +580,10 @@ async def _handle__invocation_request(self, request): http_request = await http_coordinator.get_http_request_async( invocation_id) - await sync_http_request(http_request, invoc_request) - args[fi.trigger_metadata.get('param_name')] = http_request + trigger_arg_name = fi.trigger_metadata.get('param_name') + func_http_request = args[trigger_arg_name] + await sync_http_request(http_request, func_http_request) + args[trigger_arg_name] = http_request fi_context = self._get_context(invoc_request, fi.name, fi.directory) @@ -730,6 +734,7 @@ async def _handle__function_environment_reload_request(self, request): if HttpV2Registry.http_v2_enabled(): capabilities[constants.HTTP_URI] = \ initialize_http_server(self._host) + capabilities[REQUIRES_ROUTE_PARAMETERS] = _TRUE except HttpServerInitError: raise except Exception as ex: diff --git a/azure_functions_worker/http_v2.py b/azure_functions_worker/http_v2.py index d7fff59b6..97d91fe6d 100644 --- a/azure_functions_worker/http_v2.py +++ b/azure_functions_worker/http_v2.py @@ -243,14 +243,11 @@ async def catch_all(request: request_type): # type: ignore from e -async def sync_http_request(http_request, invoc_request): +async def sync_http_request(http_request, func_http_request): # Sync http request route params from invoc_request to http_request - route_params = {key: item.string for key, item - in invoc_request.trigger_metadata.items() - if key not in ['Headers', 'Query']} (HttpV2Registry.ext_base().RequestTrackerMeta .get_synchronizer() - .sync_route_params(http_request, route_params)) + .sync_route_params(http_request, func_http_request.route_params)) class HttpV2Registry: From e998cf42f9fbf6bb05fcbe25858d8f9875dbf057 Mon Sep 17 00:00:00 2001 From: peterstone2017 <12449837+YunchuWang@users.noreply.github.com> Date: Tue, 10 Sep 2024 17:36:01 -0700 Subject: [PATCH 2/4] test: add unit tests --- tests/unittests/test_dispatcher.py | 64 +++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 2 deletions(-) diff --git a/tests/unittests/test_dispatcher.py b/tests/unittests/test_dispatcher.py index 32eca34bf..8f540e245 100644 --- a/tests/unittests/test_dispatcher.py +++ b/tests/unittests/test_dispatcher.py @@ -20,7 +20,7 @@ PYTHON_THREADPOOL_THREAD_COUNT, PYTHON_THREADPOOL_THREAD_COUNT_DEFAULT, PYTHON_THREADPOOL_THREAD_COUNT_MAX_37, - PYTHON_THREADPOOL_THREAD_COUNT_MIN, + PYTHON_THREADPOOL_THREAD_COUNT_MIN, HTTP_URI, REQUIRES_ROUTE_PARAMETERS, ) from azure_functions_worker.dispatcher import Dispatcher, ContextEnabledTask from azure_functions_worker.version import VERSION @@ -32,7 +32,9 @@ 'dispatcher_functions' / \ 'dispatcher_functions_stein' FUNCTION_APP_DIRECTORY = UNIT_TESTS_ROOT / 'dispatcher_functions' / \ - 'dispatcher_functions_stein' + 'dispatcher_functions_stein' +HTTPV2_FUNCTION_APP_DIRECTORY = UNIT_TESTS_ROOT / 'dispatcher_functions' / \ + 'http_v2' / 'fastapi' class TestThreadPoolSettingsPython37(testutils.AsyncTestCase): @@ -767,6 +769,7 @@ def setUp(self): asyncio.set_event_loop(self.loop) self.dispatcher = testutils.create_dummy_dispatcher() sys.path.append(str(FUNCTION_APP_DIRECTORY)) + sys.path.append(str(HTTPV2_FUNCTION_APP_DIRECTORY)) def tearDown(self): self.loop.close() @@ -991,6 +994,63 @@ def test_dispatcher_indexing_in_load_request_with_exception( response.function_load_response.result.exception.message, "Exception: Mocked Exception") + @patch.dict(os.environ, {PYTHON_ENABLE_INIT_INDEXING: 'true'}) + @patch("azure_functions_worker.http_v2.HttpV2Registry.http_v2_enabled", + return_value=True) + def test_dispatcher_http_v2_init_request_fail(self, mock_http_v2_enabled): + request = protos.StreamingMessage( + worker_init_request=protos.WorkerInitRequest( + host_version="2.3.4", + function_app_directory=str(HTTPV2_FUNCTION_APP_DIRECTORY) + ) + ) + + resp = self.loop.run_until_complete( + self.dispatcher._handle__worker_init_request(request) + ) + + mock_http_v2_enabled.assert_called_once() + self.assertIsNotNone(self.dispatcher._function_metadata_exception) + + capabilities = resp.worker_init_response.capabilities + self.assertNotIn(HTTP_URI, capabilities) + self.assertNotIn(REQUIRES_ROUTE_PARAMETERS, capabilities) + + # Cleanup + del sys.modules['function_app'] + + @patch.dict(os.environ, {PYTHON_ENABLE_INIT_INDEXING: 'true'}) + @patch("azure_functions_worker.http_v2.HttpV2Registry.http_v2_enabled", + return_value=True) + @patch("azure_functions_worker.dispatcher.initialize_http_server", + return_value="http://localhost:8080") + @patch("azure_functions_worker.dispatcher.Dispatcher" + ".load_function_metadata") + def test_dispatcher_http_v2_init_request_pass(self, mock_http_v2_enabled, + mock_init_http_server, + mock_load_func_metadata): + request = protos.StreamingMessage( + worker_init_request=protos.WorkerInitRequest( + host_version="2.3.4", + function_app_directory=str(HTTPV2_FUNCTION_APP_DIRECTORY) + ) + ) + + resp = self.loop.run_until_complete( + self.dispatcher._handle__worker_init_request(request) + ) + + mock_http_v2_enabled.assert_called_once() + mock_init_http_server.assert_called_once() + mock_load_func_metadata.assert_called_once() + self.assertIsNone(self.dispatcher._function_metadata_exception) + + capabilities = resp.worker_init_response.capabilities + self.assertIn(HTTP_URI, capabilities) + self.assertEqual(capabilities[HTTP_URI], "http://localhost:8080") + self.assertIn(REQUIRES_ROUTE_PARAMETERS, capabilities) + self.assertEqual(capabilities[REQUIRES_ROUTE_PARAMETERS], "true") + class TestContextEnabledTask(unittest.TestCase): def setUp(self): From ec6cb334630a17cdd815d1a7e0ab92e779831cc8 Mon Sep 17 00:00:00 2001 From: peterstone2017 <12449837+YunchuWang@users.noreply.github.com> Date: Tue, 10 Sep 2024 18:40:03 -0700 Subject: [PATCH 3/4] chore: fix linting --- tests/unittests/test_dispatcher.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unittests/test_dispatcher.py b/tests/unittests/test_dispatcher.py index 8f540e245..2bb7efdb1 100644 --- a/tests/unittests/test_dispatcher.py +++ b/tests/unittests/test_dispatcher.py @@ -32,9 +32,9 @@ 'dispatcher_functions' / \ 'dispatcher_functions_stein' FUNCTION_APP_DIRECTORY = UNIT_TESTS_ROOT / 'dispatcher_functions' / \ - 'dispatcher_functions_stein' + 'dispatcher_functions_stein' HTTPV2_FUNCTION_APP_DIRECTORY = UNIT_TESTS_ROOT / 'dispatcher_functions' / \ - 'http_v2' / 'fastapi' + 'http_v2' / 'fastapi' class TestThreadPoolSettingsPython37(testutils.AsyncTestCase): From a8f97bbf3099101da9261d7207094cbadb327868 Mon Sep 17 00:00:00 2001 From: peterstone2017 <12449837+YunchuWang@users.noreply.github.com> Date: Wed, 11 Sep 2024 14:23:32 -0700 Subject: [PATCH 4/4] fix: linting --- azure_functions_worker/dispatcher.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/azure_functions_worker/dispatcher.py b/azure_functions_worker/dispatcher.py index 366db6a71..b9e350610 100644 --- a/azure_functions_worker/dispatcher.py +++ b/azure_functions_worker/dispatcher.py @@ -39,7 +39,8 @@ PYTHON_THREADPOOL_THREAD_COUNT_DEFAULT, PYTHON_THREADPOOL_THREAD_COUNT_MAX_37, PYTHON_THREADPOOL_THREAD_COUNT_MIN, - REQUIRES_ROUTE_PARAMETERS + REQUIRES_ROUTE_PARAMETERS, + HTTP_URI ) from .extension import ExtensionManager from .http_v2 import ( @@ -404,7 +405,7 @@ async def _handle__worker_init_request(self, request): caller_info="worker_init_request") if HttpV2Registry.http_v2_enabled(): - capabilities[constants.HTTP_URI] = \ + capabilities[HTTP_URI] = \ initialize_http_server(self._host) capabilities[REQUIRES_ROUTE_PARAMETERS] = _TRUE @@ -794,7 +795,7 @@ async def _handle__function_environment_reload_request(self, request): caller_info="environment_reload_request") if HttpV2Registry.http_v2_enabled(): - capabilities[constants.HTTP_URI] = \ + capabilities[HTTP_URI] = \ initialize_http_server(self._host) capabilities[REQUIRES_ROUTE_PARAMETERS] = _TRUE except HttpServerInitError: