diff --git a/pkg/app/master/command/debug/cli.go b/pkg/app/master/command/debug/cli.go index 54e763a1..9def11cf 100644 --- a/pkg/app/master/command/debug/cli.go +++ b/pkg/app/master/command/debug/cli.go @@ -32,7 +32,7 @@ type Volume struct { } type CommandParams struct { - KubeComm *KubernetesHandlerComm + RuntimeCommunicator *RuntimeCommunicator /// the runtime environment type Runtime string /// the running container which we want to attach to diff --git a/pkg/app/master/command/debug/handle_docker_runtime.go b/pkg/app/master/command/debug/handle_docker_runtime.go index ce4e6631..39ac9a25 100644 --- a/pkg/app/master/command/debug/handle_docker_runtime.go +++ b/pkg/app/master/command/debug/handle_docker_runtime.go @@ -181,13 +181,18 @@ func HandleDockerRuntime( } //todo: need to validate that the session container exists and it's running - - r, w := io.Pipe() - go io.Copy(w, os.Stdin) + var input io.Reader + if commandParams.TUI { + input = &TUIReader{inputChan: commandParams.RuntimeCommunicator.InputChan} + } else { + r, w := io.Pipe() + input = r + go io.Copy(w, os.Stdin) + } options := dockerapi.AttachToContainerOptions{ Container: containerID, - InputStream: r, + InputStream: input, OutputStream: os.Stdout, ErrorStream: os.Stderr, Stdin: true, @@ -252,6 +257,15 @@ func HandleDockerRuntime( Terminal: commandParams.DoTerminal, } + if commandParams.TUI { + reader := &TUIReader{inputChan: commandParams.RuntimeCommunicator.InputChan} + options.IO = container.ExecutionIO{ + Input: reader, + Output: os.Stdout, + Error: os.Stderr, + } + } + exe, err := container.NewExecution( xc, logger, diff --git a/pkg/app/master/command/debug/handle_kubernetes_runtime.go b/pkg/app/master/command/debug/handle_kubernetes_runtime.go index 3ad6bc25..ca288a90 100644 --- a/pkg/app/master/command/debug/handle_kubernetes_runtime.go +++ b/pkg/app/master/command/debug/handle_kubernetes_runtime.go @@ -819,8 +819,8 @@ func HandleKubernetesRuntime( fmt.Printf("\n") //note: blocks until done streaming or failure... if commandParams.TUI { - // TODO - move KubeComm off of command params - reader := &KubeReader{inputChan: commandParams.KubeComm.InputChan} + // TODO - move RuntimeCommunicator off of command params + reader := &TUIReader{inputChan: commandParams.RuntimeCommunicator.InputChan} err = attach.StreamWithContext( ctx, remotecommand.StreamOptions{ @@ -862,16 +862,16 @@ func HandleKubernetesRuntime( // as per the comment in `debug/tui.go`. // An InputReader usable by Docker, Podman, Kubernetes, and Containerd // will be added to this directory. -type KubeReader struct { +type TUIReader struct { inputChan chan InputKey } -func (kr *KubeReader) Read(p []byte) (n int, err error) { - inputKey, ok := <-kr.inputChan +func (tuiReader *TUIReader) Read(p []byte) (n int, err error) { + inputKey, ok := <-tuiReader.inputChan if !ok { return 0, io.EOF } - log.Debugf("KubeReader received inputKey %v", inputKey) + log.Debugf("TUIReader received inputKey %v", inputKey) switch inputKey.Special { case NotSpecial: p[0] = byte(inputKey.Rune) diff --git a/pkg/app/master/command/debug/tui.go b/pkg/app/master/command/debug/tui.go index 73ccaf79..a5505d69 100644 --- a/pkg/app/master/command/debug/tui.go +++ b/pkg/app/master/command/debug/tui.go @@ -38,7 +38,7 @@ type TUI struct { // Handle kubernetes session connections subscriptionHandler subscriptionHandler isListening bool - kubeComm *KubernetesHandlerComm + runtimeCommunicator *RuntimeCommunicator exitedSession bool } @@ -94,15 +94,15 @@ type subscriptionHandler struct { } // newSubscription creates a new subscription handler with an async data channel -func newSubscription(gcvalues *command.GenericParams, kubeComm *KubernetesHandlerComm) subscriptionHandler { +func newSubscription(gcvalues *command.GenericParams, runtimeCommunicator *RuntimeCommunicator) subscriptionHandler { dataChan := make(chan terminalStartMessage) - go launchSessionHandler(dataChan, gcvalues, kubeComm) + go launchSessionHandler(dataChan, gcvalues, runtimeCommunicator) return subscriptionHandler{ dataChan: dataChan, } } -func launchSessionHandler(dataChan chan terminalStartMessage, gcvalues *command.GenericParams, kubeComm *KubernetesHandlerComm) { +func launchSessionHandler(dataChan chan terminalStartMessage, gcvalues *command.GenericParams, runtimeCommunicator *RuntimeCommunicator) { // Create a subscription channel and define subscriptionChannels map for passing data subscriptionChannel := make(chan interface{}) subscriptionChannels := map[string]chan interface{}{ @@ -117,21 +117,34 @@ func launchSessionHandler(dataChan chan terminalStartMessage, gcvalues *command. subscriptionChannels, ) - // Define command parameters for k8s runtime + // Define command parameters for docker runtime // + Hard coded values at the moment for this PoC cparams := &CommandParams{ - Runtime: "k8s", - TargetRef: "nginx", - Kubeconfig: crt.KubeconfigDefault, - TargetNamespace: "default", + Runtime: "docker", + TargetRef: "docker-amor", DebugContainerImage: BusyboxImage, DoFallbackToTargetUser: true, DoRunAsTargetShell: true, DoTerminal: true, - KubeComm: kubeComm, + RuntimeCommunicator: runtimeCommunicator, TUI: true, } + // Connect to active session | Kubernetes session + // cparams := &CommandParams{ + // Runtime: "docker", + // TargetRef: "docker-amor", + // // Kubeconfig: crt.KubeconfigDefault, + // // TargetNamespace: "default", + // DebugContainerImage: BusyboxImage, + // DoFallbackToTargetUser: true, + // DoRunAsTargetShell: true, + // DoTerminal: true, + // RuntimeCommunicator: runtimeCommunicator, + // TUI: true, + // ActionConnectSession: true, + // } + // TODO - Pass runtime communicator go OnCommand(xc, gcvalues, cparams) @@ -160,7 +173,7 @@ func launchSessionHandler(dataChan chan terminalStartMessage, gcvalues *command. if infoValue, exists := channelResponse["info"]; exists { if infoValue == "terminal.start" { dataChan <- terminalStartMessage("Session ready. Opening session below...\nPress esc to exit session.\n") - kubeComm.InputChan <- InputKey{Special: Enter} + runtimeCommunicator.InputChan <- InputKey{Special: Enter} } } } @@ -198,7 +211,7 @@ func InitialTUI(standalone bool, gcvalues *command.GenericParams) *TUI { // This handler should not live on CommandParams, // but be passed in to OnCommand, then to the respective // runtime handler. -type KubernetesHandlerComm struct { +type RuntimeCommunicator struct { InputChan chan InputKey } @@ -348,7 +361,7 @@ func (m TUI) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } select { - case m.kubeComm.InputChan <- inputKey: + case m.runtimeCommunicator.InputChan <- inputKey: // Key sent successfully default: // Channel is full or closed, handle accordingly @@ -390,11 +403,11 @@ func (m TUI) Update(msg tea.Msg) (tea.Model, tea.Cmd) { // this can be done by rendering state output, m.isListening = true log.Debug("Start listening") - kubeComm := &KubernetesHandlerComm{ + runtimeCommunicator := &RuntimeCommunicator{ InputChan: make(chan InputKey, 100), } - m.kubeComm = kubeComm - m.subscriptionHandler = newSubscription(m.gcvalues, kubeComm) + m.runtimeCommunicator = runtimeCommunicator + m.subscriptionHandler = newSubscription(m.gcvalues, runtimeCommunicator) return m, listenToAsyncData(m.subscriptionHandler.dataChan) } diff --git a/pkg/app/master/container/execution.go b/pkg/app/master/container/execution.go index d311b927..c760868b 100644 --- a/pkg/app/master/container/execution.go +++ b/pkg/app/master/container/execution.go @@ -530,11 +530,18 @@ func (ref *Execution) monitorSysExitSync() { } func (ref *Execution) startTerminal() { - r, w := io.Pipe() - go io.Copy(w, os.Stdin) + var input io.Reader + if ref.options.IO.Input == nil { + r, w := io.Pipe() + input = r + go io.Copy(w, os.Stdin) + } else { + input = ref.options.IO.Input + } + options := dockerapi.AttachToContainerOptions{ Container: ref.ContainerID, - InputStream: r, + InputStream: input, OutputStream: os.Stdout, ErrorStream: os.Stderr, Stdin: true,