From 62bede5dae528b0a4174bac5de0eceb2a67d623b Mon Sep 17 00:00:00 2001 From: pptx704 Date: Sun, 17 May 2026 02:30:32 +0600 Subject: [PATCH] fix: resolve bugs and DRY violations in sandbox manager and API handlers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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) --- .gitignore | 1 + internal/api/handlers_exec.go | 2 + internal/api/handlers_files_stream.go | 7 + internal/api/sandbox_event_consumer.go | 16 +- internal/envdclient/client.go | 25 ++- internal/hostagent/server.go | 13 +- internal/sandbox/manager.go | 171 ++++++++++++-------- internal/vm/ch.go | 5 - proto/hostagent/gen/hostagent.pb.go | 209 ++++++++++++++----------- proto/hostagent/hostagent.proto | 4 + 10 files changed, 272 insertions(+), 181 deletions(-) diff --git a/.gitignore b/.gitignore index 59c36a2..f6d98fa 100644 --- a/.gitignore +++ b/.gitignore @@ -55,3 +55,4 @@ internal/dashboard/static/* .dual-graph/ # Added by code-review-graph .code-review-graph/ +.mcp.json diff --git a/internal/api/handlers_exec.go b/internal/api/handlers_exec.go index 28fd0e1..b1230a1 100644 --- a/internal/api/handlers_exec.go +++ b/internal/api/handlers_exec.go @@ -119,6 +119,8 @@ func (h *execHandler) Exec(w http.ResponseWriter, r *http.Request) { Cmd: req.Cmd, Args: req.Args, TimeoutSec: req.TimeoutSec, + Envs: req.Envs, + Cwd: req.Cwd, })) if err != nil { status, code, msg := agentErrToHTTP(err) diff --git a/internal/api/handlers_files_stream.go b/internal/api/handlers_files_stream.go index 35dfb1f..9d8bc33 100644 --- a/internal/api/handlers_files_stream.go +++ b/internal/api/handlers_files_stream.go @@ -89,6 +89,12 @@ func (h *filesStreamHandler) StreamUpload(w http.ResponseWriter, r *http.Request // Open client-streaming RPC to host agent. stream := agent.WriteFileStream(ctx) + var streamClosed bool + defer func() { + if !streamClosed { + stream.CloseAndReceive() + } + }() // Send metadata first. if err := stream.Send(&pb.WriteFileStreamRequest{ @@ -127,6 +133,7 @@ func (h *filesStreamHandler) StreamUpload(w http.ResponseWriter, r *http.Request } // Close and receive response. + streamClosed = true if _, err := stream.CloseAndReceive(); err != nil { status, code, msg := agentErrToHTTP(err) writeError(w, status, code, msg) diff --git a/internal/api/sandbox_event_consumer.go b/internal/api/sandbox_event_consumer.go index e5a4ad9..dd55328 100644 --- a/internal/api/sandbox_event_consumer.go +++ b/internal/api/sandbox_event_consumer.go @@ -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 // to avoid clobbering concurrent operations. func (c *SandboxEventConsumer) handleFailed(ctx context.Context, sandboxID pgtype.UUID) { - // Try running → error (VM crash pushed by host agent). - if _, err := c.db.UpdateSandboxStatusIf(ctx, db.UpdateSandboxStatusIfParams{ - ID: sandboxID, Status: "running", Status_2: "error", - }); err == nil { - return + // Try each possible pre-failure state until one matches. + for _, fromStatus := range []string{"running", "starting", "pausing", "resuming"} { + if _, err := c.db.UpdateSandboxStatusIf(ctx, db.UpdateSandboxStatusIfParams{ + ID: sandboxID, Status: fromStatus, Status_2: "error", + }); 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) { diff --git a/internal/envdclient/client.go b/internal/envdclient/client.go index eaf4d4c..9dd9ca9 100644 --- a/internal/envdclient/client.go +++ b/internal/envdclient/client.go @@ -78,15 +78,30 @@ type ExecResult struct { 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. // 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 + 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{ - Process: &envdpb.ProcessConfig{ - Cmd: cmd, - Args: args, - }, + Process: proc, Stdin: &stdin, }) diff --git a/internal/hostagent/server.go b/internal/hostagent/server.go index a935a16..1ef308e 100644 --- a/internal/hostagent/server.go +++ b/internal/hostagent/server.go @@ -20,6 +20,7 @@ import ( pb "git.omukk.dev/wrenn/wrenn/proto/hostagent/gen" "git.omukk.dev/wrenn/wrenn/proto/hostagent/gen/hostagentv1connect" + "git.omukk.dev/wrenn/wrenn/internal/envdclient" "git.omukk.dev/wrenn/wrenn/internal/sandbox" ) @@ -90,7 +91,10 @@ func (s *Server) DestroySandbox( req *connect.Request[pb.DestroySandboxRequest], ) (*connect.Response[pb.DestroySandboxResponse], error) { 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 } @@ -216,7 +220,12 @@ func (s *Server) Exec( execCtx, cancel := context.WithTimeout(ctx, timeout) 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 { return nil, connect.NewError(connect.CodeInternal, fmt.Errorf("exec: %w", err)) } diff --git a/internal/sandbox/manager.go b/internal/sandbox/manager.go index df7750b..77ee979 100644 --- a/internal/sandbox/manager.go +++ b/internal/sandbox/manager.go @@ -201,24 +201,30 @@ func (m *Manager) Create(ctx context.Context, sandboxID string, teamID, template 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. slotIdx, err := m.slots.Allocate() if err != nil { - warnErr("dm-snapshot remove error", sandboxID, devicemapper.RemoveSnapshot(context.Background(), dmDev)) - os.Remove(cowPath) - m.loops.Release(baseRootfs) + res.rollback() return nil, fmt.Errorf("allocate network slot: %w", err) } + res.slotIdx = slotIdx slot := network.NewSlot(slotIdx) // Set up network. if err := network.CreateNetwork(slot); err != nil { - m.slots.Release(slotIdx) - warnErr("dm-snapshot remove error", sandboxID, devicemapper.RemoveSnapshot(context.Background(), dmDev)) - os.Remove(cowPath) - m.loops.Release(baseRootfs) + res.rollback() return nil, fmt.Errorf("create network: %w", err) } + res.slot = slot // Boot VM — CH gets the dm device path. 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 { - 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) + res.rollback() return nil, fmt.Errorf("create VM: %w", err) } + res.vm = m.vm // Wait for envd to be ready. client := envdclient.New(slot.HostIP.String()) @@ -252,12 +255,7 @@ func (m *Manager) Create(ctx context.Context, sandboxID string, teamID, template defer waitCancel() if err := client.WaitUntilReady(waitCtx); err != nil { - warnErr("vm destroy error", sandboxID, m.vm.Destroy(context.Background(), sandboxID)) - 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) + res.rollback() 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 ────────────────────── 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) return fmt.Errorf("remove dm-snapshot: %w", err) } snapshotCow := snapshot.CowPath(pauseDir, "") - if err := os.Rename(sb.dmDevice.CowPath, snapshotCow); err != nil { - os.Remove(sb.dmDevice.CowPath) + if err := os.Rename(dmDev.CowPath, snapshotCow); err != nil { + os.Remove(dmDev.CowPath) m.cleanupPauseFailure(sb, sandboxID, pauseDir) 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) } - 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. dmName := "wrenn-" + sandboxID dmDev, err := devicemapper.RestoreSnapshot(ctx, dmName, originLoop, cowPath, originSize) if err != nil { m.loops.Release(baseImagePath) - rollbackCow() + os.Rename(cowPath, savedCow) 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. slotIdx, err := m.slots.Allocate() if err != nil { - warnErr("dm-snapshot remove error", sandboxID, devicemapper.RemoveSnapshot(context.Background(), dmDev)) - rollbackCow() - m.loops.Release(baseImagePath) + res.rollback() return nil, fmt.Errorf("allocate network slot: %w", err) } + res.slotIdx = slotIdx slot := network.NewSlot(slotIdx) if err := network.CreateNetwork(slot); err != nil { - m.slots.Release(slotIdx) - warnErr("dm-snapshot remove error", sandboxID, devicemapper.RemoveSnapshot(context.Background(), dmDev)) - rollbackCow() - m.loops.Release(baseImagePath) + res.rollback() return nil, fmt.Errorf("create network: %w", err) } + res.slot = slot // Restore VM from CH snapshot. vmCfg := vm.VMConfig{ @@ -655,6 +659,7 @@ func (m *Manager) Resume(ctx context.Context, sandboxID string, timeoutSec int, TemplateID: meta.TemplateID, KernelPath: m.resolveKernelPath(kernelVersion), RootfsPath: dmDev.DevicePath, + MemoryMB: meta.MemoryMB, NetworkNamespace: slot.NamespaceID, TapDevice: slot.TapName, 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 { - 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) + res.rollback() return nil, fmt.Errorf("restore VM from snapshot: %w", err) } + res.vm = m.vm // Wait for envd to be ready. 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 { waitCancel() - warnErr("vm destroy error", sandboxID, m.vm.Destroy(context.Background(), sandboxID)) - 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) + res.rollback() return nil, fmt.Errorf("wait for envd: %w", err) } waitCancel() @@ -905,7 +902,7 @@ func (m *Manager) FlattenRootfs(ctx context.Context, sandboxID string, teamID, t func() { syncCtx, cancel := context.WithTimeout(ctx, 10*time.Second) 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) } }() @@ -1001,7 +998,7 @@ func (m *Manager) DeleteSnapshot(teamID, templateID pgtype.UUID) error { // CH handles memory restore internally (with on-demand paging). // The template's rootfs.ext4 is a flattened standalone image — we create a // 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) // 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) } + res := &createResources{ + sandboxID: sandboxID, + loops: m.loops, + loopImage: baseRootfs, + dmDevice: dmDev, + cowPath: cowPath, + slots: m.slots, + } + // Allocate network. slotIdx, err := m.slots.Allocate() if err != nil { - warnErr("dm-snapshot remove error", sandboxID, devicemapper.RemoveSnapshot(context.Background(), dmDev)) - os.Remove(cowPath) - m.loops.Release(baseRootfs) + res.rollback() return nil, fmt.Errorf("allocate network slot: %w", err) } + res.slotIdx = slotIdx slot := network.NewSlot(slotIdx) if err := network.CreateNetwork(slot); err != nil { - m.slots.Release(slotIdx) - warnErr("dm-snapshot remove error", sandboxID, devicemapper.RemoveSnapshot(context.Background(), dmDev)) - os.Remove(cowPath) - m.loops.Release(baseRootfs) + res.rollback() return nil, fmt.Errorf("create network: %w", err) } + res.slot = slot // Restore VM from CH snapshot. vmCfg := vm.VMConfig{ @@ -1051,6 +1054,7 @@ func (m *Manager) createFromSnapshot(ctx context.Context, sandboxID string, team KernelPath: m.cfg.KernelPath, RootfsPath: dmDev.DevicePath, VCPUs: vcpus, + MemoryMB: memoryMB, NetworkNamespace: slot.NamespaceID, TapDevice: slot.TapName, 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 { - 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) + res.rollback() return nil, fmt.Errorf("restore VM from snapshot: %w", err) } + res.vm = m.vm // Wait for envd. 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 { waitCancel() - warnErr("vm destroy error", sandboxID, m.vm.Destroy(context.Background(), sandboxID)) - 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) + res.rollback() return nil, fmt.Errorf("wait for envd: %w", err) } waitCancel() @@ -1109,6 +1105,7 @@ func (m *Manager) createFromSnapshot(ctx context.Context, sandboxID string, team TemplateTeamID: teamID.Bytes, TemplateID: templateID.Bytes, VCPUs: vcpus, + MemoryMB: memoryMB, TimeoutSec: timeoutSec, SlotIndex: slotIdx, HostIP: slot.HostIP, @@ -1143,7 +1140,7 @@ func (m *Manager) createFromSnapshot(ctx context.Context, sandboxID string, team } // 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) if err != nil { return nil, err @@ -1157,7 +1154,7 @@ func (m *Manager) Exec(ctx context.Context, sandboxID string, cmd string, args . sb.LastActiveAt = time.Now() 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. @@ -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. // 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 diff --git a/internal/vm/ch.go b/internal/vm/ch.go index 0c365ba..ae11841 100644 --- a/internal/vm/ch.go +++ b/internal/vm/ch.go @@ -206,8 +206,3 @@ func (c *chClient) resizeBalloon(ctx context.Context, sizeBytes int64) error { "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) -} diff --git a/proto/hostagent/gen/hostagent.pb.go b/proto/hostagent/gen/hostagent.pb.go index b94fcb1..46eba56 100644 --- a/proto/hostagent/gen/hostagent.pb.go +++ b/proto/hostagent/gen/hostagent.pb.go @@ -759,7 +759,11 @@ type ExecRequest struct { Cmd string `protobuf:"bytes,2,opt,name=cmd,proto3" json:"cmd,omitempty"` Args []string `protobuf:"bytes,3,rep,name=args,proto3" json:"args,omitempty"` // 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 sizeCache protoimpl.SizeCache } @@ -822,6 +826,20 @@ func (x *ExecRequest) GetTimeoutSec() int32 { 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 { state protoimpl.MessageState `protogen:"open.v1"` 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" + "\vtemplate_id\x18\x03 \x01(\tR\n" + "templateId\"\x18\n" + - "\x16DeleteSnapshotResponse\"s\n" + + "\x16DeleteSnapshotResponse\"\xf7\x01\n" + "\vExecRequest\x12\x1d\n" + "\n" + "sandbox_id\x18\x01 \x01(\tR\tsandboxId\x12\x10\n" + "\x03cmd\x18\x02 \x01(\tR\x03cmd\x12\x12\n" + "\x04args\x18\x03 \x03(\tR\x04args\x12\x1f\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" + "\x06stdout\x18\x01 \x01(\fR\x06stdout\x12\x16\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 } -var file_hostagent_proto_msgTypes = make([]protoimpl.MessageInfo, 76) +var file_hostagent_proto_msgTypes = make([]protoimpl.MessageInfo, 77) var file_hostagent_proto_goTypes = []any{ (*CreateSandboxRequest)(nil), // 0: hostagent.v1.CreateSandboxRequest (*CreateSandboxResponse)(nil), // 1: hostagent.v1.CreateSandboxResponse @@ -4561,99 +4584,101 @@ var file_hostagent_proto_goTypes = []any{ nil, // 70: hostagent.v1.CreateSandboxResponse.MetadataEntry nil, // 71: hostagent.v1.ResumeSandboxRequest.DefaultEnvEntry nil, // 72: hostagent.v1.ResumeSandboxResponse.MetadataEntry - nil, // 73: hostagent.v1.SandboxInfo.MetadataEntry - nil, // 74: hostagent.v1.PtyAttachRequest.EnvsEntry - nil, // 75: hostagent.v1.StartBackgroundRequest.EnvsEntry + nil, // 73: hostagent.v1.ExecRequest.EnvsEntry + nil, // 74: hostagent.v1.SandboxInfo.MetadataEntry + nil, // 75: hostagent.v1.PtyAttachRequest.EnvsEntry + nil, // 76: hostagent.v1.StartBackgroundRequest.EnvsEntry } var file_hostagent_proto_depIdxs = []int32{ 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 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 - 16, // 4: hostagent.v1.ListSandboxesResponse.sandboxes:type_name -> hostagent.v1.SandboxInfo - 73, // 5: hostagent.v1.SandboxInfo.metadata:type_name -> hostagent.v1.SandboxInfo.MetadataEntry - 23, // 6: hostagent.v1.ExecStreamResponse.start:type_name -> hostagent.v1.ExecStreamStart - 24, // 7: hostagent.v1.ExecStreamResponse.data:type_name -> hostagent.v1.ExecStreamData - 25, // 8: hostagent.v1.ExecStreamResponse.end:type_name -> hostagent.v1.ExecStreamEnd - 27, // 9: hostagent.v1.WriteFileStreamRequest.meta:type_name -> hostagent.v1.WriteFileStreamMeta - 33, // 10: hostagent.v1.ListDirResponse.entries:type_name -> hostagent.v1.FileEntry - 33, // 11: hostagent.v1.MakeDirResponse.entry:type_name -> hostagent.v1.FileEntry - 42, // 12: hostagent.v1.GetSandboxMetricsResponse.points:type_name -> hostagent.v1.MetricPoint - 42, // 13: hostagent.v1.FlushSandboxMetricsResponse.points_10m:type_name -> hostagent.v1.MetricPoint - 42, // 14: hostagent.v1.FlushSandboxMetricsResponse.points_2h:type_name -> hostagent.v1.MetricPoint - 42, // 15: hostagent.v1.FlushSandboxMetricsResponse.points_24h:type_name -> hostagent.v1.MetricPoint - 74, // 16: hostagent.v1.PtyAttachRequest.envs:type_name -> hostagent.v1.PtyAttachRequest.EnvsEntry - 51, // 17: hostagent.v1.PtyAttachResponse.started:type_name -> hostagent.v1.PtyStarted - 52, // 18: hostagent.v1.PtyAttachResponse.output:type_name -> hostagent.v1.PtyOutput - 53, // 19: hostagent.v1.PtyAttachResponse.exited:type_name -> hostagent.v1.PtyExited - 75, // 20: hostagent.v1.StartBackgroundRequest.envs:type_name -> hostagent.v1.StartBackgroundRequest.EnvsEntry - 63, // 21: hostagent.v1.ListProcessesResponse.processes:type_name -> hostagent.v1.ProcessEntry - 23, // 22: hostagent.v1.ConnectProcessResponse.start:type_name -> hostagent.v1.ExecStreamStart - 24, // 23: hostagent.v1.ConnectProcessResponse.data:type_name -> hostagent.v1.ExecStreamData - 25, // 24: hostagent.v1.ConnectProcessResponse.end:type_name -> hostagent.v1.ExecStreamEnd - 0, // 25: hostagent.v1.HostAgentService.CreateSandbox:input_type -> hostagent.v1.CreateSandboxRequest - 2, // 26: hostagent.v1.HostAgentService.DestroySandbox:input_type -> hostagent.v1.DestroySandboxRequest - 4, // 27: hostagent.v1.HostAgentService.PauseSandbox:input_type -> hostagent.v1.PauseSandboxRequest - 6, // 28: hostagent.v1.HostAgentService.ResumeSandbox:input_type -> hostagent.v1.ResumeSandboxRequest - 12, // 29: hostagent.v1.HostAgentService.Exec:input_type -> hostagent.v1.ExecRequest - 14, // 30: hostagent.v1.HostAgentService.ListSandboxes:input_type -> hostagent.v1.ListSandboxesRequest - 17, // 31: hostagent.v1.HostAgentService.WriteFile:input_type -> hostagent.v1.WriteFileRequest - 19, // 32: hostagent.v1.HostAgentService.ReadFile:input_type -> hostagent.v1.ReadFileRequest - 31, // 33: hostagent.v1.HostAgentService.ListDir:input_type -> hostagent.v1.ListDirRequest - 34, // 34: hostagent.v1.HostAgentService.MakeDir:input_type -> hostagent.v1.MakeDirRequest - 36, // 35: hostagent.v1.HostAgentService.RemovePath:input_type -> hostagent.v1.RemovePathRequest - 8, // 36: hostagent.v1.HostAgentService.CreateSnapshot:input_type -> hostagent.v1.CreateSnapshotRequest - 10, // 37: hostagent.v1.HostAgentService.DeleteSnapshot:input_type -> hostagent.v1.DeleteSnapshotRequest - 21, // 38: hostagent.v1.HostAgentService.ExecStream:input_type -> hostagent.v1.ExecStreamRequest - 26, // 39: hostagent.v1.HostAgentService.WriteFileStream:input_type -> hostagent.v1.WriteFileStreamRequest - 29, // 40: hostagent.v1.HostAgentService.ReadFileStream:input_type -> hostagent.v1.ReadFileStreamRequest - 38, // 41: hostagent.v1.HostAgentService.PingSandbox:input_type -> hostagent.v1.PingSandboxRequest - 40, // 42: hostagent.v1.HostAgentService.Terminate:input_type -> hostagent.v1.TerminateRequest - 43, // 43: hostagent.v1.HostAgentService.GetSandboxMetrics:input_type -> hostagent.v1.GetSandboxMetricsRequest - 45, // 44: hostagent.v1.HostAgentService.FlushSandboxMetrics:input_type -> hostagent.v1.FlushSandboxMetricsRequest - 47, // 45: hostagent.v1.HostAgentService.FlattenRootfs:input_type -> hostagent.v1.FlattenRootfsRequest - 49, // 46: hostagent.v1.HostAgentService.PtyAttach:input_type -> hostagent.v1.PtyAttachRequest - 54, // 47: hostagent.v1.HostAgentService.PtySendInput:input_type -> hostagent.v1.PtySendInputRequest - 56, // 48: hostagent.v1.HostAgentService.PtyResize:input_type -> hostagent.v1.PtyResizeRequest - 58, // 49: hostagent.v1.HostAgentService.PtyKill:input_type -> hostagent.v1.PtyKillRequest - 60, // 50: hostagent.v1.HostAgentService.StartBackground:input_type -> hostagent.v1.StartBackgroundRequest - 62, // 51: hostagent.v1.HostAgentService.ListProcesses:input_type -> hostagent.v1.ListProcessesRequest - 65, // 52: hostagent.v1.HostAgentService.KillProcess:input_type -> hostagent.v1.KillProcessRequest - 67, // 53: hostagent.v1.HostAgentService.ConnectProcess:input_type -> hostagent.v1.ConnectProcessRequest - 1, // 54: hostagent.v1.HostAgentService.CreateSandbox:output_type -> hostagent.v1.CreateSandboxResponse - 3, // 55: hostagent.v1.HostAgentService.DestroySandbox:output_type -> hostagent.v1.DestroySandboxResponse - 5, // 56: hostagent.v1.HostAgentService.PauseSandbox:output_type -> hostagent.v1.PauseSandboxResponse - 7, // 57: hostagent.v1.HostAgentService.ResumeSandbox:output_type -> hostagent.v1.ResumeSandboxResponse - 13, // 58: hostagent.v1.HostAgentService.Exec:output_type -> hostagent.v1.ExecResponse - 15, // 59: hostagent.v1.HostAgentService.ListSandboxes:output_type -> hostagent.v1.ListSandboxesResponse - 18, // 60: hostagent.v1.HostAgentService.WriteFile:output_type -> hostagent.v1.WriteFileResponse - 20, // 61: hostagent.v1.HostAgentService.ReadFile:output_type -> hostagent.v1.ReadFileResponse - 32, // 62: hostagent.v1.HostAgentService.ListDir:output_type -> hostagent.v1.ListDirResponse - 35, // 63: hostagent.v1.HostAgentService.MakeDir:output_type -> hostagent.v1.MakeDirResponse - 37, // 64: hostagent.v1.HostAgentService.RemovePath:output_type -> hostagent.v1.RemovePathResponse - 9, // 65: hostagent.v1.HostAgentService.CreateSnapshot:output_type -> hostagent.v1.CreateSnapshotResponse - 11, // 66: hostagent.v1.HostAgentService.DeleteSnapshot:output_type -> hostagent.v1.DeleteSnapshotResponse - 22, // 67: hostagent.v1.HostAgentService.ExecStream:output_type -> hostagent.v1.ExecStreamResponse - 28, // 68: hostagent.v1.HostAgentService.WriteFileStream:output_type -> hostagent.v1.WriteFileStreamResponse - 30, // 69: hostagent.v1.HostAgentService.ReadFileStream:output_type -> hostagent.v1.ReadFileStreamResponse - 39, // 70: hostagent.v1.HostAgentService.PingSandbox:output_type -> hostagent.v1.PingSandboxResponse - 41, // 71: hostagent.v1.HostAgentService.Terminate:output_type -> hostagent.v1.TerminateResponse - 44, // 72: hostagent.v1.HostAgentService.GetSandboxMetrics:output_type -> hostagent.v1.GetSandboxMetricsResponse - 46, // 73: hostagent.v1.HostAgentService.FlushSandboxMetrics:output_type -> hostagent.v1.FlushSandboxMetricsResponse - 48, // 74: hostagent.v1.HostAgentService.FlattenRootfs:output_type -> hostagent.v1.FlattenRootfsResponse - 50, // 75: hostagent.v1.HostAgentService.PtyAttach:output_type -> hostagent.v1.PtyAttachResponse - 55, // 76: hostagent.v1.HostAgentService.PtySendInput:output_type -> hostagent.v1.PtySendInputResponse - 57, // 77: hostagent.v1.HostAgentService.PtyResize:output_type -> hostagent.v1.PtyResizeResponse - 59, // 78: hostagent.v1.HostAgentService.PtyKill:output_type -> hostagent.v1.PtyKillResponse - 61, // 79: hostagent.v1.HostAgentService.StartBackground:output_type -> hostagent.v1.StartBackgroundResponse - 64, // 80: hostagent.v1.HostAgentService.ListProcesses:output_type -> hostagent.v1.ListProcessesResponse - 66, // 81: hostagent.v1.HostAgentService.KillProcess:output_type -> hostagent.v1.KillProcessResponse - 68, // 82: hostagent.v1.HostAgentService.ConnectProcess:output_type -> hostagent.v1.ConnectProcessResponse - 54, // [54:83] is the sub-list for method output_type - 25, // [25:54] is the sub-list for method input_type - 25, // [25:25] is the sub-list for extension type_name - 25, // [25:25] is the sub-list for extension extendee - 0, // [0:25] is the sub-list for field type_name + 73, // 4: hostagent.v1.ExecRequest.envs:type_name -> hostagent.v1.ExecRequest.EnvsEntry + 16, // 5: hostagent.v1.ListSandboxesResponse.sandboxes:type_name -> hostagent.v1.SandboxInfo + 74, // 6: hostagent.v1.SandboxInfo.metadata:type_name -> hostagent.v1.SandboxInfo.MetadataEntry + 23, // 7: hostagent.v1.ExecStreamResponse.start:type_name -> hostagent.v1.ExecStreamStart + 24, // 8: hostagent.v1.ExecStreamResponse.data:type_name -> hostagent.v1.ExecStreamData + 25, // 9: hostagent.v1.ExecStreamResponse.end:type_name -> hostagent.v1.ExecStreamEnd + 27, // 10: hostagent.v1.WriteFileStreamRequest.meta:type_name -> hostagent.v1.WriteFileStreamMeta + 33, // 11: hostagent.v1.ListDirResponse.entries:type_name -> hostagent.v1.FileEntry + 33, // 12: hostagent.v1.MakeDirResponse.entry:type_name -> hostagent.v1.FileEntry + 42, // 13: hostagent.v1.GetSandboxMetricsResponse.points:type_name -> hostagent.v1.MetricPoint + 42, // 14: hostagent.v1.FlushSandboxMetricsResponse.points_10m:type_name -> hostagent.v1.MetricPoint + 42, // 15: hostagent.v1.FlushSandboxMetricsResponse.points_2h:type_name -> hostagent.v1.MetricPoint + 42, // 16: hostagent.v1.FlushSandboxMetricsResponse.points_24h:type_name -> hostagent.v1.MetricPoint + 75, // 17: hostagent.v1.PtyAttachRequest.envs:type_name -> hostagent.v1.PtyAttachRequest.EnvsEntry + 51, // 18: hostagent.v1.PtyAttachResponse.started:type_name -> hostagent.v1.PtyStarted + 52, // 19: hostagent.v1.PtyAttachResponse.output:type_name -> hostagent.v1.PtyOutput + 53, // 20: hostagent.v1.PtyAttachResponse.exited:type_name -> hostagent.v1.PtyExited + 76, // 21: hostagent.v1.StartBackgroundRequest.envs:type_name -> hostagent.v1.StartBackgroundRequest.EnvsEntry + 63, // 22: hostagent.v1.ListProcessesResponse.processes:type_name -> hostagent.v1.ProcessEntry + 23, // 23: hostagent.v1.ConnectProcessResponse.start:type_name -> hostagent.v1.ExecStreamStart + 24, // 24: hostagent.v1.ConnectProcessResponse.data:type_name -> hostagent.v1.ExecStreamData + 25, // 25: hostagent.v1.ConnectProcessResponse.end:type_name -> hostagent.v1.ExecStreamEnd + 0, // 26: hostagent.v1.HostAgentService.CreateSandbox:input_type -> hostagent.v1.CreateSandboxRequest + 2, // 27: hostagent.v1.HostAgentService.DestroySandbox:input_type -> hostagent.v1.DestroySandboxRequest + 4, // 28: hostagent.v1.HostAgentService.PauseSandbox:input_type -> hostagent.v1.PauseSandboxRequest + 6, // 29: hostagent.v1.HostAgentService.ResumeSandbox:input_type -> hostagent.v1.ResumeSandboxRequest + 12, // 30: hostagent.v1.HostAgentService.Exec:input_type -> hostagent.v1.ExecRequest + 14, // 31: hostagent.v1.HostAgentService.ListSandboxes:input_type -> hostagent.v1.ListSandboxesRequest + 17, // 32: hostagent.v1.HostAgentService.WriteFile:input_type -> hostagent.v1.WriteFileRequest + 19, // 33: hostagent.v1.HostAgentService.ReadFile:input_type -> hostagent.v1.ReadFileRequest + 31, // 34: hostagent.v1.HostAgentService.ListDir:input_type -> hostagent.v1.ListDirRequest + 34, // 35: hostagent.v1.HostAgentService.MakeDir:input_type -> hostagent.v1.MakeDirRequest + 36, // 36: hostagent.v1.HostAgentService.RemovePath:input_type -> hostagent.v1.RemovePathRequest + 8, // 37: hostagent.v1.HostAgentService.CreateSnapshot:input_type -> hostagent.v1.CreateSnapshotRequest + 10, // 38: hostagent.v1.HostAgentService.DeleteSnapshot:input_type -> hostagent.v1.DeleteSnapshotRequest + 21, // 39: hostagent.v1.HostAgentService.ExecStream:input_type -> hostagent.v1.ExecStreamRequest + 26, // 40: hostagent.v1.HostAgentService.WriteFileStream:input_type -> hostagent.v1.WriteFileStreamRequest + 29, // 41: hostagent.v1.HostAgentService.ReadFileStream:input_type -> hostagent.v1.ReadFileStreamRequest + 38, // 42: hostagent.v1.HostAgentService.PingSandbox:input_type -> hostagent.v1.PingSandboxRequest + 40, // 43: hostagent.v1.HostAgentService.Terminate:input_type -> hostagent.v1.TerminateRequest + 43, // 44: hostagent.v1.HostAgentService.GetSandboxMetrics:input_type -> hostagent.v1.GetSandboxMetricsRequest + 45, // 45: hostagent.v1.HostAgentService.FlushSandboxMetrics:input_type -> hostagent.v1.FlushSandboxMetricsRequest + 47, // 46: hostagent.v1.HostAgentService.FlattenRootfs:input_type -> hostagent.v1.FlattenRootfsRequest + 49, // 47: hostagent.v1.HostAgentService.PtyAttach:input_type -> hostagent.v1.PtyAttachRequest + 54, // 48: hostagent.v1.HostAgentService.PtySendInput:input_type -> hostagent.v1.PtySendInputRequest + 56, // 49: hostagent.v1.HostAgentService.PtyResize:input_type -> hostagent.v1.PtyResizeRequest + 58, // 50: hostagent.v1.HostAgentService.PtyKill:input_type -> hostagent.v1.PtyKillRequest + 60, // 51: hostagent.v1.HostAgentService.StartBackground:input_type -> hostagent.v1.StartBackgroundRequest + 62, // 52: hostagent.v1.HostAgentService.ListProcesses:input_type -> hostagent.v1.ListProcessesRequest + 65, // 53: hostagent.v1.HostAgentService.KillProcess:input_type -> hostagent.v1.KillProcessRequest + 67, // 54: hostagent.v1.HostAgentService.ConnectProcess:input_type -> hostagent.v1.ConnectProcessRequest + 1, // 55: hostagent.v1.HostAgentService.CreateSandbox:output_type -> hostagent.v1.CreateSandboxResponse + 3, // 56: hostagent.v1.HostAgentService.DestroySandbox:output_type -> hostagent.v1.DestroySandboxResponse + 5, // 57: hostagent.v1.HostAgentService.PauseSandbox:output_type -> hostagent.v1.PauseSandboxResponse + 7, // 58: hostagent.v1.HostAgentService.ResumeSandbox:output_type -> hostagent.v1.ResumeSandboxResponse + 13, // 59: hostagent.v1.HostAgentService.Exec:output_type -> hostagent.v1.ExecResponse + 15, // 60: hostagent.v1.HostAgentService.ListSandboxes:output_type -> hostagent.v1.ListSandboxesResponse + 18, // 61: hostagent.v1.HostAgentService.WriteFile:output_type -> hostagent.v1.WriteFileResponse + 20, // 62: hostagent.v1.HostAgentService.ReadFile:output_type -> hostagent.v1.ReadFileResponse + 32, // 63: hostagent.v1.HostAgentService.ListDir:output_type -> hostagent.v1.ListDirResponse + 35, // 64: hostagent.v1.HostAgentService.MakeDir:output_type -> hostagent.v1.MakeDirResponse + 37, // 65: hostagent.v1.HostAgentService.RemovePath:output_type -> hostagent.v1.RemovePathResponse + 9, // 66: hostagent.v1.HostAgentService.CreateSnapshot:output_type -> hostagent.v1.CreateSnapshotResponse + 11, // 67: hostagent.v1.HostAgentService.DeleteSnapshot:output_type -> hostagent.v1.DeleteSnapshotResponse + 22, // 68: hostagent.v1.HostAgentService.ExecStream:output_type -> hostagent.v1.ExecStreamResponse + 28, // 69: hostagent.v1.HostAgentService.WriteFileStream:output_type -> hostagent.v1.WriteFileStreamResponse + 30, // 70: hostagent.v1.HostAgentService.ReadFileStream:output_type -> hostagent.v1.ReadFileStreamResponse + 39, // 71: hostagent.v1.HostAgentService.PingSandbox:output_type -> hostagent.v1.PingSandboxResponse + 41, // 72: hostagent.v1.HostAgentService.Terminate:output_type -> hostagent.v1.TerminateResponse + 44, // 73: hostagent.v1.HostAgentService.GetSandboxMetrics:output_type -> hostagent.v1.GetSandboxMetricsResponse + 46, // 74: hostagent.v1.HostAgentService.FlushSandboxMetrics:output_type -> hostagent.v1.FlushSandboxMetricsResponse + 48, // 75: hostagent.v1.HostAgentService.FlattenRootfs:output_type -> hostagent.v1.FlattenRootfsResponse + 50, // 76: hostagent.v1.HostAgentService.PtyAttach:output_type -> hostagent.v1.PtyAttachResponse + 55, // 77: hostagent.v1.HostAgentService.PtySendInput:output_type -> hostagent.v1.PtySendInputResponse + 57, // 78: hostagent.v1.HostAgentService.PtyResize:output_type -> hostagent.v1.PtyResizeResponse + 59, // 79: hostagent.v1.HostAgentService.PtyKill:output_type -> hostagent.v1.PtyKillResponse + 61, // 80: hostagent.v1.HostAgentService.StartBackground:output_type -> hostagent.v1.StartBackgroundResponse + 64, // 81: hostagent.v1.HostAgentService.ListProcesses:output_type -> hostagent.v1.ListProcessesResponse + 66, // 82: hostagent.v1.HostAgentService.KillProcess:output_type -> hostagent.v1.KillProcessResponse + 68, // 83: hostagent.v1.HostAgentService.ConnectProcess:output_type -> hostagent.v1.ConnectProcessResponse + 55, // [55:84] is the sub-list for method output_type + 26, // [26:55] is the sub-list for method input_type + 26, // [26:26] is the sub-list for extension 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() } @@ -4699,7 +4724,7 @@ func file_hostagent_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_hostagent_proto_rawDesc), len(file_hostagent_proto_rawDesc)), NumEnums: 0, - NumMessages: 76, + NumMessages: 77, NumExtensions: 0, NumServices: 1, }, diff --git a/proto/hostagent/hostagent.proto b/proto/hostagent/hostagent.proto index c8cc462..1907a63 100644 --- a/proto/hostagent/hostagent.proto +++ b/proto/hostagent/hostagent.proto @@ -222,6 +222,10 @@ message ExecRequest { repeated string args = 3; // Timeout for the command in seconds (default: 30). int32 timeout_sec = 4; + // Environment variables to set for the command. + map envs = 5; + // Working directory for the command. + string cwd = 6; } message ExecResponse {