Ensure crgw works with Keystone v3

The URLs for SSL certificates are different and the v2 client
functions do not work. This change fixes these issues.

Add missing config.yaml options.

Change-Id: Ia4c7b508e70f690493b098c6fb07d24a340bc2a6
Closes-Bug: #1708464
This commit is contained in:
David Ames 2017-09-01 14:58:45 -07:00
parent 2226518612
commit a1444c4b2f
3 changed files with 147 additions and 37 deletions

View File

@ -275,3 +275,49 @@ options:
description: |
A comma-separated list of nagios servicegroups. If left empty,
the nagios_context will be used as the servicegroup
# HAProxy Parameters
haproxy-server-timeout:
type: int
default:
description: |
Server timeout configuration in ms for haproxy, used in HA
configurations. If not provided, default value of 30000ms is used.
haproxy-client-timeout:
type: int
default:
description: |
Client timeout configuration in ms for haproxy, used in HA
configurations. If not provided, default value of 30000ms is used.
haproxy-queue-timeout:
type: int
default:
description: |
Queue timeout configuration in ms for haproxy, used in HA
configurations. If not provided, default value of 5000ms is used.
haproxy-connect-timeout:
type: int
default:
description: |
Connect timeout configuration in ms for haproxy, used in HA
configurations. If not provided, default value of 5000ms is used.
# External SSL Parameters
ssl_cert:
type: string
default:
description: |
SSL certificate to install and use for API ports. Setting this value
and ssl_key will enable reverse proxying, point Glance's entry in the
Keystone catalog to use https, and override any certificate and key
issued by Keystone (if it is configured to do so).
ssl_key:
type: string
default:
description: SSL key to use with certificate specified as ssl_cert.
ssl_ca:
type: string
default:
description: |
SSL CA to use with the certificate and key provided - this is only
required if you are providing a privately signed ssl_cert and ssl_key.

View File

@ -25,6 +25,7 @@ import ceph_radosgw_context
from charmhelpers.core.hookenv import (
log,
DEBUG,
ERROR,
INFO,
relation_get,
relation_ids,
@ -72,6 +73,7 @@ from charmhelpers.fetch import (
try:
import keystoneclient
from keystoneclient.v2_0 import client
from keystoneclient.v3 import client as client_v3
try:
# Kilo and newer
from keystoneclient.exceptions import (
@ -362,25 +364,32 @@ def defer_if_unavailable(modules):
def get_ks_cert(ksclient, auth_endpoint, cert_type):
"""Get certificate from keystone.
:param admin_token: Keystone admin token
:param ksclient: Keystone client
:param auth_endpoint: Keystone auth endpoint url
:param certs_path: Path to local certs store
:returns: certificate
"""
if ksclient.version == 'v3':
if cert_type == 'signing':
cert_type = 'certificates'
request = ("{}OS-SIMPLE-CERT/{}"
"".format(auth_endpoint, cert_type))
else:
request = "{}/certificates/{}".format(auth_endpoint, cert_type)
try:
try:
# Kilo and newer
if cert_type == 'ca':
cert = ksclient.certificates.get_ca_certificate()
elif cert_type == 'signing':
elif cert_type in ['signing', 'certificates']:
cert = ksclient.certificates.get_signing_certificate()
else:
raise KSCertSetupException("Invalid cert type "
"'{}'".format(cert_type))
except AttributeError:
# Juno and older
cert = requests.request('GET', "{}/certificates/{}".
format(auth_endpoint, cert_type)).text
# Keystone v3 or Juno and older
cert = requests.request('GET', request).text
except (ConnectionRefused, requests.exceptions.ConnectionError,
Forbidden, InternalServerError):
raise KSCertSetupException("Error connecting to keystone")
@ -389,17 +398,15 @@ def get_ks_cert(ksclient, auth_endpoint, cert_type):
@defer_if_unavailable(['keystoneclient'])
def get_ks_ca_cert(admin_token, auth_endpoint, certs_path):
def get_ks_ca_cert(ksclient, auth_endpoint, certs_path):
""""Get and store keystone CA certificate.
:param admin_token: Keystone admin token
:param ksclient: Keystone client
:param auth_endpoint: Keystone auth endpoint url
:param certs_path: Path to local certs store
:returns: None
"""
ksclient = keystoneclient.httpclient.HTTPClient(token=admin_token,
endpoint=auth_endpoint)
ca_cert = get_ks_cert(ksclient, auth_endpoint, 'ca')
if ca_cert:
try:
@ -424,15 +431,14 @@ def get_ks_ca_cert(admin_token, auth_endpoint, certs_path):
@defer_if_unavailable(['keystoneclient'])
def get_ks_signing_cert(admin_token, auth_endpoint, certs_path):
def get_ks_signing_cert(ksclient, auth_endpoint, certs_path):
""""Get and store keystone signing certificate.
:param admin_token: Keystone admin token
:param ksclient: Keystone client
:param auth_endpoint: Keystone auth endpoint url
:param certs_path: Path to local certs store
:returns: None
"""
ksclient = client.Client(token=admin_token, endpoint=auth_endpoint)
signing_cert = get_ks_cert(ksclient, auth_endpoint, 'signing')
if signing_cert:
try:
@ -474,8 +480,32 @@ def setup_keystone_certs(CONFIGS):
log("Missing relation settings - deferring cert setup",
level=DEBUG)
return
ksclient = get_keystone_client_from_relation()
if not ksclient:
log("Failed to get keystoneclient", level=ERROR)
return
auth_endpoint = ksclient.auth_endpoint
try:
get_ks_ca_cert(ksclient, auth_endpoint, certs_path)
get_ks_signing_cert(ksclient, auth_endpoint, certs_path)
except KSCertSetupException as e:
log("Keystone certs setup incomplete - {}".format(e), level=INFO)
# TODO: Move to charmhelpers
# TODO: Make it session aware
def get_keystone_client_from_relation(relation_type='identity-service'):
""" Get keystone client from relation data
:param relation_type: Relation to keystone
:returns: Keystone client
"""
rdata = {}
for relid in relation_ids('identity-service'):
for relid in relation_ids(relation_type):
for unit in related_units(relid):
rdata = relation_get(unit=unit, rid=relid)
if rdata:
@ -488,16 +518,21 @@ def setup_keystone_certs(CONFIGS):
if is_ipv6(settings.get('auth_host')):
settings['auth_host'] = format_ipv6_addr(settings.get('auth_host'))
api_version = rdata.get('api_version')
auth_endpoint = format_endpoint(auth_protocol,
settings['auth_host'],
settings['auth_port'],
settings['api_version'])
try:
get_ks_ca_cert(settings['admin_token'], auth_endpoint, certs_path)
get_ks_signing_cert(settings['admin_token'], auth_endpoint, certs_path)
except KSCertSetupException as e:
log("Keystone certs setup incomplete - {}".format(e), level=INFO)
if api_version and '3' in api_version:
ksclient = client_v3.Client(token=settings['admin_token'],
endpoint=auth_endpoint)
else:
ksclient = client.Client(token=settings['admin_token'],
endpoint=auth_endpoint)
# Add simple way to retrieve keystone auth endpoint
ksclient.auth_endpoint = auth_endpoint
return ksclient
def disable_unused_apache_sites():

View File

@ -89,38 +89,67 @@ class CephRadosGWUtilTests(CharmTestCase):
# ports=None whilst port checks are disabled.
f.assert_called_once_with('assessor', services='s1', ports=None)
@patch.object(utils, 'related_units')
@patch.object(utils, 'relation_ids')
@patch.dict('sys.modules', {'requests': MagicMock(),
'keystoneclient': MagicMock(),
'httpclient': MagicMock()})
@patch.object(utils, 'get_keystone_client_from_relation')
@patch.object(utils, 'is_ipv6', lambda addr: False)
@patch.object(utils, 'get_ks_signing_cert')
@patch.object(utils, 'get_ks_ca_cert')
@patch.object(utils, 'relation_get')
@patch.object(utils, 'mkdir')
def test_setup_keystone_certs(self, mock_mkdir, mock_relation_get,
def test_setup_keystone_certs(self, mock_mkdir,
mock_get_ks_ca_cert,
mock_get_ks_signing_cert,
mock_relation_ids, mock_related_units):
mock_get_keystone_client):
auth_host = 'foo/bar'
auth_port = 80
auth_url = 'http://%s:%s/v2.0' % (auth_host, auth_port)
mock_ksclient = MagicMock()
mock_ksclient.auth_endpoint = auth_url
mock_get_keystone_client.return_value = mock_ksclient
configs = MagicMock()
configs.complete_contexts.return_value = ['identity-service']
utils.setup_keystone_certs(configs)
mock_get_ks_signing_cert.assert_has_calls([call(mock_ksclient,
auth_url,
'/var/lib/ceph/nss')])
mock_get_ks_ca_cert.assert_has_calls([call(mock_ksclient, auth_url,
'/var/lib/ceph/nss')])
@patch.object(utils, 'client_v3')
@patch.object(utils, 'client')
@patch.object(utils, 'related_units')
@patch.object(utils, 'relation_ids')
@patch.object(utils, 'is_ipv6', lambda addr: False)
@patch.object(utils, 'relation_get')
def test_get_keystone_client_from_relation(self, mock_relation_get,
mock_relation_ids,
mock_related_units,
mock_client,
mock_client_v3):
auth_host = 'foo/bar'
auth_port = 80
admin_token = '666'
auth_url = 'http://%s:%s/v2.0' % (auth_host, auth_port)
self.format_endpoint.return_value = auth_url
configs = MagicMock()
configs.complete_contexts.return_value = ['identity-service']
mock_relation_ids.return_value = ['identity-service:5']
mock_related_units.return_value = ['keystone/1']
mock_relation_get.return_value = {'auth_host': auth_host,
'auth_port': auth_port,
'admin_token': admin_token,
'api_version': '2'}
utils.setup_keystone_certs(configs)
mock_get_ks_signing_cert.assert_has_calls([call(admin_token, auth_url,
'/var/lib/ceph/nss')])
mock_get_ks_ca_cert.assert_has_calls([call(admin_token, auth_url,
'/var/lib/ceph/nss')])
rel_data = {'auth_host': auth_host,
'auth_port': auth_port,
'admin_token': admin_token,
'api_version': '2'}
mock_relation_get.return_value = rel_data
utils.get_keystone_client_from_relation()
mock_client.Client.assert_called_with(endpoint=auth_url,
token=admin_token)
auth_url = 'http://%s:%s/v3' % (auth_host, auth_port)
self.format_endpoint.return_value = auth_url
rel_data['api_version'] = '3'
mock_relation_get.return_value = rel_data
utils.get_keystone_client_from_relation()
mock_client_v3.Client.assert_called_with(endpoint=auth_url,
token=admin_token)
@patch.object(utils, 'get_ks_cert')
@patch.object(utils.subprocess, 'Popen')