Skip to content

Commit

Permalink
fix(distro): use distro package for getting linux distribution info (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
lengau authored Sep 29, 2023
1 parent f60186c commit 8b79757
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 115 deletions.
24 changes: 7 additions & 17 deletions charmcraft/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
Union,
)

import distro
import yaml
from craft_cli import CraftError, emit
from jinja2 import Environment, FileSystemLoader, PackageLoader, StrictUndefined
Expand Down Expand Up @@ -267,23 +268,12 @@ def get_os_platform(filepath=pathlib.Path("/etc/os-release")):
machine = platform.machine()

if system == "Linux":
try:
with filepath.open("rt", encoding="utf-8") as fh:
lines = fh.readlines()
except FileNotFoundError:
emit.debug("Unable to locate 'os-release' file, using default values")
else:
os_release = {}
for line in lines:
line = line.strip()
if not line or line.startswith("#") or "=" not in line:
continue
key, value = line.rstrip().split("=", 1)
if value[0] == value[-1] and value[0] in ('"', "'"):
value = value[1:-1]
os_release[key] = value
system = os_release.get("ID", system)
release = os_release.get("VERSION_ID", release)
info = distro.info()
system = info.get("id", system)
# Treat Ubuntu derivatives as Ubuntu, as they should be compatible.
if system != "ubuntu" and "ubuntu" in info.get("like", "").split():
system = "ubuntu"
release = info.get("version", release)

return OSPlatform(system=system, release=release, machine=machine)

Expand Down
1 change: 1 addition & 0 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ craft-store==2.4.0
cryptography==41.0.4
Deprecated==1.2.14
distlib==0.3.7
distro==1.8.0
exceptiongroup==1.1.3
filelock==3.12.4
flake8==6.1.0
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ craft-providers==1.18.0
craft-store==2.4.0
cryptography==41.0.4
Deprecated==1.2.14
distro==1.8.0
humanize==4.8.0
idna==3.4
importlib-metadata==6.8.0
Expand Down
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"craft-parts>=1.18",
"craft-providers",
"craft-store>=2.4",
"distro>=1.3.0",
"humanize>=2.6.0",
"jsonschema",
"jinja2",
Expand Down
188 changes: 90 additions & 98 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@
from unittest.mock import call, patch

import dateutil.parser
import distro
import pytest
import yaml
from craft_cli import CraftError

from charmcraft.errors import DuplicateCharmsError, InvalidCharmPathError
from charmcraft.utils import (
OSPlatform,
ResourceOption,
SingleOptionEnsurer,
build_zip,
Expand Down Expand Up @@ -244,111 +246,101 @@ def test_usefulfilepath_not_a_file(tmp_path):
# -- tests for the OS platform getter


def test_get_os_platform_linux(tmp_path):
"""Utilize an /etc/os-release file to determine platform."""
# explicitly add commented and empty lines, for parser robustness
filepath = tmp_path / "os-release"
filepath.write_text(
dedent(
"""
# the following is an empty line
NAME="Ubuntu"
VERSION="20.04.1 LTS (Focal Fossa)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 20.04.1 LTS"
VERSION_ID="20.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
# more in the middle; the following even would be "out of standard", but
# we should not crash, just ignore it
SOMETHING-WEIRD
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=focal
UBUNTU_CODENAME=focal
"""
)
)
with patch("platform.machine", return_value="x86_64"):
with patch("platform.system", return_value="Linux"):
os_platform = get_os_platform(filepath)
assert os_platform.system == "ubuntu"
assert os_platform.release == "20.04"
assert os_platform.machine == "x86_64"


def test_get_os_platform_strict_snaps(tmp_path):
"""Utilize an /etc/os-release file to determine platform, core-20 values."""
# explicitly add commented and empty lines, for parser robustness
filepath = tmp_path / "os-release"
filepath.write_text(
dedent(
"""
NAME="Ubuntu Core"
VERSION="20"
ID=ubuntu-core
PRETTY_NAME="Ubuntu Core 20"
VERSION_ID="20"
HOME_URL="https://snapcraft.io/"
BUG_REPORT_URL="https://bugs.launchpad.net/snappy/"
"""
)
)
with patch("platform.machine", return_value="x86_64"):
with patch("platform.system", return_value="Linux"):
os_platform = get_os_platform(filepath)
assert os_platform.system == "ubuntu-core"
assert os_platform.release == "20"
assert os_platform.machine == "x86_64"


@pytest.mark.parametrize(
"name",
("os_release", "expected_system", "expected_release"),
[
('"foo bar"', "foo bar"), # what's normally found
("foo bar", "foo bar"), # no quotes
('"foo " bar"', 'foo " bar'), # quotes in the middle
('foo bar"', 'foo bar"'), # unbalanced quotes (no really enclosing)
('"foo bar', '"foo bar'), # unbalanced quotes (no really enclosing)
("'foo bar'", "foo bar"), # enclosing with single quote
("'foo ' bar'", "foo ' bar"), # single quote in the middle
("foo bar'", "foo bar'"), # unbalanced single quotes (no really enclosing)
("'foo bar", "'foo bar"), # unbalanced single quotes (no really enclosing)
("'foo bar\"", "'foo bar\""), # unbalanced mixed quotes
("\"foo bar'", "\"foo bar'"), # unbalanced mixed quotes
pytest.param(
dedent(
"""
# the following is an empty line
NAME="Ubuntu"
VERSION="20.04.1 LTS (Focal Fossa)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 20.04.1 LTS"
VERSION_ID="20.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
# more in the middle; the following even would be "out of standard", but
# we should not crash, just ignore it
SOMETHING-WEIRD
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=focal
UBUNTU_CODENAME=focal
"""
),
"ubuntu",
"20.04",
id="ubuntu-focal",
),
pytest.param(
dedent(
"""
NAME="Ubuntu Core"
VERSION="20"
ID=ubuntu-core
PRETTY_NAME="Ubuntu Core 20"
VERSION_ID="20"
HOME_URL="https://snapcraft.io/"
BUG_REPORT_URL="https://bugs.launchpad.net/snappy/"
"""
),
"ubuntu-core",
"20",
id="ubuntu-core-20",
),
pytest.param(
dedent(
"""
PRETTY_NAME="KDE neon 5.27"
NAME="KDE neon"
VERSION_ID="22.04"
VERSION="5.27"
VERSION_CODENAME=jammy
ID=neon
ID_LIKE="ubuntu debian"
HOME_URL="https://neon.kde.org/"
SUPPORT_URL="https://neon.kde.org/"
BUG_REPORT_URL="https://bugs.kde.org/"
PRIVACY_POLICY_URL="https://kde.org/privacypolicy/"
UBUNTU_CODENAME=jammy
LOGO=start-here-kde-neon
"""
),
"ubuntu",
"22.04",
id="kde-neon-like-ubuntu",
),
],
)
def test_get_os_platform_alternative_formats(name, tmp_path):
"""Support different ways of building the string."""
source, result = name
@pytest.mark.parametrize("machine", ["x86_64", "riscv64", "arm64"])
def test_get_os_platform_linux(tmp_path, os_release, expected_system, expected_release, machine):
"""Utilize an /etc/os-release file to determine platform."""
filepath = tmp_path / "os-release"
filepath.write_text(
dedent(
f"""
ID={source}
VERSION_ID="20.04"
"""
)
)
# need to patch this to "Linux" so actually uses /etc/os-release...
with patch("platform.system", return_value="Linux"):
os_platform = get_os_platform(filepath)
assert os_platform.system == result


def test_get_os_platform_windows():
filepath.write_text(os_release)
with patch("distro.distro._distro", distro.LinuxDistribution(os_release_file=filepath)):
with patch("platform.machine", return_value=machine):
with patch("platform.system", return_value="Linux"):
os_platform = get_os_platform(filepath)
assert os_platform.system == expected_system
assert os_platform.release == expected_release
assert os_platform.machine == machine


@pytest.mark.parametrize("system", ["Windows", "Darwin", "Java", ""])
@pytest.mark.parametrize("release", ["NT", "Sparkling", "Jaguar", "0.0", ""])
@pytest.mark.parametrize("machine", ["AMD64", "x86_86", "arm64", "riscv64"])
def test_get_os_platform_non_linux(system, release, machine):
"""Get platform from a patched Windows machine."""
with patch("platform.system", return_value="Windows"):
with patch("platform.release", return_value="10"):
with patch("platform.machine", return_value="AMD64"):
with patch("platform.system", return_value=system):
with patch("platform.release", return_value=release):
with patch("platform.machine", return_value=machine):
os_platform = get_os_platform()
assert os_platform.system == "Windows"
assert os_platform.release == "10"
assert os_platform.machine == "AMD64"
assert os_platform == OSPlatform(system, release, machine)


@pytest.mark.parametrize(
Expand Down

0 comments on commit 8b79757

Please sign in to comment.