diff --git a/cmd/backup.go b/cmd/backup.go index ab01ec9..64d5e47 100644 --- a/cmd/backup.go +++ b/cmd/backup.go @@ -3,22 +3,67 @@ package cmd import ( "fmt" "github.com/spf13/cobra" + "os" + "path/filepath" + "playbookctl/internal/space_worker" + "playbookctl/internal/types" + "playbookctl/internal/utils/logger" ) func NewCommandBackup() *cobra.Command { - spaceCmd := &cobra.Command{ - Use: "backup", - Short: "выполнить резервное копирование пространства", - RunE: backupRunE, + backupCmd := &cobra.Command{ + Use: "backup [role1, role2, ...]", + Short: "выполнить резервное копирование пространства", + Args: backupCheckArgsE, + ValidArgsFunction: ArgRoleCompletion, + RunE: backupRunE, } - return spaceCmd + backupCmd.Flags().Uint8Var(&flagAnsibleVerbose, "ansible-verbose", 0, "ansible verbose mode (0-3)") + backupCmd.Flags().StringVar(&flagAnsibleBin, "ansible-bin", "/usr/bin/ansible-playbook", "путь к ansible-playbook") + backupCmd.Flags().BoolVar(&flagGenOnly, "generate-only", false, "только сгенерировать исполняемую команду") + backupCmd.Flags().StringVar(&flagTargetHost, "target", "", "имя целевого хоста") + + if err := backupCmd.MarkFlagFilename("ansible-bin"); err != nil { + fmt.Println(err) + os.Exit(1) + } + + return backupCmd +} + +func backupCheckArgsE(_ *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 backupRunE(_ *cobra.Command, args []string) error { - fmt.Println("[dummy] backup") - 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.Backup(flagGenOnly, flagTargetHost, args...) } diff --git a/cmd/install.go b/cmd/install.go index ca2f342..bab01c2 100644 --- a/cmd/install.go +++ b/cmd/install.go @@ -10,13 +10,6 @@ import ( "playbookctl/internal/utils/logger" ) -var ( - flagAnsibleVerbose uint8 - flagAnsibleBin string - flagGenOnly bool - flagTargetHost string -) - func NewCommandInstall() *cobra.Command { installCmd := &cobra.Command{ Use: "install [role1, role2, ...]", diff --git a/cmd/root.go b/cmd/root.go index 28eeed2..072dbe3 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -11,6 +11,13 @@ var ( flagWorkdir string ) +var ( + flagAnsibleVerbose uint8 + flagAnsibleBin string + flagGenOnly bool + flagTargetHost string +) + var rootCmd = &cobra.Command{ Use: "playbookctl", Short: "Ansible Playbook Dedic2 Control", @@ -34,7 +41,7 @@ func init() { rootCmd.AddCommand(NewCommandInstall()) rootCmd.AddCommand(NewCommandHosts()) //mainCmd.AddCommand(cmd.NewCommandOpenports()) - //mainCmd.AddCommand(cmd.NewCommandBackup()) + rootCmd.AddCommand(NewCommandBackup()) //mainCmd.AddCommand(cmd.NewCommandRestore()) } diff --git a/internal/space_worker/space_worker.go b/internal/space_worker/space_worker.go index f60d925..2871e5e 100644 --- a/internal/space_worker/space_worker.go +++ b/internal/space_worker/space_worker.go @@ -12,6 +12,7 @@ import ( "playbookctl/internal/utils/logger" "slices" "strings" + "time" ) var ( @@ -247,7 +248,7 @@ func (app *SpaceWorker) Install(generateOnly bool, targetHost string, roles ...s } } - ansibleArgs, err := app.setupAnsibleArgs(app.AnsibleVerbose, host, roles) + ansibleArgs, err := app.setupInstallAnsibleArgs(app.AnsibleVerbose, host, roles) if err != nil { return err } @@ -270,7 +271,7 @@ func (app *SpaceWorker) Install(generateOnly bool, targetHost string, roles ...s return nil } -func (app *SpaceWorker) setupAnsibleArgs(verbose uint8, targetHost string, roles []string) ([]string, error) { +func (app *SpaceWorker) setupInstallAnsibleArgs(verbose uint8, targetHost string, roles []string) ([]string, error) { var ansibleArgs []string switch verbose { @@ -298,6 +299,101 @@ func (app *SpaceWorker) setupAnsibleArgs(verbose uint8, targetHost string, roles //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +func (app *SpaceWorker) Backup(generateOnly bool, targetHost 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 + } + } + + postBackupFile := fmt.Sprintf("/tmp/%s", time.Now().Format("20060102_150405")) + + ansibleArgs, err := app.setupBackupAnsibleArgs(app.AnsibleVerbose, host, roles, postBackupFile) + 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)) + + if generateOnly { + fmt.Println(strings.Join(command.Args, " ")) + return nil + } + + app.log.Debug(fmt.Sprintf("ansible command line: %s", command.Args)) + _ = command.Run() + + app.log.Info("Download Files") + + command = exec.Command("/bin/bash", "-c", postBackupFile) + command.Dir = app.workDir + command.Stdin = os.Stdin + command.Stdout = os.Stdout + command.Stderr = os.Stderr + _ = command.Run() + + if err = utils.RemoveFile(postBackupFile); err != nil { + return err + } + + command = exec.Command("/bin/bash", "-c", postBackupFile+"_clean") + command.Dir = app.workDir + command.Stdin = os.Stdin + command.Stdout = os.Stdout + command.Stderr = os.Stderr + _ = command.Run() + + if err = utils.RemoveFile(postBackupFile + "_clean"); err != nil { + return err + } + + return nil +} + +func (app *SpaceWorker) setupBackupAnsibleArgs(verbose uint8, targetHost string, roles []string, postBackupFile 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_backup_%s=true", role)) + } + } else { + ansibleArgs = append(ansibleArgs, "--extra-vars", "dd_backup=true") + } + + ansibleArgs = append(ansibleArgs, "--extra-vars", "dd_postbackup=true") + ansibleArgs = append(ansibleArgs, "--extra-vars", fmt.Sprintf("dd_postbackup_file=%s", postBackupFile)) + + 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 af09638..0a40000 100755 Binary files a/target/playbookctl and b/target/playbookctl differ