Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 62ac2934dc | |||
| c078632691 |
8
.changes/v0.12.1.md
Normal file
8
.changes/v0.12.1.md
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
## v0.12.1 - 2026-03-23
|
||||||
|
### Added
|
||||||
|
* Command output respects `command.[name].output.toLog` when standard output is enabled
|
||||||
|
### Changed
|
||||||
|
* SSH: revert scriptFile to before 0.11.3
|
||||||
|
* Output: tabs print as 4 spaces instead of '\t'
|
||||||
|
### Fixed
|
||||||
|
* Internal: output buffer will be copied to temporary buffer
|
||||||
11
.vscode/settings.json
vendored
11
.vscode/settings.json
vendored
@@ -10,5 +10,14 @@
|
|||||||
"nikoksr",
|
"nikoksr",
|
||||||
"Strs"
|
"Strs"
|
||||||
],
|
],
|
||||||
"CodeGPT.apiKey": "CodeGPT Plus Beta"
|
"CodeGPT.apiKey": "CodeGPT Plus Beta",
|
||||||
|
"yaml.schemas": {
|
||||||
|
"file:///c%3A/Users/anw12/.vscode/extensions/continue.continue-1.2.14-win32-x64/config-yaml-schema.json": [
|
||||||
|
".continue/**/*.yaml"
|
||||||
|
],
|
||||||
|
"file:///c%3A/Users/anw12/.vscode/extensions/continue.continue-1.2.16-win32-x64/config-yaml-schema.json": [
|
||||||
|
".continue/**/*.yaml"
|
||||||
|
],
|
||||||
|
"file:///c:/Users/anw12/.vscode/extensions/continue.continue-1.2.16-win32-x64/config-yaml-schema.json": "vscode-local:/c%3A/Users/anw12/.continue/config.yaml"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -6,6 +6,15 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html),
|
|||||||
and is generated by [Changie](https://github.com/miniscruff/changie).
|
and is generated by [Changie](https://github.com/miniscruff/changie).
|
||||||
|
|
||||||
|
|
||||||
|
## v0.12.1 - 2026-03-23
|
||||||
|
### Added
|
||||||
|
* Command output respects `command.[name].output.toLog` when standard output is enabled
|
||||||
|
### Changed
|
||||||
|
* SSH: revert scriptFile to before 0.11.3
|
||||||
|
* Output: tabs print as 4 spaces instead of '\t'
|
||||||
|
### Fixed
|
||||||
|
* Internal: output buffer will be copied to temporary buffer
|
||||||
|
|
||||||
## v0.12.0 - 2026-02-11
|
## v0.12.0 - 2026-02-11
|
||||||
### Changed
|
### Changed
|
||||||
* internal logic handling for cron webserver
|
* internal logic handling for cron webserver
|
||||||
|
|||||||
20
cmd/validate.go
Normal file
20
cmd/validate.go
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
// validate checks commands passed in from the command line
|
||||||
|
|
||||||
|
/*
|
||||||
|
*args:
|
||||||
|
* -t - type of cmd
|
||||||
|
* -c
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
func TestUserCmd(t *testing.T) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
}
|
||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
const versionStr = "0.12.0"
|
const versionStr = "0.12.1"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
versionCmd = &cobra.Command{
|
versionCmd = &cobra.Command{
|
||||||
|
|||||||
5
localDeploy
Executable file
5
localDeploy
Executable file
@@ -0,0 +1,5 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
go install .
|
||||||
|
systemctl --user stop backy
|
||||||
|
cp ~/go/bin/backy ~/prodConfigs/backups/backy/backy
|
||||||
|
systemctl --user start backy
|
||||||
@@ -14,7 +14,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"text/template"
|
"text/template"
|
||||||
"time"
|
|
||||||
|
|
||||||
"embed"
|
"embed"
|
||||||
|
|
||||||
@@ -152,28 +151,7 @@ func (e *LocalCommandExecutor) Run(cmd *Command, opts *ConfigOpts, logger zerolo
|
|||||||
// caller is responsible for closing the returned *os.File when non-nil.
|
// caller is responsible for closing the returned *os.File when non-nil.
|
||||||
func makeCmdOutWriters(buf *bytes.Buffer, outputFile string) (io.Writer, *os.File, error) {
|
func makeCmdOutWriters(buf *bytes.Buffer, outputFile string) (io.Writer, *os.File, error) {
|
||||||
writers := io.MultiWriter(buf)
|
writers := io.MultiWriter(buf)
|
||||||
if IsCmdStdOutEnabled() {
|
|
||||||
|
|
||||||
console := zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.RFC1123}
|
|
||||||
console.FormatLevel = func(i interface{}) string {
|
|
||||||
return strings.ToUpper(fmt.Sprintf("| %-6s|", i))
|
|
||||||
}
|
|
||||||
console.FormatMessage = func(i any) string {
|
|
||||||
if i == nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("MSG: %s", i)
|
|
||||||
}
|
|
||||||
console.FormatFieldName = func(i interface{}) string {
|
|
||||||
return fmt.Sprintf("%s: ", i)
|
|
||||||
}
|
|
||||||
console.FormatFieldValue = func(i interface{}) string {
|
|
||||||
return fmt.Sprintf("%s", i)
|
|
||||||
// return strings.ToUpper(fmt.Sprintf("%s", i))
|
|
||||||
}
|
|
||||||
|
|
||||||
writers = io.MultiWriter(console, writers)
|
|
||||||
}
|
|
||||||
if outputFile != "" {
|
if outputFile != "" {
|
||||||
|
|
||||||
fileLogger := &lumberjack.Logger{
|
fileLogger := &lumberjack.Logger{
|
||||||
@@ -202,7 +180,7 @@ func (opts *ConfigOpts) ensureRemoteHost(localCmd *Command, host string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// fallback: create a minimal Host so RunCmdOnHost sees a non-nil RemoteHost.
|
// fallback: create a minimal Host so RunCmdOnHost sees a non-nil RemoteHost.
|
||||||
// This uses host as the address/alias; further fields (user/key) will use defaults.
|
// This uses host as the address/alias; further fields (user/key) will use be looked up as needed.
|
||||||
localCmd.RemoteHost = &Host{Host: host}
|
localCmd.RemoteHost = &Host{Host: host}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,11 +205,13 @@ func (opts *ConfigOpts) ExecCommandOnHostsParallel(cmdName string) ([]CmdResult,
|
|||||||
// shallow copy to avoid races
|
// shallow copy to avoid races
|
||||||
local := *cmdObj
|
local := *cmdObj
|
||||||
local.Host = h
|
local.Host = h
|
||||||
opts.Logger.Debug().Str("host", h).Msg("executing command in parallel on host")
|
opts.Logger.Info().Str("host", h).Msg("executing command in parallel on host")
|
||||||
|
local.cmdLoggers.global = opts.Logger
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
if IsHostLocal(h) {
|
if IsHostLocal(h) {
|
||||||
_, err := local.RunCmd(local.GenerateLogger(opts), opts)
|
local.GenerateLogger(opts)
|
||||||
|
_, err := local.RunCmd(local.cmdLoggers.cmdContxt, opts)
|
||||||
resultsCh <- CmdResult{CmdName: cmdName, ListName: "", Error: err}
|
resultsCh <- CmdResult{CmdName: cmdName, ListName: "", Error: err}
|
||||||
return
|
return
|
||||||
// _, err = local.RunCmd(local.GenerateLogger(opts), opts)
|
// _, err = local.RunCmd(local.GenerateLogger(opts), opts)
|
||||||
@@ -240,9 +220,10 @@ func (opts *ConfigOpts) ExecCommandOnHostsParallel(cmdName string) ([]CmdResult,
|
|||||||
// ensure RemoteHost is populated before calling RunCmdOnHost
|
// ensure RemoteHost is populated before calling RunCmdOnHost
|
||||||
opts.ensureRemoteHost(&local, h)
|
opts.ensureRemoteHost(&local, h)
|
||||||
|
|
||||||
_, err = local.RunCmdOnHost(local.GenerateLogger(opts), opts)
|
local.GenerateLogger(opts)
|
||||||
|
_, err = local.RunCmdOnHost(local.cmdLoggers.cmdContxt, opts)
|
||||||
|
|
||||||
resultsCh <- CmdResult{CmdName: cmdName, ListName: "", Error: err}
|
resultsCh <- CmdResult{CmdName: cmdName, ListName: "", Error: err, HostName: h}
|
||||||
}(host)
|
}(host)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -252,6 +233,9 @@ func (opts *ConfigOpts) ExecCommandOnHostsParallel(cmdName string) ([]CmdResult,
|
|||||||
var results []CmdResult
|
var results []CmdResult
|
||||||
for r := range resultsCh {
|
for r := range resultsCh {
|
||||||
results = append(results, r)
|
results = append(results, r)
|
||||||
|
if r.Error != nil {
|
||||||
|
opts.Logger.Info().AnErr("error", r.Error).Str("cmd", r.CmdName).Send()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return results, nil
|
return results, nil
|
||||||
}
|
}
|
||||||
@@ -282,7 +266,11 @@ func (command *Command) RunCmd(cmdCtxLogger zerolog.Logger, opts *ConfigOpts) ([
|
|||||||
cmdCtxLogger.Warn().Msg("both 'host' and 'hosts' are set; 'hosts' will be ignored")
|
cmdCtxLogger.Warn().Msg("both 'host' and 'hosts' are set; 'hosts' will be ignored")
|
||||||
return nil, fmt.Errorf("both 'host' and 'hosts' are set; please set one or the other")
|
return nil, fmt.Errorf("both 'host' and 'hosts' are set; please set one or the other")
|
||||||
} else if command.Hosts != nil {
|
} else if command.Hosts != nil {
|
||||||
opts.ExecCommandOnHostsParallel(command.Name)
|
_, err := opts.ExecCommandOnHostsParallel(command.Name)
|
||||||
|
if err != nil {
|
||||||
|
opts.Logger.Err(err).Send()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -296,6 +284,7 @@ func (command *Command) RunCmd(cmdCtxLogger zerolog.Logger, opts *ConfigOpts) ([
|
|||||||
if command.Type == UserCommandType {
|
if command.Type == UserCommandType {
|
||||||
|
|
||||||
if command.UserOperation == "password" {
|
if command.UserOperation == "password" {
|
||||||
|
command.cmdLoggers.global.Info().Str("password", command.UserPassword).Msg("user password to be updated")
|
||||||
cmdCtxLogger.Info().Str("password", command.UserPassword).Msg("user password to be updated")
|
cmdCtxLogger.Info().Str("password", command.UserPassword).Msg("user password to be updated")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -436,7 +425,7 @@ func (command *Command) RunCmd(cmdCtxLogger zerolog.Logger, opts *ConfigOpts) ([
|
|||||||
localCMD := exec.Command(fmt.Sprintf("grep \"%s\" /etc/passwd | cut -d: -f6", command.Username))
|
localCMD := exec.Command(fmt.Sprintf("grep \"%s\" /etc/passwd | cut -d: -f6", command.Username))
|
||||||
userHome, err = localCMD.CombinedOutput()
|
userHome, err = localCMD.CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.Output.ToLog), fmt.Errorf("error finding user home from /etc/passwd: %v", err)
|
return collectOutput(&cmdOutBuf, command.Name, command.cmdLoggers.cmdContxt, command.cmdLoggers.global, command.Output.ToLog), fmt.Errorf("error finding user home from /etc/passwd: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
command.UserHome = strings.TrimSpace(string(userHome))
|
command.UserHome = strings.TrimSpace(string(userHome))
|
||||||
@@ -445,33 +434,33 @@ func (command *Command) RunCmd(cmdCtxLogger zerolog.Logger, opts *ConfigOpts) ([
|
|||||||
if _, err := os.Stat(userSshDir); os.IsNotExist(err) {
|
if _, err := os.Stat(userSshDir); os.IsNotExist(err) {
|
||||||
err := os.MkdirAll(userSshDir, 0700)
|
err := os.MkdirAll(userSshDir, 0700)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.Output.ToLog), fmt.Errorf("error creating directory %s %v", userSshDir, err)
|
return collectOutput(&cmdOutBuf, command.Name, command.cmdLoggers.cmdContxt, command.cmdLoggers.global, command.Output.ToLog), fmt.Errorf("error creating directory %s %v", userSshDir, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := os.Stat(fmt.Sprintf("%s/authorized_keys", userSshDir)); os.IsNotExist(err) {
|
if _, err := os.Stat(fmt.Sprintf("%s/authorized_keys", userSshDir)); os.IsNotExist(err) {
|
||||||
_, err := os.Create(fmt.Sprintf("%s/authorized_keys", userSshDir))
|
_, err := os.Create(fmt.Sprintf("%s/authorized_keys", userSshDir))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.Output.ToLog), fmt.Errorf("error creating file %s/authorized_keys: %v", userSshDir, err)
|
return collectOutput(&cmdOutBuf, command.Name, command.cmdLoggers.cmdContxt, command.cmdLoggers.global, command.Output.ToLog), fmt.Errorf("error creating file %s/authorized_keys: %v", userSshDir, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
authorizedKeysFile, err = os.OpenFile(fmt.Sprintf("%s/authorized_keys", userSshDir), 0700, os.ModeAppend)
|
authorizedKeysFile, err = os.OpenFile(fmt.Sprintf("%s/authorized_keys", userSshDir), 0700, os.ModeAppend)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.Output.ToLog), fmt.Errorf("error opening file %s/authorized_keys: %v", userSshDir, err)
|
return collectOutput(&cmdOutBuf, command.Name, command.cmdLoggers.cmdContxt, command.cmdLoggers.global, command.Output.ToLog), fmt.Errorf("error opening file %s/authorized_keys: %v", userSshDir, err)
|
||||||
}
|
}
|
||||||
defer authorizedKeysFile.Close()
|
defer authorizedKeysFile.Close()
|
||||||
for _, k := range command.UserSshPubKeys {
|
for _, k := range command.UserSshPubKeys {
|
||||||
buf := bytes.NewBufferString(k)
|
buf := bytes.NewBufferString(k)
|
||||||
cmdCtxLogger.Info().Str("key", k).Msg("adding SSH key")
|
cmdCtxLogger.Info().Str("key", k).Msg("adding SSH key")
|
||||||
if _, err := authorizedKeysFile.ReadFrom(buf); err != nil {
|
if _, err := authorizedKeysFile.ReadFrom(buf); err != nil {
|
||||||
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.Output.ToLog), fmt.Errorf("error adding to authorized keys: %v", err)
|
return collectOutput(&cmdOutBuf, command.Name, command.cmdLoggers.cmdContxt, command.cmdLoggers.global, command.Output.ToLog), fmt.Errorf("error adding to authorized keys: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
localCMD = exec.Command(fmt.Sprintf("chown -R %s:%s %s", command.Username, command.Username, userHome))
|
localCMD = exec.Command(fmt.Sprintf("chown -R %s:%s %s", command.Username, command.Username, userHome))
|
||||||
_, err = localCMD.CombinedOutput()
|
_, err = localCMD.CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.Output.ToLog), err
|
return collectOutput(&cmdOutBuf, command.Name, command.cmdLoggers.cmdContxt, command.cmdLoggers.global, command.Output.ToLog), err
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -495,10 +484,10 @@ func cmdListWorker(msgTemps *msgTemplates, jobs <-chan *CmdList, results chan<-
|
|||||||
commandExecuted = cmdToRun
|
commandExecuted = cmdToRun
|
||||||
currentCmd := cmdToRun.Name
|
currentCmd := cmdToRun.Name
|
||||||
fieldsMap["cmd"] = currentCmd
|
fieldsMap["cmd"] = currentCmd
|
||||||
cmdLogger = cmdToRun.GenerateLogger(opts)
|
cmdToRun.GenerateLogger(opts)
|
||||||
cmdLogger.Info().Fields(fieldsMap).Send()
|
cmdToRun.cmdLoggers.cmdContxt.Info().Fields(fieldsMap).Send()
|
||||||
|
|
||||||
outputArr, runErr := cmdToRun.RunCmd(cmdLogger, opts)
|
outputArr, runErr := cmdToRun.RunCmd(cmdToRun.cmdLoggers.cmdContxt, opts)
|
||||||
cmdsRan = append(cmdsRan, cmd)
|
cmdsRan = append(cmdsRan, cmd)
|
||||||
|
|
||||||
if runErr != nil {
|
if runErr != nil {
|
||||||
@@ -559,10 +548,10 @@ func cmdListWorkerWithHosts(msgTemps *msgTemplates, jobs <-chan *CmdList, hosts
|
|||||||
commandExecuted = cmdToRun
|
commandExecuted = cmdToRun
|
||||||
currentCmd := cmdToRun.Name
|
currentCmd := cmdToRun.Name
|
||||||
fieldsMap["cmd"] = currentCmd
|
fieldsMap["cmd"] = currentCmd
|
||||||
cmdLogger = cmdToRun.GenerateLogger(opts)
|
cmdToRun.GenerateLogger(opts)
|
||||||
cmdLogger.Info().Fields(fieldsMap).Send()
|
cmdToRun.cmdLoggers.cmdContxt.Info().Fields(fieldsMap).Send()
|
||||||
|
|
||||||
outputArr, runErr := cmdToRun.RunCmd(cmdLogger, opts)
|
outputArr, runErr := cmdToRun.RunCmd(cmdToRun.cmdLoggers.cmdContxt, opts)
|
||||||
cmdsRan = append(cmdsRan, cmd)
|
cmdsRan = append(cmdsRan, cmd)
|
||||||
|
|
||||||
if runErr != nil {
|
if runErr != nil {
|
||||||
@@ -705,8 +694,8 @@ func cmdListWorkerExecuteCommandsInParallel(msgTemps *msgTemplates, jobs <-chan
|
|||||||
cmdToRun.Host = host.Host
|
cmdToRun.Host = host.Host
|
||||||
cmdToRun.RemoteHost = host
|
cmdToRun.RemoteHost = host
|
||||||
}
|
}
|
||||||
cmdLogger = cmdToRun.GenerateLogger(opts)
|
cmdToRun.GenerateLogger(opts)
|
||||||
cmdLogger.Info().Fields(fieldsMap).Send()
|
cmdToRun.cmdLoggers.cmdContxt.Info().Fields(fieldsMap).Send()
|
||||||
print("Running cmd on: ", host.Host, "\n")
|
print("Running cmd on: ", host.Host, "\n")
|
||||||
|
|
||||||
go func(cmd string, host *Host) {
|
go func(cmd string, host *Host) {
|
||||||
@@ -714,7 +703,7 @@ func cmdListWorkerExecuteCommandsInParallel(msgTemps *msgTemplates, jobs <-chan
|
|||||||
currentCmd := cmdToRun.Name
|
currentCmd := cmdToRun.Name
|
||||||
fieldsMap["cmd"] = currentCmd
|
fieldsMap["cmd"] = currentCmd
|
||||||
|
|
||||||
outputArr, runErr := cmdToRun.RunCmd(cmdLogger, opts)
|
outputArr, runErr := cmdToRun.RunCmd(cmdToRun.cmdLoggers.cmdContxt, opts)
|
||||||
if runErr != nil {
|
if runErr != nil {
|
||||||
cmdLogger.Err(runErr).Send()
|
cmdLogger.Err(runErr).Send()
|
||||||
cmdToRun.ExecuteHooks("error", opts)
|
cmdToRun.ExecuteHooks("error", opts)
|
||||||
@@ -890,8 +879,8 @@ func (opts *ConfigOpts) ExecuteListOnHosts(lists []string, parallel bool) {
|
|||||||
func (opts *ConfigOpts) ExecuteCmds() {
|
func (opts *ConfigOpts) ExecuteCmds() {
|
||||||
for _, cmd := range opts.executeCmds {
|
for _, cmd := range opts.executeCmds {
|
||||||
cmdToRun := opts.Cmds[cmd]
|
cmdToRun := opts.Cmds[cmd]
|
||||||
cmdLogger := cmdToRun.GenerateLogger(opts)
|
cmdToRun.GenerateLogger(opts)
|
||||||
_, runErr := cmdToRun.RunCmd(cmdLogger, opts)
|
_, runErr := cmdToRun.RunCmd(cmdToRun.cmdLoggers.cmdContxt, opts)
|
||||||
if runErr != nil {
|
if runErr != nil {
|
||||||
opts.Logger.Err(runErr).Send()
|
opts.Logger.Err(runErr).Send()
|
||||||
cmdToRun.ExecuteHooks("error", opts)
|
cmdToRun.ExecuteHooks("error", opts)
|
||||||
@@ -979,30 +968,29 @@ func (cmd *Command) ExecuteHooks(hookType string, opts *ConfigOpts) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cmd *Command) GenerateLogger(opts *ConfigOpts) zerolog.Logger {
|
func (cmd *Command) GenerateLogger(opts *ConfigOpts) {
|
||||||
cmdLogger := opts.Logger.With().
|
cmd.cmdLoggers.cmdContxt = opts.Logger.With().
|
||||||
Str("Backy-cmd", cmd.Name).Str("Host", "local machine").
|
Str("Backy-cmd", cmd.Name).Str("Host", "local machine").
|
||||||
Logger()
|
Logger()
|
||||||
|
|
||||||
if !IsHostLocal(cmd.Host) {
|
if !IsHostLocal(cmd.Host) {
|
||||||
cmdLogger = opts.Logger.With().
|
cmd.cmdLoggers.cmdContxt = opts.Logger.With().
|
||||||
Str("Backy-cmd", cmd.Name).Str("Host", cmd.Host).
|
Str("Backy-cmd", cmd.Name).Str("Host", cmd.Host).
|
||||||
Logger()
|
Logger()
|
||||||
}
|
}
|
||||||
return cmdLogger
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cmd *Command) GenerateLoggerForCmd(logger zerolog.Logger) zerolog.Logger {
|
func (cmd *Command) GenerateLoggerForCmd(logger zerolog.Logger) zerolog.Logger {
|
||||||
cmdLogger := logger.With().
|
cmd.cmdLoggers.cmdContxt = logger.With().
|
||||||
Str("Backy-cmd", cmd.Name).Str("Host", "local machine").
|
Str("Backy-cmd", cmd.Name).Str("Host", "local machine").
|
||||||
Logger()
|
Logger()
|
||||||
|
|
||||||
if !IsHostLocal(cmd.Host) {
|
if !IsHostLocal(cmd.Host) {
|
||||||
cmdLogger = logger.With().
|
cmd.cmdLoggers.cmdContxt = logger.With().
|
||||||
Str("Backy-cmd", cmd.Name).Str("Host", cmd.Host).
|
Str("Backy-cmd", cmd.Name).Str("Host", cmd.Host).
|
||||||
Logger()
|
Logger()
|
||||||
}
|
}
|
||||||
return cmdLogger
|
return cmd.cmdLoggers.cmdContxt
|
||||||
}
|
}
|
||||||
|
|
||||||
func (opts *ConfigOpts) ExecCmdsOnHosts(cmdList []string, hostsList []string) {
|
func (opts *ConfigOpts) ExecCmdsOnHosts(cmdList []string, hostsList []string) {
|
||||||
@@ -1014,7 +1002,8 @@ func (opts *ConfigOpts) ExecCmdsOnHosts(cmdList []string, hostsList []string) {
|
|||||||
cmd.RemoteHost = host
|
cmd.RemoteHost = host
|
||||||
cmd.Host = h
|
cmd.Host = h
|
||||||
if IsHostLocal(h) {
|
if IsHostLocal(h) {
|
||||||
_, err := cmd.RunCmd(cmd.GenerateLogger(opts), opts)
|
cmd.GenerateLogger(opts)
|
||||||
|
_, err := cmd.RunCmd(cmd.cmdLoggers.cmdContxt, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
opts.Logger.Err(err).Str("host", h).Str("cmd", c).Send()
|
opts.Logger.Err(err).Str("host", h).Str("cmd", c).Send()
|
||||||
}
|
}
|
||||||
@@ -1022,7 +1011,8 @@ func (opts *ConfigOpts) ExecCmdsOnHosts(cmdList []string, hostsList []string) {
|
|||||||
|
|
||||||
cmd.Host = host.Host
|
cmd.Host = host.Host
|
||||||
opts.Logger.Info().Str("host", h).Str("cmd", c).Send()
|
opts.Logger.Info().Str("host", h).Str("cmd", c).Send()
|
||||||
_, err := cmd.RunCmdOnHost(cmd.GenerateLogger(opts), opts)
|
cmd.GenerateLogger(opts)
|
||||||
|
_, err := cmd.RunCmdOnHost(cmd.cmdLoggers.cmdContxt, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
opts.Logger.Err(err).Str("host", h).Str("cmd", c).Send()
|
opts.Logger.Err(err).Str("host", h).Str("cmd", c).Send()
|
||||||
}
|
}
|
||||||
@@ -1041,7 +1031,8 @@ func (opts *ConfigOpts) ExecCmdsOnHostsInParallel(cmdList []string, hostsList []
|
|||||||
cmd.RemoteHost = host
|
cmd.RemoteHost = host
|
||||||
cmd.Host = h
|
cmd.Host = h
|
||||||
if IsHostLocal(h) {
|
if IsHostLocal(h) {
|
||||||
_, err := cmd.RunCmd(cmd.GenerateLogger(opts), opts)
|
cmd.GenerateLogger(opts)
|
||||||
|
_, err := cmd.RunCmd(cmd.cmdLoggers.cmdContxt, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
opts.Logger.Err(err).Str("host", h).Str("cmd", c).Send()
|
opts.Logger.Err(err).Str("host", h).Str("cmd", c).Send()
|
||||||
}
|
}
|
||||||
@@ -1049,7 +1040,8 @@ func (opts *ConfigOpts) ExecCmdsOnHostsInParallel(cmdList []string, hostsList []
|
|||||||
|
|
||||||
cmd.Host = host.Host
|
cmd.Host = host.Host
|
||||||
opts.Logger.Info().Str("host", h).Str("cmd", c).Send()
|
opts.Logger.Info().Str("host", h).Str("cmd", c).Send()
|
||||||
_, err := cmd.RunCmdOnHost(cmd.GenerateLogger(opts), opts)
|
cmd.GenerateLogger(opts)
|
||||||
|
_, err := cmd.RunCmdOnHost(cmd.cmdLoggers.cmdContxt, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
opts.Logger.Err(err).Str("host", h).Str("cmd", c).Send()
|
opts.Logger.Err(err).Str("host", h).Str("cmd", c).Send()
|
||||||
}
|
}
|
||||||
|
|||||||
115
pkg/backy/ssh.go
115
pkg/backy/ssh.go
@@ -472,7 +472,7 @@ func (command *Command) RunCmdOnHost(cmdCtxLogger zerolog.Logger, opts *ConfigOp
|
|||||||
defer commandSession.Close()
|
defer commandSession.Close()
|
||||||
|
|
||||||
// Set output writers
|
// Set output writers
|
||||||
var file *os.File
|
// var file *os.File
|
||||||
if !IsHostLocal(command.Host) && command.Output.File != "" {
|
if !IsHostLocal(command.Host) && command.Output.File != "" {
|
||||||
if filepath.IsAbs(command.Output.File) {
|
if filepath.IsAbs(command.Output.File) {
|
||||||
fileName := filepath.Base(command.Output.File)
|
fileName := filepath.Base(command.Output.File)
|
||||||
@@ -483,19 +483,19 @@ func (command *Command) RunCmdOnHost(cmdCtxLogger zerolog.Logger, opts *ConfigOp
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cmdOutWriters, file, err = makeCmdOutWriters(&cmdOutBuf, command.Output.File)
|
cmdOutWriters, _, err = makeCmdOutWriters(&cmdOutBuf, command.Output.File)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error creating command output writers: %w", err)
|
return nil, fmt.Errorf("error creating command output writers: %w", err)
|
||||||
}
|
}
|
||||||
defer func() {
|
// defer func() {
|
||||||
if file != nil {
|
// if file != nil {
|
||||||
file.Close()
|
// file.Close()
|
||||||
}
|
// }
|
||||||
}()
|
// }()
|
||||||
// cmdOutWriters = logging.SetLoggingWriterForCommand(&cmdOutBuf, command.Output.File, IsCmdStdOutEnabled())
|
// cmdOutWriters = logging.SetLoggingWriterForCommand(&cmdOutBuf, command.Output.File, IsCmdStdOutEnabled())
|
||||||
cmdCtxLogger = zerolog.New(cmdOutWriters).With().Timestamp().Logger()
|
cmdCtxLogger = zerolog.New(cmdOutWriters).With().Timestamp().Logger()
|
||||||
cmdCtxLogger = command.GenerateLoggerForCmd(cmdCtxLogger)
|
// cmdCtxLogger = command.GenerateLoggerForCmd(cmdCtxLogger)
|
||||||
|
command.cmdLoggers.cmdContxt = cmdCtxLogger
|
||||||
// cmdCtxLogger.Info().Msgf("Executing %s", command.Cmd)
|
// cmdCtxLogger.Info().Msgf("Executing %s", command.Cmd)
|
||||||
commandSession.Stdout = cmdOutWriters
|
commandSession.Stdout = cmdOutWriters
|
||||||
commandSession.Stderr = cmdOutWriters
|
commandSession.Stderr = cmdOutWriters
|
||||||
@@ -513,13 +513,13 @@ func (command *Command) RunCmdOnHost(cmdCtxLogger zerolog.Logger, opts *ConfigOp
|
|||||||
// Handle command execution based on type
|
// Handle command execution based on type
|
||||||
switch command.Type {
|
switch command.Type {
|
||||||
case ScriptCommandType:
|
case ScriptCommandType:
|
||||||
return command.runScript(commandSession, cmdCtxLogger, &cmdOutBuf)
|
return command.runScript(commandSession, &cmdOutBuf)
|
||||||
case RemoteScriptCommandType:
|
case RemoteScriptCommandType:
|
||||||
return command.runRemoteScript(commandSession, cmdCtxLogger, &cmdOutBuf)
|
return command.runRemoteScript(commandSession, &cmdOutBuf)
|
||||||
case ScriptFileCommandType:
|
case ScriptFileCommandType:
|
||||||
commandSession.Stdout = nil
|
// commandSession.Stdout = nil
|
||||||
commandSession.Stderr = nil
|
// commandSession.Stderr = nil
|
||||||
return command.runScriptFile(commandSession, cmdCtxLogger, opts.Logger, &cmdOutBuf)
|
return command.runScriptFile(commandSession, &cmdOutBuf)
|
||||||
case PackageCommandType:
|
case PackageCommandType:
|
||||||
var remoteHostPackageExecutor RemoteHostPackageExecutor
|
var remoteHostPackageExecutor RemoteHostPackageExecutor
|
||||||
return remoteHostPackageExecutor.RunCmdOnHost(command, commandSession, cmdCtxLogger, cmdOutBuf)
|
return remoteHostPackageExecutor.RunCmdOnHost(command, commandSession, cmdCtxLogger, cmdOutBuf)
|
||||||
@@ -541,18 +541,18 @@ func (command *Command) RunCmdOnHost(cmdCtxLogger zerolog.Logger, opts *ConfigOp
|
|||||||
userNamePass := fmt.Sprintf("%s:%s", command.Username, command.UserPassword)
|
userNamePass := fmt.Sprintf("%s:%s", command.Username, command.UserPassword)
|
||||||
client, err := sftp.NewClient(command.RemoteHost.SshClient)
|
client, err := sftp.NewClient(command.RemoteHost.SshClient)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.Output.ToLog), fmt.Errorf("error creating sftp client: %v", err)
|
return collectOutput(&cmdOutBuf, command.Name, command.cmdLoggers.cmdContxt, command.cmdLoggers.global, command.Output.ToLog), fmt.Errorf("error creating sftp client: %v", err)
|
||||||
}
|
}
|
||||||
uuidFile := uuid.New()
|
uuidFile := uuid.New()
|
||||||
passFilePath := fmt.Sprintf("/tmp/%s", uuidFile.String())
|
passFilePath := fmt.Sprintf("/tmp/%s", uuidFile.String())
|
||||||
passFile, passFileErr := client.Create(passFilePath)
|
passFile, passFileErr := client.Create(passFilePath)
|
||||||
if passFileErr != nil {
|
if passFileErr != nil {
|
||||||
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.Output.ToLog), fmt.Errorf("error creating file /tmp/%s: %v", uuidFile.String(), passFileErr)
|
return collectOutput(&cmdOutBuf, command.Name, command.cmdLoggers.cmdContxt, command.cmdLoggers.global, command.Output.ToLog), fmt.Errorf("error creating file /tmp/%s: %v", uuidFile.String(), passFileErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = passFile.Write([]byte(userNamePass))
|
_, err = passFile.Write([]byte(userNamePass))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.Output.ToLog), fmt.Errorf("error writing to file /tmp/%s: %v", uuidFile.String(), err)
|
return collectOutput(&cmdOutBuf, command.Name, command.cmdLoggers.cmdContxt, command.cmdLoggers.global, command.Output.ToLog), fmt.Errorf("error writing to file /tmp/%s: %v", uuidFile.String(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ArgsStr = fmt.Sprintf("cat %s | chpasswd", passFilePath)
|
ArgsStr = fmt.Sprintf("cat %s | chpasswd", passFilePath)
|
||||||
@@ -566,7 +566,7 @@ func (command *Command) RunCmdOnHost(cmdCtxLogger zerolog.Logger, opts *ConfigOp
|
|||||||
defer rmFileFunc()
|
defer rmFileFunc()
|
||||||
}
|
}
|
||||||
if err := commandSession.Run(command.ArgStr); err != nil {
|
if err := commandSession.Run(command.ArgStr); err != nil {
|
||||||
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.Output.ToLog), fmt.Errorf("error running command: %w", err)
|
return collectOutput(&cmdOutBuf, command.Name, command.cmdLoggers.cmdContxt, command.cmdLoggers.global, command.Output.ToLog), fmt.Errorf("error running command: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if command.Type == UserCommandType {
|
if command.Type == UserCommandType {
|
||||||
@@ -587,41 +587,41 @@ func (command *Command) RunCmdOnHost(cmdCtxLogger zerolog.Logger, opts *ConfigOp
|
|||||||
commandSession, _ = command.RemoteHost.createSSHSession(opts)
|
commandSession, _ = command.RemoteHost.createSSHSession(opts)
|
||||||
userHome, err = commandSession.CombinedOutput(fmt.Sprintf("grep \"%s\" /etc/passwd | cut -d: -f6", command.Username))
|
userHome, err = commandSession.CombinedOutput(fmt.Sprintf("grep \"%s\" /etc/passwd | cut -d: -f6", command.Username))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.Output.ToLog), fmt.Errorf("error finding user home from /etc/passwd: %v", err)
|
return collectOutput(&cmdOutBuf, command.Name, command.cmdLoggers.cmdContxt, command.cmdLoggers.global, command.Output.ToLog), fmt.Errorf("error finding user home from /etc/passwd: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
command.UserHome = strings.TrimSpace(string(userHome))
|
command.UserHome = strings.TrimSpace(string(userHome))
|
||||||
userSshDir := fmt.Sprintf("%s/.ssh", command.UserHome)
|
userSshDir := fmt.Sprintf("%s/.ssh", command.UserHome)
|
||||||
client, err = sftp.NewClient(command.RemoteHost.SshClient)
|
client, err = sftp.NewClient(command.RemoteHost.SshClient)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.Output.ToLog), fmt.Errorf("error creating sftp client: %v", err)
|
return collectOutput(&cmdOutBuf, command.Name, command.cmdLoggers.cmdContxt, command.cmdLoggers.global, command.Output.ToLog), fmt.Errorf("error creating sftp client: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = client.MkdirAll(userSshDir)
|
err = client.MkdirAll(userSshDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.Output.ToLog), fmt.Errorf("error creating directory %s: %v", userSshDir, err)
|
return collectOutput(&cmdOutBuf, command.Name, command.cmdLoggers.cmdContxt, command.cmdLoggers.global, command.Output.ToLog), fmt.Errorf("error creating directory %s: %v", userSshDir, err)
|
||||||
}
|
}
|
||||||
_, err = client.Create(fmt.Sprintf("%s/authorized_keys", userSshDir))
|
_, err = client.Create(fmt.Sprintf("%s/authorized_keys", userSshDir))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.Output.ToLog), fmt.Errorf("error opening file %s/authorized_keys: %v", userSshDir, err)
|
return collectOutput(&cmdOutBuf, command.Name, command.cmdLoggers.cmdContxt, command.cmdLoggers.global, command.Output.ToLog), fmt.Errorf("error opening file %s/authorized_keys: %v", userSshDir, err)
|
||||||
}
|
}
|
||||||
f, err = client.OpenFile(fmt.Sprintf("%s/authorized_keys", userSshDir), os.O_APPEND|os.O_CREATE|os.O_WRONLY)
|
f, err = client.OpenFile(fmt.Sprintf("%s/authorized_keys", userSshDir), os.O_APPEND|os.O_CREATE|os.O_WRONLY)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.Output.ToLog), fmt.Errorf("error opening file %s/authorized_keys: %v", userSshDir, err)
|
return collectOutput(&cmdOutBuf, command.Name, command.cmdLoggers.cmdContxt, command.cmdLoggers.global, command.Output.ToLog), fmt.Errorf("error opening file %s/authorized_keys: %v", userSshDir, err)
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
for _, k := range command.UserSshPubKeys {
|
for _, k := range command.UserSshPubKeys {
|
||||||
buf := bytes.NewBufferString(k)
|
buf := bytes.NewBufferString(k)
|
||||||
cmdCtxLogger.Info().Str("key", k).Msg("adding SSH key")
|
cmdCtxLogger.Info().Str("key", k).Msg("adding SSH key")
|
||||||
if _, err := f.ReadFrom(buf); err != nil {
|
if _, err := f.ReadFrom(buf); err != nil {
|
||||||
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.Output.ToLog), fmt.Errorf("error adding to authorized keys: %v", err)
|
return collectOutput(&cmdOutBuf, command.Name, command.cmdLoggers.cmdContxt, command.cmdLoggers.global, command.Output.ToLog), fmt.Errorf("error adding to authorized keys: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
commandSession, _ = command.RemoteHost.createSSHSession(opts)
|
commandSession, _ = command.RemoteHost.createSSHSession(opts)
|
||||||
_, err = commandSession.CombinedOutput(fmt.Sprintf("chown -R %s:%s %s", command.Username, command.Username, userHome))
|
_, err = commandSession.CombinedOutput(fmt.Sprintf("chown -R %s:%s %s", command.Username, command.Username, userHome))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.Output.ToLog), err
|
return collectOutput(&cmdOutBuf, command.Name, command.cmdLoggers.cmdContxt, command.cmdLoggers.global, command.Output.ToLog), err
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -629,7 +629,7 @@ func (command *Command) RunCmdOnHost(cmdCtxLogger zerolog.Logger, opts *ConfigOp
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.Output.ToLog), nil
|
return collectOutput(&cmdOutBuf, command.Name, command.cmdLoggers.cmdContxt, command.cmdLoggers.global, command.Output.ToLog), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkPackageVersion(cmdCtxLogger zerolog.Logger, command *Command, commandSession *ssh.Session, cmdOutBuf bytes.Buffer) ([]string, error) {
|
func checkPackageVersion(cmdCtxLogger zerolog.Logger, command *Command, commandSession *ssh.Session, cmdOutBuf bytes.Buffer) ([]string, error) {
|
||||||
@@ -649,9 +649,9 @@ func checkPackageVersion(cmdCtxLogger zerolog.Logger, command *Command, commandS
|
|||||||
|
|
||||||
_, parseErr := parsePackageVersion(string(cmdOut), cmdCtxLogger, command, cmdOutBuf)
|
_, parseErr := parsePackageVersion(string(cmdOut), cmdCtxLogger, command, cmdOutBuf)
|
||||||
if parseErr != nil {
|
if parseErr != nil {
|
||||||
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.Output.ToLog), fmt.Errorf("error: packages %v not listed: %w", command.Packages, err)
|
return collectOutput(&cmdOutBuf, command.Name, command.cmdLoggers.cmdContxt, command.cmdLoggers.global, command.Output.ToLog), fmt.Errorf("error: packages %v not listed: %w", command.Packages, err)
|
||||||
}
|
}
|
||||||
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.Output.ToLog), fmt.Errorf("error running %s: %w", ArgsStr, err)
|
return collectOutput(&cmdOutBuf, command.Name, command.cmdLoggers.cmdContxt, command.cmdLoggers.global, command.Output.ToLog), fmt.Errorf("error running %s: %w", ArgsStr, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return parsePackageVersion(string(cmdOut), cmdCtxLogger, command, cmdOutBuf)
|
return parsePackageVersion(string(cmdOut), cmdCtxLogger, command, cmdOutBuf)
|
||||||
@@ -666,7 +666,7 @@ func getCommandTypeAndSetCommandInfoLabel(commandType CommandType) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// runScript handles the execution of inline scripts.
|
// runScript handles the execution of inline scripts.
|
||||||
func (command *Command) runScript(session *ssh.Session, cmdCtxLogger zerolog.Logger, outputBuf *bytes.Buffer) ([]string, error) {
|
func (command *Command) runScript(session *ssh.Session, outputBuf *bytes.Buffer) ([]string, error) {
|
||||||
script, err := command.prepareScriptBuffer()
|
script, err := command.prepareScriptBuffer()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -678,60 +678,29 @@ func (command *Command) runScript(session *ssh.Session, cmdCtxLogger zerolog.Log
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := session.Wait(); err != nil {
|
if err := session.Wait(); err != nil {
|
||||||
return collectOutput(outputBuf, command.Name, cmdCtxLogger, true), fmt.Errorf("error waiting for shell: %w", err)
|
return collectOutput(outputBuf, command.Name, command.cmdLoggers.cmdContxt, command.cmdLoggers.global, command.Output.ToLog), fmt.Errorf("error waiting for command: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return collectOutput(outputBuf, command.Name, cmdCtxLogger, command.Output.ToLog), nil
|
return collectOutput(outputBuf, command.Name, command.cmdLoggers.cmdContxt, command.cmdLoggers.global, command.Output.ToLog), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// runScriptFile handles the execution of script files.
|
// runScriptFile handles the execution of script files.
|
||||||
func (command *Command) runScriptFile(session *ssh.Session, cmdCtxLogger, globalLogger zerolog.Logger, outputBuf *bytes.Buffer) ([]string, error) {
|
func (command *Command) runScriptFile(session *ssh.Session, outputBuf *bytes.Buffer) ([]string, error) {
|
||||||
script, err := command.prepareScriptFileBuffer()
|
script, err := command.prepareScriptFileBuffer()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
session.Stdin = script
|
session.Stdin = script
|
||||||
|
|
||||||
// modes := ssh.TerminalModes{
|
|
||||||
// ssh.ECHO: 0,
|
|
||||||
// ssh.ECHOCTL: 0,
|
|
||||||
// ssh.TTY_OP_ISPEED: 14400,
|
|
||||||
// ssh.TTY_OP_OSPEED: 14400,
|
|
||||||
// }
|
|
||||||
|
|
||||||
// session.RequestPty("xterm", 80, 40, modes)
|
|
||||||
|
|
||||||
stdout, stdOutErr := session.StdoutPipe()
|
|
||||||
if stdOutErr != nil {
|
|
||||||
return nil, fmt.Errorf("error getting stdout pipe: %w", stdOutErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := session.Shell(); err != nil {
|
if err := session.Shell(); err != nil {
|
||||||
return nil, fmt.Errorf("error starting shell: %w", err)
|
return nil, fmt.Errorf("error starting shell: %w", err)
|
||||||
}
|
}
|
||||||
var LogOutputToFile bool
|
|
||||||
if command.Output.File != "" || command.Output.ToLog {
|
|
||||||
if command.Output.File != "" {
|
|
||||||
globalLogger.Info().Str("file", command.Output.File).Msg("Writing script output to file")
|
|
||||||
}
|
|
||||||
LogOutputToFile = true
|
|
||||||
}
|
|
||||||
|
|
||||||
stdOutput, stdoOutReadErr := io.ReadAll(stdout)
|
|
||||||
if err := session.Wait(); err != nil {
|
if err := session.Wait(); err != nil {
|
||||||
stdOutBuff := bytes.NewBuffer(stdOutput)
|
return collectOutput(outputBuf, command.Name, command.cmdLoggers.cmdContxt, command.cmdLoggers.global, command.Output.ToLog), fmt.Errorf("error waiting for shell: %w", err)
|
||||||
// outputBuf.Write(stdOutBuff.Bytes())
|
|
||||||
// Read output
|
|
||||||
return collectOutput(stdOutBuff, command.Name, cmdCtxLogger, LogOutputToFile), fmt.Errorf("error waiting for shell: %w", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read output
|
return collectOutput(outputBuf, command.Name, command.cmdLoggers.cmdContxt, command.cmdLoggers.global, command.Output.ToLog), nil
|
||||||
if stdoOutReadErr != nil {
|
|
||||||
return collectOutput(outputBuf, command.Name, cmdCtxLogger, LogOutputToFile), fmt.Errorf("error reading stdout after shell error: %w", stdoOutReadErr)
|
|
||||||
}
|
|
||||||
stdOutBuff := bytes.NewBuffer(stdOutput)
|
|
||||||
|
|
||||||
return collectOutput(stdOutBuff, command.Name, cmdCtxLogger, LogOutputToFile), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// prepareScriptBuffer prepares a buffer for inline scripts.
|
// prepareScriptBuffer prepares a buffer for inline scripts.
|
||||||
@@ -764,9 +733,9 @@ func (command *Command) prepareScriptBuffer() (*bytes.Buffer, error) {
|
|||||||
func (command *Command) prepareScriptFileBuffer() (*bytes.Buffer, error) {
|
func (command *Command) prepareScriptFileBuffer() (*bytes.Buffer, error) {
|
||||||
var buffer bytes.Buffer
|
var buffer bytes.Buffer
|
||||||
|
|
||||||
if !command.SaveShellHistory {
|
// if !command.SaveShellHistory {
|
||||||
buffer.WriteString("unset HISTFILE\nexport HISTSIZE=0\nexport SAVEHIST=0\n")
|
// buffer.WriteString("unset HISTFILE\nexport HISTSIZE=0\nexport SAVEHIST=0\n")
|
||||||
}
|
// }
|
||||||
|
|
||||||
for _, envVar := range command.Environment {
|
for _, envVar := range command.Environment {
|
||||||
fmt.Fprintf(&buffer, "export %s", envVar)
|
fmt.Fprintf(&buffer, "export %s", envVar)
|
||||||
@@ -794,7 +763,7 @@ func (command *Command) prepareScriptFileBuffer() (*bytes.Buffer, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// runRemoteScript handles the execution of remote scripts
|
// runRemoteScript handles the execution of remote scripts
|
||||||
func (command *Command) runRemoteScript(session *ssh.Session, cmdCtxLogger zerolog.Logger, outputBuf *bytes.Buffer) ([]string, error) {
|
func (command *Command) runRemoteScript(session *ssh.Session, outputBuf *bytes.Buffer) ([]string, error) {
|
||||||
script, err := command.Fetcher.Fetch(command.Cmd)
|
script, err := command.Fetcher.Fetch(command.Cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -806,10 +775,10 @@ func (command *Command) runRemoteScript(session *ssh.Session, cmdCtxLogger zerol
|
|||||||
err = session.Run(command.Shell)
|
err = session.Run(command.Shell)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return collectOutput(outputBuf, command.Name, cmdCtxLogger, command.Output.ToLog), fmt.Errorf("error running remote script: %w", err)
|
return collectOutput(outputBuf, command.Name, command.cmdLoggers.cmdContxt, command.cmdLoggers.global, command.Output.ToLog), fmt.Errorf("error running remote script: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return collectOutput(outputBuf, command.Name, cmdCtxLogger, command.Output.ToLog), nil
|
return collectOutput(outputBuf, command.Name, command.cmdLoggers.cmdContxt, command.cmdLoggers.global, command.Output.ToLog), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// readFileToBuffer reads a file into a buffer.
|
// readFileToBuffer reads a file into a buffer.
|
||||||
@@ -908,7 +877,7 @@ func (r RemoteHostPackageExecutor) RunCmdOnHost(command *Command, commandSession
|
|||||||
cmdCtxLogger.Debug().Str("cmd + args", ArgsStr).Send()
|
cmdCtxLogger.Debug().Str("cmd + args", ArgsStr).Send()
|
||||||
// Run simple command
|
// Run simple command
|
||||||
if err := commandSession.Run(ArgsStr); err != nil {
|
if err := commandSession.Run(ArgsStr); err != nil {
|
||||||
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.Output.ToLog), fmt.Errorf("error running command: %w", err)
|
return collectOutput(&cmdOutBuf, command.Name, command.cmdLoggers.cmdContxt, command.cmdLoggers.global, command.Output.ToLog), fmt.Errorf("error running command: %w", err)
|
||||||
}
|
}
|
||||||
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.Output.ToLog), nil
|
return collectOutput(&cmdOutBuf, command.Name, command.cmdLoggers.cmdContxt, command.cmdLoggers.global, command.Output.ToLog), nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,6 +58,8 @@ type (
|
|||||||
// See CommandType enum further down the page for acceptable values
|
// See CommandType enum further down the page for acceptable values
|
||||||
Type CommandType `yaml:"type,omitempty"`
|
Type CommandType `yaml:"type,omitempty"`
|
||||||
|
|
||||||
|
RawOutput bytes.Buffer
|
||||||
|
|
||||||
Host string `yaml:"host,omitempty"`
|
Host string `yaml:"host,omitempty"`
|
||||||
Hosts []string `yaml:"hosts,omitempty"`
|
Hosts []string `yaml:"hosts,omitempty"`
|
||||||
|
|
||||||
@@ -80,8 +82,6 @@ type (
|
|||||||
|
|
||||||
ScriptEnvFile string `yaml:"scriptEnvFile"`
|
ScriptEnvFile string `yaml:"scriptEnvFile"`
|
||||||
|
|
||||||
SaveShellHistory bool `yaml:"saveShellHistory,omitempty"`
|
|
||||||
|
|
||||||
Output struct {
|
Output struct {
|
||||||
File string `yaml:"file,omitempty"`
|
File string `yaml:"file,omitempty"`
|
||||||
ToLog bool `yaml:"toLog,omitempty"`
|
ToLog bool `yaml:"toLog,omitempty"`
|
||||||
@@ -143,6 +143,11 @@ type (
|
|||||||
|
|
||||||
// END USER CommandType FIELDS
|
// END USER CommandType FIELDS
|
||||||
|
|
||||||
|
cmdLoggers struct {
|
||||||
|
global zerolog.Logger
|
||||||
|
cmdContxt zerolog.Logger
|
||||||
|
}
|
||||||
|
|
||||||
// BEGIN FILE COMMAND FIELDS
|
// BEGIN FILE COMMAND FIELDS
|
||||||
|
|
||||||
FileOperation string `yaml:"fileOperation,omitempty"`
|
FileOperation string `yaml:"fileOperation,omitempty"`
|
||||||
@@ -305,6 +310,7 @@ type (
|
|||||||
}
|
}
|
||||||
|
|
||||||
CmdResult struct {
|
CmdResult struct {
|
||||||
|
HostName string
|
||||||
CmdName string // Name of the command executed
|
CmdName string // Name of the command executed
|
||||||
ListName string // Name of the command list
|
ListName string // Name of the command list
|
||||||
Error error // Error encountered, if any
|
Error error // Error encountered, if any
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"git.andrewnw.xyz/CyberShell/backy/pkg/logging"
|
"git.andrewnw.xyz/CyberShell/backy/pkg/logging"
|
||||||
"git.andrewnw.xyz/CyberShell/backy/pkg/remotefetcher"
|
"git.andrewnw.xyz/CyberShell/backy/pkg/remotefetcher"
|
||||||
@@ -222,17 +223,23 @@ func CheckConfigValues(config *koanf.Koanf, file string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// collectOutput collects output from a buffer and logs it.
|
// collectOutput collects output from a buffer and logs it.
|
||||||
func collectOutput(buf *bytes.Buffer, commandName string, logger zerolog.Logger, wantOutput bool) []string {
|
func collectOutput(buf *bytes.Buffer, commandName string, logger, globalLogger zerolog.Logger, wantOutput bool) []string {
|
||||||
var outputArr []string
|
var outputArr []string
|
||||||
scanner := bufio.NewScanner(buf)
|
copyBuf := bytes.NewBuffer(buf.Bytes())
|
||||||
|
scanner := bufio.NewScanner(copyBuf)
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
line := scanner.Text()
|
line := scanner.Text()
|
||||||
clean := sanitizeString(line)
|
clean := sanitizeString(line)
|
||||||
outputArr = append(outputArr, clean)
|
outputArr = append(outputArr, clean)
|
||||||
if wantOutput {
|
if wantOutput {
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
logger.Info().Str("cmd", commandName).Str("output", clean).Send()
|
logger.Info().Str("cmd", commandName).Str("output", clean).Send()
|
||||||
|
if IsCmdStdOutEnabled() {
|
||||||
|
globalLogger.Info().Str("cmd", commandName).Str("output", clean).Send()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
buf.Reset()
|
||||||
return outputArr
|
return outputArr
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,6 +247,7 @@ func collectOutput(buf *bytes.Buffer, commandName string, logger zerolog.Logger,
|
|||||||
// while preserving tabs. This helps remove color codes and other terminal control
|
// while preserving tabs. This helps remove color codes and other terminal control
|
||||||
// characters from remote command output.
|
// characters from remote command output.
|
||||||
func sanitizeString(s string) string {
|
func sanitizeString(s string) string {
|
||||||
|
|
||||||
// Remove common ANSI CSI sequences like "\x1b[31m" etc.
|
// Remove common ANSI CSI sequences like "\x1b[31m" etc.
|
||||||
var ansiCSI = regexp.MustCompile(`\x1b\[[0-9;?]*[ -/]*[@-~]`)
|
var ansiCSI = regexp.MustCompile(`\x1b\[[0-9;?]*[ -/]*[@-~]`)
|
||||||
s = ansiCSI.ReplaceAllString(s, "")
|
s = ansiCSI.ReplaceAllString(s, "")
|
||||||
@@ -255,7 +263,10 @@ func sanitizeString(s string) string {
|
|||||||
|
|
||||||
var b strings.Builder
|
var b strings.Builder
|
||||||
for _, r := range s {
|
for _, r := range s {
|
||||||
if r == '\t' || (r >= 0x20 && r != 0x7f) {
|
if r == '\t' {
|
||||||
|
b.WriteString(" ")
|
||||||
|
}
|
||||||
|
if r >= 0x20 && r != 0x7f {
|
||||||
b.WriteRune(r)
|
b.WriteRune(r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -410,7 +421,7 @@ func parsePackageVersion(output string, cmdCtxLogger zerolog.Logger, command *Co
|
|||||||
pkgVersionOnSystem, err := command.pkgMan.ParseRemotePackageManagerVersionOutput(output)
|
pkgVersionOnSystem, err := command.pkgMan.ParseRemotePackageManagerVersionOutput(output)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cmdCtxLogger.Error().AnErr("Error parsing package version output", err).Send()
|
cmdCtxLogger.Error().AnErr("Error parsing package version output", err).Send()
|
||||||
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.Output.ToLog), fmt.Errorf("error parsing package version output: %v", err)
|
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.cmdLoggers.global, command.Output.ToLog), fmt.Errorf("error parsing package version output: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, p := range pkgVersionOnSystem {
|
for _, p := range pkgVersionOnSystem {
|
||||||
@@ -453,9 +464,9 @@ func parsePackageVersion(output string, cmdCtxLogger zerolog.Logger, command *Co
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if errs == nil {
|
if errs == nil {
|
||||||
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.Output.ToLog), nil
|
return collectOutput(&cmdOutBuf, command.Name, command.cmdLoggers.cmdContxt, command.cmdLoggers.global, command.Output.ToLog), nil
|
||||||
}
|
}
|
||||||
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.Output.ToLog), fmt.Errorf("error parsing package version output: %v", errs)
|
return collectOutput(&cmdOutBuf, command.Name, command.cmdLoggers.cmdContxt, command.cmdLoggers.global, command.Output.ToLog), fmt.Errorf("error parsing package version output: %v", errs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getPackageIndexFromCommand(command *Command, name string) int {
|
func getPackageIndexFromCommand(command *Command, name string) int {
|
||||||
|
|||||||
7
pkg/backy/validate.go
Normal file
7
pkg/backy/validate.go
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package backy
|
||||||
|
|
||||||
|
func Validate(c Command) {
|
||||||
|
if c.Cmd != "" {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user