mirror of
https://github.com/NVIDIA/OpenShell.git
synced 2026-10-02 15:40:03 +08:00
* fix(snap): simplify snap hooks
The `post-refresh` hook runs after initial snap installation as well, so
there is no need to call the `install` hook from within the
`post-refresh` hook; instead, the logic can simply be moved into the
`post-refresh` hook directly, and the `install` hook removed.
Also, the existing `install` hook logic looked for an insecure
configuration, and if found, replaced the entire configuration file with
a minimal default in the current format. But OpenShell does that default
behavior without any config file, so we may as well simply remove the
configuration file entirely to keep up-to-date with the current default
behavior. Let OpenShell create a configuration file if it needs to,
rather than auto-create one via the packaging scripts.
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
* fix(snap): remove the connect-plug-docker hook
The `openshell:docker` is auto-connected to the system `:docker` slot,
so there should not be a need to separately restart the gateway service
when the interface is connected.
For locally-built test snaps which were not published to the store, the
autoconnection is not made, but when the snap is installed, the gateway
will attempt to start anyway and fail to find any available compute
driver, so quickly restart until it hits the systemd start-limit, after
which systemd prevents the service from being started again. If a user
tries to manually connect their locally-built `openshell` snap to the
`:docker` slot, then the `connect-plug-docker` hook runs and triggers a
restart of the gateway, which will usually fail because the start limit
has already been hit. An error in the hook will thus cause the interface
connection to be undone, which is undesirable.
Thus, we can remove this hook entirely, and instead allow interface
connections to succeed as intended. The user still needs to manually
restart the gateway service after making a manual connection (as was the
case previously) and probably needs to `systemctl reset-failed` first,
but at least connection will succeed beforehand so they can proceed with
these steps.
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
* fix(snap): set refresh-mode: endure again, with manual restart
Return to the previous behavior before commit a67567e58, where the
gateway is not stopped before refreshes. The `post-refresh` hook
now restarts the gateway if the TLS configuration was corrected, so we
don't have to enforce restarting the gateway on every refresh even when
not necessary. Thus, set `refresh-mode: endure`, and let the hook decide
when the gateway needs to be restarted.
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
* fix(snap): update docs and tests to reflect snap hook changes
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
* docs(snap): remove verbose explanation of snap gateway refresh behavior
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
---------
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
266 lines
11 KiB
Python
266 lines
11 KiB
Python
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
|
|
def test_generate_homebrew_formula_uses_channel_urls_and_exact_version(
|
|
tmp_path: Path,
|
|
) -> None:
|
|
release_dir = tmp_path / "release"
|
|
release_dir.mkdir()
|
|
(release_dir / "openshell-checksums-sha256.txt").write_text(
|
|
"\n".join(
|
|
[
|
|
"a" * 64 + " openshell-aarch64-apple-darwin.tar.gz",
|
|
"b" * 64 + " openshell-driver-vm-aarch64-apple-darwin.tar.gz",
|
|
]
|
|
)
|
|
+ "\n",
|
|
encoding="utf-8",
|
|
)
|
|
(release_dir / "openshell-gateway-checksums-sha256.txt").write_text(
|
|
"d" * 64 + " openshell-gateway-aarch64-apple-darwin.tar.gz\n",
|
|
encoding="utf-8",
|
|
)
|
|
(release_dir / "openshell-prover-checksums-sha256.txt").write_text(
|
|
"e" * 64 + " openshell-prover-aarch64-apple-darwin.tar.gz\n",
|
|
encoding="utf-8",
|
|
)
|
|
|
|
repo_root = Path(__file__).resolve().parents[2]
|
|
output = tmp_path / "openshell.rb"
|
|
subprocess.run(
|
|
[
|
|
sys.executable,
|
|
str(repo_root / "tasks/scripts/release.py"),
|
|
"generate-homebrew-formula",
|
|
"--release-tag",
|
|
"v0.1.0-pre.3",
|
|
"--release-dir",
|
|
str(release_dir),
|
|
"--output",
|
|
str(output),
|
|
],
|
|
check=True,
|
|
)
|
|
|
|
formula = output.read_text(encoding="utf-8")
|
|
assert (
|
|
"https://github.com/NVIDIA/OpenShell/releases/download/"
|
|
"v0.1.0-pre.3/openshell-driver-vm-aarch64-apple-darwin.tar.gz"
|
|
) in formula
|
|
assert 'version "0.1.0-pre.3"' in formula
|
|
assert 'sha256 "' + "b" * 64 + '"' in formula
|
|
assert (
|
|
"https://github.com/NVIDIA/OpenShell/releases/download/"
|
|
"v0.1.0-pre.3/openshell-prover-aarch64-apple-darwin.tar.gz"
|
|
) in formula
|
|
assert 'sha256 "' + "e" * 64 + '"' in formula
|
|
assert 'resource("openshell-prover").stage' in formula
|
|
assert 'bin.install "openshell-prover"' in formula
|
|
assert "#{bin}/openshell-prover --version" in formula
|
|
assert "OPENSHELL_COMPUTE_DRIVER: " not in formula
|
|
assert 'OPENSHELL_GATEWAY_CONFIG: "#{var}/openshell/gateway.toml"' not in formula
|
|
assert "init-gateway-config.sh" not in formula
|
|
assert 'gateway_config = var/"openshell/gateway.toml"' in formula
|
|
assert "unless gateway_config.exist?" in formula
|
|
generated_config = re.search(
|
|
r"gateway_config_contents = <<~TOML\n(?P<contents>.*?)\n TOML",
|
|
formula,
|
|
flags=re.DOTALL,
|
|
)
|
|
assert generated_config is not None
|
|
assert "version = 2" in generated_config.group("contents")
|
|
assert "[openshell.gateway]" in generated_config.group("contents")
|
|
assert "bind_address =" not in generated_config.group("contents")
|
|
|
|
legacy_empty_config = re.search(
|
|
r"legacy_empty_gateway_config_contents = <<~TOML\n(?P<contents>.*?)\n TOML",
|
|
formula,
|
|
flags=re.DOTALL,
|
|
)
|
|
assert legacy_empty_config is not None
|
|
assert "version = 1" in legacy_empty_config.group("contents")
|
|
assert "bind_address =" not in legacy_empty_config.group("contents")
|
|
|
|
legacy_ipv6_config = re.search(
|
|
r"legacy_ipv6_gateway_config_contents = <<~TOML\n(?P<contents>.*?)\n TOML",
|
|
formula,
|
|
flags=re.DOTALL,
|
|
)
|
|
assert legacy_ipv6_config is not None
|
|
assert "version = 1" in legacy_ipv6_config.group("contents")
|
|
assert 'bind_address = "[::1]:17670"' in legacy_ipv6_config.group("contents")
|
|
assert "gateway_config.read == legacy_empty_gateway_config_contents ||" in formula
|
|
assert "gateway_config.read == legacy_ipv6_gateway_config_contents" in formula
|
|
assert formula.count("gateway_config.write gateway_config_contents") == 1
|
|
assert "gateway_config.atomic_write gateway_config_contents" in formula
|
|
assert '# compute_driver = "vm"' not in formula
|
|
assert (
|
|
"openshell gateway add https://localhost:17670 --local --name openshell"
|
|
in formula
|
|
)
|
|
assert 'run opt_libexec/"openshell-gateway-homebrew-service"' in formula
|
|
assert 'xdg_config_home="${XDG_CONFIG_HOME:-${HOME}/.config}"' in formula
|
|
assert 'xdg_gateway_config="${xdg_config_home}/openshell/gateway.toml"' in formula
|
|
assert 'prefix_gateway_config="#{var}/openshell/gateway.toml"' in formula
|
|
assert (
|
|
'if [ -z "${OPENSHELL_GATEWAY_CONFIG:-}" ] && [ ! -f "${xdg_gateway_config}" ] && [ -f "${prefix_gateway_config}" ]; then'
|
|
) in formula
|
|
assert (
|
|
'exec "#{opt_bin}/openshell-gateway" --config "${prefix_gateway_config}"'
|
|
in formula
|
|
)
|
|
assert 'exec "#{opt_bin}/openshell-gateway"' in formula
|
|
assert "--db-url" not in formula
|
|
assert 'docker_tls_dir="${HOME}/.local/state/openshell/homebrew/tls"' in formula
|
|
assert (
|
|
'export OPENSHELL_LOCAL_TLS_DIR="${OPENSHELL_LOCAL_TLS_DIR:-${docker_tls_dir}}"'
|
|
in formula
|
|
)
|
|
assert '/usr/bin/install -m 0600 "#{var}/openshell/tls/server/tls.key"' in formula
|
|
assert "OPENSHELL_CONFIG_" not in formula
|
|
assert "OPENSHELL_DOCKER_TLS_DIR" not in formula
|
|
assert 'xdg_gateway_env="${xdg_config_home}/openshell/gateway.env"' in formula
|
|
assert 'prefix_gateway_env="#{var}/openshell/gateway.env"' in formula
|
|
assert '. "${xdg_gateway_env}"' in formula
|
|
assert '. "${prefix_gateway_env}"' in formula
|
|
assert 'gateway_env = var/"openshell/gateway.env"' not in formula
|
|
assert "#OPENSHELL_GATEWAY_CONFIG=#{var}/openshell/gateway.toml" not in formula
|
|
assert "environment_variables(" not in formula
|
|
assert " OPENSHELL_BIND_ADDRESS:" not in formula
|
|
assert " OPENSHELL_SERVER_PORT:" not in formula
|
|
assert " OPENSHELL_TLS_CERT:" not in formula
|
|
assert "OPENSHELL_DRIVER_DIR:" not in formula
|
|
assert "OPENSHELL_DOCKER_SUPERVISOR_IMAGE:" not in formula
|
|
assert 'OPENSHELL_DOCKER_TLS_CA: "#{var}/openshell/tls/ca.crt"' not in formula
|
|
assert "entitlements.atomic_write" in formula
|
|
assert "brew services restart openshell" in formula
|
|
|
|
|
|
def test_snap_wrapper_uses_optional_gateway_config_without_generating_toml() -> None:
|
|
repo_root = Path(__file__).resolve().parents[2]
|
|
wrapper = (repo_root / "tasks/scripts/snap-gateway-wrapper.sh").read_text(
|
|
encoding="utf-8"
|
|
)
|
|
|
|
assert "init-gateway-config.sh" not in wrapper
|
|
assert (
|
|
'export OPENSHELL_DB_URL="${OPENSHELL_DB_URL:-sqlite:${SNAP_COMMON}/gateway.db?mode=rwc}"'
|
|
in wrapper
|
|
)
|
|
assert "OPENSHELL_DISABLE_TLS" not in wrapper
|
|
assert (
|
|
'export OPENSHELL_LOCAL_TLS_DIR="${OPENSHELL_LOCAL_TLS_DIR:-${SNAP_COMMON}/tls}"'
|
|
in wrapper
|
|
)
|
|
assert (
|
|
'exec "${SNAP}/bin/openshell-gateway" --config "$CANONICAL_CONFIG_FILE" "$@"'
|
|
in wrapper
|
|
)
|
|
assert 'exec "${SNAP}/bin/openshell-gateway" "$@"' in wrapper
|
|
|
|
|
|
def test_rpm_spec_seeds_and_migrates_gateway_defaults() -> None:
|
|
repo_root = Path(__file__).resolve().parents[2]
|
|
spec = (repo_root / "openshell.spec").read_text(encoding="utf-8")
|
|
|
|
assert "init-gateway-config.sh" not in spec
|
|
assert "init-pki.sh" not in spec
|
|
assert "migrate-gateway-config.sh" in spec
|
|
assert "gateway.toml.default.v1" in spec
|
|
assert "%{name}-gateway-migrate-config" in spec
|
|
assert "ExecStartPre=/usr/bin/openshell-gateway config preflight" in spec
|
|
assert "Environment=OPENSHELL_LOCAL_TLS_DIR=%%h/.local/state/openshell/tls" in spec
|
|
assert (
|
|
"openshell-gateway generate-certs --output-dir ${OPENSHELL_LOCAL_TLS_DIR}"
|
|
in spec
|
|
)
|
|
assert "EnvironmentFile=-%%E/openshell/gateway.env" in spec
|
|
assert "%%S/openshell/tls" not in spec
|
|
assert "Environment=OPENSHELL_COMPUTE_DRIVER" not in spec
|
|
assert "Environment=OPENSHELL_BIND_ADDRESS" not in spec
|
|
assert "Environment=OPENSHELL_PODMAN_TLS_CA" not in spec
|
|
assert "ExecStart=/usr/bin/openshell-gateway" in spec
|
|
assert "--config" not in spec
|
|
assert "--db-url" not in spec
|
|
|
|
|
|
def test_deb_user_service_uses_gateway_defaults_without_config_helper() -> None:
|
|
repo_root = Path(__file__).resolve().parents[2]
|
|
unit = (repo_root / "deploy/deb/openshell-gateway.service").read_text(
|
|
encoding="utf-8"
|
|
)
|
|
|
|
assert "EnvironmentFile=-%E/openshell/gateway.env" in unit
|
|
assert "Environment=OPENSHELL_LOCAL_TLS_DIR=%h/.local/state/openshell/tls" in unit
|
|
assert (
|
|
"openshell-gateway generate-certs --output-dir ${OPENSHELL_LOCAL_TLS_DIR}"
|
|
in unit
|
|
)
|
|
assert "%S/openshell/tls" not in unit
|
|
assert "init-gateway-config.sh" not in unit
|
|
assert "ExecStart=/usr/bin/openshell-gateway" in unit
|
|
assert "--config" not in unit
|
|
assert "--db-url" not in unit
|
|
|
|
|
|
def test_rpm_exec_start_pre_argument_and_execution_order() -> None:
|
|
repo_root = Path(__file__).resolve().parents[2]
|
|
spec = (repo_root / "openshell.spec").read_text(encoding="utf-8")
|
|
|
|
migration = (
|
|
"ExecStartPre=%{_libexecdir}/%{name}-gateway-migrate-config "
|
|
"%%E/openshell/gateway.toml "
|
|
"/usr/share/openshell-gateway/gateway.toml.default "
|
|
"/usr/share/openshell-gateway/gateway.toml.default.v1"
|
|
)
|
|
preflight = "ExecStartPre=/usr/bin/openshell-gateway config preflight"
|
|
certs = "ExecStartPre=/usr/bin/openshell-gateway generate-certs"
|
|
|
|
assert migration in spec
|
|
assert preflight in spec
|
|
assert certs in spec
|
|
assert spec.index(migration) < spec.index(preflight) < spec.index(certs)
|
|
|
|
|
|
def test_schema_v2_debian_and_snap_preflight_wiring() -> None:
|
|
repo_root = Path(__file__).resolve().parents[2]
|
|
unit = (repo_root / "deploy/deb/openshell-gateway.service").read_text(
|
|
encoding="utf-8"
|
|
)
|
|
wrapper = (repo_root / "tasks/scripts/snap-gateway-wrapper.sh").read_text(
|
|
encoding="utf-8"
|
|
)
|
|
package_deb = (repo_root / "tasks/scripts/package-deb.sh").read_text(
|
|
encoding="utf-8"
|
|
)
|
|
preflight = "ExecStartPre=/usr/bin/openshell-gateway config preflight"
|
|
certs = "ExecStartPre=/usr/bin/openshell-gateway generate-certs"
|
|
assert preflight in unit
|
|
assert unit.index(preflight) < unit.index(certs)
|
|
assert "EnvironmentFile=-%E/openshell/gateway.env" in unit
|
|
assert "ExecStart=/usr/bin/openshell-gateway" in unit
|
|
assert "$src_dir/openshell-gateway.service" in package_deb
|
|
assert "$pkgroot/usr/lib/systemd/user/openshell-gateway.service" in package_deb
|
|
assert 'if [ -n "${OPENSHELL_GATEWAY_CONFIG:-}" ]; then' in wrapper
|
|
assert (
|
|
'elif [ -e "$CANONICAL_CONFIG_FILE" ] || [ -L "$CANONICAL_CONFIG_FILE" ]; then'
|
|
in wrapper
|
|
)
|
|
assert wrapper.count('"${SNAP}/bin/openshell-gateway" config preflight') == 4
|
|
assert 'config preflight -- "$@"' in wrapper
|
|
assert 'config preflight -- --config "$CANONICAL_CONFIG_FILE" "$@"' in wrapper
|
|
assert (
|
|
'exec "${SNAP}/bin/openshell-gateway" --config "$CANONICAL_CONFIG_FILE" "$@"'
|
|
in wrapper
|
|
)
|
|
assert wrapper.count('exec "${SNAP}/bin/openshell-gateway" "$@"') == 3
|
|
assert '[ -f "$CANONICAL_CONFIG_FILE" ]' not in wrapper
|