Skip to content

Add more distro_name() & distro_family() functionality #6

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
7 changes: 7 additions & 0 deletions patchwork/files.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,10 @@ def _escape_for_regex(text):
# Whereas single quotes should not be escaped
regex = regex.replace(r"\'", "'")
return regex

def cat(path, runner=run):
"""
Cat a file, returning its contents as a string.
"""
with hide('stdout'):
return runner('cat "%(path)s"' % locals()).stdout
79 changes: 76 additions & 3 deletions patchwork/info.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,45 @@
from collections import namedtuple

from fabric.api import run
from environment import has_binary
from fabric.contrib.files import exists
from files import cat


REDHAT_FAMILY_DISTROS = "rhel fedora centos amazon".split()
DEBIAN_FAMILY_DISTROS = "ubuntu debian".split()

# Holder struct for lsb_release info.
lsb_release_info=namedtuple("lsb_release_info",
["lsb_version",
"distributor",
"description",
"release_version",
"codename"])

def lsb_release():
"""
Get Linux Standard Base distro information, if available.
http://refspecs.linuxbase.org/LSB_3.1.1/LSB-Core-generic/LSB-Core-generic/lsbrelease.html

Returns None if unavailable, or else a lsb_release_info object with the
following string attributes:
lsb_version
distributor
description
release_version
codename
"""
if not has_binary('lsb_release'):
return None
def lsb_attr(flagname):
return run('lsb_release --short --%s' % flagname).stdout
result = lsb_release_info(lsb_attr('version'),
lsb_attr('id'),
lsb_attr('description'),
lsb_attr('release'),
lsb_attr('codename'))
return result

def distro_name():
"""
Expand All @@ -11,10 +51,23 @@ def distro_name():
* ``fedora``
* ``rhel``
* ``centos``
* ``amazon``
* ``ubuntu``
* ``debian``
* ``other``
"""
lsb_info = lsb_release()
if lsb_info:
try:
distributor_names = {
'Ubuntu': 'ubuntu',
'Debian': 'debian',
'AmazonAMI': 'amazon',
}
return distributor_names[lsb_info.distributor]
except KeyError:
pass

sentinel_files = {
'fedora': ('fedora-release',),
'centos': ('centos-release',),
Expand All @@ -23,6 +76,20 @@ def distro_name():
for sentinel in sentinels:
if exists('/etc/%s' % sentinel):
return name

# Redhat-like distros erratically include /etc/redhat-release
# instead of distro-specific files.
redhat_release_file = '/etc/redhat-release'
if exists(redhat_release_file):
redhat_release_content = cat(redhat_release_file)
for distro in REDHAT_FAMILY_DISTROS:
if distro in redhat_release_content.lower():
return distro

system_release_file = '/etc/system-release'
if exists(system_release_file) and 'Amazon Linux' in cat(system_release_file):
return 'amazon'

return "other"


Expand All @@ -35,15 +102,21 @@ def distro_family():
* ``debian``
* ``redhat``

If the system falls outside these categories, its specific family or
release name will be returned instead.
If the system falls outside these categories, its discovered distro_name
will be returned instead (possibly ``other`` if its type couldn't be
determined).
"""
families = {
'debian': "debian ubuntu".split(),
'redhat': "rhel centos fedora".split()
'redhat': "rhel centos fedora amazon".split()
}
distro = distro_name()
for family, members in families.iteritems():
if distro in members:
return family
# Even if we haven't been able to determine exact distro,
# we may be able to determine the overall family.
if distro == 'other':
if exists('/etc/debian_version'):
return 'debian'
return distro
63 changes: 63 additions & 0 deletions test/test_distro.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
from patchwork import info
import unittest
from unittest import TestCase

import mock
from patchwork.info import lsb_release_info


ubuntu1104_lsb_info = lsb_release_info('No LSB modules are available.',
'Ubuntu',
'Ubuntu 11.04',
'11.04',
'natty')
amazon2012_03_lsb_info = lsb_release_info(':core-4.0-amd64:core-4.0-noarch:printing-4.0-amd64:printing-4.0-noarch',
'AmazonAMI',
'Amazon Linux AMI release 2012.03',
'2012.03',
'n/a')

class DistroNameDetection(TestCase):
@mock.patch('patchwork.info.lsb_release')
@mock.patch('patchwork.info.run')
def test_ubuntu_detection_via_lsb_release(self, run, lsb_release):
lsb_release.return_value = ubuntu1104_lsb_info
self.assertEqual(info.distro_name(), 'ubuntu')
lsb_release.assert_called()
self.assertFalse(run.called)

@mock.patch('patchwork.info.lsb_release')
@mock.patch('patchwork.info.run')
def test_amazon_detection_via_lsb_release(self, run, lsb_release):
lsb_release.return_value = amazon2012_03_lsb_info
self.assertEqual(info.distro_name(), 'amazon')
lsb_release.assert_called()
self.assertFalse(run.called)

class DistroFamilyDetection(TestCase):
@mock.patch('patchwork.info.distro_name')
@mock.patch('patchwork.info.run')
def test_debian_family(self, run, distro_name_fxn):
for d in ('ubuntu', 'debian'):
distro_name_fxn.return_value = d
self.assertEqual('debian', info.distro_family())
self.assertFalse(run.called)

@mock.patch('patchwork.info.distro_name')
@mock.patch('patchwork.info.run')
def test_redhat_family(self, run, distro_name_fxn):
for d in ('redhat', 'centos', 'fedora', 'amazon'):
distro_name_fxn.return_value = d
self.assertEqual('redhat', info.distro_family())
self.assertFalse(run.called)

@mock.patch('patchwork.info.exists')
@mock.patch('patchwork.info.distro_name')
@mock.patch('patchwork.info.run')
def test_family_inference(self, run, distro_name_fxn, file_exists):
"""If debian_version exists, then it should be picked up as debian-family,
even if exact type couldn't be worked out."""
distro_name_fxn.return_value = 'other'
file_exists.side_effect = lambda s: s == '/etc/debian_version'
self.assertEqual('debian', info.distro_family())