Add radosgw-user relation
Add a radosgw-user relation to allow charms to request a user. The requesting charm should supply the 'system-role' key in the app relation data bag to indicate whether the requested user should be a system user. This charm creates the user if it does not exist or looks up the users credentials if it does. The username and credentials are then passed back to the requestor via the app relation data bag. The units radosgw url and daemon id are also passed back this time using the unit relation data bag. Change-Id: Ieff1943b02f490559ccd245f60b744fb76a5d832
This commit is contained in:
parent
fb0565a3e1
commit
fa1e41e2f8
@ -28,6 +28,7 @@ import multisite
|
|||||||
|
|
||||||
from charmhelpers.core.hookenv import (
|
from charmhelpers.core.hookenv import (
|
||||||
relation_get,
|
relation_get,
|
||||||
|
relation_id as ch_relation_id,
|
||||||
relation_ids,
|
relation_ids,
|
||||||
related_units,
|
related_units,
|
||||||
config,
|
config,
|
||||||
@ -42,8 +43,10 @@ from charmhelpers.core.hookenv import (
|
|||||||
is_leader,
|
is_leader,
|
||||||
leader_set,
|
leader_set,
|
||||||
leader_get,
|
leader_get,
|
||||||
|
remote_service_name,
|
||||||
WORKLOAD_STATES,
|
WORKLOAD_STATES,
|
||||||
)
|
)
|
||||||
|
from charmhelpers.core.strutils import bool_from_string
|
||||||
from charmhelpers.fetch import (
|
from charmhelpers.fetch import (
|
||||||
apt_update,
|
apt_update,
|
||||||
apt_install,
|
apt_install,
|
||||||
@ -243,6 +246,9 @@ def config_changed():
|
|||||||
for r_id in relation_ids('object-store'):
|
for r_id in relation_ids('object-store'):
|
||||||
object_store_joined(r_id)
|
object_store_joined(r_id)
|
||||||
|
|
||||||
|
for r_id in relation_ids('radosgw-user'):
|
||||||
|
radosgw_user_changed(r_id)
|
||||||
|
|
||||||
process_multisite_relations()
|
process_multisite_relations()
|
||||||
|
|
||||||
CONFIGS.write_all()
|
CONFIGS.write_all()
|
||||||
@ -367,6 +373,10 @@ def mon_relation(rid=None, unit=None):
|
|||||||
zone))
|
zone))
|
||||||
|
|
||||||
service_restart(service_name())
|
service_restart(service_name())
|
||||||
|
|
||||||
|
for r_id in relation_ids('radosgw-user'):
|
||||||
|
radosgw_user_changed(r_id)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
send_request_if_needed(rq, relation='mon')
|
send_request_if_needed(rq, relation='mon')
|
||||||
_mon_relation()
|
_mon_relation()
|
||||||
@ -575,6 +585,91 @@ def certs_changed(relation_id=None, unit=None):
|
|||||||
_certs_changed()
|
_certs_changed()
|
||||||
|
|
||||||
|
|
||||||
|
def get_radosgw_username(r_id):
|
||||||
|
"""Generate a username based on a relation id"""
|
||||||
|
gw_user = 'juju-' + r_id.replace(":", "-")
|
||||||
|
return gw_user
|
||||||
|
|
||||||
|
|
||||||
|
def get_radosgw_system_username(r_id):
|
||||||
|
"""Generate a username for a system user based on a relation id"""
|
||||||
|
gw_user = get_radosgw_username(r_id)
|
||||||
|
# There is no way to switch a user from being a system user to a
|
||||||
|
# non-system user, so add the '-system' suffix to ensure there is
|
||||||
|
# no clash if the user request is updated in the future.
|
||||||
|
gw_user = gw_user + "-system"
|
||||||
|
return gw_user
|
||||||
|
|
||||||
|
|
||||||
|
@hooks.hook('radosgw-user-relation-departed')
|
||||||
|
def radosgw_user_departed():
|
||||||
|
# If there are no related units then the last unit
|
||||||
|
# is currently departing.
|
||||||
|
if not related_units():
|
||||||
|
r_id = ch_relation_id()
|
||||||
|
for user in [get_radosgw_system_username(r_id),
|
||||||
|
get_radosgw_username(r_id)]:
|
||||||
|
multisite.suspend_user(user)
|
||||||
|
|
||||||
|
|
||||||
|
@hooks.hook('radosgw-user-relation-changed')
|
||||||
|
def radosgw_user_changed(relation_id=None):
|
||||||
|
if not ready_for_service(legacy=False):
|
||||||
|
log('unit not ready, deferring radosgw_user configuration')
|
||||||
|
return
|
||||||
|
if relation_id:
|
||||||
|
r_ids = [relation_id]
|
||||||
|
else:
|
||||||
|
r_ids = relation_ids('radosgw-user')
|
||||||
|
# The leader manages the users and sets the credentials using the
|
||||||
|
# the application relation data bag.
|
||||||
|
if is_leader():
|
||||||
|
for r_id in r_ids:
|
||||||
|
remote_app = remote_service_name(r_id)
|
||||||
|
relation_data = relation_get(
|
||||||
|
rid=r_id,
|
||||||
|
app=remote_app)
|
||||||
|
if 'system-role' not in relation_data:
|
||||||
|
log('system-role not in relation data, cannot create user',
|
||||||
|
level=DEBUG)
|
||||||
|
return
|
||||||
|
system_user = bool_from_string(
|
||||||
|
relation_data.get('system-role', 'false'))
|
||||||
|
if system_user:
|
||||||
|
gw_user = get_radosgw_system_username(r_id)
|
||||||
|
# If there is a pre-existing non-system user then ensure it is
|
||||||
|
# suspended
|
||||||
|
multisite.suspend_user(get_radosgw_username(r_id))
|
||||||
|
else:
|
||||||
|
gw_user = get_radosgw_username(r_id)
|
||||||
|
# If there is a pre-existing system user then ensure it is
|
||||||
|
# suspended
|
||||||
|
multisite.suspend_user(get_radosgw_system_username(r_id))
|
||||||
|
if gw_user in multisite.list_users():
|
||||||
|
(access_key, secret_key) = multisite.get_user_creds(gw_user)
|
||||||
|
else:
|
||||||
|
(access_key, secret_key) = multisite.create_user(
|
||||||
|
gw_user,
|
||||||
|
system_user=system_user)
|
||||||
|
relation_set(
|
||||||
|
app=remote_app,
|
||||||
|
relation_id=r_id,
|
||||||
|
relation_settings={
|
||||||
|
'uid': gw_user,
|
||||||
|
'access-key': access_key,
|
||||||
|
'secret-key': secret_key})
|
||||||
|
# Each unit publishes its own endpoint data and daemon id using the
|
||||||
|
# unit relation data bag.
|
||||||
|
for r_id in r_ids:
|
||||||
|
relation_set(
|
||||||
|
relation_id=r_id,
|
||||||
|
relation_settings={
|
||||||
|
'internal-url': "{}:{}".format(
|
||||||
|
canonical_url(CONFIGS, INTERNAL),
|
||||||
|
listen_port()),
|
||||||
|
'daemon-id': socket.gethostname()})
|
||||||
|
|
||||||
|
|
||||||
@hooks.hook('master-relation-joined')
|
@hooks.hook('master-relation-joined')
|
||||||
def master_relation_joined(relation_id=None):
|
def master_relation_joined(relation_id=None):
|
||||||
if not ready_for_service(legacy=False):
|
if not ready_for_service(legacy=False):
|
||||||
@ -732,6 +827,8 @@ def leader_settings_changed():
|
|||||||
if not is_leader():
|
if not is_leader():
|
||||||
for r_id in relation_ids('master'):
|
for r_id in relation_ids('master'):
|
||||||
master_relation_joined(r_id)
|
master_relation_joined(r_id)
|
||||||
|
for r_id in relation_ids('radosgw-user'):
|
||||||
|
radosgw_user_changed(r_id)
|
||||||
|
|
||||||
|
|
||||||
def process_multisite_relations():
|
def process_multisite_relations():
|
||||||
|
@ -316,12 +316,48 @@ def tidy_defaults():
|
|||||||
update_period()
|
update_period()
|
||||||
|
|
||||||
|
|
||||||
def create_system_user(username):
|
def get_user_creds(username):
|
||||||
|
cmd = [
|
||||||
|
RGW_ADMIN, '--id={}'.format(_key_name()),
|
||||||
|
'user', 'info',
|
||||||
|
'--uid={}'.format(username)
|
||||||
|
]
|
||||||
|
result = json.loads(_check_output(cmd))
|
||||||
|
return (result['keys'][0]['access_key'],
|
||||||
|
result['keys'][0]['secret_key'])
|
||||||
|
|
||||||
|
|
||||||
|
def suspend_user(username):
|
||||||
"""
|
"""
|
||||||
Create a RADOS Gateway system use for sync usage
|
Suspend a RADOS Gateway user
|
||||||
|
|
||||||
:param username: username of user to create
|
:param username: username of user to create
|
||||||
:type username: str
|
:type username: str
|
||||||
|
"""
|
||||||
|
if username not in list_users():
|
||||||
|
hookenv.log(
|
||||||
|
"Cannot suspended user {}. User not found.".format(username),
|
||||||
|
level=hookenv.DEBUG)
|
||||||
|
return
|
||||||
|
cmd = [
|
||||||
|
RGW_ADMIN, '--id={}'.format(_key_name()),
|
||||||
|
'user', 'suspend',
|
||||||
|
'--uid={}'.format(username)
|
||||||
|
]
|
||||||
|
_check_output(cmd)
|
||||||
|
hookenv.log(
|
||||||
|
"Suspended user {}".format(username),
|
||||||
|
level=hookenv.DEBUG)
|
||||||
|
|
||||||
|
|
||||||
|
def create_user(username, system_user=False):
|
||||||
|
"""
|
||||||
|
Create a RADOS Gateway user
|
||||||
|
|
||||||
|
:param username: username of user to create
|
||||||
|
:type username: str
|
||||||
|
:param system_user: Whether to grant system user role
|
||||||
|
:type system_user: bool
|
||||||
:return: access key and secret
|
:return: access key and secret
|
||||||
:rtype: (str, str)
|
:rtype: (str, str)
|
||||||
"""
|
"""
|
||||||
@ -329,9 +365,10 @@ def create_system_user(username):
|
|||||||
RGW_ADMIN, '--id={}'.format(_key_name()),
|
RGW_ADMIN, '--id={}'.format(_key_name()),
|
||||||
'user', 'create',
|
'user', 'create',
|
||||||
'--uid={}'.format(username),
|
'--uid={}'.format(username),
|
||||||
'--display-name=Synchronization User',
|
'--display-name=Synchronization User'
|
||||||
'--system',
|
|
||||||
]
|
]
|
||||||
|
if system_user:
|
||||||
|
cmd.append('--system')
|
||||||
try:
|
try:
|
||||||
result = json.loads(_check_output(cmd))
|
result = json.loads(_check_output(cmd))
|
||||||
return (result['keys'][0]['access_key'],
|
return (result['keys'][0]['access_key'],
|
||||||
@ -340,6 +377,18 @@ def create_system_user(username):
|
|||||||
return (None, None)
|
return (None, None)
|
||||||
|
|
||||||
|
|
||||||
|
def create_system_user(username):
|
||||||
|
"""
|
||||||
|
Create a RADOS Gateway system user
|
||||||
|
|
||||||
|
:param username: username of user to create
|
||||||
|
:type username: str
|
||||||
|
:return: access key and secret
|
||||||
|
:rtype: (str, str)
|
||||||
|
"""
|
||||||
|
create_user(username, system_user=True)
|
||||||
|
|
||||||
|
|
||||||
def pull_realm(url, access_key, secret):
|
def pull_realm(url, access_key, secret):
|
||||||
"""
|
"""
|
||||||
Pull in a RADOS Gateway Realm from a master RGW instance
|
Pull in a RADOS Gateway Realm from a master RGW instance
|
||||||
|
1
hooks/radosgw-user-relation-changed
Symbolic link
1
hooks/radosgw-user-relation-changed
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
hooks.py
|
1
hooks/radosgw-user-relation-departed
Symbolic link
1
hooks/radosgw-user-relation-departed
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
hooks.py
|
@ -45,6 +45,8 @@ provides:
|
|||||||
interface: radosgw-multisite
|
interface: radosgw-multisite
|
||||||
object-store:
|
object-store:
|
||||||
interface: swift-proxy
|
interface: swift-proxy
|
||||||
|
radosgw-user:
|
||||||
|
interface: radosgw-user
|
||||||
peers:
|
peers:
|
||||||
cluster:
|
cluster:
|
||||||
interface: swift-ha
|
interface: swift-ha
|
||||||
|
@ -46,6 +46,7 @@ TO_PATCH = [
|
|||||||
'relation_set',
|
'relation_set',
|
||||||
'relation_get',
|
'relation_get',
|
||||||
'related_units',
|
'related_units',
|
||||||
|
'remote_service_name',
|
||||||
'status_set',
|
'status_set',
|
||||||
'subprocess',
|
'subprocess',
|
||||||
'sys',
|
'sys',
|
||||||
@ -509,6 +510,56 @@ class CephRadosGWTests(CharmTestCase):
|
|||||||
)
|
)
|
||||||
mock_configure_https.assert_called_once_with()
|
mock_configure_https.assert_called_once_with()
|
||||||
|
|
||||||
|
@patch.object(ceph_hooks, 'canonical_url')
|
||||||
|
@patch.object(ceph_hooks, 'is_leader')
|
||||||
|
def test_radosgw_user_changed(self, is_leader, canonical_url):
|
||||||
|
relation_data = {
|
||||||
|
'radosgw-user:3': {'system-role': 'false'},
|
||||||
|
'radosgw-user:5': {'system-role': 'true'}}
|
||||||
|
user = {
|
||||||
|
'juju-radosgw-user-3': ('access1', 'key1'),
|
||||||
|
'juju-radosgw-user-5-system': ('access2', 'key2')}
|
||||||
|
self.ready_for_service.return_value = True
|
||||||
|
is_leader.return_value = True
|
||||||
|
self.remote_service_name.return_value = 'ceph-dashboard'
|
||||||
|
canonical_url.return_value = 'http://radosgw'
|
||||||
|
self.listen_port.return_value = 80
|
||||||
|
self.socket.gethostname.return_value = 'testinghostname'
|
||||||
|
self.relation_ids.return_value = relation_data.keys()
|
||||||
|
self.relation_get.side_effect = lambda rid, app: relation_data[rid]
|
||||||
|
self.multisite.list_users.return_value = ['juju-radosgw-user-3']
|
||||||
|
self.multisite.get_user_creds.side_effect = lambda u: user[u]
|
||||||
|
self.multisite.create_user.side_effect = lambda u, system_user: user[u]
|
||||||
|
ceph_hooks.radosgw_user_changed()
|
||||||
|
expected = [
|
||||||
|
call(
|
||||||
|
app='ceph-dashboard',
|
||||||
|
relation_id='radosgw-user:3',
|
||||||
|
relation_settings={
|
||||||
|
'uid': 'juju-radosgw-user-3',
|
||||||
|
'access-key': 'access1',
|
||||||
|
'secret-key': 'key1'}),
|
||||||
|
call(
|
||||||
|
app='ceph-dashboard',
|
||||||
|
relation_id='radosgw-user:5',
|
||||||
|
relation_settings={
|
||||||
|
'uid': 'juju-radosgw-user-5-system',
|
||||||
|
'access-key': 'access2',
|
||||||
|
'secret-key': 'key2'}),
|
||||||
|
call(
|
||||||
|
relation_id='radosgw-user:3',
|
||||||
|
relation_settings={
|
||||||
|
'internal-url': 'http://radosgw:80',
|
||||||
|
'daemon-id': 'testinghostname'}),
|
||||||
|
call(
|
||||||
|
relation_id='radosgw-user:5',
|
||||||
|
relation_settings={
|
||||||
|
'internal-url': 'http://radosgw:80',
|
||||||
|
'daemon-id': 'testinghostname'})]
|
||||||
|
self.relation_set.assert_has_calls(
|
||||||
|
expected,
|
||||||
|
any_order=True)
|
||||||
|
|
||||||
|
|
||||||
class MiscMultisiteTests(CharmTestCase):
|
class MiscMultisiteTests(CharmTestCase):
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user