From dca4fbeb4df614fe9143035a396f989ccd7b41b9 Mon Sep 17 00:00:00 2001 From: "Florian Rusch (cluetec GmbH)" Date: Thu, 9 Nov 2023 13:56:12 +0100 Subject: [PATCH] add changes --- cmd/backup.go | 40 +++++++++++++++---- cmd/root.go | 8 ++-- config.yaml | 4 +- internal/destination/destination.go | 21 ++++++++-- internal/destination/filesystem/config.go | 2 +- internal/destination/filesystem/writer.go | 48 +++++++++++++++++++++-- internal/source/filesystem/config.go | 2 +- internal/source/filesystem/reader.go | 36 +++++++++++++++-- internal/source/source.go | 21 ++++++++-- internal_open_discussion.md | 12 +++--- 10 files changed, 159 insertions(+), 35 deletions(-) diff --git a/cmd/backup.go b/cmd/backup.go index f1edd22..191ed63 100644 --- a/cmd/backup.go +++ b/cmd/backup.go @@ -22,8 +22,8 @@ import ( "github.com/cluetec/lifeboat/internal/logging" "github.com/cluetec/lifeboat/internal/source" "github.com/spf13/cobra" + "io" "log/slog" - "os" ) // backupCmd represents the backup command @@ -31,11 +31,11 @@ var backupCmd = &cobra.Command{ Use: "backup", Short: "Execute the backup.", Long: "Execute the backup. Used config can be overridden by providing arguments.", - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { c, err := config.New() if err != nil { slog.Error("error while initializing config", "error", err) - os.Exit(1) + return err } logging.InitSlog(c.GetLogLevel()) @@ -45,11 +45,37 @@ var backupCmd = &cobra.Command{ slog.Debug("start of backup command") - source.Prepare(c.Source) - destination.Prepare(c.Destination) + s, err := source.New(c.Source) + if err != nil { + slog.Error("error while initializing source", "error", err) + return err + } + defer func() { + err := s.Reader.Close() + if err != nil { + slog.Error("error while closing source reader", "error", err) + } + }() - slog.Info("TODO: Do backup") + d, err := destination.New(c.Destination) + if err != nil { + slog.Error("error while initializing destination", "error", err) + return err + } + defer func() { + err := d.Writer.Close() + if err != nil { + slog.Error("error while closing destination writer", "error", err) + } + }() + + n, err := io.Copy(d.Writer, s.Reader) + if err != nil { + slog.Error("error while doing the backup", "error", err) + return err + } - slog.Debug("end of backup command") + slog.Info("Backup successful", "writtenBytes", n) + return nil }, } diff --git a/cmd/root.go b/cmd/root.go index 2f97527..bb0c4a1 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -24,9 +24,11 @@ import ( // rootCmd represents the base command when called without any subcommands var rootCmd = &cobra.Command{ - Use: "lb", - Short: "Perform backups from any source to any destination.", - Long: "Lifeboat is a general purpose backup tool which supports backups for arbitrary sources and destinations.", + Use: "lb", + Short: "Perform backups from any source to any destination.", + Long: "Lifeboat is a general purpose backup tool which supports backups for arbitrary sources and destinations.", + SilenceUsage: true, + SilenceErrors: true, } // Execute adds all child commands to the root command and sets flags appropriately. diff --git a/config.yaml b/config.yaml index 6d5f15f..c7b23da 100644 --- a/config.yaml +++ b/config.yaml @@ -1,9 +1,9 @@ source: type: filesystem - path: source + path: /tmp/source.txt destination: type: filesystem - path: destination + path: /tmp/destination.txt logLevel: debug diff --git a/internal/destination/destination.go b/internal/destination/destination.go index 784802a..74f9060 100644 --- a/internal/destination/destination.go +++ b/internal/destination/destination.go @@ -19,18 +19,31 @@ package destination import ( "github.com/cluetec/lifeboat/internal/config" "github.com/cluetec/lifeboat/internal/destination/filesystem" + "io" "log/slog" - "os" ) -func Prepare(c config.DestinationConfig) { +type Destination struct { + Writer io.WriteCloser +} + +func New(c config.DestinationConfig) (*Destination, error) { + d := Destination{} if c.Type == filesystem.Type { - filesystemConfig, err := filesystem.New(c.ResourceConfig) + filesystemConfig, err := filesystem.NewConfig(c.ResourceConfig) if err != nil { slog.Error("error while initializing filesystem destination config", "error", err) - os.Exit(1) + return nil, err } slog.Debug("filesystem destination config loaded", "config", filesystemConfig) + + d.Writer, err = filesystem.NewWriter(filesystemConfig) + if err != nil { + slog.Error("error while initializing writer interface for filesystem destination", "error", err) + return nil, err + } } + + return &d, nil } diff --git a/internal/destination/filesystem/config.go b/internal/destination/filesystem/config.go index a495943..8df1550 100644 --- a/internal/destination/filesystem/config.go +++ b/internal/destination/filesystem/config.go @@ -27,7 +27,7 @@ type Config struct { Path string } -func New(c map[string]any) (*Config, error) { +func NewConfig(c map[string]any) (*Config, error) { var filesystemConfig Config err := mapstructure.Decode(c, &filesystemConfig) diff --git a/internal/destination/filesystem/writer.go b/internal/destination/filesystem/writer.go index 60cac79..17607a1 100644 --- a/internal/destination/filesystem/writer.go +++ b/internal/destination/filesystem/writer.go @@ -16,10 +16,50 @@ package filesystem -type Storeman struct { - Config Config +import ( + "errors" + "log/slog" + "os" +) + +type Writer struct { + file *os.File } -func (r *Storeman) StoreBackup() []byte { - return []byte{} +func NewWriter(c *Config) (*Writer, error) { + w := Writer{} + + // Check if destination file already exists + _, err := os.Stat(c.Path) + if err == nil { + return nil, errors.New("destination file already exists") + } else if !errors.Is(err, os.ErrNotExist) { + slog.Error("error while checking if destination file already exists", "error", err) + return nil, err + } + + // Create file + f, err := os.Create(c.Path) + if err != nil { + return nil, err + } + + w.file = f + return &w, nil +} + +func (w *Writer) Write(b []byte) (int, error) { + return w.file.Write(b) +} + +func (w *Writer) Close() error { + slog.Debug("closing filesystem writer") + + if w.file != nil { + if err := w.file.Close(); err != nil { + return err + } + w.file = nil + } + return nil } diff --git a/internal/source/filesystem/config.go b/internal/source/filesystem/config.go index 3013f79..063ddaa 100644 --- a/internal/source/filesystem/config.go +++ b/internal/source/filesystem/config.go @@ -27,7 +27,7 @@ type Config struct { Path string } -func New(c map[string]any) (*Config, error) { +func NewConfig(c map[string]any) (*Config, error) { var filesystemConfig Config err := mapstructure.Decode(c, &filesystemConfig) diff --git a/internal/source/filesystem/reader.go b/internal/source/filesystem/reader.go index 2a9cd2e..bfce89d 100644 --- a/internal/source/filesystem/reader.go +++ b/internal/source/filesystem/reader.go @@ -16,10 +16,40 @@ package filesystem +import ( + "log/slog" + "os" +) + type Reader struct { - Config *Config + file *os.File } -func (r *Reader) ProcessBackup() []byte { - return []byte{} +func NewReader(c *Config) (*Reader, error) { + r := Reader{} + + f, err := os.Open(c.Path) + if err != nil { + return nil, err + } + + r.file = f + + return &r, nil +} + +func (r *Reader) Read(b []byte) (int, error) { + return r.file.Read(b) +} + +func (r *Reader) Close() error { + slog.Debug("closing filesystem reader") + + if r.file != nil { + if err := r.file.Close(); err != nil { + return err + } + r.file = nil + } + return nil } diff --git a/internal/source/source.go b/internal/source/source.go index ff56043..633cecf 100644 --- a/internal/source/source.go +++ b/internal/source/source.go @@ -19,18 +19,31 @@ package source import ( "github.com/cluetec/lifeboat/internal/config" "github.com/cluetec/lifeboat/internal/source/filesystem" + "io" "log/slog" - "os" ) -func Prepare(c config.SourceConfig) { +type Source struct { + Reader io.ReadCloser +} + +func New(c config.SourceConfig) (*Source, error) { + s := Source{} if c.Type == filesystem.Type { - filesystemConfig, err := filesystem.New(c.ResourceConfig) + filesystemConfig, err := filesystem.NewConfig(c.ResourceConfig) if err != nil { slog.Error("error while initializing filesystem source config", "error", err) - os.Exit(1) + return nil, err } slog.Debug("filesystem source config loaded", "config", filesystemConfig) + + s.Reader, err = filesystem.NewReader(filesystemConfig) + if err != nil { + slog.Error("error while initializing reader interface for filesystem source", "error", err) + return nil, err + } } + + return &s, nil } diff --git a/internal_open_discussion.md b/internal_open_discussion.md index 46b4fc5..3cc60dd 100644 --- a/internal_open_discussion.md +++ b/internal_open_discussion.md @@ -2,13 +2,13 @@ ## Planned features -- [ ] Step: 1 +- [x] Step: 1 - [x] Init cobra & viper setup - - [ ] Dynamic config loading with viper (`type`) -- [ ] Step: 2 - - [ ] Init slog setup -- [ ] Implement Filesystem to Filesystem backup - - [ ] Think about the interfaces + - [x] Dynamic config loading with viper (`type`) +- [x] Step: 2 + - [x] Init slog setup +- [x] Implement Filesystem to Filesystem backup + - [x] Think about the interfaces - [ ] Implement Vault to Filesystem backup For later: