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}