pyinfra-dev / pyinfra

pyinfra turns Python code into shell commands and runs them on your servers. Execute ad-hoc commands and write declarative operations. Target SSH servers, local machine and Docker containers. Fast and scales from one server to thousands.
https://pyinfra.com
MIT License
3.91k stars 382 forks source link

UnboundLocalError: local variable 'combined_output_lines' referenced before assignment #846

Closed jaysoffian closed 2 years ago

jaysoffian commented 2 years ago

Describe the bug

$ pyinfra inv.py fact server.MacosVersion
--> Loading config...
--> Loading inventory...

--> Connecting to hosts...
[...]
--> Gathering facts...
[...]
    [ELIDED] Command socket/SSH error: SSHException('Server connection dropped: ',)
Traceback (most recent call last):
  File "src/gevent/greenlet.py", line 906, in gevent._gevent_cgreenlet.Greenlet.run
  File ".../.direnv/python-3.10.4/lib/python3.10/site-packages/pyinfra/api/facts.py", line 134, in get_fact
    return _get_fact(
  File ".../.direnv/python-3.10.4/lib/python3.10/site-packages/pyinfra/api/facts.py", line 238, in _get_fact
    stdout, stderr = split_combined_output(combined_output_lines)
UnboundLocalError: local variable 'combined_output_lines' referenced before assignment
2022-07-04T02:19:51Z <Greenlet at 0x1056d2320: get_fact(<pyinfra.api.state.State object at 0x1054b5ed0>, Host(ELIDED), <class 'pyinfra.facts.server.MacosVersion'>, args=(), kwargs={}, apply_failed_hosts=False)> failed with UnboundLocalError

--> An unexpected internal exception occurred:

  File ".../.direnv/python-3.10.4/lib/python3.10/site-packages/pyinfra/api/facts.py", line 238, in _get_fact
    stdout, stderr = split_combined_output(combined_output_lines)
UnboundLocalError: local variable 'combined_output_lines' referenced before assignment

--> The full traceback has been written to pyinfra-debug.log
--> If this is unexpected please consider submitting a bug report on GitHub, for more information run `pyinfra --support`.

$ cat pyinfra-debug.log
  File ".../.direnv/python-3.10.4/lib/python3.10/site-packages/pyinfra_cli/main.py", line 223, in cli
    _main(*args, **kwargs)
  File ".../.direnv/python-3.10.4/lib/python3.10/site-packages/pyinfra_cli/main.py", line 515, in _main
    fact_data[fact_key] = get_facts(
  File ".../.direnv/python-3.10.4/lib/python3.10/site-packages/pyinfra/api/facts.py", line 110, in get_facts
    results[host] = greenlet.get()
  File "src/gevent/greenlet.py", line 803, in gevent._gevent_cgreenlet.Greenlet.get
  File "src/gevent/greenlet.py", line 371, in gevent._gevent_cgreenlet.Greenlet._raise_exception
  File ".../.direnv/python-3.10.4/lib/python3.10/site-packages/gevent/_compat.py", line 65, in reraise
    raise value.with_traceback(tb)
  File "src/gevent/greenlet.py", line 906, in gevent._gevent_cgreenlet.Greenlet.run
  File ".../.direnv/python-3.10.4/lib/python3.10/site-packages/pyinfra/api/facts.py", line 134, in get_fact
    return _get_fact(
  File ".../.direnv/python-3.10.4/lib/python3.10/site-packages/pyinfra/api/facts.py", line 238, in _get_fact
    stdout, stderr = split_combined_output(combined_output_lines)
UnboundLocalError: local variable 'combined_output_lines' referenced before assignment

$ pyinfra --support
--> Support information:

    If you are having issues with pyinfra or wish to make feature requests, please
    check out the GitHub issues at https://github.com/Fizzadar/pyinfra/issues .
    When adding an issue, be sure to include the following:

    System: Darwin
      Platform: macOS-12.4-x86_64-i386-64bit
      Release: 21.5.0
      Machine: x86_64
    pyinfra: v2.2
    Executable: .../.direnv/python-3.10.4/bin/pyinfra
    Python: 3.10.4 (CPython, Clang 13.1.6 (clang-1316.0.21.2.5))

To Reproduce

See above.

Expected behavior

Not to crash.

Meta

I'm not seeing anything additionally useful when running with -vv and --debug.

However, the problem only occurs when I specify a password via config.USE_SUDO_PASSWORD = "..." in config.py AND when my inventory includes at least one host that has an SSH error.

The problem goes away either by removing the troublesome host from the inventory or by not using config.USE_SUDO_PASSWORD. The inventory consists of 66-70 hosts.

jaysoffian commented 2 years ago

Tentative fix:

diff --git i/pyinfra/api/facts.py w/pyinfra/api/facts.py
index debdfb5862..2e2822fa34 100644
--- i/pyinfra/api/facts.py
+++ w/pyinfra/api/facts.py
@@ -219,6 +219,7 @@ def _get_fact(

     status = False
     stdout = []
+    combined_output_lines = []

     try:
         status, combined_output_lines = host.run_shell_command(

(I'm not clear why specifying config.USE_SUDO_PASSWORD has anything to do with the issue.)