remote_test.go

  1package runner
  2
  3import (
  4	"bytes"
  5	"context"
  6	"os"
  7	"path/filepath"
  8	"strings"
  9	"testing"
 10	"time"
 11
 12	"git.kilimanjaro.io/ci/internal/engine"
 13	"git.kilimanjaro.io/ci/internal/manifest"
 14)
 15
 16func TestLeases(t *testing.T) {
 17	f := newFixture(t, map[string]string{"README": "x"})
 18	ctx := context.Background()
 19	if ok, _, err := f.r.Lock(ctx, "fleet", "laptop-run"); !ok || err != nil {
 20		t.Fatalf("lock: %v %v", ok, err)
 21	}
 22	if ok, by, _ := f.r.Lock(ctx, "fleet", "other"); ok || by != "laptop-run" {
 23		t.Fatalf("second holder: %v %q", ok, by)
 24	}
 25	if ok, _, _ := f.r.Lock(ctx, "fleet", "laptop-run"); !ok {
 26		t.Fatal("renewal refused")
 27	}
 28	// the lease is the same file lock the runner's segments take
 29	release, by, err := engine.FileLocker{Dir: f.r.Cfg.LocksDir}.TryLock(ctx, "fleet", "segment")
 30	if release != nil || by != "laptop-run" || err != nil {
 31		t.Fatalf("segment got the group: %v %q %v", release != nil, by, err)
 32	}
 33	f.r.Unlock("fleet", "laptop-run")
 34	if ok, _, _ := f.r.Lock(ctx, "fleet", "other"); !ok {
 35		t.Fatal("lock after unlock")
 36	}
 37	// an expired lease is taken over
 38	f.r.leaseTable().m["fleet"].expires = time.Now().Add(-time.Second)
 39	if ok, _, _ := f.r.Lock(ctx, "fleet", "third"); !ok {
 40		t.Fatal("expired lease not released")
 41	}
 42	if _, _, err := f.r.Lock(ctx, "../x", "h"); err == nil {
 43		t.Error("bad group accepted")
 44	}
 45}
 46
 47// A local run's snapshot is imported; its manual rollback runs on the
 48// runner with fresh checkouts at the recorded commit and the local run's
 49// $CI_STATE.
 50func TestImportAndManualActionOfLocalRun(t *testing.T) {
 51	wf := "engine: devenv\nsteps: [deploy]\nactions:\n  deploy: { command: 'echo prev-of-local > $CI_STATE/prev; cat marker' }\n  rollback: { command: 'cat $CI_STATE/prev; cat marker', manual: true }\n"
 52	f := newFixture(t, map[string]string{".tangled/workflows/deploy.yml": wf, "marker": "from-commit"})
 53	m, err := manifest.Parse("deploy", []byte(wf))
 54	if err != nil {
 55		t.Fatal(err)
 56	}
 57	// the "local" run, somewhere else on disk
 58	local := engine.Dir(filepath.Join(t.TempDir(), "3mxlocalrun22"))
 59	root := filepath.Join(local.Path(), "demo")
 60	os.MkdirAll(root, 0o755)
 61	os.WriteFile(filepath.Join(root, "marker"), []byte("from-laptop"), 0o644)
 62	meta := engine.Meta{Run: "3mxlocalrun22", Workflow: "deploy", Repo: "demo", Sha: f.sha, Kind: "manual", Host: "local", Root: root,
 63		Checkouts: []engine.Checkout{{Repo: "demo", Path: root, Rev: "linked"}}}
 64	e, err := engine.Create(m, local, meta, engine.Bash{}, engine.Options{})
 65	if err != nil {
 66		t.Fatal(err)
 67	}
 68	if st, _ := e.Run(context.Background()); st.Status != engine.StatusSuccess {
 69		t.Fatalf("local run: %+v", st.End)
 70	}
 71	e.Log.Close()
 72
 73	var snap bytes.Buffer
 74	if err := engine.Snapshot(local, &snap); err != nil {
 75		t.Fatal(err)
 76	}
 77	if err := f.r.Import("3mxlocalrun22", &snap); err != nil {
 78		t.Fatal(err)
 79	}
 80	x, err := f.r.GetRun("3mxlocalrun22")
 81	if err != nil || x.Status != engine.StatusSuccess || x.Workflow != "deploy" {
 82		t.Fatalf("imported %+v %v", x, err)
 83	}
 84	if _, err := os.Stat(filepath.Join(x.Dir, "demo")); err == nil {
 85		t.Error("checkouts were imported")
 86	}
 87
 88	child, err := f.r.ManualAction(context.Background(), x, "rollback", "did:plc:me")
 89	if err != nil {
 90		t.Fatal(err)
 91	}
 92	cx := f.waitStatus(child, engine.StatusSuccess)
 93	b, _ := os.ReadFile(engine.Dir(cx.Dir).Events())
 94	if !strings.Contains(string(b), `"content":"prev-of-local"`) || !strings.Contains(string(b), `"content":"from-commit"`) {
 95		t.Errorf("rollback did not use the local state and the recorded commit:\n%s", b)
 96	}
 97
 98	// a dirty local run can't be acted on remotely
 99	st, _ := engine.Load(engine.Dir(x.Dir))
100	st.Meta.Dirty = true
101	st.Save(engine.Dir(x.Dir))
102	if _, err := f.r.ManualAction(context.Background(), x, "rollback", ""); err == nil || !strings.Contains(err.Error(), "working copy with changes") {
103		t.Errorf("dirty: %v", err)
104	}
105	// imports are only for local runs, and ids must be plain
106	if err := f.r.Import("../../etc", strings.NewReader("")); err == nil {
107		t.Error("bad id accepted")
108	}
109}