From fb2f757494173ba45a631d945a936459f5ccc529 Mon Sep 17 00:00:00 2001 From: Rodrigo Barbieri Date: Thu, 13 Jun 2019 11:57:20 -0300 Subject: [PATCH] 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 --- config.yaml | 8 ++++- hooks/ceph_radosgw_context.py | 8 ++++- hooks/hooks.py | 6 +++- templates/ceph.conf | 1 + unit_tests/test_ceph_radosgw_context.py | 28 ++++++++++++----- unit_tests/test_hooks.py | 40 +++++++++++++++---------- 6 files changed, 65 insertions(+), 26 deletions(-) diff --git a/config.yaml b/config.yaml index 56c53c09..322e07f5 100644 --- a/config.yaml +++ b/config.yaml @@ -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 diff --git a/hooks/ceph_radosgw_context.py b/hooks/ceph_radosgw_context.py index 7e4fed8c..54b73822 100644 --- a/hooks/ceph_radosgw_context.py +++ b/hooks/ceph_radosgw_context.py @@ -96,7 +96,13 @@ class IdentityServiceContext(context.IdentityServiceContext): ctxt.pop('admin_domain_id') ctxt['auth_type'] = 'keystone' - ctxt['user_roles'] = config('operator-roles') + 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 diff --git a/hooks/hooks.py b/hooks/hooks.py index 7db0e240..8debca8f 100755 --- a/hooks/hooks.py +++ b/hooks/hooks.py @@ -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) diff --git a/templates/ceph.conf b/templates/ceph.conf index db55dd4e..c1ea349d 100644 --- a/templates/ceph.conf +++ b/templates/ceph.conf @@ -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 diff --git a/unit_tests/test_ceph_radosgw_context.py b/unit_tests/test_ceph_radosgw_context.py index f1c80a56..1ba768f2 100644 --- a/unit_tests/test_ceph_radosgw_context.py +++ b/unit_tests/test_ceph_radosgw_context.py @@ -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']) diff --git a/unit_tests/test_hooks.py b/unit_tests/test_hooks.py index cc046b83..45207e7c 100644 --- a/unit_tests/test_hooks.py +++ b/unit_tests/test_hooks.py @@ -244,21 +244,31 @@ class CephRadosGWTests(CharmTestCase): @patch('charmhelpers.contrib.openstack.ip.resolve_address') @patch('charmhelpers.contrib.openstack.ip.config') def test_identity_joined(self, _config, _resolve_address): - 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', - relation_id='rid', - admin_url='http://myserv:80/swift') + + 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') + 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=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')