diff --git a/.evergreen/orchestration/configs/servers/auth-ssl.json b/.evergreen/orchestration/configs/servers/auth-ssl.json index d46ad1428..636d32ac6 100644 --- a/.evergreen/orchestration/configs/servers/auth-ssl.json +++ b/.evergreen/orchestration/configs/servers/auth-ssl.json @@ -1,5 +1,6 @@ { "id" : "standalonessl", + "auth_key": "secret", "name": "mongod", "login": "bob", "password": "pwd123", diff --git a/.evergreen/orchestration/mongodb_runner.py b/.evergreen/orchestration/mongodb_runner.py index fee1afb81..ba54bb03b 100644 --- a/.evergreen/orchestration/mongodb_runner.py +++ b/.evergreen/orchestration/mongodb_runner.py @@ -61,6 +61,48 @@ def _normalize_path(path: Union[Path, str]) -> str: return re.sub("/cygdrive/(.*?)(/)", r"\1://", path, count=1) +_MR_VERSION = "6.7.1" + + +def _install_mongodb_runner() -> Path: + """Install mongodb-runner via npm with overrides to pin @mongodb-js/oidc-mock-provider. + + npx does not support npm overrides, so we manage the install manually. + @mongodb-js/oidc-mock-provider 0.13.8+ switched to yargs@18 (ESM-only), which + cannot be require()'d on Node 16. Pinning to 0.13.7 keeps it on yargs@17. + """ + install_dir = TMPDIR / f"mongodb-runner-{_MR_VERSION}" + ext = ".cmd" if PLATFORM == "win32" else "" + runner_bin = install_dir / "node_modules" / ".bin" / f"mongodb-runner{ext}" + if not runner_bin.exists(): + pkg = { + "name": "mongodb-runner-wrapper", + "version": "1.0.0", + "dependencies": {"mongodb-runner": _MR_VERSION}, + "overrides": {"@mongodb-js/oidc-mock-provider": "0.13.7"}, + } + install_dir.mkdir(parents=True, exist_ok=True) + (install_dir / "package.json").write_text(json.dumps(pkg, indent=2)) + npm = shutil.which("npm") + if npm is None: + raise RuntimeError( + "npm not found. Source init-node-and-npm-env.sh or install Node before running this script." + ) + if PLATFORM == "win32": + # .cmd files require shell=True on Windows; pass as string to avoid quoting issues. + subprocess.run( + f'"{npm}" install --silent', + cwd=str(install_dir), + check=True, + shell=True, + ) + else: + subprocess.run( + [npm, "install", "--silent"], cwd=str(install_dir), check=True + ) + return runner_bin + + def start_mongodb_runner(opts, data): mo_home = Path(opts.mongo_orchestration_home) server_log = mo_home / "server.log" @@ -80,20 +122,39 @@ def start_mongodb_runner(opts, data): binary = shutil.which("node") target = HERE / "devtools-shared/packages/mongodb-runner/bin/runner.js" target = _normalize_path(target) + binary = _normalize_path(binary) + cmd = f"{binary} {target} start --debug --config {config_file}" else: - binary = shutil.which("npx") - target = "-y mongodb-runner@^6.7.1" - binary = _normalize_path(binary) - cmd = f"{binary} {target} start --debug --config {config_file}" - LOGGER.info(f"Running mongodb-runner using {binary} {target}...") + binary = _normalize_path(_install_mongodb_runner()) + cmd = f"{binary} start --debug --config {config_file}" + LOGGER.info(f"Running mongodb-runner using {binary}...") + env = os.environ.copy() + node_bin = shutil.which("node") + if node_bin: + try: + node_ver = subprocess.check_output( + [node_bin, "--version"], encoding="utf-8" + ).strip() + node_major = int(node_ver.lstrip("v").split(".")[0]) + # Node < 19 doesn't expose WebCrypto as a global; mongodb driver needs it. + # The flag was removed in Node 22, so only add it for Node 16-18. + if node_major < 19: + existing = env.get("NODE_OPTIONS", "") + env["NODE_OPTIONS"] = ( + f"{existing} --experimental-global-webcrypto".strip() + ) + except (subprocess.CalledProcessError, ValueError): + pass try: with server_log.open("w") as fid: # Capture output while still streaming it to the file proc = subprocess.Popen( - shlex.split(cmd), + cmd if PLATFORM == "win32" else shlex.split(cmd), stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, + env=env, + shell=(PLATFORM == "win32"), ) output_lines = [] for line in proc.stdout: @@ -109,7 +170,7 @@ def start_mongodb_runner(opts, data): LOGGER.error("server.log: %s", server_log.read_text()) LOGGER.error(str(e)) raise e - LOGGER.info(f"Running mongodb-runner using {binary} {target}... done.") + LOGGER.info(f"Running mongodb-runner using {binary}... done.") cluster_file = Path(config["runnerDir"]) / f"m-{config['id']}.json" server_info = json.loads(cluster_file.read_text()) cluster_file.unlink() diff --git a/.evergreen/tests/test-install-binaries.sh b/.evergreen/tests/test-install-binaries.sh index e80254da4..02ec59617 100755 --- a/.evergreen/tests/test-install-binaries.sh +++ b/.evergreen/tests/test-install-binaries.sh @@ -7,7 +7,19 @@ SCRIPT_DIR=$(dirname ${BASH_SOURCE[0]}) pushd $SCRIPT_DIR/.. ./install-node.sh -npx -y mongodb-runner --help +. ./init-node-and-npm-env.sh +MR_INSTALL_DIR=$(mktemp -d) +case "$(uname -s)" in + CYGWIN*) MR_INSTALL_DIR=$(cygpath -m "$MR_INSTALL_DIR") ;; +esac +trap 'rm -rf "$MR_INSTALL_DIR"' EXIT +printf '{"name":"t","version":"1.0.0","dependencies":{"mongodb-runner":"6.7.1"},"overrides":{"@mongodb-js/oidc-mock-provider":"0.13.7"}}' > "$MR_INSTALL_DIR/package.json" +# Use a subshell + cd so npm install uses process.cwd() instead of --prefix, +# which avoids MSYS2/Cygwin path translation issues on Windows. +(cd "$MR_INSTALL_DIR" && npm install --silent) +# Invoke node directly to bypass the .bin/ POSIX shim, which can fail on +# Windows (CRLF shebang line or missing interpreter). +node "$MR_INSTALL_DIR/node_modules/mongodb-runner/bin/runner.js" --help source ./install-rust.sh rustup install stable diff --git a/.evergreen/tests/test-mongodb-runner.sh b/.evergreen/tests/test-mongodb-runner.sh index e9a4827cd..8c6b2cd49 100755 --- a/.evergreen/tests/test-mongodb-runner.sh +++ b/.evergreen/tests/test-mongodb-runner.sh @@ -15,25 +15,41 @@ pushd $SCRIPT_DIR/.. > /dev/null # shellcheck disable=SC2120 function connect_mongodb() { local use_tls=false + local use_auth=false + local eval_cmd='db.runCommand({"ping":1})' # Parse flags while [[ $# -gt 0 ]]; do case "$1" in --ssl) use_tls=true; shift ;; + --auth) use_auth=true; shift ;; + --eval-cmd) + if [[ -z "${2:-}" ]]; then + echo "Missing value for --eval-cmd" + return 1 + fi + eval_cmd="$2" + shift 2 + ;; *) echo "Unknown option: $1"; return 1 ;; esac done URI="mongodb://localhost:27017/?directConnection=true&serverSelectionTimeoutMS=10000" + if [[ "$use_auth" == "true" ]]; then + URI="mongodb://bob:pwd123@localhost:27017/?directConnection=true&serverSelectionTimeoutMS=10000&authSource=admin" + fi local TLS_OPTS=() if [[ "$use_tls" == "true" ]]; then TLS_OPTS+=("--tls" "--tlsCertificateKeyFile" "${DRIVERS_TOOLS}/.evergreen/x509gen/server.pem") TLS_OPTS+=("--tlsCAFile" "${DRIVERS_TOOLS}/.evergreen/x509gen/ca.pem") fi + local result=0 echo "Connecting to server..." # shellcheck disable=SC2068 - $MONGODB_BINARIES/mongosh "$URI" ${TLS_OPTS[@]:-} --eval "db.runCommand({\"ping\":1})" + $MONGODB_BINARIES/mongosh "$URI" ${TLS_OPTS[@]:-} --eval "$eval_cmd" || result=$? echo "Connecting to server... done." + return $result } # Test for default, then test cli options. @@ -41,13 +57,22 @@ bash ./run-mongodb.sh start connect_mongodb bash ./run-mongodb.sh start --topology standalone --auth -connect_mongodb +connect_mongodb --auth bash ./run-mongodb.sh start --version 7.0 --topology replica_set --ssl connect_mongodb --ssl bash ./run-mongodb.sh start --version latest --topology sharded_cluster --auth --ssl -connect_mongodb --ssl +connect_mongodb --ssl --auth + +# Verify that auth is enforced when starting with AUTH=auth SSL=yes. +# An unauthenticated connection must be rejected, and an authenticated one must succeed. +AUTH=auth SSL=yes bash ./run-mongodb.sh start +if connect_mongodb --ssl --eval-cmd 'db.adminCommand({listDatabases:1})' 2>/dev/null; then + echo "ERROR: unauthenticated connection should have been rejected on an auth+ssl server" + exit 1 +fi +connect_mongodb --ssl --auth # Ensure that we can use a downloaded mongodb directory. DOWNLOAD_DIR=mongodl_test