Merge branch 'master' of github.com:mongodb/mongo-python-driver

This commit is contained in:
Steven Silvester 2025-03-14 16:54:38 -05:00
commit a99ec0ed18
No known key found for this signature in database
GPG Key ID: B1BF5EC3A8B32F91
12 changed files with 200 additions and 120 deletions

View File

@ -227,17 +227,6 @@ functions:
args:
- ${DRIVERS_TOOLS}/.evergreen/atlas_data_lake/run-mongohouse-image.sh
"run mod_wsgi tests":
- command: subprocess.exec
type: test
params:
include_expansions_in_env: [MOD_WSGI_VERSION, MOD_WSGI_EMBEDDED, "PYTHON_BINARY"]
working_dir: "src"
binary: bash
args:
- .evergreen/scripts/run-with-env.sh
- .evergreen/scripts/run-mod-wsgi-tests.sh
"run doctests":
- command: subprocess.exec
type: test
@ -412,40 +401,6 @@ tasks:
TEST_NAME: index_management
AUTH: "auth"
- name: "mod-wsgi-standalone"
tags: ["mod_wsgi"]
commands:
- func: "run server"
vars:
TOPOLOGY: "server"
- func: "run mod_wsgi tests"
- name: "mod-wsgi-replica-set"
tags: ["mod_wsgi"]
commands:
- func: "run server"
vars:
TOPOLOGY: "replica_set"
- func: "run mod_wsgi tests"
- name: "mod-wsgi-embedded-mode-standalone"
tags: ["mod_wsgi"]
commands:
- func: "run server"
- func: "run mod_wsgi tests"
vars:
MOD_WSGI_EMBEDDED: "1"
- name: "mod-wsgi-embedded-mode-replica-set"
tags: ["mod_wsgi"]
commands:
- func: "run server"
vars:
TOPOLOGY: "replica_set"
- func: "run mod_wsgi tests"
vars:
MOD_WSGI_EMBEDDED: "1"
- name: "no-server"
tags: ["no-server"]
commands:

View File

@ -775,6 +775,48 @@ tasks:
TEST_NAME: load_balancer
tags: [load-balancer, noauth, nossl]
# Mod wsgi tests
- name: mod-wsgi-standalone
commands:
- func: run server
vars:
TOPOLOGY: standalone
- func: run tests
vars:
TEST_NAME: mod_wsgi
SUB_TEST_NAME: standalone
tags: [mod_wsgi]
- name: mod-wsgi-replica-set
commands:
- func: run server
vars:
TOPOLOGY: replica_set
- func: run tests
vars:
TEST_NAME: mod_wsgi
SUB_TEST_NAME: standalone
tags: [mod_wsgi]
- name: mod-wsgi-embedded-mode-standalone
commands:
- func: run server
vars:
TOPOLOGY: standalone
- func: run tests
vars:
TEST_NAME: mod_wsgi
SUB_TEST_NAME: embedded
tags: [mod_wsgi]
- name: mod-wsgi-embedded-mode-replica-set
commands:
- func: run server
vars:
TOPOLOGY: replica_set
- func: run tests
vars:
TEST_NAME: mod_wsgi
SUB_TEST_NAME: embedded
tags: [mod_wsgi]
# Ocsp tests
- name: test-ocsp-ecdsa-valid-cert-server-does-not-staple
commands:

View File

@ -696,10 +696,7 @@ buildvariants:
# Mod wsgi tests
- name: mod_wsgi-ubuntu-22-python3.9
tasks:
- name: mod-wsgi-standalone
- name: mod-wsgi-replica-set
- name: mod-wsgi-embedded-mode-standalone
- name: mod-wsgi-embedded-mode-replica-set
- name: .mod_wsgi
display_name: mod_wsgi Ubuntu-22 Python3.9
run_on:
- ubuntu2204-small
@ -708,10 +705,7 @@ buildvariants:
PYTHON_BINARY: /opt/python/3.9/bin/python3
- name: mod_wsgi-ubuntu-22-python3.13
tasks:
- name: mod-wsgi-standalone
- name: mod-wsgi-replica-set
- name: mod-wsgi-embedded-mode-standalone
- name: mod-wsgi-embedded-mode-replica-set
- name: .mod_wsgi
display_name: mod_wsgi Ubuntu-22 Python3.13
run_on:
- ubuntu2204-small

View File

@ -614,12 +614,7 @@ def create_atlas_data_lake_variants():
def create_mod_wsgi_variants():
variants = []
host = HOSTS["ubuntu22"]
tasks = [
"mod-wsgi-standalone",
"mod-wsgi-replica-set",
"mod-wsgi-embedded-mode-standalone",
"mod-wsgi-embedded-mode-replica-set",
]
tasks = [".mod_wsgi"]
expansions = dict(MOD_WSGI_VERSION="4")
for python in MIN_MAX_PYTHON:
display_name = get_display_name("mod_wsgi", host, python=python)
@ -892,6 +887,24 @@ def create_oidc_tasks():
return tasks
def create_mod_wsgi_tasks():
tasks = []
for test, topology in product(["standalone", "embedded-mode"], ["standalone", "replica_set"]):
if test == "standalone":
task_name = "mod-wsgi-"
else:
task_name = "mod-wsgi-embedded-mode-"
task_name += topology.replace("_", "-")
server_vars = dict(TOPOLOGY=topology)
server_func = FunctionCall(func="run server", vars=server_vars)
vars = dict(TEST_NAME="mod_wsgi", SUB_TEST_NAME=test.split("-")[0])
test_func = FunctionCall(func="run tests", vars=vars)
tags = ["mod_wsgi"]
commands = [server_func, test_func]
tasks.append(EvgTask(name=task_name, tags=tags, commands=commands))
return tasks
def _create_ocsp_task(algo, variant, server_type, base_task_name):
file_name = f"{algo}-basic-tls-ocsp-{variant}.json"

View File

@ -0,0 +1,93 @@
from __future__ import annotations
import os
import sys
import time
import urllib.error
import urllib.request
from pathlib import Path
from shutil import which
from utils import LOGGER, ROOT, run_command, write_env
def make_request(url, timeout=10):
for _ in range(int(timeout)):
try:
urllib.request.urlopen(url) # noqa: S310
return
except urllib.error.HTTPError:
pass
time.sleep(1)
raise TimeoutError(f"Failed to access {url}")
def setup_mod_wsgi(sub_test_name: str) -> None:
env = os.environ.copy()
if sub_test_name == "embedded":
env["MOD_WSGI_CONF"] = "mod_wsgi_test_embedded.conf"
elif sub_test_name == "standalone":
env["MOD_WSGI_CONF"] = "mod_wsgi_test.conf"
else:
raise ValueError("mod_wsgi sub test must be either 'standalone' or 'embedded'")
write_env("MOD_WSGI_CONF", env["MOD_WSGI_CONF"])
apache = which("apache2")
if not apache and Path("/usr/lib/apache2/mpm-prefork/apache2").exists():
apache = "/usr/lib/apache2/mpm-prefork/apache2"
if apache:
apache_config = "apache24ubuntu161404.conf"
else:
apache = which("httpd")
if not apache:
raise ValueError("Could not find apache2 or httpd")
apache_config = "apache22amazon.conf"
python_version = ".".join(str(val) for val in sys.version_info[:2])
mod_wsgi_version = 4
so_file = f"/opt/python/mod_wsgi/python_version/{python_version}/mod_wsgi_version/{mod_wsgi_version}/mod_wsgi.so"
write_env("MOD_WSGI_SO", so_file)
env["MOD_WSGI_SO"] = so_file
env["PYTHONHOME"] = f"/opt/python/{python_version}"
env["PROJECT_DIRECTORY"] = project_directory = str(ROOT)
write_env("APACHE_BINARY", apache)
write_env("APACHE_CONFIG", apache_config)
uri1 = f"http://localhost:8080/interpreter1{project_directory}"
write_env("TEST_URI1", uri1)
uri2 = f"http://localhost:8080/interpreter2{project_directory}"
write_env("TEST_URI2", uri2)
run_command(f"{apache} -k start -f {ROOT}/test/mod_wsgi_test/{apache_config}", env=env)
# Wait for the endpoints to be available.
try:
make_request(uri1, 10)
make_request(uri2, 10)
except Exception as e:
LOGGER.error(Path("error_log").read_text())
raise e
def test_mod_wsgi() -> None:
sys.path.insert(0, ROOT)
from test.mod_wsgi_test.test_client import main, parse_args
uri1 = os.environ["TEST_URI1"]
uri2 = os.environ["TEST_URI2"]
args = f"-n 25000 -t 100 parallel {uri1} {uri2}"
try:
main(*parse_args(args.split()))
args = f"-n 25000 serial {uri1} {uri2}"
main(*parse_args(args.split()))
except Exception as e:
LOGGER.error(Path("error_log").read_text())
raise e
def teardown_mod_wsgi() -> None:
apache = os.environ["APACHE_BINARY"]
apache_config = os.environ["APACHE_CONFIG"]
run_command(f"{apache} -k stop -f {ROOT}/test/mod_wsgi_test/{apache_config}")
if __name__ == "__main__":
setup_mod_wsgi()

View File

@ -1,53 +0,0 @@
#!/bin/bash
set -o xtrace
set -o errexit
APACHE=$(command -v apache2 || command -v /usr/lib/apache2/mpm-prefork/apache2) || true
if [ -n "$APACHE" ]; then
APACHE_CONFIG=apache24ubuntu161404.conf
else
APACHE=$(command -v httpd) || true
if [ -z "$APACHE" ]; then
echo "Could not find apache2 binary"
exit 1
else
APACHE_CONFIG=apache22amazon.conf
fi
fi
PYTHON_VERSION=$(${PYTHON_BINARY} -c "import sys; sys.stdout.write('.'.join(str(val) for val in sys.version_info[:2]))")
# Ensure the C extensions are installed.
${PYTHON_BINARY} -m venv --system-site-packages .venv
source .venv/bin/activate
pip install -U pip
export PYMONGO_C_EXT_MUST_BUILD=1
python -m pip install -v -e .
export MOD_WSGI_SO=/opt/python/mod_wsgi/python_version/$PYTHON_VERSION/mod_wsgi_version/$MOD_WSGI_VERSION/mod_wsgi.so
export PYTHONHOME=/opt/python/$PYTHON_VERSION
# If MOD_WSGI_EMBEDDED is set use the default embedded mode behavior instead
# of daemon mode (WSGIDaemonProcess).
if [ -n "${MOD_WSGI_EMBEDDED:-}" ]; then
export MOD_WSGI_CONF=mod_wsgi_test_embedded.conf
else
export MOD_WSGI_CONF=mod_wsgi_test.conf
fi
cd ..
$APACHE -k start -f ${PROJECT_DIRECTORY}/test/mod_wsgi_test/${APACHE_CONFIG}
trap '$APACHE -k stop -f ${PROJECT_DIRECTORY}/test/mod_wsgi_test/${APACHE_CONFIG}' EXIT HUP
wget -t 1 -T 10 -O - "http://localhost:8080/interpreter1${PROJECT_DIRECTORY}" || (cat error_log && exit 1)
wget -t 1 -T 10 -O - "http://localhost:8080/interpreter2${PROJECT_DIRECTORY}" || (cat error_log && exit 1)
python ${PROJECT_DIRECTORY}/test/mod_wsgi_test/test_client.py -n 25000 -t 100 parallel \
http://localhost:8080/interpreter1${PROJECT_DIRECTORY} http://localhost:8080/interpreter2${PROJECT_DIRECTORY} || \
(tail -n 100 error_log && exit 1)
python ${PROJECT_DIRECTORY}/test/mod_wsgi_test/test_client.py -n 25000 serial \
http://localhost:8080/interpreter1${PROJECT_DIRECTORY} http://localhost:8080/interpreter2${PROJECT_DIRECTORY} || \
(tail -n 100 error_log && exit 1)
rm -rf .venv

View File

@ -100,6 +100,13 @@ def run() -> None:
if TEST_PERF:
start_time = datetime.now()
# Run mod_wsgi tests using the helper.
if TEST_NAME == "mod_wsgi":
from mod_wsgi_tester import test_mod_wsgi
test_mod_wsgi()
return
# Send kms tests to run remotely.
if TEST_NAME == "kms" and SUB_TEST_NAME in ["azure", "gcp"]:
from kms_tester import test_kms_send_to_remote

View File

@ -251,6 +251,11 @@ def handle_test_env() -> None:
cmd = f'bash "{DRIVERS_TOOLS}/.evergreen/run-load-balancer.sh" start'
run_command(cmd)
if test_name == "mod_wsgi":
from mod_wsgi_tester import setup_mod_wsgi
setup_mod_wsgi(sub_test_name)
if test_name == "ocsp":
if sub_test_name:
os.environ["OCSP_SERVER_TYPE"] = sub_test_name
@ -381,7 +386,7 @@ def handle_test_env() -> None:
# Use --capture=tee-sys so pytest prints test output inline:
# https://docs.pytest.org/en/stable/how-to/capture-stdout-stderr.html
TEST_ARGS = f"-v --capture=tee-sys --durations=5 {TEST_ARGS}"
TEST_SUITE = TEST_SUITE_MAP[test_name]
TEST_SUITE = TEST_SUITE_MAP.get(test_name)
if TEST_SUITE:
TEST_ARGS = f"-m {TEST_SUITE} {TEST_ARGS}"

View File

@ -44,4 +44,10 @@ elif TEST_NAME == "serverless":
elif TEST_NAME == "auth_aws" and sys.platform != "darwin":
run_command(f"bash {DRIVERS_TOOLS}/.evergreen/auth_aws/teardown.sh")
# Tear down mog_wsgi if applicable.
elif TEST_NAME == "mod_wsgi":
from mod_wsgi_tester import teardown_mod_wsgi
teardown_mod_wsgi()
LOGGER.info(f"Tearing down tests of type '{TEST_NAME}'... done.")

View File

@ -50,7 +50,9 @@ TEST_SUITE_MAP = {
}
# Tests that require a sub test suite.
SUB_TEST_REQUIRED = ["auth_aws", "auth_oidc", "kms"]
SUB_TEST_REQUIRED = ["auth_aws", "auth_oidc", "kms", "mod_wsgi"]
EXTRA_TESTS = ["mod_wsgi"]
def get_test_options(
@ -62,7 +64,7 @@ def get_test_options(
if require_sub_test_name:
parser.add_argument(
"test_name",
choices=sorted(TEST_SUITE_MAP),
choices=sorted(list(TEST_SUITE_MAP) + EXTRA_TESTS),
nargs="?",
default="default",
help="The optional name of the test suite to set up, typically the same name as a pytest marker.",
@ -137,6 +139,11 @@ def run_command(cmd: str | list[str], **kwargs: Any) -> None:
cmd = " ".join(cmd)
LOGGER.info("Running command '%s'...", cmd)
kwargs.setdefault("check", True)
# Prevent overriding the python used by other tools.
env = kwargs.pop("env", os.environ).copy()
if "UV_PYTHON" in env:
del env["UV_PYTHON"]
kwargs["env"] = env
try:
subprocess.run(shlex.split(cmd), **kwargs) # noqa: PLW1510, S603
except subprocess.CalledProcessError as e:

View File

@ -275,6 +275,17 @@ Note: these tests can only be run from an Evergreen host.
- Run `just setup-tests atlas_connect`.
- Run `just run-tests`.
### mod_wsgi tests
Note: these tests can only be run from an Evergreen Linux host that has the Python toolchain.
- Run `just run-server`.
- Run `just setup-tests mod_wsgi <mode>`.
- Run `just run-tests`.
The `mode` can be `standalone` or `embedded`. For the `replica_set` version of the tests, use
`TOPOLOGY=replica_set just run-server`.
### OCSP tests
- Export the orchestration file, e.g. `export ORCHESTRATION_FILE=rsa-basic-tls-ocsp-disableStapling.json`.

View File

@ -24,7 +24,7 @@ from optparse import OptionParser
from urllib.request import urlopen
def parse_args():
def parse_args(args=None):
parser = OptionParser(
"""usage: %prog [options] mode url [<url2>...]
@ -70,7 +70,7 @@ def parse_args():
)
try:
options, args = parser.parse_args()
options, args = parser.parse_args(args or sys.argv[1:])
mode, urls = args[0], args[1:]
except (ValueError, IndexError):
parser.print_usage()
@ -103,11 +103,11 @@ class URLGetterThread(threading.Thread):
def run(self):
for _i in range(self.nrequests_per_thread):
try:
get(urls)
get(self.urls)
except Exception as e:
print(e)
if not options.continue_:
if not self.options.continue_:
thread.interrupt_main()
thread.exit()
@ -117,7 +117,7 @@ class URLGetterThread(threading.Thread):
URLGetterThread.counter += 1
counter = URLGetterThread.counter
should_print = options.verbose and not counter % 1000
should_print = self.options.verbose and not counter % 1000
if should_print:
print(counter)