|
|
|
@ -2,12 +2,14 @@ package backy
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bytes"
|
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
|
|
|
|
"io"
|
|
|
|
|
"os"
|
|
|
|
|
"os/exec"
|
|
|
|
|
|
|
|
|
|
"git.andrewnw.xyz/CyberShell/backy/pkg/logging"
|
|
|
|
|
"github.com/rs/zerolog/log"
|
|
|
|
|
"github.com/spf13/viper"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Host defines a host to which to connect
|
|
|
|
@ -16,7 +18,7 @@ type Host struct {
|
|
|
|
|
ConfigFilePath string
|
|
|
|
|
Empty bool
|
|
|
|
|
Host string
|
|
|
|
|
HostName []string
|
|
|
|
|
HostName string
|
|
|
|
|
Port uint16
|
|
|
|
|
PrivateKeyPath string
|
|
|
|
|
PrivateKeyPassword string
|
|
|
|
@ -77,28 +79,24 @@ type BackupConfig struct {
|
|
|
|
|
* Runs a backup configuration
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
func (command Command) RunCmd() logging.Logging {
|
|
|
|
|
func (command Command) RunCmd() {
|
|
|
|
|
|
|
|
|
|
var stdoutBuf, stderrBuf bytes.Buffer
|
|
|
|
|
var err error
|
|
|
|
|
var cmdArgs string
|
|
|
|
|
var cmdArgsStr string
|
|
|
|
|
for _, v := range command.CmdArgs {
|
|
|
|
|
cmdArgs += v
|
|
|
|
|
cmdArgsStr += fmt.Sprintf(" %s", v)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var remoteHost = &command.RemoteHost
|
|
|
|
|
fmt.Printf("\n\nRunning command: " + command.Cmd + " " + cmdArgs + " on host " + command.RemoteHost.Host + "...\n\n")
|
|
|
|
|
if command.Remote {
|
|
|
|
|
fmt.Printf("\n\nRunning command: " + command.Cmd + " " + cmdArgsStr + " on host " + command.Host + "...\n\n")
|
|
|
|
|
if command.Host != "" {
|
|
|
|
|
|
|
|
|
|
remoteHost.Port = 22
|
|
|
|
|
remoteHost.Host = command.RemoteHost.Host
|
|
|
|
|
|
|
|
|
|
sshClient, err := remoteHost.ConnectToSSHHost()
|
|
|
|
|
command.RemoteHost.Host = command.Host
|
|
|
|
|
command.RemoteHost.Port = 22
|
|
|
|
|
sshc, err := command.RemoteHost.ConnectToSSHHost()
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(fmt.Errorf("ssh dial: %w", err))
|
|
|
|
|
}
|
|
|
|
|
defer sshClient.Close()
|
|
|
|
|
s, err := sshClient.NewSession()
|
|
|
|
|
defer sshc.Close()
|
|
|
|
|
s, err := sshc.NewSession()
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(fmt.Errorf("new ssh session: %w", err))
|
|
|
|
|
}
|
|
|
|
@ -109,33 +107,55 @@ func (command Command) RunCmd() logging.Logging {
|
|
|
|
|
cmd += " " + a
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var stdoutBuf, stderrBuf bytes.Buffer
|
|
|
|
|
s.Stdout = io.MultiWriter(os.Stdout, &stdoutBuf)
|
|
|
|
|
s.Stderr = io.MultiWriter(os.Stderr, &stderrBuf)
|
|
|
|
|
err = s.Run(cmd)
|
|
|
|
|
log.Info().Bytes(fmt.Sprintf("%s stdout", command.Cmd), stdoutBuf.Bytes()).Send()
|
|
|
|
|
log.Info().Bytes(fmt.Sprintf("%s stderr", command.Cmd), stderrBuf.Bytes()).Send()
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
return logging.Logging{
|
|
|
|
|
Output: stdoutBuf.String(),
|
|
|
|
|
Err: fmt.Errorf("error running " + cmd + ": " + stderrBuf.String()),
|
|
|
|
|
}
|
|
|
|
|
panic(fmt.Errorf("error when running cmd " + cmd + "\n Error: " + err.Error()))
|
|
|
|
|
}
|
|
|
|
|
// fmt.Printf("Output: %s\n", string(output))
|
|
|
|
|
} else {
|
|
|
|
|
// shell := "/bin/bash"
|
|
|
|
|
var err error
|
|
|
|
|
if command.Shell != "" {
|
|
|
|
|
cmdArgsStr = fmt.Sprintf("%s %s", command.Cmd, cmdArgsStr)
|
|
|
|
|
localCMD := exec.Command(command.Shell, "-c", cmdArgsStr)
|
|
|
|
|
|
|
|
|
|
var stdoutBuf, stderrBuf bytes.Buffer
|
|
|
|
|
localCMD.Stdout = io.MultiWriter(os.Stdout, &stdoutBuf)
|
|
|
|
|
localCMD.Stderr = io.MultiWriter(os.Stderr, &stderrBuf)
|
|
|
|
|
err = localCMD.Run()
|
|
|
|
|
log.Info().Bytes(fmt.Sprintf("%s stdout", command.Cmd), stdoutBuf.Bytes()).Send()
|
|
|
|
|
log.Info().Bytes(fmt.Sprintf("%s stderr", command.Cmd), stderrBuf.Bytes()).Send()
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(fmt.Errorf("error when running cmd: %s: %w", command.Cmd, err))
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
localCMD := exec.Command(command.Cmd, command.CmdArgs...)
|
|
|
|
|
|
|
|
|
|
var stdoutBuf, stderrBuf bytes.Buffer
|
|
|
|
|
localCMD.Stdout = io.MultiWriter(os.Stdout, &stdoutBuf)
|
|
|
|
|
localCMD.Stderr = io.MultiWriter(os.Stderr, &stderrBuf)
|
|
|
|
|
err = localCMD.Run()
|
|
|
|
|
|
|
|
|
|
log.Info().Bytes(fmt.Sprintf("%s stdout", command.Cmd), stdoutBuf.Bytes()).Send()
|
|
|
|
|
log.Info().Bytes(fmt.Sprintf("%s stderr", command.Cmd), stderrBuf.Bytes()).Send()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return logging.Logging{
|
|
|
|
|
Output: stdoutBuf.String(),
|
|
|
|
|
Err: fmt.Errorf(stderrBuf.String()),
|
|
|
|
|
}
|
|
|
|
|
panic(fmt.Errorf("error when running cmd: %s: %w", command.Cmd, err))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return logging.Logging{
|
|
|
|
|
Output: stdoutBuf.String(),
|
|
|
|
|
Err: nil,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (config *BackyConfigFile) RunBackyConfig() {
|
|
|
|
|
for _, list := range config.CmdLists {
|
|
|
|
|
for _, cmd := range list {
|
|
|
|
|
cmdToRun := config.Cmds[cmd]
|
|
|
|
|
cmdToRun.RunCmd()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -152,3 +172,110 @@ func NewConfig() *BackyConfigFile {
|
|
|
|
|
Hosts: make(map[string]Host),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func ReadAndParseConfigFile() *BackyConfigFile {
|
|
|
|
|
|
|
|
|
|
backyConfigFile := NewConfig()
|
|
|
|
|
|
|
|
|
|
backyViper := viper.New()
|
|
|
|
|
backyViper.SetConfigName("backy") // name of config file (without extension)
|
|
|
|
|
backyViper.SetConfigType("yaml") // REQUIRED if the config file does not have the extension in the name
|
|
|
|
|
backyViper.AddConfigPath(".") // optionally look for config in the working directory
|
|
|
|
|
backyViper.AddConfigPath("$HOME/.config/backy") // call multiple times to add many search paths
|
|
|
|
|
err := backyViper.ReadInConfig() // Find and read the config file
|
|
|
|
|
if err != nil { // Handle errors reading the config file
|
|
|
|
|
panic(fmt.Errorf("fatal error config file: %w", err))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
commandsMap := backyViper.GetStringMapString("commands")
|
|
|
|
|
var cmdNames []string
|
|
|
|
|
for k := range commandsMap {
|
|
|
|
|
cmdNames = append(cmdNames, k)
|
|
|
|
|
}
|
|
|
|
|
hostConfigsMap := make(map[string]*viper.Viper)
|
|
|
|
|
|
|
|
|
|
for _, cmdName := range cmdNames {
|
|
|
|
|
var backupCmdStruct Command
|
|
|
|
|
println(cmdName)
|
|
|
|
|
subCmd := backyViper.Sub(getNestedConfig("commands", cmdName))
|
|
|
|
|
|
|
|
|
|
hostSet := subCmd.IsSet("host")
|
|
|
|
|
host := subCmd.GetString("host")
|
|
|
|
|
|
|
|
|
|
cmdSet := subCmd.IsSet("cmd")
|
|
|
|
|
cmd := subCmd.GetString("cmd")
|
|
|
|
|
cmdArgsSet := subCmd.IsSet("cmdargs")
|
|
|
|
|
cmdArgs := subCmd.GetStringSlice("cmdargs")
|
|
|
|
|
shellSet := subCmd.IsSet("shell")
|
|
|
|
|
shell := subCmd.GetString("shell")
|
|
|
|
|
|
|
|
|
|
if hostSet {
|
|
|
|
|
println("Host:")
|
|
|
|
|
println(host)
|
|
|
|
|
backupCmdStruct.Host = host
|
|
|
|
|
if backyViper.IsSet(getNestedConfig("hosts", host)) {
|
|
|
|
|
hostconfig := backyViper.Sub(getNestedConfig("hosts", host))
|
|
|
|
|
hostConfigsMap[host] = hostconfig
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
println("Host is not set")
|
|
|
|
|
}
|
|
|
|
|
if cmdSet {
|
|
|
|
|
println("Cmd:")
|
|
|
|
|
println(cmd)
|
|
|
|
|
backupCmdStruct.Cmd = cmd
|
|
|
|
|
} else {
|
|
|
|
|
println("Cmd is not set")
|
|
|
|
|
}
|
|
|
|
|
if shellSet {
|
|
|
|
|
println("Shell:")
|
|
|
|
|
println(shell)
|
|
|
|
|
backupCmdStruct.Shell = shell
|
|
|
|
|
} else {
|
|
|
|
|
println("Shell is not set")
|
|
|
|
|
}
|
|
|
|
|
if cmdArgsSet {
|
|
|
|
|
println("CmdArgs:")
|
|
|
|
|
for _, arg := range cmdArgs {
|
|
|
|
|
println(arg)
|
|
|
|
|
}
|
|
|
|
|
backupCmdStruct.CmdArgs = cmdArgs
|
|
|
|
|
} else {
|
|
|
|
|
println("CmdArgs are not set")
|
|
|
|
|
}
|
|
|
|
|
backyConfigFile.Cmds[cmdName] = backupCmdStruct
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cmdListCfg := backyViper.GetStringMapStringSlice("cmd-lists")
|
|
|
|
|
var cmdNotFoundSliceErr []error
|
|
|
|
|
for cmdListName, cmdList := range cmdListCfg {
|
|
|
|
|
println("Cmd list: ", cmdListName)
|
|
|
|
|
for _, cmdInList := range cmdList {
|
|
|
|
|
println("Command in list: " + cmdInList)
|
|
|
|
|
_, cmdNameFound := backyConfigFile.Cmds[cmdInList]
|
|
|
|
|
if !backyViper.IsSet(getNestedConfig("commands", cmdInList)) && !cmdNameFound {
|
|
|
|
|
cmdNotFoundStr := fmt.Sprintf("command definition %s is not in config file\n", cmdInList)
|
|
|
|
|
cmdNotFoundErr := errors.New(cmdNotFoundStr)
|
|
|
|
|
cmdNotFoundSliceErr = append(cmdNotFoundSliceErr, cmdNotFoundErr)
|
|
|
|
|
} else {
|
|
|
|
|
backyConfigFile.CmdLists[cmdListName] = append(backyConfigFile.CmdLists[cmdListName], cmdInList)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for _, err := range cmdNotFoundSliceErr {
|
|
|
|
|
if err != nil {
|
|
|
|
|
fmt.Println(err.Error())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return backyConfigFile
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func getNestedConfig(nestedConfig, key string) string {
|
|
|
|
|
return fmt.Sprintf("%s.%s", nestedConfig, key)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func getNestedSSHConfig(key string) string {
|
|
|
|
|
return fmt.Sprintf("hosts.%s.config", key)
|
|
|
|
|
}
|
|
|
|
|