Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: get ubuntu pro info on snaps #257

Merged
merged 1 commit into from
Aug 7, 2024
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
6 changes: 6 additions & 0 deletions landscape/client/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,9 @@
DEFAULT_CONFIG = (
"/etc/landscape-client.conf" if IS_SNAP else "/etc/landscape/client.conf"
)

UA_DATA_DIR = (
"/var/lib/snapd/hostfs/var/lib/ubuntu-advantage"
if IS_SNAP
else "/var/lib/ubuntu-advantage"
)
43 changes: 36 additions & 7 deletions landscape/client/manager/tests/test_ubuntuproinfo.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import json
import os
import tempfile
from datetime import datetime
from unittest import mock

Expand Down Expand Up @@ -161,14 +164,40 @@ def test_persistence_reset(self):
self.assertTrue("ubuntu-pro-info" in messages[1])
self.assertEqual(messages[1]["ubuntu-pro-info"], data)

@mock.patch("landscape.client.manager.ubuntuproinfo.IS_SNAP", new=True)
def test_pro_client_not_called_for_snap(self):
"""
The snap will not currently allow calls to the pro client.
@mock.patch.multiple(
"landscape.client.manager.ubuntuproinfo",
IS_SNAP=True,
UA_DATA_DIR=tempfile.gettempdir(),
)
def test_pro_status_file_read_for_snap(self):
"""The snap should read the status file instead of calling `pro`."""
temp_file_path = os.path.join(tempfile.gettempdir(), "status.json")
with open(temp_file_path, "w") as fp:
mocked_info = {
"_schema_version": "0.1",
"account": {
"created_at": "2024-01-08T13:26:52+00:00",
"external_account_ids": [],
"id": "zYxWvU_sRqPoNmLkJiHgFeDcBa9876543210ZyXwVuTsRqPon",
"name": "[email protected]",
},
"foo": "bar"
}
json.dump(mocked_info, fp)

Ensure that get_ubuntu_pro_info returns an empty dictionary instead of
calling the subprocess for pro.
"""
ubuntu_pro_info = get_ubuntu_pro_info()
del mocked_info["foo"]
self.assertEqual(mocked_info, ubuntu_pro_info)

os.remove(temp_file_path)

@mock.patch.multiple(
"landscape.client.manager.ubuntuproinfo",
IS_SNAP=True,
UA_DATA_DIR="/i/do/not/exist",
)
def test_pro_status_file_not_found_for_snap(self):
"""The snap will return {} if the status file is not found."""
ubuntu_pro_info = get_ubuntu_pro_info()
self.assertEqual({}, ubuntu_pro_info)

Expand Down
45 changes: 42 additions & 3 deletions landscape/client/manager/ubuntuproinfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

from landscape.client import IS_CORE
from landscape.client import IS_SNAP
from landscape.client import UA_DATA_DIR
from landscape.client.manager.plugin import ManagerPlugin
from landscape.lib.persist import Persist

Expand Down Expand Up @@ -80,9 +81,47 @@ def get_ubuntu_pro_info() -> dict:
)

if IS_SNAP:
# Snap does not support Ubuntu Pro Info and throws an error if `pro` is
# called.
return {}
# By default, Ubuntu Advantage / Pro stores the status information
# in /var/lib/ubuntu-advantage/status.json (we have a `system-files`
# plug for this).
# This `data_dir` can however be changed in
# /etc/ubuntu-advantage/uaclient.conf which would lead to
# permission errors since we don't have a plug for arbitrary
# folders on the host fs.

try:
with open(f"{UA_DATA_DIR}/status.json") as fp:
pro_info = json.load(fp)
except (FileNotFoundError, PermissionError):
# Happens if the Ubuntu Pro client isn't installed, or
# if the `data_dir` folder setting was changed from the default
return {}

# The status file has more information than `pro status`
keys_to_keep = [
"_doc",
"_schema_version",
"account",
"attached",
"config",
"config_path",
"contract",
"effective",
"environment_vars",
"errors",
"execution_details",
"execution_status",
"expires",
"features",
"machine_id",
"notices",
"result",
"services",
"simulated",
"version",
"warnings",
]
return {k: pro_info[k] for k in keys_to_keep if k in pro_info}

try:
completed_process = subprocess.run(
Expand Down
7 changes: 7 additions & 0 deletions snap/snapcraft.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ environment:
LANDSCAPE_CLIENT_SNAP: 1
PYTHONPATH: $SNAP/usr/lib/python3/dist-packages:$SNAP/usr/lib/python3.10/site-packages:$PYTHONPATH

plugs:
var-lib-ubuntu-advantage-status:
interface: system-files
read:
- /var/lib/snapd/hostfs/var/lib/ubuntu-advantage/status.json

apps:
landscape-client:
daemon: simple
Expand All @@ -42,6 +48,7 @@ apps:
- process-control
- network-control
- network-manager
- var-lib-ubuntu-advantage-status
config:
command: usr/bin/landscape-config
plugs: [network]
Expand Down
Loading