228 lines
5.8 KiB
Go
228 lines
5.8 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package execclient
|
|
|
|
// import (
|
|
// "bytes"
|
|
// "context"
|
|
// "fmt"
|
|
// "io"
|
|
// "os"
|
|
// "os/signal"
|
|
|
|
// "github.com/containerd/console"
|
|
// "google.golang.org/protobuf/proto"
|
|
// "github.com/hashicorp/go-hclog"
|
|
// grpc_net_conn "github.com/mitchellh/go-grpc-net-conn"
|
|
// sshterm "golang.org/x/crypto/ssh/terminal"
|
|
|
|
// "github.com/hashicorp/vagrant-plugin-sdk/terminal"
|
|
// "github.com/hashicorp/vagrant/internal/server/proto/vagrant_server"
|
|
// )
|
|
|
|
// type Client struct {
|
|
// Logger hclog.Logger
|
|
// UI terminal.UI
|
|
// Context context.Context
|
|
// Client vagrant_server.VagrantClient
|
|
// DeploymentId string
|
|
// DeploymentSeq uint64
|
|
// Args []string
|
|
// Stdin io.Reader
|
|
// Stdout io.Writer
|
|
// Stderr io.Writer
|
|
// }
|
|
|
|
// func (c *Client) Run() (int, error) {
|
|
// // Determine if we should allocate a pty. If we should, we need to send
|
|
// // along a TERM value to the remote end that matches our own.
|
|
// var ptyReq *vagrant_server.ExecStreamRequest_PTY
|
|
// var ptyF *os.File
|
|
// var status terminal.Status
|
|
|
|
// if f, ok := c.Stdout.(*os.File); ok && sshterm.IsTerminal(int(f.Fd())) {
|
|
// status = c.UI.Status()
|
|
// defer status.Close()
|
|
// status.Update(fmt.Sprintf("Connecting to deployment v%d...", c.DeploymentSeq))
|
|
|
|
// ptyF = f
|
|
// c, err := console.ConsoleFromFile(ptyF)
|
|
// if err != nil {
|
|
// return 0, err
|
|
// }
|
|
|
|
// sz, err := c.Size()
|
|
// c = nil
|
|
// if err != nil {
|
|
// return 0, err
|
|
// }
|
|
|
|
// ptyReq = &vagrant_server.ExecStreamRequest_PTY{
|
|
// Enable: true,
|
|
// Term: os.Getenv("TERM"),
|
|
// WindowSize: &vagrant_server.ExecStreamRequest_WindowSize{
|
|
// Rows: int32(sz.Height),
|
|
// Cols: int32(sz.Width),
|
|
// Height: int32(sz.Height),
|
|
// Width: int32(sz.Width),
|
|
// },
|
|
// }
|
|
// }
|
|
|
|
// // Start our exec stream
|
|
// client, err := c.Client.StartExecStream(c.Context)
|
|
// if err != nil {
|
|
// return 0, err
|
|
// }
|
|
|
|
// defer client.CloseSend()
|
|
|
|
// if status != nil {
|
|
// status.Update("Initializing session...")
|
|
// }
|
|
|
|
// // Send the start event
|
|
// if err := client.Send(&vagrant_server.ExecStreamRequest{
|
|
// Event: &vagrant_server.ExecStreamRequest_Start_{
|
|
// Start: &vagrant_server.ExecStreamRequest_Start{
|
|
// DeploymentId: c.DeploymentId,
|
|
// Args: c.Args,
|
|
// Pty: ptyReq,
|
|
// },
|
|
// },
|
|
// }); err != nil {
|
|
// return 0, err
|
|
// }
|
|
|
|
// if status != nil {
|
|
// status.Update("Waiting for instance assignment...")
|
|
// }
|
|
|
|
// // Receive our open message. If this fails then we weren't assigned.
|
|
// resp, err := client.Recv()
|
|
// if err != nil {
|
|
// return 1, err
|
|
// }
|
|
// if _, ok := resp.Event.(*vagrant_server.ExecStreamResponse_Open_); !ok {
|
|
// return 1, fmt.Errorf("internal protocol error: unexpected opening message")
|
|
// }
|
|
|
|
// if ptyF != nil {
|
|
// status.Close()
|
|
// c.UI.Output("Connected to deployment v%d", c.DeploymentSeq, terminal.WithSuccessStyle())
|
|
// }
|
|
|
|
// // Close our UI if we can
|
|
// if closer, ok := c.UI.(io.Closer); ok {
|
|
// closer.Close()
|
|
// }
|
|
|
|
// if ptyF != nil {
|
|
// // We need to go into raw mode with stdin
|
|
// if f, ok := c.Stdin.(*os.File); ok {
|
|
// oldState, err := sshterm.MakeRaw(int(f.Fd()))
|
|
// if err != nil {
|
|
// return 0, err
|
|
// }
|
|
// defer sshterm.Restore(int(f.Fd()), oldState)
|
|
// }
|
|
|
|
// fmt.Fprintf(c.Stdout, "\r")
|
|
// }
|
|
|
|
// // Create the context that we'll listen to that lets us cancel our
|
|
// // extra goroutines here.
|
|
// ctx, cancel := context.WithCancel(c.Context)
|
|
// defer cancel()
|
|
|
|
// input := &EscapeWatcher{Cancel: cancel, Input: c.Stdin}
|
|
|
|
// // Build our connection. We only build the stdin sending side because
|
|
// // we can receive other message types from our recv.
|
|
// go io.Copy(&grpc_net_conn.Conn{
|
|
// Stream: client,
|
|
// Request: &vagrant_server.ExecStreamRequest{},
|
|
// Encode: grpc_net_conn.SimpleEncoder(func(msg proto.Message) *[]byte {
|
|
// req := msg.(*vagrant_server.ExecStreamRequest)
|
|
// if req.Event == nil {
|
|
// req.Event = &vagrant_server.ExecStreamRequest_Input_{
|
|
// Input: &vagrant_server.ExecStreamRequest_Input{},
|
|
// }
|
|
// }
|
|
|
|
// return &req.Event.(*vagrant_server.ExecStreamRequest_Input_).Input.Data
|
|
// }),
|
|
// }, input)
|
|
|
|
// // Add our recv blocker that sends data
|
|
// recvCh := make(chan *vagrant_server.ExecStreamResponse)
|
|
// go func() {
|
|
// defer cancel()
|
|
// for {
|
|
// resp, err := client.Recv()
|
|
// if err != nil {
|
|
// c.Logger.Error("receive error", "err", err)
|
|
// return
|
|
// }
|
|
|
|
// recvCh <- resp
|
|
// }
|
|
// }()
|
|
|
|
// // Listen for window change events
|
|
// winchCh := make(chan os.Signal, 1)
|
|
// registerSigwinch(winchCh)
|
|
// defer signal.Stop(winchCh)
|
|
|
|
// // Loop for data
|
|
// for {
|
|
// select {
|
|
// case resp := <-recvCh:
|
|
// switch event := resp.Event.(type) {
|
|
// case *vagrant_server.ExecStreamResponse_Output_:
|
|
// // TODO: stderr
|
|
// out := c.Stdout
|
|
// io.Copy(out, bytes.NewReader(event.Output.Data))
|
|
|
|
// case *vagrant_server.ExecStreamResponse_Exit_:
|
|
// return int(event.Exit.Code), nil
|
|
|
|
// default:
|
|
// c.Logger.Warn("unknown event type",
|
|
// "type", fmt.Sprintf("%T", resp.Event))
|
|
// }
|
|
|
|
// case <-winchCh:
|
|
// // Window change, send new size
|
|
// c, err := console.ConsoleFromFile(ptyF)
|
|
// if err != nil {
|
|
// continue
|
|
// }
|
|
|
|
// sz, err := c.Size()
|
|
// if err != nil {
|
|
// continue
|
|
// }
|
|
|
|
// // Send the new window size
|
|
// if err := client.Send(&vagrant_server.ExecStreamRequest{
|
|
// Event: &vagrant_server.ExecStreamRequest_Winch{
|
|
// Winch: &vagrant_server.ExecStreamRequest_WindowSize{
|
|
// Rows: int32(sz.Height),
|
|
// Cols: int32(sz.Width),
|
|
// Height: int32(sz.Height),
|
|
// Width: int32(sz.Width),
|
|
// },
|
|
// },
|
|
// }); err != nil {
|
|
// // Ignore this error
|
|
// continue
|
|
// }
|
|
|
|
// case <-ctx.Done():
|
|
// return 1, nil
|
|
// }
|
|
// }
|
|
// }
|