Retry transient docker image pull failures
Problem
A job can fail with runner_system_failure before its script runs when the Docker daemon times out fetching a registry auth token. The trace shows the pull dying with context deadline exceeded (Client.Timeout exceeded while awaiting headers). That is a momentary registry or network blip, but the runner gives up on the first attempt. A real example is https://gitlab.com/gitlab-com/runbooks/-/jobs/15015208349.
The pull manager loops over the configured pull policies, but it tries each policy only once. The only retry available today is operator side: repeating a policy in config.toml (pull_policy = ["always", "always"]), which a job author on a managed runner cannot set.
On top of that, ClassifyImagePullFailure did not recognise HTTP client timeouts as transient. It matched dial tcp, i/o timeout, tls handshake and friends, but the auth-endpoint timeout fell through to the generic ImagePullFailure.
Fix
ClassifyImagePullFailure now classifies HTTP client timeouts (context deadline exceeded, Client.Timeout exceeded, request canceled) as RunnerExternalDependencyFailure. Both reasons still map to runner_system_failure for GitLab, so what the API reports does not change. The classification only feeds the retry decision below.
pullDockerImage retries the pull on transient failures using the existing helpers/retry package, bounded to a few attempts with backoff. Auth-denied and missing-image failures are not retried, so they keep failing fast and fall through to the next pull policy. The retry loop watches the executor context, so a cancelled or timed-out build stops immediately.
Tuning
The attempt count and backoff are hardcoded (3 attempts, 2 to 10 seconds). They could move to config if you would rather have operator control, and the pull-policy duplication above still works for that.