diff --git a/.evergreen/config.yml b/.evergreen/config.yml index 97845b86d4..30c4fe2002 100644 --- a/.evergreen/config.yml +++ b/.evergreen/config.yml @@ -206,7 +206,7 @@ functions: params: binary: bash working_dir: "src" - include_expansions_in_env: [VERSION, TOPOLOGY, AUTH, SSL, ORCHESTRATION_FILE, PYTHON_BINARY, + include_expansions_in_env: [VERSION, TOPOLOGY, AUTH, SSL, ORCHESTRATION_FILE, PYTHON_BINARY, PYTHON_VERSION, STORAGE_ENGINE, REQUIRE_API_VERSION, DRIVERS_TOOLS, TEST_CRYPT_SHARED, AUTH_AWS, LOAD_BALANCER] args: [.evergreen/just.sh, run-server, "${TEST_NAME}"] - command: expansions.update @@ -227,7 +227,7 @@ functions: type: test params: include_expansions_in_env: [AUTH, SSL, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, - AWS_SESSION_TOKEN, COVERAGE, PYTHON_BINARY, LIBMONGOCRYPT_URL, MONGODB_URI, + AWS_SESSION_TOKEN, COVERAGE, PYTHON_BINARY, LIBMONGOCRYPT_URL, MONGODB_URI, PYTHON_VERSION, DISABLE_TEST_COMMANDS, GREEN_FRAMEWORK, NO_EXT, COMPRESSORS, MONGODB_API_VERSION, DEBUG_LOG, ORCHESTRATION_FILE, OCSP_SERVER_TYPE] binary: bash diff --git a/.evergreen/generated_configs/tasks.yml b/.evergreen/generated_configs/tasks.yml index efc7844061..f859169ed4 100644 --- a/.evergreen/generated_configs/tasks.yml +++ b/.evergreen/generated_configs/tasks.yml @@ -723,18 +723,48 @@ tasks: tags: [doctests] # Enterprise auth tests - - name: test-enterprise-auth + - name: test-enterprise-auth-python3.9 commands: - func: run server vars: TEST_NAME: enterprise_auth AUTH: auth + PYTHON_VERSION: "3.9" - func: assume ec2 role - func: run tests vars: TEST_NAME: enterprise_auth AUTH: auth + PYTHON_VERSION: "3.9" tags: [enterprise_auth] + - name: test-enterprise-auth-python3.13 + commands: + - func: run server + vars: + TEST_NAME: enterprise_auth + AUTH: auth + PYTHON_VERSION: "3.13" + - func: assume ec2 role + - func: run tests + vars: + TEST_NAME: enterprise_auth + AUTH: auth + PYTHON_VERSION: "3.13" + tags: [enterprise_auth] + - name: test-enterprise-auth-pypy3.10 + commands: + - func: run server + vars: + TEST_NAME: enterprise_auth + AUTH: auth + PYTHON_VERSION: pypy3.10 + - func: assume ec2 role + - func: run tests + vars: + TEST_NAME: enterprise_auth + AUTH: auth + PYTHON_VERSION: pypy3.10 + tags: [enterprise_auth, pypy] # Free threading tests - name: test-free-threading diff --git a/.evergreen/generated_configs/variants.yml b/.evergreen/generated_configs/variants.yml index ca8c0e1966..2242e9c338 100644 --- a/.evergreen/generated_configs/variants.yml +++ b/.evergreen/generated_configs/variants.yml @@ -507,54 +507,24 @@ buildvariants: tags: [encryption_tag] # Enterprise auth tests - - name: auth-enterprise-macos-python3.9 + - name: auth-enterprise-macos tasks: - - name: .enterprise_auth - display_name: Auth Enterprise macOS Python3.9 + - name: .enterprise_auth !.pypy + display_name: Auth Enterprise macOS run_on: - macos-14 - expansions: - PYTHON_BINARY: /Library/Frameworks/Python.Framework/Versions/3.9/bin/python3 - - name: auth-enterprise-rhel8-python3.10 - tasks: - - name: .enterprise_auth - display_name: Auth Enterprise RHEL8 Python3.10 - run_on: - - rhel87-small - expansions: - PYTHON_BINARY: /opt/python/3.10/bin/python3 - - name: auth-enterprise-rhel8-python3.11 - tasks: - - name: .enterprise_auth - display_name: Auth Enterprise RHEL8 Python3.11 - run_on: - - rhel87-small - expansions: - PYTHON_BINARY: /opt/python/3.11/bin/python3 - - name: auth-enterprise-rhel8-python3.12 + - name: auth-enterprise-win64 tasks: - - name: .enterprise_auth - display_name: Auth Enterprise RHEL8 Python3.12 - run_on: - - rhel87-small - expansions: - PYTHON_BINARY: /opt/python/3.12/bin/python3 - - name: auth-enterprise-win64-python3.13 - tasks: - - name: .enterprise_auth - display_name: Auth Enterprise Win64 Python3.13 + - name: .enterprise_auth !.pypy + display_name: Auth Enterprise Win64 run_on: - windows-64-vsMulti-small - expansions: - PYTHON_BINARY: C:/python/Python313/python.exe - - name: auth-enterprise-rhel8-pypy3.10 + - name: auth-enterprise-rhel8 tasks: - name: .enterprise_auth - display_name: Auth Enterprise RHEL8 PyPy3.10 + display_name: Auth Enterprise RHEL8 run_on: - rhel87-small - expansions: - PYTHON_BINARY: /opt/python/pypy3.10/bin/python3 # Free threaded tests - name: free-threaded-rhel8-python3.13t diff --git a/.evergreen/scripts/generate_config.py b/.evergreen/scripts/generate_config.py index 54d5e4efe8..70a88f3a8d 100644 --- a/.evergreen/scripts/generate_config.py +++ b/.evergreen/scripts/generate_config.py @@ -187,17 +187,14 @@ def get_versions_until(max_version: str) -> list[str]: return versions -def get_display_name(base: str, host: Host | None = None, **kwargs) -> str: - """Get the display name of a variant.""" +def get_common_name(base: str, sep: str, **kwargs) -> str: display_name = base - if host is not None: - display_name += f" {host.display_name}" version = kwargs.pop("VERSION", None) version = version or kwargs.pop("version", None) if version: if version not in ["rapid", "latest"]: version = f"v{version}" - display_name = f"{display_name} {version}" + display_name = f"{display_name}{sep}{version}" for key, value in kwargs.items(): name = value if key.lower() == "python": @@ -209,10 +206,22 @@ def get_display_name(base: str, host: Host | None = None, **kwargs) -> str: name = DISPLAY_LOOKUP[key.lower()][value] else: continue - display_name = f"{display_name} {name}" + display_name = f"{display_name}{sep}{name}" return display_name +def get_variant_name(base: str, host: Host | None = None, **kwargs) -> str: + """Get the display name of a variant.""" + display_name = base + if host is not None: + display_name += f" {host.display_name}" + return get_common_name(display_name, " ", **kwargs) + + +def get_task_name(base: str, **kwargs): + return get_common_name(base, "-", **kwargs).lower() + + def zip_cycle(*iterables, empty_default=None): """Get all combinations of the inputs, cycling over the shorter list(s).""" cycles = [cycle(i) for i in iterables] @@ -252,7 +261,7 @@ def create_ocsp_variants() -> list[BuildVariant]: host = DEFAULT_HOST variant = create_variant( [".ocsp"], - get_display_name(base_display, host, version=version, python=python), + get_variant_name(base_display, host, version=version, python=python), python=python, version=version, host=host, @@ -268,7 +277,7 @@ def create_ocsp_variants() -> list[BuildVariant]: python = CPYTHONS[0] if version == "4.4" else CPYTHONS[-1] variant = create_variant( [".ocsp-rsa !.ocsp-staple"], - get_display_name(base_display, host, version=version, python=python), + get_variant_name(base_display, host, version=version, python=python), python=python, version=version, host=host, @@ -290,7 +299,7 @@ def create_server_variants() -> list[BuildVariant]: for python, c_ext in product([*MIN_MAX_PYTHON, PYPYS[-1]], C_EXTS): expansions = dict(COVERAGE="coverage") handle_c_ext(c_ext, expansions) - display_name = get_display_name(base_display_name, host, python=python, **expansions) + display_name = get_variant_name(base_display_name, host, python=python, **expansions) variant = create_variant( [f".{t} .sync_async" for t in TOPOLOGIES], display_name, @@ -304,7 +313,7 @@ def create_server_variants() -> list[BuildVariant]: # Test the rest of the pythons. for python in CPYTHONS[1:-1] + PYPYS[:-1]: display_name = f"Test {host}" - display_name = get_display_name(base_display_name, host, python=python) + display_name = get_variant_name(base_display_name, host, python=python) variant = create_variant( [f"{t} .sync_async" for t in SUB_TASKS], display_name, @@ -324,7 +333,7 @@ def create_server_variants() -> list[BuildVariant]: for version in get_versions_from("6.0"): tasks.extend(f"{t} .{version} !.sync_async" for t in SUB_TASKS) host = HOSTS[host_name] - display_name = get_display_name(base_display_name, host, python=python) + display_name = get_variant_name(base_display_name, host, python=python) variant = create_variant(tasks, display_name, python=python, host=host) variants.append(variant) @@ -340,7 +349,7 @@ def create_free_threaded_variants() -> list[BuildVariant]: tasks = [".free-threading"] host = HOSTS[host_name] python = "3.13t" - display_name = get_display_name("Free-threaded", host, python=python) + display_name = get_variant_name("Free-threaded", host, python=python) variant = create_variant(tasks, display_name, python=python, host=host) variants.append(variant) return variants @@ -365,7 +374,7 @@ def get_encryption_expansions(encryption): encryptions = ["Encryption", "Encryption crypt_shared", "Encryption PyOpenSSL"] for encryption, python in product(encryptions, [*MIN_MAX_PYTHON, PYPYS[-1]]): expansions = get_encryption_expansions(encryption) - display_name = get_display_name(encryption, host, python=python, **expansions) + display_name = get_variant_name(encryption, host, python=python, **expansions) variant = create_variant( [f"{t} .sync_async" for t in SUB_TASKS], display_name, @@ -380,7 +389,7 @@ def get_encryption_expansions(encryption): # Test the rest of the pythons on linux for all server versions. for encryption, python, task in zip_cycle(encryptions, CPYTHONS[1:-1] + PYPYS[:-1], SUB_TASKS): expansions = get_encryption_expansions(encryption) - display_name = get_display_name(encryption, host, python=python, **expansions) + display_name = get_variant_name(encryption, host, python=python, **expansions) variant = create_variant( [f"{task} .sync_async"], display_name, @@ -396,7 +405,7 @@ def get_encryption_expansions(encryption): for host_name, encryption, python in product(["macos", "win64"], encryptions, MIN_MAX_PYTHON): host = HOSTS[host_name] expansions = get_encryption_expansions(encryption) - display_name = get_display_name(encryption, host, python=python, **expansions) + display_name = get_variant_name(encryption, host, python=python, **expansions) variant = create_variant( task_names, display_name, @@ -418,7 +427,7 @@ def create_load_balancer_variants(): variants = [] for version in versions: python = CPYTHONS[0] - display_name = get_display_name("Load Balancer", host, python=python, version=version) + display_name = get_variant_name("Load Balancer", host, python=python, version=version) variant = create_variant( [".load-balancer"], display_name, @@ -443,7 +452,7 @@ def create_compression_variants(): handle_c_ext(c_ext, expansions) base_name = f"Compression {compressor}" python = CPYTHONS[ind % len(CPYTHONS)] - display_name = get_display_name(base_name, host, python=python, **expansions) + display_name = get_variant_name(base_name, host, python=python, **expansions) variant = create_variant( task_names[compressor], display_name, @@ -458,7 +467,7 @@ def create_compression_variants(): expansions = dict(COMPRESSORS=compressor) handle_c_ext(c_ext, expansions) base_name = f"Compression {compressor}" - display_name = get_display_name(base_name, host, python=python, **expansions) + display_name = get_variant_name(base_name, host, python=python, **expansions) variant = create_variant( task_names[compressor], display_name, @@ -473,17 +482,13 @@ def create_compression_variants(): def create_enterprise_auth_variants(): variants = [] - - # All python versions across platforms. - for python in ALL_PYTHONS: - if python == CPYTHONS[0]: - host = HOSTS["macos"] - elif python == CPYTHONS[-1]: - host = HOSTS["win64"] + for host in [HOSTS["macos"], HOSTS["win64"], DEFAULT_HOST]: + display_name = get_variant_name("Auth Enterprise", host) + if host == DEFAULT_HOST: + tags = [".enterprise_auth"] else: - host = DEFAULT_HOST - display_name = get_display_name("Auth Enterprise", host, python=python) - variant = create_variant([".enterprise_auth"], display_name, host=host, python=python) + tags = [".enterprise_auth !.pypy"] + variant = create_variant(tags, display_name, host=host) variants.append(variant) return variants @@ -506,7 +511,7 @@ def create_pyopenssl_variants(): else: host = DEFAULT_HOST - display_name = get_display_name(base_name, host, python=python) + display_name = get_variant_name(base_name, host, python=python) variant = create_variant( [f".replica_set .{auth} .{ssl} .sync_async", f".7.0 .{auth} .{ssl} .sync_async"], display_name, @@ -535,7 +540,7 @@ def create_storage_engine_variants(): tasks = [f".standalone .{v} .noauth .nossl .sync_async" for v in versions] + [ f".replica_set .{v} .noauth .nossl .sync_async" for v in versions ] - display_name = get_display_name(f"Storage {engine}", host, python=python) + display_name = get_variant_name(f"Storage {engine}", host, python=python) variant = create_variant( tasks, display_name, host=host, python=python, expansions=expansions ) @@ -571,7 +576,7 @@ def create_stable_api_variants(): f".standalone .{v} .noauth .nossl .sync_async" for v in get_versions_from("5.0") ] base_display_name = f"Stable API {test_type}" - display_name = get_display_name(base_display_name, host, python=python, **expansions) + display_name = get_variant_name(base_display_name, host, python=python, **expansions) variant = create_variant( tasks, display_name, host=host, python=python, tags=tags, expansions=expansions ) @@ -586,7 +591,7 @@ def create_green_framework_variants(): host = DEFAULT_HOST for python, framework in product([CPYTHONS[0], CPYTHONS[-1]], ["eventlet", "gevent"]): expansions = dict(GREEN_FRAMEWORK=framework, AUTH="auth", SSL="ssl") - display_name = get_display_name(f"Green {framework.capitalize()}", host, python=python) + display_name = get_variant_name(f"Green {framework.capitalize()}", host, python=python) variant = create_variant( tasks, display_name, host=host, python=python, expansions=expansions ) @@ -601,7 +606,7 @@ def create_no_c_ext_variants(): tasks = [f".{topology} .noauth .nossl !.sync_async"] expansions = dict() handle_c_ext(C_EXTS[0], expansions) - display_name = get_display_name("No C Ext", host, python=python) + display_name = get_variant_name("No C Ext", host, python=python) variant = create_variant( tasks, display_name, host=host, python=python, expansions=expansions ) @@ -614,7 +619,7 @@ def create_atlas_data_lake_variants(): host = HOSTS["ubuntu22"] for python in MIN_MAX_PYTHON: tasks = [".atlas_data_lake"] - display_name = get_display_name("Atlas Data Lake", host, python=python) + display_name = get_variant_name("Atlas Data Lake", host, python=python) variant = create_variant(tasks, display_name, host=host, python=python) variants.append(variant) return variants @@ -626,7 +631,7 @@ def create_mod_wsgi_variants(): 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) + display_name = get_variant_name("mod_wsgi", host, python=python) variant = create_variant( tasks, display_name, host=host, python=python, expansions=expansions ) @@ -638,7 +643,7 @@ def create_disable_test_commands_variants(): host = DEFAULT_HOST expansions = dict(AUTH="auth", SSL="ssl", DISABLE_TEST_COMMANDS="1") python = CPYTHONS[0] - display_name = get_display_name("Disable test commands", host, python=python) + display_name = get_variant_name("Disable test commands", host, python=python) tasks = [".latest .sync_async"] return [create_variant(tasks, display_name, host=host, python=python, expansions=expansions)] @@ -651,7 +656,7 @@ def create_serverless_variants(): return [ create_variant( tasks, - get_display_name(base_name, host, python=python), + get_variant_name(base_name, host, python=python), host=host, python=python, batchtime=batchtime, @@ -671,7 +676,7 @@ def create_oidc_auth_variants(): variants.append( create_variant( tasks, - get_display_name("Auth OIDC", host), + get_variant_name("Auth OIDC", host), host=host, batchtime=BATCHTIME_WEEK, ) @@ -685,7 +690,7 @@ def create_search_index_variants(): return [ create_variant( [".search_index"], - get_display_name("Search Index Helpers", host, python=python), + get_variant_name("Search Index Helpers", host, python=python), python=python, host=host, ) @@ -698,7 +703,7 @@ def create_mockupdb_variants(): return [ create_variant( [".mockupdb"], - get_display_name("MockupDB", host, python=python), + get_variant_name("MockupDB", host, python=python), python=python, host=host, ) @@ -711,7 +716,7 @@ def create_doctests_variants(): return [ create_variant( [".doctests"], - get_display_name("Doctests", host, python=python), + get_variant_name("Doctests", host, python=python), python=python, host=host, ) @@ -723,7 +728,7 @@ def create_atlas_connect_variants(): return [ create_variant( [".atlas_connect"], - get_display_name("Atlas connect", host, python=python), + get_variant_name("Atlas connect", host, python=python), python=python, host=host, ) @@ -751,7 +756,7 @@ def create_aws_auth_variants(): host = HOSTS[host_name] variant = create_variant( tasks, - get_display_name("Auth AWS", host, python=python), + get_variant_name("Auth AWS", host, python=python), host=host, python=python, expansions=expansions, @@ -773,7 +778,7 @@ def create_alternative_hosts_variants(): variants.append( create_variant( [".5.0 .standalone !.sync_async"], - get_display_name("OpenSSL 1.0.2", host, python=CPYTHONS[0]), + get_variant_name("OpenSSL 1.0.2", host, python=CPYTHONS[0]), host=host, python=CPYTHONS[0], batchtime=batchtime, @@ -790,7 +795,7 @@ def create_alternative_hosts_variants(): variants.append( create_variant( tags, - display_name=get_display_name("Other hosts", host), + display_name=get_variant_name("Other hosts", host), batchtime=batchtime, host=host, expansions=expansions, @@ -983,13 +988,20 @@ def create_atlas_connect_tasks(): def create_enterprise_auth_tasks(): - vars = dict(TEST_NAME="enterprise_auth", AUTH="auth") - server_func = FunctionCall(func="run server", vars=vars) - assume_func = FunctionCall(func="assume ec2 role") - test_func = FunctionCall(func="run tests", vars=vars) - task_name = "test-enterprise-auth" - tags = ["enterprise_auth"] - return [EvgTask(name=task_name, tags=tags, commands=[server_func, assume_func, test_func])] + tasks = [] + for python in [*MIN_MAX_PYTHON, PYPYS[-1]]: + vars = dict(TEST_NAME="enterprise_auth", AUTH="auth", PYTHON_VERSION=python) + server_func = FunctionCall(func="run server", vars=vars) + assume_func = FunctionCall(func="assume ec2 role") + test_func = FunctionCall(func="run tests", vars=vars) + task_name = get_task_name("test-enterprise-auth", python=python) + tags = ["enterprise_auth"] + if python in PYPYS: + tags += ["pypy"] + tasks.append( + EvgTask(name=task_name, tags=tags, commands=[server_func, assume_func, test_func]) + ) + return tasks def create_perf_tasks(): diff --git a/.evergreen/scripts/setup-dev-env.sh b/.evergreen/scripts/setup-dev-env.sh index d1c4be3494..d9b88e3385 100755 --- a/.evergreen/scripts/setup-dev-env.sh +++ b/.evergreen/scripts/setup-dev-env.sh @@ -31,7 +31,11 @@ if [ ! -d $BIN_DIR ]; then . $ROOT/.evergreen/utils.sh if [ -z "${PYTHON_BINARY:-}" ]; then - PYTHON_BINARY=$(find_python3) + if [ -n "${PYTHON_VERSION:-}" ]; then + PYTHON_BINARY=$(get_python_binary $PYTHON_VERSION) + else + PYTHON_BINARY=$(find_python3) + fi fi export UV_PYTHON=${PYTHON_BINARY} echo "export UV_PYTHON=$UV_PYTHON" >> $HERE/env.sh diff --git a/.evergreen/scripts/setup_tests.py b/.evergreen/scripts/setup_tests.py index d02cd8759a..8f4299a6d0 100644 --- a/.evergreen/scripts/setup_tests.py +++ b/.evergreen/scripts/setup_tests.py @@ -124,7 +124,7 @@ def load_config_from_file(path: str | Path) -> dict[str, str]: def get_secrets(name: str) -> dict[str, str]: secrets_dir = Path(f"{DRIVERS_TOOLS}/.evergreen/secrets_handling") - run_command(f"bash {secrets_dir}/setup-secrets.sh {name}", cwd=secrets_dir) + run_command(f"bash {secrets_dir.as_posix()}/setup-secrets.sh {name}", cwd=secrets_dir) return load_config_from_file(secrets_dir / "secrets-export.sh") diff --git a/.evergreen/utils.sh b/.evergreen/utils.sh index bb3ed8dabd..8dc7dd72f0 100755 --- a/.evergreen/utils.sh +++ b/.evergreen/utils.sh @@ -113,3 +113,24 @@ is_python_39() { return 1 fi } + + +# Function that gets a python binary given a python version string. +# Versions can be of the form 3.xx or pypy3.xx. +get_python_binary() { + version=$1 + if [ "$(uname -s)" = "Darwin" ]; then + PYTHON="/Library/Frameworks/Python.Framework/Versions/$version/bin/python3" + elif [ "Windows_NT" = "${OS:-}" ]; then + version=$(echo $version | cut -d. -f1,2 | sed 's/\.//g') + PYTHON="C:/python/Python$version/python.exe" + else + PYTHON="/opt/python/$version/bin/python3" + fi + if is_python_39 "$(command -v $PYTHON)"; then + echo "$PYTHON" + else + echo "Could not find suitable python binary for '$version'" >&2 + return 1 + fi +}