identity: Normalise output of application credentials commands

cliff is now smarter (I9155763eee15e19eab23b48989dfcc19ea2c5d34), so we
can effectively revert change I6b4f1b793dc383856bfdf9a01514381be3cd2bf1.
We bump the dependencies to ensure this.

Change-Id: I2af19043fd66b5be0826a774baeabeac7110a4aa
Signed-off-by: Stephen Finucane <stephenfin@redhat.com>
This commit is contained in:
Stephen Finucane
2025-05-15 15:48:34 +01:00
parent 79de137152
commit f1cd38aabf
5 changed files with 142 additions and 135 deletions

View File

@@ -20,6 +20,7 @@ import json
import logging import logging
import uuid import uuid
from cliff import columns as cliff_columns
from osc_lib.command import command from osc_lib.command import command
from osc_lib import exceptions from osc_lib import exceptions
from osc_lib import utils from osc_lib import utils
@@ -27,10 +28,84 @@ from osc_lib import utils
from openstackclient.i18n import _ from openstackclient.i18n import _
from openstackclient.identity import common from openstackclient.identity import common
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
class RolesColumn(cliff_columns.FormattableColumn):
"""Generate a formatted string of role names."""
def human_readable(self):
return utils.format_list(r['name'] for r in self._value)
def _format_application_credential(
application_credential, *, include_secret=False
):
column_headers: tuple[str, ...] = (
'ID',
'Name',
'Description',
'Project ID',
'Roles',
'Unrestricted',
'Access Rules',
'Expires At',
)
columns: tuple[str, ...] = (
'id',
'name',
'description',
'project_id',
'roles',
'unrestricted',
'access_rules',
'expires_at',
)
if include_secret:
column_headers += ('Secret',)
columns += ('secret',)
return (
column_headers,
utils.get_item_properties(
application_credential, columns, formatters={'roles': RolesColumn}
),
)
def _format_application_credentials(application_credentials):
column_headers = (
'ID',
'Name',
'Description',
'Project ID',
'Roles',
'Unrestricted',
'Access Rules',
'Expires At',
)
columns = (
'id',
'name',
'description',
'project_id',
'roles',
'unrestricted',
'access_rules',
'expires_at',
)
return (
column_headers,
(
utils.get_item_properties(
x, columns, formatters={'roles': RolesColumn}
)
for x in application_credentials
),
)
# TODO(stephenfin): Move this to osc_lib since it's useful elsewhere # TODO(stephenfin): Move this to osc_lib since it's useful elsewhere
def is_uuid_like(value) -> bool: def is_uuid_like(value) -> bool:
"""Returns validation of a value as a UUID. """Returns validation of a value as a UUID.
@@ -38,9 +113,6 @@ def is_uuid_like(value) -> bool:
:param val: Value to verify :param val: Value to verify
:type val: string :type val: string
:returns: bool :returns: bool
.. versionchanged:: 1.1.1
Support non-lowercase UUIDs.
""" """
try: try:
formatted_value = ( formatted_value = (
@@ -179,31 +251,8 @@ class CreateApplicationCredential(command.ShowOne):
access_rules=access_rules, access_rules=access_rules,
) )
# Format roles into something sensible return _format_application_credential(
if application_credential['roles']: application_credential, include_secret=True
roles = application_credential['roles']
msg = ' '.join(r['name'] for r in roles)
application_credential['roles'] = msg
columns = (
'id',
'name',
'description',
'project_id',
'roles',
'unrestricted',
'access_rules',
'expires_at',
'secret',
)
return (
columns,
(
utils.get_dict_properties(
application_credential,
columns,
)
),
) )
@@ -252,6 +301,8 @@ class DeleteApplicationCredential(command.Command):
) % {'errors': errors, 'total': total} ) % {'errors': errors, 'total': total}
raise exceptions.CommandError(msg) raise exceptions.CommandError(msg)
return None
class ListApplicationCredential(command.Lister): class ListApplicationCredential(command.Lister):
_description = _("List application credentials") _description = _("List application credentials")
@@ -276,39 +327,12 @@ class ListApplicationCredential(command.Lister):
conn = self.app.client_manager.sdk_connection conn = self.app.client_manager.sdk_connection
user_id = conn.config.get_auth().get_user_id(conn.identity) user_id = conn.config.get_auth().get_user_id(conn.identity)
data = identity_client.application_credentials(user=user_id) application_credentials = identity_client.application_credentials(
user=user_id
data_formatted = []
for ac in data:
# Format roles into something sensible
roles = ac['roles']
msg = ' '.join(r['name'] for r in roles)
ac['roles'] = msg
data_formatted.append(ac)
columns = (
'ID',
'Name',
'Description',
'Project ID',
'Roles',
'Unrestricted',
'Access Rules',
'Expires At',
)
return (
columns,
(
utils.get_item_properties(
s,
columns,
formatters={},
)
for s in data_formatted
),
) )
return _format_application_credentials(application_credentials)
class ShowApplicationCredential(command.ShowOne): class ShowApplicationCredential(command.ShowOne):
_description = _("Display application credential details") _description = _("Display application credential details")
@@ -327,31 +351,8 @@ class ShowApplicationCredential(command.ShowOne):
conn = self.app.client_manager.sdk_connection conn = self.app.client_manager.sdk_connection
user_id = conn.config.get_auth().get_user_id(conn.identity) user_id = conn.config.get_auth().get_user_id(conn.identity)
app_cred = identity_client.find_application_credential( application_credential = identity_client.find_application_credential(
user_id, parsed_args.application_credential user_id, parsed_args.application_credential
) )
# Format roles into something sensible return _format_application_credential(application_credential)
roles = app_cred['roles']
msg = ' '.join(r['name'] for r in roles)
app_cred['roles'] = msg
columns = (
'id',
'name',
'description',
'project_id',
'roles',
'unrestricted',
'access_rules',
'expires_at',
)
return (
columns,
(
utils.get_dict_properties(
app_cred,
columns,
)
),
)

View File

@@ -62,7 +62,7 @@ class AccessRuleTests(common.IdentityTests):
items = self.parse_show_as_object(raw_output) items = self.parse_show_as_object(raw_output)
self.access_rule_ids = [ self.access_rule_ids = [
x['id'] for x in ast.literal_eval(items['access_rules']) x['id'] for x in ast.literal_eval(items['Access Rules'])
] ]
self.addCleanup( self.addCleanup(
self.openstack, self.openstack,

View File

@@ -21,13 +21,13 @@ from openstackclient.tests.functional.identity.v3 import common
class ApplicationCredentialTests(common.IdentityTests): class ApplicationCredentialTests(common.IdentityTests):
APPLICATION_CREDENTIAL_FIELDS = [ APPLICATION_CREDENTIAL_FIELDS = [
'id', 'ID',
'name', 'Name',
'project_id', 'Project ID',
'description', 'Description',
'roles', 'Roles',
'expires_at', 'Expires At',
'unrestricted', 'Unrestricted',
] ]
APPLICATION_CREDENTIAL_LIST_HEADERS = [ APPLICATION_CREDENTIAL_LIST_HEADERS = [
'ID', 'ID',

View File

@@ -31,18 +31,6 @@ from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes
class TestApplicationCredentialCreate(identity_fakes.TestIdentityv3): class TestApplicationCredentialCreate(identity_fakes.TestIdentityv3):
columns = (
'id',
'name',
'description',
'project_id',
'roles',
'unrestricted',
'access_rules',
'expires_at',
'secret',
)
def setUp(self): def setUp(self):
super().setUp() super().setUp()
@@ -52,12 +40,25 @@ class TestApplicationCredentialCreate(identity_fakes.TestIdentityv3):
roles=[], roles=[],
) )
self.datalist = ( self.columns = (
'ID',
'Name',
'Description',
'Project ID',
'Roles',
'Unrestricted',
'Access Rules',
'Expires At',
'Secret',
)
self.data = (
self.application_credential.id, self.application_credential.id,
self.application_credential.name, self.application_credential.name,
self.application_credential.description, self.application_credential.description,
self.application_credential.project_id, self.application_credential.project_id,
self.application_credential.roles, application_credential.RolesColumn(
self.application_credential.roles
),
self.application_credential.unrestricted, self.application_credential.unrestricted,
self.application_credential.access_rules, self.application_credential.access_rules,
self.application_credential.expires_at, self.application_credential.expires_at,
@@ -101,7 +102,7 @@ class TestApplicationCredentialCreate(identity_fakes.TestIdentityv3):
) )
self.assertEqual(self.columns, columns) self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, data) self.assertEqual(self.data, data)
def test_application_credential_create_with_options(self): def test_application_credential_create_with_options(self):
name = self.application_credential.name name = self.application_credential.name
@@ -147,7 +148,7 @@ class TestApplicationCredentialCreate(identity_fakes.TestIdentityv3):
) )
self.assertEqual(self.columns, columns) self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, data) self.assertEqual(self.data, data)
def test_application_credential_create_with_access_rules_string(self): def test_application_credential_create_with_access_rules_string(self):
name = self.application_credential.name name = self.application_credential.name
@@ -191,7 +192,7 @@ class TestApplicationCredentialCreate(identity_fakes.TestIdentityv3):
) )
self.assertEqual(self.columns, columns) self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, data) self.assertEqual(self.data, data)
@mock.patch('openstackclient.identity.v3.application_credential.json.load') @mock.patch('openstackclient.identity.v3.application_credential.json.load')
@mock.patch('openstackclient.identity.v3.application_credential.open') @mock.patch('openstackclient.identity.v3.application_credential.open')
@@ -231,7 +232,7 @@ class TestApplicationCredentialCreate(identity_fakes.TestIdentityv3):
) )
self.assertEqual(self.columns, columns) self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, data) self.assertEqual(self.data, data)
class TestApplicationCredentialDelete(identity_fakes.TestIdentityv3): class TestApplicationCredentialDelete(identity_fakes.TestIdentityv3):
@@ -345,7 +346,9 @@ class TestApplicationCredentialList(identity_fakes.TestIdentityv3):
self.application_credential.name, self.application_credential.name,
self.application_credential.description, self.application_credential.description,
self.application_credential.project_id, self.application_credential.project_id,
'', application_credential.RolesColumn(
self.application_credential.roles
),
self.application_credential.unrestricted, self.application_credential.unrestricted,
self.application_credential.access_rules, self.application_credential.access_rules,
self.application_credential.expires_at, self.application_credential.expires_at,
@@ -408,6 +411,29 @@ class TestApplicationCredentialShow(identity_fakes.TestIdentityv3):
self.application_credential self.application_credential
) )
self.columns = (
'ID',
'Name',
'Description',
'Project ID',
'Roles',
'Unrestricted',
'Access Rules',
'Expires At',
)
self.data = (
self.application_credential.id,
self.application_credential.name,
self.application_credential.description,
self.application_credential.project_id,
application_credential.RolesColumn(
self.application_credential.roles
),
self.application_credential.unrestricted,
self.application_credential.access_rules,
self.application_credential.expires_at,
)
# Get the command object to test # Get the command object to test
self.cmd = application_credential.ShowApplicationCredential( self.cmd = application_credential.ShowApplicationCredential(
self.app, None self.app, None
@@ -434,25 +460,5 @@ class TestApplicationCredentialShow(identity_fakes.TestIdentityv3):
user_id, self.application_credential.id user_id, self.application_credential.id
) )
collist = ( self.assertEqual(self.columns, columns)
'id', self.assertEqual(self.data, data)
'name',
'description',
'project_id',
'roles',
'unrestricted',
'access_rules',
'expires_at',
)
self.assertEqual(collist, columns)
datalist = (
self.application_credential.id,
self.application_credential.name,
self.application_credential.description,
self.application_credential.project_id,
self.application_credential.roles,
self.application_credential.unrestricted,
self.application_credential.access_rules,
self.application_credential.expires_at,
)
self.assertEqual(datalist, data)

View File

@@ -5,7 +5,7 @@
pbr!=2.1.0,>=2.0.0 # Apache-2.0 pbr!=2.1.0,>=2.0.0 # Apache-2.0
cryptography>=2.7 # BSD/Apache-2.0 cryptography>=2.7 # BSD/Apache-2.0
cliff>=3.5.0 # Apache-2.0 cliff>=4.8.0 # Apache-2.0
iso8601>=0.1.11 # MIT iso8601>=0.1.11 # MIT
openstacksdk>=4.5.0 # Apache-2.0 openstacksdk>=4.5.0 # Apache-2.0
osc-lib>=2.3.0 # Apache-2.0 osc-lib>=2.3.0 # Apache-2.0