Merge #339 Refactor failables, step 1
				
					
				
			This commit is contained in:
		
						commit
						247149d4e1
					
				| @ -117,13 +117,6 @@ Example | ||||
|     base_product_short = "Fedora" | ||||
|     base_product_version = "23" | ||||
| 
 | ||||
| **tree_arches** | ||||
|     ([*str*]) -- list of architectures which should be included; if undefined, all architectures from variants.xml will be included | ||||
| 
 | ||||
| **tree_variants** | ||||
|     ([*str*]) -- list of variants which should be included; if undefined, all variants from variants.xml will be included | ||||
| 
 | ||||
| 
 | ||||
| General Settings | ||||
| ================ | ||||
| 
 | ||||
| @ -138,28 +131,12 @@ Options | ||||
| 
 | ||||
| **failable_deliverables** [optional] | ||||
|     (*list*) -- list which deliverables on which variant and architecture can | ||||
|     fail and not abort the whole compose | ||||
| 
 | ||||
|     Currently handled deliverables are: | ||||
|      * buildinstall | ||||
|      * iso | ||||
|      * live | ||||
|      * image-build | ||||
|      * live-media | ||||
|      * ostree | ||||
|      * ostree-installer | ||||
|      * osbs | ||||
| 
 | ||||
|        .. note:: | ||||
| 
 | ||||
|            Image building is not run per-architecture. If you want to mark it | ||||
|            as failable, specify it in a block with arch set as ``*``. | ||||
|     fail and not abort the whole compose. This only applies to ``buildinstall`` | ||||
|     and ``iso`` parts. All other artifacts can be configured in their | ||||
|     respective part of configuration. | ||||
| 
 | ||||
|     Please note that ``*`` as a wildcard matches all architectures but ``src``. | ||||
| 
 | ||||
|     tree_arches = ["x86_64"] | ||||
|     tree_variants = ["Server"] | ||||
| 
 | ||||
| **comps_filter_environments** [optional] | ||||
|     (*bool*) -- When set to ``False``, the comps files for variants will not | ||||
|     have their environments filtered to match the variant. | ||||
| @ -168,6 +145,15 @@ Options | ||||
|     (*list*) -- List of variants for which the original comps file will be | ||||
|     copied without any modifications. Overwrites `comps_filter_environments`. | ||||
| 
 | ||||
| **tree_arches** | ||||
|     ([*str*]) -- list of architectures which should be included; if undefined, | ||||
|     all architectures from variants.xml will be included | ||||
| 
 | ||||
| **tree_variants** | ||||
|     ([*str*]) -- list of variants which should be included; if undefined, all | ||||
|     variants from variants.xml will be included | ||||
| 
 | ||||
| 
 | ||||
| Example | ||||
| ------- | ||||
| :: | ||||
| @ -196,6 +182,9 @@ Example | ||||
|         }) | ||||
|     ] | ||||
| 
 | ||||
|     tree_arches = ["x86_64"] | ||||
|     tree_variants = ["Server"] | ||||
| 
 | ||||
| 
 | ||||
| Image Naming | ||||
| ============ | ||||
| @ -836,6 +825,14 @@ will be generated based on date, compose type and respin. | ||||
|  * ``image_build_release`` | ||||
|  * ``live_images_release`` | ||||
| 
 | ||||
| Each configuration block can also optionally specify a list of architectures | ||||
| that are not release blocking with ``failable`` key. If any deliverable fails, | ||||
| it will not abort the whole compose. Due to limitations in how the tasks are | ||||
| done in Koji, if any architecture fails, all of them fail. Until this is | ||||
| resolved, it is not possible to configure failability per architecture. An | ||||
| empty list means required deliverable, non-empty list means non-blocking | ||||
| deliverable. | ||||
| 
 | ||||
| 
 | ||||
| Live Images Settings | ||||
| ==================== | ||||
| @ -1059,6 +1056,8 @@ runroot environment. | ||||
| 
 | ||||
|     * ``config_branch`` -- (*str*) Git branch of the repo to use. Defaults to | ||||
|       ``master``. | ||||
|     * ``failable`` -- (*[str]*) List of architectures for which this | ||||
|       deliverable is not release blocking. | ||||
| 
 | ||||
| 
 | ||||
| Example config | ||||
| @ -1096,6 +1095,8 @@ an OSTree repository. This always runs in Koji as a ``runroot`` task. | ||||
| 
 | ||||
|     * ``release`` -- (*str*) Release value to set for the installer image. Set | ||||
|       to ``None`` to use the date.respin format. | ||||
|     * ``failable`` -- (*[str]*) List of architectures for which this | ||||
|       deliverable is not release blocking. | ||||
| 
 | ||||
|     These optional keys are passed to ``lorax`` to customize the build. | ||||
| 
 | ||||
| @ -1163,6 +1164,14 @@ they are not scratch builds). | ||||
|       Please see :ref:`git-urls` section for more details. | ||||
|     * ``target`` -- (*str*) A Koji target to build the image for. | ||||
| 
 | ||||
|     Optionally you can specify ``failable``. If it has a truthy value, failure | ||||
|     to create the image will not abort the whole compose. | ||||
| 
 | ||||
|     .. note:: | ||||
|         Once OSBS gains support for multiple architectures, the usage of this | ||||
|         option will most likely change to list architectures that are allowed | ||||
|         to fail. | ||||
| 
 | ||||
|     The configuration will pass other attributes directly to the Koji task. | ||||
|     This includes ``name``, ``version``, ``scratch`` and ``priority``. | ||||
| 
 | ||||
|  | ||||
| @ -190,9 +190,10 @@ class BuildinstallPhase(PhaseBase): | ||||
|                 # TODO: label is not used | ||||
|                 label = "" | ||||
|                 volid = get_volid(self.compose, arch, variant, escape_spaces=False, disc_type=disc_type) | ||||
|                 with failable(self.compose, variant, arch, 'buildinstall'): | ||||
|                 can_fail = self.compose.can_fail(variant, arch, 'buildinstall') | ||||
|                 with failable(self.compose, can_fail, variant, arch, 'buildinstall'): | ||||
|                     tweak_buildinstall(buildinstall_dir, os_tree, arch, variant.uid, label, volid, kickstart_file) | ||||
|                     link_boot_iso(self.compose, arch, variant) | ||||
|                     link_boot_iso(self.compose, arch, variant, can_fail) | ||||
| 
 | ||||
| 
 | ||||
| def get_kickstart_file(compose): | ||||
| @ -315,7 +316,7 @@ def tweak_buildinstall(src, dst, arch, variant, label, volid, kickstart_file=Non | ||||
|     shutil.rmtree(tmp_dir) | ||||
| 
 | ||||
| 
 | ||||
| def link_boot_iso(compose, arch, variant): | ||||
| def link_boot_iso(compose, arch, variant, can_fail): | ||||
|     if arch == "src": | ||||
|         return | ||||
| 
 | ||||
| @ -369,6 +370,7 @@ def link_boot_iso(compose, arch, variant): | ||||
|     img.bootable = True | ||||
|     img.subvariant = variant.name | ||||
|     img.implant_md5 = implant_md5 | ||||
|     setattr(img, 'can_fail', can_fail) | ||||
|     setattr(img, 'deliverable', 'buildinstall') | ||||
|     try: | ||||
|         img.volume_id = iso.get_volume_id(new_boot_iso_path) | ||||
| @ -382,7 +384,8 @@ class BuildinstallThread(WorkerThread): | ||||
|     def process(self, item, num): | ||||
|         # The variant is None unless lorax is used as buildinstall method. | ||||
|         compose, arch, variant, cmd = item | ||||
|         with failable(compose, variant, arch, 'buildinstall'): | ||||
|         can_fail = compose.can_fail(variant, arch, 'buildinstall') | ||||
|         with failable(compose, can_fail, variant, arch, 'buildinstall'): | ||||
|             self.worker(compose, arch, variant, cmd, num) | ||||
| 
 | ||||
|     def worker(self, compose, arch, variant, cmd, num): | ||||
|  | ||||
| @ -183,7 +183,8 @@ class CreateIsoThread(WorkerThread): | ||||
| 
 | ||||
|     def process(self, item, num): | ||||
|         compose, cmd, variant, arch = item | ||||
|         with failable(compose, variant, arch, 'iso'): | ||||
|         can_fail = compose.can_fail(variant, arch, 'iso') | ||||
|         with failable(compose, can_fail, variant, arch, 'iso'): | ||||
|             self.worker(compose, cmd, variant, arch, num) | ||||
| 
 | ||||
|     def worker(self, compose, cmd, variant, arch, num): | ||||
|  | ||||
| @ -157,6 +157,7 @@ class ImageBuildPhase(base.ImageConfigMixin, base.ConfigGuardedPhase): | ||||
|                     ), | ||||
|                     "link_type": self.compose.conf.get("link_type", "hardlink-or-copy"), | ||||
|                     "scratch": image_conf['image-build'].pop('scratch', False), | ||||
|                     "failable_arches": image_conf.pop('failable', []), | ||||
|                 } | ||||
|                 self.pool.add(CreateImageBuildThread(self.pool)) | ||||
|                 self.pool.queue_put((self.compose, cmd)) | ||||
| @ -172,7 +173,10 @@ class CreateImageBuildThread(WorkerThread): | ||||
|         compose, cmd = item | ||||
|         variant = cmd["image_conf"]["image-build"]["variant"] | ||||
|         subvariant = cmd["image_conf"]["image-build"].get("subvariant", variant.uid) | ||||
|         with failable(compose, variant, '*', 'image-build', subvariant): | ||||
|         failable_arches = cmd.get('failable_arches', []) | ||||
|         self.can_fail = bool(failable_arches) | ||||
|         # TODO handle failure per architecture; currently not possible in single task | ||||
|         with failable(compose, self.can_fail, variant, '*', 'image-build', subvariant): | ||||
|             self.worker(num, compose, variant, subvariant, cmd) | ||||
| 
 | ||||
|     def worker(self, num, compose, variant, subvariant, cmd): | ||||
| @ -253,6 +257,7 @@ class CreateImageBuildThread(WorkerThread): | ||||
|             img.disc_count = 1 | ||||
|             img.bootable = False | ||||
|             img.subvariant = subvariant | ||||
|             setattr(img, 'can_fail', self.can_fail) | ||||
|             setattr(img, 'deliverable', 'image-build') | ||||
|             compose.im.add(variant=variant.uid, arch=image_info['arch'], image=img) | ||||
| 
 | ||||
|  | ||||
| @ -157,6 +157,7 @@ class LiveImagesPhase(base.ImageConfigMixin, base.ConfigGuardedPhase): | ||||
|                         "type": type, | ||||
|                         "label": "",  # currently not used | ||||
|                         "subvariant": subvariant, | ||||
|                         "failable_arches": data.get('failable', []), | ||||
|                     } | ||||
| 
 | ||||
|                     cmd["repos"] = self._get_repos(arch, variant, data) | ||||
| @ -199,7 +200,10 @@ class CreateLiveImageThread(WorkerThread): | ||||
| 
 | ||||
|     def process(self, item, num): | ||||
|         compose, cmd, variant, arch = item | ||||
|         with failable(compose, variant, arch, 'live', cmd.get('subvariant')): | ||||
|         self.failable_arches = cmd.get('failable_arches', []) | ||||
|         # TODO handle failure per architecture; currently not possible in single task | ||||
|         self.can_fail = bool(self.failable_arches) | ||||
|         with failable(compose, self.can_fail, variant, arch, 'live', cmd.get('subvariant')): | ||||
|             self.worker(compose, cmd, variant, arch, num) | ||||
| 
 | ||||
|     def worker(self, compose, cmd, variant, arch, num): | ||||
| @ -290,6 +294,7 @@ class CreateLiveImageThread(WorkerThread): | ||||
|         img.disc_count = 1 | ||||
|         img.bootable = True | ||||
|         img.subvariant = subvariant | ||||
|         setattr(img, 'can_fail', self.can_fail) | ||||
|         setattr(img, 'deliverable', 'live') | ||||
|         compose.im.add(variant=variant.uid, arch=arch, image=img) | ||||
| 
 | ||||
|  | ||||
| @ -116,6 +116,7 @@ class LiveMediaPhase(ImageConfigMixin, ConfigGuardedPhase): | ||||
|                     'repo': self._get_repos(image_conf, variant), | ||||
|                     'install_tree': self._get_install_tree(image_conf, variant), | ||||
|                     'version': self.get_config(image_conf, 'version'), | ||||
|                     'failable_arches': image_conf.get('failable', []), | ||||
|                 } | ||||
|                 self.pool.add(LiveMediaThread(self.pool)) | ||||
|                 self.pool.queue_put((self.compose, variant, config)) | ||||
| @ -127,8 +128,10 @@ class LiveMediaThread(WorkerThread): | ||||
|     def process(self, item, num): | ||||
|         compose, variant, config = item | ||||
|         subvariant = config.pop('subvariant') | ||||
|         self.failable_arches = config.pop('failable_arches') | ||||
|         self.num = num | ||||
|         with failable(compose, variant, '*', 'live-media', subvariant): | ||||
|         # TODO handle failure per architecture; currently not possible in single task | ||||
|         with failable(compose, bool(self.failable_arches), variant, '*', 'live-media', subvariant): | ||||
|             self.worker(compose, variant, subvariant, config) | ||||
| 
 | ||||
|     def _get_log_file(self, compose, variant, subvariant, config): | ||||
| @ -204,6 +207,7 @@ class LiveMediaThread(WorkerThread): | ||||
|             img.disc_count = 1 | ||||
|             img.bootable = True | ||||
|             img.subvariant = subvariant | ||||
|             setattr(img, 'can_fail', bool(self.failable_arches)) | ||||
|             setattr(img, 'deliverable', 'live-media') | ||||
|             compose.im.add(variant=variant.uid, arch=image_info['arch'], image=img) | ||||
| 
 | ||||
|  | ||||
| @ -47,7 +47,7 @@ class OSBSThread(WorkerThread): | ||||
|     def process(self, item, num): | ||||
|         compose, variant, config = item | ||||
|         self.num = num | ||||
|         with util.failable(compose, variant, '*', 'osbs'): | ||||
|         with util.failable(compose, bool(config.pop('failable', None)), variant, '*', 'osbs'): | ||||
|             self.worker(compose, variant, config) | ||||
| 
 | ||||
|     def worker(self, compose, variant, config): | ||||
|  | ||||
| @ -39,7 +39,9 @@ class OSTreeThread(WorkerThread): | ||||
|     def process(self, item, num): | ||||
|         compose, variant, arch, config = item | ||||
|         self.num = num | ||||
|         with util.failable(compose, variant, arch, 'ostree'): | ||||
|         failable_arches = config.get('failable', []) | ||||
|         with util.failable(compose, util.can_arch_fail(failable_arches, arch), | ||||
|                            variant, arch, 'ostree'): | ||||
|             self.worker(compose, variant, arch, config) | ||||
| 
 | ||||
|     def worker(self, compose, variant, arch, config): | ||||
|  | ||||
| @ -42,7 +42,9 @@ class OstreeInstallerThread(WorkerThread): | ||||
|     def process(self, item, num): | ||||
|         compose, variant, arch, config = item | ||||
|         self.num = num | ||||
|         with util.failable(compose, variant, arch, 'ostree-installer'): | ||||
|         failable_arches = config.get('failable', []) | ||||
|         self.can_fail = util.can_arch_fail(failable_arches, arch) | ||||
|         with util.failable(compose, self.can_fail, variant, arch, 'ostree-installer'): | ||||
|             self.worker(compose, variant, arch, config) | ||||
| 
 | ||||
|     def worker(self, compose, variant, arch, config): | ||||
| @ -119,6 +121,7 @@ class OstreeInstallerThread(WorkerThread): | ||||
|         img.bootable = True | ||||
|         img.subvariant = variant.name | ||||
|         img.implant_md5 = implant_md5 | ||||
|         setattr(img, 'can_fail', self.can_fail) | ||||
|         setattr(img, 'deliverable', 'ostree-installer') | ||||
|         try: | ||||
|             img.volume_id = iso_wrapper.get_volume_id(full_iso_path) | ||||
|  | ||||
| @ -125,7 +125,9 @@ def check(compose, variant, arch, image): | ||||
|     result = True | ||||
|     path = os.path.join(compose.paths.compose.topdir(), image.path) | ||||
|     deliverable = getattr(image, 'deliverable') | ||||
|     with failable(compose, variant, arch, deliverable, subvariant=image.subvariant): | ||||
|     can_fail = getattr(image, 'can_fail', False) | ||||
|     with failable(compose, can_fail, variant, arch, deliverable, | ||||
|                   subvariant=image.subvariant): | ||||
|         with open(path) as f: | ||||
|             iso = is_iso(f) | ||||
|             if image.format == 'iso' and not iso: | ||||
|  | ||||
| @ -464,10 +464,9 @@ def process_args(fmt, args): | ||||
| 
 | ||||
| 
 | ||||
| @contextlib.contextmanager | ||||
| def failable(compose, variant, arch, deliverable, subvariant=None): | ||||
| def failable(compose, can_fail, variant, arch, deliverable, subvariant=None): | ||||
|     """If a deliverable can fail, log a message and go on as if it succeeded.""" | ||||
|     msg = deliverable.replace('-', ' ').capitalize() | ||||
|     can_fail = compose.can_fail(variant, arch, deliverable) | ||||
|     if can_fail: | ||||
|         compose.attempt_deliverable(variant, arch, deliverable, subvariant) | ||||
|     else: | ||||
| @ -489,6 +488,11 @@ def failable(compose, variant, arch, deliverable, subvariant=None): | ||||
|             compose.log_debug(tb) | ||||
| 
 | ||||
| 
 | ||||
| def can_arch_fail(failable_arches, arch): | ||||
|     """Check if `arch` is in `failable_arches` or `*` can fail.""" | ||||
|     return '*' in failable_arches or arch in failable_arches | ||||
| 
 | ||||
| 
 | ||||
| def get_format_substs(compose, **kwargs): | ||||
|     """Return a dict of basic format substitutions. | ||||
| 
 | ||||
|  | ||||
| @ -62,7 +62,7 @@ class DummyCompose(object): | ||||
|         self.log_debug = mock.Mock() | ||||
|         self.log_warning = mock.Mock() | ||||
|         self.get_image_name = mock.Mock(return_value='image-name') | ||||
|         self.image = mock.Mock(path='Client/i386/iso/image.iso') | ||||
|         self.image = mock.Mock(path='Client/i386/iso/image.iso', can_fail=False) | ||||
|         self.im = mock.Mock(images={'Client': {'amd64': [self.image]}}) | ||||
|         self.old_composes = [] | ||||
|         self.config_dir = '/home/releng/config' | ||||
|  | ||||
| @ -355,9 +355,9 @@ class TestCopyFiles(PungiTestCase): | ||||
|                        'amd64', 'Client', '', 'Client.amd64', 'kickstart')]) | ||||
|         self.assertItemsEqual( | ||||
|             link_boot_iso.mock_calls, | ||||
|             [mock.call(compose, 'x86_64', compose.variants['Server']), | ||||
|              mock.call(compose, 'amd64', compose.variants['Client']), | ||||
|              mock.call(compose, 'amd64', compose.variants['Server'])]) | ||||
|             [mock.call(compose, 'x86_64', compose.variants['Server'], False), | ||||
|              mock.call(compose, 'amd64', compose.variants['Client'], False), | ||||
|              mock.call(compose, 'amd64', compose.variants['Server'], False)]) | ||||
| 
 | ||||
|     @mock.patch('pungi.phases.buildinstall.link_boot_iso') | ||||
|     @mock.patch('pungi.phases.buildinstall.tweak_buildinstall') | ||||
| @ -397,9 +397,9 @@ class TestCopyFiles(PungiTestCase): | ||||
|                        'amd64', 'Client', '', 'Client.amd64', 'kickstart')]) | ||||
|         self.assertItemsEqual( | ||||
|             link_boot_iso.mock_calls, | ||||
|             [mock.call(compose, 'x86_64', compose.variants['Server']), | ||||
|              mock.call(compose, 'amd64', compose.variants['Client']), | ||||
|              mock.call(compose, 'amd64', compose.variants['Server'])]) | ||||
|             [mock.call(compose, 'x86_64', compose.variants['Server'], False), | ||||
|              mock.call(compose, 'amd64', compose.variants['Client'], False), | ||||
|              mock.call(compose, 'amd64', compose.variants['Server'], False)]) | ||||
| 
 | ||||
|     @mock.patch('pungi.phases.buildinstall.link_boot_iso') | ||||
|     @mock.patch('pungi.phases.buildinstall.tweak_buildinstall') | ||||
| @ -622,7 +622,7 @@ class TestSymlinkIso(PungiTestCase): | ||||
|         get_file_size.return_value = 1024 | ||||
|         get_mtime.return_value = 13579 | ||||
| 
 | ||||
|         link_boot_iso(self.compose, 'x86_64', self.compose.variants['Server']) | ||||
|         link_boot_iso(self.compose, 'x86_64', self.compose.variants['Server'], False) | ||||
| 
 | ||||
|         tgt = self.topdir + '/compose/Server/x86_64/iso/image-name' | ||||
|         self.assertTrue(os.path.isfile(tgt)) | ||||
| @ -654,6 +654,7 @@ class TestSymlinkIso(PungiTestCase): | ||||
|         self.assertEqual(image.disc_count, 1) | ||||
|         self.assertEqual(image.bootable, True) | ||||
|         self.assertEqual(image.implant_md5, IsoWrapper.get_implanted_md5.return_value) | ||||
|         self.assertEqual(image.can_fail, False) | ||||
|         self.assertEqual(self.compose.im.add.mock_calls, | ||||
|                          [mock.call('Server', 'x86_64', image)]) | ||||
| 
 | ||||
| @ -671,7 +672,7 @@ class TestSymlinkIso(PungiTestCase): | ||||
|         get_file_size.return_value = 1024 | ||||
|         get_mtime.return_value = 13579 | ||||
| 
 | ||||
|         link_boot_iso(self.compose, 'x86_64', self.compose.variants['Server']) | ||||
|         link_boot_iso(self.compose, 'x86_64', self.compose.variants['Server'], True) | ||||
| 
 | ||||
|         tgt = self.topdir + '/compose/Server/x86_64/iso/image-name' | ||||
|         self.assertTrue(os.path.isfile(tgt)) | ||||
| @ -703,6 +704,7 @@ class TestSymlinkIso(PungiTestCase): | ||||
|         self.assertEqual(image.disc_count, 1) | ||||
|         self.assertEqual(image.bootable, True) | ||||
|         self.assertEqual(image.implant_md5, IsoWrapper.get_implanted_md5.return_value) | ||||
|         self.assertEqual(image.can_fail, True) | ||||
|         self.assertEqual(self.compose.im.add.mock_calls, | ||||
|                          [mock.call('Server', 'x86_64', image)]) | ||||
| 
 | ||||
|  | ||||
| @ -71,6 +71,7 @@ class TestImageBuildPhase(PungiTestCase): | ||||
|             "relative_image_dir": 'Client/%(arch)s/images', | ||||
|             "link_type": 'hardlink-or-copy', | ||||
|             "scratch": False, | ||||
|             "failable_arches": [], | ||||
|         } | ||||
|         server_args = { | ||||
|             "format": [('docker', 'tar.xz')], | ||||
| @ -95,6 +96,7 @@ class TestImageBuildPhase(PungiTestCase): | ||||
|             "relative_image_dir": 'Server/%(arch)s/images', | ||||
|             "link_type": 'hardlink-or-copy', | ||||
|             "scratch": False, | ||||
|             "failable_arches": [], | ||||
|         } | ||||
|         self.maxDiff = None | ||||
|         self.assertItemsEqual(phase.pool.queue_put.mock_calls, | ||||
| @ -155,6 +157,7 @@ class TestImageBuildPhase(PungiTestCase): | ||||
|             "relative_image_dir": 'Server/%(arch)s/images', | ||||
|             "link_type": 'hardlink-or-copy', | ||||
|             "scratch": False, | ||||
|             "failable_arches": [], | ||||
|         } | ||||
|         self.maxDiff = None | ||||
|         self.assertItemsEqual(phase.pool.queue_put.mock_calls, | ||||
| @ -249,6 +252,7 @@ class TestImageBuildPhase(PungiTestCase): | ||||
|             "relative_image_dir": 'Server/%(arch)s/images', | ||||
|             "link_type": 'hardlink-or-copy', | ||||
|             "scratch": False, | ||||
|             "failable_arches": [], | ||||
|         }) | ||||
| 
 | ||||
|     @mock.patch('pungi.phases.image_build.ThreadPool') | ||||
| @ -310,6 +314,7 @@ class TestImageBuildPhase(PungiTestCase): | ||||
|             "relative_image_dir": 'Server/%(arch)s/images', | ||||
|             "link_type": 'hardlink-or-copy', | ||||
|             "scratch": False, | ||||
|             "failable_arches": [], | ||||
|         }) | ||||
| 
 | ||||
|     @mock.patch('pungi.phases.image_build.ThreadPool') | ||||
| @ -370,6 +375,7 @@ class TestImageBuildPhase(PungiTestCase): | ||||
|             "relative_image_dir": 'Server/%(arch)s/images', | ||||
|             "link_type": 'hardlink-or-copy', | ||||
|             "scratch": False, | ||||
|             "failable_arches": [], | ||||
|         }) | ||||
| 
 | ||||
|     @mock.patch('pungi.phases.image_build.ThreadPool') | ||||
| @ -621,14 +627,7 @@ class TestCreateImageBuildThread(PungiTestCase): | ||||
|     @mock.patch('pungi.phases.image_build.KojiWrapper') | ||||
|     @mock.patch('pungi.phases.image_build.Linker') | ||||
|     def test_process_handle_fail(self, Linker, KojiWrapper): | ||||
|         compose = DummyCompose(self.topdir, { | ||||
|             'koji_profile': 'koji', | ||||
|             'failable_deliverables': [ | ||||
|                 ('^.*$', { | ||||
|                     '*': ['image-build'] | ||||
|                 }) | ||||
|             ] | ||||
|         }) | ||||
|         compose = DummyCompose(self.topdir, {'koji_profile': 'koji'}) | ||||
|         pool = mock.Mock() | ||||
|         cmd = { | ||||
|             "format": [('docker', 'tar.xz'), ('qcow2', 'qcow2')], | ||||
| @ -653,6 +652,7 @@ class TestCreateImageBuildThread(PungiTestCase): | ||||
|             "relative_image_dir": 'image_dir/Client/%(arch)s', | ||||
|             "link_type": 'hardlink-or-copy', | ||||
|             'scratch': False, | ||||
|             "failable_arches": ['*'], | ||||
|         } | ||||
|         koji_wrapper = KojiWrapper.return_value | ||||
|         koji_wrapper.run_blocking_cmd.return_value = { | ||||
| @ -675,14 +675,7 @@ class TestCreateImageBuildThread(PungiTestCase): | ||||
|     @mock.patch('pungi.phases.image_build.KojiWrapper') | ||||
|     @mock.patch('pungi.phases.image_build.Linker') | ||||
|     def test_process_handle_exception(self, Linker, KojiWrapper): | ||||
|         compose = DummyCompose(self.topdir, { | ||||
|             'koji_profile': 'koji', | ||||
|             'failable_deliverables': [ | ||||
|                 ('^.*$', { | ||||
|                     '*': ['image-build'] | ||||
|                 }) | ||||
|             ] | ||||
|         }) | ||||
|         compose = DummyCompose(self.topdir, {'koji_profile': 'koji'}) | ||||
|         pool = mock.Mock() | ||||
|         cmd = { | ||||
|             "format": [('docker', 'tar.xz'), ('qcow2', 'qcow2')], | ||||
| @ -707,6 +700,7 @@ class TestCreateImageBuildThread(PungiTestCase): | ||||
|             "relative_image_dir": 'image_dir/Client/%(arch)s', | ||||
|             "link_type": 'hardlink-or-copy', | ||||
|             'scratch': False, | ||||
|             "failable_arches": ['*'], | ||||
|         } | ||||
| 
 | ||||
|         koji_wrapper = KojiWrapper.return_value | ||||
|  | ||||
| @ -56,6 +56,7 @@ class TestLiveImagesPhase(PungiTestCase): | ||||
|                                            'type': 'live', | ||||
|                                            'release': '20151203.t.0', | ||||
|                                            'subvariant': 'Client', | ||||
|                                            'failable_arches': [], | ||||
|                                            'ksurl': None}, | ||||
|                                           compose.variants['Client'], | ||||
|                                           'amd64'))]) | ||||
| @ -104,6 +105,7 @@ class TestLiveImagesPhase(PungiTestCase): | ||||
|                                            'type': 'live', | ||||
|                                            'release': '20151203.t.0', | ||||
|                                            'subvariant': 'Client', | ||||
|                                            'failable_arches': [], | ||||
|                                            'ksurl': None}, | ||||
|                                           compose.variants['Client'], | ||||
|                                           'amd64'))]) | ||||
| @ -149,6 +151,7 @@ class TestLiveImagesPhase(PungiTestCase): | ||||
|                                            'type': 'live', | ||||
|                                            'release': '20151203.t.0', | ||||
|                                            'subvariant': 'Client', | ||||
|                                            'failable_arches': [], | ||||
|                                            'ksurl': None}, | ||||
|                                           compose.variants['Client'], | ||||
|                                           'amd64'))]) | ||||
| @ -196,6 +199,7 @@ class TestLiveImagesPhase(PungiTestCase): | ||||
|                                            'type': 'live', | ||||
|                                            'release': None, | ||||
|                                            'subvariant': 'Client', | ||||
|                                            'failable_arches': [], | ||||
|                                            'ksurl': None}, | ||||
|                                           compose.variants['Client'], | ||||
|                                           'amd64')), | ||||
| @ -216,6 +220,7 @@ class TestLiveImagesPhase(PungiTestCase): | ||||
|                                            'type': 'live', | ||||
|                                            'release': None, | ||||
|                                            'subvariant': 'Client', | ||||
|                                            'failable_arches': [], | ||||
|                                            'ksurl': None}, | ||||
|                                           compose.variants['Client'], | ||||
|                                           'amd64'))]) | ||||
| @ -264,6 +269,7 @@ class TestLiveImagesPhase(PungiTestCase): | ||||
|                                            'type': 'appliance', | ||||
|                                            'release': None, | ||||
|                                            'subvariant': 'Client', | ||||
|                                            'failable_arches': [], | ||||
|                                            'ksurl': 'https://git.example.com/kickstarts.git?#CAFEBABE'}, | ||||
|                                           compose.variants['Client'], | ||||
|                                           'amd64'))]) | ||||
| @ -316,6 +322,7 @@ class TestLiveImagesPhase(PungiTestCase): | ||||
|                                            'type': 'appliance', | ||||
|                                            'release': '20151203.t.0', | ||||
|                                            'subvariant': 'Client', | ||||
|                                            'failable_arches': [], | ||||
|                                            'ksurl': 'https://git.example.com/kickstarts.git?#CAFEBABE'}, | ||||
|                                           compose.variants['Client'], | ||||
|                                           'amd64'))]) | ||||
| @ -368,6 +375,7 @@ class TestLiveImagesPhase(PungiTestCase): | ||||
|                                            'type': 'appliance', | ||||
|                                            'release': '20151203.t.0', | ||||
|                                            'subvariant': 'Client', | ||||
|                                            'failable_arches': [], | ||||
|                                            'ksurl': 'https://git.example.com/kickstarts.git?#CAFEBABE'}, | ||||
|                                           compose.variants['Client'], | ||||
|                                           'amd64'))]) | ||||
| @ -415,6 +423,7 @@ class TestLiveImagesPhase(PungiTestCase): | ||||
|                                            'type': 'live', | ||||
|                                            'release': '20151203.t.0', | ||||
|                                            'subvariant': 'Client', | ||||
|                                            'failable_arches': [], | ||||
|                                            'ksurl': None}, | ||||
|                                           compose.variants['Client'], | ||||
|                                           'amd64'))]) | ||||
| @ -665,10 +674,7 @@ class TestCreateLiveImageThread(PungiTestCase): | ||||
|     @mock.patch('pungi.phases.live_images.run') | ||||
|     @mock.patch('pungi.phases.live_images.KojiWrapper') | ||||
|     def test_process_handles_fail(self, KojiWrapper, run, copy2): | ||||
|         compose = DummyCompose(self.topdir, { | ||||
|             'koji_profile': 'koji', | ||||
|             'failable_deliverables': [('^.+$', {'*': ['live']})], | ||||
|         }) | ||||
|         compose = DummyCompose(self.topdir, {'koji_profile': 'koji'}) | ||||
|         pool = mock.Mock() | ||||
|         cmd = { | ||||
|             'ks_file': '/path/to/ks_file', | ||||
| @ -687,6 +693,7 @@ class TestCreateLiveImageThread(PungiTestCase): | ||||
|             'subvariant': 'Client', | ||||
|             'release': 'xyz', | ||||
|             'type': 'live', | ||||
|             'failable_arches': ['*'], | ||||
|         } | ||||
| 
 | ||||
|         koji_wrapper = KojiWrapper.return_value | ||||
| @ -711,10 +718,7 @@ class TestCreateLiveImageThread(PungiTestCase): | ||||
|     @mock.patch('pungi.phases.live_images.run') | ||||
|     @mock.patch('pungi.phases.live_images.KojiWrapper') | ||||
|     def test_process_handles_exception(self, KojiWrapper, run, copy2): | ||||
|         compose = DummyCompose(self.topdir, { | ||||
|             'koji_profile': 'koji', | ||||
|             'failable_deliverables': [('^.+$', {'*': ['live']})], | ||||
|         }) | ||||
|         compose = DummyCompose(self.topdir, {'koji_profile': 'koji'}) | ||||
|         pool = mock.Mock() | ||||
|         cmd = { | ||||
|             'ks_file': '/path/to/ks_file', | ||||
| @ -733,6 +737,7 @@ class TestCreateLiveImageThread(PungiTestCase): | ||||
|             'subvariant': 'Client', | ||||
|             'release': 'xyz', | ||||
|             'type': 'live', | ||||
|             'failable_arches': ['*'], | ||||
|         } | ||||
| 
 | ||||
|         koji_wrapper = KojiWrapper.return_value | ||||
|  | ||||
| @ -73,6 +73,7 @@ class TestLiveMediaPhase(PungiTestCase): | ||||
|                                          'install_tree': self.topdir + '/compose/Server/$basearch/os', | ||||
|                                          'version': 'Rawhide', | ||||
|                                          'subvariant': 'Server', | ||||
|                                          'failable_arches': [], | ||||
|                                      }))]) | ||||
| 
 | ||||
|     @mock.patch('pungi.util.resolve_git_url') | ||||
| @ -133,6 +134,7 @@ class TestLiveMediaPhase(PungiTestCase): | ||||
|                                          'install_tree': self.topdir + '/compose/Server/$basearch/os', | ||||
|                                          'version': 'Rawhide', | ||||
|                                          'subvariant': 'Server', | ||||
|                                          'failable_arches': [], | ||||
|                                      })), | ||||
|                           mock.call((compose, | ||||
|                                      compose.variants['Server'], | ||||
| @ -151,6 +153,7 @@ class TestLiveMediaPhase(PungiTestCase): | ||||
|                                          'install_tree': self.topdir + '/compose/Server/$basearch/os', | ||||
|                                          'version': 'Rawhide', | ||||
|                                          'subvariant': 'Server', | ||||
|                                          'failable_arches': [], | ||||
|                                      })), | ||||
|                           mock.call((compose, | ||||
|                                      compose.variants['Server'], | ||||
| @ -169,6 +172,7 @@ class TestLiveMediaPhase(PungiTestCase): | ||||
|                                          'install_tree': self.topdir + '/compose/Server/$basearch/os', | ||||
|                                          'version': '25', | ||||
|                                          'subvariant': 'Server', | ||||
|                                          'failable_arches': [], | ||||
|                                      }))]) | ||||
| 
 | ||||
|     @mock.patch('pungi.util.resolve_git_url') | ||||
| @ -229,6 +233,7 @@ class TestLiveMediaPhase(PungiTestCase): | ||||
|                                          'install_tree': self.topdir + '/compose/Server/$basearch/os', | ||||
|                                          'version': 'Rawhide', | ||||
|                                          'subvariant': 'Server', | ||||
|                                          'failable_arches': [], | ||||
|                                      })), | ||||
|                           mock.call((compose, | ||||
|                                      compose.variants['Server'], | ||||
| @ -247,6 +252,7 @@ class TestLiveMediaPhase(PungiTestCase): | ||||
|                                          'install_tree': self.topdir + '/compose/Server/$basearch/os', | ||||
|                                          'version': 'Rawhide', | ||||
|                                          'subvariant': 'Server', | ||||
|                                          'failable_arches': [], | ||||
|                                      })), | ||||
|                           mock.call((compose, | ||||
|                                      compose.variants['Server'], | ||||
| @ -265,6 +271,7 @@ class TestLiveMediaPhase(PungiTestCase): | ||||
|                                          'install_tree': self.topdir + '/compose/Server/$basearch/os', | ||||
|                                          'version': '25', | ||||
|                                          'subvariant': 'Server', | ||||
|                                          'failable_arches': [], | ||||
|                                      }))]) | ||||
| 
 | ||||
|     @mock.patch('pungi.phases.livemedia_phase.ThreadPool') | ||||
| @ -335,6 +342,7 @@ class TestLiveMediaPhase(PungiTestCase): | ||||
|                         'version': 'Rawhide', | ||||
|                         'install_tree_from': 'Everything', | ||||
|                         'subvariant': 'Something', | ||||
|                         'failable': ['*'], | ||||
|                     } | ||||
|                 ] | ||||
|             } | ||||
| @ -366,6 +374,7 @@ class TestLiveMediaPhase(PungiTestCase): | ||||
|                                          'install_tree': self.topdir + '/compose/Everything/$basearch/os', | ||||
|                                          'version': 'Rawhide', | ||||
|                                          'subvariant': 'Something', | ||||
|                                          'failable_arches': ['*'], | ||||
|                                      }))]) | ||||
| 
 | ||||
| 
 | ||||
| @ -393,6 +402,7 @@ class TestLiveMediaThread(PungiTestCase): | ||||
|             'title': None, | ||||
|             'version': 'Rawhide', | ||||
|             'subvariant': 'KDE', | ||||
|             'failable_arches': [], | ||||
|         } | ||||
|         pool = mock.Mock() | ||||
| 
 | ||||
| @ -479,9 +489,6 @@ class TestLiveMediaThread(PungiTestCase): | ||||
|     def test_handle_koji_fail(self, KojiWrapper, get_file_size, get_mtime): | ||||
|         compose = DummyCompose(self.topdir, { | ||||
|             'koji_profile': 'koji', | ||||
|             'failable_deliverables': [ | ||||
|                 ('^.+$', {'*': ['live-media']}) | ||||
|             ] | ||||
|         }) | ||||
|         config = { | ||||
|             'arches': ['amd64', 'x86_64'], | ||||
| @ -497,6 +504,7 @@ class TestLiveMediaThread(PungiTestCase): | ||||
|             'title': None, | ||||
|             'version': 'Rawhide', | ||||
|             'subvariant': 'KDE', | ||||
|             'failable_arches': ['*'], | ||||
|         } | ||||
|         pool = mock.Mock() | ||||
| 
 | ||||
| @ -543,6 +551,7 @@ class TestLiveMediaThread(PungiTestCase): | ||||
|             'title': None, | ||||
|             'version': 'Rawhide', | ||||
|             'subvariant': 'KDE', | ||||
|             'failable_arches': ['*'], | ||||
|         } | ||||
|         pool = mock.Mock() | ||||
| 
 | ||||
|  | ||||
| @ -231,6 +231,21 @@ class OSBSThreadTest(helpers.PungiTestCase): | ||||
|         self._assertCorrectCalls({}) | ||||
|         self._assertCorrectMetadata() | ||||
| 
 | ||||
|     @mock.patch('pungi.util.resolve_git_url') | ||||
|     @mock.patch('pungi.phases.osbs.kojiwrapper.KojiWrapper') | ||||
|     def test_run_failable(self, KojiWrapper, resolve_git_url): | ||||
|         cfg = { | ||||
|             'url': 'git://example.com/repo?#HEAD', | ||||
|             'target': 'f24-docker-candidate', | ||||
|             'failable': ['*'] | ||||
|         } | ||||
|         self._setupMock(KojiWrapper, resolve_git_url) | ||||
| 
 | ||||
|         self.t.process((self.compose, self.compose.variants['Server'], cfg), 1) | ||||
| 
 | ||||
|         self._assertCorrectCalls({}) | ||||
|         self._assertCorrectMetadata() | ||||
| 
 | ||||
|     @mock.patch('pungi.util.resolve_git_url') | ||||
|     @mock.patch('pungi.phases.osbs.kojiwrapper.KojiWrapper') | ||||
|     def test_run_with_more_args(self, KojiWrapper, resolve_git_url): | ||||
| @ -296,10 +311,10 @@ class OSBSThreadTest(helpers.PungiTestCase): | ||||
|         cfg = { | ||||
|             'url': 'git://example.com/repo?#HEAD', | ||||
|             'target': 'fedora-24-docker-candidate', | ||||
|             'failable': ['*'] | ||||
|         } | ||||
|         self._setupMock(KojiWrapper, resolve_git_url) | ||||
|         self.wrapper.watch_task.return_value = 1 | ||||
|         self.compose.conf['failable_deliverables'] = [('.*', {'*': ['osbs']})] | ||||
| 
 | ||||
|         self.t.process((self.compose, self.compose.variants['Server'], cfg), 1) | ||||
| 
 | ||||
|  | ||||
| @ -432,14 +432,12 @@ class OstreeThreadTest(helpers.PungiTestCase): | ||||
|             'release_version': 'Rawhide', | ||||
|             'koji_profile': 'koji', | ||||
|             'runroot_tag': 'rrt', | ||||
|             'failable_deliverables': [ | ||||
|                 ('^.+$', {'*': ['ostree-installer']}) | ||||
|             ], | ||||
|         }) | ||||
|         pool = mock.Mock() | ||||
|         cfg = { | ||||
|             'source_repo_from': 'Everything', | ||||
|             'release': None, | ||||
|             'failable': ['x86_64'] | ||||
|         } | ||||
|         koji = KojiWrapper.return_value | ||||
|         koji.run_runroot_cmd.side_effect = helpers.boom | ||||
| @ -466,14 +464,12 @@ class OstreeThreadTest(helpers.PungiTestCase): | ||||
|             'release_version': 'Rawhide', | ||||
|             'koji_profile': 'koji', | ||||
|             'runroot_tag': 'rrt', | ||||
|             'failable_deliverables': [ | ||||
|                 ('^.+$', {'*': ['ostree-installer']}) | ||||
|             ], | ||||
|         }) | ||||
|         pool = mock.Mock() | ||||
|         cfg = { | ||||
|             'source_repo_from': 'Everything', | ||||
|             'release': None, | ||||
|             'failable': ['*'], | ||||
|         } | ||||
|         koji = KojiWrapper.return_value | ||||
|         koji.run_runroot_cmd.return_value = { | ||||
|  | ||||
| @ -155,9 +155,6 @@ class OSTreeThreadTest(helpers.PungiTestCase): | ||||
|         compose = helpers.DummyCompose(self.topdir, { | ||||
|             'koji_profile': 'koji', | ||||
|             'runroot_tag': 'rrt', | ||||
|             'failable_deliverables': [ | ||||
|                 ('^.*$', {'*': ['ostree']}) | ||||
|             ] | ||||
|         }) | ||||
|         pool = mock.Mock() | ||||
|         cfg = { | ||||
| @ -166,6 +163,7 @@ class OSTreeThreadTest(helpers.PungiTestCase): | ||||
|             'config_branch': 'f24', | ||||
|             'treefile': 'fedora-atomic-docker-host.json', | ||||
|             'ostree_repo': self.repo, | ||||
|             'failable': ['*'] | ||||
|         } | ||||
|         koji = KojiWrapper.return_value | ||||
|         koji.run_runroot_cmd.return_value = { | ||||
| @ -192,9 +190,6 @@ class OSTreeThreadTest(helpers.PungiTestCase): | ||||
|         compose = helpers.DummyCompose(self.topdir, { | ||||
|             'koji_profile': 'koji', | ||||
|             'runroot_tag': 'rrt', | ||||
|             'failable_deliverables': [ | ||||
|                 ('^.*$', {'*': ['ostree']}) | ||||
|             ] | ||||
|         }) | ||||
|         pool = mock.Mock() | ||||
|         cfg = { | ||||
| @ -203,6 +198,7 @@ class OSTreeThreadTest(helpers.PungiTestCase): | ||||
|             'config_branch': 'f24', | ||||
|             'treefile': 'fedora-atomic-docker-host.json', | ||||
|             'ostree_repo': self.repo, | ||||
|             'failable': ['*'] | ||||
|         } | ||||
|         koji = KojiWrapper.return_value | ||||
|         koji.run_runroot_cmd.side_effect = helpers.boom | ||||
|  | ||||
| @ -17,12 +17,6 @@ import pungi.phases.test as test_phase | ||||
| from tests.helpers import DummyCompose, PungiTestCase, touch | ||||
| 
 | ||||
| 
 | ||||
| FAILABLE_CONFIG = { | ||||
|     'failable_deliverables': [ | ||||
|         ('^.+$', {'*': ['iso']}), | ||||
|     ] | ||||
| } | ||||
| 
 | ||||
| PAD = '\0' * 100 | ||||
| UNBOOTABLE_ISO = ('\0' * 0x8001) + 'CD001' + PAD | ||||
| ISO_WITH_MBR = ('\0' * 0x1fe) + '\x55\xAA' + ('\0' * 0x7e01) + 'CD001' + PAD | ||||
| @ -40,8 +34,9 @@ class TestCheckImageSanity(PungiTestCase): | ||||
|             test_phase.check_image_sanity(compose) | ||||
| 
 | ||||
|     def test_missing_file_doesnt_report_if_failable(self): | ||||
|         compose = DummyCompose(self.topdir, FAILABLE_CONFIG) | ||||
|         compose = DummyCompose(self.topdir, {}) | ||||
|         compose.image.deliverable = 'iso' | ||||
|         compose.image.can_fail = True | ||||
| 
 | ||||
|         try: | ||||
|             test_phase.check_image_sanity(compose) | ||||
| @ -83,10 +78,11 @@ class TestCheckImageSanity(PungiTestCase): | ||||
|                       str(ctx.exception)) | ||||
| 
 | ||||
|     def test_failable_bootable_iso_without_mbr_gpt_doesnt_raise(self): | ||||
|         compose = DummyCompose(self.topdir, FAILABLE_CONFIG) | ||||
|         compose = DummyCompose(self.topdir, {}) | ||||
|         compose.image.format = 'iso' | ||||
|         compose.image.bootable = True | ||||
|         compose.image.deliverable = 'iso' | ||||
|         compose.image.can_fail = True | ||||
|         touch(os.path.join(self.topdir, 'compose', compose.image.path), UNBOOTABLE_ISO) | ||||
| 
 | ||||
|         try: | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user