LCVcode / jockey

MIT License
2 stars 3 forks source link

Feature: enable configured SSH destinations in remote Juju options #96

Open LCVcode opened 1 month ago

LCVcode commented 1 month ago

Problem

When running remote Jockey commands with juju-jockey -H <hostname> ..., that hostname does not consider configured SSH hosts.

Proposed Feature

juju-jockey -H <hostname> should consider SSH configs to determine the host's IP.

Additional Context

Below is a quick example, which has been sanitized. In this example, I am only using the -H flag, which should not be an issue, so far as I understand. This problem seems to occur well before any other flags are even considered.

Here is a snippet of my SSH config:

Host infra.customer.cloud infra1.customer.cloud...
   Hostname 10.111.134.1
   ProxyJump vpn.customer.cloud

When I run the below command, it fails to find this IP address. Again, this is sanitized, but I ran the real thing on my end:

juju-jockey -H infra.customer.clouds units                     
╭─────────────────────────────── Traceback (most recent call last) ────────────────────────────────╮
│ /home/lcv/.cache/pypoetry/virtualenvs/jockey-mWLLoW7i-py3.10/bin/juju-jockey:6 in <module>       │
│                                                                                                  │
│   3 from jockey.__main__ import main                                                             │
│   4                                                                                              │
│   5 if __name__ == '__main__':                                                                   │
│ ❱ 6 │   sys.exit(main())                                                                         │
│   7                                                                                              │
│                                                                                                  │
│ ╭──────────── locals ─────────────╮                                                              │
│ │ sys = <module 'sys' (built-in)> │                                                              │
│ ╰─────────────────────────────────╯                                                              │
│                                                                                                  │
│ /home/lcv/Work/repos/jockey/src/jockey/__main__.py:129 in main                                   │
│                                                                                                  │
│   126 │   │   cloud_juju = args.juju if "juju" in args else None                                 │
│   127 │   │                                                                                      │
│   128 │   │   # connect to the cloud and get the Juju status                                     │
│ ❱ 129 │   │   cloud = Cloud(host=cloud_host, user=cloud_user, doas=cloud_doas, timeout=cloud_t   │
│   130 │   │                                                                                      │
│   131 │   │   try:                                                                               │
│   132 │   │   │   status = cloud.juju_status                                                     │
│                                                                                                  │
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
│ │           args = Namespace(verbose=0, object='units', sudo=None, refresh=False,              │ │
│ │                  host='infra.customer.cloud')                                                │ │
│ │           argv = ['-H', 'infra.infra.customer.cloud', 'units']                               │ │
│ │          cache = FileCache(/home/lcv/.cache/jockey, max_age=300)                             │ │
│ │      cache_age = None                                                                        │ │
│ │      cache_dir = None                                                                        │ │
│ │  cache_refresh = False                                                                       │ │
│ │     cloud_doas = None                                                                        │ │
│ │     cloud_host = 'infra.customer.cloud'                                                      │ │
│ │     cloud_juju = None                                                                        │ │
│ │  cloud_timeout = None                                                                        │ │
│ │     cloud_user = None                                                                        │ │
│ │            obj = Object.UNIT                                                                 │ │
│ │ obj_expression = 'units'                                                                     │ │
│ │      obj_field = None                                                                        │ │
│ │      verbosity = 0                                                                           │ │
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
│                                                                                                  │
│ /home/lcv/Work/repos/jockey/src/jockey/cloud.py:130 in __init__                                  │
│                                                                                                  │
│   127 │   │   self.command_timeout = timeout                                                     │
│   128 │   │   self.original_host = host                                                          │
│   129 │   │                                                                                      │
│ ❱ 130 │   │   if Cloud.is_localhost_address(host):                                               │
│   131 │   │   │   config = CloudInvokeConfig()                                                   │
│   132 │   │   │   config.update(timeouts={"command": timeout})                                   │
│   133 │   │   │   Context.__init__(self, config)                                                 │
│                                                                                                  │
│ ╭───────────────────────── locals ──────────────────────────╮                                    │
│ │    args = ()                                              │                                    │
│ │   cache = None                                            │                                    │
│ │    doas = None                                            │                                    │
│ │    host = 'infra.infra.customer.cloud'                    │                                    │
│ │    juju = None                                            │                                    │
│ │  kwargs = {'user': None}                                  │                                    │
│ │    self = <repr-error 'maximum recursion depth exceeded'> │                                    │
│ │ timeout = None                                            │                                    │
│ ╰───────────────────────────────────────────────────────────╯                                    │
│                                                                                                  │
│ /home/lcv/Work/repos/jockey/src/jockey/cloud.py:206 in is_localhost_address                      │
│                                                                                                  │
│   203 │   │   return (                                                                           │
│   204 │   │   │   not address                                                                    │
│   205 │   │   │   or address in ("local", "localhost", "localhost.localdomain", "127.0.0.1", "   │
│ ❱ 206 │   │   │   or ip_address(address).is_loopback                                             │
│   207 │   │   )                                                                                  │
│   208 │                                                                                          │
│   209 │   def open(self) -> None:                                                                │
│                                                                                                  │
│ ╭──────────────────── locals ─────────────────────╮                                              │
│ │ address = 'infra.customer.cloud'                │                                              │
│ ╰─────────────────────────────────────────────────╯                                              │
│                                                                                                  │
│ /usr/lib/python3.10/ipaddress.py:54 in ip_address                                                │
│                                                                                                  │
│     51 │   except (AddressValueError, NetmaskValueError):                                        │
│     52 │   │   pass                                                                              │
│     53 │                                                                                         │
│ ❱   54 │   raise ValueError(f'{address!r} does not appear to be an IPv4 or IPv6 address')        │
│     55                                                                                           │
│     56                                                                                           │
│     57 def ip_network(address, strict=True):                                                     │
│                                                                                                  │
│ ╭──────────────────── locals ─────────────────────╮                                              │
│ │ address = 'infra.customer.cloud'                │                                              │
│ ╰─────────────────────────────────────────────────╯                                              │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
ValueError: 'infra.customer.cloud' does not appear to be an IPv4 or IPv6 address

Surprisingly, we're hitting a maximum recursion depth exception here.

johnlettman commented 1 month ago

This looks like a nasty bug in the localhost lookup. I'll take a look at that.