Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion lib/charms/operator_libs_linux/v2/snap.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@

# Increment this PATCH version before using `charmcraft publish-lib` or reset
# to 0 if you are raising the major API version
LIBPATCH = 13
LIBPATCH = 14

PYDEPS = ["opentelemetry-api"]

Expand Down Expand Up @@ -149,6 +149,7 @@ class _SnapDict(TypedDict, total=True):
name: str
channel: str
revision: str
version: str
confinement: str
apps: NotRequired[list[dict[str, JSONType]] | None]

Expand Down Expand Up @@ -309,6 +310,7 @@ class Snap:
- channel: "stable", "candidate", "beta", and "edge" are common
- revision: a string representing the snap's revision
- confinement: "classic", "strict", or "devmode"
- version: a string representing the snap's version, if set by the snap author
"""

def __init__(
Expand All @@ -320,6 +322,8 @@ def __init__(
confinement: str,
apps: list[dict[str, JSONType]] | None = None,
cohort: str | None = None,
*,
version: str | None = None,
) -> None:
self._name = name
self._state = state
Expand All @@ -328,6 +332,7 @@ def __init__(
self._confinement = confinement
self._cohort = cohort or ""
self._apps = apps or []
self._version = version
self._snap_client = SnapClient()

def __eq__(self, other: object) -> bool:
Expand Down Expand Up @@ -783,6 +788,11 @@ def held(self) -> bool:
info = self._snap("info")
return "hold:" in info

@property
def version(self) -> str | None:
"""Returns the version for a snap."""
return self._version


class _UnixSocketConnection(http.client.HTTPConnection):
"""Implementation of HTTPConnection that connects to a named Unix socket."""
Expand Down Expand Up @@ -1048,6 +1058,7 @@ def _load_installed_snaps(self) -> None:
revision=i["revision"],
confinement=i["confinement"],
apps=i.get("apps"),
version=i.get("version"),
)
self._snap_map[snap.name] = snap

Expand All @@ -1067,6 +1078,7 @@ def _load_info(self, name: str) -> Snap:
revision=info["revision"],
confinement=info["confinement"],
apps=None,
version=info.get("version"),
)


Expand Down
13 changes: 9 additions & 4 deletions tests/integration/test_snap.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,12 +204,14 @@ def test_snap_ensure_revision():
["snap", "info", "juju"], capture_output=True, encoding="utf-8"
).stdout.split("\n")

edge_version = None
edge_revision = None
for line in snap_info_juju:
match = re.search(r"3/stable.*\((\d+)\)", line)
match = re.search(r"3/stable:\s+([^\s]+).+\((\d+)\)", line)

if match:
edge_revision = match.group(1)
edge_version = match.group(1)
edge_revision = match.group(2)
break
assert edge_revision is not None

Expand All @@ -226,10 +228,13 @@ def test_snap_ensure_revision():
assert "installed" in snap_info_juju
for line in snap_info_juju.split("\n"):
if "installed" in line:
match = re.search(r"installed.*\((\d+)\)", line)
match = re.search(r"installed:\s+([^\s]+).+\((\d+)\)", line)

assert match is not None
assert match.group(1) == edge_revision
assert match.group(1) == edge_version
assert match.group(2) == edge_revision

assert juju.version == edge_version


def test_snap_start():
Expand Down
8 changes: 7 additions & 1 deletion tests/unit/test_snap.py
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,7 @@ def test_can_lazy_load_snap_info(self, mock_exists, m):
self.assertEqual(result.channel, "stable")
self.assertEqual(result.confinement, "strict")
self.assertEqual(result.revision, "233")
self.assertEqual(result.version, "7.78.0")

@patch("os.path.isfile")
def test_can_load_installed_snap_info(self, mock_exists):
Expand Down Expand Up @@ -326,10 +327,15 @@ def test_raises_error_if_snap_not_running(self, mock_exists):
self.assertIn("snapd is not running", ctx.exception.message)

def test_can_compare_snap_equality(self):
foo1 = snap.Snap("foo", snap.SnapState.Present, "stable", "1", "classic")
foo1 = snap.Snap("foo", snap.SnapState.Present, "stable", "1", "classic", version="v42")
foo2 = snap.Snap("foo", snap.SnapState.Present, "stable", "1", "classic")
self.assertEqual(foo1, foo2)

def test_can_compare_snap_inequality(self):
foo1 = snap.Snap("foo", snap.SnapState.Present, "stable", "1", "classic", version="v42")
foo2 = snap.Snap("foo", snap.SnapState.Present, "stable", "2", "classic", version="v42")
self.assertNotEqual(foo1, foo2)

def test_snap_magic_methods(self):
foo = snap.Snap("foo", snap.SnapState.Present, "stable", "1", "classic")
self.assertEqual(hash(foo), hash((foo._name, foo._revision)))
Expand Down