124 lines
4.5 KiB
Go
124 lines
4.5 KiB
Go
//go:build linux
|
|
|
|
package main
|
|
|
|
// An OPT-IN end-to-end run of a mirror sync against the real world: a real slot
|
|
// (docker, --network none) running Gitea on its socket, a real reach grant with
|
|
// two small public GitHub repos, a real `git fetch` over this machine's own
|
|
// connection, and a real push through the bridge into the server. Skipped
|
|
// unless BASTION_MIRROR_E2E=1 — it needs docker, git, the internet and root.
|
|
//
|
|
// go test -c -o /tmp/b.test . && BASTION_MIRROR_E2E=1 /tmp/b.test -test.run TestMirrorEndToEnd -test.v
|
|
|
|
import (
|
|
"context"
|
|
"crypto/ed25519"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"io"
|
|
"net"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestMirrorEndToEnd(t *testing.T) {
|
|
if os.Getenv("BASTION_MIRROR_E2E") != "1" {
|
|
t.Skip("set BASTION_MIRROR_E2E=1 (needs docker, git, the internet)")
|
|
}
|
|
t.Setenv("XDG_CONFIG_HOME", t.TempDir())
|
|
name := "e2emirror"
|
|
spec := harnessSpec{Mode: "service", ServiceOp: "up", ServiceName: name, owner: "h.example",
|
|
Image: "gitea/gitea:1.26.2-rootless", Socket: "gitea.sock", DataPath: "/var/lib/gitea", DataUID: 1000,
|
|
Envs: map[string]string{
|
|
"GITEA__security__INSTALL_LOCK": "true",
|
|
"GITEA__server__PROTOCOL": "http+unix",
|
|
"GITEA__server__HTTP_ADDR": "/var/lib/gitea/gitea.sock",
|
|
"GITEA__server__UNIX_SOCKET_PERMISSION": "660",
|
|
"GITEA__server__ROOT_URL": "http://forge.local/",
|
|
"GITEA__server__DISABLE_SSH": "true",
|
|
"GITEA__repository__ENABLE_PUSH_CREATE_USER": "true",
|
|
"GITEA__repository__DEFAULT_PUSH_CREATE_PRIVATE": "true",
|
|
}}
|
|
if b, code := runService(spec); code != 0 {
|
|
t.Fatalf("up: %s", b)
|
|
}
|
|
defer runService(harnessSpec{Mode: "service", ServiceOp: "down", ServiceName: name, Purge: true, owner: "h.example"})
|
|
|
|
sock, ok := "", false
|
|
for i := 0; i < 60 && !ok; i++ {
|
|
time.Sleep(time.Second)
|
|
sock, ok = serviceUpstream(name)
|
|
}
|
|
if !ok {
|
|
t.Fatal("the server's socket never answered")
|
|
}
|
|
cname := serviceContainerPrefix + name
|
|
rt := serviceRuntime()
|
|
if out, err := runCmd(time.Minute, rt, "exec", cname, "gitea", "admin", "user", "create", "--username", "rig",
|
|
"--password", "E2e-pass-12345!", "--email", "rig@example.invalid", "--admin", "--must-change-password=false"); err != nil {
|
|
t.Fatalf("create user: %s", out)
|
|
}
|
|
tok, err := runCmd(time.Minute, rt, "exec", cname, "gitea", "admin", "user", "generate-access-token",
|
|
"--username", "rig", "--token-name", "e2e", "--scopes", "all", "--raw")
|
|
if err != nil {
|
|
t.Fatalf("token: %s", tok)
|
|
}
|
|
tok = strings.TrimSpace(tok)
|
|
|
|
dir := t.TempDir()
|
|
_, priv, _ := ed25519.GenerateKey(nil)
|
|
pub := base64.StdEncoding.EncodeToString(priv.Public().(ed25519.PublicKey))
|
|
terms := reachTermsFor("h.example", name, 30)
|
|
terms["mirrors"] = []map[string]any{
|
|
{"url": "https://github.com/kelseyhightower/nocode.git", "repo": "rig/nocode"},
|
|
{"url": "https://github.com/golang/example.git", "repo": "rig/example"},
|
|
}
|
|
tc, sig := signedTerms(t, priv, terms)
|
|
if err := dcnSaveFile(dir, &dcnGrantFile{VerifyPubkey: pub, Stores: map[string]dcnGrantRec{},
|
|
Reach: map[string]dcnGrantRec{"1": {TermsCanonical: tc, Signature: sig}}}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
b, code := runService(harnessSpec{Mode: "service", ServiceOp: "mirror", ServiceName: name,
|
|
PushToken: tok, owner: "h.example", dcnDir: dir})
|
|
t.Logf("mirror result: %s", b)
|
|
if code != 0 {
|
|
t.Fatalf("the sync must succeed: %s", b)
|
|
}
|
|
|
|
// Read the server's own view over its socket: both repos exist, with commits.
|
|
hc := &http.Client{Transport: &http.Transport{DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) {
|
|
return (&net.Dialer{}).DialContext(ctx, "unix", sock)
|
|
}}}
|
|
// Gitea indexes a pushed repo in a background queue, so poll for a moment.
|
|
for _, repo := range []string{"rig/nocode", "rig/example"} {
|
|
var last string
|
|
arrived := false
|
|
for i := 0; i < 30 && !arrived; i++ {
|
|
req, _ := http.NewRequest("GET", "http://forge.local/api/v1/repos/"+repo+"/commits?limit=1", nil)
|
|
req.Header.Set("Authorization", "token "+tok)
|
|
resp, err := hc.Do(req)
|
|
if err != nil {
|
|
t.Fatalf("%s: %v", repo, err)
|
|
}
|
|
body, _ := io.ReadAll(resp.Body)
|
|
resp.Body.Close()
|
|
var commits []map[string]any
|
|
_ = json.Unmarshal(body, &commits)
|
|
if resp.StatusCode == 200 && len(commits) > 0 {
|
|
arrived = true
|
|
t.Logf("%s arrived after %ds: latest commit %v", repo, i, commits[0]["sha"])
|
|
} else {
|
|
last = string(body)
|
|
time.Sleep(time.Second)
|
|
}
|
|
}
|
|
if !arrived {
|
|
t.Fatalf("%s did not arrive in the server: %s", repo, last)
|
|
}
|
|
}
|
|
}
|