This repository was archived by the owner on Apr 1, 2026. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 245
Expand file tree
/
Copy pathexec.go
More file actions
126 lines (103 loc) · 3.78 KB
/
exec.go
File metadata and controls
126 lines (103 loc) · 3.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
package exec
import (
"bufio"
"context"
"fmt"
"io"
"os"
"strings"
"k8s.io/klog/v2"
"k8s.io/kubectl/pkg/util/term"
"github.com/redhat-developer/odo/pkg/log"
"github.com/redhat-developer/odo/pkg/platform"
)
type ExecClient struct {
platformClient platform.Client
}
func NewExecClient(platformClient platform.Client) *ExecClient {
return &ExecClient{
platformClient: platformClient,
}
}
// ExecuteCommand executes the given command in the pod's container,
// writing the output to the specified respective pipe writers
// when directRun is true, will execute the command with terminal in Raw mode and connected to local standard I/Os
// so input, including Ctrl-c, is sent to the remote process
func (o ExecClient) ExecuteCommand(ctx context.Context, command []string, podName string, containerName string, directRun bool, stdoutWriter *io.PipeWriter, stderrWriter *io.PipeWriter) (stdout []string, stderr []string, err error) {
if !directRun {
soutReader, soutWriter := io.Pipe()
serrReader, serrWriter := io.Pipe()
klog.V(2).Infof("Executing command %v for pod: %v in container: %v", command, podName, containerName)
// Read stdout and stderr, store their output in cmdOutput, and also pass output to consoleOutput Writers (if non-nil)
stdoutCompleteChannel := startReaderGoroutine(os.Stdout, soutReader, directRun, &stdout, stdoutWriter)
stderrCompleteChannel := startReaderGoroutine(os.Stderr, serrReader, directRun, &stderr, stderrWriter)
err = o.platformClient.ExecCMDInContainer(ctx, containerName, podName, command, soutWriter, serrWriter, nil, false)
// Block until we have received all the container output from each stream
_ = soutWriter.Close()
<-stdoutCompleteChannel
_ = serrWriter.Close()
<-stderrCompleteChannel
// Details are displayed only if no outputs are displayed
if err != nil && !directRun {
// It is safe to read from stdout and stderr here, as the goroutines are guaranteed to have terminated at this point.
klog.V(2).Infof("ExecuteCommand returned an an err: %v. for command '%v'\nstdout: %v\nstderr: %v",
err, command, stdout, stderr)
msg := fmt.Sprintf("unable to exec command %v", command)
if len(stdout) != 0 {
msg += fmt.Sprintf("\n=== stdout===\n%s", strings.Join(stdout, "\n"))
}
if len(stderr) != 0 {
msg += fmt.Sprintf("\n=== stderr===\n%s", strings.Join(stderr, "\n"))
}
return stdout, stderr, fmt.Errorf("%s: %w", msg, err)
}
return stdout, stderr, err
}
tty := setupTTY()
fn := func() error {
return o.platformClient.ExecCMDInContainer(ctx, containerName, podName, command, tty.Out, os.Stderr, tty.In, tty.Raw)
}
return nil, nil, tty.Safe(fn)
}
// This goroutine will automatically pipe the output from the writer (passed into ExecCMDInContainer) to
// the loggers.
// The returned channel will contain a single nil entry once the reader has closed.
func startReaderGoroutine(logWriter io.Writer, reader io.Reader, show bool, cmdOutput *[]string, consoleOutput *io.PipeWriter) chan interface{} {
result := make(chan interface{})
go func() {
scanner := bufio.NewScanner(reader)
for scanner.Scan() {
line := scanner.Text()
if show {
_, err := fmt.Fprintln(logWriter, line)
if err != nil {
log.Errorf("Unable to print to stdout: %s", err.Error())
}
} else {
klog.V(2).Infof(line)
}
if cmdOutput != nil {
*cmdOutput = append(*cmdOutput, line)
}
if consoleOutput != nil {
_, err := consoleOutput.Write([]byte(line + "\n"))
if err != nil {
log.Errorf("Error occurred on writing string to consoleOutput writer: %s", err.Error())
}
}
}
result <- nil
}()
return result
}
func setupTTY() term.TTY {
tty := term.TTY{
In: os.Stdin,
Out: os.Stdout,
}
if !tty.IsTerminalIn() || !tty.IsTerminalOut() {
return tty
}
tty.Raw = true
return tty
}