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-103
chore: add `rsync --delete` test
Eric Bower
pgs/ssh_test.go
+149
-5
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