Merge "Add config for extra regions"
This commit is contained in:
commit
f433c3fb90
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