pico

created pr with 47.1 on 2025-02-21T21:07:49Z · by c8ef7d19
added 47.2 on 2025-02-21T21:21:15Z · by c8ef7d19
1: 0306c3f < -: ------- chore: rsync test
2: 971b354 ! 1: 745b513 chore: add `rsync --delete` test
added 47.3 on 2025-02-21T21:21:56Z · by c8ef7d19
1: 745b513 ! 1: 76a6734 chore: add `rsync --delete` test
added 47.4 on 2025-02-21T21:22:20Z · by c8ef7d19
1: 76a6734 ! 1: 57bb60c chore: add `rsync --delete` test
added 47.5 on 2025-02-22T02:12:03Z · by c8ef7d19
1: 57bb60c = 1: 57bb60c chore: add `rsync --delete` test
-: ------- > 2: 5c8d929 chore: test is failing on `--delete`
changed status to accepted on 2025-03-13T14:53:20Z · by c8ef7d19
cmds
checkout latest patchset:
ssh pr.pico.sh print 47 | git am -3
checkout any patchset in a patch request:
ssh pr.pico.sh print 47.[rev] | git am -3
add changes to patch request:
git format-patch main --stdout | ssh pr.pico.sh pr add 47
set PR to open (enables RSS notifications):
ssh pr.pico.sh pr open 47
set PR to draft (stops RSS notifications):
ssh pr.pico.sh pr draft 47

Patchset 47.5 on 2025-02-22T02:12:03Z · commit 57bb60c

chore: add `rsync --delete` test
Eric Bower 2025-02-17T15:36:03Z
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
```
Semantic diff summary
3 added, 3 modified, 0 signature changed, 1 removed across 1 analyzed file
+149 -5 pgs/ssh_test.go #
......@@ -3,9 +3,14 @@ package pgs
33 import (
44 "crypto/ed25519"
55 "crypto/rand"
6+ "crypto/x509"
7+ "encoding/pem"
8+ "fmt"
69 "io"
710 "log/slog"
811 "os"
12+ "os/exec"
13+ "path/filepath"
914 "strings"
1015 "testing"
1116 "time"
......@@ -18,7 +23,7 @@ import (
1823 "golang.org/x/crypto/ssh"
1924 )
2025
21-func TestSshServer(t *testing.T) {
26+func TestSshServerSftp(t *testing.T) {
2227 logger := slog.Default()
2328 dbpool := pgsdb.NewDBMemory(logger)
2429 // setup test data
......@@ -57,9 +62,142 @@ func TestSshServer(t *testing.T) {
5762 done <- nil
5863 }
5964
65+func TestSshServerRsync(t *testing.T) {
66+ logger := slog.Default()
67+ dbpool := pgsdb.NewDBMemory(logger)
68+ // setup test data
69+ dbpool.SetupTestData()
70+ st, err := storage.NewStorageMemory(map[string]map[string]string{})
71+ if err != nil {
72+ panic(err)
73+ }
74+ cfg := NewPgsConfig(logger, dbpool, st)
75+ done := make(chan error)
76+ go StartSshServer(cfg, done)
77+ // Hack to wait for startup
78+ time.Sleep(time.Millisecond * 100)
79+
80+ user := GenerateUser()
81+ key := utils.KeyForKeyText(user.signer.PublicKey())
82+ // add user's pubkey to the default test account
83+ dbpool.Pubkeys = append(dbpool.Pubkeys, &db.PublicKey{
84+ ID: "nice-pubkey",
85+ UserID: dbpool.Users[0].ID,
86+ Key: key,
87+ })
88+
89+ conn, err := user.NewClient()
90+ if err != nil {
91+ t.Error(err)
92+ return
93+ }
94+ defer conn.Close()
95+
96+ // open an SFTP session over an existing ssh connection.
97+ client, err := sftp.NewClient(conn)
98+ if err != nil {
99+ cfg.Logger.Error("could not create sftp client", "err", err)
100+ panic(err)
101+ }
102+ defer client.Close()
103+
104+ name, err := os.MkdirTemp("", "rsync-")
105+ if err != nil {
106+ panic(err)
107+ }
108+
109+ // remove the temporary directory at the end of the program
110+ defer os.RemoveAll(name)
111+
112+ block := &pem.Block{
113+ Type: "PRIVATE KEY",
114+ Bytes: user.privateKey,
115+ }
116+ keyFile := filepath.Join(name, "id_ed25519")
117+ err = os.WriteFile(
118+ keyFile,
119+ pem.EncodeToMemory(block), 0600,
120+ )
121+
122+ index := "<!doctype html><html><body>index</body></html>"
123+ err = os.WriteFile(
124+ filepath.Join(name, "index.html"),
125+ []byte(index), 0666,
126+ )
127+
128+ about := "<!doctype html><html><body>about</body></html>"
129+ aboutFile := filepath.Join(name, "about.html")
130+ err = os.WriteFile(
131+ aboutFile,
132+ []byte(about), 0666,
133+ )
134+
135+ contact := "<!doctype html><html><body>contact</body></html>"
136+ err = os.WriteFile(
137+ filepath.Join(name, "contact.html"),
138+ []byte(contact), 0666,
139+ )
140+
141+ eCmd := fmt.Sprintf(
142+ `"ssh -p 2222 -o IdentitiesOnly=yes -i %s -o StrictHostKeyChecking=no"`,
143+ keyFile,
144+ )
145+
146+ // copy files
147+ cmd := exec.Command("rsync", "-rv", "-e", eCmd, name+"/", "localhost:/test")
148+ fmt.Println(cmd.Args)
149+ result, err := cmd.CombinedOutput()
150+ if err != nil {
151+ fmt.Println(string(result), err)
152+ t.Error(err)
153+ return
154+ }
155+
156+ // check it's there
157+ fi, err := client.Lstat("about.html")
158+ if err != nil {
159+ cfg.Logger.Error("could not get stat for file", "err", err)
160+ t.Error("about.html not found")
161+ return
162+ }
163+ if fi.Size() != 0 {
164+ cfg.Logger.Error("about.html wrong size", "size", fi.Size())
165+ t.Error("about.html wrong size")
166+ return
167+ }
168+
169+ // remove about file
170+ os.Remove(aboutFile)
171+
172+ // copy files with delete
173+ delCmd := exec.Command("rsync", "-rv", "--delete", "-e", eCmd, name+"/", "localhost:/test")
174+ err = delCmd.Run()
175+ if err != nil {
176+ t.Error(err)
177+ return
178+ }
179+
180+ done <- nil
181+}
182+
183+func createTmpFile(name, contents, ext string) *os.File {
184+ file, err := os.CreateTemp("tmp", fmt.Sprintf("%s-*.%s", name, ext))
185+ if err != nil {
186+ panic(err)
187+ }
188+
189+ data := []byte(contents)
190+ if _, err := file.Write(data); err != nil {
191+ panic(err)
192+ }
193+
194+ return file
195+}
196+
60197 type UserSSH struct {
61- username string
62- signer ssh.Signer
198+ username string
199+ signer ssh.Signer
200+ privateKey []byte
63201 }
64202
65203 func NewUserSSH(username string, signer ssh.Signer) *UserSSH {
......@@ -146,14 +284,20 @@ func GenerateUser() UserSSH {
146284 panic(err)
147285 }
148286
287+ b, err := x509.MarshalPKCS8PrivateKey(userKey)
288+ if err != nil {
289+ panic(err)
290+ }
291+
149292 userSigner, err := ssh.NewSignerFromKey(userKey)
150293 if err != nil {
151294 panic(err)
152295 }
153296
154297 return UserSSH{
155- username: "testuser",
156- signer: userSigner,
298+ username: "testuser",
299+ signer: userSigner,
300+ privateKey: b,
157301 }
158302 }
159303
Back to top