diff --git a/setup.cfg b/setup.cfg index 3ab45a1f8..cc0adaeff 100644 --- a/setup.cfg +++ b/setup.cfg @@ -105,6 +105,7 @@ openstack.tripleoclient.v2 = overcloud_ffwd-upgrade_converge = tripleoclient.v1.overcloud_ffwd_upgrade:FFWDUpgradeConverge overcloud_generate_fencing = tripleoclient.v1.overcloud_parameters:GenerateFencingParameters tripleo_container_image_build = tripleoclient.v2.tripleo_container_image:Build + tripleo_container_image_hotfix = tripleoclient.v2.tripleo_container_image:HotFix tripleo_container_image_delete = tripleoclient.v1.container_image:TripleOContainerImageDelete tripleo_container_image_list = tripleoclient.v1.container_image:TripleOContainerImageList tripleo_container_image_show = tripleoclient.v1.container_image:TripleOContainerImageShow diff --git a/tripleoclient/tests/v2/container_image/test_tripleo_container_image.py b/tripleoclient/tests/v2/container_image/test_tripleo_container_image.py index 38640e19e..489a34124 100644 --- a/tripleoclient/tests/v2/container_image/test_tripleo_container_image.py +++ b/tripleoclient/tests/v2/container_image/test_tripleo_container_image.py @@ -221,3 +221,58 @@ class TestContainerImages(deploy_fakes.TestDeployOvercloud): with mock.patch("os.path.isfile", autospec=True) as mock_isfile: mock_isfile.return_value = True self.assertRaises(IOError, self.cmd.take_action, parsed_args) + + +class TestContainerImagesHotfix(deploy_fakes.TestDeployOvercloud): + def setUp(self): + super(TestContainerImagesHotfix, self).setUp() + self.run_ansible_playbook = mock.patch( + "tripleoclient.utils.run_ansible_playbook", autospec=True + ) + self.run_ansible_playbook.start() + self.addCleanup(self.run_ansible_playbook.stop) + self.cmd = tcib.HotFix(self.app, None) + + def _take_action(self, parsed_args): + with mock.patch("os.path.isfile", autospec=True) as mock_isfile: + mock_isfile.return_value = True + self.cmd.take_action(parsed_args) + + def test_image_hotfix(self): + arglist = ["--image", "container1", "--rpms-path", "/opt"] + verifylist = [ + ("images", ["container1"]), + ("rpms_path", "/opt"), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self._take_action(parsed_args=parsed_args) + + def test_image_hotfix_multi_image(self): + arglist = [ + "--image", + "container1", + "--image", + "container2", + "--rpms-path", + "/opt", + ] + verifylist = [ + ("images", ["container1", "container2"]), + ("rpms_path", "/opt"), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self._take_action(parsed_args=parsed_args) + + def test_image_hotfix_missing_args(self): + arglist = [] + verifylist = [] + + self.assertRaises( + deploy_fakes.fakes.utils.ParserException, + self.check_parser, + self.cmd, + arglist, + verifylist, + ) diff --git a/tripleoclient/v2/tripleo_container_image.py b/tripleoclient/v2/tripleo_container_image.py index 9748b616e..5c8c9c37b 100644 --- a/tripleoclient/v2/tripleo_container_image.py +++ b/tripleoclient/v2/tripleo_container_image.py @@ -464,8 +464,7 @@ class Build(command.Command): # "from" option. If the reference "from" # option is valid, it will be used. image_config["tcib_from"] = image_config.get( - "tcib_from", - image_from + "tcib_from", image_from ) tcib_inventory_hosts[image_parsed_name] = image_config @@ -548,3 +547,81 @@ class Build(command.Command): self.log.error( "Buildah failed with the following error: {}".format(exp) ) + + +class HotFix(command.Command): + """Hotfix tripleo container images with tripleo-ansible.""" + + def get_parser(self, prog_name): + parser = super(HotFix, self).get_parser(prog_name) + parser.add_argument( + "--image", + dest="images", + metavar="", + default=[], + action="append", + required=True, + help=_( + "Fully qualified reference to the source image to be " + "modified. Can be specified multiple times (one per " + "image) (default: %(default)s)." + ), + ) + parser.add_argument( + "--rpms-path", + dest="rpms_path", + metavar="", + required=True, + help=_("Path containing RPMs to install (default: %(default)s)."), + ) + parser.add_argument( + "--tag", + dest="tag", + metavar="", + default="latest", + help=_("Image hotfix tag (default: %(default)s)"), + ) + return parser + + def take_action(self, parsed_args): + with utils.TempDirs() as tmp: + tasks = list() + for image in parsed_args.images: + tasks.append( + { + "name": "include ansible-role-tripleo-modify-image", + "import_role": {"name": "tripleo-modify-image"}, + "vars": { + "container_build_tool": "buildah", + "tasks_from": "rpm_install.yml", + "source_image": image, + "rpms_path": parsed_args.rpms_path, + "modified_append_tag": "-{}".format( + parsed_args.tag + ), + "modify_dir_path": tmp, + }, + } + ) + + playbook = os.path.join(tmp, "tripleo-hotfix-playbook.yaml") + playdata = { + "name": "Generate hotfixs", + "connection": "local", + "hosts": "localhost", + "gather_facts": False, + "tasks": tasks, + } + + with open(playbook, "w") as f: + yaml.safe_dump( + [playdata], f, default_flow_style=False, width=4096 + ) + + utils.run_ansible_playbook( + playbook=playbook, + inventory="localhost", + workdir=tmp, + playbook_dir=tmp, + verbosity=utils.playbook_verbosity(self=self), + )