lager-cli 0.31.0__tar.gz → 0.31.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {lager_cli-0.31.0/lager_cli.egg-info → lager_cli-0.31.2}/PKG-INFO +1 -1
- {lager_cli-0.31.0 → lager_cli-0.31.2}/__init__.py +1 -1
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/box/_ssh.py +31 -16
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/utility/update.py +100 -37
- {lager_cli-0.31.0 → lager_cli-0.31.2/lager_cli.egg-info}/PKG-INFO +1 -1
- {lager_cli-0.31.0 → lager_cli-0.31.2}/LICENSE +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/MANIFEST.in +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/README.md +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/__main__.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/address_utils.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/battery/__init__.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/battery/battery_tui.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/battery/websocket_client.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/box_storage.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/__init__.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/box/__init__.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/box/_host_ops.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/box/_mount_prep.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/box/_pip_validation.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/box/_shim_verbs.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/box/authorize.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/box/box_group.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/box/boxes.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/box/config.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/box/diagnose.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/box/dut.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/box/hello.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/box/instruments.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/box/labjack_pins.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/box/lock.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/box/net_tui.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/box/nets.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/box/ssh.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/communication/__init__.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/communication/ble.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/communication/blufi.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/communication/i2c.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/communication/router.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/communication/spi.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/communication/uart.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/communication/usb.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/communication/websocket_client.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/communication/wifi.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/development/__init__.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/development/arm.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/development/debug/__init__.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/development/debug/commands.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/development/debug/gdb.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/development/debug/net_cache.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/development/debug/service_client.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/development/debug/service_helper.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/development/debug/tunnel.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/development/devenv.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/development/python.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/measurement/__init__.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/measurement/adc.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/measurement/dac.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/measurement/energy.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/measurement/gpi.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/measurement/gpo.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/measurement/logic.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/measurement/scope.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/measurement/thermocouple.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/measurement/watt.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/power/__init__.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/power/battery.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/power/eload.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/power/solar.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/power/supply.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/utility/__init__.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/utility/binaries.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/utility/defaults.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/utility/exec_.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/utility/install.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/utility/install_wheel.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/utility/logs.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/utility/uninstall.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/commands/utility/webcam.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/config.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/context/__init__.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/context/ci_detection.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/context/constants.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/context/core.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/context/error_handlers.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/context/session.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/core/__init__.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/core/group_usage.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/core/matchers.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/core/net_group.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/core/net_helpers.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/core/net_storage.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/core/param_types.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/core/ssh_utils.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/core/utils.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/core/version_skew.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/core/ws_diagnose.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/deployment/__init__.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/deployment/scripts/__init__.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/deployment/scripts/convert_to_sparse_checkout.sh +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/deployment/scripts/setup_and_deploy_box.sh +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/deployment/scripts/setup_ssh_key.sh +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/deployment/security/__init__.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/deployment/security/secure_box_firewall.sh +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/elftools/__init__.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/elftools/common/__init__.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/elftools/common/construct_utils.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/elftools/common/exceptions.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/elftools/common/py3compat.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/elftools/common/utils.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/elftools/construct/__init__.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/elftools/construct/adapters.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/elftools/construct/core.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/elftools/construct/debug.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/elftools/construct/macros.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/elftools/dwarf/__init__.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/elftools/dwarf/abbrevtable.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/elftools/dwarf/aranges.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/elftools/dwarf/callframe.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/elftools/dwarf/compileunit.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/elftools/dwarf/constants.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/elftools/dwarf/descriptions.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/elftools/dwarf/die.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/elftools/dwarf/dwarf_expr.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/elftools/dwarf/dwarfinfo.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/elftools/dwarf/enums.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/elftools/dwarf/lineprogram.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/elftools/dwarf/locationlists.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/elftools/dwarf/namelut.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/elftools/dwarf/ranges.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/elftools/dwarf/structs.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/elftools/ehabi/__init__.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/elftools/ehabi/constants.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/elftools/ehabi/decoder.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/elftools/ehabi/ehabiinfo.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/elftools/ehabi/structs.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/elftools/elf/__init__.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/elftools/elf/constants.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/elftools/elf/descriptions.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/elftools/elf/dynamic.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/elftools/elf/elffile.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/elftools/elf/enums.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/elftools/elf/gnuversions.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/elftools/elf/hash.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/elftools/elf/notes.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/elftools/elf/relocation.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/elftools/elf/sections.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/elftools/elf/segments.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/elftools/elf/structs.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/errors.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/exceptions.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/impl/__init__.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/impl/box_config.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/impl/communication/__init__.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/impl/communication/ble.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/impl/communication/blufi.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/impl/communication/i2c.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/impl/communication/router.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/impl/communication/spi.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/impl/communication/uart.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/impl/communication/wifi.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/impl/custom_devices.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/impl/device/__init__.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/impl/device/arm.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/impl/device/hello.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/impl/device/usb.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/impl/device/webcam.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/impl/measurement/__init__.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/impl/measurement/adc.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/impl/measurement/dac.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/impl/measurement/energy.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/impl/measurement/gpio.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/impl/measurement/scope.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/impl/measurement/scope_stream.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/impl/measurement/thermocouple.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/impl/measurement/watt.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/impl/net.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/impl/power/__init__.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/impl/power/battery.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/impl/power/eload.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/impl/power/enable_disable.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/impl/power/solar.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/impl/power/supply.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/impl/query_instruments.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/lager_cli.egg-info/SOURCES.txt +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/lager_cli.egg-info/dependency_links.txt +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/lager_cli.egg-info/entry_points.txt +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/lager_cli.egg-info/requires.txt +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/lager_cli.egg-info/top_level.txt +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/main.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/pyproject.toml +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/safe_unpickle.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/setup.cfg +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/setup.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/simple_hdlc.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/sort_utils.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/status.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/supply/__init__.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/supply/supply_tui.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/supply/websocket_client.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/terminal/__init__.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/terminal/core/__init__.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/terminal/core/executor.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/terminal/main.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/terminal/ui/__init__.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/terminal/ui/completer.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/terminal/ui/display.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/terminal/ui/logo.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/terminal/ui/repl.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/terminal/ui/themes.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/tests/test_box_lager_imports.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/tests/test_box_storage.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/tests/test_io_imports.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/update_check.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/vendor/PyCRC/CRC16.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/vendor/PyCRC/CRC16DNP.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/vendor/PyCRC/CRC16Kermit.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/vendor/PyCRC/CRC16SICK.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/vendor/PyCRC/CRC32.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/vendor/PyCRC/CRCCCITT.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/vendor/PyCRC/__init__.py +0 -0
- {lager_cli-0.31.0 → lager_cli-0.31.2}/vendor/__init__.py +0 -0
|
@@ -14,6 +14,7 @@ fixes don't have to be made three times.
|
|
|
14
14
|
from __future__ import annotations
|
|
15
15
|
|
|
16
16
|
import os
|
|
17
|
+
import shutil
|
|
17
18
|
import subprocess
|
|
18
19
|
from typing import Callable, Optional, Tuple
|
|
19
20
|
|
|
@@ -52,11 +53,20 @@ def ensure_lager_box_keypair(key_path: str = _LAGER_BOX_KEY) -> bool:
|
|
|
52
53
|
if os.path.exists(key_path):
|
|
53
54
|
return False
|
|
54
55
|
os.makedirs(os.path.dirname(key_path), mode=0o700, exist_ok=True)
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
56
|
+
keygen_exe = shutil.which("ssh-keygen")
|
|
57
|
+
if keygen_exe is None:
|
|
58
|
+
raise LagerError(
|
|
59
|
+
"ssh-keygen not found — install the OpenSSH client.",
|
|
60
|
+
fixes=["Windows: Settings → System → Optional Features → 'OpenSSH Client'"],
|
|
61
|
+
)
|
|
62
|
+
try:
|
|
63
|
+
proc = subprocess.run(
|
|
64
|
+
[keygen_exe, "-t", "ed25519", "-f", key_path, "-N", "", "-C", "lager-box-access"],
|
|
65
|
+
capture_output=True,
|
|
66
|
+
text=True,
|
|
67
|
+
)
|
|
68
|
+
except OSError as exc:
|
|
69
|
+
raise LagerError("ssh-keygen failed to launch.", cause=str(exc), raw=exc) from exc
|
|
60
70
|
if proc.returncode != 0:
|
|
61
71
|
raise LagerError(
|
|
62
72
|
"Could not generate the SSH key.",
|
|
@@ -85,17 +95,22 @@ def key_auth_works(
|
|
|
85
95
|
"key not authorized", which makes `lager update` spuriously re-prompt
|
|
86
96
|
"SSH key not configured" on every run for a box that is in fact set up.
|
|
87
97
|
"""
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
98
|
+
if shutil.which("ssh") is None:
|
|
99
|
+
return False
|
|
100
|
+
try:
|
|
101
|
+
proc = subprocess.run(
|
|
102
|
+
[
|
|
103
|
+
"ssh", "-i", key_path,
|
|
104
|
+
"-o", "BatchMode=yes",
|
|
105
|
+
"-o", "StrictHostKeyChecking=accept-new",
|
|
106
|
+
"-o", f"ConnectTimeout={connect_timeout}",
|
|
107
|
+
dest, "true",
|
|
108
|
+
],
|
|
109
|
+
capture_output=True,
|
|
110
|
+
text=True,
|
|
111
|
+
)
|
|
112
|
+
except OSError:
|
|
113
|
+
return False
|
|
99
114
|
return proc.returncode == 0
|
|
100
115
|
|
|
101
116
|
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
import click
|
|
12
12
|
import requests
|
|
13
13
|
import re
|
|
14
|
+
import shlex
|
|
14
15
|
import shutil
|
|
15
16
|
import subprocess
|
|
16
17
|
import threading
|
|
@@ -536,22 +537,48 @@ def _update_logic(ctx, *, box, yes, version, verbose, check, force=False):
|
|
|
536
537
|
except LagerError:
|
|
537
538
|
log_error('Error: Failed to create SSH key')
|
|
538
539
|
return False
|
|
540
|
+
except FileNotFoundError:
|
|
541
|
+
click.secho('ssh-keygen not found. Install OpenSSH client tools.', fg='red')
|
|
542
|
+
click.echo(' Windows: Settings → System → Optional Features → "OpenSSH Client"')
|
|
543
|
+
return False
|
|
539
544
|
|
|
540
|
-
# Copy key to box
|
|
545
|
+
# Copy key to box using ssh directly — ssh-copy-id is a POSIX shell
|
|
546
|
+
# script not available on Windows, even when Git for Windows is installed.
|
|
541
547
|
click.echo()
|
|
542
548
|
click.echo('Copying SSH key to box (enter password when prompted):')
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
549
|
+
try:
|
|
550
|
+
pub_key = open(f'{key_file}.pub').read().strip()
|
|
551
|
+
except OSError as e:
|
|
552
|
+
click.secho(f'Could not read public key {key_file}.pub: {e}', fg='red')
|
|
553
|
+
return False
|
|
554
|
+
|
|
555
|
+
remote_cmd = (
|
|
556
|
+
f'mkdir -p ~/.ssh && echo {shlex.quote(pub_key)} >> ~/.ssh/authorized_keys'
|
|
557
|
+
' && chmod 600 ~/.ssh/authorized_keys'
|
|
552
558
|
)
|
|
559
|
+
try:
|
|
560
|
+
copy_result = subprocess.run(
|
|
561
|
+
[
|
|
562
|
+
'ssh',
|
|
563
|
+
'-o', 'StrictHostKeyChecking=accept-new',
|
|
564
|
+
'-o', 'ConnectTimeout=30',
|
|
565
|
+
ssh_host,
|
|
566
|
+
remote_cmd,
|
|
567
|
+
],
|
|
568
|
+
timeout=300, # 5 minutes — allow time for user to enter password
|
|
569
|
+
)
|
|
570
|
+
except (FileNotFoundError, OSError):
|
|
571
|
+
click.echo()
|
|
572
|
+
click.secho('ssh not found on this system.', fg='red')
|
|
573
|
+
click.echo('Install the OpenSSH client:')
|
|
574
|
+
click.echo(' Windows: Settings → System → Optional Features → "OpenSSH Client"')
|
|
575
|
+
return False
|
|
553
576
|
|
|
554
|
-
|
|
577
|
+
try:
|
|
578
|
+
_key_works = copy_result.returncode == 0 and key_auth_works(ssh_host)
|
|
579
|
+
except OSError:
|
|
580
|
+
_key_works = False
|
|
581
|
+
if _key_works:
|
|
555
582
|
click.echo()
|
|
556
583
|
click.secho('SSH key installed successfully!', fg='green')
|
|
557
584
|
click.echo('Future connections will not require a password.')
|
|
@@ -578,6 +605,10 @@ def _update_logic(ctx, *, box, yes, version, verbose, check, force=False):
|
|
|
578
605
|
click.secho('SSH key not configured for this box', fg='yellow')
|
|
579
606
|
click.echo()
|
|
580
607
|
|
|
608
|
+
if check:
|
|
609
|
+
click.secho("Run 'lager update --box ...' (without --check) to set up the SSH key first.", fg='yellow')
|
|
610
|
+
ctx.exit(2)
|
|
611
|
+
|
|
581
612
|
if yes or click.confirm('Set up SSH key for this box? (requires password once, then never again)'):
|
|
582
613
|
if setup_ssh_key():
|
|
583
614
|
# Key setup successful, reinitialize progress bar
|
|
@@ -611,8 +642,14 @@ def _update_logic(ctx, *, box, yes, version, verbose, check, force=False):
|
|
|
611
642
|
log_error(f'Error: Connection to {ssh_host} timed out')
|
|
612
643
|
ctx.exit(1)
|
|
613
644
|
except Exception as e:
|
|
614
|
-
|
|
615
|
-
|
|
645
|
+
import traceback as _tb
|
|
646
|
+
tb_str = _tb.format_exc()
|
|
647
|
+
try:
|
|
648
|
+
if progress:
|
|
649
|
+
progress.finish(success=False)
|
|
650
|
+
except Exception:
|
|
651
|
+
pass
|
|
652
|
+
click.echo(tb_str)
|
|
616
653
|
log_error(f'Error: {str(e)}')
|
|
617
654
|
ctx.exit(1)
|
|
618
655
|
|
|
@@ -630,15 +667,26 @@ def _update_logic(ctx, *, box, yes, version, verbose, check, force=False):
|
|
|
630
667
|
# (e.g. a devenv where it's a single-file bind mount and ssh's atomic replace
|
|
631
668
|
# errors with "Invalid cross-device link"): accept-new lets the connection
|
|
632
669
|
# proceed despite the write failure.
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
670
|
+
if sys.platform == 'win32':
|
|
671
|
+
# ControlPath is a Unix-domain socket — Windows OpenSSH treats any ControlPath
|
|
672
|
+
# value as a literal filename and then fails at getsockname() with
|
|
673
|
+
# "Not a socket". ServerAliveInterval also triggers getsockname on some
|
|
674
|
+
# Windows builds when stdout/stderr are piped. Use the minimal safe set.
|
|
675
|
+
_ssh_mux_opts = [
|
|
676
|
+
'-o', 'ControlMaster=no',
|
|
677
|
+
'-o', 'StrictHostKeyChecking=accept-new',
|
|
678
|
+
'-o', 'ConnectTimeout=10',
|
|
679
|
+
]
|
|
680
|
+
else:
|
|
681
|
+
_ssh_mux_opts = [
|
|
682
|
+
'-o', 'StrictHostKeyChecking=accept-new',
|
|
683
|
+
'-o', 'ConnectTimeout=30',
|
|
684
|
+
'-o', 'ControlMaster=auto',
|
|
685
|
+
'-o', f'ControlPath={_ssh_control_path}',
|
|
686
|
+
'-o', 'ControlPersist=10m',
|
|
687
|
+
'-o', 'ServerAliveInterval=30',
|
|
688
|
+
'-o', 'ServerAliveCountMax=3',
|
|
689
|
+
]
|
|
642
690
|
|
|
643
691
|
# Tear down any leftover control master for this host before the first
|
|
644
692
|
# probe. A socket orphaned by an earlier interrupted/failed run (or one
|
|
@@ -647,18 +695,19 @@ def _update_logic(ctx, *, box, yes, version, verbose, check, force=False):
|
|
|
647
695
|
# confusing "Permission denied (publickey,password)" on the probe even
|
|
648
696
|
# though the key authenticates fine on a fresh connection. Ask the master
|
|
649
697
|
# to exit, then unlink the socket if it lingers; both are best-effort.
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
os.
|
|
660
|
-
|
|
661
|
-
|
|
698
|
+
if sys.platform != 'win32':
|
|
699
|
+
try:
|
|
700
|
+
subprocess.run(
|
|
701
|
+
['ssh', '-o', f'ControlPath={_ssh_control_path}', '-O', 'exit', ssh_host],
|
|
702
|
+
capture_output=True, timeout=10,
|
|
703
|
+
)
|
|
704
|
+
except (subprocess.SubprocessError, OSError):
|
|
705
|
+
pass
|
|
706
|
+
try:
|
|
707
|
+
if os.path.exists(_ssh_control_path):
|
|
708
|
+
os.remove(_ssh_control_path)
|
|
709
|
+
except OSError:
|
|
710
|
+
pass
|
|
662
711
|
|
|
663
712
|
# Helper function to run SSH commands
|
|
664
713
|
def run_ssh_command_with_output(cmd, timeout_secs=120):
|
|
@@ -671,7 +720,7 @@ def _update_logic(ctx, *, box, yes, version, verbose, check, force=False):
|
|
|
671
720
|
ssh_cmd.extend(_ssh_mux_opts)
|
|
672
721
|
ssh_cmd.append(ssh_host)
|
|
673
722
|
ssh_cmd.append(cmd)
|
|
674
|
-
return subprocess.run(ssh_cmd, capture_output=True, text=True, timeout=timeout_secs)
|
|
723
|
+
return subprocess.run(ssh_cmd, capture_output=True, text=True, encoding='utf-8', errors='replace', timeout=timeout_secs)
|
|
675
724
|
|
|
676
725
|
def run_ssh_command_interactive(cmd, timeout_secs=300, allow_sudo_prompt=False):
|
|
677
726
|
"""Run an SSH command that may require sudo password input.
|
|
@@ -1705,6 +1754,8 @@ def _update_logic(ctx, *, box, yes, version, verbose, check, force=False):
|
|
|
1705
1754
|
stdout=subprocess.PIPE,
|
|
1706
1755
|
stderr=subprocess.STDOUT,
|
|
1707
1756
|
text=True,
|
|
1757
|
+
encoding='utf-8',
|
|
1758
|
+
errors='replace',
|
|
1708
1759
|
bufsize=1
|
|
1709
1760
|
)
|
|
1710
1761
|
if process.stdout:
|
|
@@ -1718,7 +1769,9 @@ def _update_logic(ctx, *, box, yes, version, verbose, check, force=False):
|
|
|
1718
1769
|
ssh_cmd,
|
|
1719
1770
|
stdout=subprocess.PIPE,
|
|
1720
1771
|
stderr=subprocess.STDOUT,
|
|
1721
|
-
text=True
|
|
1772
|
+
text=True,
|
|
1773
|
+
encoding='utf-8',
|
|
1774
|
+
errors='replace',
|
|
1722
1775
|
)
|
|
1723
1776
|
# Read and store output for potential error reporting
|
|
1724
1777
|
if process.stdout:
|
|
@@ -1962,7 +2015,17 @@ def _update_logic(ctx, *, box, yes, version, verbose, check, force=False):
|
|
|
1962
2015
|
if container_lines:
|
|
1963
2016
|
log_status('OK', 'green')
|
|
1964
2017
|
else:
|
|
1965
|
-
|
|
2018
|
+
if progress:
|
|
2019
|
+
progress.finish(success=False)
|
|
2020
|
+
log_status('FAILED', 'red')
|
|
2021
|
+
log_error('Error: lager container is not running after startup')
|
|
2022
|
+
click.secho(
|
|
2023
|
+
'The container may have crashed immediately after starting. Check logs on the box:',
|
|
2024
|
+
fg='yellow', err=True
|
|
2025
|
+
)
|
|
2026
|
+
click.echo(f' ssh {ssh_host} "docker logs lager --tail 50"', err=True)
|
|
2027
|
+
click.echo(f' ssh {ssh_host} "docker ps -a | grep lager"', err=True)
|
|
2028
|
+
ctx.exit(1)
|
|
1966
2029
|
|
|
1967
2030
|
if verbose and container_lines:
|
|
1968
2031
|
click.echo()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|