vole
Clone
Read only, no credentials:
https://rodent.software/git/vole.git
Write over SSH with a registered key:
git clone ssh://git@0.0.0.0:2222/vole.git
A free and open-source forge that lives in your Git repo.
Merge change request 01M70WES5ZZDJ6RP226WB11BBZ: Let people rename and remove their own passkeys
keenan · 2h ago
4b8c9e1d
| 1 | package agent |
| 2 | |
| 3 | import ( |
| 4 | "bufio" |
| 5 | "encoding/json" |
| 6 | "io" |
| 7 | "testing" |
| 8 | ) |
| 9 | |
| 10 | // captureSession is a session with no ssh under it that records the requests it is |
| 11 | // given, so a test can say what the runner asked for and not only what it did with |
| 12 | // the answer. One reply is written per request. |
| 13 | func captureSession(t *testing.T, reqs chan<- runnerRequest, replies ...runnerReply) *runnerSession { |
| 14 | t.Helper() |
| 15 | reqR, reqW := io.Pipe() |
| 16 | repR, repW := io.Pipe() |
| 17 | go func() { |
| 18 | defer repW.Close() |
| 19 | dec := json.NewDecoder(reqR) |
| 20 | enc := json.NewEncoder(repW) |
| 21 | for _, reply := range replies { |
| 22 | var req runnerRequest |
| 23 | if err := dec.Decode(&req); err != nil { |
| 24 | return |
| 25 | } |
| 26 | select { |
| 27 | case reqs <- req: |
| 28 | default: |
| 29 | } |
| 30 | if err := enc.Encode(reply); err != nil { |
| 31 | return |
| 32 | } |
| 33 | } |
| 34 | }() |
| 35 | t.Cleanup(func() { _ = reqW.Close() }) |
| 36 | return &runnerSession{ |
| 37 | in: bufio.NewWriter(reqW), |
| 38 | out: bufio.NewReader(repR), |
| 39 | enc: json.NewEncoder(reqW), |
| 40 | dec: json.NewDecoder(repR), |
| 41 | } |
| 42 | } |
| 43 | |
| 44 | // The renewal between steps has to be the request the server expects, carrying the |
| 45 | // run it belongs to: the whole point of the step boundary is that no command is |
| 46 | // running to send it. |
| 47 | func TestHeartbeatBetweenStepsNamesTheRun(t *testing.T) { |
| 48 | t.Parallel() |
| 49 | for _, held := range []bool{true, false} { |
| 50 | reqs := make(chan runnerRequest, 1) |
| 51 | s := captureSession(t, reqs, runnerReply{Op: "heartbeat", OK: held}) |
| 52 | if got := s.heartbeat("run-1"); got != held { |
| 53 | t.Fatalf("heartbeat = %v, want %v", got, held) |
| 54 | } |
| 55 | select { |
| 56 | case req := <-reqs: |
| 57 | if req.Op != "heartbeat" || req.Run != "run-1" { |
| 58 | t.Fatalf("heartbeat sent %+v; want op=heartbeat run=run-1", req) |
| 59 | } |
| 60 | default: |
| 61 | t.Fatal("heartbeat sent no request") |
| 62 | } |
| 63 | } |
| 64 | } |
| 65 | |
| 66 | // A heartbeat that cannot be sent is not a lease: the attempt must stop rather than |
| 67 | // run a step the queue may already have given to another runner. |
| 68 | func TestHeartbeatWithoutAConnectionIsNotHeld(t *testing.T) { |
| 69 | t.Parallel() |
| 70 | reqR, reqW := io.Pipe() |
| 71 | repR, repW := io.Pipe() |
| 72 | _ = reqR.Close() |
| 73 | _ = repW.Close() |
| 74 | t.Cleanup(func() { _ = reqW.Close(); _ = repR.Close() }) |
| 75 | s := &runnerSession{ |
| 76 | in: bufio.NewWriter(reqW), |
| 77 | out: bufio.NewReader(repR), |
| 78 | enc: json.NewEncoder(reqW), |
| 79 | dec: json.NewDecoder(repR), |
| 80 | } |
| 81 | if s.heartbeat("run-1") { |
| 82 | t.Fatal("a heartbeat on a dead connection reported the lease as held") |
| 83 | } |
| 84 | } |
| 85 |