Skip to content

Commit

Permalink
httpx HTTP/2 Testing (#1245)
Browse files Browse the repository at this point in the history
* Add nginx runner for tests

* Reorganize HTTPX instrumentation

* Add HTTPX HTTP/2 testing

* Update cert.pem example command

* Fix HTTPX host routing in tests

* Combine all http ports for nginx

* Update nginx settings

---------

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
  • Loading branch information
TimPansino and mergify[bot] authored Nov 11, 2024
1 parent 4334b5e commit dacf155
Show file tree
Hide file tree
Showing 8 changed files with 214 additions and 82 deletions.
64 changes: 64 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ jobs:
- mongodb
- mssql
- mysql
- nginx
- postgres16
- postgres9
- rabbitmq
Expand Down Expand Up @@ -250,6 +251,69 @@ jobs:
path: ./**/.coverage.*
retention-days: 1

nginx:
env:
TOTAL_GROUPS: 1

strategy:
fail-fast: false
matrix:
group-number: [1]

runs-on: ubuntu-latest
container:
image: ghcr.io/newrelic/newrelic-python-agent-ci:latest
options: >-
--add-host=host.docker.internal:host-gateway
timeout-minutes: 30
services:
nginx:
image: tpansino1643652/nginx-hello-world:latest
ports:
- 8080:8080
- 8081:8081
- 8082:8082
# Set health checks to wait until nginx has started
options: >-
--health-cmd "service nginx status || exit 1"
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # 4.1.1

- name: Fetch git tags
run: |
git config --global --add safe.directory "$GITHUB_WORKSPACE"
git fetch --tags origin
- name: Configure pip cache
run: |
mkdir -p /github/home/.cache/pip
chown -R $(whoami) /github/home/.cache/pip
- name: Get Environments
id: get-envs
run: |
echo "envs=$(tox -l | grep '^${{ github.job }}\-' | ./.github/workflows/get-envs.py)" >> $GITHUB_OUTPUT
env:
GROUP_NUMBER: ${{ matrix.group-number }}

- name: Test
run: |
tox -vv -e ${{ steps.get-envs.outputs.envs }} -p auto
env:
TOX_PARALLEL_NO_SPINNER: 1
PY_COLORS: 0

- name: Upload Coverage Artifacts
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # 4.3.1
with:
name: coverage-${{ github.job }}-${{ strategy.job-index }}
path: ./**/.coverage.*
retention-days: 1

postgres16:
env:
TOTAL_GROUPS: 2
Expand Down
47 changes: 17 additions & 30 deletions newrelic/hooks/external_httpx.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,41 +101,28 @@ async def async_send_wrapper(wrapped, instance, args, kwargs):
return await wrapped(*args, **kwargs)


@property
def nr_first_event_hooks(self):
if not hasattr(self, "_nr_event_hooks"):
# This branch should only be hit if agent initialize is called after
# the initialization of the http client
self._event_hooks = vars(self)["_event_hooks"]
del vars(self)["_event_hooks"]
return self._nr_event_hooks
def create_nr_first_event_hooks(is_async=False):
@property
def nr_first_event_hooks(self):
if not hasattr(self, "_nr_event_hooks"):
# This branch should only be hit if agent initialize is called after
# the initialization of the http client
self._event_hooks = vars(self)["_event_hooks"]
del vars(self)["_event_hooks"]
return self._nr_event_hooks


@nr_first_event_hooks.setter
def nr_first_event_hooks(self, value):
value = NewRelicFirstDict(value, is_async=False)
self._nr_event_hooks = value


@property
def nr_first_event_hooks_async(self):
if not hasattr(self, "_nr_event_hooks"):
# This branch should only be hit if agent initialize is called after
# the initialization of the http client
self._event_hooks = vars(self)["_event_hooks"]
del vars(self)["_event_hooks"]
return self._nr_event_hooks


@nr_first_event_hooks_async.setter
def nr_first_event_hooks_async(self, value):
value = NewRelicFirstDict(value, is_async=True)
self._nr_event_hooks = value
@nr_first_event_hooks.setter
def nr_first_event_hooks(self, value):
value = NewRelicFirstDict(value, is_async=is_async)
self._nr_event_hooks = value

return nr_first_event_hooks


def instrument_httpx_client(module):
module.Client._event_hooks = nr_first_event_hooks
module.AsyncClient._event_hooks = nr_first_event_hooks_async
module.Client._event_hooks = create_nr_first_event_hooks(is_async=False)
module.AsyncClient._event_hooks = create_nr_first_event_hooks(is_async=True)

wrap_function_wrapper(module, "Client.send", sync_send_wrapper)
wrap_function_wrapper(module, "AsyncClient.send", async_send_wrapper)
2 changes: 1 addition & 1 deletion tests/agent_unittests/cert.pem
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
This is not a secret key!
This private key is used only for testing and is not functionally used in the agent.
To generate a new key and certificate, use the following.
openssl req -nodes -newkey rsa:2048 -x509 -keyout key.pem -out cert.pem -subj '/CN=localhost' -days 3650
openssl req -nodes -newkey rsa:2048 -x509 -keyout cert.pem -out cert.pem -subj '/CN=localhost' -days 3650
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDYF0U/0zXikonW
Ez532avkDL1QbQ8Yz5ULMwZz8j+cLEhdw/4pQJ7Dox6KEZbsan1nZZqpcZWT0d39
Expand Down
22 changes: 22 additions & 0 deletions tests/external_httpx/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
collector_agent_registration_fixture,
collector_available_fixture,
)
from testing_support.db_settings import nginx_settings

_default_settings = {
"package_reporting.enabled": False, # Turn off package reporting for testing as it causes slow downs.
Expand All @@ -40,3 +41,24 @@ def httpx():
import httpx

return httpx


@pytest.fixture(scope="session")
def real_server():
settings = nginx_settings()[0]

class RealHTTP2Server:
host = settings["host"]
port = settings["port"]

yield RealHTTP2Server


@pytest.fixture(scope="function")
def sync_client(httpx):
return httpx.Client()


@pytest.fixture(scope="function")
def async_client(httpx):
return httpx.AsyncClient()
Loading

0 comments on commit dacf155

Please sign in to comment.