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. Update register_endpoints to also provide new application data bag JSON encoded data when this method is called from a lead unit. This relies on the type and description of the endpoint being provided which will require a charm change on adoption. Change-Id: I921d173c64b12c35f5ffc17270a0fc2bb83891c4
This commit is contained in:
parent
9ad5cade97
commit
1a5142bac6
93
requires.py
93
requires.py
@ -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('-', '_')
|
||||
@ -37,24 +38,64 @@ class KeystoneAutoAccessors(type):
|
||||
|
||||
@staticmethod
|
||||
def _accessor(field):
|
||||
def __accessor(self):
|
||||
return self.all_joined_units.received.get(field)
|
||||
return __accessor
|
||||
def _accessor_internal(self):
|
||||
# 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_internal
|
||||
|
||||
|
||||
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 +187,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 +209,26 @@ 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 all((service_type,
|
||||
service_description,
|
||||
reactive.is_flag_set('leadership.is_leader'),)):
|
||||
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')
|
||||
|
||||
|
2
tox.ini
2
tox.ini
@ -81,4 +81,4 @@ commands = {posargs}
|
||||
|
||||
[flake8]
|
||||
# E402 ignore necessary for path append before sys module import in actions
|
||||
ignore = E402
|
||||
ignore = E402 W503
|
||||
|
@ -10,6 +10,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import json
|
||||
|
||||
from unittest import mock
|
||||
|
||||
@ -19,6 +20,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 +103,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')
|
||||
@ -152,10 +201,14 @@ class TestKeystoneRequires(test_utils.PatchHelper):
|
||||
'endpoint.some-relation.changed')
|
||||
|
||||
def test_register_endpoints(self):
|
||||
self.patch_object(requires.reactive, 'is_flag_set')
|
||||
self.is_flag_set.return_value = True
|
||||
relation = mock.MagicMock()
|
||||
self.patch_target('_relations')
|
||||
self._relations.__iter__.return_value = [relation]
|
||||
self.target.register_endpoints('s', 'r', 'p_url', 'i_url', 'a_url')
|
||||
self.target.register_endpoints(
|
||||
's', 'r', 'p_url', 'i_url', 'a_url',
|
||||
service_type='stype', service_description='sdesc')
|
||||
result = {
|
||||
'service': 's',
|
||||
'public_url': 'p_url',
|
||||
@ -164,6 +217,21 @@ class TestKeystoneRequires(test_utils.PatchHelper):
|
||||
'region': 'r',
|
||||
}
|
||||
relation.to_publish_raw.update.assert_called_once_with(result)
|
||||
# This should only happen when the charm is the leader and
|
||||
# register_endpoints is called with type and description
|
||||
# information.
|
||||
relation.to_publish_app_raw.update.assert_called_once_with({
|
||||
'region': 'r',
|
||||
'service-endpoints': json.dumps([{
|
||||
"admin_url": "a_url",
|
||||
"description": "sdesc",
|
||||
"internal_url": "i_url",
|
||||
"public_url": "p_url",
|
||||
"service_name": "s",
|
||||
"type": "stype"}],
|
||||
sort_keys=True
|
||||
)
|
||||
})
|
||||
|
||||
def test_register_endpoints_requested_roles(self):
|
||||
relation = mock.MagicMock()
|
||||
|
Loading…
x
Reference in New Issue
Block a user