-
Notifications
You must be signed in to change notification settings - Fork 463
/
Copy pathbackup_selector.go
237 lines (197 loc) · 7.22 KB
/
backup_selector.go
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
package internal
import (
"fmt"
"reflect"
"strings"
"github.com/wal-g/wal-g/internal/multistorage"
"github.com/wal-g/wal-g/internal/multistorage/consts"
"github.com/wal-g/wal-g/utility"
"github.com/pkg/errors"
"github.com/wal-g/tracelog"
"github.com/wal-g/wal-g/pkg/storages/storage"
)
const LatestString = "LATEST"
// BackupSelector returns the backup chosen according to the internal rules.
// Returns NoBackupsFoundError in case there are no backups matching the criteria.
type BackupSelector interface {
Select(folder storage.Folder) (Backup, error)
}
// LatestBackupSelector selects the latest backup from storage
type LatestBackupSelector struct {
}
func NewLatestBackupSelector() LatestBackupSelector {
return LatestBackupSelector{}
}
func (s LatestBackupSelector) Select(folder storage.Folder) (Backup, error) {
return GetLatestBackup(folder.GetSubFolder(utility.BaseBackupPath))
}
// UserDataBackupSelector selects a backup which has the provided user data
type UserDataBackupSelector struct {
userData interface{}
metaFetcher GenericMetaFetcher
}
func NewUserDataBackupSelector(userDataRaw string, metaFetcher GenericMetaFetcher) (UserDataBackupSelector, error) {
userData, err := UnmarshalSentinelUserData(userDataRaw)
if err != nil {
return UserDataBackupSelector{}, err
}
return UserDataBackupSelector{
userData: userData,
metaFetcher: metaFetcher,
}, nil
}
func (s UserDataBackupSelector) Select(folder storage.Folder) (Backup, error) {
return s.findBackupByUserData(s.userData, folder)
}
// Exists backup with UserData exactly matching the provided one
func (s UserDataBackupSelector) findBackupByUserData(userData interface{}, folder storage.Folder) (Backup, error) {
matchUserData := func(d GenericMetadata) bool {
return reflect.DeepEqual(userData, d.UserData)
}
foundMetas, err := searchInMetadata(matchUserData, folder, s.metaFetcher)
if err != nil {
return Backup{}, errors.Wrapf(err, "UserData search failed")
}
if len(foundMetas) == 0 {
return Backup{}, NewNoBackupsFoundError()
}
var foundBackupName string
var foundStorage string
uniqueNames := map[string]bool{}
for i := range foundMetas {
foundBackupName = foundMetas[i].BackupName
foundStorage = foundMetas[i].StorageName
uniqueNames[foundBackupName] = true
}
if len(uniqueNames) > 1 {
var backupNames []string
for st := range foundMetas {
backupNames = append(backupNames, foundMetas[st].BackupName)
}
return Backup{}, fmt.Errorf("too many backups (%d) found with specified user data: %s",
len(uniqueNames), strings.Join(backupNames, " "))
}
baseBackupFolder := folder.GetSubFolder(utility.BaseBackupPath)
return NewBackupInStorage(baseBackupFolder, foundBackupName, foundStorage)
}
type GenericMetadataInStorage struct {
GenericMetadata
StorageName string
}
// Search backups in storage using specified criteria
func searchInMetadata(
criteria func(GenericMetadata) bool,
folder storage.Folder,
metaFetcher GenericMetaFetcher,
) ([]GenericMetadataInStorage, error) {
sentinels, err := GetBackupSentinelObjects(folder)
if err != nil {
return nil, err
}
backupTimes := GetBackupTimeSlices(sentinels)
foundMeta := make([]GenericMetadataInStorage, 0)
for _, backupTime := range backupTimes {
specificFolder, err := multistorage.UseSpecificStorage(backupTime.StorageName, folder)
if err != nil {
tracelog.WarningLogger.Printf("Failed to select source storage for backup %s to fetch its meta: %s\n",
backupTime.BackupName, err.Error())
continue
}
meta, err := metaFetcher.Fetch(backupTime.BackupName, specificFolder.GetSubFolder(utility.BaseBackupPath))
if err != nil {
tracelog.WarningLogger.Printf("Failed to get metadata of backup %s, error: %s\n",
backupTime.BackupName, err.Error())
continue
}
if criteria(meta) {
foundMeta = append(foundMeta, GenericMetadataInStorage{
GenericMetadata: meta,
StorageName: backupTime.StorageName,
})
}
}
return foundMeta, nil
}
// Select backup by provided backup name
type BackupNameSelector struct {
backupName string
checkExistence bool
}
func NewBackupNameSelector(backupName string, checkExistence bool) (BackupNameSelector, error) {
return BackupNameSelector{backupName: backupName, checkExistence: checkExistence}, nil
}
func (s BackupNameSelector) Select(folder storage.Folder) (Backup, error) {
if !s.checkExistence {
return NewBackupInStorage(folder, s.backupName, consts.DefaultStorage)
}
return GetBackupByName(s.backupName, utility.BaseBackupPath, folder)
}
func NewTargetBackupSelector(targetUserData, targetName string, metaFetcher GenericMetaFetcher) (BackupSelector, error) {
var err error
switch {
case targetName != "" && targetUserData != "":
err = errors.New("incorrect arguments. Specify target backup name OR target userdata, not both")
case targetName == LatestString:
tracelog.InfoLogger.Printf("Selecting the latest backup...\n")
return NewLatestBackupSelector(), nil
case targetName != "":
tracelog.InfoLogger.Printf("Selecting the backup with name %s...\n", targetName)
return NewBackupNameSelector(targetName, true)
case targetUserData != "":
tracelog.InfoLogger.Println("Selecting the backup with the specified user data...")
return NewUserDataBackupSelector(targetUserData, metaFetcher)
default:
err = errors.New("insufficient arguments")
}
return nil, err
}
// NewDeltaBaseSelector returns the BackupSelector for delta backup base according to the provided flags
func NewDeltaBaseSelector(
targetBackupName, targetUserData string, metaFetcher GenericMetaFetcher) (BackupSelector, error) {
switch {
case targetUserData != "" && targetBackupName != "":
return nil, errors.New("only one delta target should be specified")
case targetBackupName != "":
tracelog.InfoLogger.Printf("Selecting the backup with name %s as the base for the current delta backup...\n",
targetBackupName)
return NewBackupNameSelector(targetBackupName, true)
case targetUserData != "":
tracelog.InfoLogger.Println(
"Selecting the backup with specified user data as the base for the current delta backup...")
return NewUserDataBackupSelector(targetUserData, metaFetcher)
default:
return NewLatestBackupSelector(), nil
}
}
// OldestNonPermanentSelector finds oldest non-permanent backup available in storage.
type OldestNonPermanentSelector struct {
metaFetcher GenericMetaFetcher
}
func NewOldestNonPermanentSelector(metaFetcher GenericMetaFetcher) *OldestNonPermanentSelector {
return &OldestNonPermanentSelector{metaFetcher: metaFetcher}
}
func (s *OldestNonPermanentSelector) Select(folder storage.Folder) (Backup, error) {
searchFn := func(d GenericMetadata) bool {
if !d.IsPermanent {
return true
}
tracelog.InfoLogger.Printf(
"Backup %s is permanent, it is not eligible to be selected "+
"as the oldest backup\n", d.BackupName)
return false
}
foundMetas, err := searchInMetadata(searchFn, folder, s.metaFetcher)
if err != nil {
return Backup{}, errors.Wrapf(err, "backups lookup failed")
}
if len(foundMetas) == 0 {
return Backup{}, NewNoBackupsFoundError()
}
oldestMeta := foundMetas[0]
for i := range foundMetas {
if foundMetas[i].StartTime.Before(oldestMeta.StartTime) {
oldestMeta = foundMetas[i]
}
}
return NewBackupInStorage(folder, oldestMeta.BackupName, oldestMeta.StorageName)
}