Add config for extra regions
This is so we can register extra region endpoints in horizon, in situations where the keystone for the extra regions cannot be integrated via juju (for example, completely separate deployment). Closes-Bug: #1714926 Change-Id: I52cecec88437fd2bc5a012653f24471039e6b819
This commit is contained in:
parent
63d0ffc157
commit
422611f034
16
config.yaml
16
config.yaml
|
@ -512,3 +512,19 @@ options:
|
|||
this option sets True as the default value, which is consistent with the
|
||||
default value 'WSGISocketRotation On' in Apache. This option should be
|
||||
used with caution. Please read the Apache doc page for more information.
|
||||
extra-regions:
|
||||
type: string
|
||||
default: "{}"
|
||||
description: |
|
||||
Define extra regions to register in the region selector.
|
||||
Only use this if it's not possible to integrate the keystone unit with juju.
|
||||
It must be a json dictionary where the keys are region names,
|
||||
and the values are keystone endpoint urls.
|
||||
|
||||
Example:
|
||||
|
||||
{
|
||||
"cluster2": "https://cluster2.example.com/identity/v3",
|
||||
"another cluster": "https://another.example.com/identity/v3"
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
import functools
|
||||
import json
|
||||
from typing import Set, Tuple
|
||||
|
||||
from charmhelpers.core.hookenv import (
|
||||
config,
|
||||
|
@ -55,6 +56,25 @@ SSL_CERT_FILE = '/etc/apache2/ssl/horizon/cert_dashboard'
|
|||
SSL_KEY_FILE = '/etc/apache2/ssl/horizon/key_dashboard'
|
||||
|
||||
|
||||
def get_extra_regions() -> Set[Tuple[str, str]]:
|
||||
"""
|
||||
Get extra regions from config, as a set of (name, url) tuples
|
||||
|
||||
Raises ValueError if parsing fails.
|
||||
"""
|
||||
extra_regions = set()
|
||||
try:
|
||||
extra_regions_config = json.loads(config("extra-regions"))
|
||||
for title, endpoint in extra_regions_config.items():
|
||||
if isinstance(title, str) and isinstance(endpoint, str):
|
||||
extra_regions.add((endpoint, title))
|
||||
else:
|
||||
raise ValueError("keys and values must be strings")
|
||||
except Exception as e:
|
||||
raise ValueError(f"failed parsing extra-regions config: {e!r}")
|
||||
return extra_regions
|
||||
|
||||
|
||||
class HorizonHAProxyContext(OSContextGenerator):
|
||||
def __call__(self):
|
||||
'''
|
||||
|
@ -203,6 +223,14 @@ class IdentityServiceContext(OSContextGenerator):
|
|||
if len(ctxt) == 0:
|
||||
ctxt = local_ctxt
|
||||
|
||||
try:
|
||||
regions.update(get_extra_regions())
|
||||
except ValueError as e:
|
||||
log(
|
||||
f"Ignoring 'extra-regions' config, it is invalid. Err: {e}",
|
||||
WARNING
|
||||
)
|
||||
|
||||
if len(regions) > 1:
|
||||
avail_regions = map(lambda r: {'endpoint': r[0], 'title': r[1]},
|
||||
regions)
|
||||
|
|
|
@ -114,6 +114,8 @@ from hooks.horizon_utils import (
|
|||
update_plugin_packages_in_kv,
|
||||
)
|
||||
|
||||
from hooks.horizon_contexts import get_extra_regions
|
||||
|
||||
|
||||
hooks = Hooks()
|
||||
# Note that CONFIGS is now set up via resolve_CONFIGS so that it is not a
|
||||
|
@ -227,6 +229,12 @@ def config_changed():
|
|||
application_dashboard_relation_changed()
|
||||
dashboard_relation_changed()
|
||||
|
||||
# Provide a message to the user if extra regions config is invalid
|
||||
try:
|
||||
get_extra_regions()
|
||||
except ValueError:
|
||||
status_set("blocked", "Invalid 'extra-regions' config value")
|
||||
|
||||
|
||||
@hooks.hook('identity-service-relation-joined')
|
||||
def keystone_joined(rel_id=None):
|
||||
|
|
|
@ -1071,6 +1071,68 @@ class TestHorizonContexts(CharmTestCase):
|
|||
{'endpoint': 'http://foo:5000/v2.0',
|
||||
'title': 'regionTwo'}]})
|
||||
|
||||
@patch("hooks.horizon_contexts.format_ipv6_addr")
|
||||
def test_IdentityServiceContext_multi_region_with_extra(
|
||||
self, mock_format_ipv6_addr
|
||||
):
|
||||
mock_format_ipv6_addr.side_effect = lambda x: x
|
||||
self.relation_ids.return_value = ['foo']
|
||||
self.related_units.return_value = ['bar', 'baz']
|
||||
self.relation_get.side_effect = self.test_relation.get
|
||||
self.test_relation.set({'service_host': 'foo', 'service_port': 5000,
|
||||
'internal_host': 'bar', 'internal_port': 5001,
|
||||
'region': 'regionOne regionTwo'})
|
||||
self.test_config.set(
|
||||
'extra-regions', '{"regionThree": "http://example.com/v3"}'
|
||||
)
|
||||
self.maxDiff = None
|
||||
self.context_complete.return_value = True
|
||||
self.assertEqual(
|
||||
with_regions_sorted(horizon_contexts.IdentityServiceContext()),
|
||||
{'service_host': 'foo', 'service_port': 5000,
|
||||
'service_protocol': 'http', 'api_version': '2',
|
||||
'internal_host': 'bar', 'internal_port': 5001,
|
||||
'internal_protocol': 'http',
|
||||
'ks_host': 'foo', 'ks_port': 5000,
|
||||
'ks_protocol': 'http',
|
||||
'ks_endpoint_path': 'v2.0',
|
||||
'default_role': 'member',
|
||||
'regions': [{'endpoint': 'http://foo:5000/v2.0',
|
||||
'title': 'regionOne'},
|
||||
{'endpoint': 'http://example.com/v3',
|
||||
'title': 'regionThree'},
|
||||
{'endpoint': 'http://foo:5000/v2.0',
|
||||
'title': 'regionTwo'}]})
|
||||
|
||||
@patch("hooks.horizon_contexts.format_ipv6_addr")
|
||||
def test_IdentityServiceContext_multi_region_with_invalid_extra_regions(
|
||||
self, mock_format_ipv6_addr
|
||||
):
|
||||
mock_format_ipv6_addr.side_effect = lambda x: x
|
||||
self.relation_ids.return_value = ['foo']
|
||||
self.related_units.return_value = ['bar', 'baz']
|
||||
self.relation_get.side_effect = self.test_relation.get
|
||||
self.test_relation.set({'service_host': 'foo', 'service_port': 5000,
|
||||
'internal_host': 'bar', 'internal_port': 5001,
|
||||
'region': 'regionOne regionTwo'})
|
||||
self.test_config.set('extra-regions', '{{{{')
|
||||
self.maxDiff = None
|
||||
self.context_complete.return_value = True
|
||||
self.assertEqual(
|
||||
with_regions_sorted(horizon_contexts.IdentityServiceContext()),
|
||||
{'service_host': 'foo', 'service_port': 5000,
|
||||
'service_protocol': 'http', 'api_version': '2',
|
||||
'internal_host': 'bar', 'internal_port': 5001,
|
||||
'internal_protocol': 'http',
|
||||
'ks_host': 'foo', 'ks_port': 5000,
|
||||
'ks_protocol': 'http',
|
||||
'ks_endpoint_path': 'v2.0',
|
||||
'default_role': 'member',
|
||||
'regions': [{'endpoint': 'http://foo:5000/v2.0',
|
||||
'title': 'regionOne'},
|
||||
{'endpoint': 'http://foo:5000/v2.0',
|
||||
'title': 'regionTwo'}]})
|
||||
|
||||
@patch("hooks.horizon_contexts.format_ipv6_addr")
|
||||
def test_IdentityServiceContext_multi_region_v3(self,
|
||||
mock_format_ipv6_addr):
|
||||
|
|
|
@ -266,6 +266,7 @@ class TestHorizonHooks(CharmTestCase):
|
|||
'action-managed-upgrade': False,
|
||||
'webroot': '/horizon',
|
||||
'site-name': 'local',
|
||||
'extra-regions': '{}',
|
||||
}[key]
|
||||
self.config.side_effect = config_side_effect
|
||||
_is_leader.return_value = True
|
||||
|
@ -283,6 +284,88 @@ class TestHorizonHooks(CharmTestCase):
|
|||
self.open_port.assert_has_calls([call(80), call(443)])
|
||||
self.assertTrue(_custom_theme.called)
|
||||
|
||||
@patch('hooks.horizon_contexts.config')
|
||||
@patch('hooks.horizon_hooks.check_custom_theme')
|
||||
@patch('hooks.horizon_hooks.keystone_joined')
|
||||
@patch('hooks.horizon_hooks.is_leader')
|
||||
@patch('os.environ.get')
|
||||
def test_config_changed_invalid_extra_regions(
|
||||
self, _environ_get, _is_leader, _joined, _custom_theme, _config
|
||||
):
|
||||
self.relation_ids.side_effect = lambda _: []
|
||||
self.config.side_effect = _config.side_effect = lambda key: {
|
||||
'ssl_key': 'somekey',
|
||||
'enforce-ssl': True,
|
||||
'dns-ha': True,
|
||||
'os-public-hostname': 'dashboard.intranet.test',
|
||||
'prefer-ipv6': False,
|
||||
'action-managed-upgrade': False,
|
||||
'webroot': '/horizon',
|
||||
'site-name': 'local',
|
||||
'extra-regions': '{',
|
||||
}[key]
|
||||
_is_leader.return_value = True
|
||||
_environ_get.return_value = ''
|
||||
self.openstack_upgrade_available.return_value = False
|
||||
self._call_hook('config-changed')
|
||||
self.status_set.assert_has_calls([
|
||||
call("blocked", "Invalid 'extra-regions' config value")
|
||||
])
|
||||
|
||||
@patch('hooks.horizon_contexts.config')
|
||||
@patch('hooks.horizon_hooks.check_custom_theme')
|
||||
@patch('hooks.horizon_hooks.keystone_joined')
|
||||
@patch('hooks.horizon_hooks.is_leader')
|
||||
@patch('os.environ.get')
|
||||
def test_config_changed_invalid_extra_regions2(
|
||||
self, _environ_get, _is_leader, _joined, _custom_theme, _config
|
||||
):
|
||||
self.relation_ids.side_effect = lambda _: []
|
||||
self.config.side_effect = _config.side_effect = lambda key: {
|
||||
'ssl_key': 'somekey',
|
||||
'enforce-ssl': True,
|
||||
'dns-ha': True,
|
||||
'os-public-hostname': 'dashboard.intranet.test',
|
||||
'prefer-ipv6': False,
|
||||
'action-managed-upgrade': False,
|
||||
'webroot': '/horizon',
|
||||
'site-name': 'local',
|
||||
'extra-regions': '{"test": 2}',
|
||||
}[key]
|
||||
_is_leader.return_value = True
|
||||
_environ_get.return_value = ''
|
||||
self.openstack_upgrade_available.return_value = False
|
||||
self._call_hook('config-changed')
|
||||
self.status_set.assert_has_calls([
|
||||
call("blocked", "Invalid 'extra-regions' config value")
|
||||
])
|
||||
|
||||
@patch('hooks.horizon_contexts.config')
|
||||
@patch('hooks.horizon_hooks.check_custom_theme')
|
||||
@patch('hooks.horizon_hooks.keystone_joined')
|
||||
@patch('hooks.horizon_hooks.is_leader')
|
||||
@patch('os.environ.get')
|
||||
def test_config_changed_valid_extra_regions(
|
||||
self, _environ_get, _is_leader, _joined, _custom_theme, _config
|
||||
):
|
||||
self.relation_ids.side_effect = lambda _: []
|
||||
self.config.side_effect = _config.side_effect = lambda key: {
|
||||
'ssl_key': 'somekey',
|
||||
'enforce-ssl': True,
|
||||
'dns-ha': True,
|
||||
'os-public-hostname': 'dashboard.intranet.test',
|
||||
'prefer-ipv6': False,
|
||||
'action-managed-upgrade': False,
|
||||
'webroot': '/horizon',
|
||||
'site-name': 'local',
|
||||
'extra-regions': '{"test": "http://example.com/v3"}',
|
||||
}[key]
|
||||
_is_leader.return_value = True
|
||||
_environ_get.return_value = ''
|
||||
self.openstack_upgrade_available.return_value = False
|
||||
self._call_hook('config-changed')
|
||||
self.status_set.assert_not_called()
|
||||
|
||||
@patch('hooks.horizon_hooks.check_custom_theme')
|
||||
@patch('hooks.horizon_hooks.is_leader')
|
||||
def test_config_changed_do_upgrade(self, _is_leader, _custom_theme):
|
||||
|
|
Loading…
Reference in New Issue