lager-cli 0.32.1__tar.gz → 0.32.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.32.1/lager_cli.egg-info → lager_cli-0.32.2}/PKG-INFO +3 -3
- {lager_cli-0.32.1 → lager_cli-0.32.2}/README.md +2 -2
- {lager_cli-0.32.1 → lager_cli-0.32.2}/__init__.py +1 -1
- {lager_cli-0.32.1 → lager_cli-0.32.2}/box_storage.py +53 -8
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/box/_host_ops.py +8 -8
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/box/_mount_prep.py +2 -2
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/box/_pip_validation.py +1 -1
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/box/_ssh.py +3 -3
- lager_cli-0.32.2/commands/box/box_group.py +36 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/box/config.py +42 -42
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/box/diagnose.py +1 -1
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/box/dut.py +2 -2
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/box/hello.py +2 -2
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/box/instruments.py +1 -1
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/box/net_tui.py +2 -2
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/box/nets.py +2 -2
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/box/ssh.py +1 -1
- lager_cli-0.32.1/commands/box/authorize.py → lager_cli-0.32.2/commands/box/ssh_setup.py +21 -4
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/communication/router.py +1 -1
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/communication/uart.py +1 -1
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/communication/usb.py +1 -1
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/power/battery.py +2 -2
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/power/supply.py +1 -1
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/utility/__init__.py +4 -3
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/utility/binaries.py +4 -4
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/utility/install.py +10 -10
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/utility/login.py +35 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/utility/uninstall.py +3 -3
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/utility/update.py +6 -6
- {lager_cli-0.32.1 → lager_cli-0.32.2}/core/net_group.py +1 -1
- {lager_cli-0.32.1 → lager_cli-0.32.2}/core/net_helpers.py +4 -4
- {lager_cli-0.32.1 → lager_cli-0.32.2}/errors.py +1 -1
- {lager_cli-0.32.1 → lager_cli-0.32.2}/gateway_auth.py +53 -8
- {lager_cli-0.32.1 → lager_cli-0.32.2/lager_cli.egg-info}/PKG-INFO +3 -3
- {lager_cli-0.32.1 → lager_cli-0.32.2}/lager_cli.egg-info/SOURCES.txt +2 -2
- {lager_cli-0.32.1 → lager_cli-0.32.2}/main.py +13 -4
- {lager_cli-0.32.1 → lager_cli-0.32.2}/tests/test_gateway_auth.py +83 -0
- lager_cli-0.32.1/commands/box/box_group.py +0 -21
- {lager_cli-0.32.1 → lager_cli-0.32.2}/LICENSE +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/MANIFEST.in +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/__main__.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/address_utils.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/battery/__init__.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/battery/battery_tui.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/battery/websocket_client.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/__init__.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/box/__init__.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/box/_shim_verbs.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/box/boxes.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/box/labjack_pins.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/box/lock.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/communication/__init__.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/communication/ble.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/communication/blufi.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/communication/i2c.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/communication/spi.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/communication/websocket_client.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/communication/wifi.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/development/__init__.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/development/arm.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/development/debug/__init__.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/development/debug/commands.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/development/debug/gdb.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/development/debug/net_cache.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/development/debug/service_client.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/development/debug/service_helper.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/development/debug/tunnel.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/development/devenv.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/development/python.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/measurement/__init__.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/measurement/adc.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/measurement/dac.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/measurement/energy.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/measurement/gpi.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/measurement/gpo.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/measurement/logic.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/measurement/scope.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/measurement/thermocouple.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/measurement/watt.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/power/__init__.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/power/battery_model_csv.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/power/eload.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/power/solar.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/utility/defaults.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/utility/exec_.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/utility/install_wheel.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/utility/logs.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/commands/utility/webcam.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/config.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/context/__init__.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/context/ci_detection.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/context/constants.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/context/core.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/context/error_handlers.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/context/session.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/core/__init__.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/core/group_usage.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/core/matchers.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/core/net_storage.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/core/param_types.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/core/ssh_utils.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/core/utils.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/core/version_skew.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/core/ws_diagnose.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/deployment/__init__.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/deployment/scripts/__init__.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/deployment/scripts/configure_docker_dns.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/deployment/scripts/configure_docker_dns.sh +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/deployment/scripts/convert_to_sparse_checkout.sh +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/deployment/scripts/setup_and_deploy_box.sh +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/deployment/scripts/setup_ssh_key.sh +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/deployment/security/__init__.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/deployment/security/secure_box_firewall.sh +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/elftools/__init__.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/elftools/common/__init__.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/elftools/common/construct_utils.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/elftools/common/exceptions.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/elftools/common/py3compat.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/elftools/common/utils.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/elftools/construct/__init__.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/elftools/construct/adapters.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/elftools/construct/core.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/elftools/construct/debug.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/elftools/construct/macros.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/elftools/dwarf/__init__.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/elftools/dwarf/abbrevtable.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/elftools/dwarf/aranges.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/elftools/dwarf/callframe.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/elftools/dwarf/compileunit.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/elftools/dwarf/constants.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/elftools/dwarf/descriptions.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/elftools/dwarf/die.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/elftools/dwarf/dwarf_expr.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/elftools/dwarf/dwarfinfo.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/elftools/dwarf/enums.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/elftools/dwarf/lineprogram.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/elftools/dwarf/locationlists.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/elftools/dwarf/namelut.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/elftools/dwarf/ranges.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/elftools/dwarf/structs.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/elftools/ehabi/__init__.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/elftools/ehabi/constants.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/elftools/ehabi/decoder.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/elftools/ehabi/ehabiinfo.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/elftools/ehabi/structs.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/elftools/elf/__init__.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/elftools/elf/constants.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/elftools/elf/descriptions.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/elftools/elf/dynamic.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/elftools/elf/elffile.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/elftools/elf/enums.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/elftools/elf/gnuversions.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/elftools/elf/hash.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/elftools/elf/notes.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/elftools/elf/relocation.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/elftools/elf/sections.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/elftools/elf/segments.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/elftools/elf/structs.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/exceptions.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/impl/__init__.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/impl/box_config.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/impl/measurement/__init__.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/impl/measurement/scope.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/impl/measurement/scope_stream.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/impl/power/__init__.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/impl/power/enable_disable.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/lager_cli.egg-info/dependency_links.txt +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/lager_cli.egg-info/entry_points.txt +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/lager_cli.egg-info/requires.txt +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/lager_cli.egg-info/top_level.txt +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/pyproject.toml +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/safe_unpickle.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/setup.cfg +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/setup.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/simple_hdlc.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/sort_utils.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/status.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/supply/__init__.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/supply/supply_tui.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/supply/websocket_client.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/terminal/__init__.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/terminal/core/__init__.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/terminal/core/executor.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/terminal/main.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/terminal/ui/__init__.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/terminal/ui/completer.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/terminal/ui/display.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/terminal/ui/logo.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/terminal/ui/repl.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/terminal/ui/themes.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/tests/test_box_lager_imports.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/tests/test_box_storage.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/tests/test_io_imports.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/update_check.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/vendor/PyCRC/CRC16.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/vendor/PyCRC/CRC16DNP.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/vendor/PyCRC/CRC16Kermit.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/vendor/PyCRC/CRC16SICK.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/vendor/PyCRC/CRC32.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/vendor/PyCRC/CRCCCITT.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/vendor/PyCRC/__init__.py +0 -0
- {lager_cli-0.32.1 → lager_cli-0.32.2}/vendor/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: lager-cli
|
|
3
|
-
Version: 0.32.
|
|
3
|
+
Version: 0.32.2
|
|
4
4
|
Summary: Lager CLI - Box and Docker connectivity
|
|
5
5
|
Home-page: https://github.com/lagerdata/lager
|
|
6
6
|
Author: Lager Data
|
|
@@ -274,10 +274,10 @@ lager devenv terminal
|
|
|
274
274
|
### Package Management
|
|
275
275
|
```bash
|
|
276
276
|
# Add a package to the box's declarative config
|
|
277
|
-
lager box
|
|
277
|
+
lager box-config pip add numpy
|
|
278
278
|
|
|
279
279
|
# Apply the change (restarts the lager container, runs pip install)
|
|
280
|
-
lager box
|
|
280
|
+
lager box-config apply
|
|
281
281
|
```
|
|
282
282
|
|
|
283
283
|
## Supported Hardware
|
|
@@ -221,10 +221,10 @@ lager devenv terminal
|
|
|
221
221
|
### Package Management
|
|
222
222
|
```bash
|
|
223
223
|
# Add a package to the box's declarative config
|
|
224
|
-
lager box
|
|
224
|
+
lager box-config pip add numpy
|
|
225
225
|
|
|
226
226
|
# Apply the change (restarts the lager container, runs pip install)
|
|
227
|
-
lager box
|
|
227
|
+
lager box-config apply
|
|
228
228
|
```
|
|
229
229
|
|
|
230
230
|
## Supported Hardware
|
|
@@ -458,7 +458,7 @@ def _check_box_lock(ip, box_name):
|
|
|
458
458
|
|
|
459
459
|
try:
|
|
460
460
|
resp = requests.get(f'http://{ip}:9000/lock', timeout=3, **_gateway_kwargs(ip))
|
|
461
|
-
_check_gateway(resp, ip)
|
|
461
|
+
resp = _check_gateway(resp, ip)
|
|
462
462
|
if resp.status_code == 404:
|
|
463
463
|
# :9000 answered but has no /lock route: the box image predates
|
|
464
464
|
# the lock API on :9000. Locks can't be enforced against it, so
|
|
@@ -581,12 +581,57 @@ def _gateway_kwargs(ip):
|
|
|
581
581
|
return {'headers': headers} if headers else {}
|
|
582
582
|
|
|
583
583
|
|
|
584
|
+
def _resend_with_auth(prepared, headers):
|
|
585
|
+
"""Re-send an already-prepared request with extra headers merged in.
|
|
586
|
+
Returns the new response, or None if the resend itself failed."""
|
|
587
|
+
import requests
|
|
588
|
+
req = prepared.copy()
|
|
589
|
+
for key, value in headers.items():
|
|
590
|
+
req.headers[key] = value
|
|
591
|
+
try:
|
|
592
|
+
return requests.Session().send(req, timeout=30, stream=False)
|
|
593
|
+
except requests.RequestException:
|
|
594
|
+
return None
|
|
595
|
+
|
|
596
|
+
|
|
584
597
|
def _check_gateway(resp, ip):
|
|
585
|
-
"""
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
598
|
+
"""Resolve a gateway response, returning the response the caller should use.
|
|
599
|
+
|
|
600
|
+
On a plain (un-gated) box this is a passthrough. On a gated box:
|
|
601
|
+
|
|
602
|
+
- First contact — we sent no token (the box→auth-server link is only
|
|
603
|
+
learned from this very 401's discovery header) but we already hold a
|
|
604
|
+
session for that server: record the mapping, attach the token, and
|
|
605
|
+
retry the request once. The caller gets the authenticated response and
|
|
606
|
+
never sees the round trip. A plain box never receives the token,
|
|
607
|
+
because only a gateway sends the discovery header.
|
|
608
|
+
- Genuine denials (revoked session, no access grant, auth server down)
|
|
609
|
+
raise the actionable `lager login` / "ask your admin" error as before.
|
|
610
|
+
|
|
611
|
+
Callers should adopt the return value: ``resp = _check_gateway(resp, ip)``.
|
|
612
|
+
"""
|
|
613
|
+
from .gateway_auth import (
|
|
614
|
+
DISCOVERY_HEADER, handle_gateway_denial,
|
|
615
|
+
record_box_auth_server, auth_headers_for_box,
|
|
616
|
+
)
|
|
617
|
+
if not (resp.status_code in (401, 403, 503) and DISCOVERY_HEADER in resp.headers):
|
|
618
|
+
return resp
|
|
619
|
+
|
|
620
|
+
sent_auth = 'Authorization' in getattr(resp.request, 'headers', {})
|
|
621
|
+
if resp.status_code == 401 and not sent_auth:
|
|
622
|
+
record_box_auth_server(ip, resp.headers[DISCOVERY_HEADER])
|
|
623
|
+
headers = auth_headers_for_box(ip)
|
|
624
|
+
if headers:
|
|
625
|
+
retried = _resend_with_auth(resp.request, headers)
|
|
626
|
+
if retried is not None:
|
|
627
|
+
gated = (retried.status_code in (401, 403, 503)
|
|
628
|
+
and DISCOVERY_HEADER in retried.headers)
|
|
629
|
+
if not gated:
|
|
630
|
+
return retried # transparently authenticated
|
|
631
|
+
resp = retried # still refused — report on the retry
|
|
632
|
+
|
|
633
|
+
handle_gateway_denial(resp, ip) # raises the actionable error
|
|
634
|
+
return resp
|
|
590
635
|
|
|
591
636
|
|
|
592
637
|
def _lock_url(ip, suffix=''):
|
|
@@ -637,7 +682,7 @@ def acquire_box_lock(
|
|
|
637
682
|
# the pre-existing lock as freshly acquired — and release it on exit.
|
|
638
683
|
try:
|
|
639
684
|
pre = requests.get(_lock_url(ip), timeout=5, **_gateway_kwargs(ip))
|
|
640
|
-
_check_gateway(pre, ip)
|
|
685
|
+
pre = _check_gateway(pre, ip)
|
|
641
686
|
if pre.status_code == 200:
|
|
642
687
|
try:
|
|
643
688
|
pre_data = pre.json()
|
|
@@ -663,7 +708,7 @@ def acquire_box_lock(
|
|
|
663
708
|
while True:
|
|
664
709
|
try:
|
|
665
710
|
resp = requests.post(_lock_url(ip), json=payload, timeout=5, **_gateway_kwargs(ip))
|
|
666
|
-
_check_gateway(resp, ip)
|
|
711
|
+
resp = _check_gateway(resp, ip)
|
|
667
712
|
except requests.exceptions.RequestException as exc:
|
|
668
713
|
if not quiet:
|
|
669
714
|
click.secho(
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
|
|
4
4
|
"""
|
|
5
|
-
Host-side SSH helpers for `lager box
|
|
5
|
+
Host-side SSH helpers for `lager box-config apply`:
|
|
6
6
|
- apt_install: `sudo apt-get install -y` over SSH
|
|
7
7
|
- sysctl_apply: write /etc/sysctl.d/99-lager-box-config.conf + sysctl --system
|
|
8
8
|
|
|
@@ -25,7 +25,7 @@ from ._ssh import SshRunner, default_ssh_runner, sudo_error_message
|
|
|
25
25
|
|
|
26
26
|
SYSCTL_CONF_PATH = "/etc/sysctl.d/99-lager-box-config.conf"
|
|
27
27
|
_SYSCTL_HEADER = (
|
|
28
|
-
"# Managed by `lager box
|
|
28
|
+
"# Managed by `lager box-config sysctl`; manual edits are overwritten on apply.\n"
|
|
29
29
|
)
|
|
30
30
|
|
|
31
31
|
# User udev rules live in their own file so they never collide with the
|
|
@@ -37,13 +37,13 @@ UDEV_RULES_DIR = "/etc/udev/rules.d/"
|
|
|
37
37
|
UDEV_RULES_PATH = UDEV_RULES_DIR + UDEV_RULES_FILENAME
|
|
38
38
|
_UDEV_TMP_PATH = "/tmp/" + UDEV_RULES_FILENAME
|
|
39
39
|
_UDEV_HEADER = (
|
|
40
|
-
"# Managed by `lager box
|
|
40
|
+
"# Managed by `lager box-config udev`; manual edits are overwritten on apply.\n"
|
|
41
41
|
)
|
|
42
42
|
|
|
43
43
|
# --- Box-config sudoers rule: single source of truth ------------------------
|
|
44
44
|
#
|
|
45
45
|
# `lager install` and `lager update` write this rule to BOXCFG_SUDOERS_PATH
|
|
46
|
-
# so `lager box
|
|
46
|
+
# so `lager box-config apply` can run apt-get/sysctl/mkdir/chown over
|
|
47
47
|
# BatchMode SSH. The rule must name the actual login user: it used to
|
|
48
48
|
# hardcode `lagerdata`, so on boxes with a different login user the grant
|
|
49
49
|
# never matched — install ended with "Sudoers file installed
|
|
@@ -69,7 +69,7 @@ def is_valid_unix_username(user: Optional[str]) -> bool:
|
|
|
69
69
|
|
|
70
70
|
|
|
71
71
|
def boxcfg_sudoers_rules(user: str = "lagerdata") -> List[str]:
|
|
72
|
-
"""The NOPASSWD rule lines for `lager box
|
|
72
|
+
"""The NOPASSWD rule lines for `lager box-config apply`. tee/rm/sysctl
|
|
73
73
|
are path-scoped so a compromised account cannot escalate via them;
|
|
74
74
|
apt-get and mkdir/chown are unscoped because the package list and host
|
|
75
75
|
paths are user-defined. SETENV on apt-get is required so
|
|
@@ -127,7 +127,7 @@ def udev_sudoers_bootstrap(user: str = "lagerdata") -> str:
|
|
|
127
127
|
" SUDOERS\n"
|
|
128
128
|
" sudo chmod 440 /etc/sudoers.d/lagerdata-udev\n"
|
|
129
129
|
"\n"
|
|
130
|
-
"Then re-run `lager box
|
|
130
|
+
"Then re-run `lager box-config apply`."
|
|
131
131
|
)
|
|
132
132
|
|
|
133
133
|
|
|
@@ -150,7 +150,7 @@ def sudoers_bootstrap(user: str = "lagerdata") -> str:
|
|
|
150
150
|
f" sudo chmod 440 {BOXCFG_SUDOERS_PATH}\n"
|
|
151
151
|
f" sudo touch {BOXCFG_SUDOERS_MARKER} && sudo chmod 644 {BOXCFG_SUDOERS_MARKER}\n"
|
|
152
152
|
"\n"
|
|
153
|
-
"Then re-run `lager box
|
|
153
|
+
"Then re-run `lager box-config apply`. tee/rm/sysctl are path-scoped so a "
|
|
154
154
|
f"compromised {user} account cannot escalate to root via them; apt-get "
|
|
155
155
|
"and mkdir/chown are unscoped because the package list and host paths are "
|
|
156
156
|
"user-defined. SETENV on apt-get is required so "
|
|
@@ -264,7 +264,7 @@ def render_udev_rules_file(rules: List[Dict[str, object]]) -> str:
|
|
|
264
264
|
vid = str(r.get("vid", ""))
|
|
265
265
|
pid = str(r.get("pid", ""))
|
|
266
266
|
mode = str(r.get("mode", "0660"))
|
|
267
|
-
body += f"# vid:pid {vid}:{pid} (added via `lager box
|
|
267
|
+
body += f"# vid:pid {vid}:{pid} (added via `lager box-config udev`)\n"
|
|
268
268
|
body += (
|
|
269
269
|
f'SUBSYSTEM=="usb", ATTRS{{idVendor}}=="{vid}", '
|
|
270
270
|
f'ATTRS{{idProduct}}=="{pid}", MODE="{mode}", GROUP="lager"\n'
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
|
|
4
4
|
"""
|
|
5
|
-
Auto-prep helper for `lager box
|
|
5
|
+
Auto-prep helper for `lager box-config` mounts.
|
|
6
6
|
|
|
7
7
|
The lager container runs as uid 33 (www-data). For every host->container
|
|
8
8
|
bind mount, the host path must (a) exist and (b) for read-write mounts,
|
|
@@ -49,7 +49,7 @@ def sudoers_bootstrap(user: str = "lagerdata") -> str:
|
|
|
49
49
|
" | sudo tee /etc/sudoers.d/lager-box-config\n"
|
|
50
50
|
" sudo chmod 440 /etc/sudoers.d/lager-box-config\n"
|
|
51
51
|
"\n"
|
|
52
|
-
"Then re-run `lager box
|
|
52
|
+
"Then re-run `lager box-config mount add ...` (or `apply`). The "
|
|
53
53
|
"rule is narrow-scoped (mkdir + chown only)."
|
|
54
54
|
)
|
|
55
55
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
|
|
4
4
|
"""
|
|
5
|
-
Host-side helpers for `lager box
|
|
5
|
+
Host-side helpers for `lager box-config pip add`:
|
|
6
6
|
- PyPI existence check (best-effort, network-error tolerant)
|
|
7
7
|
|
|
8
8
|
Format validation lives box-side only (see box/lager/box_config/config.py).
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
|
|
4
4
|
"""
|
|
5
|
-
Shared SSH transport for `lager box
|
|
5
|
+
Shared SSH transport for `lager box-config` host-side operations.
|
|
6
6
|
|
|
7
7
|
A single default runner (with optional stdin), and a parameterized
|
|
8
8
|
sudo-error helper. Callers (mount prep, apply-time host ops, container
|
|
@@ -23,7 +23,7 @@ from ...errors import LagerError
|
|
|
23
23
|
SshRunner = Callable[..., Tuple[int, str, str]]
|
|
24
24
|
|
|
25
25
|
# Dedicated key used by `lager install` / `lager update`. When present,
|
|
26
|
-
# it's tried first for all `lager box
|
|
26
|
+
# it's tried first for all `lager box-config` SSH calls — on boxes
|
|
27
27
|
# provisioned by lager it's the only key authorized for the box user.
|
|
28
28
|
# Passing `-i` replaces ssh's default identity list, so when this key
|
|
29
29
|
# is NOT authorized for the box's user (customer-managed users), a key
|
|
@@ -46,7 +46,7 @@ def ensure_lager_box_keypair(key_path: str = _LAGER_BOX_KEY) -> bool:
|
|
|
46
46
|
|
|
47
47
|
Returns True if a new key was generated, False if one already existed.
|
|
48
48
|
Raises :class:`LagerError` if ssh-keygen fails. This is the single
|
|
49
|
-
definition of how the lager_box key is created — ``lager
|
|
49
|
+
definition of how the lager_box key is created — ``lager ssh-setup``
|
|
50
50
|
and ``lager update`` both call it, so the key type and comment can
|
|
51
51
|
never drift apart between the two provisioning paths.
|
|
52
52
|
"""
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Copyright 2024-2026 Lager Data
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
Deprecated `lager box` group. The subcommands moved to top level —
|
|
6
|
+
`lager box config` became `lager box-config`, and `lager box dut`
|
|
7
|
+
became `lager dut`. This hidden group keeps the old spellings working
|
|
8
|
+
with a deprecation warning until they are removed.
|
|
9
|
+
"""
|
|
10
|
+
import click
|
|
11
|
+
|
|
12
|
+
from .config import box_config
|
|
13
|
+
from .dut import box_dut
|
|
14
|
+
from ...core.group_usage import LagerGroup
|
|
15
|
+
|
|
16
|
+
_RENAMES = {
|
|
17
|
+
"config": "lager box-config",
|
|
18
|
+
"dut": "lager dut",
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@click.group(name="box", cls=LagerGroup, hidden=True)
|
|
23
|
+
@click.pass_context
|
|
24
|
+
def box(ctx: click.Context) -> None:
|
|
25
|
+
"""Deprecated aliases for `lager box-config` and `lager dut`."""
|
|
26
|
+
new = _RENAMES.get(ctx.invoked_subcommand)
|
|
27
|
+
if new:
|
|
28
|
+
click.secho(
|
|
29
|
+
f"DEPRECATED: `lager box {ctx.invoked_subcommand}` is now `{new}`. "
|
|
30
|
+
"The old spelling still works but will be removed in a future release.",
|
|
31
|
+
fg="yellow", err=True,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
box.add_command(box_config, name="config")
|
|
36
|
+
box.add_command(box_dut, name="dut")
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
|
|
4
4
|
"""
|
|
5
|
-
`lager box
|
|
5
|
+
`lager box-config` — declarative per-box provisioning.
|
|
6
6
|
|
|
7
7
|
Reads/writes /etc/lager/box_config.json on the box (via the
|
|
8
8
|
in-container shim cli/impl/box_config.py) and ships an `apply` verb
|
|
@@ -141,7 +141,7 @@ def _list_field(
|
|
|
141
141
|
) -> None:
|
|
142
142
|
"""Resolve box, fetch `show`, pluck `key`, print formatted entries.
|
|
143
143
|
|
|
144
|
-
Shared by every `lager box
|
|
144
|
+
Shared by every `lager box-config <group> list` command. List-valued
|
|
145
145
|
fields (mounts/volumes/pip_packages/...) iterate items; the one dict
|
|
146
146
|
field (sysctl) iterates key/value pairs — the formatter takes a tuple
|
|
147
147
|
in that case, mirroring the diff printer's convention.
|
|
@@ -166,7 +166,7 @@ def _list_field(
|
|
|
166
166
|
click.echo(formatter(item))
|
|
167
167
|
|
|
168
168
|
|
|
169
|
-
@click.group(name="config", cls=LagerGroup, invoke_without_command=True, help="Manage declarative box provisioning")
|
|
169
|
+
@click.group(name="box-config", cls=LagerGroup, invoke_without_command=True, help="Manage declarative box provisioning")
|
|
170
170
|
@click.option("--box", help="Lagerbox name or IP")
|
|
171
171
|
@click.pass_context
|
|
172
172
|
def box_config(ctx: click.Context, box: Optional[str]) -> None:
|
|
@@ -200,7 +200,7 @@ def show_cmd(
|
|
|
200
200
|
if i > 0:
|
|
201
201
|
click.echo()
|
|
202
202
|
click.secho(
|
|
203
|
-
f"No box_config.json on {resolved}. Run `lager box
|
|
203
|
+
f"No box_config.json on {resolved}. Run `lager box-config init`.",
|
|
204
204
|
fg="yellow",
|
|
205
205
|
)
|
|
206
206
|
continue
|
|
@@ -495,7 +495,7 @@ def init_cmd(ctx: click.Context, box: Optional[str], force: bool) -> None:
|
|
|
495
495
|
+ ", ".join(imported),
|
|
496
496
|
fg="green",
|
|
497
497
|
)
|
|
498
|
-
click.echo("Run `lager box
|
|
498
|
+
click.echo("Run `lager box-config apply` to (re)install them in the container.")
|
|
499
499
|
skipped = payload.get("skipped") or []
|
|
500
500
|
if skipped:
|
|
501
501
|
click.secho(f"Skipped {len(skipped)} legacy package(s):", fg="yellow")
|
|
@@ -653,7 +653,7 @@ def diff_cmd(ctx: click.Context, box: Optional[str], as_json: bool) -> None:
|
|
|
653
653
|
def status_cmd(ctx: click.Context, box: Optional[str], as_json: bool) -> None:
|
|
654
654
|
"""Quick health check: clean vs. drifted, field counts, last audit entry.
|
|
655
655
|
|
|
656
|
-
Designed for `lager box
|
|
656
|
+
Designed for `lager box-config status --box <BOX>,<BOX>,<BOX>` —
|
|
657
657
|
one line of signal per box without needing to read full `show` output.
|
|
658
658
|
"""
|
|
659
659
|
targets = _resolve_boxes(ctx, box)
|
|
@@ -694,7 +694,7 @@ def _print_status_human(s: dict) -> None:
|
|
|
694
694
|
state = click.style("clean", fg="green") if s["clean"] else click.style("DRIFT", fg="yellow")
|
|
695
695
|
click.echo(f"{box}: {state}")
|
|
696
696
|
if not s["clean"]:
|
|
697
|
-
click.echo(f" run `lager box
|
|
697
|
+
click.echo(f" run `lager box-config diff --box {box}` to see pending changes")
|
|
698
698
|
nonzero = {k: n for k, n in s["counts"].items() if n}
|
|
699
699
|
if nonzero:
|
|
700
700
|
# Compact rendering: "2 mounts, 3 pip, 1 cargo"
|
|
@@ -758,7 +758,7 @@ def import_cmd(ctx: click.Context, path: str, box: Optional[str], yes: bool) ->
|
|
|
758
758
|
_print_errors(response.get("errors") or [response.get("error", "unknown error")])
|
|
759
759
|
ctx.exit(1)
|
|
760
760
|
click.secho(f"Imported {path} to {resolved}.", fg="green")
|
|
761
|
-
click.echo("Run `lager box
|
|
761
|
+
click.echo("Run `lager box-config apply` to put the new config into effect.")
|
|
762
762
|
|
|
763
763
|
|
|
764
764
|
@box_config.command("edit", help="Open the box's config in $EDITOR for live editing.")
|
|
@@ -827,7 +827,7 @@ def edit_cmd(ctx: click.Context, box: Optional[str]) -> None:
|
|
|
827
827
|
)
|
|
828
828
|
if response.get("ok"):
|
|
829
829
|
click.secho(f"Saved on {resolved}.", fg="green")
|
|
830
|
-
click.echo("Run `lager box
|
|
830
|
+
click.echo("Run `lager box-config apply` to put the new config into effect.")
|
|
831
831
|
return
|
|
832
832
|
click.secho("Config has errors:", fg="red", err=True)
|
|
833
833
|
_print_errors(response.get("errors") or [])
|
|
@@ -883,7 +883,7 @@ def copy_cmd(ctx: click.Context, src: str, dst: str, yes: bool) -> None:
|
|
|
883
883
|
click.secho(
|
|
884
884
|
f"Copied config from {src_resolved} to {dst_resolved}.", fg="green",
|
|
885
885
|
)
|
|
886
|
-
click.echo(f"Run `lager box
|
|
886
|
+
click.echo(f"Run `lager box-config apply --box {dst}` to put the new config into effect.")
|
|
887
887
|
|
|
888
888
|
|
|
889
889
|
@box_config.command(
|
|
@@ -897,11 +897,11 @@ def repair_cmd(ctx: click.Context, box: Optional[str], yes: bool) -> None:
|
|
|
897
897
|
"""Recover a box whose box_config.json is broken or whose container
|
|
898
898
|
is wedged on a bad config.
|
|
899
899
|
|
|
900
|
-
Does what `lager box
|
|
900
|
+
Does what `lager box-config apply`'s rollback path does automatically
|
|
901
901
|
when a bounce fails — but exposed as a manual verb for situations
|
|
902
902
|
that don't trigger automatic rollback:
|
|
903
903
|
- Operator hand-edited /etc/lager/box_config.json into invalid JSON
|
|
904
|
-
(bypassing `lager box
|
|
904
|
+
(bypassing `lager box-config edit`'s validation)
|
|
905
905
|
- First-apply bounce failed AND there's no snapshot yet (rollback
|
|
906
906
|
can't fire) — in that case this command also exits with no-snapshot
|
|
907
907
|
- Container is up but the renderer soft-failed on a stale config and
|
|
@@ -925,9 +925,9 @@ def repair_cmd(ctx: click.Context, box: Optional[str], yes: bool) -> None:
|
|
|
925
925
|
if rc != 0:
|
|
926
926
|
click.secho(
|
|
927
927
|
f"No applied snapshot on {resolved}; cannot repair. This box has "
|
|
928
|
-
"never had a successful `lager box
|
|
928
|
+
"never had a successful `lager box-config apply`. Manual fix: ssh "
|
|
929
929
|
"in, delete or hand-edit /etc/lager/box_config.json, then re-run "
|
|
930
|
-
"`lager box
|
|
930
|
+
"`lager box-config init`.",
|
|
931
931
|
fg="red", err=True,
|
|
932
932
|
)
|
|
933
933
|
ctx.exit(1)
|
|
@@ -1084,7 +1084,7 @@ def reset_cmd(ctx: click.Context, box: Optional[str], yes: bool, do_apply: bool)
|
|
|
1084
1084
|
# config, even if it happened to already be empty.
|
|
1085
1085
|
ctx.invoke(apply_cmd, box=box, yes=True, force=True)
|
|
1086
1086
|
else:
|
|
1087
|
-
click.echo("Run `lager box
|
|
1087
|
+
click.echo("Run `lager box-config apply` to restart the container on the empty config.")
|
|
1088
1088
|
|
|
1089
1089
|
|
|
1090
1090
|
@box_config.command(
|
|
@@ -1153,7 +1153,7 @@ def _apply_one(
|
|
|
1153
1153
|
payload = _parse_response(raw, ctx)
|
|
1154
1154
|
if not payload.get("exists", True):
|
|
1155
1155
|
click.secho(
|
|
1156
|
-
f"No box_config.json on {resolved}. Run `lager box
|
|
1156
|
+
f"No box_config.json on {resolved}. Run `lager box-config init` first.",
|
|
1157
1157
|
fg="yellow",
|
|
1158
1158
|
)
|
|
1159
1159
|
return False
|
|
@@ -1254,7 +1254,7 @@ def _apply_one(
|
|
|
1254
1254
|
click.secho(
|
|
1255
1255
|
f"Box config was NOT applied on {resolved} (the container is up and "
|
|
1256
1256
|
"running the previous config). The applied-hash was left unchanged, "
|
|
1257
|
-
"so `lager box
|
|
1257
|
+
"so `lager box-config apply` will retry this config once the box is "
|
|
1258
1258
|
"repaired — typically with `lager update --box <BOX>`.",
|
|
1259
1259
|
fg="red",
|
|
1260
1260
|
err=True,
|
|
@@ -1274,7 +1274,7 @@ def _apply_one(
|
|
|
1274
1274
|
click.secho(
|
|
1275
1275
|
"Rolled back to the previously applied config; the new config "
|
|
1276
1276
|
"was rejected. The container is up on the previous config — fix "
|
|
1277
|
-
"/etc/lager/box_config.json and re-run `lager box
|
|
1277
|
+
"/etc/lager/box_config.json and re-run `lager box-config apply`.",
|
|
1278
1278
|
fg="yellow",
|
|
1279
1279
|
err=True,
|
|
1280
1280
|
)
|
|
@@ -1342,7 +1342,7 @@ def _post_apply_consistency_ok(
|
|
|
1342
1342
|
)
|
|
1343
1343
|
_print_errors(post_validate.get("errors") or [])
|
|
1344
1344
|
click.secho(
|
|
1345
|
-
"Not updating applied-hash. Fix the file and re-run `lager box
|
|
1345
|
+
"Not updating applied-hash. Fix the file and re-run `lager box-config apply`.",
|
|
1346
1346
|
fg="yellow",
|
|
1347
1347
|
err=True,
|
|
1348
1348
|
)
|
|
@@ -1356,7 +1356,7 @@ def _post_apply_consistency_ok(
|
|
|
1356
1356
|
"Warning: box_config.json was modified during apply. Container is "
|
|
1357
1357
|
"running with the snapshot captured at the start of apply; the "
|
|
1358
1358
|
"latest on-disk changes have NOT been applied. Re-run "
|
|
1359
|
-
"`lager box
|
|
1359
|
+
"`lager box-config apply` to pick them up.",
|
|
1360
1360
|
fg="yellow",
|
|
1361
1361
|
err=True,
|
|
1362
1362
|
)
|
|
@@ -1602,7 +1602,7 @@ def _preflight_mounts(ctx: click.Context, resolved: str, *, recursive: bool) ->
|
|
|
1602
1602
|
"Warning: could not verify mount host paths over SSH; continuing with "
|
|
1603
1603
|
"apply. If the container fails to start or a mounted path is missing "
|
|
1604
1604
|
"or unwritable, fix SSH access to the box host (see above) and re-run "
|
|
1605
|
-
"`lager box
|
|
1605
|
+
"`lager box-config apply`.",
|
|
1606
1606
|
fg="yellow",
|
|
1607
1607
|
err=True,
|
|
1608
1608
|
)
|
|
@@ -1686,7 +1686,7 @@ def _bounce_container_rc(ctx: click.Context, resolved_box: str) -> int:
|
|
|
1686
1686
|
click.secho(
|
|
1687
1687
|
f"SSH command timed out after {_BOUNCE_TIMEOUT_SECONDS // 60} minutes. "
|
|
1688
1688
|
"start_box.sh may still be running on the box (cargo build, etc.); "
|
|
1689
|
-
"re-run `lager box
|
|
1689
|
+
"re-run `lager box-config apply` once it finishes.",
|
|
1690
1690
|
fg="red", err=True,
|
|
1691
1691
|
)
|
|
1692
1692
|
return _BOUNCE_FAILED
|
|
@@ -1823,7 +1823,7 @@ def mount_add_cmd(
|
|
|
1823
1823
|
# let apply's pre-flight re-check once SSH access is fixed.
|
|
1824
1824
|
click.secho(f"Warning: {prep_result.message}", fg="yellow", err=True)
|
|
1825
1825
|
click.secho(
|
|
1826
|
-
"Adding the mount to box_config.json anyway; `lager box
|
|
1826
|
+
"Adding the mount to box_config.json anyway; `lager box-config "
|
|
1827
1827
|
"apply` re-checks the host path before restarting the container.",
|
|
1828
1828
|
fg="yellow",
|
|
1829
1829
|
)
|
|
@@ -1868,7 +1868,7 @@ def mount_add_cmd(
|
|
|
1868
1868
|
else:
|
|
1869
1869
|
click.secho(f"Host path: {prep_result.message}", fg="green")
|
|
1870
1870
|
|
|
1871
|
-
click.echo("Run `lager box
|
|
1871
|
+
click.echo("Run `lager box-config apply` to restart the container.")
|
|
1872
1872
|
|
|
1873
1873
|
|
|
1874
1874
|
@mount_group.command("remove", help="Remove a bind mount by host+container path.")
|
|
@@ -1892,7 +1892,7 @@ def mount_remove_cmd(
|
|
|
1892
1892
|
payload = _parse_response(raw, ctx)
|
|
1893
1893
|
if payload.get("removed"):
|
|
1894
1894
|
click.secho(f"Removed mount {host} -> {container} on {resolved}.", fg="green")
|
|
1895
|
-
click.echo("Run `lager box
|
|
1895
|
+
click.echo("Run `lager box-config apply` to restart the container.")
|
|
1896
1896
|
else:
|
|
1897
1897
|
click.secho(f"No matching mount on {resolved}.", fg="yellow")
|
|
1898
1898
|
|
|
@@ -1933,7 +1933,7 @@ def volume_add_cmd(
|
|
|
1933
1933
|
_print_errors(payload.get("errors") or [payload.get("error", "unknown error")])
|
|
1934
1934
|
ctx.exit(1)
|
|
1935
1935
|
click.secho(f"Added volume {name} -> {container} on {resolved}.", fg="green")
|
|
1936
|
-
click.echo("Run `lager box
|
|
1936
|
+
click.echo("Run `lager box-config apply` to restart the container.")
|
|
1937
1937
|
|
|
1938
1938
|
|
|
1939
1939
|
@volume_group.command("remove", help="Remove a named docker volume by name.")
|
|
@@ -1955,7 +1955,7 @@ def volume_remove_cmd(
|
|
|
1955
1955
|
payload = _parse_response(raw, ctx)
|
|
1956
1956
|
if payload.get("removed"):
|
|
1957
1957
|
click.secho(f"Removed volume {name} on {resolved}.", fg="green")
|
|
1958
|
-
click.echo("Run `lager box
|
|
1958
|
+
click.echo("Run `lager box-config apply` to restart the container.")
|
|
1959
1959
|
else:
|
|
1960
1960
|
click.secho(f"No volume named {name} on {resolved}.", fg="yellow")
|
|
1961
1961
|
|
|
@@ -2022,7 +2022,7 @@ def pip_add_cmd(
|
|
|
2022
2022
|
ctx.exit(1)
|
|
2023
2023
|
added = payload.get("added") or list(packages)
|
|
2024
2024
|
click.secho(f"Added {len(added)} pip package(s) on {resolved}: " + ", ".join(added), fg="green")
|
|
2025
|
-
click.echo("Run `lager box
|
|
2025
|
+
click.echo("Run `lager box-config apply` to install them in the container.")
|
|
2026
2026
|
|
|
2027
2027
|
|
|
2028
2028
|
@pip_group.command("remove", help="Remove one or more pip packages from the box config.")
|
|
@@ -2040,7 +2040,7 @@ def pip_remove_cmd(ctx: click.Context, packages: tuple, box: Optional[str]) -> N
|
|
|
2040
2040
|
removed = payload.get("removed") or []
|
|
2041
2041
|
if removed:
|
|
2042
2042
|
click.secho(f"Removed {len(removed)} pip package(s): " + ", ".join(removed), fg="green")
|
|
2043
|
-
click.echo("Run `lager box
|
|
2043
|
+
click.echo("Run `lager box-config apply` to update the running container.")
|
|
2044
2044
|
else:
|
|
2045
2045
|
click.secho("No matching pip packages were configured.", fg="yellow")
|
|
2046
2046
|
|
|
@@ -2063,7 +2063,7 @@ def pip_import_legacy_cmd(ctx: click.Context, box: Optional[str]) -> None:
|
|
|
2063
2063
|
skipped = payload.get("skipped") or []
|
|
2064
2064
|
if imported:
|
|
2065
2065
|
click.secho(f"Imported {len(imported)} package(s): " + ", ".join(imported), fg="green")
|
|
2066
|
-
click.echo("Run `lager box
|
|
2066
|
+
click.echo("Run `lager box-config apply` to (re)install them.")
|
|
2067
2067
|
else:
|
|
2068
2068
|
click.secho("No new packages to import.", fg="yellow")
|
|
2069
2069
|
if skipped:
|
|
@@ -2109,7 +2109,7 @@ def apt_add_cmd(ctx: click.Context, packages: tuple, box: Optional[str]) -> None
|
|
|
2109
2109
|
ctx.exit(1)
|
|
2110
2110
|
added = payload.get("added") or list(packages)
|
|
2111
2111
|
click.secho(f"Added {len(added)} apt package(s) on {resolved}: " + ", ".join(added), fg="green")
|
|
2112
|
-
click.echo("Run `lager box
|
|
2112
|
+
click.echo("Run `lager box-config apply` to install them on the box host.")
|
|
2113
2113
|
|
|
2114
2114
|
|
|
2115
2115
|
@apt_group.command("remove", help="Remove one or more apt packages from the box config.")
|
|
@@ -2128,7 +2128,7 @@ def apt_remove_cmd(ctx: click.Context, packages: tuple, box: Optional[str]) -> N
|
|
|
2128
2128
|
if removed:
|
|
2129
2129
|
click.secho(f"Removed {len(removed)} apt package(s): " + ", ".join(removed), fg="green")
|
|
2130
2130
|
click.echo(
|
|
2131
|
-
"Run `lager box
|
|
2131
|
+
"Run `lager box-config apply` to record the change. Note: existing "
|
|
2132
2132
|
"installs are not auto-uninstalled."
|
|
2133
2133
|
)
|
|
2134
2134
|
else:
|
|
@@ -2199,7 +2199,7 @@ def udev_add_cmd(
|
|
|
2199
2199
|
ctx.exit(1)
|
|
2200
2200
|
added = payload.get("added") or [f"{r['vid']}:{r['pid']}" for r in rules]
|
|
2201
2201
|
click.secho(f"Added {len(added)} udev rule(s) on {resolved}: " + ", ".join(added), fg="green")
|
|
2202
|
-
click.echo("Run `lager box
|
|
2202
|
+
click.echo("Run `lager box-config apply` to install them on the box host.")
|
|
2203
2203
|
|
|
2204
2204
|
|
|
2205
2205
|
@udev_group.command("remove", help="Remove udev rules by VID:PID.")
|
|
@@ -2218,7 +2218,7 @@ def udev_remove_cmd(ctx: click.Context, devices: tuple, box: Optional[str]) -> N
|
|
|
2218
2218
|
removed = payload.get("removed") or []
|
|
2219
2219
|
if removed:
|
|
2220
2220
|
click.secho(f"Removed {len(removed)} udev rule(s): " + ", ".join(removed), fg="green")
|
|
2221
|
-
click.echo("Run `lager box
|
|
2221
|
+
click.echo("Run `lager box-config apply` to update udev on the box host.")
|
|
2222
2222
|
else:
|
|
2223
2223
|
click.secho("No matching udev rules were configured.", fg="yellow")
|
|
2224
2224
|
|
|
@@ -2281,7 +2281,7 @@ def sysctl_set_cmd(ctx: click.Context, entries: tuple, box: Optional[str]) -> No
|
|
|
2281
2281
|
f"Set {len(set_keys)} sysctl key(s) on {resolved}: " + ", ".join(set_keys),
|
|
2282
2282
|
fg="green",
|
|
2283
2283
|
)
|
|
2284
|
-
click.echo("Run `lager box
|
|
2284
|
+
click.echo("Run `lager box-config apply` to write /etc/sysctl.d/ and reload.")
|
|
2285
2285
|
|
|
2286
2286
|
|
|
2287
2287
|
@sysctl_group.command("unset", help="Remove one or more sysctl keys from the box config.")
|
|
@@ -2299,7 +2299,7 @@ def sysctl_unset_cmd(ctx: click.Context, keys: tuple, box: Optional[str]) -> Non
|
|
|
2299
2299
|
removed = payload.get("removed") or []
|
|
2300
2300
|
if removed:
|
|
2301
2301
|
click.secho(f"Removed {len(removed)} sysctl key(s): " + ", ".join(removed), fg="green")
|
|
2302
|
-
click.echo("Run `lager box
|
|
2302
|
+
click.echo("Run `lager box-config apply` to update /etc/sysctl.d/ and reload.")
|
|
2303
2303
|
else:
|
|
2304
2304
|
click.secho("No matching sysctl keys were configured.", fg="yellow")
|
|
2305
2305
|
|
|
@@ -2362,7 +2362,7 @@ def env_set_cmd(ctx: click.Context, entries: tuple, box: Optional[str]) -> None:
|
|
|
2362
2362
|
f"Set {len(set_keys)} env var(s) on {resolved}: " + ", ".join(set_keys),
|
|
2363
2363
|
fg="green",
|
|
2364
2364
|
)
|
|
2365
|
-
click.echo("Run `lager box
|
|
2365
|
+
click.echo("Run `lager box-config apply` to put the new env vars into effect.")
|
|
2366
2366
|
|
|
2367
2367
|
|
|
2368
2368
|
@env_group.command("unset", help="Remove one or more env vars from the box config.")
|
|
@@ -2380,7 +2380,7 @@ def env_unset_cmd(ctx: click.Context, keys: tuple, box: Optional[str]) -> None:
|
|
|
2380
2380
|
removed = payload.get("removed") or []
|
|
2381
2381
|
if removed:
|
|
2382
2382
|
click.secho(f"Removed {len(removed)} env var(s): " + ", ".join(removed), fg="green")
|
|
2383
|
-
click.echo("Run `lager box
|
|
2383
|
+
click.echo("Run `lager box-config apply` to update the running container.")
|
|
2384
2384
|
else:
|
|
2385
2385
|
click.secho("No matching env vars were configured.", fg="yellow")
|
|
2386
2386
|
|
|
@@ -2420,7 +2420,7 @@ def cargo_add_cmd(ctx: click.Context, packages: tuple, box: Optional[str]) -> No
|
|
|
2420
2420
|
ctx.exit(1)
|
|
2421
2421
|
added = payload.get("added") or list(packages)
|
|
2422
2422
|
click.secho(f"Added {len(added)} cargo crate(s) on {resolved}: " + ", ".join(added), fg="green")
|
|
2423
|
-
click.echo("Run `lager box
|
|
2423
|
+
click.echo("Run `lager box-config apply` to install them in the container.")
|
|
2424
2424
|
|
|
2425
2425
|
|
|
2426
2426
|
@cargo_group.command("remove", help="Remove one or more cargo crates from the box config.")
|
|
@@ -2439,7 +2439,7 @@ def cargo_remove_cmd(ctx: click.Context, packages: tuple, box: Optional[str]) ->
|
|
|
2439
2439
|
if removed:
|
|
2440
2440
|
click.secho(f"Removed {len(removed)} cargo crate(s): " + ", ".join(removed), fg="green")
|
|
2441
2441
|
click.echo(
|
|
2442
|
-
"Run `lager box
|
|
2442
|
+
"Run `lager box-config apply` to record the change. Note: existing "
|
|
2443
2443
|
"installs in the container are not auto-uninstalled."
|
|
2444
2444
|
)
|
|
2445
2445
|
else:
|
|
@@ -2481,7 +2481,7 @@ def npm_add_cmd(ctx: click.Context, packages: tuple, box: Optional[str]) -> None
|
|
|
2481
2481
|
ctx.exit(1)
|
|
2482
2482
|
added = payload.get("added") or list(packages)
|
|
2483
2483
|
click.secho(f"Added {len(added)} npm package(s) on {resolved}: " + ", ".join(added), fg="green")
|
|
2484
|
-
click.echo("Run `lager box
|
|
2484
|
+
click.echo("Run `lager box-config apply` to install them in the container.")
|
|
2485
2485
|
|
|
2486
2486
|
|
|
2487
2487
|
@npm_group.command("remove", help="Remove one or more npm packages from the box config.")
|
|
@@ -2500,7 +2500,7 @@ def npm_remove_cmd(ctx: click.Context, packages: tuple, box: Optional[str]) -> N
|
|
|
2500
2500
|
if removed:
|
|
2501
2501
|
click.secho(f"Removed {len(removed)} npm package(s): " + ", ".join(removed), fg="green")
|
|
2502
2502
|
click.echo(
|
|
2503
|
-
"Run `lager box
|
|
2503
|
+
"Run `lager box-config apply` to record the change. Note: existing "
|
|
2504
2504
|
"installs in the container are not auto-uninstalled."
|
|
2505
2505
|
)
|
|
2506
2506
|
else:
|