Progress on sub-commands, added unmarshalling
This commit is contained in:
parent
ae87ccb4b5
commit
15a7ca6c3d
28
cmd/backup.go
Normal file
28
cmd/backup.go
Normal file
@ -0,0 +1,28 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"git.andrewnw.xyz/CyberShell/backy/pkg/backy"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
backupCmd = &cobra.Command{
|
||||
Use: "backup [--commands==list1,list2]",
|
||||
Short: "Runs commands defined in config file.",
|
||||
Long: `Backup executes commands defined in config file,
|
||||
use the -cmds flag to execute the specified commands.`,
|
||||
}
|
||||
)
|
||||
var CmdList *[]string
|
||||
|
||||
func init() {
|
||||
cobra.OnInitialize(initConfig)
|
||||
|
||||
backupCmd.Flags().StringSliceVarP(CmdList, "commands", "cmds", nil, "Accepts a comma-separated list of command lists to execute.")
|
||||
}
|
||||
|
||||
func backup() {
|
||||
backyConfig := backy.NewOpts(cfgFile)
|
||||
backyConfig.GetConfig()
|
||||
}
|
@ -32,7 +32,7 @@ func init() {
|
||||
|
||||
rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "", "config file to read from")
|
||||
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Sets verbose level")
|
||||
rootCmd.PersistentFlags().Bool("viper", true, "use Viper for configuration")
|
||||
|
||||
}
|
||||
|
||||
func initConfig() {
|
||||
|
@ -7,15 +7,19 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/spf13/viper"
|
||||
"gopkg.in/natefinch/lumberjack.v2"
|
||||
)
|
||||
|
||||
// Host defines a host to which to connect
|
||||
// If not provided, the values will be looked up in the default ssh config files
|
||||
type Host struct {
|
||||
ConfigFilePath string
|
||||
UseConfigFile bool
|
||||
Empty bool
|
||||
Host string
|
||||
HostName string
|
||||
@ -26,44 +30,55 @@ type Host struct {
|
||||
}
|
||||
|
||||
type Command struct {
|
||||
Remote bool
|
||||
Remote bool `yaml:"remote,omitempty"`
|
||||
|
||||
// command to run
|
||||
Cmd string
|
||||
Cmd string `yaml:"cmd"`
|
||||
|
||||
// host on which to run cmd
|
||||
Host string
|
||||
Host *string `yaml:"host,omitempty"`
|
||||
|
||||
/*
|
||||
Shell specifies which shell to run the command in, if any
|
||||
Not applicable when host is defined
|
||||
Shell specifies which shell to run the command in, if any.
|
||||
Not applicable when host is defined.
|
||||
*/
|
||||
Shell string
|
||||
Shell string `yaml:"shell,omitempty"`
|
||||
|
||||
RemoteHost Host
|
||||
RemoteHost Host `yaml:"-"`
|
||||
|
||||
// cmdArgs is an array that holds the arguments to cmd
|
||||
CmdArgs []string
|
||||
CmdArgs []string `yaml:"cmdArgs,omitempty"`
|
||||
|
||||
/*
|
||||
Dir specifies a directory in which to run the command.
|
||||
Ignored if Host is set.
|
||||
*/
|
||||
Dir *string `yaml:"dir,omitempty"`
|
||||
}
|
||||
|
||||
type BackyGlobalOpts struct {
|
||||
}
|
||||
|
||||
type BackyConfigFile struct {
|
||||
/*
|
||||
Cmds holds the commands for a list
|
||||
key is the name of the command
|
||||
Cmds holds the commands for a list.
|
||||
Key is the name of the command,
|
||||
*/
|
||||
Cmds map[string]Command
|
||||
Cmds map[string]Command `yaml:"commands"`
|
||||
|
||||
/*
|
||||
CmdLists holds the lists of commands to be run in order
|
||||
key is the command list name
|
||||
CmdLists holds the lists of commands to be run in order.
|
||||
Key is the command list name.
|
||||
*/
|
||||
CmdLists map[string][]string
|
||||
CmdLists map[string][]string `yaml:"cmd-lists"`
|
||||
|
||||
/*
|
||||
Hosts holds the Host config
|
||||
key is the host
|
||||
Hosts holds the Host config.
|
||||
key is the host.
|
||||
*/
|
||||
Hosts map[string]Host
|
||||
Hosts map[string]Host `yaml:"hosts"`
|
||||
|
||||
Logger zerolog.Logger
|
||||
}
|
||||
|
||||
// BackupConfig is a configuration struct that is used to define backups
|
||||
@ -79,19 +94,19 @@ type BackupConfig struct {
|
||||
* Runs a backup configuration
|
||||
*/
|
||||
|
||||
func (command Command) RunCmd() {
|
||||
func (command Command) RunCmd(log *zerolog.Logger) {
|
||||
|
||||
var cmdArgsStr string
|
||||
for _, v := range command.CmdArgs {
|
||||
cmdArgsStr += fmt.Sprintf(" %s", v)
|
||||
}
|
||||
|
||||
fmt.Printf("\n\nRunning command: " + command.Cmd + " " + cmdArgsStr + " on host " + command.Host + "...\n\n")
|
||||
if command.Host != "" {
|
||||
fmt.Printf("\n\nRunning command: " + command.Cmd + " " + cmdArgsStr + " on host " + *command.Host + "...\n\n")
|
||||
if command.Host != nil {
|
||||
|
||||
command.RemoteHost.Host = command.Host
|
||||
command.RemoteHost.Host = *command.Host
|
||||
command.RemoteHost.Port = 22
|
||||
sshc, err := command.RemoteHost.ConnectToSSHHost()
|
||||
sshc, err := command.RemoteHost.ConnectToSSHHost(log)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("ssh dial: %w", err))
|
||||
}
|
||||
@ -123,6 +138,9 @@ func (command Command) RunCmd() {
|
||||
if command.Shell != "" {
|
||||
cmdArgsStr = fmt.Sprintf("%s %s", command.Cmd, cmdArgsStr)
|
||||
localCMD := exec.Command(command.Shell, "-c", cmdArgsStr)
|
||||
if command.Dir != nil {
|
||||
localCMD.Dir = *command.Dir
|
||||
}
|
||||
|
||||
var stdoutBuf, stderrBuf bytes.Buffer
|
||||
localCMD.Stdout = io.MultiWriter(os.Stdout, &stdoutBuf)
|
||||
@ -137,7 +155,9 @@ func (command Command) RunCmd() {
|
||||
return
|
||||
}
|
||||
localCMD := exec.Command(command.Cmd, command.CmdArgs...)
|
||||
|
||||
if command.Dir != nil {
|
||||
localCMD.Dir = *command.Dir
|
||||
}
|
||||
var stdoutBuf, stderrBuf bytes.Buffer
|
||||
localCMD.Stdout = io.MultiWriter(os.Stdout, &stdoutBuf)
|
||||
localCMD.Stderr = io.MultiWriter(os.Stderr, &stderrBuf)
|
||||
@ -154,17 +174,49 @@ func (config *BackyConfigFile) RunBackyConfig() {
|
||||
for _, list := range config.CmdLists {
|
||||
for _, cmd := range list {
|
||||
cmdToRun := config.Cmds[cmd]
|
||||
cmdToRun.RunCmd()
|
||||
cmdToRun.RunCmd(&config.Logger)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type BackyConfigOpts struct {
|
||||
// Holds config file
|
||||
ConfigFile *BackyConfigFile
|
||||
// Holds config file
|
||||
ConfigFilePath string
|
||||
|
||||
// Global log level
|
||||
BackyLogLvl *string
|
||||
}
|
||||
|
||||
type BackyOptionFunc func(*BackyConfigOpts)
|
||||
|
||||
func (c *BackyConfigOpts) LogLvl(level string) BackyOptionFunc {
|
||||
return func(bco *BackyConfigOpts) {
|
||||
c.BackyLogLvl = &level
|
||||
}
|
||||
}
|
||||
func (c *BackyConfigOpts) GetConfig() {
|
||||
c.ConfigFile = ReadAndParseConfigFile(c.ConfigFilePath)
|
||||
}
|
||||
|
||||
func New() BackupConfig {
|
||||
return BackupConfig{}
|
||||
}
|
||||
|
||||
// NewConfig initializes new config that holds information
|
||||
// from the config file
|
||||
func NewOpts(configFilePath string, opts ...BackyOptionFunc) *BackyConfigOpts {
|
||||
b := &BackyConfigOpts{}
|
||||
b.ConfigFilePath = configFilePath
|
||||
for _, opt := range opts {
|
||||
opt(b)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
/*
|
||||
* NewConfig initializes new config that holds information
|
||||
* from the config file
|
||||
*/
|
||||
func NewConfig() *BackyConfigFile {
|
||||
return &BackyConfigFile{
|
||||
Cmds: make(map[string]Command),
|
||||
@ -173,21 +225,89 @@ func NewConfig() *BackyConfigFile {
|
||||
}
|
||||
}
|
||||
|
||||
func ReadAndParseConfigFile() *BackyConfigFile {
|
||||
func ReadAndParseConfigFile(configFile string) *BackyConfigFile {
|
||||
|
||||
backyConfigFile := NewConfig()
|
||||
|
||||
backyViper := viper.New()
|
||||
|
||||
if configFile != "" {
|
||||
backyViper.SetConfigFile(configFile)
|
||||
} else {
|
||||
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))
|
||||
panic(fmt.Errorf("fatal error finding config file: %w", err))
|
||||
}
|
||||
|
||||
backyLoggingOpts := backyViper.Sub("logging")
|
||||
verbose := backyLoggingOpts.GetBool("verbose")
|
||||
|
||||
logFile := backyLoggingOpts.GetString("file")
|
||||
if verbose {
|
||||
zerolog.Level.String(zerolog.DebugLevel)
|
||||
}
|
||||
output := zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.RFC1123}
|
||||
output.FormatLevel = func(i interface{}) string {
|
||||
return strings.ToUpper(fmt.Sprintf("| %-6s|", i))
|
||||
}
|
||||
output.FormatMessage = func(i interface{}) string {
|
||||
return fmt.Sprintf("%s", i)
|
||||
}
|
||||
output.FormatFieldName = func(i interface{}) string {
|
||||
return fmt.Sprintf("%s: ", i)
|
||||
}
|
||||
output.FormatFieldValue = func(i interface{}) string {
|
||||
return strings.ToUpper(fmt.Sprintf("%s", i))
|
||||
}
|
||||
|
||||
fileLogger := &lumberjack.Logger{
|
||||
MaxSize: 500, // megabytes
|
||||
MaxBackups: 3,
|
||||
MaxAge: 28, //days
|
||||
Compress: true, // disabled by default
|
||||
}
|
||||
if strings.Trim(logFile, " ") != "" {
|
||||
fileLogger.Filename = logFile
|
||||
} else {
|
||||
fileLogger.Filename = "./backy.log"
|
||||
}
|
||||
|
||||
// UNIX Time is faster and smaller than most timestamps
|
||||
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
|
||||
// zerolog.TimeFieldFormat = time.RFC1123
|
||||
writers := zerolog.MultiLevelWriter(os.Stdout, fileLogger)
|
||||
log := zerolog.New(writers).With().Timestamp().Logger()
|
||||
|
||||
backyConfigFile.Logger = log
|
||||
|
||||
commandsMap := backyViper.GetStringMapString("commands")
|
||||
commandsMapViper := backyViper.Sub("commands")
|
||||
unmarshalErr := commandsMapViper.Unmarshal(&backyConfigFile.Cmds)
|
||||
if unmarshalErr != nil {
|
||||
panic(fmt.Errorf("error unmarshalling cmds struct: %w", unmarshalErr))
|
||||
} else {
|
||||
for cmdName, cmdConf := range backyConfigFile.Cmds {
|
||||
fmt.Printf("\nCommand Name: %s\n", cmdName)
|
||||
fmt.Printf("Shell: %v\n", cmdConf.Shell)
|
||||
fmt.Printf("Command: %s\n", cmdConf.Cmd)
|
||||
|
||||
if len(cmdConf.CmdArgs) > 0 {
|
||||
fmt.Println("\nCmd Args:")
|
||||
for _, args := range cmdConf.CmdArgs {
|
||||
fmt.Printf("%s\n", args)
|
||||
}
|
||||
}
|
||||
if cmdConf.Host != nil {
|
||||
fmt.Printf("Host: %s\n", *backyConfigFile.Cmds[cmdName].Host)
|
||||
}
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
var cmdNames []string
|
||||
for k := range commandsMap {
|
||||
cmdNames = append(cmdNames, k)
|
||||
@ -196,63 +316,30 @@ func ReadAndParseConfigFile() *BackyConfigFile {
|
||||
|
||||
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
|
||||
log.Debug().Timestamp().Str(cmdName, "host is set").Str("host", host).Send()
|
||||
backupCmdStruct.Host = &host
|
||||
if backyViper.IsSet(getNestedConfig("hosts", host)) {
|
||||
hostconfig := backyViper.Sub(getNestedConfig("hosts", host))
|
||||
hostConfigsMap[host] = hostconfig
|
||||
}
|
||||
} else {
|
||||
println("Host is not set")
|
||||
log.Debug().Timestamp().Str(cmdName, "host is not set").Send()
|
||||
}
|
||||
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
|
||||
|
||||
// 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)
|
||||
|
@ -2,18 +2,15 @@ package backy
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/kevinburke/ssh_config"
|
||||
"github.com/rs/zerolog"
|
||||
"golang.org/x/crypto/ssh"
|
||||
"golang.org/x/crypto/ssh/knownhosts"
|
||||
"gopkg.in/natefinch/lumberjack.v2"
|
||||
)
|
||||
|
||||
type SshConfig struct {
|
||||
@ -53,45 +50,7 @@ func (config SshConfig) GetSSHConfig() (SshConfig, error) {
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func (remoteConfig *Host) ConnectToSSHHost() (*ssh.Client, error) {
|
||||
output := zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.RFC3339}
|
||||
output.FormatLevel = func(i interface{}) string {
|
||||
return strings.ToUpper(fmt.Sprintf("| %-6s|", i))
|
||||
}
|
||||
output.FormatMessage = func(i interface{}) string {
|
||||
return fmt.Sprintf("%s", i)
|
||||
}
|
||||
output.FormatFieldName = func(i interface{}) string {
|
||||
return fmt.Sprintf("%s: ", i)
|
||||
}
|
||||
output.FormatFieldValue = func(i interface{}) string {
|
||||
return strings.ToUpper(fmt.Sprintf("%s", i))
|
||||
}
|
||||
|
||||
fileLogger := &lumberjack.Logger{
|
||||
Filename: "./backy.log",
|
||||
MaxSize: 500, // megabytes
|
||||
MaxBackups: 3,
|
||||
MaxAge: 28, //days
|
||||
Compress: true, // disabled by default
|
||||
}
|
||||
|
||||
// fileOutput := zerolog.ConsoleWriter{Out: fileLogger, TimeFormat: time.RFC3339}
|
||||
// fileOutput.FormatLevel = func(i interface{}) string {
|
||||
// return strings.ToUpper(fmt.Sprintf("| %-6s|", i))
|
||||
// }
|
||||
// fileOutput.FormatMessage = func(i interface{}) string {
|
||||
// return fmt.Sprintf("%s", i)
|
||||
// }
|
||||
// fileOutput.FormatFieldName = func(i interface{}) string {
|
||||
// return fmt.Sprintf("%s: ", i)
|
||||
// }
|
||||
// fileOutput.FormatFieldValue = func(i interface{}) string {
|
||||
// return strings.ToUpper(fmt.Sprintf("%s", i))
|
||||
// }
|
||||
zerolog.TimeFieldFormat = time.RFC1123
|
||||
writers := zerolog.MultiLevelWriter(os.Stdout, fileLogger)
|
||||
log := zerolog.New(writers).With().Timestamp().Logger()
|
||||
func (remoteConfig *Host) ConnectToSSHHost(log *zerolog.Logger) (*ssh.Client, error) {
|
||||
|
||||
var sshClient *ssh.Client
|
||||
var connectErr error
|
||||
|
Loading…
Reference in New Issue
Block a user