Skip to content
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 101 additions & 0 deletions services/doctor/actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,17 @@ import (
"context"
"fmt"

actions_model "code.gitea.io/gitea/models/actions"
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
unit_model "code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
repo_service "code.gitea.io/gitea/services/repository"

"xorm.io/builder"
)

func disableMirrorActionsUnit(ctx context.Context, logger log.Logger, autofix bool) error {
Expand Down Expand Up @@ -59,6 +64,95 @@ func disableMirrorActionsUnit(ctx context.Context, logger log.Logger, autofix bo
return nil
}

func fixUnfinishedRunStatus(ctx context.Context, logger log.Logger, autofix bool) error {
total := 0
inconsistent := 0
fixed := 0

cond := builder.In("status", []actions_model.Status{
actions_model.StatusWaiting,
actions_model.StatusRunning,
actions_model.StatusBlocked,
}).And(builder.Lt{"updated": timeutil.TimeStampNow().AddDuration(-setting.Actions.ZombieTaskTimeout)})

err := db.Iterate(
ctx,
cond,
func(ctx context.Context, run *actions_model.ActionRun) error {
total++

jobs, err := actions_model.GetRunJobsByRunID(ctx, run.ID)
if err != nil {
return fmt.Errorf("GetRunJobsByRunID: %w", err)
}
expected := actions_model.AggregateJobStatus(jobs)
if expected == run.Status {
return nil
}

inconsistent++
logger.Warn("Run %d (repo_id=%d, index=%d) has status %s, expected %s", run.ID, run.RepoID, run.Index, run.Status, expected)

if !autofix {
return nil
}

run.Started, run.Stopped = getRunTimestampsFromJobs(run, expected, jobs)
run.Status = expected

if err := actions_model.UpdateRun(ctx, run, "status", "started", "stopped"); err != nil {
return fmt.Errorf("UpdateRun: %w", err)
}
fixed++

return nil
},
)
if err != nil {
logger.Critical("Unable to iterate unfinished runs: %v", err)
return err
}

if inconsistent == 0 {
logger.Info("Checked %d unfinished runs; all statuses are consistent.", total)
return nil
}

if autofix {
logger.Info("Checked %d unfinished runs; fixed %d of %d runs.", total, fixed, inconsistent)
} else {
logger.Warn("Checked %d unfinished runs; found %d runs need to be fixed", total, inconsistent)
}

return nil
}

func getRunTimestampsFromJobs(run *actions_model.ActionRun, newStatus actions_model.Status, jobs actions_model.ActionJobList) (started, stopped timeutil.TimeStamp) {
started = run.Started
if (newStatus.IsRunning() || newStatus.IsDone()) && started.IsZero() {
var earliest timeutil.TimeStamp
for _, job := range jobs {
if job.Started > 0 && (earliest.IsZero() || job.Started < earliest) {
earliest = job.Started
}
}
started = earliest
}

stopped = run.Stopped
if newStatus.IsDone() && stopped.IsZero() {
var latest timeutil.TimeStamp
for _, job := range jobs {
if job.Stopped > latest {
latest = job.Stopped
}
}
stopped = latest
}

return started, stopped
}

func init() {
Register(&Check{
Title: "Disable the actions unit for all mirrors",
Expand All @@ -67,4 +161,11 @@ func init() {
Run: disableMirrorActionsUnit,
Priority: 9,
})
Register(&Check{
Title: "Fix inconsistent status for unfinished actions runs",
Name: "fix-actions-unfinished-run-status",
IsDefault: false,
Run: fixUnfinishedRunStatus,
Priority: 9,
})
}
Loading