Logs
Patchsets
Range Diff ↕ rd-101
1: 0306c3f < -: ------- chore: rsync test
2: 971b354 ! 1: 745b513 chore: add `rsync --delete` test
Range Diff ↕ rd-102
1: 745b513 ! 1: 76a6734 chore: add `rsync --delete` test
Range Diff ↕ rd-103
1: 76a6734 ! 1: 57bb60c chore: add `rsync --delete` test
Range Diff ↕ rd-104
1: 57bb60c = 1: 57bb60c chore: add `rsync --delete` test
-: ------- > 2: 5c8d929 chore: test is failing on `--delete`
Patchset ps-104
chore: add `rsync --delete` test
Eric Bower
pgs/ssh_test.go
+149
-5
chore: test is failing on `--delete`
Eric Bower
go.mod
+1
-0
go.sum
+2
-0
pgs/ssh_test.go
+23
-14
chore: add `rsync --delete` test
I can't figure out how to format the `exec.Command` for rsync with `-e` flag Current error: ``` [rsync -rv -e "ssh -p 2222 -o IdentitiesOnly=yes -i /tmp/rsync-3246995037/id_ed25519 -o StrictHostKeyChecking=no" /tmp/rsync-3246995037/ localhost:/test] rsync: [sender] Failed to exec ssh -p 2222 -o IdentitiesOnly=yes -i /tmp/rsync-3246995037/id_ed25519 -o StrictHostKeyChecking=no: No such file or directory (2) rsync error: error in IPC code (code 14) at pipe.c(85) [sender=3.4.0] rsync: connection unexpectedly closed (0 bytes received so far) [sender] rsync error: error in IPC code (code 14) at io.c(232) [sender=3.4.0] exit status 14 --- FAIL: TestSshServerRsync (0.10s) ssh_test.go:152: exit status 14 ```
pgs/ssh_test.go
link
+149
-5
+149
-5
1diff --git a/pgs/ssh_test.go b/pgs/ssh_test.go
2index d30fa31..fe97468 100644
3--- a/pgs/ssh_test.go
4+++ b/pgs/ssh_test.go
5@@ -3,9 +3,14 @@ package pgs
6 import (
7 "crypto/ed25519"
8 "crypto/rand"
9+ "crypto/x509"
10+ "encoding/pem"
11+ "fmt"
12 "io"
13 "log/slog"
14 "os"
15+ "os/exec"
16+ "path/filepath"
17 "strings"
18 "testing"
19 "time"
20@@ -18,7 +23,7 @@ import (
21 "golang.org/x/crypto/ssh"
22 )
23
24-func TestSshServer(t *testing.T) {
25+func TestSshServerSftp(t *testing.T) {
26 logger := slog.Default()
27 dbpool := pgsdb.NewDBMemory(logger)
28 // setup test data
29@@ -57,9 +62,142 @@ func TestSshServer(t *testing.T) {
30 done <- nil
31 }
32
33+func TestSshServerRsync(t *testing.T) {
34+ logger := slog.Default()
35+ dbpool := pgsdb.NewDBMemory(logger)
36+ // setup test data
37+ dbpool.SetupTestData()
38+ st, err := storage.NewStorageMemory(map[string]map[string]string{})
39+ if err != nil {
40+ panic(err)
41+ }
42+ cfg := NewPgsConfig(logger, dbpool, st)
43+ done := make(chan error)
44+ go StartSshServer(cfg, done)
45+ // Hack to wait for startup
46+ time.Sleep(time.Millisecond * 100)
47+
48+ user := GenerateUser()
49+ key := utils.KeyForKeyText(user.signer.PublicKey())
50+ // add user's pubkey to the default test account
51+ dbpool.Pubkeys = append(dbpool.Pubkeys, &db.PublicKey{
52+ ID: "nice-pubkey",
53+ UserID: dbpool.Users[0].ID,
54+ Key: key,
55+ })
56+
57+ conn, err := user.NewClient()
58+ if err != nil {
59+ t.Error(err)
60+ return
61+ }
62+ defer conn.Close()
63+
64+ // open an SFTP session over an existing ssh connection.
65+ client, err := sftp.NewClient(conn)
66+ if err != nil {
67+ cfg.Logger.Error("could not create sftp client", "err", err)
68+ panic(err)
69+ }
70+ defer client.Close()
71+
72+ name, err := os.MkdirTemp("", "rsync-")
73+ if err != nil {
74+ panic(err)
75+ }
76+
77+ // remove the temporary directory at the end of the program
78+ defer os.RemoveAll(name)
79+
80+ block := &pem.Block{
81+ Type: "PRIVATE KEY",
82+ Bytes: user.privateKey,
83+ }
84+ keyFile := filepath.Join(name, "id_ed25519")
85+ err = os.WriteFile(
86+ keyFile,
87+ pem.EncodeToMemory(block), 0600,
88+ )
89+
90+ index := "<!doctype html><html><body>index</body></html>"
91+ err = os.WriteFile(
92+ filepath.Join(name, "index.html"),
93+ []byte(index), 0666,
94+ )
95+
96+ about := "<!doctype html><html><body>about</body></html>"
97+ aboutFile := filepath.Join(name, "about.html")
98+ err = os.WriteFile(
99+ aboutFile,
100+ []byte(about), 0666,
101+ )
102+
103+ contact := "<!doctype html><html><body>contact</body></html>"
104+ err = os.WriteFile(
105+ filepath.Join(name, "contact.html"),
106+ []byte(contact), 0666,
107+ )
108+
109+ eCmd := fmt.Sprintf(
110+ `"ssh -p 2222 -o IdentitiesOnly=yes -i %s -o StrictHostKeyChecking=no"`,
111+ keyFile,
112+ )
113+
114+ // copy files
115+ cmd := exec.Command("rsync", "-rv", "-e", eCmd, name+"/", "localhost:/test")
116+ fmt.Println(cmd.Args)
117+ result, err := cmd.CombinedOutput()
118+ if err != nil {
119+ fmt.Println(string(result), err)
120+ t.Error(err)
121+ return
122+ }
123+
124+ // check it's there
125+ fi, err := client.Lstat("about.html")
126+ if err != nil {
127+ cfg.Logger.Error("could not get stat for file", "err", err)
128+ t.Error("about.html not found")
129+ return
130+ }
131+ if fi.Size() != 0 {
132+ cfg.Logger.Error("about.html wrong size", "size", fi.Size())
133+ t.Error("about.html wrong size")
134+ return
135+ }
136+
137+ // remove about file
138+ os.Remove(aboutFile)
139+
140+ // copy files with delete
141+ delCmd := exec.Command("rsync", "-rv", "--delete", "-e", eCmd, name+"/", "localhost:/test")
142+ err = delCmd.Run()
143+ if err != nil {
144+ t.Error(err)
145+ return
146+ }
147+
148+ done <- nil
149+}
150+
151+func createTmpFile(name, contents, ext string) *os.File {
152+ file, err := os.CreateTemp("tmp", fmt.Sprintf("%s-*.%s", name, ext))
153+ if err != nil {
154+ panic(err)
155+ }
156+
157+ data := []byte(contents)
158+ if _, err := file.Write(data); err != nil {
159+ panic(err)
160+ }
161+
162+ return file
163+}
164+
165 type UserSSH struct {
166- username string
167- signer ssh.Signer
168+ username string
169+ signer ssh.Signer
170+ privateKey []byte
171 }
172
173 func NewUserSSH(username string, signer ssh.Signer) *UserSSH {
174@@ -146,14 +284,20 @@ func GenerateUser() UserSSH {
175 panic(err)
176 }
177
178+ b, err := x509.MarshalPKCS8PrivateKey(userKey)
179+ if err != nil {
180+ panic(err)
181+ }
182+
183 userSigner, err := ssh.NewSignerFromKey(userKey)
184 if err != nil {
185 panic(err)
186 }
187
188 return UserSSH{
189- username: "testuser",
190- signer: userSigner,
191+ username: "testuser",
192+ signer: userSigner,
193+ privateKey: b,
194 }
195 }
196
chore: test is failing on `--delete`
Alright, the test is failing because of the new `--delete` flag. ``` time=2025-02-21T21:10:11.225-05:00 level=ERROR source=/home/erock/go/pkg/mod/github.com/picosh/send@v0.0.0-20250213162645-ec2027b68462/protocols/rsync/rsync.go:157 msg="error running rsync middleware" service=pgs err="runtime error: index out of range [7876097] with length 0" ```
go.mod
link
+1
-0
+1
-0
1diff --git a/go.mod b/go.mod
2index ced7737..d93cede 100644
3--- a/go.mod
4+++ b/go.mod
5@@ -48,6 +48,7 @@ require (
6 github.com/jmoiron/sqlx v1.4.0
7 github.com/lib/pq v1.10.9
8 github.com/microcosm-cc/bluemonday v1.0.27
9+ github.com/mikesmitty/edkey v0.0.0-20170222072505-3356ea4e686a
10 github.com/minio/minio-go/v7 v7.0.80
11 github.com/mmcdole/gofeed v1.3.0
12 github.com/muesli/reflow v0.3.0
go.sum
link
+2
-0
+2
-0
1diff --git a/go.sum b/go.sum
2index 89d665e..5ce2828 100644
3--- a/go.sum
4+++ b/go.sum
5@@ -634,6 +634,8 @@ github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKju
6 github.com/miekg/dns v1.1.45/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
7 github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ=
8 github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ=
9+github.com/mikesmitty/edkey v0.0.0-20170222072505-3356ea4e686a h1:eU8j/ClY2Ty3qdHnn0TyW3ivFoPC/0F1gQZz8yTxbbE=
10+github.com/mikesmitty/edkey v0.0.0-20170222072505-3356ea4e686a/go.mod h1:v8eSC2SMp9/7FTKUncp7fH9IwPfw+ysMObcEz5FWheQ=
11 github.com/minio/madmin-go/v3 v3.0.77 h1:cqp5kVeT5anDyocvoN81puwpy+GN7t+Xdj6xCLpnONE=
12 github.com/minio/madmin-go/v3 v3.0.77/go.mod h1:ku/RUc2xeo4Uui/GHUURMoNEVXk4Ih40xA3KHLyMF1o=
13 github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
pgs/ssh_test.go
link
+23
-14
+23
-14
1diff --git a/pgs/ssh_test.go b/pgs/ssh_test.go
2index fe97468..bad40a5 100644
3--- a/pgs/ssh_test.go
4+++ b/pgs/ssh_test.go
5@@ -3,7 +3,6 @@ package pgs
6 import (
7 "crypto/ed25519"
8 "crypto/rand"
9- "crypto/x509"
10 "encoding/pem"
11 "fmt"
12 "io"
13@@ -15,6 +14,7 @@ import (
14 "testing"
15 "time"
16
17+ "github.com/mikesmitty/edkey"
18 "github.com/picosh/pico/db"
19 pgsdb "github.com/picosh/pico/pgs/db"
20 "github.com/picosh/pico/shared/storage"
21@@ -24,7 +24,13 @@ import (
22 )
23
24 func TestSshServerSftp(t *testing.T) {
25- logger := slog.Default()
26+ opts := &slog.HandlerOptions{
27+ AddSource: true,
28+ Level: slog.LevelInfo,
29+ }
30+ logger := slog.New(
31+ slog.NewTextHandler(os.Stdout, opts),
32+ )
33 dbpool := pgsdb.NewDBMemory(logger)
34 // setup test data
35 dbpool.SetupTestData()
36@@ -63,7 +69,13 @@ func TestSshServerSftp(t *testing.T) {
37 }
38
39 func TestSshServerRsync(t *testing.T) {
40- logger := slog.Default()
41+ opts := &slog.HandlerOptions{
42+ AddSource: true,
43+ Level: slog.LevelInfo,
44+ }
45+ logger := slog.New(
46+ slog.NewTextHandler(os.Stdout, opts),
47+ )
48 dbpool := pgsdb.NewDBMemory(logger)
49 // setup test data
50 dbpool.SetupTestData()
51@@ -107,10 +119,10 @@ func TestSshServerRsync(t *testing.T) {
52 }
53
54 // remove the temporary directory at the end of the program
55- defer os.RemoveAll(name)
56+ // defer os.RemoveAll(name)
57
58 block := &pem.Block{
59- Type: "PRIVATE KEY",
60+ Type: "OPENSSH PRIVATE KEY",
61 Bytes: user.privateKey,
62 }
63 keyFile := filepath.Join(name, "id_ed25519")
64@@ -139,7 +151,7 @@ func TestSshServerRsync(t *testing.T) {
65 )
66
67 eCmd := fmt.Sprintf(
68- `"ssh -p 2222 -o IdentitiesOnly=yes -i %s -o StrictHostKeyChecking=no"`,
69+ "ssh -p 2222 -o IdentitiesOnly=yes -i %s -o StrictHostKeyChecking=no",
70 keyFile,
71 )
72
73@@ -154,13 +166,13 @@ func TestSshServerRsync(t *testing.T) {
74 }
75
76 // check it's there
77- fi, err := client.Lstat("about.html")
78+ fi, err := client.Lstat("/test/about.html")
79 if err != nil {
80 cfg.Logger.Error("could not get stat for file", "err", err)
81 t.Error("about.html not found")
82 return
83 }
84- if fi.Size() != 0 {
85+ if fi.Size() != 46 {
86 cfg.Logger.Error("about.html wrong size", "size", fi.Size())
87 t.Error("about.html wrong size")
88 return
89@@ -171,8 +183,9 @@ func TestSshServerRsync(t *testing.T) {
90
91 // copy files with delete
92 delCmd := exec.Command("rsync", "-rv", "--delete", "-e", eCmd, name+"/", "localhost:/test")
93- err = delCmd.Run()
94+ result, err = delCmd.CombinedOutput()
95 if err != nil {
96+ fmt.Println(string(result), err)
97 t.Error(err)
98 return
99 }
100@@ -284,11 +297,7 @@ func GenerateUser() UserSSH {
101 panic(err)
102 }
103
104- b, err := x509.MarshalPKCS8PrivateKey(userKey)
105- if err != nil {
106- panic(err)
107- }
108-
109+ b := edkey.MarshalED25519PrivateKey(userKey)
110 userSigner, err := ssh.NewSignerFromKey(userKey)
111 if err != nil {
112 panic(err)