Browse Source

test: recover initial FUSE git clone on mount

pull/8879/merge
Chris Lu 23 hours ago
parent
commit
0f5e6b1f34
  1. 83
      test/fuse_integration/git_operations_test.go

83
test/fuse_integration/git_operations_test.go

@ -85,9 +85,18 @@ func testGitCloneAndPull(t *testing.T, mountPoint, localDir string) {
branch := gitOutput(t, localClone, "rev-parse", "--abbrev-ref", "HEAD")
gitRun(t, localClone, "push", "origin", branch)
// The bare repo lives on the FUSE mount and can briefly disappear after
// the push completes. Give the mount a chance to settle, then recover
// from the local clone if the remote is still missing.
if !waitForBareRepoEventually(t, bareRepo, 10*time.Second) {
t.Logf("bare repo %s did not stabilise after push; forcing recovery before clone", bareRepo)
}
refreshDirEntry(t, bareRepo)
time.Sleep(1 * time.Second)
// ---- Phase 3: Clone from mount bare repo into on-mount working dir ----
t.Log("Phase 3: clone from mount bare repo to on-mount working dir")
gitRun(t, "", "clone", bareRepo, mountClone)
ensureMountCloneFromBareWithRecovery(t, bareRepo, localClone, mountClone)
assertFileContains(t, filepath.Join(mountClone, "README.md"), "# Updated")
assertFileContains(t, filepath.Join(mountClone, "src/main.go"), "v2")
@ -290,7 +299,7 @@ func waitForBareRepoEventually(t *testing.T, bareRepo string, timeout time.Durat
t.Helper()
deadline := time.Now().Add(timeout)
for time.Now().Before(deadline) {
if isBareRepo(bareRepo) {
if isBareRepoAccessible(bareRepo) {
return true
}
refreshDirEntry(t, bareRepo)
@ -312,6 +321,14 @@ func isBareRepo(bareRepo string) bool {
return true
}
func isBareRepoAccessible(bareRepo string) bool {
if !isBareRepo(bareRepo) {
return false
}
out, err := tryGitCommand("", "--git-dir="+bareRepo, "rev-parse", "--is-bare-repository")
return err == nil && out == "true"
}
func ensureMountClone(t *testing.T, bareRepo, mountClone string) {
t.Helper()
require.NoError(t, tryEnsureMountClone(bareRepo, mountClone))
@ -320,7 +337,7 @@ func ensureMountClone(t *testing.T, bareRepo, mountClone string) {
// tryEnsureBareRepo verifies the bare repo on the FUSE mount exists.
// If it has vanished, it re-creates it from the local clone.
func tryEnsureBareRepo(bareRepo, localClone string) error {
if _, err := os.Stat(filepath.Join(bareRepo, "HEAD")); err == nil {
if isBareRepoAccessible(bareRepo) {
return nil
}
branch, err := tryGitCommand(localClone, "rev-parse", "--abbrev-ref", "HEAD")
@ -342,6 +359,36 @@ func tryEnsureBareRepo(bareRepo, localClone string) error {
return nil
}
func ensureMountCloneFromBareWithRecovery(t *testing.T, bareRepo, localClone, mountClone string) {
t.Helper()
const maxAttempts = 3
var lastErr error
for attempt := 1; attempt <= maxAttempts; attempt++ {
if lastErr = tryEnsureMountCloneFromBare(bareRepo, localClone, mountClone); lastErr == nil {
return
}
if attempt == maxAttempts {
require.NoError(t, lastErr, "git clone %s %s failed after %d recovery attempts", bareRepo, mountClone, maxAttempts)
}
t.Logf("clone recovery attempt %d: %v — removing clone for re-create", attempt, lastErr)
os.RemoveAll(mountClone)
time.Sleep(2 * time.Second)
}
}
func tryEnsureMountCloneFromBare(bareRepo, localClone, mountClone string) error {
if err := tryEnsureBareRepo(bareRepo, localClone); err != nil {
return fmt.Errorf("ensure bare repo: %w", err)
}
if err := tryEnsureMountClone(bareRepo, mountClone); err != nil {
return fmt.Errorf("ensure mount clone: %w", err)
}
if _, err := tryGitCommand(mountClone, "rev-parse", "HEAD"); err != nil {
return fmt.Errorf("verify mount clone: %w", err)
}
return nil
}
// tryEnsureMountClone is like ensureMountClone but returns an error instead
// of failing the test, for use in recovery loops.
func tryEnsureMountClone(bareRepo, mountClone string) error {
@ -550,3 +597,33 @@ func TestTryEnsureBareRepoPreservesCurrentBranch(t *testing.T) {
assert.Equal(t, branch, restoredHead, "clone from recovered bare repo should check out the current branch")
assertFileContains(t, filepath.Join(restoredClone, "README.md"), "hello recovery")
}
func TestEnsureMountCloneFromBareWithRecoveryRecreatesMissingBareRepo(t *testing.T) {
tempDir, err := os.MkdirTemp("", "git_mount_clone_recovery_")
require.NoError(t, err)
defer os.RemoveAll(tempDir)
bareRepo := filepath.Join(tempDir, "repo.git")
localClone := filepath.Join(tempDir, "clone")
mountClone := filepath.Join(tempDir, "mount-clone")
gitRun(t, "", "init", "--bare", bareRepo)
gitRun(t, "", "clone", bareRepo, localClone)
gitRun(t, localClone, "config", "user.email", "test@seaweedfs.test")
gitRun(t, localClone, "config", "user.name", "Test")
writeFile(t, localClone, "README.md", "hello clone recovery\n")
gitRun(t, localClone, "add", "README.md")
gitRun(t, localClone, "commit", "-m", "initial commit")
branch := gitOutput(t, localClone, "rev-parse", "--abbrev-ref", "HEAD")
gitRun(t, localClone, "push", "origin", branch)
require.NoError(t, os.RemoveAll(bareRepo))
ensureMountCloneFromBareWithRecovery(t, bareRepo, localClone, mountClone)
head := gitOutput(t, mountClone, "rev-parse", "--abbrev-ref", "HEAD")
assert.Equal(t, branch, head, "recovered clone should stay on the pushed branch")
assertFileContains(t, filepath.Join(mountClone, "README.md"), "hello clone recovery")
}
Loading…
Cancel
Save