From 44df642b207b58b692a1dd78077155cfa6237216 Mon Sep 17 00:00:00 2001 From: Lubomír Sedlář Date: Jun 18 2018 09:54:11 +0000 Subject: legacy: Allow marking variant as addon Even though two variants can be created as standalone variants, we may want to mark them as addons in the legacy .composeinfo to make it possible to import to Beaker. Signed-off-by: Lubomír Sedlář --- diff --git a/bin/compose-create-legacy-composeinfo b/bin/compose-create-legacy-composeinfo index f04af21..4c9a3fa 100755 --- a/bin/compose-create-legacy-composeinfo +++ b/bin/compose-create-legacy-composeinfo @@ -20,7 +20,17 @@ def run(opts): compose = productmd.compose.Compose(opts.compose) target = opts.path or compose.compose_path with open(os.path.join(target, '.composeinfo'), 'w') as f: - write_legacy_composeinfo(compose.info, f) + write_legacy_composeinfo(compose.info, f, extra_addon_list=opts.addon) + + +def parse_variant_pair(val): + try: + base, addon = val.split(":") + return (base, addon) + except Exception: + raise argparse.ArgumentParseError( + "%s is not a colon separated pair of variant IDs" % val + ) if __name__ == '__main__': @@ -29,6 +39,13 @@ if __name__ == '__main__': parser.add_argument('-p', '--path', help='Directory in which to write the .composeinfo file. ' 'Defaults to the compose subdirectory.') + parser.add_argument( + "--addon", + metavar="BASE_VARIANT:ADDON", + help="Mark variant ADDON as an addon to BASE_VARIANT", + type=parse_variant_pair, + nargs='+', + ) opts = parser.parse_args() run(opts) diff --git a/compose_utils/legacy.py b/compose_utils/legacy.py index 5ce6473..5810073 100644 --- a/compose_utils/legacy.py +++ b/compose_utils/legacy.py @@ -1,16 +1,22 @@ # -*- coding: utf-8 -*- +from collections import defaultdict +from itertools import chain + from six.moves import configparser -def write_legacy_composeinfo(ci, file): +def write_legacy_composeinfo(ci, file, extra_addon_list=None): """Write a legacy INI-style composeinfo data into `file`.""" conf = configparser.SafeConfigParser(dict_type=SortedDict) _serialize_compose(conf, ci.compose) _serialize_product(conf, ci.release, ci.compose) if ci.release.is_layered: _serialize_base_product(conf, ci.base_product) - _serialize_variants(conf, ci.variants) + extra_addons = defaultdict(set) + for base, addon in extra_addon_list or []: + extra_addons[base].add(addon) + _serialize_variants(conf, ci.variants, extra_addons=extra_addons) conf.write(file) @@ -88,7 +94,7 @@ def _serialize_paths(conf, section, paths, arch): _set("debuginfo", paths.debug_repository, arch) -def _serialize_variant(conf, variant): +def _serialize_variant(conf, variant, extra_addons): section = 'variant-%s' % variant.uid conf.add_section(section) @@ -107,19 +113,23 @@ def _serialize_variant(conf, variant): children = set() for child in variant.variants.values(): - _serialize_variant(conf, child) + _serialize_variant(conf, child, set()) children.add(child.uid) + children |= extra_addons + if children: conf.set(section, 'variants', ','.join(sorted(children))) -def _serialize_variants(conf, variants): - ids = sorted(variants.variants.keys()) +def _serialize_variants(conf, variants, extra_addons): + ids = sorted( + set(variants.variants.keys()) - set(chain.from_iterable(extra_addons.values())) + ) conf.set('product', 'variants', ','.join(ids)) - for variant_id in ids: + for variant_id in sorted(variants.variants): variant = variants.variants[variant_id] - _serialize_variant(conf, variant) + _serialize_variant(conf, variant, extra_addons.get(variant_id, set())) class SortedDict(dict): diff --git a/doc/compose-create-legacy-composeinfo.1 b/doc/compose-create-legacy-composeinfo.1 index 2998ac1..9d1d0a7 100644 --- a/doc/compose-create-legacy-composeinfo.1 +++ b/doc/compose-create-legacy-composeinfo.1 @@ -19,6 +19,10 @@ Set directory in which to create the files. By default the \fIcompose/\fR subdirectory will be used. This is where most consumers expect the file to be. +.TP +.BR \-\-addon = \fIBASE\fR:\fIADDON\fR +Mark \fIADDON\fR variant as an addon to \fIBASE\fR, even though it may not be +created as such. Multiple pairs be specified. .SH BUGS Please report bugs at .br diff --git a/tests/composes/DP-1.0-20160720.t.8/compose/.addon.composeinfo b/tests/composes/DP-1.0-20160720.t.8/compose/.addon.composeinfo new file mode 100644 index 0000000..3fec0e0 --- /dev/null +++ b/tests/composes/DP-1.0-20160720.t.8/compose/.addon.composeinfo @@ -0,0 +1,36 @@ +[compose] +date = 20160720 +id = DP-1.0-20160720.t.8 +respin = 8 +type = test + +[product] +family = Dummy Product +name = DP-1.0-20160720.t.8 +short = DP +type = ga +variants = Server +version = 1.0 + +[variant-Client] +arches = s390x,x86_64 +id = Client +name = Client +type = variant +uid = Client + +[variant-Client.s390x] + +[variant-Client.x86_64] + +[variant-Server] +arches = s390x,x86_64 +id = Server +name = Server +type = variant +uid = Server +variants = Client + +[variant-Server.s390x] + +[variant-Server.x86_64] diff --git a/tests/test_legacy_composeinfo.py b/tests/test_legacy_composeinfo.py index 23fd09d..cb5d891 100644 --- a/tests/test_legacy_composeinfo.py +++ b/tests/test_legacy_composeinfo.py @@ -53,6 +53,26 @@ class LegacyComposeInfoTest(unittest.TestCase): expected = f.read().split() self.assertEqual(output.getvalue().split(), expected) + def test_write_legacy_file__extra_addon(self): + output = StringIO() + # Keep only Server variant and remove all paths for server + server = self.compose.info.variants['Server'] + server.variants = {} + client = self.compose.info.variants['Client'] + client.variants = {} + client.arches = ['s390x', 'x86_64'] + self.compose.info.variants.variants = {'Server': server, 'Client': client} + for v in ('Client', 'Server'): + for type in self.compose.info.variants[v].paths._fields: + setattr(self.compose.info.variants[v].paths, type, {}) + + # Mark Client as addon for Server. + write_legacy_composeinfo(self.compose.info, output, [('Server', 'Client')]) + self.maxDiff = None + with open(os.path.join(BASE_PATH, 'compose', get_expected('.addon'))) as f: + expected = f.read().split() + self.assertEqual(output.getvalue().split(), expected) + if __name__ == '__main__': unittest.main()