[billy-olsen,r=corey.bryant] Provide support for user-specified public endpoint hostname.

This commit is contained in:
Corey Bryant 2015-06-10 16:31:46 -04:00
commit c008fe0823
3 changed files with 107 additions and 59 deletions

View File

@ -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

View File

@ -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)

View File

@ -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()