diff --git a/releasenotes/notes/tripleo-container-image-show-af7453683ad74182.yaml b/releasenotes/notes/tripleo-container-image-show-af7453683ad74182.yaml new file mode 100644 index 000000000..f70815e86 --- /dev/null +++ b/releasenotes/notes/tripleo-container-image-show-af7453683ad74182.yaml @@ -0,0 +1,8 @@ +--- +features: + - | + With the new podman container setup comes an Apache served local image + registry. + + `openstack tripleo container image show` will perform an inspection on + a given image, and present the details. diff --git a/setup.cfg b/setup.cfg index 08eec4f1d..9a130ae95 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_list = tripleoclient.v1.container_image:TripleOContainerImageList + tripleo_container_image_show = tripleoclient.v1.container_image:TripleOContainerImageShow tripleo_container_image_prepare = tripleoclient.v1.container_image:TripleOImagePrepare tripleo_container_image_prepare_default = tripleoclient.v1.container_image:TripleOImagePrepareDefault undercloud_deploy = tripleoclient.v1.undercloud_deploy:DeployUndercloud diff --git a/tripleoclient/command.py b/tripleoclient/command.py index b77f2b364..ec640b994 100644 --- a/tripleoclient/command.py +++ b/tripleoclient/command.py @@ -41,6 +41,10 @@ class Lister(Command, command.Lister): pass +class ShowOne(Command, command.ShowOne): + pass + + class DeprecatedActionStore(_StoreAction): """To deprecated an option an store the value""" log = logging.getLogger(__name__) diff --git a/tripleoclient/tests/v1/test_container_image.py b/tripleoclient/tests/v1/test_container_image.py index 2d8bc77fa..084052a0b 100644 --- a/tripleoclient/tests/v1/test_container_image.py +++ b/tripleoclient/tests/v1/test_container_image.py @@ -145,6 +145,58 @@ class TestContainerImageList(TestPluginV1): self.assertEqual(actual, rv) +class TestContainerImageShow(TestPluginV1): + + def setUp(self): + super(TestContainerImageShow, self).setUp() + self.cmd = container_image.TripleOContainerImageShow(self.app, None) + + @mock.patch('tripleoclient.v1.container_image.TripleOContainerImageShow.' + 'format_image_inspect') + @mock.patch('tripleo_common.image.image_uploader.ImageUploadManager') + def test_take_action(self, mock_manager, mock_formatter): + + arglist = ['foo'] + verifylist = [('image_to_inspect', 'foo')] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # mock manager object + mock_mgr = mock.Mock() + mock_manager.return_value = mock_mgr + + # mock uploader object + mock_uploader = mock.Mock() + mock_mgr.uploader.return_value = mock_uploader + + # mock return url object from uploader._image_to_url + mock_url = mock.Mock() + mock_url.geturl.return_value = 'munged-reg-url' + + mock_uploader._image_to_url.return_value = mock_url + + # mock return session object from uploader.authenticate + mock_session = mock.Mock() + mock_uploader.authenticate.return_value = mock_session + mock_inspect = mock.Mock() + data = {'Name': 'a', 'Layers': 'b'} + mock_inspect.return_value = data + mock_uploader.inspect = mock_inspect + + # mock format image inspect + formatted_data = (['Name', 'Layers'], ['a', 'b']) + mock_formatter.return_value = formatted_data + + rv = self.cmd.take_action(parsed_args) + + mock_formatter.assert_called_once_with(data) + self.assertEqual(formatted_data, rv) + + def test_format_image_inspect(self): + test_data = {'Name': 'foo', 'Layers': 'bar'} + self.assertEqual(self.cmd.format_image_inspect(test_data), + (['Name', 'Layers'], ['foo', 'bar'])) + + class TestContainerImagePrepare(TestPluginV1): def setUp(self): diff --git a/tripleoclient/v1/container_image.py b/tripleoclient/v1/container_image.py index 86a61a96a..f76b1ff2a 100644 --- a/tripleoclient/v1/container_image.py +++ b/tripleoclient/v1/container_image.py @@ -556,6 +556,68 @@ class TripleOContainerImageList(command.Lister): return (("Image Name",), cliff_results) +class TripleOContainerImageShow(command.ShowOne): + """Show image selected from the registry.""" + + auth_required = False + log = logging.getLogger(__name__ + ".TripleoContainerImageShow") + + def get_parser(self, prog_name): + parser = super(TripleOContainerImageShow, self).get_parser(prog_name) + parser.add_argument( + "--registry-url", + dest="registry_url", + metavar='', + default=image_uploader.get_undercloud_registry(), + help=_("URL of registry images are to be listed from in the " + "form :.") + ) + parser.add_argument( + "--username", + dest="username", + metavar='', + help=_("Username for image registry.") + ) + parser.add_argument( + "--password", + dest="password", + metavar='', + help=_("Password for image registry.") + ) + parser.add_argument( + dest="image_to_inspect", + metavar='', + help=_("Image to be inspected.") + ) + return parser + + def take_action(self, parsed_args): + self.log.debug("take_action(%s)" % parsed_args) + + manager = image_uploader.ImageUploadManager() + uploader = manager.uploader('python') + url = uploader._image_to_url(parsed_args.registry_url) + session = uploader.authenticate(url, parsed_args.username, + parsed_args.password) + + image_inspect_result = uploader.inspect(parsed_args.image_to_inspect, + session=session) + + return self.format_image_inspect(image_inspect_result) + + def format_image_inspect(self, image_inspect_result): + column_names = ['Name'] + data = [image_inspect_result.pop('Name')] + + result_fields = list(image_inspect_result.keys()) + result_fields.sort() + for field in result_fields: + column_names.append(field) + data.append(image_inspect_result[field]) + + return column_names, data + + class TripleOImagePrepareDefault(command.Command): """Generate a default ContainerImagePrepare parameter."""