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  }