From 182892b1f5d4068621c31b13e9e9c9f04edddb46 Mon Sep 17 00:00:00 2001 From: Kevin Carter Date: Tue, 26 May 2020 12:14:01 -0500 Subject: [PATCH] Add tripleo container image hotfix command This change adds a CLI command to invoke hotfixes via the TripleO modify image role. This CLI command will ensure a uniform experience for operators when needing to apply a hotfixes to containers. Change-Id: I364fb42a41b4cb4ae076f07704c72c6ea442a198 Signed-off-by: Kevin Carter (cherry picked from commit 4967f4f6eec7de8745a43fb1cd73e56ccc50a6a1) (cherry picked from commit 39c270006d98ec5a20992a575cd5a145762bc40d) Signed-off-by: Kevin Carter --- setup.cfg | 1 + .../test_tripleo_container_image.py | 43 ++++++++++ tripleoclient/v1/tripleo_container_image.py | 80 ++++++++++++++++++- 3 files changed, 122 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index c043676b9..c729c4718 100644 --- a/setup.cfg +++ b/setup.cfg @@ -104,6 +104,7 @@ openstack.tripleoclient.v1 = overcloud_execute = tripleoclient.v1.overcloud_execute:RemoteExecute overcloud_generate_fencing = tripleoclient.v1.overcloud_parameters:GenerateFencingParameters tripleo_container_image_build = tripleoclient.v1.tripleo_container_image:Build + tripleo_container_image_hotfix = tripleoclient.v1.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/v1/container_image/test_tripleo_container_image.py b/tripleoclient/tests/v1/container_image/test_tripleo_container_image.py index 7f7a7d255..d66f4a0ab 100644 --- a/tripleoclient/tests/v1/container_image/test_tripleo_container_image.py +++ b/tripleoclient/tests/v1/container_image/test_tripleo_container_image.py @@ -221,3 +221,46 @@ 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) diff --git a/tripleoclient/v1/tripleo_container_image.py b/tripleoclient/v1/tripleo_container_image.py index 96d62d152..3f5d39ba9 100644 --- a/tripleoclient/v1/tripleo_container_image.py +++ b/tripleoclient/v1/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 @@ -556,3 +555,80 @@ 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( + logger=self.log, + workdir=tmp, + playbook=playbook, + inventory="localhost" + )