Skip to content

Commit

Permalink
feat(snaps): inject snaps from host if try to install same channel
Browse files Browse the repository at this point in the history
  • Loading branch information
syu-w committed Nov 7, 2023
1 parent b885aea commit f94ca4b
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 3 deletions.
70 changes: 68 additions & 2 deletions craft_providers/actions/snap_installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
from craft_providers.const import TIMEOUT_COMPLEX, TIMEOUT_SIMPLE
from craft_providers.errors import (
BaseConfigurationError,
IncompatibleSnapError,
ProviderError,
details_from_called_process_error,
)
Expand Down Expand Up @@ -340,7 +341,13 @@ def inject_from_host(*, executor: Executor, snap_name: str, classic: bool) -> No
classic,
)

host_revision = get_host_snap_info(snap_name)["revision"]
host_snap_info = get_host_snap_info(snap_name)
host_snap_base = host_snap_info.get("base", None)
if host_snap_base:
logger.debug("Installing base snap %r from host", host_snap_base)
inject_from_host(executor=executor, snap_name=host_snap_base, classic=False)

host_revision = host_snap_info["revision"]
target_revision = _get_snap_revision_ensuring_source(
snap_name=snap_store_name,
source=SNAP_SRC_HOST,
Expand Down Expand Up @@ -398,8 +405,41 @@ def inject_from_host(*, executor: Executor, snap_name: str, classic: bool) -> No
)


def _try_install_from_host(
*,
executor: Executor,
snap_name: str,
channel: str,
classic: bool,
) -> None:
"""Try to install snap from host if has same channel."""
try:
host_snap_info = get_host_snap_info(snap_name)
except requests.exceptions.HTTPError as error:
logger.debug(f"Host snap {snap_name} not exists, skipping")
raise IncompatibleSnapError(f"Host snap {snap_name} not exists") from error

if host_snap_info.get("channel", None) != channel:
logger.debug(f"Host snap {snap_name} has different channel, skipping")
raise IncompatibleSnapError(f"Host snap {snap_name} has different channel")

# find and inject base snap if exists
host_snap_base = host_snap_info.get("base", None)
if host_snap_base:
host_snap_base_info = get_host_snap_info(host_snap_base)
if host_snap_base_info.get("name", None):
inject_from_host(executor=executor, snap_name=host_snap_base, classic=False)

inject_from_host(executor=executor, snap_name=snap_name, classic=classic)


def install_from_store(
*, executor: Executor, snap_name: str, channel: str, classic: bool
*,
executor: Executor,
snap_name: str,
channel: str,
classic: bool,
try_local: bool = True,
) -> None:
"""Install snap from store into target.
Expand All @@ -409,11 +449,37 @@ def install_from_store(
:param snap_name: Name of snap to install.
:param channel: Channel to install from.
:param classic: Install in classic mode.
:param try_local: Try to install from local snap if available.
:raises SnapInstallationError: on unexpected error.
"""
# trim the `_name` suffix, if present
snap_store_name = snap_name.split("_")[0]

if try_local:
logger.debug(
"Checking snap %r from host snapd (channel=%r, classic=%s).",
snap_name,
channel,
classic,
)

try:
_try_install_from_host(
executor=executor, snap_name=snap_name, channel=channel, classic=classic
)
except IncompatibleSnapError as error:
logger.debug(
"Failed to find snap %r from host snapd (channel=%r, classic=%s).",
snap_name,
channel,
classic,
)
logger.debug("Error: %s", error)
else:
logger.debug("Snap %r installed from host snapd.", snap_name)
return

if snap_name == snap_store_name:
logger.debug(
"Installing snap %r from store (channel=%r, classic=%s).",
Expand Down
5 changes: 5 additions & 0 deletions craft_providers/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,3 +130,8 @@ def __init__(self) -> None:
"see https://craft-providers.readthedocs.io/ for further reference."
)
super().__init__(brief=brief, resolution=resolution)


@dataclasses.dataclass
class IncompatibleSnapError(ProviderError, RuntimeError):
"""Host snap is incompatible with the target."""
4 changes: 4 additions & 0 deletions tests/integration/actions/test_snap_installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ def test_install_from_store_strict(core22_lxd_instance, installed_snap, caplog):
snap_name="hello-world",
channel="latest/stable",
classic=False,
try_local=False,
)

core22_lxd_instance.execute_run(["test", "-f", "/snap/bin/hello-world"], check=True)
Expand Down Expand Up @@ -177,6 +178,7 @@ def test_install_from_store_classic(core22_lxd_instance, installed_snap, caplog)
snap_name="charmcraft",
channel="latest/stable",
classic=True,
try_local=False,
)

core22_lxd_instance.execute_run(["test", "-f", "/snap/bin/charmcraft"], check=True)
Expand Down Expand Up @@ -216,6 +218,7 @@ def test_install_from_store_channel(core22_lxd_instance, installed_snap, caplog)
snap_name="go",
channel="1.15/stable",
classic=True,
try_local=False,
)

proc = core22_lxd_instance.execute_run(
Expand Down Expand Up @@ -263,6 +266,7 @@ def test_install_from_store_snap_name_suffix(
snap_name="hello-world_suffix",
channel="latest/stable",
classic=False,
try_local=False,
)

core22_lxd_instance.execute_run(["test", "-f", "/snap/bin/hello-world"], check=True)
Expand Down
10 changes: 9 additions & 1 deletion tests/unit/actions/test_snap_installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,7 @@ def test_install_from_store_strict(
snap_name="test-name_suffix",
classic=False,
channel="test-chan",
try_local=False,
)

assert len(fake_process.calls) == 1
Expand Down Expand Up @@ -652,7 +653,11 @@ def test_install_from_store_classic(
)

snap_installer.install_from_store(
executor=fake_executor, snap_name="test-name", classic=True, channel="test-chan"
executor=fake_executor,
snap_name="test-name",
classic=True,
channel="test-chan",
try_local=False,
)

assert len(fake_process.calls) == 1
Expand Down Expand Up @@ -691,6 +696,7 @@ def test_refresh_from_store(
snap_name="test-name",
classic=False,
channel="test-chan",
try_local=False,
)

assert len(fake_process.calls) == 1
Expand Down Expand Up @@ -741,6 +747,7 @@ def test_install_from_store_failure(
snap_name="test-name",
classic=True,
channel="test-chan",
try_local=False,
)

assert exc_info.value == snap_installer.SnapInstallationError(
Expand Down Expand Up @@ -779,6 +786,7 @@ def test_install_from_store_trim_suffix(
snap_name="test-name",
classic=False,
channel="test-chan",
try_local=False,
)

assert len(fake_process.calls) == 1
Expand Down

0 comments on commit f94ca4b

Please sign in to comment.