fix: stop racing the daemon when removing containers (#1093)

Containers set `HostConfig.AutoRemove` but act also removes them explicitly, so the two removers race and the loser logs a 409 `removal of container X is already in progress` — seen at the end of nearly every `uses: docker://` step.

The explicit remove is redundant for `docker://` steps and docker actions (`Start(true)` already awaited exit), so it's skipped. Job and service containers keep both removers — their `sleep` entrypoint needs `AutoRemove` as a fallback reaper — so there the race is inherent and `remove()` now treats `NotFound` and `Conflict` as success.

---------

Co-authored-by: bircni <bircni@icloud.com>
Reviewed-on: https://gitea.com/gitea/runner/pulls/1093
Reviewed-by: bircni <bircni@icloud.com>
Co-authored-by: silverwind <me@silverwind.io>
This commit is contained in:
silverwind
2026-07-21 11:03:15 +00:00
committed by silverwind
parent 0c08b0f2da
commit 89467c9dd0
6 changed files with 124 additions and 3 deletions

View File

@@ -376,6 +376,11 @@ func (cr *containerReference) find() common.Executor {
}
}
// isContainerGone reports whether a failed remove still left the container gone (NotFound or Conflict).
func isContainerGone(err error) bool {
return cerrdefs.IsNotFound(err) || cerrdefs.IsConflict(err)
}
func (cr *containerReference) remove() common.Executor {
return func(ctx context.Context) error {
if cr.id == "" {
@@ -387,7 +392,7 @@ func (cr *containerReference) remove() common.Executor {
RemoveVolumes: true,
Force: true,
})
if err != nil {
if err != nil && !isContainerGone(err) {
logger.Error(fmt.Errorf("failed to remove container: %w", err))
}