Implement initial 'wdcli backup_instance' command
This commit performs an initial implementation of the 'backup_instance' command.
This commit is contained in:
parent
a64c02cd78
commit
2a14d93d3c
17 changed files with 437 additions and 135 deletions
|
|
@ -4,6 +4,7 @@ import (
|
|||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
var ErrCopySameFile = errors.New("src and dst must be different files")
|
||||
|
|
@ -39,3 +40,70 @@ func CopyFile(dst, src string) error {
|
|||
_, err = io.Copy(dstFile, srcFile)
|
||||
return err
|
||||
}
|
||||
|
||||
var ErrCopyNoDirectory = errors.New("dst is not a directory")
|
||||
|
||||
// CopyDirectory copies the directory src to dst recursively.
|
||||
// The destination directory must exist, or an error is returned.
|
||||
//
|
||||
// onCopy, when not nil, is called for each file or directory being copied.
|
||||
func CopyDirectory(dst, src string, onCopy func(dst, src string)) error {
|
||||
// TODO: Allow copying in parallel? Maybe with a mutex?
|
||||
|
||||
// sanity checks
|
||||
if src == dst {
|
||||
return ErrCopySameFile
|
||||
}
|
||||
if !IsDirectory(dst) {
|
||||
return ErrCopyNoDirectory
|
||||
}
|
||||
|
||||
// call onCopy for this directory!
|
||||
if onCopy != nil {
|
||||
onCopy(dst, src)
|
||||
}
|
||||
|
||||
// iterate over the entries or bail out
|
||||
entries, err := os.ReadDir(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
name := entry.Name()
|
||||
eDest := filepath.Join(dst, name)
|
||||
eSrc := filepath.Join(src, name)
|
||||
|
||||
// it is not a directory => Use CopyFile
|
||||
if !entry.IsDir() {
|
||||
if onCopy != nil {
|
||||
onCopy(eDest, eSrc)
|
||||
}
|
||||
|
||||
// do the copy!
|
||||
if err := CopyFile(eDest, eSrc); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
// find out the mode of the entry
|
||||
eInfo, err := entry.Info()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// make the target directory
|
||||
if err := os.Mkdir(eDest, eInfo.Mode()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// do the copy!
|
||||
if err := CopyDirectory(eDest, eSrc, onCopy); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ func (ds Stack) Exec(io stream.IOStream, service, executable string, args ...str
|
|||
if io.StdinIsATerminal() {
|
||||
compose = append(compose, "-ti")
|
||||
}
|
||||
compose = append(compose, service)
|
||||
compose = append(compose, executable)
|
||||
compose = append(compose, args...)
|
||||
return ds.compose(io, compose...)
|
||||
|
|
|
|||
69
internal/targz/targz.go
Normal file
69
internal/targz/targz.go
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
package targz
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"compress/gzip"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// Package packages the directory src into dst.
|
||||
// onCopy, when not nil, is called for each file being copied into the archive.
|
||||
func Package(dst, src string, onCopy func(src string)) error {
|
||||
// create the target archive
|
||||
archive, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer archive.Close()
|
||||
|
||||
// create a gzip writer
|
||||
zipHandle := gzip.NewWriter(archive)
|
||||
defer zipHandle.Close()
|
||||
|
||||
// create a tar writer
|
||||
tarHandle := tar.NewWriter(zipHandle)
|
||||
defer tarHandle.Close()
|
||||
|
||||
// and walk through it!
|
||||
return filepath.Walk(src, func(path string, info fs.FileInfo, err error) error {
|
||||
if onCopy != nil {
|
||||
onCopy(path)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// create a file info header!
|
||||
tInfo, err := tar.FileInfoHeader(info, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tInfo.Name = filepath.ToSlash(path)
|
||||
|
||||
// write it!
|
||||
if err := tarHandle.WriteHeader(tInfo); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// a directory => no more writing required
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// open the file
|
||||
handle, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer handle.Close()
|
||||
|
||||
// and copy it into the archive
|
||||
_, err = io.Copy(tarHandle, handle)
|
||||
return err
|
||||
})
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue