Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1d2a3a8306 | ||
|
|
045dedef67 | ||
|
|
60989a3905 | ||
|
|
e789924547 | ||
|
|
62ac2934dc | ||
|
|
c078632691 | ||
|
|
52e25aad77 | ||
|
|
9f996f60c6 | ||
|
|
4c152f8089 | ||
|
|
4d2e4ce533 | ||
|
|
9e3960ce9f | ||
|
|
b2d89352a3 |
@@ -0,0 +1,3 @@
|
||||
## v0.11.4 - 2026-02-01
|
||||
### Changed
|
||||
* Command.[name].output.file: now appends correctly to the beginning of file in an absolute path
|
||||
@@ -0,0 +1,3 @@
|
||||
## v0.11.5 - 2026-02-10
|
||||
### Changed
|
||||
* Command.Type: scriptFile no longer requests psudoterminal
|
||||
@@ -0,0 +1,3 @@
|
||||
## v0.12.0 - 2026-02-11
|
||||
### Changed
|
||||
* internal logic handling for cron webserver
|
||||
@@ -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
|
||||
@@ -0,0 +1,5 @@
|
||||
## v0.13.0 - 2026-08-15
|
||||
### Changed
|
||||
* List subcommands print out everything with no arguments
|
||||
### Fixed
|
||||
* Error hooks no longer return after one is executed
|
||||
Vendored
+10
-1
@@ -10,5 +10,14 @@
|
||||
"nikoksr",
|
||||
"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,33 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html),
|
||||
and is generated by [Changie](https://github.com/miniscruff/changie).
|
||||
|
||||
|
||||
## v0.13.0 - 2026-08-15
|
||||
### Changed
|
||||
* List subcommands print out everything with no arguments
|
||||
### Fixed
|
||||
* Error hooks no longer return after one is executed
|
||||
|
||||
## 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
|
||||
### Changed
|
||||
* internal logic handling for cron webserver
|
||||
|
||||
## v0.11.5 - 2026-02-10
|
||||
### Changed
|
||||
* Command.Type: scriptFile no longer requests psudoterminal
|
||||
|
||||
## v0.11.4 - 2026-02-01
|
||||
### Changed
|
||||
* Command.[name].output.file: now appends correctly to the beginning of file in an absolute path
|
||||
|
||||
## v0.11.3 - 2026-01-31
|
||||
### Added
|
||||
* Command: saveShellHistory for scriptFile commands over SSH
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@ import (
|
||||
var (
|
||||
hostExecCommand = &cobra.Command{
|
||||
Use: "host [--command=command1 --command=command2 ... | -c command1 -c command2 ...] [--hosts=host1 --hosts=hosts2 ... | -m host1 -m host2 ...] ",
|
||||
Short: "Runs command defined in config file on the hosts in order specified.",
|
||||
Short: "Specify command(s) defined in config file on the host in order specified.",
|
||||
Long: "Host executes specified commands on the hosts defined in config file.\nUse the --commands or -c flag to choose the commands.",
|
||||
Run: Host,
|
||||
}
|
||||
|
||||
+42
-28
@@ -5,8 +5,10 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"git.andrewnw.xyz/CyberShell/backy/pkg/backy"
|
||||
"git.andrewnw.xyz/CyberShell/backy/pkg/logging"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@@ -15,28 +17,26 @@ var (
|
||||
listCmd = &cobra.Command{
|
||||
Use: "list [command]",
|
||||
Short: "List commands, lists, or hosts defined in config file.",
|
||||
Long: "List commands, lists, or hosts defined in config file",
|
||||
Long: "List commands, lists, or hosts defined in config file. The subcommands take zero or more arguments to print specific commands or lists",
|
||||
}
|
||||
|
||||
listCmds = &cobra.Command{
|
||||
Use: "cmds [cmd1 cmd2 cmd3...]",
|
||||
Short: "List commands defined in config file.",
|
||||
Long: "List commands defined in config file",
|
||||
Short: "Prints commands defined in config file.",
|
||||
Long: "Prints commands defined in config file. Pass no arguments to print all commands",
|
||||
Run: ListCommands,
|
||||
}
|
||||
listCmdLists = &cobra.Command{
|
||||
Use: "lists [list1 list2 ...]",
|
||||
Short: "List lists defined in config file.",
|
||||
Long: "List lists defined in config file",
|
||||
Short: "Prints lists defined in config file.",
|
||||
Long: "Prints lists defined in config file. Pass no arguments to print all lists",
|
||||
Run: ListCommandLists,
|
||||
}
|
||||
)
|
||||
|
||||
var listsToList []string
|
||||
var cmdsToList []string
|
||||
|
||||
func init() {
|
||||
listCmd.AddCommand(listCmds, listCmdLists)
|
||||
parseS3Config()
|
||||
|
||||
}
|
||||
|
||||
@@ -46,13 +46,6 @@ func ListCommands(cmd *cobra.Command, args []string) {
|
||||
// - cmds
|
||||
// - lists
|
||||
// - if none, list all commands
|
||||
if len(args) > 0 {
|
||||
cmdsToList = args
|
||||
} else {
|
||||
logging.ExitWithMSG("Error: list cmds subcommand needs commands to list", 1, nil)
|
||||
}
|
||||
|
||||
parseS3Config()
|
||||
|
||||
opts := backy.NewConfigOptions(configFile,
|
||||
backy.SetLogFile(logFile),
|
||||
@@ -61,21 +54,28 @@ func ListCommands(cmd *cobra.Command, args []string) {
|
||||
opts.InitConfig()
|
||||
opts.ParseConfigurationFile()
|
||||
|
||||
for _, v := range cmdsToList {
|
||||
opts.ListCommand(v)
|
||||
if len(args) > 0 {
|
||||
for _, v := range args {
|
||||
opts.ListCommand(v)
|
||||
}
|
||||
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if len(opts.Cmds) == 0 {
|
||||
fmt.Println("No commands defined in config file")
|
||||
os.Exit(1)
|
||||
}
|
||||
for c := range opts.Cmds {
|
||||
println()
|
||||
println()
|
||||
println("---------------------------------------------------------------------------------")
|
||||
opts.ListCommand(c)
|
||||
}
|
||||
}
|
||||
|
||||
func ListCommandLists(cmd *cobra.Command, args []string) {
|
||||
|
||||
parseS3Config()
|
||||
|
||||
if len(args) > 0 {
|
||||
listsToList = args
|
||||
} else {
|
||||
logging.ExitWithMSG("Error: lists subcommand needs lists", 1, nil)
|
||||
}
|
||||
|
||||
opts := backy.NewConfigOptions(configFile,
|
||||
backy.SetLogFile(logFile),
|
||||
backy.SetHostsConfigFile(hostsConfigFile))
|
||||
@@ -83,8 +83,22 @@ func ListCommandLists(cmd *cobra.Command, args []string) {
|
||||
opts.InitConfig()
|
||||
opts.ParseConfigurationFile()
|
||||
|
||||
for _, v := range listsToList {
|
||||
opts.ListCommandList(v)
|
||||
if len(args) > 0 {
|
||||
for _, v := range args {
|
||||
opts.ListCommandList(v)
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if len(opts.CmdConfigLists) == 0 {
|
||||
fmt.Println("No command lists defined in config file")
|
||||
os.Exit(1)
|
||||
}
|
||||
for c := range opts.CmdConfigLists {
|
||||
println()
|
||||
println()
|
||||
println("---------------------------------------------------------------------------------")
|
||||
opts.ListCommandList(c)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
/*
|
||||
|
||||
*/
|
||||
|
||||
}
|
||||
+1
-1
@@ -7,7 +7,7 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
const versionStr = "0.11.3"
|
||||
const versionStr = "0.13.0"
|
||||
|
||||
var (
|
||||
versionCmd = &cobra.Command{
|
||||
|
||||
@@ -88,7 +88,7 @@ Usage:
|
||||
backy exec [command]
|
||||
|
||||
Available Commands:
|
||||
host Runs command defined in config file on the hosts in order specified.
|
||||
host Specify command(s) defined in config file on the host in order specified.
|
||||
hosts Runs command defined in config file on the hosts in order specified.
|
||||
|
||||
Flags:
|
||||
@@ -153,14 +153,14 @@ Global Flags:
|
||||
## list
|
||||
|
||||
```
|
||||
List commands, lists, or hosts defined in config file
|
||||
List commands, lists, or hosts defined in config file. The subcommands take zero or more arguments to print specific commands or lists
|
||||
|
||||
Usage:
|
||||
backy list [command]
|
||||
|
||||
Available Commands:
|
||||
cmds List commands defined in config file.
|
||||
lists List lists defined in config file.
|
||||
cmds Prints commands defined in config file.
|
||||
lists Prints lists defined in config file.
|
||||
|
||||
Flags:
|
||||
-h, --help help for list
|
||||
@@ -178,7 +178,7 @@ Use "backy list [command] --help" for more information about a command.
|
||||
## list cmds
|
||||
|
||||
```
|
||||
List commands defined in config file
|
||||
Prints commands defined in config file. Pass no arguments to print all commands
|
||||
|
||||
Usage:
|
||||
backy list cmds [cmd1 cmd2 cmd3...] [flags]
|
||||
@@ -197,7 +197,7 @@ Global Flags:
|
||||
## list lists
|
||||
|
||||
```
|
||||
List lists defined in config file
|
||||
Prints lists defined in config file. Pass no arguments to print all lists
|
||||
|
||||
Usage:
|
||||
backy list lists [list1 list2 ...] [flags]
|
||||
|
||||
@@ -2,14 +2,23 @@
|
||||
title: Exec
|
||||
---
|
||||
|
||||
The `exec` subcommand can do some things that the configuration file can't do yet. The command `exec host` can execute commands on many hosts.
|
||||
~~The `exec` subcommand can do some things that the configuration file can't do yet. The command `exec host` can execute commands on many hosts.~~
|
||||
|
||||
{{% notice info %}}
|
||||
For now only the `exec host` sub-command is implemented. More to come.
|
||||
{{% /notice %}}
|
||||
|
||||
{{% notice warning %}}
|
||||
The config file's `hosts` are overridden by this command. Hooks are not altered. For consistent results, create a local config file with only the hosts you want to use.
|
||||
{{% /notice %}}
|
||||
|
||||
|
||||
`exec host` takes the following arguments:
|
||||
|
||||
```sh
|
||||
-c, --commands strings Accepts space-separated names of commands.
|
||||
-h, --help help for host
|
||||
-m, --hosts strings Accepts space-separated names of hosts.
|
||||
-c, --command stringArray Accepts space-separated names of commands. Specify multiple times for multiple commands.
|
||||
-h, --help help for host
|
||||
-m, --hosts stringArray Accepts space-separated names of hosts. Specify multiple times for multiple hosts.
|
||||
```
|
||||
|
||||
The commands have to be defined in the config file. The hosts need to at least be in the ssh_config(5) file.
|
||||
|
||||
@@ -3,7 +3,7 @@ title: List
|
||||
---
|
||||
|
||||
|
||||
List commands, lists, or hosts defined in config file
|
||||
List commands, lists, or hosts defined in config file. The subcommands take zero or more arguments to print specific commands or lists
|
||||
|
||||
Usage:
|
||||
```
|
||||
@@ -11,8 +11,8 @@ Usage:
|
||||
```
|
||||
|
||||
Available Commands:
|
||||
cmds List commands defined in config file.
|
||||
lists List lists defined in config file.
|
||||
cmds Prints commands defined in config file.
|
||||
lists Prints lists defined in config file.
|
||||
|
||||
Flags:
|
||||
```
|
||||
|
||||
Executable
+5
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
go install .
|
||||
systemctl --user stop backy
|
||||
cp ~/go/bin/backy ~/prodConfigs/backups/backy/backy
|
||||
systemctl --user start backy
|
||||
+48
-57
@@ -14,7 +14,6 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"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.
|
||||
func makeCmdOutWriters(buf *bytes.Buffer, outputFile string) (io.Writer, *os.File, error) {
|
||||
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 != "" {
|
||||
|
||||
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.
|
||||
// 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}
|
||||
}
|
||||
|
||||
@@ -227,11 +205,13 @@ func (opts *ConfigOpts) ExecCommandOnHostsParallel(cmdName string) ([]CmdResult,
|
||||
// shallow copy to avoid races
|
||||
local := *cmdObj
|
||||
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
|
||||
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}
|
||||
return
|
||||
// _, 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
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -252,6 +233,9 @@ func (opts *ConfigOpts) ExecCommandOnHostsParallel(cmdName string) ([]CmdResult,
|
||||
var results []CmdResult
|
||||
for r := range resultsCh {
|
||||
results = append(results, r)
|
||||
if r.Error != nil {
|
||||
opts.Logger.Info().AnErr("error", r.Error).Str("cmd", r.CmdName).Send()
|
||||
}
|
||||
}
|
||||
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")
|
||||
return nil, fmt.Errorf("both 'host' and 'hosts' are set; please set one or the other")
|
||||
} 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
|
||||
}
|
||||
|
||||
@@ -296,6 +284,7 @@ func (command *Command) RunCmd(cmdCtxLogger zerolog.Logger, opts *ConfigOpts) ([
|
||||
if command.Type == UserCommandType {
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
userHome, err = localCMD.CombinedOutput()
|
||||
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))
|
||||
@@ -445,33 +434,33 @@ func (command *Command) RunCmd(cmdCtxLogger zerolog.Logger, opts *ConfigOpts) ([
|
||||
if _, err := os.Stat(userSshDir); os.IsNotExist(err) {
|
||||
err := os.MkdirAll(userSshDir, 0700)
|
||||
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) {
|
||||
_, err := os.Create(fmt.Sprintf("%s/authorized_keys", userSshDir))
|
||||
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)
|
||||
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()
|
||||
for _, k := range command.UserSshPubKeys {
|
||||
buf := bytes.NewBufferString(k)
|
||||
cmdCtxLogger.Info().Str("key", k).Msg("adding SSH key")
|
||||
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))
|
||||
_, err = localCMD.CombinedOutput()
|
||||
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
|
||||
currentCmd := cmdToRun.Name
|
||||
fieldsMap["cmd"] = currentCmd
|
||||
cmdLogger = cmdToRun.GenerateLogger(opts)
|
||||
cmdLogger.Info().Fields(fieldsMap).Send()
|
||||
cmdToRun.GenerateLogger(opts)
|
||||
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)
|
||||
|
||||
if runErr != nil {
|
||||
@@ -559,10 +548,10 @@ func cmdListWorkerWithHosts(msgTemps *msgTemplates, jobs <-chan *CmdList, hosts
|
||||
commandExecuted = cmdToRun
|
||||
currentCmd := cmdToRun.Name
|
||||
fieldsMap["cmd"] = currentCmd
|
||||
cmdLogger = cmdToRun.GenerateLogger(opts)
|
||||
cmdLogger.Info().Fields(fieldsMap).Send()
|
||||
cmdToRun.GenerateLogger(opts)
|
||||
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)
|
||||
|
||||
if runErr != nil {
|
||||
@@ -705,8 +694,8 @@ func cmdListWorkerExecuteCommandsInParallel(msgTemps *msgTemplates, jobs <-chan
|
||||
cmdToRun.Host = host.Host
|
||||
cmdToRun.RemoteHost = host
|
||||
}
|
||||
cmdLogger = cmdToRun.GenerateLogger(opts)
|
||||
cmdLogger.Info().Fields(fieldsMap).Send()
|
||||
cmdToRun.GenerateLogger(opts)
|
||||
cmdToRun.cmdLoggers.cmdContxt.Info().Fields(fieldsMap).Send()
|
||||
print("Running cmd on: ", host.Host, "\n")
|
||||
|
||||
go func(cmd string, host *Host) {
|
||||
@@ -714,7 +703,7 @@ func cmdListWorkerExecuteCommandsInParallel(msgTemps *msgTemplates, jobs <-chan
|
||||
currentCmd := cmdToRun.Name
|
||||
fieldsMap["cmd"] = currentCmd
|
||||
|
||||
outputArr, runErr := cmdToRun.RunCmd(cmdLogger, opts)
|
||||
outputArr, runErr := cmdToRun.RunCmd(cmdToRun.cmdLoggers.cmdContxt, opts)
|
||||
if runErr != nil {
|
||||
cmdLogger.Err(runErr).Send()
|
||||
cmdToRun.ExecuteHooks("error", opts)
|
||||
@@ -890,8 +879,8 @@ func (opts *ConfigOpts) ExecuteListOnHosts(lists []string, parallel bool) {
|
||||
func (opts *ConfigOpts) ExecuteCmds() {
|
||||
for _, cmd := range opts.executeCmds {
|
||||
cmdToRun := opts.Cmds[cmd]
|
||||
cmdLogger := cmdToRun.GenerateLogger(opts)
|
||||
_, runErr := cmdToRun.RunCmd(cmdLogger, opts)
|
||||
cmdToRun.GenerateLogger(opts)
|
||||
_, runErr := cmdToRun.RunCmd(cmdToRun.cmdLoggers.cmdContxt, opts)
|
||||
if runErr != nil {
|
||||
opts.Logger.Err(runErr).Send()
|
||||
cmdToRun.ExecuteHooks("error", opts)
|
||||
@@ -955,7 +944,6 @@ func (cmd *Command) ExecuteHooks(hookType string, opts *ConfigOpts) {
|
||||
cmdLogger.Info().Msgf("Running error hook command %s", v)
|
||||
// URGENT: Never returns
|
||||
_, _ = errCmd.RunCmd(cmdLogger, opts)
|
||||
return
|
||||
}
|
||||
|
||||
case "success":
|
||||
@@ -979,30 +967,29 @@ func (cmd *Command) ExecuteHooks(hookType string, opts *ConfigOpts) {
|
||||
}
|
||||
}
|
||||
|
||||
func (cmd *Command) GenerateLogger(opts *ConfigOpts) zerolog.Logger {
|
||||
cmdLogger := opts.Logger.With().
|
||||
func (cmd *Command) GenerateLogger(opts *ConfigOpts) {
|
||||
cmd.cmdLoggers.cmdContxt = opts.Logger.With().
|
||||
Str("Backy-cmd", cmd.Name).Str("Host", "local machine").
|
||||
Logger()
|
||||
|
||||
if !IsHostLocal(cmd.Host) {
|
||||
cmdLogger = opts.Logger.With().
|
||||
cmd.cmdLoggers.cmdContxt = opts.Logger.With().
|
||||
Str("Backy-cmd", cmd.Name).Str("Host", cmd.Host).
|
||||
Logger()
|
||||
}
|
||||
return cmdLogger
|
||||
}
|
||||
|
||||
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").
|
||||
Logger()
|
||||
|
||||
if !IsHostLocal(cmd.Host) {
|
||||
cmdLogger = logger.With().
|
||||
cmd.cmdLoggers.cmdContxt = logger.With().
|
||||
Str("Backy-cmd", cmd.Name).Str("Host", cmd.Host).
|
||||
Logger()
|
||||
}
|
||||
return cmdLogger
|
||||
return cmd.cmdLoggers.cmdContxt
|
||||
}
|
||||
|
||||
func (opts *ConfigOpts) ExecCmdsOnHosts(cmdList []string, hostsList []string) {
|
||||
@@ -1014,7 +1001,8 @@ func (opts *ConfigOpts) ExecCmdsOnHosts(cmdList []string, hostsList []string) {
|
||||
cmd.RemoteHost = host
|
||||
cmd.Host = h
|
||||
if IsHostLocal(h) {
|
||||
_, err := cmd.RunCmd(cmd.GenerateLogger(opts), opts)
|
||||
cmd.GenerateLogger(opts)
|
||||
_, err := cmd.RunCmd(cmd.cmdLoggers.cmdContxt, opts)
|
||||
if err != nil {
|
||||
opts.Logger.Err(err).Str("host", h).Str("cmd", c).Send()
|
||||
}
|
||||
@@ -1022,7 +1010,8 @@ func (opts *ConfigOpts) ExecCmdsOnHosts(cmdList []string, hostsList []string) {
|
||||
|
||||
cmd.Host = host.Host
|
||||
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 {
|
||||
opts.Logger.Err(err).Str("host", h).Str("cmd", c).Send()
|
||||
}
|
||||
@@ -1041,7 +1030,8 @@ func (opts *ConfigOpts) ExecCmdsOnHostsInParallel(cmdList []string, hostsList []
|
||||
cmd.RemoteHost = host
|
||||
cmd.Host = h
|
||||
if IsHostLocal(h) {
|
||||
_, err := cmd.RunCmd(cmd.GenerateLogger(opts), opts)
|
||||
cmd.GenerateLogger(opts)
|
||||
_, err := cmd.RunCmd(cmd.cmdLoggers.cmdContxt, opts)
|
||||
if err != nil {
|
||||
opts.Logger.Err(err).Str("host", h).Str("cmd", c).Send()
|
||||
}
|
||||
@@ -1049,7 +1039,8 @@ func (opts *ConfigOpts) ExecCmdsOnHostsInParallel(cmdList []string, hostsList []
|
||||
|
||||
cmd.Host = host.Host
|
||||
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 {
|
||||
opts.Logger.Err(err).Str("host", h).Str("cmd", c).Send()
|
||||
}
|
||||
|
||||
+3
-1
@@ -73,6 +73,8 @@ func (opts *ConfigOpts) Cron() {
|
||||
srv := server.NewServer(s, opts.GoCron.Port)
|
||||
// srv := server.NewServer(scheduler, 8080, server.WithTitle("My Custom Scheduler")) // with custom title if you want to customize the title of the UI (optional)
|
||||
opts.Logger.Info().Msgf("GoCron UI available at http://%s", opts.GoCron.BindAddress)
|
||||
opts.Logger.Fatal().Msg(http.ListenAndServe(opts.GoCron.BindAddress, srv.Router).Error())
|
||||
if err := http.ListenAndServe(opts.GoCron.BindAddress, srv.Router); err != nil {
|
||||
opts.Logger.Fatal().Msg(err.Error())
|
||||
}
|
||||
select {} // wait forever
|
||||
}
|
||||
|
||||
@@ -101,6 +101,10 @@ func (r *RemoteFileCommandExecutor) copyFile(source, destination string, Perms f
|
||||
return nil
|
||||
}
|
||||
|
||||
if sshClient == nil {
|
||||
return fmt.Errorf("SSH Client is nil")
|
||||
}
|
||||
|
||||
client, err := sftp.NewClient(sshClient)
|
||||
|
||||
if err != nil {
|
||||
|
||||
+50
-12
@@ -36,6 +36,8 @@ func (opts *ConfigOpts) ListCommand(cmd string) {
|
||||
// print the command's information
|
||||
if cmdFound {
|
||||
|
||||
print("Backy Command: ")
|
||||
println(cmd)
|
||||
println("Command: ")
|
||||
|
||||
print(cmdInfo.Cmd)
|
||||
@@ -45,17 +47,57 @@ func (opts *ConfigOpts) ListCommand(cmd string) {
|
||||
print(v) // print command arg
|
||||
}
|
||||
|
||||
// is it remote or local
|
||||
if !IsHostLocal(cmdInfo.Host) {
|
||||
if cmdInfo.Type.String() != "" {
|
||||
println()
|
||||
print("Host: ", cmdInfo.Host)
|
||||
print("Type: ", cmdInfo.Type.String())
|
||||
println()
|
||||
}
|
||||
|
||||
if cmdInfo.Hosts != nil {
|
||||
for n, h := range cmdInfo.Hosts {
|
||||
println()
|
||||
fmt.Printf("Host %d: %s", n, h)
|
||||
println()
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
println()
|
||||
print("Host: Runs on Local Machine\n\n")
|
||||
// is it remote or local
|
||||
if !IsHostLocal(cmdInfo.Host) {
|
||||
println()
|
||||
print("Host: ", cmdInfo.Host)
|
||||
println()
|
||||
|
||||
} else {
|
||||
|
||||
println()
|
||||
print("Host: Runs on Local Machine\n\n")
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if len(cmdInfo.Environment) > 0 {
|
||||
fmt.Print("Environment: ")
|
||||
for _, env := range cmdInfo.Environment {
|
||||
fmt.Printf("%s ", env)
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
if cmdInfo.PackageManager != "" {
|
||||
fmt.Printf("\nPackage Manager: %s\n", cmdInfo.PackageManager)
|
||||
if len(cmdInfo.Packages) > 0 {
|
||||
for _, pkg := range cmdInfo.Packages {
|
||||
fmt.Printf("Package: %v\n", pkg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if cmdInfo.Username != "" || cmdInfo.UserID != "" {
|
||||
fmt.Println("\nUser Settings:")
|
||||
fmt.Printf("Username: %s\n", cmdInfo.Username)
|
||||
fmt.Printf("UserID: %s\n", cmdInfo.UserID)
|
||||
}
|
||||
|
||||
if cmdInfo.Dir != nil {
|
||||
@@ -64,11 +106,6 @@ func (opts *ConfigOpts) ListCommand(cmd string) {
|
||||
println()
|
||||
}
|
||||
|
||||
if cmdInfo.Type.String() != "" {
|
||||
print("Type: ", cmdInfo.Type.String())
|
||||
println()
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
fmt.Printf("Command %s not found. Check spelling.\n", cmd)
|
||||
@@ -100,9 +137,10 @@ func (opts *ConfigOpts) ListCommandList(list string) {
|
||||
println("List: ", list)
|
||||
println()
|
||||
|
||||
for _, v := range listInfo.Order {
|
||||
for n, cmd := range listInfo.Order {
|
||||
println()
|
||||
opts.ListCommand(v)
|
||||
fmt.Printf("Command %d: \n", n+1)
|
||||
opts.ListCommand(cmd)
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
@@ -2,6 +2,7 @@ package backy
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
@@ -75,6 +76,17 @@ func (metricFile *MetricFile) SaveToFile() error {
|
||||
}
|
||||
|
||||
func LoadMetricsFromFile(filename string) (*MetricFile, error) {
|
||||
if err := testFile(filename); err != nil {
|
||||
|
||||
_, createErr := os.Create(filename)
|
||||
if createErr != nil {
|
||||
return nil, fmt.Errorf("error creating file and root cause: %w", err)
|
||||
}
|
||||
metricData := NewMetricsFromFile(filename)
|
||||
data, _ := json.MarshalIndent(metricData, "", " ")
|
||||
os.WriteFile(filename, data, 0600)
|
||||
return metricData, nil
|
||||
}
|
||||
jsonData, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
+57
-62
@@ -1,67 +1,62 @@
|
||||
package backy
|
||||
|
||||
// import (
|
||||
// "testing"
|
||||
// "time"
|
||||
// )
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// func TestAddingMetricsForCommand(t *testing.T) {
|
||||
func TestAddingMetricsForCommand(t *testing.T) {
|
||||
// Create a new MetricFile
|
||||
metricFile := NewMetricsFromFile("test_metrics.json")
|
||||
metricFile, err := LoadMetricsFromFile(metricFile.Filename)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to load metrics from file: %v", err)
|
||||
}
|
||||
// Add metrics for a command
|
||||
commandName := "test_command"
|
||||
if metricFile.CommandMetrics != nil {
|
||||
if _, exists := metricFile.CommandMetrics[commandName]; !exists {
|
||||
metricFile.CommandMetrics[commandName] = NewMetrics()
|
||||
}
|
||||
}
|
||||
// Update the metrics for the command
|
||||
executionTime := 1.8 // Example execution time in seconds
|
||||
success := true // Example success status
|
||||
metricFile.CommandMetrics[commandName].Update(success, executionTime, time.Now())
|
||||
// Check if the metrics were updated correctly
|
||||
if metricFile.CommandMetrics[commandName].SuccessfulExecutions > 50 {
|
||||
t.Errorf("Expected 1 successful execution, got %d", metricFile.CommandMetrics[commandName].SuccessfulExecutions)
|
||||
}
|
||||
if metricFile.CommandMetrics[commandName].TotalExecutions > 50 {
|
||||
t.Errorf("Expected 1 total execution, got %d", metricFile.CommandMetrics[commandName].TotalExecutions)
|
||||
}
|
||||
if metricFile.CommandMetrics[commandName].TotalExecutionTime != executionTime {
|
||||
t.Errorf("Expected execution time %f, got %f", executionTime, metricFile.CommandMetrics[commandName].TotalExecutionTime)
|
||||
}
|
||||
err = metricFile.SaveToFile()
|
||||
if err != nil {
|
||||
t.Errorf("Failed to save metrics to file: %v", err)
|
||||
}
|
||||
listName := "test_list"
|
||||
if _, exists := metricFile.ListMetrics[listName]; !exists {
|
||||
metricFile.ListMetrics[listName] = NewMetrics()
|
||||
}
|
||||
// Update the metrics for the list
|
||||
metricFile.ListMetrics[listName].Update(success, executionTime, time.Now())
|
||||
if metricFile.ListMetrics[listName].SuccessfulExecutions > 50 {
|
||||
t.Errorf("Expected 1 successful execution for list, got %d", metricFile.ListMetrics[listName].SuccessfulExecutions)
|
||||
}
|
||||
if metricFile.ListMetrics[listName].TotalExecutions > 50 {
|
||||
t.Errorf("Expected 1 total execution for list, got %d", metricFile.ListMetrics[listName].TotalExecutions)
|
||||
}
|
||||
if metricFile.ListMetrics[listName].TotalExecutionTime > executionTime {
|
||||
t.Errorf("Expected execution time %f for list, got %f", executionTime, metricFile.ListMetrics[listName].TotalExecutionTime)
|
||||
}
|
||||
|
||||
// // Create a new MetricFile
|
||||
// metricFile := NewMetricsFromFile("test_metrics.json")
|
||||
// Save the metrics to a file
|
||||
err = metricFile.SaveToFile()
|
||||
if err != nil {
|
||||
t.Errorf("Failed to save metrics to file: %v", err)
|
||||
}
|
||||
|
||||
// metricFile, err := LoadMetricsFromFile(metricFile.Filename)
|
||||
// if err != nil {
|
||||
// t.Errorf("Failed to load metrics from file: %v", err)
|
||||
// }
|
||||
|
||||
// // Add metrics for a command
|
||||
// commandName := "test_command"
|
||||
// if _, exists := metricFile.CommandMetrics[commandName]; !exists {
|
||||
// metricFile.CommandMetrics[commandName] = NewMetrics()
|
||||
// }
|
||||
|
||||
// // Update the metrics for the command
|
||||
// executionTime := 1.8 // Example execution time in seconds
|
||||
// success := true // Example success status
|
||||
// metricFile.CommandMetrics[commandName].Update(success, executionTime, time.Now())
|
||||
|
||||
// // Check if the metrics were updated correctly
|
||||
// if metricFile.CommandMetrics[commandName].SuccessfulExecutions > 50 {
|
||||
// t.Errorf("Expected 1 successful execution, got %d", metricFile.CommandMetrics[commandName].SuccessfulExecutions)
|
||||
// }
|
||||
// if metricFile.CommandMetrics[commandName].TotalExecutions > 50 {
|
||||
// t.Errorf("Expected 1 total execution, got %d", metricFile.CommandMetrics[commandName].TotalExecutions)
|
||||
// }
|
||||
// // if metricFile.CommandMetrics[commandName].TotalExecutionTime != executionTime {
|
||||
// // t.Errorf("Expected execution time %f, got %f", executionTime, metricFile.CommandMetrics[commandName].TotalExecutionTime)
|
||||
// // }
|
||||
|
||||
// err = metricFile.SaveToFile()
|
||||
// if err != nil {
|
||||
// t.Errorf("Failed to save metrics to file: %v", err)
|
||||
// }
|
||||
|
||||
// listName := "test_list"
|
||||
// if _, exists := metricFile.ListMetrics[listName]; !exists {
|
||||
// metricFile.ListMetrics[listName] = NewMetrics()
|
||||
// }
|
||||
// // Update the metrics for the list
|
||||
// metricFile.ListMetrics[listName].Update(success, executionTime, time.Now())
|
||||
// if metricFile.ListMetrics[listName].SuccessfulExecutions > 50 {
|
||||
// t.Errorf("Expected 1 successful execution for list, got %d", metricFile.ListMetrics[listName].SuccessfulExecutions)
|
||||
// }
|
||||
// if metricFile.ListMetrics[listName].TotalExecutions > 50 {
|
||||
// t.Errorf("Expected 1 total execution for list, got %d", metricFile.ListMetrics[listName].TotalExecutions)
|
||||
// }
|
||||
// // if metricFile.ListMetrics[listName].TotalExecutionTime > executionTime {
|
||||
// // t.Errorf("Expected execution time %f for list, got %f", executionTime, metricFile.ListMetrics[listName].TotalExecutionTime)
|
||||
// // }
|
||||
|
||||
// // Save the metrics to a file
|
||||
// err = metricFile.SaveToFile()
|
||||
// if err != nil {
|
||||
// t.Errorf("Failed to save metrics to file: %v", err)
|
||||
// }
|
||||
|
||||
// }
|
||||
}
|
||||
|
||||
+54
-80
@@ -10,6 +10,7 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -471,24 +472,30 @@ func (command *Command) RunCmdOnHost(cmdCtxLogger zerolog.Logger, opts *ConfigOp
|
||||
defer commandSession.Close()
|
||||
|
||||
// Set output writers
|
||||
var file *os.File
|
||||
// var file *os.File
|
||||
if !IsHostLocal(command.Host) && command.Output.File != "" {
|
||||
command.Output.File = fmt.Sprintf("%s_%s", command.RemoteHost.Host, command.Output.File)
|
||||
if filepath.IsAbs(command.Output.File) {
|
||||
fileName := filepath.Base(command.Output.File)
|
||||
fileName = fmt.Sprintf("%s_%s", command.RemoteHost.Host, fileName)
|
||||
command.Output.File = filepath.Join(filepath.Dir(command.Output.File), fileName)
|
||||
} else {
|
||||
command.Output.File = fmt.Sprintf("%s_%s", command.RemoteHost.Host, command.Output.File)
|
||||
}
|
||||
}
|
||||
|
||||
cmdOutWriters, file, err = makeCmdOutWriters(&cmdOutBuf, command.Output.File)
|
||||
cmdOutWriters, _, err = makeCmdOutWriters(&cmdOutBuf, command.Output.File)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating command output writers: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
if file != nil {
|
||||
file.Close()
|
||||
}
|
||||
}()
|
||||
// defer func() {
|
||||
// if file != nil {
|
||||
// file.Close()
|
||||
// }
|
||||
// }()
|
||||
// cmdOutWriters = logging.SetLoggingWriterForCommand(&cmdOutBuf, command.Output.File, IsCmdStdOutEnabled())
|
||||
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)
|
||||
commandSession.Stdout = cmdOutWriters
|
||||
commandSession.Stderr = cmdOutWriters
|
||||
@@ -506,13 +513,13 @@ func (command *Command) RunCmdOnHost(cmdCtxLogger zerolog.Logger, opts *ConfigOp
|
||||
// Handle command execution based on type
|
||||
switch command.Type {
|
||||
case ScriptCommandType:
|
||||
return command.runScript(commandSession, cmdCtxLogger, &cmdOutBuf)
|
||||
return command.runScript(commandSession, &cmdOutBuf)
|
||||
case RemoteScriptCommandType:
|
||||
return command.runRemoteScript(commandSession, cmdCtxLogger, &cmdOutBuf)
|
||||
return command.runRemoteScript(commandSession, &cmdOutBuf)
|
||||
case ScriptFileCommandType:
|
||||
commandSession.Stdout = nil
|
||||
commandSession.Stderr = nil
|
||||
return command.runScriptFile(commandSession, cmdCtxLogger, opts.Logger, &cmdOutBuf)
|
||||
// commandSession.Stdout = nil
|
||||
// commandSession.Stderr = nil
|
||||
return command.runScriptFile(commandSession, &cmdOutBuf)
|
||||
case PackageCommandType:
|
||||
var remoteHostPackageExecutor RemoteHostPackageExecutor
|
||||
return remoteHostPackageExecutor.RunCmdOnHost(command, commandSession, cmdCtxLogger, cmdOutBuf)
|
||||
@@ -534,18 +541,18 @@ func (command *Command) RunCmdOnHost(cmdCtxLogger zerolog.Logger, opts *ConfigOp
|
||||
userNamePass := fmt.Sprintf("%s:%s", command.Username, command.UserPassword)
|
||||
client, err := sftp.NewClient(command.RemoteHost.SshClient)
|
||||
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()
|
||||
passFilePath := fmt.Sprintf("/tmp/%s", uuidFile.String())
|
||||
passFile, passFileErr := client.Create(passFilePath)
|
||||
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))
|
||||
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)
|
||||
@@ -559,7 +566,7 @@ func (command *Command) RunCmdOnHost(cmdCtxLogger zerolog.Logger, opts *ConfigOp
|
||||
defer rmFileFunc()
|
||||
}
|
||||
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 {
|
||||
@@ -580,41 +587,41 @@ func (command *Command) RunCmdOnHost(cmdCtxLogger zerolog.Logger, opts *ConfigOp
|
||||
commandSession, _ = command.RemoteHost.createSSHSession(opts)
|
||||
userHome, err = commandSession.CombinedOutput(fmt.Sprintf("grep \"%s\" /etc/passwd | cut -d: -f6", command.Username))
|
||||
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))
|
||||
userSshDir := fmt.Sprintf("%s/.ssh", command.UserHome)
|
||||
client, err = sftp.NewClient(command.RemoteHost.SshClient)
|
||||
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)
|
||||
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))
|
||||
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)
|
||||
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()
|
||||
for _, k := range command.UserSshPubKeys {
|
||||
buf := bytes.NewBufferString(k)
|
||||
cmdCtxLogger.Info().Str("key", k).Msg("adding SSH key")
|
||||
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)
|
||||
_, err = commandSession.CombinedOutput(fmt.Sprintf("chown -R %s:%s %s", command.Username, command.Username, userHome))
|
||||
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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -622,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) {
|
||||
@@ -642,9 +649,9 @@ func checkPackageVersion(cmdCtxLogger zerolog.Logger, command *Command, commandS
|
||||
|
||||
_, parseErr := parsePackageVersion(string(cmdOut), cmdCtxLogger, command, cmdOutBuf)
|
||||
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)
|
||||
@@ -659,7 +666,7 @@ func getCommandTypeAndSetCommandInfoLabel(commandType CommandType) string {
|
||||
}
|
||||
|
||||
// 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()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -671,63 +678,29 @@ func (command *Command) runScript(session *ssh.Session, cmdCtxLogger zerolog.Log
|
||||
}
|
||||
|
||||
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.
|
||||
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()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 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)
|
||||
|
||||
stdin, _ := session.StdinPipe()
|
||||
stdout, stdOutErr := session.StdoutPipe()
|
||||
if stdOutErr != nil {
|
||||
return nil, fmt.Errorf("error getting stdout pipe: %w", stdOutErr)
|
||||
}
|
||||
session.Stdin = script
|
||||
|
||||
if err := session.Shell(); err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
stdin.Write(script.Bytes())
|
||||
|
||||
stdOutput, stdoOutReadErr := io.ReadAll(stdout)
|
||||
if err := session.Wait(); err != nil {
|
||||
stdOutBuff := bytes.NewBuffer(stdOutput)
|
||||
// outputBuf.Write(stdOutBuff.Bytes())
|
||||
// Read output
|
||||
return collectOutput(stdOutBuff, command.Name, cmdCtxLogger, LogOutputToFile), 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 shell: %w", err)
|
||||
}
|
||||
|
||||
// Read output
|
||||
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
|
||||
return collectOutput(outputBuf, command.Name, command.cmdLoggers.cmdContxt, command.cmdLoggers.global, command.Output.ToLog), nil
|
||||
}
|
||||
|
||||
// prepareScriptBuffer prepares a buffer for inline scripts.
|
||||
@@ -750,7 +723,8 @@ func (command *Command) prepareScriptBuffer() (*bytes.Buffer, error) {
|
||||
|
||||
buffer.WriteString(command.Cmd)
|
||||
for _, arg := range command.Args {
|
||||
buffer.WriteString(" " + arg)
|
||||
buffer.WriteString(" ")
|
||||
buffer.WriteString(arg)
|
||||
}
|
||||
buffer.WriteByte('\n')
|
||||
return &buffer, nil
|
||||
@@ -760,9 +734,9 @@ func (command *Command) prepareScriptBuffer() (*bytes.Buffer, error) {
|
||||
func (command *Command) prepareScriptFileBuffer() (*bytes.Buffer, error) {
|
||||
var buffer bytes.Buffer
|
||||
|
||||
if !command.SaveShellHistory {
|
||||
buffer.WriteString("unset HISTFILE\nexport HISTSIZE=0\nexport SAVEHIST=0\n")
|
||||
}
|
||||
// if !command.SaveShellHistory {
|
||||
// buffer.WriteString("unset HISTFILE\nexport HISTSIZE=0\nexport SAVEHIST=0\n")
|
||||
// }
|
||||
|
||||
for _, envVar := range command.Environment {
|
||||
fmt.Fprintf(&buffer, "export %s", envVar)
|
||||
@@ -790,7 +764,7 @@ func (command *Command) prepareScriptFileBuffer() (*bytes.Buffer, error) {
|
||||
}
|
||||
|
||||
// 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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -802,10 +776,10 @@ func (command *Command) runRemoteScript(session *ssh.Session, cmdCtxLogger zerol
|
||||
err = session.Run(command.Shell)
|
||||
|
||||
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.
|
||||
@@ -874,7 +848,7 @@ func DoesHostHaveHostName(host string) (bool, string) {
|
||||
}
|
||||
|
||||
func IsHostLocal(host string) bool {
|
||||
host = strings.ToLower(host)
|
||||
host = strings.ToLower(strings.TrimSpace(host))
|
||||
return host == "127.0.0.1" || host == "localhost" || host == ""
|
||||
}
|
||||
|
||||
@@ -904,7 +878,7 @@ func (r RemoteHostPackageExecutor) RunCmdOnHost(command *Command, commandSession
|
||||
cmdCtxLogger.Debug().Str("cmd + args", ArgsStr).Send()
|
||||
// Run simple command
|
||||
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
|
||||
}
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
Command list {{.listName }} failed.
|
||||
Backy Command: {{.CmdName}}.
|
||||
|
||||
The command run was {{.CmdName}}.
|
||||
The command executed: {{.Command}} {{ if .Args }} {{- range .Args}} {{.}} {{end}} {{end}}
|
||||
|
||||
The command executed was {{.Command}} {{ if .Args }} {{- range .Args}} {{.}} {{end}} {{end}}
|
||||
{{ if .Err }} Error: {{ .Err }}{{ end }}
|
||||
|
||||
{{ if .Err }} The error was {{ .Err }}{{ end }}
|
||||
|
||||
{{ if .Output }} The output was: {{- range .Output}} {{.}} {{end}} {{end}}
|
||||
{{ if .Output }} Output: {{- range .Output}} {{.}} {{end}} {{end}}
|
||||
|
||||
{{ if .CmdsRan }}
|
||||
The following commands ran:
|
||||
Commands executed (last one failed):
|
||||
{{- range .CmdsRan}}
|
||||
- {{. -}}
|
||||
{{end}}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
Command list {{ .listName }} completed successfully.
|
||||
|
||||
The following commands ran:
|
||||
Commands executed:
|
||||
{{- range .CmdsRan}}
|
||||
- {{. -}}
|
||||
{{end}}
|
||||
|
||||
+8
-2
@@ -58,6 +58,8 @@ type (
|
||||
// See CommandType enum further down the page for acceptable values
|
||||
Type CommandType `yaml:"type,omitempty"`
|
||||
|
||||
RawOutput bytes.Buffer
|
||||
|
||||
Host string `yaml:"host,omitempty"`
|
||||
Hosts []string `yaml:"hosts,omitempty"`
|
||||
|
||||
@@ -80,8 +82,6 @@ type (
|
||||
|
||||
ScriptEnvFile string `yaml:"scriptEnvFile"`
|
||||
|
||||
SaveShellHistory bool `yaml:"saveShellHistory,omitempty"`
|
||||
|
||||
Output struct {
|
||||
File string `yaml:"file,omitempty"`
|
||||
ToLog bool `yaml:"toLog,omitempty"`
|
||||
@@ -143,6 +143,11 @@ type (
|
||||
|
||||
// END USER CommandType FIELDS
|
||||
|
||||
cmdLoggers struct {
|
||||
global zerolog.Logger
|
||||
cmdContxt zerolog.Logger
|
||||
}
|
||||
|
||||
// BEGIN FILE COMMAND FIELDS
|
||||
|
||||
FileOperation string `yaml:"fileOperation,omitempty"`
|
||||
@@ -305,6 +310,7 @@ type (
|
||||
}
|
||||
|
||||
CmdResult struct {
|
||||
HostName string
|
||||
CmdName string // Name of the command executed
|
||||
ListName string // Name of the command list
|
||||
Error error // Error encountered, if any
|
||||
|
||||
+22
-6
@@ -16,6 +16,7 @@ import (
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.andrewnw.xyz/CyberShell/backy/pkg/logging"
|
||||
"git.andrewnw.xyz/CyberShell/backy/pkg/remotefetcher"
|
||||
@@ -222,17 +223,28 @@ func CheckConfigValues(config *koanf.Koanf, file string) {
|
||||
}
|
||||
|
||||
// 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
|
||||
scanner := bufio.NewScanner(buf)
|
||||
copyBuf := bytes.NewBuffer(buf.Bytes())
|
||||
if buf.Len() == 0 {
|
||||
logger.Info().Str("cmd", commandName).Msg("NO OUTPUT!!!!!!!")
|
||||
globalLogger.Info().Str("cmd", commandName).Msg("NO OUTPUT!!!!!!!")
|
||||
return outputArr
|
||||
}
|
||||
scanner := bufio.NewScanner(copyBuf)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
clean := sanitizeString(line)
|
||||
outputArr = append(outputArr, clean)
|
||||
if wantOutput {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
logger.Info().Str("cmd", commandName).Str("output", clean).Send()
|
||||
if IsCmdStdOutEnabled() {
|
||||
globalLogger.Info().Str("cmd", commandName).Str("output", clean).Send()
|
||||
}
|
||||
}
|
||||
}
|
||||
buf.Reset()
|
||||
return outputArr
|
||||
}
|
||||
|
||||
@@ -240,6 +252,7 @@ func collectOutput(buf *bytes.Buffer, commandName string, logger zerolog.Logger,
|
||||
// while preserving tabs. This helps remove color codes and other terminal control
|
||||
// characters from remote command output.
|
||||
func sanitizeString(s string) string {
|
||||
|
||||
// Remove common ANSI CSI sequences like "\x1b[31m" etc.
|
||||
var ansiCSI = regexp.MustCompile(`\x1b\[[0-9;?]*[ -/]*[@-~]`)
|
||||
s = ansiCSI.ReplaceAllString(s, "")
|
||||
@@ -255,7 +268,10 @@ func sanitizeString(s string) string {
|
||||
|
||||
var b strings.Builder
|
||||
for _, r := range s {
|
||||
if r == '\t' || (r >= 0x20 && r != 0x7f) {
|
||||
if r == '\t' {
|
||||
b.WriteString(" ")
|
||||
}
|
||||
if r >= 0x20 && r != 0x7f {
|
||||
b.WriteRune(r)
|
||||
}
|
||||
}
|
||||
@@ -410,7 +426,7 @@ func parsePackageVersion(output string, cmdCtxLogger zerolog.Logger, command *Co
|
||||
pkgVersionOnSystem, err := command.pkgMan.ParseRemotePackageManagerVersionOutput(output)
|
||||
if err != nil {
|
||||
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 {
|
||||
@@ -453,9 +469,9 @@ func parsePackageVersion(output string, cmdCtxLogger zerolog.Logger, command *Co
|
||||
}
|
||||
}
|
||||
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 {
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package backy
|
||||
|
||||
func Validate(c Command) {
|
||||
if c.Cmd != "" {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
# localhost:2222 SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13.14
|
||||
# localhost:2222 SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13.14
|
||||
[localhost]:2222 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDATufWA1HRnNayIQLjSpA2+P9N6h0WF+jP+abMaINlZkiHFnFVDAoqD5/onVXymskrgQaKEYmBOs+Kv0t+Acvdor2IcvYgFueSm+jkslpSK/uuf1mx0gVJO77S2BIjqyWtUzVv96Iy4Gjt2RsrnalgYNYmi3OyPkG0IUA+3Im+2gztSECCy+nW3R/vaoPLwr4kImpLlrijcSHc4mHOY6BurrcWKNuGrsvTAOKgUZqlya6uDd+yD7fUfsmL1MqBKwZqfP3JAdp/Dd+laNNGcvEM4WhzYFSPfhqblewD0rjbto9MSOSXLyQz5RPmdITj/m5M4lj2ECmcI2gzraDMoj8ZkuJAss50oX6fmVUZestN5jlz7Y7XKEvXuH8qfLHKwaOUTZlcGbfAMz6uSrh8DNT6KzRG4j5nZ9Z5pTn1huz/p6jnJUGuHt2Ez3EK+isM+sHS6TntXavIkebaq7ErcBCO8A1fZFZlhlHoI9o9W62tMY7gbtlGodW8dKxK89+1a88=
|
||||
# localhost:2222 SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13.14
|
||||
[localhost]:2222 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBK9fEYfiGGgu0Eh7X2JT4jR4+utcfpm6Ee+Cer1x/XbMHzCPZg6YmYy6OaCSms/0VJ/QWxD+0HlsO7sqO5oeO60=
|
||||
# localhost:2222 SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13.14
|
||||
[localhost]:2222 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKT5+Cbi/ynOAPzwv0IaOVBtGFYtW33LIvNUuBKYqqyJ
|
||||
# localhost:2222 SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13.14
|
||||
# localhost:2222 SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13.16
|
||||
[localhost]:2222 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDZi/dbBsgthhSj9kf4SIxTZCX+thRmvSgQ+ogC0yyHvoQrIio0Pu2MA66S779LE7Zng3CJ1lxiesffqdiTdcv8mrlnwHlzshNy4trl85NOawdNpwwlY8aiYQkxifXqAXSoHlgG+AlrbLcZ4q0YbeLnwgB3eEywN+eCHuJdetNr1Lq9NkqiGKltBIVYxHofEkUj+jBTJCyPDcp7/CKSYXhcELCaSw8vBkB7+PfsYYy0+TPXspiu/euLZfiagh6ik2fMcfc0k5P4Yn0GRt5Uk4jr85lob7ucO6Q2VPJpfavSjetcqa/oL6peEOJ4/SQzzrbxmh0I/5Tq76lTtrFT+R+tfqn0+n11FkNhS85IulmpdD2kLPGE6yH+vfjYrYD5TdH0qiOltqfXuiJCekRryoU9oWS/VSDi7aEVPryCQWf0IUqj05F/sa4RRtO0k7UOYL8NwJTS8q02sCt+/W+nX03f6XNoG7K57MZlYnREUGS6scqnvktb9bwKBunDZePMwZk=
|
||||
# localhost:2222 SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13.16
|
||||
[localhost]:2222 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEy0ReNz4EO68wUiQGHmkKOwLQhAUhY9oCyzpyROUWM0FWQId124SKPPcLD3DcYHQpofdG8h11NZ8Dyk1g/mW8M=
|
||||
# localhost:2222 SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13.16
|
||||
# localhost:2222 SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13.16
|
||||
# localhost:2222 SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13.16
|
||||
[localhost]:2222 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIArPKqZ3cHWzv0o6RUko8TNpr95olBJAzHRiPIvYD7bH
|
||||
|
||||
@@ -4,4 +4,5 @@ set -euo pipefail
|
||||
DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
echo "Starting SSH test container (building if needed)..."
|
||||
docker compose -f "$DIR/compose.yml" up -d --build
|
||||
ssh-keyscan -p 2222 localhost > "$DIR/known_hosts"
|
||||
echo "Container started on localhost:2222"
|
||||
Reference in New Issue
Block a user