From 1ae4be91077978e324f2e463eaa97dcccfdb7057 Mon Sep 17 00:00:00 2001 From: Ivan Devat Date: Thu, 20 Jun 2019 11:44:46 +0200 Subject: [PATCH 2/3] squash bz1657166 Updating a bundle is a bit cumber disallow to specify container type in bundle reset fix id conflict in bundle reset --- pcs/cli/resource/parse_args.py | 21 ++ pcs/lib/cib/resource/bundle.py | 216 +++++++------- pcs/lib/commands/resource.py | 41 +-- pcs/lib/xml_tools.py | 15 + pcs/pcs.8 | 2 +- pcs/resource.py | 35 ++- pcs/usage.py | 2 +- pcs_test/tier0/cib_resource/test_bundle.py | 2 +- .../lib/commands/resource/bundle_common.py | 4 +- .../commands/resource/test_bundle_create.py | 6 +- .../commands/resource/test_bundle_reset.py | 263 +++++++++++++++--- 11 files changed, 402 insertions(+), 205 deletions(-) diff --git a/pcs/cli/resource/parse_args.py b/pcs/cli/resource/parse_args.py index 88da12a6..ff86f477 100644 --- a/pcs/cli/resource/parse_args.py +++ b/pcs/cli/resource/parse_args.py @@ -101,6 +101,27 @@ def parse_bundle_create_options(arg_list): } return parts +def parse_bundle_reset_options(arg_list): + """ + Commandline options: no options + """ + groups = _parse_bundle_groups(arg_list) + container_options = groups.get("container", []) + parts = { + "container": prepare_options(container_options), + "network": prepare_options(groups.get("network", [])), + "port_map": [ + prepare_options(port_map) + for port_map in groups.get("port-map", []) + ], + "storage_map": [ + prepare_options(storage_map) + for storage_map in groups.get("storage-map", []) + ], + "meta": prepare_options(groups.get("meta", [])) + } + return parts + def _split_bundle_map_update_op_and_options( map_arg_list, result_parts, map_name ): diff --git a/pcs/lib/cib/resource/bundle.py b/pcs/lib/cib/resource/bundle.py index 5c2910c8..2b80608e 100644 --- a/pcs/lib/cib/resource/bundle.py +++ b/pcs/lib/cib/resource/bundle.py @@ -18,6 +18,7 @@ from pcs.lib.xml_tools import ( append_when_useful, get_sub_element, update_attributes_remove_empty, + reset_element, ) TAG = "bundle" @@ -84,15 +85,13 @@ def validate_new( id_provider=id_provider ).validate({"id": bundle_id}) + - validate_reset( - id_provider, - container_type, - container_options, - network_options, - port_map, - storage_map, - force_options - ) + _validate_container(container_type, container_options, force_options) + + + _validate_network_options_new(network_options, force_options) + + + _validate_port_map_list(port_map, id_provider, force_options) + + + _validate_storage_map_list(storage_map, id_provider, force_options) ) def append_new( @@ -130,14 +129,14 @@ def append_new( return bundle_element def validate_reset( - id_provider, container_type, container_options, network_options, - port_map, storage_map, force_options=False + id_provider, bundle_el, container_options, network_options, port_map, + storage_map, force_options=False ): """ Validate bundle parameters, return list of report items IdProvider id_provider -- elements' ids generator and uniqueness checker - string container_type -- bundle container type + etree bundle_el -- the bundle to be reset dict container_options -- container options dict network_options -- network options list of dict port_map -- list of port mapping options @@ -145,7 +144,7 @@ def validate_reset( bool force_options -- return warnings instead of forceable errors """ return ( - _validate_container(container_type, container_options, force_options) + _validate_container_reset(bundle_el, container_options, force_options) + _validate_network_options_new(network_options, force_options) + @@ -154,72 +153,46 @@ def validate_reset( _validate_storage_map_list(storage_map, id_provider, force_options) ) -def reset( - bundle_element, id_provider, bundle_id, container_type, container_options, - network_options, port_map, storage_map, meta_attributes -): +def validate_reset_to_minimal(bundle_element): """ - Remove configuration of bundle_element and create new one. + Validate removing configuration of bundle_element and keep the minimal one. etree bundle_element -- the bundle element that will be reset - IdProvider id_provider -- elements' ids generator - string bundle_id -- id of the bundle - string container_type -- bundle container type - dict container_options -- container options - dict network_options -- network options - list of dict port_map -- list of port mapping options - list of dict storage_map -- list of storage mapping options - dict meta_attributes -- meta attributes """ - # pylint: disable=too-many-arguments + if not _is_supported_container(_get_container_element(bundle_element)): + return [_get_report_unsupported_container(bundle_element)] + return [] - # Old bundle configuration is removed and re-created. We aren't trying - # to keep ids: - # * It doesn't make sense to reference these ids. - # * Newly created ids are based on (are prefixed by) the bundle element id, - # which does not change. Therefore, it is VERY HIGHLY probable the newly - # created ids will be the same as the original ones. - elements_without_reset_impact = [] +def reset_to_minimal(bundle_element): + """ + Remove configuration of bundle_element and keep the minimal one. + etree bundle_element -- the bundle element that will be reset + """ # Elements network, storage and meta_attributes must be kept even if they # are without children. # See https://bugzilla.redhat.com/show_bug.cgi?id=1642514 - # - # The only scenario that makes sense is that these elements are empty - # and no attributes or children are requested for them. So we collect only - # deleted tags and we will ensure creation minimal relevant elements at - # least. - indelible_tags = [] - for child in list(bundle_element): - if child.tag in ["network", "storage", META_ATTRIBUTES_TAG]: - indelible_tags.append(child.tag) - elif child.tag not in list(GENERIC_CONTAINER_TYPES): - # Only primitive should be found here, currently. - # The order of various element tags has no practical impact so we - # don't care about it here. - elements_without_reset_impact.append(child) - bundle_element.remove(child) + # Element of container type is required. - _append_container(bundle_element, container_type, container_options) - if network_options or port_map or "network" in indelible_tags: - _append_network( - bundle_element, - id_provider, - bundle_id, - network_options, - port_map, - ) - if storage_map or "storage" in indelible_tags: - _append_storage(bundle_element, id_provider, bundle_id, storage_map) - if meta_attributes or META_ATTRIBUTES_TAG in indelible_tags: - append_new_meta_attributes( - bundle_element, - meta_attributes, - id_provider, - enforce_append=True, - ) - for element in elements_without_reset_impact: - bundle_element.append(element) + # There can be other elements beside bundle configuration (e.g. primitive). + # These elements stay untouched. + # Like any function that manipulates with cib, this also assumes prior + # validation that container is supported. + for child in list(bundle_element): + if child.tag in ["network", "storage"]: + reset_element(child) + if child.tag == META_ATTRIBUTES_TAG: + reset_element(child, keep_attrs=["id"]) + if child.tag in list(GENERIC_CONTAINER_TYPES): + # GENERIC_CONTAINER_TYPES elements require the "image" attribute to + # be set. + reset_element(child, keep_attrs=["image"]) + +def _get_report_unsupported_container(bundle_el): + return reports.resource_bundle_unsupported_container_type( + bundle_el.get("id"), + GENERIC_CONTAINER_TYPES, + ) def validate_update( id_provider, bundle_el, container_options, network_options, @@ -240,65 +213,26 @@ def validate_update( list of string storage_map_remove -- list of storage mapping ids to remove bool force_options -- return warnings instead of forceable errors """ - report_list = [] - - # validate container options only if they are being updated - if container_options: - container_el = _get_container_element(bundle_el) - if ( - container_el is not None - and - container_el.tag in GENERIC_CONTAINER_TYPES - ): - report_list.extend( - _validate_generic_container_options_update( - container_el, - container_options, - force_options - ) - ) - else: - report_list.append( - reports.resource_bundle_unsupported_container_type( - bundle_el.get("id"), GENERIC_CONTAINER_TYPES - ) - ) - - network_el = bundle_el.find("network") - if network_el is None: - report_list.extend( - _validate_network_options_new(network_options, force_options) - ) - else: - report_list.extend( - _validate_network_options_update( - bundle_el, - network_el, - network_options, - force_options - ) - ) - # TODO It will probably be needed to split the following validators to # create and update variants. It should be done once the need exists and # not sooner. - report_list.extend( + return ( + _validate_container_update(bundle_el, container_options, force_options) + + + _validate_network_update(bundle_el, network_options, force_options) + + _validate_port_map_list(port_map_add, id_provider, force_options) - ) - report_list.extend( + + _validate_storage_map_list(storage_map_add, id_provider, force_options) - ) - report_list.extend( + + _validate_map_ids_exist( bundle_el, "port-mapping", "port-map", port_map_remove ) - ) - report_list.extend( + + _validate_map_ids_exist( bundle_el, "storage-mapping", "storage-map", storage_map_remove ) ) - return report_list def update( id_provider, bundle_el, container_options, network_options, @@ -420,8 +354,15 @@ def get_inner_resource(bundle_el): return resources[0] return None +def _is_supported_container(container_el): + return ( + container_el is not None + and + container_el.tag in GENERIC_CONTAINER_TYPES + ) + def _validate_container(container_type, container_options, force_options=False): - if not container_type in GENERIC_CONTAINER_TYPES: + if container_type not in GENERIC_CONTAINER_TYPES: return [ reports.invalid_option_value( "container type", @@ -429,7 +370,10 @@ def _validate_container(container_type, container_options, force_options=False): GENERIC_CONTAINER_TYPES, ) ] + return _validate_generic_container_options(container_options, force_options) + +def _validate_generic_container_options(container_options, force_options=False): validators = [ validate.NamesIn( GENERIC_CONTAINER_OPTIONS, @@ -463,8 +407,32 @@ def _validate_container(container_type, container_options, force_options=False): deprecation_reports ) +def _validate_container_reset(bundle_el, container_options, force_options): + # Unlike in the case of update, in reset empty options are not necessary + # valid - user MUST set everything (including required options e.g. image). + if ( + container_options + and + not _is_supported_container(_get_container_element(bundle_el)) + ): + return [_get_report_unsupported_container(bundle_el)] + return _validate_generic_container_options(container_options, force_options) + +def _validate_container_update(bundle_el, options, force_options): + # Validate container options only if they are being updated. Empty options + # are valid - user DOESN'T NEED to change anything. + if not options: + return [] + + container_el = _get_container_element(bundle_el) + if not _is_supported_container(container_el): + return [_get_report_unsupported_container(bundle_el)] + return _validate_generic_container_options_update( + container_el, options, force_options + ) + def _validate_generic_container_options_update( - docker_el, options, force_options + container_el, options, force_options ): validators_optional_options = [ validate.ValueNonnegativeInteger("masters"), @@ -517,7 +485,7 @@ def _validate_generic_container_options_update( if ( options.get("masters") and - docker_el.get("promoted-max") and options.get("promoted-max") != "" + container_el.get("promoted-max") and options.get("promoted-max") != "" ): deprecation_reports.append( reports.prerequisite_option_must_not_be_set( @@ -527,7 +495,7 @@ def _validate_generic_container_options_update( if ( options.get("promoted-max") and - docker_el.get("masters") and options.get("masters") != "" + container_el.get("masters") and options.get("masters") != "" ): deprecation_reports.append( reports.prerequisite_option_must_not_be_set( @@ -571,6 +539,14 @@ def _is_pcmk_remote_acccessible_after_update(network_el, options): return not (case1 or case2 or case3) +def _validate_network_update(bundle_el, options, force_options): + network_el = bundle_el.find("network") + if network_el is None: + return _validate_network_options_new(options, force_options) + return _validate_network_options_update( + bundle_el, network_el, options, force_options + ) + def _validate_network_options_update( bundle_el, network_el, options, force_options ): diff --git a/pcs/lib/commands/resource.py b/pcs/lib/commands/resource.py index 89e7f225..af648022 100644 --- a/pcs/lib/commands/resource.py +++ b/pcs/lib/commands/resource.py @@ -180,7 +180,9 @@ def _check_special_cases( _find_bundle = partial(find_element_by_tag_and_id, resource.bundle.TAG) -def _get_required_cib_version_for_container(container_type, container_options): +def _get_required_cib_version_for_container( + container_options, container_type=None +): if container_type == "podman": return Version(3, 2, 0) @@ -567,8 +569,8 @@ def bundle_create( resource.common.are_meta_disabled(meta_attributes) ), required_cib_version=_get_required_cib_version_for_container( + container_options, container_type, - container_options ), ) as resources_section: # no need to run validations related to remote and guest nodes as those @@ -602,7 +604,7 @@ def bundle_create( resource.common.disable(bundle_element, id_provider) def bundle_reset( - env, bundle_id, container_type, container_options=None, + env, bundle_id, container_options=None, network_options=None, port_map=None, storage_map=None, meta_attributes=None, force_options=False, ensure_disabled=False, @@ -614,7 +616,6 @@ def bundle_reset( LibraryEnvironment env -- provides communication with externals string bundle_id -- id of the bundle to reset - string container_type -- container engine name (docker, lxc...) dict container_options -- container options dict network_options -- network options list of dict port_map -- a list of port mapping options @@ -640,15 +641,20 @@ def bundle_reset( resource.common.are_meta_disabled(meta_attributes) ), required_cib_version=_get_required_cib_version_for_container( - container_type, container_options ), ) as resources_section: + bundle_element = _find_bundle(resources_section, bundle_id) + env.report_processor.process_list( + resource.bundle.validate_reset_to_minimal(bundle_element) + ) + resource.bundle.reset_to_minimal(bundle_element) + id_provider = IdProvider(resources_section) env.report_processor.process_list( resource.bundle.validate_reset( id_provider, - container_type, + bundle_element, container_options, network_options, port_map, @@ -658,23 +664,21 @@ def bundle_reset( ) ) - bundle_element = _find_bundle(resources_section, bundle_id) - resource.bundle.reset( - bundle_element, + resource.bundle.update( id_provider, - bundle_id, - container_type, + bundle_element, container_options, network_options, - port_map, - storage_map, - meta_attributes, + port_map_add=port_map, + port_map_remove=[], + storage_map_add=storage_map, + storage_map_remove=[], + meta_attributes=meta_attributes, ) if ensure_disabled: resource.common.disable(bundle_element, id_provider) - def bundle_update( env, bundle_id, container_options=None, network_options=None, port_map_add=None, port_map_remove=None, storage_map_add=None, @@ -706,14 +710,13 @@ def bundle_update( storage_map_remove = storage_map_remove or [] meta_attributes = meta_attributes or {} - required_cib_version = Version(2, 8, 0) - if "promoted-max" in container_options: - required_cib_version = Version(3, 0, 0) with resource_environment( env, wait, [bundle_id], - required_cib_version=required_cib_version + required_cib_version=_get_required_cib_version_for_container( + container_options + ), ) as resources_section: # no need to run validations related to remote and guest nodes as those # nodes can only be created from primitive resources diff --git a/pcs/lib/xml_tools.py b/pcs/lib/xml_tools.py index 43cde3b5..c058c288 100644 --- a/pcs/lib/xml_tools.py +++ b/pcs/lib/xml_tools.py @@ -154,3 +154,18 @@ def remove_when_pointless(element, attribs_important=True): """ if not is_element_useful(element, attribs_important): element.getparent().remove(element) + +def reset_element(element, keep_attrs=None): + """ + Remove all subelements and all attributes (except mentioned in keep_attrs) + of given element. + + lxml.etree.element element -- element to reset + list keep_attrs -- names of attributes thas should be kept + """ + keep_attrs = keep_attrs or [] + for child in list(element): + element.remove(child) + for key in element.attrib.keys(): + if key not in keep_attrs: + del element.attrib[key] diff --git a/pcs/pcs.8 b/pcs/pcs.8 index 1e794c60..4ae646e2 100644 --- a/pcs/pcs.8 +++ b/pcs/pcs.8 @@ -191,7 +191,7 @@ Remove the clone which contains the specified group or resource (the resource or bundle create container [] [network ] [port\-map ]... [storage\-map ]... [meta ] [\fB\-\-disabled\fR] [\fB\-\-wait\fR[=n]] Create a new bundle encapsulating no resources. The bundle can be used either as it is or a resource may be put into it at any time. If \fB\-\-disabled\fR is specified, the bundle is not started automatically. If \fB\-\-wait\fR is specified, pcs will wait up to 'n' seconds for the bundle to start and then return 0 on success or 1 on error. If 'n' is not specified it defaults to 60 minutes. .TP -bundle reset container [] [network ] [port\-map ]... [storage\-map ]... [meta ] [\fB\-\-disabled\fR] [\fB\-\-wait\fR[=n]] +bundle reset [container ] [network ] [port\-map ]... [storage\-map ]... [meta ] [\fB\-\-disabled\fR] [\fB\-\-wait\fR[=n]] Configure specified bundle with given options. Unlike bundle update, this command resets the bundle according given options - no previous options are kept. Resources inside the bundle are kept as they are. If \fB\-\-disabled\fR is specified, the bundle is not started automatically. If \fB\-\-wait\fR is specified, pcs will wait up to 'n' seconds for the bundle to start and then return 0 on success or 1 on error. If 'n' is not specified it defaults to 60 minutes. .TP bundle update [container ] [network ] [port\-map (add ) | (delete | remove ...)]... [storage\-map (add ) | (delete | remove ...)]... [meta ] [\fB\-\-wait\fR[=n]] diff --git a/pcs/resource.py b/pcs/resource.py index a29ace5f..973f9e64 100644 --- a/pcs/resource.py +++ b/pcs/resource.py @@ -22,6 +22,7 @@ from pcs.cli.common.parse_args import ( ) from pcs.cli.resource.parse_args import ( parse_bundle_create_options, + parse_bundle_reset_options, parse_bundle_update_options, parse_create as parse_create_args, ) @@ -2929,19 +2930,26 @@ def resource_bundle_create_cmd(lib, argv, modifiers): * --wait * -f - CIB file """ - _resource_bundle_configure(lib.resource.bundle_create, argv, modifiers) + modifiers.ensure_only_supported("--force", "--disabled", "--wait", "-f") + if not argv: + raise CmdLineInputError() -def resource_bundle_reset_cmd(lib, argv, modifiers): - """ - Options: - * --force - allow unknown options - * --disabled - create as a stopped bundle - * --wait - * -f - CIB file - """ - _resource_bundle_configure(lib.resource.bundle_reset, argv, modifiers) + bundle_id = argv[0] + parts = parse_bundle_create_options(argv[1:]) + lib.resource.bundle_create( + bundle_id, + parts["container_type"], + container_options=parts["container"], + network_options=parts["network"], + port_map=parts["port_map"], + storage_map=parts["storage_map"], + meta_attributes=parts["meta"], + force_options=modifiers.get("--force"), + ensure_disabled=modifiers.get("--disabled"), + wait=modifiers.get("--wait"), + ) -def _resource_bundle_configure(call_lib, argv, modifiers): +def resource_bundle_reset_cmd(lib, argv, modifiers): """ Options: * --force - allow unknown options @@ -2954,10 +2962,9 @@ def _resource_bundle_configure(call_lib, argv, modifiers): raise CmdLineInputError() bundle_id = argv[0] - parts = parse_bundle_create_options(argv[1:]) - call_lib( + parts = parse_bundle_reset_options(argv[1:]) + lib.resource.bundle_reset( bundle_id, - parts["container_type"], container_options=parts["container"], network_options=parts["network"], port_map=parts["port_map"], diff --git a/pcs/usage.py b/pcs/usage.py index 2566d522..c0c6d712 100644 --- a/pcs/usage.py +++ b/pcs/usage.py @@ -476,7 +476,7 @@ Commands: to start and then return 0 on success or 1 on error. If 'n' is not specified it defaults to 60 minutes. - bundle reset container [] + bundle reset [container ] [network ] [port-map ]... [storage-map ]... [meta ] [--disabled] [--wait[=n]] diff --git a/pcs_test/tier0/cib_resource/test_bundle.py b/pcs_test/tier0/cib_resource/test_bundle.py index b80b0606..96fcc082 100644 --- a/pcs_test/tier0/cib_resource/test_bundle.py +++ b/pcs_test/tier0/cib_resource/test_bundle.py @@ -79,7 +79,7 @@ class BundleReset(BundleCreateCommon): "resource bundle create B2 container docker image=pcs:test" ) self.assert_effect( - "resource bundle reset B1 container docker image=pcs:new", + "resource bundle reset B1 container image=pcs:new", """ diff --git a/pcs_test/tier0/lib/commands/resource/bundle_common.py b/pcs_test/tier0/lib/commands/resource/bundle_common.py index 4fbf00e4..5d7a4b42 100644 --- a/pcs_test/tier0/lib/commands/resource/bundle_common.py +++ b/pcs_test/tier0/lib/commands/resource/bundle_common.py @@ -50,13 +50,13 @@ class SetUpMixin: ) class UpgradeMixin(FixturesMixin): - upgraded_cib_filename = None + old_version_cib_filename = None def test_cib_upgrade(self): (self.config .runner.cib.load( name="load_cib_old_version", - filename=self.upgraded_cib_filename, + filename=self.old_version_cib_filename, before="runner.cib.load" ) .runner.cib.upgrade(before="runner.cib.load") diff --git a/pcs_test/tier0/lib/commands/resource/test_bundle_create.py b/pcs_test/tier0/lib/commands/resource/test_bundle_create.py index 0bc9dc65..ba1ee0ac 100644 --- a/pcs_test/tier0/lib/commands/resource/test_bundle_create.py +++ b/pcs_test/tier0/lib/commands/resource/test_bundle_create.py @@ -82,16 +82,16 @@ class CreateParametrizedContainerMixin( class CreateDocker(CreateParametrizedContainerMixin, TestCase): container_type = "docker" - upgraded_cib_filename = "cib-empty-2.0.xml" + old_version_cib_filename = "cib-empty-2.0.xml" class CreatePodman(CreateParametrizedContainerMixin, TestCase): container_type = "podman" - upgraded_cib_filename = "cib-empty-3.1.xml" + old_version_cib_filename = "cib-empty-3.1.xml" class CreateRkt(CreateParametrizedContainerMixin, TestCase): container_type = "rkt" - upgraded_cib_filename = "cib-empty-2.9.xml" + old_version_cib_filename = "cib-empty-2.9.xml" class CreateWithNetwork(CreateCommandMixin, NetworkMixin, TestCase): container_type = "docker" diff --git a/pcs_test/tier0/lib/commands/resource/test_bundle_reset.py b/pcs_test/tier0/lib/commands/resource/test_bundle_reset.py index e5d6ef16..cfc84500 100644 --- a/pcs_test/tier0/lib/commands/resource/test_bundle_reset.py +++ b/pcs_test/tier0/lib/commands/resource/test_bundle_reset.py @@ -12,6 +12,7 @@ from pcs_test.tier0.lib.commands.resource.bundle_common import( AllOptionsMixin, WaitMixin, ) +from pcs_test.tools import fixture from pcs.common import report_codes from pcs.lib.commands.resource import bundle_reset @@ -26,16 +27,13 @@ class BaseMixin(FixturesMixin): def initial_resources(self): return self.fixture_resources_bundle_simple - def bundle_reset( - self, bundle_id=None, container_type=None, **params - ): + def bundle_reset(self, bundle_id=None, **params): if "container_options" not in params: params["container_options"] = {"image": self.image} bundle_reset( self.env_assist.get_env(), bundle_id=bundle_id or self.bundle_id, - container_type=container_type or self.container_type, **params ) @@ -44,10 +42,11 @@ class BaseMixin(FixturesMixin): class MinimalMixin(BaseMixin, SetUpMixin): container_type = None - new_container_type = None initial_cib_filename = "cib-empty-3.2.xml" def test_success_zero_change(self): + # Resets a bundle with only an image set to a bundle with the same + # image set and no other options. self.config.env.push_cib(resources=self.initial_resources) self.bundle_reset() @@ -63,13 +62,12 @@ class MinimalMixin(BaseMixin, SetUpMixin): """ .format( bundle_id=self.bundle_id, - container_type=self.new_container_type, + container_type=self.container_type, image=new_image, ) , }) self.bundle_reset( - container_type=self.new_container_type, container_options={"image": new_image}, ) @@ -92,9 +90,20 @@ class MinimalMixin(BaseMixin, SetUpMixin): expected_in_processor=False, ) + def test_no_options_set(self): + self.env_assist.assert_raise_library_error( + lambda: bundle_reset(self.env_assist.get_env(), self.bundle_id), + [ + fixture.error( + report_codes.REQUIRED_OPTIONS_ARE_MISSING, + option_names=["image"], + option_type="container", + ), + ] + ) + class FullMixin(SetUpMixin, BaseMixin): container_type = None - new_container_type = None fixture_primitive = """ """ @@ -104,25 +113,12 @@ class FullMixin(SetUpMixin, BaseMixin): return """ - - - <{container_type} image="{image}" promoted-max="0" - replicas="0" - replicas-per-host="0" + replicas="1" + replicas-per-host="1" /> - - - + + + {fixture_primitive} @@ -174,7 +177,7 @@ class FullMixin(SetUpMixin, BaseMixin): """ .format( - container_type=self.new_container_type, + container_type=self.container_type, bundle_id=self.bundle_id, fixture_primitive=self.fixture_primitive, image=new_image, @@ -183,7 +186,6 @@ class FullMixin(SetUpMixin, BaseMixin): }) self.bundle_reset( - container_type=self.new_container_type, container_options={"image": new_image}, ) @@ -220,12 +222,13 @@ class FullMixin(SetUpMixin, BaseMixin): - @@ -234,7 +237,7 @@ class FullMixin(SetUpMixin, BaseMixin): """ .format( - container_type=self.new_container_type, + container_type=self.container_type, bundle_id=self.bundle_id, fixture_primitive=self.fixture_primitive, image=new_image, @@ -242,7 +245,6 @@ class FullMixin(SetUpMixin, BaseMixin): , }) self.bundle_reset( - container_type=self.new_container_type, container_options={ "image": new_image, "promoted-max": "1", @@ -262,8 +264,8 @@ class FullMixin(SetUpMixin, BaseMixin): storage_map=[ { "options": "extra options 2", - "source-dir": f"/tmp/{self.new_container_type}2a", - "target-dir": f"/tmp/{self.new_container_type}2b", + "source-dir": f"/tmp/{self.container_type}2aa", + "target-dir": f"/tmp/{self.container_type}2bb", }, ], meta_attributes={ @@ -271,6 +273,80 @@ class FullMixin(SetUpMixin, BaseMixin): } ) + def test_success_keep_map_ids(self): + self.config.env.push_cib(replace={ + ".//resources/bundle/network": + f""" + + + + + """ + , + ".//resources/bundle/storage": + f""" + + + + """ + , + }) + + # Every value is kept as before except port_map and storage_map. + self.bundle_reset( + container_options={ + "image": self.image, + "promoted-max": "0", + "replicas": "1", + "replicas-per-host": "1", + }, + network_options={ + "control-port": "12345", + "host-interface": "eth0", + "host-netmask": "24", + "ip-range-start": "192.168.100.200", + }, + port_map=[ + { + "id": f"{self.bundle_id}-port-map-1001", + "internal-port": "3002", + "port": "3000", + }, + { + "id": f"{self.bundle_id}-port-map-3000-3300", + "range": "4000-4400", + }, + ], + storage_map=[ + { + "id": f"{self.bundle_id}-storage-map", + "options": "extra options 2", + "source-dir": f"/tmp/{self.container_type}2aa", + "target-dir": f"/tmp/{self.container_type}2bb", + }, + ], + meta_attributes={ + "target-role": "Stopped", + } + ) + class ResetParametrizedContainerMixin( BaseMixin, ParametrizedContainerMixin, UpgradeMixin ): @@ -278,39 +354,33 @@ class ResetParametrizedContainerMixin( class MinimalRkt(MinimalMixin, TestCase): container_type = "rkt" - new_container_type = "docker" class MinimalPodman(MinimalMixin, TestCase): container_type = "podman" - new_container_type = "rkt" class MinimalDocker(MinimalMixin, TestCase): container_type = "docker" - new_container_type = "rkt" class FullRkt(FullMixin, TestCase): container_type = "rkt" - new_container_type = "docker" class FullPodman(FullMixin, TestCase): container_type = "podman" - new_container_type = "rkt" class FullDocker(FullMixin, TestCase): container_type = "docker" - new_container_type = "docker" -class CreateParametrizedPodman(ResetParametrizedContainerMixin, TestCase): +class ResetParametrizedPodman(ResetParametrizedContainerMixin, TestCase): container_type = "podman" - upgraded_cib_filename = "cib-empty-3.1.xml" + old_version_cib_filename = "cib-empty-2.6.xml" -class CreateParametrizedDocker(ResetParametrizedContainerMixin, TestCase): +class ResetParametrizedDocker(ResetParametrizedContainerMixin, TestCase): container_type = "docker" - upgraded_cib_filename = "cib-empty-2.0.xml" + old_version_cib_filename = "cib-empty-2.0.xml" -class CreateParametrizedRkt(ResetParametrizedContainerMixin, TestCase): +class ResetParametrizedRkt(ResetParametrizedContainerMixin, TestCase): container_type = "rkt" - upgraded_cib_filename = "cib-empty-2.9.xml" + old_version_cib_filename = "cib-empty-2.6.xml" class ResetWithNetwork(BaseMixin, NetworkMixin, TestCase): container_type = "docker" @@ -323,9 +393,114 @@ class ResetWithStorageMap(BaseMixin, StorageMapMixin, TestCase): class ResetWithMetaMap(BaseMixin, MetaMixin, TestCase): container_type = "docker" + def test_success(self): + # When there is no meta attributes the new one are put on the first + # possition (since reset now uses update internally). This is the reason + # for reimplementation of this MetaMixin test. + self.config.env.push_cib( + resources=""" + + + + + + + <{container_type} image="{image}" /> + + + """ + .format( + container_type=self.container_type, + bundle_id=self.bundle_id, + image=self.image, + ) + ) + self.run_bundle_cmd( + meta_attributes={ + "target-role": "Stopped", + "is-managed": "false", + } + ) class ResetWithAllOptions(BaseMixin, AllOptionsMixin, TestCase): container_type = "docker" class ResetWithWait(BaseMixin, WaitMixin, TestCase): container_type = "docker" + +class ResetUnknownContainerType(BaseMixin, SetUpMixin, TestCase): + container_type = "unknown" + def test_error_or_unknown_container(self): + self.env_assist.assert_raise_library_error( + lambda: bundle_reset(self.env_assist.get_env(), self.bundle_id), + [ + fixture.error( + report_codes.RESOURCE_BUNDLE_UNSUPPORTED_CONTAINER_TYPE, + bundle_id="B1", + supported_container_types=["docker", "podman", "rkt"], + ), + ] + ) + +class NoMetaIdRegenerationMixin(BaseMixin, SetUpMixin): + @property + def initial_resources(self): + return """ + + + <{container_type} + image="{image}" + promoted-max="0" + replicas="1" + replicas-per-host="1" + /> + + + + + + """.format( + container_type=self.container_type, + bundle_id=self.bundle_id, + image=self.image, + ) + def test_dont_regenerate_meta_attributes_id(self): + self.config.env.push_cib(replace={ + ".//resources/bundle/meta_attributes": + f""" + + + + """ + , + }) + self.bundle_reset( + container_options={ + "image": self.image, + "promoted-max": "0", + "replicas": "1", + "replicas-per-host": "1", + }, + meta_attributes={ + "target-role": "Stopped", + } + ) + +class NoMetaIdRegenerationDocker(NoMetaIdRegenerationMixin, TestCase): + container_type = "docker" + +class NoMetaIdRegenerationPodman(NoMetaIdRegenerationMixin, TestCase): + container_type = "podman" + +class NoMetaIdRegenerationRkt(NoMetaIdRegenerationMixin, TestCase): + container_type = "rkt" -- 2.21.0