diff --git a/internal/location.go b/internal/location.go index 7c669a5..79f9bce 100644 --- a/internal/location.go +++ b/internal/location.go @@ -10,6 +10,7 @@ import ( "github.com/cupcakearmy/autorestic/internal/colors" "github.com/cupcakearmy/autorestic/internal/lock" + "github.com/cupcakearmy/autorestic/internal/metadata" "github.com/robfig/cron" ) @@ -130,6 +131,9 @@ func (l Location) Backup(cron bool) []error { t := l.getType() options := ExecuteOptions{ Command: "bash", + Envs: map[string]string{ + "AUTORESTIC_LOCATION": l.name, + }, } if err := l.validate(); err != nil { @@ -151,7 +155,7 @@ func (l Location) Backup(cron bool) []error { } // Backup - for _, to := range l.To { + for i, to := range l.To { backend, _ := GetBackend(to) colors.Secondary.Printf("Backend: %s\n", backend.name) env, err := backend.getEnv() @@ -187,6 +191,13 @@ func (l Location) Backup(cron bool) []error { errors = append(errors, err) continue } + + md := metadata.ExtractMetadataFromBackupLog(out) + mdEnv := metadata.MakeEnvFromMetadata(&md) + for k, v := range mdEnv { + options.Envs[k+"_"+fmt.Sprint(i)] = v + options.Envs[k+"_"+strings.ToUpper(backend.name)] = v + } if VERBOSE { colors.Faint.Println(out) } diff --git a/internal/metadata/extractor_added.go b/internal/metadata/extractor_added.go new file mode 100644 index 0000000..2e9a638 --- /dev/null +++ b/internal/metadata/extractor_added.go @@ -0,0 +1,22 @@ +package metadata + +import ( + "regexp" + "strings" +) + +type addedExtractor struct { + re *regexp.Regexp +} + +func (e addedExtractor) Matches(line string) bool { + return e.re.MatchString(line) +} +func (e addedExtractor) Extract(metadata *BackupLogMetadata, line string) { + // Sample line: "Added to the repo: 0 B" + metadata.AddedSize = strings.TrimSpace(e.re.ReplaceAllString(line, "")) +} + +func NewAddedExtractor() MetadatExtractor { + return addedExtractor{regexp.MustCompile(`(?i)^Added to the repo:`)} +} diff --git a/internal/metadata/extractor_changeset.go b/internal/metadata/extractor_changeset.go new file mode 100644 index 0000000..6fe67fa --- /dev/null +++ b/internal/metadata/extractor_changeset.go @@ -0,0 +1,57 @@ +package metadata + +import ( + "regexp" + "strings" +) + +type ChangeSetExtractor struct { + re *regexp.Regexp + cleaner *regexp.Regexp + saver changeSetSaver +} + +func (e ChangeSetExtractor) Matches(line string) bool { + return e.re.MatchString(line) +} +func (e ChangeSetExtractor) Extract(metadata *BackupLogMetadata, line string) { + // Sample line: "Files: 0 new, 0 changed, 2 unmodified" + trimmed := strings.TrimSpace(e.re.ReplaceAllString(line, "")) + splitted := strings.Split(trimmed, ",") + var changeset BackupLogMetadataChangeset = BackupLogMetadataChangeset{} + changeset.Added = e.cleaner.ReplaceAllString(splitted[0], "") + changeset.Changed = e.cleaner.ReplaceAllString(splitted[1], "") + changeset.Unmodified = e.cleaner.ReplaceAllString(splitted[2], "") + e.saver.Save(metadata, changeset) +} + +type changeSetSaver interface { + Save(metadata *BackupLogMetadata, changeset BackupLogMetadataChangeset) +} + +type fileSaver struct{} + +func (f fileSaver) Save(metadata *BackupLogMetadata, changeset BackupLogMetadataChangeset) { + metadata.Files = changeset +} + +type dirsSaver struct{} + +func (d dirsSaver) Save(metadata *BackupLogMetadata, changeset BackupLogMetadataChangeset) { + metadata.Dirs = changeset +} + +func NewFilesExtractor() MetadatExtractor { + return ChangeSetExtractor{ + re: regexp.MustCompile(`(?i)^Files:`), + cleaner: regexp.MustCompile(`[^\d]`), + saver: fileSaver{}, + } +} +func NewDirsExtractor() MetadatExtractor { + return ChangeSetExtractor{ + re: regexp.MustCompile(`(?i)^Dirs:`), + cleaner: regexp.MustCompile(`[^\d]`), + saver: dirsSaver{}, + } +} diff --git a/internal/metadata/extractor_parent.go b/internal/metadata/extractor_parent.go new file mode 100644 index 0000000..d2b3f7d --- /dev/null +++ b/internal/metadata/extractor_parent.go @@ -0,0 +1,22 @@ +package metadata + +import ( + "regexp" + "strings" +) + +type parentSnapshotIDExtractor struct { + re *regexp.Regexp +} + +func (e parentSnapshotIDExtractor) Matches(line string) bool { + return e.re.MatchString(line) +} +func (e parentSnapshotIDExtractor) Extract(metadata *BackupLogMetadata, line string) { + // Sample line: "using parent snapshot c65d9310" + metadata.ParentSnapshotID = strings.TrimSpace(e.re.ReplaceAllString(line, "")) +} + +func NewParentSnapshotIDExtractor() MetadatExtractor { + return parentSnapshotIDExtractor{regexp.MustCompile(`(?i)^using parent snapshot`)} +} diff --git a/internal/metadata/extractor_processed.go b/internal/metadata/extractor_processed.go new file mode 100644 index 0000000..c76b0ec --- /dev/null +++ b/internal/metadata/extractor_processed.go @@ -0,0 +1,32 @@ +package metadata + +import ( + "regexp" + "strings" +) + +type processedExtractor struct { + re *regexp.Regexp + cleaner *regexp.Regexp +} + +func (e processedExtractor) Matches(line string) bool { + return e.re.MatchString(line) +} +func (e processedExtractor) Extract(metadata *BackupLogMetadata, line string) { + // Sample line: "processed 2 files, 24 B in 0:00" + var processed = BackupLogMetadataProcessed{} + split := strings.Split(line, "in") + processed.Duration = strings.TrimSpace(split[1]) + split = strings.Split(split[0], ",") + processed.Files = e.cleaner.ReplaceAllString(split[0], "") + processed.Size = strings.TrimSpace(split[1]) + metadata.Processed = processed +} + +func NewProcessedExtractor() MetadatExtractor { + return processedExtractor{ + regexp.MustCompile(`(?i)^processed \d* files`), + regexp.MustCompile(`(?i)[^\d]`), + } +} diff --git a/internal/metadata/extractor_snapshot.go b/internal/metadata/extractor_snapshot.go new file mode 100644 index 0000000..3099a56 --- /dev/null +++ b/internal/metadata/extractor_snapshot.go @@ -0,0 +1,22 @@ +package metadata + +import ( + "regexp" + "strings" +) + +type snapshotExtractor struct { + re *regexp.Regexp +} + +func (e snapshotExtractor) Matches(line string) bool { + return e.re.MatchString(line) +} +func (e snapshotExtractor) Extract(metadata *BackupLogMetadata, line string) { + // Sample line: "snapshot 917c7691 saved" + metadata.SnapshotID = strings.Split(line, " ")[1] +} + +func NewSnapshotExtractor() MetadatExtractor { + return snapshotExtractor{regexp.MustCompile(`(?i)^snapshot \w+ saved`)} +} diff --git a/internal/metadata/metadata.go b/internal/metadata/metadata.go new file mode 100644 index 0000000..228cf22 --- /dev/null +++ b/internal/metadata/metadata.go @@ -0,0 +1,72 @@ +package metadata + +import ( + "strings" +) + +type BackupLogMetadataChangeset struct { + Added string + Changed string + Unmodified string +} +type BackupLogMetadataProcessed struct { + Files string + Size string + Duration string +} +type BackupLogMetadata struct { + ParentSnapshotID string + Files BackupLogMetadataChangeset + Dirs BackupLogMetadataChangeset + AddedSize string + Processed BackupLogMetadataProcessed + SnapshotID string +} + +type MetadatExtractor interface { + Matches(line string) bool + Extract(metadata *BackupLogMetadata, line string) +} + +var extractors = []MetadatExtractor{ + NewParentSnapshotIDExtractor(), + NewFilesExtractor(), + NewDirsExtractor(), + NewAddedExtractor(), + NewProcessedExtractor(), + NewSnapshotExtractor(), +} + +func ExtractMetadataFromBackupLog(log string) BackupLogMetadata { + var md BackupLogMetadata + for _, line := range strings.Split(log, "\n") { + line = strings.TrimSpace(line) + for _, extractor := range extractors { + if extractor.Matches(line) { + extractor.Extract(&md, line) + continue + } + } + } + return md +} + +func MakeEnvFromMetadata(metadata *BackupLogMetadata) map[string]string { + env := make(map[string]string) + var prefix = "AUTORESTIC_" + + env[prefix+"SNAPSHOT_ID"] = metadata.SnapshotID + env[prefix+"PARENT_SNAPSHOT_ID"] = metadata.ParentSnapshotID + env[prefix+"FILES_ADDED"] = metadata.Files.Added + env[prefix+"FILES_CHANGED"] = metadata.Files.Changed + env[prefix+"FILES_UNMODIFIED"] = metadata.Files.Unmodified + env[prefix+"DIRS_ADDED"] = metadata.Dirs.Added + env[prefix+"DIRS_CHANGED"] = metadata.Dirs.Changed + env[prefix+"DIRS_UNMODIFIED"] = metadata.Dirs.Unmodified + env[prefix+"ADDED_SIZE"] = metadata.AddedSize + env[prefix+"PROCESSED_FILES"] = metadata.Processed.Files + env[prefix+"PROCESSED_SIZE"] = metadata.Processed.Size + env[prefix+"PROCESSED_DURATION"] = metadata.Processed.Duration + + return env +}