Serve deb virtual indexes by hash and bound cached suites
ci/woodpecker/pr/pre-commit Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful
ci/woodpecker/pr/build Pipeline was successful

This commit is contained in:
2026-10-17 12:46:14 +11:00
parent eca686b7bb
commit ed6216d78b
5 changed files with 197 additions and 27 deletions
+54 -7
View File
@@ -152,19 +152,21 @@ func TestVirtualDebMerge(t *testing.T) {
uploadFile(t, "vdeb-b", "vdeb-one_1.0.0_amd64.deb", testsupport.MinimalDeb("vdeb-one", "1.0.0", "amd64"), "application/vnd.debian.binary-package")
uploadFile(t, "vdeb-b", "vdeb-two_2.0.0_amd64.deb", testsupport.MinimalDeb("vdeb-two", "2.0.0", "amd64"), "application/vnd.debian.binary-package")
createVirtual(t, `{"name":"vdeb","package_type":"deb","members":["vdeb-a","vdeb-b"]}`)
defer deleteVirtual(t, "vdeb")
var pk string
// Local indexes regenerate asynchronously; merge only once vdeb-b lists both.
deadline := time.Now().Add(15 * time.Second)
for {
resp, body := doRequest(t, http.MethodGet, api("/api/v1/virtual/vdeb/Packages"), nil, "")
pk = string(body)
if resp.StatusCode == http.StatusOK && strings.Contains(pk, "vdeb-two") || time.Now().After(deadline) {
resp, body := doRequest(t, http.MethodGet, api("/api/v1/local/vdeb-b/Packages"), nil, "")
if resp.StatusCode == http.StatusOK && strings.Contains(string(body), "vdeb-two") || time.Now().After(deadline) {
break
}
time.Sleep(500 * time.Millisecond)
}
createVirtual(t, `{"name":"vdeb","package_type":"deb","members":["vdeb-a","vdeb-b"]}`)
defer deleteVirtual(t, "vdeb")
_, body := doRequest(t, http.MethodGet, api("/api/v1/virtual/vdeb/Packages"), nil, "")
pk := string(body)
if strings.Count(pk, "Package: vdeb-one\n") != 1 || !strings.Contains(pk, "Filename: vdeb-a/pool/vdeb-one_1.0.0_amd64.deb") ||
!strings.Contains(pk, "Filename: vdeb-b/pool/vdeb-two_2.0.0_amd64.deb") {
t.Fatalf("merged Packages should hold vdeb-one from the first member and vdeb-two:\n%s", pk)
@@ -192,3 +194,48 @@ func TestVirtualDebMerge(t *testing.T) {
t.Fatalf("real apt against deb virtual failed: %v\n%s", err, out)
}
}
// TestVirtualDebSuite merges two proxied deb.debian.org remotes and drives a
// real apt through the virtual's dists/<suite> layout (by-hash indexes).
func TestVirtualDebSuite(t *testing.T) {
network := os.Getenv("COMPOSE_NETWORK")
internal := os.Getenv("ARTIFACTAPI_INTERNAL")
if network == "" || internal == "" {
t.Skip("COMPOSE_NETWORK/ARTIFACTAPI_INTERNAL not set; skipping real apt")
}
if _, err := exec.LookPath("docker"); err != nil {
t.Skip("docker not available on the test host")
}
for _, n := range []string{"vdebs-a", "vdebs-b"} {
createRepo(t, `{"name":"`+n+`","package_type":"deb","repo_type":"remote","base_url":"http://deb.debian.org/debian"}`)
defer deleteRepo(t, n)
}
createVirtual(t, `{"name":"vdebs","package_type":"deb","members":["vdebs-a","vdebs-b"]}`)
defer deleteVirtual(t, "vdebs")
resp, rel := doRequest(t, http.MethodGet, api("/api/v1/virtual/vdebs/dists/trixie-updates/Release"), nil, "")
if resp.StatusCode != http.StatusOK || !strings.Contains(string(rel), "Acquire-By-Hash: yes\n") ||
!strings.Contains(string(rel), " main/binary-amd64/Packages.gz\n") {
t.Fatalf("merged suite Release: status %d:\n%s", resp.StatusCode, rel)
}
script := "set -euo pipefail; " +
"rm -f /etc/apt/sources.list.d/*; " +
"echo \"deb [trusted=yes] $URL trixie-updates main\" > /etc/apt/sources.list.d/vdebs.list; " +
"apt-get update; " +
"idx=/tmp/avail; apt-cache dumpavail > $idx; " +
"grep -q '^Filename: vdebs-a/pool/' $idx; ! grep -q '^Filename: vdebs-b/' $idx; " +
"pkg=$(awk '/^Package:/{p=$2} /^Size:/{if(!m||$2<m){m=$2;b=p}} END{print b}' $idx); " +
"cd /tmp && apt-get download $pkg && ls -l /tmp/*.deb"
out, err := exec.Command("docker", "run", "--rm", "--network", network,
"-e", "URL="+strings.TrimRight(internal, "/")+"/api/v1/virtual/vdebs/",
"debian:13", "bash", "-c", script).CombinedOutput()
if err != nil {
t.Fatalf("real apt against deb virtual suite failed: %v\n%s", err, out)
}
t.Logf("%s", out)
resp, _ = doRequest(t, http.MethodGet, api("/api/v1/remote/vdebs-a/dists/trixie-updates/main/binary-amd64/Packages.xz"), nil, "")
if src := resp.Header.Get("X-Artifact-Source"); resp.StatusCode != http.StatusOK || src != "cache" {
t.Fatalf("member index the virtual merged should be cached: status %d source %q", resp.StatusCode, src)
}
}