1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
|
package backup
import (
"context"
"errors"
"fmt"
"io/ioutil"
"path/filepath"
"strings"
"gitlab.com/gitlab-org/gitaly/v14/internal/helper/text"
"gitlab.com/gitlab-org/gitaly/v14/proto/go/gitalypb"
)
// LegacyLocator locates backup paths for historic backups. This is the
// structure that gitlab used before incremental backups were introduced.
//
// Existing backup files are expected to be overwritten by the latest backup
// files.
//
// Structure:
// <repo relative path>.bundle
// <repo relative path>.refs
// <repo relative path>/custom_hooks.tar
type LegacyLocator struct{}
// BeginFull returns the static paths for a legacy repository backup
func (l LegacyLocator) BeginFull(ctx context.Context, repo *gitalypb.Repository, backupID string) *Full {
return l.newFull(repo)
}
// CommitFull is unused as the locations are static
func (l LegacyLocator) CommitFull(ctx context.Context, full *Full) error {
return nil
}
// FindLatestFull returns the static paths for a legacy repository backup
func (l LegacyLocator) FindLatestFull(ctx context.Context, repo *gitalypb.Repository) (*Full, error) {
return l.newFull(repo), nil
}
func (l LegacyLocator) newFull(repo *gitalypb.Repository) *Full {
backupPath := strings.TrimSuffix(repo.RelativePath, ".git")
return &Full{
BundlePath: backupPath + ".bundle",
RefPath: backupPath + ".refs",
CustomHooksPath: filepath.Join(backupPath, "custom_hooks.tar"),
}
}
// PointerLocator locates backup paths where each full backup is put into a
// unique timestamp directory and the latest backup taken is pointed to by a
// file named LATEST.
//
// Structure:
// <repo relative path>/<backup id>/full.bundle
// <repo relative path>/<backup id>/full.refs
// <repo relative path>/<backup id>/custom_hooks.tar
// <repo relative path>/LATEST
type PointerLocator struct {
Sink Sink
Fallback Locator
}
// BeginFull returns paths for a new full backup
func (l PointerLocator) BeginFull(ctx context.Context, repo *gitalypb.Repository, backupID string) *Full {
backupPath := strings.TrimSuffix(repo.RelativePath, ".git")
return &Full{
BundlePath: filepath.Join(backupPath, backupID, "full.bundle"),
RefPath: filepath.Join(backupPath, backupID, "full.refs"),
CustomHooksPath: filepath.Join(backupPath, backupID, "custom_hooks.tar"),
}
}
// CommitFull persists the paths for a new backup so that it can be looked up by FindLatestFull
func (l PointerLocator) CommitFull(ctx context.Context, full *Full) error {
bundleDir := filepath.Dir(full.BundlePath)
backupID := filepath.Base(bundleDir)
backupPath := filepath.Dir(bundleDir)
return l.commitLatestID(ctx, backupPath, backupID)
}
// FindLatestFull returns the paths committed by the latest call to CommitFull.
//
// If there is no `LATEST` file, the result of the `Fallback` is used.
func (l PointerLocator) FindLatestFull(ctx context.Context, repo *gitalypb.Repository) (*Full, error) {
backupPath := strings.TrimSuffix(repo.RelativePath, ".git")
latest, err := l.findLatestID(ctx, backupPath)
if err != nil {
if l.Fallback != nil && errors.Is(err, ErrDoesntExist) {
return l.Fallback.FindLatestFull(ctx, repo)
}
return nil, fmt.Errorf("pointer locator: %w", err)
}
return &Full{
BundlePath: filepath.Join(backupPath, latest, "full.bundle"),
RefPath: filepath.Join(backupPath, latest, "full.refs"),
CustomHooksPath: filepath.Join(backupPath, latest, "custom_hooks.tar"),
}, nil
}
func (l PointerLocator) findLatestID(ctx context.Context, backupPath string) (string, error) {
r, err := l.Sink.GetReader(ctx, filepath.Join(backupPath, "LATEST"))
if err != nil {
return "", fmt.Errorf("find latest ID: %w", err)
}
defer r.Close()
latest, err := ioutil.ReadAll(r)
if err != nil {
return "", fmt.Errorf("find latest ID: %w", err)
}
return text.ChompBytes(latest), nil
}
func (l PointerLocator) commitLatestID(ctx context.Context, backupPath, backupID string) error {
latest := strings.NewReader(backupID)
if err := l.Sink.Write(ctx, filepath.Join(backupPath, "LATEST"), latest); err != nil {
return fmt.Errorf("commit latest ID: %w", err)
}
return nil
}
|