wal-g

Форк
0
/
delete_handler.go 
682 строки · 21.5 Кб
1
package internal
2

3
import (
4
	"fmt"
5
	"os"
6
	"sort"
7
	"strconv"
8
	"strings"
9
	"time"
10

11
	"github.com/pkg/errors"
12
	"github.com/spf13/cobra"
13
	"github.com/wal-g/tracelog"
14
	"github.com/wal-g/wal-g/internal/multistorage"
15
	"github.com/wal-g/wal-g/pkg/storages/storage"
16
	"github.com/wal-g/wal-g/utility"
17
)
18

19
const (
20
	NoDeleteModifier = iota
21
	FullDeleteModifier
22
	FindFullDeleteModifier
23
	ForceDeleteModifier
24
	ConfirmFlag            = "confirm"
25
	DeleteShortDescription = "Clears old backups and WALs"
26

27
	DeleteRetainExamples = `  retain 5                      keep 5 backups
28
  retain FULL 5                 keep 5 full backups and all deltas of them
29
  retain FIND_FULL 5            find necessary full for 5th and keep everything after it
30
  retain 5 --after 2019-12-12T12:12:12   keep 5 most recent backups and backups made after 2019-12-12 12:12:12`
31

32
	DeleteBeforeExamples = `  before base_0123              keep everything after base_0123 including itself
33
  before FIND_FULL base_0123    keep everything after the base of base_0123`
34

35
	DeleteEverythingExamples = `  everything                
36
	delete every backup only if there is no permanent backups
37
  everything FORCE          delete every backup include permanents`
38

39
	DeleteTargetExamples = `  target base_0000000100000000000000C4	delete base backup by name
40
  target --target-user-data "{ \"x\": [3], \"y\": 4 }"	delete backup specified by user data
41
  target base_0000000100000000000000C9_D_0000000100000000000000C4	delete delta backup and all dependant delta backups 
42
  target FIND_FULL base_0000000100000000000000C9_D_0000000100000000000000C4	delete delta backup and all delta backups with the same base backup`  //nolint:lll
43

44
	DeleteEverythingUsageExample = "everything [FORCE]"
45
	DeleteRetainUsageExample     = "retain [FULL|FIND_FULL] backup_count"
46
	DeleteBeforeUsageExample     = "before [FIND_FULL] backup_name|timestamp"
47
	DeleteTargetUsageExample     = "target [FIND_FULL] backup_name | --target-user-data <data>"
48

49
	DeleteTargetUserDataFlag        = "target-user-data"
50
	DeleteTargetUserDataDescription = "delete storage backup which has the specified user data"
51
)
52

53
var StringModifiers = []string{"FULL", "FIND_FULL"}
54
var StringModifiersDeleteEverything = []string{"FORCE"}
55
var errNotFound = errors.New("not found")
56
var errIncorrectArguments = errors.New("incorrect arguments")
57

58
// BackupObject represents
59
// the backup sentinel object uploaded on storage
60
type BackupObject interface {
61
	storage.Object
62
	multistorage.StorageTeller
63
	GetBackupTime() time.Time
64
	GetBackupName() string
65

66
	// TODO: move increment info into separate struct (in backup.go)
67
	IsFullBackup() bool
68
	GetBaseBackupName() string
69
	GetIncrementFromName() string
70
}
71

72
type DeleteHandlerOption func(h *DeleteHandler)
73

74
func IsPermanentFunc(isPermanent func(storage.Object) bool) DeleteHandlerOption {
75
	return func(h *DeleteHandler) {
76
		h.isPermanent = isPermanent
77
	}
78
}
79

80
func NewDeleteHandler(
81
	folder storage.Folder,
82
	backups []BackupObject,
83
	less func(object1, object2 storage.Object) bool,
84
	options ...DeleteHandlerOption,
85
) *DeleteHandler {
86
	deleteHandler := &DeleteHandler{
87
		Folder:  folder,
88
		backups: backups,
89
		less:    less,
90
		greater: func(object1, object2 storage.Object) bool {
91
			return less(object2, object1)
92
		},
93
		// by default, all storage objects are impermanent
94
		isPermanent: func(storage.Object) bool { return false },
95
	}
96

97
	for _, option := range options {
98
		option(deleteHandler)
99
	}
100

101
	return deleteHandler
102
}
103

104
type DeleteHandler struct {
105
	Folder  storage.Folder
106
	backups []BackupObject
107

108
	less    func(object1, object2 storage.Object) bool
109
	greater func(object1, object2 storage.Object) bool
110

111
	isPermanent func(object storage.Object) bool
112
}
113

114
func (h *DeleteHandler) HandleDeleteBefore(args []string, confirmed bool) {
115
	modifier, beforeStr := ExtractDeleteModifierFromArgs(args)
116

117
	target, err := h.FindTargetBefore(beforeStr, modifier)
118
	tracelog.ErrorLogger.FatalOnError(err)
119
	if target == nil {
120
		tracelog.InfoLogger.Printf("No backup found for deletion")
121
		os.Exit(0)
122
	}
123

124
	err = h.DeleteBeforeTarget(target, confirmed)
125
	tracelog.ErrorLogger.FatalOnError(err)
126
}
127

128
func (h *DeleteHandler) HandleDeleteRetain(args []string, confirmed bool) {
129
	modifier, retentionStr := ExtractDeleteModifierFromArgs(args)
130
	retentionCount, err := strconv.Atoi(retentionStr)
131
	tracelog.ErrorLogger.FatalOnError(err)
132

133
	target, err := h.FindTargetRetain(retentionCount, modifier)
134
	tracelog.ErrorLogger.FatalOnError(err)
135
	if target == nil {
136
		tracelog.InfoLogger.Printf("No backup found for deletion")
137
		os.Exit(0)
138
	}
139
	err = h.DeleteBeforeTarget(target, confirmed)
140
	tracelog.ErrorLogger.FatalOnError(err)
141
}
142

143
func (h *DeleteHandler) HandleDeleteRetainAfter(args []string, confirmed bool) {
144
	modifier, retentionSir, afterStr := ExtractDeleteRetainAfterModifierFromArgs(args)
145
	retentionCount, err := strconv.Atoi(retentionSir)
146
	tracelog.ErrorLogger.FatalOnError(err)
147

148
	target, err := h.FindTargetRetainAfter(retentionCount, afterStr, modifier)
149
	tracelog.ErrorLogger.FatalOnError(err)
150

151
	if target == nil {
152
		tracelog.InfoLogger.Printf("No backup found for deletion")
153
		os.Exit(0)
154
	}
155

156
	err = h.DeleteBeforeTarget(target, confirmed)
157
	tracelog.ErrorLogger.FatalOnError(err)
158
}
159

160
func (h *DeleteHandler) HandleDeleteTarget(targetSelector BackupSelector, confirmed, findFull bool) {
161
	target, err := h.FindTargetBySelector(targetSelector)
162
	tracelog.ErrorLogger.FatalOnError(err)
163

164
	if target == nil {
165
		// since we want to delete the target backup, we should fail if
166
		// we didn't find the requested backup for deletion
167
		tracelog.ErrorLogger.Fatal("Requested backup was not found")
168
	}
169

170
	folderFilter := func(name string) bool { return true }
171
	err = h.DeleteTarget(target, confirmed, findFull, folderFilter)
172
	tracelog.ErrorLogger.FatalOnError(err)
173
}
174

175
func (h *DeleteHandler) HandleDeleteEverything(args []string, permanentBackups []string, confirmed bool) {
176
	forceModifier := false
177
	modifier := ExtractDeleteEverythingModifierFromArgs(args)
178
	if modifier == ForceDeleteModifier {
179
		forceModifier = true
180
	}
181

182
	if len(permanentBackups) > 0 {
183
		if !forceModifier {
184
			tracelog.ErrorLogger.Fatalf("Found permanent backups=%v\n", permanentBackups)
185
		}
186
		tracelog.InfoLogger.Printf("Found permanent backups=%v\n", permanentBackups)
187
	}
188
	h.DeleteEverything(confirmed)
189
}
190

191
// TODO: unit tests
192
func (h *DeleteHandler) FindTargetBefore(beforeStr string, modifier int) (BackupObject, error) {
193
	timeLine, err := time.Parse(time.RFC3339, beforeStr)
194
	if err == nil {
195
		return h.FindTargetBeforeTime(timeLine, modifier)
196
	}
197

198
	return h.FindTargetBeforeName(beforeStr, modifier)
199
}
200

201
func (h *DeleteHandler) FindTargetBeforeName(name string, modifier int) (BackupObject, error) {
202
	choiceFunc := getBeforeChoiceFunc(name, modifier)
203
	if choiceFunc == nil {
204
		return nil, utility.NewForbiddenActionError("Not allowed modifier for 'delete before'")
205
	}
206
	return findTarget(h.backups, h.greater, choiceFunc)
207
}
208

209
// TODO: unit tests
210
func (h *DeleteHandler) FindTargetBeforeTime(timeLine time.Time, modifier int) (BackupObject, error) {
211
	potentialTarget, err := findTarget(h.backups, h.less, func(object BackupObject) bool {
212
		backupTime := object.GetBackupTime()
213
		return timeLine.Before(backupTime) || timeLine.Equal(backupTime)
214
	})
215
	if err != nil && err != errNotFound {
216
		return nil, err
217
	}
218
	if potentialTarget == nil {
219
		return nil, nil
220
	}
221

222
	return h.FindTargetBeforeName(potentialTarget.GetName(), modifier)
223
}
224

225
func (h *DeleteHandler) FindTargetRetain(retentionCount, modifier int) (BackupObject, error) {
226
	choiceFunc := getRetainChoiceFunc(retentionCount, modifier)
227
	if choiceFunc == nil {
228
		return nil, utility.NewForbiddenActionError("Not allowed modifier for 'delete retain'")
229
	}
230
	target, err := findTarget(h.backups, h.greater, choiceFunc)
231
	// it is OK to have no backups found outside the specified retain window, skip this error
232
	if err != nil && err != errNotFound {
233
		return nil, err
234
	}
235
	return target, nil
236
}
237

238
// TODO: unit tests
239
func (h *DeleteHandler) FindTargetByName(bname string) (BackupObject, error) {
240
	return findTarget(h.backups, h.greater, func(object BackupObject) bool {
241
		return strings.HasPrefix(object.GetName(), bname)
242
	})
243
}
244

245
// TODO: unit tests
246
func (h *DeleteHandler) FindTargetBySelector(targetSelector BackupSelector) (BackupObject, error) {
247
	targetBackup, err := targetSelector.Select(h.Folder)
248
	if err != nil {
249
		if _, ok := err.(NoBackupsFoundError); ok {
250
			// it is OK to have no backups found for the provided selector,
251
			// just return no target found
252
			return nil, nil
253
		}
254
		return nil, err
255
	}
256

257
	var target BackupObject
258
	for idx := range h.backups {
259
		if h.backups[idx].GetBackupName() == targetBackup.Name {
260
			target = h.backups[idx]
261
			break
262
		}
263
	}
264

265
	return target, nil
266
}
267

268
// TODO: unit tests
269
func (h *DeleteHandler) FindTargetRetainAfter(retentionCount int, afterStr string, modifier int) (BackupObject, error) {
270
	timeLine, err := time.Parse(time.RFC3339, afterStr)
271
	if err == nil {
272
		return h.FindTargetRetainAfterTime(retentionCount, timeLine, modifier)
273
	}
274

275
	return h.FindTargetRetainAfterName(retentionCount, afterStr, modifier)
276
}
277

278
// TODO: unit tests
279
func (h *DeleteHandler) FindTargetRetainAfterName(
280
	retentionCount int, name string, modifier int) (BackupObject, error) {
281
	choiceFuncRetain := getRetainChoiceFunc(retentionCount, modifier)
282
	if choiceFuncRetain == nil {
283
		return nil, utility.NewForbiddenActionError("Not allowed modifier for 'delete before'")
284
	}
285
	meetName := false
286
	choiceFuncAfterName := func(object BackupObject) bool {
287
		meetName = meetName || strings.HasPrefix(object.GetName(), name)
288
		if modifier == NoDeleteModifier {
289
			return meetName
290
		}
291
		return meetName && object.IsFullBackup()
292
	}
293

294
	target1, err := findTarget(h.backups, h.greater, choiceFuncRetain)
295
	if err != nil && err != errNotFound {
296
		return nil, err
297
	}
298
	target2, err := findTarget(h.backups, h.less, choiceFuncAfterName)
299
	if err != nil && err != errNotFound {
300
		return nil, err
301
	}
302

303
	if h.greater(target2, target1) {
304
		return target1, nil
305
	}
306
	return target2, nil
307
}
308

309
// TODO: unit tests
310
func (h *DeleteHandler) FindTargetRetainAfterTime(retentionCount int, timeLine time.Time, modifier int,
311
) (BackupObject, error) {
312
	choiceFuncRetain := getRetainChoiceFunc(retentionCount, modifier)
313
	if choiceFuncRetain == nil {
314
		return nil, utility.NewForbiddenActionError("Not allowed modifier for 'delete retain'")
315
	}
316
	choiceFuncAfter := func(object BackupObject) bool {
317
		backupTime := object.GetBackupTime()
318
		timeCheck := timeLine.Before(backupTime) || timeLine.Equal(backupTime)
319
		if modifier == NoDeleteModifier {
320
			return timeCheck
321
		}
322
		return timeCheck && object.IsFullBackup()
323
	}
324

325
	target1, err := findTarget(h.backups, h.greater, choiceFuncRetain)
326
	if err != nil && err != errNotFound {
327
		return nil, err
328
	}
329
	target2, err := findTarget(h.backups, h.less, choiceFuncAfter)
330
	if err != nil && err != errNotFound {
331
		return nil, err
332
	}
333

334
	if target1 == nil {
335
		return target2, nil
336
	}
337
	if target2 == nil {
338
		return target1, nil
339
	}
340

341
	if h.greater(target2, target1) {
342
		return target1, nil
343
	}
344

345
	return target2, nil
346
}
347

348
func (h *DeleteHandler) DeleteEverything(confirmed bool) {
349
	filter := func(object storage.Object) bool { return true }
350
	folderFilter := func(path string) bool { return true }
351
	err := DeleteObjectsWhere(h.Folder, confirmed, filter, folderFilter)
352
	tracelog.ErrorLogger.FatalOnError(err)
353
}
354

355
func (h *DeleteHandler) DeleteBeforeTarget(target BackupObject, confirmed bool) error {
356
	objFilter := func(storage.Object) bool { return true }
357
	folderFilter := func(string) bool { return true }
358

359
	return h.DeleteBeforeTargetWhere(target, confirmed, objFilter, folderFilter)
360
}
361

362
func (h *DeleteHandler) DeleteBeforeTargetWhere(
363
	target BackupObject,
364
	confirmed bool,
365
	objSelector func(object storage.Object) bool,
366
	folderFilter func(name string) bool,
367
) error {
368
	if !target.IsFullBackup() {
369
		errorMessage := "%v is incremental and it's predecessors cannot be deleted. Consider FIND_FULL option."
370
		return utility.NewForbiddenActionError(fmt.Sprintf(errorMessage, target.GetName()))
371
	}
372
	tracelog.InfoLogger.Println("Start delete")
373

374
	return DeleteObjectsWhere(h.Folder, confirmed, func(object storage.Object) bool {
375
		return objSelector(object) && h.less(object, target) && !h.isPermanent(object)
376
	}, folderFilter)
377
}
378

379
func (h *DeleteHandler) DeleteTarget(target BackupObject, confirmed, findFull bool,
380
	folderFilter func(name string) bool) error {
381
	var backupsToDelete []BackupObject
382
	if findFull {
383
		// delete all backups with the same base backup as the target
384
		backupsToDelete = h.findRelatedBackups(target)
385
	} else {
386
		// delete all dependant backups
387
		backupsToDelete = h.findDependantBackups(target)
388
	}
389
	tracelog.DebugLogger.Printf("backupsToDelete: %v", backupsToDelete)
390

391
	backupNamesToDelete := make(map[string]bool)
392
	for _, bTarget := range backupsToDelete {
393
		if h.isPermanent(bTarget) {
394
			tracelog.ErrorLogger.Fatalf("Unable to delete permanent backup %s\n", bTarget.GetName())
395
		}
396
		backupNamesToDelete[bTarget.GetBackupName()] = true
397
	}
398

399
	return DeleteObjectsWhere(h.Folder.GetSubFolder(utility.BaseBackupPath),
400
		confirmed, func(object storage.Object) bool {
401
			return backupNamesToDelete[utility.StripLeftmostBackupName(object.GetName())] && !h.isPermanent(object)
402
		}, folderFilter)
403
}
404

405
// TODO: unit tests
406
// Find all backups related to the target.
407
// All delta backups with the same base backup are considered as related.
408
func (h *DeleteHandler) findRelatedBackups(target BackupObject) []BackupObject {
409
	relatedBackups := make([]BackupObject, 0)
410

411
	var related func(target BackupObject, other BackupObject) bool
412
	if target.IsFullBackup() {
413
		related = func(target BackupObject, other BackupObject) bool {
414
			// remove base backup
415
			isBaseBackup := target.GetBackupName() == other.GetBackupName()
416
			// remove all increments from the target backup too
417
			isIncrement := target.GetBackupName() == other.GetBaseBackupName()
418
			return isBaseBackup || isIncrement
419
		}
420
	} else {
421
		related = func(target BackupObject, other BackupObject) bool {
422
			// remove base backup
423
			isBaseBackup := target.GetBaseBackupName() == other.GetBackupName()
424
			// remove all other increments from the target base backup
425
			hasCommonBaseBackup := target.GetBaseBackupName() == other.GetBaseBackupName()
426
			return isBaseBackup || hasCommonBaseBackup
427
		}
428
	}
429

430
	for _, backup := range h.backups {
431
		if related(target, backup) {
432
			relatedBackups = append(relatedBackups, backup)
433
		}
434
	}
435
	return relatedBackups
436
}
437

438
// TODO: unit tests
439
// Find all backups dependant on the target.
440
// All delta backups which have the target as the ancestor in increment chain
441
// are considered as dependant.
442
func (h *DeleteHandler) findDependantBackups(target BackupObject) []BackupObject {
443
	dependantBackups := make([]BackupObject, 0)
444

445
	incrementsByBackup := make(map[string][]BackupObject)
446
	for _, backup := range h.backups {
447
		if !backup.IsFullBackup() {
448
			incrementFrom := backup.GetIncrementFromName()
449
			incrementsByBackup[incrementFrom] = append(incrementsByBackup[incrementFrom], backup)
450
		}
451
	}
452

453
	queue := []BackupObject{target}
454
	var curr BackupObject
455
	for len(queue) > 0 {
456
		curr, queue = queue[0], queue[1:]
457
		dependantBackups = append(dependantBackups, curr)
458
		queue = append(queue, incrementsByBackup[curr.GetBackupName()]...)
459
	}
460
	return dependantBackups
461
}
462

463
func DeleteObjectsWhere(
464
	folder storage.Folder,
465
	confirm bool,
466
	objFilter func(object1 storage.Object) bool,
467
	folderFilter func(name string) bool,
468
) error {
469
	relativePathObjects, err := multistorage.ListFolderRecursivelyWithFilter(folder, folderFilter)
470
	if err != nil {
471
		return err
472
	}
473
	filteredRelativePaths := make([]string, 0)
474
	tracelog.InfoLogger.Println("Objects in folder:")
475
	for _, object := range relativePathObjects {
476
		if objFilter(object) {
477
			tracelog.InfoLogger.Printf("\twill be deleted: %s, from storage: %s\n", object.GetName(), multistorage.GetStorage(object))
478
			filteredRelativePaths = append(filteredRelativePaths, object.GetName())
479
		} else {
480
			tracelog.DebugLogger.Printf("\tskipped: %s, in storage: %s\n", object.GetName(), multistorage.GetStorage(object))
481
		}
482
	}
483
	if len(filteredRelativePaths) == 0 {
484
		return nil
485
	}
486
	if confirm {
487
		return folder.DeleteObjects(filteredRelativePaths)
488
	}
489
	tracelog.InfoLogger.Println("Dry run, nothing were deleted")
490
	return nil
491
}
492

493
func findTarget(objects []BackupObject,
494
	compare func(object1, object2 storage.Object) bool,
495
	isTarget func(object BackupObject) bool) (BackupObject, error) {
496
	sort.Slice(objects, func(i, j int) bool {
497
		return compare(objects[i], objects[j])
498
	})
499
	for _, object := range objects {
500
		tracelog.DebugLogger.Printf("processing %s\n", object.GetName())
501
		if isTarget(object) {
502
			return object, nil
503
		}
504
	}
505
	return nil, errNotFound
506
}
507

508
func getBeforeChoiceFunc(name string, modifier int) func(object BackupObject) bool {
509
	meetName := false
510
	switch modifier {
511
	case NoDeleteModifier:
512
		return func(object BackupObject) bool {
513
			return strings.HasPrefix(object.GetName(), name)
514
		}
515
	case FindFullDeleteModifier:
516
		return func(object BackupObject) bool {
517
			meetName = meetName || strings.HasPrefix(object.GetName(), name)
518
			return meetName && object.IsFullBackup()
519
		}
520
	}
521
	return nil
522
}
523

524
func getRetainChoiceFunc(retentionCount, modifier int) func(object BackupObject) bool {
525
	uniqueNames := map[string]bool{}
526
	switch modifier {
527
	case NoDeleteModifier:
528
		return func(object BackupObject) bool {
529
			uniqueNames[object.GetBackupName()] = true
530

531
			return len(uniqueNames) == retentionCount
532
		}
533
	case FullDeleteModifier:
534
		return func(object BackupObject) bool {
535
			if object.IsFullBackup() {
536
				uniqueNames[object.GetBackupName()] = true
537
			}
538
			if len(uniqueNames) == retentionCount {
539
				return true
540
			}
541
			return false
542
		}
543
	case FindFullDeleteModifier:
544
		return func(object BackupObject) bool {
545
			uniqueNames[object.GetBackupName()] = true
546
			if len(uniqueNames) >= retentionCount && object.IsFullBackup() {
547
				return true
548
			}
549
			return false
550
		}
551
	}
552
	return nil
553
}
554

555
// ExtractDeleteRetainAfterModifierFromArgs extracts the args for the "delete retain --after" command
556
func ExtractDeleteRetainAfterModifierFromArgs(args []string) (int, string, string) {
557
	if len(args) == 2 {
558
		return NoDeleteModifier, args[0], args[1]
559
	} else if args[0] == StringModifiers[0] {
560
		return FullDeleteModifier, args[1], args[2]
561
	}
562
	return FindFullDeleteModifier, args[1], args[2]
563
}
564

565
// ExtractDeleteEverythingModifierFromArgs extracts the args for the "delete everything" command
566
func ExtractDeleteEverythingModifierFromArgs(args []string) int {
567
	if len(args) == 0 {
568
		return NoDeleteModifier
569
	}
570
	return ForceDeleteModifier
571
}
572

573
// ExtractDeleteTargetModifierFromArgs extracts the args for the "delete target" command
574
func ExtractDeleteTargetModifierFromArgs(args []string) int {
575
	if len(args) >= 1 && args[0] == StringModifiers[1] {
576
		return FindFullDeleteModifier
577
	}
578

579
	return NoDeleteModifier
580
}
581

582
// ExtractDeleteModifierFromArgs extracts the delete modifier the "delete retain"/"delete before" commands
583
func ExtractDeleteModifierFromArgs(args []string) (int, string) {
584
	if len(args) == 1 {
585
		return NoDeleteModifier, args[0]
586
	} else if args[0] == StringModifiers[0] {
587
		return FullDeleteModifier, args[1]
588
	}
589
	return FindFullDeleteModifier, args[1]
590
}
591

592
func DeleteBeforeArgsValidator(cmd *cobra.Command, args []string) error {
593
	err := DeleteArgsValidator(args, StringModifiers, 1, 2)
594
	if err != nil {
595
		return err
596
	}
597
	modifier, beforeStr := ExtractDeleteModifierFromArgs(args)
598
	if modifier == FullDeleteModifier {
599
		return fmt.Errorf("unsupported moodifier for delete before command")
600
	}
601
	if before, err := time.Parse(time.RFC3339, beforeStr); err == nil {
602
		if before.After(utility.TimeNowCrossPlatformUTC()) {
603
			return fmt.Errorf("cannot delete before future date")
604
		}
605
	}
606
	return nil
607
}
608

609
func DeleteTargetArgsValidator(cmd *cobra.Command, args []string) error {
610
	err := cobra.RangeArgs(0, 2)(cmd, args)
611
	if err != nil {
612
		return err
613
	}
614

615
	switch {
616
	case len(args) == 0 && !cmd.Flags().Changed(DeleteTargetUserDataFlag):
617
		// allow 0 arguments only when target user data flag is set
618
		return errIncorrectArguments
619

620
	case len(args) == 2 && args[0] != StringModifiers[1]:
621
		return errIncorrectArguments
622

623
	default:
624
		return nil
625
	}
626
}
627

628
func DeleteEverythingArgsValidator(cmd *cobra.Command, args []string) error {
629
	return DeleteArgsValidator(args, StringModifiersDeleteEverything, 0, 1)
630
}
631

632
func DeleteRetainArgsValidator(cmd *cobra.Command, args []string) error {
633
	_, retentionStr := ExtractDeleteModifierFromArgs(args)
634
	retentionNumber, err := strconv.Atoi(retentionStr)
635
	if err != nil {
636
		return errors.Wrapf(err, "expected to get a number as retantion count, but got: '%s'", retentionStr)
637
	}
638
	if retentionNumber <= 0 {
639
		return fmt.Errorf("cannot retain less than one backup. Check out delete everything")
640
	}
641
	return nil
642
}
643

644
func DeleteRetainAfterArgsValidator(cmd *cobra.Command, args []string) error {
645
	err := DeleteArgsValidator(args, StringModifiers, 2, 3)
646
	if err != nil {
647
		return err
648
	}
649
	_, retentionStr, afterStr := ExtractDeleteRetainAfterModifierFromArgs(args)
650
	retentionNumber, err := strconv.Atoi(retentionStr)
651
	if err != nil {
652
		return errors.Wrapf(err, "expected to get a number as retantion count, but got: '%s'", retentionStr)
653
	}
654
	if retentionNumber <= 0 {
655
		return fmt.Errorf("cannot retain less than one backup. Check out delete everything")
656
	}
657
	if before, err := time.Parse(time.RFC3339, afterStr); err == nil {
658
		if before.After(utility.TimeNowCrossPlatformUTC()) {
659
			return fmt.Errorf("cannot delete retain future date")
660
		}
661
	}
662
	return nil
663
}
664

665
func DeleteArgsValidator(args, stringModifiers []string, minArgs int, maxArgs int) error {
666
	if len(args) < minArgs || len(args) > maxArgs {
667
		return fmt.Errorf("accepts between %d and %d arg(s), received %d", minArgs, maxArgs, len(args))
668
	}
669
	if len(args) == maxArgs {
670
		expectedModifier := args[0]
671
		isModifierInList := false
672
		for _, modifier := range stringModifiers {
673
			if isModifierInList = modifier == expectedModifier; isModifierInList {
674
				break
675
			}
676
		}
677
		if !isModifierInList {
678
			return fmt.Errorf("expected to get one of modifiers: %v as first argument", stringModifiers)
679
		}
680
	}
681
	return nil
682
}
683

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.