mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-11-10 13:58:24 +01:00
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:
1 parent
8236c8d136
commit
6c473433c5
9 files changed
+308
-20
No files matched your search
+8
-10
@@ -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/*
|
||||
|
||||
@@ -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()
|
||||
Reference in new issue
Block a user