vector: bound request buffering before authentication and document installation (#12537)

* s3vectors: bound body buffering before authentication

* docs: include vector engine installation and source builds

* s3vectors: prevent MIME sniffing of JSON error responses

* docs: load vector Cargo configuration when building from source

* docs: note the native protoc requirement for Windows vector builds

* docs: make vector source checkout and working directory explicit

* ci: handle deb822 sources during Ubuntu mirror failover

* ci: bound Ubuntu mirror attempts and reject partial index updates
This commit is contained in:
Chris Lu authored and GitHub committed 2026-11-10 04:21:56 -08:00
1 parent 8236c8d136
commit 6c473433c5
9 files changed
+308 -20

No files matched your search

+8 -10
View File
@@ -3,17 +3,15 @@
# unreachable. See docker/Dockerfile.e2e.
set -e
# Every rewrite starts from the pristine list, so a mirror that just failed does
# not become the pattern the next rewrite has to match.
[ -f /etc/apt/sources.list.orig ] || cp /etc/apt/sources.list /etc/apt/sources.list.orig
for mirror in azure.archive.ubuntu.com archive.ubuntu.com; do
# Any archive host, so this works whether the pristine list came from the
# base image (archive.ubuntu.com) or a CI runner (azure.archive.ubuntu.com).
sed "s|http://[a-z0-9.]*archive\.ubuntu\.com/ubuntu|http://$mirror/ubuntu|g; s|http://security\.ubuntu\.com/ubuntu|http://$mirror/ubuntu|g" \
/etc/apt/sources.list.orig > /etc/apt/sources.list
if apt-get -o Acquire::Retries=3 -o Acquire::http::Timeout=15 update && \
DEBIAN_FRONTEND=noninteractive apt-get -o Acquire::Retries=3 -o Acquire::http::Timeout=15 install -y \
for apt_source in /etc/apt/sources.list /etc/apt/sources.list.d/*.list /etc/apt/sources.list.d/*.sources; do
[ -f "$apt_source" ] || continue
[ -f "$apt_source.orig" ] || cp "$apt_source" "$apt_source.orig"
sed -E "s|(https?://)[a-z0-9.]*archive\.ubuntu\.com/ubuntu|\1$mirror/ubuntu|g; s|(https?://)security\.ubuntu\.com/ubuntu|\1$mirror/ubuntu|g" \
"$apt_source.orig" > "$apt_source"
done
if timeout --kill-after=5s 120s apt-get -o Acquire::Retries=1 -o Acquire::http::Timeout=15 -o Acquire::https::Timeout=15 -o APT::Update::Error-Mode=any update && \
DEBIAN_FRONTEND=noninteractive timeout --kill-after=5s 180s apt-get -o Acquire::Retries=1 -o Acquire::http::Timeout=15 -o Acquire::https::Timeout=15 install -y \
--no-install-recommends --no-install-suggests "$@"; then
apt-get clean
rm -rf /var/lib/apt/lists/*
+110
View File
@@ -0,0 +1,110 @@
import json
import os
from pathlib import Path
import subprocess
import tempfile
import unittest
class AptInstallTests(unittest.TestCase):
def run_helper(self, sources, fail_action="update", timeout_azure=False):
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
apt = root / "apt"
(apt / "sources.list.d").mkdir(parents=True)
(apt / "sources.list").write_text("")
for name, content in sources.items():
(apt / name).write_text(content)
helper = root / "apt-install"
helper.write_text(Path(__file__).with_name("apt-install").read_text()
.replace("/etc/apt", str(apt))
.replace("/var/lib/apt", str(root / "state")))
commands = root / "bin"
commands.mkdir()
fake_apt = commands / "apt-get"
fake_apt.write_text(r"""#!/usr/bin/env python3
import json, os, pathlib, sys
root = pathlib.Path(os.environ["VECTOR_APT_TEST_ROOT"])
apt = root / "apt"
sources = [apt / "sources.list", *apt.glob("sources.list.d/*.list"), *apt.glob("sources.list.d/*.sources")]
content = "".join(p.read_text() for p in sources)
action = next(a for a in sys.argv[1:] if a in ("update", "install", "clean"))
with (root / "calls").open("a") as output:
output.write(json.dumps({"action": action, "sources": content, "args": sys.argv[1:]}) + "\n")
if "azure.archive.ubuntu.com" in content and action == "update" and os.environ["VECTOR_APT_FAIL_ACTION"] == "partial":
sys.exit(100 if "APT::Update::Error-Mode=any" in sys.argv else 0)
if "azure.archive.ubuntu.com" in content and action == os.environ["VECTOR_APT_FAIL_ACTION"]:
sys.exit(100)
""")
fake_apt.chmod(0o755)
fake_timeout = commands / "timeout"
fake_timeout.write_text(r"""#!/usr/bin/env python3
import json, os, pathlib, sys
root = pathlib.Path(os.environ["VECTOR_APT_TEST_ROOT"])
apt = root / "apt"
sources = [apt / "sources.list", *apt.glob("sources.list.d/*.list"), *apt.glob("sources.list.d/*.sources")]
content = "".join(p.read_text() for p in sources)
with (root / "calls").open("a") as output:
output.write(json.dumps({"action": "timeout", "args": sys.argv[1:]}) + "\n")
if os.environ["VECTOR_APT_TIMEOUT_AZURE"] and "azure.archive.ubuntu.com" in content:
sys.exit(124)
os.execvp(sys.argv[3], sys.argv[3:])
""")
fake_timeout.chmod(0o755)
result = subprocess.run(["/bin/sh", str(helper), "fuse"],
env={**os.environ, "PATH": str(commands) + os.pathsep + os.environ["PATH"],
"VECTOR_APT_TEST_ROOT": str(root), "VECTOR_APT_FAIL_ACTION": fail_action,
"VECTOR_APT_TIMEOUT_AZURE": "1" if timeout_azure else ""},
text=True, capture_output=True, timeout=5)
calls = [json.loads(line) for line in (root / "calls").read_text().splitlines()] if (root / "calls").exists() else []
return result, calls
def test_fallback_rewrites_all_source_formats(self):
for protocol in ("http", "https"):
for layout in ("legacy", "deb822", "mixed"):
with self.subTest(protocol=protocol, layout=layout):
sources = {}
if layout in ("legacy", "mixed"):
sources["sources.list"] = f"deb {protocol}://azure.archive.ubuntu.com/ubuntu noble main\n"
if layout in ("deb822", "mixed"):
sources["sources.list.d/ubuntu.sources"] = f"Types: deb\nURIs: {protocol}://azure.archive.ubuntu.com/ubuntu\nSuites: noble\nComponents: main\n"
result, calls = self.run_helper(sources)
self.assertEqual(result.returncode, 0, result.stderr)
installs = [call for call in calls if call["action"] == "install"]
self.assertEqual(len(installs), 1)
self.assertNotIn("azure.archive.ubuntu.com", installs[0]["sources"])
self.assertIn(protocol + "://archive.ubuntu.com", installs[0]["sources"])
def test_install_failure_also_tries_the_next_mirror(self):
result, calls = self.run_helper({"sources.list.d/ubuntu.sources":
"Types: deb\nURIs: http://security.ubuntu.com/ubuntu\nSuites: noble-security\nComponents: main\n"},
fail_action="install")
self.assertEqual(result.returncode, 0, result.stderr)
installs = [call for call in calls if call["action"] == "install"]
self.assertEqual(len(installs), 2)
self.assertIn("azure.archive.ubuntu.com", installs[0]["sources"])
self.assertNotIn("azure.archive.ubuntu.com", installs[1]["sources"])
def test_partial_update_does_not_install_stale_indexes(self):
result, calls = self.run_helper({"sources.list": "deb http://azure.archive.ubuntu.com/ubuntu noble main\n"},
fail_action="partial")
self.assertEqual(result.returncode, 0, result.stderr)
installs = [call for call in calls if call["action"] == "install"]
self.assertEqual(len(installs), 1)
self.assertNotIn("azure.archive.ubuntu.com", installs[0]["sources"])
def test_timeout_advances_to_the_next_mirror(self):
result, calls = self.run_helper({"sources.list": "deb http://azure.archive.ubuntu.com/ubuntu noble main\n"},
fail_action="none", timeout_azure=True)
self.assertEqual(result.returncode, 0, result.stderr)
timeouts = [call for call in calls if call["action"] == "timeout"]
self.assertEqual(len(timeouts), 3)
for call in timeouts:
self.assertEqual(call["args"][0], "--kill-after=5s")
self.assertLessEqual(int(call["args"][1].removesuffix("s")), 180)
installs = [call for call in calls if call["action"] == "install"]
self.assertNotIn("azure.archive.ubuntu.com", installs[0]["sources"])
if __name__ == "__main__":
unittest.main()