tenseleyflow/shithub / be95514

Browse files

actions: harden live runner smoke path

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
be9551470a5b3b0d44d0d02d386e5e31689c77f5
Parents
9bd52be
Tree
e51a7f5

4 changed files

StatusFile+-
M internal/runner/engine/docker.go 1 1
M internal/runner/engine/docker_test.go 1 1
M internal/web/handlers/api/api.go 0 1
M internal/web/handlers/api/runners_test.go 49 0
internal/runner/engine/docker.gomodified
@@ -378,7 +378,7 @@ func (d *Docker) dockerInvocation(job Job, step Step) (dockerInvocation, error)
378378
 		"--ulimit", "nproc=" + defaultNprocLimit,
379379
 		"--user", user,
380380
 		"--workdir=" + workdir,
381
-		"--mount", "type=bind,src=" + job.WorkspaceDir + ",dst=/workspace,rw",
381
+		"--mount", "type=bind,src=" + job.WorkspaceDir + ",dst=/workspace",
382382
 	}
383383
 	for _, dns := range d.cfg.DNSServers {
384384
 		dns = strings.TrimSpace(dns)
internal/runner/engine/docker_test.gomodified
@@ -153,7 +153,7 @@ func TestDockerExecute_BuildsResourceCappedRunCommand(t *testing.T) {
153153
 	if !reflect.DeepEqual(rec.args, want) {
154154
 		t.Fatalf("args:\ngot  %#v\nwant %#v", rec.args, want)
155155
 	}
156
-	if !strings.HasPrefix(rec.args[25], "type=bind,src=") || !strings.HasSuffix(rec.args[25], ",dst=/workspace,rw") {
156
+	if !strings.HasPrefix(rec.args[25], "type=bind,src=") || !strings.HasSuffix(rec.args[25], ",dst=/workspace") || strings.Contains(rec.args[25], ",rw") {
157157
 		t.Fatalf("workspace mount arg: %q", rec.args[25])
158158
 	}
159159
 	if wantEnv := []string{"A=job", "B=step"}; !reflect.DeepEqual(rec.env, wantEnv) {
internal/web/handlers/api/api.gomodified
@@ -113,7 +113,6 @@ func (h *Handlers) Mount(r chi.Router) {
113113
 	})
114114
 	r.Group(func(r chi.Router) {
115115
 		r.Use(middleware.MaxBodySize(runnerAPIMaxBodyBytes))
116
-		r.Use(apiLimitMW)
117116
 		h.mountRunners(r)
118117
 	})
119118
 	r.Group(func(r chi.Router) {
internal/web/handlers/api/runners_test.gomodified
@@ -32,11 +32,13 @@ import (
3232
 	"github.com/tenseleyFlow/shithub/internal/auth/secretbox"
3333
 	"github.com/tenseleyFlow/shithub/internal/infra/metrics"
3434
 	"github.com/tenseleyFlow/shithub/internal/infra/storage"
35
+	"github.com/tenseleyFlow/shithub/internal/ratelimit"
3536
 	repogit "github.com/tenseleyFlow/shithub/internal/repos/git"
3637
 	reposdb "github.com/tenseleyFlow/shithub/internal/repos/sqlc"
3738
 	"github.com/tenseleyFlow/shithub/internal/testing/dbtest"
3839
 	usersdb "github.com/tenseleyFlow/shithub/internal/users/sqlc"
3940
 	apih "github.com/tenseleyFlow/shithub/internal/web/handlers/api"
41
+	"github.com/tenseleyFlow/shithub/internal/web/handlers/api/apilimit"
4042
 	workerdb "github.com/tenseleyFlow/shithub/internal/worker/sqlc"
4143
 )
4244
 
@@ -186,6 +188,53 @@ func TestRunnerHeartbeatClaimsQueuedJob(t *testing.T) {
186188
 	}
187189
 }
188190
 
191
+func TestRunnerHeartbeatBypassesGlobalAnonAPILimit(t *testing.T) {
192
+	pool := dbtest.NewTestDB(t)
193
+	logger := slog.New(slog.NewTextHandler(io.Discard, nil))
194
+	token, _ := registerRunnerForTest(t, pool, []string{"ubuntu-latest", "linux"}, 1)
195
+	signer := runnerAPISigner(t, time.Date(2026, 5, 10, 12, 0, 0, 0, time.UTC))
196
+
197
+	h, err := apih.New(apih.Deps{
198
+		Pool:        pool,
199
+		Logger:      logger,
200
+		BaseURL:     "https://shithub.test",
201
+		RunnerJWT:   signer,
202
+		RateLimiter: ratelimit.New(pool),
203
+		APILimit: apilimit.Config{
204
+			AuthedPerHour: 1,
205
+			AnonPerHour:   1,
206
+			Logger:        logger,
207
+		},
208
+	})
209
+	if err != nil {
210
+		t.Fatalf("api.New: %v", err)
211
+	}
212
+	router := chi.NewRouter()
213
+	h.Mount(router)
214
+
215
+	req := httptest.NewRequest(http.MethodGet, "/api/v1/meta", nil)
216
+	req.RemoteAddr = "10.0.0.77:12345"
217
+	rr := httptest.NewRecorder()
218
+	router.ServeHTTP(rr, req)
219
+	if rr.Code != http.StatusOK {
220
+		t.Fatalf("meta status: got %d, want 200; body=%s", rr.Code, rr.Body.String())
221
+	}
222
+
223
+	req = httptest.NewRequest(http.MethodPost, "/api/v1/runners/heartbeat",
224
+		strings.NewReader(`{"labels":["ubuntu-latest","linux"],"capacity":1}`))
225
+	req.Header.Set("Authorization", "Bearer "+token)
226
+	req.RemoteAddr = "10.0.0.77:12346"
227
+	rr = httptest.NewRecorder()
228
+	router.ServeHTTP(rr, req)
229
+
230
+	if rr.Code != http.StatusNoContent {
231
+		t.Fatalf("heartbeat status: got %d, want 204; body=%s", rr.Code, rr.Body.String())
232
+	}
233
+	if got := rr.Header().Get("X-RateLimit-Limit"); got != "60" {
234
+		t.Errorf("runner heartbeat limit header: got %q, want 60", got)
235
+	}
236
+}
237
+
189238
 func TestRunnerSecretsAreClaimedAndServerScrubsLogs(t *testing.T) {
190239
 	ctx := context.Background()
191240
 	pool := dbtest.NewTestDB(t)