github.com/mponton/terratest@v0.44.0/modules/ssh/session.go (about) 1 package ssh 2 3 import ( 4 "io" 5 "net" 6 "reflect" 7 "strconv" 8 9 "github.com/mponton/terratest/modules/collections" 10 "github.com/mponton/terratest/modules/logger" 11 "github.com/mponton/terratest/modules/testing" 12 "golang.org/x/crypto/ssh" 13 ) 14 15 // SshConnectionOptions are the options for an SSH connection. 16 type SshConnectionOptions struct { 17 Username string 18 Address string 19 Port int 20 AuthMethods []ssh.AuthMethod 21 Command string 22 JumpHost *SshConnectionOptions 23 } 24 25 // ConnectionString returns the connection string for an SSH connection. 26 func (options *SshConnectionOptions) ConnectionString() string { 27 return net.JoinHostPort(options.Address, strconv.Itoa(options.Port)) 28 } 29 30 // SshSession is a container object for all resources created by an SSH session. The reason we need this is so that we can do a 31 // single defer in a top-level method that calls the Cleanup method to go through and ensure all of these resources are 32 // released and cleaned up. 33 type SshSession struct { 34 Options *SshConnectionOptions 35 Client *ssh.Client 36 Session *ssh.Session 37 JumpHost *JumpHostSession 38 Input *func(io.WriteCloser) 39 } 40 41 // Cleanup cleans up an existing SSH session. 42 func (sshSession *SshSession) Cleanup(t testing.TestingT) { 43 if sshSession == nil { 44 return 45 } 46 47 // Closing the session may result in an EOF error if it's already closed (e.g. due to hitting CTRL + D), so 48 // don't report those errors, as there is nothing actually wrong in that case. 49 Close(t, sshSession.Session, io.EOF.Error()) 50 Close(t, sshSession.Client) 51 sshSession.JumpHost.Cleanup(t) 52 } 53 54 // JumpHostSession is a session with a jump host. 55 type JumpHostSession struct { 56 JumpHostClient *ssh.Client 57 HostVirtualConnection net.Conn 58 HostConnection ssh.Conn 59 } 60 61 // Cleanup cleans the jump host session up. 62 func (jumpHost *JumpHostSession) Cleanup(t testing.TestingT) { 63 if jumpHost == nil { 64 return 65 } 66 67 // Closing a connection may result in an EOF error if it's already closed (e.g. due to hitting CTRL + D), so 68 // don't report those errors, as there is nothing actually wrong in that case. 69 Close(t, jumpHost.HostConnection, io.EOF.Error()) 70 Close(t, jumpHost.HostVirtualConnection, io.EOF.Error()) 71 Close(t, jumpHost.JumpHostClient) 72 } 73 74 // Closeable can be closed. 75 type Closeable interface { 76 Close() error 77 } 78 79 // Close closes a Closeable. 80 func Close(t testing.TestingT, closeable Closeable, ignoreErrors ...string) { 81 if interfaceIsNil(closeable) { 82 return 83 } 84 85 if err := closeable.Close(); err != nil && !collections.ListContains(ignoreErrors, err.Error()) { 86 logger.Logf(t, "Error closing %s: %s", closeable, err.Error()) 87 } 88 } 89 90 // Go is a shitty language. Checking an interface directly against nil does not work, and if you don't know the exact 91 // types the interface may be ahead of time, the only way to know if you're dealing with nil is to use reflection. 92 // http://stackoverflow.com/questions/13476349/check-for-nil-and-nil-interface-in-go 93 func interfaceIsNil(i interface{}) bool { 94 return i == nil || reflect.ValueOf(i).IsNil() 95 }