Skip to content

Commit 06a663e

Browse files
authored
Merge pull request #25 from buildkite/refactor/remove-workspace-copy
refactor: remove workspace copy mechanism
2 parents 2f3584e + 2100704 commit 06a663e

File tree

11 files changed

+44
-382
lines changed

11 files changed

+44
-382
lines changed

README.md

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ cleanroom serve --listen tsnet://cleanroom:7777
5353
Then connect with:
5454

5555
```bash
56-
cleanroom exec --host tsnet://cleanroom:7777 -- "npm test"
56+
cleanroom exec --host http://cleanroom.tailnet.ts.net:7777 -c /path/to/repo -- "npm test"
5757
```
5858

5959
### 3) Launch -> Run -> Terminate via API
@@ -99,8 +99,6 @@ Request:
9999
"cwd": "/path/to/repo",
100100
"backend": "firecracker",
101101
"options": {
102-
"run_dir": "/tmp/cleanroom-runs",
103-
"read_only_workspace": false,
104102
"launch_seconds": 30
105103
}
106104
}
@@ -111,7 +109,6 @@ Response fields:
111109
- `backend`
112110
- `policy_source`
113111
- `policy_hash`
114-
- `run_dir_root`
115112

116113
### `POST /v1/cleanrooms/run`
117114

@@ -173,8 +170,6 @@ Example:
173170

174171
```yaml
175172
default_backend: firecracker
176-
workspace:
177-
access: rw
178173
backends:
179174
firecracker:
180175
binary_path: firecracker
@@ -189,8 +184,6 @@ backends:
189184
## Isolation Model
190185

191186
- Workload runs in a Firecracker microVM
192-
- Workspace is copied per run and sent to the guest agent
193-
- Workspace can be read-only (`workspace.access: ro` or request override)
194187
- Host egress is controlled with TAP + iptables rules from compiled policy
195188
- Default network behavior is deny
196189
- Rootfs writes are discarded after each run
@@ -217,7 +210,6 @@ cleanroom status --run-id <run-id>
217210
- policy host-resolution time
218211
- rootfs preparation time
219212
- Firecracker process start time
220-
- workspace archive preparation time
221213
- network setup time
222214
- VM ready time (process start -> guest agent ready)
223215
- vsock wait time (wait-to-connect for guest agent)

cmd/cleanroom-guest-agent/main.go

Lines changed: 0 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,14 @@
33
package main
44

55
import (
6-
"archive/tar"
76
"bytes"
8-
"compress/gzip"
97
"encoding/binary"
108
"errors"
119
"fmt"
1210
"io"
13-
"io/fs"
1411
"net"
1512
"os"
1613
"os/exec"
17-
"path/filepath"
1814
"strconv"
1915
"strings"
2016
"sync"
@@ -64,15 +60,6 @@ func handleConn(conn net.Conn) {
6460
_ = vsockexec.EncodeResponse(conn, vsockexec.ExecResponse{ExitCode: 1, Error: err.Error()})
6561
return
6662
}
67-
if len(req.WorkspaceTarGz) > 0 {
68-
if err := materializeWorkspace(req.WorkspaceTarGz, "/workspace", req.WorkspaceAccess); err != nil {
69-
_ = vsockexec.EncodeResponse(conn, vsockexec.ExecResponse{ExitCode: 1, Error: err.Error()})
70-
return
71-
}
72-
if req.Dir == "" {
73-
req.Dir = "/workspace"
74-
}
75-
}
7663
if len(req.EntropySeed) > 0 {
7764
_ = injectEntropy(req.EntropySeed)
7865
}
@@ -234,100 +221,6 @@ func buildCommandEnv(requestEnv []string) []string {
234221
return out
235222
}
236223

237-
func materializeWorkspace(tarGz []byte, destRoot, access string) error {
238-
if err := os.RemoveAll(destRoot); err != nil {
239-
return fmt.Errorf("reset workspace root: %w", err)
240-
}
241-
if err := os.MkdirAll(destRoot, 0o755); err != nil {
242-
return fmt.Errorf("create workspace root: %w", err)
243-
}
244-
if err := extractTarGz(tarGz, destRoot); err != nil {
245-
return fmt.Errorf("extract workspace: %w", err)
246-
}
247-
if strings.EqualFold(strings.TrimSpace(access), "ro") {
248-
if err := makeTreeReadOnly(destRoot); err != nil {
249-
return fmt.Errorf("mark workspace read-only: %w", err)
250-
}
251-
}
252-
return nil
253-
}
254-
255-
func extractTarGz(content []byte, destRoot string) error {
256-
gr, err := gzip.NewReader(bytes.NewReader(content))
257-
if err != nil {
258-
return err
259-
}
260-
defer gr.Close()
261-
262-
tr := tar.NewReader(gr)
263-
root := filepath.Clean(destRoot)
264-
prefix := root + string(os.PathSeparator)
265-
for {
266-
hdr, err := tr.Next()
267-
if errors.Is(err, io.EOF) {
268-
return nil
269-
}
270-
if err != nil {
271-
return err
272-
}
273-
name := strings.TrimPrefix(filepath.Clean("/"+hdr.Name), "/")
274-
if name == "." || name == "" {
275-
continue
276-
}
277-
target := filepath.Join(root, name)
278-
cleanTarget := filepath.Clean(target)
279-
if cleanTarget != root && !strings.HasPrefix(cleanTarget, prefix) {
280-
return fmt.Errorf("invalid archive path %q", hdr.Name)
281-
}
282-
283-
switch hdr.Typeflag {
284-
case tar.TypeDir:
285-
if err := os.MkdirAll(cleanTarget, fs.FileMode(hdr.Mode)); err != nil {
286-
return err
287-
}
288-
case tar.TypeReg, tar.TypeRegA:
289-
if err := os.MkdirAll(filepath.Dir(cleanTarget), 0o755); err != nil {
290-
return err
291-
}
292-
f, err := os.OpenFile(cleanTarget, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, fs.FileMode(hdr.Mode))
293-
if err != nil {
294-
return err
295-
}
296-
if _, err := io.Copy(f, tr); err != nil {
297-
_ = f.Close()
298-
return err
299-
}
300-
if err := f.Close(); err != nil {
301-
return err
302-
}
303-
case tar.TypeSymlink:
304-
return fmt.Errorf("symlinks are not supported in workspace snapshots: %q", hdr.Name)
305-
default:
306-
// Skip unsupported entries (devices, fifos, etc.) in MVP.
307-
}
308-
}
309-
}
310-
311-
func makeTreeReadOnly(root string) error {
312-
return filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
313-
if err != nil {
314-
return err
315-
}
316-
if d.Type()&os.ModeSymlink != 0 {
317-
return nil
318-
}
319-
info, err := d.Info()
320-
if err != nil {
321-
return err
322-
}
323-
mode := info.Mode().Perm()
324-
if d.IsDir() {
325-
return os.Chmod(path, mode&^0o222|0o555)
326-
}
327-
return os.Chmod(path, mode&^0o222|0o444)
328-
})
329-
}
330-
331224
func errorsIsClosed(err error) bool {
332225
if err == nil {
333226
return false

internal/backend/backend.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,8 @@ type FirecrackerConfig struct {
4242
BinaryPath string
4343
KernelImagePath string
4444
RootFSPath string
45-
RunDir string
46-
WorkspaceHost string
47-
WorkspaceAccess string // rw|ro
48-
VCPUs int64
45+
RunDir string
46+
VCPUs int64
4947
MemoryMiB int64
5048
GuestCID uint32
5149
GuestPort uint32

0 commit comments

Comments
 (0)