forked from wrenn/wrenn
fix: resolve bugs and DRY violations in sandbox manager and API handlers
- Fix createFromSnapshot discarding memoryMB param (balloon optimization was dead) - Fix double dm-snapshot removal in Pause() cleanupPauseFailure path - Fix DestroySandbox RPC mapping all errors to CodeNotFound - Fix handleFailed event consumer missing pausing/resuming → error transitions - Fix stream resource leak in StreamUpload on early-return paths - Add envs/cwd fields to ExecRequest proto for foreground exec parity - Extract createResources rollback helper to eliminate 4x duplicated teardown - Remove unused chClient.ping method - Add .mcp.json to gitignore Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@ -55,3 +55,4 @@ internal/dashboard/static/*
|
|||||||
.dual-graph/
|
.dual-graph/
|
||||||
# Added by code-review-graph
|
# Added by code-review-graph
|
||||||
.code-review-graph/
|
.code-review-graph/
|
||||||
|
.mcp.json
|
||||||
|
|||||||
@ -119,6 +119,8 @@ func (h *execHandler) Exec(w http.ResponseWriter, r *http.Request) {
|
|||||||
Cmd: req.Cmd,
|
Cmd: req.Cmd,
|
||||||
Args: req.Args,
|
Args: req.Args,
|
||||||
TimeoutSec: req.TimeoutSec,
|
TimeoutSec: req.TimeoutSec,
|
||||||
|
Envs: req.Envs,
|
||||||
|
Cwd: req.Cwd,
|
||||||
}))
|
}))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
status, code, msg := agentErrToHTTP(err)
|
status, code, msg := agentErrToHTTP(err)
|
||||||
|
|||||||
@ -89,6 +89,12 @@ func (h *filesStreamHandler) StreamUpload(w http.ResponseWriter, r *http.Request
|
|||||||
|
|
||||||
// Open client-streaming RPC to host agent.
|
// Open client-streaming RPC to host agent.
|
||||||
stream := agent.WriteFileStream(ctx)
|
stream := agent.WriteFileStream(ctx)
|
||||||
|
var streamClosed bool
|
||||||
|
defer func() {
|
||||||
|
if !streamClosed {
|
||||||
|
stream.CloseAndReceive()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
// Send metadata first.
|
// Send metadata first.
|
||||||
if err := stream.Send(&pb.WriteFileStreamRequest{
|
if err := stream.Send(&pb.WriteFileStreamRequest{
|
||||||
@ -127,6 +133,7 @@ func (h *filesStreamHandler) StreamUpload(w http.ResponseWriter, r *http.Request
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Close and receive response.
|
// Close and receive response.
|
||||||
|
streamClosed = true
|
||||||
if _, err := stream.CloseAndReceive(); err != nil {
|
if _, err := stream.CloseAndReceive(); err != nil {
|
||||||
status, code, msg := agentErrToHTTP(err)
|
status, code, msg := agentErrToHTTP(err)
|
||||||
writeError(w, status, code, msg)
|
writeError(w, status, code, msg)
|
||||||
|
|||||||
@ -210,16 +210,14 @@ func (c *SandboxEventConsumer) handleStopped(ctx context.Context, sandboxID pgty
|
|||||||
// or the CP's background goroutine publishes a failure. Uses conditional update
|
// or the CP's background goroutine publishes a failure. Uses conditional update
|
||||||
// to avoid clobbering concurrent operations.
|
// to avoid clobbering concurrent operations.
|
||||||
func (c *SandboxEventConsumer) handleFailed(ctx context.Context, sandboxID pgtype.UUID) {
|
func (c *SandboxEventConsumer) handleFailed(ctx context.Context, sandboxID pgtype.UUID) {
|
||||||
// Try running → error (VM crash pushed by host agent).
|
// Try each possible pre-failure state until one matches.
|
||||||
if _, err := c.db.UpdateSandboxStatusIf(ctx, db.UpdateSandboxStatusIfParams{
|
for _, fromStatus := range []string{"running", "starting", "pausing", "resuming"} {
|
||||||
ID: sandboxID, Status: "running", Status_2: "error",
|
if _, err := c.db.UpdateSandboxStatusIf(ctx, db.UpdateSandboxStatusIfParams{
|
||||||
}); err == nil {
|
ID: sandboxID, Status: fromStatus, Status_2: "error",
|
||||||
return
|
}); err == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Try starting → error (create failed).
|
|
||||||
_, _ = c.db.UpdateSandboxStatusIf(ctx, db.UpdateSandboxStatusIfParams{
|
|
||||||
ID: sandboxID, Status: "starting", Status_2: "error",
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *SandboxEventConsumer) handleAutoPaused(ctx context.Context, sandboxID pgtype.UUID, _ SandboxEvent) {
|
func (c *SandboxEventConsumer) handleAutoPaused(ctx context.Context, sandboxID pgtype.UUID, _ SandboxEvent) {
|
||||||
|
|||||||
@ -78,15 +78,30 @@ type ExecResult struct {
|
|||||||
ExitCode int32
|
ExitCode int32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExecOpts holds optional parameters for Exec.
|
||||||
|
type ExecOpts struct {
|
||||||
|
Envs map[string]string
|
||||||
|
Cwd string
|
||||||
|
}
|
||||||
|
|
||||||
// Exec runs a command inside the sandbox and collects all stdout/stderr output.
|
// Exec runs a command inside the sandbox and collects all stdout/stderr output.
|
||||||
// It blocks until the command completes.
|
// It blocks until the command completes.
|
||||||
func (c *Client) Exec(ctx context.Context, cmd string, args ...string) (*ExecResult, error) {
|
func (c *Client) Exec(ctx context.Context, cmd string, args []string, opts *ExecOpts) (*ExecResult, error) {
|
||||||
stdin := false
|
stdin := false
|
||||||
|
proc := &envdpb.ProcessConfig{
|
||||||
|
Cmd: cmd,
|
||||||
|
Args: args,
|
||||||
|
}
|
||||||
|
if opts != nil {
|
||||||
|
if len(opts.Envs) > 0 {
|
||||||
|
proc.Envs = opts.Envs
|
||||||
|
}
|
||||||
|
if opts.Cwd != "" {
|
||||||
|
proc.Cwd = &opts.Cwd
|
||||||
|
}
|
||||||
|
}
|
||||||
req := connect.NewRequest(&envdpb.StartRequest{
|
req := connect.NewRequest(&envdpb.StartRequest{
|
||||||
Process: &envdpb.ProcessConfig{
|
Process: proc,
|
||||||
Cmd: cmd,
|
|
||||||
Args: args,
|
|
||||||
},
|
|
||||||
Stdin: &stdin,
|
Stdin: &stdin,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import (
|
|||||||
pb "git.omukk.dev/wrenn/wrenn/proto/hostagent/gen"
|
pb "git.omukk.dev/wrenn/wrenn/proto/hostagent/gen"
|
||||||
"git.omukk.dev/wrenn/wrenn/proto/hostagent/gen/hostagentv1connect"
|
"git.omukk.dev/wrenn/wrenn/proto/hostagent/gen/hostagentv1connect"
|
||||||
|
|
||||||
|
"git.omukk.dev/wrenn/wrenn/internal/envdclient"
|
||||||
"git.omukk.dev/wrenn/wrenn/internal/sandbox"
|
"git.omukk.dev/wrenn/wrenn/internal/sandbox"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -90,7 +91,10 @@ func (s *Server) DestroySandbox(
|
|||||||
req *connect.Request[pb.DestroySandboxRequest],
|
req *connect.Request[pb.DestroySandboxRequest],
|
||||||
) (*connect.Response[pb.DestroySandboxResponse], error) {
|
) (*connect.Response[pb.DestroySandboxResponse], error) {
|
||||||
if err := s.mgr.Destroy(ctx, req.Msg.SandboxId); err != nil {
|
if err := s.mgr.Destroy(ctx, req.Msg.SandboxId); err != nil {
|
||||||
return nil, connect.NewError(connect.CodeNotFound, err)
|
if strings.Contains(err.Error(), "not found") {
|
||||||
|
return nil, connect.NewError(connect.CodeNotFound, err)
|
||||||
|
}
|
||||||
|
return nil, connect.NewError(connect.CodeInternal, err)
|
||||||
}
|
}
|
||||||
return connect.NewResponse(&pb.DestroySandboxResponse{}), nil
|
return connect.NewResponse(&pb.DestroySandboxResponse{}), nil
|
||||||
}
|
}
|
||||||
@ -216,7 +220,12 @@ func (s *Server) Exec(
|
|||||||
execCtx, cancel := context.WithTimeout(ctx, timeout)
|
execCtx, cancel := context.WithTimeout(ctx, timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
result, err := s.mgr.Exec(execCtx, msg.SandboxId, msg.Cmd, msg.Args...)
|
var opts *envdclient.ExecOpts
|
||||||
|
if len(msg.Envs) > 0 || msg.Cwd != "" {
|
||||||
|
opts = &envdclient.ExecOpts{Envs: msg.Envs, Cwd: msg.Cwd}
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := s.mgr.Exec(execCtx, msg.SandboxId, msg.Cmd, msg.Args, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, connect.NewError(connect.CodeInternal, fmt.Errorf("exec: %w", err))
|
return nil, connect.NewError(connect.CodeInternal, fmt.Errorf("exec: %w", err))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -201,24 +201,30 @@ func (m *Manager) Create(ctx context.Context, sandboxID string, teamID, template
|
|||||||
return nil, fmt.Errorf("create dm-snapshot: %w", err)
|
return nil, fmt.Errorf("create dm-snapshot: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
res := &createResources{
|
||||||
|
sandboxID: sandboxID,
|
||||||
|
loops: m.loops,
|
||||||
|
loopImage: baseRootfs,
|
||||||
|
dmDevice: dmDev,
|
||||||
|
cowPath: cowPath,
|
||||||
|
slots: m.slots,
|
||||||
|
}
|
||||||
|
|
||||||
// Allocate network slot.
|
// Allocate network slot.
|
||||||
slotIdx, err := m.slots.Allocate()
|
slotIdx, err := m.slots.Allocate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
warnErr("dm-snapshot remove error", sandboxID, devicemapper.RemoveSnapshot(context.Background(), dmDev))
|
res.rollback()
|
||||||
os.Remove(cowPath)
|
|
||||||
m.loops.Release(baseRootfs)
|
|
||||||
return nil, fmt.Errorf("allocate network slot: %w", err)
|
return nil, fmt.Errorf("allocate network slot: %w", err)
|
||||||
}
|
}
|
||||||
|
res.slotIdx = slotIdx
|
||||||
slot := network.NewSlot(slotIdx)
|
slot := network.NewSlot(slotIdx)
|
||||||
|
|
||||||
// Set up network.
|
// Set up network.
|
||||||
if err := network.CreateNetwork(slot); err != nil {
|
if err := network.CreateNetwork(slot); err != nil {
|
||||||
m.slots.Release(slotIdx)
|
res.rollback()
|
||||||
warnErr("dm-snapshot remove error", sandboxID, devicemapper.RemoveSnapshot(context.Background(), dmDev))
|
|
||||||
os.Remove(cowPath)
|
|
||||||
m.loops.Release(baseRootfs)
|
|
||||||
return nil, fmt.Errorf("create network: %w", err)
|
return nil, fmt.Errorf("create network: %w", err)
|
||||||
}
|
}
|
||||||
|
res.slot = slot
|
||||||
|
|
||||||
// Boot VM — CH gets the dm device path.
|
// Boot VM — CH gets the dm device path.
|
||||||
vmCfg := vm.VMConfig{
|
vmCfg := vm.VMConfig{
|
||||||
@ -238,13 +244,10 @@ func (m *Manager) Create(ctx context.Context, sandboxID string, teamID, template
|
|||||||
}
|
}
|
||||||
|
|
||||||
if _, err := m.vm.Create(ctx, vmCfg); err != nil {
|
if _, err := m.vm.Create(ctx, vmCfg); err != nil {
|
||||||
warnErr("network cleanup error", sandboxID, network.RemoveNetwork(slot))
|
res.rollback()
|
||||||
m.slots.Release(slotIdx)
|
|
||||||
warnErr("dm-snapshot remove error", sandboxID, devicemapper.RemoveSnapshot(context.Background(), dmDev))
|
|
||||||
os.Remove(cowPath)
|
|
||||||
m.loops.Release(baseRootfs)
|
|
||||||
return nil, fmt.Errorf("create VM: %w", err)
|
return nil, fmt.Errorf("create VM: %w", err)
|
||||||
}
|
}
|
||||||
|
res.vm = m.vm
|
||||||
|
|
||||||
// Wait for envd to be ready.
|
// Wait for envd to be ready.
|
||||||
client := envdclient.New(slot.HostIP.String())
|
client := envdclient.New(slot.HostIP.String())
|
||||||
@ -252,12 +255,7 @@ func (m *Manager) Create(ctx context.Context, sandboxID string, teamID, template
|
|||||||
defer waitCancel()
|
defer waitCancel()
|
||||||
|
|
||||||
if err := client.WaitUntilReady(waitCtx); err != nil {
|
if err := client.WaitUntilReady(waitCtx); err != nil {
|
||||||
warnErr("vm destroy error", sandboxID, m.vm.Destroy(context.Background(), sandboxID))
|
res.rollback()
|
||||||
warnErr("network cleanup error", sandboxID, network.RemoveNetwork(slot))
|
|
||||||
m.slots.Release(slotIdx)
|
|
||||||
warnErr("dm-snapshot remove error", sandboxID, devicemapper.RemoveSnapshot(context.Background(), dmDev))
|
|
||||||
os.Remove(cowPath)
|
|
||||||
m.loops.Release(baseRootfs)
|
|
||||||
return nil, fmt.Errorf("wait for envd: %w", err)
|
return nil, fmt.Errorf("wait for envd: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -538,14 +536,16 @@ func (m *Manager) Pause(ctx context.Context, sandboxID string) error {
|
|||||||
|
|
||||||
// ── Step 7: Remove dm-snapshot and save CoW ──────────────────────
|
// ── Step 7: Remove dm-snapshot and save CoW ──────────────────────
|
||||||
if sb.dmDevice != nil {
|
if sb.dmDevice != nil {
|
||||||
if err := devicemapper.RemoveSnapshot(ctx, sb.dmDevice); err != nil {
|
dmDev := sb.dmDevice
|
||||||
|
sb.dmDevice = nil
|
||||||
|
if err := devicemapper.RemoveSnapshot(ctx, dmDev); err != nil {
|
||||||
m.cleanupPauseFailure(sb, sandboxID, pauseDir)
|
m.cleanupPauseFailure(sb, sandboxID, pauseDir)
|
||||||
return fmt.Errorf("remove dm-snapshot: %w", err)
|
return fmt.Errorf("remove dm-snapshot: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
snapshotCow := snapshot.CowPath(pauseDir, "")
|
snapshotCow := snapshot.CowPath(pauseDir, "")
|
||||||
if err := os.Rename(sb.dmDevice.CowPath, snapshotCow); err != nil {
|
if err := os.Rename(dmDev.CowPath, snapshotCow); err != nil {
|
||||||
os.Remove(sb.dmDevice.CowPath)
|
os.Remove(dmDev.CowPath)
|
||||||
m.cleanupPauseFailure(sb, sandboxID, pauseDir)
|
m.cleanupPauseFailure(sb, sandboxID, pauseDir)
|
||||||
return fmt.Errorf("move cow file: %w", err)
|
return fmt.Errorf("move cow file: %w", err)
|
||||||
}
|
}
|
||||||
@ -616,38 +616,42 @@ func (m *Manager) Resume(ctx context.Context, sandboxID string, timeoutSec int,
|
|||||||
return nil, fmt.Errorf("move cow file: %w", err)
|
return nil, fmt.Errorf("move cow file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
rollbackCow := func() {
|
|
||||||
if err := os.Rename(cowPath, savedCow); err != nil {
|
|
||||||
slog.Warn("failed to rollback cow file", "src", cowPath, "dst", savedCow, "error", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Restore dm-snapshot from existing persistent CoW file.
|
// Restore dm-snapshot from existing persistent CoW file.
|
||||||
dmName := "wrenn-" + sandboxID
|
dmName := "wrenn-" + sandboxID
|
||||||
dmDev, err := devicemapper.RestoreSnapshot(ctx, dmName, originLoop, cowPath, originSize)
|
dmDev, err := devicemapper.RestoreSnapshot(ctx, dmName, originLoop, cowPath, originSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
m.loops.Release(baseImagePath)
|
m.loops.Release(baseImagePath)
|
||||||
rollbackCow()
|
os.Rename(cowPath, savedCow)
|
||||||
return nil, fmt.Errorf("restore dm-snapshot: %w", err)
|
return nil, fmt.Errorf("restore dm-snapshot: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
res := &createResources{
|
||||||
|
sandboxID: sandboxID,
|
||||||
|
loops: m.loops,
|
||||||
|
loopImage: baseImagePath,
|
||||||
|
dmDevice: dmDev,
|
||||||
|
slots: m.slots,
|
||||||
|
rollCow: func() {
|
||||||
|
if err := os.Rename(cowPath, savedCow); err != nil {
|
||||||
|
slog.Warn("failed to rollback cow file", "src", cowPath, "dst", savedCow, "error", err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
// Allocate network slot.
|
// Allocate network slot.
|
||||||
slotIdx, err := m.slots.Allocate()
|
slotIdx, err := m.slots.Allocate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
warnErr("dm-snapshot remove error", sandboxID, devicemapper.RemoveSnapshot(context.Background(), dmDev))
|
res.rollback()
|
||||||
rollbackCow()
|
|
||||||
m.loops.Release(baseImagePath)
|
|
||||||
return nil, fmt.Errorf("allocate network slot: %w", err)
|
return nil, fmt.Errorf("allocate network slot: %w", err)
|
||||||
}
|
}
|
||||||
|
res.slotIdx = slotIdx
|
||||||
slot := network.NewSlot(slotIdx)
|
slot := network.NewSlot(slotIdx)
|
||||||
|
|
||||||
if err := network.CreateNetwork(slot); err != nil {
|
if err := network.CreateNetwork(slot); err != nil {
|
||||||
m.slots.Release(slotIdx)
|
res.rollback()
|
||||||
warnErr("dm-snapshot remove error", sandboxID, devicemapper.RemoveSnapshot(context.Background(), dmDev))
|
|
||||||
rollbackCow()
|
|
||||||
m.loops.Release(baseImagePath)
|
|
||||||
return nil, fmt.Errorf("create network: %w", err)
|
return nil, fmt.Errorf("create network: %w", err)
|
||||||
}
|
}
|
||||||
|
res.slot = slot
|
||||||
|
|
||||||
// Restore VM from CH snapshot.
|
// Restore VM from CH snapshot.
|
||||||
vmCfg := vm.VMConfig{
|
vmCfg := vm.VMConfig{
|
||||||
@ -655,6 +659,7 @@ func (m *Manager) Resume(ctx context.Context, sandboxID string, timeoutSec int,
|
|||||||
TemplateID: meta.TemplateID,
|
TemplateID: meta.TemplateID,
|
||||||
KernelPath: m.resolveKernelPath(kernelVersion),
|
KernelPath: m.resolveKernelPath(kernelVersion),
|
||||||
RootfsPath: dmDev.DevicePath,
|
RootfsPath: dmDev.DevicePath,
|
||||||
|
MemoryMB: meta.MemoryMB,
|
||||||
NetworkNamespace: slot.NamespaceID,
|
NetworkNamespace: slot.NamespaceID,
|
||||||
TapDevice: slot.TapName,
|
TapDevice: slot.TapName,
|
||||||
TapMAC: slot.TapMAC,
|
TapMAC: slot.TapMAC,
|
||||||
@ -665,13 +670,10 @@ func (m *Manager) Resume(ctx context.Context, sandboxID string, timeoutSec int,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if _, err := m.vm.CreateFromSnapshot(ctx, vmCfg, pauseDir); err != nil {
|
if _, err := m.vm.CreateFromSnapshot(ctx, vmCfg, pauseDir); err != nil {
|
||||||
warnErr("network cleanup error", sandboxID, network.RemoveNetwork(slot))
|
res.rollback()
|
||||||
m.slots.Release(slotIdx)
|
|
||||||
warnErr("dm-snapshot remove error", sandboxID, devicemapper.RemoveSnapshot(context.Background(), dmDev))
|
|
||||||
rollbackCow()
|
|
||||||
m.loops.Release(baseImagePath)
|
|
||||||
return nil, fmt.Errorf("restore VM from snapshot: %w", err)
|
return nil, fmt.Errorf("restore VM from snapshot: %w", err)
|
||||||
}
|
}
|
||||||
|
res.vm = m.vm
|
||||||
|
|
||||||
// Wait for envd to be ready.
|
// Wait for envd to be ready.
|
||||||
client := envdclient.New(slot.HostIP.String())
|
client := envdclient.New(slot.HostIP.String())
|
||||||
@ -679,12 +681,7 @@ func (m *Manager) Resume(ctx context.Context, sandboxID string, timeoutSec int,
|
|||||||
|
|
||||||
if err := client.WaitUntilReady(waitCtx); err != nil {
|
if err := client.WaitUntilReady(waitCtx); err != nil {
|
||||||
waitCancel()
|
waitCancel()
|
||||||
warnErr("vm destroy error", sandboxID, m.vm.Destroy(context.Background(), sandboxID))
|
res.rollback()
|
||||||
warnErr("network cleanup error", sandboxID, network.RemoveNetwork(slot))
|
|
||||||
m.slots.Release(slotIdx)
|
|
||||||
warnErr("dm-snapshot remove error", sandboxID, devicemapper.RemoveSnapshot(context.Background(), dmDev))
|
|
||||||
rollbackCow()
|
|
||||||
m.loops.Release(baseImagePath)
|
|
||||||
return nil, fmt.Errorf("wait for envd: %w", err)
|
return nil, fmt.Errorf("wait for envd: %w", err)
|
||||||
}
|
}
|
||||||
waitCancel()
|
waitCancel()
|
||||||
@ -905,7 +902,7 @@ func (m *Manager) FlattenRootfs(ctx context.Context, sandboxID string, teamID, t
|
|||||||
func() {
|
func() {
|
||||||
syncCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
|
syncCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
if _, err := sb.client.Exec(syncCtx, "/bin/sync"); err != nil {
|
if _, err := sb.client.Exec(syncCtx, "/bin/sync", nil, nil); err != nil {
|
||||||
slog.Warn("flatten: guest sync failed (non-fatal)", "id", sb.ID, "error", err)
|
slog.Warn("flatten: guest sync failed (non-fatal)", "id", sb.ID, "error", err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@ -1001,7 +998,7 @@ func (m *Manager) DeleteSnapshot(teamID, templateID pgtype.UUID) error {
|
|||||||
// CH handles memory restore internally (with on-demand paging).
|
// CH handles memory restore internally (with on-demand paging).
|
||||||
// The template's rootfs.ext4 is a flattened standalone image — we create a
|
// The template's rootfs.ext4 is a flattened standalone image — we create a
|
||||||
// dm-snapshot on top of it just like a normal Create.
|
// dm-snapshot on top of it just like a normal Create.
|
||||||
func (m *Manager) createFromSnapshot(ctx context.Context, sandboxID string, teamID, templateID pgtype.UUID, vcpus, _, timeoutSec, diskSizeMB int) (*models.Sandbox, error) {
|
func (m *Manager) createFromSnapshot(ctx context.Context, sandboxID string, teamID, templateID pgtype.UUID, vcpus, memoryMB, timeoutSec, diskSizeMB int) (*models.Sandbox, error) {
|
||||||
tmplDir := layout.TemplateDir(m.cfg.WrennDir, teamID, templateID)
|
tmplDir := layout.TemplateDir(m.cfg.WrennDir, teamID, templateID)
|
||||||
|
|
||||||
// Set up dm-snapshot on the template's flattened rootfs.
|
// Set up dm-snapshot on the template's flattened rootfs.
|
||||||
@ -1026,23 +1023,29 @@ func (m *Manager) createFromSnapshot(ctx context.Context, sandboxID string, team
|
|||||||
return nil, fmt.Errorf("create dm-snapshot: %w", err)
|
return nil, fmt.Errorf("create dm-snapshot: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
res := &createResources{
|
||||||
|
sandboxID: sandboxID,
|
||||||
|
loops: m.loops,
|
||||||
|
loopImage: baseRootfs,
|
||||||
|
dmDevice: dmDev,
|
||||||
|
cowPath: cowPath,
|
||||||
|
slots: m.slots,
|
||||||
|
}
|
||||||
|
|
||||||
// Allocate network.
|
// Allocate network.
|
||||||
slotIdx, err := m.slots.Allocate()
|
slotIdx, err := m.slots.Allocate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
warnErr("dm-snapshot remove error", sandboxID, devicemapper.RemoveSnapshot(context.Background(), dmDev))
|
res.rollback()
|
||||||
os.Remove(cowPath)
|
|
||||||
m.loops.Release(baseRootfs)
|
|
||||||
return nil, fmt.Errorf("allocate network slot: %w", err)
|
return nil, fmt.Errorf("allocate network slot: %w", err)
|
||||||
}
|
}
|
||||||
|
res.slotIdx = slotIdx
|
||||||
slot := network.NewSlot(slotIdx)
|
slot := network.NewSlot(slotIdx)
|
||||||
|
|
||||||
if err := network.CreateNetwork(slot); err != nil {
|
if err := network.CreateNetwork(slot); err != nil {
|
||||||
m.slots.Release(slotIdx)
|
res.rollback()
|
||||||
warnErr("dm-snapshot remove error", sandboxID, devicemapper.RemoveSnapshot(context.Background(), dmDev))
|
|
||||||
os.Remove(cowPath)
|
|
||||||
m.loops.Release(baseRootfs)
|
|
||||||
return nil, fmt.Errorf("create network: %w", err)
|
return nil, fmt.Errorf("create network: %w", err)
|
||||||
}
|
}
|
||||||
|
res.slot = slot
|
||||||
|
|
||||||
// Restore VM from CH snapshot.
|
// Restore VM from CH snapshot.
|
||||||
vmCfg := vm.VMConfig{
|
vmCfg := vm.VMConfig{
|
||||||
@ -1051,6 +1054,7 @@ func (m *Manager) createFromSnapshot(ctx context.Context, sandboxID string, team
|
|||||||
KernelPath: m.cfg.KernelPath,
|
KernelPath: m.cfg.KernelPath,
|
||||||
RootfsPath: dmDev.DevicePath,
|
RootfsPath: dmDev.DevicePath,
|
||||||
VCPUs: vcpus,
|
VCPUs: vcpus,
|
||||||
|
MemoryMB: memoryMB,
|
||||||
NetworkNamespace: slot.NamespaceID,
|
NetworkNamespace: slot.NamespaceID,
|
||||||
TapDevice: slot.TapName,
|
TapDevice: slot.TapName,
|
||||||
TapMAC: slot.TapMAC,
|
TapMAC: slot.TapMAC,
|
||||||
@ -1061,13 +1065,10 @@ func (m *Manager) createFromSnapshot(ctx context.Context, sandboxID string, team
|
|||||||
}
|
}
|
||||||
|
|
||||||
if _, err := m.vm.CreateFromSnapshot(ctx, vmCfg, tmplDir); err != nil {
|
if _, err := m.vm.CreateFromSnapshot(ctx, vmCfg, tmplDir); err != nil {
|
||||||
warnErr("network cleanup error", sandboxID, network.RemoveNetwork(slot))
|
res.rollback()
|
||||||
m.slots.Release(slotIdx)
|
|
||||||
warnErr("dm-snapshot remove error", sandboxID, devicemapper.RemoveSnapshot(context.Background(), dmDev))
|
|
||||||
os.Remove(cowPath)
|
|
||||||
m.loops.Release(baseRootfs)
|
|
||||||
return nil, fmt.Errorf("restore VM from snapshot: %w", err)
|
return nil, fmt.Errorf("restore VM from snapshot: %w", err)
|
||||||
}
|
}
|
||||||
|
res.vm = m.vm
|
||||||
|
|
||||||
// Wait for envd.
|
// Wait for envd.
|
||||||
client := envdclient.New(slot.HostIP.String())
|
client := envdclient.New(slot.HostIP.String())
|
||||||
@ -1075,12 +1076,7 @@ func (m *Manager) createFromSnapshot(ctx context.Context, sandboxID string, team
|
|||||||
|
|
||||||
if err := client.WaitUntilReady(waitCtx); err != nil {
|
if err := client.WaitUntilReady(waitCtx); err != nil {
|
||||||
waitCancel()
|
waitCancel()
|
||||||
warnErr("vm destroy error", sandboxID, m.vm.Destroy(context.Background(), sandboxID))
|
res.rollback()
|
||||||
warnErr("network cleanup error", sandboxID, network.RemoveNetwork(slot))
|
|
||||||
m.slots.Release(slotIdx)
|
|
||||||
warnErr("dm-snapshot remove error", sandboxID, devicemapper.RemoveSnapshot(context.Background(), dmDev))
|
|
||||||
os.Remove(cowPath)
|
|
||||||
m.loops.Release(baseRootfs)
|
|
||||||
return nil, fmt.Errorf("wait for envd: %w", err)
|
return nil, fmt.Errorf("wait for envd: %w", err)
|
||||||
}
|
}
|
||||||
waitCancel()
|
waitCancel()
|
||||||
@ -1109,6 +1105,7 @@ func (m *Manager) createFromSnapshot(ctx context.Context, sandboxID string, team
|
|||||||
TemplateTeamID: teamID.Bytes,
|
TemplateTeamID: teamID.Bytes,
|
||||||
TemplateID: templateID.Bytes,
|
TemplateID: templateID.Bytes,
|
||||||
VCPUs: vcpus,
|
VCPUs: vcpus,
|
||||||
|
MemoryMB: memoryMB,
|
||||||
TimeoutSec: timeoutSec,
|
TimeoutSec: timeoutSec,
|
||||||
SlotIndex: slotIdx,
|
SlotIndex: slotIdx,
|
||||||
HostIP: slot.HostIP,
|
HostIP: slot.HostIP,
|
||||||
@ -1143,7 +1140,7 @@ func (m *Manager) createFromSnapshot(ctx context.Context, sandboxID string, team
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Exec runs a command inside a sandbox.
|
// Exec runs a command inside a sandbox.
|
||||||
func (m *Manager) Exec(ctx context.Context, sandboxID string, cmd string, args ...string) (*envdclient.ExecResult, error) {
|
func (m *Manager) Exec(ctx context.Context, sandboxID string, cmd string, args []string, opts *envdclient.ExecOpts) (*envdclient.ExecResult, error) {
|
||||||
sb, err := m.get(sandboxID)
|
sb, err := m.get(sandboxID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -1157,7 +1154,7 @@ func (m *Manager) Exec(ctx context.Context, sandboxID string, cmd string, args .
|
|||||||
sb.LastActiveAt = time.Now()
|
sb.LastActiveAt = time.Now()
|
||||||
m.mu.Unlock()
|
m.mu.Unlock()
|
||||||
|
|
||||||
return sb.client.Exec(ctx, cmd, args...)
|
return sb.client.Exec(ctx, cmd, args, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExecStream runs a command inside a sandbox and returns a channel of streaming events.
|
// ExecStream runs a command inside a sandbox and returns a channel of streaming events.
|
||||||
@ -1538,6 +1535,44 @@ func warnErr(msg string, id string, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// createResources tracks partially-acquired resources during sandbox creation
|
||||||
|
// so they can be rolled back in reverse order on failure.
|
||||||
|
type createResources struct {
|
||||||
|
sandboxID string
|
||||||
|
loops *devicemapper.LoopRegistry
|
||||||
|
vm *vm.Manager
|
||||||
|
loopImage string
|
||||||
|
dmDevice *devicemapper.SnapshotDevice
|
||||||
|
cowPath string
|
||||||
|
slotIdx int
|
||||||
|
slots *network.SlotAllocator
|
||||||
|
slot *network.Slot
|
||||||
|
rollCow func() // optional custom cow rollback (e.g. rename back)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *createResources) rollback() {
|
||||||
|
if r.vm != nil && r.sandboxID != "" {
|
||||||
|
warnErr("vm destroy error", r.sandboxID, r.vm.Destroy(context.Background(), r.sandboxID))
|
||||||
|
}
|
||||||
|
if r.slot != nil {
|
||||||
|
warnErr("network cleanup error", r.sandboxID, network.RemoveNetwork(r.slot))
|
||||||
|
}
|
||||||
|
if r.slots != nil && r.slotIdx > 0 {
|
||||||
|
r.slots.Release(r.slotIdx)
|
||||||
|
}
|
||||||
|
if r.dmDevice != nil {
|
||||||
|
warnErr("dm-snapshot remove error", r.sandboxID, devicemapper.RemoveSnapshot(context.Background(), r.dmDevice))
|
||||||
|
}
|
||||||
|
if r.rollCow != nil {
|
||||||
|
r.rollCow()
|
||||||
|
} else if r.cowPath != "" {
|
||||||
|
os.Remove(r.cowPath)
|
||||||
|
}
|
||||||
|
if r.loopImage != "" {
|
||||||
|
r.loops.Release(r.loopImage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// startCrashWatcher monitors the VM process for unexpected exits.
|
// startCrashWatcher monitors the VM process for unexpected exits.
|
||||||
// If the process exits while the sandbox is still in m.boxes (i.e. not a
|
// If the process exits while the sandbox is still in m.boxes (i.e. not a
|
||||||
// deliberate Destroy), the sandbox is cleaned up and a sandbox.error event
|
// deliberate Destroy), the sandbox is cleaned up and a sandbox.error event
|
||||||
|
|||||||
@ -206,8 +206,3 @@ func (c *chClient) resizeBalloon(ctx context.Context, sizeBytes int64) error {
|
|||||||
"desired_balloon": sizeBytes,
|
"desired_balloon": sizeBytes,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// ping checks if the VMM is alive and ready to accept commands.
|
|
||||||
func (c *chClient) ping(ctx context.Context) error {
|
|
||||||
return c.do(ctx, http.MethodGet, "/api/v1/vmm.ping", nil)
|
|
||||||
}
|
|
||||||
|
|||||||
@ -759,7 +759,11 @@ type ExecRequest struct {
|
|||||||
Cmd string `protobuf:"bytes,2,opt,name=cmd,proto3" json:"cmd,omitempty"`
|
Cmd string `protobuf:"bytes,2,opt,name=cmd,proto3" json:"cmd,omitempty"`
|
||||||
Args []string `protobuf:"bytes,3,rep,name=args,proto3" json:"args,omitempty"`
|
Args []string `protobuf:"bytes,3,rep,name=args,proto3" json:"args,omitempty"`
|
||||||
// Timeout for the command in seconds (default: 30).
|
// Timeout for the command in seconds (default: 30).
|
||||||
TimeoutSec int32 `protobuf:"varint,4,opt,name=timeout_sec,json=timeoutSec,proto3" json:"timeout_sec,omitempty"`
|
TimeoutSec int32 `protobuf:"varint,4,opt,name=timeout_sec,json=timeoutSec,proto3" json:"timeout_sec,omitempty"`
|
||||||
|
// Environment variables to set for the command.
|
||||||
|
Envs map[string]string `protobuf:"bytes,5,rep,name=envs,proto3" json:"envs,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
|
||||||
|
// Working directory for the command.
|
||||||
|
Cwd string `protobuf:"bytes,6,opt,name=cwd,proto3" json:"cwd,omitempty"`
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
@ -822,6 +826,20 @@ func (x *ExecRequest) GetTimeoutSec() int32 {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *ExecRequest) GetEnvs() map[string]string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Envs
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ExecRequest) GetCwd() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Cwd
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
type ExecResponse struct {
|
type ExecResponse struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
Stdout []byte `protobuf:"bytes,1,opt,name=stdout,proto3" json:"stdout,omitempty"`
|
Stdout []byte `protobuf:"bytes,1,opt,name=stdout,proto3" json:"stdout,omitempty"`
|
||||||
@ -4196,14 +4214,19 @@ const file_hostagent_proto_rawDesc = "" +
|
|||||||
"\ateam_id\x18\x02 \x01(\tR\x06teamId\x12\x1f\n" +
|
"\ateam_id\x18\x02 \x01(\tR\x06teamId\x12\x1f\n" +
|
||||||
"\vtemplate_id\x18\x03 \x01(\tR\n" +
|
"\vtemplate_id\x18\x03 \x01(\tR\n" +
|
||||||
"templateId\"\x18\n" +
|
"templateId\"\x18\n" +
|
||||||
"\x16DeleteSnapshotResponse\"s\n" +
|
"\x16DeleteSnapshotResponse\"\xf7\x01\n" +
|
||||||
"\vExecRequest\x12\x1d\n" +
|
"\vExecRequest\x12\x1d\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"sandbox_id\x18\x01 \x01(\tR\tsandboxId\x12\x10\n" +
|
"sandbox_id\x18\x01 \x01(\tR\tsandboxId\x12\x10\n" +
|
||||||
"\x03cmd\x18\x02 \x01(\tR\x03cmd\x12\x12\n" +
|
"\x03cmd\x18\x02 \x01(\tR\x03cmd\x12\x12\n" +
|
||||||
"\x04args\x18\x03 \x03(\tR\x04args\x12\x1f\n" +
|
"\x04args\x18\x03 \x03(\tR\x04args\x12\x1f\n" +
|
||||||
"\vtimeout_sec\x18\x04 \x01(\x05R\n" +
|
"\vtimeout_sec\x18\x04 \x01(\x05R\n" +
|
||||||
"timeoutSec\"[\n" +
|
"timeoutSec\x127\n" +
|
||||||
|
"\x04envs\x18\x05 \x03(\v2#.hostagent.v1.ExecRequest.EnvsEntryR\x04envs\x12\x10\n" +
|
||||||
|
"\x03cwd\x18\x06 \x01(\tR\x03cwd\x1a7\n" +
|
||||||
|
"\tEnvsEntry\x12\x10\n" +
|
||||||
|
"\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" +
|
||||||
|
"\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"[\n" +
|
||||||
"\fExecResponse\x12\x16\n" +
|
"\fExecResponse\x12\x16\n" +
|
||||||
"\x06stdout\x18\x01 \x01(\fR\x06stdout\x12\x16\n" +
|
"\x06stdout\x18\x01 \x01(\fR\x06stdout\x12\x16\n" +
|
||||||
"\x06stderr\x18\x02 \x01(\fR\x06stderr\x12\x1b\n" +
|
"\x06stderr\x18\x02 \x01(\fR\x06stderr\x12\x1b\n" +
|
||||||
@ -4486,7 +4509,7 @@ func file_hostagent_proto_rawDescGZIP() []byte {
|
|||||||
return file_hostagent_proto_rawDescData
|
return file_hostagent_proto_rawDescData
|
||||||
}
|
}
|
||||||
|
|
||||||
var file_hostagent_proto_msgTypes = make([]protoimpl.MessageInfo, 76)
|
var file_hostagent_proto_msgTypes = make([]protoimpl.MessageInfo, 77)
|
||||||
var file_hostagent_proto_goTypes = []any{
|
var file_hostagent_proto_goTypes = []any{
|
||||||
(*CreateSandboxRequest)(nil), // 0: hostagent.v1.CreateSandboxRequest
|
(*CreateSandboxRequest)(nil), // 0: hostagent.v1.CreateSandboxRequest
|
||||||
(*CreateSandboxResponse)(nil), // 1: hostagent.v1.CreateSandboxResponse
|
(*CreateSandboxResponse)(nil), // 1: hostagent.v1.CreateSandboxResponse
|
||||||
@ -4561,99 +4584,101 @@ var file_hostagent_proto_goTypes = []any{
|
|||||||
nil, // 70: hostagent.v1.CreateSandboxResponse.MetadataEntry
|
nil, // 70: hostagent.v1.CreateSandboxResponse.MetadataEntry
|
||||||
nil, // 71: hostagent.v1.ResumeSandboxRequest.DefaultEnvEntry
|
nil, // 71: hostagent.v1.ResumeSandboxRequest.DefaultEnvEntry
|
||||||
nil, // 72: hostagent.v1.ResumeSandboxResponse.MetadataEntry
|
nil, // 72: hostagent.v1.ResumeSandboxResponse.MetadataEntry
|
||||||
nil, // 73: hostagent.v1.SandboxInfo.MetadataEntry
|
nil, // 73: hostagent.v1.ExecRequest.EnvsEntry
|
||||||
nil, // 74: hostagent.v1.PtyAttachRequest.EnvsEntry
|
nil, // 74: hostagent.v1.SandboxInfo.MetadataEntry
|
||||||
nil, // 75: hostagent.v1.StartBackgroundRequest.EnvsEntry
|
nil, // 75: hostagent.v1.PtyAttachRequest.EnvsEntry
|
||||||
|
nil, // 76: hostagent.v1.StartBackgroundRequest.EnvsEntry
|
||||||
}
|
}
|
||||||
var file_hostagent_proto_depIdxs = []int32{
|
var file_hostagent_proto_depIdxs = []int32{
|
||||||
69, // 0: hostagent.v1.CreateSandboxRequest.default_env:type_name -> hostagent.v1.CreateSandboxRequest.DefaultEnvEntry
|
69, // 0: hostagent.v1.CreateSandboxRequest.default_env:type_name -> hostagent.v1.CreateSandboxRequest.DefaultEnvEntry
|
||||||
70, // 1: hostagent.v1.CreateSandboxResponse.metadata:type_name -> hostagent.v1.CreateSandboxResponse.MetadataEntry
|
70, // 1: hostagent.v1.CreateSandboxResponse.metadata:type_name -> hostagent.v1.CreateSandboxResponse.MetadataEntry
|
||||||
71, // 2: hostagent.v1.ResumeSandboxRequest.default_env:type_name -> hostagent.v1.ResumeSandboxRequest.DefaultEnvEntry
|
71, // 2: hostagent.v1.ResumeSandboxRequest.default_env:type_name -> hostagent.v1.ResumeSandboxRequest.DefaultEnvEntry
|
||||||
72, // 3: hostagent.v1.ResumeSandboxResponse.metadata:type_name -> hostagent.v1.ResumeSandboxResponse.MetadataEntry
|
72, // 3: hostagent.v1.ResumeSandboxResponse.metadata:type_name -> hostagent.v1.ResumeSandboxResponse.MetadataEntry
|
||||||
16, // 4: hostagent.v1.ListSandboxesResponse.sandboxes:type_name -> hostagent.v1.SandboxInfo
|
73, // 4: hostagent.v1.ExecRequest.envs:type_name -> hostagent.v1.ExecRequest.EnvsEntry
|
||||||
73, // 5: hostagent.v1.SandboxInfo.metadata:type_name -> hostagent.v1.SandboxInfo.MetadataEntry
|
16, // 5: hostagent.v1.ListSandboxesResponse.sandboxes:type_name -> hostagent.v1.SandboxInfo
|
||||||
23, // 6: hostagent.v1.ExecStreamResponse.start:type_name -> hostagent.v1.ExecStreamStart
|
74, // 6: hostagent.v1.SandboxInfo.metadata:type_name -> hostagent.v1.SandboxInfo.MetadataEntry
|
||||||
24, // 7: hostagent.v1.ExecStreamResponse.data:type_name -> hostagent.v1.ExecStreamData
|
23, // 7: hostagent.v1.ExecStreamResponse.start:type_name -> hostagent.v1.ExecStreamStart
|
||||||
25, // 8: hostagent.v1.ExecStreamResponse.end:type_name -> hostagent.v1.ExecStreamEnd
|
24, // 8: hostagent.v1.ExecStreamResponse.data:type_name -> hostagent.v1.ExecStreamData
|
||||||
27, // 9: hostagent.v1.WriteFileStreamRequest.meta:type_name -> hostagent.v1.WriteFileStreamMeta
|
25, // 9: hostagent.v1.ExecStreamResponse.end:type_name -> hostagent.v1.ExecStreamEnd
|
||||||
33, // 10: hostagent.v1.ListDirResponse.entries:type_name -> hostagent.v1.FileEntry
|
27, // 10: hostagent.v1.WriteFileStreamRequest.meta:type_name -> hostagent.v1.WriteFileStreamMeta
|
||||||
33, // 11: hostagent.v1.MakeDirResponse.entry:type_name -> hostagent.v1.FileEntry
|
33, // 11: hostagent.v1.ListDirResponse.entries:type_name -> hostagent.v1.FileEntry
|
||||||
42, // 12: hostagent.v1.GetSandboxMetricsResponse.points:type_name -> hostagent.v1.MetricPoint
|
33, // 12: hostagent.v1.MakeDirResponse.entry:type_name -> hostagent.v1.FileEntry
|
||||||
42, // 13: hostagent.v1.FlushSandboxMetricsResponse.points_10m:type_name -> hostagent.v1.MetricPoint
|
42, // 13: hostagent.v1.GetSandboxMetricsResponse.points:type_name -> hostagent.v1.MetricPoint
|
||||||
42, // 14: hostagent.v1.FlushSandboxMetricsResponse.points_2h:type_name -> hostagent.v1.MetricPoint
|
42, // 14: hostagent.v1.FlushSandboxMetricsResponse.points_10m:type_name -> hostagent.v1.MetricPoint
|
||||||
42, // 15: hostagent.v1.FlushSandboxMetricsResponse.points_24h:type_name -> hostagent.v1.MetricPoint
|
42, // 15: hostagent.v1.FlushSandboxMetricsResponse.points_2h:type_name -> hostagent.v1.MetricPoint
|
||||||
74, // 16: hostagent.v1.PtyAttachRequest.envs:type_name -> hostagent.v1.PtyAttachRequest.EnvsEntry
|
42, // 16: hostagent.v1.FlushSandboxMetricsResponse.points_24h:type_name -> hostagent.v1.MetricPoint
|
||||||
51, // 17: hostagent.v1.PtyAttachResponse.started:type_name -> hostagent.v1.PtyStarted
|
75, // 17: hostagent.v1.PtyAttachRequest.envs:type_name -> hostagent.v1.PtyAttachRequest.EnvsEntry
|
||||||
52, // 18: hostagent.v1.PtyAttachResponse.output:type_name -> hostagent.v1.PtyOutput
|
51, // 18: hostagent.v1.PtyAttachResponse.started:type_name -> hostagent.v1.PtyStarted
|
||||||
53, // 19: hostagent.v1.PtyAttachResponse.exited:type_name -> hostagent.v1.PtyExited
|
52, // 19: hostagent.v1.PtyAttachResponse.output:type_name -> hostagent.v1.PtyOutput
|
||||||
75, // 20: hostagent.v1.StartBackgroundRequest.envs:type_name -> hostagent.v1.StartBackgroundRequest.EnvsEntry
|
53, // 20: hostagent.v1.PtyAttachResponse.exited:type_name -> hostagent.v1.PtyExited
|
||||||
63, // 21: hostagent.v1.ListProcessesResponse.processes:type_name -> hostagent.v1.ProcessEntry
|
76, // 21: hostagent.v1.StartBackgroundRequest.envs:type_name -> hostagent.v1.StartBackgroundRequest.EnvsEntry
|
||||||
23, // 22: hostagent.v1.ConnectProcessResponse.start:type_name -> hostagent.v1.ExecStreamStart
|
63, // 22: hostagent.v1.ListProcessesResponse.processes:type_name -> hostagent.v1.ProcessEntry
|
||||||
24, // 23: hostagent.v1.ConnectProcessResponse.data:type_name -> hostagent.v1.ExecStreamData
|
23, // 23: hostagent.v1.ConnectProcessResponse.start:type_name -> hostagent.v1.ExecStreamStart
|
||||||
25, // 24: hostagent.v1.ConnectProcessResponse.end:type_name -> hostagent.v1.ExecStreamEnd
|
24, // 24: hostagent.v1.ConnectProcessResponse.data:type_name -> hostagent.v1.ExecStreamData
|
||||||
0, // 25: hostagent.v1.HostAgentService.CreateSandbox:input_type -> hostagent.v1.CreateSandboxRequest
|
25, // 25: hostagent.v1.ConnectProcessResponse.end:type_name -> hostagent.v1.ExecStreamEnd
|
||||||
2, // 26: hostagent.v1.HostAgentService.DestroySandbox:input_type -> hostagent.v1.DestroySandboxRequest
|
0, // 26: hostagent.v1.HostAgentService.CreateSandbox:input_type -> hostagent.v1.CreateSandboxRequest
|
||||||
4, // 27: hostagent.v1.HostAgentService.PauseSandbox:input_type -> hostagent.v1.PauseSandboxRequest
|
2, // 27: hostagent.v1.HostAgentService.DestroySandbox:input_type -> hostagent.v1.DestroySandboxRequest
|
||||||
6, // 28: hostagent.v1.HostAgentService.ResumeSandbox:input_type -> hostagent.v1.ResumeSandboxRequest
|
4, // 28: hostagent.v1.HostAgentService.PauseSandbox:input_type -> hostagent.v1.PauseSandboxRequest
|
||||||
12, // 29: hostagent.v1.HostAgentService.Exec:input_type -> hostagent.v1.ExecRequest
|
6, // 29: hostagent.v1.HostAgentService.ResumeSandbox:input_type -> hostagent.v1.ResumeSandboxRequest
|
||||||
14, // 30: hostagent.v1.HostAgentService.ListSandboxes:input_type -> hostagent.v1.ListSandboxesRequest
|
12, // 30: hostagent.v1.HostAgentService.Exec:input_type -> hostagent.v1.ExecRequest
|
||||||
17, // 31: hostagent.v1.HostAgentService.WriteFile:input_type -> hostagent.v1.WriteFileRequest
|
14, // 31: hostagent.v1.HostAgentService.ListSandboxes:input_type -> hostagent.v1.ListSandboxesRequest
|
||||||
19, // 32: hostagent.v1.HostAgentService.ReadFile:input_type -> hostagent.v1.ReadFileRequest
|
17, // 32: hostagent.v1.HostAgentService.WriteFile:input_type -> hostagent.v1.WriteFileRequest
|
||||||
31, // 33: hostagent.v1.HostAgentService.ListDir:input_type -> hostagent.v1.ListDirRequest
|
19, // 33: hostagent.v1.HostAgentService.ReadFile:input_type -> hostagent.v1.ReadFileRequest
|
||||||
34, // 34: hostagent.v1.HostAgentService.MakeDir:input_type -> hostagent.v1.MakeDirRequest
|
31, // 34: hostagent.v1.HostAgentService.ListDir:input_type -> hostagent.v1.ListDirRequest
|
||||||
36, // 35: hostagent.v1.HostAgentService.RemovePath:input_type -> hostagent.v1.RemovePathRequest
|
34, // 35: hostagent.v1.HostAgentService.MakeDir:input_type -> hostagent.v1.MakeDirRequest
|
||||||
8, // 36: hostagent.v1.HostAgentService.CreateSnapshot:input_type -> hostagent.v1.CreateSnapshotRequest
|
36, // 36: hostagent.v1.HostAgentService.RemovePath:input_type -> hostagent.v1.RemovePathRequest
|
||||||
10, // 37: hostagent.v1.HostAgentService.DeleteSnapshot:input_type -> hostagent.v1.DeleteSnapshotRequest
|
8, // 37: hostagent.v1.HostAgentService.CreateSnapshot:input_type -> hostagent.v1.CreateSnapshotRequest
|
||||||
21, // 38: hostagent.v1.HostAgentService.ExecStream:input_type -> hostagent.v1.ExecStreamRequest
|
10, // 38: hostagent.v1.HostAgentService.DeleteSnapshot:input_type -> hostagent.v1.DeleteSnapshotRequest
|
||||||
26, // 39: hostagent.v1.HostAgentService.WriteFileStream:input_type -> hostagent.v1.WriteFileStreamRequest
|
21, // 39: hostagent.v1.HostAgentService.ExecStream:input_type -> hostagent.v1.ExecStreamRequest
|
||||||
29, // 40: hostagent.v1.HostAgentService.ReadFileStream:input_type -> hostagent.v1.ReadFileStreamRequest
|
26, // 40: hostagent.v1.HostAgentService.WriteFileStream:input_type -> hostagent.v1.WriteFileStreamRequest
|
||||||
38, // 41: hostagent.v1.HostAgentService.PingSandbox:input_type -> hostagent.v1.PingSandboxRequest
|
29, // 41: hostagent.v1.HostAgentService.ReadFileStream:input_type -> hostagent.v1.ReadFileStreamRequest
|
||||||
40, // 42: hostagent.v1.HostAgentService.Terminate:input_type -> hostagent.v1.TerminateRequest
|
38, // 42: hostagent.v1.HostAgentService.PingSandbox:input_type -> hostagent.v1.PingSandboxRequest
|
||||||
43, // 43: hostagent.v1.HostAgentService.GetSandboxMetrics:input_type -> hostagent.v1.GetSandboxMetricsRequest
|
40, // 43: hostagent.v1.HostAgentService.Terminate:input_type -> hostagent.v1.TerminateRequest
|
||||||
45, // 44: hostagent.v1.HostAgentService.FlushSandboxMetrics:input_type -> hostagent.v1.FlushSandboxMetricsRequest
|
43, // 44: hostagent.v1.HostAgentService.GetSandboxMetrics:input_type -> hostagent.v1.GetSandboxMetricsRequest
|
||||||
47, // 45: hostagent.v1.HostAgentService.FlattenRootfs:input_type -> hostagent.v1.FlattenRootfsRequest
|
45, // 45: hostagent.v1.HostAgentService.FlushSandboxMetrics:input_type -> hostagent.v1.FlushSandboxMetricsRequest
|
||||||
49, // 46: hostagent.v1.HostAgentService.PtyAttach:input_type -> hostagent.v1.PtyAttachRequest
|
47, // 46: hostagent.v1.HostAgentService.FlattenRootfs:input_type -> hostagent.v1.FlattenRootfsRequest
|
||||||
54, // 47: hostagent.v1.HostAgentService.PtySendInput:input_type -> hostagent.v1.PtySendInputRequest
|
49, // 47: hostagent.v1.HostAgentService.PtyAttach:input_type -> hostagent.v1.PtyAttachRequest
|
||||||
56, // 48: hostagent.v1.HostAgentService.PtyResize:input_type -> hostagent.v1.PtyResizeRequest
|
54, // 48: hostagent.v1.HostAgentService.PtySendInput:input_type -> hostagent.v1.PtySendInputRequest
|
||||||
58, // 49: hostagent.v1.HostAgentService.PtyKill:input_type -> hostagent.v1.PtyKillRequest
|
56, // 49: hostagent.v1.HostAgentService.PtyResize:input_type -> hostagent.v1.PtyResizeRequest
|
||||||
60, // 50: hostagent.v1.HostAgentService.StartBackground:input_type -> hostagent.v1.StartBackgroundRequest
|
58, // 50: hostagent.v1.HostAgentService.PtyKill:input_type -> hostagent.v1.PtyKillRequest
|
||||||
62, // 51: hostagent.v1.HostAgentService.ListProcesses:input_type -> hostagent.v1.ListProcessesRequest
|
60, // 51: hostagent.v1.HostAgentService.StartBackground:input_type -> hostagent.v1.StartBackgroundRequest
|
||||||
65, // 52: hostagent.v1.HostAgentService.KillProcess:input_type -> hostagent.v1.KillProcessRequest
|
62, // 52: hostagent.v1.HostAgentService.ListProcesses:input_type -> hostagent.v1.ListProcessesRequest
|
||||||
67, // 53: hostagent.v1.HostAgentService.ConnectProcess:input_type -> hostagent.v1.ConnectProcessRequest
|
65, // 53: hostagent.v1.HostAgentService.KillProcess:input_type -> hostagent.v1.KillProcessRequest
|
||||||
1, // 54: hostagent.v1.HostAgentService.CreateSandbox:output_type -> hostagent.v1.CreateSandboxResponse
|
67, // 54: hostagent.v1.HostAgentService.ConnectProcess:input_type -> hostagent.v1.ConnectProcessRequest
|
||||||
3, // 55: hostagent.v1.HostAgentService.DestroySandbox:output_type -> hostagent.v1.DestroySandboxResponse
|
1, // 55: hostagent.v1.HostAgentService.CreateSandbox:output_type -> hostagent.v1.CreateSandboxResponse
|
||||||
5, // 56: hostagent.v1.HostAgentService.PauseSandbox:output_type -> hostagent.v1.PauseSandboxResponse
|
3, // 56: hostagent.v1.HostAgentService.DestroySandbox:output_type -> hostagent.v1.DestroySandboxResponse
|
||||||
7, // 57: hostagent.v1.HostAgentService.ResumeSandbox:output_type -> hostagent.v1.ResumeSandboxResponse
|
5, // 57: hostagent.v1.HostAgentService.PauseSandbox:output_type -> hostagent.v1.PauseSandboxResponse
|
||||||
13, // 58: hostagent.v1.HostAgentService.Exec:output_type -> hostagent.v1.ExecResponse
|
7, // 58: hostagent.v1.HostAgentService.ResumeSandbox:output_type -> hostagent.v1.ResumeSandboxResponse
|
||||||
15, // 59: hostagent.v1.HostAgentService.ListSandboxes:output_type -> hostagent.v1.ListSandboxesResponse
|
13, // 59: hostagent.v1.HostAgentService.Exec:output_type -> hostagent.v1.ExecResponse
|
||||||
18, // 60: hostagent.v1.HostAgentService.WriteFile:output_type -> hostagent.v1.WriteFileResponse
|
15, // 60: hostagent.v1.HostAgentService.ListSandboxes:output_type -> hostagent.v1.ListSandboxesResponse
|
||||||
20, // 61: hostagent.v1.HostAgentService.ReadFile:output_type -> hostagent.v1.ReadFileResponse
|
18, // 61: hostagent.v1.HostAgentService.WriteFile:output_type -> hostagent.v1.WriteFileResponse
|
||||||
32, // 62: hostagent.v1.HostAgentService.ListDir:output_type -> hostagent.v1.ListDirResponse
|
20, // 62: hostagent.v1.HostAgentService.ReadFile:output_type -> hostagent.v1.ReadFileResponse
|
||||||
35, // 63: hostagent.v1.HostAgentService.MakeDir:output_type -> hostagent.v1.MakeDirResponse
|
32, // 63: hostagent.v1.HostAgentService.ListDir:output_type -> hostagent.v1.ListDirResponse
|
||||||
37, // 64: hostagent.v1.HostAgentService.RemovePath:output_type -> hostagent.v1.RemovePathResponse
|
35, // 64: hostagent.v1.HostAgentService.MakeDir:output_type -> hostagent.v1.MakeDirResponse
|
||||||
9, // 65: hostagent.v1.HostAgentService.CreateSnapshot:output_type -> hostagent.v1.CreateSnapshotResponse
|
37, // 65: hostagent.v1.HostAgentService.RemovePath:output_type -> hostagent.v1.RemovePathResponse
|
||||||
11, // 66: hostagent.v1.HostAgentService.DeleteSnapshot:output_type -> hostagent.v1.DeleteSnapshotResponse
|
9, // 66: hostagent.v1.HostAgentService.CreateSnapshot:output_type -> hostagent.v1.CreateSnapshotResponse
|
||||||
22, // 67: hostagent.v1.HostAgentService.ExecStream:output_type -> hostagent.v1.ExecStreamResponse
|
11, // 67: hostagent.v1.HostAgentService.DeleteSnapshot:output_type -> hostagent.v1.DeleteSnapshotResponse
|
||||||
28, // 68: hostagent.v1.HostAgentService.WriteFileStream:output_type -> hostagent.v1.WriteFileStreamResponse
|
22, // 68: hostagent.v1.HostAgentService.ExecStream:output_type -> hostagent.v1.ExecStreamResponse
|
||||||
30, // 69: hostagent.v1.HostAgentService.ReadFileStream:output_type -> hostagent.v1.ReadFileStreamResponse
|
28, // 69: hostagent.v1.HostAgentService.WriteFileStream:output_type -> hostagent.v1.WriteFileStreamResponse
|
||||||
39, // 70: hostagent.v1.HostAgentService.PingSandbox:output_type -> hostagent.v1.PingSandboxResponse
|
30, // 70: hostagent.v1.HostAgentService.ReadFileStream:output_type -> hostagent.v1.ReadFileStreamResponse
|
||||||
41, // 71: hostagent.v1.HostAgentService.Terminate:output_type -> hostagent.v1.TerminateResponse
|
39, // 71: hostagent.v1.HostAgentService.PingSandbox:output_type -> hostagent.v1.PingSandboxResponse
|
||||||
44, // 72: hostagent.v1.HostAgentService.GetSandboxMetrics:output_type -> hostagent.v1.GetSandboxMetricsResponse
|
41, // 72: hostagent.v1.HostAgentService.Terminate:output_type -> hostagent.v1.TerminateResponse
|
||||||
46, // 73: hostagent.v1.HostAgentService.FlushSandboxMetrics:output_type -> hostagent.v1.FlushSandboxMetricsResponse
|
44, // 73: hostagent.v1.HostAgentService.GetSandboxMetrics:output_type -> hostagent.v1.GetSandboxMetricsResponse
|
||||||
48, // 74: hostagent.v1.HostAgentService.FlattenRootfs:output_type -> hostagent.v1.FlattenRootfsResponse
|
46, // 74: hostagent.v1.HostAgentService.FlushSandboxMetrics:output_type -> hostagent.v1.FlushSandboxMetricsResponse
|
||||||
50, // 75: hostagent.v1.HostAgentService.PtyAttach:output_type -> hostagent.v1.PtyAttachResponse
|
48, // 75: hostagent.v1.HostAgentService.FlattenRootfs:output_type -> hostagent.v1.FlattenRootfsResponse
|
||||||
55, // 76: hostagent.v1.HostAgentService.PtySendInput:output_type -> hostagent.v1.PtySendInputResponse
|
50, // 76: hostagent.v1.HostAgentService.PtyAttach:output_type -> hostagent.v1.PtyAttachResponse
|
||||||
57, // 77: hostagent.v1.HostAgentService.PtyResize:output_type -> hostagent.v1.PtyResizeResponse
|
55, // 77: hostagent.v1.HostAgentService.PtySendInput:output_type -> hostagent.v1.PtySendInputResponse
|
||||||
59, // 78: hostagent.v1.HostAgentService.PtyKill:output_type -> hostagent.v1.PtyKillResponse
|
57, // 78: hostagent.v1.HostAgentService.PtyResize:output_type -> hostagent.v1.PtyResizeResponse
|
||||||
61, // 79: hostagent.v1.HostAgentService.StartBackground:output_type -> hostagent.v1.StartBackgroundResponse
|
59, // 79: hostagent.v1.HostAgentService.PtyKill:output_type -> hostagent.v1.PtyKillResponse
|
||||||
64, // 80: hostagent.v1.HostAgentService.ListProcesses:output_type -> hostagent.v1.ListProcessesResponse
|
61, // 80: hostagent.v1.HostAgentService.StartBackground:output_type -> hostagent.v1.StartBackgroundResponse
|
||||||
66, // 81: hostagent.v1.HostAgentService.KillProcess:output_type -> hostagent.v1.KillProcessResponse
|
64, // 81: hostagent.v1.HostAgentService.ListProcesses:output_type -> hostagent.v1.ListProcessesResponse
|
||||||
68, // 82: hostagent.v1.HostAgentService.ConnectProcess:output_type -> hostagent.v1.ConnectProcessResponse
|
66, // 82: hostagent.v1.HostAgentService.KillProcess:output_type -> hostagent.v1.KillProcessResponse
|
||||||
54, // [54:83] is the sub-list for method output_type
|
68, // 83: hostagent.v1.HostAgentService.ConnectProcess:output_type -> hostagent.v1.ConnectProcessResponse
|
||||||
25, // [25:54] is the sub-list for method input_type
|
55, // [55:84] is the sub-list for method output_type
|
||||||
25, // [25:25] is the sub-list for extension type_name
|
26, // [26:55] is the sub-list for method input_type
|
||||||
25, // [25:25] is the sub-list for extension extendee
|
26, // [26:26] is the sub-list for extension type_name
|
||||||
0, // [0:25] is the sub-list for field type_name
|
26, // [26:26] is the sub-list for extension extendee
|
||||||
|
0, // [0:26] is the sub-list for field type_name
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() { file_hostagent_proto_init() }
|
func init() { file_hostagent_proto_init() }
|
||||||
@ -4699,7 +4724,7 @@ func file_hostagent_proto_init() {
|
|||||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_hostagent_proto_rawDesc), len(file_hostagent_proto_rawDesc)),
|
RawDescriptor: unsafe.Slice(unsafe.StringData(file_hostagent_proto_rawDesc), len(file_hostagent_proto_rawDesc)),
|
||||||
NumEnums: 0,
|
NumEnums: 0,
|
||||||
NumMessages: 76,
|
NumMessages: 77,
|
||||||
NumExtensions: 0,
|
NumExtensions: 0,
|
||||||
NumServices: 1,
|
NumServices: 1,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -222,6 +222,10 @@ message ExecRequest {
|
|||||||
repeated string args = 3;
|
repeated string args = 3;
|
||||||
// Timeout for the command in seconds (default: 30).
|
// Timeout for the command in seconds (default: 30).
|
||||||
int32 timeout_sec = 4;
|
int32 timeout_sec = 4;
|
||||||
|
// Environment variables to set for the command.
|
||||||
|
map<string, string> envs = 5;
|
||||||
|
// Working directory for the command.
|
||||||
|
string cwd = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
message ExecResponse {
|
message ExecResponse {
|
||||||
|
|||||||
Reference in New Issue
Block a user