lookout-cli 5.0.1__tar.gz → 5.2.0__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.
- {lookout_cli-5.0.1 → lookout_cli-5.2.0}/PKG-INFO +1 -1
- {lookout_cli-5.0.1 → lookout_cli-5.2.0}/lookout_cli/cli.py +1 -0
- lookout_cli-5.2.0/lookout_cli/docker/docker-compose.cpu.yaml +11 -0
- {lookout_cli-5.0.1 → lookout_cli-5.2.0}/lookout_cli/docker/docker-compose.yaml +1 -0
- {lookout_cli-5.0.1 → lookout_cli-5.2.0}/lookout_cli/groups/base.py +116 -12
- {lookout_cli-5.0.1 → lookout_cli-5.2.0}/lookout_cli/groups/model.py +0 -1
- {lookout_cli-5.0.1 → lookout_cli-5.2.0}/lookout_cli.egg-info/PKG-INFO +1 -1
- {lookout_cli-5.0.1 → lookout_cli-5.2.0}/lookout_cli.egg-info/SOURCES.txt +1 -0
- {lookout_cli-5.0.1 → lookout_cli-5.2.0}/pyproject.toml +1 -1
- {lookout_cli-5.0.1 → lookout_cli-5.2.0}/README.md +0 -0
- {lookout_cli-5.0.1 → lookout_cli-5.2.0}/lookout_cli/__init__.py +0 -0
- {lookout_cli-5.0.1 → lookout_cli-5.2.0}/lookout_cli/auth_helpers.py +0 -0
- {lookout_cli-5.0.1 → lookout_cli-5.2.0}/lookout_cli/banner.py +0 -0
- {lookout_cli-5.0.1 → lookout_cli-5.2.0}/lookout_cli/docker/docker-compose.cache.yaml +0 -0
- {lookout_cli-5.0.1 → lookout_cli-5.2.0}/lookout_cli/docker/docker-compose.dev.yaml +0 -0
- {lookout_cli-5.0.1 → lookout_cli-5.2.0}/lookout_cli/docker/docker-compose.jetson.yaml +0 -0
- {lookout_cli-5.0.1 → lookout_cli-5.2.0}/lookout_cli/docker/docker-compose.network-host.yaml +0 -0
- {lookout_cli-5.0.1 → lookout_cli-5.2.0}/lookout_cli/docker/docker-compose.network-proxy.yaml +0 -0
- {lookout_cli-5.0.1 → lookout_cli-5.2.0}/lookout_cli/docker/docker-compose.prod.yaml +0 -0
- {lookout_cli-5.0.1 → lookout_cli-5.2.0}/lookout_cli/docker/docker-compose.x86.yaml +0 -0
- {lookout_cli-5.0.1 → lookout_cli-5.2.0}/lookout_cli/docker_helpers.py +0 -0
- {lookout_cli-5.0.1 → lookout_cli-5.2.0}/lookout_cli/groups/__init__.py +0 -0
- {lookout_cli-5.0.1 → lookout_cli-5.2.0}/lookout_cli/groups/setup.py +0 -0
- {lookout_cli-5.0.1 → lookout_cli-5.2.0}/lookout_cli/helpers.py +0 -0
- {lookout_cli-5.0.1 → lookout_cli-5.2.0}/lookout_cli/test/lookout_cli_test.py +0 -0
- {lookout_cli-5.0.1 → lookout_cli-5.2.0}/lookout_cli.egg-info/dependency_links.txt +0 -0
- {lookout_cli-5.0.1 → lookout_cli-5.2.0}/lookout_cli.egg-info/entry_points.txt +0 -0
- {lookout_cli-5.0.1 → lookout_cli-5.2.0}/lookout_cli.egg-info/requires.txt +0 -0
- {lookout_cli-5.0.1 → lookout_cli-5.2.0}/lookout_cli.egg-info/top_level.txt +0 -0
- {lookout_cli-5.0.1 → lookout_cli-5.2.0}/lookout_cli.egg-info/zip-safe +0 -0
- {lookout_cli-5.0.1 → lookout_cli-5.2.0}/setup.cfg +0 -0
- {lookout_cli-5.0.1 → lookout_cli-5.2.0}/setup.py +0 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
name: lookout
|
|
2
|
+
|
|
3
|
+
# Drops the NVIDIA device reservations so the stack can start on machines
|
|
4
|
+
# without a GPU (e.g. CI runners). Selected automatically by the CLI when
|
|
5
|
+
# nvidia-smi is absent.
|
|
6
|
+
services:
|
|
7
|
+
lookout_core:
|
|
8
|
+
deploy: !reset null
|
|
9
|
+
|
|
10
|
+
lookout_greenstream:
|
|
11
|
+
deploy: !reset null
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import platform
|
|
3
|
+
import shutil
|
|
4
|
+
import socket
|
|
3
5
|
import subprocess
|
|
6
|
+
import time
|
|
4
7
|
from pathlib import Path
|
|
5
8
|
from typing import List, Optional, cast
|
|
6
9
|
|
|
@@ -56,6 +59,7 @@ DOCKER_NETWORK_HOST = docker_compose_path("./docker-compose.network-host.yaml")
|
|
|
56
59
|
DOCKER_NETWORK_PROXY = docker_compose_path("./docker-compose.network-proxy.yaml")
|
|
57
60
|
DOCKER_JETSON = docker_compose_path("./docker-compose.jetson.yaml")
|
|
58
61
|
DOCKER_X86 = docker_compose_path("./docker-compose.x86.yaml")
|
|
62
|
+
DOCKER_CPU = docker_compose_path("./docker-compose.cpu.yaml")
|
|
59
63
|
|
|
60
64
|
SERVICES = [
|
|
61
65
|
"lookout_core",
|
|
@@ -95,6 +99,9 @@ def _get_compose_files(
|
|
|
95
99
|
compose_files.append(DOCKER_JETSON)
|
|
96
100
|
else:
|
|
97
101
|
compose_files.append(DOCKER_X86)
|
|
102
|
+
if shutil.which("nvidia-smi") is None:
|
|
103
|
+
# no NVIDIA driver (e.g. CI runners): drop the GPU device reservations
|
|
104
|
+
compose_files.append(DOCKER_CPU)
|
|
98
105
|
|
|
99
106
|
if proxy:
|
|
100
107
|
compose_files.append(DOCKER_NETWORK_PROXY)
|
|
@@ -153,6 +160,18 @@ def log_config(config: LookoutConfig):
|
|
|
153
160
|
)
|
|
154
161
|
|
|
155
162
|
|
|
163
|
+
def _prepare_mounted_dirs(config: LookoutConfig) -> None:
|
|
164
|
+
"""Create the host directories the containers bind-mount; docker would
|
|
165
|
+
otherwise create missing mount sources root-owned (e.g. on CI runners)."""
|
|
166
|
+
get_config_io().get_path().chmod(0o777)
|
|
167
|
+
greenroom_tmp_directory = Path("/tmp/greenroom")
|
|
168
|
+
make_dir_set_permission(Path(config.log_directory).expanduser())
|
|
169
|
+
make_dir_set_permission(Path(config.recording_directory).expanduser())
|
|
170
|
+
make_dir_set_permission(Path(config.models_directory).expanduser())
|
|
171
|
+
make_dir_set_permission(greenroom_tmp_directory)
|
|
172
|
+
make_dir_set_permission(greenroom_tmp_directory / "sockets")
|
|
173
|
+
|
|
174
|
+
|
|
156
175
|
def set_initial_env(config_dir: str):
|
|
157
176
|
version = get_version()
|
|
158
177
|
os.environ["LOOKOUT_CONFIG_DIR"] = config_dir
|
|
@@ -239,18 +258,7 @@ def up(
|
|
|
239
258
|
if config.prod and build:
|
|
240
259
|
raise click.UsageError("Cannot build in production mode. Run `lookout build` instead")
|
|
241
260
|
|
|
242
|
-
|
|
243
|
-
log_directory = Path(config.log_directory).expanduser()
|
|
244
|
-
recording_directory = Path(config.recording_directory).expanduser()
|
|
245
|
-
models_directory = Path(config.models_directory).expanduser()
|
|
246
|
-
greenroom_tmp_directory = Path("/tmp/greenroom")
|
|
247
|
-
sockets_directory = greenroom_tmp_directory / "sockets"
|
|
248
|
-
get_config_io().get_path().chmod(0o777)
|
|
249
|
-
make_dir_set_permission(log_directory)
|
|
250
|
-
make_dir_set_permission(recording_directory)
|
|
251
|
-
make_dir_set_permission(models_directory)
|
|
252
|
-
make_dir_set_permission(greenroom_tmp_directory)
|
|
253
|
-
make_dir_set_permission(sockets_directory)
|
|
261
|
+
_prepare_mounted_dirs(config)
|
|
254
262
|
set_env_from_config(config)
|
|
255
263
|
|
|
256
264
|
services_list = list(services) if services else None
|
|
@@ -452,6 +460,102 @@ def lint():
|
|
|
452
460
|
call("pre-commit run --all")
|
|
453
461
|
|
|
454
462
|
|
|
463
|
+
def _wait_for_port(port: int, timeout: float) -> bool:
|
|
464
|
+
deadline = time.time() + timeout
|
|
465
|
+
while time.time() < deadline:
|
|
466
|
+
try:
|
|
467
|
+
with socket.create_connection(("localhost", port), timeout=0.5):
|
|
468
|
+
return True
|
|
469
|
+
except OSError:
|
|
470
|
+
time.sleep(0.5)
|
|
471
|
+
return False
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
def _dump_container_logs(container: str, tail: int = 150) -> None:
|
|
475
|
+
"""Print a container's recent logs so a failure is diagnosable (e.g. in CI)."""
|
|
476
|
+
click.echo(click.style(f"--- last {tail} log lines from {container} ---", fg="yellow"))
|
|
477
|
+
try:
|
|
478
|
+
subprocess.run(["docker", "logs", "--tail", str(tail), container], check=False, timeout=15)
|
|
479
|
+
except (OSError, subprocess.TimeoutExpired) as error:
|
|
480
|
+
click.echo(f"could not fetch logs: {error}")
|
|
481
|
+
|
|
482
|
+
|
|
483
|
+
def _container_env(container: str) -> str | None:
|
|
484
|
+
"""The container's environment, or None when it can't be inspected."""
|
|
485
|
+
try:
|
|
486
|
+
result = subprocess.run(
|
|
487
|
+
["docker", "inspect", container, "--format", "{{json .Config.Env}}"],
|
|
488
|
+
capture_output=True,
|
|
489
|
+
text=True,
|
|
490
|
+
check=False,
|
|
491
|
+
timeout=15,
|
|
492
|
+
)
|
|
493
|
+
except (OSError, subprocess.TimeoutExpired):
|
|
494
|
+
return None
|
|
495
|
+
return result.stdout if result.returncode == 0 else None
|
|
496
|
+
|
|
497
|
+
|
|
498
|
+
@click.command(name="test")
|
|
499
|
+
@click.option(
|
|
500
|
+
"--up",
|
|
501
|
+
type=bool,
|
|
502
|
+
default=False,
|
|
503
|
+
is_flag=True,
|
|
504
|
+
help="Bring up lookout_core first instead of requiring a running stack",
|
|
505
|
+
)
|
|
506
|
+
@config_dir_option
|
|
507
|
+
@click.argument("args", nargs=-1)
|
|
508
|
+
def test(up: bool, config_dir: str, args: tuple[str, ...]) -> None:
|
|
509
|
+
"""Runs the platform bridge smoke tests against a running Lookout.
|
|
510
|
+
|
|
511
|
+
Bring the stack up first (`lookout up`), or pass --up. Extra ARGS are
|
|
512
|
+
forwarded to pytest (e.g. `-- -k tracks`).
|
|
513
|
+
"""
|
|
514
|
+
set_initial_env(config_dir)
|
|
515
|
+
dev_mode = is_dev_version()
|
|
516
|
+
|
|
517
|
+
config = get_config_io().read()
|
|
518
|
+
config.prod = maybe_ignore_prod(dev_mode, config.prod)
|
|
519
|
+
set_env_from_config(config)
|
|
520
|
+
|
|
521
|
+
# the bridge trusts the committed test keypair so the tests can mint tokens (keys/README.md)
|
|
522
|
+
os.environ["LOOKOUT_TEST_MODE"] = "true"
|
|
523
|
+
|
|
524
|
+
if up:
|
|
525
|
+
_prepare_mounted_dirs(config)
|
|
526
|
+
docker = _get_docker_client(prod=config.prod, proxy=config.proxy.enabled)
|
|
527
|
+
services = ["lookout_core"] + (["lookout_proxy"] if config.proxy.enabled else [])
|
|
528
|
+
docker.compose.up(services, detach=True)
|
|
529
|
+
|
|
530
|
+
click.echo("Waiting for the platform bridge on localhost:50051...")
|
|
531
|
+
if not _wait_for_port(50051, timeout=180):
|
|
532
|
+
_dump_container_logs("lookout_core")
|
|
533
|
+
raise click.UsageError(
|
|
534
|
+
"The platform bridge is not reachable on localhost:50051 — run "
|
|
535
|
+
"`LOOKOUT_TEST_MODE=true lookout up` first (or pass --up)"
|
|
536
|
+
)
|
|
537
|
+
if not up:
|
|
538
|
+
env = _container_env("lookout_core")
|
|
539
|
+
if env is not None and "LOOKOUT_TEST_MODE=true" not in env:
|
|
540
|
+
raise click.UsageError(
|
|
541
|
+
"lookout_core is running without LOOKOUT_TEST_MODE=true, so the bridge "
|
|
542
|
+
"will reject test tokens — restart with `LOOKOUT_TEST_MODE=true lookout up` "
|
|
543
|
+
"or pass --up"
|
|
544
|
+
)
|
|
545
|
+
if config.proxy.enabled and not _wait_for_port(config.proxy.https_port, timeout=60):
|
|
546
|
+
_dump_container_logs("lookout_proxy")
|
|
547
|
+
raise click.UsageError(
|
|
548
|
+
f"The proxy is not reachable on localhost:{config.proxy.https_port}"
|
|
549
|
+
)
|
|
550
|
+
|
|
551
|
+
# missing client deps fail instead of skipping; see tests/integration/README.md
|
|
552
|
+
os.environ["LOOKOUT_INTEGRATION_REQUIRE"] = "1"
|
|
553
|
+
tests_dir = Path(get_project_root()) / "tests" / "integration"
|
|
554
|
+
result = subprocess.run(["python3", "-m", "pytest", "-v", str(tests_dir), *args], check=False)
|
|
555
|
+
if result.returncode != 0:
|
|
556
|
+
raise SystemExit(result.returncode)
|
|
557
|
+
|
|
558
|
+
|
|
455
559
|
@click.command(name="generate")
|
|
456
560
|
@config_dir_option
|
|
457
561
|
@click.option(
|
|
@@ -15,6 +15,7 @@ lookout_cli.egg-info/requires.txt
|
|
|
15
15
|
lookout_cli.egg-info/top_level.txt
|
|
16
16
|
lookout_cli.egg-info/zip-safe
|
|
17
17
|
lookout_cli/docker/docker-compose.cache.yaml
|
|
18
|
+
lookout_cli/docker/docker-compose.cpu.yaml
|
|
18
19
|
lookout_cli/docker/docker-compose.dev.yaml
|
|
19
20
|
lookout_cli/docker/docker-compose.jetson.yaml
|
|
20
21
|
lookout_cli/docker/docker-compose.network-host.yaml
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{lookout_cli-5.0.1 → lookout_cli-5.2.0}/lookout_cli/docker/docker-compose.network-proxy.yaml
RENAMED
|
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
|