Add config option for keystone admin roles

RADOS Gateway supports setting keystone operator and admin
roles. RADOS Gateway requires admin roles for keystone users
to change their user quota. Regular operator/member roles
are not allowed to do so.

The lack of this config option prevents swift users with admin
roles from being able to set their quotas. Therefore, a config
option 'admin-roles' is now added to the charm to map to
'rgw keystone accepted admin roles' RADOS Gateway config.

Please note that this is only effective from Luminous
Ceph Release.

Change-Id: Ic0b9aa39eef9fbc6c43eb4e66ab72d90787c2017
Closes-Bug: #1831577
This commit is contained in:
Rodrigo Barbieri 2019-06-13 11:57:20 -03:00
parent c6ac5946ba
commit fb2f757494
6 changed files with 65 additions and 26 deletions

View File

@ -121,10 +121,16 @@ options:
# Keystone integration
operator-roles:
type: string
default: "Member,Admin"
default: "Member"
description: |
Comma-separated list of Swift operator roles; used when integrating with
OpenStack Keystone.
admin-roles:
type: string
default: "Admin"
description: |
Comma-separated list of Swift admin roles; used when integrating with
OpenStack Keystone. Admin roles can set the user quota amount.
region:
type: string
default: RegionOne

View File

@ -96,7 +96,13 @@ class IdentityServiceContext(context.IdentityServiceContext):
ctxt.pop('admin_domain_id')
ctxt['auth_type'] = 'keystone'
if cmp_pkgrevno('radosgw', "11.0.0") >= 0:
ctxt['user_roles'] = config('operator-roles')
ctxt['admin_roles'] = config('admin-roles')
else:
ctxt['user_roles'] = config('operator-roles')
if config('admin-roles'):
ctxt['user_roles'] += (',' + config('admin-roles'))
ctxt['cache_size'] = config('cache-size')
if self.context_complete(ctxt):
return ctxt

View File

@ -298,11 +298,15 @@ def identity_joined(relid=None):
(canonical_url(CONFIGS, INTERNAL), port)
public_url = '%s:%s/swift/v1' % \
(canonical_url(CONFIGS, PUBLIC), port)
roles = [x for x in [config('operator-roles'), config('admin-roles')] if x]
requested_roles = ''
if roles:
requested_roles = ','.join(roles) if len(roles) > 1 else roles[0]
relation_set(service='swift',
region=config('region'),
public_url=public_url, internal_url=internal_url,
admin_url=admin_url,
requested_roles=config('operator-roles'),
requested_roles=requested_roles,
relation_id=relid)

View File

@ -51,6 +51,7 @@ rgw keystone admin project = {{ admin_tenant_name }}
rgw keystone admin token = {{ admin_token }}
{% endif -%}
rgw keystone accepted roles = {{ user_roles }}
rgw keystone accepted admin roles = {{ admin_roles }}
rgw keystone token cache size = {{ cache_size }}
rgw s3 auth use keystone = true
rgw s3 auth order = local, external

View File

@ -85,8 +85,12 @@ class IdentityServiceContextTest(CharmTestCase):
@patch.object(charmhelpers.contrib.openstack.context, 'log')
def test_ids_ctxt(self, _log, _rids, _runits, _rget, _ctxt_comp,
_format_ipv6_addr, _filter_installed_packages,
jewel_installed=False):
jewel_installed=False, cmp_pkgrevno_side_effects=None):
self.cmp_pkgrevno.side_effect = (cmp_pkgrevno_side_effects
if cmp_pkgrevno_side_effects
else [-1, -1])
self.test_config.set('operator-roles', 'Babel')
self.test_config.set('admin-roles', 'Dart')
self.test_config.set('cache-size', '42')
self.test_relation.set({'admin_token': 'ubuntutesting'})
self.relation_ids.return_value = ['identity-service:5']
@ -95,9 +99,6 @@ class IdentityServiceContextTest(CharmTestCase):
_rids.return_value = 'rid1'
_runits.return_value = 'runit'
_ctxt_comp.return_value = True
self.cmp_pkgrevno.return_value = -1
if jewel_installed:
self.cmp_pkgrevno.return_value = 0
id_data = {
'service_port': 9876,
'service_host': '127.0.0.4',
@ -127,8 +128,12 @@ class IdentityServiceContextTest(CharmTestCase):
'service_host': '127.0.0.4',
'service_port': 9876,
'service_protocol': 'http',
'user_roles': 'Babel',
}
if cmp_pkgrevno_side_effects and cmp_pkgrevno_side_effects[1] >= 0:
expect['user_roles'] = 'Babel'
expect['admin_roles'] = 'Dart'
else:
expect['user_roles'] = 'Babel,Dart'
if jewel_installed:
expect['auth_keystone_v3_supported'] = True
self.assertEqual(expect, ids_ctxt())
@ -145,6 +150,7 @@ class IdentityServiceContextTest(CharmTestCase):
self, _log, _rids, _runits, _rget, _ctxt_comp, _format_ipv6_addr,
_filter_installed_packages, jewel_installed=False):
self.test_config.set('operator-roles', 'Babel')
self.test_config.set('admin-roles', 'Dart')
self.test_config.set('cache-size', '42')
self.test_relation.set({'admin_token': 'ubuntutesting'})
self.relation_ids.return_value = ['identity-service:5']
@ -183,7 +189,7 @@ class IdentityServiceContextTest(CharmTestCase):
'service_host': '127.0.0.4',
'service_port': 9876,
'service_protocol': 'http',
'user_roles': 'Babel',
'user_roles': 'Babel,Dart',
}
if jewel_installed:
expect['auth_keystone_v3_supported'] = True
@ -201,6 +207,7 @@ class IdentityServiceContextTest(CharmTestCase):
self, _log, _rids, _runits, _rget, _ctxt_comp, _format_ipv6_addr,
_filter_installed_packages, jewel_installed=False):
self.test_config.set('operator-roles', 'Babel')
self.test_config.set('admin-roles', 'Dart')
self.test_config.set('cache-size', '42')
self.test_relation.set({'admin_token': 'ubuntutesting'})
self.relation_ids.return_value = ['identity-service:5']
@ -244,14 +251,19 @@ class IdentityServiceContextTest(CharmTestCase):
'service_host': '127.0.0.4',
'service_port': 9876,
'service_protocol': 'http',
'user_roles': 'Babel',
'user_roles': 'Babel,Dart',
}
if jewel_installed:
expect['auth_keystone_v3_supported'] = True
self.assertEqual(expect, ids_ctxt())
def test_ids_ctxt_jewel(self):
self.test_ids_ctxt(jewel_installed=True)
self.test_ids_ctxt(jewel_installed=True,
cmp_pkgrevno_side_effects=[0, -1])
def test_ids_ctxt_luminous(self):
self.test_ids_ctxt(jewel_installed=True,
cmp_pkgrevno_side_effects=[1, 0])
@patch.object(charmhelpers.contrib.openstack.context,
'filter_installed_packages', return_value=['absent-pkg'])

View File

@ -244,22 +244,32 @@ class CephRadosGWTests(CharmTestCase):
@patch('charmhelpers.contrib.openstack.ip.resolve_address')
@patch('charmhelpers.contrib.openstack.ip.config')
def test_identity_joined(self, _config, _resolve_address):
def _test_identify_joined(expected):
self.related_units = ['unit/0']
self.cmp_pkgrevno.return_value = 1
_resolve_address.return_value = 'myserv'
_config.side_effect = self.test_config.get
self.test_config.set('region', 'region1')
self.test_config.set('operator-roles', 'admin')
ceph_hooks.identity_joined(relid='rid')
self.relation_set.assert_called_with(
service='swift',
region='region1',
public_url='http://myserv:80/swift/v1',
internal_url='http://myserv:80/swift/v1',
requested_roles='admin',
requested_roles=expected,
relation_id='rid',
admin_url='http://myserv:80/swift')
inputs = [{'operator': 'foo', 'admin': 'bar', 'expected': 'foo,bar'},
{'operator': 'foo', 'expected': 'foo'},
{'admin': 'bar', 'expected': 'bar'},
{'expected': ''}]
for input in inputs:
self.test_config.set('operator-roles', input.get('operator', ''))
self.test_config.set('admin-roles', input.get('admin', ''))
_test_identify_joined(input['expected'])
@patch('charmhelpers.contrib.openstack.ip.service_name',
lambda *args: 'ceph-radosgw')
@patch('charmhelpers.contrib.openstack.ip.is_clustered')