Add forwards compat with k8s operator

The new keystone-k8s operator uses the application data bag and
more up-to-date key names for endpoint and authentication information.

Check for this information and then fallback to the existing
keystone charm unit data bag data set if not found.

Change-Id: I921d173c64b12c35f5ffc17270a0fc2bb83891c4
This commit is contained in:
James Page 2022-09-08 09:33:46 +01:00
parent 9ad5cade97
commit c9e21c7f37
3 changed files with 106 additions and 14 deletions

View File

@ -23,6 +23,7 @@ class KeystoneAutoAccessors(type):
Metaclass that converts fields referenced by ``auto_accessors`` into
accessor methods with very basic doc strings.
"""
def __new__(cls, name, parents, dct):
for field in dct.get('auto_accessors', []):
meth_name = field.replace('-', '_')
@ -38,23 +39,45 @@ class KeystoneAutoAccessors(type):
@staticmethod
def _accessor(field):
def __accessor(self):
return self.all_joined_units.received.get(field)
# Use remapped or transposed key for application
# data bag lookup for forwards compat
app_field = self._forward_compat_remaps.get(
field,
field.replace('_', '-')
)
return self.relations[0].received_app_raw.get(
app_field,
self.all_joined_units.received.get(field)
)
return __accessor
class KeystoneRequires(reactive.Endpoint, metaclass=KeystoneAutoAccessors):
auto_accessors = ['service_host', 'service_protocol',
'service_port', 'service_tenant', 'service_username',
'service_password', 'service_tenant_id', 'auth_host',
'auth_protocol', 'auth_port', 'admin_token', 'ssl_key',
'ca_cert', 'ssl_cert', 'https_keystone',
'ssl_cert_admin', 'ssl_cert_internal',
'ssl_cert_public', 'ssl_key_admin', 'ssl_key_internal',
'ssl_key_public', 'api_version', 'service_domain',
'service_domain_id', 'ep_changed',
'admin_domain_id', 'admin_user_id', 'admin_project_id',
'service_type']
auto_accessors = [
'service_host', 'service_protocol',
'service_port', 'service_tenant', 'service_username',
'service_password', 'service_tenant_id', 'auth_host',
'auth_protocol', 'auth_port', 'admin_token', 'ssl_key',
'ca_cert', 'ssl_cert', 'https_keystone',
'ssl_cert_admin', 'ssl_cert_internal',
'ssl_cert_public', 'ssl_key_admin', 'ssl_key_internal',
'ssl_key_public', 'api_version', 'service_domain',
'service_domain_id', 'ep_changed',
'admin_domain_id', 'admin_user_id', 'admin_project_id',
'service_type',
'public-auth-url',
'internal-auth-url',
'admin-auth-url',
]
_forward_compat_remaps = {
'admin_user': 'admin-user-name',
'service_username': 'service-user-name',
'service_tenant': 'service-project-name',
'service_tenant_id': 'service-project-id',
'service_domain': 'service-domain-name',
}
@reactive.when('endpoint.{endpoint_name}.joined')
def joined(self):
@ -146,7 +169,9 @@ class KeystoneRequires(reactive.Endpoint, metaclass=KeystoneAutoAccessors):
def register_endpoints(self, service, region, public_url, internal_url,
admin_url, requested_roles=None,
add_role_to_admin=None):
add_role_to_admin=None,
service_type=None,
service_description=None):
"""
Register this service with keystone
"""
@ -166,6 +191,25 @@ class KeystoneRequires(reactive.Endpoint, metaclass=KeystoneAutoAccessors):
for relation in self.relations:
relation.to_publish_raw.update(relation_info)
# NOTE: forwards compatible data presentation for keystone-k8s
if (reactive.is_flag_set('leadership.is_leader')
and all((service_type, service_description,))):
application_info = {
'region': region,
'service-endpoints': json.dumps([
{
'service_name': service,
'type': service_type,
'description': service_description,
'internal_url': internal_url,
'admin_url': admin_url,
'public_url': public_url,
}
], sort_keys=True)
}
for relation in self.relations:
relation.to_publish_app_raw.update(application_info)
def request_keystone_endpoint_information(self):
self.register_endpoints('None', 'None', 'None', 'None', 'None')

View File

@ -81,4 +81,4 @@ commands = {posargs}
[flake8]
# E402 ignore necessary for path append before sys module import in actions
ignore = E402
ignore = E402 W503

View File

@ -19,6 +19,35 @@ import charms_openstack.test_utils as test_utils
_hook_args = {}
IDENTITY_APP_DATA = {
'api-version': '3',
'auth-host': 'authhost',
'auth-port': '5000',
'auth-protocol': 'http',
'internal-host': 'internalhost',
'internal-port': '5000',
'internal-protocol': 'http',
'service-host': 'servicehost',
'service-port': '5000',
'service-protocol': 'http',
'admin-domain-name': 'admin-domain',
'admin-domain-id': 'ca9e66dd-920c-493c-8ebd-dcc893afcc3b',
'admin-project-name': 'admin',
'admin-project-id': '5c9fd12c-87eb-4688-931a-05da83db14ad',
'admin-user-name': 'admin',
'admin-user-id': 'cc28fa26-70bc-4acb-97a4-5614799257bb',
'service-domain-name': 'service-domain',
'service-domain-id': '8fa8e4c1-b9f6-44ae-b646-0626d44786c2',
'service-project-name': 'services',
'service-project-id': '0626e4d8-0846-4fd5-98c9-324fbbe24301',
'service-user-name': 'gnocchi',
'service-user-id': 'fa8c4a9a-f97c-41e7-a204-73571c5a7b51',
'service-password': 'foobar',
'internal-auth-url': 'http://internalhost:80/keystone',
'admin-auth-url': 'http://adminhost:80/keystone',
'public-auth-url': 'http://publichost:80/keystone',
}
class TestKeystoneRequires(test_utils.PatchHelper):
@ -73,6 +102,25 @@ class TestKeystoneRequires(test_utils.PatchHelper):
self.service_tenant.return_value = None
assert self.target.base_data_complete() is False
def test_app_data_complete(self):
relation = mock.MagicMock()
relation.received_app_raw.get.side_effect = (
lambda k, d: IDENTITY_APP_DATA.get(k, d)
)
self.target._relations = [relation]
self.assertEqual(self.target.service_host(), 'servicehost')
self.assertEqual(self.target.auth_host(), 'authhost')
self.assertEqual(
self.target.public_auth_url(), 'http://publichost:80/keystone')
self.assertEqual(self.target.service_tenant(), 'services')
self.assertEqual(self.target.service_password(), 'foobar')
self.assertEqual(
self.target.service_tenant_id(),
'0626e4d8-0846-4fd5-98c9-324fbbe24301')
self.assertTrue(self.target.base_data_complete())
self.assertFalse(self.target.ssl_data_complete())
self.assertFalse(self.target.ssl_data_complete_legacy())
def test_ssl_data_complete(self):
self.patch_target('ssl_cert_admin', '1')
self.patch_target('ssl_cert_internal', '2')