github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/network/ssh/reachable_test.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package ssh_test
     5  
     6  import (
     7  	"net"
     8  	"time"
     9  
    10  	_ "github.com/juju/errors"
    11  	jc "github.com/juju/testing/checkers"
    12  	gc "gopkg.in/check.v1"
    13  
    14  	"github.com/juju/juju/network"
    15  	"github.com/juju/juju/network/ssh"
    16  	sshtesting "github.com/juju/juju/network/ssh/testing"
    17  	coretesting "github.com/juju/juju/testing"
    18  )
    19  
    20  type SSHReachableHostPortSuite struct {
    21  	coretesting.BaseSuite
    22  }
    23  
    24  var _ = gc.Suite(&SSHReachableHostPortSuite{})
    25  
    26  var searchTimeout = 300 * time.Millisecond
    27  var dialTimeout = 100 * time.Millisecond
    28  
    29  func makeChecker() ssh.ReachableChecker {
    30  	dialer := &net.Dialer{Timeout: dialTimeout}
    31  	checker := ssh.NewReachableChecker(dialer, searchTimeout)
    32  	return checker
    33  }
    34  
    35  func (s *SSHReachableHostPortSuite) TestAllUnreachable(c *gc.C) {
    36  	checker := makeChecker()
    37  	unreachableHPs := closedTCPHostPorts(c, 10)
    38  	best, err := checker.FindHost(unreachableHPs, nil)
    39  	c.Check(err, gc.ErrorMatches, "cannot connect to any address: .*")
    40  	c.Check(best, gc.Equals, network.HostPort{})
    41  }
    42  
    43  func (s *SSHReachableHostPortSuite) TestReachableInvalidPublicKey(c *gc.C) {
    44  	hostPorts := []network.HostPort{
    45  		// We use Key2, but are looking for Pub1
    46  		testSSHServer(c, s, sshtesting.SSHKey2),
    47  	}
    48  	checker := makeChecker()
    49  	best, err := checker.FindHost(hostPorts, []string{sshtesting.SSHPub1})
    50  	c.Check(err, gc.ErrorMatches, "cannot connect to any address: .*")
    51  	c.Check(best, gc.Equals, network.HostPort{})
    52  }
    53  
    54  func (s *SSHReachableHostPortSuite) TestReachableValidPublicKey(c *gc.C) {
    55  	hostPorts := []network.HostPort{
    56  		testSSHServer(c, s, sshtesting.SSHKey1),
    57  	}
    58  	checker := makeChecker()
    59  	best, err := checker.FindHost(hostPorts, []string{sshtesting.SSHPub1})
    60  	c.Check(err, jc.ErrorIsNil)
    61  	c.Check(best, gc.Equals, hostPorts[0])
    62  }
    63  
    64  func (s *SSHReachableHostPortSuite) TestReachableMixedPublicKeys(c *gc.C) {
    65  	// One is just closed, one is TCP only, one is SSH but the wrong key, one
    66  	// is SSH with the right key
    67  	fakeHostPort := closedTCPHostPorts(c, 1)[0]
    68  	hostPorts := []network.HostPort{
    69  		fakeHostPort,
    70  		testTCPServer(c, s),
    71  		testSSHServer(c, s, sshtesting.SSHKey2),
    72  		testSSHServer(c, s, sshtesting.SSHKey1),
    73  	}
    74  	checker := makeChecker()
    75  	best, err := checker.FindHost(hostPorts, []string{sshtesting.SSHPub1})
    76  	c.Check(err, jc.ErrorIsNil)
    77  	c.Check(best, jc.DeepEquals, hostPorts[3])
    78  }
    79  
    80  func (s *SSHReachableHostPortSuite) TestReachableNoPublicKeysPassed(c *gc.C) {
    81  	fakeHostPort := closedTCPHostPorts(c, 1)[0]
    82  	hostPorts := []network.HostPort{
    83  		fakeHostPort,
    84  		testTCPServer(c, s),
    85  		testSSHServer(c, s, sshtesting.SSHKey1),
    86  	}
    87  	checker := makeChecker()
    88  	// Without a list of public keys, we should just check that the remote host is an SSH server
    89  	best, err := checker.FindHost(hostPorts, nil)
    90  	c.Check(err, jc.ErrorIsNil)
    91  	c.Check(best, jc.DeepEquals, hostPorts[2]) // the only real ssh server
    92  }
    93  
    94  func (s *SSHReachableHostPortSuite) TestReachableNoPublicKeysAvailable(c *gc.C) {
    95  	fakeHostPort := closedTCPHostPorts(c, 1)[0]
    96  	hostPorts := []network.HostPort{
    97  		fakeHostPort,
    98  		testTCPServer(c, s),
    99  	}
   100  	checker := makeChecker()
   101  	best, err := checker.FindHost(hostPorts, []string{sshtesting.SSHPub1})
   102  	c.Check(err, gc.ErrorMatches, "cannot connect to any address: .*")
   103  	c.Check(best, gc.Equals, network.HostPort{})
   104  }
   105  
   106  func (s *SSHReachableHostPortSuite) TestMultiplePublicKeys(c *gc.C) {
   107  	hostPorts := []network.HostPort{
   108  		testSSHServer(c, s, sshtesting.SSHKey1, sshtesting.SSHKey2),
   109  	}
   110  	checker := makeChecker()
   111  	best, err := checker.FindHost(hostPorts, []string{sshtesting.SSHPub1, sshtesting.SSHPub2})
   112  	c.Check(err, jc.ErrorIsNil)
   113  	c.Check(best, gc.Equals, hostPorts[0])
   114  }
   115  
   116  // closedTCPHostPorts opens and then immediately closes a bunch of ports and
   117  // saves their port numbers so we're unlikely to find a real listener at that
   118  // address.
   119  func closedTCPHostPorts(c *gc.C, count int) []network.HostPort {
   120  	ports := make([]network.HostPort, count)
   121  	for i := 0; i < count; i++ {
   122  		listener, err := net.Listen("tcp", "127.0.0.1:0")
   123  		c.Assert(err, jc.ErrorIsNil)
   124  		defer listener.Close()
   125  		listenAddress := listener.Addr().String()
   126  		port, err := network.ParseHostPort(listenAddress)
   127  		c.Assert(err, jc.ErrorIsNil)
   128  		ports[i] = *port
   129  	}
   130  	// By the time we return all the listeners are closed
   131  	return ports
   132  }
   133  
   134  type Cleaner interface {
   135  	AddCleanup(cleanup func(*gc.C))
   136  }
   137  
   138  // testTCPServer only listens on the socket, but doesn't speak SSH
   139  func testTCPServer(c *gc.C, cleaner Cleaner) network.HostPort {
   140  	listenAddress, shutdown := sshtesting.CreateTCPServer(c, func(tcpConn net.Conn) {
   141  		// We accept a connection, but then immediately close.
   142  		tcpConn.Close()
   143  	})
   144  	hostPort, err := network.ParseHostPort(listenAddress)
   145  	c.Assert(err, jc.ErrorIsNil)
   146  	cleaner.AddCleanup(func(*gc.C) { close(shutdown) })
   147  
   148  	return *hostPort
   149  }
   150  
   151  // testSSHServer will listen on the socket and respond with the appropriate
   152  // public key information and then die.
   153  func testSSHServer(c *gc.C, cleaner Cleaner, privateKeys ...string) network.HostPort {
   154  	address, shutdown := sshtesting.CreateSSHServer(c, privateKeys...)
   155  	hostPort, err := network.ParseHostPort(address)
   156  	c.Assert(err, jc.ErrorIsNil)
   157  	cleaner.AddCleanup(func(*gc.C) { close(shutdown) })
   158  
   159  	return *hostPort
   160  }