@@ -20,19 +20,21 @@ import (
20
20
"bytes"
21
21
"context"
22
22
"fmt"
23
+ "io/ioutil"
23
24
"net"
24
25
"os"
25
26
"path/filepath"
26
27
"time"
27
28
28
29
"github.com/onsi/gomega"
30
+
29
31
"golang.org/x/crypto/ssh"
32
+
30
33
v1 "k8s.io/api/core/v1"
31
34
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
32
35
"k8s.io/apimachinery/pkg/fields"
33
36
"k8s.io/apimachinery/pkg/util/wait"
34
37
clientset "k8s.io/client-go/kubernetes"
35
- sshutil "k8s.io/kubernetes/pkg/ssh"
36
38
e2elog "k8s.io/kubernetes/test/e2e/framework/log"
37
39
testutils "k8s.io/kubernetes/test/utils"
38
40
)
@@ -54,7 +56,7 @@ const (
54
56
func GetSigner (provider string ) (ssh.Signer , error ) {
55
57
// honor a consistent SSH key across all providers
56
58
if path := os .Getenv ("KUBE_SSH_KEY_PATH" ); len (path ) > 0 {
57
- return sshutil . MakePrivateKeySignerFromFile (path )
59
+ return makePrivateKeySignerFromFile (path )
58
60
}
59
61
60
62
// Select the key itself to use. When implementing more providers here,
@@ -93,7 +95,21 @@ func GetSigner(provider string) (ssh.Signer, error) {
93
95
keyfile = filepath .Join (keydir , keyfile )
94
96
}
95
97
96
- return sshutil .MakePrivateKeySignerFromFile (keyfile )
98
+ return makePrivateKeySignerFromFile (keyfile )
99
+ }
100
+
101
+ func makePrivateKeySignerFromFile (key string ) (ssh.Signer , error ) {
102
+ buffer , err := ioutil .ReadFile (key )
103
+ if err != nil {
104
+ return nil , fmt .Errorf ("error reading SSH key %s: '%v'" , key , err )
105
+ }
106
+
107
+ signer , err := ssh .ParsePrivateKey (buffer )
108
+ if err != nil {
109
+ return nil , fmt .Errorf ("error parsing SSH key: '%v'" , err )
110
+ }
111
+
112
+ return signer , err
97
113
}
98
114
99
115
// NodeSSHHosts returns SSH-able host names for all schedulable nodes - this
@@ -169,14 +185,68 @@ func SSH(cmd, host, provider string) (Result, error) {
169
185
return result , err
170
186
}
171
187
172
- stdout , stderr , code , err := sshutil . RunSSHCommand (cmd , result .User , host , signer )
188
+ stdout , stderr , code , err := runSSHCommand (cmd , result .User , host , signer )
173
189
result .Stdout = stdout
174
190
result .Stderr = stderr
175
191
result .Code = code
176
192
177
193
return result , err
178
194
}
179
195
196
+ // runSSHCommandViaBastion returns the stdout, stderr, and exit code from running cmd on
197
+ // host as specific user, along with any SSH-level error.
198
+ func runSSHCommand (cmd , user , host string , signer ssh.Signer ) (string , string , int , error ) {
199
+ if user == "" {
200
+ user = os .Getenv ("USER" )
201
+ }
202
+ // Setup the config, dial the server, and open a session.
203
+ config := & ssh.ClientConfig {
204
+ User : user ,
205
+ Auth : []ssh.AuthMethod {ssh .PublicKeys (signer )},
206
+ HostKeyCallback : ssh .InsecureIgnoreHostKey (),
207
+ }
208
+ client , err := ssh .Dial ("tcp" , host , config )
209
+ if err != nil {
210
+ err = wait .Poll (5 * time .Second , 20 * time .Second , func () (bool , error ) {
211
+ fmt .Printf ("error dialing %s@%s: '%v', retrying\n " , user , host , err )
212
+ if client , err = ssh .Dial ("tcp" , host , config ); err != nil {
213
+ return false , err
214
+ }
215
+ return true , nil
216
+ })
217
+ }
218
+ if err != nil {
219
+ return "" , "" , 0 , fmt .Errorf ("error getting SSH client to %s@%s: '%v'" , user , host , err )
220
+ }
221
+ defer client .Close ()
222
+ session , err := client .NewSession ()
223
+ if err != nil {
224
+ return "" , "" , 0 , fmt .Errorf ("error creating session to %s@%s: '%v'" , user , host , err )
225
+ }
226
+ defer session .Close ()
227
+
228
+ // Run the command.
229
+ code := 0
230
+ var bout , berr bytes.Buffer
231
+ session .Stdout , session .Stderr = & bout , & berr
232
+ if err = session .Run (cmd ); err != nil {
233
+ // Check whether the command failed to run or didn't complete.
234
+ if exiterr , ok := err .(* ssh.ExitError ); ok {
235
+ // If we got an ExitError and the exit code is nonzero, we'll
236
+ // consider the SSH itself successful (just that the command run
237
+ // errored on the host).
238
+ if code = exiterr .ExitStatus (); code != 0 {
239
+ err = nil
240
+ }
241
+ } else {
242
+ // Some other kind of error happened (e.g. an IOError); consider the
243
+ // SSH unsuccessful.
244
+ err = fmt .Errorf ("failed running `%s` on %s@%s: '%v'" , cmd , user , host , err )
245
+ }
246
+ }
247
+ return bout .String (), berr .String (), code , err
248
+ }
249
+
180
250
// runSSHCommandViaBastion returns the stdout, stderr, and exit code from running cmd on
181
251
// host as specific user, along with any SSH-level error. It uses an SSH proxy to connect
182
252
// to bastion, then via that tunnel connects to the remote host. Similar to
0 commit comments