|
3 | 3 | package remote |
4 | 4 |
|
5 | 5 | import ( |
6 | | - "bufio" |
7 | 6 | "context" |
8 | 7 | "errors" |
9 | 8 | "fmt" |
10 | 9 | "os" |
| 10 | + "os/exec" |
11 | 11 | "path/filepath" |
12 | | - "strings" |
13 | | - "sync" |
14 | 12 | "time" |
15 | 13 |
|
16 | 14 | "github.com/pkg/sftp" |
@@ -94,99 +92,112 @@ func (c *Client) Close() error { |
94 | 92 |
|
95 | 93 | // Run runs the given command on the remote host and returns the combined output |
96 | 94 | // while also printing the command output as it occurs. |
97 | | -func (c Client) Run(ctx context.Context, cmd string) ([]byte, error) { |
| 95 | +func (c Client) Run(ctx context.Context, cmdStr string) ([]byte, error) { |
98 | 96 | ctx, cancel := context.WithTimeout(ctx, commandTimeout) |
99 | 97 | defer cancel() |
100 | 98 |
|
101 | | - // Create a session |
102 | | - session, err := c.client.NewSession() |
| 99 | + // Run the command via `ssh` directly |
| 100 | + cmd := exec.CommandContext(ctx, "ssh", "-vv", c.client.User()+"@"+c.host, cmdStr) |
| 101 | + cmd.Stderr = os.Stderr |
| 102 | + cmd.Stdout = os.Stdout |
| 103 | + err := cmd.Run() |
103 | 104 | if err != nil { |
104 | | - return nil, fmt.Errorf("failed to create session: %w", err) |
105 | | - } |
106 | | - defer session.Close() |
107 | | - |
108 | | - err = session.RequestPty("xterm", 80, 40, ssh.TerminalModes{}) |
109 | | - if err != nil { |
110 | | - log.Fatalf("Request for pseudo terminal failed: %v", err) |
111 | | - } |
112 | | - |
113 | | - // Create pipes for stdout and stderr |
114 | | - stdout, err := session.StdoutPipe() |
115 | | - if err != nil { |
116 | | - return nil, fmt.Errorf("failed to create stdout pipe: %w", err) |
117 | | - } |
118 | | - stderr, err := session.StderrPipe() |
119 | | - if err != nil { |
120 | | - return nil, fmt.Errorf("failed to create stderr pipe: %w", err) |
121 | | - } |
122 | | - |
123 | | - log.Infof("Running command %q on remote host %q", cmd, c.client.RemoteAddr().String()) |
124 | | - |
125 | | - // Start the remote command |
126 | | - startTime := time.Now() |
127 | | - if err := session.Start(cmd); err != nil { |
128 | | - return nil, fmt.Errorf("failed to start command: %w", err) |
129 | | - } |
130 | | - |
131 | | - // Create scanners to read stdout and stderr line by line |
132 | | - stdoutScanner := bufio.NewScanner(stdout) |
133 | | - stderrScanner := bufio.NewScanner(stderr) |
134 | | - //stdoutScanner.Split(bufio.ScanWords) |
135 | | - var combinedOutput []string |
136 | | - var mu sync.Mutex |
137 | | - var wg sync.WaitGroup |
138 | | - |
139 | | - log.SetLevel(log.DebugLevel) |
140 | | - |
141 | | - // Use goroutines to read and print both stdout and stderr concurrently |
142 | | - wg.Add(2) |
143 | | - go func() { |
144 | | - for stdoutScanner.Scan() { |
145 | | - line := stdoutScanner.Text() |
146 | | - log.Debug("\t", line) |
147 | | - mu.Lock() |
148 | | - combinedOutput = append(combinedOutput, line) |
149 | | - mu.Unlock() |
| 105 | + if errors.Is(err, context.DeadlineExceeded) { |
| 106 | + return nil, fmt.Errorf("command timed out after %s: %w", commandTimeout, err) |
150 | 107 | } |
151 | | - wg.Done() |
152 | | - }() |
153 | | - go func() { |
154 | | - for stderrScanner.Scan() { |
155 | | - line := stderrScanner.Text() |
156 | | - log.Warning("\t", line) |
157 | | - mu.Lock() |
158 | | - combinedOutput = append(combinedOutput, line) |
159 | | - mu.Unlock() |
160 | | - } |
161 | | - wg.Done() |
162 | | - }() |
163 | | - |
164 | | - waitDone := make(chan error, 1) |
165 | | - go func() { |
166 | | - waitDone <- session.Wait() |
167 | | - }() |
168 | | - |
169 | | - select { |
170 | | - case <-ctx.Done(): |
171 | | - if errors.Is(ctx.Err(), context.DeadlineExceeded) { |
172 | | - return nil, fmt.Errorf("command timed out after %s", commandTimeout) |
173 | | - } |
174 | | - return nil, fmt.Errorf("command cancelled: %w", ctx.Err()) |
175 | | - case err := <-waitDone: |
176 | | - elapsedTime := time.Since(startTime) |
177 | | - wg.Wait() // wait for scanners to finish |
178 | | - mu.Lock() |
179 | | - defer mu.Unlock() |
180 | | - |
181 | | - out := []byte(strings.Join(combinedOutput, "\n")) |
182 | | - if err != nil { |
183 | | - log.Warningf("Command %q failed in %s", cmd, elapsedTime) |
184 | | - return out, fmt.Errorf("command failed: %w", err) |
185 | | - } |
186 | | - log.Infof("Command %q finished in %s", cmd, elapsedTime) |
187 | | - |
188 | | - return out, nil |
189 | | - } |
| 108 | + return nil, fmt.Errorf("failed to run command %q on host %q: %w", cmdStr, c.host, err) |
| 109 | + } |
| 110 | + return nil, nil |
| 111 | + |
| 112 | + //// Create a session |
| 113 | + //session, err := c.client.NewSession() |
| 114 | + //if err != nil { |
| 115 | + // return nil, fmt.Errorf("failed to create session: %w", err) |
| 116 | + //} |
| 117 | + //defer session.Close() |
| 118 | + // |
| 119 | + //err = session.RequestPty("xterm", 80, 40, ssh.TerminalModes{}) |
| 120 | + //if err != nil { |
| 121 | + // log.Fatalf("Request for pseudo terminal failed: %v", err) |
| 122 | + //} |
| 123 | + // |
| 124 | + //// Create pipes for stdout and stderr |
| 125 | + //stdout, err := session.StdoutPipe() |
| 126 | + //if err != nil { |
| 127 | + // return nil, fmt.Errorf("failed to create stdout pipe: %w", err) |
| 128 | + //} |
| 129 | + //stderr, err := session.StderrPipe() |
| 130 | + //if err != nil { |
| 131 | + // return nil, fmt.Errorf("failed to create stderr pipe: %w", err) |
| 132 | + //} |
| 133 | + // |
| 134 | + //log.Infof("Running command %q on remote host %q", cmdStr, c.client.RemoteAddr().String()) |
| 135 | + // |
| 136 | + //// Start the remote command |
| 137 | + //startTime := time.Now() |
| 138 | + //if err := session.Start(cmdStr); err != nil { |
| 139 | + // return nil, fmt.Errorf("failed to start command: %w", err) |
| 140 | + //} |
| 141 | + // |
| 142 | + //// Create scanners to read stdout and stderr line by line |
| 143 | + //stdoutScanner := bufio.NewScanner(stdout) |
| 144 | + //stderrScanner := bufio.NewScanner(stderr) |
| 145 | + ////stdoutScanner.Split(bufio.ScanWords) |
| 146 | + //var combinedOutput []string |
| 147 | + //var mu sync.Mutex |
| 148 | + //var wg sync.WaitGroup |
| 149 | + // |
| 150 | + //log.SetLevel(log.DebugLevel) |
| 151 | + // |
| 152 | + //// Use goroutines to read and print both stdout and stderr concurrently |
| 153 | + //wg.Add(2) |
| 154 | + //go func() { |
| 155 | + // for stdoutScanner.Scan() { |
| 156 | + // line := stdoutScanner.Text() |
| 157 | + // log.Debug("\t", line) |
| 158 | + // mu.Lock() |
| 159 | + // combinedOutput = append(combinedOutput, line) |
| 160 | + // mu.Unlock() |
| 161 | + // } |
| 162 | + // wg.Done() |
| 163 | + //}() |
| 164 | + //go func() { |
| 165 | + // for stderrScanner.Scan() { |
| 166 | + // line := stderrScanner.Text() |
| 167 | + // log.Warning("\t", line) |
| 168 | + // mu.Lock() |
| 169 | + // combinedOutput = append(combinedOutput, line) |
| 170 | + // mu.Unlock() |
| 171 | + // } |
| 172 | + // wg.Done() |
| 173 | + //}() |
| 174 | + // |
| 175 | + //waitDone := make(chan error, 1) |
| 176 | + //go func() { |
| 177 | + // waitDone <- session.Wait() |
| 178 | + //}() |
| 179 | + // |
| 180 | + //select { |
| 181 | + //case <-ctx.Done(): |
| 182 | + // if errors.Is(ctx.Err(), context.DeadlineExceeded) { |
| 183 | + // return nil, fmt.Errorf("command timed out after %s", commandTimeout) |
| 184 | + // } |
| 185 | + // return nil, fmt.Errorf("command cancelled: %w", ctx.Err()) |
| 186 | + //case err := <-waitDone: |
| 187 | + // elapsedTime := time.Since(startTime) |
| 188 | + // wg.Wait() // wait for scanners to finish |
| 189 | + // mu.Lock() |
| 190 | + // defer mu.Unlock() |
| 191 | + // |
| 192 | + // out := []byte(strings.Join(combinedOutput, "\n")) |
| 193 | + // if err != nil { |
| 194 | + // log.Warningf("Command %q failed in %s", cmdStr, elapsedTime) |
| 195 | + // return out, fmt.Errorf("command failed: %w", err) |
| 196 | + // } |
| 197 | + // log.Infof("Command %q finished in %s", cmdStr, elapsedTime) |
| 198 | + // |
| 199 | + // return out, nil |
| 200 | + //} |
190 | 201 | } |
191 | 202 |
|
192 | 203 | // Upload uploads the given local file to the remote host. |
|
0 commit comments