Skip to content

Commit

Permalink
feat(snaps): inject base snap from host (#444)
Browse files Browse the repository at this point in the history
  • Loading branch information
syu-w authored Nov 10, 2023
1 parent b885aea commit 03704c1
Show file tree
Hide file tree
Showing 2 changed files with 178 additions and 3 deletions.
10 changes: 9 additions & 1 deletion craft_providers/actions/snap_installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,15 @@ 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 for %r from host", host_snap_base, snap_name
)
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
171 changes: 169 additions & 2 deletions tests/unit/actions/test_snap_installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,100 @@ def test_inject_from_host_snap_name(
}


def test_inject_from_host_snap_name_with_base(
config_fixture,
mock_requests,
fake_executor,
fake_process,
logs,
tmp_path,
mocker,
):
"""Inject a snap and its base installed locally with the `--name` parameter."""
# register 'snap known' calls
fake_process.register_subprocess(
["snap", "known", fake_process.any()], occurrences=8
)
fake_process.register_subprocess(
["fake-executor", "snap", "ack", "/tmp/coreXX.assert"]
)
fake_process.register_subprocess(
["fake-executor", "snap", "ack", "/tmp/test-name.assert"]
)
fake_process.register_subprocess(
["fake-executor", "snap", "install", "/tmp/coreXX.snap"]
)
fake_process.register_subprocess(
["fake-executor", "snap", "install", "/tmp/test-name.snap"]
)

mocker.patch(
"craft_providers.actions.snap_installer.get_host_snap_info",
side_effect=[
{
"id": "",
"name": "test-name",
"type": "app",
"base": "coreXX",
"version": "0.1",
"channel": "",
"revision": "2",
"publisher": {"id": ""},
"confinement": "classic",
},
{
"id": "",
"name": "coreXX",
"type": "base",
"version": "0.1",
"channel": "latest/stable",
"revision": "1",
"publisher": {"id": ""},
"confinement": "strict",
},
{
"id": "",
"name": "coreXX",
"type": "base",
"version": "0.1",
"channel": "latest/stable",
"revision": "1",
"publisher": {"id": ""},
"confinement": "strict",
},
{
"id": "",
"name": "test-name",
"type": "app",
"base": "coreXX",
"version": "0.1",
"channel": "",
"revision": "2",
"publisher": {"id": ""},
"confinement": "classic",
},
],
)

snap_installer.inject_from_host(
executor=fake_executor, snap_name="test-name_suffix", classic=False
)

mock_requests.get.assert_called_with(
"http+unix://%2Frun%2Fsnapd.socket/v2/snaps/test-name_suffix/file"
)
assert len(fake_process.calls) == 12
assert (
"Installing base snap 'coreXX' for 'test-name_suffix' from host" in logs.debug
)
assert (
Exact(
"Installing snap 'test-name_suffix' from host as 'test-name' in instance (classic=False)."
)
in logs.debug
)


@pytest.mark.parametrize("mock_get_host_snap_info", [{"revision": "x3"}], indirect=True)
def test_inject_from_host_dangerous(
config_fixture,
Expand Down Expand Up @@ -432,10 +526,25 @@ def test_inject_from_host_no_snapd(mock_get_host_snap_info, fake_executor):
)


def test_inject_from_host_push_error(mock_requests, fake_executor):
def test_inject_from_host_push_error(mock_requests, fake_executor, mocker):
mock_executor = mock.Mock(spec=fake_executor, wraps=fake_executor)
mock_executor.push_file.side_effect = ProviderError(brief="foo")

mocker.patch(
"craft_providers.actions.snap_installer.get_host_snap_info",
side_effect=[
{
"id": "",
"name": "test-name",
"type": "app",
"version": "0.1",
"channel": "",
"revision": "x1",
"confinement": "classic",
},
],
)

with pytest.raises(snap_installer.SnapInstallationError) as exc_info:
snap_installer.inject_from_host(
executor=mock_executor, snap_name="test-name", classic=False
Expand All @@ -448,6 +557,47 @@ def test_inject_from_host_push_error(mock_requests, fake_executor):
assert exc_info.value.__cause__ is not None


def test_inject_from_host_with_base_push_error(mock_requests, fake_executor, mocker):
mock_executor = mock.Mock(spec=fake_executor, wraps=fake_executor)
mock_executor.push_file.side_effect = ProviderError(brief="foo")

mocker.patch(
"craft_providers.actions.snap_installer.get_host_snap_info",
side_effect=[
{
"id": "",
"name": "test-name",
"type": "app",
"base": "coreXX",
"version": "0.1",
"channel": "",
"revision": "x1",
"confinement": "classic",
},
{
"id": "",
"name": "coreXX",
"type": "base",
"version": "0.1",
"channel": "",
"revision": "x1",
"confinement": "strict",
},
],
)

with pytest.raises(snap_installer.SnapInstallationError) as exc_info:
snap_installer.inject_from_host(
executor=mock_executor, snap_name="test-name", classic=False
)

assert exc_info.value == snap_installer.SnapInstallationError(
brief="failed to copy snap file for snap 'coreXX'",
details="error copying snap file into target environment",
)
assert exc_info.value.__cause__ is not None


def test_inject_from_host_snapd_connection_error_using_pack_fallback(
mock_get_host_snap_info,
mock_requests,
Expand Down Expand Up @@ -545,7 +695,9 @@ def test_inject_from_host_snapd_http_error_using_pack_fallback(
assert len(fake_process.calls) == 7


def test_inject_from_host_install_failure(mock_requests, fake_executor, fake_process):
def test_inject_from_host_install_failure(
mock_requests, fake_executor, fake_process, mocker
):
fake_process.register_subprocess(
[
"fake-executor",
Expand All @@ -559,6 +711,21 @@ def test_inject_from_host_install_failure(mock_requests, fake_executor, fake_pro
returncode=1,
)

mocker.patch(
"craft_providers.actions.snap_installer.get_host_snap_info",
side_effect=[
{
"id": "",
"name": "test-name",
"type": "app",
"version": "0.1",
"channel": "",
"revision": "x1",
"confinement": "classic",
},
],
)

with pytest.raises(snap_installer.SnapInstallationError) as exc_info:
snap_installer.inject_from_host(
executor=fake_executor, snap_name="test-name", classic=False
Expand Down

0 comments on commit 03704c1

Please sign in to comment.