arcsecond 4.4.0__tar.gz → 4.4.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.
- {arcsecond-4.4.0 → arcsecond-4.4.2}/PKG-INFO +1 -1
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/hosting/composedoc.py +2 -1
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/hosting/doctor.py +2 -8
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/hosting/local.py +40 -4
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/hosting/network/probe.py +5 -1
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/hosting/utils.py +14 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/pyproject.toml +1 -1
- {arcsecond-4.4.0 → arcsecond-4.4.2}/tests/test_hosting_local.py +69 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/tests/test_hosting_network_probe.py +3 -1
- {arcsecond-4.4.0 → arcsecond-4.4.2}/uv.lock +1 -1
- {arcsecond-4.4.0 → arcsecond-4.4.2}/.docker/Dockerfile_postgres +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/.docker/Dockerfile_redis +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/.flake8 +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/.gitattributes +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/.github/dependabot.yml +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/.github/workflows/pythonpublish.yml +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/.github/workflows/tests.yml +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/.gitignore +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/LICENSE +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/Makefile +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/README.md +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/__init__.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/__version__.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/alpaca/__init__.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/alpaca/commands.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/alpaca/dome_probe.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/alpaca/probe.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/alpaca/telescope_probe.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/api/__init__.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/api/config.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/api/constants.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/api/endpoint.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/api/main.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/api/resources.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/cli.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/cloud/__init__.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/cloud/auth.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/cloud/resources.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/cloud/uploader/__init__.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/cloud/uploader/constants.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/cloud/uploader/context.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/cloud/uploader/datafiles/__init__.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/cloud/uploader/datafiles/context.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/cloud/uploader/datafiles/errors.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/cloud/uploader/datafiles/uploader.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/cloud/uploader/datafiles/utils.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/cloud/uploader/errors.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/cloud/uploader/logger.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/cloud/uploader/uploader.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/cloud/uploader/utils.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/cloud/uploader/walker.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/cloud/uploads.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/docgen.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/errors.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/hosting/__init__.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/hosting/backups.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/hosting/database.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/hosting/docker/__init__.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/hosting/docker/docker-compose.yml +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/hosting/lifecycle.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/hosting/network/__init__.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/hosting/network/guard.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/hosting/network/manifest.toml +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/hosting/network/outbound.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/hosting/network/sheet.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/hosting/skybrightness.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/hosting/stack.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/hosting/token.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/imagesources/__init__.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/imagesources/autostart.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/imagesources/commands.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/imagesources/detection.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/imagesources/proxy.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/imagesources/registry.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/imagesources/runtime.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/imagesources/sources/__init__.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/imagesources/sources/base.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/imagesources/sources/filewatch.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/imagesources/sources/mdns.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/imagesources/sources/network.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/imagesources/sources/opencv.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/imagesources/store.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/options.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/arcsecond/targets.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/examples/example_upload_files.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/requirements.txt +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/setup.cfg +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/setup.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/sonar-project.properties +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/tests/__init__.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/tests/api/__init__.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/tests/api/test_api.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/tests/api/test_api_endpoint.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/tests/api/test_config.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/tests/api/test_config_pointer.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/tests/api/test_targets.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/tests/cloud/__init__.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/tests/cloud/uploader/__init__.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/tests/cloud/uploader/datafiles/__init__.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/tests/cloud/uploader/datafiles/test_uploader_errors.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/tests/cloud/uploader/datafiles/test_uploader_full_process.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/tests/cloud/uploader/datafiles/test_uploader_init.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/tests/cloud/uploader/datafiles/test_uploader_prepare.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/tests/cloud/uploader/datafiles/test_uploader_upload.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/tests/conftest.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/tests/fixtures/docker-compose-6.2.yml +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/tests/fixtures/docker-compose-6.4.yml +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/tests/fixtures/file1.fits +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/tests/test_alpaca_probe.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/tests/test_cli.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/tests/test_cli_api_commands.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/tests/test_docgen.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/tests/test_hosting_backups_install_dir.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/tests/test_hosting_backups_restore.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/tests/test_hosting_composedoc.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/tests/test_hosting_database.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/tests/test_hosting_doctor.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/tests/test_hosting_lifecycle.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/tests/test_hosting_network_guard.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/tests/test_hosting_network_manifest.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/tests/test_hosting_network_outbound.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/tests/test_hosting_network_sheet.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/tests/test_hosting_skybrightness.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/tests/test_hosting_stack.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/tests/test_hosting_token.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/tests/test_hosting_utils_secret_key.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/tests/test_imagesources_autostart.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/tests/test_imagesources_commands.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/tests/test_imagesources_detection.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/tests/test_imagesources_mdns.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/tests/test_imagesources_network.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/tests/test_imagesources_proxy.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/tests/test_imagesources_store.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/tests/test_imagesources_webcam.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/tests/test_targets_planning.py +0 -0
- {arcsecond-4.4.0 → arcsecond-4.4.2}/tests/utils.py +0 -0
|
@@ -226,7 +226,8 @@ ENV_KEYS: Dict[str, tuple] = {
|
|
|
226
226
|
OPERATOR,
|
|
227
227
|
"The address other computers reach the installation at, port included (e.g. 192.168.1.42:5555). The backend "
|
|
228
228
|
"builds invitation and password-reset links from it. Empty means localhost:5555. "
|
|
229
|
-
"
|
|
229
|
+
"`arcsecond setup` fills it in with this machine's network address when it is empty; "
|
|
230
|
+
"set it with `arcsecond setup --lan-host`.",
|
|
230
231
|
),
|
|
231
232
|
OPTIONAL_SERVICES_ENV_KEY: (
|
|
232
233
|
SETUP,
|
|
@@ -58,6 +58,7 @@ from .local import (
|
|
|
58
58
|
REQUIRED_ENV_PROVIDERS,
|
|
59
59
|
template_versions,
|
|
60
60
|
)
|
|
61
|
+
from .utils import lan_ipv4
|
|
61
62
|
|
|
62
63
|
OK, WARN, FAIL, SKIP = "ok", "warn", "fail", "skip"
|
|
63
64
|
|
|
@@ -236,14 +237,7 @@ class Host:
|
|
|
236
237
|
return parse_ss(result.stdout)
|
|
237
238
|
|
|
238
239
|
def lan_ipv4(self) -> Optional[str]:
|
|
239
|
-
|
|
240
|
-
# the default route would use, and that is the address to hand around.
|
|
241
|
-
try:
|
|
242
|
-
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
|
|
243
|
-
s.connect(("10.255.255.255", 1))
|
|
244
|
-
return s.getsockname()[0]
|
|
245
|
-
except OSError:
|
|
246
|
-
return None
|
|
240
|
+
return lan_ipv4()
|
|
247
241
|
|
|
248
242
|
def http_ok(self, url: str) -> Optional[int]:
|
|
249
243
|
try:
|
|
@@ -15,7 +15,9 @@ from .utils import (
|
|
|
15
15
|
_get_encryption_key,
|
|
16
16
|
_get_random_postgres_password,
|
|
17
17
|
_get_random_secret_key,
|
|
18
|
+
_read_env_value,
|
|
18
19
|
_set_env_value,
|
|
20
|
+
lan_ipv4,
|
|
19
21
|
)
|
|
20
22
|
|
|
21
23
|
ENV_FILENAME = ".env"
|
|
@@ -113,7 +115,8 @@ REQUIRED_ENV_PROVIDERS = {
|
|
|
113
115
|
# Empty placeholders: the operator pastes their own GCN credentials here.
|
|
114
116
|
"GCN_CONSUMER_CLIENT_ID": lambda: "",
|
|
115
117
|
"GCN_CONSUMER_CLIENT_SECRET": lambda: "",
|
|
116
|
-
# Empty placeholder:
|
|
118
|
+
# Empty placeholder: filled in by `setup` with this machine's LAN address
|
|
119
|
+
# (_fill_in_lan_host), or set by `arcsecond setup --lan-host`, or by hand.
|
|
117
120
|
FRONTEND_HOST_ENV_KEY: lambda: "",
|
|
118
121
|
}
|
|
119
122
|
|
|
@@ -500,6 +503,30 @@ def _normalise_lan_host(value):
|
|
|
500
503
|
return value
|
|
501
504
|
|
|
502
505
|
|
|
506
|
+
# What `--lan-host ""` writes. Spelled out rather than left empty, so that the
|
|
507
|
+
# next `setup` does not take an empty value for "never decided" and declare the
|
|
508
|
+
# LAN address over the operator's choice.
|
|
509
|
+
THIS_MACHINE_ONLY_HOST = "localhost:5555"
|
|
510
|
+
|
|
511
|
+
|
|
512
|
+
def _fill_in_lan_host(env_path):
|
|
513
|
+
"""Declare this machine's LAN address when no address is declared yet.
|
|
514
|
+
|
|
515
|
+
Left empty, every invitation and password-reset link the server emails
|
|
516
|
+
says localhost, which only works on this machine. A value already there,
|
|
517
|
+
whoever wrote it, is never touched: `doctor` is what notices when a
|
|
518
|
+
declared IP no longer matches the machine.
|
|
519
|
+
"""
|
|
520
|
+
if (_read_env_value(FRONTEND_HOST_ENV_KEY, env_path) or "").strip():
|
|
521
|
+
return None
|
|
522
|
+
detected = lan_ipv4()
|
|
523
|
+
if not detected or detected.startswith("127."):
|
|
524
|
+
return None
|
|
525
|
+
host = _normalise_lan_host(detected)
|
|
526
|
+
_set_env_value(env_path, FRONTEND_HOST_ENV_KEY, host)
|
|
527
|
+
return host
|
|
528
|
+
|
|
529
|
+
|
|
503
530
|
@click.command(short_help="Prepare the installation of Arcsecond.local.")
|
|
504
531
|
@click.option(
|
|
505
532
|
"--with-alerts/--without-alerts",
|
|
@@ -515,7 +542,9 @@ def _normalise_lan_host(value):
|
|
|
515
542
|
metavar="HOST[:PORT]",
|
|
516
543
|
help="The address other computers reach this machine at (e.g. "
|
|
517
544
|
"192.168.1.42 or arcsecond.local). Needed for invitation and "
|
|
518
|
-
"password-reset links to work from other computers. Port defaults to 5555."
|
|
545
|
+
"password-reset links to work from other computers. Port defaults to 5555. "
|
|
546
|
+
"Without it, setup declares this machine's network address when none is "
|
|
547
|
+
"declared yet; an empty value keeps the links on this machine.",
|
|
519
548
|
)
|
|
520
549
|
@click.option(
|
|
521
550
|
"--with-sky-map/--without-sky-map",
|
|
@@ -549,12 +578,19 @@ def setup(with_alerts, lan_host, with_sky_map):
|
|
|
549
578
|
for name, answer in to_record:
|
|
550
579
|
_record_optional_service_decision(env_path, name, answer)
|
|
551
580
|
if lan_host is not None:
|
|
552
|
-
host = _normalise_lan_host(lan_host)
|
|
581
|
+
host = _normalise_lan_host(lan_host) or THIS_MACHINE_ONLY_HOST
|
|
553
582
|
_set_env_value(env_path, FRONTEND_HOST_ENV_KEY, host)
|
|
554
|
-
if host:
|
|
583
|
+
if host != THIS_MACHINE_ONLY_HOST:
|
|
555
584
|
print(f"Other computers will reach this installation at http://{host}")
|
|
556
585
|
else:
|
|
557
586
|
print("This installation is reachable from this machine only.")
|
|
587
|
+
else:
|
|
588
|
+
host = _fill_in_lan_host(env_path)
|
|
589
|
+
if host:
|
|
590
|
+
print(
|
|
591
|
+
f"Other computers will reach this installation at http://{host}"
|
|
592
|
+
" (this machine's network address; change it with --lan-host)"
|
|
593
|
+
)
|
|
558
594
|
write_docker_compose_file(enabled_services=enabled, directory=directory)
|
|
559
595
|
|
|
560
596
|
from .stack import remember_install_dir
|
|
@@ -87,7 +87,11 @@ def _issuer_of(der: bytes) -> str:
|
|
|
87
87
|
Python only decodes certificates it has verified, except through this one
|
|
88
88
|
helper; when it is not there, the issuer is simply not reported."""
|
|
89
89
|
try:
|
|
90
|
-
|
|
90
|
+
# Reopened by name while still open: Windows refuses that unless the
|
|
91
|
+
# file is kept past close. It still goes when the block ends.
|
|
92
|
+
with tempfile.NamedTemporaryFile(
|
|
93
|
+
"w", suffix=".pem", delete_on_close=False
|
|
94
|
+
) as handle:
|
|
91
95
|
handle.write(ssl.DER_cert_to_PEM_cert(der))
|
|
92
96
|
handle.flush()
|
|
93
97
|
decoded = ssl._ssl._test_decode_cert(handle.name) # type: ignore[attr-defined]
|
|
@@ -1,10 +1,24 @@
|
|
|
1
1
|
import base64
|
|
2
2
|
import os
|
|
3
3
|
import secrets
|
|
4
|
+
import socket
|
|
4
5
|
import subprocess
|
|
5
6
|
from pathlib import Path
|
|
6
7
|
|
|
7
8
|
|
|
9
|
+
def lan_ipv4():
|
|
10
|
+
"""This machine's address on the local network, or None.
|
|
11
|
+
|
|
12
|
+
No packet is sent: connecting a UDP socket only picks the interface the
|
|
13
|
+
default route would use, and that is the address to hand around."""
|
|
14
|
+
try:
|
|
15
|
+
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
|
|
16
|
+
s.connect(("10.255.255.255", 1))
|
|
17
|
+
return s.getsockname()[0]
|
|
18
|
+
except OSError:
|
|
19
|
+
return None
|
|
20
|
+
|
|
21
|
+
|
|
8
22
|
def _container_running(name):
|
|
9
23
|
try:
|
|
10
24
|
out = subprocess.run(
|
|
@@ -17,6 +17,9 @@ def _stub_env_generators(monkeypatch):
|
|
|
17
17
|
monkeypatch.setattr(local, "prompt_shared_data_path", lambda: "/tmp/shared-data")
|
|
18
18
|
monkeypatch.setattr(local, "_offer_token", lambda: None)
|
|
19
19
|
monkeypatch.setattr(local, "_offer_sky_map", lambda env_path, flag: None)
|
|
20
|
+
# The machine running the tests has a LAN address of its own; tests that
|
|
21
|
+
# want one give it.
|
|
22
|
+
monkeypatch.setattr(local, "lan_ipv4", lambda: None)
|
|
20
23
|
|
|
21
24
|
|
|
22
25
|
def _packaged_compose_text():
|
|
@@ -495,6 +498,72 @@ def test_setup_lan_host_refuses_a_scheme_or_a_path(tmp_path, monkeypatch):
|
|
|
495
498
|
assert "without scheme" in result.output
|
|
496
499
|
|
|
497
500
|
|
|
501
|
+
def _setup_on_lan(tmp_path, monkeypatch, lan, *args):
|
|
502
|
+
monkeypatch.chdir(tmp_path)
|
|
503
|
+
_stub_env_generators(monkeypatch)
|
|
504
|
+
monkeypatch.setattr(local, "_stdin_is_interactive", lambda: False)
|
|
505
|
+
monkeypatch.setattr(local, "lan_ipv4", lambda: lan)
|
|
506
|
+
return CliRunner().invoke(local.setup, list(args))
|
|
507
|
+
|
|
508
|
+
|
|
509
|
+
def _declared(tmp_path):
|
|
510
|
+
for line in (tmp_path / ".env").read_text(encoding="utf-8").splitlines():
|
|
511
|
+
if line.startswith(local.FRONTEND_HOST_ENV_KEY + "="):
|
|
512
|
+
return line.split("=", 1)[1]
|
|
513
|
+
return None
|
|
514
|
+
|
|
515
|
+
|
|
516
|
+
def test_setup_declares_the_lan_address_when_none_is_declared(tmp_path, monkeypatch):
|
|
517
|
+
result = _setup_on_lan(tmp_path, monkeypatch, "192.168.1.42")
|
|
518
|
+
assert result.exit_code == 0, result.output
|
|
519
|
+
assert _declared(tmp_path) == "192.168.1.42:5555"
|
|
520
|
+
assert "http://192.168.1.42:5555" in result.output
|
|
521
|
+
assert "--lan-host" in result.output
|
|
522
|
+
|
|
523
|
+
|
|
524
|
+
def test_setup_fills_in_an_empty_address_left_by_an_older_setup(tmp_path, monkeypatch):
|
|
525
|
+
(tmp_path / ".env").write_text(f"{local.FRONTEND_HOST_ENV_KEY}=\n")
|
|
526
|
+
result = _setup_on_lan(tmp_path, monkeypatch, "192.168.1.42")
|
|
527
|
+
assert result.exit_code == 0, result.output
|
|
528
|
+
assert _declared(tmp_path) == "192.168.1.42:5555"
|
|
529
|
+
|
|
530
|
+
|
|
531
|
+
def test_setup_never_overwrites_a_declared_address(tmp_path, monkeypatch):
|
|
532
|
+
(tmp_path / ".env").write_text(
|
|
533
|
+
f"{local.FRONTEND_HOST_ENV_KEY}=arcsecond.local:5555\n"
|
|
534
|
+
)
|
|
535
|
+
result = _setup_on_lan(tmp_path, monkeypatch, "192.168.1.42")
|
|
536
|
+
assert result.exit_code == 0, result.output
|
|
537
|
+
assert _declared(tmp_path) == "arcsecond.local:5555"
|
|
538
|
+
assert "192.168.1.42" not in result.output
|
|
539
|
+
|
|
540
|
+
|
|
541
|
+
def test_setup_lan_host_wins_over_the_detected_address(tmp_path, monkeypatch):
|
|
542
|
+
result = _setup_on_lan(
|
|
543
|
+
tmp_path, monkeypatch, "192.168.1.42", "--lan-host", "arcsecond.local"
|
|
544
|
+
)
|
|
545
|
+
assert result.exit_code == 0, result.output
|
|
546
|
+
assert _declared(tmp_path) == "arcsecond.local:5555"
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
def test_setup_leaves_the_address_empty_without_a_network(tmp_path, monkeypatch):
|
|
550
|
+
for lan in (None, "127.0.0.1"):
|
|
551
|
+
result = _setup_on_lan(tmp_path, monkeypatch, lan)
|
|
552
|
+
assert result.exit_code == 0, result.output
|
|
553
|
+
assert _declared(tmp_path) == ""
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
def test_an_empty_lan_host_keeps_the_links_on_this_machine_across_setups(
|
|
557
|
+
tmp_path, monkeypatch
|
|
558
|
+
):
|
|
559
|
+
first = _setup_on_lan(tmp_path, monkeypatch, "192.168.1.42", "--lan-host", "")
|
|
560
|
+
assert first.exit_code == 0, first.output
|
|
561
|
+
assert "this machine only" in first.output
|
|
562
|
+
second = _setup_on_lan(tmp_path, monkeypatch, "192.168.1.42")
|
|
563
|
+
assert second.exit_code == 0, second.output
|
|
564
|
+
assert _declared(tmp_path) == local.THIS_MACHINE_ONLY_HOST
|
|
565
|
+
|
|
566
|
+
|
|
498
567
|
def test_setup_remembers_the_folder_for_start_to_find(tmp_path, monkeypatch):
|
|
499
568
|
from arcsecond.hosting import stack
|
|
500
569
|
|
|
@@ -85,7 +85,9 @@ def self_signed(tmp_path_factory):
|
|
|
85
85
|
|
|
86
86
|
|
|
87
87
|
def test_a_port_nothing_listens_on_is_refused_not_timed_out():
|
|
88
|
-
|
|
88
|
+
# The probe's own timeout, not a shorter one: Windows retries a refused
|
|
89
|
+
# connection for about two seconds per address before saying so.
|
|
90
|
+
result = probing.probe("localhost", _free_port(), timeout=probing.TIMEOUT)
|
|
89
91
|
assert not result.ok and result.failure == probing.REFUSED
|
|
90
92
|
|
|
91
93
|
|
|
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
|
{arcsecond-4.4.0 → arcsecond-4.4.2}/tests/cloud/uploader/datafiles/test_uploader_full_process.py
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
|
|
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
|