Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
1e3cf70
Add mock external openai server
TimPansino Oct 2, 2023
1354d7a
Add mocked OpenAI server fixtures
TimPansino Oct 2, 2023
728529d
Set up recorded responses.
TimPansino Oct 3, 2023
8e20722
Clean mock server to depend on http server
TimPansino Oct 3, 2023
8b0d911
Linting
TimPansino Oct 3, 2023
eef6916
Pin flask version for flask restx tests. (#931)
umaannamalai Oct 9, 2023
d577a69
Ignore new redis methods. (#932)
umaannamalai Oct 9, 2023
6922db2
Remove approved paths
TimPansino Oct 9, 2023
13e9891
Update CI Image (#930)
TimPansino Oct 9, 2023
8e19650
Add mocking for embedding endpoint
TimPansino Oct 9, 2023
7c82a65
[Mega-Linter] Apply linters fixes
TimPansino Oct 9, 2023
91a3fc6
Add ratelimit headers
TimPansino Oct 9, 2023
7660e8c
[Mega-Linter] Apply linters fixes
TimPansino Oct 9, 2023
43160af
Only get package version once (#928)
hmstepanek Oct 9, 2023
0f57dfd
Add datalib dependency for embedding testing.
umaannamalai Oct 9, 2023
e15ebbd
Add OpenAI Test Infrastructure (#926)
umaannamalai Oct 2, 2023
5eb2e84
Add mock external openai server
TimPansino Oct 2, 2023
c503b55
Add mocked OpenAI server fixtures
TimPansino Oct 2, 2023
498f898
Set up recorded responses.
TimPansino Oct 3, 2023
8c01f5f
Clean mock server to depend on http server
TimPansino Oct 3, 2023
7579884
Linting
TimPansino Oct 3, 2023
b6596ad
Remove approved paths
TimPansino Oct 9, 2023
d6d0d6f
Add mocking for embedding endpoint
TimPansino Oct 9, 2023
6ec69d5
[Mega-Linter] Apply linters fixes
TimPansino Oct 9, 2023
d7c6e5c
Add ratelimit headers
TimPansino Oct 9, 2023
ef99ac7
[Mega-Linter] Apply linters fixes
TimPansino Oct 9, 2023
1613f57
Add datalib dependency for embedding testing.
umaannamalai Oct 9, 2023
5cff482
Merge branch 'feature-openai-mock' of github.com:newrelic/newrelic-py…
umaannamalai Oct 9, 2023
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
2 changes: 1 addition & 1 deletion .github/containers/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ RUN echo 'eval "$(pyenv init -)"' >>$HOME/.bashrc && \
pyenv update

# Install Python
ARG PYTHON_VERSIONS="3.10 3.9 3.8 3.7 3.11 2.7 pypy2.7-7.3.12 pypy3.8-7.3.11"
ARG PYTHON_VERSIONS="3.11 3.10 3.9 3.8 3.7 3.12 2.7 pypy2.7-7.3.12 pypy3.8-7.3.11"
COPY --chown=1000:1000 --chmod=+x ./install-python.sh /tmp/install-python.sh
RUN /tmp/install-python.sh && \
rm /tmp/install-python.sh
Expand Down
61 changes: 42 additions & 19 deletions .github/containers/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,37 +12,60 @@
# See the License for the specific language governing permissions and
# limitations under the License.

# Repository root for mounting into container.
MAKEFILE_DIR:=$(dir $(realpath $(firstword $(MAKEFILE_LIST))))
REPO_ROOT:=$(realpath $(MAKEFILE_DIR)../../)
# Override constants
PLATFORM_OVERRIDE:=
PYTHON_VERSIONS_OVERRIDE:=

# Computed variables
IMAGE_NAME:=ghcr.io/newrelic/newrelic-python-agent-ci
MAKEFILE_DIR:=$(dir $(realpath $(firstword ${MAKEFILE_LIST})))
REPO_ROOT:=$(realpath ${MAKEFILE_DIR}../../)
UNAME_P:=$(shell uname -p)
PLATFORM_AUTOMATIC:=$(if $(findstring arm,${UNAME_P}),linux/arm64,linux/amd64)
PLATFORM:=$(if ${PLATFORM_OVERRIDE},${PLATFORM_OVERRIDE},${PLATFORM_AUTOMATIC})
PYTHON_VERSIONS_AUTOMATIC:=3.10 2.7
PYTHON_VERSIONS:=$(if ${PYTHON_VERSIONS_OVERRIDE},${PYTHON_VERSIONS_OVERRIDE},${PYTHON_VERSIONS_AUTOMATIC})

.PHONY: default
default: test

# Perform a shortened build for testing
.PHONY: build
build:
@docker build $(MAKEFILE_DIR) \
-t ghcr.io/newrelic/newrelic-python-agent-ci:local \
--build-arg='PYTHON_VERSIONS=3.10 2.7'

# Ensure python versions are usable
.PHONY: test
test: build
@docker run --rm ghcr.io/newrelic/python-agent-ci:local /bin/bash -c '\
python3.10 --version && \
python2.7 --version && \
touch tox.ini && tox --version && \
echo "Success! Python versions installed."'
@docker build ${MAKEFILE_DIR} \
--platform=${PLATFORM} \
-t ${IMAGE_NAME}:local \
--build-arg='PYTHON_VERSIONS=${PYTHON_VERSIONS}'

# Run the local tag as a container.
.PHONY: run
run: build
run: run.local

# Run a specific tag as a container.
# Usage: make run.<tag>
# Defaults to run.local, but can instead be run.latest or any other tag.
.PHONY: run.%
run.%:
# Build image if local was specified, else pull latest
@if [[ "$*" = "local" ]]; then cd ${MAKEFILE_DIR} && $(MAKE) build; else docker pull ${IMAGE_NAME}:$*; fi
@docker run --rm -it \
--mount type=bind,source="$(REPO_ROOT)",target=/home/github/python-agent \
--platform=${PLATFORM} \
--mount type=bind,source="${REPO_ROOT}",target=/home/github/python-agent \
--workdir=/home/github/python-agent \
--add-host=host.docker.internal:host-gateway \
-e NEW_RELIC_HOST="${NEW_RELIC_HOST}" \
-e NEW_RELIC_LICENSE_KEY="${NEW_RELIC_LICENSE_KEY}" \
-e NEW_RELIC_DEVELOPER_MODE="${NEW_RELIC_DEVELOPER_MODE}" \
-e GITHUB_ACTIONS="true" \
ghcr.io/newrelic/newrelic-python-agent-ci:local /bin/bash
${IMAGE_NAME}:$* /bin/bash

# Ensure python versions are usable. Cannot be automatically used with PYTHON_VERSIONS_OVERRIDE.
.PHONY: test
test: build
@docker run --rm \
--platform=${PLATFORM} \
ghcr.io/newrelic/python-agent-ci:local \
/bin/bash -c '\
python3.10 --version && \
python2.7 --version && \
touch tox.ini && tox --version && \
echo "Success! Python versions installed."'
2 changes: 1 addition & 1 deletion .github/workflows/build-ci-image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,6 @@ jobs:
with:
push: ${{ github.event_name != 'pull_request' }}
context: .github/containers
platforms: ${{ (github.ref == 'refs/head/main') && 'linux/amd64,linux/arm64' || 'linux/amd64' }}
platforms: ${{ (format('refs/heads/{0}', github.event.repository.default_branch) == github.ref) && 'linux/amd64,linux/arm64' || 'linux/amd64' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
8 changes: 5 additions & 3 deletions newrelic/hooks/datastore_aioredis.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
_redis_operation_re,
)

AIOREDIS_VERSION = get_package_version_tuple("aioredis")


def _conn_attrs_to_dict(connection):
host = getattr(connection, "host", None)
Expand Down Expand Up @@ -58,14 +60,13 @@ def _nr_wrapper_AioRedis_method_(wrapped, instance, args, kwargs):
# Check for transaction and return early if found.
# Method will return synchronously without executing,
# it will be added to the command stack and run later.
aioredis_version = get_package_version_tuple("aioredis")

# This conditional is for versions of aioredis that are outside
# New Relic's supportability window but will still work. New
# Relic does not provide testing/support for this. In order to
# keep functionality without affecting coverage metrics, this
# segment is excluded from coverage analysis.
if aioredis_version and aioredis_version < (2,): # pragma: no cover
if AIOREDIS_VERSION and AIOREDIS_VERSION < (2,): # pragma: no cover
# AioRedis v1 uses a RedisBuffer instead of a real connection for queueing up pipeline commands
from aioredis.commands.transaction import _RedisBuffer

Expand All @@ -75,7 +76,7 @@ def _nr_wrapper_AioRedis_method_(wrapped, instance, args, kwargs):
return wrapped(*args, **kwargs)
else:
# AioRedis v2 uses a Pipeline object for a client and internally queues up pipeline commands
if aioredis_version:
if AIOREDIS_VERSION:
from aioredis.client import Pipeline
if isinstance(instance, Pipeline):
return wrapped(*args, **kwargs)
Expand Down Expand Up @@ -139,6 +140,7 @@ async def wrap_Connection_send_command(wrapped, instance, args, kwargs):
):
return await wrapped(*args, **kwargs)


# This wrapper is for versions of aioredis that are outside
# New Relic's supportability window but will still work. New
# Relic does not provide testing/support for this. In order to
Expand Down
114 changes: 60 additions & 54 deletions tests/datastore_redis/test_custom_conn_pool.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,22 @@
# See the License for the specific language governing permissions and
# limitations under the License.

''' The purpose of these tests is to confirm that using a non-standard
""" The purpose of these tests is to confirm that using a non-standard
connection pool that does not have a `connection_kwargs` attribute
will not result in an error.
'''
"""

import pytest
import redis

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

from testing_support.fixtures import override_application_settings
from testing_support.validators.validate_transaction_metrics import validate_transaction_metrics
from testing_support.db_settings import redis_settings
from testing_support.fixtures import override_application_settings
from testing_support.util import instance_hostname
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

DB_SETTINGS = redis_settings()[0]
REDIS_PY_VERSION = get_package_version_tuple("redis")
Expand All @@ -45,13 +45,17 @@ def get_connection(self, name, *keys, **options):
def release(self, connection):
self.connection.disconnect()

def disconnect(self):
self.connection.disconnect()


# Settings

_enable_instance_settings = {
'datastore_tracer.instance_reporting.enabled': True,
"datastore_tracer.instance_reporting.enabled": True,
}
_disable_instance_settings = {
'datastore_tracer.instance_reporting.enabled': False,
"datastore_tracer.instance_reporting.enabled": False,
}

# Metrics
Expand All @@ -61,98 +65,100 @@ def release(self, connection):
datastore_all_metric_count = 5 if REDIS_PY_VERSION >= (5, 0) else 3

_base_scoped_metrics = [
('Datastore/operation/Redis/get', 1),
('Datastore/operation/Redis/set', 1),
('Datastore/operation/Redis/client_list', 1),
("Datastore/operation/Redis/get", 1),
("Datastore/operation/Redis/set", 1),
("Datastore/operation/Redis/client_list", 1),
]
# client_setinfo was introduced in v5.0.0 and assigns info displayed in client_list output
if REDIS_PY_VERSION >= (5, 0):
_base_scoped_metrics.append(('Datastore/operation/Redis/client_setinfo', 2),)
_base_scoped_metrics.append(
("Datastore/operation/Redis/client_setinfo", 2),
)

_base_rollup_metrics = [
('Datastore/all', datastore_all_metric_count),
('Datastore/allOther', datastore_all_metric_count),
('Datastore/Redis/all', datastore_all_metric_count),
('Datastore/Redis/allOther', datastore_all_metric_count),
('Datastore/operation/Redis/get', 1),
('Datastore/operation/Redis/set', 1),
('Datastore/operation/Redis/client_list', 1),
("Datastore/all", datastore_all_metric_count),
("Datastore/allOther", datastore_all_metric_count),
("Datastore/Redis/all", datastore_all_metric_count),
("Datastore/Redis/allOther", datastore_all_metric_count),
("Datastore/operation/Redis/get", 1),
("Datastore/operation/Redis/set", 1),
("Datastore/operation/Redis/client_list", 1),
]
if REDIS_PY_VERSION >= (5, 0):
_base_rollup_metrics.append(('Datastore/operation/Redis/client_setinfo', 2),)
_base_rollup_metrics.append(
("Datastore/operation/Redis/client_setinfo", 2),
)

_host = instance_hostname(DB_SETTINGS['host'])
_port = DB_SETTINGS['port']
_host = instance_hostname(DB_SETTINGS["host"])
_port = DB_SETTINGS["port"]

_instance_metric_name = 'Datastore/instance/Redis/%s/%s' % (_host, _port)
_instance_metric_name = "Datastore/instance/Redis/%s/%s" % (_host, _port)

instance_metric_count = 5 if REDIS_PY_VERSION >= (5, 0) else 3

_enable_rollup_metrics = _base_rollup_metrics.append(
(_instance_metric_name, instance_metric_count)
)
_enable_rollup_metrics = _base_rollup_metrics.append((_instance_metric_name, instance_metric_count))

_disable_rollup_metrics = _base_rollup_metrics.append(
(_instance_metric_name, None)
)
_disable_rollup_metrics = _base_rollup_metrics.append((_instance_metric_name, None))

# Operations


def exercise_redis(client):
client.set('key', 'value')
client.get('key')
client.execute_command('CLIENT', 'LIST', parse='LIST')
client.set("key", "value")
client.get("key")
client.execute_command("CLIENT", "LIST", parse="LIST")


# Tests

@pytest.mark.skipif(REDIS_PY_VERSION < (2, 7),
reason='Client list command introduced in 2.7')

@pytest.mark.skipif(REDIS_PY_VERSION < (2, 7), reason="Client list command introduced in 2.7")
@override_application_settings(_enable_instance_settings)
@validate_transaction_metrics(
'test_custom_conn_pool:test_fake_conn_pool_enable_instance',
scoped_metrics=_base_scoped_metrics,
rollup_metrics=_enable_rollup_metrics,
background_task=True)
"test_custom_conn_pool:test_fake_conn_pool_enable_instance",
scoped_metrics=_base_scoped_metrics,
rollup_metrics=_enable_rollup_metrics,
background_task=True,
)
@background_task()
def test_fake_conn_pool_enable_instance():
client = redis.StrictRedis(host=DB_SETTINGS['host'],
port=DB_SETTINGS['port'], db=0)
client = redis.StrictRedis(host=DB_SETTINGS["host"], port=DB_SETTINGS["port"], db=0)

# Get a real connection

conn = client.connection_pool.get_connection('GET')
conn = client.connection_pool.get_connection("GET")

# Replace the original connection pool with one that doesn't
# have the `connection_kwargs` attribute.

fake_pool = FakeConnectionPool(conn)
client.connection_pool = fake_pool
assert not hasattr(client.connection_pool, 'connection_kwargs')
assert not hasattr(client.connection_pool, "connection_kwargs")

exercise_redis(client)

@pytest.mark.skipif(REDIS_PY_VERSION < (2, 7),
reason='Client list command introduced in 2.7')

@pytest.mark.skipif(REDIS_PY_VERSION < (2, 7), reason="Client list command introduced in 2.7")
@override_application_settings(_disable_instance_settings)
@validate_transaction_metrics(
'test_custom_conn_pool:test_fake_conn_pool_disable_instance',
scoped_metrics=_base_scoped_metrics,
rollup_metrics=_disable_rollup_metrics,
background_task=True)
"test_custom_conn_pool:test_fake_conn_pool_disable_instance",
scoped_metrics=_base_scoped_metrics,
rollup_metrics=_disable_rollup_metrics,
background_task=True,
)
@background_task()
def test_fake_conn_pool_disable_instance():
client = redis.StrictRedis(host=DB_SETTINGS['host'],
port=DB_SETTINGS['port'], db=0)
client = redis.StrictRedis(host=DB_SETTINGS["host"], port=DB_SETTINGS["port"], db=0)

# Get a real connection

conn = client.connection_pool.get_connection('GET')
conn = client.connection_pool.get_connection("GET")

# Replace the original connection pool with one that doesn't
# have the `connection_kwargs` attribute.

fake_pool = FakeConnectionPool(conn)
client.connection_pool = fake_pool
assert not hasattr(client.connection_pool, 'connection_kwargs')
assert not hasattr(client.connection_pool, "connection_kwargs")

exercise_redis(client)
2 changes: 2 additions & 0 deletions tests/datastore_redis/test_uninstrumented_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"append_no_scale",
"append_values_and_weights",
"append_weights",
"auto_close_connection_pool",
"batch_indexer",
"BatchIndexer",
"bulk",
Expand All @@ -55,6 +56,7 @@
"edges",
"execute_command",
"flush",
"from_pool",
"from_url",
"get_connection_kwargs",
"get_encoder",
Expand Down
Loading