[billy-olsen,r=corey.bryant] Provide support for user-specified public endpoint hostname.
This commit is contained in:
commit
4754820af4
13
config.yaml
13
config.yaml
@ -207,6 +207,19 @@ options:
|
|||||||
192.168.0.0/24)
|
192.168.0.0/24)
|
||||||
.
|
.
|
||||||
This network will be used for public endpoints.
|
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 cinder
|
||||||
|
in the keystone identity provider.
|
||||||
|
.
|
||||||
|
This value will be used for public endpoints. For example, an
|
||||||
|
os-public-hostname set to 'cinder.example.com' with ssl enabled will
|
||||||
|
create two public endpoints for cinder:
|
||||||
|
.
|
||||||
|
https://cinder.example.com:443/v1/$(tenant_id)s and
|
||||||
|
https://cinder.example.com:443/v2/$(tenant_id)s
|
||||||
prefer-ipv6:
|
prefer-ipv6:
|
||||||
type: boolean
|
type: boolean
|
||||||
default: False
|
default: False
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
from charmhelpers.core.hookenv import (
|
from charmhelpers.core.hookenv import (
|
||||||
config,
|
config,
|
||||||
unit_get,
|
unit_get,
|
||||||
|
service_name,
|
||||||
)
|
)
|
||||||
from charmhelpers.contrib.network.ip import (
|
from charmhelpers.contrib.network.ip import (
|
||||||
get_address_in_network,
|
get_address_in_network,
|
||||||
@ -26,8 +27,6 @@ from charmhelpers.contrib.network.ip import (
|
|||||||
)
|
)
|
||||||
from charmhelpers.contrib.hahelpers.cluster import is_clustered
|
from charmhelpers.contrib.hahelpers.cluster import is_clustered
|
||||||
|
|
||||||
from functools import partial
|
|
||||||
|
|
||||||
PUBLIC = 'public'
|
PUBLIC = 'public'
|
||||||
INTERNAL = 'int'
|
INTERNAL = 'int'
|
||||||
ADMIN = 'admin'
|
ADMIN = 'admin'
|
||||||
@ -35,15 +34,18 @@ ADMIN = 'admin'
|
|||||||
ADDRESS_MAP = {
|
ADDRESS_MAP = {
|
||||||
PUBLIC: {
|
PUBLIC: {
|
||||||
'config': 'os-public-network',
|
'config': 'os-public-network',
|
||||||
'fallback': 'public-address'
|
'fallback': 'public-address',
|
||||||
|
'override': 'os-public-hostname',
|
||||||
},
|
},
|
||||||
INTERNAL: {
|
INTERNAL: {
|
||||||
'config': 'os-internal-network',
|
'config': 'os-internal-network',
|
||||||
'fallback': 'private-address'
|
'fallback': 'private-address',
|
||||||
|
'override': 'os-internal-hostname',
|
||||||
},
|
},
|
||||||
ADMIN: {
|
ADMIN: {
|
||||||
'config': 'os-admin-network',
|
'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 endpoint_type: str endpoint type to resolve.
|
||||||
:param returns: str base URL for services on the current service unit.
|
:param returns: str base URL for services on the current service unit.
|
||||||
"""
|
"""
|
||||||
scheme = 'http'
|
scheme = _get_scheme(configs)
|
||||||
if 'https' in configs.complete_contexts():
|
|
||||||
scheme = 'https'
|
|
||||||
address = resolve_address(endpoint_type)
|
address = resolve_address(endpoint_type)
|
||||||
if is_ipv6(address):
|
if is_ipv6(address):
|
||||||
address = "[{}]".format(address)
|
address = "[{}]".format(address)
|
||||||
|
|
||||||
return '%s://%s' % (scheme, 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):
|
def resolve_address(endpoint_type=PUBLIC):
|
||||||
"""Return unit address depending on net config.
|
"""Return unit address depending on net config.
|
||||||
|
|
||||||
@ -77,7 +114,10 @@ def resolve_address(endpoint_type=PUBLIC):
|
|||||||
|
|
||||||
:param endpoint_type: Network endpoing type
|
: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')
|
vips = config('vip')
|
||||||
if vips:
|
if vips:
|
||||||
vips = vips.split()
|
vips = vips.split()
|
||||||
@ -109,38 +149,3 @@ def resolve_address(endpoint_type=PUBLIC):
|
|||||||
"clustered=%s)" % (net_type, clustered))
|
"clustered=%s)" % (net_type, clustered))
|
||||||
|
|
||||||
return resolved_address
|
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)
|
|
||||||
|
@ -68,7 +68,6 @@ TO_PATCH = [
|
|||||||
'openstack_upgrade_available',
|
'openstack_upgrade_available',
|
||||||
'os_release',
|
'os_release',
|
||||||
# charmhelpers.contrib.hahelpers.cluster_utils
|
# charmhelpers.contrib.hahelpers.cluster_utils
|
||||||
'canonical_url',
|
|
||||||
'is_elected_leader',
|
'is_elected_leader',
|
||||||
'get_hacluster_config',
|
'get_hacluster_config',
|
||||||
'execd_preinstall',
|
'execd_preinstall',
|
||||||
@ -413,12 +412,13 @@ class TestJoinedHooks(CharmTestCase):
|
|||||||
vhost='openstack',
|
vhost='openstack',
|
||||||
relation_id='amqp:1')
|
relation_id='amqp:1')
|
||||||
|
|
||||||
def test_identity_service_joined(self):
|
@patch.object(hooks, 'canonical_url')
|
||||||
|
def test_identity_service_joined(self, _canonical_url):
|
||||||
'It properly requests unclustered endpoint via identity-service'
|
'It properly requests unclustered endpoint via identity-service'
|
||||||
self.os_release.return_value = 'havana'
|
self.os_release.return_value = 'havana'
|
||||||
self.unit_get.return_value = 'cindernode1'
|
self.unit_get.return_value = 'cindernode1'
|
||||||
self.config.side_effect = self.test_config.get
|
self.config.side_effect = self.test_config.get
|
||||||
self.canonical_url.return_value = 'http://cindernode1'
|
_canonical_url.return_value = 'http://cindernode1'
|
||||||
hooks.hooks.execute(['hooks/identity-service-relation-joined'])
|
hooks.hooks.execute(['hooks/identity-service-relation-joined'])
|
||||||
expected = {
|
expected = {
|
||||||
'region': None,
|
'region': None,
|
||||||
@ -435,12 +435,13 @@ class TestJoinedHooks(CharmTestCase):
|
|||||||
}
|
}
|
||||||
self.relation_set.assert_called_with(**expected)
|
self.relation_set.assert_called_with(**expected)
|
||||||
|
|
||||||
def test_identity_service_joined_icehouse(self):
|
@patch.object(hooks, 'canonical_url')
|
||||||
|
def test_identity_service_joined_icehouse(self, _canonical_url):
|
||||||
'It properly requests unclustered endpoint via identity-service'
|
'It properly requests unclustered endpoint via identity-service'
|
||||||
self.os_release.return_value = 'icehouse'
|
self.os_release.return_value = 'icehouse'
|
||||||
self.unit_get.return_value = 'cindernode1'
|
self.unit_get.return_value = 'cindernode1'
|
||||||
self.config.side_effect = self.test_config.get
|
self.config.side_effect = self.test_config.get
|
||||||
self.canonical_url.return_value = 'http://cindernode1'
|
_canonical_url.return_value = 'http://cindernode1'
|
||||||
hooks.hooks.execute(['hooks/identity-service-relation-joined'])
|
hooks.hooks.execute(['hooks/identity-service-relation-joined'])
|
||||||
expected = {
|
expected = {
|
||||||
'region': None,
|
'region': None,
|
||||||
@ -463,6 +464,42 @@ class TestJoinedHooks(CharmTestCase):
|
|||||||
}
|
}
|
||||||
self.relation_set.assert_called_with(**expected)
|
self.relation_set.assert_called_with(**expected)
|
||||||
|
|
||||||
|
@patch('charmhelpers.contrib.openstack.ip.config')
|
||||||
|
@patch('charmhelpers.contrib.openstack.ip.unit_get')
|
||||||
|
@patch('charmhelpers.contrib.openstack.ip.is_clustered')
|
||||||
|
def test_identity_service_joined_public_name(self, _is_clustered,
|
||||||
|
_unit_get, _config):
|
||||||
|
self.os_release.return_value = 'icehouse'
|
||||||
|
self.unit_get.return_value = 'cindernode1'
|
||||||
|
_unit_get.return_value = 'cindernode1'
|
||||||
|
self.config.side_effect = self.test_config.get
|
||||||
|
_config.side_effect = self.test_config.get
|
||||||
|
self.test_config.set('os-public-hostname', 'public.example.com')
|
||||||
|
_is_clustered.return_value = False
|
||||||
|
hooks.hooks.execute(['hooks/identity-service-relation-joined'])
|
||||||
|
v1_url = 'http://public.example.com:8776/v1/$(tenant_id)s'
|
||||||
|
v2_url = 'http://public.example.com:8776/v2/$(tenant_id)s'
|
||||||
|
expected = {
|
||||||
|
'region': None,
|
||||||
|
'service': None,
|
||||||
|
'public_url': None,
|
||||||
|
'internal_url': None,
|
||||||
|
'admin_url': None,
|
||||||
|
'cinder_service': 'cinder',
|
||||||
|
'cinder_region': 'RegionOne',
|
||||||
|
'cinder_public_url': v1_url,
|
||||||
|
'cinder_admin_url': 'http://cindernode1:8776/v1/$(tenant_id)s',
|
||||||
|
'cinder_internal_url': 'http://cindernode1:8776/v1/$(tenant_id)s',
|
||||||
|
'cinderv2_service': 'cinderv2',
|
||||||
|
'cinderv2_region': 'RegionOne',
|
||||||
|
'cinderv2_public_url': v2_url,
|
||||||
|
'cinderv2_admin_url': 'http://cindernode1:8776/v2/$(tenant_id)s',
|
||||||
|
'cinderv2_internal_url': ('http://cindernode1:8776/'
|
||||||
|
'v2/$(tenant_id)s'),
|
||||||
|
'relation_id': None,
|
||||||
|
}
|
||||||
|
self.relation_set.assert_called_with(**expected)
|
||||||
|
|
||||||
@patch('os.mkdir')
|
@patch('os.mkdir')
|
||||||
def test_ceph_joined(self, mkdir):
|
def test_ceph_joined(self, mkdir):
|
||||||
'It correctly prepares for a ceph changed hook'
|
'It correctly prepares for a ceph changed hook'
|
||||||
@ -502,8 +539,10 @@ class TestJoinedHooks(CharmTestCase):
|
|||||||
self.assertNotIn(c, self.CONFIGS.write.call_args_list)
|
self.assertNotIn(c, self.CONFIGS.write.call_args_list)
|
||||||
self.assertFalse(self.set_ceph_env_variables.called)
|
self.assertFalse(self.set_ceph_env_variables.called)
|
||||||
|
|
||||||
|
@patch('charmhelpers.core.host.service')
|
||||||
@patch("cinder_hooks.relation_get", autospec=True)
|
@patch("cinder_hooks.relation_get", autospec=True)
|
||||||
def test_ceph_changed_broker_success(self, mock_relation_get):
|
def test_ceph_changed_broker_success(self, mock_relation_get,
|
||||||
|
_service):
|
||||||
'It ensures ceph assets created on ceph changed'
|
'It ensures ceph assets created on ceph changed'
|
||||||
self.CONFIGS.complete_contexts.return_value = ['ceph']
|
self.CONFIGS.complete_contexts.return_value = ['ceph']
|
||||||
self.service_name.return_value = 'cinder'
|
self.service_name.return_value = 'cinder'
|
||||||
|
Loading…
Reference in New Issue
Block a user