Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/vole.git

Write over SSH with a registered key:

git clone ssh://git@rodent.software:2222/vole.git

A free and open-source forge that lives in your Git repo.

Add one Admin page with status, accounts, and the audit log

43e681d8a5d94567aa9530d32b2c89223c1a17f9 · Keenan Nemetz · 5d ago · parent af018ce8

Add one Admin page with status, accounts, and the audit log

Instance administration was split between the Status and Accounts pages,
and the audit log had no browser view at all. The new /admin page gathers
the status tabs, the account roster, and a read-only audit viewer into one
place with a tab for each, and /status, /runners, and /users redirect to
the tab they named so old links keep working.

The audit reader walks the file backwards a chunk at a time, returns the
newest records first, and shows one extra record to know whether an older
page exists. It skips a line it cannot parse rather than failing the page
and refuses a symlink or a non-regular file, as startup already does.

Refs #198 (01M6MK0NNZ2XR1D5TQKNWYT4HN).

server/runners_page_test.go +11−11

@@ -24,7 +24,7 @@ func queuedRun(id, repo, job string, created time.Time) runs.Run {
24 24 }
25 25 }
26 26
27 func TestStatusPageShowsTheRunnerHoldingAJobAndTheQueueDepth(t *testing.T) {
27 func TestAdminRunnersTabShowsAHeldJobAndTheQueueDepth(t *testing.T) {
28 28 t.Parallel()
29 29 f := newFixture(t)
30 30 f.publish(f.repo)
@@ -47,9 +47,9 @@ func TestStatusPageShowsTheRunnerHoldingAJobAndTheQueueDepth(t *testing.T) {
47 47 t.Fatal(err)
48 48 }
49 49
50 code, body := f.adminGet(t, "/status?tab=runners")
50 code, body := f.adminGet(t, "/admin?tab=runners")
51 51 if code != 200 {
52 t.Fatalf("GET /status?tab=runners = %d\n%s", code, body)
52 t.Fatalf("GET /admin?tab=runners = %d\n%s", code, body)
53 53 }
54 54 for _, want := range []string{
55 55 "keenan", // the account the key resolved to
@@ -73,7 +73,7 @@ func TestStatusPageShowsTheRunnerHoldingAJobAndTheQueueDepth(t *testing.T) {
73 73 // A repository at its per-repository cap is the case where a waiting job has a
74 74 // reason, and the page is where that reason has to be readable: "7 waiting, 4 running"
75 75 // is a mystery, "4 of 4 (at the cap)" is not.
76 func TestStatusPageSaysWhenARepositoryIsAtItsCap(t *testing.T) {
76 func TestAdminRunnersTabSaysWhenARepositoryIsAtItsCap(t *testing.T) {
77 77 t.Parallel()
78 78 f := newFixtureWith(t, func(c *config.Config) { c.MaxJobsPerRepo = 1 })
79 79 f.publish(f.repo)
@@ -93,23 +93,23 @@ func TestStatusPageSaysWhenARepositoryIsAtItsCap(t *testing.T) {
93 93 t.Fatal(err)
94 94 }
95 95
96 code, body := f.adminGet(t, "/status?tab=runners")
96 code, body := f.adminGet(t, "/admin?tab=runners")
97 97 if code != 200 {
98 t.Fatalf("GET /status?tab=runners = %d\n%s", code, body)
98 t.Fatalf("GET /admin?tab=runners = %d\n%s", code, body)
99 99 }
100 100 if !strings.Contains(body, "1 of 1") || !strings.Contains(body, "at the cap") {
101 101 t.Fatalf("the page does not say the repository is at its cap of 1:\n%s", body)
102 102 }
103 103 }
104 104
105 func TestRunnersRedirectsToStatus(t *testing.T) {
105 func TestRunnersRedirectsToAdmin(t *testing.T) {
106 106 t.Parallel()
107 107 f := newFixture(t)
108 108 resp, _ := f.browserGet(t, "/runners", "")
109 109 if resp.StatusCode != http.StatusMovedPermanently {
110 110 t.Fatalf("GET /runners = %d, want a redirect", resp.StatusCode)
111 111 }
112 if got := resp.Header.Get("Location"); got != "/status?tab=runners" {
112 if got := resp.Header.Get("Location"); got != "/admin?tab=runners" {
113 113 t.Fatalf("GET /runners redirected to %q", got)
114 114 }
115 115 }
@@ -117,7 +117,7 @@ func TestRunnersRedirectsToStatus(t *testing.T) {
117 117 // A job waiting for a machine that is not connected is invisible from the queue side:
118 118 // the depth says one is waiting, and nothing says why nobody took it. The runner's own
119 119 // labels are the missing half, so the page shows them next to the agent.
120 func TestStatusPageShowsWhatEachRunnerTakes(t *testing.T) {
120 func TestAdminRunnersTabShowsWhatEachRunnerTakes(t *testing.T) {
121 121 t.Parallel()
122 122 f := newFixture(t)
123 123 f.publish(f.repo)
@@ -130,9 +130,9 @@ func TestStatusPageShowsWhatEachRunnerTakes(t *testing.T) {
130 130 t.Fatal(err)
131 131 }
132 132
133 code, body := f.adminGet(t, "/status?tab=runners")
133 code, body := f.adminGet(t, "/admin?tab=runners")
134 134 if code != 200 {
135 t.Fatalf("GET /status?tab=runners = %d\n%s", code, body)
135 t.Fatalf("GET /admin?tab=runners = %d\n%s", code, body)
136 136 }
137 137 if !strings.Contains(body, "linux, arm64") {
138 138 t.Fatalf("the page does not show the labels a runner carries:\n%s", body)