-
Notifications
You must be signed in to change notification settings - Fork 40
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement TerminalCommand and Terminal command mode (#563)
This change implements a new mode called `TERMINAL`. In this mode, the server, right after starting the script, writes initial commands that are responsible for collecting environment variables, which will be added or deleted to the current session. The `TERMINAL` mode is supported only in the `v2alpha1` runner. Fixes #551
- Loading branch information
Showing
20 changed files
with
590 additions
and
204 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
package command | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/pkg/errors" | ||
) | ||
|
||
type TerminalCommand struct { | ||
*VirtualCommand | ||
|
||
envCollector *shellEnvCollector | ||
} | ||
|
||
var _ Command = (*TerminalCommand)(nil) | ||
|
||
func NewTerminal(cfg *Config, opts Options) *TerminalCommand { | ||
return &TerminalCommand{ | ||
VirtualCommand: NewVirtual(cfg, opts), | ||
} | ||
} | ||
|
||
func (c *TerminalCommand) Start(ctx context.Context) error { | ||
if isNil(c.opts.StdinWriter) { | ||
return errors.New("stdin writer is nil") | ||
} | ||
|
||
if err := c.VirtualCommand.Start(ctx); err != nil { | ||
return err | ||
} | ||
|
||
c.opts.Logger.Info("a terminal command started") | ||
|
||
c.envCollector = &shellEnvCollector{ | ||
buf: c.opts.StdinWriter, | ||
} | ||
return c.envCollector.Init() | ||
} | ||
|
||
func (c *TerminalCommand) Wait() (err error) { | ||
err = c.VirtualCommand.Wait() | ||
|
||
if cErr := c.collectEnv(); err == nil && cErr != nil { | ||
err = cErr | ||
} | ||
|
||
return err | ||
} | ||
|
||
func (c *TerminalCommand) collectEnv() error { | ||
if c.opts.Session == nil || c.envCollector == nil { | ||
return nil | ||
} | ||
|
||
changed, deleted, err := c.envCollector.Collect() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if err := c.opts.Session.SetEnv(changed...); err != nil { | ||
return errors.WithMessage(err, "failed to set the new or updated env") | ||
} | ||
|
||
c.opts.Session.DeleteEnv(deleted...) | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
//go:build !windows | ||
|
||
package command | ||
|
||
import ( | ||
"bytes" | ||
"context" | ||
"io" | ||
"testing" | ||
"time" | ||
|
||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
"go.uber.org/zap/zaptest" | ||
|
||
runnerv2alpha1 "github.com/stateful/runme/v3/internal/gen/proto/go/runme/runner/v2alpha1" | ||
) | ||
|
||
func TestTerminalCommand_Options_Stdinwriter_Nil(t *testing.T) { | ||
cmd := NewTerminal( | ||
&Config{ | ||
ProgramName: "bash", | ||
Mode: runnerv2alpha1.CommandMode_COMMAND_MODE_TERMINAL, | ||
}, | ||
Options{}, | ||
) | ||
|
||
ctx, cancel := context.WithTimeout(context.Background(), time.Second) | ||
defer cancel() | ||
|
||
require.ErrorContains(t, cmd.Start(ctx), "stdin writer is nil") | ||
} | ||
|
||
func TestTerminalCommand(t *testing.T) { | ||
logger := zaptest.NewLogger(t) | ||
session := NewSession() | ||
|
||
stdinR, stdinW := io.Pipe() | ||
stdout := bytes.NewBuffer(nil) | ||
|
||
cmd := NewTerminal( | ||
&Config{ | ||
ProgramName: "bash", | ||
Mode: runnerv2alpha1.CommandMode_COMMAND_MODE_TERMINAL, | ||
}, | ||
Options{ | ||
Logger: logger, | ||
Session: session, | ||
StdinWriter: stdinW, | ||
Stdin: stdinR, | ||
Stdout: stdout, | ||
}, | ||
) | ||
|
||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) | ||
defer cancel() | ||
|
||
require.NoError(t, cmd.Start(ctx)) | ||
|
||
// TODO(adamb): on macOS is is not necessary, but on Linux | ||
// we need to wait for the shell to start before we start sending commands. | ||
time.Sleep(time.Second) | ||
|
||
_, err := stdinW.Write([]byte("export TEST_ENV=1\n")) | ||
require.NoError(t, err) | ||
_, err = stdinW.Write([]byte{0x04}) // EOT | ||
require.NoError(t, err) | ||
|
||
require.NoError(t, cmd.Wait()) | ||
assert.Equal(t, []string{"TEST_ENV=1"}, session.GetEnv()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.