diff --git a/cmd/host.go b/cmd/host.go index 79847ff..8711abe 100755 --- a/cmd/host.go +++ b/cmd/host.go @@ -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, } diff --git a/cmd/list.go b/cmd/list.go index c557a4d..42eb149 100755 --- a/cmd/list.go +++ b/cmd/list.go @@ -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) } } diff --git a/docs/content/cli/_index.md b/docs/content/cli/_index.md index 9bee796..aa7c610 100644 --- a/docs/content/cli/_index.md +++ b/docs/content/cli/_index.md @@ -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] diff --git a/docs/content/cli/exec.md b/docs/content/cli/exec.md index fff7afd..03e9b0f 100755 --- a/docs/content/cli/exec.md +++ b/docs/content/cli/exec.md @@ -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. diff --git a/docs/content/cli/list.md b/docs/content/cli/list.md index ce4fbad..74005f1 100755 --- a/docs/content/cli/list.md +++ b/docs/content/cli/list.md @@ -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: ``` diff --git a/pkg/backy/backy.go b/pkg/backy/backy.go index 87b1092..406286d 100755 --- a/pkg/backy/backy.go +++ b/pkg/backy/backy.go @@ -944,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": diff --git a/pkg/backy/file.go b/pkg/backy/file.go index a254ea8..64c4aa4 100644 --- a/pkg/backy/file.go +++ b/pkg/backy/file.go @@ -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 { diff --git a/pkg/backy/list.go b/pkg/backy/list.go index 24e9356..3fe77bb 100755 --- a/pkg/backy/list.go +++ b/pkg/backy/list.go @@ -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 { diff --git a/pkg/backy/metrics.go b/pkg/backy/metrics.go index 18c34a1..899a836 100755 --- a/pkg/backy/metrics.go +++ b/pkg/backy/metrics.go @@ -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 diff --git a/pkg/backy/metrics_test.go b/pkg/backy/metrics_test.go index 28ccd8b..e75ec98 100755 --- a/pkg/backy/metrics_test.go +++ b/pkg/backy/metrics_test.go @@ -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) -// } - -// } +} diff --git a/pkg/backy/ssh.go b/pkg/backy/ssh.go index fce953f..8a3c86f 100755 --- a/pkg/backy/ssh.go +++ b/pkg/backy/ssh.go @@ -723,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 @@ -847,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 == "" } diff --git a/pkg/backy/templates/error.txt b/pkg/backy/templates/error.txt index ae0a4a2..13babc7 100755 --- a/pkg/backy/templates/error.txt +++ b/pkg/backy/templates/error.txt @@ -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}} diff --git a/pkg/backy/templates/success.txt b/pkg/backy/templates/success.txt index bfeb34d..2217ab1 100755 --- a/pkg/backy/templates/success.txt +++ b/pkg/backy/templates/success.txt @@ -1,6 +1,4 @@ -Command list {{ .listName }} completed successfully. - -The following commands ran: +Commands executed: {{- range .CmdsRan}} - {{. -}} {{end}} diff --git a/pkg/backy/utils.go b/pkg/backy/utils.go index 615b9f6..a4bed68 100755 --- a/pkg/backy/utils.go +++ b/pkg/backy/utils.go @@ -226,6 +226,11 @@ func CheckConfigValues(config *koanf.Koanf, file string) { func collectOutput(buf *bytes.Buffer, commandName string, logger, globalLogger zerolog.Logger, wantOutput bool) []string { var outputArr []string 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() diff --git a/tests/docker/known_hosts b/tests/docker/known_hosts index 92c97e4..cdb3eab 100644 --- a/tests/docker/known_hosts +++ b/tests/docker/known_hosts @@ -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 diff --git a/tests/docker/start.sh b/tests/docker/start.sh index 4e509e8..0d6772c 100755 --- a/tests/docker/start.sh +++ b/tests/docker/start.sh @@ -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" \ No newline at end of file