]> git.apps.os.sepia.ceph.com Git - teuthology.git/commitdiff
Introduces packaging.GitbuilderProject
authorAndrew Schoen <aschoen@redhat.com>
Wed, 22 Jul 2015 21:03:11 +0000 (16:03 -0500)
committerAndrew Schoen <aschoen@redhat.com>
Mon, 3 Aug 2015 20:01:48 +0000 (15:01 -0500)
This is a new class that can be used to interface with gitbuilder to get
information about a project.  Currently it's used primarily for version
checks and constructing the correct base url for a projects location on
gitbuilder.

Signed-off-by: Andrew Schoen <aschoen@redhat.com>
teuthology/packaging.py
teuthology/task/install.py
teuthology/test/task/test_install.py
teuthology/test/test_packaging.py

index 2b228225532b8b3cea3da68397156ce631fcbaad..d3ca69a02a8a05357572c9a0d3ad08f7171b0926 100644 (file)
@@ -1,6 +1,7 @@
 import logging
 import ast
 import re
+import requests
 
 from cStringIO import StringIO
 
@@ -26,6 +27,27 @@ _SERVICE_MAP = {
     'httpd': {'deb': 'apache2', 'rpm': 'httpd'}
 }
 
+DISTRO_CODENAME_MAP = {
+    "ubuntu": {
+        "14.04": "trusty",
+        "12.04": "precise",
+        "15.04": "vivid",
+    },
+    "debian": {
+        "7": "wheezy",
+    },
+}
+
+DEFAULT_OS_VERSION = dict(
+    ubuntu="14.04",
+    fedora="20",
+    centos="7.0",
+    opensuse="12.2",
+    sles="11-sp2",
+    rhel="7.0",
+    debian='7.0'
+)
+
 
 def get_package_name(pkg, rem):
     """
@@ -369,3 +391,243 @@ def get_package_version(remote, package):
         )
 
     return installed_ver
+
+
+def _get_config_value_for_remote(ctx, remote, config, key):
+    """
+    Look through config, and attempt to determine the "best" value to use
+    for a given key. For example, given:
+
+        config = {
+            'all':
+                {'branch': 'master'},
+            'branch': 'next'
+        }
+        _get_config_value_for_remote(ctx, remote, config, 'branch')
+
+    would return 'master'.
+
+    :param ctx: the argparse.Namespace object
+    :param remote: the teuthology.orchestra.remote.Remote object
+    :param config: the config dict
+    :param key: the name of the value to retrieve
+    """
+    roles = ctx.cluster.remotes[remote]
+    if 'all' in config:
+        return config['all'].get(key)
+    elif roles:
+        for role in roles:
+            if role in config and key in config[role]:
+                return config[role].get(key)
+    return config.get(key)
+
+
+class GitbuilderProject(object):
+    """
+    Represents a project that is built by gitbuilder.
+    """
+
+    def __init__(self, project, job_config, ctx=None, remote=None):
+        self.project = project
+        self.job_config = job_config
+        self.ctx = ctx
+        self.remote = remote
+        # avoiding circular imports
+        from teuthology.suite import get_install_task_flavor
+        self.flavor = get_install_task_flavor(self.job_config)
+        self.sha1 = self.job_config.get("sha1")
+
+        if remote and ctx:
+            self._init_from_remote()
+        else:
+            self._init_from_config()
+
+    def _init_from_remote(self):
+        """
+        Initializes the class from a teuthology.orchestra.remote.Remote object
+        """
+        self.arch = self.remote.arch
+        self.os_type = self.remote.os.name
+        self.os_version = self.remote.os.version
+        self.pkg_type = self.remote.system_type
+        self.distro = self._get_distro(
+            distro=self.remote.os.name,
+            version=self.remote.os.version,
+            codename=self.remote.os.codename,
+        )
+
+    def _init_from_config(self):
+        """
+        Initializes the class from a teuthology job config
+        """
+        # a bad assumption, but correct for most situations I believe
+        self.arch = "x86_64"
+        self.os_type = self.job_config.get("os_type")
+        self.os_version = self._get_version()
+        self.distro = self._get_distro(
+            distro=self.os_type,
+            version=self.os_version,
+        )
+        self.pkg_type = "deb" if self.os_type.lower() in (
+            "ubuntu",
+            "debian",
+        ) else "rpm"
+
+    @property
+    def version(self):
+        if not hasattr(self, '_version'):
+            self._version = self._get_package_version()
+        return self._version
+
+    @property
+    def base_url(self):
+        return self._get_base_url()
+
+    @property
+    def uri(self):
+        return self._get_uri()
+
+    def _parse_version(self, version):
+        """
+        Parses a distro version string and returns a modified string
+        that matches the format needed for the gitbuilder url.
+
+        Minor version numbers are ignored if they end in a zero. If they do
+        not end in a zero the minor version number is included with an
+        underscore as the separator instead of a period.
+        """
+        version_tokens = version.split(".")
+        include_minor_version = (
+            len(version_tokens) > 1 and
+            version_tokens[1] != "0"
+        )
+        if include_minor_version:
+            return "_".join(version_tokens)
+
+        # return only the major version
+        return version_tokens[0]
+
+    def _get_distro(self, distro=None, version=None, codename=None):
+        """
+        Given a distro and a version, returned the combined string
+        to use in a gitbuilder url.
+
+        :param distro:   The distro as a string
+        :param version:  The version as a string
+        :param codename: The codename for the distro.
+                         Used for deb based distros.
+        """
+        if distro in ('centos', 'rhel'):
+            distro = "centos"
+        elif distro == "fedora":
+            distro = "fc"
+        else:
+            # deb based systems use codename instead of a distro/version combo
+            if not codename:
+                # lookup codename based on distro string
+                codename = self._get_codename(distro, version)
+                if not codename:
+                    msg = "No codename found for: {distro} {version}".format(
+                        distro=distro,
+                        version=version,
+                    )
+                    log.exception(msg)
+                    raise RuntimeError()
+            return codename
+
+        return "{distro}{version}".format(
+            distro=distro,
+            version=self._parse_version(version),
+        )
+
+    def _get_codename(self, distro, version):
+        major_version = version.split(".")[0]
+        distro_codes = DISTRO_CODENAME_MAP.get(distro)
+        if not distro_codes:
+            return None
+        codename = distro_codes.get(version)
+        if not codename:
+            codename = distro_codes.get(major_version)
+
+        return codename
+
+    def _get_version(self):
+        version = self.job_config.get("os_version")
+        if not version:
+            version = DEFAULT_OS_VERSION.get(self.os_type)
+
+        return version
+
+    def _get_uri(self):
+        """
+        Set the uri -- common code used by both install and debian upgrade
+        """
+        tag = branch = sha1 = None
+        if self.remote:
+            tag = _get_config_value_for_remote(self.ctx, self.job_config,
+                                               self.remote, 'tag')
+            branch = _get_config_value_for_remote(self.ctx, self.job_config,
+                                                  self.remote, 'branch')
+            sha1 = _get_config_value_for_remote(self.ctx, self.job_config,
+                                                self.remote, 'sha1')
+        else:
+            sha1 = self.sha1
+
+        if tag:
+            uri = 'ref/' + tag
+        elif branch:
+            uri = 'ref/' + branch
+        elif sha1:
+            uri = 'sha1/' + sha1
+        else:
+            # FIXME: Should master be the default?
+            log.debug("defaulting to master branch")
+            uri = 'ref/master'
+        return uri
+
+    def _get_base_url(self):
+        """
+        Figures out which package repo base URL to use.
+        """
+        template = config.baseurl_template
+        # get distro name and arch
+        base_url = template.format(
+            host=config.gitbuilder_host,
+            proj=self.project,
+            pkg_type=self.pkg_type,
+            arch=self.arch,
+            dist=self.distro,
+            flavor=self.flavor,
+            uri=self.uri,
+        )
+        return base_url
+
+    def _get_package_version(self):
+        """
+        Look for, and parse, a file called 'version' in base_url.
+        """
+        url = "{0}/version".format(self.base_url)
+        log.info("Looking for package version: {0}".format(url))
+        resp = requests.get(url)
+        version = None
+        if not resp.ok:
+            log.info(
+                'Package not there yet (got HTTP code %s), waiting...',
+                resp.status_code,
+            )
+        else:
+            version = resp.text.strip()
+            # TODO: move this parsing into a different function for
+            # easier testing
+            # FIXME: 'version' as retreived from the repo is actually the
+            # RPM version PLUS *part* of the release. Example:
+            # Right now, ceph master is given the following version in the
+            # repo file: v0.67-rc3.164.gd5aa3a9 - whereas in reality the RPM
+            # version is 0.61.7 and the release is 37.g1243c97.el6 (centos6).
+            # Point being, I have to mangle a little here.
+            if version[0] == 'v':
+                version = version[1:]
+            if '-' in version:
+                version = version.split('-')[0]
+            log.info("Found version: {0}".format(version))
+        return version
index caea7cebe89fd29ba73765245d4bbc2f23211f9a..2353913ccc217c69cdb43df5cb871437ca2a57b9 100644 (file)
@@ -150,7 +150,7 @@ def _get_baseurlinfo_and_dist(ctx, remote, config):
         distro=distro,
         version=version,
     )
-    # this is used when contructing the rpm name
+    # this is used when constructing the rpm name
     result["dist_release"] = dist_release
 
     # branch/tag/sha1 flavor
@@ -173,7 +173,7 @@ def _get_gitbuilder_version(version):
 
     Minor version numbers are ignored if they end in a zero. If they do
     not end in a zero the minor version number is included with a dash as
-    the separtor instead of a period.
+    the separator instead of a period.
     """
     version_tokens = version.split(".")
     include_minor_version = (
@@ -181,7 +181,7 @@ def _get_gitbuilder_version(version):
         version_tokens[1] != "0"
     )
     if include_minor_version:
-        return "-".join(version_tokens)
+        return "_".join(version_tokens)
 
     # return only the major version
     return version_tokens[0]
index eb99befe1d90714675c8bd04be91ec28f336b98b..2fc5150b2ce9fa7f661bb8b0e668e3b667ce56bd 100644 (file)
@@ -107,11 +107,11 @@ class TestInstall(object):
         m_get_config_value_for_remote.return_value = "tag"
         result = install._get_baseurlinfo_and_dist(Mock(), remote, dict())
         expected = dict(
-            dist="centos6-5",
+            dist="centos6_5",
             arch="x86_64",
             flavor="basic",
             uri="ref/tag",
-            dist_release="el6-5",
+            dist_release="el6_5",
         )
         assert result == expected
 
index 938092b2ff244fc8c91faf93ee294da1e5e9cddf..a7b06d78d6ab7ade13975141f2cdf7db4d14031f 100644 (file)
@@ -279,3 +279,118 @@ class TestPackaging(object):
     @pytest.mark.parametrize("input, expected", KOJI_TASK_RPMS_MATRIX)
     def test_get_koji_task_result_package_name(self, input, expected):
         assert packaging._get_koji_task_result_package_name(input) == expected
+
+
+class TestGitbuilderProject(object):
+
+    def _get_remote(self, arch="x86_64", system_type="deb", distro="ubuntu",
+                    codename="trusty", version="14.04"):
+        rem = Mock()
+        rem.system_type = system_type
+        rem.os.name = distro
+        rem.os.codename = codename
+        rem.os.version = version
+        rem.arch = arch
+
+        return rem
+
+    @patch("teuthology.packaging.config")
+    @patch("teuthology.packaging._get_config_value_for_remote")
+    def test_init_from_remote_base_url(self, m_get_config_value, m_config):
+        m_config.baseurl_template = 'http://{host}/{proj}-{pkg_type}-{dist}-{arch}-{flavor}/{uri}'
+        m_config.gitbuilder_host = "gitbuilder.ceph.com"
+        m_get_config_value.return_value = None
+        rem = self._get_remote()
+        ctx = dict(foo="bar")
+        gp = packaging.GitbuilderProject("ceph", {}, ctx=ctx, remote=rem)
+        result = gp.base_url
+        expected = "http://gitbuilder.ceph.com/ceph-deb-trusty-x86_64-basic/ref/master"
+        assert result == expected
+
+    @patch("teuthology.packaging.config")
+    def test_init_from_config_base_url(self, m_config):
+        m_config.baseurl_template = 'http://{host}/{proj}-{pkg_type}-{dist}-{arch}-{flavor}/{uri}'
+        m_config.gitbuilder_host = "gitbuilder.ceph.com"
+        config = dict(
+            os_type="ubuntu",
+            os_version="14.04",
+            sha1="sha1",
+        )
+        gp = packaging.GitbuilderProject("ceph", config)
+        result = gp.base_url
+        expected = "http://gitbuilder.ceph.com/ceph-deb-trusty-x86_64-basic/sha1/sha1"
+        assert result == expected
+
+    @patch("teuthology.packaging.config")
+    @patch("teuthology.packaging._get_config_value_for_remote")
+    @patch("requests.get")
+    def test_get_package_version_found(self, m_get, m_get_config_value,
+                                       m_config):
+        m_config.baseurl_template = 'http://{host}/{proj}-{pkg_type}-{dist}-{arch}-{flavor}/{uri}'
+        m_config.gitbuilder_host = "gitbuilder.ceph.com"
+        m_get_config_value.return_value = None
+        resp = Mock()
+        resp.ok = True
+        resp.text = "0.90.0"
+        m_get.return_value = resp
+        rem = self._get_remote()
+        ctx = dict(foo="bar")
+        gp = packaging.GitbuilderProject("ceph", {}, ctx=ctx, remote=rem)
+        assert gp.version == "0.90.0"
+
+    @patch("teuthology.packaging.config")
+    @patch("teuthology.packaging._get_config_value_for_remote")
+    @patch("requests.get")
+    def test_get_package_version_not_found(self, m_get, m_get_config_value,
+                                           m_config):
+        m_config.baseurl_template = 'http://{host}/{proj}-{pkg_type}-{dist}-{arch}-{flavor}/{uri}'
+        m_config.gitbuilder_host = "gitbuilder.ceph.com"
+        m_get_config_value.return_value = None
+        resp = Mock()
+        resp.ok = False
+        m_get.return_value = resp
+        rem = self._get_remote()
+        ctx = dict(foo="bar")
+        gp = packaging.GitbuilderProject("ceph", {}, ctx=ctx, remote=rem)
+        assert not gp.version
+
+    GITBUILDER_DISTRO_MATRIX = [
+        ('rhel', '7.0', None, 'centos7'),
+        ('centos', '6.5', None, 'centos6_5'),
+        ('fedora', '20', None, 'fc20'),
+        ('ubuntu', '14.04', 'trusty', 'trusty'),
+        ('ubuntu', '14.04', None, 'trusty'),
+        ('debian', '7.0', None, 'wheezy'),
+        ('debian', '7', None, 'wheezy'),
+        ('ubuntu', '12.04', None, 'precise'),
+        ('ubuntu', '14.04', None, 'trusty'),
+    ]
+
+    @pytest.mark.parametrize(
+        "distro, version, codename, expected",
+        GITBUILDER_DISTRO_MATRIX
+    )
+    def test_get_distro_remote(self, distro, version, codename, expected):
+        rem = self._get_remote(distro=distro, version=version,
+                               codename=codename)
+        ctx = dict(foo="bar")
+        gp = packaging.GitbuilderProject("ceph", {}, ctx=ctx, remote=rem)
+        assert gp.distro == expected
+
+    @pytest.mark.parametrize(
+        "distro, version, codename, expected",
+        GITBUILDER_DISTRO_MATRIX + [
+            ('rhel', None, None, 'centos7'),
+            ('centos', None, None, 'centos7'),
+            ('fedora', None, None, 'fc20'),
+            ('ubuntu', None, None, 'trusty'),
+            ('debian', None, None, 'wheezy'),
+        ]
+    )
+    def test_get_distro_config(self, distro, version, codename, expected):
+        config = dict(
+            os_type=distro,
+            os_version=version
+        )
+        gp = packaging.GitbuilderProject("ceph", config)
+        assert gp.distro == expected