198 lines
4.9 KiB
Go
198 lines
4.9 KiB
Go
package component
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"path/filepath"
|
|
|
|
"github.com/FAU-CDI/wisski-distillery/internal/models"
|
|
"github.com/FAU-CDI/wisski-distillery/pkg/environment"
|
|
"github.com/FAU-CDI/wisski-distillery/pkg/fsx"
|
|
"github.com/FAU-CDI/wisski-distillery/pkg/logging"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
// Backupable represents a component with a Backup method
|
|
type Backupable interface {
|
|
Component
|
|
|
|
// BackupName returns a new name to be used as an argument for path.
|
|
BackupName() string
|
|
|
|
// Backup backs up this component into the destination path path
|
|
Backup(context StagingContext) error
|
|
}
|
|
|
|
// Snapshotable represents a component with a Snapshot method.
|
|
type Snapshotable interface {
|
|
Component
|
|
|
|
// SnapshotNeedsRunning returns if this Snapshotable requires a running instance.
|
|
SnapshotNeedsRunning() bool
|
|
|
|
// SnapshotName returns a new name to be used as an argument for path.
|
|
SnapshotName() string
|
|
|
|
// Snapshot snapshots a part of the instance
|
|
Snapshot(wisski models.Instance, context StagingContext) error
|
|
}
|
|
|
|
// StagingContext represents a context for [Backupable] and [Snapshotable]
|
|
type StagingContext interface {
|
|
// Progress returns a writer to write progress information to.
|
|
Progress() io.Writer
|
|
|
|
// Name creates a new directory inside the destination.
|
|
// Passing the empty path creates the destination as a directory.
|
|
//
|
|
// It then allows op to fill the file.
|
|
AddDirectory(path string, op func(context.Context) error) error
|
|
|
|
// CopyFile copies a file from src to dst.
|
|
CopyFile(dst, src string) error
|
|
|
|
// CopyDirectory copies a directory from src to dst.
|
|
CopyDirectory(dst, src string) error
|
|
|
|
// AddFile creates a new file at the provided path inside the destination.
|
|
// Passing the empty path creates the destination as a file.
|
|
//
|
|
// It then allows op to write to the file.
|
|
//
|
|
// The op function must not retain file.
|
|
// The underlying file does not need to be closed.
|
|
// AddFile will not return before op has returned.
|
|
AddFile(path string, op func(ctx context.Context, file io.Writer) error) error
|
|
}
|
|
|
|
// NewStagingContext returns a new [StagingContext]
|
|
func NewStagingContext(ctx context.Context, env environment.Environment, progress io.Writer, path string, manifest chan<- string) StagingContext {
|
|
return &stagingContext{
|
|
ctx: ctx,
|
|
env: env,
|
|
progress: progress,
|
|
path: path,
|
|
manifest: manifest,
|
|
}
|
|
}
|
|
|
|
// stagingContext implements [components.StagingContext]
|
|
type stagingContext struct {
|
|
ctx context.Context
|
|
env environment.Environment // environment
|
|
progress io.Writer // writer to direct progress to
|
|
path string // path to send files to
|
|
manifest chan<- string // channel the manifest is sent to
|
|
}
|
|
|
|
func (bc *stagingContext) sendPath(path string) {
|
|
// resolve the path, or bail out!
|
|
// TODO: Use the relative path here!
|
|
dst, err := bc.resolve(path)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
logging.Progress(bc.progress, bc.ctx, dst)
|
|
bc.manifest <- dst
|
|
}
|
|
|
|
func (bc *stagingContext) Progress() io.Writer {
|
|
return bc.progress
|
|
}
|
|
|
|
var errResolveAbsolute = errors.New("resolve: path must be relative")
|
|
|
|
func (bc *stagingContext) resolve(path string) (dest string, err error) {
|
|
if path == "" {
|
|
return bc.path, nil
|
|
}
|
|
if filepath.IsAbs(path) {
|
|
return "", errResolveAbsolute
|
|
}
|
|
return filepath.Join(bc.path, path), nil
|
|
}
|
|
|
|
func (sc *stagingContext) AddDirectory(path string, op func(context.Context) error) error {
|
|
// check if we are already done
|
|
if err, ok := sc.ctxdone(); ok {
|
|
return err
|
|
}
|
|
|
|
// resolve the path!
|
|
dst, err := sc.resolve(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// run the make directory
|
|
if err := fsx.Mkdir(dst, fsx.DefaultDirPerm); err != nil {
|
|
return err
|
|
}
|
|
|
|
// tell the files that we are creating it!
|
|
sc.sendPath(path)
|
|
|
|
// and run the files!
|
|
return op(sc.ctx)
|
|
}
|
|
|
|
func (sc *stagingContext) CopyFile(dst, src string) error {
|
|
if err, ok := sc.ctxdone(); ok {
|
|
return err
|
|
}
|
|
|
|
dstPath, err := sc.resolve(dst)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
sc.sendPath(dst)
|
|
return fsx.CopyFile(sc.ctx, dstPath, src)
|
|
}
|
|
|
|
func (sc *stagingContext) CopyDirectory(dst, src string) error {
|
|
if err, ok := sc.ctxdone(); ok {
|
|
return err
|
|
}
|
|
|
|
dstPath, err := sc.resolve(dst)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return fsx.CopyDirectory(sc.ctx, dstPath, src, func(dst, src string) {
|
|
sc.sendPath(dst)
|
|
})
|
|
}
|
|
|
|
func (sc *stagingContext) AddFile(path string, op func(ctx context.Context, file io.Writer) error) error {
|
|
// check if we're already done
|
|
if err, ok := sc.ctxdone(); ok {
|
|
return err
|
|
}
|
|
|
|
// resolve the path!
|
|
dst, err := sc.resolve(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// create the file
|
|
file, err := fsx.Create(dst, fsx.DefaultFilePerm)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer file.Close()
|
|
|
|
// tell them that we are creating it!
|
|
sc.sendPath(path)
|
|
|
|
// and do whatever they wanted to do
|
|
return op(sc.ctx, file)
|
|
}
|
|
|
|
func (sc *stagingContext) ctxdone() (err error, done bool) {
|
|
err = sc.ctx.Err()
|
|
done = (err != nil)
|
|
return
|
|
}
|