Skip to content

Commit 49b7390

Browse files
authored
Merge pull request #60 from diggerhq/fix/docs-testing-bugs
testing fixes
2 parents 52e0f77 + 9cccc9a commit 49b7390

File tree

5 files changed

+181
-34
lines changed

5 files changed

+181
-34
lines changed

internal/api/exec_session.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,16 @@ package api
33
import (
44
"context"
55
"encoding/binary"
6+
"fmt"
67
"net/http"
78
"time"
89

910
"github.com/gorilla/websocket"
1011
"github.com/labstack/echo/v4"
12+
"github.com/opensandbox/opensandbox/internal/auth"
1113
"github.com/opensandbox/opensandbox/internal/sandbox"
1214
"github.com/opensandbox/opensandbox/pkg/types"
15+
pb "github.com/opensandbox/opensandbox/proto/worker"
1316
)
1417

1518
func (s *Server) createExecSession(c echo.Context) error {
@@ -225,6 +228,15 @@ func (s *Server) execRun(c echo.Context) error {
225228
})
226229
}
227230

231+
// Server mode: route exec to the worker that owns this sandbox via gRPC
232+
if s.workerRegistry != nil {
233+
return s.execRunRemote(c, id, req)
234+
}
235+
236+
if s.manager == nil {
237+
return c.JSON(http.StatusServiceUnavailable, errSandboxNotAvailable)
238+
}
239+
228240
var result *types.ProcessResult
229241

230242
routeOp := func(ctx context.Context) error {
@@ -250,6 +262,54 @@ func (s *Server) execRun(c echo.Context) error {
250262
return c.JSON(http.StatusOK, result)
251263
}
252264

265+
// execRunRemote routes an exec/run request to the worker via gRPC.
266+
func (s *Server) execRunRemote(c echo.Context, sandboxID string, req types.ProcessConfig) error {
267+
orgID, _ := auth.GetOrgID(c)
268+
269+
session, err := s.store.GetSandboxSession(c.Request().Context(), sandboxID)
270+
if err != nil {
271+
return c.JSON(http.StatusNotFound, map[string]string{"error": "sandbox not found"})
272+
}
273+
if session.OrgID != orgID {
274+
return c.JSON(http.StatusNotFound, map[string]string{"error": "sandbox not found"})
275+
}
276+
277+
client, err := s.workerRegistry.GetWorkerClient(session.WorkerID)
278+
if err != nil {
279+
return c.JSON(http.StatusServiceUnavailable, map[string]string{
280+
"error": fmt.Sprintf("worker unavailable: %v", err),
281+
})
282+
}
283+
284+
timeout := int32(req.Timeout)
285+
if timeout <= 0 {
286+
timeout = 30
287+
}
288+
289+
grpcCtx, cancel := context.WithTimeout(c.Request().Context(), time.Duration(timeout+5)*time.Second)
290+
defer cancel()
291+
292+
resp, err := client.ExecCommand(grpcCtx, &pb.ExecCommandRequest{
293+
SandboxId: sandboxID,
294+
Command: req.Command,
295+
Args: req.Args,
296+
Envs: req.Env,
297+
Cwd: req.Cwd,
298+
Timeout: timeout,
299+
})
300+
if err != nil {
301+
return c.JSON(http.StatusInternalServerError, map[string]string{
302+
"error": err.Error(),
303+
})
304+
}
305+
306+
return c.JSON(http.StatusOK, &types.ProcessResult{
307+
ExitCode: int(resp.ExitCode),
308+
Stdout: resp.Stdout,
309+
Stderr: resp.Stderr,
310+
})
311+
}
312+
253313
func (s *Server) killExecSession(c echo.Context) error {
254314
if s.execSessionManager == nil {
255315
return c.JSON(http.StatusServiceUnavailable, errSandboxNotAvailable)

internal/db/store.go

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -879,8 +879,23 @@ func (s *Store) CountCheckpoints(ctx context.Context, sandboxID string) (int, er
879879
}
880880

881881
// DeleteCheckpoint deletes a checkpoint (only if owned by org).
882+
// Clears any sandbox_sessions FK references first to avoid constraint violations.
882883
func (s *Store) DeleteCheckpoint(ctx context.Context, orgID uuid.UUID, checkpointID uuid.UUID) error {
883-
tag, err := s.pool.Exec(ctx,
884+
tx, err := s.pool.Begin(ctx)
885+
if err != nil {
886+
return fmt.Errorf("begin tx: %w", err)
887+
}
888+
defer tx.Rollback(ctx)
889+
890+
// Clear FK references from sandboxes forked from this checkpoint
891+
_, err = tx.Exec(ctx,
892+
`UPDATE sandbox_sessions SET based_on_checkpoint_id = NULL WHERE based_on_checkpoint_id = $1`,
893+
checkpointID)
894+
if err != nil {
895+
return fmt.Errorf("clear checkpoint references: %w", err)
896+
}
897+
898+
tag, err := tx.Exec(ctx,
884899
`DELETE FROM sandbox_checkpoints WHERE id = $1 AND org_id = $2`,
885900
checkpointID, orgID)
886901
if err != nil {
@@ -889,7 +904,8 @@ func (s *Store) DeleteCheckpoint(ctx context.Context, orgID uuid.UUID, checkpoin
889904
if tag.RowsAffected() == 0 {
890905
return fmt.Errorf("checkpoint not found or not owned by org")
891906
}
892-
return nil
907+
908+
return tx.Commit(ctx)
893909
}
894910

895911
// --- Checkpoint Patch operations ---

internal/firecracker/api.go

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -141,10 +141,7 @@ func (c *FirecrackerClient) CreateSnapshot(snapshotPath, memFilePath string) err
141141

142142
// LoadSnapshot restores a VM from a snapshot.
143143
// If resumeVM is true, the VM starts running immediately after load.
144-
// clockDeltaUs is the microseconds elapsed since the snapshot was taken; Firecracker
145-
// advances the guest clock by this amount so the VM wakes with the correct wall time.
146-
// Pass 0 to skip clock correction (e.g. for legacy snapshots without a recorded time).
147-
func (c *FirecrackerClient) LoadSnapshot(snapshotPath, memFilePath string, resumeVM bool, clockDeltaUs int64) error {
144+
func (c *FirecrackerClient) LoadSnapshot(snapshotPath, memFilePath string, resumeVM bool) error {
148145
body := map[string]interface{}{
149146
"snapshot_path": snapshotPath,
150147
"mem_backend": map[string]string{
@@ -154,9 +151,6 @@ func (c *FirecrackerClient) LoadSnapshot(snapshotPath, memFilePath string, resum
154151
"enable_diff_snapshots": false,
155152
"resume_vm": resumeVM,
156153
}
157-
if clockDeltaUs > 0 {
158-
body["clock_delta_us"] = clockDeltaUs
159-
}
160154
return c.put("/snapshot/load", body)
161155
}
162156

internal/firecracker/manager.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,13 @@ func (m *Manager) waitForAgent(ctx context.Context, vsockPath string, timeout ti
449449
var lastErr error
450450
attempts := 0
451451

452+
// Log initial vsock file state for diagnostics
453+
if _, err := os.Stat(vsockPath); err != nil {
454+
log.Printf("firecracker: waitForAgent: vsock.sock does not exist yet at %s", vsockPath)
455+
} else {
456+
log.Printf("firecracker: waitForAgent: vsock.sock exists at %s", vsockPath)
457+
}
458+
452459
for time.Now().Before(deadline) {
453460
attempts++
454461
tAttempt := time.Now()

0 commit comments

Comments
 (0)