Skip to content

Commit 62a85f3

Browse files
rremerRoyce Remer
authored and
Royce Remer
committed
Fail mirroring more gracefully:
* reuse recoverable error checks across mirror_pull * add new cases for 'cannot lock ref/not our ref' (race condition in fetch) and 'Unable to create/lock" * move lfs sync right after commit graph write, and before other maintenance which may fail * try a prune for 'broken reference' as well as 'not our ref' * always sync LFS right after commit graph write, and before other maintenance which may fail
1 parent d6e94fa commit 62a85f3

File tree

2 files changed

+74
-11
lines changed

2 files changed

+74
-11
lines changed

services/mirror/mirror_pull.go

+29-11
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,24 @@ func pruneBrokenReferences(ctx context.Context,
235235
return pruneErr
236236
}
237237

238+
// checkRecoverableSyncError takes an error message from a git fetch command and returns false if it should be a fatal/blocking error
239+
func checkRecoverableSyncError(stderrMessage string) bool {
240+
switch {
241+
case strings.Contains(stderrMessage, "unable to resolve reference") && strings.Contains(stderrMessage, "reference broken"):
242+
return true
243+
case strings.Contains(stderrMessage, "remote error") && strings.Contains(stderrMessage, "not our ref"):
244+
return true
245+
case strings.Contains(stderrMessage, "cannot lock ref") && strings.Contains(stderrMessage, "but expected"):
246+
return true
247+
case strings.Contains(stderrMessage, "cannot lock ref") && strings.Contains(stderrMessage, "unable to resolve reference"):
248+
return true
249+
case strings.Contains(stderrMessage, "Unable to create") && strings.Contains(stderrMessage, ".lock"):
250+
return true
251+
default:
252+
return false
253+
}
254+
}
255+
238256
// runSync returns true if sync finished without error.
239257
func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bool) {
240258
repoPath := m.Repo.RepoPath()
@@ -275,7 +293,7 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo
275293
stdoutMessage := util.SanitizeCredentialURLs(stdout)
276294

277295
// Now check if the error is a resolve reference due to broken reference
278-
if strings.Contains(stderr, "unable to resolve reference") && strings.Contains(stderr, "reference broken") {
296+
if checkRecoverableSyncError(stderr) {
279297
log.Warn("SyncMirrors [repo: %-v]: failed to update mirror repository due to broken references:\nStdout: %s\nStderr: %s\nErr: %v\nAttempting Prune", m.Repo, stdoutMessage, stderrMessage, err)
280298
err = nil
281299

@@ -324,6 +342,15 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo
324342
return nil, false
325343
}
326344

345+
if m.LFS && setting.LFS.StartServer {
346+
log.Trace("SyncMirrors [repo: %-v]: syncing LFS objects...", m.Repo)
347+
endpoint := lfs.DetermineEndpoint(remoteURL.String(), m.LFSEndpoint)
348+
lfsClient := lfs.NewClient(endpoint, nil)
349+
if err = repo_module.StoreMissingLfsObjectsInRepository(ctx, m.Repo, gitRepo, lfsClient); err != nil {
350+
log.Error("SyncMirrors [repo: %-v]: failed to synchronize LFS objects for repository: %v", m.Repo, err)
351+
}
352+
}
353+
327354
log.Trace("SyncMirrors [repo: %-v]: syncing branches...", m.Repo)
328355
if _, err = repo_module.SyncRepoBranchesWithRepo(ctx, m.Repo, gitRepo, 0); err != nil {
329356
log.Error("SyncMirrors [repo: %-v]: failed to synchronize branches: %v", m.Repo, err)
@@ -333,15 +360,6 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo
333360
if err = repo_module.SyncReleasesWithTags(ctx, m.Repo, gitRepo); err != nil {
334361
log.Error("SyncMirrors [repo: %-v]: failed to synchronize tags to releases: %v", m.Repo, err)
335362
}
336-
337-
if m.LFS && setting.LFS.StartServer {
338-
log.Trace("SyncMirrors [repo: %-v]: syncing LFS objects...", m.Repo)
339-
endpoint := lfs.DetermineEndpoint(remoteURL.String(), m.LFSEndpoint)
340-
lfsClient := lfs.NewClient(endpoint, nil)
341-
if err = repo_module.StoreMissingLfsObjectsInRepository(ctx, m.Repo, gitRepo, lfsClient); err != nil {
342-
log.Error("SyncMirrors [repo: %-v]: failed to synchronize LFS objects for repository: %v", m.Repo, err)
343-
}
344-
}
345363
gitRepo.Close()
346364

347365
log.Trace("SyncMirrors [repo: %-v]: updating size of repository", m.Repo)
@@ -368,7 +386,7 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo
368386
stdoutMessage := util.SanitizeCredentialURLs(stdout)
369387

370388
// Now check if the error is a resolve reference due to broken reference
371-
if strings.Contains(stderrMessage, "unable to resolve reference") && strings.Contains(stderrMessage, "reference broken") {
389+
if checkRecoverableSyncError(stderrMessage) {
372390
log.Warn("SyncMirrors [repo: %-v Wiki]: failed to update mirror wiki repository due to broken references:\nStdout: %s\nStderr: %s\nErr: %v\nAttempting Prune", m.Repo, stdoutMessage, stderrMessage, err)
373391
err = nil
374392

services/mirror/mirror_test.go renamed to services/mirror/mirror_pull_test.go

+45
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,48 @@ func Test_parseRemoteUpdateOutput(t *testing.T) {
6464
assert.EqualValues(t, "1c97ebc746", results[9].oldCommitID)
6565
assert.EqualValues(t, "976d27d52f", results[9].newCommitID)
6666
}
67+
68+
func Test_checkRecoverableSyncError(t *testing.T) {
69+
70+
recoverable_description := " should be marked as recoverable."
71+
fatal_description := " should be considered fatal and not recoverable."
72+
73+
// would exit status 128
74+
description := "A race condition in http git-fetch where certain refs were listed on the remote and are no longer there"
75+
stderr := "fatal: remote error: upload-pack: not our ref 988881adc9fc3655077dc2d4d757d480b5ea0e11"
76+
assert.True(t, checkRecoverableSyncError(stderr), description+recoverable_description)
77+
78+
// would exit status 1
79+
description = "A race condition where a local gc/prune removes a named ref during a git-fetch"
80+
stderr = "cannot lock ref 'refs/pull/123456/merge': unable to resolve reference 'refs/pull/134153/merge'"
81+
assert.True(t, checkRecoverableSyncError(stderr), description+recoverable_description)
82+
83+
description = "A race condition in http git-fetch where named refs were listed on the remote and are no longer there"
84+
stderr = "error: cannot lock ref 'refs/remotes/origin/foo': unable to resolve reference 'refs/remotes/origin/foo': reference broken"
85+
assert.True(t, checkRecoverableSyncError(stderr), description+recoverable_description)
86+
87+
// would exit status 128
88+
description = "A race condition in http git-fetch where named refs were force-pushed during the update"
89+
stderr = "error: cannot lock ref 'refs/pull/123456/merge': is at 988881adc9fc3655077dc2d4d757d480b5ea0e11 but expected 7f894307ffc9553edbd0b671cab829786866f7b2"
90+
assert.True(t, checkRecoverableSyncError(stderr), description+recoverable_description)
91+
92+
// would exit status 128
93+
description = "A race condition with other local git operations, such as git-maintenance,"
94+
stderr = "fatal: Unable to create '/data/gitea-repositories/foo-org/bar-repo.git/./objects/info/commit-graphs/commit-graph-chain.lock': File exists."
95+
assert.True(t, checkRecoverableSyncError(stderr), description+recoverable_description)
96+
97+
// would exit status 128
98+
description = "Missing or unauthorized credentials"
99+
stderr = "fatal: Authentication failed for 'https://example.com/foo-does-not-exist/bar.git/'"
100+
assert.False(t, checkRecoverableSyncError(stderr), description+fatal_description)
101+
102+
// would exit status 128
103+
description = "A non-existent remote repository"
104+
stderr = "fatal: Could not read from remote repository."
105+
assert.False(t, checkRecoverableSyncError(stderr), description+fatal_description)
106+
107+
// would exit status 128
108+
description = "A non-functioning proxy"
109+
stderr = "fatal: unable to access 'https://example.com/foo-does-not-exist/bar.git/': Failed to connect to configured-https-proxy port 1080 after 0 ms: Couldn't connect to server"
110+
assert.False(t, checkRecoverableSyncError(stderr), description+fatal_description)
111+
}

0 commit comments

Comments
 (0)