Compare commits

...

7 Commits

Author SHA1 Message Date
5fe0629b0f update version and docs 2024-11-19 20:17:03 -06:00
7d6acd77b5 update version 2024-11-19 14:25:00 -06:00
9d646297c7 fix: check for nil Command.Hooks in ExecuteHooks [#12] 2024-11-15 10:46:27 -06:00
bf8d261cf3 added timeout to golangci-lint command 2024-11-14 21:18:14 -06:00
686cd0019a Added Changie files 2024-11-14 21:13:40 -06:00
b7b002bd72 Added: Hooks for Commands.[name]: error, success, and final. Closes [#12]
Added Command.generateLogger() method.

Fixed: make command logger be used for errors, not just when running the command.
2024-11-14 21:10:49 -06:00
b8a63f39f5 add working command hooks
Some checks failed
ci/woodpecker/push/go-lint Pipeline failed
2024-11-11 22:44:28 -06:00
13 changed files with 271 additions and 79 deletions

View File

@ -1,3 +1,3 @@
kind: Added kind: Added
body: Lists config can now go in a file. See docs for more information. body: Lists can now go in a file. See docs for more information.
time: 2024-08-28T14:27:30.427754114-05:00 time: 2024-08-28T14:27:30.427754114-05:00

View File

@ -0,0 +1,3 @@
kind: Added
body: Hooks for Commands.[name]. Error, success, and final. [#12]
time: 2024-11-14T21:01:48.823426401-06:00

View File

@ -0,0 +1,3 @@
kind: Fixed
body: make command logger be used for errors, not just when running the command
time: 2024-11-14T21:13:06.404134926-06:00

View File

@ -7,4 +7,4 @@ steps:
release: release:
image: golangci/golangci-lint:v1.53.3 image: golangci/golangci-lint:v1.53.3
commands: commands:
- golangci-lint run -v - golangci-lint run -v --timeout 5m

View File

@ -20,7 +20,7 @@ package cmd
// func config(cmd *cobra.Command, args []string) { // func config(cmd *cobra.Command, args []string) {
// opts := backy.NewOpts(cfgFile, backy.UseCron()) // opts := backy.NewOpts(cfgFile, backy.cronEnabled())
// opts.InitConfig() // opts.InitConfig()
// } // }

View File

@ -17,7 +17,7 @@ var (
func cron(cmd *cobra.Command, args []string) { func cron(cmd *cobra.Command, args []string) {
opts := backy.NewOpts(cfgFile, backy.UseCron()) opts := backy.NewOpts(cfgFile, backy.CronEnabled())
opts.InitConfig() opts.InitConfig()
backy.ReadConfig(opts) backy.ReadConfig(opts)
opts.Cron() opts.Cron()

View File

@ -7,7 +7,7 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
const versionStr = "0.4.0" const versionStr = "0.5.0"
var ( var (
versionCmd = &cobra.Command{ versionCmd = &cobra.Command{

View File

@ -9,6 +9,11 @@ Command lists are for executing commands in sequence and getting notifications f
The top-level object key can be anything you want but not the same as another. The top-level object key can be anything you want but not the same as another.
Lists can go in a separate file. Command lists should be in a separate file if:
1. key 'cmd-lists.file' is found
2. hosts.yml or hosts.yaml is found in the same directory as the backy config file
```yaml ```yaml
test2: test2:
name: test2 name: test2
@ -51,7 +56,7 @@ An array of notification IDs to use on success and failure. Must match any of th
### Name ### Name
Name is optional for logging. If name is not defined, name will be the object's map key. Name is optional. If name is not defined, name will be the object's map key.
### Cron mode ### Cron mode

View File

@ -20,6 +20,13 @@ commands:
# if host is not defined, command will be run locally # if host is not defined, command will be run locally
# The host has to be defined in either the config file or the SSH Config files # The host has to be defined in either the config file or the SSH Config files
host: some-host host: some-host
hooks
error:
- some-other-command-when-failing
success:
- success-command
final:
- final-command
backup-docker-container-script: backup-docker-container-script:
cmd: /path/to/local/script cmd: /path/to/local/script
# script file is input as stdin to SSH # script file is input as stdin to SSH
@ -41,6 +48,7 @@ Values available for this section:
| `host` | If not specified, the command will execute locally. | `string` | no | | `host` | If not specified, the command will execute locally. | `string` | no |
| `scriptEnvFile` | When type is `scriptFile`, the script is appended to this file. | `string` | no | | `scriptEnvFile` | When type is `scriptFile`, the script is appended to this file. | `string` | no |
| `shell` | Only applicable when host is not specified | `string` | no | | `shell` | Only applicable when host is not specified | `string` | no |
| `hooks` | Hooks are used at the end of the individual command. Must be another command. | `[]string` | no |
#### cmd #### cmd
@ -93,9 +101,9 @@ Make sure to escape any shell input.
Path to a file. Path to a file.
When type is `scriptFile`, the script is appended to this file. When type is specified, the script is appended to this file.
This is useful for specifiing environment variables or other things so they don't have to be included in the script. This is useful for specifying environment variables or other things so they don't have to be included in the script.
### type ### type
@ -116,3 +124,23 @@ For now, the variables have to be defined in an `.env` file in the same director
If using it with host specified, the SSH server has to be configured to accept those env variables. If using it with host specified, the SSH server has to be configured to accept those env variables.
If the command is run locally, the OS's environment is added. If the command is run locally, the OS's environment is added.
### hooks
Hooks are run after the command is run.
Errors are run if the command errors, success if it returns no error. Final hooks are run regardless of error condition.
Values for hooks are as follows:
```yaml
command:
hook:
# these commands are defined elsewhere in the file
error:
- errcommand
success:
- successcommand
final:
- donecommand
```

View File

@ -30,6 +30,8 @@ var Sprintf = fmt.Sprintf
// The environment of local commands will be the machine's environment plus any extra // The environment of local commands will be the machine's environment plus any extra
// variables specified in the Env file or Environment. // variables specified in the Env file or Environment.
// Dir can also be specified for local commands. // Dir can also be specified for local commands.
//
// Returns the output as a slice and an error, if any
func (command *Command) RunCmd(cmdCtxLogger zerolog.Logger, opts *ConfigOpts) ([]string, error) { func (command *Command) RunCmd(cmdCtxLogger zerolog.Logger, opts *ConfigOpts) ([]string, error) {
var ( var (
@ -53,9 +55,9 @@ func (command *Command) RunCmd(cmdCtxLogger zerolog.Logger, opts *ConfigOpts) ([
command.Type = strings.TrimSpace(command.Type) command.Type = strings.TrimSpace(command.Type)
if command.Type != "" { if command.Type != "" {
cmdCtxLogger.Info().Str("Command", fmt.Sprintf("Running script %s on host %s", command.Cmd, *command.Host)).Send() cmdCtxLogger.Info().Str("Command", fmt.Sprintf("Running script %s on host %s", command.Name, *command.Host)).Send()
} else { } else {
cmdCtxLogger.Info().Str("Command", fmt.Sprintf("Running command %s %s on host %s", command.Cmd, ArgsStr, *command.Host)).Send() cmdCtxLogger.Info().Str("Command", fmt.Sprintf("Running command %s on host %s", command.Name, *command.Host)).Send()
} }
if command.RemoteHost.SshClient == nil { if command.RemoteHost.SshClient == nil {
@ -165,7 +167,7 @@ func (command *Command) RunCmd(cmdCtxLogger zerolog.Logger, opts *ConfigOpts) ([
outScanner := bufio.NewScanner(&cmdOutBuf) outScanner := bufio.NewScanner(&cmdOutBuf)
for outScanner.Scan() { for outScanner.Scan() {
outMap := make(map[string]interface{}) outMap := make(map[string]interface{})
outMap["cmd"] = cmd outMap["cmd"] = command.Name
outMap["output"] = outScanner.Text() outMap["output"] = outScanner.Text()
if str, ok := outMap["output"].(string); ok { if str, ok := outMap["output"].(string); ok {
outputArr = append(outputArr, str) outputArr = append(outputArr, str)
@ -178,7 +180,7 @@ func (command *Command) RunCmd(cmdCtxLogger zerolog.Logger, opts *ConfigOpts) ([
outScanner := bufio.NewScanner(&cmdOutBuf) outScanner := bufio.NewScanner(&cmdOutBuf)
for outScanner.Scan() { for outScanner.Scan() {
outMap := make(map[string]interface{}) outMap := make(map[string]interface{})
outMap["cmd"] = cmd outMap["cmd"] = command.Name
outMap["output"] = outScanner.Text() outMap["output"] = outScanner.Text()
if str, ok := outMap["output"].(string); ok { if str, ok := outMap["output"].(string); ok {
outputArr = append(outputArr, str) outputArr = append(outputArr, str)
@ -288,7 +290,7 @@ func (command *Command) RunCmd(cmdCtxLogger zerolog.Logger, opts *ConfigOpts) ([
outScanner := bufio.NewScanner(&cmdOutBuf) outScanner := bufio.NewScanner(&cmdOutBuf)
for outScanner.Scan() { for outScanner.Scan() {
outMap := make(map[string]interface{}) outMap := make(map[string]interface{})
outMap["cmd"] = cmd outMap["cmd"] = command.Name
outMap["output"] = outScanner.Text() outMap["output"] = outScanner.Text()
if str, ok := outMap["output"].(string); ok { if str, ok := outMap["output"].(string); ok {
outputArr = append(outputArr, str) outputArr = append(outputArr, str)
@ -297,14 +299,14 @@ func (command *Command) RunCmd(cmdCtxLogger zerolog.Logger, opts *ConfigOpts) ([
} }
if err != nil { if err != nil {
cmdCtxLogger.Error().Err(fmt.Errorf("error when running cmd: %s: %w", command.Cmd, err)).Send() cmdCtxLogger.Error().Err(fmt.Errorf("error when running cmd: %s: %w", command.Name, err)).Send()
return outputArr, err return outputArr, err
} }
} else { } else {
var err error var err error
if command.Shell != "" { if command.Shell != "" {
cmdCtxLogger.Info().Str("Command", fmt.Sprintf("Running command %s %s on local machine in %s", command.Cmd, ArgsStr, command.Shell)).Send() cmdCtxLogger.Info().Str("Command", fmt.Sprintf("Running command %s on local machine in %s", command.Name, command.Shell)).Send()
ArgsStr = fmt.Sprintf("%s %s", command.Cmd, ArgsStr) ArgsStr = fmt.Sprintf("%s %s", command.Cmd, ArgsStr)
@ -330,7 +332,7 @@ func (command *Command) RunCmd(cmdCtxLogger zerolog.Logger, opts *ConfigOpts) ([
for outScanner.Scan() { for outScanner.Scan() {
outMap := make(map[string]interface{}) outMap := make(map[string]interface{})
outMap["cmd"] = command.Cmd outMap["cmd"] = command.Name
outMap["output"] = outScanner.Text() outMap["output"] = outScanner.Text()
if str, ok := outMap["output"].(string); ok { if str, ok := outMap["output"].(string); ok {
outputArr = append(outputArr, str) outputArr = append(outputArr, str)
@ -339,13 +341,13 @@ func (command *Command) RunCmd(cmdCtxLogger zerolog.Logger, opts *ConfigOpts) ([
} }
if err != nil { if err != nil {
cmdCtxLogger.Error().Err(fmt.Errorf("error when running cmd %s: %w", command.Cmd, err)).Send() cmdCtxLogger.Error().Err(fmt.Errorf("error when running cmd %s: %w", command.Name, err)).Send()
return outputArr, err return outputArr, err
} }
return outputArr, nil return outputArr, nil
} }
cmdCtxLogger.Info().Str("Command", fmt.Sprintf("Running command %s %s on local machine", command.Cmd, ArgsStr)).Send() cmdCtxLogger.Info().Str("Command", fmt.Sprintf("Running command %s on local machine", command.Name)).Send()
localCMD := exec.Command(command.Cmd, command.Args...) localCMD := exec.Command(command.Cmd, command.Args...)
@ -379,7 +381,7 @@ func (command *Command) RunCmd(cmdCtxLogger zerolog.Logger, opts *ConfigOpts) ([
cmdCtxLogger.Info().Fields(outMap).Send() cmdCtxLogger.Info().Fields(outMap).Send()
} }
if err != nil { if err != nil {
cmdCtxLogger.Error().Err(fmt.Errorf("error when running cmd %s: %w", command.Cmd, err)).Send() cmdCtxLogger.Error().Err(fmt.Errorf("error when running cmd %s: %w", command.Name, err)).Send()
return outputArr, err return outputArr, err
} }
} }
@ -388,42 +390,35 @@ func (command *Command) RunCmd(cmdCtxLogger zerolog.Logger, opts *ConfigOpts) ([
// cmdListWorker // cmdListWorker
func cmdListWorker(msgTemps *msgTemplates, jobs <-chan *CmdList, results chan<- string, opts *ConfigOpts) { func cmdListWorker(msgTemps *msgTemplates, jobs <-chan *CmdList, results chan<- string, opts *ConfigOpts) {
// iterate over list to run // iterate over list to run
res := CmdListResults{}
for list := range jobs { for list := range jobs {
fieldsMap := make(map[string]interface{}) fieldsMap := make(map[string]interface{})
fieldsMap["list"] = list.Name fieldsMap["list"] = list.Name
var cmdLogger zerolog.Logger
cmdLog := opts.Logger.Info()
var count int // count of how many commands have been executed var count int // count of how many commands have been executed
var cmdsRan []string // store the commands that have been executed var cmdsRan []string // store the commands that have been executed
var outStructArr []outStruct // stores output messages var outStructArr []outStruct // stores output messages
for _, cmd := range list.Order { for _, cmd := range list.Order {
currentCmd := opts.Cmds[cmd].Cmd
fieldsMap["cmd"] = opts.Cmds[cmd].Cmd currentCmd := opts.Cmds[cmd].Name
fieldsMap["cmd"] = opts.Cmds[cmd].Name
cmdToRun := opts.Cmds[cmd] cmdToRun := opts.Cmds[cmd]
cmdLog.Fields(fieldsMap).Send()
cmdLogger := opts.Logger.With(). cmdLogger = cmdToRun.generateLogger(opts)
Str("backy-cmd", cmd).Str("Host", "local machine"). cmdLogger.Info().Fields(fieldsMap).Send()
Logger()
if cmdToRun.Host != nil {
cmdLogger = opts.Logger.With().
Str("backy-cmd", cmd).Str("Host", *cmdToRun.Host).
Logger()
}
outputArr, runOutErr := cmdToRun.RunCmd(cmdLogger, opts) outputArr, runOutErr := cmdToRun.RunCmd(cmdLogger, opts)
if list.NotifyConfig != nil { if list.NotifyConfig != nil {
// check if the command output should be included // check if the command output should be included
if cmdToRun.GetOutput || list.GetOutput { if cmdToRun.GetOutput || list.GetOutput {
outputStruct := outStruct{ outputStruct := outStruct{
CmdName: cmd, CmdName: cmdToRun.Name,
CmdExecuted: currentCmd, CmdExecuted: currentCmd,
Output: outputArr, Output: outputArr,
} }
@ -434,6 +429,7 @@ func cmdListWorker(msgTemps *msgTemplates, jobs <-chan *CmdList, results chan<-
} }
count++ count++
if runOutErr != nil { if runOutErr != nil {
res.ErrCmd = cmd
if list.NotifyConfig != nil { if list.NotifyConfig != nil {
var errMsg bytes.Buffer var errMsg bytes.Buffer
errStruct := make(map[string]interface{}) errStruct := make(map[string]interface{})
@ -451,23 +447,24 @@ func cmdListWorker(msgTemps *msgTemplates, jobs <-chan *CmdList, results chan<-
tmpErr := msgTemps.err.Execute(&errMsg, errStruct) tmpErr := msgTemps.err.Execute(&errMsg, errStruct)
if tmpErr != nil { if tmpErr != nil {
opts.Logger.Err(tmpErr).Send() cmdLogger.Err(tmpErr).Send()
} }
notifySendErr := list.NotifyConfig.Send(context.Background(), fmt.Sprintf("List %s failed", list.Name), errMsg.String()) notifySendErr := list.NotifyConfig.Send(context.Background(), fmt.Sprintf("List %s failed", list.Name), errMsg.String())
if notifySendErr != nil { if notifySendErr != nil {
opts.Logger.Err(notifySendErr).Send() cmdLogger.Err(notifySendErr).Send()
} }
} }
opts.Logger.Err(runOutErr).Send() cmdLogger.Err(runOutErr).Send()
break break
} else { } else {
if count == len(list.Order) {
cmdsRan = append(cmdsRan, cmd) cmdsRan = append(cmdsRan, cmd)
if count == len(list.Order) {
var successMsg bytes.Buffer var successMsg bytes.Buffer
// if notification config is not nil, and NotifyOnSuccess is true or GetOuput is true, // if notification config is not nil, and NotifyOnSuccess is true or GetOuput is true,
@ -483,23 +480,21 @@ func cmdListWorker(msgTemps *msgTemplates, jobs <-chan *CmdList, results chan<-
tmpErr := msgTemps.success.Execute(&successMsg, successStruct) tmpErr := msgTemps.success.Execute(&successMsg, successStruct)
if tmpErr != nil { if tmpErr != nil {
opts.Logger.Err(tmpErr).Send() cmdLogger.Err(tmpErr).Send()
break break
} }
err := list.NotifyConfig.Send(context.Background(), fmt.Sprintf("List %s succeded", list.Name), successMsg.String()) err := list.NotifyConfig.Send(context.Background(), fmt.Sprintf("List %s succeeded", list.Name), successMsg.String())
if err != nil { if err != nil {
opts.Logger.Err(err).Send() cmdLogger.Err(err).Send()
} }
} }
} else {
cmdsRan = append(cmdsRan, cmd)
} }
} }
} }
results <- "done" results <- res.ErrCmd
} }
} }
@ -535,7 +530,23 @@ func (opts *ConfigOpts) RunListConfig(cron string) {
close(listChan) close(listChan)
for a := 1; a <= configListsLen; a++ { for a := 1; a <= configListsLen; a++ {
<-results l := <-results
opts.Logger.Debug().Msg(l)
if l != "" {
// execute error hooks
opts.Logger.Debug().Msg("hooks are working")
opts.Cmds[l].ExecuteHooks("error", opts)
} else {
// execute success hooks
opts.Cmds[l].ExecuteHooks("success", opts)
}
// execute final hooks
opts.Cmds[l].ExecuteHooks("final", opts)
} }
opts.closeHostConnections() opts.closeHostConnections()
@ -544,13 +555,18 @@ func (opts *ConfigOpts) RunListConfig(cron string) {
func (config *ConfigOpts) ExecuteCmds(opts *ConfigOpts) { func (config *ConfigOpts) ExecuteCmds(opts *ConfigOpts) {
for _, cmd := range opts.executeCmds { for _, cmd := range opts.executeCmds {
cmdToRun := opts.Cmds[cmd] cmdToRun := opts.Cmds[cmd]
cmdLogger := opts.Logger.With(). cmdLogger := cmdToRun.generateLogger(opts)
Str("backy-cmd", cmd).
Logger()
_, runErr := cmdToRun.RunCmd(cmdLogger, opts) _, runErr := cmdToRun.RunCmd(cmdLogger, opts)
if runErr != nil { if runErr != nil {
opts.Logger.Err(runErr).Send() opts.Logger.Err(runErr).Send()
cmdToRun.ExecuteHooks("error", opts)
} else {
cmdToRun.ExecuteHooks("success", opts)
} }
cmdToRun.ExecuteHooks("final", opts)
} }
opts.closeHostConnections() opts.closeHostConnections()
@ -593,3 +609,50 @@ func (c *ConfigOpts) closeHostConnections() {
} }
} }
} }
func (cmd *Command) ExecuteHooks(hookType string, opts *ConfigOpts) {
if cmd.Hooks == nil {
return
}
switch hookType {
case "error":
for _, v := range cmd.Hooks.Error {
errCmd := opts.Cmds[v]
cmdLogger := opts.Logger.With().
Str("backy-cmd", v).
Logger()
errCmd.RunCmd(cmdLogger, opts)
}
case "success":
for _, v := range cmd.Hooks.Success {
successCmd := opts.Cmds[v]
cmdLogger := opts.Logger.With().
Str("backy-cmd", v).
Logger()
successCmd.RunCmd(cmdLogger, opts)
}
case "final":
for _, v := range cmd.Hooks.Final {
finalCmd := opts.Cmds[v]
cmdLogger := opts.Logger.With().
Str("backy-cmd", v).
Logger()
finalCmd.RunCmd(cmdLogger, opts)
}
}
}
func (cmd *Command) generateLogger(opts *ConfigOpts) zerolog.Logger {
cmdLogger := opts.Logger.With().
Str("backy-cmd", cmd.Name).Str("Host", "local machine").
Logger()
if cmd.Host != nil {
cmdLogger = opts.Logger.With().
Str("backy-cmd", cmd.Name).Str("Host", *cmd.Host).
Logger()
}
return cmdLogger
}

View File

@ -245,6 +245,7 @@ func ReadConfig(opts *ConfigOpts) *ConfigOpts {
cmdListFilePath := path.Clean(opts.CmdListFile) cmdListFilePath := path.Clean(opts.CmdListFile)
// if path is not absolute, check config directory
if !strings.HasPrefix(cmdListFilePath, "/") { if !strings.HasPrefix(cmdListFilePath, "/") {
opts.CmdListFile = path.Join(backyConfigFileDir, cmdListFilePath) opts.CmdListFile = path.Join(backyConfigFileDir, cmdListFilePath)
} }
@ -259,7 +260,7 @@ func ReadConfig(opts *ConfigOpts) *ConfigOpts {
logging.ExitWithMSG(fmt.Sprintf("error loading config: %v", err), 1, &opts.Logger) logging.ExitWithMSG(fmt.Sprintf("error loading config: %v", err), 1, &opts.Logger)
} }
log.Info().Str("lists config file", opts.CmdListFile).Send() log.Info().Str("using lists config file", opts.CmdListFile).Send()
} }
@ -269,7 +270,7 @@ func ReadConfig(opts *ConfigOpts) *ConfigOpts {
var cmdNotFoundSliceErr []error var cmdNotFoundSliceErr []error
for cmdListName, cmdList := range opts.CmdConfigLists { for cmdListName, cmdList := range opts.CmdConfigLists {
if opts.useCron { if opts.cronEnabled {
cron := strings.TrimSpace(cmdList.Cron) cron := strings.TrimSpace(cmdList.Cron)
if cron == "" { if cron == "" {
delete(opts.CmdConfigLists, cmdListName) delete(opts.CmdConfigLists, cmdListName)
@ -291,14 +292,13 @@ func ReadConfig(opts *ConfigOpts) *ConfigOpts {
cmdNotFoundErrorLog.Errs("commands not found", cmdNotFoundSliceErr).Send() cmdNotFoundErrorLog.Errs("commands not found", cmdNotFoundSliceErr).Send()
} }
if opts.useCron && (len(opts.CmdConfigLists) == 0) { if opts.cronEnabled && (len(opts.CmdConfigLists) == 0) {
logging.ExitWithMSG("No cron fields detected in any command lists", 1, nil) logging.ExitWithMSG("No cron fields detected in any command lists", 1, nil)
} }
for c := range opts.Cmds { // process commands
if opts.executeCmds != nil && !contains(opts.executeCmds, c) { if err := processCmds(opts); err != nil {
delete(opts.Cmds, c) logging.ExitWithMSG(err.Error(), 1, &opts.Logger)
}
} }
if len(opts.executeLists) > 0 { if len(opts.executeLists) > 0 {
@ -317,23 +317,8 @@ func ReadConfig(opts *ConfigOpts) *ConfigOpts {
} }
} }
for _, cmd := range opts.Cmds {
if cmd.Host != nil {
host, hostFound := opts.Hosts[*cmd.Host]
if hostFound {
cmd.RemoteHost = host
cmd.RemoteHost.Host = host.Host
if host.HostName != "" {
cmd.RemoteHost.HostName = host.HostName
}
} else {
opts.Hosts[*cmd.Host] = &Host{Host: *cmd.Host}
cmd.RemoteHost = &Host{Host: *cmd.Host}
}
}
}
opts.SetupNotify() opts.SetupNotify()
if err := opts.setupVault(); err != nil { if err := opts.setupVault(); err != nil {
log.Err(err).Send() log.Err(err).Send()
} }
@ -460,3 +445,88 @@ func GetVaultKey(str string, opts *ConfigOpts, log zerolog.Logger) string {
} }
return value return value
} }
func processCmds(opts *ConfigOpts) error {
// process commands
for cmdName, cmd := range opts.Cmds {
if cmd.Name == "" {
cmd.Name = cmdName
}
// println("Cmd.Name = " + cmd.Name)
hooks := cmd.Hooks
// resolve hooks
if hooks != nil {
processHookSuccess := processHooks(cmd, hooks.Error, opts, "error")
if processHookSuccess != nil {
return processHookSuccess
}
processHookSuccess = processHooks(cmd, hooks.Success, opts, "success")
if processHookSuccess != nil {
return processHookSuccess
}
processHookSuccess = processHooks(cmd, hooks.Final, opts, "final")
if processHookSuccess != nil {
return processHookSuccess
}
}
// resolve hosts
if cmd.Host != nil {
host, hostFound := opts.Hosts[*cmd.Host]
if hostFound {
cmd.RemoteHost = host
cmd.RemoteHost.Host = host.Host
if host.HostName != "" {
cmd.RemoteHost.HostName = host.HostName
}
} else {
opts.Hosts[*cmd.Host] = &Host{Host: *cmd.Host}
cmd.RemoteHost = &Host{Host: *cmd.Host}
}
}
}
return nil
}
// processHooks evaluates if hooks are valid Commands
//
// Takes the following arguments:
//
// 1. a []string of hooks
// 2. a map of Commands as arguments
// 3. a string hookType, must be the hook type
//
// The cmds.hookRef is modified in this function.
//
// Returns the following:
//
// An error, if any, if the command is not found
func processHooks(cmd *Command, hooks []string, opts *ConfigOpts, hookType string) error {
// initialize hook type
var hookCmdFound bool
cmd.hookRefs = map[string]map[string]*Command{}
cmd.hookRefs[hookType] = map[string]*Command{}
for _, hook := range hooks {
var hookCmd *Command
// TODO: match by Command.Name
hookCmd, hookCmdFound = opts.Cmds[hook]
if !hookCmdFound {
return fmt.Errorf("error in command %s hook %s list: command %s not found", cmd.Name, hookType, hook)
}
cmd.hookRefs[hookType][hook] = hookCmd
// Recursive, decide if this is good
// if hookCmd.hookRefs == nil {
// }
// hookRef[hookType][h] = hookCmd
}
return nil
}

View File

@ -43,17 +43,24 @@ type (
} }
Command struct { Command struct {
Name string `yaml:"name,omitempty"`
// command to run // command to run
Cmd string `yaml:"cmd"` Cmd string `yaml:"cmd"`
// Possible values: script, scriptFile // Possible values: script, scriptFile
// If blank, it is regualar command. // If blank, it is regular command.
Type string `yaml:"type"` Type string `yaml:"type,omitempty"`
// host on which to run cmd // host on which to run cmd
Host *string `yaml:"host,omitempty"` Host *string `yaml:"host,omitempty"`
// Hooks are for running commands on certain events
Hooks *Hooks `yaml:"hooks,omitempty"`
// hook refs are internal references of commands for each hook type
hookRefs map[string]map[string]*Command
/* /*
Shell specifies which shell to run the command in, if any. Shell specifies which shell to run the command in, if any.
Not applicable when host is defined. Not applicable when host is defined.
@ -123,7 +130,7 @@ type (
CmdListFile string CmdListFile string
// use command lists using cron // use command lists using cron
useCron bool cronEnabled bool
// Holds commands to execute for the exec command // Holds commands to execute for the exec command
executeCmds []string executeCmds []string
// Holds lists to execute for the backup command // Holds lists to execute for the backup command
@ -188,4 +195,17 @@ type (
Commands []string Commands []string
Hosts []string Hosts []string
} }
Hooks struct {
Error []string `yaml:"error,omitempty"`
Success []string `yaml:"success,omitempty"`
Final []string `yaml:"final,omitempty"`
}
CmdListResults struct {
// name of the list
ListName string
// command that caused the list to fail
ErrCmd string
}
) )

View File

@ -56,10 +56,10 @@ func SetCmdsToSearch(cmds []string) BackyOptionFunc {
} }
} }
// UseCron enables the execution of command lists at specified times // cronEnabled enables the execution of command lists at specified times
func UseCron() BackyOptionFunc { func CronEnabled() BackyOptionFunc {
return func(bco *ConfigOpts) { return func(bco *ConfigOpts) {
bco.useCron = true bco.cronEnabled = true
} }
} }