SERVER-111483: Use more secure and reliable form of installing the to… (#41957)
GitOrigin-RevId: 612823c3fad1e41c1bfedca645f36d0a07fad604
This commit is contained in:
parent
27ea8392bc
commit
cb9d24f20d
7
.devcontainer/.gitignore
vendored
Normal file
7
.devcontainer/.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
# Toolchain configuration is checked in to lock the version
|
||||
# Uncomment the following line if you want each developer to generate their own:
|
||||
# toolchain_config.env
|
||||
|
||||
# Temporary files
|
||||
*.tar.gz
|
||||
*.tmp
|
||||
@ -1,3 +1,4 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
ARG BASE_IMAGE=quay.io/mongodb/bazel-remote-execution:ubuntu24-2025_09_05-17_18_29
|
||||
FROM $BASE_IMAGE
|
||||
ARG BASE_IMAGE
|
||||
@ -9,7 +10,7 @@ ARG USER_GID=$USER_UID
|
||||
# Create the user
|
||||
RUN groupadd $USERNAME && useradd -s /bin/bash --gid $USER_GID -m $USERNAME
|
||||
|
||||
RUN apt-get update && apt-get install -y sudo curl
|
||||
RUN apt-get update && apt-get install -y sudo curl ca-certificates
|
||||
|
||||
# Give user sudo access
|
||||
RUN echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/devcontaineruser && chmod 0440 /etc/sudoers.d/devcontaineruser
|
||||
@ -21,11 +22,24 @@ RUN SNIPPET="export PROMPT_COMMAND='history -a' && export HISTFILE=/commandhisto
|
||||
&& chown -R $USERNAME /commandhistory \
|
||||
&& echo "$SNIPPET" >> "/home/$USERNAME/.bashrc"
|
||||
|
||||
# Toolchain installation
|
||||
RUN curl -o /toolchain_installer.sh http://mongodbtoolchain.build.10gen.cc/installer.sh && chmod a+x /toolchain_installer.sh
|
||||
# Toolchain installation with SHA256 verification
|
||||
# Run "python3 toolchain.py generate" to update toolchain_config.env
|
||||
COPY .devcontainer/toolchain_config.env /tmp/toolchain_config.env
|
||||
RUN set -e; \
|
||||
. /tmp/toolchain_config.env; \
|
||||
echo "Installing toolchain from: $TOOLCHAIN_URL"; \
|
||||
echo "Expected SHA256: $TOOLCHAIN_SHA256"; \
|
||||
curl -fSL "$TOOLCHAIN_URL" -o /tmp/toolchain.tar.gz; \
|
||||
echo "Verifying checksum..."; \
|
||||
echo "$TOOLCHAIN_SHA256 /tmp/toolchain.tar.gz" | sha256sum -c -;
|
||||
RUN echo "Extracting toolchain..."; \
|
||||
mkdir -p /opt/mongodbtoolchain/revisions && tar -xzf /tmp/toolchain.tar.gz -C /opt/mongodbtoolchain/revisions; \
|
||||
rm /tmp/toolchain.tar.gz; \
|
||||
chown -R ${USERNAME} /opt/mongodbtoolchain;
|
||||
|
||||
USER $USERNAME
|
||||
ENV USER ${USERNAME}
|
||||
RUN /toolchain_installer.sh
|
||||
ENV USER=${USERNAME}
|
||||
RUN /opt/mongodbtoolchain/revisions/*/scripts/install.sh; echo "Toolchain installation complete"
|
||||
|
||||
# Bazel telemetry
|
||||
RUN echo "common --bes_keywords=devcontainer:use=true" >> "$HOME/.bazelrc" && \
|
||||
|
||||
174
.devcontainer/toolchain.py
Executable file
174
.devcontainer/toolchain.py
Executable file
@ -0,0 +1,174 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
MongoDB Toolchain Management for DevContainers
|
||||
|
||||
This script handles fetching, downloading, and configuring MongoDB toolchains from S3.
|
||||
Supports both dynamic (latest) and static (locked with SHA256) approaches.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import os
|
||||
import sys
|
||||
import xml.etree.ElementTree as ET
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
from urllib import request
|
||||
from urllib.error import HTTPError, URLError
|
||||
|
||||
|
||||
def list_s3_objects(bucket: str, prefix: str) -> list[dict]:
|
||||
"""Query S3 REST API for objects matching prefix."""
|
||||
try:
|
||||
url = f"https://s3.amazonaws.com/{bucket}?list-type=2&prefix={prefix}"
|
||||
print(f"Querying S3: {url}", file=sys.stderr)
|
||||
|
||||
with request.urlopen(url) as response:
|
||||
xml_data = response.read()
|
||||
|
||||
root = ET.fromstring(xml_data)
|
||||
ns = {"s3": "http://s3.amazonaws.com/doc/2006-03-01/"}
|
||||
|
||||
objects = []
|
||||
for content in root.findall("s3:Contents", ns):
|
||||
key_elem = content.find("s3:Key", ns)
|
||||
modified_elem = content.find("s3:LastModified", ns)
|
||||
|
||||
if key_elem is not None and modified_elem is not None:
|
||||
objects.append(
|
||||
{
|
||||
"Key": key_elem.text,
|
||||
"LastModified": datetime.fromisoformat(
|
||||
modified_elem.text.replace("Z", "+00:00")
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
return objects
|
||||
|
||||
except (HTTPError, URLError, ET.ParseError) as e:
|
||||
print(f"Error querying S3: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def find_latest(bucket: str, prefix: str) -> tuple[str, str, datetime]:
|
||||
"""Find most recently modified artifact."""
|
||||
objects = list_s3_objects(bucket, prefix)
|
||||
|
||||
if not objects:
|
||||
print(f"No artifacts found with prefix: {prefix}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
latest = max(objects, key=lambda x: x["LastModified"])
|
||||
url = f"https://s3.amazonaws.com/{bucket}/{latest['Key']}"
|
||||
|
||||
print(f"Latest: {latest['Key']}", file=sys.stderr)
|
||||
print(f"Modified: {latest['LastModified']}", file=sys.stderr)
|
||||
|
||||
return url, latest["Key"], latest["LastModified"]
|
||||
|
||||
|
||||
def download_file(url: str, output_path: str) -> None:
|
||||
"""Download file from URL."""
|
||||
print(f"Downloading {url}...", file=sys.stderr)
|
||||
try:
|
||||
request.urlretrieve(url, output_path)
|
||||
print(f"Saved to {output_path}", file=sys.stderr)
|
||||
except (HTTPError, URLError) as e:
|
||||
print(f"Download failed: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def calculate_sha256(file_path: str) -> str:
|
||||
"""Calculate SHA256 checksum of file."""
|
||||
sha256_hash = hashlib.sha256()
|
||||
with open(file_path, "rb") as f:
|
||||
for chunk in iter(lambda: f.read(8192), b""):
|
||||
sha256_hash.update(chunk)
|
||||
return sha256_hash.hexdigest()
|
||||
|
||||
|
||||
def generate_config(bucket: str, prefix: str, output_file: str) -> None:
|
||||
"""Generate locked toolchain configuration with SHA256."""
|
||||
url, key, last_modified = find_latest(bucket, prefix)
|
||||
|
||||
# Download to temp location to calculate checksum
|
||||
import tempfile
|
||||
|
||||
with tempfile.NamedTemporaryFile(delete=False, suffix=".tar.gz") as tmp:
|
||||
tmp_path = tmp.name
|
||||
|
||||
try:
|
||||
download_file(url, tmp_path)
|
||||
sha256 = calculate_sha256(tmp_path)
|
||||
print(f"SHA256: {sha256}", file=sys.stderr)
|
||||
finally:
|
||||
os.unlink(tmp_path)
|
||||
|
||||
# Write config file
|
||||
toolchain_id = os.path.basename(key).replace(".tar.gz", "")
|
||||
|
||||
with open(output_file, "w") as f:
|
||||
f.write("# Generated by toolchain.py\n")
|
||||
f.write("# DO NOT EDIT MANUALLY - run: python3 toolchain.py generate\n")
|
||||
f.write("#\n")
|
||||
f.write(f"# Generated: {datetime.now().isoformat()}\n")
|
||||
f.write(f"# Last Modified: {last_modified.isoformat()}\n")
|
||||
f.write(f"# Toolchain ID: {toolchain_id}\n")
|
||||
f.write("\n")
|
||||
f.write(f'TOOLCHAIN_URL="{url}"\n')
|
||||
f.write(f'TOOLCHAIN_SHA256="{sha256}"\n')
|
||||
f.write(f'TOOLCHAIN_KEY="{key}"\n')
|
||||
f.write(f'TOOLCHAIN_LAST_MODIFIED="{last_modified.isoformat()}"\n')
|
||||
|
||||
print(f"\n✅ Configuration written to: {output_file}", file=sys.stderr)
|
||||
print(f"\nContents:", file=sys.stderr)
|
||||
with open(output_file) as f:
|
||||
print(f.read(), file=sys.stderr)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="MongoDB Toolchain Management")
|
||||
subparsers = parser.add_subparsers(dest="command", help="Command")
|
||||
|
||||
# find-latest: Just print the latest URL
|
||||
find_parser = subparsers.add_parser("find-latest", help="Find latest toolchain URL")
|
||||
find_parser.add_argument("--bucket", default="boxes.10gen.com")
|
||||
find_parser.add_argument("--prefix", default="build/toolchain/mongodbtoolchain-ubuntu24")
|
||||
|
||||
# generate: Generate locked config
|
||||
gen_parser = subparsers.add_parser("generate", help="Generate locked config with SHA256")
|
||||
gen_parser.add_argument("--bucket", default="boxes.10gen.com")
|
||||
gen_parser.add_argument("--prefix", default="build/toolchain/mongodbtoolchain-ubuntu24")
|
||||
gen_parser.add_argument("--output", default="toolchain_config.env")
|
||||
|
||||
# download: Download to file
|
||||
dl_parser = subparsers.add_parser("download", help="Download toolchain")
|
||||
dl_parser.add_argument("--bucket", default="boxes.10gen.com")
|
||||
dl_parser.add_argument("--prefix", default="build/toolchain/mongodbtoolchain-ubuntu24")
|
||||
dl_parser.add_argument("--output", required=True, help="Output file path")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if not args.command:
|
||||
parser.print_help()
|
||||
sys.exit(1)
|
||||
|
||||
if args.command == "find-latest":
|
||||
url, _, _ = find_latest(args.bucket, args.prefix)
|
||||
print(url)
|
||||
|
||||
elif args.command == "generate":
|
||||
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
output_path = (
|
||||
args.output if os.path.isabs(args.output) else os.path.join(script_dir, args.output)
|
||||
)
|
||||
generate_config(args.bucket, args.prefix, output_path)
|
||||
|
||||
elif args.command == "download":
|
||||
url, _, _ = find_latest(args.bucket, args.prefix)
|
||||
download_file(url, args.output)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
11
.devcontainer/toolchain_config.env
Normal file
11
.devcontainer/toolchain_config.env
Normal file
@ -0,0 +1,11 @@
|
||||
# Generated by toolchain.py
|
||||
# DO NOT EDIT MANUALLY - run: python3 toolchain.py generate
|
||||
#
|
||||
# Generated: 2025-09-29T13:23:13.132683
|
||||
# Last Modified: 2025-07-21T20:49:55+00:00
|
||||
# Toolchain ID: mongodbtoolchain-ubuntu2404-arm64-c36013b8bab41fcd3cbfd5e4b4590cd0c10ea6ce
|
||||
|
||||
TOOLCHAIN_URL="https://s3.amazonaws.com/boxes.10gen.com/build/toolchain/mongodbtoolchain-ubuntu2404-arm64-c36013b8bab41fcd3cbfd5e4b4590cd0c10ea6ce.tar.gz"
|
||||
TOOLCHAIN_SHA256="cdd2a58ed4a67dfa1be237d6042b7523552b543bb104c20db8f29068f3899fd6"
|
||||
TOOLCHAIN_KEY="build/toolchain/mongodbtoolchain-ubuntu2404-arm64-c36013b8bab41fcd3cbfd5e4b4590cd0c10ea6ce.tar.gz"
|
||||
TOOLCHAIN_LAST_MODIFIED="2025-07-21T20:49:55+00:00"
|
||||
Loading…
Reference in New Issue
Block a user