mirror of
https://github.com/git-pkgs/proxy.git
synced 2026-11-09 15:49:16 -05:00
162 lines
6 KiB
Go
162 lines
6 KiB
Go
|
|
package server
|
||
|
|
|
||
|
|
import (
|
||
|
|
"archive/tar"
|
||
|
|
"bytes"
|
||
|
|
"compress/gzip"
|
||
|
|
"io"
|
||
|
|
"log/slog"
|
||
|
|
"net/http"
|
||
|
|
"net/http/httptest"
|
||
|
|
"strings"
|
||
|
|
"sync/atomic"
|
||
|
|
"testing"
|
||
|
|
|
||
|
|
"github.com/git-pkgs/proxy/internal/config"
|
||
|
|
"github.com/git-pkgs/proxy/internal/handler"
|
||
|
|
"github.com/git-pkgs/proxy/internal/mirror"
|
||
|
|
"github.com/git-pkgs/registries"
|
||
|
|
)
|
||
|
|
|
||
|
|
const (
|
||
|
|
juliaExampleUUID = "7876af07-990d-54b4-ab0e-23690620f79a"
|
||
|
|
juliaExampleTree = "e1f0e1a832ccd8e97d6d0348dec33ee139a5aeaf"
|
||
|
|
juliaGeneralUUID = "23338594-aafe-5451-b93e-139f81909106"
|
||
|
|
juliaRegistryTree = "465d1e55da75e226473a37c40bf2c20aee19234d"
|
||
|
|
)
|
||
|
|
|
||
|
|
// juliaUpstream serves the General registry's files for Example, which has
|
||
|
|
// version 0.5.5, and as a Pkg server /registries, the General registry
|
||
|
|
// tarball and Example's 0.5.5 source tarball. It counts package downloads.
|
||
|
|
type juliaUpstream struct {
|
||
|
|
*httptest.Server
|
||
|
|
tarball string
|
||
|
|
packageHits atomic.Int32
|
||
|
|
registryHits atomic.Int32
|
||
|
|
}
|
||
|
|
|
||
|
|
func newJuliaUpstream(t *testing.T) *juliaUpstream {
|
||
|
|
t.Helper()
|
||
|
|
u := &juliaUpstream{tarball: "Example 0.5.5 source"}
|
||
|
|
registry := juliaRegistryTarball(t, "[packages]\n"+juliaExampleUUID+` = { name = "Example", path = "E/Example" }`+"\n")
|
||
|
|
u.Server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||
|
|
switch r.URL.Path {
|
||
|
|
case "/E/Example/Package.toml":
|
||
|
|
_, _ = io.WriteString(w, "name = \"Example\"\nuuid = \""+juliaExampleUUID+"\"\nrepo = \"https://github.com/JuliaLang/Example.jl.git\"\n")
|
||
|
|
case "/E/Example/Versions.toml":
|
||
|
|
_, _ = io.WriteString(w, "[\"0.5.4\"]\ngit-tree-sha1 = \"11820aa9c229fd3833d4bd69e5e75ef4e7273bf1\"\n\n[\"0.5.5\"]\ngit-tree-sha1 = \""+juliaExampleTree+"\"\n")
|
||
|
|
case "/registries":
|
||
|
|
_, _ = io.WriteString(w, "/registry/"+juliaGeneralUUID+"/"+juliaRegistryTree+"\n")
|
||
|
|
case "/registry/" + juliaGeneralUUID + "/" + juliaRegistryTree:
|
||
|
|
u.registryHits.Add(1)
|
||
|
|
_, _ = w.Write(registry)
|
||
|
|
case "/package/" + juliaExampleUUID + "/" + juliaExampleTree:
|
||
|
|
u.packageHits.Add(1)
|
||
|
|
_, _ = io.WriteString(w, u.tarball)
|
||
|
|
default:
|
||
|
|
http.NotFound(w, r)
|
||
|
|
}
|
||
|
|
}))
|
||
|
|
t.Cleanup(u.Close)
|
||
|
|
return u
|
||
|
|
}
|
||
|
|
|
||
|
|
// juliaRegistryTarball returns a gzipped registry tarball holding registryToml.
|
||
|
|
func juliaRegistryTarball(t *testing.T, registryToml string) []byte {
|
||
|
|
t.Helper()
|
||
|
|
var buf bytes.Buffer
|
||
|
|
gz := gzip.NewWriter(&buf)
|
||
|
|
tw := tar.NewWriter(gz)
|
||
|
|
if err := tw.WriteHeader(&tar.Header{Name: "Registry.toml", Mode: 0o644, Size: int64(len(registryToml))}); err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
if _, err := io.WriteString(tw, registryToml); err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
if err := tw.Close(); err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
if err := gz.Close(); err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
return buf.Bytes()
|
||
|
|
}
|
||
|
|
|
||
|
|
// mirrorJulia mirrors purls from upstream as the Pkg server, with the
|
||
|
|
// registry's files read from it too.
|
||
|
|
func mirrorJulia(t *testing.T, upstream *juliaUpstream, purls ...string) (*mirror.Progress, *handler.Proxy) {
|
||
|
|
t.Helper()
|
||
|
|
cfg := config.Default()
|
||
|
|
cfg.Upstream.Julia = upstream.URL
|
||
|
|
versions := mirror.RegistryVersions{
|
||
|
|
Client: registries.NewClient(registries.WithHTTPClient(upstream.Client())),
|
||
|
|
URL: func(string, string) (string, error) { return upstream.URL, nil },
|
||
|
|
}
|
||
|
|
return mirrorWithVersions(t, cfg, versions, purls...)
|
||
|
|
}
|
||
|
|
|
||
|
|
func getJuliaPackage(julia *handler.JuliaHandler) *httptest.ResponseRecorder {
|
||
|
|
rec := httptest.NewRecorder()
|
||
|
|
julia.Routes().ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/package/"+juliaExampleUUID+"/"+juliaExampleTree, nil))
|
||
|
|
return rec
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestMirrorJuliaPackage(t *testing.T) {
|
||
|
|
upstream := newJuliaUpstream(t)
|
||
|
|
|
||
|
|
progress, proxy := mirrorJulia(t, upstream, "pkg:julia/Example@0.5.5?uuid="+juliaExampleUUID)
|
||
|
|
|
||
|
|
if progress.Completed != 1 || progress.Failed != 0 || upstream.packageHits.Load() != 1 {
|
||
|
|
t.Fatalf("completed = %d, failed = %d, package hits = %d, errors = %v", progress.Completed, progress.Failed, upstream.packageHits.Load(), progress.Errors)
|
||
|
|
}
|
||
|
|
if cached, _ := proxy.GetCachedArtifact(t.Context(), "julia", "Example", juliaExampleTree, juliaExampleTree+".tar.gz"); cached == nil {
|
||
|
|
t.Fatal("the tarball isn't cached under Example and its tree hash")
|
||
|
|
} else {
|
||
|
|
_ = cached.Reader.Close()
|
||
|
|
}
|
||
|
|
|
||
|
|
// With the registry's names loaded, the handler finds the tarball under
|
||
|
|
// the package's name without fetching it again.
|
||
|
|
julia := handler.NewJuliaHandlerWithUpstream(proxy, upstream.URL)
|
||
|
|
if rec := getJuliaPackage(julia); rec.Code != http.StatusOK || rec.Body.String() != upstream.tarball {
|
||
|
|
t.Errorf("status = %d, body = %q, want 200 and the tarball", rec.Code, rec.Body)
|
||
|
|
}
|
||
|
|
if upstream.registryHits.Load() == 0 {
|
||
|
|
t.Error("the handler didn't load the registry's names")
|
||
|
|
}
|
||
|
|
|
||
|
|
// Without the upstream, a new handler can't load the names, and finds
|
||
|
|
// the tarball by its tree hash.
|
||
|
|
upstream.Close()
|
||
|
|
if rec := getJuliaPackage(handler.NewJuliaHandlerWithUpstream(proxy, upstream.URL)); rec.Code != http.StatusOK || rec.Body.String() != upstream.tarball {
|
||
|
|
t.Errorf("from cache: status = %d, body = %q, want 200 and the tarball", rec.Code, rec.Body)
|
||
|
|
}
|
||
|
|
if got := upstream.packageHits.Load(); got != 1 {
|
||
|
|
t.Errorf("package fetched %d times, want 1", got)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestMirrorJuliaRefusesOtherUUID(t *testing.T) {
|
||
|
|
upstream := newJuliaUpstream(t)
|
||
|
|
|
||
|
|
progress, _ := mirrorJulia(t, upstream, "pkg:julia/Example@0.5.5?uuid=00000000-0000-0000-0000-000000000000")
|
||
|
|
|
||
|
|
if progress.Failed != 1 || len(progress.Errors) != 1 || !strings.Contains(progress.Errors[0].Error, juliaExampleUUID) {
|
||
|
|
t.Errorf("failed = %d, errors = %v; want a failure naming General's uuid", progress.Failed, progress.Errors)
|
||
|
|
}
|
||
|
|
if upstream.packageHits.Load() != 0 {
|
||
|
|
t.Error("a tarball was fetched for a package General doesn't have")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestServerMirrorFetchesJuliaFromUpstream(t *testing.T) {
|
||
|
|
cfg := config.Default()
|
||
|
|
cfg.Upstream.Julia = "https://julia.example.com"
|
||
|
|
logger := slog.New(slog.NewTextHandler(io.Discard, nil))
|
||
|
|
proxy, _ := NewMirrorProxy(cfg, nil, nil, logger)
|
||
|
|
s := &Server{cfg: cfg, logger: logger, ecr: newECRTokens(logger)}
|
||
|
|
|
||
|
|
if got := s.newMirror(proxy, 1).JuliaUpstream; got != cfg.Upstream.Julia {
|
||
|
|
t.Errorf("JuliaUpstream = %q, want upstream.julia", got)
|
||
|
|
}
|
||
|
|
}
|