Exemple #1
0
 def accepts_configuration_value(self):
     gw = Connection("jumpbox")
     config = Config(overrides={"gateway": gw})
     # TODO: the fact that they will be eq, but _not_ necessarily be
     # the same object, could be problematic in some cases...
     cxn = Connection("host", config=config)
     assert cxn.gateway == gw
Exemple #2
0
 def multi_hop_works_ok(self):
     cxn = self._runtime_cxn(basename="proxyjump_multi")
     innermost = cxn.gateway.gateway.gateway
     middle = cxn.gateway.gateway
     outermost = cxn.gateway
     assert innermost == Connection("jumpuser3@jumphost3:411")
     assert middle == Connection("jumpuser2@jumphost2:872")
     assert outermost == Connection("jumpuser@jumphost:373")
 def multihop_plus_wildcards_still_no_recursion(self):
     conf = self._runtime_config(
         basename="proxyjump_multi_recursive"
     )
     cxn = Connection("runtime.tld", config=conf)
     outer = cxn.gateway
     inner = cxn.gateway.gateway
     assert outer == Connection("bastion1.tld")
     assert inner == Connection("bastion2.tld")
     assert inner.gateway is None
Exemple #4
0
 def ipv6_addresses_work_ok_but_avoid_port_shorthand(self):
     for addr in ("2001:DB8:0:0:0:0:0:1", "2001:DB8::1", "::1"):
         c = Connection(addr, port=123)
         assert c.user == get_local_user()
         assert c.host == addr
         assert c.port == 123
         c2 = Connection("somebody@{}".format(addr), port=123)
         assert c2.user == "somebody"
         assert c2.host == addr
         assert c2.port == 123
 def param_comparison_uses_config(self):
     conf = Config(overrides={"user": "******"})
     c = Connection(
         user="******", host="myhost", port=123, config=conf
     )
     template = "<Connection host=myhost port=123>"
     assert repr(c) == template
Exemple #6
0
 def refuses_to_overwrite_connect_kwargs_with_others(self, client):
     for key, value, kwargs in (
             # Core connection args should definitely not get overwritten!
             # NOTE: recall that these keys are the SSHClient.connect()
             # kwarg names, NOT our own config/kwarg names!
         ("hostname", "nothost", {}),
         ("port", 17, {}),
         ("username", "zerocool", {}),
             # These might arguably still be allowed to work, but let's head
             # off confusion anyways.
         ("timeout", 100, {
             "connect_timeout": 25
         }),
     ):
         try:
             Connection("host", connect_kwargs={
                 key: value
             }, **kwargs).open()
         except ValueError as e:
             err = (
                 "Refusing to be ambiguous: connect() kwarg '{}' was given both via regular arg and via connect_kwargs!"  # noqa
             )
             assert str(e) == err.format(key)
         else:
             assert False, "Did not raise ValueError!"
Exemple #7
0
 def calls_agent_handler_close_if_enabled(self, Handler, client):
     c = Connection("host", forward_agent=True)
     c.create_session()
     c.close()
     # NOTE: this will need to change if, for w/e reason, we ever want
     # to run multiple handlers at once
     Handler.return_value.close.assert_called_once_with()
Exemple #8
0
 def merges_sources(self, client, ssh, invoke, kwarg, expected):
     config_kwargs = {}
     if ssh:
         # SSH config with 2x IdentityFile directives.
         config_kwargs["runtime_ssh_path"] = join(
             support, "ssh_config", "runtime_identity.conf")
     if invoke:
         # Use overrides config level to mimic --identity use NOTE: (the
         # fact that --identity is an override, and thus overrides eg
         # invoke config file values is part of invoke's config test
         # suite)
         config_kwargs["overrides"] = {
             "connect_kwargs": {
                 "key_filename": ["configured.key"]
             }
         }
     conf = Config_(**config_kwargs)
     connect_kwargs = {}
     if kwarg:
         # Stitch in connect_kwargs value
         connect_kwargs = {"key_filename": ["kwarg.key"]}
     # Tie in all sources that were configured & open()
     Connection("runtime", config=conf,
                connect_kwargs=connect_kwargs).open()
     # Ensure we got the expected list of keys
     kwargs = client.connect.call_args[1]
     if expected:
         assert kwargs["key_filename"] == expected
     else:
         # No key filenames -> it's not even passed in as connect_kwargs
         # is gonna be a blank dict
         assert "key_filename" not in kwargs
Exemple #9
0
 def loses_to_explicit(self):
     # Would be "my gateway", as above
     config = self._runtime_config()
     cxn = Connection("runtime",
                      config=config,
                      gateway="other gateway")
     assert cxn.gateway == "other gateway"
Exemple #10
0
 def kwarg_wins_over_config(self):
     # TODO: should this be more of a merge-down?
     c = Config(overrides={"connect_kwargs": {"origin": "config"}})
     cxn = Connection("host",
                      connect_kwargs={"origin": "kwarg"},
                      config=c)
     assert cxn.connect_kwargs == {"origin": "kwarg"}
Exemple #11
0
 def loses_to_explicit(self):
     # Would be True, as above
     config = self._runtime_config()
     cxn = Connection("runtime",
                      config=config,
                      forward_agent=False)
     assert cxn.forward_agent is False
Exemple #12
0
 def sets_missing_host_key_policy(self, Policy, client):
     # TODO: should make the policy configurable early on
     sentinel = Mock()
     Policy.return_value = sentinel
     Connection("host")
     set_policy = client.set_missing_host_key_policy
     set_policy.assert_called_once_with(sentinel)
Exemple #13
0
 def is_connected_still_False_when_connect_fails(self, client):
     client.connect.side_effect = socket.error
     cxn = Connection("host")
     try:
         cxn.open()
     except socket.error:
         pass
     assert cxn.is_connected is False
Exemple #14
0
 def connect_kwargs_protection_not_tripped_by_defaults(self, client):
     Connection("host", connect_kwargs={"timeout": 300}).open()
     client.connect.assert_called_with(
         username=get_local_user(),
         hostname="host",
         port=22,
         timeout=300,
     )
Exemple #15
0
 def passes_through_connect_kwargs(self, client):
     Connection("host", connect_kwargs={"foobar": "bizbaz"}).open()
     client.connect.assert_called_with(
         username=get_local_user(),
         hostname="host",
         port=22,
         foobar="bizbaz",
     )
Exemple #16
0
 def loses_to_explicit(self):
     # Would be a Connection equal to self._expected_gw, as
     # above
     config = self._runtime_config(basename="proxyjump")
     cxn = Connection("runtime",
                      config=config,
                      gateway="other gateway")
     assert cxn.gateway == "other gateway"
Exemple #17
0
 def short_circuits_if_already_connected(self, client):
     cxn = Connection("host")
     # First call will set self.transport to fixture's mock
     cxn.open()
     # Second call will check .is_connected which will see active==True,
     # and short circuit
     cxn.open()
     assert client.connect.call_count == 1
Exemple #18
0
 def lazily_caches_result(self, client):
     sentinel1, sentinel2 = object(), object()
     client.open_sftp.side_effect = [sentinel1, sentinel2]
     cxn = Connection("host")
     first = cxn.sftp()
     # TODO: why aren't we just asserting about calls of open_sftp???
     err = "{0!r} wasn't the sentinel object()!"
     assert first is sentinel1, err.format(first)
     second = cxn.sftp()
     assert second is sentinel1, err.format(second)
Exemple #19
0
 def uses_proxycommand_as_sock_for_Client_connect(self, moxy, client):
     "uses ProxyCommand from gateway as 'sock' arg to SSHClient.connect"
     # Setup
     main = Connection("host", gateway="net catty %h %p")
     main.open()
     # Expect ProxyCommand instantiation
     moxy.assert_called_once_with("net catty host 22")
     # Expect result of that as sock arg to connect()
     sock_arg = client.connect.call_args[1]["sock"]
     assert sock_arg is moxy.return_value
Exemple #20
0
 def uses_gateway_channel_as_sock_for_SSHClient_connect(self, Client):
     "uses Connection gateway as 'sock' arg to SSHClient.connect"
     # Setup
     mock_gw = Mock()
     mock_main = Mock()
     Client.side_effect = [mock_gw, mock_main]
     gw = Connection("otherhost")
     gw.open = Mock(wraps=gw.open)
     main = Connection("host", gateway=gw)
     main.open()
     # Expect gateway is also open()'d
     gw.open.assert_called_once_with()
     # Expect direct-tcpip channel open on 1st client
     open_channel = mock_gw.get_transport.return_value.open_channel
     kwargs = open_channel.call_args[1]
     assert kwargs["kind"] == "direct-tcpip"
     assert kwargs["dest_addr"], "host" == 22
     # Expect result of that channel open as sock arg to connect()
     sock_arg = mock_main.connect.call_args[1]["sock"]
     assert sock_arg is open_channel.return_value
Exemple #21
0
 def hostname_directive_overrides_host_attr(self):
     # TODO: not 100% convinced this is the absolute most
     # obvious API for 'translation' of given hostname to
     # ssh-configured hostname, but it feels okay for now.
     path = join(support, "ssh_config",
                 "overridden_hostname.conf")
     config = Config_(runtime_ssh_path=path)
     cxn = Connection("aliasname", config=config)
     assert cxn.host == "realname"
     assert cxn.original_host == "aliasname"
     assert cxn.port == 2222
Exemple #22
0
 def if_given_an_invoke_Config_we_upgrade_to_our_own_Config(self):
     # Scenario: user has Fabric-level data present at vanilla
     # Invoke config level, and is then creating Connection objects
     # with those vanilla invoke Configs.
     # (Could also _not_ have any Fabric-level data, but then that's
     # just a base case...)
     # TODO: adjust this if we ever switch to all our settings being
     # namespaced...
     vanilla = InvokeConfig(overrides={"forward_agent": True})
     cxn = Connection("host", config=vanilla)
     assert cxn.forward_agent is True  # not False, which is default
Exemple #23
0
 def basic_invocation(self, Remote, client):
     # Technically duplicates Invoke-level tests, but ensures things
     # still work correctly at our level.
     cxn = Connection("host")
     cxn.sudo("foo")
     cmd = "sudo -S -p '{}' foo".format(cxn.config.sudo.prompt)
     # NOTE: this is another spot where Mock.call_args is inexplicably
     # None despite call_args_list being populated. WTF. (Also,
     # Remote.return_value is two different Mocks now, despite Remote's
     # own Mock having the same ID here and in code under test. WTF!!)
     expected = [call(cxn), call().run(cmd, watchers=ANY)]
     assert Remote.mock_calls == expected
Exemple #24
0
 def sorting_works(self):
     # Hostname...
     assert Connection("a-host") < Connection("b-host")
     # User...
     assert Connection("a-host", user="******") < Connection(
         "a-host", user="******")
     # then port...
     assert Connection("a-host", port=1) < Connection("a-host", port=2)
 def gateway_Connections_get_parent_connection_configs(self):
     conf = self._runtime_config(
         basename="proxyjump",
         overrides={"some_random_option": "a-value"},
     )
     cxn = Connection("runtime", config=conf)
     # Sanity
     assert cxn.config is conf
     assert cxn.gateway == self._expected_gw
     # Real check
     assert cxn.gateway.config.some_random_option == "a-value"
     # Prove copy not reference
     # TODO: would we ever WANT a reference? can't imagine...
     assert cxn.gateway.config is not conf
Exemple #26
0
 def _forward_remote(self, kwargs, Client, select, mocket):
     # TODO: unhappy with how much this duplicates of the code under
     # test, re: sig/default vals
     # Set up parameter values/defaults
     remote_port = kwargs["remote_port"]
     remote_host = kwargs.get("remote_host", "127.0.0.1")
     local_port = kwargs.get("local_port", remote_port)
     local_host = kwargs.get("local_host", "localhost")
     # Mock/etc setup, anything that can be prepped before the forward
     # occurs (which is most things)
     tun_socket = mocket.return_value
     cxn = Connection("host")
     # Channel that will yield data when read from
     chan = Mock()
     chan.recv.return_value = "data"
     # And make select() yield it as being ready once, when called
     select.select.side_effect = _select_result(chan)
     with cxn.forward_remote(**kwargs):
         # At this point Connection.open() has run and generated a
         # Transport mock for us (because SSHClient is mocked). Let's
         # first make sure we asked it for the port forward...
         # NOTE: this feels like it's too limited/tautological a test,
         # until you realize that it's functionally impossible to mock
         # out everything required for Paramiko's inner guts to run
         # _parse_channel_open() and suchlike :(
         call = cxn.transport.request_port_forward.call_args_list[0]
         assert call[1]["address"] == remote_host
         assert call[1]["port"] == remote_port
         # Pretend the Transport called our callback with mock Channel
         call[1]["handler"](chan, tuple(), tuple())
         # Then have to sleep a bit to make sure we give the tunnel
         # created by that callback to spin up; otherwise ~5% of the
         # time we exit the contextmanager so fast, the tunnel's "you're
         # done!" flag is set before it even gets a chance to select()
         # once.
         time.sleep(0.01)
         # And make sure we hooked up to the local socket OK
         tup = (local_host, local_port)
         tun_socket.connect.assert_called_once_with(tup)
     # Expect that our socket got written to by the tunnel (due to the
     # above-setup select() and channel mocking). Need to do this after
     # tunnel shutdown or we risk thread ordering issues.
     tun_socket.sendall.assert_called_once_with("data")
     # Ensure we closed down the mock socket
     mocket.return_value.close.assert_called_once_with()
     # And that the transport canceled the port forward on the remote
     # end.
     assert cxn.transport.cancel_port_forward.call_count == 1
Exemple #27
0
 def comparison_uses_host_user_and_port(self):
     # Just host
     assert Connection("host") == Connection("host")
     # Host + user
     c1 = Connection("host", user="******")
     c2 = Connection("host", user="******")
     assert c1 == c2
     # Host + user + port
     c1 = Connection("host", user="******", port=123)
     c2 = Connection("host", user="******", port=123)
     assert c1 == c2
Exemple #28
0
 def calls_Remote_run_with_command_and_kwargs_and_returns_its_result(
         self, Remote, client):
     remote = Remote.return_value
     sentinel = object()
     remote.run.return_value = sentinel
     c = Connection("host")
     r1 = c.run("command")
     r2 = c.run("command", warn=True, hide="stderr")
     # NOTE: somehow, .call_args & the methods built on it (like
     # .assert_called_with()) stopped working, apparently triggered by
     # our code...somehow...after commit (roughly) 80906c7.
     # And yet, .call_args_list and its brethren work fine. Wha?
     Remote.assert_any_call(c)
     remote.run.assert_has_calls(
         [call("command"),
          call("command", warn=True, hide="stderr")])
     for r in (r1, r2):
         assert r is sentinel
Exemple #29
0
 def _forward_local(self, kwargs, Client, mocket, select):
     # Tease out bits of kwargs for use in the mocking/expecting.
     # But leave it alone for raw passthru to the API call itself.
     # TODO: unhappy with how much this apes the real code & its sig...
     local_port = kwargs["local_port"]
     remote_port = kwargs.get("remote_port", local_port)
     local_host = kwargs.get("local_host", "localhost")
     remote_host = kwargs.get("remote_host", "localhost")
     # These aren't part of the real sig, but this is easier than trying
     # to reconcile the mock decorators + optional-value kwargs. meh.
     tunnel_exception = kwargs.pop("tunnel_exception", None)
     listener_exception = kwargs.pop("listener_exception", False)
     # Mock setup
     client = Client.return_value
     listener_sock = Mock(name="listener_sock")
     if listener_exception:
         listener_sock.bind.side_effect = listener_exception
     data = b("Some data")
     tunnel_sock = Mock(name="tunnel_sock", recv=lambda n: data)
     local_addr = Mock()
     transport = client.get_transport.return_value
     channel = transport.open_channel.return_value
     # socket.socket is only called once directly
     mocket.return_value = listener_sock
     # The 2nd socket is obtained via an accept() (which should only
     # fire once & raise EAGAIN after)
     listener_sock.accept.side_effect = chain(
         [(tunnel_sock, local_addr)],
         repeat(socket.error(errno.EAGAIN, "nothing yet")),
     )
     obj = tunnel_sock if tunnel_exception is None else tunnel_exception
     select.select.side_effect = _select_result(obj)
     with Connection("host").forward_local(**kwargs):
         # Make sure we give listener thread enough time to boot up :(
         # Otherwise we might assert before it does things. (NOTE:
         # doesn't need to be much, even at 0.01s, 0/100 trials failed
         # (vs 45/100 with no sleep)
         time.sleep(0.015)
         assert client.connect.call_args[1]["hostname"] == "host"
         listener_sock.setsockopt.assert_called_once_with(
             socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
         listener_sock.setblocking.assert_called_once_with(0)
         listener_sock.bind.assert_called_once_with(
             (local_host, local_port))
         if not listener_exception:
             listener_sock.listen.assert_called_once_with(1)
             transport.open_channel.assert_called_once_with(
                 "direct-tcpip", (remote_host, remote_port), local_addr)
         # Local write to tunnel_sock is implied by its mocked-out
         # recv() call above...
         # NOTE: don't assert if explodey; we want to mimic "the only
         # error that occurred was within the thread" behavior being
         # tested by thread-exception-handling tests
         if not (tunnel_exception or listener_exception):
             channel.sendall.assert_called_once_with(data)
     # Shutdown, with another sleep because threads.
     time.sleep(0.015)
     if not listener_exception:
         tunnel_sock.close.assert_called_once_with()
         channel.close.assert_called_once_with()
         listener_sock.close.assert_called_once_with()
Exemple #30
0
 def calls_Transfer_put(self, Transfer):
     "calls Transfer.put()"
     c = Connection("host")
     c.put("meh")
     Transfer.assert_called_with(c)
     Transfer.return_value.put.assert_called_with("meh")