Skip to content

Commit 3ad45fb

Browse files
Wrap error and display for debugging
Signed-off-by: Anisur Rahman <anisur@appscode.com>
1 parent d3dee70 commit 3ad45fb

1 file changed

Lines changed: 85 additions & 44 deletions

File tree

pkg/purge_repos.go

Lines changed: 85 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -323,9 +323,10 @@ func (opt *purgeOptions) executePurgeWorkflow(setupOpt restic.SetupOptions, cuto
323323
return fmt.Errorf("failed to create restic wrapper: %v", err)
324324
}
325325

326+
fmt.Println("\n🔎 Searching for repositories. This may take a while depending on the number of repositories...")
326327
repoList, err := opt.findRepositoriesToPurge(rw, cutoffTime)
327328
if err != nil {
328-
return err
329+
displayRepositoryErrors(err)
329330
}
330331

331332
if len(repoList) == 0 {
@@ -371,10 +372,7 @@ func (opt *purgeOptions) findRepositoriesToPurge(rw *restic.ResticWrapper, cutof
371372
}
372373

373374
err = extractRepoListFromOutput(out, subDirs, cutoffTime, &repos)
374-
if err != nil {
375-
return nil, err
376-
}
377-
return repos, nil
375+
return repos, err
378376
}
379377

380378
func (opt *purgeOptions) listSubdirectories(path string) ([]string, error) {
@@ -435,58 +433,101 @@ func runResticScriptViaDocker(script string) (string, error) {
435433
}
436434

437435
func extractRepoListFromOutput(out string, subDirs []string, cutoffTime time.Time, repos *[]repositoryInfo) error {
438-
type snapshot struct {
439-
Time string `json:"time"`
440-
}
441-
dirIndex := 0
442-
var errs []error
443-
var snapshots []snapshot
436+
var (
437+
dirIndex int
438+
errs []error
439+
)
440+
444441
lines := strings.Split(out, "\n")
445442
for _, line := range lines {
446443
line = strings.TrimSpace(line)
447-
// Skip error messages and separators
448-
if strings.HasPrefix(line, "Failed to access repository") ||
449-
strings.Contains(line, "Fatal: repository does not exist") {
450-
// If we hit an error, we should still increment dirIndex to stay in sync
451-
if dirIndex < len(subDirs) {
452-
dirIndex++
453-
}
444+
if line == "" {
454445
continue
455446
}
456-
457-
// Parse JSON array
458-
if strings.HasPrefix(line, "[") {
459-
if err := json.Unmarshal([]byte(line), &snapshots); err != nil {
460-
errs = append(errs, fmt.Errorf("failed to parse JSON for %s: %v", line, err))
461-
if dirIndex < len(subDirs) {
462-
dirIndex++
463-
}
447+
switch {
448+
case strings.HasPrefix(line, "["):
449+
if err := processSnapshotLine(line, subDirs, &dirIndex, cutoffTime, repos, &errs); err != nil {
464450
continue
465451
}
466-
467-
if len(snapshots) > 0 {
468-
snapshotTime, err := time.Parse(time.RFC3339Nano, snapshots[0].Time)
469-
if err != nil {
470-
errs = append(errs, fmt.Errorf("failed to parse time for %s: %v", line, err))
471-
if dirIndex < len(subDirs) {
472-
dirIndex++
473-
}
474-
continue
475-
}
476-
if dirIndex < len(subDirs) && snapshotTime.Before(cutoffTime) {
477-
*repos = append(*repos, repositoryInfo{
478-
Path: subDirs[dirIndex],
479-
LastModified: snapshotTime,
480-
})
481-
}
452+
case strings.HasPrefix(line, "{"):
453+
processErrorJSONLine(line, subDirs, dirIndex, &errs)
454+
case strings.HasPrefix(line, "Failed to access repository") ||
455+
strings.Contains(line, "Fatal: repository does not exist"):
456+
// Handle plain text error lines
457+
if dirIndex < len(subDirs) {
458+
dirIndex++
482459
}
483-
dirIndex++
484460
}
485461
}
486462

487463
return kerr.NewAggregate(errs)
488464
}
489465

466+
func processSnapshotLine(line string, subDirs []string, dirIndex *int, cutoffTime time.Time, repos *[]repositoryInfo, errs *[]error) error {
467+
type snapshot struct {
468+
Time string `json:"time"`
469+
}
470+
471+
increaseDirIndexAndAppendErr := func(dirIndex *int, err error) {
472+
if *dirIndex < len(subDirs) {
473+
*errs = append(*errs, err)
474+
*dirIndex++
475+
}
476+
}
477+
478+
var snapshots []snapshot
479+
if err := json.Unmarshal([]byte(line), &snapshots); err != nil {
480+
increaseDirIndexAndAppendErr(dirIndex, fmt.Errorf("failed to parse JSON for %s: %v", subDirs[*dirIndex], err))
481+
return err
482+
}
483+
484+
if len(snapshots) > 0 {
485+
snapshotTime, err := time.Parse(time.RFC3339Nano, snapshots[0].Time)
486+
if err != nil {
487+
increaseDirIndexAndAppendErr(dirIndex, fmt.Errorf("failed to parse time for %s: %v", subDirs[*dirIndex], err))
488+
return err
489+
}
490+
if *dirIndex < len(subDirs) && snapshotTime.Before(cutoffTime) {
491+
*repos = append(*repos, repositoryInfo{
492+
Path: subDirs[*dirIndex],
493+
LastModified: snapshotTime,
494+
})
495+
}
496+
}
497+
*dirIndex++
498+
return nil
499+
}
500+
501+
func processErrorJSONLine(line string, subDirs []string, dirIndex int, errs *[]error) {
502+
errMsg := struct {
503+
MessageType string `json:"message_type"`
504+
Code int `json:"code"`
505+
Message string `json:"message"`
506+
}{}
507+
if err := json.Unmarshal([]byte(line), &errMsg); err == nil && errMsg.Message != "" {
508+
// Skip "repository does not exist" (no repo to purge)
509+
if dirIndex < len(subDirs) && !strings.Contains(strings.ToLower(errMsg.Message), "repository does not exist") {
510+
*errs = append(*errs, fmt.Errorf("repository %s: %s", subDirs[dirIndex], errMsg.Message))
511+
}
512+
}
513+
}
514+
515+
func displayRepositoryErrors(err error) {
516+
if err == nil {
517+
return
518+
}
519+
fmt.Println("\n⚠️ Some repositories could not be processed:")
520+
// kerr.NewAggregate returns something that implements Errors()
521+
if agg, ok := err.(interface{ Errors() []error }); ok {
522+
for _, e := range agg.Errors() {
523+
fmt.Printf(" - %s\n", e.Error())
524+
}
525+
} else {
526+
// fallback in case it's not an aggregate
527+
fmt.Printf(" - %s\n", err.Error())
528+
}
529+
}
530+
490531
func (opt *purgeOptions) displayNoRepositoriesMessage() {
491532
fmt.Println("✅ No repositories found matching the criteria.")
492533
fmt.Printf(" - Age filter: older than %s\n", opt.olderThan)
@@ -574,7 +615,7 @@ func (opt *purgeOptions) deleteRepositories(rw *restic.ResticWrapper, repos []re
574615
}
575616

576617
// Execute restic purge operations
577-
fmt.Println("Starting repository deletion process...")
618+
fmt.Println("\n🔥 Starting repository deletion. This process can be lengthy, please do not interrupt.")
578619
script := opt.generateRepoPurgeScript(rw, repoBase, repos)
579620
out, err := runResticScriptViaDocker(script)
580621
if err != nil {

0 commit comments

Comments
 (0)