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 }