From 857b0e0452708fcb5645ace7334d8c3620924dda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Jefe=20Lindst=C3=A4dt?= Date: Fri, 26 Jan 2024 22:05:50 +0100 Subject: [PATCH] feat(backup): recursively create backup directory Handles simple cases, such as the documented one, that easily runs into ENODIR: hostctl backup --path $HOME/hostctl/ ...when you run that for the first time. --- cmd/hostctl/actions/backup_test.go | 31 +++++++++++++++++++ .../actions/integration_runner_test.go | 5 ++- pkg/file/file_backup.go | 12 ++++++- 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/cmd/hostctl/actions/backup_test.go b/cmd/hostctl/actions/backup_test.go index 56c0d24..4a78ced 100644 --- a/cmd/hostctl/actions/backup_test.go +++ b/cmd/hostctl/actions/backup_test.go @@ -37,3 +37,34 @@ func Test_Backup(t *testing.T) { +----------+--------+-----------+------------+ `, r.Hostfile(), backupFile) } + +func Test_Backup_Deep_Target(t *testing.T) { + cmd := NewRootCmd() + + r := NewRunner(t, cmd, "/deep/backup-deep") + defer r.Clean() + + date := time.Now().UTC().Format("20060102") + + backupFile := fmt.Sprintf("%s.%s", r.Hostfile(), date) + defer os.Remove(backupFile) + + r.Run("hostctl backup --path /tmp/deep"). + Containsf(` + [ℹ] Using hosts file: %s + + [✔] Backup '%s' created. + + +----------+--------+-----------+------------+ + | PROFILE | STATUS | IP | DOMAIN | + +----------+--------+-----------+------------+ + | default | on | 127.0.0.1 | localhost | + +----------+--------+-----------+------------+ + | profile1 | on | 127.0.0.1 | first.loc | + | profile1 | on | 127.0.0.1 | second.loc | + +----------+--------+-----------+------------+ + | profile2 | off | 127.0.0.1 | first.loc | + | profile2 | off | 127.0.0.1 | second.loc | + +----------+--------+-----------+------------+ + `, r.Hostfile(), backupFile) +} diff --git a/cmd/hostctl/actions/integration_runner_test.go b/cmd/hostctl/actions/integration_runner_test.go index 7a7d520..fd0fb63 100644 --- a/cmd/hostctl/actions/integration_runner_test.go +++ b/cmd/hostctl/actions/integration_runner_test.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "os" + "path" "strings" "testing" @@ -141,7 +142,9 @@ func (c *cmdRunner) RunE(cmd string, expectedErr error) Runner { } func (c *cmdRunner) TempHostfile(pattern string) *os.File { - file, err := os.CreateTemp("/tmp", fmt.Sprintf("%s_%s_", c.root.Name(), pattern)) + deep_path := path.Dir(pattern) + tmp_path := path.Join("/tmp", deep_path) + file, err := os.CreateTemp(tmp_path, fmt.Sprintf("%s_%s_", c.root.Name(), path.Base(pattern))) as.NoError(c.t, err) _, _ = file.WriteString(` diff --git a/pkg/file/file_backup.go b/pkg/file/file_backup.go index 2e31729..9ac9990 100644 --- a/pkg/file/file_backup.go +++ b/pkg/file/file_backup.go @@ -3,16 +3,26 @@ package file import ( "fmt" "io" + "os" "path" "time" ) -// Backup creates a copy of your hosts file to a new location with the date as extension. +// Backup creates a copy of your hosts file to a new location with the date as +// extension. It recursively creates the target directory if it does not exist. func (f *File) Backup(dst string) (string, error) { _, _ = f.src.Seek(0, io.SeekStart) bkpFilename := fmt.Sprintf("%s.%s", f.src.Name(), time.Now().UTC().Format("20060102")) bkpFilename = path.Join(dst, path.Base(bkpFilename)) + // check if directory exists, else make it + if _, err := f.fs.Stat(dst); os.IsNotExist(err) { + err := f.fs.MkdirAll(dst, os.ModePerm) + if err != nil { + return "", err + } + } + b, err := f.fs.Create(bkpFilename) if err != nil { return "", err