Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 6 additions & 65 deletions cli/commands/app_run.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
package commands

import (
"io"
"os"
"os/signal"
"strings"

"github.com/containerd/console"
"golang.org/x/sys/unix"
"miren.dev/runtime/api/exec/exec_v1alpha"
"miren.dev/runtime/pkg/rpc/stream"
)
Expand All @@ -18,60 +13,12 @@ func AppRun(ctx *Context, opts struct {
Args []string `rest:"true"`
}) error {
opt := new(exec_v1alpha.ShellOptions)

if len(opts.Args) > 0 {
opt.SetCommand(opts.Args)
}

winCh := make(chan os.Signal, 1)
winUpdates := make(chan *exec_v1alpha.WindowSize, 1)

var (
in io.Reader
out io.Writer
)

if con, err := console.ConsoleFromFile(os.Stdin); err == nil {
in = con
out = con

if csz, err := con.Size(); err == nil {
ws := new(exec_v1alpha.WindowSize)
ws.SetHeight(int32(csz.Height))
ws.SetWidth(int32(csz.Width))
opt.SetWinSize(ws)
}

defer con.Reset()
con.SetRaw()

signal.Notify(winCh, unix.SIGWINCH)
defer signal.Stop(winCh)

go func() {
for {
select {
case <-ctx.Done():
return
case <-winCh:
csz, err := con.Size()
if err != nil {
ctx.Log.Error("failed to get console size", "error", err)
continue
}

ws := new(exec_v1alpha.WindowSize)
ws.SetHeight(int32(csz.Height))
ws.SetWidth(int32(csz.Width))

winUpdates <- ws
}
}
}()
} else {
in = os.Stdin
out = os.Stdout
}
in, out, winUpdates, cleanup := setupExecIO(ctx, opt)
defer cleanup()

cl, err := ctx.RPCClient("dev.miren.runtime/exec")
if err != nil {
Expand All @@ -80,25 +27,19 @@ func AppRun(ctx *Context, opts struct {

sec := exec_v1alpha.NewSandboxExecClient(cl)

input := stream.ServeReader(ctx, in)
output := stream.ServeWriter(ctx, out)

winUS := stream.ChanReader(winUpdates)

results, err := sec.Exec(
ctx,
"app", opts.App,
strings.Join(opts.Args, " "),
opt,
input, output,
winUS,
stream.ServeReader(ctx, in),
stream.ServeWriter(ctx, out),
stream.ChanReader(winUpdates),
)
if err != nil {
return err
}

status := results.Code()
ctx.SetExitCode(int(status))

ctx.SetExitCode(int(results.Code()))
return nil
}
78 changes: 78 additions & 0 deletions cli/commands/exec_io.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package commands

import (
"io"
"os"
"os/signal"

"github.com/containerd/console"
"golang.org/x/sys/unix"
"miren.dev/runtime/api/exec/exec_v1alpha"
)

// setupExecIO wires stdin/stdout for an interactive exec call.
//
// When both stdin and stdout are TTYs, it puts stdin in raw mode, fills in
// opt.WinSize, and starts a SIGWINCH goroutine that pushes resize events onto
// the returned channel. Otherwise it returns os.Stdin/os.Stdout untouched and
// leaves opt.WinSize unset, which signals the server not to allocate a PTY
// (preserving binary output — see MIR-1001).
//
// The returned cleanup func resets the console and stops the signal handler
// and must always be deferred by the caller.
func setupExecIO(ctx *Context, opt *exec_v1alpha.ShellOptions) (
io.Reader,
io.Writer,
<-chan *exec_v1alpha.WindowSize,
func(),
) {
winUpdates := make(chan *exec_v1alpha.WindowSize, 1)

stdinCon, stdinErr := console.ConsoleFromFile(os.Stdin)
stdoutCon, stdoutErr := console.ConsoleFromFile(os.Stdout)
if stdinErr != nil || stdoutErr != nil {
return os.Stdin, os.Stdout, winUpdates, func() {}
}

if csz, err := stdinCon.Size(); err == nil {
ws := new(exec_v1alpha.WindowSize)
ws.SetHeight(int32(csz.Height))
ws.SetWidth(int32(csz.Width))
opt.SetWinSize(ws)
}

if err := stdinCon.SetRaw(); err != nil {
ctx.Log.Error("failed to set raw mode on stdin", "error", err)
}

winCh := make(chan os.Signal, 1)
signal.Notify(winCh, unix.SIGWINCH)

go func() {
for {
select {
case <-ctx.Done():
return
case <-winCh:
csz, err := stdinCon.Size()
if err != nil {
ctx.Log.Error("failed to get console size", "error", err)
continue
}

ws := new(exec_v1alpha.WindowSize)
ws.SetHeight(int32(csz.Height))
ws.SetWidth(int32(csz.Width))

winUpdates <- ws
}
}
}()

cleanup := func() {
signal.Stop(winCh)
stdinCon.Reset()
}

return stdinCon, stdoutCon, winUpdates, cleanup
}
67 changes: 5 additions & 62 deletions cli/commands/sandbox_exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,8 @@ package commands

import (
"fmt"
"io"
"os"
"os/signal"
"strings"

"github.com/containerd/console"
"golang.org/x/sys/unix"
"miren.dev/runtime/api/exec/exec_v1alpha"
"miren.dev/runtime/pkg/rpc/stream"
)
Expand All @@ -35,72 +30,20 @@ func SandboxExec(ctx *Context, opts struct {

sec := exec_v1alpha.NewSandboxExecClient(cl)

winCh := make(chan os.Signal, 1)
winUpdates := make(chan *exec_v1alpha.WindowSize, 1)

opt := new(exec_v1alpha.ShellOptions)

var (
in io.Reader
out io.Writer
)

opt.SetCommand(args)

// Set up interactive console if available, otherwise use standard streams
if con, err := console.ConsoleFromFile(os.Stdin); err == nil {
in = con
out = con

if csz, err := con.Size(); err == nil {
ws := new(exec_v1alpha.WindowSize)
ws.SetHeight(int32(csz.Height))
ws.SetWidth(int32(csz.Width))
opt.SetWinSize(ws)
}

defer con.Reset()
con.SetRaw()

signal.Notify(winCh, unix.SIGWINCH)
defer signal.Stop(winCh)

go func() {
for {
select {
case <-ctx.Done():
return
case <-winCh:
csz, err := con.Size()
if err != nil {
ctx.Log.Error("failed to get console size", "error", err)
continue
}

ws := new(exec_v1alpha.WindowSize)
ws.SetHeight(int32(csz.Height))
ws.SetWidth(int32(csz.Width))

winUpdates <- ws
}
}
}()
} else {
in = os.Stdin
out = os.Stdout
}

input := stream.ServeReader(ctx, in)
output := stream.ServeWriter(ctx, out)
winUS := stream.ChanReader(winUpdates)
in, out, winUpdates, cleanup := setupExecIO(ctx, opt)
defer cleanup()

res, err := sec.Exec(
ctx,
"id", id,
strings.Join(args, " "),
opt,
input, output,
winUS,
stream.ServeReader(ctx, in),
stream.ServeWriter(ctx, out),
stream.ChanReader(winUpdates),
)
if err != nil {
return err
Expand Down
1 change: 1 addition & 0 deletions servers/exec_proxy/exec_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ func (s *Server) Exec(ctx context.Context, req *exec_v1alpha.SandboxExecExec) er
}

found = ret.Entity().Entity()
id = found.Id().String()

case "app":
name := args.Value()
Expand Down
Loading