166 lines
4.6 KiB
Go
166 lines
4.6 KiB
Go
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.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.ExporterLogger.Add(ctx, entry)
|
|
|
|
// and we're done!
|
|
return nil
|
|
}
|