Skip to content

Commit 42c2ca2

Browse files
committed
add TestAbandonConcurrentRun
1 parent 6a9eb9e commit 42c2ca2

File tree

2 files changed

+101
-1
lines changed

2 files changed

+101
-1
lines changed

services/actions/clear_tasks.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ func stopTasks(ctx context.Context, opts actions_model.FindTaskOptions) error {
120120
func CancelAbandonedJobs(ctx context.Context) error {
121121
jobs, err := db.Find[actions_model.ActionRunJob](ctx, actions_model.FindRunJobOptions{
122122
Statuses: []actions_model.Status{actions_model.StatusWaiting, actions_model.StatusBlocked},
123-
UpdatedBefore: timeutil.TimeStamp(time.Now().Add(-setting.Actions.AbandonedJobTimeout).Unix()),
123+
UpdatedBefore: timeutil.TimeStampNow().AddDuration(-setting.Actions.AbandonedJobTimeout),
124124
})
125125
if err != nil {
126126
log.Warn("find abandoned tasks: %v", err)

tests/integration/actions_concurrency_test.go

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
repo_model "code.gitea.io/gitea/models/repo"
1717
"code.gitea.io/gitea/models/unittest"
1818
user_model "code.gitea.io/gitea/models/user"
19+
"code.gitea.io/gitea/modules/setting"
1920
api "code.gitea.io/gitea/modules/structs"
2021
"code.gitea.io/gitea/modules/timeutil"
2122
"code.gitea.io/gitea/modules/util"
@@ -1247,6 +1248,105 @@ jobs:
12471248
})
12481249
}
12491250

1251+
func TestAbandonConcurrentRun(t *testing.T) {
1252+
onGiteaRun(t, func(t *testing.T, u *url.URL) {
1253+
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
1254+
user2Session := loginUser(t, user2.Name)
1255+
user2Token := getTokenForLoggedInUser(t, user2Session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
1256+
1257+
apiRepo := createActionsTestRepo(t, user2Token, "actions-concurrency", false)
1258+
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: apiRepo.ID})
1259+
user2APICtx := NewAPITestContext(t, repo.OwnerName, repo.Name, auth_model.AccessTokenScopeWriteRepository)
1260+
defer doAPIDeleteRepository(user2APICtx)(t)
1261+
1262+
runner := newMockRunner()
1263+
runner.registerAsRepoRunner(t, repo.OwnerName, repo.Name, "mock-runner", []string{"ubuntu-latest"}, false)
1264+
1265+
wf1TreePath := ".gitea/workflows/workflow-1.yml"
1266+
wf1FileContent := `name: Workflow-1
1267+
on:
1268+
push:
1269+
paths:
1270+
- '.gitea/workflows/workflow-1.yml'
1271+
concurrency:
1272+
group: test-group
1273+
jobs:
1274+
wf1-job1:
1275+
runs-on: ubuntu-latest
1276+
steps:
1277+
- run: echo 'wf1-job1'
1278+
wf1-job2:
1279+
runs-on: customized-runner
1280+
steps:
1281+
- run: echo 'wf1-job1'
1282+
`
1283+
1284+
wf2TreePath := ".gitea/workflows/workflow-2.yml"
1285+
wf2FileContent := `name: Workflow-2
1286+
on:
1287+
push:
1288+
paths:
1289+
- '.gitea/workflows/workflow-2.yml'
1290+
concurrency:
1291+
group: test-group
1292+
jobs:
1293+
wf2-job1:
1294+
runs-on: ubuntu-latest
1295+
steps:
1296+
- run: echo 'wf2-job1'
1297+
`
1298+
// push workflow1
1299+
opts1 := getWorkflowCreateFileOptions(user2, repo.DefaultBranch, "create %s"+wf1TreePath, wf1FileContent)
1300+
createWorkflowFile(t, user2Token, repo.OwnerName, repo.Name, wf1TreePath, opts1)
1301+
1302+
// fetch wf1-job1
1303+
w1j1Task := runner.fetchTask(t)
1304+
_, _, run1 := getTaskAndJobAndRunByTaskID(t, w1j1Task.Id)
1305+
assert.Equal(t, "test-group", run1.ConcurrencyGroup)
1306+
assert.Equal(t, actions_model.StatusRunning, run1.Status)
1307+
// query wf1-job2 from db and check its status
1308+
w1j2Job := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRunJob{RunID: run1.ID, JobID: "wf1-job2"})
1309+
// wf1-job2 is waiting but no runner will run it
1310+
assert.Equal(t, actions_model.StatusWaiting, w1j2Job.Status)
1311+
1312+
time.Sleep(time.Second)
1313+
now := time.Now()
1314+
time.Sleep(time.Second)
1315+
1316+
// push workflow2
1317+
opts2 := getWorkflowCreateFileOptions(user2, repo.DefaultBranch, "create %s"+wf2TreePath, wf2FileContent)
1318+
createWorkflowFile(t, user2Token, repo.OwnerName, repo.Name, wf2TreePath, opts2)
1319+
1320+
// query run2 from db and check its status
1321+
run2 := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{RepoID: repo.ID, WorkflowID: "workflow-2.yml"})
1322+
// run2 is blocked because it is blocked by workflow1's concurrency group "test-group"
1323+
assert.Equal(t, actions_model.StatusBlocked, run2.Status)
1324+
1325+
// mock time
1326+
fakeNow := now.Add(setting.Actions.AbandonedJobTimeout)
1327+
timeutil.MockSet(fakeNow)
1328+
defer timeutil.MockUnset()
1329+
1330+
// call CancelAbandonedJobs manually
1331+
assert.NoError(t, actions_service.CancelAbandonedJobs(t.Context()))
1332+
1333+
// check the status of wf1-job2
1334+
w1j2Job = unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRunJob{ID: w1j2Job.ID})
1335+
assert.Equal(t, actions_model.StatusCancelled, w1j2Job.Status)
1336+
// check the status of run1
1337+
run1 = unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{ID: run1.ID})
1338+
assert.Equal(t, actions_model.StatusCancelled, run1.Status)
1339+
1340+
// fetch wf2-job1 and check
1341+
w2j1Task := runner.fetchTask(t)
1342+
_, w2j1Job, run2 := getTaskAndJobAndRunByTaskID(t, w2j1Task.Id)
1343+
assert.Equal(t, "test-group", run2.ConcurrencyGroup)
1344+
assert.Equal(t, "wf2-job1", w2j1Job.JobID)
1345+
assert.Equal(t, actions_model.StatusRunning, run2.Status)
1346+
assert.Equal(t, actions_model.StatusRunning, w2j1Job.Status)
1347+
})
1348+
}
1349+
12501350
func TestRunAndJobWithSameConcurrencyGroup(t *testing.T) {
12511351
onGiteaRun(t, func(t *testing.T, u *url.URL) {
12521352
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})

0 commit comments

Comments
 (0)