package exporter import ( "context" "io" "path/filepath" "github.com/FAU-CDI/wisski-distillery/internal/models" "github.com/FAU-CDI/wisski-distillery/internal/wisski" "github.com/FAU-CDI/wisski-distillery/pkg/environment" "github.com/FAU-CDI/wisski-distillery/pkg/logging" "github.com/FAU-CDI/wisski-distillery/pkg/targz" "github.com/tkw1536/goprogram/status" ) // ExportTask describes a task that makes either a [Backup] or a [Snapshot]. // See [Exporter.MakeExport] type ExportTask struct { // Dest is the destination path to write the backup to. // When empty, this is created automatically in the staging or archive directory. Dest string // By default, a .tar.gz file is generated. // To generated an unpacked directory, set [StagingOnly] to true. StagingOnly bool // Instance is the instance to generate a snapshot of. // To generate a backup, leave this to be nil. Instance *wisski.WissKI // BackupDescriptions and SnapshotDescriptions further specitfy options for the export. // The Dest parameter is ignored, and updated automatically. BackupDescription BackupDescription SnapshotDescription SnapshotDescription } // export is implemented by [Backup] and [Snapshot] type export interface { LogEntry() models.Export Report(w io.Writer) (int, error) } // MakeExport performs an export task as described by flags. // Output is directed to the provided io. func (exporter *Exporter) MakeExport(ctx context.Context, progress io.Writer, task ExportTask) (err error) { // extract parameters Title := "Backup" Slug := "" if task.Instance != nil { Title = "Snapshot" Slug = task.Instance.Slug } // determine target paths logging.LogMessage(progress, ctx, "Determining target paths") var stagingDir, archivePath string if task.StagingOnly { stagingDir = task.Dest } else { archivePath = task.Dest } if stagingDir == "" { stagingDir, err = exporter.NewStagingDir(Slug) if err != nil { return err } } if !task.StagingOnly && archivePath == "" { archivePath = exporter.NewArchivePath(Slug) } logging.ProgressF(progress, ctx, "Staging Directory: %s\n", stagingDir) logging.ProgressF(progress, ctx, "Archive Path: %s\n", archivePath) // create the staging directory logging.LogMessage(progress, ctx, "Creating staging directory") err = exporter.Environment.Mkdir(stagingDir, environment.DefaultDirPerm) if !environment.IsExist(err) && err != nil { return err } // if it was requested to not do staging only // we need the staging directory to be deleted at the end if !task.StagingOnly { defer func() { logging.LogMessage(progress, ctx, "Removing staging directory") exporter.Environment.RemoveAll(stagingDir) }() } // create the actual snapshot or backup // write out the report // and retain a log entry var entry models.Export logging.LogOperation(func() error { var sl export if task.Instance == nil { task.BackupDescription.Dest = stagingDir backup := exporter.NewBackup(ctx, progress, task.BackupDescription) sl = &backup } else { task.SnapshotDescription.Dest = stagingDir snapshot := exporter.NewSnapshot(ctx, task.Instance, progress, task.SnapshotDescription) sl = &snapshot } // create a log entry entry = sl.LogEntry() // find the report path reportPath := filepath.Join(stagingDir, "report.txt") logging.ProgressF(progress, ctx, reportPath) // create the path report, err := exporter.Environment.Create(reportPath, environment.DefaultFilePerm) if err != nil { return err } // and write out the report { _, err := sl.Report(report) return err } }, progress, ctx, "Generating %s", Title) // if we only requested staging // all that is left is to write the log entry if task.StagingOnly { logging.LogMessage(progress, ctx, "Writing Log Entry") // write out the log entry entry.Path = stagingDir entry.Packed = false exporter.Dependencies.ExporterLogger.Add(ctx, entry) logging.ProgressF(progress, ctx, "Wrote %s\n", stagingDir) return nil } // package everything up as an archive! if err := logging.LogOperation(func() error { var count int64 defer func() { logging.ProgressF(progress, ctx, "Wrote %d byte(s) to %s\n", count, archivePath) }() st := status.NewWithCompat(progress, 1) st.Start() defer st.Stop() count, err = targz.Package(exporter.Environment, archivePath, stagingDir, func(dst, src string) { st.Set(0, dst) }) return err }, progress, ctx, "Writing archive"); err != nil { return err } // write out the log entry logging.LogMessage(progress, ctx, "Writing Log Entry") entry.Path = archivePath entry.Packed = true exporter.Dependencies.ExporterLogger.Add(ctx, entry) // and we're done! return nil }