Merge ~tiago.pasqualini/layer-snap:snap_list into layer-snap:master

Proposed by Tiago Pasqualini da Silva
Status: Merged
Approved by: Tom Haddon
Approved revision: e5755c5a7bbdf73dd6b3c23b6074c77d90722599
Merged at revision: c37f9333bdf85563439244d1aed87dc733a8f085
Proposed branch: ~tiago.pasqualini/layer-snap:snap_list
Merge into: layer-snap:master
Diff against target: 71 lines (+32/-6)
1 file modified
lib/charms/layer/snap.py (+32/-6)
Reviewer Review Type Date Requested Status
Tom Haddon Approve
Canonical IS Reviewers Pending
layer-snap-charmers Pending
Review via email: mp+410304@code.launchpad.net

This proposal supersedes a proposal from 2021-07-21.

Commit message

Change 'snap info' to 'snap list'

Currently snap info is being used to retrieve the version and
tracking channel of the installed snaps, which will fail if the
system does not have access to the snapstore. This patch changes
the layer to use 'snap list', which is an offline command.

To post a comment you must log in.
Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote : Posted in a previous version of this proposal

This merge proposal is being monitored by mergebot. Change the status to Approved to merge.

Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote : Posted in a previous version of this proposal

Unable to determine commit message from repository - please click "Set commit message" and enter the commit message manually.

Revision history for this message
Tom Haddon (mthaddon) wrote : Posted in a previous version of this proposal

One comment inline about docstring.

The way this is set up currently we're going to have a different exception from previous. With the previous code we'd raise an IndexError if the snap existed but wasn't installed, or a subprocess.CalledProcessError if the snap didn't exist. This code will now raise a KeyError in both cases.

I don't think that's terrible, but I think it might be nice to raise a custom exception so that charm writers can have some stability there by inspecting that.

Revision history for this message
Tiago Pasqualini da Silva (tiago.pasqualini) wrote : Posted in a previous version of this proposal

Hi Tom,

Fixed the docstring and rebased on top of master.

Regarding the exception, the code already checks if the snap is installed and returns nothing in that case, so it is not expected to fail if used correctly.

Please let me know if changed are enough.

Thanks,
Tiago

Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote :

This merge proposal is being monitored by mergebot. Change the status to Approved to merge.

Revision history for this message
Tiago Pasqualini da Silva (tiago.pasqualini) wrote :

Added the same treatment from get_installed_channel on get_installed_version. Now the exception will be swallowed by the functions and no exceptions will be raised to the caller.

Revision history for this message
Tiago Pasqualini da Silva (tiago.pasqualini) wrote :

Also fixed small indexing issue, which was causing it to ignore the last line from snap list.

I'm currently testing this with the graylog charm and it is working.

Thanks,
Tiago

Revision history for this message
Tom Haddon (mthaddon) wrote :

Some docstrings now need updating based on changed behaviour.

Revision history for this message
Tom Haddon (mthaddon) wrote :

LGTM, thx

review: Approve
Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote :

Change successfully merged at revision c37f9333bdf85563439244d1aed87dc733a8f085

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/lib/charms/layer/snap.py b/lib/charms/layer/snap.py
2index 06cc4b1..ae9be45 100644
3--- a/lib/charms/layer/snap.py
4+++ b/lib/charms/layer/snap.py
5@@ -273,11 +273,30 @@ def get(snapname, key):
6 return subprocess.check_output(["snap", "get", snapname, key]).strip()
7
8
9+def _snap_list():
10+ """Constructs a dict with all installed snaps.
11+
12+ Queries all the snaps installed and returns a dict containing their
13+ versions and tracking channels, indexed by the snap name.
14+ """
15+ cmd = ["snap", "list"]
16+ out = subprocess.check_output(cmd).decode("utf-8", errors="replace").split()
17+ snaps = {}
18+ for i in range(6, len(out) - 5, 6): # Skip first six, which are the titles
19+ # Snap list has 6 columns:
20+ # name, version, revision, tracking channel, publisher and notes
21+ # We only care about name (0), version (1) and tracking channel (3)
22+ snaps[out[i]] = {
23+ 'version': out[i + 1],
24+ 'channel': out[i + 3],
25+ }
26+ return snaps
27+
28+
29 def get_installed_version(snapname):
30 """Gets the installed version of a snapname.
31- This function will fail if snapname is not an installed snap.
32+ This function will return nothing if snapname is not an installed snap.
33 """
34- cmd = ["snap", "info", snapname]
35 hookenv.log("Get installed key for snap {}".format(snapname))
36 if not reactive.is_flag_set(get_installed_flag(snapname)):
37 hookenv.log(
38@@ -285,14 +304,21 @@ def get_installed_version(snapname):
39 hookenv.WARNING,
40 )
41 return
42- return subprocess.check_output(cmd).decode("utf-8", errors="replace").partition("installed:")[-1].split()[0]
43+ try:
44+ return _snap_list()[snapname]['version']
45+ except Exception as e:
46+ # If it fails to get the version information(ex. installed via resource), return nothing.
47+ hookenv.log(
48+ "Cannot get snap version: {}".format(e),
49+ hookenv.WARNING,
50+ )
51+ return
52
53
54 def get_installed_channel(snapname):
55 """Gets the tracking (channel) of a snapname.
56- This function will fail if snapname is not an installed snap.
57+ This function will return nothing if snapname is not an installed snap.
58 """
59- cmd = ["snap", "info", snapname]
60 hookenv.log("Get channel for snap {}".format(snapname))
61 if not reactive.is_flag_set(get_installed_flag(snapname)):
62 hookenv.log(
63@@ -301,7 +327,7 @@ def get_installed_channel(snapname):
64 )
65 return
66 try:
67- return subprocess.check_output(cmd).decode("utf-8", errors="replace").partition("tracking:")[-1].split()[0]
68+ return _snap_list()[snapname]['channel']
69 except Exception as e:
70 # If it fails to get the channel information(ex. installed via resource), return nothing.
71 hookenv.log(

Subscribers

People subscribed via source and target branches