Bodhi directive created. It provides action download, which downloads rpms
from builds of specified bodhi update.
Bodhi utils are used for communitation with bodhi
| kparal | |
| tflink | |
| mkrizek | |
| jskladan |
Bodhi directive created. It provides action download, which downloads rpms
from builds of specified bodhi update.
Bodhi utils are used for communitation with bodhi
Tests for both b.-directive and b.-utils are included
| Lint Skipped |
| Unit Tests Skipped |
Overall, it looks pretty good. The runner args may need to be updated if we decide to go with more generic item and item_type route in T106 but we can deal with that later.
| libtaskotron/directives/bodhi_directive.py | ||
|---|---|---|
| 57 | I'd like to see this extracted into a standalone method since we'll be adding more code for bodhi comments etc. | |
| 81 | This is a bit more complex than it needs to be, I think it'd be better as: [build['nvr'] for build in res['builds]] | |
| Path | |||
|---|---|---|---|
| A | M | libtaskotron/bodhi_utils.py (30 lines) | |
| A | M | libtaskotron/directives/bodhi_directive.py (93 lines) | |
| M | libtaskotron/runner.py (13 lines) | ||
| A | M | testing/test_bodhi_directive.py (164 lines) | |
| A | M | testing/test_bodhi_utils.py (54 lines) |
| Commit | Tree | Parents | Author | Summary | Date |
|---|---|---|---|---|---|
| 75156613e87f | 38e542e5bcee | 34ec8783d7d8 | Petr Schindler | action downloaded put in the standalone method | Mar 28 2014, 2:01 PM |
| 34ec8783d7d8 | 2ec27c1de616 | b55c05399ad6 | Petr Schindler | T47 bodhi download (Show More…) | Mar 26 2014, 3:24 PM |
| b55c05399ad6 | 1a4e4e609ef6 | 19e74ed4d5d4 | Petr Schindler | Final update, huraaaaaaaaaa | Mar 26 2014, 3:22 PM |
| 19e74ed4d5d4 | 039862be175d | c164bf49f128 | Petr Schindler | update | Mar 26 2014, 3:10 PM |
| c164bf49f128 | 244678413f33 | 33ea169b810e | Petr Schindler | Comments added | Mar 26 2014, 1:37 PM |
| 33ea169b810e | ea87936068ef | edff5b5d0f59 | Petr Schindler | Unit tests finished, known bugs fixed | Mar 18 2014, 3:21 PM |
| edff5b5d0f59 | 6bfbb710cd42 | ae6c0bfebac6 | Petr Schindler | Logging and one test added (Show More…) | Mar 12 2014, 3:31 PM |
| ae6c0bfebac6 | 26ca424aa883 | 8158705593ea | Petr Schindler | Bodhi directive and b. utils created (Show More…) | Mar 12 2014, 9:52 AM |
| 1 | # -*- coding: utf-8 -*- | ||||
|---|---|---|---|---|---|
| 2 | # Copyright 2010-2014, Red Hat, Inc. | ||||
| 3 | # License: GNU General Public License version 2 or later | ||||
| 4 | | ||||
| 5 | '''Utility functions for dealing with Bodhi''' | ||||
| 6 | | ||||
| 7 | import fedora.client | ||||
| 8 | | ||||
| 9 | from .logger import log | ||||
| 10 | | ||||
| 11 | bodhi = fedora.client.bodhi.BodhiClient() | ||||
| 12 | | ||||
| 13 | | ||||
| 14 | def query_update(package): | ||||
| 15 | '''Get the last Bodhi update matching criteria. | ||||
| 16 | | ||||
| 17 | @param package package NVR or package name or update title or update ID | ||||
| 18 | Note: Only NVR allowed, not ENVR. See | ||||
| 19 | https://fedorahosted.org/bodhi/ticket/592. | ||||
| 20 | | ||||
| 21 | @return most recent Bodhi update object matching criteria, or None | ||||
| 22 | when no update is found. | ||||
| 23 | ''' | ||||
| 24 | log.debug('Searching Bodhi updates for: %s', package) | ||||
| 25 | res = bodhi.query(package=package, limit=1) | ||||
| 26 | if res['updates']: | ||||
| 27 | return res['updates'][0] | ||||
| 28 | else: | ||||
| 29 | return None | ||||
| 30 | | ||||
| 1 | # -*- coding: utf-8 -*- | ||||
|---|---|---|---|---|---|
| 2 | # Copyright 2014, Red Hat, Inc. | ||||
| 3 | # License: GNU General Public License version 2 or later | ||||
| 4 | | ||||
| 5 | import libtaskotron.bodhi_utils as bodhi | ||||
| 6 | from libtaskotron.koji_utils import KojiClient | ||||
| 7 | from libtaskotron.directives import BaseDirective | ||||
| 8 | | ||||
| 9 | from libtaskotron.logger import log | ||||
| 10 | from libtaskotron.exceptions import TaskotronDirectiveError | ||||
| 11 | | ||||
| 12 | directive_class = 'BodhiDirective' | ||||
| 13 | | ||||
| 14 | class BodhiDirective(BaseDirective): | ||||
| 15 | """The bodhi directive interfaces with Bodhi to facilitate | ||||
| 16 | various bodhi actions. | ||||
| 17 | | ||||
| 18 | format: "bodhi: | ||||
| 19 | action: [download] | ||||
| 20 | <command args>" | ||||
| 21 | | ||||
| 22 | Valid Commands: | ||||
| 23 | | ||||
| 24 | download | ||||
| 25 | Downloads rpms of builds from update specified by its name or id | ||||
| 26 | format: "bodhi: | ||||
| 27 | action: download | ||||
| 28 | bodhi-id: <name or id of update> | ||||
| 29 | arch=<desired arch>" | ||||
| 30 | """ | ||||
| 31 | def __init__(self, bodhi_api=None, koji_session=None): | ||||
| 32 | | ||||
| 33 | if bodhi_api: | ||||
| 34 | self.bodhi_api = bodhi_api | ||||
| 35 | else: | ||||
| 36 | self.bodhi_api = bodhi | ||||
| 37 | | ||||
| 38 | if koji_session: | ||||
| 39 | self.koji_session = koji_session | ||||
| 40 | else: | ||||
| 41 | self.koji_session = KojiClient() | ||||
| 42 | | ||||
| 43 | def action_download(self, update, arches, workdir): | ||||
| 44 | | ||||
| 45 | res = self.bodhi_api.query_update(update) | ||||
| 46 | | ||||
| 47 | if res is None: | ||||
| 48 | raise TaskotronDirectiveError("Update with id '%s' wasn't found" | ||||
| 49 | % update) | ||||
| 50 | | ||||
| 51 | nvrs = [build['nvr'] for build in res['builds']] | ||||
| 52 | | ||||
| 53 | downloaded_rpms = [] | ||||
| 54 | | ||||
| 55 | for nvr in nvrs: | ||||
| 56 | res = self.koji_session.get_nvr_rpms(nvr, workdir, arches) | ||||
| 57 | downloaded_rpms.extend(res) | ||||
| 58 | | ||||
| 59 | return downloaded_rpms | ||||
| 60 | | ||||
| 61 | def process(self, input_data, env_data): | ||||
| 62 | output_data = {} | ||||
| 63 | | ||||
| 64 | valid_actions = ['download'] | ||||
| 65 | | ||||
| 66 | action = input_data['action'] | ||||
| 67 | | ||||
| 68 | if action not in valid_actions: | ||||
| 69 | raise TaskotronDirectiveError('%s is not a valid command for bodhi directive' | ||||
| 70 | % action) | ||||
| 71 | | ||||
| 72 | if action == 'download': | ||||
| 73 | if 'bodhi_id' not in input_data or 'arch' not in input_data: | ||||
| 74 | detected_args = ', '.join(input_data.keys()) | ||||
| 75 | raise TaskotronDirectiveError("The bodhi directive 'download'" | ||||
| 76 | " requires both 'bodhi-id' and" | ||||
| 77 | " 'arch' arguments. Detected arguments: %s" % detected_args) | ||||
| 78 | | ||||
| 79 | workdir = env_data['workdir'] | ||||
| 80 | update = input_data['bodhi_id'] | ||||
| 81 | if input_data['arch'] == 'all': | ||||
| 82 | arches = ['i386', 'x86_64', 'noarch', 'armhfp'] | ||||
| 83 | else: | ||||
| 84 | arches = [input_data['arch']] | ||||
| 85 | if 'noarch' not in arches and arches != ['src']: | ||||
| 86 | arches.append('noarch') | ||||
| 87 | | ||||
| 88 | log.info("getting rpms for update %s (%s) and downloading to %s", | ||||
| 89 | update, arches, workdir) | ||||
| 90 | | ||||
| 91 | output_data['downloaded_rpms'] = self.action_download(update, arches, workdir) | ||||
| 92 | | ||||
| 93 | return output_data | ||||
| Show First 20 Lines • Show All 127 Lines • ▼ Show 20 Line(s) | 127 | def _validate_env(self): | |||
|---|---|---|---|---|---|
| 128 | raise NotImplementedError("Environment validation is not" | 128 | raise NotImplementedError("Environment validation is not" | ||
| 129 | " yet implemented") | 129 | " yet implemented") | ||
| 130 | 130 | | |||
| 131 | 131 | | |||
| 132 | def get_argparser(): | 132 | def get_argparser(): | ||
| 133 | parser = argparse.ArgumentParser() | 133 | parser = argparse.ArgumentParser() | ||
| 134 | parser.add_argument("task", nargs=1, help="task to run") | 134 | parser.add_argument("task", nargs=1, help="task to run") | ||
| 135 | parser.add_argument("-a", "--arch", | 135 | parser.add_argument("-a", "--arch", | ||
| 136 | choices=["i386", "x86_64", "armhfp", "noarch"], help="" | 136 | choices=["i386", "x86_64", "armhfp", "noarch", "all"], | ||
| 137 | "Architecture to be used; support arches are x86,\n" | 137 | help=""" | ||
| 138 | "x86_64, and armhfp.\nIf omitted, defaults to noarch.") | 138 | Architecture to be used; support arches are x86, x86_64, and armhfp. | ||
| 139 | If omitted, defaults to noarch. If every architechtures are wanted | ||||
| 140 | use all. | ||||
| 141 | """) | ||||
| 139 | parser.add_argument("-e", "--envr", help="envr to be used as input") | 142 | parser.add_argument("-e", "--envr", help="envr to be used as input") | ||
| 140 | parser.add_argument("-t", "--tag", help="koji tag to be used as input") | 143 | parser.add_argument("-t", "--tag", help="koji tag to be used as input") | ||
| 144 | parser.add_argument("-b", "--bodhi-id", | ||||
| 145 | help=""" | ||||
| 146 | Identification of bodhi update. Update id and name could be used. | ||||
| 147 | """) | ||||
| 141 | return parser | 148 | return parser | ||
| 142 | 149 | | |||
| 143 | 150 | | |||
| 144 | def main(): | 151 | def main(): | ||
| 145 | parser = get_argparser() | 152 | parser = get_argparser() | ||
| 146 | args = parser.parse_args() | 153 | args = parser.parse_args() | ||
| 147 | 154 | | |||
| 148 | logger.init(level=logging.DEBUG) | 155 | logger.init(level=logging.DEBUG) | ||
| Show All 14 Lines | |||||
| 1 | # -*- coding: utf-8 -*- | ||||
|---|---|---|---|---|---|
| 2 | # Copyright 2014, Red Hat, Inc. | ||||
| 3 | # License: GNU General Public License version 2 or later | ||||
| 4 | | ||||
| 5 | '''Unit tests for libtaskotron/directives/bodhi_directive.py''' | ||||
| 6 | | ||||
| 7 | import pytest | ||||
| 8 | from dingus import Dingus | ||||
| 9 | | ||||
| 10 | from libtaskotron.directives import bodhi_directive | ||||
| 11 | from libtaskotron.exceptions import TaskotronDirectiveError | ||||
| 12 | | ||||
| 13 | class TestBodhiDownloads(): | ||||
| 14 | | ||||
| 15 | def setup_method(self, method): | ||||
| 16 | '''Run this before every test invocation''' | ||||
| 17 | self.ref_arch = 'x86_64' | ||||
| 18 | self.ref_bodhi_id = 'FEDORA-1234-56789' | ||||
| 19 | self.ref_workdir = '/var/tmp/foo' | ||||
| 20 | self.ref_taskfile = '/foo/bar/task.yml' | ||||
| 21 | self.ref_nvr1 = 'foo-1.2.3.fc99' | ||||
| 22 | self.ref_nvr2 = 'bar-1.2.3.fc99' | ||||
| 23 | self.ref_update = {'title': 'Random update', | ||||
| 24 | 'builds': [{'nvr': self.ref_nvr1, 'package': {}}, | ||||
| 25 | {'nvr': self.ref_nvr2, 'package': {}} | ||||
| 26 | ], | ||||
| 27 | 'other_keys': {} | ||||
| 28 | } | ||||
| 29 | self.ref_input = {'action': 'download', | ||||
| 30 | 'arch': self.ref_arch, | ||||
| 31 | 'bodhi_id': self.ref_bodhi_id | ||||
| 32 | } | ||||
| 33 | self.ref_envdata = {'workdir': self.ref_workdir} | ||||
| 34 | self.ref_rpmfile = '%s/%s.rpm' % (self.ref_workdir, self.ref_nvr1) | ||||
| 35 | | ||||
| 36 | self.helper = bodhi_directive.BodhiDirective() | ||||
| 37 | | ||||
| 38 | | ||||
| 39 | def test_action_download_existing_update(self): | ||||
| 40 | '''Test download of existing update''' | ||||
| 41 | stub_bodhi = Dingus(query_update__returns = self.ref_update) | ||||
| 42 | stub_koji = Dingus(get_nvr_rpms__returns = [self.ref_rpmfile]) | ||||
| 43 | | ||||
| 44 | test_bodhi = bodhi_directive.BodhiDirective(stub_bodhi, stub_koji) | ||||
| 45 | | ||||
| 46 | output_data = test_bodhi.process(self.ref_input, self.ref_envdata) | ||||
| 47 | | ||||
| 48 | ref_output_data = {'downloaded_rpms': [self.ref_rpmfile, | ||||
| 49 | self.ref_rpmfile]} | ||||
| 50 | getrpms_calls = stub_koji.calls() | ||||
| 51 | requested_nvrs = map(lambda x: x[1][0], getrpms_calls) | ||||
| 52 | | ||||
| 53 | # checks whether get_nvr_rpms was called for every update | ||||
| 54 | # with correct nvrs | ||||
| 55 | assert len(getrpms_calls) == 2 | ||||
| 56 | assert requested_nvrs == [self.ref_nvr1, self.ref_nvr2] | ||||
| 57 | assert output_data == ref_output_data | ||||
| 58 | | ||||
| 59 | | ||||
| 60 | def test_action_download_all_archs(self): | ||||
| 61 | '''Test download when all arches are demanded''' | ||||
| 62 | self.ref_input['arch'] = 'all' | ||||
| 63 | | ||||
| 64 | stub_bodhi = Dingus(query_update__returns = self.ref_update) | ||||
| 65 | stub_koji = Dingus(get_nvr_rpms__returns = [self.ref_rpmfile]) | ||||
| 66 | | ||||
| 67 | test_bodhi = bodhi_directive.BodhiDirective(stub_bodhi, stub_koji) | ||||
| 68 | | ||||
| 69 | test_bodhi.process(self.ref_input, self.ref_envdata) | ||||
| 70 | | ||||
| 71 | ref_arches = set(['i386', 'x86_64', 'noarch', 'armhfp']) | ||||
| 72 | getrpms_calls = stub_koji.calls() | ||||
| 73 | req_arches = map(lambda x: x[1][2], getrpms_calls) | ||||
| 74 | | ||||
| 75 | # checks whether all get_nvr_rpms calls demanded all arches | ||||
| 76 | assert True == all(map(lambda x: set(x) == ref_arches, req_arches)) | ||||
| 77 | | ||||
| 78 | def test_action_download_source(self): | ||||
| 79 | '''Test download of source packages''' | ||||
| 80 | self.ref_input['arch'] = 'src' | ||||
| 81 | | ||||
| 82 | stub_bodhi = Dingus(query_update__returns = self.ref_update) | ||||
| 83 | stub_koji = Dingus(get_nvr_rpms__returns = [self.ref_rpmfile]) | ||||
| 84 | | ||||
| 85 | test_bodhi = bodhi_directive.BodhiDirective(stub_bodhi, stub_koji) | ||||
| 86 | | ||||
| 87 | test_bodhi.process(self.ref_input, self.ref_envdata) | ||||
| 88 | | ||||
| 89 | getrpms_calls = stub_koji.calls() | ||||
| 90 | req_arches = map(lambda x: x[1][2], getrpms_calls) | ||||
| 91 | | ||||
| 92 | # checks whether all get_nvr_rpms calls demanded only source pkgs | ||||
| 93 | assert all(map(lambda x: x == ['src'], req_arches)) | ||||
| 94 | | ||||
| 95 | def test_action_download_added_noarch(self): | ||||
| 96 | '''Test whether noarch is automaticaly added''' | ||||
| 97 | self.ref_input['arch'] = 'i386' | ||||
| 98 | | ||||
| 99 | stub_bodhi = Dingus(query_update__returns = self.ref_update) | ||||
| 100 | stub_koji = Dingus(get_nvr_rpms__returns = [self.ref_rpmfile]) | ||||
| 101 | | ||||
| 102 | test_bodhi = bodhi_directive.BodhiDirective(stub_bodhi, stub_koji) | ||||
| 103 | | ||||
| 104 | test_bodhi.process(self.ref_input, self.ref_envdata) | ||||
| 105 | | ||||
| 106 | ref_arches = set(['i386', 'noarch']) | ||||
| 107 | getrpms_calls = stub_koji.calls() | ||||
| 108 | req_arches = map(lambda x: x[1][2], getrpms_calls) | ||||
| 109 | | ||||
| 110 | # checks whether noarch is demanded in all get_nvr_rpms calls | ||||
| 111 | assert all(map(lambda x: 'noarch' in x, req_arches)) | ||||
| 112 | | ||||
| 113 | | ||||
| 114 | def test_action_download_nonexisting_update(self): | ||||
| 115 | '''Test whether exception is raised when no update is found''' | ||||
| 116 | stub_bodhi = Dingus(query_update__returns = None) | ||||
| 117 | stub_koji = Dingus(get_nvr_rpms__returns = "It shouldn't go this far") | ||||
| 118 | | ||||
| 119 | test_bodhi = bodhi_directive.BodhiDirective(stub_bodhi, stub_koji) | ||||
| 120 | | ||||
| 121 | # No update is found, b. directive should raise an Exception | ||||
| 122 | with pytest.raises(TaskotronDirectiveError): | ||||
| 123 | test_bodhi.process(self.ref_input, self.ref_envdata) | ||||
| 124 | | ||||
| 125 | | ||||
| 126 | def test_invalid_action(self): | ||||
| 127 | '''Test response on non-existing action''' | ||||
| 128 | | ||||
| 129 | self.ref_input['action'] = 'foo' | ||||
| 130 | | ||||
| 131 | stub_bodhi = Dingus() | ||||
| 132 | stub_koji = Dingus() | ||||
| 133 | | ||||
| 134 | test_bodhi = bodhi_directive.BodhiDirective(stub_bodhi, stub_koji) | ||||
| 135 | | ||||
| 136 | # Unknown action should raise an Exception | ||||
| 137 | with pytest.raises(TaskotronDirectiveError): | ||||
| 138 | test_bodhi.process(self.ref_input, self.ref_envdata) | ||||
| 139 | | ||||
| 140 | | ||||
| 141 | def test_action_download_insufficient_input_data(self): | ||||
| 142 | '''Test response on unsufficient input for download action''' | ||||
| 143 | ref_input = {'action': 'download'} | ||||
| 144 | | ||||
| 145 | stub_bodhi = Dingus() | ||||
| 146 | stub_koji = Dingus() | ||||
| 147 | | ||||
| 148 | test_bodhi = bodhi_directive.BodhiDirective(stub_bodhi, stub_koji) | ||||
| 149 | | ||||
| 150 | # Tests with no information | ||||
| 151 | with pytest.raises(TaskotronDirectiveError): | ||||
| 152 | test_bodhi.process(ref_input, self.ref_envdata) | ||||
| 153 | | ||||
| 154 | ref_input['arch'] = self.ref_arch | ||||
| 155 | | ||||
| 156 | # Tests with only arch, or bodhi_id given | ||||
| 157 | with pytest.raises(TaskotronDirectiveError): | ||||
| 158 | test_bodhi.process(ref_input, self.ref_envdata) | ||||
| 159 | | ||||
| 160 | ref_input.pop('arch') | ||||
| 161 | ref_input['bodhi_id'] = self.ref_bodhi_id | ||||
| 162 | | ||||
| 163 | with pytest.raises(TaskotronDirectiveError): | ||||
| 164 | test_bodhi.process(ref_input, self.ref_envdata) | ||||
| 1 | # -*- coding: utf-8 -*- | ||||
|---|---|---|---|---|---|
| 2 | # Copyright 2014, Red Hat, Inc. | ||||
| 3 | # License: GNU General Public License version 2 or later | ||||
| 4 | | ||||
| 5 | '''Unit tests for libtaskotron/bodhi_utils.py''' | ||||
| 6 | | ||||
| 7 | import pytest | ||||
| 8 | from dingus import Dingus | ||||
| 9 | | ||||
| 10 | from libtaskotron import bodhi_utils | ||||
| 11 | | ||||
| 12 | class TestKojiDownloads(): | ||||
| 13 | | ||||
| 14 | def setup_method(self, method): | ||||
| 15 | '''Run this before every test invocation''' | ||||
| 16 | self.ref_bodhi_id = 'FEDORA-1234-56789' | ||||
| 17 | self.ref_update = {'title': 'Random update', | ||||
| 18 | 'builds': [{'nvr': 'foo-1.2-3.fc99', | ||||
| 19 | 'package': {}}], | ||||
| 20 | 'other_keys': {} | ||||
| 21 | } | ||||
| 22 | | ||||
| 23 | | ||||
| 24 | def test_query_existing_update(self, monkeypatch): | ||||
| 25 | '''Test query for existing update''' | ||||
| 26 | ref_query_answer = {'tg_flash': None, | ||||
| 27 | 'num_items': 1, | ||||
| 28 | 'title': '1 update found', | ||||
| 29 | 'updates': [self.ref_update] | ||||
| 30 | } | ||||
| 31 | stub_bodhi = Dingus(query__returns = ref_query_answer) | ||||
| 32 | | ||||
| 33 | monkeypatch.setattr(bodhi_utils, 'bodhi', stub_bodhi) | ||||
| 34 | | ||||
| 35 | update = bodhi_utils.query_update(self.ref_bodhi_id) | ||||
| 36 | | ||||
| 37 | assert update == self.ref_update | ||||
| 38 | | ||||
| 39 | def test_query_non_existing_update(self, monkeypatch): | ||||
| 40 | '''Test query for non-existing update''' | ||||
| 41 | ref_query_answer = {'tg_flash': None, | ||||
| 42 | 'num_items': 0, | ||||
| 43 | 'title': '0 updates found', | ||||
| 44 | 'updates': [] | ||||
| 45 | } | ||||
| 46 | stub_bodhi = Dingus(query__returns = ref_query_answer) | ||||
| 47 | | ||||
| 48 | monkeypatch.setattr(bodhi_utils, 'bodhi', stub_bodhi) | ||||
| 49 | | ||||
| 50 | update = bodhi_utils.query_update(self.ref_bodhi_id) | ||||
| 51 | | ||||
| 52 | assert update is None | ||||
| 53 | | ||||
| 54 | | ||||
I'd like to see this extracted into a standalone method since we'll be adding more code for bodhi comments etc.