Compare commits
13 Commits
b8a63f39f5
...
v0.5.0
Author | SHA1 | Date | |
---|---|---|---|
291a954e9c | |||
e43eecf383 | |||
ea676fe0da | |||
e73bd9ff3b | |||
fd9426181f | |||
c25edc5d78 | |||
aebfbda64e | |||
5fe0629b0f | |||
7d6acd77b5 | |||
9d646297c7 | |||
bf8d261cf3 | |||
686cd0019a | |||
b7b002bd72 |
@ -1,3 +0,0 @@
|
||||
kind: Added
|
||||
body: Lists config can now go in a file. See docs for more information.
|
||||
time: 2024-08-28T14:27:30.427754114-05:00
|
@ -1,3 +0,0 @@
|
||||
kind: Added
|
||||
body: 'new commands.[name].type: script. Works just like scriptFile, but can take arguments.'
|
||||
time: 2024-10-08T22:37:33.664140846-05:00
|
@ -1,3 +0,0 @@
|
||||
kind: Changed
|
||||
body: GetKnownHosts is now a method of Host
|
||||
time: 2024-05-14T19:58:17.516072381-05:00
|
9
.changes/v0.5.0.md
Normal file
9
.changes/v0.5.0.md
Normal file
@ -0,0 +1,9 @@
|
||||
## v0.5.0 - 2024-11-19
|
||||
### Added
|
||||
* Lists can now go in a file. See docs for more information.
|
||||
* commands.[name].type: script now opens `scriptEnvFile`.
|
||||
* Hooks for Commands.[name]. Error, success, and final. [#12]
|
||||
### Changed
|
||||
* GetKnownHosts is now a method of Host
|
||||
### Fixed
|
||||
* make command logger be used for errors, not just when running the command
|
@ -1,3 +1,4 @@
|
||||
version: 2
|
||||
before:
|
||||
hooks:
|
||||
# You may remove this if you don't use go modules.
|
||||
@ -31,9 +32,9 @@ archives:
|
||||
checksum:
|
||||
name_template: 'checksums.txt'
|
||||
snapshot:
|
||||
name_template: "{{ incpatch .Version }}-next"
|
||||
version_template: "{{ incpatch .Version }}-next"
|
||||
changelog:
|
||||
skip: false
|
||||
disable: false
|
||||
|
||||
gitea_urls:
|
||||
api: https://git.andrewnw.xyz/api/v1
|
||||
|
@ -1,5 +1,6 @@
|
||||
# This is an example .goreleaser.yml file with some sensible defaults.
|
||||
# Make sure to check the documentation at https://goreleaser.com
|
||||
version: 2
|
||||
before:
|
||||
hooks:
|
||||
# You may remove this if you don't use go modules.
|
||||
@ -32,7 +33,7 @@ archives:
|
||||
checksum:
|
||||
name_template: 'checksums.txt'
|
||||
snapshot:
|
||||
name_template: "{{ incpatch .Version }}-next"
|
||||
version_template: "{{ incpatch .Version }}-next"
|
||||
changelog:
|
||||
sort: asc
|
||||
filters:
|
||||
|
@ -7,4 +7,4 @@ steps:
|
||||
release:
|
||||
image: golangci/golangci-lint:v1.53.3
|
||||
commands:
|
||||
- golangci-lint run -v
|
||||
- golangci-lint run -v --timeout 5m
|
@ -7,7 +7,7 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
const versionStr = "0.4.0"
|
||||
const versionStr = "0.5.0"
|
||||
|
||||
var (
|
||||
versionCmd = &cobra.Command{
|
||||
|
@ -110,6 +110,7 @@ Usage:
|
||||
backy list [--list=list1,list2,... | -l list1, list2,...] [ -cmd cmd1 cmd2 cmd3...] [flags]
|
||||
|
||||
Flags:
|
||||
-c, --cmds strings Accepts comma-separated names of commands to list.
|
||||
-h, --help help for list
|
||||
-l, --lists strings Accepts comma-separated names of command lists to list.
|
||||
|
||||
|
@ -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.
|
||||
|
||||
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
|
||||
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 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
|
||||
|
||||
|
@ -19,7 +19,14 @@ commands:
|
||||
- down
|
||||
# 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
|
||||
host: some-host
|
||||
host: some-host
|
||||
hooks
|
||||
error:
|
||||
- some-other-command-when-failing
|
||||
success:
|
||||
- success-command
|
||||
final:
|
||||
- final-command
|
||||
backup-docker-container-script:
|
||||
cmd: /path/to/local/script
|
||||
# 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 |
|
||||
| `scriptEnvFile` | When type is `scriptFile`, the script is appended to this file. | `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
|
||||
|
||||
@ -93,9 +101,9 @@ Make sure to escape any shell input.
|
||||
|
||||
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
|
||||
|
||||
@ -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 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
|
||||
```
|
@ -391,12 +391,11 @@ func (command *Command) RunCmd(cmdCtxLogger zerolog.Logger, opts *ConfigOpts) ([
|
||||
// cmdListWorker
|
||||
func cmdListWorker(msgTemps *msgTemplates, jobs <-chan *CmdList, results chan<- string, opts *ConfigOpts) {
|
||||
// iterate over list to run
|
||||
res := CmdListResults{}
|
||||
for list := range jobs {
|
||||
res := CmdListResults{}
|
||||
fieldsMap := make(map[string]interface{})
|
||||
fieldsMap["list"] = list.Name
|
||||
|
||||
cmdLog := opts.Logger.Info()
|
||||
var cmdLogger zerolog.Logger
|
||||
|
||||
var count int // count of how many commands have been executed
|
||||
var cmdsRan []string // store the commands that have been executed
|
||||
@ -408,17 +407,9 @@ func cmdListWorker(msgTemps *msgTemplates, jobs <-chan *CmdList, results chan<-
|
||||
|
||||
fieldsMap["cmd"] = opts.Cmds[cmd].Name
|
||||
cmdToRun := opts.Cmds[cmd]
|
||||
cmdLog.Fields(fieldsMap).Send()
|
||||
|
||||
cmdLogger := opts.Logger.With().
|
||||
Str("backy-cmd", cmd).Str("Host", "local machine").
|
||||
Logger()
|
||||
|
||||
if cmdToRun.Host != nil {
|
||||
cmdLogger = opts.Logger.With().
|
||||
Str("backy-cmd", cmd).Str("Host", *cmdToRun.Host).
|
||||
Logger()
|
||||
}
|
||||
cmdLogger = cmdToRun.generateLogger(opts)
|
||||
cmdLogger.Info().Fields(fieldsMap).Send()
|
||||
|
||||
outputArr, runOutErr := cmdToRun.RunCmd(cmdLogger, opts)
|
||||
|
||||
@ -456,17 +447,17 @@ func cmdListWorker(msgTemps *msgTemplates, jobs <-chan *CmdList, results chan<-
|
||||
tmpErr := msgTemps.err.Execute(&errMsg, errStruct)
|
||||
|
||||
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())
|
||||
|
||||
if notifySendErr != nil {
|
||||
opts.Logger.Err(notifySendErr).Send()
|
||||
cmdLogger.Err(notifySendErr).Send()
|
||||
}
|
||||
}
|
||||
|
||||
opts.Logger.Err(runOutErr).Send()
|
||||
cmdLogger.Err(runOutErr).Send()
|
||||
|
||||
break
|
||||
} else {
|
||||
@ -489,14 +480,14 @@ func cmdListWorker(msgTemps *msgTemplates, jobs <-chan *CmdList, results chan<-
|
||||
tmpErr := msgTemps.success.Execute(&successMsg, successStruct)
|
||||
|
||||
if tmpErr != nil {
|
||||
opts.Logger.Err(tmpErr).Send()
|
||||
cmdLogger.Err(tmpErr).Send()
|
||||
break
|
||||
}
|
||||
|
||||
err := list.NotifyConfig.Send(context.Background(), fmt.Sprintf("List %s succeeded", list.Name), successMsg.String())
|
||||
|
||||
if err != nil {
|
||||
opts.Logger.Err(err).Send()
|
||||
cmdLogger.Err(err).Send()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -538,7 +529,7 @@ func (opts *ConfigOpts) RunListConfig(cron string) {
|
||||
}
|
||||
close(listChan)
|
||||
|
||||
for a := 0; a <= configListsLen; a++ {
|
||||
for a := 1; a <= configListsLen; a++ {
|
||||
l := <-results
|
||||
|
||||
opts.Logger.Debug().Msg(l)
|
||||
@ -546,11 +537,15 @@ func (opts *ConfigOpts) RunListConfig(cron string) {
|
||||
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)
|
||||
|
||||
}
|
||||
|
||||
@ -560,20 +555,18 @@ func (opts *ConfigOpts) RunListConfig(cron string) {
|
||||
func (config *ConfigOpts) ExecuteCmds(opts *ConfigOpts) {
|
||||
for _, cmd := range opts.executeCmds {
|
||||
cmdToRun := opts.Cmds[cmd]
|
||||
cmdLogger := opts.Logger.With().
|
||||
Str("backy-cmd", cmd).
|
||||
Logger()
|
||||
cmdLogger := cmdToRun.generateLogger(opts)
|
||||
_, runErr := cmdToRun.RunCmd(cmdLogger, opts)
|
||||
if runErr != nil {
|
||||
opts.Logger.Err(runErr).Send()
|
||||
|
||||
ExecuteHooks(*cmdToRun, "error", opts)
|
||||
cmdToRun.ExecuteHooks("error", opts)
|
||||
} else {
|
||||
|
||||
ExecuteHooks(*cmdToRun, "success", opts)
|
||||
cmdToRun.ExecuteHooks("success", opts)
|
||||
}
|
||||
|
||||
ExecuteHooks(*cmdToRun, "final", opts)
|
||||
cmdToRun.ExecuteHooks("final", opts)
|
||||
}
|
||||
|
||||
opts.closeHostConnections()
|
||||
@ -617,7 +610,10 @@ func (c *ConfigOpts) closeHostConnections() {
|
||||
}
|
||||
}
|
||||
|
||||
func ExecuteHooks(cmd Command, hookType string, opts *ConfigOpts) {
|
||||
func (cmd *Command) ExecuteHooks(hookType string, opts *ConfigOpts) {
|
||||
if cmd.Hooks == nil {
|
||||
return
|
||||
}
|
||||
switch hookType {
|
||||
case "error":
|
||||
for _, v := range cmd.Hooks.Error {
|
||||
@ -628,5 +624,35 @@ func ExecuteHooks(cmd Command, hookType string, opts *ConfigOpts) {
|
||||
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
|
||||
}
|
||||
|
@ -298,12 +298,14 @@ func ReadConfig(opts *ConfigOpts) *ConfigOpts {
|
||||
|
||||
// process commands
|
||||
if err := processCmds(opts); err != nil {
|
||||
log.Panic().Err(err).Send()
|
||||
logging.ExitWithMSG(err.Error(), 1, &opts.Logger)
|
||||
}
|
||||
|
||||
for l := range opts.CmdConfigLists {
|
||||
if !contains(opts.executeLists, l) {
|
||||
delete(opts.CmdConfigLists, l)
|
||||
if len(opts.executeLists) > 0 {
|
||||
for l := range opts.CmdConfigLists {
|
||||
if !contains(opts.executeLists, l) {
|
||||
delete(opts.CmdConfigLists, l)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -447,9 +449,6 @@ func GetVaultKey(str string, opts *ConfigOpts, log zerolog.Logger) string {
|
||||
func processCmds(opts *ConfigOpts) error {
|
||||
// process commands
|
||||
for cmdName, cmd := range opts.Cmds {
|
||||
cmd.hookRefs = map[string]map[string]*Command{}
|
||||
cmd.hookRefs["error"] = map[string]*Command{}
|
||||
cmd.hookRefs["success"] = map[string]*Command{}
|
||||
|
||||
if cmd.Name == "" {
|
||||
cmd.Name = cmdName
|
||||
@ -458,19 +457,19 @@ func processCmds(opts *ConfigOpts) error {
|
||||
hooks := cmd.Hooks
|
||||
// resolve hooks
|
||||
if hooks != nil {
|
||||
opts.Logger.Debug().Msg("Hooks found")
|
||||
|
||||
errHook, hookRefs, processHookErr := processHooks(hooks.Error, opts.Cmds, "error")
|
||||
if !processHookErr {
|
||||
return fmt.Errorf("error in command %s hook list: hook command %s not found", cmd.Name, errHook)
|
||||
processHookSuccess := processHooks(cmd, hooks.Error, opts, "error")
|
||||
if processHookSuccess != nil {
|
||||
return processHookSuccess
|
||||
}
|
||||
cmd.hookRefs["error"] = hookRefs
|
||||
|
||||
successHook, SuccessHookRefs, processHookSuccess := processHooks(hooks.Error, opts.Cmds, "error")
|
||||
if !processHookSuccess {
|
||||
return fmt.Errorf("error in command %s hook list: hook command %s not found", cmd.Name, successHook)
|
||||
processHookSuccess = processHooks(cmd, hooks.Success, opts, "success")
|
||||
if processHookSuccess != nil {
|
||||
return processHookSuccess
|
||||
}
|
||||
processHookSuccess = processHooks(cmd, hooks.Final, opts, "final")
|
||||
if processHookSuccess != nil {
|
||||
return processHookSuccess
|
||||
}
|
||||
cmd.hookRefs["success"] = SuccessHookRefs
|
||||
}
|
||||
|
||||
// resolve hosts
|
||||
@ -503,34 +502,31 @@ func processCmds(opts *ConfigOpts) error {
|
||||
//
|
||||
// Returns the following:
|
||||
//
|
||||
// 1. command string
|
||||
// 2. each hook type's command map
|
||||
// 2. a bool which determines if the command is valid
|
||||
func processHooks(hooks []string, cmds map[string]*Command, hookType string) (hook string, hookRefs map[string]*Command, hookCmdFound bool) {
|
||||
// fmt.Printf("%v\n", hooks)
|
||||
// for _, v := range cmds {
|
||||
// An error, if any, if the command is not found
|
||||
func processHooks(cmd *Command, hooks []string, opts *ConfigOpts, hookType string) error {
|
||||
|
||||
// fmt.Printf("CmdName=%v\n", v.Name)
|
||||
// fmt.Printf("Cmd=%v\n", v.Cmd)
|
||||
// }
|
||||
// initialize hook type
|
||||
hookRefs = make(map[string]*Command)
|
||||
// hookRefs[hookType] = map[string]*Command{}
|
||||
for _, hook = range hooks {
|
||||
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 = cmds[hook]
|
||||
hookCmd, hookCmdFound = opts.Cmds[hook]
|
||||
|
||||
if !hookCmdFound {
|
||||
return
|
||||
return fmt.Errorf("error in command %s hook %s list: command %s not found", cmd.Name, hookType, hook)
|
||||
}
|
||||
hookRefs[hook] = hookCmd
|
||||
|
||||
cmd.hookRefs[hookType][hook] = hookCmd
|
||||
|
||||
// Recursive, decide if this is good
|
||||
// if hookCmd.hookRefs == nil {
|
||||
// }
|
||||
// hookRef[hookType][h] = hookCmd
|
||||
}
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
@ -197,9 +197,9 @@ type (
|
||||
}
|
||||
|
||||
Hooks struct {
|
||||
Error []string `yaml:"error,omitempty"`
|
||||
SuccessHooks []string `yaml:"success,omitempty"`
|
||||
FinalHooks []string `yaml:"final,omitempty"`
|
||||
Error []string `yaml:"error,omitempty"`
|
||||
Success []string `yaml:"success,omitempty"`
|
||||
Final []string `yaml:"final,omitempty"`
|
||||
}
|
||||
|
||||
CmdListResults struct {
|
||||
|
Reference in New Issue
Block a user