From c272d2c707c2fac19f042f1845843a09fe3d8c32 Mon Sep 17 00:00:00 2001 From: Aymen Frikha Date: Mon, 17 Dec 2018 13:46:13 +0000 Subject: [PATCH] Add support for Middleware This patch creates a new middleware context to retrieve data from subordinate charm and update the kesytone configuration file. It also allows integration with keystone-middleware interface: https://github.com/openstack-charmers/interface-keystone-middleware This patch uses the subordinate configuration approach to retrieve data from the subordinate charm. Every changes required for paste.ini file will be handled by the subordinate charm. The latter should deal with keystone upgrades. Closes-Bug: #1808597 Change-Id: I4897011fbc791abc97e34e75826579820e80a4f1 --- hooks/keystone-middleware-relation-broken | 1 + hooks/keystone-middleware-relation-changed | 1 + hooks/keystone-middleware-relation-departed | 1 + hooks/keystone-middleware-relation-joined | 1 + hooks/keystone_context.py | 20 +++++++ hooks/keystone_hooks.py | 9 +++ hooks/keystone_utils.py | 7 ++- metadata.yaml | 3 + templates/ocata/keystone.conf | 4 ++ templates/parts/section-middleware | 6 ++ unit_tests/test_keystone_contexts.py | 66 +++++++++++++++++++++ 11 files changed, 118 insertions(+), 1 deletion(-) create mode 120000 hooks/keystone-middleware-relation-broken create mode 120000 hooks/keystone-middleware-relation-changed create mode 120000 hooks/keystone-middleware-relation-departed create mode 120000 hooks/keystone-middleware-relation-joined create mode 100644 templates/parts/section-middleware diff --git a/hooks/keystone-middleware-relation-broken b/hooks/keystone-middleware-relation-broken new file mode 120000 index 00000000..dd3b3eff --- /dev/null +++ b/hooks/keystone-middleware-relation-broken @@ -0,0 +1 @@ +keystone_hooks.py \ No newline at end of file diff --git a/hooks/keystone-middleware-relation-changed b/hooks/keystone-middleware-relation-changed new file mode 120000 index 00000000..dd3b3eff --- /dev/null +++ b/hooks/keystone-middleware-relation-changed @@ -0,0 +1 @@ +keystone_hooks.py \ No newline at end of file diff --git a/hooks/keystone-middleware-relation-departed b/hooks/keystone-middleware-relation-departed new file mode 120000 index 00000000..dd3b3eff --- /dev/null +++ b/hooks/keystone-middleware-relation-departed @@ -0,0 +1 @@ +keystone_hooks.py \ No newline at end of file diff --git a/hooks/keystone-middleware-relation-joined b/hooks/keystone-middleware-relation-joined new file mode 120000 index 00000000..dd3b3eff --- /dev/null +++ b/hooks/keystone-middleware-relation-joined @@ -0,0 +1 @@ +keystone_hooks.py \ No newline at end of file diff --git a/hooks/keystone_context.py b/hooks/keystone_context.py index 5287a8aa..d145f966 100644 --- a/hooks/keystone_context.py +++ b/hooks/keystone_context.py @@ -42,6 +42,26 @@ from charmhelpers.contrib.openstack.utils import ( ) +class MiddlewareContext(context.OSContextGenerator): + interfaces = ['keystone-middleware'] + + def __call__(self): + + middlewares = [] + + for rid in relation_ids('keystone-middleware'): + if related_units(rid): + for unit in related_units(rid): + middleware_name = relation_get('middleware_name', + rid=rid, + unit=unit) + if middleware_name: + middlewares.append(middleware_name) + return { + 'middlewares': ",".join(middlewares) + } + + class ApacheSSLContext(context.ApacheSSLContext): interfaces = ['https'] external_ports = [] diff --git a/hooks/keystone_hooks.py b/hooks/keystone_hooks.py index 7d6d1172..f922248f 100755 --- a/hooks/keystone_hooks.py +++ b/hooks/keystone_hooks.py @@ -789,6 +789,15 @@ def certs_changed(relation_id=None, unit=None): update_all_domain_backends() +@hooks.hook('keystone-middleware-relation-joined', + 'keystone-middleware-relation-changed', + 'keystone-middleware-relation-broken', + 'keystone-middleware-relation-departed') +@restart_on_change(restart_map()) +def keystone_middleware_changed(): + CONFIGS.write(KEYSTONE_CONF) + + @hooks.hook('pre-series-upgrade') def pre_series_upgrade(): log("Running prepare series upgrade hook", "INFO") diff --git a/hooks/keystone_utils.py b/hooks/keystone_utils.py index 16354e56..8a0e9dec 100644 --- a/hooks/keystone_utils.py +++ b/hooks/keystone_utils.py @@ -233,7 +233,12 @@ BASE_RESOURCE_MAP = OrderedDict([ context.WorkerConfigContext(), context.MemcacheContext(package='keystone'), keystone_context.KeystoneFIDServiceProviderContext(), - keystone_context.WebSSOTrustedDashboardContext()], + keystone_context.WebSSOTrustedDashboardContext(), + keystone_context.context.SubordinateConfigContext( + interface=['keystone-middleware'], + service='keystone', + config_file=KEYSTONE_CONF), + keystone_context.MiddlewareContext()] }), (KEYSTONE_LOGGER_CONF, { 'contexts': [keystone_context.KeystoneLoggingContext()], diff --git a/metadata.yaml b/metadata.yaml index aad95f17..c19347ec 100644 --- a/metadata.yaml +++ b/metadata.yaml @@ -46,6 +46,9 @@ requires: interface: websso-trusted-dashboard certificates: interface: tls-certificates + keystone-middleware: + interface: keystone-middleware + scope: container peers: cluster: interface: keystone-ha diff --git a/templates/ocata/keystone.conf b/templates/ocata/keystone.conf index d9c2bd48..aede63fe 100644 --- a/templates/ocata/keystone.conf +++ b/templates/ocata/keystone.conf @@ -69,11 +69,15 @@ driver = {{ assignment_backend }} [oauth1] +{% if middlewares -%} +{% include "parts/section-middleware" %} +{% else %} [auth] methods = external,password,token,oauth1,mapped,openid,totp password = keystone.auth.plugins.password.Password token = keystone.auth.plugins.token.Token oauth1 = keystone.auth.plugins.oauth1.OAuth +{% endif %} [paste_deploy] config_file = {{ paste_config_file }} diff --git a/templates/parts/section-middleware b/templates/parts/section-middleware new file mode 100644 index 00000000..e65f1d98 --- /dev/null +++ b/templates/parts/section-middleware @@ -0,0 +1,6 @@ +{% for section in sections -%} +[{{section}}] +{% for key, value in sections[section].items() -%} +{{ key }} = {{ value }} +{% endfor %} +{%- endfor %} diff --git a/unit_tests/test_keystone_contexts.py b/unit_tests/test_keystone_contexts.py index 8a78329a..0b4fd16a 100644 --- a/unit_tests/test_keystone_contexts.py +++ b/unit_tests/test_keystone_contexts.py @@ -439,3 +439,69 @@ class TestKeystoneContexts(CharmTestCase): self.maxDiff = None self.assertCountEqual(ctxt(), {}) + + @patch.object(context, 'relation_ids') + def test_middleware_no_related_units(self, mock_relation_ids): + os.environ['JUJU_UNIT_NAME'] = 'keystone' + + def relation_ids_side_effect(rname): + return { + 'keystone-middleware': {} + }[rname] + + mock_relation_ids.side_effect = relation_ids_side_effect + ctxt = context.MiddlewareContext() + + self.assertEqual(ctxt(), {'middlewares': ''}) + + @patch('charmhelpers.contrib.openstack.context.relation_ids') + @patch('charmhelpers.contrib.openstack.context.related_units') + @patch('charmhelpers.contrib.openstack.context.relation_get') + def test_middleware_related_units( + self, mock_relation_get, mock_related_units, mock_relation_ids): + mock_relation_ids.return_value = ['keystone-middleware:0'] + mock_related_units.return_value = ['keystone-ico/0'] + settings = \ + { + 'middleware_name': 'keystone-ico', + 'subordinate_configuration': + '{"keystone":' + '{"/etc/keystone/keystone.conf":' + '{"sections":' + '{"authentication":' + '[["simple_token_header", "SimpleToken"],' + '["simple_token_secret", "foobar"]],' + '"auth":' + '[["methods", "external,password,token,oauth1"],' + '["external", "keystone.auth.plugins.external.Domain"],' + '["password", "keystone.auth.plugins.password.Password"],' + '["token", "keystone.auth.plugins.token.Token"],' + '["oauth1", "keystone.auth.plugins.oauth1.OAuth"]]' + '}}}}' + + } + + def fake_rel_get(attribute=None, unit=None, rid=None): + return settings[attribute] + + mock_relation_get.side_effect = fake_rel_get + ctxt = context.context.SubordinateConfigContext( + interface=['keystone-middleware'], + service='keystone', + config_file='/etc/keystone/keystone.conf') + + exp = {'sections': { + u'auth': [[u'methods', + u'external,password,token,oauth1'], + [u'external', + u'keystone.auth.plugins.external.Domain'], + [u'password', + u'keystone.auth.plugins.password.Password'], + [u'token', + u'keystone.auth.plugins.token.Token'], + [u'oauth1', + u'keystone.auth.plugins.oauth1.OAuth']], + u'authentication': [[u'simple_token_header', u'SimpleToken'], + [u'simple_token_secret', u'foobar']]}} + + self.assertEqual(ctxt(), exp)