refactor: use crypto/ssh instead of ssh binary

Carlos Alexandro Becker created

Change summary

go.mod                             |  4 +-
go.sum                             |  8 +++---
testscript/script_test.go          | 41 +++++++++++++++++--------------
testscript/testdata/settings.txtar | 29 +++++-----------------
4 files changed, 36 insertions(+), 46 deletions(-)

Detailed changes

go.mod 🔗

@@ -31,7 +31,7 @@ require (
 	github.com/muesli/roff v0.1.0
 	github.com/prometheus/client_golang v1.15.1
 	github.com/robfig/cron/v3 v3.0.1
-	github.com/rogpeppe/go-internal v1.10.0
+	github.com/rogpeppe/go-internal v1.10.1-0.20230524175051-ec119421bb97
 	github.com/spf13/cobra v1.7.0
 	go.uber.org/automaxprocs v1.5.2
 	goji.io v2.0.2+incompatible
@@ -78,7 +78,7 @@ require (
 	github.com/spf13/pflag v1.0.5 // indirect
 	github.com/yuin/goldmark v1.5.2 // indirect
 	github.com/yuin/goldmark-emoji v1.0.1 // indirect
-	golang.org/x/mod v0.8.0 // indirect
+	golang.org/x/mod v0.9.0 // indirect
 	golang.org/x/net v0.10.0 // indirect
 	golang.org/x/sys v0.8.0 // indirect
 	golang.org/x/term v0.8.0 // indirect

go.sum 🔗

@@ -178,8 +178,8 @@ github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
 github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
 github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
 github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
-github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
-github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
+github.com/rogpeppe/go-internal v1.10.1-0.20230524175051-ec119421bb97 h1:3RPlVWzZ/PDqmVuf/FKHARG5EMid/tl7cv54Sw/QRVY=
+github.com/rogpeppe/go-internal v1.10.1-0.20230524175051-ec119421bb97/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI=
 github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
@@ -224,8 +224,8 @@ golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
 golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
-golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
-golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
+golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs=
+golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=

testscript/script_test.go 🔗

@@ -6,9 +6,7 @@ import (
 	"fmt"
 	"net"
 	"os"
-	"os/exec"
 	"path/filepath"
-	"runtime"
 	"strings"
 	"sync"
 	"testing"
@@ -19,6 +17,7 @@ import (
 	"github.com/charmbracelet/soft-serve/server/config"
 	"github.com/charmbracelet/soft-serve/server/test"
 	"github.com/rogpeppe/go-internal/testscript"
+	"golang.org/x/crypto/ssh"
 )
 
 var update = flag.Bool("update", false, "update script files")
@@ -66,22 +65,28 @@ func TestScript(t *testing.T) {
 		UpdateScripts: *update,
 		Cmds: map[string]func(ts *testscript.TestScript, neg bool, args []string){
 			"soft": func(ts *testscript.TestScript, neg bool, args []string) {
-				args = append(
-					sshArgs,
-					append([]string{
-						"-p", ts.Getenv("SSH_PORT"),
-						"localhost",
-						"--",
-					}, args...)...,
+				cli, err := ssh.Dial(
+					"tcp",
+					net.JoinHostPort("localhost", ts.Getenv("SSH_PORT")),
+					&ssh.ClientConfig{
+						User: "admin",
+						Auth: []ssh.AuthMethod{
+							ssh.PublicKeys(admin1.Signer()),
+						},
+						HostKeyCallback: ssh.InsecureIgnoreHostKey(),
+					},
 				)
-				if runtime.GOOS == "windows" {
-					cmd := exec.Command("ssh.exe", args...)
-					out, err := cmd.CombinedOutput()
-					ts.Logf("WINDOWS RAN %v:\n\tOUTPUT: %s\n\tERROR: %v", cmd.Args, string(out), err)
-					check(ts, err, neg)
-				} else {
-					check(ts, ts.Exec("ssh", args...), neg)
-				}
+				check(ts, err, neg)
+				defer cli.Close()
+
+				sess, err := cli.NewSession()
+				check(ts, err, neg)
+				defer sess.Close()
+
+				sess.Stdout = ts.Stdout()
+				sess.Stderr = ts.Stderr()
+
+				check(ts, sess.Run(strings.Join(args, " ")), neg)
 			},
 			"git": func(ts *testscript.TestScript, neg bool, args []string) {
 				ts.Setenv(
@@ -155,7 +160,7 @@ func TestScript(t *testing.T) {
 			}()
 
 			e.Defer(func() {
-				ctx, cancel := context.WithTimeout(context.Background(), time.Second)
+				ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
 				defer cancel()
 				if err := srv.Shutdown(ctx); err != nil {
 					e.T().Fatal(err)

testscript/testdata/settings.txtar 🔗

@@ -2,45 +2,30 @@
 # check default allow-keyless
 soft settings allow-keyless true
 soft settings allow-keyless
-cmp stdout allow-keyless1.txt
+stdout 'true.*'
 
 # change allow-keyless and check
 soft settings allow-keyless false
 soft settings allow-keyless
-cmp stdout allow-keyless2.txt
+stdout 'false.*'
 
 # check default anon-access
 soft settings anon-access
-cmp stdout anon-access1.txt
+stdout 'read-only.*'
 
 # chaneg anon-access to all available options, and check them
 soft settings anon-access no-access
 soft settings anon-access
-cmp stdout anon-access2.txt
+stdout 'no-access.*'
 
 soft settings anon-access read-only
 soft settings anon-access
-cmp stdout anon-access3.txt
+stdout 'read-only.*'
 
 soft settings anon-access read-write
 soft settings anon-access
-cmp stdout anon-access4.txt
+stdout 'read-write.*'
 
 soft settings anon-access admin-access
 soft settings anon-access
-cmp stdout anon-access5.txt
-
--- allow-keyless1.txt --
-true
--- allow-keyless2.txt --
-false
--- anon-access1.txt --
-read-only
--- anon-access2.txt --
-no-access
--- anon-access3.txt --
-read-only
--- anon-access4.txt --
-read-write
--- anon-access5.txt --
-admin-access
+stdout 'admin-access.*'