diff --git a/cmd/restore.go b/cmd/restore.go index 19192c1..d8d06f1 100644 --- a/cmd/restore.go +++ b/cmd/restore.go @@ -3,22 +3,74 @@ package cmd import ( "fmt" "github.com/spf13/cobra" + "os" + "path/filepath" + "playbookctl/internal/space_worker" + "playbookctl/internal/types" + "playbookctl/internal/utils/logger" +) + +var ( + flagRestoreTimestamp string + flagRestoreInventory string ) func NewCommandRestore() *cobra.Command { - spaceCmd := &cobra.Command{ - Use: "restore", - Short: "восстановить резервную копированию пространства", - RunE: restoreRunE, + restoreCmd := &cobra.Command{ + Use: "restore [role1, role2, ...]", + Short: "восстановить резервную копированию пространства", + Args: restoreCheckArgsE, + ValidArgsFunction: ArgRoleCompletion, + RunE: restoreRunE, } - return spaceCmd + restoreCmd.Flags().Uint8Var(&flagAnsibleVerbose, "ansible-verbose", 0, "ansible verbose mode (0-3)") + restoreCmd.Flags().StringVar(&flagAnsibleBin, "ansible-bin", "/usr/bin/ansible-playbook", "путь к ansible-playbook") + restoreCmd.Flags().BoolVar(&flagGenOnly, "generate-only", false, "только сгенерировать исполняемую команду") + restoreCmd.Flags().StringVar(&flagTargetHost, "target", "", "имя целевого хоста") + restoreCmd.Flags().StringVar(&flagRestoreTimestamp, "timestamp", "latest", "выбор времени бекапа") + restoreCmd.Flags().StringVar(&flagRestoreInventory, "inventory", "", "выбор хост бекапа") + + if err := restoreCmd.MarkFlagFilename("ansible-bin"); err != nil { + fmt.Println(err) + os.Exit(1) + } + + return restoreCmd +} + +func restoreCheckArgsE(_ *cobra.Command, args []string) error { + if len(args) == 0 { + return nil + } + + workDir, err := filepath.Abs(flagWorkdir) + if err != nil { + return err + } + + playbook, err := types.ReadPlaybook(workDir) + if err != nil { + return err + } + + for _, roleName := range args { + if !containsInSlice(roleName, playbook.Roles) { + return fmt.Errorf("роли \"%s\" не существует или она не добавлена в playbook.yml", roleName) + } + } + + return nil } func restoreRunE(_ *cobra.Command, args []string) error { - fmt.Println("[dummy] restore") - for _, elm := range args { - fmt.Printf("- %s\n", elm) + workDir, err := filepath.Abs(flagWorkdir) + if err != nil { + return err } - return nil + + spaceWorker := space_worker.NewSpaceWorker(logger.LogVerbose(flagVerbose), workDir) + spaceWorker.AnsibleBin = flagAnsibleBin + spaceWorker.AnsibleVerbose = flagAnsibleVerbose + return spaceWorker.Restore(flagGenOnly, flagTargetHost, flagRestoreTimestamp, flagRestoreInventory, args...) } diff --git a/cmd/root.go b/cmd/root.go index 072dbe3..067c730 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -42,7 +42,7 @@ func init() { rootCmd.AddCommand(NewCommandHosts()) //mainCmd.AddCommand(cmd.NewCommandOpenports()) rootCmd.AddCommand(NewCommandBackup()) - //mainCmd.AddCommand(cmd.NewCommandRestore()) + rootCmd.AddCommand(NewCommandRestore()) } func Execute() { diff --git a/internal/space_worker/space_worker.go b/internal/space_worker/space_worker.go index 2871e5e..0e37bc8 100644 --- a/internal/space_worker/space_worker.go +++ b/internal/space_worker/space_worker.go @@ -394,6 +394,138 @@ func (app *SpaceWorker) setupBackupAnsibleArgs(verbose uint8, targetHost string, //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +func (app *SpaceWorker) Restore(generateOnly bool, targetHost string, timestamp string, inventory string, roles ...string) error { + app.log.Debug(fmt.Sprintf("workDir: %s", app.workDir)) + + var host string + if targetHost != "" { + host = targetHost + } else { + var err error + host, err = app.GetDefaultHost() + if err != nil { + return err + } + } + + preRestoreFile := fmt.Sprintf("/tmp/%s", time.Now().Format("20060102_150405")) + + ansibleArgs, err := app.setupPreRestoreAnsibleArgs(app.AnsibleVerbose, host, roles, preRestoreFile, timestamp, inventory) + if err != nil { + return err + } + + command := exec.Command(app.AnsibleBin, ansibleArgs...) + command.Dir = app.workDir + command.Stdin = os.Stdin + command.Stdout = os.Stdout + command.Stderr = os.Stderr + + app.log.Info(fmt.Sprintf("Target Host: %s", host)) + + app.log.Info("Pre restore Ansible") + _ = command.Run() + + app.log.Info("Send files") + command = exec.Command("/bin/bash", "-c", preRestoreFile) + command.Dir = app.workDir + command.Stdin = os.Stdin + command.Stdout = os.Stdout + command.Stderr = os.Stderr + _ = command.Run() + + app.log.Info("Restore Ansible") + ansibleArgs, err = app.setupRestoreAnsibleArgs(app.AnsibleVerbose, host, roles, preRestoreFile, timestamp, inventory) + if err != nil { + return err + } + + command = exec.Command(app.AnsibleBin, ansibleArgs...) + command.Dir = app.workDir + command.Stdin = os.Stdin + command.Stdout = os.Stdout + command.Stderr = os.Stderr + _ = command.Run() + + app.log.Info("Clean temp files") + command = exec.Command("/bin/bash", "-c", preRestoreFile+"_clean") + command.Dir = app.workDir + command.Stdin = os.Stdin + command.Stdout = os.Stdout + command.Stderr = os.Stderr + _ = command.Run() + + if err = utils.RemoveFile(preRestoreFile); err != nil { + return err + } + if err = utils.RemoveFile(preRestoreFile + "_clean"); err != nil { + return err + } + + return nil +} + +func (app *SpaceWorker) setupPreRestoreAnsibleArgs(verbose uint8, targetHost string, roles []string, preRestoreFile string, timestamp string, inventory string) ([]string, error) { + var ansibleArgs []string + + switch verbose { + case 1: + ansibleArgs = append(ansibleArgs, "-v") + case 2: + ansibleArgs = append(ansibleArgs, "-vv") + case 3: + ansibleArgs = append(ansibleArgs, "-vvv") + } + + ansibleArgs = append(ansibleArgs, "-i", "hosts.yml") + ansibleArgs = append(ansibleArgs, "-l", targetHost) + + if len(roles) > 0 { + for _, role := range roles { + ansibleArgs = append(ansibleArgs, "--extra-vars", fmt.Sprintf("dd_prerestore_%s=true", role)) + } + } else { + ansibleArgs = append(ansibleArgs, "--extra-vars", "dd_prerestore=true") + } + + ansibleArgs = append(ansibleArgs, "--extra-vars", fmt.Sprintf("dd_restore_datetime=%s", timestamp)) + ansibleArgs = append(ansibleArgs, "--extra-vars", fmt.Sprintf("dd_restore_inventory=%s", inventory)) + ansibleArgs = append(ansibleArgs, "--extra-vars", fmt.Sprintf("dd_prerestore_file=%s", preRestoreFile)) + + return append(ansibleArgs, "playbook.yml"), nil +} + +func (app *SpaceWorker) setupRestoreAnsibleArgs(verbose uint8, targetHost string, roles []string, preRestoreFile string, timestamp string, inventory string) ([]string, error) { + var ansibleArgs []string + + switch verbose { + case 1: + ansibleArgs = append(ansibleArgs, "-v") + case 2: + ansibleArgs = append(ansibleArgs, "-vv") + case 3: + ansibleArgs = append(ansibleArgs, "-vvv") + } + + ansibleArgs = append(ansibleArgs, "-i", "hosts.yml") + ansibleArgs = append(ansibleArgs, "-l", targetHost) + + if len(roles) > 0 { + for _, role := range roles { + ansibleArgs = append(ansibleArgs, "--extra-vars", fmt.Sprintf("dd_restore_%s=true", role)) + } + } else { + ansibleArgs = append(ansibleArgs, "--extra-vars", "dd_restore=true") + } + + ansibleArgs = append(ansibleArgs, "--extra-vars", fmt.Sprintf("dd_restore_datetime=%s", timestamp)) + ansibleArgs = append(ansibleArgs, "--extra-vars", fmt.Sprintf("dd_restore_inventory=%s", inventory)) + + return append(ansibleArgs, "playbook.yml"), nil +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + func (app *SpaceWorker) ListHosts() error { app.log.Debug(fmt.Sprintf("workDir: %s", app.workDir)) diff --git a/target/playbookctl b/target/playbookctl index 0a40000..1688d38 100755 Binary files a/target/playbookctl and b/target/playbookctl differ