mirror of
https://github.com/git-pkgs/proxy.git
synced 2026-11-09 15:49:16 -05:00
proxy mirror, mirror API jobs and SBOMs take pkg:git/{host}/{path}@{commit}
and cache the repository and commit in the cache /git serves, so a later
clone is a cache hit, also with the upstream down. Part of #403.
132 lines
4.1 KiB
Go
132 lines
4.1 KiB
Go
package main
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/cgi"
|
|
"net/http/httptest"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/git-pkgs/proxy/internal/gitcache"
|
|
)
|
|
|
|
// gitMirrorUpstream serves team/repo, with one commit on main, from git
|
|
// http-backend, and returns the server and the commit.
|
|
func gitMirrorUpstream(t *testing.T) (*httptest.Server, string) {
|
|
t.Helper()
|
|
git := func(dir string, args ...string) string {
|
|
t.Helper()
|
|
cmd := exec.Command("git", args...)
|
|
cmd.Dir = dir
|
|
cmd.Env = []string{
|
|
"PATH=" + os.Getenv("PATH"), "HOME=" + t.TempDir(), "GIT_CONFIG_NOSYSTEM=1",
|
|
"GIT_AUTHOR_NAME=test", "GIT_AUTHOR_EMAIL=test@example.com",
|
|
"GIT_COMMITTER_NAME=test", "GIT_COMMITTER_EMAIL=test@example.com",
|
|
}
|
|
out, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
t.Fatalf("git %s: %v\n%s", strings.Join(args, " "), err, out)
|
|
}
|
|
return strings.TrimSpace(string(out))
|
|
}
|
|
backend := ""
|
|
for _, name := range []string{"git-http-backend", "git-http-backend.exe"} {
|
|
if path := filepath.Join(git("", "--exec-path"), name); fileExistsForTest(path) {
|
|
backend = path
|
|
}
|
|
}
|
|
if backend == "" {
|
|
t.Skip("git http-backend is not installed")
|
|
}
|
|
root := t.TempDir()
|
|
bare := filepath.Join(root, "team", "repo.git")
|
|
git("", "init", "--quiet", "--bare", "--initial-branch=main", bare)
|
|
work := t.TempDir()
|
|
git("", "clone", "--quiet", bare, work)
|
|
git(work, "commit", "--quiet", "--allow-empty", "-m", "first")
|
|
git(work, "push", "--quiet", "origin", "HEAD:main")
|
|
head := git(work, "rev-parse", "HEAD")
|
|
|
|
server := httptest.NewServer(&cgi.Handler{
|
|
Path: backend,
|
|
Env: []string{"PATH=" + os.Getenv("PATH"), "GIT_PROJECT_ROOT=" + root, "GIT_HTTP_EXPORT_ALL=1"},
|
|
})
|
|
t.Cleanup(server.Close)
|
|
return server, head
|
|
}
|
|
|
|
func fileExistsForTest(path string) bool {
|
|
_, err := os.Stat(path)
|
|
return err == nil
|
|
}
|
|
|
|
// writeGitMirrorConfig writes a config with upstream.git "up" at
|
|
// upstreamURL and the git cache, storage and database under dir.
|
|
func writeGitMirrorConfig(t *testing.T, dir, upstreamURL string) string {
|
|
t.Helper()
|
|
cfg := "storage:\n" +
|
|
" url: file://" + filepath.Join(dir, "artifacts") + "\n" +
|
|
"database:\n" +
|
|
" path: " + filepath.Join(dir, "proxy.db") + "\n" +
|
|
"upstream:\n" +
|
|
" allow_loopback: true\n" +
|
|
" git:\n" +
|
|
" up: " + upstreamURL + "\n" +
|
|
"git:\n" +
|
|
" cache_dir: " + filepath.Join(dir, "git") + "\n"
|
|
path := filepath.Join(dir, "config.yaml")
|
|
if err := os.WriteFile(path, []byte(cfg), 0o600); err != nil {
|
|
t.Fatalf("writing config: %v", err)
|
|
}
|
|
return path
|
|
}
|
|
|
|
func TestMirrorCommandCachesGitCommit(t *testing.T) {
|
|
upstream, head := gitMirrorUpstream(t)
|
|
dir := t.TempDir()
|
|
configPath := writeGitMirrorConfig(t, dir, upstream.URL)
|
|
purl := "pkg:git/" + strings.TrimPrefix(upstream.URL, "http://") + "/team/repo@" + head
|
|
|
|
output := runMirrorProcess(t, 0, "-config", configPath, purl)
|
|
if !strings.Contains(output, "1 downloaded") {
|
|
t.Errorf("output:\n%s", output)
|
|
}
|
|
cached, _ := filepath.Glob(filepath.Join(dir, "git", "up-*", "team", "repo.git"))
|
|
if len(cached) != 1 {
|
|
t.Errorf("cached repositories %v, want team/repo.git", cached)
|
|
}
|
|
|
|
output = runMirrorProcess(t, 0, "-config", configPath, purl)
|
|
if !strings.Contains(output, "1 skipped") {
|
|
t.Errorf("second run output:\n%s", output)
|
|
}
|
|
}
|
|
|
|
// TestMirrorCommandGitCacheInUse checks pkg:git items fail, pointing at the
|
|
// mirror API, while the running server holds git.cache_dir.
|
|
func TestMirrorCommandGitCacheInUse(t *testing.T) {
|
|
upstream, head := gitMirrorUpstream(t)
|
|
dir := t.TempDir()
|
|
configPath := writeGitMirrorConfig(t, dir, upstream.URL)
|
|
cache, err := gitcache.New(gitcache.Options{
|
|
CacheDir: filepath.Join(dir, "git"),
|
|
RefreshWait: time.Second,
|
|
Client: &http.Client{},
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer func() { _ = cache.Close() }()
|
|
purl := "pkg:git/" + strings.TrimPrefix(upstream.URL, "http://") + "/team/repo@" + head
|
|
|
|
output := runMirrorProcess(t, 1, "-config", configPath, purl)
|
|
for _, want := range []string{"in use by another process", "mirror API", "1 failed"} {
|
|
if !strings.Contains(output, want) {
|
|
t.Errorf("output missing %q:\n%s", want, output)
|
|
}
|
|
}
|
|
}
|