[billy-olsen,r=corey.bryant] Provide support for user-specified public endpoint hostname.
This commit is contained in:
commit
c008fe0823
12
config.yaml
12
config.yaml
@ -176,6 +176,18 @@ options:
|
||||
192.168.0.0/24)
|
||||
.
|
||||
This network will be used for public endpoints.
|
||||
os-public-hostname:
|
||||
type: string
|
||||
default:
|
||||
description: |
|
||||
The hostname or address of the public endpoints created for glance
|
||||
in the keystone identity provider.
|
||||
.
|
||||
This value will be used for public endpoints. For example, an
|
||||
os-public-hostname set to 'glance.example.com' with ssl enabled will
|
||||
create a public endpoint for glance of:
|
||||
.
|
||||
https://glance.example.com:9292/
|
||||
prefer-ipv6:
|
||||
type: boolean
|
||||
default: False
|
||||
|
@ -17,6 +17,7 @@
|
||||
from charmhelpers.core.hookenv import (
|
||||
config,
|
||||
unit_get,
|
||||
service_name,
|
||||
)
|
||||
from charmhelpers.contrib.network.ip import (
|
||||
get_address_in_network,
|
||||
@ -26,8 +27,6 @@ from charmhelpers.contrib.network.ip import (
|
||||
)
|
||||
from charmhelpers.contrib.hahelpers.cluster import is_clustered
|
||||
|
||||
from functools import partial
|
||||
|
||||
PUBLIC = 'public'
|
||||
INTERNAL = 'int'
|
||||
ADMIN = 'admin'
|
||||
@ -35,15 +34,18 @@ ADMIN = 'admin'
|
||||
ADDRESS_MAP = {
|
||||
PUBLIC: {
|
||||
'config': 'os-public-network',
|
||||
'fallback': 'public-address'
|
||||
'fallback': 'public-address',
|
||||
'override': 'os-public-hostname',
|
||||
},
|
||||
INTERNAL: {
|
||||
'config': 'os-internal-network',
|
||||
'fallback': 'private-address'
|
||||
'fallback': 'private-address',
|
||||
'override': 'os-internal-hostname',
|
||||
},
|
||||
ADMIN: {
|
||||
'config': 'os-admin-network',
|
||||
'fallback': 'private-address'
|
||||
'fallback': 'private-address',
|
||||
'override': 'os-admin-hostname',
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,15 +59,50 @@ def canonical_url(configs, endpoint_type=PUBLIC):
|
||||
:param endpoint_type: str endpoint type to resolve.
|
||||
:param returns: str base URL for services on the current service unit.
|
||||
"""
|
||||
scheme = 'http'
|
||||
if 'https' in configs.complete_contexts():
|
||||
scheme = 'https'
|
||||
scheme = _get_scheme(configs)
|
||||
|
||||
address = resolve_address(endpoint_type)
|
||||
if is_ipv6(address):
|
||||
address = "[{}]".format(address)
|
||||
|
||||
return '%s://%s' % (scheme, address)
|
||||
|
||||
|
||||
def _get_scheme(configs):
|
||||
"""Returns the scheme to use for the url (either http or https)
|
||||
depending upon whether https is in the configs value.
|
||||
|
||||
:param configs: OSTemplateRenderer config templating object to inspect
|
||||
for a complete https context.
|
||||
:returns: either 'http' or 'https' depending on whether https is
|
||||
configured within the configs context.
|
||||
"""
|
||||
scheme = 'http'
|
||||
if configs and 'https' in configs.complete_contexts():
|
||||
scheme = 'https'
|
||||
return scheme
|
||||
|
||||
|
||||
def _get_address_override(endpoint_type=PUBLIC):
|
||||
"""Returns any address overrides that the user has defined based on the
|
||||
endpoint type.
|
||||
|
||||
Note: this function allows for the service name to be inserted into the
|
||||
address if the user specifies {service_name}.somehost.org.
|
||||
|
||||
:param endpoint_type: the type of endpoint to retrieve the override
|
||||
value for.
|
||||
:returns: any endpoint address or hostname that the user has overridden
|
||||
or None if an override is not present.
|
||||
"""
|
||||
override_key = ADDRESS_MAP[endpoint_type]['override']
|
||||
addr_override = config(override_key)
|
||||
if not addr_override:
|
||||
return None
|
||||
else:
|
||||
return addr_override.format(service_name=service_name())
|
||||
|
||||
|
||||
def resolve_address(endpoint_type=PUBLIC):
|
||||
"""Return unit address depending on net config.
|
||||
|
||||
@ -77,7 +114,10 @@ def resolve_address(endpoint_type=PUBLIC):
|
||||
|
||||
:param endpoint_type: Network endpoing type
|
||||
"""
|
||||
resolved_address = None
|
||||
resolved_address = _get_address_override(endpoint_type)
|
||||
if resolved_address:
|
||||
return resolved_address
|
||||
|
||||
vips = config('vip')
|
||||
if vips:
|
||||
vips = vips.split()
|
||||
@ -109,38 +149,3 @@ def resolve_address(endpoint_type=PUBLIC):
|
||||
"clustered=%s)" % (net_type, clustered))
|
||||
|
||||
return resolved_address
|
||||
|
||||
|
||||
def endpoint_url(configs, url_template, port, endpoint_type=PUBLIC,
|
||||
override=None):
|
||||
"""Returns the correct endpoint URL to advertise to Keystone.
|
||||
|
||||
This method provides the correct endpoint URL which should be advertised to
|
||||
the keystone charm for endpoint creation. This method allows for the url to
|
||||
be overridden to force a keystone endpoint to have specific URL for any of
|
||||
the defined scopes (admin, internal, public).
|
||||
|
||||
:param configs: OSTemplateRenderer config templating object to inspect
|
||||
for a complete https context.
|
||||
:param url_template: str format string for creating the url template. Only
|
||||
two values will be passed - the scheme+hostname
|
||||
returned by the canonical_url and the port.
|
||||
:param endpoint_type: str endpoint type to resolve.
|
||||
:param override: str the name of the config option which overrides the
|
||||
endpoint URL defined by the charm itself. None will
|
||||
disable any overrides (default).
|
||||
"""
|
||||
if override:
|
||||
# Return any user-defined overrides for the keystone endpoint URL.
|
||||
user_value = config(override)
|
||||
if user_value:
|
||||
return user_value.strip()
|
||||
|
||||
return url_template % (canonical_url(configs, endpoint_type), port)
|
||||
|
||||
|
||||
public_endpoint = partial(endpoint_url, endpoint_type=PUBLIC)
|
||||
|
||||
internal_endpoint = partial(endpoint_url, endpoint_type=INTERNAL)
|
||||
|
||||
admin_endpoint = partial(endpoint_url, endpoint_type=ADMIN)
|
||||
|
@ -24,7 +24,6 @@ utils.restart_map = _map
|
||||
TO_PATCH = [
|
||||
# charmhelpers.core.hookenv
|
||||
'Hooks',
|
||||
'canonical_url',
|
||||
'config',
|
||||
'juju_log',
|
||||
'is_relation_made',
|
||||
@ -321,8 +320,9 @@ class GlanceRelationTests(CharmTestCase):
|
||||
)
|
||||
self.migrate_database.assert_called_with()
|
||||
|
||||
def test_image_service_joined_leader(self):
|
||||
self.canonical_url.return_value = 'http://glancehost'
|
||||
@patch.object(relations, 'canonical_url')
|
||||
def test_image_service_joined_leader(self, _canonical_url):
|
||||
_canonical_url.return_value = 'http://glancehost'
|
||||
relations.image_service_joined()
|
||||
args = {
|
||||
'glance-api-server': 'http://glancehost:9292',
|
||||
@ -330,8 +330,9 @@ class GlanceRelationTests(CharmTestCase):
|
||||
}
|
||||
self.relation_set.assert_called_with(**args)
|
||||
|
||||
def test_image_service_joined_specified_interface(self):
|
||||
self.canonical_url.return_value = 'http://glancehost'
|
||||
@patch.object(relations, 'canonical_url')
|
||||
def test_image_service_joined_specified_interface(self, _canonical_url):
|
||||
_canonical_url.return_value = 'http://glancehost'
|
||||
relations.image_service_joined(relation_id='image-service:1')
|
||||
args = {
|
||||
'glance-api-server': 'http://glancehost:9292',
|
||||
@ -418,10 +419,12 @@ class GlanceRelationTests(CharmTestCase):
|
||||
for c in [call('/etc/glance/glance.conf')]:
|
||||
self.assertNotIn(c, configs.write.call_args_list)
|
||||
|
||||
@patch("charmhelpers.core.host.service")
|
||||
@patch("glance_relations.relation_get", autospec=True)
|
||||
@patch.object(relations, 'CONFIGS')
|
||||
def test_ceph_changed_with_key_and_relation_data(self, configs,
|
||||
mock_relation_get):
|
||||
mock_relation_get,
|
||||
mock_service):
|
||||
configs.complete_contexts = MagicMock()
|
||||
configs.complete_contexts.return_value = ['ceph']
|
||||
configs.write = MagicMock()
|
||||
@ -441,8 +444,9 @@ class GlanceRelationTests(CharmTestCase):
|
||||
self.delete_keyring.assert_called_with(service='glance')
|
||||
self.assertTrue(configs.write_all.called)
|
||||
|
||||
def test_keystone_joined(self):
|
||||
self.canonical_url.return_value = 'http://glancehost'
|
||||
@patch.object(relations, 'canonical_url')
|
||||
def test_keystone_joined(self, _canonical_url):
|
||||
_canonical_url.return_value = 'http://glancehost'
|
||||
relations.keystone_joined()
|
||||
ex = {
|
||||
'region': 'RegionOne',
|
||||
@ -454,8 +458,9 @@ class GlanceRelationTests(CharmTestCase):
|
||||
}
|
||||
self.relation_set.assert_called_with(**ex)
|
||||
|
||||
def test_keystone_joined_with_relation_id(self):
|
||||
self.canonical_url.return_value = 'http://glancehost'
|
||||
@patch.object(relations, 'canonical_url')
|
||||
def test_keystone_joined_with_relation_id(self, _canonical_url):
|
||||
_canonical_url.return_value = 'http://glancehost'
|
||||
relations.keystone_joined(relation_id='identity-service:0')
|
||||
ex = {
|
||||
'region': 'RegionOne',
|
||||
@ -467,6 +472,26 @@ class GlanceRelationTests(CharmTestCase):
|
||||
}
|
||||
self.relation_set.assert_called_with(**ex)
|
||||
|
||||
@patch('charmhelpers.contrib.openstack.ip.is_clustered')
|
||||
@patch('charmhelpers.contrib.openstack.ip.unit_get')
|
||||
@patch('charmhelpers.contrib.openstack.ip.config')
|
||||
def test_keystone_joined_public_endpoint(self, _config, _unit_get,
|
||||
_is_clustered):
|
||||
_unit_get.return_value = 'glancehost'
|
||||
_is_clustered.return_value = False
|
||||
self.test_config.set('os-public-hostname', 'glance.example.com')
|
||||
_config.side_effect = self.test_config.get
|
||||
relations.keystone_joined()
|
||||
ex = {
|
||||
'region': 'RegionOne',
|
||||
'public_url': 'http://glance.example.com:9292',
|
||||
'admin_url': 'http://glancehost:9292',
|
||||
'service': 'glance',
|
||||
'internal_url': 'http://glancehost:9292',
|
||||
'relation_id': None,
|
||||
}
|
||||
self.relation_set.assert_called_with(**ex)
|
||||
|
||||
@patch.object(relations, 'CONFIGS')
|
||||
def test_keystone_changes_incomplete(self, configs):
|
||||
configs.complete_contexts.return_value = []
|
||||
@ -572,9 +597,11 @@ class GlanceRelationTests(CharmTestCase):
|
||||
call('/etc/haproxy/haproxy.cfg')],
|
||||
configs.write.call_args_list)
|
||||
|
||||
@patch.object(relations, 'canonical_url')
|
||||
@patch.object(relations, 'relation_set')
|
||||
@patch.object(relations, 'CONFIGS')
|
||||
def test_cluster_changed_with_ipv6(self, configs, relation_set):
|
||||
def test_cluster_changed_with_ipv6(self, configs, relation_set,
|
||||
_canonical_url):
|
||||
self.test_config.set('prefer-ipv6', True)
|
||||
configs.complete_contexts = MagicMock()
|
||||
configs.complete_contexts.return_value = ['cluster']
|
||||
@ -685,10 +712,11 @@ class GlanceRelationTests(CharmTestCase):
|
||||
'ha_changed: hacluster subordinate is not fully clustered.'
|
||||
)
|
||||
|
||||
@patch.object(relations, 'canonical_url')
|
||||
@patch.object(relations, 'keystone_joined')
|
||||
@patch.object(relations, 'CONFIGS')
|
||||
def test_configure_https_enable_with_identity_service(
|
||||
self, configs, keystone_joined):
|
||||
self, configs, keystone_joined, _canonical_url):
|
||||
configs.complete_contexts = MagicMock()
|
||||
configs.complete_contexts.return_value = ['https']
|
||||
configs.write = MagicMock()
|
||||
@ -699,10 +727,11 @@ class GlanceRelationTests(CharmTestCase):
|
||||
self.check_call.assert_called_has_calls(calls)
|
||||
keystone_joined.assert_called_with(relation_id='identity-service:0')
|
||||
|
||||
@patch.object(relations, 'canonical_url')
|
||||
@patch.object(relations, 'keystone_joined')
|
||||
@patch.object(relations, 'CONFIGS')
|
||||
def test_configure_https_disable_with_keystone_joined(
|
||||
self, configs, keystone_joined):
|
||||
self, configs, keystone_joined, _canonical_url):
|
||||
configs.complete_contexts = MagicMock()
|
||||
configs.complete_contexts.return_value = ['']
|
||||
configs.write = MagicMock()
|
||||
@ -713,10 +742,11 @@ class GlanceRelationTests(CharmTestCase):
|
||||
self.check_call.assert_called_has_calls(calls)
|
||||
keystone_joined.assert_called_with(relation_id='identity-service:0')
|
||||
|
||||
@patch.object(relations, 'canonical_url')
|
||||
@patch.object(relations, 'image_service_joined')
|
||||
@patch.object(relations, 'CONFIGS')
|
||||
def test_configure_https_enable_with_image_service(
|
||||
self, configs, image_service_joined):
|
||||
self, configs, image_service_joined, _canonical_url):
|
||||
configs.complete_contexts = MagicMock()
|
||||
configs.complete_contexts.return_value = ['https']
|
||||
configs.write = MagicMock()
|
||||
@ -727,10 +757,11 @@ class GlanceRelationTests(CharmTestCase):
|
||||
self.check_call.assert_called_has_calls(calls)
|
||||
image_service_joined.assert_called_with(relation_id='image-service:0')
|
||||
|
||||
@patch.object(relations, 'canonical_url')
|
||||
@patch.object(relations, 'image_service_joined')
|
||||
@patch.object(relations, 'CONFIGS')
|
||||
def test_configure_https_disable_with_image_service(
|
||||
self, configs, image_service_joined):
|
||||
self, configs, image_service_joined, _canonical_url):
|
||||
configs.complete_contexts = MagicMock()
|
||||
configs.complete_contexts.return_value = ['']
|
||||
configs.write = MagicMock()
|
||||
|
Loading…
x
Reference in New Issue
Block a user